Revert "Hydrogenisation of binops"
This reverts r17052-17054 for various build breaks. TBR=mstarzinger@chromium.org BUG= Review URL: https://codereview.chromium.org/25571002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17055 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
cee81cf0b7
commit
9459ed3ab4
File diff suppressed because it is too large
Load Diff
@ -1968,7 +1968,7 @@ class CountOperation V8_FINAL : public Expression {
|
|||||||
virtual KeyedAccessStoreMode GetStoreMode() V8_OVERRIDE {
|
virtual KeyedAccessStoreMode GetStoreMode() V8_OVERRIDE {
|
||||||
return store_mode_;
|
return store_mode_;
|
||||||
}
|
}
|
||||||
Handle<Type> type() const { return type_; }
|
TypeInfo type() const { return type_; }
|
||||||
|
|
||||||
BailoutId AssignmentId() const { return assignment_id_; }
|
BailoutId AssignmentId() const { return assignment_id_; }
|
||||||
|
|
||||||
@ -1997,7 +1997,7 @@ class CountOperation V8_FINAL : public Expression {
|
|||||||
bool is_monomorphic_ : 1;
|
bool is_monomorphic_ : 1;
|
||||||
KeyedAccessStoreMode store_mode_ : 5; // Windows treats as signed,
|
KeyedAccessStoreMode store_mode_ : 5; // Windows treats as signed,
|
||||||
// must have extra bit.
|
// must have extra bit.
|
||||||
Handle<Type> type_;
|
TypeInfo type_;
|
||||||
|
|
||||||
Expression* expression_;
|
Expression* expression_;
|
||||||
int pos_;
|
int pos_;
|
||||||
|
@ -841,101 +841,6 @@ Handle<Code> CompareNilICStub::GenerateCode(Isolate* isolate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <>
|
|
||||||
HValue* CodeStubGraphBuilder<BinaryOpStub>::BuildCodeInitializedStub() {
|
|
||||||
BinaryOpStub* stub = casted_stub();
|
|
||||||
HValue* left = GetParameter(0);
|
|
||||||
HValue* right = GetParameter(1);
|
|
||||||
|
|
||||||
Handle<Type> left_type = stub->GetLeftType(isolate());
|
|
||||||
Handle<Type> right_type = stub->GetRightType(isolate());
|
|
||||||
Handle<Type> result_type = stub->GetResultType(isolate());
|
|
||||||
|
|
||||||
ASSERT(!left_type->Is(Type::None()) && !right_type->Is(Type::None()) &&
|
|
||||||
(stub->HasSideEffects(isolate()) || !result_type->Is(Type::None())));
|
|
||||||
|
|
||||||
HValue* result = NULL;
|
|
||||||
if (stub->operation() == 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 add is performance
|
|
||||||
// critical.
|
|
||||||
if (left_type->Maybe(Type::String())) {
|
|
||||||
IfBuilder left_string(this);
|
|
||||||
left_string.IfNot<HIsSmiAndBranch>(left);
|
|
||||||
left_string.AndIf<HIsStringAndBranch>(left);
|
|
||||||
left_string.Then();
|
|
||||||
Push(Add<HStringAdd>(left, right, STRING_ADD_CHECK_RIGHT));
|
|
||||||
left_string.Else();
|
|
||||||
Push(AddInstruction(BuildBinaryOperation(stub->operation(),
|
|
||||||
left, right, left_type, right_type, result_type,
|
|
||||||
stub->fixed_right_arg(), true)));
|
|
||||||
left_string.End();
|
|
||||||
result = Pop();
|
|
||||||
} else {
|
|
||||||
IfBuilder right_string(this);
|
|
||||||
right_string.IfNot<HIsSmiAndBranch>(right);
|
|
||||||
right_string.AndIf<HIsStringAndBranch>(right);
|
|
||||||
right_string.Then();
|
|
||||||
Push(Add<HStringAdd>(left, right, STRING_ADD_CHECK_LEFT));
|
|
||||||
right_string.Else();
|
|
||||||
Push(AddInstruction(BuildBinaryOperation(stub->operation(),
|
|
||||||
left, right, left_type, right_type, result_type,
|
|
||||||
stub->fixed_right_arg(), true)));
|
|
||||||
right_string.End();
|
|
||||||
result = Pop();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = AddInstruction(BuildBinaryOperation(stub->operation(),
|
|
||||||
left, right, left_type, right_type, result_type,
|
|
||||||
stub->fixed_right_arg(), true));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 (result_type->Is(Type::Smi())) {
|
|
||||||
if (stub->operation() == Token::SHR) {
|
|
||||||
// TODO(olivf) Replace this by a SmiTagU Instruction.
|
|
||||||
// 0x40000000: this number would convert to negative when interpreting
|
|
||||||
// the register as signed value;
|
|
||||||
IfBuilder if_of(this);
|
|
||||||
if_of.IfNot<HCompareNumericAndBranch>(result,
|
|
||||||
Add<HConstant>(static_cast<int>(0x40000000)), Token::EQ_STRICT);
|
|
||||||
if_of.Then();
|
|
||||||
if_of.ElseDeopt("UInt->Smi oveflow");
|
|
||||||
if_of.End();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = EnforceNumberType(result, result_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reuse the double box if we are allowed to (i.e. chained binops).
|
|
||||||
if (stub->CanReuseDoubleBox()) {
|
|
||||||
HValue* reuse = (stub->mode() == OVERWRITE_LEFT) ? left : right;
|
|
||||||
IfBuilder if_heap_number(this);
|
|
||||||
if_heap_number.IfNot<HIsSmiAndBranch>(reuse);
|
|
||||||
if_heap_number.Then();
|
|
||||||
HValue* res_val = Add<HForceRepresentation>(result,
|
|
||||||
Representation::Double());
|
|
||||||
HObjectAccess access = HObjectAccess::ForHeapNumberValue();
|
|
||||||
Add<HStoreNamedField>(reuse, access, res_val);
|
|
||||||
Push(reuse);
|
|
||||||
if_heap_number.Else();
|
|
||||||
Push(result);
|
|
||||||
if_heap_number.End();
|
|
||||||
result = Pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Handle<Code> BinaryOpStub::GenerateCode(Isolate* isolate) {
|
|
||||||
return DoGenerateCode(isolate, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
HValue* CodeStubGraphBuilder<ToBooleanStub>::BuildCodeInitializedStub() {
|
HValue* CodeStubGraphBuilder<ToBooleanStub>::BuildCodeInitializedStub() {
|
||||||
ToBooleanStub* stub = casted_stub();
|
ToBooleanStub* stub = casted_stub();
|
||||||
|
@ -137,7 +137,6 @@ Handle<Code> CodeStub::GetCode(Isolate* isolate) {
|
|||||||
? FindCodeInSpecialCache(&code, isolate)
|
? FindCodeInSpecialCache(&code, isolate)
|
||||||
: FindCodeInCache(&code, isolate)) {
|
: FindCodeInCache(&code, isolate)) {
|
||||||
ASSERT(IsPregenerated(isolate) == code->is_pregenerated());
|
ASSERT(IsPregenerated(isolate) == code->is_pregenerated());
|
||||||
ASSERT(GetCodeKind() == code->kind());
|
|
||||||
return Handle<Code>(code);
|
return Handle<Code>(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,307 +203,119 @@ void CodeStub::PrintName(StringStream* stream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BinaryOpStub::PrintBaseName(StringStream* stream) {
|
void BinaryOpStub::Generate(MacroAssembler* masm) {
|
||||||
|
// Explicitly allow generation of nested stubs. It is safe here because
|
||||||
|
// generation code does not use any raw pointers.
|
||||||
|
AllowStubCallsScope allow_stub_calls(masm, true);
|
||||||
|
|
||||||
|
BinaryOpIC::TypeInfo operands_type = Max(left_type_, right_type_);
|
||||||
|
if (left_type_ == BinaryOpIC::ODDBALL && right_type_ == BinaryOpIC::ODDBALL) {
|
||||||
|
// The OddballStub handles a number and an oddball, not two oddballs.
|
||||||
|
operands_type = BinaryOpIC::GENERIC;
|
||||||
|
}
|
||||||
|
switch (operands_type) {
|
||||||
|
case BinaryOpIC::UNINITIALIZED:
|
||||||
|
GenerateTypeTransition(masm);
|
||||||
|
break;
|
||||||
|
case BinaryOpIC::SMI:
|
||||||
|
GenerateSmiStub(masm);
|
||||||
|
break;
|
||||||
|
case BinaryOpIC::INT32:
|
||||||
|
GenerateInt32Stub(masm);
|
||||||
|
break;
|
||||||
|
case BinaryOpIC::NUMBER:
|
||||||
|
GenerateNumberStub(masm);
|
||||||
|
break;
|
||||||
|
case BinaryOpIC::ODDBALL:
|
||||||
|
GenerateOddballStub(masm);
|
||||||
|
break;
|
||||||
|
case BinaryOpIC::STRING:
|
||||||
|
GenerateStringStub(masm);
|
||||||
|
break;
|
||||||
|
case BinaryOpIC::GENERIC:
|
||||||
|
GenerateGeneric(masm);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define __ ACCESS_MASM(masm)
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpStub::GenerateCallRuntime(MacroAssembler* masm) {
|
||||||
|
switch (op_) {
|
||||||
|
case Token::ADD:
|
||||||
|
__ InvokeBuiltin(Builtins::ADD, CALL_FUNCTION);
|
||||||
|
break;
|
||||||
|
case Token::SUB:
|
||||||
|
__ InvokeBuiltin(Builtins::SUB, CALL_FUNCTION);
|
||||||
|
break;
|
||||||
|
case Token::MUL:
|
||||||
|
__ InvokeBuiltin(Builtins::MUL, CALL_FUNCTION);
|
||||||
|
break;
|
||||||
|
case Token::DIV:
|
||||||
|
__ InvokeBuiltin(Builtins::DIV, CALL_FUNCTION);
|
||||||
|
break;
|
||||||
|
case Token::MOD:
|
||||||
|
__ InvokeBuiltin(Builtins::MOD, CALL_FUNCTION);
|
||||||
|
break;
|
||||||
|
case Token::BIT_OR:
|
||||||
|
__ InvokeBuiltin(Builtins::BIT_OR, CALL_FUNCTION);
|
||||||
|
break;
|
||||||
|
case Token::BIT_AND:
|
||||||
|
__ InvokeBuiltin(Builtins::BIT_AND, CALL_FUNCTION);
|
||||||
|
break;
|
||||||
|
case Token::BIT_XOR:
|
||||||
|
__ InvokeBuiltin(Builtins::BIT_XOR, CALL_FUNCTION);
|
||||||
|
break;
|
||||||
|
case Token::SAR:
|
||||||
|
__ InvokeBuiltin(Builtins::SAR, CALL_FUNCTION);
|
||||||
|
break;
|
||||||
|
case Token::SHR:
|
||||||
|
__ InvokeBuiltin(Builtins::SHR, CALL_FUNCTION);
|
||||||
|
break;
|
||||||
|
case Token::SHL:
|
||||||
|
__ InvokeBuiltin(Builtins::SHL, CALL_FUNCTION);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#undef __
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpStub::PrintName(StringStream* stream) {
|
||||||
const char* op_name = Token::Name(op_);
|
const char* op_name = Token::Name(op_);
|
||||||
const char* ovr = "";
|
const char* overwrite_name;
|
||||||
if (mode_ == OVERWRITE_LEFT) ovr = "_ReuseLeft";
|
switch (mode_) {
|
||||||
if (mode_ == OVERWRITE_RIGHT) ovr = "_ReuseRight";
|
case NO_OVERWRITE: overwrite_name = "Alloc"; break;
|
||||||
stream->Add("BinaryOpStub_%s%s", op_name, ovr);
|
case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
|
||||||
}
|
case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
|
||||||
|
default: overwrite_name = "UnknownOverwrite"; break;
|
||||||
|
|
||||||
void BinaryOpStub::PrintState(StringStream* stream) {
|
|
||||||
stream->Add("(");
|
|
||||||
stream->Add(StateToName(left_state_));
|
|
||||||
if (left_bool_) {
|
|
||||||
stream->Add(",Boolean");
|
|
||||||
}
|
}
|
||||||
stream->Add("*");
|
stream->Add("BinaryOpStub_%s_%s_%s+%s",
|
||||||
if (fixed_right_arg_.has_value) {
|
op_name,
|
||||||
stream->Add("%d", fixed_right_arg_.value);
|
overwrite_name,
|
||||||
} else {
|
BinaryOpIC::GetName(left_type_),
|
||||||
stream->Add(StateToName(right_state_));
|
BinaryOpIC::GetName(right_type_));
|
||||||
if (right_bool_) {
|
|
||||||
stream->Add(",Boolean");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stream->Add("->");
|
|
||||||
stream->Add(StateToName(result_state_));
|
|
||||||
stream->Add(")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Maybe<Handle<Object> > BinaryOpStub::Result(Handle<Object> left,
|
void BinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
|
||||||
Handle<Object> right,
|
ASSERT(left_type_ == BinaryOpIC::STRING || right_type_ == BinaryOpIC::STRING);
|
||||||
Isolate* isolate) {
|
ASSERT(op_ == Token::ADD);
|
||||||
Handle<JSBuiltinsObject> builtins(isolate->js_builtins_object());
|
if (left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING) {
|
||||||
Builtins::JavaScript func = BinaryOpIC::TokenToJSBuiltin(op_);
|
GenerateBothStringStub(masm);
|
||||||
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;
|
|
||||||
left_bool_ = right_bool_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void BinaryOpStub::Generate(Token::Value op,
|
|
||||||
State left,
|
|
||||||
State right,
|
|
||||||
State result,
|
|
||||||
Isolate* isolate) {
|
|
||||||
BinaryOpStub stub(INITIALIZED);
|
|
||||||
stub.op_ = op;
|
|
||||||
stub.left_state_ = left;
|
|
||||||
stub.right_state_ = right;
|
|
||||||
stub.result_state_ = result;
|
|
||||||
stub.mode_ = NO_OVERWRITE;
|
|
||||||
stub.GetCode(isolate);
|
|
||||||
stub.mode_ = OVERWRITE_LEFT;
|
|
||||||
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};
|
|
||||||
// TODO(olivf) NumberTagU is not snapshot safe yet so we have to skip SHR
|
|
||||||
// since that produces a unsigned int32.
|
|
||||||
Token::Value bitop[] = {Token::BIT_OR, Token::BIT_AND, Token::BIT_XOR,
|
|
||||||
Token::SAR, Token::SHL /* Token::SHR */};
|
|
||||||
Token::Value arithop[] = {Token::ADD, Token::SUB, Token::MOD,
|
|
||||||
Token::DIV, Token::MUL};
|
|
||||||
for (unsigned i = 0; i < ARRAY_SIZE(binop); i++) {
|
|
||||||
BinaryOpStub stub(UNINITIALIZED);
|
|
||||||
stub.op_ = binop[i];
|
|
||||||
stub.GetCode(isolate);
|
|
||||||
}
|
|
||||||
for (unsigned i = 0; i < ARRAY_SIZE(arithop); i++) {
|
|
||||||
Generate(arithop[i], SMI, SMI, SMI, isolate);
|
|
||||||
Generate(arithop[i], SMI, SMI, INT32, isolate);
|
|
||||||
Generate(arithop[i], SMI, SMI, NUMBER, isolate);
|
|
||||||
Generate(arithop[i], INT32, INT32, INT32, isolate);
|
|
||||||
Generate(arithop[i], NUMBER, SMI, SMI, isolate);
|
|
||||||
Generate(arithop[i], NUMBER, SMI, NUMBER, isolate);
|
|
||||||
Generate(arithop[i], NUMBER, INT32, NUMBER, isolate);
|
|
||||||
Generate(arithop[i], NUMBER, NUMBER, NUMBER, isolate);
|
|
||||||
}
|
|
||||||
Generate(Token::SHR, SMI, SMI, SMI, isolate);
|
|
||||||
for (unsigned i = 0; i < ARRAY_SIZE(bitop); i++) {
|
|
||||||
Generate(bitop[i], SMI, SMI, SMI, isolate);
|
|
||||||
Generate(bitop[i], SMI, INT32, INT32, isolate);
|
|
||||||
Generate(bitop[i], INT32, INT32, INT32, isolate);
|
|
||||||
Generate(bitop[i], NUMBER, INT32, INT32, isolate);
|
|
||||||
Generate(bitop[i], NUMBER, NUMBER, INT32, isolate);
|
|
||||||
}
|
|
||||||
Generate(Token::ADD, STRING, STRING, STRING, isolate);
|
|
||||||
|
|
||||||
BinaryOpStub stub(INITIALIZED);
|
|
||||||
stub.op_ = Token::MOD;
|
|
||||||
stub.left_state_ = SMI;
|
|
||||||
stub.right_state_ = SMI;
|
|
||||||
stub.result_state_ = SMI;
|
|
||||||
stub.fixed_right_arg_.has_value = true;
|
|
||||||
stub.fixed_right_arg_.value = 4;
|
|
||||||
stub.mode_ = NO_OVERWRITE;
|
|
||||||
stub.GetCode(isolate);
|
|
||||||
stub.fixed_right_arg_.value = 8;
|
|
||||||
stub.GetCode(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_, &left_bool_);
|
|
||||||
UpdateStatus(right, &right_state_, &right_bool_);
|
|
||||||
|
|
||||||
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_, NULL);
|
|
||||||
|
|
||||||
State max_result = has_int_result() ? INT32 : NUMBER;
|
|
||||||
State max_input = Max(left_state_, right_state_);
|
|
||||||
|
|
||||||
// Avoid unnecessary Representation changes.
|
|
||||||
if (left_state_ == STRING && right_state_ < STRING) {
|
|
||||||
right_state_ = GENERIC;
|
|
||||||
} else if (right_state_ == STRING && left_state_ < STRING) {
|
|
||||||
left_state_ = GENERIC;
|
|
||||||
} else if ((right_state_ == GENERIC && left_state_ != STRING) ||
|
|
||||||
(left_state_ == GENERIC && right_state_ != STRING)) {
|
|
||||||
left_state_ = right_state_ = GENERIC;
|
|
||||||
} else if (max_input <= NUMBER && max_input > result_state_) {
|
|
||||||
result_state_ = Min(max_result, max_input);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(result_state_ <= max_result || op_ == Token::ADD);
|
|
||||||
|
|
||||||
if (old_state == GetExtraICState()) {
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void BinaryOpStub::UpdateStatus(Handle<Object> object,
|
|
||||||
State* state,
|
|
||||||
bool* bool_state) {
|
|
||||||
if (object->IsBoolean() && bool_state != NULL) {
|
|
||||||
*bool_state = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
v8::internal::TypeInfo type = v8::internal::TypeInfo::FromValue(object);
|
// Try to add arguments as strings, otherwise, transition to the generic
|
||||||
if (object->IsUndefined()) {
|
// BinaryOpIC type.
|
||||||
// Undefined will be automatically truncated for us by HChange.
|
GenerateAddStrings(masm);
|
||||||
type = (op_ == Token::BIT_AND || op_ == Token::BIT_OR ||
|
GenerateTypeTransition(masm);
|
||||||
op_ == Token::BIT_XOR || op_ == Token::SAR ||
|
|
||||||
op_ == Token::SHL || op_ == Token::SHR)
|
|
||||||
? TypeInfo::Integer32()
|
|
||||||
: TypeInfo::Double();
|
|
||||||
}
|
|
||||||
State int_state = SmiValuesAre32Bits() ? NUMBER : INT32;
|
|
||||||
State new_state = NONE;
|
|
||||||
if (type.IsSmi()) {
|
|
||||||
new_state = SMI;
|
|
||||||
} else if (type.IsInteger32()) {
|
|
||||||
new_state = int_state;
|
|
||||||
} else if (type.IsNumber()) {
|
|
||||||
new_state = NUMBER;
|
|
||||||
} else if (object->IsString() && operation() == Token::ADD) {
|
|
||||||
new_state = STRING;
|
|
||||||
} else {
|
|
||||||
new_state = GENERIC;
|
|
||||||
}
|
|
||||||
if ((new_state <= NUMBER && *state > NUMBER) ||
|
|
||||||
(new_state > NUMBER && *state <= NUMBER && *state != NONE)) {
|
|
||||||
new_state = GENERIC;
|
|
||||||
}
|
|
||||||
*state = Max(*state, new_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Handle<Type> BinaryOpStub::StateToType(State state,
|
|
||||||
bool seen_bool,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
if (seen_bool) {
|
|
||||||
t = handle(Type::Union(t, handle(Type::Boolean(), isolate)), isolate);
|
|
||||||
}
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Handle<Type> BinaryOpStub::GetLeftType(Isolate* isolate) const {
|
|
||||||
return StateToType(left_state_, left_bool_, isolate);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Handle<Type> BinaryOpStub::GetRightType(Isolate* isolate) const {
|
|
||||||
return StateToType(right_state_, right_bool_, isolate);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Handle<Type> BinaryOpStub::GetResultType(Isolate* isolate) const {
|
|
||||||
if (HasSideEffects(isolate)) return StateToType(NONE, false, 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_, false, isolate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
292
src/code-stubs.h
292
src/code-stubs.h
@ -200,9 +200,6 @@ class CodeStub BASE_EMBEDDED {
|
|||||||
|
|
||||||
virtual void PrintName(StringStream* stream);
|
virtual void PrintName(StringStream* stream);
|
||||||
|
|
||||||
// Returns a name for logging/debugging purposes.
|
|
||||||
SmartArrayPointer<const char> GetName();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static bool CanUseFPRegisters();
|
static bool CanUseFPRegisters();
|
||||||
|
|
||||||
@ -214,6 +211,8 @@ class CodeStub BASE_EMBEDDED {
|
|||||||
// a fixed (non-moveable) code object.
|
// a fixed (non-moveable) code object.
|
||||||
virtual bool NeedsImmovableCode() { return false; }
|
virtual bool NeedsImmovableCode() { return false; }
|
||||||
|
|
||||||
|
// Returns a name for logging/debugging purposes.
|
||||||
|
SmartArrayPointer<const char> GetName();
|
||||||
virtual void PrintBaseName(StringStream* stream);
|
virtual void PrintBaseName(StringStream* stream);
|
||||||
virtual void PrintState(StringStream* stream) { }
|
virtual void PrintState(StringStream* stream) { }
|
||||||
|
|
||||||
@ -996,177 +995,156 @@ class KeyedLoadFieldStub: public LoadFieldStub {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class BinaryOpStub: public HydrogenCodeStub {
|
class BinaryOpStub: public PlatformCodeStub {
|
||||||
public:
|
public:
|
||||||
BinaryOpStub(Token::Value op, OverwriteMode mode)
|
BinaryOpStub(Token::Value op, OverwriteMode mode)
|
||||||
: HydrogenCodeStub(UNINITIALIZED), op_(op), mode_(mode) {
|
: op_(op),
|
||||||
ASSERT(op <= LAST_TOKEN && op >= FIRST_TOKEN);
|
mode_(mode),
|
||||||
|
platform_specific_bit_(false),
|
||||||
|
left_type_(BinaryOpIC::UNINITIALIZED),
|
||||||
|
right_type_(BinaryOpIC::UNINITIALIZED),
|
||||||
|
result_type_(BinaryOpIC::UNINITIALIZED),
|
||||||
|
encoded_right_arg_(false, encode_arg_value(1)) {
|
||||||
Initialize();
|
Initialize();
|
||||||
|
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit BinaryOpStub(Code::ExtraICState state)
|
BinaryOpStub(
|
||||||
: op_(decode_token(OpBits::decode(state))),
|
int key,
|
||||||
mode_(OverwriteModeField::decode(state)),
|
BinaryOpIC::TypeInfo left_type,
|
||||||
fixed_right_arg_(
|
BinaryOpIC::TypeInfo right_type,
|
||||||
Maybe<int>(HasFixedRightArgBits::decode(state),
|
BinaryOpIC::TypeInfo result_type,
|
||||||
decode_arg_value(FixedRightArgValueBits::decode(state)))),
|
Maybe<int32_t> fixed_right_arg)
|
||||||
left_state_(LeftStateField::decode(state)),
|
: op_(OpBits::decode(key)),
|
||||||
left_bool_(LeftBoolField::decode(state)),
|
mode_(ModeBits::decode(key)),
|
||||||
right_state_(fixed_right_arg_.has_value
|
platform_specific_bit_(PlatformSpecificBits::decode(key)),
|
||||||
? ((fixed_right_arg_.value <= Smi::kMaxValue) ? SMI : INT32)
|
left_type_(left_type),
|
||||||
: RightStateField::decode(state)),
|
right_type_(right_type),
|
||||||
right_bool_(fixed_right_arg_.has_value
|
result_type_(result_type),
|
||||||
? false : RightBoolField::decode(state)),
|
encoded_right_arg_(fixed_right_arg.has_value,
|
||||||
result_state_(ResultStateField::decode(state)) {
|
encode_arg_value(fixed_right_arg.value)) { }
|
||||||
// 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
|
static void decode_types_from_minor_key(int minor_key,
|
||||||
// generation we always want it to reflect the current state.
|
BinaryOpIC::TypeInfo* left_type,
|
||||||
ASSERT(!fixed_right_arg_.has_value ||
|
BinaryOpIC::TypeInfo* right_type,
|
||||||
can_encode_arg_value(fixed_right_arg_.value));
|
BinaryOpIC::TypeInfo* result_type) {
|
||||||
|
*left_type =
|
||||||
|
static_cast<BinaryOpIC::TypeInfo>(LeftTypeBits::decode(minor_key));
|
||||||
|
*right_type =
|
||||||
|
static_cast<BinaryOpIC::TypeInfo>(RightTypeBits::decode(minor_key));
|
||||||
|
*result_type =
|
||||||
|
static_cast<BinaryOpIC::TypeInfo>(ResultTypeBits::decode(minor_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
static const int FIRST_TOKEN = Token::BIT_OR;
|
static Token::Value decode_op_from_minor_key(int minor_key) {
|
||||||
static const int LAST_TOKEN = Token::MOD;
|
return static_cast<Token::Value>(OpBits::decode(minor_key));
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Code::Kind GetCodeKind() const { return Code::BINARY_OP_IC; }
|
static Maybe<int> decode_fixed_right_arg_from_minor_key(int minor_key) {
|
||||||
virtual InlineCacheState GetICState() {
|
return Maybe<int>(
|
||||||
if (Max(left_state_, right_state_) == NONE && !left_bool_ && !right_bool_) {
|
HasFixedRightArgBits::decode(minor_key),
|
||||||
return ::v8::internal::UNINITIALIZED;
|
decode_arg_value(FixedRightArgValueBits::decode(minor_key)));
|
||||||
}
|
|
||||||
if (Max(left_state_, right_state_) == GENERIC) return MEGAMORPHIC;
|
|
||||||
return MONOMORPHIC;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Code::ExtraICState GetExtraICState() {
|
int fixed_right_arg_value() const {
|
||||||
bool sse_field = Max(result_state_, Max(left_state_, right_state_)) > SMI &&
|
return decode_arg_value(encoded_right_arg_.value);
|
||||||
CpuFeatures::IsSafeForSnapshot(SSE2);
|
|
||||||
|
|
||||||
return OpBits::encode(encode_token(op_))
|
|
||||||
| LeftStateField::encode(left_state_)
|
|
||||||
| LeftBoolField::encode(left_bool_)
|
|
||||||
| RightStateField::encode(fixed_right_arg_.has_value
|
|
||||||
? NONE : right_state_)
|
|
||||||
| RightBoolField::encode(fixed_right_arg_.has_value
|
|
||||||
? false
|
|
||||||
: right_bool_)
|
|
||||||
| 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_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanReuseDoubleBox() {
|
static bool can_encode_arg_value(int32_t value) {
|
||||||
return result_state_ <= NUMBER && result_state_ > SMI &&
|
return value > 0 &&
|
||||||
((left_state_ > SMI && left_state_ <= NUMBER &&
|
IsPowerOf2(value) &&
|
||||||
mode_ == OVERWRITE_LEFT) ||
|
FixedRightArgValueBits::is_valid(WhichPowerOf2(value));
|
||||||
(right_state_ > SMI && right_state_ <= NUMBER &&
|
|
||||||
mode_ == OVERWRITE_RIGHT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasSideEffects(Isolate* isolate) const {
|
enum SmiCodeGenerateHeapNumberResults {
|
||||||
return GetLeftType(isolate)->Maybe(Type::Receiver()) ||
|
ALLOW_HEAPNUMBER_RESULTS,
|
||||||
GetRightType(isolate)->Maybe(Type::Receiver());
|
NO_HEAPNUMBER_RESULTS
|
||||||
}
|
};
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit BinaryOpStub(InitializationState state) : HydrogenCodeStub(state),
|
|
||||||
op_(Token::ADD),
|
|
||||||
mode_(NO_OVERWRITE) {
|
|
||||||
Initialize();
|
|
||||||
}
|
|
||||||
void Initialize();
|
|
||||||
|
|
||||||
enum State { NONE, SMI, INT32, NUMBER, STRING, GENERIC };
|
|
||||||
|
|
||||||
// We truncate the last bit of the token.
|
|
||||||
STATIC_ASSERT(LAST_TOKEN - FIRST_TOKEN < (1 << 5));
|
|
||||||
class LeftStateField: public BitField<State, 0, 3> {};
|
|
||||||
class LeftBoolField: public BitField<bool, 3, 1> {};
|
|
||||||
// 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 RightBoolField: public BitField<bool, 8, 1> {};
|
|
||||||
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> {};
|
|
||||||
|
|
||||||
virtual CodeStub::Major MajorKey() { return BinaryOp; }
|
|
||||||
virtual int NotMissMinorKey() { return GetExtraICState(); }
|
|
||||||
|
|
||||||
static Handle<Type> StateToType(State state,
|
|
||||||
bool seen_bool,
|
|
||||||
Isolate* isolate);
|
|
||||||
|
|
||||||
static void Generate(Token::Value op,
|
|
||||||
State left,
|
|
||||||
State right,
|
|
||||||
State result,
|
|
||||||
Isolate* isolate);
|
|
||||||
|
|
||||||
void UpdateStatus(Handle<Object> object,
|
|
||||||
State* state,
|
|
||||||
bool* bool_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* StateToName(State state);
|
|
||||||
|
|
||||||
void PrintBaseName(StringStream* stream);
|
|
||||||
|
|
||||||
Token::Value op_;
|
Token::Value op_;
|
||||||
OverwriteMode mode_;
|
OverwriteMode mode_;
|
||||||
|
bool platform_specific_bit_; // Indicates SSE3 on IA32.
|
||||||
|
|
||||||
Maybe<int> fixed_right_arg_;
|
// Operand type information determined at runtime.
|
||||||
State left_state_;
|
BinaryOpIC::TypeInfo left_type_;
|
||||||
bool left_bool_;
|
BinaryOpIC::TypeInfo right_type_;
|
||||||
State right_state_;
|
BinaryOpIC::TypeInfo result_type_;
|
||||||
bool right_bool_;
|
|
||||||
State result_state_;
|
Maybe<int> encoded_right_arg_;
|
||||||
|
|
||||||
|
static int encode_arg_value(int32_t value) {
|
||||||
|
ASSERT(can_encode_arg_value(value));
|
||||||
|
return WhichPowerOf2(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t decode_arg_value(int value) {
|
||||||
|
return 1 << value;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void PrintName(StringStream* stream);
|
||||||
|
|
||||||
|
// Minor key encoding in all 25 bits FFFFFHTTTRRRLLLPOOOOOOOMM.
|
||||||
|
// Note: We actually do not need 7 bits for the operation, just 4 bits to
|
||||||
|
// encode ADD, SUB, MUL, DIV, MOD, BIT_OR, BIT_AND, BIT_XOR, SAR, SHL, SHR.
|
||||||
|
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
|
||||||
|
class OpBits: public BitField<Token::Value, 2, 7> {};
|
||||||
|
class PlatformSpecificBits: public BitField<bool, 9, 1> {};
|
||||||
|
class LeftTypeBits: public BitField<BinaryOpIC::TypeInfo, 10, 3> {};
|
||||||
|
class RightTypeBits: public BitField<BinaryOpIC::TypeInfo, 13, 3> {};
|
||||||
|
class ResultTypeBits: public BitField<BinaryOpIC::TypeInfo, 16, 3> {};
|
||||||
|
class HasFixedRightArgBits: public BitField<bool, 19, 1> {};
|
||||||
|
class FixedRightArgValueBits: public BitField<int, 20, 5> {};
|
||||||
|
|
||||||
|
Major MajorKey() { return BinaryOp; }
|
||||||
|
int MinorKey() {
|
||||||
|
return OpBits::encode(op_)
|
||||||
|
| ModeBits::encode(mode_)
|
||||||
|
| PlatformSpecificBits::encode(platform_specific_bit_)
|
||||||
|
| LeftTypeBits::encode(left_type_)
|
||||||
|
| RightTypeBits::encode(right_type_)
|
||||||
|
| ResultTypeBits::encode(result_type_)
|
||||||
|
| HasFixedRightArgBits::encode(encoded_right_arg_.has_value)
|
||||||
|
| FixedRightArgValueBits::encode(encoded_right_arg_.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Platform-independent implementation.
|
||||||
|
void Generate(MacroAssembler* masm);
|
||||||
|
void GenerateCallRuntime(MacroAssembler* masm);
|
||||||
|
|
||||||
|
// Platform-independent signature, platform-specific implementation.
|
||||||
|
void Initialize();
|
||||||
|
void GenerateAddStrings(MacroAssembler* masm);
|
||||||
|
void GenerateBothStringStub(MacroAssembler* masm);
|
||||||
|
void GenerateGeneric(MacroAssembler* masm);
|
||||||
|
void GenerateGenericStub(MacroAssembler* masm);
|
||||||
|
void GenerateNumberStub(MacroAssembler* masm);
|
||||||
|
void GenerateInt32Stub(MacroAssembler* masm);
|
||||||
|
void GenerateLoadArguments(MacroAssembler* masm);
|
||||||
|
void GenerateOddballStub(MacroAssembler* masm);
|
||||||
|
void GenerateRegisterArgsPush(MacroAssembler* masm);
|
||||||
|
void GenerateReturn(MacroAssembler* masm);
|
||||||
|
void GenerateSmiStub(MacroAssembler* masm);
|
||||||
|
void GenerateStringStub(MacroAssembler* masm);
|
||||||
|
void GenerateTypeTransition(MacroAssembler* masm);
|
||||||
|
void GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm);
|
||||||
|
void GenerateUninitializedStub(MacroAssembler* masm);
|
||||||
|
|
||||||
|
// Entirely platform-specific methods are defined as static helper
|
||||||
|
// functions in the <arch>/code-stubs-<arch>.cc files.
|
||||||
|
|
||||||
|
virtual Code::Kind GetCodeKind() const { return Code::BINARY_OP_IC; }
|
||||||
|
|
||||||
|
virtual InlineCacheState GetICState() {
|
||||||
|
return BinaryOpIC::ToState(Max(left_type_, right_type_));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void FinishCode(Handle<Code> code) {
|
||||||
|
code->set_stub_info(MinorKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class CodeGenerator;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1739,9 +1717,7 @@ class DoubleToIStub : public PlatformCodeStub {
|
|||||||
DestinationRegisterBits::encode(destination.code_) |
|
DestinationRegisterBits::encode(destination.code_) |
|
||||||
OffsetBits::encode(offset) |
|
OffsetBits::encode(offset) |
|
||||||
IsTruncatingBits::encode(is_truncating) |
|
IsTruncatingBits::encode(is_truncating) |
|
||||||
SkipFastPathBits::encode(skip_fastpath) |
|
SkipFastPathBits::encode(skip_fastpath);
|
||||||
SSEBits::encode(CpuFeatures::IsSafeForSnapshot(SSE2) ?
|
|
||||||
CpuFeatures::IsSafeForSnapshot(SSE3) ? 2 : 1 : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Register source() {
|
Register source() {
|
||||||
@ -1784,8 +1760,6 @@ class DoubleToIStub : public PlatformCodeStub {
|
|||||||
public BitField<int, 2 * kBitsPerRegisterNumber + 1, 3> {}; // NOLINT
|
public BitField<int, 2 * kBitsPerRegisterNumber + 1, 3> {}; // NOLINT
|
||||||
class SkipFastPathBits:
|
class SkipFastPathBits:
|
||||||
public BitField<int, 2 * kBitsPerRegisterNumber + 4, 1> {}; // NOLINT
|
public BitField<int, 2 * kBitsPerRegisterNumber + 4, 1> {}; // NOLINT
|
||||||
class SSEBits:
|
|
||||||
public BitField<int, 2 * kBitsPerRegisterNumber + 5, 2> {}; // NOLINT
|
|
||||||
|
|
||||||
Major MajorKey() { return DoubleToI; }
|
Major MajorKey() { return DoubleToI; }
|
||||||
int MinorKey() { return bit_field_; }
|
int MinorKey() { return bit_field_; }
|
||||||
|
@ -763,9 +763,6 @@ class HValue : public ZoneObject {
|
|||||||
void SetFlag(Flag f) { flags_ |= (1 << f); }
|
void SetFlag(Flag f) { flags_ |= (1 << f); }
|
||||||
void ClearFlag(Flag f) { flags_ &= ~(1 << f); }
|
void ClearFlag(Flag f) { flags_ &= ~(1 << f); }
|
||||||
bool CheckFlag(Flag f) const { return (flags_ & (1 << f)) != 0; }
|
bool CheckFlag(Flag f) const { return (flags_ & (1 << f)) != 0; }
|
||||||
void CopyFlag(Flag f, HValue* other) {
|
|
||||||
if (other->CheckFlag(f)) SetFlag(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the flag specified is set for all uses, false otherwise.
|
// Returns true if the flag specified is set for all uses, false otherwise.
|
||||||
bool CheckUsesForFlag(Flag f) const;
|
bool CheckUsesForFlag(Flag f) const;
|
||||||
|
@ -7501,7 +7501,7 @@ HInstruction* HOptimizedGraphBuilder::BuildIncrement(
|
|||||||
bool returns_original_input,
|
bool returns_original_input,
|
||||||
CountOperation* expr) {
|
CountOperation* expr) {
|
||||||
// The input to the count operation is on top of the expression stack.
|
// The input to the count operation is on top of the expression stack.
|
||||||
Handle<Type> info = expr->type();
|
TypeInfo info = expr->type();
|
||||||
Representation rep = Representation::FromType(info);
|
Representation rep = Representation::FromType(info);
|
||||||
if (rep.IsNone() || rep.IsTagged()) {
|
if (rep.IsNone() || rep.IsTagged()) {
|
||||||
rep = Representation::Smi();
|
rep = Representation::Smi();
|
||||||
@ -7865,8 +7865,7 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(
|
|||||||
Handle<Type> left_type,
|
Handle<Type> left_type,
|
||||||
Handle<Type> right_type,
|
Handle<Type> right_type,
|
||||||
Handle<Type> result_type,
|
Handle<Type> result_type,
|
||||||
Maybe<int> fixed_right_arg,
|
Maybe<int> fixed_right_arg) {
|
||||||
bool binop_stub) {
|
|
||||||
|
|
||||||
Representation left_rep = Representation::FromType(left_type);
|
Representation left_rep = Representation::FromType(left_type);
|
||||||
Representation right_rep = Representation::FromType(right_type);
|
Representation right_rep = Representation::FromType(right_type);
|
||||||
@ -7895,29 +7894,13 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(
|
|||||||
right_rep = Representation::FromType(right_type);
|
right_rep = Representation::FromType(right_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (binop_stub) {
|
|
||||||
left = EnforceNumberType(left, left_type);
|
|
||||||
right = EnforceNumberType(right, right_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
Representation result_rep = Representation::FromType(result_type);
|
Representation result_rep = Representation::FromType(result_type);
|
||||||
|
|
||||||
bool is_non_primitive = (left_rep.IsTagged() && !left_rep.IsSmi()) ||
|
|
||||||
(right_rep.IsTagged() && !right_rep.IsSmi());
|
|
||||||
bool is_string_add = op == Token::ADD &&
|
bool is_string_add = op == Token::ADD &&
|
||||||
(left_type->Is(Type::String()) ||
|
(left_type->Is(Type::String()) ||
|
||||||
right_type->Is(Type::String()));
|
right_type->Is(Type::String()));
|
||||||
|
|
||||||
HInstruction* instr = NULL;
|
HInstruction* instr = NULL;
|
||||||
// Only the stub is allowed to call into the runtime, since otherwise we would
|
|
||||||
// inline several instructions (including the two pushes) for every tagged
|
|
||||||
// operation in optimized code, which is more expensive, than a stub call.
|
|
||||||
if (binop_stub && is_non_primitive && !is_string_add) {
|
|
||||||
HValue* function = AddLoadJSBuiltin(BinaryOpIC::TokenToJSBuiltin(op));
|
|
||||||
Add<HPushArgument>(left);
|
|
||||||
Add<HPushArgument>(right);
|
|
||||||
instr = NewUncasted<HInvokeFunction>(function, 2);
|
|
||||||
} else {
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case Token::ADD:
|
case Token::ADD:
|
||||||
if (is_string_add) {
|
if (is_string_add) {
|
||||||
@ -7981,26 +7964,12 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(
|
|||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (instr->IsBinaryOperation()) {
|
if (instr->IsBinaryOperation()) {
|
||||||
HBinaryOperation* binop = HBinaryOperation::cast(instr);
|
HBinaryOperation* binop = HBinaryOperation::cast(instr);
|
||||||
binop->set_observed_input_representation(1, left_rep);
|
binop->set_observed_input_representation(1, left_rep);
|
||||||
binop->set_observed_input_representation(2, right_rep);
|
binop->set_observed_input_representation(2, right_rep);
|
||||||
binop->initialize_output_representation(result_rep);
|
binop->initialize_output_representation(result_rep);
|
||||||
if (binop_stub) {
|
|
||||||
// Stub should not call into stub.
|
|
||||||
instr->SetFlag(HValue::kCannotBeTagged);
|
|
||||||
// And should truncate on HForceRepresentation already.
|
|
||||||
if (left->IsForceRepresentation()) {
|
|
||||||
left->CopyFlag(HValue::kTruncatingToSmi, instr);
|
|
||||||
left->CopyFlag(HValue::kTruncatingToInt32, instr);
|
|
||||||
}
|
|
||||||
if (right->IsForceRepresentation()) {
|
|
||||||
right->CopyFlag(HValue::kTruncatingToSmi, instr);
|
|
||||||
right->CopyFlag(HValue::kTruncatingToInt32, instr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return instr;
|
return instr;
|
||||||
}
|
}
|
||||||
|
@ -1291,8 +1291,7 @@ class HGraphBuilder {
|
|||||||
Handle<Type> left_type,
|
Handle<Type> left_type,
|
||||||
Handle<Type> right_type,
|
Handle<Type> right_type,
|
||||||
Handle<Type> result_type,
|
Handle<Type> result_type,
|
||||||
Maybe<int> fixed_right_arg,
|
Maybe<int> fixed_right_arg);
|
||||||
bool binop_stub = false);
|
|
||||||
|
|
||||||
HLoadNamedField* AddLoadFixedArrayLength(HValue *object);
|
HLoadNamedField* AddLoadFixedArrayLength(HValue *object);
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -212,7 +212,6 @@ void Deoptimizer::SetPlatformCompiledStubRegisters(
|
|||||||
|
|
||||||
|
|
||||||
void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
|
void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
|
||||||
if (!CpuFeatures::IsSupported(SSE2)) return;
|
|
||||||
for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
|
for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
|
||||||
double double_value = input_->GetDoubleRegister(i);
|
double double_value = input_->GetDoubleRegister(i);
|
||||||
output_frame->SetDoubleRegister(i, double_value);
|
output_frame->SetDoubleRegister(i, double_value);
|
||||||
|
@ -253,8 +253,8 @@ void MacroAssembler::X87TOSToI(Register result_reg,
|
|||||||
Label::Distance dst) {
|
Label::Distance dst) {
|
||||||
Label done;
|
Label done;
|
||||||
sub(esp, Immediate(kPointerSize));
|
sub(esp, Immediate(kPointerSize));
|
||||||
fld(0);
|
|
||||||
fist_s(MemOperand(esp, 0));
|
fist_s(MemOperand(esp, 0));
|
||||||
|
fld(0);
|
||||||
fild_s(MemOperand(esp, 0));
|
fild_s(MemOperand(esp, 0));
|
||||||
pop(result_reg);
|
pop(result_reg);
|
||||||
FCmp();
|
FCmp();
|
||||||
@ -453,7 +453,6 @@ static double kUint32Bias =
|
|||||||
void MacroAssembler::LoadUint32(XMMRegister dst,
|
void MacroAssembler::LoadUint32(XMMRegister dst,
|
||||||
Register src,
|
Register src,
|
||||||
XMMRegister scratch) {
|
XMMRegister scratch) {
|
||||||
ASSERT(!Serializer::enabled());
|
|
||||||
Label done;
|
Label done;
|
||||||
cmp(src, Immediate(0));
|
cmp(src, Immediate(0));
|
||||||
movdbl(scratch,
|
movdbl(scratch,
|
||||||
|
338
src/ic.cc
338
src/ic.cc
@ -2371,6 +2371,11 @@ RUNTIME_FUNCTION(MaybeObject*, ElementsTransitionAndStoreIC_Miss) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpIC::patch(Code* code) {
|
||||||
|
set_target(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const char* BinaryOpIC::GetName(TypeInfo type_info) {
|
const char* BinaryOpIC::GetName(TypeInfo type_info) {
|
||||||
switch (type_info) {
|
switch (type_info) {
|
||||||
case UNINITIALIZED: return "Uninitialized";
|
case UNINITIALIZED: return "Uninitialized";
|
||||||
@ -2385,64 +2390,256 @@ const char* BinaryOpIC::GetName(TypeInfo type_info) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MaybeObject* BinaryOpIC::Transition(Handle<Object> left, Handle<Object> right) {
|
BinaryOpIC::State BinaryOpIC::ToState(TypeInfo type_info) {
|
||||||
Code::ExtraICState extra_ic_state = target()->extended_extra_ic_state();
|
switch (type_info) {
|
||||||
BinaryOpStub stub(extra_ic_state);
|
case UNINITIALIZED:
|
||||||
|
return ::v8::internal::UNINITIALIZED;
|
||||||
bool smi_was_enabled = stub.GetLeftType(isolate())->Maybe(Type::Smi()) &&
|
case SMI:
|
||||||
stub.GetRightType(isolate())->Maybe(Type::Smi());
|
case INT32:
|
||||||
|
case NUMBER:
|
||||||
Maybe<Handle<Object> > result = stub.Result(left, right, isolate());
|
case ODDBALL:
|
||||||
|
case STRING:
|
||||||
#ifdef DEBUG
|
return MONOMORPHIC;
|
||||||
if (FLAG_trace_ic) {
|
case GENERIC:
|
||||||
char buffer[100];
|
return ::v8::internal::GENERIC;
|
||||||
NoAllocationStringAllocator allocator(buffer,
|
|
||||||
static_cast<unsigned>(sizeof(buffer)));
|
|
||||||
StringStream stream(&allocator);
|
|
||||||
stream.Add("[");
|
|
||||||
stub.PrintName(&stream);
|
|
||||||
|
|
||||||
stub.UpdateStatus(left, right, result);
|
|
||||||
|
|
||||||
stream.Add(" => ");
|
|
||||||
stub.PrintState(&stream);
|
|
||||||
stream.Add(" ");
|
|
||||||
stream.OutputToStdOut();
|
|
||||||
PrintF(" @ %p <- ", static_cast<void*>(*stub.GetCode(isolate())));
|
|
||||||
JavaScriptFrame::PrintTop(isolate(), stdout, false, true);
|
|
||||||
PrintF("]\n");
|
|
||||||
} else {
|
|
||||||
stub.UpdateStatus(left, right, result);
|
|
||||||
}
|
}
|
||||||
#else
|
UNREACHABLE();
|
||||||
stub.UpdateStatus(left, right, result);
|
return ::v8::internal::UNINITIALIZED;
|
||||||
#endif
|
|
||||||
|
|
||||||
Handle<Code> code = stub.GetCode(isolate());
|
|
||||||
set_target(*code);
|
|
||||||
|
|
||||||
bool enable_smi = stub.GetLeftType(isolate())->Maybe(Type::Smi()) &&
|
|
||||||
stub.GetRightType(isolate())->Maybe(Type::Smi());
|
|
||||||
|
|
||||||
if (!smi_was_enabled && enable_smi) {
|
|
||||||
PatchInlinedSmiCode(address(), ENABLE_INLINED_SMI_CHECK);
|
|
||||||
} else if (smi_was_enabled && !enable_smi) {
|
|
||||||
PatchInlinedSmiCode(address(), DISABLE_INLINED_SMI_CHECK);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.has_value
|
|
||||||
? static_cast<MaybeObject*>(*result.value)
|
|
||||||
: Failure::Exception();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RUNTIME_FUNCTION(MaybeObject*, BinaryOpIC_Miss) {
|
Handle<Type> BinaryOpIC::TypeInfoToType(BinaryOpIC::TypeInfo binary_type,
|
||||||
|
Isolate* isolate) {
|
||||||
|
switch (binary_type) {
|
||||||
|
case UNINITIALIZED:
|
||||||
|
return handle(Type::None(), isolate);
|
||||||
|
case SMI:
|
||||||
|
return handle(Type::Smi(), isolate);
|
||||||
|
case INT32:
|
||||||
|
return handle(Type::Signed32(), isolate);
|
||||||
|
case NUMBER:
|
||||||
|
return handle(Type::Number(), isolate);
|
||||||
|
case ODDBALL:
|
||||||
|
return handle(Type::Optional(
|
||||||
|
handle(Type::Union(
|
||||||
|
handle(Type::Number(), isolate),
|
||||||
|
handle(Type::String(), isolate)), isolate)), isolate);
|
||||||
|
case STRING:
|
||||||
|
return handle(Type::String(), isolate);
|
||||||
|
case GENERIC:
|
||||||
|
return handle(Type::Any(), isolate);
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return handle(Type::Any(), isolate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpIC::StubInfoToType(int minor_key,
|
||||||
|
Handle<Type>* left,
|
||||||
|
Handle<Type>* right,
|
||||||
|
Handle<Type>* result,
|
||||||
|
Isolate* isolate) {
|
||||||
|
TypeInfo left_typeinfo, right_typeinfo, result_typeinfo;
|
||||||
|
BinaryOpStub::decode_types_from_minor_key(
|
||||||
|
minor_key, &left_typeinfo, &right_typeinfo, &result_typeinfo);
|
||||||
|
*left = TypeInfoToType(left_typeinfo, isolate);
|
||||||
|
*right = TypeInfoToType(right_typeinfo, isolate);
|
||||||
|
*result = TypeInfoToType(result_typeinfo, isolate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static BinaryOpIC::TypeInfo TypeInfoFromValue(Handle<Object> value,
|
||||||
|
Token::Value op) {
|
||||||
|
v8::internal::TypeInfo type = v8::internal::TypeInfo::FromValue(value);
|
||||||
|
if (type.IsSmi()) return BinaryOpIC::SMI;
|
||||||
|
if (type.IsInteger32()) {
|
||||||
|
if (SmiValuesAre32Bits()) return BinaryOpIC::SMI;
|
||||||
|
return BinaryOpIC::INT32;
|
||||||
|
}
|
||||||
|
if (type.IsNumber()) return BinaryOpIC::NUMBER;
|
||||||
|
if (type.IsString()) return BinaryOpIC::STRING;
|
||||||
|
if (value->IsUndefined()) {
|
||||||
|
if (op == Token::BIT_AND ||
|
||||||
|
op == Token::BIT_OR ||
|
||||||
|
op == Token::BIT_XOR ||
|
||||||
|
op == Token::SAR ||
|
||||||
|
op == Token::SHL ||
|
||||||
|
op == Token::SHR) {
|
||||||
|
if (SmiValuesAre32Bits()) return BinaryOpIC::SMI;
|
||||||
|
return BinaryOpIC::INT32;
|
||||||
|
}
|
||||||
|
return BinaryOpIC::ODDBALL;
|
||||||
|
}
|
||||||
|
return BinaryOpIC::GENERIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static BinaryOpIC::TypeInfo InputState(BinaryOpIC::TypeInfo old_type,
|
||||||
|
Handle<Object> value,
|
||||||
|
Token::Value op) {
|
||||||
|
BinaryOpIC::TypeInfo new_type = TypeInfoFromValue(value, op);
|
||||||
|
if (old_type == BinaryOpIC::STRING) {
|
||||||
|
if (new_type == BinaryOpIC::STRING) return new_type;
|
||||||
|
return BinaryOpIC::GENERIC;
|
||||||
|
}
|
||||||
|
return Max(old_type, new_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
static void TraceBinaryOp(BinaryOpIC::TypeInfo left,
|
||||||
|
BinaryOpIC::TypeInfo right,
|
||||||
|
Maybe<int32_t> fixed_right_arg,
|
||||||
|
BinaryOpIC::TypeInfo result) {
|
||||||
|
PrintF("%s*%s", BinaryOpIC::GetName(left), BinaryOpIC::GetName(right));
|
||||||
|
if (fixed_right_arg.has_value) PrintF("{%d}", fixed_right_arg.value);
|
||||||
|
PrintF("->%s", BinaryOpIC::GetName(result));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
RUNTIME_FUNCTION(MaybeObject*, BinaryOp_Patch) {
|
||||||
|
ASSERT(args.length() == 3);
|
||||||
|
|
||||||
HandleScope scope(isolate);
|
HandleScope scope(isolate);
|
||||||
Handle<Object> left = args.at<Object>(0);
|
Handle<Object> left = args.at<Object>(0);
|
||||||
Handle<Object> right = args.at<Object>(1);
|
Handle<Object> right = args.at<Object>(1);
|
||||||
|
int key = args.smi_at(2);
|
||||||
|
Token::Value op = BinaryOpStub::decode_op_from_minor_key(key);
|
||||||
|
|
||||||
|
BinaryOpIC::TypeInfo previous_left, previous_right, previous_result;
|
||||||
|
BinaryOpStub::decode_types_from_minor_key(
|
||||||
|
key, &previous_left, &previous_right, &previous_result);
|
||||||
|
|
||||||
|
BinaryOpIC::TypeInfo new_left = InputState(previous_left, left, op);
|
||||||
|
BinaryOpIC::TypeInfo new_right = InputState(previous_right, right, op);
|
||||||
|
BinaryOpIC::TypeInfo result_type = BinaryOpIC::UNINITIALIZED;
|
||||||
|
|
||||||
|
// STRING is only used for ADD operations.
|
||||||
|
if ((new_left == BinaryOpIC::STRING || new_right == BinaryOpIC::STRING) &&
|
||||||
|
op != Token::ADD) {
|
||||||
|
new_left = new_right = BinaryOpIC::GENERIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryOpIC::TypeInfo new_overall = Max(new_left, new_right);
|
||||||
|
BinaryOpIC::TypeInfo previous_overall = Max(previous_left, previous_right);
|
||||||
|
|
||||||
|
Maybe<int> previous_fixed_right_arg =
|
||||||
|
BinaryOpStub::decode_fixed_right_arg_from_minor_key(key);
|
||||||
|
|
||||||
|
int32_t value;
|
||||||
|
bool new_has_fixed_right_arg =
|
||||||
|
op == Token::MOD &&
|
||||||
|
right->ToInt32(&value) &&
|
||||||
|
BinaryOpStub::can_encode_arg_value(value) &&
|
||||||
|
(previous_overall == BinaryOpIC::UNINITIALIZED ||
|
||||||
|
(previous_fixed_right_arg.has_value &&
|
||||||
|
previous_fixed_right_arg.value == value));
|
||||||
|
Maybe<int32_t> new_fixed_right_arg(
|
||||||
|
new_has_fixed_right_arg, new_has_fixed_right_arg ? value : 1);
|
||||||
|
|
||||||
|
if (previous_fixed_right_arg.has_value == new_fixed_right_arg.has_value) {
|
||||||
|
if (new_overall == BinaryOpIC::SMI && previous_overall == BinaryOpIC::SMI) {
|
||||||
|
if (op == Token::DIV ||
|
||||||
|
op == Token::MUL ||
|
||||||
|
op == Token::SHR ||
|
||||||
|
SmiValuesAre32Bits()) {
|
||||||
|
// Arithmetic on two Smi inputs has yielded a heap number.
|
||||||
|
// That is the only way to get here from the Smi stub.
|
||||||
|
// With 32-bit Smis, all overflows give heap numbers, but with
|
||||||
|
// 31-bit Smis, most operations overflow to int32 results.
|
||||||
|
result_type = BinaryOpIC::NUMBER;
|
||||||
|
} else {
|
||||||
|
// Other operations on SMIs that overflow yield int32s.
|
||||||
|
result_type = BinaryOpIC::INT32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (new_overall == BinaryOpIC::INT32 &&
|
||||||
|
previous_overall == BinaryOpIC::INT32) {
|
||||||
|
if (new_left == previous_left && new_right == previous_right) {
|
||||||
|
result_type = BinaryOpIC::NUMBER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryOpStub stub(key, new_left, new_right, result_type, new_fixed_right_arg);
|
||||||
|
Handle<Code> code = stub.GetCode(isolate);
|
||||||
|
if (!code.is_null()) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (FLAG_trace_ic) {
|
||||||
|
PrintF("[BinaryOpIC in ");
|
||||||
|
JavaScriptFrame::PrintTop(isolate, stdout, false, true);
|
||||||
|
PrintF(" ");
|
||||||
|
TraceBinaryOp(previous_left, previous_right, previous_fixed_right_arg,
|
||||||
|
previous_result);
|
||||||
|
PrintF(" => ");
|
||||||
|
TraceBinaryOp(new_left, new_right, new_fixed_right_arg, result_type);
|
||||||
|
PrintF(" #%s @ %p]\n", Token::Name(op), static_cast<void*>(*code));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
BinaryOpIC ic(isolate);
|
BinaryOpIC ic(isolate);
|
||||||
return ic.Transition(left, right);
|
ic.patch(*code);
|
||||||
|
|
||||||
|
// Activate inlined smi code.
|
||||||
|
if (previous_overall == BinaryOpIC::UNINITIALIZED) {
|
||||||
|
PatchInlinedSmiCode(ic.address(), ENABLE_INLINED_SMI_CHECK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<JSBuiltinsObject> builtins(isolate->js_builtins_object());
|
||||||
|
Object* builtin = NULL; // Initialization calms down the compiler.
|
||||||
|
switch (op) {
|
||||||
|
case Token::ADD:
|
||||||
|
builtin = builtins->javascript_builtin(Builtins::ADD);
|
||||||
|
break;
|
||||||
|
case Token::SUB:
|
||||||
|
builtin = builtins->javascript_builtin(Builtins::SUB);
|
||||||
|
break;
|
||||||
|
case Token::MUL:
|
||||||
|
builtin = builtins->javascript_builtin(Builtins::MUL);
|
||||||
|
break;
|
||||||
|
case Token::DIV:
|
||||||
|
builtin = builtins->javascript_builtin(Builtins::DIV);
|
||||||
|
break;
|
||||||
|
case Token::MOD:
|
||||||
|
builtin = builtins->javascript_builtin(Builtins::MOD);
|
||||||
|
break;
|
||||||
|
case Token::BIT_AND:
|
||||||
|
builtin = builtins->javascript_builtin(Builtins::BIT_AND);
|
||||||
|
break;
|
||||||
|
case Token::BIT_OR:
|
||||||
|
builtin = builtins->javascript_builtin(Builtins::BIT_OR);
|
||||||
|
break;
|
||||||
|
case Token::BIT_XOR:
|
||||||
|
builtin = builtins->javascript_builtin(Builtins::BIT_XOR);
|
||||||
|
break;
|
||||||
|
case Token::SHR:
|
||||||
|
builtin = builtins->javascript_builtin(Builtins::SHR);
|
||||||
|
break;
|
||||||
|
case Token::SAR:
|
||||||
|
builtin = builtins->javascript_builtin(Builtins::SAR);
|
||||||
|
break;
|
||||||
|
case Token::SHL:
|
||||||
|
builtin = builtins->javascript_builtin(Builtins::SHL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<JSFunction> builtin_function(JSFunction::cast(builtin), isolate);
|
||||||
|
|
||||||
|
bool caught_exception;
|
||||||
|
Handle<Object> builtin_args[] = { right };
|
||||||
|
Handle<Object> result = Execution::Call(isolate,
|
||||||
|
builtin_function,
|
||||||
|
left,
|
||||||
|
ARRAY_SIZE(builtin_args),
|
||||||
|
builtin_args,
|
||||||
|
&caught_exception);
|
||||||
|
if (caught_exception) {
|
||||||
|
return Failure::Exception();
|
||||||
|
}
|
||||||
|
return *result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2743,47 +2940,6 @@ RUNTIME_FUNCTION(MaybeObject*, Unreachable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Builtins::JavaScript BinaryOpIC::TokenToJSBuiltin(Token::Value op) {
|
|
||||||
switch (op) {
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
case Token::ADD:
|
|
||||||
return Builtins::ADD;
|
|
||||||
break;
|
|
||||||
case Token::SUB:
|
|
||||||
return Builtins::SUB;
|
|
||||||
break;
|
|
||||||
case Token::MUL:
|
|
||||||
return Builtins::MUL;
|
|
||||||
break;
|
|
||||||
case Token::DIV:
|
|
||||||
return Builtins::DIV;
|
|
||||||
break;
|
|
||||||
case Token::MOD:
|
|
||||||
return Builtins::MOD;
|
|
||||||
break;
|
|
||||||
case Token::BIT_OR:
|
|
||||||
return Builtins::BIT_OR;
|
|
||||||
break;
|
|
||||||
case Token::BIT_AND:
|
|
||||||
return Builtins::BIT_AND;
|
|
||||||
break;
|
|
||||||
case Token::BIT_XOR:
|
|
||||||
return Builtins::BIT_XOR;
|
|
||||||
break;
|
|
||||||
case Token::SAR:
|
|
||||||
return Builtins::SAR;
|
|
||||||
break;
|
|
||||||
case Token::SHR:
|
|
||||||
return Builtins::SHR;
|
|
||||||
break;
|
|
||||||
case Token::SHL:
|
|
||||||
return Builtins::SHL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MaybeObject* ToBooleanIC::ToBoolean(Handle<Object> object,
|
MaybeObject* ToBooleanIC::ToBoolean(Handle<Object> object,
|
||||||
Code::ExtraICState extra_ic_state) {
|
Code::ExtraICState extra_ic_state) {
|
||||||
ToBooleanStub stub(extra_ic_state);
|
ToBooleanStub stub(extra_ic_state);
|
||||||
|
19
src/ic.h
19
src/ic.h
@ -57,8 +57,8 @@ namespace internal {
|
|||||||
ICU(LoadPropertyWithInterceptorForCall) \
|
ICU(LoadPropertyWithInterceptorForCall) \
|
||||||
ICU(KeyedLoadPropertyWithInterceptor) \
|
ICU(KeyedLoadPropertyWithInterceptor) \
|
||||||
ICU(StoreInterceptorProperty) \
|
ICU(StoreInterceptorProperty) \
|
||||||
|
ICU(BinaryOp_Patch) \
|
||||||
ICU(CompareIC_Miss) \
|
ICU(CompareIC_Miss) \
|
||||||
ICU(BinaryOpIC_Miss) \
|
|
||||||
ICU(CompareNilIC_Miss) \
|
ICU(CompareNilIC_Miss) \
|
||||||
ICU(Unreachable) \
|
ICU(Unreachable) \
|
||||||
ICU(ToBooleanIC_Miss)
|
ICU(ToBooleanIC_Miss)
|
||||||
@ -735,14 +735,22 @@ class BinaryOpIC: public IC {
|
|||||||
GENERIC
|
GENERIC
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit BinaryOpIC(Isolate* isolate) : IC(EXTRA_CALL_FRAME, isolate) { }
|
static void StubInfoToType(int minor_key,
|
||||||
|
Handle<Type>* left,
|
||||||
|
Handle<Type>* right,
|
||||||
|
Handle<Type>* result,
|
||||||
|
Isolate* isolate);
|
||||||
|
|
||||||
static Builtins::JavaScript TokenToJSBuiltin(Token::Value op);
|
explicit BinaryOpIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) { }
|
||||||
|
|
||||||
|
void patch(Code* code);
|
||||||
|
|
||||||
static const char* GetName(TypeInfo type_info);
|
static const char* GetName(TypeInfo type_info);
|
||||||
|
|
||||||
MUST_USE_RESULT MaybeObject* Transition(Handle<Object> left,
|
static State ToState(TypeInfo type_info);
|
||||||
Handle<Object> right);
|
|
||||||
|
private:
|
||||||
|
static Handle<Type> TypeInfoToType(TypeInfo binary_type, Isolate* isolate);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -849,7 +857,6 @@ DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_MissFromStubFailure);
|
|||||||
DECLARE_RUNTIME_FUNCTION(MaybeObject*, UnaryOpIC_Miss);
|
DECLARE_RUNTIME_FUNCTION(MaybeObject*, UnaryOpIC_Miss);
|
||||||
DECLARE_RUNTIME_FUNCTION(MaybeObject*, StoreIC_MissFromStubFailure);
|
DECLARE_RUNTIME_FUNCTION(MaybeObject*, StoreIC_MissFromStubFailure);
|
||||||
DECLARE_RUNTIME_FUNCTION(MaybeObject*, ElementsTransitionAndStoreIC_Miss);
|
DECLARE_RUNTIME_FUNCTION(MaybeObject*, ElementsTransitionAndStoreIC_Miss);
|
||||||
DECLARE_RUNTIME_FUNCTION(MaybeObject*, BinaryOpIC_Miss);
|
|
||||||
DECLARE_RUNTIME_FUNCTION(MaybeObject*, CompareNilIC_Miss);
|
DECLARE_RUNTIME_FUNCTION(MaybeObject*, CompareNilIC_Miss);
|
||||||
DECLARE_RUNTIME_FUNCTION(MaybeObject*, ToBooleanIC_Miss);
|
DECLARE_RUNTIME_FUNCTION(MaybeObject*, ToBooleanIC_Miss);
|
||||||
|
|
||||||
|
@ -2338,7 +2338,6 @@ bool Isolate::Init(Deserializer* des) {
|
|||||||
DONT_TRACK_ALLOCATION_SITE, 0);
|
DONT_TRACK_ALLOCATION_SITE, 0);
|
||||||
stub.InitializeInterfaceDescriptor(
|
stub.InitializeInterfaceDescriptor(
|
||||||
this, code_stub_interface_descriptor(CodeStub::FastCloneShallowArray));
|
this, code_stub_interface_descriptor(CodeStub::FastCloneShallowArray));
|
||||||
BinaryOpStub::InitializeForIsolate(this);
|
|
||||||
CompareNilICStub::InitializeForIsolate(this);
|
CompareNilICStub::InitializeForIsolate(this);
|
||||||
ToBooleanStub::InitializeForIsolate(this);
|
ToBooleanStub::InitializeForIsolate(this);
|
||||||
ArrayConstructorStubBase::InstallDescriptors(this);
|
ArrayConstructorStubBase::InstallDescriptors(this);
|
||||||
|
@ -1610,12 +1610,7 @@ void Logger::LogCodeObject(Object* object) {
|
|||||||
case Code::FUNCTION:
|
case Code::FUNCTION:
|
||||||
case Code::OPTIMIZED_FUNCTION:
|
case Code::OPTIMIZED_FUNCTION:
|
||||||
return; // We log this later using LogCompiledFunctions.
|
return; // We log this later using LogCompiledFunctions.
|
||||||
case Code::BINARY_OP_IC: {
|
case Code::BINARY_OP_IC: // fall through
|
||||||
BinaryOpStub stub(code_object->extended_extra_ic_state());
|
|
||||||
description = stub.GetName().Detach();
|
|
||||||
tag = Logger::STUB_TAG;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Code::COMPARE_IC: // fall through
|
case Code::COMPARE_IC: // fall through
|
||||||
case Code::COMPARE_NIL_IC: // fall through
|
case Code::COMPARE_NIL_IC: // fall through
|
||||||
case Code::TO_BOOLEAN_IC: // fall through
|
case Code::TO_BOOLEAN_IC: // fall through
|
||||||
|
@ -4883,8 +4883,7 @@ class Code: public HeapObject {
|
|||||||
// TODO(danno): This is a bit of a hack right now since there are still
|
// TODO(danno): This is a bit of a hack right now since there are still
|
||||||
// clients of this API that pass "extra" values in for argc. These clients
|
// clients of this API that pass "extra" values in for argc. These clients
|
||||||
// should be retrofitted to used ExtendedExtraICState.
|
// should be retrofitted to used ExtendedExtraICState.
|
||||||
return kind == COMPARE_NIL_IC || kind == TO_BOOLEAN_IC ||
|
return kind == COMPARE_NIL_IC || kind == TO_BOOLEAN_IC;
|
||||||
kind == BINARY_OP_IC;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline StubType type(); // Only valid for monomorphic IC stubs.
|
inline StubType type(); // Only valid for monomorphic IC stubs.
|
||||||
|
@ -1056,7 +1056,7 @@ intptr_t PagedSpace::SizeOfFirstPage() {
|
|||||||
int size = 0;
|
int size = 0;
|
||||||
switch (identity()) {
|
switch (identity()) {
|
||||||
case OLD_POINTER_SPACE:
|
case OLD_POINTER_SPACE:
|
||||||
size = 72 * kPointerSize * KB;
|
size = 64 * kPointerSize * KB;
|
||||||
break;
|
break;
|
||||||
case OLD_DATA_SPACE:
|
case OLD_DATA_SPACE:
|
||||||
size = 192 * KB;
|
size = 192 * KB;
|
||||||
|
@ -381,29 +381,20 @@ void TypeFeedbackOracle::BinaryType(TypeFeedbackId id,
|
|||||||
Handle<Type>* left,
|
Handle<Type>* left,
|
||||||
Handle<Type>* right,
|
Handle<Type>* right,
|
||||||
Handle<Type>* result,
|
Handle<Type>* result,
|
||||||
Maybe<int>* fixed_right_arg,
|
Maybe<int>* fixed_right_arg) {
|
||||||
Token::Value operation) {
|
|
||||||
Handle<Object> object = GetInfo(id);
|
Handle<Object> object = GetInfo(id);
|
||||||
if (!object->IsCode()) {
|
if (!object->IsCode()) {
|
||||||
// For some binary ops we don't have ICs, e.g. Token::COMMA, but for the
|
// For some binary ops we don't have ICs, e.g. Token::COMMA.
|
||||||
// operations covered by the BinaryOpStub we should always have them.
|
|
||||||
ASSERT(!(operation >= BinaryOpStub::FIRST_TOKEN &&
|
|
||||||
operation <= BinaryOpStub::LAST_TOKEN));
|
|
||||||
*left = *right = *result = handle(Type::None(), isolate_);
|
*left = *right = *result = handle(Type::None(), isolate_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Handle<Code> code = Handle<Code>::cast(object);
|
Handle<Code> code = Handle<Code>::cast(object);
|
||||||
ASSERT(code->is_binary_op_stub());
|
ASSERT(code->is_binary_op_stub());
|
||||||
|
|
||||||
BinaryOpStub stub(code->extended_extra_ic_state());
|
int minor_key = code->stub_info();
|
||||||
|
BinaryOpIC::StubInfoToType(minor_key, left, right, result, isolate());
|
||||||
// Sanity check.
|
*fixed_right_arg =
|
||||||
ASSERT(stub.operation() == operation);
|
BinaryOpStub::decode_fixed_right_arg_from_minor_key(minor_key);
|
||||||
|
|
||||||
*left = stub.GetLeftType(isolate());
|
|
||||||
*right = stub.GetRightType(isolate());
|
|
||||||
*result = stub.GetResultType(isolate());
|
|
||||||
*fixed_right_arg = stub.fixed_right_arg();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -419,15 +410,36 @@ Handle<Type> TypeFeedbackOracle::ClauseType(TypeFeedbackId id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Handle<Type> TypeFeedbackOracle::IncrementType(CountOperation* expr) {
|
TypeInfo TypeFeedbackOracle::IncrementType(CountOperation* expr) {
|
||||||
Handle<Object> object = GetInfo(expr->CountBinOpFeedbackId());
|
Handle<Object> object = GetInfo(expr->CountBinOpFeedbackId());
|
||||||
Handle<Type> unknown(Type::None(), isolate_);
|
TypeInfo unknown = TypeInfo::Unknown();
|
||||||
if (!object->IsCode()) return unknown;
|
if (!object->IsCode()) return unknown;
|
||||||
Handle<Code> code = Handle<Code>::cast(object);
|
Handle<Code> code = Handle<Code>::cast(object);
|
||||||
if (!code->is_binary_op_stub()) return unknown;
|
if (!code->is_binary_op_stub()) return unknown;
|
||||||
|
|
||||||
BinaryOpStub stub(code->extended_extra_ic_state());
|
BinaryOpIC::TypeInfo left_type, right_type, unused_result_type;
|
||||||
return stub.GetLeftType(isolate());
|
BinaryOpStub::decode_types_from_minor_key(code->stub_info(), &left_type,
|
||||||
|
&right_type, &unused_result_type);
|
||||||
|
// CountOperations should always have +1 or -1 as their right input.
|
||||||
|
ASSERT(right_type == BinaryOpIC::SMI ||
|
||||||
|
right_type == BinaryOpIC::UNINITIALIZED);
|
||||||
|
|
||||||
|
switch (left_type) {
|
||||||
|
case BinaryOpIC::UNINITIALIZED:
|
||||||
|
case BinaryOpIC::SMI:
|
||||||
|
return TypeInfo::Smi();
|
||||||
|
case BinaryOpIC::INT32:
|
||||||
|
return TypeInfo::Integer32();
|
||||||
|
case BinaryOpIC::NUMBER:
|
||||||
|
return TypeInfo::Double();
|
||||||
|
case BinaryOpIC::STRING:
|
||||||
|
case BinaryOpIC::GENERIC:
|
||||||
|
return unknown;
|
||||||
|
default:
|
||||||
|
return unknown;
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -301,8 +301,7 @@ class TypeFeedbackOracle: public ZoneObject {
|
|||||||
Handle<Type>* left,
|
Handle<Type>* left,
|
||||||
Handle<Type>* right,
|
Handle<Type>* right,
|
||||||
Handle<Type>* result,
|
Handle<Type>* result,
|
||||||
Maybe<int>* fixed_right_arg,
|
Maybe<int>* fixed_right_arg);
|
||||||
Token::Value operation);
|
|
||||||
|
|
||||||
void CompareType(TypeFeedbackId id,
|
void CompareType(TypeFeedbackId id,
|
||||||
Handle<Type>* left,
|
Handle<Type>* left,
|
||||||
@ -311,7 +310,7 @@ class TypeFeedbackOracle: public ZoneObject {
|
|||||||
|
|
||||||
Handle<Type> ClauseType(TypeFeedbackId id);
|
Handle<Type> ClauseType(TypeFeedbackId id);
|
||||||
|
|
||||||
Handle<Type> IncrementType(CountOperation* expr);
|
TypeInfo IncrementType(CountOperation* expr);
|
||||||
|
|
||||||
Zone* zone() const { return zone_; }
|
Zone* zone() const { return zone_; }
|
||||||
Isolate* isolate() const { return isolate_; }
|
Isolate* isolate() const { return isolate_; }
|
||||||
|
@ -543,7 +543,7 @@ void AstTyper::VisitBinaryOperation(BinaryOperation* expr) {
|
|||||||
Handle<Type> type, left_type, right_type;
|
Handle<Type> type, left_type, right_type;
|
||||||
Maybe<int> fixed_right_arg;
|
Maybe<int> fixed_right_arg;
|
||||||
oracle()->BinaryType(expr->BinaryOperationFeedbackId(),
|
oracle()->BinaryType(expr->BinaryOperationFeedbackId(),
|
||||||
&left_type, &right_type, &type, &fixed_right_arg, expr->op());
|
&left_type, &right_type, &type, &fixed_right_arg);
|
||||||
NarrowLowerType(expr, type);
|
NarrowLowerType(expr, type);
|
||||||
NarrowLowerType(expr->left(), left_type);
|
NarrowLowerType(expr->left(), left_type);
|
||||||
NarrowLowerType(expr->right(), right_type);
|
NarrowLowerType(expr->right(), right_type);
|
||||||
|
@ -204,6 +204,7 @@ namespace internal {
|
|||||||
SC(enum_cache_hits, V8.EnumCacheHits) \
|
SC(enum_cache_hits, V8.EnumCacheHits) \
|
||||||
SC(enum_cache_misses, V8.EnumCacheMisses) \
|
SC(enum_cache_misses, V8.EnumCacheMisses) \
|
||||||
SC(zone_segment_bytes, V8.ZoneSegmentBytes) \
|
SC(zone_segment_bytes, V8.ZoneSegmentBytes) \
|
||||||
|
SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \
|
||||||
SC(fast_new_closure_total, V8.FastNewClosureTotal) \
|
SC(fast_new_closure_total, V8.FastNewClosureTotal) \
|
||||||
SC(fast_new_closure_try_optimized, V8.FastNewClosureTryOptimized) \
|
SC(fast_new_closure_try_optimized, V8.FastNewClosureTryOptimized) \
|
||||||
SC(fast_new_closure_install_optimized, V8.FastNewClosureInstallOptimized) \
|
SC(fast_new_closure_install_optimized, V8.FastNewClosureInstallOptimized) \
|
||||||
|
@ -155,18 +155,6 @@ void TransitionElementsKindStub::InitializeInterfaceDescriptor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BinaryOpStub::InitializeInterfaceDescriptor(
|
|
||||||
Isolate* isolate,
|
|
||||||
CodeStubInterfaceDescriptor* descriptor) {
|
|
||||||
static Register registers[] = { rdx, rax };
|
|
||||||
descriptor->register_param_count_ = 2;
|
|
||||||
descriptor->register_params_ = registers;
|
|
||||||
descriptor->deoptimization_handler_ = FUNCTION_ADDR(BinaryOpIC_Miss);
|
|
||||||
descriptor->SetMissHandler(
|
|
||||||
ExternalReference(IC_Utility(IC::kBinaryOpIC_Miss), isolate));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void InitializeArrayConstructorDescriptor(
|
static void InitializeArrayConstructorDescriptor(
|
||||||
Isolate* isolate,
|
Isolate* isolate,
|
||||||
CodeStubInterfaceDescriptor* descriptor,
|
CodeStubInterfaceDescriptor* descriptor,
|
||||||
@ -459,8 +447,35 @@ class FloatingPointHelper : public AllStatic {
|
|||||||
// If the operands are not both numbers, jump to not_numbers.
|
// If the operands are not both numbers, jump to not_numbers.
|
||||||
// Leaves rdx and rax unchanged. SmiOperands assumes both are smis.
|
// Leaves rdx and rax unchanged. SmiOperands assumes both are smis.
|
||||||
// NumberOperands assumes both are smis or heap numbers.
|
// NumberOperands assumes both are smis or heap numbers.
|
||||||
|
static void LoadSSE2SmiOperands(MacroAssembler* masm);
|
||||||
static void LoadSSE2UnknownOperands(MacroAssembler* masm,
|
static void LoadSSE2UnknownOperands(MacroAssembler* masm,
|
||||||
Label* not_numbers);
|
Label* not_numbers);
|
||||||
|
|
||||||
|
// Takes the operands in rdx and rax and loads them as integers in rax
|
||||||
|
// and rcx.
|
||||||
|
static void LoadAsIntegers(MacroAssembler* masm,
|
||||||
|
Label* operand_conversion_failure,
|
||||||
|
Register heap_number_map);
|
||||||
|
|
||||||
|
// Tries to convert two values to smis losslessly.
|
||||||
|
// This fails if either argument is not a Smi nor a HeapNumber,
|
||||||
|
// or if it's a HeapNumber with a value that can't be converted
|
||||||
|
// losslessly to a Smi. In that case, control transitions to the
|
||||||
|
// on_not_smis label.
|
||||||
|
// On success, either control goes to the on_success label (if one is
|
||||||
|
// provided), or it falls through at the end of the code (if on_success
|
||||||
|
// is NULL).
|
||||||
|
// On success, both first and second holds Smi tagged values.
|
||||||
|
// One of first or second must be non-Smi when entering.
|
||||||
|
static void NumbersToSmis(MacroAssembler* masm,
|
||||||
|
Register first,
|
||||||
|
Register second,
|
||||||
|
Register scratch1,
|
||||||
|
Register scratch2,
|
||||||
|
Register scratch3,
|
||||||
|
Label* on_success,
|
||||||
|
Label* on_not_smis,
|
||||||
|
ConvertUndefined convert_undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -548,6 +563,569 @@ void DoubleToIStub::Generate(MacroAssembler* masm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpStub::Initialize() {}
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
|
||||||
|
__ PopReturnAddressTo(rcx);
|
||||||
|
__ push(rdx);
|
||||||
|
__ push(rax);
|
||||||
|
// Left and right arguments are now on top.
|
||||||
|
__ Push(Smi::FromInt(MinorKey()));
|
||||||
|
|
||||||
|
__ PushReturnAddressFrom(rcx);
|
||||||
|
|
||||||
|
// Patch the caller to an appropriate specialized stub and return the
|
||||||
|
// operation result to the caller of the stub.
|
||||||
|
__ TailCallExternalReference(
|
||||||
|
ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
|
||||||
|
masm->isolate()),
|
||||||
|
3,
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void BinaryOpStub_GenerateSmiCode(
|
||||||
|
MacroAssembler* masm,
|
||||||
|
Label* slow,
|
||||||
|
BinaryOpStub::SmiCodeGenerateHeapNumberResults allow_heapnumber_results,
|
||||||
|
Token::Value op) {
|
||||||
|
|
||||||
|
// Arguments to BinaryOpStub are in rdx and rax.
|
||||||
|
const Register left = rdx;
|
||||||
|
const Register right = rax;
|
||||||
|
|
||||||
|
// We only generate heapnumber answers for overflowing calculations
|
||||||
|
// for the four basic arithmetic operations and logical right shift by 0.
|
||||||
|
bool generate_inline_heapnumber_results =
|
||||||
|
(allow_heapnumber_results == BinaryOpStub::ALLOW_HEAPNUMBER_RESULTS) &&
|
||||||
|
(op == Token::ADD || op == Token::SUB ||
|
||||||
|
op == Token::MUL || op == Token::DIV || op == Token::SHR);
|
||||||
|
|
||||||
|
// Smi check of both operands. If op is BIT_OR, the check is delayed
|
||||||
|
// until after the OR operation.
|
||||||
|
Label not_smis;
|
||||||
|
Label use_fp_on_smis;
|
||||||
|
Label fail;
|
||||||
|
|
||||||
|
if (op != Token::BIT_OR) {
|
||||||
|
Comment smi_check_comment(masm, "-- Smi check arguments");
|
||||||
|
__ JumpIfNotBothSmi(left, right, ¬_smis);
|
||||||
|
}
|
||||||
|
|
||||||
|
Label smi_values;
|
||||||
|
__ bind(&smi_values);
|
||||||
|
// Perform the operation.
|
||||||
|
Comment perform_smi(masm, "-- Perform smi operation");
|
||||||
|
switch (op) {
|
||||||
|
case Token::ADD:
|
||||||
|
ASSERT(right.is(rax));
|
||||||
|
__ SmiAdd(right, right, left, &use_fp_on_smis); // ADD is commutative.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::SUB:
|
||||||
|
__ SmiSub(left, left, right, &use_fp_on_smis);
|
||||||
|
__ movq(rax, left);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::MUL:
|
||||||
|
ASSERT(right.is(rax));
|
||||||
|
__ SmiMul(right, right, left, &use_fp_on_smis); // MUL is commutative.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::DIV:
|
||||||
|
// SmiDiv will not accept left in rdx or right in rax.
|
||||||
|
__ movq(rbx, rax);
|
||||||
|
__ movq(rcx, rdx);
|
||||||
|
__ SmiDiv(rax, rcx, rbx, &use_fp_on_smis);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::MOD:
|
||||||
|
// SmiMod will not accept left in rdx or right in rax.
|
||||||
|
__ movq(rbx, rax);
|
||||||
|
__ movq(rcx, rdx);
|
||||||
|
__ SmiMod(rax, rcx, rbx, &use_fp_on_smis);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::BIT_OR: {
|
||||||
|
ASSERT(right.is(rax));
|
||||||
|
__ SmiOrIfSmis(right, right, left, ¬_smis); // BIT_OR is commutative.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Token::BIT_XOR:
|
||||||
|
ASSERT(right.is(rax));
|
||||||
|
__ SmiXor(right, right, left); // BIT_XOR is commutative.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::BIT_AND:
|
||||||
|
ASSERT(right.is(rax));
|
||||||
|
__ SmiAnd(right, right, left); // BIT_AND is commutative.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::SHL:
|
||||||
|
__ SmiShiftLeft(left, left, right);
|
||||||
|
__ movq(rax, left);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::SAR:
|
||||||
|
__ SmiShiftArithmeticRight(left, left, right);
|
||||||
|
__ movq(rax, left);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::SHR:
|
||||||
|
__ SmiShiftLogicalRight(left, left, right, &use_fp_on_smis);
|
||||||
|
__ movq(rax, left);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Emit return of result in rax. Some operations have registers pushed.
|
||||||
|
__ ret(0);
|
||||||
|
|
||||||
|
if (use_fp_on_smis.is_linked()) {
|
||||||
|
// 6. For some operations emit inline code to perform floating point
|
||||||
|
// operations on known smis (e.g., if the result of the operation
|
||||||
|
// overflowed the smi range).
|
||||||
|
__ bind(&use_fp_on_smis);
|
||||||
|
if (op == Token::DIV || op == Token::MOD) {
|
||||||
|
// Restore left and right to rdx and rax.
|
||||||
|
__ movq(rdx, rcx);
|
||||||
|
__ movq(rax, rbx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generate_inline_heapnumber_results) {
|
||||||
|
__ AllocateHeapNumber(rcx, rbx, slow);
|
||||||
|
Comment perform_float(masm, "-- Perform float operation on smis");
|
||||||
|
if (op == Token::SHR) {
|
||||||
|
__ SmiToInteger32(left, left);
|
||||||
|
__ cvtqsi2sd(xmm0, left);
|
||||||
|
} else {
|
||||||
|
FloatingPointHelper::LoadSSE2SmiOperands(masm);
|
||||||
|
switch (op) {
|
||||||
|
case Token::ADD: __ addsd(xmm0, xmm1); break;
|
||||||
|
case Token::SUB: __ subsd(xmm0, xmm1); break;
|
||||||
|
case Token::MUL: __ mulsd(xmm0, xmm1); break;
|
||||||
|
case Token::DIV: __ divsd(xmm0, xmm1); break;
|
||||||
|
default: UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0);
|
||||||
|
__ movq(rax, rcx);
|
||||||
|
__ ret(0);
|
||||||
|
} else {
|
||||||
|
__ jmp(&fail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Non-smi operands reach the end of the code generated by
|
||||||
|
// GenerateSmiCode, and fall through to subsequent code,
|
||||||
|
// with the operands in rdx and rax.
|
||||||
|
// But first we check if non-smi values are HeapNumbers holding
|
||||||
|
// values that could be smi.
|
||||||
|
__ bind(¬_smis);
|
||||||
|
Comment done_comment(masm, "-- Enter non-smi code");
|
||||||
|
FloatingPointHelper::ConvertUndefined convert_undefined =
|
||||||
|
FloatingPointHelper::BAILOUT_ON_UNDEFINED;
|
||||||
|
// This list must be in sync with BinaryOpPatch() behavior in ic.cc.
|
||||||
|
if (op == Token::BIT_AND ||
|
||||||
|
op == Token::BIT_OR ||
|
||||||
|
op == Token::BIT_XOR ||
|
||||||
|
op == Token::SAR ||
|
||||||
|
op == Token::SHL ||
|
||||||
|
op == Token::SHR) {
|
||||||
|
convert_undefined = FloatingPointHelper::CONVERT_UNDEFINED_TO_ZERO;
|
||||||
|
}
|
||||||
|
FloatingPointHelper::NumbersToSmis(masm, left, right, rbx, rdi, rcx,
|
||||||
|
&smi_values, &fail, convert_undefined);
|
||||||
|
__ jmp(&smi_values);
|
||||||
|
__ bind(&fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
|
||||||
|
Label* alloc_failure,
|
||||||
|
OverwriteMode mode);
|
||||||
|
|
||||||
|
|
||||||
|
static void BinaryOpStub_GenerateFloatingPointCode(MacroAssembler* masm,
|
||||||
|
Label* allocation_failure,
|
||||||
|
Label* non_numeric_failure,
|
||||||
|
Token::Value op,
|
||||||
|
OverwriteMode mode) {
|
||||||
|
switch (op) {
|
||||||
|
case Token::ADD:
|
||||||
|
case Token::SUB:
|
||||||
|
case Token::MUL:
|
||||||
|
case Token::DIV: {
|
||||||
|
FloatingPointHelper::LoadSSE2UnknownOperands(masm, non_numeric_failure);
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case Token::ADD: __ addsd(xmm0, xmm1); break;
|
||||||
|
case Token::SUB: __ subsd(xmm0, xmm1); break;
|
||||||
|
case Token::MUL: __ mulsd(xmm0, xmm1); break;
|
||||||
|
case Token::DIV: __ divsd(xmm0, xmm1); break;
|
||||||
|
default: UNREACHABLE();
|
||||||
|
}
|
||||||
|
BinaryOpStub_GenerateHeapResultAllocation(
|
||||||
|
masm, allocation_failure, mode);
|
||||||
|
__ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm0);
|
||||||
|
__ ret(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Token::MOD: {
|
||||||
|
// For MOD we jump to the allocation_failure label, to call runtime.
|
||||||
|
__ jmp(allocation_failure);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Token::BIT_OR:
|
||||||
|
case Token::BIT_AND:
|
||||||
|
case Token::BIT_XOR:
|
||||||
|
case Token::SAR:
|
||||||
|
case Token::SHL:
|
||||||
|
case Token::SHR: {
|
||||||
|
Label non_smi_shr_result;
|
||||||
|
Register heap_number_map = r9;
|
||||||
|
__ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
|
||||||
|
FloatingPointHelper::LoadAsIntegers(masm, non_numeric_failure,
|
||||||
|
heap_number_map);
|
||||||
|
switch (op) {
|
||||||
|
case Token::BIT_OR: __ orl(rax, rcx); break;
|
||||||
|
case Token::BIT_AND: __ andl(rax, rcx); break;
|
||||||
|
case Token::BIT_XOR: __ xorl(rax, rcx); break;
|
||||||
|
case Token::SAR: __ sarl_cl(rax); break;
|
||||||
|
case Token::SHL: __ shll_cl(rax); break;
|
||||||
|
case Token::SHR: {
|
||||||
|
__ shrl_cl(rax);
|
||||||
|
// Check if result is negative. This can only happen for a shift
|
||||||
|
// by zero.
|
||||||
|
__ testl(rax, rax);
|
||||||
|
__ j(negative, &non_smi_shr_result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: UNREACHABLE();
|
||||||
|
}
|
||||||
|
STATIC_ASSERT(kSmiValueSize == 32);
|
||||||
|
// Tag smi result and return.
|
||||||
|
__ Integer32ToSmi(rax, rax);
|
||||||
|
__ Ret();
|
||||||
|
|
||||||
|
// Logical shift right can produce an unsigned int32 that is not
|
||||||
|
// an int32, and so is not in the smi range. Allocate a heap number
|
||||||
|
// in that case.
|
||||||
|
if (op == Token::SHR) {
|
||||||
|
__ bind(&non_smi_shr_result);
|
||||||
|
Label allocation_failed;
|
||||||
|
__ movl(rbx, rax); // rbx holds result value (uint32 value as int64).
|
||||||
|
// Allocate heap number in new space.
|
||||||
|
// Not using AllocateHeapNumber macro in order to reuse
|
||||||
|
// already loaded heap_number_map.
|
||||||
|
__ Allocate(HeapNumber::kSize, rax, rdx, no_reg, &allocation_failed,
|
||||||
|
TAG_OBJECT);
|
||||||
|
// Set the map.
|
||||||
|
__ AssertRootValue(heap_number_map,
|
||||||
|
Heap::kHeapNumberMapRootIndex,
|
||||||
|
kHeapNumberMapRegisterClobbered);
|
||||||
|
__ movq(FieldOperand(rax, HeapObject::kMapOffset),
|
||||||
|
heap_number_map);
|
||||||
|
__ cvtqsi2sd(xmm0, rbx);
|
||||||
|
__ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm0);
|
||||||
|
__ Ret();
|
||||||
|
|
||||||
|
__ bind(&allocation_failed);
|
||||||
|
// We need tagged values in rdx and rax for the following code,
|
||||||
|
// not int32 in rax and rcx.
|
||||||
|
__ Integer32ToSmi(rax, rcx);
|
||||||
|
__ Integer32ToSmi(rdx, rbx);
|
||||||
|
__ jmp(allocation_failure);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: UNREACHABLE(); break;
|
||||||
|
}
|
||||||
|
// No fall-through from this generated code.
|
||||||
|
if (FLAG_debug_code) {
|
||||||
|
__ Abort(kUnexpectedFallThroughInBinaryStubGenerateFloatingPointCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void BinaryOpStub_GenerateRegisterArgsPushUnderReturn(
|
||||||
|
MacroAssembler* masm) {
|
||||||
|
// Push arguments, but ensure they are under the return address
|
||||||
|
// for a tail call.
|
||||||
|
__ PopReturnAddressTo(rcx);
|
||||||
|
__ push(rdx);
|
||||||
|
__ push(rax);
|
||||||
|
__ PushReturnAddressFrom(rcx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
|
||||||
|
ASSERT(op_ == Token::ADD);
|
||||||
|
Label left_not_string, call_runtime;
|
||||||
|
|
||||||
|
// Registers containing left and right operands respectively.
|
||||||
|
Register left = rdx;
|
||||||
|
Register right = rax;
|
||||||
|
|
||||||
|
// Test if left operand is a string.
|
||||||
|
__ JumpIfSmi(left, &left_not_string, Label::kNear);
|
||||||
|
__ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx);
|
||||||
|
__ j(above_equal, &left_not_string, Label::kNear);
|
||||||
|
StringAddStub string_add_left_stub(
|
||||||
|
(StringAddFlags)(STRING_ADD_CHECK_RIGHT | STRING_ADD_ERECT_FRAME));
|
||||||
|
BinaryOpStub_GenerateRegisterArgsPushUnderReturn(masm);
|
||||||
|
__ TailCallStub(&string_add_left_stub);
|
||||||
|
|
||||||
|
// Left operand is not a string, test right.
|
||||||
|
__ bind(&left_not_string);
|
||||||
|
__ JumpIfSmi(right, &call_runtime, Label::kNear);
|
||||||
|
__ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx);
|
||||||
|
__ j(above_equal, &call_runtime, Label::kNear);
|
||||||
|
|
||||||
|
StringAddStub string_add_right_stub(
|
||||||
|
(StringAddFlags)(STRING_ADD_CHECK_LEFT | STRING_ADD_ERECT_FRAME));
|
||||||
|
BinaryOpStub_GenerateRegisterArgsPushUnderReturn(masm);
|
||||||
|
__ TailCallStub(&string_add_right_stub);
|
||||||
|
|
||||||
|
// Neither argument is a string.
|
||||||
|
__ bind(&call_runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
|
||||||
|
Label right_arg_changed, call_runtime;
|
||||||
|
|
||||||
|
if (op_ == Token::MOD && encoded_right_arg_.has_value) {
|
||||||
|
// It is guaranteed that the value will fit into a Smi, because if it
|
||||||
|
// didn't, we wouldn't be here, see BinaryOp_Patch.
|
||||||
|
__ Cmp(rax, Smi::FromInt(fixed_right_arg_value()));
|
||||||
|
__ j(not_equal, &right_arg_changed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result_type_ == BinaryOpIC::UNINITIALIZED ||
|
||||||
|
result_type_ == BinaryOpIC::SMI) {
|
||||||
|
// Only allow smi results.
|
||||||
|
BinaryOpStub_GenerateSmiCode(masm, NULL, NO_HEAPNUMBER_RESULTS, op_);
|
||||||
|
} else {
|
||||||
|
// Allow heap number result and don't make a transition if a heap number
|
||||||
|
// cannot be allocated.
|
||||||
|
BinaryOpStub_GenerateSmiCode(
|
||||||
|
masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code falls through if the result is not returned as either a smi or heap
|
||||||
|
// number.
|
||||||
|
__ bind(&right_arg_changed);
|
||||||
|
GenerateTypeTransition(masm);
|
||||||
|
|
||||||
|
if (call_runtime.is_linked()) {
|
||||||
|
__ bind(&call_runtime);
|
||||||
|
{
|
||||||
|
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||||
|
GenerateRegisterArgsPush(masm);
|
||||||
|
GenerateCallRuntime(masm);
|
||||||
|
}
|
||||||
|
__ Ret();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
|
||||||
|
// The int32 case is identical to the Smi case. We avoid creating this
|
||||||
|
// ic state on x64.
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
|
||||||
|
Label call_runtime;
|
||||||
|
ASSERT(left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING);
|
||||||
|
ASSERT(op_ == Token::ADD);
|
||||||
|
// If both arguments are strings, call the string add stub.
|
||||||
|
// Otherwise, do a transition.
|
||||||
|
|
||||||
|
// Registers containing left and right operands respectively.
|
||||||
|
Register left = rdx;
|
||||||
|
Register right = rax;
|
||||||
|
|
||||||
|
// Test if left operand is a string.
|
||||||
|
__ JumpIfSmi(left, &call_runtime);
|
||||||
|
__ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx);
|
||||||
|
__ j(above_equal, &call_runtime);
|
||||||
|
|
||||||
|
// Test if right operand is a string.
|
||||||
|
__ JumpIfSmi(right, &call_runtime);
|
||||||
|
__ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx);
|
||||||
|
__ j(above_equal, &call_runtime);
|
||||||
|
|
||||||
|
StringAddStub string_add_stub(
|
||||||
|
(StringAddFlags)(STRING_ADD_CHECK_NONE | STRING_ADD_ERECT_FRAME));
|
||||||
|
BinaryOpStub_GenerateRegisterArgsPushUnderReturn(masm);
|
||||||
|
__ TailCallStub(&string_add_stub);
|
||||||
|
|
||||||
|
__ bind(&call_runtime);
|
||||||
|
GenerateTypeTransition(masm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
|
||||||
|
Label call_runtime;
|
||||||
|
|
||||||
|
if (op_ == Token::ADD) {
|
||||||
|
// Handle string addition here, because it is the only operation
|
||||||
|
// that does not do a ToNumber conversion on the operands.
|
||||||
|
GenerateAddStrings(masm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert oddball arguments to numbers.
|
||||||
|
Label check, done;
|
||||||
|
__ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
|
||||||
|
__ j(not_equal, &check, Label::kNear);
|
||||||
|
if (Token::IsBitOp(op_)) {
|
||||||
|
__ xor_(rdx, rdx);
|
||||||
|
} else {
|
||||||
|
__ LoadRoot(rdx, Heap::kNanValueRootIndex);
|
||||||
|
}
|
||||||
|
__ jmp(&done, Label::kNear);
|
||||||
|
__ bind(&check);
|
||||||
|
__ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
|
||||||
|
__ j(not_equal, &done, Label::kNear);
|
||||||
|
if (Token::IsBitOp(op_)) {
|
||||||
|
__ xor_(rax, rax);
|
||||||
|
} else {
|
||||||
|
__ LoadRoot(rax, Heap::kNanValueRootIndex);
|
||||||
|
}
|
||||||
|
__ bind(&done);
|
||||||
|
|
||||||
|
GenerateNumberStub(masm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void BinaryOpStub_CheckSmiInput(MacroAssembler* masm,
|
||||||
|
Register input,
|
||||||
|
Label* fail) {
|
||||||
|
Label ok;
|
||||||
|
__ JumpIfSmi(input, &ok, Label::kNear);
|
||||||
|
Register heap_number_map = r8;
|
||||||
|
Register scratch1 = r9;
|
||||||
|
Register scratch2 = r10;
|
||||||
|
// HeapNumbers containing 32bit integer values are also allowed.
|
||||||
|
__ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
|
||||||
|
__ cmpq(FieldOperand(input, HeapObject::kMapOffset), heap_number_map);
|
||||||
|
__ j(not_equal, fail);
|
||||||
|
__ movsd(xmm0, FieldOperand(input, HeapNumber::kValueOffset));
|
||||||
|
// Convert, convert back, and compare the two doubles' bits.
|
||||||
|
__ cvttsd2siq(scratch2, xmm0);
|
||||||
|
__ Cvtlsi2sd(xmm1, scratch2);
|
||||||
|
__ movq(scratch1, xmm0);
|
||||||
|
__ movq(scratch2, xmm1);
|
||||||
|
__ cmpq(scratch1, scratch2);
|
||||||
|
__ j(not_equal, fail);
|
||||||
|
__ bind(&ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpStub::GenerateNumberStub(MacroAssembler* masm) {
|
||||||
|
Label gc_required, not_number;
|
||||||
|
|
||||||
|
// It could be that only SMIs have been seen at either the left
|
||||||
|
// or the right operand. For precise type feedback, patch the IC
|
||||||
|
// again if this changes.
|
||||||
|
if (left_type_ == BinaryOpIC::SMI) {
|
||||||
|
BinaryOpStub_CheckSmiInput(masm, rdx, ¬_number);
|
||||||
|
}
|
||||||
|
if (right_type_ == BinaryOpIC::SMI) {
|
||||||
|
BinaryOpStub_CheckSmiInput(masm, rax, ¬_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryOpStub_GenerateFloatingPointCode(
|
||||||
|
masm, &gc_required, ¬_number, op_, mode_);
|
||||||
|
|
||||||
|
__ bind(¬_number);
|
||||||
|
GenerateTypeTransition(masm);
|
||||||
|
|
||||||
|
__ bind(&gc_required);
|
||||||
|
{
|
||||||
|
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||||
|
GenerateRegisterArgsPush(masm);
|
||||||
|
GenerateCallRuntime(masm);
|
||||||
|
}
|
||||||
|
__ Ret();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
|
||||||
|
Label call_runtime, call_string_add_or_runtime;
|
||||||
|
|
||||||
|
BinaryOpStub_GenerateSmiCode(
|
||||||
|
masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_);
|
||||||
|
|
||||||
|
BinaryOpStub_GenerateFloatingPointCode(
|
||||||
|
masm, &call_runtime, &call_string_add_or_runtime, op_, mode_);
|
||||||
|
|
||||||
|
__ bind(&call_string_add_or_runtime);
|
||||||
|
if (op_ == Token::ADD) {
|
||||||
|
GenerateAddStrings(masm);
|
||||||
|
}
|
||||||
|
|
||||||
|
__ bind(&call_runtime);
|
||||||
|
{
|
||||||
|
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||||
|
GenerateRegisterArgsPush(masm);
|
||||||
|
GenerateCallRuntime(masm);
|
||||||
|
}
|
||||||
|
__ Ret();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
|
||||||
|
Label* alloc_failure,
|
||||||
|
OverwriteMode mode) {
|
||||||
|
Label skip_allocation;
|
||||||
|
switch (mode) {
|
||||||
|
case OVERWRITE_LEFT: {
|
||||||
|
// If the argument in rdx is already an object, we skip the
|
||||||
|
// allocation of a heap number.
|
||||||
|
__ JumpIfNotSmi(rdx, &skip_allocation);
|
||||||
|
// Allocate a heap number for the result. Keep rax and rdx intact
|
||||||
|
// for the possible runtime call.
|
||||||
|
__ AllocateHeapNumber(rbx, rcx, alloc_failure);
|
||||||
|
// Now rdx can be overwritten losing one of the arguments as we are
|
||||||
|
// now done and will not need it any more.
|
||||||
|
__ movq(rdx, rbx);
|
||||||
|
__ bind(&skip_allocation);
|
||||||
|
// Use object in rdx as a result holder
|
||||||
|
__ movq(rax, rdx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OVERWRITE_RIGHT:
|
||||||
|
// If the argument in rax is already an object, we skip the
|
||||||
|
// allocation of a heap number.
|
||||||
|
__ JumpIfNotSmi(rax, &skip_allocation);
|
||||||
|
// Fall through!
|
||||||
|
case NO_OVERWRITE:
|
||||||
|
// Allocate a heap number for the result. Keep rax and rdx intact
|
||||||
|
// for the possible runtime call.
|
||||||
|
__ AllocateHeapNumber(rbx, rcx, alloc_failure);
|
||||||
|
// Now rax can be overwritten losing one of the arguments as we are
|
||||||
|
// now done and will not need it any more.
|
||||||
|
__ movq(rax, rbx);
|
||||||
|
__ bind(&skip_allocation);
|
||||||
|
break;
|
||||||
|
default: UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
|
||||||
|
__ push(rdx);
|
||||||
|
__ push(rax);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
|
void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
|
||||||
// TAGGED case:
|
// TAGGED case:
|
||||||
// Input:
|
// Input:
|
||||||
@ -854,6 +1432,67 @@ void TranscendentalCacheStub::GenerateOperation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Input: rdx, rax are the left and right objects of a bit op.
|
||||||
|
// Output: rax, rcx are left and right integers for a bit op.
|
||||||
|
// Jump to conversion_failure: rdx and rax are unchanged.
|
||||||
|
void FloatingPointHelper::LoadAsIntegers(MacroAssembler* masm,
|
||||||
|
Label* conversion_failure,
|
||||||
|
Register heap_number_map) {
|
||||||
|
// Check float operands.
|
||||||
|
Label arg1_is_object, check_undefined_arg1;
|
||||||
|
Label arg2_is_object, check_undefined_arg2;
|
||||||
|
Label load_arg2, done;
|
||||||
|
|
||||||
|
__ JumpIfNotSmi(rdx, &arg1_is_object);
|
||||||
|
__ SmiToInteger32(r8, rdx);
|
||||||
|
__ jmp(&load_arg2);
|
||||||
|
|
||||||
|
// If the argument is undefined it converts to zero (ECMA-262, section 9.5).
|
||||||
|
__ bind(&check_undefined_arg1);
|
||||||
|
__ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
|
||||||
|
__ j(not_equal, conversion_failure);
|
||||||
|
__ Set(r8, 0);
|
||||||
|
__ jmp(&load_arg2);
|
||||||
|
|
||||||
|
__ bind(&arg1_is_object);
|
||||||
|
__ cmpq(FieldOperand(rdx, HeapObject::kMapOffset), heap_number_map);
|
||||||
|
__ j(not_equal, &check_undefined_arg1);
|
||||||
|
// Get the untagged integer version of the rdx heap number in r8.
|
||||||
|
__ TruncateHeapNumberToI(r8, rdx);
|
||||||
|
|
||||||
|
// Here r8 has the untagged integer, rax has a Smi or a heap number.
|
||||||
|
__ bind(&load_arg2);
|
||||||
|
// Test if arg2 is a Smi.
|
||||||
|
__ JumpIfNotSmi(rax, &arg2_is_object);
|
||||||
|
__ SmiToInteger32(rcx, rax);
|
||||||
|
__ jmp(&done);
|
||||||
|
|
||||||
|
// If the argument is undefined it converts to zero (ECMA-262, section 9.5).
|
||||||
|
__ bind(&check_undefined_arg2);
|
||||||
|
__ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
|
||||||
|
__ j(not_equal, conversion_failure);
|
||||||
|
__ Set(rcx, 0);
|
||||||
|
__ jmp(&done);
|
||||||
|
|
||||||
|
__ bind(&arg2_is_object);
|
||||||
|
__ cmpq(FieldOperand(rax, HeapObject::kMapOffset), heap_number_map);
|
||||||
|
__ j(not_equal, &check_undefined_arg2);
|
||||||
|
// Get the untagged integer version of the rax heap number in rcx.
|
||||||
|
__ TruncateHeapNumberToI(rcx, rax);
|
||||||
|
|
||||||
|
__ bind(&done);
|
||||||
|
__ movl(rax, r8);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FloatingPointHelper::LoadSSE2SmiOperands(MacroAssembler* masm) {
|
||||||
|
__ SmiToInteger32(kScratchRegister, rdx);
|
||||||
|
__ Cvtlsi2sd(xmm0, kScratchRegister);
|
||||||
|
__ SmiToInteger32(kScratchRegister, rax);
|
||||||
|
__ Cvtlsi2sd(xmm1, kScratchRegister);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void FloatingPointHelper::LoadSSE2UnknownOperands(MacroAssembler* masm,
|
void FloatingPointHelper::LoadSSE2UnknownOperands(MacroAssembler* masm,
|
||||||
Label* not_numbers) {
|
Label* not_numbers) {
|
||||||
Label load_smi_rdx, load_nonsmi_rax, load_smi_rax, load_float_rax, done;
|
Label load_smi_rdx, load_nonsmi_rax, load_smi_rax, load_float_rax, done;
|
||||||
@ -884,6 +1523,83 @@ void FloatingPointHelper::LoadSSE2UnknownOperands(MacroAssembler* masm,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FloatingPointHelper::NumbersToSmis(MacroAssembler* masm,
|
||||||
|
Register first,
|
||||||
|
Register second,
|
||||||
|
Register scratch1,
|
||||||
|
Register scratch2,
|
||||||
|
Register scratch3,
|
||||||
|
Label* on_success,
|
||||||
|
Label* on_not_smis,
|
||||||
|
ConvertUndefined convert_undefined) {
|
||||||
|
Register heap_number_map = scratch3;
|
||||||
|
Register smi_result = scratch1;
|
||||||
|
Label done, maybe_undefined_first, maybe_undefined_second, first_done;
|
||||||
|
|
||||||
|
__ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
|
||||||
|
|
||||||
|
Label first_smi;
|
||||||
|
__ JumpIfSmi(first, &first_smi, Label::kNear);
|
||||||
|
__ cmpq(FieldOperand(first, HeapObject::kMapOffset), heap_number_map);
|
||||||
|
__ j(not_equal,
|
||||||
|
(convert_undefined == CONVERT_UNDEFINED_TO_ZERO)
|
||||||
|
? &maybe_undefined_first
|
||||||
|
: on_not_smis);
|
||||||
|
// Convert HeapNumber to smi if possible.
|
||||||
|
__ movsd(xmm0, FieldOperand(first, HeapNumber::kValueOffset));
|
||||||
|
__ movq(scratch2, xmm0);
|
||||||
|
__ cvttsd2siq(smi_result, xmm0);
|
||||||
|
// Check if conversion was successful by converting back and
|
||||||
|
// comparing to the original double's bits.
|
||||||
|
__ Cvtlsi2sd(xmm1, smi_result);
|
||||||
|
__ movq(kScratchRegister, xmm1);
|
||||||
|
__ cmpq(scratch2, kScratchRegister);
|
||||||
|
__ j(not_equal, on_not_smis);
|
||||||
|
__ Integer32ToSmi(first, smi_result);
|
||||||
|
|
||||||
|
__ bind(&first_done);
|
||||||
|
__ JumpIfSmi(second, (on_success != NULL) ? on_success : &done);
|
||||||
|
__ bind(&first_smi);
|
||||||
|
__ AssertNotSmi(second);
|
||||||
|
__ cmpq(FieldOperand(second, HeapObject::kMapOffset), heap_number_map);
|
||||||
|
__ j(not_equal,
|
||||||
|
(convert_undefined == CONVERT_UNDEFINED_TO_ZERO)
|
||||||
|
? &maybe_undefined_second
|
||||||
|
: on_not_smis);
|
||||||
|
// Convert second to smi, if possible.
|
||||||
|
__ movsd(xmm0, FieldOperand(second, HeapNumber::kValueOffset));
|
||||||
|
__ movq(scratch2, xmm0);
|
||||||
|
__ cvttsd2siq(smi_result, xmm0);
|
||||||
|
__ Cvtlsi2sd(xmm1, smi_result);
|
||||||
|
__ movq(kScratchRegister, xmm1);
|
||||||
|
__ cmpq(scratch2, kScratchRegister);
|
||||||
|
__ j(not_equal, on_not_smis);
|
||||||
|
__ Integer32ToSmi(second, smi_result);
|
||||||
|
if (on_success != NULL) {
|
||||||
|
__ jmp(on_success);
|
||||||
|
} else {
|
||||||
|
__ jmp(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
__ bind(&maybe_undefined_first);
|
||||||
|
__ CompareRoot(first, Heap::kUndefinedValueRootIndex);
|
||||||
|
__ j(not_equal, on_not_smis);
|
||||||
|
__ xor_(first, first);
|
||||||
|
__ jmp(&first_done);
|
||||||
|
|
||||||
|
__ bind(&maybe_undefined_second);
|
||||||
|
__ CompareRoot(second, Heap::kUndefinedValueRootIndex);
|
||||||
|
__ j(not_equal, on_not_smis);
|
||||||
|
__ xor_(second, second);
|
||||||
|
if (on_success != NULL) {
|
||||||
|
__ jmp(on_success);
|
||||||
|
}
|
||||||
|
// Else: fall through.
|
||||||
|
|
||||||
|
__ bind(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void MathPowStub::Generate(MacroAssembler* masm) {
|
void MathPowStub::Generate(MacroAssembler* masm) {
|
||||||
const Register exponent = rdx;
|
const Register exponent = rdx;
|
||||||
const Register base = rax;
|
const Register base = rax;
|
||||||
@ -2750,7 +3466,6 @@ void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) {
|
|||||||
RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate);
|
RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate);
|
||||||
ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
|
ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
|
||||||
CreateAllocationSiteStub::GenerateAheadOfTime(isolate);
|
CreateAllocationSiteStub::GenerateAheadOfTime(isolate);
|
||||||
BinaryOpStub::GenerateAheadOfTime(isolate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user