Index: runtime/vm/intrinsifier_x64.cc |
=================================================================== |
--- runtime/vm/intrinsifier_x64.cc (revision 9596) |
+++ runtime/vm/intrinsifier_x64.cc (working copy) |
@@ -677,6 +677,34 @@ |
bool Intrinsifier::Integer_shl(Assembler* assembler) { |
+ ASSERT(kSmiTagShift == 1); |
+ ASSERT(kSmiTag == 0); |
+ Label fall_through, overflow; |
+ TestBothArgumentsSmis(assembler, &fall_through); |
+ // Shift value is in RAX. Compare with tagged Smi. |
+ __ cmpq(RAX, Immediate(Smi::RawValue(Smi::kBits))); |
+ __ j(ABOVE_EQUAL, &fall_through, Assembler::kNearJump); |
+ |
+ __ SmiUntag(RAX); |
+ __ movq(RCX, RAX); // Shift amount must be in RCX. |
+ __ movq(RAX, Address(RSP, + 2 * kWordSize)); // Value. |
+ |
+ // Overflow test - all the shifted-out bits must be same as the sign bit. |
+ __ movq(RDI, RAX); |
+ __ shlq(RAX, RCX); |
+ __ sarq(RAX, RCX); |
+ __ cmpq(RAX, RDI); |
+ __ j(NOT_EQUAL, &overflow, Assembler::kNearJump); |
+ |
+ __ shlq(RAX, RCX); // Shift for result now we know there is no overflow. |
+ |
+ // RAX is a correctly tagged Smi. |
+ __ ret(); |
+ |
+ __ Bind(&overflow); |
+ // Mint is rarely used on x64 (only for integers requiring 64 bit instead of |
+ // 63 bits as represented by Smi). |
+ __ Bind(&fall_through); |
return false; |
} |
@@ -725,7 +753,56 @@ |
} |
+// This is called for Smi, Mint and Bigint receivers. The right argument |
+// can be Smi, Mint, Bigint or double. |
bool Intrinsifier::Integer_equalToInteger(Assembler* assembler) { |
+ Label fall_through, true_label, check_for_mint; |
+ const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
+ const Bool& bool_false = Bool::ZoneHandle(Bool::False()); |
+ // For integer receiver '===' check first. |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); |
+ __ movq(RCX, Address(RSP, + 2 * kWordSize)); |
+ __ cmpq(RAX, RCX); |
+ __ j(EQUAL, &true_label, Assembler::kNearJump); |
+ __ orq(RAX, RCX); |
+ __ testq(RAX, Immediate(kSmiTagMask)); |
+ __ j(NOT_ZERO, &check_for_mint, Assembler::kNearJump); |
+ // Both arguments are smi, '===' is good enough. |
+ __ LoadObject(RAX, bool_false); |
+ __ ret(); |
+ __ Bind(&true_label); |
+ __ LoadObject(RAX, bool_true); |
+ __ ret(); |
+ |
+ // At least one of the arguments was not Smi. |
+ Label receiver_not_smi; |
+ __ Bind(&check_for_mint); |
+ __ movq(RAX, Address(RSP, + 2 * kWordSize)); // Receiver. |
+ __ testq(RAX, Immediate(kSmiTagMask)); |
+ __ j(NOT_ZERO, &receiver_not_smi); |
+ |
+ // Left (receiver) is Smi, return false if right is not Double. |
+ // Note that an instance of Mint or Bigint never contains a value that can be |
+ // represented by Smi. |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); |
+ __ CompareClassId(RAX, kDouble); |
+ __ j(EQUAL, &fall_through); |
+ __ LoadObject(RAX, bool_false); |
+ __ ret(); |
+ |
+ __ Bind(&receiver_not_smi); |
+ // RAX:: receiver. |
+ __ CompareClassId(RAX, kMint); |
+ __ j(NOT_EQUAL, &fall_through); |
+ // Receiver is Mint, return false if right is Smi. |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); // Right argument. |
+ __ testq(RAX, Immediate(kSmiTagMask)); |
+ __ j(NOT_ZERO, &fall_through); |
+ __ LoadObject(RAX, bool_false); // Smi == Mint -> false. |
+ __ ret(); |
+ // TODO(srdjan): Implement Mint == Mint comparison. |
+ |
+ __ Bind(&fall_through); |
return false; |
} |
@@ -736,37 +813,111 @@ |
bool Intrinsifier::Integer_sar(Assembler* assembler) { |
+ Label fall_through, shift_count_ok; |
+ TestBothArgumentsSmis(assembler, &fall_through); |
+ Immediate count_limit = Immediate(0x3F); |
+ // Check that the count is not larger than what the hardware can handle. |
+ // For shifting right a Smi the result is the same for all numbers |
+ // >= count_limit. |
+ __ SmiUntag(RAX); |
+ // Negative counts throw exception. |
+ __ cmpq(RAX, Immediate(0)); |
+ __ j(LESS, &fall_through, Assembler::kNearJump); |
+ __ cmpq(RAX, count_limit); |
+ __ j(LESS_EQUAL, &shift_count_ok, Assembler::kNearJump); |
+ __ movq(RAX, count_limit); |
+ __ Bind(&shift_count_ok); |
+ __ movq(RCX, RAX); // Shift amount must be in RCX. |
+ __ movq(RAX, Address(RSP, + 2 * kWordSize)); // Value. |
+ __ SmiUntag(RAX); // Value. |
+ __ sarq(RAX, RCX); |
+ __ SmiTag(RAX); |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
+// Argument is Smi (receiver). |
bool Intrinsifier::Smi_bitNegate(Assembler* assembler) { |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); // Index. |
+ __ notq(RAX); |
+ __ andq(RAX, Immediate(~kSmiTagMask)); // Remove inverted smi-tag. |
+ __ ret(); |
+ return true; |
+} |
+ |
+ |
+// Check if the last argument is a double, jump to label 'is_smi' if smi |
+// (easy to convert to double), otherwise jump to label 'not_double_smi', |
+// Returns the last argument in RAX. |
+static void TestLastArgumentIsDouble(Assembler* assembler, |
+ Label* is_smi, |
+ Label* not_double_smi) { |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); |
+ __ testq(RAX, Immediate(kSmiTagMask)); |
+ __ j(ZERO, is_smi, Assembler::kNearJump); // Jump if Smi. |
+ __ CompareClassId(RAX, kDouble); |
+ __ j(NOT_EQUAL, not_double_smi, Assembler::kNearJump); |
+ // Fall through if double. |
+} |
+ |
+ |
+// Both arguments on stack, left argument is a double, right argument is of |
+// unknown type. Return true or false object in RAX. Any NaN argument |
+// returns false. Any non-double argument causes control flow to fall through |
+// to the slow case (compiled method body). |
+static bool CompareDoubles(Assembler* assembler, Condition true_condition) { |
+ const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
+ const Bool& bool_false = Bool::ZoneHandle(Bool::False()); |
+ Label fall_through, is_false, is_true, is_smi, double_op; |
+ TestLastArgumentIsDouble(assembler, &is_smi, &fall_through); |
+ // Both arguments are double, right operand is in RAX. |
+ __ movsd(XMM1, FieldAddress(RAX, Double::value_offset())); |
+ __ Bind(&double_op); |
+ __ movq(RAX, Address(RSP, + 2 * kWordSize)); // Left argument. |
+ __ movsd(XMM0, FieldAddress(RAX, Double::value_offset())); |
+ __ comisd(XMM0, XMM1); |
+ __ j(PARITY_EVEN, &is_false, Assembler::kNearJump); // NaN -> false; |
+ __ j(true_condition, &is_true, Assembler::kNearJump); |
+ // Fall through false. |
+ __ Bind(&is_false); |
+ __ LoadObject(RAX, bool_false); |
+ __ ret(); |
+ __ Bind(&is_true); |
+ __ LoadObject(RAX, bool_true); |
+ __ ret(); |
+ __ Bind(&is_smi); |
+ __ SmiUntag(RAX); |
+ __ cvtsi2sd(XMM1, RAX); |
+ __ jmp(&double_op); |
+ __ Bind(&fall_through); |
return false; |
} |
bool Intrinsifier::Double_greaterThan(Assembler* assembler) { |
- return false; |
+ return CompareDoubles(assembler, ABOVE); |
} |
bool Intrinsifier::Double_greaterEqualThan(Assembler* assembler) { |
- return false; |
+ return CompareDoubles(assembler, ABOVE_EQUAL); |
} |
bool Intrinsifier::Double_lessThan(Assembler* assembler) { |
- return false; |
+ return CompareDoubles(assembler, BELOW); |
} |
bool Intrinsifier::Double_equal(Assembler* assembler) { |
- return false; |
+ return CompareDoubles(assembler, EQUAL); |
} |
bool Intrinsifier::Double_lessEqualThan(Assembler* assembler) { |
- return false; |
+ return CompareDoubles(assembler, BELOW_EQUAL); |
} |
@@ -779,32 +930,99 @@ |
return true; |
} |
-bool Intrinsifier::Double_add(Assembler* assembler) { |
+ |
+// Expects left argument to be double (receiver). Right argument is unknown. |
+// Both arguments are on stack. |
+static bool DoubleArithmeticOperations(Assembler* assembler, Token::Kind kind) { |
+ Label fall_through; |
+ TestLastArgumentIsDouble(assembler, &fall_through, &fall_through); |
+ // Both arguments are double, right operand is in RAX. |
+ __ movsd(XMM1, FieldAddress(RAX, Double::value_offset())); |
+ __ movq(RAX, Address(RSP, + 2 * kWordSize)); // Left argument. |
+ __ movsd(XMM0, FieldAddress(RAX, Double::value_offset())); |
+ switch (kind) { |
+ case Token::kADD: __ addsd(XMM0, XMM1); break; |
+ case Token::kSUB: __ subsd(XMM0, XMM1); break; |
+ case Token::kMUL: __ mulsd(XMM0, XMM1); break; |
+ case Token::kDIV: __ divsd(XMM0, XMM1); break; |
+ default: UNREACHABLE(); |
+ } |
+ const Class& double_class = Class::Handle( |
+ Isolate::Current()->object_store()->double_class()); |
+ AssemblerMacros::TryAllocate(assembler, |
+ double_class, |
+ &fall_through, |
+ RAX); // Result register. |
+ __ movsd(FieldAddress(RAX, Double::value_offset()), XMM0); |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
+bool Intrinsifier::Double_add(Assembler* assembler) { |
+ return DoubleArithmeticOperations(assembler, Token::kADD); |
+} |
+ |
+ |
bool Intrinsifier::Double_mul(Assembler* assembler) { |
- return false; |
+ return DoubleArithmeticOperations(assembler, Token::kMUL); |
} |
bool Intrinsifier::Double_sub(Assembler* assembler) { |
- return false; |
+ return DoubleArithmeticOperations(assembler, Token::kSUB); |
} |
bool Intrinsifier::Double_div(Assembler* assembler) { |
- return false; |
+ return DoubleArithmeticOperations(assembler, Token::kDIV); |
} |
bool Intrinsifier::Double_mulFromInteger(Assembler* assembler) { |
+ Label fall_through; |
+ // Only Smi-s allowed. |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); |
+ __ testq(RAX, Immediate(kSmiTagMask)); |
+ __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); |
+ // Is Smi. |
+ __ SmiUntag(RAX); |
+ __ cvtsi2sd(XMM1, RAX); |
+ __ movq(RAX, Address(RSP, + 2 * kWordSize)); |
+ __ movsd(XMM0, FieldAddress(RAX, Double::value_offset())); |
+ __ mulsd(XMM0, XMM1); |
+ const Class& double_class = Class::Handle( |
+ Isolate::Current()->object_store()->double_class()); |
+ AssemblerMacros::TryAllocate(assembler, |
+ double_class, |
+ &fall_through, |
+ RAX); // Result register. |
+ __ movsd(FieldAddress(RAX, Double::value_offset()), XMM0); |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
+// Left is double right is integer (Bigint, Mint or Smi) |
bool Intrinsifier::Double_fromInteger(Assembler* assembler) { |
+ Label fall_through; |
+ __ movq(RAX, Address(RSP, +1 * kWordSize)); |
+ __ testq(RAX, Immediate(kSmiTagMask)); |
+ __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); |
+ // Is Smi. |
+ __ SmiUntag(RAX); |
+ __ cvtsi2sd(XMM0, RAX); |
+ const Class& double_class = Class::Handle( |
+ Isolate::Current()->object_store()->double_class()); |
+ AssemblerMacros::TryAllocate(assembler, |
+ double_class, |
+ &fall_through, |
+ RAX); // Result register. |
+ __ movsd(FieldAddress(RAX, Double::value_offset()), XMM0); |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
@@ -853,26 +1071,12 @@ |
} |
-// Check if the last argument is a double, jump to label 'is_smi' if smi |
-// (easy to convert to double), otherwise jump to label 'not_double_smi', |
-// Returns the last argument in RAX. |
-static void TestLastArgumentIsDouble(Assembler* assembler, |
- Label* is_smi, |
- Label* not_double_smi) { |
- __ movq(RAX, Address(RSP, + 1 * kWordSize)); |
- __ testq(RAX, Immediate(kSmiTagMask)); |
- __ j(ZERO, is_smi, Assembler::kNearJump); // Jump if Smi. |
- __ CompareClassId(RAX, kDouble); |
- __ j(NOT_EQUAL, not_double_smi, Assembler::kNearJump); |
- // Fall through if double. |
-} |
- |
- |
enum TrigonometricFunctions { |
kSine, |
kCosine, |
}; |
+ |
static void EmitTrigonometric(Assembler* assembler, |
TrigonometricFunctions kind) { |
Label fall_through, is_smi, double_op; |