Index: runtime/vm/intrinsifier_x64.cc |
=================================================================== |
--- runtime/vm/intrinsifier_x64.cc (revision 9572) |
+++ runtime/vm/intrinsifier_x64.cc (working copy) |
@@ -8,6 +8,7 @@ |
#include "vm/intrinsifier.h" |
#include "vm/assembler.h" |
+#include "vm/assembler_macros.h" |
#include "vm/instructions.h" |
#include "vm/object_store.h" |
@@ -485,12 +486,21 @@ |
bool Intrinsifier::Integer_addFromInteger(Assembler* assembler) { |
+ Label fall_through; |
+ TestBothArgumentsSmis(assembler, &fall_through); |
+ // RAX contains right argument. |
+ __ movq(RCX, Address(RSP, + 2 * kWordSize)); |
+ __ addq(RAX, RCX); |
+ __ j(OVERFLOW, &fall_through, Assembler::kNearJump); |
+ // Result is in RAX. |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
bool Intrinsifier::Integer_add(Assembler* assembler) { |
- return false; |
+ return Integer_addFromInteger(assembler); |
} |
@@ -508,18 +518,39 @@ |
bool Intrinsifier::Integer_sub(Assembler* assembler) { |
+ Label fall_through; |
+ TestBothArgumentsSmis(assembler, &fall_through); |
+ // RAX contains right argument, which is the actual subtrahend of subtraction. |
+ __ movq(RCX, RAX); |
+ __ movq(RAX, Address(RSP, + 2 * kWordSize)); |
+ __ subq(RAX, RCX); |
+ __ j(OVERFLOW, &fall_through, Assembler::kNearJump); |
+ // Result is in RAX. |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
bool Intrinsifier::Integer_mulFromInteger(Assembler* assembler) { |
+ Label fall_through; |
+ TestBothArgumentsSmis(assembler, &fall_through); |
+ // RAX is the right argument. |
+ ASSERT(kSmiTag == 0); // Adjust code below if not the case. |
+ __ SmiUntag(RAX); |
+ __ movq(RCX, Address(RSP, + 2 * kWordSize)); |
+ __ imulq(RAX, RCX); |
+ __ j(OVERFLOW, &fall_through, Assembler::kNearJump); |
+ // Result is in RAX. |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
bool Intrinsifier::Integer_mul(Assembler* assembler) { |
- return false; |
+ return Integer_mulFromInteger(assembler); |
} |
@@ -558,42 +589,95 @@ |
bool Intrinsifier::Integer_truncDivide(Assembler* assembler) { |
+ Label fall_through; |
+ TestBothArgumentsSmis(assembler, &fall_through); |
+ // RAX: right argument (divisor) |
+ __ cmpq(RAX, Immediate(0)); |
+ __ j(EQUAL, &fall_through, Assembler::kNearJump); |
+ __ movq(RCX, RAX); |
+ __ SmiUntag(RCX); |
+ __ movq(RAX, Address(RSP, + 2 * kWordSize)); // Left argument (dividend). |
+ __ SmiUntag(RAX); |
+ __ pushq(RDX); // Preserve RDX in case of 'fall_through'. |
+ __ cqo(); |
+ __ idivq(RCX); |
+ __ popq(RDX); |
+ // Check the corner case of dividing the 'MIN_SMI' with -1, in which case we |
+ // cannot tag the result. |
+ __ cmpq(RAX, Immediate(0x4000000000000000)); |
+ __ j(EQUAL, &fall_through); |
+ __ SmiTag(RAX); |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
bool Intrinsifier::Integer_negate(Assembler* assembler) { |
+ Label fall_through; |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); |
+ __ testq(RAX, Immediate(kSmiTagMask)); |
+ __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); // Non-smi value. |
+ __ negq(RAX); |
+ __ j(OVERFLOW, &fall_through, Assembler::kNearJump); |
+ // Result is in RAX. |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
bool Intrinsifier::Integer_bitAndFromInteger(Assembler* assembler) { |
+ Label fall_through; |
+ TestBothArgumentsSmis(assembler, &fall_through); |
+ // RAX is the right argument. |
+ __ movq(RCX, Address(RSP, + 2 * kWordSize)); |
+ __ andq(RAX, RCX); |
+ // Result is in RAX. |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
bool Intrinsifier::Integer_bitAnd(Assembler* assembler) { |
- return false; |
+ return Integer_bitAndFromInteger(assembler); |
} |
bool Intrinsifier::Integer_bitOrFromInteger(Assembler* assembler) { |
+ Label fall_through; |
+ TestBothArgumentsSmis(assembler, &fall_through); |
+ // RAX is the right argument. |
+ __ movq(RCX, Address(RSP, + 2 * kWordSize)); |
+ __ orq(RAX, RCX); |
+ // Result is in RAX. |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
bool Intrinsifier::Integer_bitOr(Assembler* assembler) { |
- return false; |
+ return Integer_bitOrFromInteger(assembler); |
} |
bool Intrinsifier::Integer_bitXorFromInteger(Assembler* assembler) { |
+ Label fall_through; |
+ TestBothArgumentsSmis(assembler, &fall_through); |
+ // RAX is the right argument. |
+ __ movq(RCX, Address(RSP, + 2 * kWordSize)); |
+ __ xorq(RAX, RCX); |
+ // Result is in RAX. |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
bool Intrinsifier::Integer_bitXor(Assembler* assembler) { |
- return false; |
+ return Integer_bitXorFromInteger(assembler); |
} |
@@ -652,7 +736,7 @@ |
bool Intrinsifier::Integer_equal(Assembler* assembler) { |
- return false; |
+ return Integer_equalToInteger(assembler); |
} |
@@ -748,57 +832,293 @@ |
bool Intrinsifier::Double_isNegative(Assembler* assembler) { |
- return false; |
+ const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
+ const Bool& bool_false = Bool::ZoneHandle(Bool::False()); |
+ Label is_false, is_true, is_zero; |
+ __ movq(RAX, Address(RSP, +1 * kWordSize)); |
+ __ movsd(XMM0, FieldAddress(RAX, Double::value_offset())); |
+ __ xorpd(XMM1, XMM1); // 0.0 -> XMM1. |
+ __ comisd(XMM0, XMM1); |
+ __ j(PARITY_EVEN, &is_false, Assembler::kNearJump); // NaN -> false. |
+ __ j(EQUAL, &is_zero, Assembler::kNearJump); // Check for negative zero. |
+ __ j(ABOVE_EQUAL, &is_false, Assembler::kNearJump); // >= 0 -> false. |
+ __ Bind(&is_true); |
+ __ LoadObject(RAX, bool_true); |
+ __ ret(); |
+ __ Bind(&is_false); |
+ __ LoadObject(RAX, bool_false); |
+ __ ret(); |
+ __ Bind(&is_zero); |
+ // Check for negative zero (get the sign bit). |
+ __ movmskpd(RAX, XMM0); |
+ __ testq(RAX, Immediate(1)); |
+ __ j(NOT_ZERO, &is_true, Assembler::kNearJump); |
+ __ jmp(&is_false, Assembler::kNearJump); |
+ return true; // Method is complete, no slow case. |
} |
+// 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; |
+ TestLastArgumentIsDouble(assembler, &is_smi, &fall_through); |
+ // Argument is double and is in EAX. |
+ __ fldl(FieldAddress(RAX, Double::value_offset())); |
+ __ Bind(&double_op); |
+ switch (kind) { |
+ case kSine: __ fsin(); break; |
+ case kCosine: __ fcos(); break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ const Class& double_class = Class::Handle( |
+ Isolate::Current()->object_store()->double_class()); |
+ Label alloc_failed; |
+ AssemblerMacros::TryAllocate(assembler, |
+ double_class, |
+ &alloc_failed, |
+ RAX); // Result register. |
+ __ fstpl(FieldAddress(RAX, Double::value_offset())); |
+ __ ret(); |
+ |
+ __ Bind(&is_smi); // smi -> double. |
+ __ SmiUntag(RAX); |
+ __ pushq(RAX); |
+ __ fildl(Address(RSP, 0)); |
+ __ popq(RAX); |
+ __ jmp(&double_op); |
+ |
+ __ Bind(&alloc_failed); |
+ __ ffree(0); |
+ __ fincstp(); |
+ |
+ __ Bind(&fall_through); |
+} |
+ |
+ |
bool Intrinsifier::Math_sqrt(Assembler* assembler) { |
+ Label fall_through, is_smi, double_op; |
+ TestLastArgumentIsDouble(assembler, &is_smi, &fall_through); |
+ // Argument is double and is in RAX. |
+ __ movsd(XMM1, FieldAddress(RAX, Double::value_offset())); |
+ __ Bind(&double_op); |
+ __ sqrtsd(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(&is_smi); |
+ __ SmiUntag(RAX); |
+ __ cvtsi2sd(XMM1, RAX); |
+ __ jmp(&double_op); |
+ __ Bind(&fall_through); |
return false; |
} |
bool Intrinsifier::Math_sin(Assembler* assembler) { |
- return false; |
+ EmitTrigonometric(assembler, kSine); |
+ return false; // Compile method for slow case. |
} |
bool Intrinsifier::Math_cos(Assembler* assembler) { |
- return false; |
+ EmitTrigonometric(assembler, kCosine); |
+ return false; // Compile method for slow case. |
} |
+// Identity comparison. |
bool Intrinsifier::Object_equal(Assembler* assembler) { |
- return false; |
+ Label is_true; |
+ const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
+ const Bool& bool_false = Bool::ZoneHandle(Bool::False()); |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); |
+ __ cmpq(RAX, Address(RSP, + 2 * kWordSize)); |
+ __ j(EQUAL, &is_true, Assembler::kNearJump); |
+ __ LoadObject(RAX, bool_false); |
+ __ ret(); |
+ __ Bind(&is_true); |
+ __ LoadObject(RAX, bool_true); |
+ __ ret(); |
+ return true; |
} |
+static intptr_t GetOffsetForField(const char* class_name_p, |
+ const char* field_name_p) { |
+ const String& class_name = String::Handle(String::NewSymbol(class_name_p)); |
+ const String& field_name = String::Handle(String::NewSymbol(field_name_p)); |
+ const Class& cls = Class::Handle(Library::Handle( |
+ Library::CoreImplLibrary()).LookupClass(class_name)); |
+ ASSERT(!cls.IsNull()); |
+ const Field& field = Field::ZoneHandle(cls.LookupInstanceField(field_name)); |
+ ASSERT(!field.IsNull()); |
+ return field.Offset(); |
+} |
+ |
+static const char* kFixedSizeArrayIteratorClassName = "FixedSizeArrayIterator"; |
+ |
+// Class 'FixedSizeArrayIterator': |
+// T next() { |
+// return _array[_pos++]; |
+// } |
+// Intrinsify: return _array[_pos++]; |
+// TODO(srdjan): Throw a 'NoMoreElementsException' exception if the iterator |
+// has no more elements. |
bool Intrinsifier::FixedSizeArrayIterator_next(Assembler* assembler) { |
+ Label fall_through; |
+ const intptr_t array_offset = |
+ GetOffsetForField(kFixedSizeArrayIteratorClassName, "_array"); |
+ const intptr_t pos_offset = |
+ GetOffsetForField(kFixedSizeArrayIteratorClassName, "_pos"); |
+ ASSERT((array_offset >= 0) && (pos_offset >= 0)); |
+ // Receiver is not NULL. |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); // Receiver. |
+ __ movq(RCX, FieldAddress(RAX, pos_offset)); // Field _pos. |
+ // '_pos' cannot be greater than array length and therefore is always Smi. |
+#if defined(DEBUG) |
+ Label pos_ok; |
+ __ testq(RCX, Immediate(kSmiTagMask)); |
+ __ j(ZERO, &pos_ok, Assembler::kNearJump); |
+ __ Stop("pos must be Smi"); |
+ __ Bind(&pos_ok); |
+#endif |
+ // Check that we are not trying to call 'next' when 'hasNext' is false. |
+ __ movq(RAX, FieldAddress(RAX, array_offset)); // Field _array. |
+ __ cmpq(RCX, FieldAddress(RAX, Array::length_offset())); // Range check. |
+ __ j(ABOVE_EQUAL, &fall_through, Assembler::kNearJump); |
+ |
+ // RCX is Smi, i.e, times 2. |
+ ASSERT(kSmiTagShift == 1); |
+ __ movq(RDI, FieldAddress(RAX, RCX, TIMES_4, sizeof(RawArray))); // Result. |
+ const Immediate value = Immediate(reinterpret_cast<int64_t>(Smi::New(1))); |
+ __ addq(RCX, value); // _pos++. |
+ __ j(OVERFLOW, &fall_through, Assembler::kNearJump); |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); // Receiver. |
+ __ StoreIntoObjectNoBarrier(RAX, |
+ FieldAddress(RAX, pos_offset), |
+ RCX); // Store _pos. |
+ __ movq(RAX, RDI); |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
+// Class 'FixedSizeArrayIterator': |
+// bool hasNext() { |
+// return _length > _pos; |
+// } |
bool Intrinsifier::FixedSizeArrayIterator_hasNext(Assembler* assembler) { |
+ Label fall_through, is_true; |
+ const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
+ const Bool& bool_false = Bool::ZoneHandle(Bool::False()); |
+ const intptr_t length_offset = |
+ GetOffsetForField(kFixedSizeArrayIteratorClassName, "_length"); |
+ const intptr_t pos_offset = |
+ GetOffsetForField(kFixedSizeArrayIteratorClassName, "_pos"); |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); // Receiver. |
+ __ movq(RCX, FieldAddress(RAX, length_offset)); // Field _length. |
+ __ movq(RAX, FieldAddress(RAX, pos_offset)); // Field _pos. |
+ __ movq(RDI, RAX); |
+ __ orq(RDI, RCX); |
+ __ testq(RDI, Immediate(kSmiTagMask)); |
+ __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); // Non-smi _length/_pos. |
+ __ cmpq(RCX, RAX); // _length > _pos. |
+ __ j(GREATER, &is_true, Assembler::kNearJump); |
+ __ LoadObject(RAX, bool_false); |
+ __ ret(); |
+ __ Bind(&is_true); |
+ __ LoadObject(RAX, bool_true); |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
bool Intrinsifier::String_getLength(Assembler* assembler) { |
- return false; |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); // String object. |
+ __ movq(RAX, FieldAddress(RAX, String::length_offset())); |
+ __ ret(); |
+ return true; |
} |
+// TODO(srdjan): Implement for two and four byte strings as well. |
bool Intrinsifier::String_charCodeAt(Assembler* assembler) { |
+ Label fall_through; |
+ __ movq(RCX, Address(RSP, + 1 * kWordSize)); // Index. |
+ __ movq(RAX, Address(RSP, + 2 * kWordSize)); // String. |
+ __ testq(RCX, Immediate(kSmiTagMask)); |
+ __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); // Non-smi index. |
+ // Range check. |
+ __ cmpq(RCX, FieldAddress(RAX, String::length_offset())); |
+ // Runtime throws exception. |
+ __ j(ABOVE_EQUAL, &fall_through, Assembler::kNearJump); |
+ __ CompareClassId(RAX, kOneByteString); |
+ __ j(NOT_EQUAL, &fall_through); |
+ __ SmiUntag(RCX); |
+ __ movzxb(RAX, FieldAddress(RAX, RCX, TIMES_1, OneByteString::data_offset())); |
+ __ SmiTag(RAX); |
+ __ ret(); |
+ __ Bind(&fall_through); |
return false; |
} |
bool Intrinsifier::String_hashCode(Assembler* assembler) { |
+ Label fall_through; |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); // String object. |
+ __ movq(RAX, FieldAddress(RAX, String::hash_offset())); |
+ __ cmpq(RAX, Immediate(0)); |
+ __ j(EQUAL, &fall_through, Assembler::kNearJump); |
+ __ ret(); |
+ __ Bind(&fall_through); |
+ // Hash not yet computed. |
return false; |
} |
bool Intrinsifier::String_isEmpty(Assembler* assembler) { |
- return false; |
+ Label is_true; |
+ const Bool& bool_true = Bool::ZoneHandle(Bool::True()); |
+ const Bool& bool_false = Bool::ZoneHandle(Bool::False()); |
+ // Get length. |
+ __ movq(RAX, Address(RSP, + 1 * kWordSize)); // String object. |
+ __ movq(RAX, FieldAddress(RAX, String::length_offset())); |
+ __ cmpq(RAX, Immediate(Smi::RawValue(0))); |
+ __ j(EQUAL, &is_true, Assembler::kNearJump); |
+ __ LoadObject(RAX, bool_false); |
+ __ ret(); |
+ __ Bind(&is_true); |
+ __ LoadObject(RAX, bool_true); |
+ __ ret(); |
+ return true; |
} |
#undef __ |