Home | History | Annotate | Download | only in arm
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/v8.h"
      6 
      7 #if V8_TARGET_ARCH_ARM
      8 
      9 #include "src/arm/simulator-arm.h"
     10 #include "src/codegen.h"
     11 #include "src/macro-assembler.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 
     16 
     17 #define __ masm.
     18 
     19 
     20 #if defined(USE_SIMULATOR)
     21 byte* fast_exp_arm_machine_code = NULL;
     22 double fast_exp_simulator(double x) {
     23   return Simulator::current(Isolate::Current())->CallFPReturnsDouble(
     24       fast_exp_arm_machine_code, x, 0);
     25 }
     26 #endif
     27 
     28 
     29 UnaryMathFunction CreateExpFunction() {
     30   if (!FLAG_fast_math) return &std::exp;
     31   size_t actual_size;
     32   byte* buffer =
     33       static_cast<byte*>(base::OS::Allocate(1 * KB, &actual_size, true));
     34   if (buffer == NULL) return &std::exp;
     35   ExternalReference::InitializeMathExpData();
     36 
     37   MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
     38 
     39   {
     40     DwVfpRegister input = d0;
     41     DwVfpRegister result = d1;
     42     DwVfpRegister double_scratch1 = d2;
     43     DwVfpRegister double_scratch2 = d3;
     44     Register temp1 = r4;
     45     Register temp2 = r5;
     46     Register temp3 = r6;
     47 
     48     if (masm.use_eabi_hardfloat()) {
     49       // Input value is in d0 anyway, nothing to do.
     50     } else {
     51       __ vmov(input, r0, r1);
     52     }
     53     __ Push(temp3, temp2, temp1);
     54     MathExpGenerator::EmitMathExp(
     55         &masm, input, result, double_scratch1, double_scratch2,
     56         temp1, temp2, temp3);
     57     __ Pop(temp3, temp2, temp1);
     58     if (masm.use_eabi_hardfloat()) {
     59       __ vmov(d0, result);
     60     } else {
     61       __ vmov(r0, r1, result);
     62     }
     63     __ Ret();
     64   }
     65 
     66   CodeDesc desc;
     67   masm.GetCode(&desc);
     68   DCHECK(!RelocInfo::RequiresRelocation(desc));
     69 
     70   CpuFeatures::FlushICache(buffer, actual_size);
     71   base::OS::ProtectCode(buffer, actual_size);
     72 
     73 #if !defined(USE_SIMULATOR)
     74   return FUNCTION_CAST<UnaryMathFunction>(buffer);
     75 #else
     76   fast_exp_arm_machine_code = buffer;
     77   return &fast_exp_simulator;
     78 #endif
     79 }
     80 
     81 #if defined(V8_HOST_ARCH_ARM)
     82 MemCopyUint8Function CreateMemCopyUint8Function(MemCopyUint8Function stub) {
     83 #if defined(USE_SIMULATOR)
     84   return stub;
     85 #else
     86   if (!CpuFeatures::IsSupported(UNALIGNED_ACCESSES)) return stub;
     87   size_t actual_size;
     88   byte* buffer =
     89       static_cast<byte*>(base::OS::Allocate(1 * KB, &actual_size, true));
     90   if (buffer == NULL) return stub;
     91 
     92   MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
     93 
     94   Register dest = r0;
     95   Register src = r1;
     96   Register chars = r2;
     97   Register temp1 = r3;
     98   Label less_4;
     99 
    100   if (CpuFeatures::IsSupported(NEON)) {
    101     Label loop, less_256, less_128, less_64, less_32, _16_or_less, _8_or_less;
    102     Label size_less_than_8;
    103     __ pld(MemOperand(src, 0));
    104 
    105     __ cmp(chars, Operand(8));
    106     __ b(lt, &size_less_than_8);
    107     __ cmp(chars, Operand(32));
    108     __ b(lt, &less_32);
    109     if (CpuFeatures::cache_line_size() == 32) {
    110       __ pld(MemOperand(src, 32));
    111     }
    112     __ cmp(chars, Operand(64));
    113     __ b(lt, &less_64);
    114     __ pld(MemOperand(src, 64));
    115     if (CpuFeatures::cache_line_size() == 32) {
    116       __ pld(MemOperand(src, 96));
    117     }
    118     __ cmp(chars, Operand(128));
    119     __ b(lt, &less_128);
    120     __ pld(MemOperand(src, 128));
    121     if (CpuFeatures::cache_line_size() == 32) {
    122       __ pld(MemOperand(src, 160));
    123     }
    124     __ pld(MemOperand(src, 192));
    125     if (CpuFeatures::cache_line_size() == 32) {
    126       __ pld(MemOperand(src, 224));
    127     }
    128     __ cmp(chars, Operand(256));
    129     __ b(lt, &less_256);
    130     __ sub(chars, chars, Operand(256));
    131 
    132     __ bind(&loop);
    133     __ pld(MemOperand(src, 256));
    134     __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex));
    135     if (CpuFeatures::cache_line_size() == 32) {
    136       __ pld(MemOperand(src, 256));
    137     }
    138     __ vld1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(src, PostIndex));
    139     __ sub(chars, chars, Operand(64), SetCC);
    140     __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex));
    141     __ vst1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(dest, PostIndex));
    142     __ b(ge, &loop);
    143     __ add(chars, chars, Operand(256));
    144 
    145     __ bind(&less_256);
    146     __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex));
    147     __ vld1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(src, PostIndex));
    148     __ sub(chars, chars, Operand(128));
    149     __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex));
    150     __ vst1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(dest, PostIndex));
    151     __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex));
    152     __ vld1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(src, PostIndex));
    153     __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex));
    154     __ vst1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(dest, PostIndex));
    155     __ cmp(chars, Operand(64));
    156     __ b(lt, &less_64);
    157 
    158     __ bind(&less_128);
    159     __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex));
    160     __ vld1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(src, PostIndex));
    161     __ sub(chars, chars, Operand(64));
    162     __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex));
    163     __ vst1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(dest, PostIndex));
    164 
    165     __ bind(&less_64);
    166     __ cmp(chars, Operand(32));
    167     __ b(lt, &less_32);
    168     __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex));
    169     __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex));
    170     __ sub(chars, chars, Operand(32));
    171 
    172     __ bind(&less_32);
    173     __ cmp(chars, Operand(16));
    174     __ b(le, &_16_or_less);
    175     __ vld1(Neon8, NeonListOperand(d0, 2), NeonMemOperand(src, PostIndex));
    176     __ vst1(Neon8, NeonListOperand(d0, 2), NeonMemOperand(dest, PostIndex));
    177     __ sub(chars, chars, Operand(16));
    178 
    179     __ bind(&_16_or_less);
    180     __ cmp(chars, Operand(8));
    181     __ b(le, &_8_or_less);
    182     __ vld1(Neon8, NeonListOperand(d0), NeonMemOperand(src, PostIndex));
    183     __ vst1(Neon8, NeonListOperand(d0), NeonMemOperand(dest, PostIndex));
    184     __ sub(chars, chars, Operand(8));
    185 
    186     // Do a last copy which may overlap with the previous copy (up to 8 bytes).
    187     __ bind(&_8_or_less);
    188     __ rsb(chars, chars, Operand(8));
    189     __ sub(src, src, Operand(chars));
    190     __ sub(dest, dest, Operand(chars));
    191     __ vld1(Neon8, NeonListOperand(d0), NeonMemOperand(src));
    192     __ vst1(Neon8, NeonListOperand(d0), NeonMemOperand(dest));
    193 
    194     __ Ret();
    195 
    196     __ bind(&size_less_than_8);
    197 
    198     __ bic(temp1, chars, Operand(0x3), SetCC);
    199     __ b(&less_4, eq);
    200     __ ldr(temp1, MemOperand(src, 4, PostIndex));
    201     __ str(temp1, MemOperand(dest, 4, PostIndex));
    202   } else {
    203     Register temp2 = ip;
    204     Label loop;
    205 
    206     __ bic(temp2, chars, Operand(0x3), SetCC);
    207     __ b(&less_4, eq);
    208     __ add(temp2, dest, temp2);
    209 
    210     __ bind(&loop);
    211     __ ldr(temp1, MemOperand(src, 4, PostIndex));
    212     __ str(temp1, MemOperand(dest, 4, PostIndex));
    213     __ cmp(dest, temp2);
    214     __ b(&loop, ne);
    215   }
    216 
    217   __ bind(&less_4);
    218   __ mov(chars, Operand(chars, LSL, 31), SetCC);
    219   // bit0 => Z (ne), bit1 => C (cs)
    220   __ ldrh(temp1, MemOperand(src, 2, PostIndex), cs);
    221   __ strh(temp1, MemOperand(dest, 2, PostIndex), cs);
    222   __ ldrb(temp1, MemOperand(src), ne);
    223   __ strb(temp1, MemOperand(dest), ne);
    224   __ Ret();
    225 
    226   CodeDesc desc;
    227   masm.GetCode(&desc);
    228   DCHECK(!RelocInfo::RequiresRelocation(desc));
    229 
    230   CpuFeatures::FlushICache(buffer, actual_size);
    231   base::OS::ProtectCode(buffer, actual_size);
    232   return FUNCTION_CAST<MemCopyUint8Function>(buffer);
    233 #endif
    234 }
    235 
    236 
    237 // Convert 8 to 16. The number of character to copy must be at least 8.
    238 MemCopyUint16Uint8Function CreateMemCopyUint16Uint8Function(
    239     MemCopyUint16Uint8Function stub) {
    240 #if defined(USE_SIMULATOR)
    241   return stub;
    242 #else
    243   if (!CpuFeatures::IsSupported(UNALIGNED_ACCESSES)) return stub;
    244   size_t actual_size;
    245   byte* buffer =
    246       static_cast<byte*>(base::OS::Allocate(1 * KB, &actual_size, true));
    247   if (buffer == NULL) return stub;
    248 
    249   MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
    250 
    251   Register dest = r0;
    252   Register src = r1;
    253   Register chars = r2;
    254   if (CpuFeatures::IsSupported(NEON)) {
    255     Register temp = r3;
    256     Label loop;
    257 
    258     __ bic(temp, chars, Operand(0x7));
    259     __ sub(chars, chars, Operand(temp));
    260     __ add(temp, dest, Operand(temp, LSL, 1));
    261 
    262     __ bind(&loop);
    263     __ vld1(Neon8, NeonListOperand(d0), NeonMemOperand(src, PostIndex));
    264     __ vmovl(NeonU8, q0, d0);
    265     __ vst1(Neon16, NeonListOperand(d0, 2), NeonMemOperand(dest, PostIndex));
    266     __ cmp(dest, temp);
    267     __ b(&loop, ne);
    268 
    269     // Do a last copy which will overlap with the previous copy (1 to 8 bytes).
    270     __ rsb(chars, chars, Operand(8));
    271     __ sub(src, src, Operand(chars));
    272     __ sub(dest, dest, Operand(chars, LSL, 1));
    273     __ vld1(Neon8, NeonListOperand(d0), NeonMemOperand(src));
    274     __ vmovl(NeonU8, q0, d0);
    275     __ vst1(Neon16, NeonListOperand(d0, 2), NeonMemOperand(dest));
    276     __ Ret();
    277   } else {
    278     Register temp1 = r3;
    279     Register temp2 = ip;
    280     Register temp3 = lr;
    281     Register temp4 = r4;
    282     Label loop;
    283     Label not_two;
    284 
    285     __ Push(lr, r4);
    286     __ bic(temp2, chars, Operand(0x3));
    287     __ add(temp2, dest, Operand(temp2, LSL, 1));
    288 
    289     __ bind(&loop);
    290     __ ldr(temp1, MemOperand(src, 4, PostIndex));
    291     __ uxtb16(temp3, Operand(temp1, ROR, 0));
    292     __ uxtb16(temp4, Operand(temp1, ROR, 8));
    293     __ pkhbt(temp1, temp3, Operand(temp4, LSL, 16));
    294     __ str(temp1, MemOperand(dest));
    295     __ pkhtb(temp1, temp4, Operand(temp3, ASR, 16));
    296     __ str(temp1, MemOperand(dest, 4));
    297     __ add(dest, dest, Operand(8));
    298     __ cmp(dest, temp2);
    299     __ b(&loop, ne);
    300 
    301     __ mov(chars, Operand(chars, LSL, 31), SetCC);  // bit0 => ne, bit1 => cs
    302     __ b(&not_two, cc);
    303     __ ldrh(temp1, MemOperand(src, 2, PostIndex));
    304     __ uxtb(temp3, Operand(temp1, ROR, 8));
    305     __ mov(temp3, Operand(temp3, LSL, 16));
    306     __ uxtab(temp3, temp3, Operand(temp1, ROR, 0));
    307     __ str(temp3, MemOperand(dest, 4, PostIndex));
    308     __ bind(&not_two);
    309     __ ldrb(temp1, MemOperand(src), ne);
    310     __ strh(temp1, MemOperand(dest), ne);
    311     __ Pop(pc, r4);
    312   }
    313 
    314   CodeDesc desc;
    315   masm.GetCode(&desc);
    316 
    317   CpuFeatures::FlushICache(buffer, actual_size);
    318   base::OS::ProtectCode(buffer, actual_size);
    319 
    320   return FUNCTION_CAST<MemCopyUint16Uint8Function>(buffer);
    321 #endif
    322 }
    323 #endif
    324 
    325 UnaryMathFunction CreateSqrtFunction() {
    326 #if defined(USE_SIMULATOR)
    327   return &std::sqrt;
    328 #else
    329   size_t actual_size;
    330   byte* buffer =
    331       static_cast<byte*>(base::OS::Allocate(1 * KB, &actual_size, true));
    332   if (buffer == NULL) return &std::sqrt;
    333 
    334   MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
    335 
    336   __ MovFromFloatParameter(d0);
    337   __ vsqrt(d0, d0);
    338   __ MovToFloatResult(d0);
    339   __ Ret();
    340 
    341   CodeDesc desc;
    342   masm.GetCode(&desc);
    343   DCHECK(!RelocInfo::RequiresRelocation(desc));
    344 
    345   CpuFeatures::FlushICache(buffer, actual_size);
    346   base::OS::ProtectCode(buffer, actual_size);
    347   return FUNCTION_CAST<UnaryMathFunction>(buffer);
    348 #endif
    349 }
    350 
    351 #undef __
    352 
    353 
    354 // -------------------------------------------------------------------------
    355 // Platform-specific RuntimeCallHelper functions.
    356 
    357 void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
    358   masm->EnterFrame(StackFrame::INTERNAL);
    359   DCHECK(!masm->has_frame());
    360   masm->set_has_frame(true);
    361 }
    362 
    363 
    364 void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
    365   masm->LeaveFrame(StackFrame::INTERNAL);
    366   DCHECK(masm->has_frame());
    367   masm->set_has_frame(false);
    368 }
    369 
    370 
    371 // -------------------------------------------------------------------------
    372 // Code generators
    373 
    374 #define __ ACCESS_MASM(masm)
    375 
    376 void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
    377     MacroAssembler* masm,
    378     Register receiver,
    379     Register key,
    380     Register value,
    381     Register target_map,
    382     AllocationSiteMode mode,
    383     Label* allocation_memento_found) {
    384   Register scratch_elements = r4;
    385   DCHECK(!AreAliased(receiver, key, value, target_map,
    386                      scratch_elements));
    387 
    388   if (mode == TRACK_ALLOCATION_SITE) {
    389     DCHECK(allocation_memento_found != NULL);
    390     __ JumpIfJSArrayHasAllocationMemento(
    391         receiver, scratch_elements, allocation_memento_found);
    392   }
    393 
    394   // Set transitioned map.
    395   __ str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
    396   __ RecordWriteField(receiver,
    397                       HeapObject::kMapOffset,
    398                       target_map,
    399                       r9,
    400                       kLRHasNotBeenSaved,
    401                       kDontSaveFPRegs,
    402                       EMIT_REMEMBERED_SET,
    403                       OMIT_SMI_CHECK);
    404 }
    405 
    406 
    407 void ElementsTransitionGenerator::GenerateSmiToDouble(
    408     MacroAssembler* masm,
    409     Register receiver,
    410     Register key,
    411     Register value,
    412     Register target_map,
    413     AllocationSiteMode mode,
    414     Label* fail) {
    415   // Register lr contains the return address.
    416   Label loop, entry, convert_hole, gc_required, only_change_map, done;
    417   Register elements = r4;
    418   Register length = r5;
    419   Register array = r6;
    420   Register array_end = array;
    421 
    422   // target_map parameter can be clobbered.
    423   Register scratch1 = target_map;
    424   Register scratch2 = r9;
    425 
    426   // Verify input registers don't conflict with locals.
    427   DCHECK(!AreAliased(receiver, key, value, target_map,
    428                      elements, length, array, scratch2));
    429 
    430   if (mode == TRACK_ALLOCATION_SITE) {
    431     __ JumpIfJSArrayHasAllocationMemento(receiver, elements, fail);
    432   }
    433 
    434   // Check for empty arrays, which only require a map transition and no changes
    435   // to the backing store.
    436   __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
    437   __ CompareRoot(elements, Heap::kEmptyFixedArrayRootIndex);
    438   __ b(eq, &only_change_map);
    439 
    440   __ push(lr);
    441   __ ldr(length, FieldMemOperand(elements, FixedArray::kLengthOffset));
    442   // length: number of elements (smi-tagged)
    443 
    444   // Allocate new FixedDoubleArray.
    445   // Use lr as a temporary register.
    446   __ mov(lr, Operand(length, LSL, 2));
    447   __ add(lr, lr, Operand(FixedDoubleArray::kHeaderSize));
    448   __ Allocate(lr, array, elements, scratch2, &gc_required, DOUBLE_ALIGNMENT);
    449   // array: destination FixedDoubleArray, not tagged as heap object.
    450   __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
    451   // r4: source FixedArray.
    452 
    453   // Set destination FixedDoubleArray's length and map.
    454   __ LoadRoot(scratch2, Heap::kFixedDoubleArrayMapRootIndex);
    455   __ str(length, MemOperand(array, FixedDoubleArray::kLengthOffset));
    456   // Update receiver's map.
    457   __ str(scratch2, MemOperand(array, HeapObject::kMapOffset));
    458 
    459   __ str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
    460   __ RecordWriteField(receiver,
    461                       HeapObject::kMapOffset,
    462                       target_map,
    463                       scratch2,
    464                       kLRHasBeenSaved,
    465                       kDontSaveFPRegs,
    466                       OMIT_REMEMBERED_SET,
    467                       OMIT_SMI_CHECK);
    468   // Replace receiver's backing store with newly created FixedDoubleArray.
    469   __ add(scratch1, array, Operand(kHeapObjectTag));
    470   __ str(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
    471   __ RecordWriteField(receiver,
    472                       JSObject::kElementsOffset,
    473                       scratch1,
    474                       scratch2,
    475                       kLRHasBeenSaved,
    476                       kDontSaveFPRegs,
    477                       EMIT_REMEMBERED_SET,
    478                       OMIT_SMI_CHECK);
    479 
    480   // Prepare for conversion loop.
    481   __ add(scratch1, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
    482   __ add(scratch2, array, Operand(FixedDoubleArray::kHeaderSize));
    483   __ add(array_end, scratch2, Operand(length, LSL, 2));
    484 
    485   // Repurpose registers no longer in use.
    486   Register hole_lower = elements;
    487   Register hole_upper = length;
    488 
    489   __ mov(hole_lower, Operand(kHoleNanLower32));
    490   __ mov(hole_upper, Operand(kHoleNanUpper32));
    491   // scratch1: begin of source FixedArray element fields, not tagged
    492   // hole_lower: kHoleNanLower32
    493   // hole_upper: kHoleNanUpper32
    494   // array_end: end of destination FixedDoubleArray, not tagged
    495   // scratch2: begin of FixedDoubleArray element fields, not tagged
    496 
    497   __ b(&entry);
    498 
    499   __ bind(&only_change_map);
    500   __ str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
    501   __ RecordWriteField(receiver,
    502                       HeapObject::kMapOffset,
    503                       target_map,
    504                       scratch2,
    505                       kLRHasNotBeenSaved,
    506                       kDontSaveFPRegs,
    507                       OMIT_REMEMBERED_SET,
    508                       OMIT_SMI_CHECK);
    509   __ b(&done);
    510 
    511   // Call into runtime if GC is required.
    512   __ bind(&gc_required);
    513   __ pop(lr);
    514   __ b(fail);
    515 
    516   // Convert and copy elements.
    517   __ bind(&loop);
    518   __ ldr(lr, MemOperand(scratch1, 4, PostIndex));
    519   // lr: current element
    520   __ UntagAndJumpIfNotSmi(lr, lr, &convert_hole);
    521 
    522   // Normal smi, convert to double and store.
    523   __ vmov(s0, lr);
    524   __ vcvt_f64_s32(d0, s0);
    525   __ vstr(d0, scratch2, 0);
    526   __ add(scratch2, scratch2, Operand(8));
    527   __ b(&entry);
    528 
    529   // Hole found, store the-hole NaN.
    530   __ bind(&convert_hole);
    531   if (FLAG_debug_code) {
    532     // Restore a "smi-untagged" heap object.
    533     __ SmiTag(lr);
    534     __ orr(lr, lr, Operand(1));
    535     __ CompareRoot(lr, Heap::kTheHoleValueRootIndex);
    536     __ Assert(eq, kObjectFoundInSmiOnlyArray);
    537   }
    538   __ Strd(hole_lower, hole_upper, MemOperand(scratch2, 8, PostIndex));
    539 
    540   __ bind(&entry);
    541   __ cmp(scratch2, array_end);
    542   __ b(lt, &loop);
    543 
    544   __ pop(lr);
    545   __ bind(&done);
    546 }
    547 
    548 
    549 void ElementsTransitionGenerator::GenerateDoubleToObject(
    550     MacroAssembler* masm,
    551     Register receiver,
    552     Register key,
    553     Register value,
    554     Register target_map,
    555     AllocationSiteMode mode,
    556     Label* fail) {
    557   // Register lr contains the return address.
    558   Label entry, loop, convert_hole, gc_required, only_change_map;
    559   Register elements = r4;
    560   Register array = r6;
    561   Register length = r5;
    562   Register scratch = r9;
    563 
    564   // Verify input registers don't conflict with locals.
    565   DCHECK(!AreAliased(receiver, key, value, target_map,
    566                      elements, array, length, scratch));
    567 
    568   if (mode == TRACK_ALLOCATION_SITE) {
    569     __ JumpIfJSArrayHasAllocationMemento(receiver, elements, fail);
    570   }
    571 
    572   // Check for empty arrays, which only require a map transition and no changes
    573   // to the backing store.
    574   __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
    575   __ CompareRoot(elements, Heap::kEmptyFixedArrayRootIndex);
    576   __ b(eq, &only_change_map);
    577 
    578   __ push(lr);
    579   __ Push(target_map, receiver, key, value);
    580   __ ldr(length, FieldMemOperand(elements, FixedArray::kLengthOffset));
    581   // elements: source FixedDoubleArray
    582   // length: number of elements (smi-tagged)
    583 
    584   // Allocate new FixedArray.
    585   // Re-use value and target_map registers, as they have been saved on the
    586   // stack.
    587   Register array_size = value;
    588   Register allocate_scratch = target_map;
    589   __ mov(array_size, Operand(FixedDoubleArray::kHeaderSize));
    590   __ add(array_size, array_size, Operand(length, LSL, 1));
    591   __ Allocate(array_size, array, allocate_scratch, scratch, &gc_required,
    592               NO_ALLOCATION_FLAGS);
    593   // array: destination FixedArray, not tagged as heap object
    594   // Set destination FixedDoubleArray's length and map.
    595   __ LoadRoot(scratch, Heap::kFixedArrayMapRootIndex);
    596   __ str(length, MemOperand(array, FixedDoubleArray::kLengthOffset));
    597   __ str(scratch, MemOperand(array, HeapObject::kMapOffset));
    598 
    599   // Prepare for conversion loop.
    600   Register src_elements = elements;
    601   Register dst_elements = target_map;
    602   Register dst_end = length;
    603   Register heap_number_map = scratch;
    604   __ add(src_elements, elements,
    605          Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag + 4));
    606   __ add(dst_elements, array, Operand(FixedArray::kHeaderSize));
    607   __ add(array, array, Operand(kHeapObjectTag));
    608   __ add(dst_end, dst_elements, Operand(length, LSL, 1));
    609   __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
    610   // Using offsetted addresses in src_elements to fully take advantage of
    611   // post-indexing.
    612   // dst_elements: begin of destination FixedArray element fields, not tagged
    613   // src_elements: begin of source FixedDoubleArray element fields,
    614   //               not tagged, +4
    615   // dst_end: end of destination FixedArray, not tagged
    616   // array: destination FixedArray
    617   // heap_number_map: heap number map
    618   __ b(&entry);
    619 
    620   // Call into runtime if GC is required.
    621   __ bind(&gc_required);
    622   __ Pop(target_map, receiver, key, value);
    623   __ pop(lr);
    624   __ b(fail);
    625 
    626   __ bind(&loop);
    627   Register upper_bits = key;
    628   __ ldr(upper_bits, MemOperand(src_elements, 8, PostIndex));
    629   // upper_bits: current element's upper 32 bit
    630   // src_elements: address of next element's upper 32 bit
    631   __ cmp(upper_bits, Operand(kHoleNanUpper32));
    632   __ b(eq, &convert_hole);
    633 
    634   // Non-hole double, copy value into a heap number.
    635   Register heap_number = receiver;
    636   Register scratch2 = value;
    637   __ AllocateHeapNumber(heap_number, scratch2, lr, heap_number_map,
    638                         &gc_required);
    639   // heap_number: new heap number
    640   __ ldr(scratch2, MemOperand(src_elements, 12, NegOffset));
    641   __ Strd(scratch2, upper_bits,
    642           FieldMemOperand(heap_number, HeapNumber::kValueOffset));
    643   __ mov(scratch2, dst_elements);
    644   __ str(heap_number, MemOperand(dst_elements, 4, PostIndex));
    645   __ RecordWrite(array,
    646                  scratch2,
    647                  heap_number,
    648                  kLRHasBeenSaved,
    649                  kDontSaveFPRegs,
    650                  EMIT_REMEMBERED_SET,
    651                  OMIT_SMI_CHECK);
    652   __ b(&entry);
    653 
    654   // Replace the-hole NaN with the-hole pointer.
    655   __ bind(&convert_hole);
    656   __ LoadRoot(scratch2, Heap::kTheHoleValueRootIndex);
    657   __ str(scratch2, MemOperand(dst_elements, 4, PostIndex));
    658 
    659   __ bind(&entry);
    660   __ cmp(dst_elements, dst_end);
    661   __ b(lt, &loop);
    662 
    663   __ Pop(target_map, receiver, key, value);
    664   // Replace receiver's backing store with newly created and filled FixedArray.
    665   __ str(array, FieldMemOperand(receiver, JSObject::kElementsOffset));
    666   __ RecordWriteField(receiver,
    667                       JSObject::kElementsOffset,
    668                       array,
    669                       scratch,
    670                       kLRHasBeenSaved,
    671                       kDontSaveFPRegs,
    672                       EMIT_REMEMBERED_SET,
    673                       OMIT_SMI_CHECK);
    674   __ pop(lr);
    675 
    676   __ bind(&only_change_map);
    677   // Update receiver's map.
    678   __ str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
    679   __ RecordWriteField(receiver,
    680                       HeapObject::kMapOffset,
    681                       target_map,
    682                       scratch,
    683                       kLRHasNotBeenSaved,
    684                       kDontSaveFPRegs,
    685                       OMIT_REMEMBERED_SET,
    686                       OMIT_SMI_CHECK);
    687 }
    688 
    689 
    690 void StringCharLoadGenerator::Generate(MacroAssembler* masm,
    691                                        Register string,
    692                                        Register index,
    693                                        Register result,
    694                                        Label* call_runtime) {
    695   // Fetch the instance type of the receiver into result register.
    696   __ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
    697   __ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
    698 
    699   // We need special handling for indirect strings.
    700   Label check_sequential;
    701   __ tst(result, Operand(kIsIndirectStringMask));
    702   __ b(eq, &check_sequential);
    703 
    704   // Dispatch on the indirect string shape: slice or cons.
    705   Label cons_string;
    706   __ tst(result, Operand(kSlicedNotConsMask));
    707   __ b(eq, &cons_string);
    708 
    709   // Handle slices.
    710   Label indirect_string_loaded;
    711   __ ldr(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
    712   __ ldr(string, FieldMemOperand(string, SlicedString::kParentOffset));
    713   __ add(index, index, Operand::SmiUntag(result));
    714   __ jmp(&indirect_string_loaded);
    715 
    716   // Handle cons strings.
    717   // Check whether the right hand side is the empty string (i.e. if
    718   // this is really a flat string in a cons string). If that is not
    719   // the case we would rather go to the runtime system now to flatten
    720   // the string.
    721   __ bind(&cons_string);
    722   __ ldr(result, FieldMemOperand(string, ConsString::kSecondOffset));
    723   __ CompareRoot(result, Heap::kempty_stringRootIndex);
    724   __ b(ne, call_runtime);
    725   // Get the first of the two strings and load its instance type.
    726   __ ldr(string, FieldMemOperand(string, ConsString::kFirstOffset));
    727 
    728   __ bind(&indirect_string_loaded);
    729   __ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
    730   __ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
    731 
    732   // Distinguish sequential and external strings. Only these two string
    733   // representations can reach here (slices and flat cons strings have been
    734   // reduced to the underlying sequential or external string).
    735   Label external_string, check_encoding;
    736   __ bind(&check_sequential);
    737   STATIC_ASSERT(kSeqStringTag == 0);
    738   __ tst(result, Operand(kStringRepresentationMask));
    739   __ b(ne, &external_string);
    740 
    741   // Prepare sequential strings
    742   STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
    743   __ add(string,
    744          string,
    745          Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
    746   __ jmp(&check_encoding);
    747 
    748   // Handle external strings.
    749   __ bind(&external_string);
    750   if (FLAG_debug_code) {
    751     // Assert that we do not have a cons or slice (indirect strings) here.
    752     // Sequential strings have already been ruled out.
    753     __ tst(result, Operand(kIsIndirectStringMask));
    754     __ Assert(eq, kExternalStringExpectedButNotFound);
    755   }
    756   // Rule out short external strings.
    757   STATIC_ASSERT(kShortExternalStringTag != 0);
    758   __ tst(result, Operand(kShortExternalStringMask));
    759   __ b(ne, call_runtime);
    760   __ ldr(string, FieldMemOperand(string, ExternalString::kResourceDataOffset));
    761 
    762   Label one_byte, done;
    763   __ bind(&check_encoding);
    764   STATIC_ASSERT(kTwoByteStringTag == 0);
    765   __ tst(result, Operand(kStringEncodingMask));
    766   __ b(ne, &one_byte);
    767   // Two-byte string.
    768   __ ldrh(result, MemOperand(string, index, LSL, 1));
    769   __ jmp(&done);
    770   __ bind(&one_byte);
    771   // One-byte string.
    772   __ ldrb(result, MemOperand(string, index));
    773   __ bind(&done);
    774 }
    775 
    776 
    777 static MemOperand ExpConstant(int index, Register base) {
    778   return MemOperand(base, index * kDoubleSize);
    779 }
    780 
    781 
    782 void MathExpGenerator::EmitMathExp(MacroAssembler* masm,
    783                                    DwVfpRegister input,
    784                                    DwVfpRegister result,
    785                                    DwVfpRegister double_scratch1,
    786                                    DwVfpRegister double_scratch2,
    787                                    Register temp1,
    788                                    Register temp2,
    789                                    Register temp3) {
    790   DCHECK(!input.is(result));
    791   DCHECK(!input.is(double_scratch1));
    792   DCHECK(!input.is(double_scratch2));
    793   DCHECK(!result.is(double_scratch1));
    794   DCHECK(!result.is(double_scratch2));
    795   DCHECK(!double_scratch1.is(double_scratch2));
    796   DCHECK(!temp1.is(temp2));
    797   DCHECK(!temp1.is(temp3));
    798   DCHECK(!temp2.is(temp3));
    799   DCHECK(ExternalReference::math_exp_constants(0).address() != NULL);
    800   DCHECK(!masm->serializer_enabled());  // External references not serializable.
    801 
    802   Label zero, infinity, done;
    803 
    804   __ mov(temp3, Operand(ExternalReference::math_exp_constants(0)));
    805 
    806   __ vldr(double_scratch1, ExpConstant(0, temp3));
    807   __ VFPCompareAndSetFlags(double_scratch1, input);
    808   __ b(ge, &zero);
    809 
    810   __ vldr(double_scratch2, ExpConstant(1, temp3));
    811   __ VFPCompareAndSetFlags(input, double_scratch2);
    812   __ b(ge, &infinity);
    813 
    814   __ vldr(double_scratch1, ExpConstant(3, temp3));
    815   __ vldr(result, ExpConstant(4, temp3));
    816   __ vmul(double_scratch1, double_scratch1, input);
    817   __ vadd(double_scratch1, double_scratch1, result);
    818   __ VmovLow(temp2, double_scratch1);
    819   __ vsub(double_scratch1, double_scratch1, result);
    820   __ vldr(result, ExpConstant(6, temp3));
    821   __ vldr(double_scratch2, ExpConstant(5, temp3));
    822   __ vmul(double_scratch1, double_scratch1, double_scratch2);
    823   __ vsub(double_scratch1, double_scratch1, input);
    824   __ vsub(result, result, double_scratch1);
    825   __ vmul(double_scratch2, double_scratch1, double_scratch1);
    826   __ vmul(result, result, double_scratch2);
    827   __ vldr(double_scratch2, ExpConstant(7, temp3));
    828   __ vmul(result, result, double_scratch2);
    829   __ vsub(result, result, double_scratch1);
    830   // Mov 1 in double_scratch2 as math_exp_constants_array[8] == 1.
    831   DCHECK(*reinterpret_cast<double*>
    832          (ExternalReference::math_exp_constants(8).address()) == 1);
    833   __ vmov(double_scratch2, 1);
    834   __ vadd(result, result, double_scratch2);
    835   __ mov(temp1, Operand(temp2, LSR, 11));
    836   __ Ubfx(temp2, temp2, 0, 11);
    837   __ add(temp1, temp1, Operand(0x3ff));
    838 
    839   // Must not call ExpConstant() after overwriting temp3!
    840   __ mov(temp3, Operand(ExternalReference::math_exp_log_table()));
    841   __ add(temp3, temp3, Operand(temp2, LSL, 3));
    842   __ ldm(ia, temp3, temp2.bit() | temp3.bit());
    843   // The first word is loaded is the lower number register.
    844   if (temp2.code() < temp3.code()) {
    845     __ orr(temp1, temp3, Operand(temp1, LSL, 20));
    846     __ vmov(double_scratch1, temp2, temp1);
    847   } else {
    848     __ orr(temp1, temp2, Operand(temp1, LSL, 20));
    849     __ vmov(double_scratch1, temp3, temp1);
    850   }
    851   __ vmul(result, result, double_scratch1);
    852   __ b(&done);
    853 
    854   __ bind(&zero);
    855   __ vmov(result, kDoubleRegZero);
    856   __ b(&done);
    857 
    858   __ bind(&infinity);
    859   __ vldr(result, ExpConstant(2, temp3));
    860 
    861   __ bind(&done);
    862 }
    863 
    864 #undef __
    865 
    866 #ifdef DEBUG
    867 // add(r0, pc, Operand(-8))
    868 static const uint32_t kCodeAgePatchFirstInstruction = 0xe24f0008;
    869 #endif
    870 
    871 CodeAgingHelper::CodeAgingHelper() {
    872   DCHECK(young_sequence_.length() == kNoCodeAgeSequenceLength);
    873   // Since patcher is a large object, allocate it dynamically when needed,
    874   // to avoid overloading the stack in stress conditions.
    875   // DONT_FLUSH is used because the CodeAgingHelper is initialized early in
    876   // the process, before ARM simulator ICache is setup.
    877   SmartPointer<CodePatcher> patcher(
    878       new CodePatcher(young_sequence_.start(),
    879                       young_sequence_.length() / Assembler::kInstrSize,
    880                       CodePatcher::DONT_FLUSH));
    881   PredictableCodeSizeScope scope(patcher->masm(), young_sequence_.length());
    882   patcher->masm()->PushFixedFrame(r1);
    883   patcher->masm()->nop(ip.code());
    884   patcher->masm()->add(
    885       fp, sp, Operand(StandardFrameConstants::kFixedFrameSizeFromFp));
    886 }
    887 
    888 
    889 #ifdef DEBUG
    890 bool CodeAgingHelper::IsOld(byte* candidate) const {
    891   return Memory::uint32_at(candidate) == kCodeAgePatchFirstInstruction;
    892 }
    893 #endif
    894 
    895 
    896 bool Code::IsYoungSequence(Isolate* isolate, byte* sequence) {
    897   bool result = isolate->code_aging_helper()->IsYoung(sequence);
    898   DCHECK(result || isolate->code_aging_helper()->IsOld(sequence));
    899   return result;
    900 }
    901 
    902 
    903 void Code::GetCodeAgeAndParity(Isolate* isolate, byte* sequence, Age* age,
    904                                MarkingParity* parity) {
    905   if (IsYoungSequence(isolate, sequence)) {
    906     *age = kNoAgeCodeAge;
    907     *parity = NO_MARKING_PARITY;
    908   } else {
    909     Address target_address = Memory::Address_at(
    910         sequence + (kNoCodeAgeSequenceLength - Assembler::kInstrSize));
    911     Code* stub = GetCodeFromTargetAddress(target_address);
    912     GetCodeAgeAndParity(stub, age, parity);
    913   }
    914 }
    915 
    916 
    917 void Code::PatchPlatformCodeAge(Isolate* isolate,
    918                                 byte* sequence,
    919                                 Code::Age age,
    920                                 MarkingParity parity) {
    921   uint32_t young_length = isolate->code_aging_helper()->young_sequence_length();
    922   if (age == kNoAgeCodeAge) {
    923     isolate->code_aging_helper()->CopyYoungSequenceTo(sequence);
    924     CpuFeatures::FlushICache(sequence, young_length);
    925   } else {
    926     Code* stub = GetCodeAgeStub(isolate, age, parity);
    927     CodePatcher patcher(sequence, young_length / Assembler::kInstrSize);
    928     patcher.masm()->add(r0, pc, Operand(-8));
    929     patcher.masm()->ldr(pc, MemOperand(pc, -4));
    930     patcher.masm()->emit_code_stub_address(stub);
    931   }
    932 }
    933 
    934 
    935 } }  // namespace v8::internal
    936 
    937 #endif  // V8_TARGET_ARCH_ARM
    938