Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(32)

Unified Diff: src/arm/code-stubs-arm.cc

Issue 6697023: Merge 6800:7180 from the bleeding edge branch to the experimental/gc branch. (Closed) Base URL: http://v8.googlecode.com/svn/branches/experimental/gc/
Patch Set: Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/arm/code-stubs-arm.h ('k') | src/arm/codegen-arm.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/arm/code-stubs-arm.cc
===================================================================
--- src/arm/code-stubs-arm.cc (revision 7180)
+++ src/arm/code-stubs-arm.cc (working copy)
@@ -397,18 +397,89 @@
Register scratch2,
Label* not_number);
- // Loads the number from object into dst as a 32-bit integer if possible. If
- // the object is not a 32-bit integer control continues at the label
- // not_int32. If VFP is supported double_scratch is used but not scratch2.
- static void LoadNumberAsInteger(MacroAssembler* masm,
- Register object,
- Register dst,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- DwVfpRegister double_scratch,
- Label* not_int32);
+ // Convert the smi or heap number in object to an int32 using the rules
+ // for ToInt32 as described in ECMAScript 9.5.: the value is truncated
+ // and brought into the range -2^31 .. +2^31 - 1.
+ static void ConvertNumberToInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ DwVfpRegister double_scratch,
+ Label* not_int32);
+ // Load the number from object into double_dst in the double format.
+ // Control will jump to not_int32 if the value cannot be exactly represented
+ // by a 32-bit integer.
+ // Floating point value in the 32-bit integer range that are not exact integer
+ // won't be loaded.
+ static void LoadNumberAsInt32Double(MacroAssembler* masm,
+ Register object,
+ Destination destination,
+ DwVfpRegister double_dst,
+ Register dst1,
+ Register dst2,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ SwVfpRegister single_scratch,
+ Label* not_int32);
+
+ // Loads the number from object into dst as a 32-bit integer.
+ // Control will jump to not_int32 if the object cannot be exactly represented
+ // by a 32-bit integer.
+ // Floating point value in the 32-bit integer range that are not exact integer
+ // won't be converted.
+ // scratch3 is not used when VFP3 is supported.
+ static void LoadNumberAsInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ DwVfpRegister double_scratch,
+ Label* not_int32);
+
+ // Generate non VFP3 code to check if a double can be exactly represented by a
+ // 32-bit integer. This does not check for 0 or -0, which need
+ // to be checked for separately.
+ // Control jumps to not_int32 if the value is not a 32-bit integer, and falls
+ // through otherwise.
+ // src1 and src2 will be cloberred.
+ //
+ // Expected input:
+ // - src1: higher (exponent) part of the double value.
+ // - src2: lower (mantissa) part of the double value.
+ // Output status:
+ // - dst: 32 higher bits of the mantissa. (mantissa[51:20])
+ // - src2: contains 1.
+ // - other registers are clobbered.
+ static void DoubleIs32BitInteger(MacroAssembler* masm,
+ Register src1,
+ Register src2,
+ Register dst,
+ Register scratch,
+ Label* not_int32);
+
+ // Generates code to call a C function to do a double operation using core
+ // registers. (Used when VFP3 is not supported.)
+ // This code never falls through, but returns with a heap number containing
+ // the result in r0.
+ // Register heapnumber_result must be a heap number in which the
+ // result of the operation will be stored.
+ // Requires the following layout on entry:
+ // r0: Left value (least significant part of mantissa).
+ // r1: Left value (sign, exponent, top of mantissa).
+ // r2: Right value (least significant part of mantissa).
+ // r3: Right value (sign, exponent, top of mantissa).
+ static void CallCCodeForDoubleOperation(MacroAssembler* masm,
+ Token::Value op,
+ Register heap_number_result,
+ Register scratch);
+
private:
static void LoadNumber(MacroAssembler* masm,
FloatingPointHelper::Destination destination,
@@ -533,34 +604,365 @@
}
-void FloatingPointHelper::LoadNumberAsInteger(MacroAssembler* masm,
- Register object,
- Register dst,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- DwVfpRegister double_scratch,
- Label* not_int32) {
+void FloatingPointHelper::ConvertNumberToInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ DwVfpRegister double_scratch,
+ Label* not_number) {
if (FLAG_debug_code) {
__ AbortIfNotRootValue(heap_number_map,
Heap::kHeapNumberMapRootIndex,
"HeapNumberMap register clobbered.");
}
- Label is_smi, done;
+ Label is_smi;
+ Label done;
+ Label not_in_int32_range;
+
__ JumpIfSmi(object, &is_smi);
__ ldr(scratch1, FieldMemOperand(object, HeapNumber::kMapOffset));
__ cmp(scratch1, heap_number_map);
- __ b(ne, not_int32);
- __ ConvertToInt32(
- object, dst, scratch1, scratch2, double_scratch, not_int32);
+ __ b(ne, not_number);
+ __ ConvertToInt32(object,
+ dst,
+ scratch1,
+ scratch2,
+ double_scratch,
+ &not_in_int32_range);
__ jmp(&done);
+
+ __ bind(&not_in_int32_range);
+ __ ldr(scratch1, FieldMemOperand(object, HeapNumber::kExponentOffset));
+ __ ldr(scratch2, FieldMemOperand(object, HeapNumber::kMantissaOffset));
+
+ __ EmitOutOfInt32RangeTruncate(dst,
+ scratch1,
+ scratch2,
+ scratch3);
+ __ jmp(&done);
+
__ bind(&is_smi);
__ SmiUntag(dst, object);
__ bind(&done);
}
+void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm,
+ Register object,
+ Destination destination,
+ DwVfpRegister double_dst,
+ Register dst1,
+ Register dst2,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ SwVfpRegister single_scratch,
+ Label* not_int32) {
+ ASSERT(!scratch1.is(object) && !scratch2.is(object));
+ ASSERT(!scratch1.is(scratch2));
+ ASSERT(!heap_number_map.is(object) &&
+ !heap_number_map.is(scratch1) &&
+ !heap_number_map.is(scratch2));
+ Label done, obj_is_not_smi;
+
+ __ JumpIfNotSmi(object, &obj_is_not_smi);
+ __ SmiUntag(scratch1, object);
+ if (CpuFeatures::IsSupported(VFP3)) {
+ CpuFeatures::Scope scope(VFP3);
+ __ vmov(single_scratch, scratch1);
+ __ vcvt_f64_s32(double_dst, single_scratch);
+ if (destination == kCoreRegisters) {
+ __ vmov(dst1, dst2, double_dst);
+ }
+ } else {
+ Label fewer_than_20_useful_bits;
+ // Expected output:
+ // | dst1 | dst2 |
+ // | s | exp | mantissa |
+
+ // Check for zero.
+ __ cmp(scratch1, Operand(0));
+ __ mov(dst1, scratch1);
+ __ mov(dst2, scratch1);
+ __ b(eq, &done);
+
+ // Preload the sign of the value.
+ __ and_(dst1, scratch1, Operand(HeapNumber::kSignMask), SetCC);
+ // Get the absolute value of the object (as an unsigned integer).
+ __ rsb(scratch1, scratch1, Operand(0), SetCC, mi);
+
+ // Get mantisssa[51:20].
+
+ // Get the position of the first set bit.
+ __ CountLeadingZeros(dst2, scratch1, scratch2);
+ __ rsb(dst2, dst2, Operand(31));
+
+ // Set the exponent.
+ __ add(scratch2, dst2, Operand(HeapNumber::kExponentBias));
+ __ Bfi(dst1, scratch2, scratch2,
+ HeapNumber::kExponentShift, HeapNumber::kExponentBits);
+
+ // Clear the first non null bit.
+ __ mov(scratch2, Operand(1));
+ __ bic(scratch1, scratch1, Operand(scratch2, LSL, dst2));
+
+ __ cmp(dst2, Operand(HeapNumber::kMantissaBitsInTopWord));
+ // Get the number of bits to set in the lower part of the mantissa.
+ __ sub(scratch2, dst2, Operand(HeapNumber::kMantissaBitsInTopWord), SetCC);
+ __ b(mi, &fewer_than_20_useful_bits);
+ // Set the higher 20 bits of the mantissa.
+ __ orr(dst1, dst1, Operand(scratch1, LSR, scratch2));
+ __ rsb(scratch2, scratch2, Operand(32));
+ __ mov(dst2, Operand(scratch1, LSL, scratch2));
+ __ b(&done);
+
+ __ bind(&fewer_than_20_useful_bits);
+ __ rsb(scratch2, dst2, Operand(HeapNumber::kMantissaBitsInTopWord));
+ __ mov(scratch2, Operand(scratch1, LSL, scratch2));
+ __ orr(dst1, dst1, scratch2);
+ // Set dst2 to 0.
+ __ mov(dst2, Operand(0));
+ }
+
+ __ b(&done);
+
+ __ bind(&obj_is_not_smi);
+ if (FLAG_debug_code) {
+ __ AbortIfNotRootValue(heap_number_map,
+ Heap::kHeapNumberMapRootIndex,
+ "HeapNumberMap register clobbered.");
+ }
+ __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_int32);
+
+ // Load the number.
+ if (CpuFeatures::IsSupported(VFP3)) {
+ CpuFeatures::Scope scope(VFP3);
+ // Load the double value.
+ __ sub(scratch1, object, Operand(kHeapObjectTag));
+ __ vldr(double_dst, scratch1, HeapNumber::kValueOffset);
+
+ __ EmitVFPTruncate(kRoundToZero,
+ single_scratch,
+ double_dst,
+ scratch1,
+ scratch2,
+ kCheckForInexactConversion);
+
+ // Jump to not_int32 if the operation did not succeed.
+ __ b(ne, not_int32);
+
+ if (destination == kCoreRegisters) {
+ __ vmov(dst1, dst2, double_dst);
+ }
+
+ } else {
+ ASSERT(!scratch1.is(object) && !scratch2.is(object));
+ // Load the double value in the destination registers..
+ __ Ldrd(dst1, dst2, FieldMemOperand(object, HeapNumber::kValueOffset));
+
+ // Check for 0 and -0.
+ __ bic(scratch1, dst1, Operand(HeapNumber::kSignMask));
+ __ orr(scratch1, scratch1, Operand(dst2));
+ __ cmp(scratch1, Operand(0));
+ __ b(eq, &done);
+
+ // Check that the value can be exactly represented by a 32-bit integer.
+ // Jump to not_int32 if that's not the case.
+ DoubleIs32BitInteger(masm, dst1, dst2, scratch1, scratch2, not_int32);
+
+ // dst1 and dst2 were trashed. Reload the double value.
+ __ Ldrd(dst1, dst2, FieldMemOperand(object, HeapNumber::kValueOffset));
+ }
+
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::LoadNumberAsInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ DwVfpRegister double_scratch,
+ Label* not_int32) {
+ ASSERT(!dst.is(object));
+ ASSERT(!scratch1.is(object) && !scratch2.is(object) && !scratch3.is(object));
+ ASSERT(!scratch1.is(scratch2) &&
+ !scratch1.is(scratch3) &&
+ !scratch2.is(scratch3));
+
+ Label done;
+
+ // Untag the object into the destination register.
+ __ SmiUntag(dst, object);
+ // Just return if the object is a smi.
+ __ JumpIfSmi(object, &done);
+
+ if (FLAG_debug_code) {
+ __ AbortIfNotRootValue(heap_number_map,
+ Heap::kHeapNumberMapRootIndex,
+ "HeapNumberMap register clobbered.");
+ }
+ __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_int32);
+
+ // Object is a heap number.
+ // Convert the floating point value to a 32-bit integer.
+ if (CpuFeatures::IsSupported(VFP3)) {
+ CpuFeatures::Scope scope(VFP3);
+ SwVfpRegister single_scratch = double_scratch.low();
+ // Load the double value.
+ __ sub(scratch1, object, Operand(kHeapObjectTag));
+ __ vldr(double_scratch, scratch1, HeapNumber::kValueOffset);
+
+ __ EmitVFPTruncate(kRoundToZero,
+ single_scratch,
+ double_scratch,
+ scratch1,
+ scratch2,
+ kCheckForInexactConversion);
+
+ // Jump to not_int32 if the operation did not succeed.
+ __ b(ne, not_int32);
+ // Get the result in the destination register.
+ __ vmov(dst, single_scratch);
+
+ } else {
+ // Load the double value in the destination registers.
+ __ ldr(scratch1, FieldMemOperand(object, HeapNumber::kExponentOffset));
+ __ ldr(scratch2, FieldMemOperand(object, HeapNumber::kMantissaOffset));
+
+ // Check for 0 and -0.
+ __ bic(dst, scratch1, Operand(HeapNumber::kSignMask));
+ __ orr(dst, scratch2, Operand(dst));
+ __ cmp(dst, Operand(0));
+ __ b(eq, &done);
+
+ DoubleIs32BitInteger(masm, scratch1, scratch2, dst, scratch3, not_int32);
+
+ // Registers state after DoubleIs32BitInteger.
+ // dst: mantissa[51:20].
+ // scratch2: 1
+
+ // Shift back the higher bits of the mantissa.
+ __ mov(dst, Operand(dst, LSR, scratch3));
+ // Set the implicit first bit.
+ __ rsb(scratch3, scratch3, Operand(32));
+ __ orr(dst, dst, Operand(scratch2, LSL, scratch3));
+ // Set the sign.
+ __ ldr(scratch1, FieldMemOperand(object, HeapNumber::kExponentOffset));
+ __ tst(scratch1, Operand(HeapNumber::kSignMask));
+ __ rsb(dst, dst, Operand(0), LeaveCC, mi);
+ }
+
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::DoubleIs32BitInteger(MacroAssembler* masm,
+ Register src1,
+ Register src2,
+ Register dst,
+ Register scratch,
+ Label* not_int32) {
+ // Get exponent alone in scratch.
+ __ Ubfx(scratch,
+ src1,
+ HeapNumber::kExponentShift,
+ HeapNumber::kExponentBits);
+
+ // Substract the bias from the exponent.
+ __ sub(scratch, scratch, Operand(HeapNumber::kExponentBias), SetCC);
+
+ // src1: higher (exponent) part of the double value.
+ // src2: lower (mantissa) part of the double value.
+ // scratch: unbiased exponent.
+
+ // Fast cases. Check for obvious non 32-bit integer values.
+ // Negative exponent cannot yield 32-bit integers.
+ __ b(mi, not_int32);
+ // Exponent greater than 31 cannot yield 32-bit integers.
+ // Also, a positive value with an exponent equal to 31 is outside of the
+ // signed 32-bit integer range.
+ // Another way to put it is that if (exponent - signbit) > 30 then the
+ // number cannot be represented as an int32.
+ Register tmp = dst;
+ __ sub(tmp, scratch, Operand(src1, LSR, 31));
+ __ cmp(tmp, Operand(30));
+ __ b(gt, not_int32);
+ // - Bits [21:0] in the mantissa are not null.
+ __ tst(src2, Operand(0x3fffff));
+ __ b(ne, not_int32);
+
+ // Otherwise the exponent needs to be big enough to shift left all the
+ // non zero bits left. So we need the (30 - exponent) last bits of the
+ // 31 higher bits of the mantissa to be null.
+ // Because bits [21:0] are null, we can check instead that the
+ // (32 - exponent) last bits of the 32 higher bits of the mantisssa are null.
+
+ // Get the 32 higher bits of the mantissa in dst.
+ __ Ubfx(dst,
+ src2,
+ HeapNumber::kMantissaBitsInTopWord,
+ 32 - HeapNumber::kMantissaBitsInTopWord);
+ __ orr(dst,
+ dst,
+ Operand(src1, LSL, HeapNumber::kNonMantissaBitsInTopWord));
+
+ // Create the mask and test the lower bits (of the higher bits).
+ __ rsb(scratch, scratch, Operand(32));
+ __ mov(src2, Operand(1));
+ __ mov(src1, Operand(src2, LSL, scratch));
+ __ sub(src1, src1, Operand(1));
+ __ tst(dst, src1);
+ __ b(ne, not_int32);
+}
+
+
+void FloatingPointHelper::CallCCodeForDoubleOperation(
+ MacroAssembler* masm,
+ Token::Value op,
+ Register heap_number_result,
+ Register scratch) {
+ // Using core registers:
+ // r0: Left value (least significant part of mantissa).
+ // r1: Left value (sign, exponent, top of mantissa).
+ // r2: Right value (least significant part of mantissa).
+ // r3: Right value (sign, exponent, top of mantissa).
+
+ // Assert that heap_number_result is callee-saved.
+ // We currently always use r5 to pass it.
+ ASSERT(heap_number_result.is(r5));
+
+ // Push the current return address before the C call. Return will be
+ // through pop(pc) below.
+ __ push(lr);
+ __ PrepareCallCFunction(4, scratch); // Two doubles are 4 arguments.
+ // Call C routine that may not cause GC or other trouble.
+ __ CallCFunction(ExternalReference::double_fp_operation(op), 4);
+ // Store answer in the overwritable heap number.
+#if !defined(USE_ARM_EABI)
+ // Double returned in fp coprocessor register 0 and 1, encoded as
+ // register cr8. Offsets must be divisible by 4 for coprocessor so we
+ // need to substract the tag from heap_number_result.
+ __ sub(scratch, heap_number_result, Operand(kHeapObjectTag));
+ __ stc(p1, cr8, MemOperand(scratch, HeapNumber::kValueOffset));
+#else
+ // Double returned in registers 0 and 1.
+ __ Strd(r0, r1, FieldMemOperand(heap_number_result,
+ HeapNumber::kValueOffset));
+#endif
+ // Place heap_number_result in r0 and return to the pushed return address.
+ __ mov(r0, Operand(heap_number_result));
+ __ pop(pc);
+}
+
+
// See comment for class.
void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) {
Label max_negative_int;
@@ -1296,6 +1698,9 @@
// This stub does not handle the inlined cases (Smis, Booleans, undefined).
// The stub returns zero for false, and a non-zero value for true.
void ToBooleanStub::Generate(MacroAssembler* masm) {
+ // This stub uses VFP3 instructions.
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
+
Label false_result;
Label not_heap_number;
Register scratch = r9.is(tos_) ? r7 : r9;
@@ -2636,6 +3041,7 @@
Register right = r0;
Register scratch1 = r7;
Register scratch2 = r9;
+ Register scratch3 = r4;
ASSERT(smi_operands || (not_numbers != NULL));
if (smi_operands && FLAG_debug_code) {
@@ -2661,8 +3067,8 @@
// Allocate new heap number for result.
Register result = r5;
- __ AllocateHeapNumber(
- result, scratch1, scratch2, heap_number_map, gc_required);
+ GenerateHeapResultAllocation(
+ masm, result, heap_number_map, scratch1, scratch2, gc_required);
// Load the operands.
if (smi_operands) {
@@ -2704,33 +3110,11 @@
__ add(r0, r0, Operand(kHeapObjectTag));
__ Ret();
} else {
- // Using core registers:
- // r0: Left value (least significant part of mantissa).
- // r1: Left value (sign, exponent, top of mantissa).
- // r2: Right value (least significant part of mantissa).
- // r3: Right value (sign, exponent, top of mantissa).
-
- // Push the current return address before the C call. Return will be
- // through pop(pc) below.
- __ push(lr);
- __ PrepareCallCFunction(4, scratch1); // Two doubles are 4 arguments.
- // Call C routine that may not cause GC or other trouble. r5 is callee
- // save.
- __ CallCFunction(ExternalReference::double_fp_operation(op_), 4);
- // Store answer in the overwritable heap number.
-#if !defined(USE_ARM_EABI)
- // Double returned in fp coprocessor register 0 and 1, encoded as
- // register cr8. Offsets must be divisible by 4 for coprocessor so we
- // need to substract the tag from r5.
- __ sub(scratch1, result, Operand(kHeapObjectTag));
- __ stc(p1, cr8, MemOperand(scratch1, HeapNumber::kValueOffset));
-#else
- // Double returned in registers 0 and 1.
- __ Strd(r0, r1, FieldMemOperand(result, HeapNumber::kValueOffset));
-#endif
- // Plase result in r0 and return to the pushed return address.
- __ mov(r0, Operand(result));
- __ pop(pc);
+ // Call the C function to handle the double operation.
+ FloatingPointHelper::CallCCodeForDoubleOperation(masm,
+ op_,
+ result,
+ scratch1);
}
break;
}
@@ -2745,22 +3129,24 @@
__ SmiUntag(r2, right);
} else {
// Convert operands to 32-bit integers. Right in r2 and left in r3.
- FloatingPointHelper::LoadNumberAsInteger(masm,
- left,
- r3,
- heap_number_map,
- scratch1,
- scratch2,
- d0,
- not_numbers);
- FloatingPointHelper::LoadNumberAsInteger(masm,
- right,
- r2,
- heap_number_map,
- scratch1,
- scratch2,
- d0,
- not_numbers);
+ FloatingPointHelper::ConvertNumberToInt32(masm,
+ left,
+ r3,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ d0,
+ not_numbers);
+ FloatingPointHelper::ConvertNumberToInt32(masm,
+ right,
+ r2,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ d0,
+ not_numbers);
}
Label result_not_a_smi;
@@ -2776,7 +3162,6 @@
break;
case Token::SAR:
// Use only the 5 least significant bits of the shift count.
- __ and_(r2, r2, Operand(0x1f));
__ GetLeastBitsFromInt32(r2, r2, 5);
__ mov(r2, Operand(r3, ASR, r2));
break;
@@ -2811,8 +3196,14 @@
// Allocate new heap number for result.
__ bind(&result_not_a_smi);
- __ AllocateHeapNumber(
- r5, scratch1, scratch2, heap_number_map, gc_required);
+ Register result = r5;
+ if (smi_operands) {
+ __ AllocateHeapNumber(
+ result, scratch1, scratch2, heap_number_map, gc_required);
+ } else {
+ GenerateHeapResultAllocation(
+ masm, result, heap_number_map, scratch1, scratch2, gc_required);
+ }
// r2: Answer as signed int32.
// r5: Heap number to write answer into.
@@ -2915,64 +3306,344 @@
void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
ASSERT(operands_type_ == TRBinaryOpIC::INT32);
- GenerateTypeTransition(masm);
+ Register left = r1;
+ Register right = r0;
+ Register scratch1 = r7;
+ Register scratch2 = r9;
+ DwVfpRegister double_scratch = d0;
+ SwVfpRegister single_scratch = s3;
+
+ Register heap_number_result = no_reg;
+ Register heap_number_map = r6;
+ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+
+ Label call_runtime;
+ // Labels for type transition, used for wrong input or output types.
+ // Both label are currently actually bound to the same position. We use two
+ // different label to differentiate the cause leading to type transition.
+ Label transition;
+
+ // Smi-smi fast case.
+ Label skip;
+ __ orr(scratch1, left, right);
+ __ JumpIfNotSmi(scratch1, &skip);
+ GenerateSmiSmiOperation(masm);
+ // Fall through if the result is not a smi.
+ __ bind(&skip);
+
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD: {
+ // Load both operands and check that they are 32-bit integer.
+ // Jump to type transition if they are not. The registers r0 and r1 (right
+ // and left) are preserved for the runtime call.
+ FloatingPointHelper::Destination destination =
+ CpuFeatures::IsSupported(VFP3) && op_ != Token::MOD ?
+ FloatingPointHelper::kVFPRegisters :
+ FloatingPointHelper::kCoreRegisters;
+
+ FloatingPointHelper::LoadNumberAsInt32Double(masm,
+ right,
+ destination,
+ d7,
+ r2,
+ r3,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ s0,
+ &transition);
+ FloatingPointHelper::LoadNumberAsInt32Double(masm,
+ left,
+ destination,
+ d6,
+ r4,
+ r5,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ s0,
+ &transition);
+
+ if (destination == FloatingPointHelper::kVFPRegisters) {
+ CpuFeatures::Scope scope(VFP3);
+ Label return_heap_number;
+ switch (op_) {
+ case Token::ADD:
+ __ vadd(d5, d6, d7);
+ break;
+ case Token::SUB:
+ __ vsub(d5, d6, d7);
+ break;
+ case Token::MUL:
+ __ vmul(d5, d6, d7);
+ break;
+ case Token::DIV:
+ __ vdiv(d5, d6, d7);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ if (op_ != Token::DIV) {
+ // These operations produce an integer result.
+ // Try to return a smi if we can.
+ // Otherwise return a heap number if allowed, or jump to type
+ // transition.
+
+ __ EmitVFPTruncate(kRoundToZero,
+ single_scratch,
+ d5,
+ scratch1,
+ scratch2);
+
+ if (result_type_ <= TRBinaryOpIC::INT32) {
+ // If the ne condition is set, result does
+ // not fit in a 32-bit integer.
+ __ b(ne, &transition);
+ }
+
+ // Check if the result fits in a smi.
+ __ vmov(scratch1, single_scratch);
+ __ add(scratch2, scratch1, Operand(0x40000000), SetCC);
+ // If not try to return a heap number.
+ __ b(mi, &return_heap_number);
+ // Tag the result and return.
+ __ SmiTag(r0, scratch1);
+ __ Ret();
+ }
+
+ if (result_type_ >= (op_ == Token::DIV) ? TRBinaryOpIC::HEAP_NUMBER
+ : TRBinaryOpIC::INT32) {
+ __ bind(&return_heap_number);
+ // We are using vfp registers so r5 is available.
+ heap_number_result = r5;
+ GenerateHeapResultAllocation(masm,
+ heap_number_result,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ &call_runtime);
+ __ sub(r0, heap_number_result, Operand(kHeapObjectTag));
+ __ vstr(d5, r0, HeapNumber::kValueOffset);
+ __ mov(r0, heap_number_result);
+ __ Ret();
+ }
+
+ // A DIV operation expecting an integer result falls through
+ // to type transition.
+
+ } else {
+ // We preserved r0 and r1 to be able to call runtime.
+ // Save the left value on the stack.
+ __ Push(r5, r4);
+
+ // Allocate a heap number to store the result.
+ heap_number_result = r5;
+ GenerateHeapResultAllocation(masm,
+ heap_number_result,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ &call_runtime);
+
+ // Load the left value from the value saved on the stack.
+ __ Pop(r1, r0);
+
+ // Call the C function to handle the double operation.
+ FloatingPointHelper::CallCCodeForDoubleOperation(
+ masm, op_, heap_number_result, scratch1);
+ }
+
+ break;
+ }
+
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ case Token::SAR:
+ case Token::SHR:
+ case Token::SHL: {
+ Label return_heap_number;
+ Register scratch3 = r5;
+ // Convert operands to 32-bit integers. Right in r2 and left in r3. The
+ // registers r0 and r1 (right and left) are preserved for the runtime
+ // call.
+ FloatingPointHelper::LoadNumberAsInt32(masm,
+ left,
+ r3,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ d0,
+ &transition);
+ FloatingPointHelper::LoadNumberAsInt32(masm,
+ right,
+ r2,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ d0,
+ &transition);
+
+ // The ECMA-262 standard specifies that, for shift operations, only the
+ // 5 least significant bits of the shift value should be used.
+ switch (op_) {
+ case Token::BIT_OR:
+ __ orr(r2, r3, Operand(r2));
+ break;
+ case Token::BIT_XOR:
+ __ eor(r2, r3, Operand(r2));
+ break;
+ case Token::BIT_AND:
+ __ and_(r2, r3, Operand(r2));
+ break;
+ case Token::SAR:
+ __ and_(r2, r2, Operand(0x1f));
+ __ mov(r2, Operand(r3, ASR, r2));
+ break;
+ case Token::SHR:
+ __ and_(r2, r2, Operand(0x1f));
+ __ mov(r2, Operand(r3, LSR, r2), SetCC);
+ // SHR is special because it is required to produce a positive answer.
+ // We only get a negative result if the shift value (r2) is 0.
+ // This result cannot be respresented as a signed 32-bit integer, try
+ // to return a heap number if we can.
+ // The non vfp3 code does not support this special case, so jump to
+ // runtime if we don't support it.
+ if (CpuFeatures::IsSupported(VFP3)) {
+ __ b(mi,
+ (result_type_ <= TRBinaryOpIC::INT32) ? &transition
+ : &return_heap_number);
+ } else {
+ __ b(mi, (result_type_ <= TRBinaryOpIC::INT32) ? &transition
+ : &call_runtime);
+ }
+ break;
+ case Token::SHL:
+ __ and_(r2, r2, Operand(0x1f));
+ __ mov(r2, Operand(r3, LSL, r2));
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ // Check if the result fits in a smi.
+ __ add(scratch1, r2, Operand(0x40000000), SetCC);
+ // If not try to return a heap number. (We know the result is an int32.)
+ __ b(mi, &return_heap_number);
+ // Tag the result and return.
+ __ SmiTag(r0, r2);
+ __ Ret();
+
+ __ bind(&return_heap_number);
+ if (CpuFeatures::IsSupported(VFP3)) {
+ CpuFeatures::Scope scope(VFP3);
+ heap_number_result = r5;
+ GenerateHeapResultAllocation(masm,
+ heap_number_result,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ &call_runtime);
+
+ if (op_ != Token::SHR) {
+ // Convert the result to a floating point value.
+ __ vmov(double_scratch.low(), r2);
+ __ vcvt_f64_s32(double_scratch, double_scratch.low());
+ } else {
+ // The result must be interpreted as an unsigned 32-bit integer.
+ __ vmov(double_scratch.low(), r2);
+ __ vcvt_f64_u32(double_scratch, double_scratch.low());
+ }
+
+ // Store the result.
+ __ sub(r0, heap_number_result, Operand(kHeapObjectTag));
+ __ vstr(double_scratch, r0, HeapNumber::kValueOffset);
+ __ mov(r0, heap_number_result);
+ __ Ret();
+ } else {
+ // Tail call that writes the int32 in r2 to the heap number in r0, using
+ // r3 as scratch. r0 is preserved and returned.
+ WriteInt32ToHeapNumberStub stub(r2, r0, r3);
+ __ TailCallStub(&stub);
+ }
+
+ break;
+ }
+
+ default:
+ UNREACHABLE();
+ }
+
+ if (transition.is_linked()) {
+ __ bind(&transition);
+ GenerateTypeTransition(masm);
+ }
+
+ __ bind(&call_runtime);
+ GenerateCallRuntime(masm);
}
void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
- Label not_numbers, call_runtime;
+ Label call_runtime;
ASSERT(operands_type_ == TRBinaryOpIC::HEAP_NUMBER);
- GenerateFPOperation(masm, false, &not_numbers, &call_runtime);
+ GenerateFPOperation(masm, false, &call_runtime, &call_runtime);
- __ bind(&not_numbers);
- GenerateTypeTransition(masm);
-
__ bind(&call_runtime);
GenerateCallRuntime(masm);
}
void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
- Label call_runtime;
+ Label call_runtime, call_string_add_or_runtime;
GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
- // If all else fails, use the runtime system to get the correct
- // result.
- __ bind(&call_runtime);
+ GenerateFPOperation(masm, false, &call_string_add_or_runtime, &call_runtime);
- // Try to add strings before calling runtime.
+ __ bind(&call_string_add_or_runtime);
if (op_ == Token::ADD) {
GenerateAddStrings(masm);
}
- GenericBinaryOpStub stub(op_, mode_, r1, r0);
- __ TailCallStub(&stub);
+ __ bind(&call_runtime);
+ GenerateCallRuntime(masm);
}
void TypeRecordingBinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
ASSERT(op_ == Token::ADD);
+ Label left_not_string, call_runtime;
Register left = r1;
Register right = r0;
- Label call_runtime;
- // Check if first argument is a string.
- __ JumpIfSmi(left, &call_runtime);
+ // Check if left argument is a string.
+ __ JumpIfSmi(left, &left_not_string);
__ CompareObjectType(left, r2, r2, FIRST_NONSTRING_TYPE);
- __ b(ge, &call_runtime);
+ __ b(ge, &left_not_string);
- // First argument is a a string, test second.
+ StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB);
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_left_stub);
+
+ // Left operand is not a string, test right.
+ __ bind(&left_not_string);
__ JumpIfSmi(right, &call_runtime);
__ CompareObjectType(right, r2, r2, FIRST_NONSTRING_TYPE);
__ b(ge, &call_runtime);
- // First and second argument are strings.
- StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
+ StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB);
GenerateRegisterArgsPush(masm);
- __ TailCallStub(&string_add_stub);
+ __ TailCallStub(&string_add_right_stub);
// At least one argument is not a string.
__ bind(&call_runtime);
@@ -3061,32 +3732,47 @@
void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
- // Argument is a number and is on stack and in r0.
- Label runtime_call;
+ // Untagged case: double input in d2, double result goes
+ // into d2.
+ // Tagged case: tagged input on top of stack and in r0,
+ // tagged result (heap number) goes into r0.
+
Label input_not_smi;
Label loaded;
+ Label calculate;
+ Label invalid_cache;
+ const Register scratch0 = r9;
+ const Register scratch1 = r7;
+ const Register cache_entry = r0;
+ const bool tagged = (argument_type_ == TAGGED);
if (CpuFeatures::IsSupported(VFP3)) {
- // Load argument and check if it is a smi.
- __ JumpIfNotSmi(r0, &input_not_smi);
-
CpuFeatures::Scope scope(VFP3);
- // Input is a smi. Convert to double and load the low and high words
- // of the double into r2, r3.
- __ IntegerToDoubleConversionWithVFP3(r0, r3, r2);
- __ b(&loaded);
+ if (tagged) {
+ // Argument is a number and is on stack and in r0.
+ // Load argument and check if it is a smi.
+ __ JumpIfNotSmi(r0, &input_not_smi);
- __ bind(&input_not_smi);
- // Check if input is a HeapNumber.
- __ CheckMap(r0,
- r1,
- Heap::kHeapNumberMapRootIndex,
- &runtime_call,
- true);
- // Input is a HeapNumber. Load it to a double register and store the
- // low and high words into r2, r3.
- __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
+ // Input is a smi. Convert to double and load the low and high words
+ // of the double into r2, r3.
+ __ IntegerToDoubleConversionWithVFP3(r0, r3, r2);
+ __ b(&loaded);
+ __ bind(&input_not_smi);
+ // Check if input is a HeapNumber.
+ __ CheckMap(r0,
+ r1,
+ Heap::kHeapNumberMapRootIndex,
+ &calculate,
+ true);
+ // Input is a HeapNumber. Load it to a double register and store the
+ // low and high words into r2, r3.
+ __ vldr(d0, FieldMemOperand(r0, HeapNumber::kValueOffset));
+ __ vmov(r2, r3, d0);
+ } else {
+ // Input is untagged double in d2. Output goes to d2.
+ __ vmov(r2, r3, d2);
+ }
__ bind(&loaded);
// r2 = low 32 bits of double value
// r3 = high 32 bits of double value
@@ -3101,14 +3787,15 @@
// r2 = low 32 bits of double value.
// r3 = high 32 bits of double value.
// r1 = TranscendentalCache::hash(double value).
- __ mov(r0,
+ __ mov(cache_entry,
Operand(ExternalReference::transcendental_cache_array_address()));
// r0 points to cache array.
- __ ldr(r0, MemOperand(r0, type_ * sizeof(TranscendentalCache::caches_[0])));
+ __ ldr(cache_entry, MemOperand(cache_entry,
+ type_ * sizeof(TranscendentalCache::caches_[0])));
// r0 points to the cache for the type type_.
// If NULL, the cache hasn't been initialized yet, so go through runtime.
- __ cmp(r0, Operand(0, RelocInfo::NONE));
- __ b(eq, &runtime_call);
+ __ cmp(cache_entry, Operand(0, RelocInfo::NONE));
+ __ b(eq, &invalid_cache);
#ifdef DEBUG
// Check that the layout of cache elements match expectations.
@@ -3127,21 +3814,109 @@
// Find the address of the r1'st entry in the cache, i.e., &r0[r1*12].
__ add(r1, r1, Operand(r1, LSL, 1));
- __ add(r0, r0, Operand(r1, LSL, 2));
+ __ add(cache_entry, cache_entry, Operand(r1, LSL, 2));
// Check if cache matches: Double value is stored in uint32_t[2] array.
- __ ldm(ia, r0, r4.bit()| r5.bit() | r6.bit());
+ __ ldm(ia, cache_entry, r4.bit() | r5.bit() | r6.bit());
__ cmp(r2, r4);
- __ b(ne, &runtime_call);
+ __ b(ne, &calculate);
__ cmp(r3, r5);
- __ b(ne, &runtime_call);
- // Cache hit. Load result, pop argument and return.
- __ mov(r0, Operand(r6));
- __ pop();
+ __ b(ne, &calculate);
+ // Cache hit. Load result, cleanup and return.
+ if (tagged) {
+ // Pop input value from stack and load result into r0.
+ __ pop();
+ __ mov(r0, Operand(r6));
+ } else {
+ // Load result into d2.
+ __ vldr(d2, FieldMemOperand(r6, HeapNumber::kValueOffset));
+ }
__ Ret();
+ } // if (CpuFeatures::IsSupported(VFP3))
+
+ __ bind(&calculate);
+ if (tagged) {
+ __ bind(&invalid_cache);
+ __ TailCallExternalReference(ExternalReference(RuntimeFunction()), 1, 1);
+ } else {
+ if (!CpuFeatures::IsSupported(VFP3)) UNREACHABLE();
+ CpuFeatures::Scope scope(VFP3);
+
+ Label no_update;
+ Label skip_cache;
+ const Register heap_number_map = r5;
+
+ // Call C function to calculate the result and update the cache.
+ // Register r0 holds precalculated cache entry address; preserve
+ // it on the stack and pop it into register cache_entry after the
+ // call.
+ __ push(cache_entry);
+ GenerateCallCFunction(masm, scratch0);
+ __ GetCFunctionDoubleResult(d2);
+
+ // Try to update the cache. If we cannot allocate a
+ // heap number, we return the result without updating.
+ __ pop(cache_entry);
+ __ LoadRoot(r5, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(r6, scratch0, scratch1, r5, &no_update);
+ __ vstr(d2, FieldMemOperand(r6, HeapNumber::kValueOffset));
+ __ stm(ia, cache_entry, r2.bit() | r3.bit() | r6.bit());
+ __ Ret();
+
+ __ bind(&invalid_cache);
+ // The cache is invalid. Call runtime which will recreate the
+ // cache.
+ __ LoadRoot(r5, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(r0, scratch0, scratch1, r5, &skip_cache);
+ __ vstr(d2, FieldMemOperand(r0, HeapNumber::kValueOffset));
+ __ EnterInternalFrame();
+ __ push(r0);
+ __ CallRuntime(RuntimeFunction(), 1);
+ __ LeaveInternalFrame();
+ __ vldr(d2, FieldMemOperand(r0, HeapNumber::kValueOffset));
+ __ Ret();
+
+ __ bind(&skip_cache);
+ // Call C function to calculate the result and answer directly
+ // without updating the cache.
+ GenerateCallCFunction(masm, scratch0);
+ __ GetCFunctionDoubleResult(d2);
+ __ bind(&no_update);
+
+ // We return the value in d2 without adding it to the cache, but
+ // we cause a scavenging GC so that future allocations will succeed.
+ __ EnterInternalFrame();
+
+ // Allocate an aligned object larger than a HeapNumber.
+ ASSERT(4 * kPointerSize >= HeapNumber::kSize);
+ __ mov(scratch0, Operand(4 * kPointerSize));
+ __ push(scratch0);
+ __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace);
+ __ LeaveInternalFrame();
+ __ Ret();
}
+}
- __ bind(&runtime_call);
- __ TailCallExternalReference(ExternalReference(RuntimeFunction()), 1, 1);
+
+void TranscendentalCacheStub::GenerateCallCFunction(MacroAssembler* masm,
+ Register scratch) {
+ __ push(lr);
+ __ PrepareCallCFunction(2, scratch);
+ __ vmov(r0, r1, d2);
+ switch (type_) {
+ case TranscendentalCache::SIN:
+ __ CallCFunction(ExternalReference::math_sin_double_function(), 2);
+ break;
+ case TranscendentalCache::COS:
+ __ CallCFunction(ExternalReference::math_cos_double_function(), 2);
+ break;
+ case TranscendentalCache::LOG:
+ __ CallCFunction(ExternalReference::math_log_double_function(), 2);
+ break;
+ default:
+ UNIMPLEMENTED();
+ break;
+ }
+ __ pop(lr);
}
@@ -3298,6 +4073,111 @@
}
+void MathPowStub::Generate(MacroAssembler* masm) {
+ Label call_runtime;
+
+ if (CpuFeatures::IsSupported(VFP3)) {
+ CpuFeatures::Scope scope(VFP3);
+
+ Label base_not_smi;
+ Label exponent_not_smi;
+ Label convert_exponent;
+
+ const Register base = r0;
+ const Register exponent = r1;
+ const Register heapnumbermap = r5;
+ const Register heapnumber = r6;
+ const DoubleRegister double_base = d0;
+ const DoubleRegister double_exponent = d1;
+ const DoubleRegister double_result = d2;
+ const SwVfpRegister single_scratch = s0;
+ const Register scratch = r9;
+ const Register scratch2 = r7;
+
+ __ LoadRoot(heapnumbermap, Heap::kHeapNumberMapRootIndex);
+ __ ldr(base, MemOperand(sp, 1 * kPointerSize));
+ __ ldr(exponent, MemOperand(sp, 0 * kPointerSize));
+
+ // Convert base to double value and store it in d0.
+ __ JumpIfNotSmi(base, &base_not_smi);
+ // Base is a Smi. Untag and convert it.
+ __ SmiUntag(base);
+ __ vmov(single_scratch, base);
+ __ vcvt_f64_s32(double_base, single_scratch);
+ __ b(&convert_exponent);
+
+ __ bind(&base_not_smi);
+ __ ldr(scratch, FieldMemOperand(base, JSObject::kMapOffset));
+ __ cmp(scratch, heapnumbermap);
+ __ b(ne, &call_runtime);
+ // Base is a heapnumber. Load it into double register.
+ __ vldr(double_base, FieldMemOperand(base, HeapNumber::kValueOffset));
+
+ __ bind(&convert_exponent);
+ __ JumpIfNotSmi(exponent, &exponent_not_smi);
+ __ SmiUntag(exponent);
+
+ // The base is in a double register and the exponent is
+ // an untagged smi. Allocate a heap number and call a
+ // C function for integer exponents. The register containing
+ // the heap number is callee-saved.
+ __ AllocateHeapNumber(heapnumber,
+ scratch,
+ scratch2,
+ heapnumbermap,
+ &call_runtime);
+ __ push(lr);
+ __ PrepareCallCFunction(3, scratch);
+ __ mov(r2, exponent);
+ __ vmov(r0, r1, double_base);
+ __ CallCFunction(ExternalReference::power_double_int_function(), 3);
+ __ pop(lr);
+ __ GetCFunctionDoubleResult(double_result);
+ __ vstr(double_result,
+ FieldMemOperand(heapnumber, HeapNumber::kValueOffset));
+ __ mov(r0, heapnumber);
+ __ Ret(2 * kPointerSize);
+
+ __ bind(&exponent_not_smi);
+ __ ldr(scratch, FieldMemOperand(exponent, JSObject::kMapOffset));
+ __ cmp(scratch, heapnumbermap);
+ __ b(ne, &call_runtime);
+ // Exponent is a heapnumber. Load it into double register.
+ __ vldr(double_exponent,
+ FieldMemOperand(exponent, HeapNumber::kValueOffset));
+
+ // The base and the exponent are in double registers.
+ // Allocate a heap number and call a C function for
+ // double exponents. The register containing
+ // the heap number is callee-saved.
+ __ AllocateHeapNumber(heapnumber,
+ scratch,
+ scratch2,
+ heapnumbermap,
+ &call_runtime);
+ __ push(lr);
+ __ PrepareCallCFunction(4, scratch);
+ __ vmov(r0, r1, double_base);
+ __ vmov(r2, r3, double_exponent);
+ __ CallCFunction(ExternalReference::power_double_double_function(), 4);
+ __ pop(lr);
+ __ GetCFunctionDoubleResult(double_result);
+ __ vstr(double_result,
+ FieldMemOperand(heapnumber, HeapNumber::kValueOffset));
+ __ mov(r0, heapnumber);
+ __ Ret(2 * kPointerSize);
+ }
+
+ __ bind(&call_runtime);
+ __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
+}
+
+
+bool CEntryStub::NeedsImmovableCode() {
+ return true;
+}
+
+
void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
__ Throw(r0);
}
@@ -3706,7 +4586,7 @@
// The offset was stored in r4 safepoint slot.
// (See LCodeGen::DoDeferredLInstanceOfKnownGlobal)
- __ ldr(scratch, MacroAssembler::SafepointRegisterSlot(r4));
+ __ LoadFromSafepointRegisterSlot(scratch, r4);
__ sub(inline_site, lr, scratch);
// Get the map location in scratch and patch it.
__ GetRelocatedValueLocation(inline_site, scratch);
@@ -4155,7 +5035,7 @@
__ CompareObjectType(r7, r0, r0, CODE_TYPE);
__ b(ne, &runtime);
- // r3: encoding of subject string (1 if ascii, 0 if two_byte);
+ // r3: encoding of subject string (1 if ASCII, 0 if two_byte);
// r7: code
// subject: Subject string
// regexp_data: RegExp data (FixedArray)
@@ -4165,7 +5045,7 @@
__ mov(r1, Operand(r1, ASR, kSmiTagSize));
// r1: previous index
- // r3: encoding of subject string (1 if ascii, 0 if two_byte);
+ // r3: encoding of subject string (1 if ASCII, 0 if two_byte);
// r7: code
// subject: Subject string
// regexp_data: RegExp data (FixedArray)
@@ -4701,7 +5581,7 @@
__ b(ne, &slow_case_);
__ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
- // At this point code register contains smi tagged ascii char code.
+ // At this point code register contains smi tagged ASCII char code.
STATIC_ASSERT(kSmiTag == 0);
__ add(result_, result_, Operand(code_, LSL, kPointerSizeLog2 - kSmiTagSize));
__ ldr(result_, FieldMemOperand(result_, FixedArray::kHeaderSize));
@@ -5033,7 +5913,6 @@
Register symbol_table = c2;
__ LoadRoot(symbol_table, Heap::kSymbolTableRootIndex);
- // Load undefined value
Register undefined = scratch4;
__ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
@@ -5054,6 +5933,7 @@
// mask: capacity mask
// first_symbol_table_element: address of the first element of
// the symbol table
+ // undefined: the undefined object
// scratch: -
// Perform a number of probes in the symbol table.
@@ -5081,20 +5961,32 @@
kPointerSizeLog2));
// If entry is undefined no string with this hash can be found.
- __ cmp(candidate, undefined);
+ Label is_string;
+ __ CompareObjectType(candidate, scratch, scratch, ODDBALL_TYPE);
+ __ b(ne, &is_string);
+
+ __ cmp(undefined, candidate);
__ b(eq, not_found);
+ // Must be null (deleted entry).
+ if (FLAG_debug_code) {
+ __ LoadRoot(ip, Heap::kNullValueRootIndex);
+ __ cmp(ip, candidate);
+ __ Assert(eq, "oddball in symbol table is not undefined or null");
+ }
+ __ jmp(&next_probe[i]);
+ __ bind(&is_string);
+
+ // Check that the candidate is a non-external ASCII string. The instance
+ // type is still in the scratch register from the CompareObjectType
+ // operation.
+ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch, &next_probe[i]);
+
// If length is not 2 the string is not a candidate.
__ ldr(scratch, FieldMemOperand(candidate, String::kLengthOffset));
__ cmp(scratch, Operand(Smi::FromInt(2)));
__ b(ne, &next_probe[i]);
- // Check that the candidate is a non-external ascii string.
- __ ldr(scratch, FieldMemOperand(candidate, HeapObject::kMapOffset));
- __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
- __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch,
- &next_probe[i]);
-
// Check if the two characters match.
// Assumes that word load is little endian.
__ ldrh(scratch, FieldMemOperand(candidate, SeqAsciiString::kHeaderSize));
@@ -5250,7 +6142,7 @@
// r3: from index (untaged smi)
// r5: string.
// r7 (a.k.a. from): from offset (smi)
- // Check for flat ascii string.
+ // Check for flat ASCII string.
Label non_ascii_flat;
__ tst(r1, Operand(kStringEncodingMask));
STATIC_ASSERT(kTwoByteStringTag == 0);
@@ -5426,10 +6318,10 @@
__ bind(&not_same);
- // Check that both objects are sequential ascii strings.
+ // Check that both objects are sequential ASCII strings.
__ JumpIfNotBothSequentialAsciiStrings(r1, r0, r2, r3, &runtime);
- // Compare flat ascii strings natively. Remove arguments from stack first.
+ // Compare flat ASCII strings natively. Remove arguments from stack first.
__ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
__ add(sp, sp, Operand(2 * kPointerSize));
GenerateCompareFlatAsciiStrings(masm, r1, r0, r2, r3, r4, r5);
@@ -5442,18 +6334,19 @@
void StringAddStub::Generate(MacroAssembler* masm) {
- Label string_add_runtime;
+ Label string_add_runtime, call_builtin;
+ Builtins::JavaScript builtin_id = Builtins::ADD;
+
// Stack on entry:
- // sp[0]: second argument.
- // sp[4]: first argument.
+ // sp[0]: second argument (right).
+ // sp[4]: first argument (left).
// Load the two arguments.
__ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // First argument.
__ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // Second argument.
// Make sure that both arguments are strings if not known in advance.
- if (string_check_) {
- STATIC_ASSERT(kSmiTag == 0);
+ if (flags_ == NO_STRING_ADD_FLAGS) {
__ JumpIfEitherSmi(r0, r1, &string_add_runtime);
// Load instance types.
__ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
@@ -5465,13 +6358,27 @@
__ tst(r4, Operand(kIsNotStringMask));
__ tst(r5, Operand(kIsNotStringMask), eq);
__ b(ne, &string_add_runtime);
+ } else {
+ // Here at least one of the arguments is definitely a string.
+ // We convert the one that is not known to be a string.
+ if ((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) == 0) {
+ ASSERT((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) != 0);
+ GenerateConvertArgument(
+ masm, 1 * kPointerSize, r0, r2, r3, r4, r5, &call_builtin);
+ builtin_id = Builtins::STRING_ADD_RIGHT;
+ } else if ((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) == 0) {
+ ASSERT((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) != 0);
+ GenerateConvertArgument(
+ masm, 0 * kPointerSize, r1, r2, r3, r4, r5, &call_builtin);
+ builtin_id = Builtins::STRING_ADD_LEFT;
+ }
}
// Both arguments are strings.
// r0: first string
// r1: second string
- // r4: first string instance type (if string_check_)
- // r5: second string instance type (if string_check_)
+ // r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
{
Label strings_not_empty;
// Check if either of the strings are empty. In that case return the other.
@@ -5499,20 +6406,20 @@
// r1: second string
// r2: length of first string
// r3: length of second string
- // r4: first string instance type (if string_check_)
- // r5: second string instance type (if string_check_)
+ // r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
// Look at the length of the result of adding the two strings.
Label string_add_flat_result, longer_than_two;
// Adding two lengths can't overflow.
STATIC_ASSERT(String::kMaxLength < String::kMaxLength * 2);
__ add(r6, r2, Operand(r3));
- // Use the runtime system when adding two one character strings, as it
- // contains optimizations for this specific case using the symbol table.
+ // Use the symbol table when adding two one character strings, as it
+ // helps later optimizations to return a symbol here.
__ cmp(r6, Operand(2));
__ b(ne, &longer_than_two);
- // Check that both strings are non-external ascii strings.
- if (!string_check_) {
+ // Check that both strings are non-external ASCII strings.
+ if (flags_ != NO_STRING_ADD_FLAGS) {
__ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
__ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
__ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
@@ -5559,8 +6466,8 @@
__ b(hs, &string_add_runtime);
// If result is not supposed to be flat, allocate a cons string object.
- // If both strings are ascii the result is an ascii cons string.
- if (!string_check_) {
+ // If both strings are ASCII the result is an ASCII cons string.
+ if (flags_ != NO_STRING_ADD_FLAGS) {
__ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
__ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
__ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
@@ -5586,7 +6493,7 @@
__ bind(&non_ascii);
// At least one of the strings is two-byte. Check whether it happens
- // to contain only ascii characters.
+ // to contain only ASCII characters.
// r4: first instance type.
// r5: second instance type.
__ tst(r4, Operand(kAsciiDataHintMask));
@@ -5608,11 +6515,11 @@
// r1: second string
// r2: length of first string
// r3: length of second string
- // r4: first string instance type (if string_check_)
- // r5: second string instance type (if string_check_)
+ // r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
// r6: sum of lengths.
__ bind(&string_add_flat_result);
- if (!string_check_) {
+ if (flags_ != NO_STRING_ADD_FLAGS) {
__ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
__ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
__ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
@@ -5710,56 +6617,60 @@
// Just jump to runtime to add the two strings.
__ bind(&string_add_runtime);
__ TailCallRuntime(Runtime::kStringAdd, 2, 1);
+
+ if (call_builtin.is_linked()) {
+ __ bind(&call_builtin);
+ __ InvokeBuiltin(builtin_id, JUMP_JS);
+ }
}
-void StringCharAtStub::Generate(MacroAssembler* masm) {
- // Expects two arguments (object, index) on the stack:
- // lr: return address
- // sp[0]: index
- // sp[4]: object
- Register object = r1;
- Register index = r0;
- Register scratch1 = r2;
- Register scratch2 = r3;
- Register result = r0;
+void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
+ int stack_offset,
+ Register arg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Label* slow) {
+ // First check if the argument is already a string.
+ Label not_string, done;
+ __ JumpIfSmi(arg, &not_string);
+ __ CompareObjectType(arg, scratch1, scratch1, FIRST_NONSTRING_TYPE);
+ __ b(lt, &done);
- // Get object and index from the stack.
- __ pop(index);
- __ pop(object);
-
- Label need_conversion;
- Label index_out_of_range;
- Label done;
- StringCharAtGenerator generator(object,
- index,
- scratch1,
- scratch2,
- result,
- &need_conversion,
- &need_conversion,
- &index_out_of_range,
- STRING_INDEX_IS_NUMBER);
- generator.GenerateFast(masm);
- __ b(&done);
-
- __ bind(&index_out_of_range);
- // When the index is out of range, the spec requires us to return
- // the empty string.
- __ LoadRoot(result, Heap::kEmptyStringRootIndex);
+ // Check the number to string cache.
+ Label not_cached;
+ __ bind(&not_string);
+ // Puts the cached result into scratch1.
+ NumberToStringStub::GenerateLookupNumberStringCache(masm,
+ arg,
+ scratch1,
+ scratch2,
+ scratch3,
+ scratch4,
+ false,
+ &not_cached);
+ __ mov(arg, scratch1);
+ __ str(arg, MemOperand(sp, stack_offset));
__ jmp(&done);
- __ bind(&need_conversion);
- // Move smi zero into the result register, which will trigger
- // conversion.
- __ mov(result, Operand(Smi::FromInt(0)));
- __ b(&done);
+ // Check if the argument is a safe string wrapper.
+ __ bind(&not_cached);
+ __ JumpIfSmi(arg, slow);
+ __ CompareObjectType(
+ arg, scratch1, scratch2, JS_VALUE_TYPE); // map -> scratch1.
+ __ b(ne, slow);
+ __ ldrb(scratch2, FieldMemOperand(scratch1, Map::kBitField2Offset));
+ __ and_(scratch2,
+ scratch2, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ cmp(scratch2,
+ Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ b(ne, slow);
+ __ ldr(arg, FieldMemOperand(arg, JSValue::kValueOffset));
+ __ str(arg, MemOperand(sp, stack_offset));
- StubRuntimeCallHelper call_helper;
- generator.GenerateSlow(masm, call_helper);
-
__ bind(&done);
- __ Ret();
}
@@ -5884,11 +6795,10 @@
void DirectCEntryStub::GenerateCall(MacroAssembler* masm,
- ApiFunction *function) {
+ ExternalReference function) {
__ mov(lr, Operand(reinterpret_cast<intptr_t>(GetCode().location()),
RelocInfo::CODE_TARGET));
- __ mov(r2,
- Operand(ExternalReference(function, ExternalReference::DIRECT_CALL)));
+ __ mov(r2, Operand(function));
// Push return address (accessible to GC through exit frame pc).
__ str(pc, MemOperand(sp, 0));
__ Jump(r2); // Call the api function.
@@ -5905,158 +6815,6 @@
}
-void GenerateFastPixelArrayLoad(MacroAssembler* masm,
- Register receiver,
- Register key,
- Register elements_map,
- Register elements,
- Register scratch1,
- Register scratch2,
- Register result,
- Label* not_pixel_array,
- Label* key_not_smi,
- Label* out_of_range) {
- // Register use:
- //
- // receiver - holds the receiver on entry.
- // Unchanged unless 'result' is the same register.
- //
- // key - holds the smi key on entry.
- // Unchanged unless 'result' is the same register.
- //
- // elements - set to be the receiver's elements on exit.
- //
- // elements_map - set to be the map of the receiver's elements
- // on exit.
- //
- // result - holds the result of the pixel array load on exit,
- // tagged as a smi if successful.
- //
- // Scratch registers:
- //
- // scratch1 - used a scratch register in map check, if map
- // check is successful, contains the length of the
- // pixel array, the pointer to external elements and
- // the untagged result.
- //
- // scratch2 - holds the untaged key.
-
- // Some callers already have verified that the key is a smi. key_not_smi is
- // set to NULL as a sentinel for that case. Otherwise, add an explicit check
- // to ensure the key is a smi must be added.
- if (key_not_smi != NULL) {
- __ JumpIfNotSmi(key, key_not_smi);
- } else {
- if (FLAG_debug_code) {
- __ AbortIfNotSmi(key);
- }
- }
- __ SmiUntag(scratch2, key);
-
- // Verify that the receiver has pixel array elements.
- __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
- __ CheckMap(elements, scratch1, Heap::kPixelArrayMapRootIndex,
- not_pixel_array, true);
-
- // Key must be in range of the pixel array.
- __ ldr(scratch1, FieldMemOperand(elements, PixelArray::kLengthOffset));
- __ cmp(scratch2, scratch1);
- __ b(hs, out_of_range); // unsigned check handles negative keys.
-
- // Perform the indexed load and tag the result as a smi.
- __ ldr(scratch1,
- FieldMemOperand(elements, PixelArray::kExternalPointerOffset));
- __ ldrb(scratch1, MemOperand(scratch1, scratch2));
- __ SmiTag(r0, scratch1);
- __ Ret();
-}
-
-
-void GenerateFastPixelArrayStore(MacroAssembler* masm,
- Register receiver,
- Register key,
- Register value,
- Register elements,
- Register elements_map,
- Register scratch1,
- Register scratch2,
- bool load_elements_from_receiver,
- bool load_elements_map_from_elements,
- Label* key_not_smi,
- Label* value_not_smi,
- Label* not_pixel_array,
- Label* out_of_range) {
- // Register use:
- // receiver - holds the receiver and is unchanged unless the
- // store succeeds.
- // key - holds the key (must be a smi) and is unchanged.
- // value - holds the value (must be a smi) and is unchanged.
- // elements - holds the element object of the receiver on entry if
- // load_elements_from_receiver is false, otherwise used
- // internally to store the pixel arrays elements and
- // external array pointer.
- // elements_map - holds the map of the element object if
- // load_elements_map_from_elements is false, otherwise
- // loaded with the element map.
- //
- Register external_pointer = elements;
- Register untagged_key = scratch1;
- Register untagged_value = scratch2;
-
- if (load_elements_from_receiver) {
- __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
- }
-
- // By passing NULL as not_pixel_array, callers signal that they have already
- // verified that the receiver has pixel array elements.
- if (not_pixel_array != NULL) {
- if (load_elements_map_from_elements) {
- __ ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
- }
- __ LoadRoot(ip, Heap::kPixelArrayMapRootIndex);
- __ cmp(elements_map, ip);
- __ b(ne, not_pixel_array);
- } else {
- if (FLAG_debug_code) {
- // Map check should have already made sure that elements is a pixel array.
- __ ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
- __ LoadRoot(ip, Heap::kPixelArrayMapRootIndex);
- __ cmp(elements_map, ip);
- __ Assert(eq, "Elements isn't a pixel array");
- }
- }
-
- // Some callers already have verified that the key is a smi. key_not_smi is
- // set to NULL as a sentinel for that case. Otherwise, add an explicit check
- // to ensure the key is a smi must be added.
- if (key_not_smi != NULL) {
- __ JumpIfNotSmi(key, key_not_smi);
- } else {
- if (FLAG_debug_code) {
- __ AbortIfNotSmi(key);
- }
- }
-
- __ SmiUntag(untagged_key, key);
-
- // Perform bounds check.
- __ ldr(scratch2, FieldMemOperand(elements, PixelArray::kLengthOffset));
- __ cmp(untagged_key, scratch2);
- __ b(hs, out_of_range); // unsigned check handles negative keys.
-
- __ JumpIfNotSmi(value, value_not_smi);
- __ SmiUntag(untagged_value, value);
-
- // Clamp the value to [0..255].
- __ Usat(untagged_value, 8, Operand(untagged_value));
- // Get the pointer to the external array. This clobbers elements.
- __ ldr(external_pointer,
- FieldMemOperand(elements, PixelArray::kExternalPointerOffset));
- __ strb(untagged_value, MemOperand(external_pointer, untagged_key));
- __ Ret();
-}
-
-
#undef __
} } // namespace v8::internal
« no previous file with comments | « src/arm/code-stubs-arm.h ('k') | src/arm/codegen-arm.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698