| 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,
|
| + ¬_in_int32_range);
|
| __ jmp(&done);
|
| +
|
| + __ bind(¬_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, ¬_numbers, &call_runtime);
|
| + GenerateFPOperation(masm, false, &call_runtime, &call_runtime);
|
|
|
| - __ bind(¬_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(¬_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, ¬_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(¬_string);
|
| + // Puts the cached result into scratch1.
|
| + NumberToStringStub::GenerateLookupNumberStringCache(masm,
|
| + arg,
|
| + scratch1,
|
| + scratch2,
|
| + scratch3,
|
| + scratch4,
|
| + false,
|
| + ¬_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(¬_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
|
|
|