js/src/jit/arm/CodeGenerator-arm.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "jit/arm/CodeGenerator-arm.h"
     9 #include "mozilla/MathAlgorithms.h"
    11 #include "jscntxt.h"
    12 #include "jscompartment.h"
    13 #include "jsnum.h"
    15 #include "jit/CodeGenerator.h"
    16 #include "jit/IonFrames.h"
    17 #include "jit/JitCompartment.h"
    18 #include "jit/MIR.h"
    19 #include "jit/MIRGraph.h"
    20 #include "vm/Shape.h"
    21 #include "vm/TraceLogging.h"
    23 #include "jsscriptinlines.h"
    25 #include "jit/shared/CodeGenerator-shared-inl.h"
    27 using namespace js;
    28 using namespace js::jit;
    30 using mozilla::FloorLog2;
    31 using mozilla::NegativeInfinity;
    32 using JS::GenericNaN;
    34 // shared
    35 CodeGeneratorARM::CodeGeneratorARM(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
    36   : CodeGeneratorShared(gen, graph, masm)
    37 {
    38 }
    40 bool
    41 CodeGeneratorARM::generatePrologue()
    42 {
    43     JS_ASSERT(!gen->compilingAsmJS());
    45     // Note that this automatically sets MacroAssembler::framePushed().
    46     masm.reserveStack(frameSize());
    47     masm.checkStackAlignment();
    48     return true;
    49 }
    51 bool
    52 CodeGeneratorARM::generateAsmJSPrologue(Label *stackOverflowLabel)
    53 {
    54     JS_ASSERT(gen->compilingAsmJS());
    56     masm.Push(lr);
    58     // The asm.js over-recursed handler wants to be able to assume that SP
    59     // points to the return address, so perform the check after pushing lr but
    60     // before pushing frameDepth.
    61     if (!omitOverRecursedCheck()) {
    62         masm.branchPtr(Assembler::AboveOrEqual,
    63                        AsmJSAbsoluteAddress(AsmJSImm_StackLimit),
    64                        StackPointer,
    65                        stackOverflowLabel);
    66     }
    68     // Note that this automatically sets MacroAssembler::framePushed().
    69     masm.reserveStack(frameDepth_);
    70     masm.checkStackAlignment();
    71     return true;
    72 }
    74 bool
    75 CodeGeneratorARM::generateEpilogue()
    76 {
    77     masm.bind(&returnLabel_);
    79 #ifdef JS_TRACE_LOGGING
    80     if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
    81         if (!emitTracelogStopEvent(TraceLogger::IonMonkey))
    82             return false;
    83         if (!emitTracelogScriptStop())
    84             return false;
    85     }
    86 #endif
    88     if (gen->compilingAsmJS()) {
    89         // Pop the stack we allocated at the start of the function.
    90         masm.freeStack(frameDepth_);
    91         masm.Pop(pc);
    92         JS_ASSERT(masm.framePushed() == 0);
    93         //masm.as_bkpt();
    94     } else {
    95         // Pop the stack we allocated at the start of the function.
    96         masm.freeStack(frameSize());
    97         JS_ASSERT(masm.framePushed() == 0);
    98         masm.ma_pop(pc);
    99     }
   100     masm.dumpPool();
   101     return true;
   102 }
   104 void
   105 CodeGeneratorARM::emitBranch(Assembler::Condition cond, MBasicBlock *mirTrue, MBasicBlock *mirFalse)
   106 {
   107     if (isNextBlock(mirFalse->lir())) {
   108         jumpToBlock(mirTrue, cond);
   109     } else {
   110         jumpToBlock(mirFalse, Assembler::InvertCondition(cond));
   111         jumpToBlock(mirTrue);
   112     }
   113 }
   116 bool
   117 OutOfLineBailout::accept(CodeGeneratorARM *codegen)
   118 {
   119     return codegen->visitOutOfLineBailout(this);
   120 }
   122 bool
   123 CodeGeneratorARM::visitTestIAndBranch(LTestIAndBranch *test)
   124 {
   125     const LAllocation *opd = test->getOperand(0);
   126     MBasicBlock *ifTrue = test->ifTrue();
   127     MBasicBlock *ifFalse = test->ifFalse();
   129     // Test the operand
   130     masm.ma_cmp(ToRegister(opd), Imm32(0));
   132     if (isNextBlock(ifFalse->lir())) {
   133         jumpToBlock(ifTrue, Assembler::NonZero);
   134     } else if (isNextBlock(ifTrue->lir())) {
   135         jumpToBlock(ifFalse, Assembler::Zero);
   136     } else {
   137         jumpToBlock(ifFalse, Assembler::Zero);
   138         jumpToBlock(ifTrue);
   139     }
   140     return true;
   141 }
   143 bool
   144 CodeGeneratorARM::visitCompare(LCompare *comp)
   145 {
   146     Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop());
   147     const LAllocation *left = comp->getOperand(0);
   148     const LAllocation *right = comp->getOperand(1);
   149     const LDefinition *def = comp->getDef(0);
   151     if (right->isConstant())
   152         masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right)));
   153     else
   154         masm.ma_cmp(ToRegister(left), ToOperand(right));
   155     masm.ma_mov(Imm32(0), ToRegister(def));
   156     masm.ma_mov(Imm32(1), ToRegister(def), NoSetCond, cond);
   157     return true;
   158 }
   160 bool
   161 CodeGeneratorARM::visitCompareAndBranch(LCompareAndBranch *comp)
   162 {
   163     Assembler::Condition cond = JSOpToCondition(comp->cmpMir()->compareType(), comp->jsop());
   164     if (comp->right()->isConstant())
   165         masm.ma_cmp(ToRegister(comp->left()), Imm32(ToInt32(comp->right())));
   166     else
   167         masm.ma_cmp(ToRegister(comp->left()), ToOperand(comp->right()));
   168     emitBranch(cond, comp->ifTrue(), comp->ifFalse());
   169     return true;
   171 }
   173 bool
   174 CodeGeneratorARM::generateOutOfLineCode()
   175 {
   176     if (!CodeGeneratorShared::generateOutOfLineCode())
   177         return false;
   179     if (deoptLabel_.used()) {
   180         // All non-table-based bailouts will go here.
   181         masm.bind(&deoptLabel_);
   183         // Push the frame size, so the handler can recover the IonScript.
   184         masm.ma_mov(Imm32(frameSize()), lr);
   186         JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler();
   187         masm.branch(handler);
   188     }
   190     return true;
   191 }
   193 bool
   194 CodeGeneratorARM::bailoutIf(Assembler::Condition condition, LSnapshot *snapshot)
   195 {
   196     CompileInfo &info = snapshot->mir()->block()->info();
   197     switch (info.executionMode()) {
   199       case ParallelExecution: {
   200         // in parallel mode, make no attempt to recover, just signal an error.
   201         OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported,
   202                                              snapshot->mir()->block(),
   203                                              snapshot->mir()->pc());
   204         masm.ma_b(ool->entry(), condition);
   205         return true;
   206       }
   207       case SequentialExecution:
   208         break;
   209       default:
   210         MOZ_ASSUME_UNREACHABLE("No such execution mode");
   211     }
   212     if (!encode(snapshot))
   213         return false;
   215     // Though the assembler doesn't track all frame pushes, at least make sure
   216     // the known value makes sense. We can't use bailout tables if the stack
   217     // isn't properly aligned to the static frame size.
   218     JS_ASSERT_IF(frameClass_ != FrameSizeClass::None(),
   219                  frameClass_.frameSize() == masm.framePushed());
   221     if (assignBailoutId(snapshot)) {
   222         uint8_t *code = deoptTable_->raw() + snapshot->bailoutId() * BAILOUT_TABLE_ENTRY_SIZE;
   223         masm.ma_b(code, Relocation::HARDCODED, condition);
   224         return true;
   225     }
   227     // We could not use a jump table, either because all bailout IDs were
   228     // reserved, or a jump table is not optimal for this frame size or
   229     // platform. Whatever, we will generate a lazy bailout.
   230     OutOfLineBailout *ool = new(alloc()) OutOfLineBailout(snapshot, masm.framePushed());
   231     if (!addOutOfLineCode(ool))
   232         return false;
   234     masm.ma_b(ool->entry(), condition);
   236     return true;
   237 }
   238 bool
   239 CodeGeneratorARM::bailoutFrom(Label *label, LSnapshot *snapshot)
   240 {
   241     if (masm.bailed())
   242         return false;
   243     JS_ASSERT(label->used());
   244     JS_ASSERT(!label->bound());
   246     CompileInfo &info = snapshot->mir()->block()->info();
   247     switch (info.executionMode()) {
   249       case ParallelExecution: {
   250         // in parallel mode, make no attempt to recover, just signal an error.
   251         OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported,
   252                                              snapshot->mir()->block(),
   253                                              snapshot->mir()->pc());
   254         masm.retarget(label, ool->entry());
   255         return true;
   256       }
   257       case SequentialExecution:
   258         break;
   259       default:
   260         MOZ_ASSUME_UNREACHABLE("No such execution mode");
   261     }
   263     if (!encode(snapshot))
   264         return false;
   266     // Though the assembler doesn't track all frame pushes, at least make sure
   267     // the known value makes sense. We can't use bailout tables if the stack
   268     // isn't properly aligned to the static frame size.
   269     JS_ASSERT_IF(frameClass_ != FrameSizeClass::None(),
   270                  frameClass_.frameSize() == masm.framePushed());
   272     // On ARM we don't use a bailout table.
   273     OutOfLineBailout *ool = new(alloc()) OutOfLineBailout(snapshot, masm.framePushed());
   274     if (!addOutOfLineCode(ool)) {
   275         return false;
   276     }
   278     masm.retarget(label, ool->entry());
   280     return true;
   281 }
   283 bool
   284 CodeGeneratorARM::bailout(LSnapshot *snapshot)
   285 {
   286     Label label;
   287     masm.ma_b(&label);
   288     return bailoutFrom(&label, snapshot);
   289 }
   291 bool
   292 CodeGeneratorARM::visitOutOfLineBailout(OutOfLineBailout *ool)
   293 {
   294     masm.ma_mov(Imm32(ool->snapshot()->snapshotOffset()), ScratchRegister);
   295     masm.ma_push(ScratchRegister); // BailoutStack::padding_
   296     masm.ma_push(ScratchRegister); // BailoutStack::snapshotOffset_
   297     masm.ma_b(&deoptLabel_);
   298     return true;
   299 }
   301 bool
   302 CodeGeneratorARM::visitMinMaxD(LMinMaxD *ins)
   303 {
   304     FloatRegister first = ToFloatRegister(ins->first());
   305     FloatRegister second = ToFloatRegister(ins->second());
   306     FloatRegister output = ToFloatRegister(ins->output());
   308     JS_ASSERT(first == output);
   310     Assembler::Condition cond = ins->mir()->isMax()
   311         ? Assembler::VFP_LessThanOrEqual
   312         : Assembler::VFP_GreaterThanOrEqual;
   313     Label nan, equal, returnSecond, done;
   315     masm.compareDouble(first, second);
   316     masm.ma_b(&nan, Assembler::VFP_Unordered); // first or second is NaN, result is NaN.
   317     masm.ma_b(&equal, Assembler::VFP_Equal); // make sure we handle -0 and 0 right.
   318     masm.ma_b(&returnSecond, cond);
   319     masm.ma_b(&done);
   321     // Check for zero.
   322     masm.bind(&equal);
   323     masm.compareDouble(first, InvalidFloatReg);
   324     masm.ma_b(&done, Assembler::VFP_NotEqualOrUnordered); // first wasn't 0 or -0, so just return it.
   325     // So now both operands are either -0 or 0.
   326     if (ins->mir()->isMax()) {
   327         masm.ma_vadd(second, first, first); // -0 + -0 = -0 and -0 + 0 = 0.
   328     } else {
   329         masm.ma_vneg(first, first);
   330         masm.ma_vsub(first, second, first);
   331         masm.ma_vneg(first, first);
   332     }
   333     masm.ma_b(&done);
   335     masm.bind(&nan);
   336     masm.loadConstantDouble(GenericNaN(), output);
   337     masm.ma_b(&done);
   339     masm.bind(&returnSecond);
   340     masm.ma_vmov(second, output);
   342     masm.bind(&done);
   343     return true;
   344 }
   346 bool
   347 CodeGeneratorARM::visitAbsD(LAbsD *ins)
   348 {
   349     FloatRegister input = ToFloatRegister(ins->input());
   350     JS_ASSERT(input == ToFloatRegister(ins->output()));
   351     masm.ma_vabs(input, input);
   352     return true;
   353 }
   355 bool
   356 CodeGeneratorARM::visitAbsF(LAbsF *ins)
   357 {
   358     FloatRegister input = ToFloatRegister(ins->input());
   359     JS_ASSERT(input == ToFloatRegister(ins->output()));
   360     masm.ma_vabs_f32(input, input);
   361     return true;
   362 }
   364 bool
   365 CodeGeneratorARM::visitSqrtD(LSqrtD *ins)
   366 {
   367     FloatRegister input = ToFloatRegister(ins->input());
   368     FloatRegister output = ToFloatRegister(ins->output());
   369     masm.ma_vsqrt(input, output);
   370     return true;
   371 }
   373 bool
   374 CodeGeneratorARM::visitSqrtF(LSqrtF *ins)
   375 {
   376     FloatRegister input = ToFloatRegister(ins->input());
   377     FloatRegister output = ToFloatRegister(ins->output());
   378     masm.ma_vsqrt_f32(input, output);
   379     return true;
   380 }
   382 bool
   383 CodeGeneratorARM::visitAddI(LAddI *ins)
   384 {
   385     const LAllocation *lhs = ins->getOperand(0);
   386     const LAllocation *rhs = ins->getOperand(1);
   387     const LDefinition *dest = ins->getDef(0);
   389     if (rhs->isConstant())
   390         masm.ma_add(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCond);
   391     else
   392         masm.ma_add(ToRegister(lhs), ToOperand(rhs), ToRegister(dest), SetCond);
   394     if (ins->snapshot() && !bailoutIf(Assembler::Overflow, ins->snapshot()))
   395         return false;
   397     return true;
   398 }
   400 bool
   401 CodeGeneratorARM::visitSubI(LSubI *ins)
   402 {
   403     const LAllocation *lhs = ins->getOperand(0);
   404     const LAllocation *rhs = ins->getOperand(1);
   405     const LDefinition *dest = ins->getDef(0);
   407     if (rhs->isConstant())
   408         masm.ma_sub(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCond);
   409     else
   410         masm.ma_sub(ToRegister(lhs), ToOperand(rhs), ToRegister(dest), SetCond);
   412     if (ins->snapshot() && !bailoutIf(Assembler::Overflow, ins->snapshot()))
   413         return false;
   414     return true;
   415 }
   417 bool
   418 CodeGeneratorARM::visitMulI(LMulI *ins)
   419 {
   420     const LAllocation *lhs = ins->getOperand(0);
   421     const LAllocation *rhs = ins->getOperand(1);
   422     const LDefinition *dest = ins->getDef(0);
   423     MMul *mul = ins->mir();
   424     JS_ASSERT_IF(mul->mode() == MMul::Integer, !mul->canBeNegativeZero() && !mul->canOverflow());
   426     if (rhs->isConstant()) {
   427         // Bailout when this condition is met.
   428         Assembler::Condition c = Assembler::Overflow;
   429         // Bailout on -0.0
   430         int32_t constant = ToInt32(rhs);
   431         if (mul->canBeNegativeZero() && constant <= 0) {
   432             Assembler::Condition bailoutCond = (constant == 0) ? Assembler::LessThan : Assembler::Equal;
   433             masm.ma_cmp(ToRegister(lhs), Imm32(0));
   434             if (!bailoutIf(bailoutCond, ins->snapshot()))
   435                 return false;
   436         }
   437         // TODO: move these to ma_mul.
   438         switch (constant) {
   439           case -1:
   440             masm.ma_rsb(ToRegister(lhs), Imm32(0), ToRegister(dest), SetCond);
   441             break;
   442           case 0:
   443             masm.ma_mov(Imm32(0), ToRegister(dest));
   444             return true; // escape overflow check;
   445           case 1:
   446             // nop
   447             masm.ma_mov(ToRegister(lhs), ToRegister(dest));
   448             return true; // escape overflow check;
   449           case 2:
   450             masm.ma_add(ToRegister(lhs), ToRegister(lhs), ToRegister(dest), SetCond);
   451             // Overflow is handled later.
   452             break;
   453           default: {
   454             bool handled = false;
   455             if (constant > 0) {
   456                 // Try shift and add sequences for a positive constant.
   457                 if (!mul->canOverflow()) {
   458                     // If it cannot overflow, we can do lots of optimizations
   459                     Register src = ToRegister(lhs);
   460                     uint32_t shift = FloorLog2(constant);
   461                     uint32_t rest = constant - (1 << shift);
   462                     // See if the constant has one bit set, meaning it can be encoded as a bitshift
   463                     if ((1 << shift) == constant) {
   464                         masm.ma_lsl(Imm32(shift), src, ToRegister(dest));
   465                         handled = true;
   466                     } else {
   467                         // If the constant cannot be encoded as (1<<C1), see if it can be encoded as
   468                         // (1<<C1) | (1<<C2), which can be computed using an add and a shift
   469                         uint32_t shift_rest = FloorLog2(rest);
   470                         if ((1u << shift_rest) == rest) {
   471                             masm.as_add(ToRegister(dest), src, lsl(src, shift-shift_rest));
   472                             if (shift_rest != 0)
   473                                 masm.ma_lsl(Imm32(shift_rest), ToRegister(dest), ToRegister(dest));
   474                             handled = true;
   475                         }
   476                     }
   477                 } else if (ToRegister(lhs) != ToRegister(dest)) {
   478                     // To stay on the safe side, only optimize things that are a
   479                     // power of 2.
   481                     uint32_t shift = FloorLog2(constant);
   482                     if ((1 << shift) == constant) {
   483                         // dest = lhs * pow(2,shift)
   484                         masm.ma_lsl(Imm32(shift), ToRegister(lhs), ToRegister(dest));
   485                         // At runtime, check (lhs == dest >> shift), if this does not hold,
   486                         // some bits were lost due to overflow, and the computation should
   487                         // be resumed as a double.
   488                         masm.as_cmp(ToRegister(lhs), asr(ToRegister(dest), shift));
   489                         c = Assembler::NotEqual;
   490                         handled = true;
   491                     }
   492                 }
   493             }
   495             if (!handled) {
   496                 if (mul->canOverflow())
   497                     c = masm.ma_check_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), c);
   498                 else
   499                     masm.ma_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest));
   500             }
   501           }
   502         }
   503         // Bailout on overflow
   504         if (mul->canOverflow() && !bailoutIf(c, ins->snapshot()))
   505             return false;
   506     } else {
   507         Assembler::Condition c = Assembler::Overflow;
   509         //masm.imull(ToOperand(rhs), ToRegister(lhs));
   510         if (mul->canOverflow())
   511             c = masm.ma_check_mul(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), c);
   512         else
   513             masm.ma_mul(ToRegister(lhs), ToRegister(rhs), ToRegister(dest));
   515         // Bailout on overflow
   516         if (mul->canOverflow() && !bailoutIf(c, ins->snapshot()))
   517             return false;
   519         if (mul->canBeNegativeZero()) {
   520             Label done;
   521             masm.ma_cmp(ToRegister(dest), Imm32(0));
   522             masm.ma_b(&done, Assembler::NotEqual);
   524             // Result is -0 if lhs or rhs is negative.
   525             masm.ma_cmn(ToRegister(lhs), ToRegister(rhs));
   526             if (!bailoutIf(Assembler::Signed, ins->snapshot()))
   527                 return false;
   529             masm.bind(&done);
   530         }
   531     }
   533     return true;
   534 }
   536 bool
   537 CodeGeneratorARM::divICommon(MDiv *mir, Register lhs, Register rhs, Register output,
   538                              LSnapshot *snapshot, Label &done)
   539 {
   540     if (mir->canBeNegativeOverflow()) {
   541         // Handle INT32_MIN / -1;
   542         // The integer division will give INT32_MIN, but we want -(double)INT32_MIN.
   543         masm.ma_cmp(lhs, Imm32(INT32_MIN)); // sets EQ if lhs == INT32_MIN
   544         masm.ma_cmp(rhs, Imm32(-1), Assembler::Equal); // if EQ (LHS == INT32_MIN), sets EQ if rhs == -1
   545         if (mir->canTruncateOverflow()) {
   546             // (-INT32_MIN)|0 = INT32_MIN
   547             Label skip;
   548             masm.ma_b(&skip, Assembler::NotEqual);
   549             masm.ma_mov(Imm32(INT32_MIN), output);
   550             masm.ma_b(&done);
   551             masm.bind(&skip);
   552         } else {
   553             JS_ASSERT(mir->fallible());
   554             if (!bailoutIf(Assembler::Equal, snapshot))
   555                 return false;
   556         }
   557     }
   559     // Handle divide by zero.
   560     if (mir->canBeDivideByZero()) {
   561         masm.ma_cmp(rhs, Imm32(0));
   562         if (mir->canTruncateInfinities()) {
   563             // Infinity|0 == 0
   564             Label skip;
   565             masm.ma_b(&skip, Assembler::NotEqual);
   566             masm.ma_mov(Imm32(0), output);
   567             masm.ma_b(&done);
   568             masm.bind(&skip);
   569         } else {
   570             JS_ASSERT(mir->fallible());
   571             if (!bailoutIf(Assembler::Equal, snapshot))
   572                 return false;
   573         }
   574     }
   576     // Handle negative 0.
   577     if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) {
   578         Label nonzero;
   579         masm.ma_cmp(lhs, Imm32(0));
   580         masm.ma_b(&nonzero, Assembler::NotEqual);
   581         masm.ma_cmp(rhs, Imm32(0));
   582         JS_ASSERT(mir->fallible());
   583         if (!bailoutIf(Assembler::LessThan, snapshot))
   584             return false;
   585         masm.bind(&nonzero);
   586     }
   588     return true;
   589 }
   591 bool
   592 CodeGeneratorARM::visitDivI(LDivI *ins)
   593 {
   594     // Extract the registers from this instruction
   595     Register lhs = ToRegister(ins->lhs());
   596     Register rhs = ToRegister(ins->rhs());
   597     Register temp = ToRegister(ins->getTemp(0));
   598     Register output = ToRegister(ins->output());
   599     MDiv *mir = ins->mir();
   601     Label done;
   602     if (!divICommon(mir, lhs, rhs, output, ins->snapshot(), done))
   603         return false;
   605     if (mir->canTruncateRemainder()) {
   606         masm.ma_sdiv(lhs, rhs, output);
   607     } else {
   608         masm.ma_sdiv(lhs, rhs, ScratchRegister);
   609         masm.ma_mul(ScratchRegister, rhs, temp);
   610         masm.ma_cmp(lhs, temp);
   611         if (!bailoutIf(Assembler::NotEqual, ins->snapshot()))
   612             return false;
   613         masm.ma_mov(ScratchRegister, output);
   614     }
   616     masm.bind(&done);
   618     return true;
   619 }
   621 extern "C" {
   622     extern int64_t __aeabi_idivmod(int,int);
   623     extern int64_t __aeabi_uidivmod(int,int);
   624 }
   626 bool
   627 CodeGeneratorARM::visitSoftDivI(LSoftDivI *ins)
   628 {
   629     // Extract the registers from this instruction
   630     Register lhs = ToRegister(ins->lhs());
   631     Register rhs = ToRegister(ins->rhs());
   632     Register output = ToRegister(ins->output());
   633     MDiv *mir = ins->mir();
   635     Label done;
   636     if (!divICommon(mir, lhs, rhs, output, ins->snapshot(), done))
   637         return false;
   639     masm.setupAlignedABICall(2);
   640     masm.passABIArg(lhs);
   641     masm.passABIArg(rhs);
   642     if (gen->compilingAsmJS())
   643         masm.callWithABI(AsmJSImm_aeabi_idivmod);
   644     else
   645         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, __aeabi_idivmod));
   646     // idivmod returns the quotient in r0, and the remainder in r1.
   647     if (!mir->canTruncateRemainder()) {
   648         JS_ASSERT(mir->fallible());
   649         masm.ma_cmp(r1, Imm32(0));
   650         if (!bailoutIf(Assembler::NonZero, ins->snapshot()))
   651             return false;
   652     }
   654     masm.bind(&done);
   656     return true;
   657 }
   659 bool
   660 CodeGeneratorARM::visitDivPowTwoI(LDivPowTwoI *ins)
   661 {
   662     Register lhs = ToRegister(ins->numerator());
   663     Register output = ToRegister(ins->output());
   664     int32_t shift = ins->shift();
   666     if (shift != 0) {
   667         MDiv *mir = ins->mir();
   668         if (!mir->isTruncated()) {
   669             // If the remainder is != 0, bailout since this must be a double.
   670             masm.as_mov(ScratchRegister, lsl(lhs, 32 - shift), SetCond);
   671             if (!bailoutIf(Assembler::NonZero, ins->snapshot()))
   672                 return false;
   673         }
   675         if (!mir->canBeNegativeDividend()) {
   676             // Numerator is unsigned, so needs no adjusting. Do the shift.
   677             masm.as_mov(output, asr(lhs, shift));
   678             return true;
   679         }
   681         // Adjust the value so that shifting produces a correctly rounded result
   682         // when the numerator is negative. See 10-1 "Signed Division by a Known
   683         // Power of 2" in Henry S. Warren, Jr.'s Hacker's Delight.
   684         if (shift > 1) {
   685             masm.as_mov(ScratchRegister, asr(lhs, 31));
   686             masm.as_add(ScratchRegister, lhs, lsr(ScratchRegister, 32 - shift));
   687         } else
   688             masm.as_add(ScratchRegister, lhs, lsr(lhs, 32 - shift));
   690         // Do the shift.
   691         masm.as_mov(output, asr(ScratchRegister, shift));
   692     } else {
   693         masm.ma_mov(lhs, output);
   694     }
   696     return true;
   697 }
   699 bool
   700 CodeGeneratorARM::modICommon(MMod *mir, Register lhs, Register rhs, Register output,
   701                              LSnapshot *snapshot, Label &done)
   702 {
   703     // 0/X (with X < 0) is bad because both of these values *should* be doubles, and
   704     // the result should be -0.0, which cannot be represented in integers.
   705     // X/0 is bad because it will give garbage (or abort), when it should give
   706     // either \infty, -\infty or NAN.
   708     // Prevent 0 / X (with X < 0) and X / 0
   709     // testing X / Y.  Compare Y with 0.
   710     // There are three cases: (Y < 0), (Y == 0) and (Y > 0)
   711     // If (Y < 0), then we compare X with 0, and bail if X == 0
   712     // If (Y == 0), then we simply want to bail.  Since this does not set
   713     // the flags necessary for LT to trigger, we don't test X, and take the
   714     // bailout because the EQ flag is set.
   715     // if (Y > 0), we don't set EQ, and we don't trigger LT, so we don't take the bailout.
   716     if (mir->canBeDivideByZero() || mir->canBeNegativeDividend()) {
   717         masm.ma_cmp(rhs, Imm32(0));
   718         masm.ma_cmp(lhs, Imm32(0), Assembler::LessThan);
   719         if (mir->isTruncated()) {
   720             // NaN|0 == 0 and (0 % -X)|0 == 0
   721             Label skip;
   722             masm.ma_b(&skip, Assembler::NotEqual);
   723             masm.ma_mov(Imm32(0), output);
   724             masm.ma_b(&done);
   725             masm.bind(&skip);
   726         } else {
   727             JS_ASSERT(mir->fallible());
   728             if (!bailoutIf(Assembler::Equal, snapshot))
   729                 return false;
   730         }
   731     }
   733     return true;
   734 }
   736 bool
   737 CodeGeneratorARM::visitModI(LModI *ins)
   738 {
   739     Register lhs = ToRegister(ins->lhs());
   740     Register rhs = ToRegister(ins->rhs());
   741     Register output = ToRegister(ins->output());
   742     Register callTemp = ToRegister(ins->callTemp());
   743     MMod *mir = ins->mir();
   745     // save the lhs in case we end up with a 0 that should be a -0.0 because lhs < 0.
   746     masm.ma_mov(lhs, callTemp);
   748     Label done;
   749     if (!modICommon(mir, lhs, rhs, output, ins->snapshot(), done))
   750         return false;
   752     masm.ma_smod(lhs, rhs, output);
   754     // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0
   755     if (mir->canBeNegativeDividend()) {
   756         if (mir->isTruncated()) {
   757             // -0.0|0 == 0
   758         } else {
   759             JS_ASSERT(mir->fallible());
   760             // See if X < 0
   761             masm.ma_cmp(output, Imm32(0));
   762             masm.ma_b(&done, Assembler::NotEqual);
   763             masm.ma_cmp(callTemp, Imm32(0));
   764             if (!bailoutIf(Assembler::Signed, ins->snapshot()))
   765                 return false;
   766         }
   767     }
   769     masm.bind(&done);
   770     return true;
   771 }
   773 bool
   774 CodeGeneratorARM::visitSoftModI(LSoftModI *ins)
   775 {
   776     // Extract the registers from this instruction
   777     Register lhs = ToRegister(ins->lhs());
   778     Register rhs = ToRegister(ins->rhs());
   779     Register output = ToRegister(ins->output());
   780     Register callTemp = ToRegister(ins->callTemp());
   781     MMod *mir = ins->mir();
   782     Label done;
   784     // save the lhs in case we end up with a 0 that should be a -0.0 because lhs < 0.
   785     JS_ASSERT(callTemp.code() > r3.code() && callTemp.code() < r12.code());
   786     masm.ma_mov(lhs, callTemp);
   788     // Prevent INT_MIN % -1;
   789     // The integer division will give INT_MIN, but we want -(double)INT_MIN.
   790     if (mir->canBeNegativeDividend()) {
   791         masm.ma_cmp(lhs, Imm32(INT_MIN)); // sets EQ if lhs == INT_MIN
   792         masm.ma_cmp(rhs, Imm32(-1), Assembler::Equal); // if EQ (LHS == INT_MIN), sets EQ if rhs == -1
   793         if (mir->isTruncated()) {
   794             // (INT_MIN % -1)|0 == 0
   795             Label skip;
   796             masm.ma_b(&skip, Assembler::NotEqual);
   797             masm.ma_mov(Imm32(0), output);
   798             masm.ma_b(&done);
   799             masm.bind(&skip);
   800         } else {
   801             JS_ASSERT(mir->fallible());
   802             if (!bailoutIf(Assembler::Equal, ins->snapshot()))
   803                 return false;
   804         }
   805     }
   807     if (!modICommon(mir, lhs, rhs, output, ins->snapshot(), done))
   808         return false;
   810     masm.setupAlignedABICall(2);
   811     masm.passABIArg(lhs);
   812     masm.passABIArg(rhs);
   813     if (gen->compilingAsmJS())
   814         masm.callWithABI(AsmJSImm_aeabi_idivmod);
   815     else
   816         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, __aeabi_idivmod));
   818     // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0
   819     if (mir->canBeNegativeDividend()) {
   820         if (mir->isTruncated()) {
   821             // -0.0|0 == 0
   822         } else {
   823             JS_ASSERT(mir->fallible());
   824             // See if X < 0
   825             masm.ma_cmp(r1, Imm32(0));
   826             masm.ma_b(&done, Assembler::NotEqual);
   827             masm.ma_cmp(callTemp, Imm32(0));
   828             if (!bailoutIf(Assembler::Signed, ins->snapshot()))
   829                 return false;
   830         }
   831     }
   832     masm.bind(&done);
   833     return true;
   834 }
   836 bool
   837 CodeGeneratorARM::visitModPowTwoI(LModPowTwoI *ins)
   838 {
   839     Register in = ToRegister(ins->getOperand(0));
   840     Register out = ToRegister(ins->getDef(0));
   841     MMod *mir = ins->mir();
   842     Label fin;
   843     // bug 739870, jbramley has a different sequence that may help with speed here
   844     masm.ma_mov(in, out, SetCond);
   845     masm.ma_b(&fin, Assembler::Zero);
   846     masm.ma_rsb(Imm32(0), out, NoSetCond, Assembler::Signed);
   847     masm.ma_and(Imm32((1<<ins->shift())-1), out);
   848     masm.ma_rsb(Imm32(0), out, SetCond, Assembler::Signed);
   849     if (mir->canBeNegativeDividend()) {
   850         if (!mir->isTruncated()) {
   851             JS_ASSERT(mir->fallible());
   852             if (!bailoutIf(Assembler::Zero, ins->snapshot()))
   853                 return false;
   854         } else {
   855             // -0|0 == 0
   856         }
   857     }
   858     masm.bind(&fin);
   859     return true;
   860 }
   862 bool
   863 CodeGeneratorARM::visitModMaskI(LModMaskI *ins)
   864 {
   865     Register src = ToRegister(ins->getOperand(0));
   866     Register dest = ToRegister(ins->getDef(0));
   867     Register tmp1 = ToRegister(ins->getTemp(0));
   868     Register tmp2 = ToRegister(ins->getTemp(1));
   869     MMod *mir = ins->mir();
   870     masm.ma_mod_mask(src, dest, tmp1, tmp2, ins->shift());
   871     if (mir->canBeNegativeDividend()) {
   872         if (!mir->isTruncated()) {
   873             JS_ASSERT(mir->fallible());
   874             if (!bailoutIf(Assembler::Zero, ins->snapshot()))
   875                 return false;
   876         } else {
   877             // -0|0 == 0
   878         }
   879     }
   880     return true;
   881 }
   882 bool
   883 CodeGeneratorARM::visitBitNotI(LBitNotI *ins)
   884 {
   885     const LAllocation *input = ins->getOperand(0);
   886     const LDefinition *dest = ins->getDef(0);
   887     // this will not actually be true on arm.
   888     // We can not an imm8m in order to get a wider range
   889     // of numbers
   890     JS_ASSERT(!input->isConstant());
   892     masm.ma_mvn(ToRegister(input), ToRegister(dest));
   893     return true;
   894 }
   896 bool
   897 CodeGeneratorARM::visitBitOpI(LBitOpI *ins)
   898 {
   899     const LAllocation *lhs = ins->getOperand(0);
   900     const LAllocation *rhs = ins->getOperand(1);
   901     const LDefinition *dest = ins->getDef(0);
   902     // all of these bitops should be either imm32's, or integer registers.
   903     switch (ins->bitop()) {
   904       case JSOP_BITOR:
   905         if (rhs->isConstant())
   906             masm.ma_orr(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest));
   907         else
   908             masm.ma_orr(ToRegister(rhs), ToRegister(lhs), ToRegister(dest));
   909         break;
   910       case JSOP_BITXOR:
   911         if (rhs->isConstant())
   912             masm.ma_eor(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest));
   913         else
   914             masm.ma_eor(ToRegister(rhs), ToRegister(lhs), ToRegister(dest));
   915         break;
   916       case JSOP_BITAND:
   917         if (rhs->isConstant())
   918             masm.ma_and(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest));
   919         else
   920             masm.ma_and(ToRegister(rhs), ToRegister(lhs), ToRegister(dest));
   921         break;
   922       default:
   923         MOZ_ASSUME_UNREACHABLE("unexpected binary opcode");
   924     }
   926     return true;
   927 }
   929 bool
   930 CodeGeneratorARM::visitShiftI(LShiftI *ins)
   931 {
   932     Register lhs = ToRegister(ins->lhs());
   933     const LAllocation *rhs = ins->rhs();
   934     Register dest = ToRegister(ins->output());
   936     if (rhs->isConstant()) {
   937         int32_t shift = ToInt32(rhs) & 0x1F;
   938         switch (ins->bitop()) {
   939           case JSOP_LSH:
   940             if (shift)
   941                 masm.ma_lsl(Imm32(shift), lhs, dest);
   942             else
   943                 masm.ma_mov(lhs, dest);
   944             break;
   945           case JSOP_RSH:
   946             if (shift)
   947                 masm.ma_asr(Imm32(shift), lhs, dest);
   948             else
   949                 masm.ma_mov(lhs, dest);
   950             break;
   951           case JSOP_URSH:
   952             if (shift) {
   953                 masm.ma_lsr(Imm32(shift), lhs, dest);
   954             } else {
   955                 // x >>> 0 can overflow.
   956                 masm.ma_mov(lhs, dest);
   957                 if (ins->mir()->toUrsh()->fallible()) {
   958                     masm.ma_cmp(dest, Imm32(0));
   959                     if (!bailoutIf(Assembler::LessThan, ins->snapshot()))
   960                         return false;
   961                 }
   962             }
   963             break;
   964           default:
   965             MOZ_ASSUME_UNREACHABLE("Unexpected shift op");
   966         }
   967     } else {
   968         // The shift amounts should be AND'ed into the 0-31 range since arm
   969         // shifts by the lower byte of the register (it will attempt to shift
   970         // by 250 if you ask it to).
   971         masm.ma_and(Imm32(0x1F), ToRegister(rhs), dest);
   973         switch (ins->bitop()) {
   974           case JSOP_LSH:
   975             masm.ma_lsl(dest, lhs, dest);
   976             break;
   977           case JSOP_RSH:
   978             masm.ma_asr(dest, lhs, dest);
   979             break;
   980           case JSOP_URSH:
   981             masm.ma_lsr(dest, lhs, dest);
   982             if (ins->mir()->toUrsh()->fallible()) {
   983                 // x >>> 0 can overflow.
   984                 masm.ma_cmp(dest, Imm32(0));
   985                 if (!bailoutIf(Assembler::LessThan, ins->snapshot()))
   986                     return false;
   987             }
   988             break;
   989           default:
   990             MOZ_ASSUME_UNREACHABLE("Unexpected shift op");
   991         }
   992     }
   994     return true;
   995 }
   997 bool
   998 CodeGeneratorARM::visitUrshD(LUrshD *ins)
   999 {
  1000     Register lhs = ToRegister(ins->lhs());
  1001     Register temp = ToRegister(ins->temp());
  1003     const LAllocation *rhs = ins->rhs();
  1004     FloatRegister out = ToFloatRegister(ins->output());
  1006     if (rhs->isConstant()) {
  1007         int32_t shift = ToInt32(rhs) & 0x1F;
  1008         if (shift)
  1009             masm.ma_lsr(Imm32(shift), lhs, temp);
  1010         else
  1011             masm.ma_mov(lhs, temp);
  1012     } else {
  1013         masm.ma_and(Imm32(0x1F), ToRegister(rhs), temp);
  1014         masm.ma_lsr(temp, lhs, temp);
  1017     masm.convertUInt32ToDouble(temp, out);
  1018     return true;
  1021 bool
  1022 CodeGeneratorARM::visitPowHalfD(LPowHalfD *ins)
  1024     FloatRegister input = ToFloatRegister(ins->input());
  1025     FloatRegister output = ToFloatRegister(ins->output());
  1027     Label done;
  1029     // Masm.pow(-Infinity, 0.5) == Infinity.
  1030     masm.ma_vimm(NegativeInfinity<double>(), ScratchFloatReg);
  1031     masm.compareDouble(input, ScratchFloatReg);
  1032     masm.ma_vneg(ScratchFloatReg, output, Assembler::Equal);
  1033     masm.ma_b(&done, Assembler::Equal);
  1035     // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). Adding 0 converts any -0 to 0.
  1036     masm.ma_vimm(0.0, ScratchFloatReg);
  1037     masm.ma_vadd(ScratchFloatReg, input, output);
  1038     masm.ma_vsqrt(output, output);
  1040     masm.bind(&done);
  1041     return true;
  1044 MoveOperand
  1045 CodeGeneratorARM::toMoveOperand(const LAllocation *a) const
  1047     if (a->isGeneralReg())
  1048         return MoveOperand(ToRegister(a));
  1049     if (a->isFloatReg())
  1050         return MoveOperand(ToFloatRegister(a));
  1051     JS_ASSERT((ToStackOffset(a) & 3) == 0);
  1052     int32_t offset = ToStackOffset(a);
  1054     // The way the stack slots work, we assume that everything from depth == 0 downwards is writable
  1055     // however, since our frame is included in this, ensure that the frame gets skipped
  1056     if (gen->compilingAsmJS())
  1057         offset -= AlignmentMidPrologue;
  1059     return MoveOperand(StackPointer, offset);
  1062 class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorARM>
  1064     MTableSwitch *mir_;
  1065     Vector<CodeLabel, 8, IonAllocPolicy> codeLabels_;
  1067     bool accept(CodeGeneratorARM *codegen) {
  1068         return codegen->visitOutOfLineTableSwitch(this);
  1071   public:
  1072     OutOfLineTableSwitch(TempAllocator &alloc, MTableSwitch *mir)
  1073       : mir_(mir),
  1074         codeLabels_(alloc)
  1075     {}
  1077     MTableSwitch *mir() const {
  1078         return mir_;
  1081     bool addCodeLabel(CodeLabel label) {
  1082         return codeLabels_.append(label);
  1084     CodeLabel codeLabel(unsigned i) {
  1085         return codeLabels_[i];
  1087 };
  1089 bool
  1090 CodeGeneratorARM::visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool)
  1092     MTableSwitch *mir = ool->mir();
  1094     size_t numCases = mir->numCases();
  1095     for (size_t i = 0; i < numCases; i++) {
  1096         LBlock *caseblock = mir->getCase(numCases - 1 - i)->lir();
  1097         Label *caseheader = caseblock->label();
  1098         uint32_t caseoffset = caseheader->offset();
  1100         // The entries of the jump table need to be absolute addresses and thus
  1101         // must be patched after codegen is finished.
  1102         CodeLabel cl = ool->codeLabel(i);
  1103         cl.src()->bind(caseoffset);
  1104         if (!masm.addCodeLabel(cl))
  1105             return false;
  1108     return true;
  1111 bool
  1112 CodeGeneratorARM::emitTableSwitchDispatch(MTableSwitch *mir, const Register &index,
  1113                                           const Register &base)
  1115     // the code generated by this is utter hax.
  1116     // the end result looks something like:
  1117     // SUBS index, input, #base
  1118     // RSBSPL index, index, #max
  1119     // LDRPL pc, pc, index lsl 2
  1120     // B default
  1122     // If the range of targets in N through M, we first subtract off the lowest
  1123     // case (N), which both shifts the arguments into the range 0 to (M-N) with
  1124     // and sets the MInus flag if the argument was out of range on the low end.
  1126     // Then we a reverse subtract with the size of the jump table, which will
  1127     // reverse the order of range (It is size through 0, rather than 0 through
  1128     // size).  The main purpose of this is that we set the same flag as the lower
  1129     // bound check for the upper bound check.  Lastly, we do this conditionally
  1130     // on the previous check succeeding.
  1132     // Then we conditionally load the pc offset by the (reversed) index (times
  1133     // the address size) into the pc, which branches to the correct case.
  1134     // NOTE: when we go to read the pc, the value that we get back is the pc of
  1135     // the current instruction *PLUS 8*.  This means that ldr foo, [pc, +0]
  1136     // reads $pc+8.  In other words, there is an empty word after the branch into
  1137     // the switch table before the table actually starts.  Since the only other
  1138     // unhandled case is the default case (both out of range high and out of range low)
  1139     // I then insert a branch to default case into the extra slot, which ensures
  1140     // we don't attempt to execute the address table.
  1141     Label *defaultcase = mir->getDefault()->lir()->label();
  1143     int32_t cases = mir->numCases();
  1144     // Lower value with low value
  1145     masm.ma_sub(index, Imm32(mir->low()), index, SetCond);
  1146     masm.ma_rsb(index, Imm32(cases - 1), index, SetCond, Assembler::NotSigned);
  1147     AutoForbidPools afp(&masm);
  1148     masm.ma_ldr(DTRAddr(pc, DtrRegImmShift(index, LSL, 2)), pc, Offset, Assembler::NotSigned);
  1149     masm.ma_b(defaultcase);
  1151     // To fill in the CodeLabels for the case entries, we need to first
  1152     // generate the case entries (we don't yet know their offsets in the
  1153     // instruction stream).
  1154     OutOfLineTableSwitch *ool = new(alloc()) OutOfLineTableSwitch(alloc(), mir);
  1155     for (int32_t i = 0; i < cases; i++) {
  1156         CodeLabel cl;
  1157         masm.writeCodePointer(cl.dest());
  1158         if (!ool->addCodeLabel(cl))
  1159             return false;
  1161     if (!addOutOfLineCode(ool))
  1162         return false;
  1164     return true;
  1167 bool
  1168 CodeGeneratorARM::visitMathD(LMathD *math)
  1170     const LAllocation *src1 = math->getOperand(0);
  1171     const LAllocation *src2 = math->getOperand(1);
  1172     const LDefinition *output = math->getDef(0);
  1174     switch (math->jsop()) {
  1175       case JSOP_ADD:
  1176         masm.ma_vadd(ToFloatRegister(src1), ToFloatRegister(src2), ToFloatRegister(output));
  1177         break;
  1178       case JSOP_SUB:
  1179         masm.ma_vsub(ToFloatRegister(src1), ToFloatRegister(src2), ToFloatRegister(output));
  1180         break;
  1181       case JSOP_MUL:
  1182         masm.ma_vmul(ToFloatRegister(src1), ToFloatRegister(src2), ToFloatRegister(output));
  1183         break;
  1184       case JSOP_DIV:
  1185         masm.ma_vdiv(ToFloatRegister(src1), ToFloatRegister(src2), ToFloatRegister(output));
  1186         break;
  1187       default:
  1188         MOZ_ASSUME_UNREACHABLE("unexpected opcode");
  1190     return true;
  1193 bool
  1194 CodeGeneratorARM::visitMathF(LMathF *math)
  1196     const LAllocation *src1 = math->getOperand(0);
  1197     const LAllocation *src2 = math->getOperand(1);
  1198     const LDefinition *output = math->getDef(0);
  1200     switch (math->jsop()) {
  1201       case JSOP_ADD:
  1202         masm.ma_vadd_f32(ToFloatRegister(src1), ToFloatRegister(src2), ToFloatRegister(output));
  1203         break;
  1204       case JSOP_SUB:
  1205         masm.ma_vsub_f32(ToFloatRegister(src1), ToFloatRegister(src2), ToFloatRegister(output));
  1206         break;
  1207       case JSOP_MUL:
  1208         masm.ma_vmul_f32(ToFloatRegister(src1), ToFloatRegister(src2), ToFloatRegister(output));
  1209         break;
  1210       case JSOP_DIV:
  1211         masm.ma_vdiv_f32(ToFloatRegister(src1), ToFloatRegister(src2), ToFloatRegister(output));
  1212         break;
  1213       default:
  1214         MOZ_ASSUME_UNREACHABLE("unexpected opcode");
  1216     return true;
  1219 bool
  1220 CodeGeneratorARM::visitFloor(LFloor *lir)
  1222     FloatRegister input = ToFloatRegister(lir->input());
  1223     Register output = ToRegister(lir->output());
  1224     Label bail;
  1225     masm.floor(input, output, &bail);
  1226     if (!bailoutFrom(&bail, lir->snapshot()))
  1227         return false;
  1228     return true;
  1231 bool
  1232 CodeGeneratorARM::visitFloorF(LFloorF *lir)
  1234     FloatRegister input = ToFloatRegister(lir->input());
  1235     Register output = ToRegister(lir->output());
  1236     Label bail;
  1237     masm.floorf(input, output, &bail);
  1238     if (!bailoutFrom(&bail, lir->snapshot()))
  1239         return false;
  1240     return true;
  1243 bool
  1244 CodeGeneratorARM::visitRound(LRound *lir)
  1246     FloatRegister input = ToFloatRegister(lir->input());
  1247     Register output = ToRegister(lir->output());
  1248     FloatRegister tmp = ToFloatRegister(lir->temp());
  1249     Label bail;
  1250     // Output is either correct, or clamped.  All -0 cases have been translated to a clamped
  1251     // case.a
  1252     masm.round(input, output, &bail, tmp);
  1253     if (!bailoutFrom(&bail, lir->snapshot()))
  1254         return false;
  1255     return true;
  1258 bool
  1259 CodeGeneratorARM::visitRoundF(LRoundF *lir)
  1261     FloatRegister input = ToFloatRegister(lir->input());
  1262     Register output = ToRegister(lir->output());
  1263     FloatRegister tmp = ToFloatRegister(lir->temp());
  1264     Label bail;
  1265     // Output is either correct, or clamped.  All -0 cases have been translated to a clamped
  1266     // case.a
  1267     masm.roundf(input, output, &bail, tmp);
  1268     if (!bailoutFrom(&bail, lir->snapshot()))
  1269         return false;
  1270     return true;
  1273 void
  1274 CodeGeneratorARM::emitRoundDouble(const FloatRegister &src, const Register &dest, Label *fail)
  1276     masm.ma_vcvt_F64_I32(src, ScratchFloatReg);
  1277     masm.ma_vxfer(ScratchFloatReg, dest);
  1278     masm.ma_cmp(dest, Imm32(0x7fffffff));
  1279     masm.ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
  1280     masm.ma_b(fail, Assembler::Equal);
  1283 bool
  1284 CodeGeneratorARM::visitTruncateDToInt32(LTruncateDToInt32 *ins)
  1286     return emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()));
  1289 bool
  1290 CodeGeneratorARM::visitTruncateFToInt32(LTruncateFToInt32 *ins)
  1292     return emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output()));
  1295 static const uint32_t FrameSizes[] = { 128, 256, 512, 1024 };
  1297 FrameSizeClass
  1298 FrameSizeClass::FromDepth(uint32_t frameDepth)
  1300     for (uint32_t i = 0; i < JS_ARRAY_LENGTH(FrameSizes); i++) {
  1301         if (frameDepth < FrameSizes[i])
  1302             return FrameSizeClass(i);
  1305     return FrameSizeClass::None();
  1308 FrameSizeClass
  1309 FrameSizeClass::ClassLimit()
  1311     return FrameSizeClass(JS_ARRAY_LENGTH(FrameSizes));
  1314 uint32_t
  1315 FrameSizeClass::frameSize() const
  1317     JS_ASSERT(class_ != NO_FRAME_SIZE_CLASS_ID);
  1318     JS_ASSERT(class_ < JS_ARRAY_LENGTH(FrameSizes));
  1320     return FrameSizes[class_];
  1323 ValueOperand
  1324 CodeGeneratorARM::ToValue(LInstruction *ins, size_t pos)
  1326     Register typeReg = ToRegister(ins->getOperand(pos + TYPE_INDEX));
  1327     Register payloadReg = ToRegister(ins->getOperand(pos + PAYLOAD_INDEX));
  1328     return ValueOperand(typeReg, payloadReg);
  1331 ValueOperand
  1332 CodeGeneratorARM::ToOutValue(LInstruction *ins)
  1334     Register typeReg = ToRegister(ins->getDef(TYPE_INDEX));
  1335     Register payloadReg = ToRegister(ins->getDef(PAYLOAD_INDEX));
  1336     return ValueOperand(typeReg, payloadReg);
  1339 ValueOperand
  1340 CodeGeneratorARM::ToTempValue(LInstruction *ins, size_t pos)
  1342     Register typeReg = ToRegister(ins->getTemp(pos + TYPE_INDEX));
  1343     Register payloadReg = ToRegister(ins->getTemp(pos + PAYLOAD_INDEX));
  1344     return ValueOperand(typeReg, payloadReg);
  1347 bool
  1348 CodeGeneratorARM::visitValue(LValue *value)
  1350     const ValueOperand out = ToOutValue(value);
  1352     masm.moveValue(value->value(), out);
  1353     return true;
  1356 bool
  1357 CodeGeneratorARM::visitBox(LBox *box)
  1359     const LDefinition *type = box->getDef(TYPE_INDEX);
  1361     JS_ASSERT(!box->getOperand(0)->isConstant());
  1363     // On x86, the input operand and the output payload have the same
  1364     // virtual register. All that needs to be written is the type tag for
  1365     // the type definition.
  1366     masm.ma_mov(Imm32(MIRTypeToTag(box->type())), ToRegister(type));
  1367     return true;
  1370 bool
  1371 CodeGeneratorARM::visitBoxFloatingPoint(LBoxFloatingPoint *box)
  1373     const LDefinition *payload = box->getDef(PAYLOAD_INDEX);
  1374     const LDefinition *type = box->getDef(TYPE_INDEX);
  1375     const LAllocation *in = box->getOperand(0);
  1377     FloatRegister reg = ToFloatRegister(in);
  1378     if (box->type() == MIRType_Float32) {
  1379         masm.convertFloat32ToDouble(reg, ScratchFloatReg);
  1380         reg = ScratchFloatReg;
  1383     //masm.as_vxfer(ToRegister(payload), ToRegister(type),
  1384     //              VFPRegister(ToFloatRegister(in)), Assembler::FloatToCore);
  1385     masm.ma_vxfer(VFPRegister(reg), ToRegister(payload), ToRegister(type));
  1386     return true;
  1389 bool
  1390 CodeGeneratorARM::visitUnbox(LUnbox *unbox)
  1392     // Note that for unbox, the type and payload indexes are switched on the
  1393     // inputs.
  1394     MUnbox *mir = unbox->mir();
  1395     Register type = ToRegister(unbox->type());
  1397     if (mir->fallible()) {
  1398         masm.ma_cmp(type, Imm32(MIRTypeToTag(mir->type())));
  1399         if (!bailoutIf(Assembler::NotEqual, unbox->snapshot()))
  1400             return false;
  1402     return true;
  1405 bool
  1406 CodeGeneratorARM::visitDouble(LDouble *ins)
  1409     const LDefinition *out = ins->getDef(0);
  1411     masm.ma_vimm(ins->getDouble(), ToFloatRegister(out));
  1412     return true;
  1415 bool
  1416 CodeGeneratorARM::visitFloat32(LFloat32 *ins)
  1418     const LDefinition *out = ins->getDef(0);
  1419     masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out));
  1420     return true;
  1423 Register
  1424 CodeGeneratorARM::splitTagForTest(const ValueOperand &value)
  1426     return value.typeReg();
  1429 bool
  1430 CodeGeneratorARM::visitTestDAndBranch(LTestDAndBranch *test)
  1432     const LAllocation *opd = test->input();
  1433     masm.ma_vcmpz(ToFloatRegister(opd));
  1434     masm.as_vmrs(pc);
  1436     MBasicBlock *ifTrue = test->ifTrue();
  1437     MBasicBlock *ifFalse = test->ifFalse();
  1438     // If the compare set the  0 bit, then the result
  1439     // is definately false.
  1440     jumpToBlock(ifFalse, Assembler::Zero);
  1441     // it is also false if one of the operands is NAN, which is
  1442     // shown as Overflow.
  1443     jumpToBlock(ifFalse, Assembler::Overflow);
  1444     jumpToBlock(ifTrue);
  1445     return true;
  1448 bool
  1449 CodeGeneratorARM::visitTestFAndBranch(LTestFAndBranch *test)
  1451     const LAllocation *opd = test->input();
  1452     masm.ma_vcmpz_f32(ToFloatRegister(opd));
  1453     masm.as_vmrs(pc);
  1455     MBasicBlock *ifTrue = test->ifTrue();
  1456     MBasicBlock *ifFalse = test->ifFalse();
  1457     // If the compare set the  0 bit, then the result
  1458     // is definately false.
  1459     jumpToBlock(ifFalse, Assembler::Zero);
  1460     // it is also false if one of the operands is NAN, which is
  1461     // shown as Overflow.
  1462     jumpToBlock(ifFalse, Assembler::Overflow);
  1463     jumpToBlock(ifTrue);
  1464     return true;
  1467 bool
  1468 CodeGeneratorARM::visitCompareD(LCompareD *comp)
  1470     FloatRegister lhs = ToFloatRegister(comp->left());
  1471     FloatRegister rhs = ToFloatRegister(comp->right());
  1473     Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
  1474     masm.compareDouble(lhs, rhs);
  1475     masm.emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()));
  1476     return true;
  1479 bool
  1480 CodeGeneratorARM::visitCompareF(LCompareF *comp)
  1482     FloatRegister lhs = ToFloatRegister(comp->left());
  1483     FloatRegister rhs = ToFloatRegister(comp->right());
  1485     Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
  1486     masm.compareFloat(lhs, rhs);
  1487     masm.emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()));
  1488     return true;
  1491 bool
  1492 CodeGeneratorARM::visitCompareDAndBranch(LCompareDAndBranch *comp)
  1494     FloatRegister lhs = ToFloatRegister(comp->left());
  1495     FloatRegister rhs = ToFloatRegister(comp->right());
  1497     Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop());
  1498     masm.compareDouble(lhs, rhs);
  1499     emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(), comp->ifFalse());
  1500     return true;
  1503 bool
  1504 CodeGeneratorARM::visitCompareFAndBranch(LCompareFAndBranch *comp)
  1506     FloatRegister lhs = ToFloatRegister(comp->left());
  1507     FloatRegister rhs = ToFloatRegister(comp->right());
  1509     Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop());
  1510     masm.compareFloat(lhs, rhs);
  1511     emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(), comp->ifFalse());
  1512     return true;
  1515 bool
  1516 CodeGeneratorARM::visitCompareB(LCompareB *lir)
  1518     MCompare *mir = lir->mir();
  1520     const ValueOperand lhs = ToValue(lir, LCompareB::Lhs);
  1521     const LAllocation *rhs = lir->rhs();
  1522     const Register output = ToRegister(lir->output());
  1524     JS_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
  1526     Label notBoolean, done;
  1527     masm.branchTestBoolean(Assembler::NotEqual, lhs, &notBoolean);
  1529         if (rhs->isConstant())
  1530             masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean()));
  1531         else
  1532             masm.cmp32(lhs.payloadReg(), ToRegister(rhs));
  1533         masm.emitSet(JSOpToCondition(mir->compareType(), mir->jsop()), output);
  1534         masm.jump(&done);
  1537     masm.bind(&notBoolean);
  1539         masm.move32(Imm32(mir->jsop() == JSOP_STRICTNE), output);
  1542     masm.bind(&done);
  1543     return true;
  1546 bool
  1547 CodeGeneratorARM::visitCompareBAndBranch(LCompareBAndBranch *lir)
  1549     MCompare *mir = lir->cmpMir();
  1550     const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs);
  1551     const LAllocation *rhs = lir->rhs();
  1553     JS_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
  1555     Assembler::Condition cond = masm.testBoolean(Assembler::NotEqual, lhs);
  1556     jumpToBlock((mir->jsop() == JSOP_STRICTEQ) ? lir->ifFalse() : lir->ifTrue(), cond);
  1558     if (rhs->isConstant())
  1559         masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean()));
  1560     else
  1561         masm.cmp32(lhs.payloadReg(), ToRegister(rhs));
  1562     emitBranch(JSOpToCondition(mir->compareType(), mir->jsop()), lir->ifTrue(), lir->ifFalse());
  1563     return true;
  1566 bool
  1567 CodeGeneratorARM::visitCompareV(LCompareV *lir)
  1569     MCompare *mir = lir->mir();
  1570     Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
  1571     const ValueOperand lhs = ToValue(lir, LCompareV::LhsInput);
  1572     const ValueOperand rhs = ToValue(lir, LCompareV::RhsInput);
  1573     const Register output = ToRegister(lir->output());
  1575     JS_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
  1576               mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
  1578     Label notEqual, done;
  1579     masm.cmp32(lhs.typeReg(), rhs.typeReg());
  1580     masm.j(Assembler::NotEqual, &notEqual);
  1582         masm.cmp32(lhs.payloadReg(), rhs.payloadReg());
  1583         masm.emitSet(cond, output);
  1584         masm.jump(&done);
  1586     masm.bind(&notEqual);
  1588         masm.move32(Imm32(cond == Assembler::NotEqual), output);
  1591     masm.bind(&done);
  1592     return true;
  1595 bool
  1596 CodeGeneratorARM::visitCompareVAndBranch(LCompareVAndBranch *lir)
  1598     MCompare *mir = lir->cmpMir();
  1599     Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
  1600     const ValueOperand lhs = ToValue(lir, LCompareVAndBranch::LhsInput);
  1601     const ValueOperand rhs = ToValue(lir, LCompareVAndBranch::RhsInput);
  1603     JS_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
  1604               mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
  1606     MBasicBlock *notEqual = (cond == Assembler::Equal) ? lir->ifFalse() : lir->ifTrue();
  1608     masm.cmp32(lhs.typeReg(), rhs.typeReg());
  1609     jumpToBlock(notEqual, Assembler::NotEqual);
  1610     masm.cmp32(lhs.payloadReg(), rhs.payloadReg());
  1611     emitBranch(cond, lir->ifTrue(), lir->ifFalse());
  1613     return true;
  1616 bool
  1617 CodeGeneratorARM::visitBitAndAndBranch(LBitAndAndBranch *baab)
  1619     if (baab->right()->isConstant())
  1620         masm.ma_tst(ToRegister(baab->left()), Imm32(ToInt32(baab->right())));
  1621     else
  1622         masm.ma_tst(ToRegister(baab->left()), ToRegister(baab->right()));
  1623     emitBranch(Assembler::NonZero, baab->ifTrue(), baab->ifFalse());
  1624     return true;
  1627 bool
  1628 CodeGeneratorARM::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble *lir)
  1630     masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
  1631     return true;
  1634 bool
  1635 CodeGeneratorARM::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32 *lir)
  1637     masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output()));
  1638     return true;
  1641 bool
  1642 CodeGeneratorARM::visitNotI(LNotI *ins)
  1644     // It is hard to optimize !x, so just do it the basic way for now.
  1645     masm.ma_cmp(ToRegister(ins->input()), Imm32(0));
  1646     masm.emitSet(Assembler::Equal, ToRegister(ins->output()));
  1647     return true;
  1650 bool
  1651 CodeGeneratorARM::visitNotD(LNotD *ins)
  1653     // Since this operation is not, we want to set a bit if
  1654     // the double is falsey, which means 0.0, -0.0 or NaN.
  1655     // when comparing with 0, an input of 0 will set the Z bit (30)
  1656     // and NaN will set the V bit (28) of the APSR.
  1657     FloatRegister opd = ToFloatRegister(ins->input());
  1658     Register dest = ToRegister(ins->output());
  1660     // Do the compare
  1661     masm.ma_vcmpz(opd);
  1662     // TODO There are three variations here to compare performance-wise.
  1663     bool nocond = true;
  1664     if (nocond) {
  1665         // Load the value into the dest register
  1666         masm.as_vmrs(dest);
  1667         masm.ma_lsr(Imm32(28), dest, dest);
  1668         masm.ma_alu(dest, lsr(dest, 2), dest, op_orr); // 28 + 2 = 30
  1669         masm.ma_and(Imm32(1), dest);
  1670     } else {
  1671         masm.as_vmrs(pc);
  1672         masm.ma_mov(Imm32(0), dest);
  1673         masm.ma_mov(Imm32(1), dest, NoSetCond, Assembler::Equal);
  1674         masm.ma_mov(Imm32(1), dest, NoSetCond, Assembler::Overflow);
  1676     return true;
  1679 bool
  1680 CodeGeneratorARM::visitNotF(LNotF *ins)
  1682     // Since this operation is not, we want to set a bit if
  1683     // the double is falsey, which means 0.0, -0.0 or NaN.
  1684     // when comparing with 0, an input of 0 will set the Z bit (30)
  1685     // and NaN will set the V bit (28) of the APSR.
  1686     FloatRegister opd = ToFloatRegister(ins->input());
  1687     Register dest = ToRegister(ins->output());
  1689     // Do the compare
  1690     masm.ma_vcmpz_f32(opd);
  1691     // TODO There are three variations here to compare performance-wise.
  1692     bool nocond = true;
  1693     if (nocond) {
  1694         // Load the value into the dest register
  1695         masm.as_vmrs(dest);
  1696         masm.ma_lsr(Imm32(28), dest, dest);
  1697         masm.ma_alu(dest, lsr(dest, 2), dest, op_orr); // 28 + 2 = 30
  1698         masm.ma_and(Imm32(1), dest);
  1699     } else {
  1700         masm.as_vmrs(pc);
  1701         masm.ma_mov(Imm32(0), dest);
  1702         masm.ma_mov(Imm32(1), dest, NoSetCond, Assembler::Equal);
  1703         masm.ma_mov(Imm32(1), dest, NoSetCond, Assembler::Overflow);
  1705     return true;
  1708 bool
  1709 CodeGeneratorARM::visitLoadSlotV(LLoadSlotV *load)
  1711     const ValueOperand out = ToOutValue(load);
  1712     Register base = ToRegister(load->input());
  1713     int32_t offset = load->mir()->slot() * sizeof(js::Value);
  1715     masm.loadValue(Address(base, offset), out);
  1716     return true;
  1719 bool
  1720 CodeGeneratorARM::visitLoadSlotT(LLoadSlotT *load)
  1722     Register base = ToRegister(load->input());
  1723     int32_t offset = load->mir()->slot() * sizeof(js::Value);
  1725     if (load->mir()->type() == MIRType_Double)
  1726         masm.loadInt32OrDouble(Operand(base, offset), ToFloatRegister(load->output()));
  1727     else
  1728         masm.ma_ldr(Operand(base, offset + NUNBOX32_PAYLOAD_OFFSET), ToRegister(load->output()));
  1729     return true;
  1732 bool
  1733 CodeGeneratorARM::visitStoreSlotT(LStoreSlotT *store)
  1736     Register base = ToRegister(store->slots());
  1737     int32_t offset = store->mir()->slot() * sizeof(js::Value);
  1739     const LAllocation *value = store->value();
  1740     MIRType valueType = store->mir()->value()->type();
  1742     if (store->mir()->needsBarrier())
  1743         emitPreBarrier(Address(base, offset), store->mir()->slotType());
  1745     if (valueType == MIRType_Double) {
  1746         masm.ma_vstr(ToFloatRegister(value), Operand(base, offset));
  1747         return true;
  1750     // Store the type tag if needed.
  1751     if (valueType != store->mir()->slotType())
  1752         masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), Operand(base, offset));
  1754     // Store the payload.
  1755     if (value->isConstant())
  1756         masm.storePayload(*value->toConstant(), Operand(base, offset));
  1757     else
  1758         masm.storePayload(ToRegister(value), Operand(base, offset));
  1760     return true;
  1763 bool
  1764 CodeGeneratorARM::visitLoadElementT(LLoadElementT *load)
  1766     Register base = ToRegister(load->elements());
  1767     if (load->mir()->type() == MIRType_Double) {
  1768         FloatRegister fpreg = ToFloatRegister(load->output());
  1769         if (load->index()->isConstant()) {
  1770             Address source(base, ToInt32(load->index()) * sizeof(Value));
  1771             if (load->mir()->loadDoubles())
  1772                 masm.loadDouble(source, fpreg);
  1773             else
  1774                 masm.loadInt32OrDouble(source, fpreg);
  1775         } else {
  1776             Register index = ToRegister(load->index());
  1777             if (load->mir()->loadDoubles())
  1778                 masm.loadDouble(BaseIndex(base, index, TimesEight), fpreg);
  1779             else
  1780                 masm.loadInt32OrDouble(base, index, fpreg);
  1782     } else {
  1783         if (load->index()->isConstant()) {
  1784             Address source(base, ToInt32(load->index()) * sizeof(Value));
  1785             masm.load32(source, ToRegister(load->output()));
  1786         } else {
  1787             masm.ma_ldr(DTRAddr(base, DtrRegImmShift(ToRegister(load->index()), LSL, 3)),
  1788                         ToRegister(load->output()));
  1791     JS_ASSERT(!load->mir()->needsHoleCheck());
  1792     return true;
  1795 void
  1796 CodeGeneratorARM::storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
  1797                                     const Register &elements, const LAllocation *index)
  1799     if (index->isConstant()) {
  1800         Address dest = Address(elements, ToInt32(index) * sizeof(Value));
  1801         if (valueType == MIRType_Double) {
  1802             masm.ma_vstr(ToFloatRegister(value), Operand(dest));
  1803             return;
  1806         // Store the type tag if needed.
  1807         if (valueType != elementType)
  1808             masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest);
  1810         // Store the payload.
  1811         if (value->isConstant())
  1812             masm.storePayload(*value->toConstant(), dest);
  1813         else
  1814             masm.storePayload(ToRegister(value), dest);
  1815     } else {
  1816         Register indexReg = ToRegister(index);
  1817         if (valueType == MIRType_Double) {
  1818             masm.ma_vstr(ToFloatRegister(value), elements, indexReg);
  1819             return;
  1822         // Store the type tag if needed.
  1823         if (valueType != elementType)
  1824             masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), elements, indexReg);
  1826         // Store the payload.
  1827         if (value->isConstant())
  1828             masm.storePayload(*value->toConstant(), elements, indexReg);
  1829         else
  1830             masm.storePayload(ToRegister(value), elements, indexReg);
  1834 bool
  1835 CodeGeneratorARM::visitGuardShape(LGuardShape *guard)
  1837     Register obj = ToRegister(guard->input());
  1838     Register tmp = ToRegister(guard->tempInt());
  1840     masm.ma_ldr(DTRAddr(obj, DtrOffImm(JSObject::offsetOfShape())), tmp);
  1841     masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->shape()));
  1843     return bailoutIf(Assembler::NotEqual, guard->snapshot());
  1846 bool
  1847 CodeGeneratorARM::visitGuardObjectType(LGuardObjectType *guard)
  1849     Register obj = ToRegister(guard->input());
  1850     Register tmp = ToRegister(guard->tempInt());
  1852     masm.ma_ldr(DTRAddr(obj, DtrOffImm(JSObject::offsetOfType())), tmp);
  1853     masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->typeObject()));
  1855     Assembler::Condition cond =
  1856         guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
  1857     return bailoutIf(cond, guard->snapshot());
  1860 bool
  1861 CodeGeneratorARM::visitGuardClass(LGuardClass *guard)
  1863     Register obj = ToRegister(guard->input());
  1864     Register tmp = ToRegister(guard->tempInt());
  1866     masm.loadObjClass(obj, tmp);
  1867     masm.ma_cmp(tmp, Imm32((uint32_t)guard->mir()->getClass()));
  1868     if (!bailoutIf(Assembler::NotEqual, guard->snapshot()))
  1869         return false;
  1870     return true;
  1873 bool
  1874 CodeGeneratorARM::visitImplicitThis(LImplicitThis *lir)
  1876     Register callee = ToRegister(lir->callee());
  1877     const ValueOperand out = ToOutValue(lir);
  1879     // The implicit |this| is always |undefined| if the function's environment
  1880     // is the current global.
  1881     masm.ma_ldr(DTRAddr(callee, DtrOffImm(JSFunction::offsetOfEnvironment())), out.typeReg());
  1882     masm.ma_cmp(out.typeReg(), ImmGCPtr(&gen->info().script()->global()));
  1884     // TODO: OOL stub path.
  1885     if (!bailoutIf(Assembler::NotEqual, lir->snapshot()))
  1886         return false;
  1888     masm.moveValue(UndefinedValue(), out);
  1889     return true;
  1892 bool
  1893 CodeGeneratorARM::visitInterruptCheck(LInterruptCheck *lir)
  1895     OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
  1896     if (!ool)
  1897         return false;
  1899     void *interrupt = (void*)GetIonContext()->runtime->addressOfInterrupt();
  1900     masm.load32(AbsoluteAddress(interrupt), lr);
  1901     masm.ma_cmp(lr, Imm32(0));
  1902     masm.ma_b(ool->entry(), Assembler::NonZero);
  1903     masm.bind(ool->rejoin());
  1904     return true;
  1907 bool
  1908 CodeGeneratorARM::generateInvalidateEpilogue()
  1910     // Ensure that there is enough space in the buffer for the OsiPoint
  1911     // patching to occur. Otherwise, we could overwrite the invalidation
  1912     // epilogue.
  1913     for (size_t i = 0; i < sizeof(void *); i+= Assembler::nopSize())
  1914         masm.nop();
  1916     masm.bind(&invalidate_);
  1918     // Push the return address of the point that we bailed out at onto the stack
  1919     masm.Push(lr);
  1921     // Push the Ion script onto the stack (when we determine what that pointer is).
  1922     invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1)));
  1923     JitCode *thunk = gen->jitRuntime()->getInvalidationThunk();
  1925     masm.branch(thunk);
  1927     // We should never reach this point in JIT code -- the invalidation thunk should
  1928     // pop the invalidated JS frame and return directly to its caller.
  1929     masm.assumeUnreachable("Should have returned directly to its caller instead of here.");
  1930     return true;
  1933 void
  1934 DispatchIonCache::initializeAddCacheState(LInstruction *ins, AddCacheState *addState)
  1936     // Can always use the scratch register on ARM.
  1937     addState->dispatchScratch = ScratchRegister;
  1940 template <class U>
  1941 Register
  1942 getBase(U *mir)
  1944     switch (mir->base()) {
  1945       case U::Heap: return HeapReg;
  1946       case U::Global: return GlobalReg;
  1948     return InvalidReg;
  1951 bool
  1952 CodeGeneratorARM::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins)
  1954     MOZ_ASSUME_UNREACHABLE("NYI");
  1957 bool
  1958 CodeGeneratorARM::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins)
  1960     MOZ_ASSUME_UNREACHABLE("NYI");
  1963 bool
  1964 CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
  1966     const MAsmJSLoadHeap *mir = ins->mir();
  1967     bool isSigned;
  1968     int size;
  1969     bool isFloat = false;
  1970     switch (mir->viewType()) {
  1971       case ArrayBufferView::TYPE_INT8:    isSigned = true;  size =  8; break;
  1972       case ArrayBufferView::TYPE_UINT8:   isSigned = false; size =  8; break;
  1973       case ArrayBufferView::TYPE_INT16:   isSigned = true;  size = 16; break;
  1974       case ArrayBufferView::TYPE_UINT16:  isSigned = false; size = 16; break;
  1975       case ArrayBufferView::TYPE_INT32:
  1976       case ArrayBufferView::TYPE_UINT32:  isSigned = true;  size = 32; break;
  1977       case ArrayBufferView::TYPE_FLOAT64: isFloat = true;   size = 64; break;
  1978       case ArrayBufferView::TYPE_FLOAT32: isFloat = true;   size = 32; break;
  1979       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
  1982     const LAllocation *ptr = ins->ptr();
  1984     if (ptr->isConstant()) {
  1985         JS_ASSERT(mir->skipBoundsCheck());
  1986         int32_t ptrImm = ptr->toConstant()->toInt32();
  1987         JS_ASSERT(ptrImm >= 0);
  1988         if (isFloat) {
  1989             VFPRegister vd(ToFloatRegister(ins->output()));
  1990             if (size == 32)
  1991                 masm.ma_vldr(Operand(HeapReg, ptrImm), vd.singleOverlay(), Assembler::Always);
  1992             else
  1993                 masm.ma_vldr(Operand(HeapReg, ptrImm), vd, Assembler::Always);
  1994         }  else {
  1995             masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, Imm32(ptrImm),
  1996                                   ToRegister(ins->output()), Offset, Assembler::Always);
  1998         return true;
  2001     Register ptrReg = ToRegister(ptr);
  2003     if (mir->skipBoundsCheck()) {
  2004         if (isFloat) {
  2005             VFPRegister vd(ToFloatRegister(ins->output()));
  2006             if (size == 32)
  2007                 masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Always);
  2008             else
  2009                 masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Always);
  2010         } else {
  2011             masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg,
  2012                                   ToRegister(ins->output()), Offset, Assembler::Always);
  2014         return true;
  2017     BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
  2018     if (isFloat) {
  2019         FloatRegister dst = ToFloatRegister(ins->output());
  2020         VFPRegister vd(dst);
  2021         if (size == 32) {
  2022             masm.convertDoubleToFloat32(NANReg, dst, Assembler::AboveOrEqual);
  2023             masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Below);
  2024         } else {
  2025             masm.ma_vmov(NANReg, dst, Assembler::AboveOrEqual);
  2026             masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Below);
  2028     } else {
  2029         Register d = ToRegister(ins->output());
  2030         masm.ma_mov(Imm32(0), d, NoSetCond, Assembler::AboveOrEqual);
  2031         masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, d, Offset, Assembler::Below);
  2033     return masm.append(AsmJSHeapAccess(bo.getOffset()));
  2036 bool
  2037 CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
  2039     const MAsmJSStoreHeap *mir = ins->mir();
  2040     bool isSigned;
  2041     int size;
  2042     bool isFloat = false;
  2043     switch (mir->viewType()) {
  2044       case ArrayBufferView::TYPE_INT8:
  2045       case ArrayBufferView::TYPE_UINT8:   isSigned = false; size = 8; break;
  2046       case ArrayBufferView::TYPE_INT16:
  2047       case ArrayBufferView::TYPE_UINT16:  isSigned = false; size = 16; break;
  2048       case ArrayBufferView::TYPE_INT32:
  2049       case ArrayBufferView::TYPE_UINT32:  isSigned = true;  size = 32; break;
  2050       case ArrayBufferView::TYPE_FLOAT64: isFloat  = true;  size = 64; break;
  2051       case ArrayBufferView::TYPE_FLOAT32: isFloat = true;   size = 32; break;
  2052       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
  2054     const LAllocation *ptr = ins->ptr();
  2055     if (ptr->isConstant()) {
  2056         JS_ASSERT(mir->skipBoundsCheck());
  2057         int32_t ptrImm = ptr->toConstant()->toInt32();
  2058         JS_ASSERT(ptrImm >= 0);
  2059         if (isFloat) {
  2060             VFPRegister vd(ToFloatRegister(ins->value()));
  2061             if (size == 32)
  2062                 masm.ma_vstr(vd.singleOverlay(), Operand(HeapReg, ptrImm), Assembler::Always);
  2063             else
  2064                 masm.ma_vstr(vd, Operand(HeapReg, ptrImm), Assembler::Always);
  2065         } else {
  2066             masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm),
  2067                                   ToRegister(ins->value()), Offset, Assembler::Always);
  2069         return true;
  2072     Register ptrReg = ToRegister(ptr);
  2074     if (mir->skipBoundsCheck()) {
  2075         Register ptrReg = ToRegister(ptr);
  2076         if (isFloat) {
  2077             VFPRegister vd(ToFloatRegister(ins->value()));
  2078             if (size == 32)
  2079                 masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Always);
  2080             else
  2081                 masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Always);
  2082         } else {
  2083             masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
  2084                                   ToRegister(ins->value()), Offset, Assembler::Always);
  2086         return true;
  2089     BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
  2090     if (isFloat) {
  2091         VFPRegister vd(ToFloatRegister(ins->value()));
  2092         if (size == 32)
  2093             masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Below);
  2094         else
  2095             masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Below);
  2096     } else {
  2097         masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
  2098                               ToRegister(ins->value()), Offset, Assembler::Below);
  2100     return masm.append(AsmJSHeapAccess(bo.getOffset()));
  2103 bool
  2104 CodeGeneratorARM::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins)
  2106     const MAsmJSPassStackArg *mir = ins->mir();
  2107     Operand dst(StackPointer, mir->spOffset());
  2108     if (ins->arg()->isConstant()) {
  2109         //masm.as_bkpt();
  2110         masm.ma_storeImm(Imm32(ToInt32(ins->arg())), dst);
  2111     } else {
  2112         if (ins->arg()->isGeneralReg())
  2113             masm.ma_str(ToRegister(ins->arg()), dst);
  2114         else
  2115             masm.ma_vstr(ToFloatRegister(ins->arg()), dst);
  2118     return true;
  2121 bool
  2122 CodeGeneratorARM::visitUDiv(LUDiv *ins)
  2124     Register lhs = ToRegister(ins->lhs());
  2125     Register rhs = ToRegister(ins->rhs());
  2126     Register output = ToRegister(ins->output());
  2128     Label done;
  2129     if (ins->mir()->canBeDivideByZero()) {
  2130         masm.ma_cmp(rhs, Imm32(0));
  2131         if (ins->mir()->isTruncated()) {
  2132             // Infinity|0 == 0
  2133             Label skip;
  2134             masm.ma_b(&skip, Assembler::NotEqual);
  2135             masm.ma_mov(Imm32(0), output);
  2136             masm.ma_b(&done);
  2137             masm.bind(&skip);
  2138         } else {
  2139             JS_ASSERT(ins->mir()->fallible());
  2140             if (!bailoutIf(Assembler::Equal, ins->snapshot()))
  2141                 return false;
  2145     masm.ma_udiv(lhs, rhs, output);
  2147     if (!ins->mir()->isTruncated()) {
  2148         masm.ma_cmp(output, Imm32(0));
  2149         if (!bailoutIf(Assembler::LessThan, ins->snapshot()))
  2150             return false;
  2153     masm.bind(&done);
  2154     return true;
  2157 bool
  2158 CodeGeneratorARM::visitUMod(LUMod *ins)
  2160     Register lhs = ToRegister(ins->lhs());
  2161     Register rhs = ToRegister(ins->rhs());
  2162     Register output = ToRegister(ins->output());
  2163     Label done;
  2165     if (ins->mir()->canBeDivideByZero()) {
  2166         masm.ma_cmp(rhs, Imm32(0));
  2167         if (ins->mir()->isTruncated()) {
  2168             // Infinity|0 == 0
  2169             Label skip;
  2170             masm.ma_b(&skip, Assembler::NotEqual);
  2171             masm.ma_mov(Imm32(0), output);
  2172             masm.ma_b(&done);
  2173             masm.bind(&skip);
  2174         } else {
  2175             JS_ASSERT(ins->mir()->fallible());
  2176             if (!bailoutIf(Assembler::Equal, ins->snapshot()))
  2177                 return false;
  2181     masm.ma_umod(lhs, rhs, output);
  2183     if (!ins->mir()->isTruncated()) {
  2184         masm.ma_cmp(output, Imm32(0));
  2185         if (!bailoutIf(Assembler::LessThan, ins->snapshot()))
  2186             return false;
  2189     masm.bind(&done);
  2190     return true;
  2193 bool
  2194 CodeGeneratorARM::visitSoftUDivOrMod(LSoftUDivOrMod *ins)
  2196     Register lhs = ToRegister(ins->lhs());
  2197     Register rhs = ToRegister(ins->rhs());
  2198     Register output = ToRegister(ins->output());
  2200     JS_ASSERT(lhs == r0);
  2201     JS_ASSERT(rhs == r1);
  2202     JS_ASSERT(ins->mirRaw()->isDiv() || ins->mirRaw()->isMod());
  2203     JS_ASSERT_IF(ins->mirRaw()->isDiv(), output == r0);
  2204     JS_ASSERT_IF(ins->mirRaw()->isMod(), output == r1);
  2206     Label afterDiv;
  2208     masm.ma_cmp(rhs, Imm32(0));
  2209     Label notzero;
  2210     masm.ma_b(&notzero, Assembler::NonZero);
  2211     masm.ma_mov(Imm32(0), output);
  2212     masm.ma_b(&afterDiv);
  2213     masm.bind(&notzero);
  2215     masm.setupAlignedABICall(2);
  2216     masm.passABIArg(lhs);
  2217     masm.passABIArg(rhs);
  2218     if (gen->compilingAsmJS())
  2219         masm.callWithABI(AsmJSImm_aeabi_uidivmod);
  2220     else
  2221         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, __aeabi_uidivmod));
  2223     masm.bind(&afterDiv);
  2224     return true;
  2227 bool
  2228 CodeGeneratorARM::visitEffectiveAddress(LEffectiveAddress *ins)
  2230     const MEffectiveAddress *mir = ins->mir();
  2231     Register base = ToRegister(ins->base());
  2232     Register index = ToRegister(ins->index());
  2233     Register output = ToRegister(ins->output());
  2234     masm.as_add(output, base, lsl(index, mir->scale()));
  2235     masm.ma_add(Imm32(mir->displacement()), output);
  2236     return true;
  2239 bool
  2240 CodeGeneratorARM::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
  2242     const MAsmJSLoadGlobalVar *mir = ins->mir();
  2243     unsigned addr = mir->globalDataOffset();
  2244     if (mir->type() == MIRType_Int32) {
  2245         masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr), ToRegister(ins->output()));
  2246     } else if (mir->type() == MIRType_Float32) {
  2247         VFPRegister vd(ToFloatRegister(ins->output()));
  2248         masm.ma_vldr(Operand(GlobalReg, addr), vd.singleOverlay());
  2249     } else {
  2250         masm.ma_vldr(Operand(GlobalReg, addr), ToFloatRegister(ins->output()));
  2252     return true;
  2255 bool
  2256 CodeGeneratorARM::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins)
  2258     const MAsmJSStoreGlobalVar *mir = ins->mir();
  2260     MIRType type = mir->value()->type();
  2261     JS_ASSERT(IsNumberType(type));
  2262     unsigned addr = mir->globalDataOffset();
  2263     if (mir->value()->type() == MIRType_Int32) {
  2264         masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value()));
  2265     } else if (mir->value()->type() == MIRType_Float32) {
  2266         VFPRegister vd(ToFloatRegister(ins->value()));
  2267         masm.ma_vstr(vd.singleOverlay(), Operand(GlobalReg, addr));
  2268     } else {
  2269         masm.ma_vstr(ToFloatRegister(ins->value()), Operand(GlobalReg, addr));
  2271     return true;
  2274 bool
  2275 CodeGeneratorARM::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins)
  2277     const MAsmJSLoadFuncPtr *mir = ins->mir();
  2279     Register index = ToRegister(ins->index());
  2280     Register tmp = ToRegister(ins->temp());
  2281     Register out = ToRegister(ins->output());
  2282     unsigned addr = mir->globalDataOffset();
  2283     masm.ma_mov(Imm32(addr), tmp);
  2284     masm.as_add(tmp, tmp, lsl(index, 2));
  2285     masm.ma_ldr(DTRAddr(GlobalReg, DtrRegImmShift(tmp, LSL, 0)), out);
  2287     return true;
  2290 bool
  2291 CodeGeneratorARM::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins)
  2293     const MAsmJSLoadFFIFunc *mir = ins->mir();
  2295     masm.ma_ldr(Operand(GlobalReg, mir->globalDataOffset()), ToRegister(ins->output()));
  2297     return true;
  2300 bool
  2301 CodeGeneratorARM::visitNegI(LNegI *ins)
  2303     Register input = ToRegister(ins->input());
  2304     masm.ma_neg(input, ToRegister(ins->output()));
  2305     return true;
  2308 bool
  2309 CodeGeneratorARM::visitNegD(LNegD *ins)
  2311     FloatRegister input = ToFloatRegister(ins->input());
  2312     masm.ma_vneg(input, ToFloatRegister(ins->output()));
  2313     return true;
  2316 bool
  2317 CodeGeneratorARM::visitNegF(LNegF *ins)
  2319     FloatRegister input = ToFloatRegister(ins->input());
  2320     masm.ma_vneg_f32(input, ToFloatRegister(ins->output()));
  2321     return true;
  2324 bool
  2325 CodeGeneratorARM::visitForkJoinGetSlice(LForkJoinGetSlice *ins)
  2327     MOZ_ASSUME_UNREACHABLE("NYI");
  2330 JitCode *
  2331 JitRuntime::generateForkJoinGetSliceStub(JSContext *cx)
  2333     MOZ_ASSUME_UNREACHABLE("NYI");

mercurial