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