void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
- Label not_smis, call_runtime;
+ Label right_arg_changed, call_runtime;
+
+ if (op_ == Token::MOD && has_fixed_right_arg_) {
+ // 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(r0, Operand(Smi::FromInt(fixed_right_arg_value())));
+ __ b(ne, &right_arg_changed);
+ }
if (result_type_ == BinaryOpIC::UNINITIALIZED ||
result_type_ == BinaryOpIC::SMI) {
// Code falls through if the result is not returned as either a smi or heap
// number.
+ __ bind(&right_arg_changed);
GenerateTypeTransition(masm);
__ bind(&call_runtime);
// to type transition.
} else {
+ if (has_fixed_right_arg_) {
+ __ Vmov(d8, fixed_right_arg_value(), scratch1);
+ __ VFPCompareAndSetFlags(d1, d8);
+ __ b(ne, &transition);
+ }
+
// We preserved r0 and r1 to be able to call runtime.
// Save the left value on the stack.
__ Push(r5, r4);
void BinaryOperation::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
- oracle->BinaryType(this, &left_type_, &right_type_, &result_type_);
+ oracle->BinaryType(this, &left_type_, &right_type_, &result_type_,
+ &has_fixed_right_arg_, &fixed_right_arg_value_);
}
TypeInfo left_type() const { return left_type_; }
TypeInfo right_type() const { return right_type_; }
TypeInfo result_type() const { return result_type_; }
+ bool has_fixed_right_arg() const { return has_fixed_right_arg_; }
+ int fixed_right_arg_value() const { return fixed_right_arg_value_; }
protected:
BinaryOperation(Isolate* isolate,
TypeInfo left_type_;
TypeInfo right_type_;
TypeInfo result_type_;
+ bool has_fixed_right_arg_;
+ int fixed_right_arg_value_;
// The short-circuit logical operations need an AST ID for their
// right-hand subexpression.
platform_specific_bit_(false),
left_type_(BinaryOpIC::UNINITIALIZED),
right_type_(BinaryOpIC::UNINITIALIZED),
- result_type_(BinaryOpIC::UNINITIALIZED) {
+ result_type_(BinaryOpIC::UNINITIALIZED),
+ has_fixed_right_arg_(false),
+ encoded_right_arg_(encode_arg_value(1)) {
Initialize();
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
int key,
BinaryOpIC::TypeInfo left_type,
BinaryOpIC::TypeInfo right_type,
- BinaryOpIC::TypeInfo result_type = BinaryOpIC::UNINITIALIZED)
+ BinaryOpIC::TypeInfo result_type,
+ bool has_fixed_right_arg,
+ int32_t fixed_right_arg_value)
: op_(OpBits::decode(key)),
mode_(ModeBits::decode(key)),
platform_specific_bit_(PlatformSpecificBits::decode(key)),
left_type_(left_type),
right_type_(right_type),
- result_type_(result_type) { }
+ result_type_(result_type),
+ has_fixed_right_arg_(has_fixed_right_arg),
+ encoded_right_arg_(encode_arg_value(fixed_right_arg_value)) { }
static void decode_types_from_minor_key(int minor_key,
BinaryOpIC::TypeInfo* left_type,
return static_cast<Token::Value>(OpBits::decode(minor_key));
}
+ static bool decode_has_fixed_right_arg_from_minor_key(int minor_key) {
+ return HasFixedRightArgBits::decode(minor_key);
+ }
+
+ static int decode_fixed_right_arg_value_from_minor_key(int minor_key) {
+ return decode_arg_value(FixedRightArgValueBits::decode(minor_key));
+ }
+
+ int fixed_right_arg_value() const {
+ return decode_arg_value(encoded_right_arg_);
+ }
+
+ static bool can_encode_arg_value(int32_t value) {
+ return value > 0 &&
+ IsPowerOf2(value) &&
+ FixedRightArgValueBits::is_valid(WhichPowerOf2(value));
+ }
+
enum SmiCodeGenerateHeapNumberResults {
ALLOW_HEAPNUMBER_RESULTS,
NO_HEAPNUMBER_RESULTS
BinaryOpIC::TypeInfo right_type_;
BinaryOpIC::TypeInfo result_type_;
+ bool has_fixed_right_arg_;
+ 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 19 bits TTTRRRLLLSOOOOOOOMM.
+ // 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() {
| PlatformSpecificBits::encode(platform_specific_bit_)
| LeftTypeBits::encode(left_type_)
| RightTypeBits::encode(right_type_)
- | ResultTypeBits::encode(result_type_);
+ | ResultTypeBits::encode(result_type_)
+ | HasFixedRightArgBits::encode(has_fixed_right_arg_)
+ | FixedRightArgValueBits::encode(encoded_right_arg_);
}
}
-static bool ObjectToInt32(Object* obj, int32_t* value) {
- if (obj->IsSmi()) {
- *value = Smi::cast(obj)->value();
- return true;
- }
-
- if (obj->IsHeapNumber()) {
- double num = HeapNumber::cast(obj)->value();
- if (FastI2D(FastD2I(num)) != num) {
- if (FLAG_trace_osr) {
- PrintF("**** %g could not be converted to int32 ****\n",
- HeapNumber::cast(obj)->value());
- }
- return false;
- }
-
- *value = FastD2I(num);
- return true;
- }
-
- return false;
-}
-
-
-static bool ObjectToUint32(Object* obj, uint32_t* value) {
- if (obj->IsSmi()) {
- if (Smi::cast(obj)->value() < 0) return false;
-
- *value = static_cast<uint32_t>(Smi::cast(obj)->value());
- return true;
- }
-
- if (obj->IsHeapNumber()) {
- double num = HeapNumber::cast(obj)->value();
- if ((num < 0) || (FastUI2D(FastD2UI(num)) != num)) {
- if (FLAG_trace_osr) {
- PrintF("**** %g could not be converted to uint32 ****\n",
- HeapNumber::cast(obj)->value());
- }
- return false;
- }
-
- *value = FastD2UI(num);
- return true;
- }
-
- return false;
-}
-
-
bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
int* input_offset) {
disasm::NameConverter converter;
case Translation::INT32_REGISTER: {
int32_t int32_value = 0;
- if (!ObjectToInt32(input_object, &int32_value)) return false;
+ if (!input_object->ToInt32(&int32_value)) return false;
int output_reg = iterator->Next();
if (FLAG_trace_osr) {
case Translation::UINT32_REGISTER: {
uint32_t uint32_value = 0;
- if (!ObjectToUint32(input_object, &uint32_value)) return false;
+ if (!input_object->ToUint32(&uint32_value)) return false;
int output_reg = iterator->Next();
if (FLAG_trace_osr) {
case Translation::INT32_STACK_SLOT: {
int32_t int32_value = 0;
- if (!ObjectToInt32(input_object, &int32_value)) return false;
+ if (!input_object->ToInt32(&int32_value)) return false;
int output_index = iterator->Next();
unsigned output_offset =
case Translation::UINT32_STACK_SLOT: {
uint32_t uint32_value = 0;
- if (!ObjectToUint32(input_object, &uint32_value)) return false;
+ if (!input_object->ToUint32(&uint32_value)) return false;
int output_index = iterator->Next();
unsigned output_offset =
}
-HInstruction* HMod::New(
- Zone* zone, HValue* context, HValue* left, HValue* right) {
+HInstruction* HMod::New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right,
+ bool has_fixed_right_arg,
+ int fixed_right_arg_value) {
if (FLAG_fold_constants && left->IsConstant() && right->IsConstant()) {
HConstant* c_left = HConstant::cast(left);
HConstant* c_right = HConstant::cast(right);
}
}
}
- return new(zone) HMod(context, left, right);
+ return new(zone) HMod(context,
+ left,
+ right,
+ has_fixed_right_arg,
+ fixed_right_arg_value);
}
static HInstruction* New(Zone* zone,
HValue* context,
HValue* left,
- HValue* right);
+ HValue* right,
+ bool has_fixed_right_arg,
+ int fixed_right_arg_value);
+
+ bool has_fixed_right_arg() const { return has_fixed_right_arg_; }
+ int fixed_right_arg_value() const { return fixed_right_arg_value_; }
bool HasPowerOf2Divisor() {
if (right()->IsConstant() &&
virtual Range* InferRange(Zone* zone);
private:
- HMod(HValue* context, HValue* left, HValue* right)
- : HArithmeticBinaryOperation(context, left, right) {
+ HMod(HValue* context,
+ HValue* left,
+ HValue* right,
+ bool has_fixed_right_arg,
+ int fixed_right_arg_value)
+ : HArithmeticBinaryOperation(context, left, right),
+ has_fixed_right_arg_(has_fixed_right_arg),
+ fixed_right_arg_value_(fixed_right_arg_value) {
SetFlag(kCanBeDivByZero);
SetFlag(kCanOverflow);
}
+
+ const bool has_fixed_right_arg_;
+ const int fixed_right_arg_value_;
};
TypeInfo left_info = expr->left_type();
TypeInfo right_info = expr->right_type();
TypeInfo result_info = expr->result_type();
- TypeInfo combined_info;
+ bool has_fixed_right_arg = expr->has_fixed_right_arg();
+ int fixed_right_arg_value = expr->fixed_right_arg_value();
Representation left_rep = ToRepresentation(left_info);
Representation right_rep = ToRepresentation(right_info);
Representation result_rep = ToRepresentation(result_info);
instr = HMul::New(zone(), context, left, right);
break;
case Token::MOD:
- instr = HMod::New(zone(), context, left, right);
+ instr = HMod::New(zone(),
+ context,
+ left,
+ right,
+ has_fixed_right_arg,
+ fixed_right_arg_value);
break;
case Token::DIV:
instr = HDiv::New(zone(), context, left, right);
// Expects operands in edx, eax.
static void LoadSSE2Smis(MacroAssembler* masm, Register scratch);
- // Checks that the two floating point numbers loaded into xmm0 and xmm1
- // have int32 values.
- static void CheckSSE2OperandsAreInt32(MacroAssembler* masm,
- Label* non_int32,
- Register scratch);
-
// Checks that |operand| has an int32 value. If |int32_result| is different
// from |scratch|, it will contain that int32 value.
static void CheckSSE2OperandIsInt32(MacroAssembler* masm,
void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
- Label call_runtime;
+ Label right_arg_changed, call_runtime;
switch (op_) {
case Token::ADD:
UNREACHABLE();
}
+ if (op_ == Token::MOD && has_fixed_right_arg_) {
+ // 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(eax, Immediate(Smi::FromInt(fixed_right_arg_value())));
+ __ j(not_equal, &right_arg_changed);
+ }
+
if (result_type_ == BinaryOpIC::UNINITIALIZED ||
result_type_ == BinaryOpIC::SMI) {
BinaryOpStub_GenerateSmiCode(
// Code falls through if the result is not returned as either a smi or heap
// number.
+ __ bind(&right_arg_changed);
switch (op_) {
case Token::ADD:
case Token::SUB:
case Token::MUL:
case Token::DIV:
case Token::MOD: {
- Label not_floats;
- Label not_int32;
+ Label not_floats, not_int32, right_arg_changed;
if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope use_sse2(masm, SSE2);
// It could be that only SMIs have been seen at either the left
__ JumpIfNotSmi(eax, ¬_int32);
}
FloatingPointHelper::LoadSSE2Operands(masm, ¬_floats);
- FloatingPointHelper::CheckSSE2OperandsAreInt32(masm, ¬_int32, ecx);
+ FloatingPointHelper::CheckSSE2OperandIsInt32(
+ masm, ¬_int32, xmm0, ebx, ecx, xmm2);
+ FloatingPointHelper::CheckSSE2OperandIsInt32(
+ masm, ¬_int32, xmm1, edi, ecx, xmm2);
if (op_ == Token::MOD) {
+ if (has_fixed_right_arg_) {
+ __ cmp(edi, Immediate(fixed_right_arg_value()));
+ __ j(not_equal, &right_arg_changed);
+ }
GenerateRegisterArgsPush(masm);
__ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
} else {
__ bind(¬_floats);
__ bind(¬_int32);
+ __ bind(&right_arg_changed);
GenerateTypeTransition(masm);
break;
}
}
-void FloatingPointHelper::CheckSSE2OperandsAreInt32(MacroAssembler* masm,
- Label* non_int32,
- Register scratch) {
- CheckSSE2OperandIsInt32(masm, non_int32, xmm0, scratch, scratch, xmm2);
- CheckSSE2OperandIsInt32(masm, non_int32, xmm1, scratch, scratch, xmm2);
-}
-
-
void FloatingPointHelper::CheckSSE2OperandIsInt32(MacroAssembler* masm,
Label* non_int32,
XMMRegister operand,
if (FLAG_trace_ic) {
PrintF("[UnaryOpIC in ");
JavaScriptFrame::PrintTop(isolate, stdout, false, true);
- PrintF(" (%s->%s)#%s @ %p]\n",
+ PrintF(" %s => %s #%s @ %p]\n",
UnaryOpIC::GetName(previous_type),
UnaryOpIC::GetName(type),
Token::Name(op),
}
+#ifdef DEBUG
+static void TraceBinaryOp(BinaryOpIC::TypeInfo left,
+ BinaryOpIC::TypeInfo right,
+ bool has_fixed_right_arg,
+ int32_t fixed_right_arg_value,
+ BinaryOpIC::TypeInfo result) {
+ PrintF("%s*%s", BinaryOpIC::GetName(left), BinaryOpIC::GetName(right));
+ if (has_fixed_right_arg) PrintF("{%d}", fixed_right_arg_value);
+ PrintF("->%s", BinaryOpIC::GetName(result));
+}
+#endif
+
+
RUNTIME_FUNCTION(MaybeObject*, BinaryOp_Patch) {
ASSERT(args.length() == 3);
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, unused_previous_result;
+
+ BinaryOpIC::TypeInfo previous_left, previous_right, previous_result;
BinaryOpStub::decode_types_from_minor_key(
- key, &previous_left, &previous_right, &unused_previous_result);
+ 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 new_overall = Max(new_left, new_right);
BinaryOpIC::TypeInfo previous_overall = Max(previous_left, previous_right);
- if (new_overall == BinaryOpIC::SMI && previous_overall == BinaryOpIC::SMI) {
- if (op == Token::DIV ||
- op == Token::MUL ||
- op == Token::SHR ||
- kSmiValueSize == 32) {
- // 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;
+ bool previous_has_fixed_right_arg =
+ BinaryOpStub::decode_has_fixed_right_arg_from_minor_key(key);
+ int previous_fixed_right_arg_value =
+ BinaryOpStub::decode_fixed_right_arg_value_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_has_fixed_right_arg &&
+ previous_fixed_right_arg_value == value));
+ int32_t new_fixed_right_arg_value = new_has_fixed_right_arg ? value : 1;
+
+ if (previous_has_fixed_right_arg == new_has_fixed_right_arg) {
+ if (new_overall == BinaryOpIC::SMI && previous_overall == BinaryOpIC::SMI) {
+ if (op == Token::DIV ||
+ op == Token::MUL ||
+ op == Token::SHR ||
+ kSmiValueSize == 32) {
+ // 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;
+ 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);
+ BinaryOpStub stub(key, new_left, new_right, result_type,
+ new_has_fixed_right_arg, new_fixed_right_arg_value);
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(" ((%s+%s)->((%s+%s)->%s))#%s @ %p]\n",
- BinaryOpIC::GetName(previous_left),
- BinaryOpIC::GetName(previous_right),
- BinaryOpIC::GetName(new_left),
- BinaryOpIC::GetName(new_right),
- BinaryOpIC::GetName(result_type),
- Token::Name(op),
- static_cast<void*>(*code));
+ PrintF(" ");
+ TraceBinaryOp(previous_left, previous_right, previous_has_fixed_right_arg,
+ previous_fixed_right_arg_value, previous_result);
+ PrintF(" => ");
+ TraceBinaryOp(new_left, new_right, new_has_fixed_right_arg,
+ new_fixed_right_arg_value, result_type);
+ PrintF(" #%s @ %p]\n", Token::Name(op), static_cast<void*>(*code));
}
#endif
BinaryOpIC ic(isolate);
}
+bool Object::ToInt32(int32_t* value) {
+ if (IsSmi()) {
+ *value = Smi::cast(this)->value();
+ return true;
+ }
+ if (IsHeapNumber()) {
+ double num = HeapNumber::cast(this)->value();
+ if (FastI2D(FastD2I(num)) == num) {
+ *value = FastD2I(num);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool Object::ToUint32(uint32_t* value) {
+ if (IsSmi()) {
+ int num = Smi::cast(this)->value();
+ if (num >= 0) {
+ *value = static_cast<uint32_t>(num);
+ return true;
+ }
+ }
+ if (IsHeapNumber()) {
+ double num = HeapNumber::cast(this)->value();
+ if (num >= 0 && FastUI2D(FastD2UI(num)) == num) {
+ *value = FastD2UI(num);
+ return true;
+ }
+ }
+ return false;
+}
+
+
template<typename To>
static inline To* CheckedCast(void *from) {
uintptr_t temp = reinterpret_cast<uintptr_t>(from);
// Extract the number.
inline double Number();
inline bool IsNaN();
+ bool ToInt32(int32_t* value);
+ bool ToUint32(uint32_t* value);
inline Representation OptimalRepresentation() {
if (FLAG_track_fields && IsSmi()) {
void TypeFeedbackOracle::BinaryType(BinaryOperation* expr,
TypeInfo* left,
TypeInfo* right,
- TypeInfo* result) {
+ TypeInfo* result,
+ bool* has_fixed_right_arg,
+ int* fixed_right_arg_value) {
Handle<Object> object = GetInfo(expr->BinaryOperationFeedbackId());
TypeInfo unknown = TypeInfo::Unknown();
if (!object->IsCode()) {
}
Handle<Code> code = Handle<Code>::cast(object);
if (code->is_binary_op_stub()) {
+ int minor_key = code->stub_info();
BinaryOpIC::TypeInfo left_type, right_type, result_type;
- BinaryOpStub::decode_types_from_minor_key(code->stub_info(), &left_type,
- &right_type, &result_type);
+ BinaryOpStub::decode_types_from_minor_key(
+ minor_key, &left_type, &right_type, &result_type);
*left = TypeFromBinaryOpType(left_type);
*right = TypeFromBinaryOpType(right_type);
*result = TypeFromBinaryOpType(result_type);
+ *has_fixed_right_arg =
+ BinaryOpStub::decode_has_fixed_right_arg_from_minor_key(minor_key);
+ *fixed_right_arg_value =
+ BinaryOpStub::decode_fixed_right_arg_value_from_minor_key(minor_key);
return;
}
// Not a binary op stub.
void BinaryType(BinaryOperation* expr,
TypeInfo* left,
TypeInfo* right,
- TypeInfo* result);
+ TypeInfo* result,
+ bool* has_fixed_right_arg,
+ int* fixed_right_arg_value);
void CompareType(CompareOperation* expr,
TypeInfo* left_type,
TypeInfo* right_type,
void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
- Label call_runtime;
+ Label right_arg_changed, call_runtime;
+
+ if (op_ == Token::MOD && has_fixed_right_arg_) {
+ // 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.
// 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()) {