Implement the remaining methods in intrinsifier_x64.cc

Review URL: https://chromiumcodereview.appspot.com//10704192

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@9605 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
srdjan@google.com 2012-07-12 19:10:50 +00:00
parent 2c2583e304
commit b9aed1cd3c
2 changed files with 242 additions and 41 deletions

View file

@ -804,7 +804,8 @@ bool Intrinsifier::Integer_greaterEqualThan(Assembler* assembler) {
}
// This is called for Smi, Mint and Bigint receivers. Bigints are not handled.
// 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());
@ -824,20 +825,19 @@ bool Intrinsifier::Integer_equalToInteger(Assembler* assembler) {
__ LoadObject(EAX, bool_true);
__ ret();
// At least one of the arguments was not Smi, inline code for Smi/Mint
// equality comparison.
// At least one of the arguments was not Smi.
Label receiver_not_smi;
__ Bind(&check_for_mint);
__ movl(EAX, Address(ESP, + 2 * kWordSize)); // Receiver.
__ testl(EAX, Immediate(kSmiTagMask));
__ j(NOT_ZERO, &receiver_not_smi);
// Note that an instance of Mint never contains a value that can be
// 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.
// Left is Smi, return false if right is Mint, otherwise fall through.
__ movl(EAX, Address(ESP, + 1 * kWordSize)); // Right argument.
__ CompareClassId(EAX, kMint, EDI);
__ j(NOT_EQUAL, &fall_through);
__ CompareClassId(EAX, kDouble, EDI);
__ j(EQUAL, &fall_through);
__ LoadObject(EAX, bool_false); // Smi == Mint -> false.
__ ret();
@ -849,7 +849,7 @@ bool Intrinsifier::Integer_equalToInteger(Assembler* assembler) {
__ movl(EAX, Address(ESP, + 1 * kWordSize)); // Right argument.
__ testl(EAX, Immediate(kSmiTagMask));
__ j(NOT_ZERO, &fall_through);
__ LoadObject(EAX, bool_false); // Smi == Mint -> false.
__ LoadObject(EAX, bool_false);
__ ret();
// TODO(srdjan): Implement Mint == Mint comparison.
@ -890,16 +890,13 @@ bool Intrinsifier::Integer_sar(Assembler* assembler) {
}
// Argument is Smi (receiver).
bool Intrinsifier::Smi_bitNegate(Assembler* assembler) {
Label fall_through;
__ movl(EAX, Address(ESP, + 1 * kWordSize)); // Index.
__ testl(EAX, Immediate(kSmiTagMask));
__ j(NOT_ZERO, &fall_through, Assembler::kNearJump); // Non-smi.
__ notl(EAX);
__ andl(EAX, Immediate(~kSmiTagMask)); // Remove inverted smi-tag.
__ ret();
__ Bind(&fall_through);
return false;
return true;
}
@ -988,12 +985,12 @@ bool Intrinsifier::Double_toDouble(Assembler* assembler) {
}
// Expects EAX to contain right argument, left argument is on stack. Left
// argument is double, right argument is of unknown type.
// 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 EAX, class in EBX.
// Both arguments are double, right operand is in EAX.
__ movsd(XMM1, FieldAddress(EAX, Double::value_offset()));
__ movl(EAX, Address(ESP, + 2 * kWordSize)); // Left argument.
__ movsd(XMM0, FieldAddress(EAX, Double::value_offset()));
@ -1037,7 +1034,7 @@ bool Intrinsifier::Double_div(Assembler* assembler) {
}
// Left is double right is integer (bigint or Smi)
// Left is double right is integer (Bigint, Mint or Smi)
bool Intrinsifier::Double_mulFromInteger(Assembler* assembler) {
Label fall_through;
// Only Smi-s allowed.

View file

@ -677,6 +677,34 @@ bool Intrinsifier::Integer_bitXor(Assembler* assembler) {
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 @@ bool Intrinsifier::Integer_greaterEqualThan(Assembler* assembler) {
}
// 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_equal(Assembler* assembler) {
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 @@ bool Intrinsifier::Double_toDouble(Assembler* assembler) {
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 @@ bool Intrinsifier::Double_isNegative(Assembler* assembler) {
}
// 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;