Home | History | Annotate | Download | only in optimizing
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "code_generator_arm.h"
     18 
     19 #include "entrypoints/quick/quick_entrypoints.h"
     20 #include "gc/accounting/card_table.h"
     21 #include "mirror/array.h"
     22 #include "mirror/art_method.h"
     23 #include "thread.h"
     24 #include "utils/assembler.h"
     25 #include "utils/arm/assembler_arm.h"
     26 #include "utils/arm/managed_register_arm.h"
     27 #include "utils/stack_checks.h"
     28 
     29 namespace art {
     30 
     31 arm::ArmManagedRegister Location::AsArm() const {
     32   return reg().AsArm();
     33 }
     34 
     35 namespace arm {
     36 
     37 static constexpr bool kExplicitStackOverflowCheck = false;
     38 
     39 static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2;  // LR, R6, R7
     40 static constexpr int kCurrentMethodStackOffset = 0;
     41 
     42 static Location ArmCoreLocation(Register reg) {
     43   return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg));
     44 }
     45 
     46 static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2 };
     47 static constexpr size_t kRuntimeParameterCoreRegistersLength =
     48     arraysize(kRuntimeParameterCoreRegisters);
     49 
     50 class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
     51  public:
     52   InvokeRuntimeCallingConvention()
     53       : CallingConvention(kRuntimeParameterCoreRegisters,
     54                           kRuntimeParameterCoreRegistersLength) {}
     55 
     56  private:
     57   DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
     58 };
     59 
     60 #define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())->
     61 
     62 class NullCheckSlowPathARM : public SlowPathCode {
     63  public:
     64   explicit NullCheckSlowPathARM(uint32_t dex_pc) : dex_pc_(dex_pc) {}
     65 
     66   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     67     __ Bind(GetEntryLabel());
     68     int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowNullPointer).Int32Value();
     69     __ ldr(LR, Address(TR, offset));
     70     __ blx(LR);
     71     codegen->RecordPcInfo(dex_pc_);
     72   }
     73 
     74  private:
     75   const uint32_t dex_pc_;
     76   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
     77 };
     78 
     79 class StackOverflowCheckSlowPathARM : public SlowPathCode {
     80  public:
     81   StackOverflowCheckSlowPathARM() {}
     82 
     83   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     84     __ Bind(GetEntryLabel());
     85     __ LoadFromOffset(kLoadWord, PC, TR,
     86         QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowStackOverflow).Int32Value());
     87   }
     88 
     89  private:
     90   DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM);
     91 };
     92 
     93 class BoundsCheckSlowPathARM : public SlowPathCode {
     94  public:
     95   explicit BoundsCheckSlowPathARM(uint32_t dex_pc,
     96                                   Location index_location,
     97                                   Location length_location)
     98       : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
     99 
    100   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
    101     CodeGeneratorARM* arm_codegen = reinterpret_cast<CodeGeneratorARM*>(codegen);
    102     __ Bind(GetEntryLabel());
    103     InvokeRuntimeCallingConvention calling_convention;
    104     arm_codegen->Move32(ArmCoreLocation(calling_convention.GetRegisterAt(0)), index_location_);
    105     arm_codegen->Move32(ArmCoreLocation(calling_convention.GetRegisterAt(1)), length_location_);
    106     int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowArrayBounds).Int32Value();
    107     __ ldr(LR, Address(TR, offset));
    108     __ blx(LR);
    109     codegen->RecordPcInfo(dex_pc_);
    110   }
    111 
    112  private:
    113   const uint32_t dex_pc_;
    114   const Location index_location_;
    115   const Location length_location_;
    116 
    117   DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM);
    118 };
    119 
    120 #undef __
    121 #define __ reinterpret_cast<ArmAssembler*>(GetAssembler())->
    122 
    123 inline Condition ARMCondition(IfCondition cond) {
    124   switch (cond) {
    125     case kCondEQ: return EQ;
    126     case kCondNE: return NE;
    127     case kCondLT: return LT;
    128     case kCondLE: return LE;
    129     case kCondGT: return GT;
    130     case kCondGE: return GE;
    131     default:
    132       LOG(FATAL) << "Unknown if condition";
    133   }
    134   return EQ;        // Unreachable.
    135 }
    136 
    137 inline Condition ARMOppositeCondition(IfCondition cond) {
    138   switch (cond) {
    139     case kCondEQ: return NE;
    140     case kCondNE: return EQ;
    141     case kCondLT: return GE;
    142     case kCondLE: return GT;
    143     case kCondGT: return LE;
    144     case kCondGE: return LT;
    145     default:
    146       LOG(FATAL) << "Unknown if condition";
    147   }
    148   return EQ;        // Unreachable.
    149 }
    150 
    151 void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const {
    152   stream << ArmManagedRegister::FromCoreRegister(Register(reg));
    153 }
    154 
    155 void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
    156   stream << ArmManagedRegister::FromDRegister(DRegister(reg));
    157 }
    158 
    159 CodeGeneratorARM::CodeGeneratorARM(HGraph* graph)
    160     : CodeGenerator(graph, kNumberOfRegIds),
    161       location_builder_(graph, this),
    162       instruction_visitor_(graph, this),
    163       move_resolver_(graph->GetArena(), this),
    164       assembler_(true) {}
    165 
    166 size_t CodeGeneratorARM::FrameEntrySpillSize() const {
    167   return kNumberOfPushedRegistersAtEntry * kArmWordSize;
    168 }
    169 
    170 static bool* GetBlockedRegisterPairs(bool* blocked_registers) {
    171   return blocked_registers + kNumberOfAllocIds;
    172 }
    173 
    174 ManagedRegister CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type,
    175                                                        bool* blocked_registers) const {
    176   switch (type) {
    177     case Primitive::kPrimLong: {
    178       bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
    179       size_t reg = AllocateFreeRegisterInternal(blocked_register_pairs, kNumberOfRegisterPairs);
    180       ArmManagedRegister pair =
    181           ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg));
    182       blocked_registers[pair.AsRegisterPairLow()] = true;
    183       blocked_registers[pair.AsRegisterPairHigh()] = true;
    184        // Block all other register pairs that share a register with `pair`.
    185       for (int i = 0; i < kNumberOfRegisterPairs; i++) {
    186         ArmManagedRegister current =
    187             ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i));
    188         if (current.AsRegisterPairLow() == pair.AsRegisterPairLow()
    189             || current.AsRegisterPairLow() == pair.AsRegisterPairHigh()
    190             || current.AsRegisterPairHigh() == pair.AsRegisterPairLow()
    191             || current.AsRegisterPairHigh() == pair.AsRegisterPairHigh()) {
    192           blocked_register_pairs[i] = true;
    193         }
    194       }
    195       return pair;
    196     }
    197 
    198     case Primitive::kPrimByte:
    199     case Primitive::kPrimBoolean:
    200     case Primitive::kPrimChar:
    201     case Primitive::kPrimShort:
    202     case Primitive::kPrimInt:
    203     case Primitive::kPrimNot: {
    204       int reg = AllocateFreeRegisterInternal(blocked_registers, kNumberOfCoreRegisters);
    205       // Block all register pairs that contain `reg`.
    206       bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
    207       for (int i = 0; i < kNumberOfRegisterPairs; i++) {
    208         ArmManagedRegister current =
    209             ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i));
    210         if (current.AsRegisterPairLow() == reg || current.AsRegisterPairHigh() == reg) {
    211           blocked_register_pairs[i] = true;
    212         }
    213       }
    214       return ArmManagedRegister::FromCoreRegister(static_cast<Register>(reg));
    215     }
    216 
    217     case Primitive::kPrimFloat:
    218     case Primitive::kPrimDouble:
    219       LOG(FATAL) << "Unimplemented register type " << type;
    220 
    221     case Primitive::kPrimVoid:
    222       LOG(FATAL) << "Unreachable type " << type;
    223   }
    224 
    225   return ManagedRegister::NoRegister();
    226 }
    227 
    228 void CodeGeneratorARM::SetupBlockedRegisters(bool* blocked_registers) const {
    229   bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
    230 
    231   // Don't allocate the dalvik style register pair passing.
    232   blocked_register_pairs[R1_R2] = true;
    233 
    234   // Stack register, LR and PC are always reserved.
    235   blocked_registers[SP] = true;
    236   blocked_registers[LR] = true;
    237   blocked_registers[PC] = true;
    238 
    239   // Reserve R4 for suspend check.
    240   blocked_registers[R4] = true;
    241   blocked_register_pairs[R4_R5] = true;
    242 
    243   // Reserve thread register.
    244   blocked_registers[TR] = true;
    245 
    246   // Reserve temp register.
    247   blocked_registers[IP] = true;
    248 
    249   // TODO: We currently don't use Quick's callee saved registers.
    250   // We always save and restore R6 and R7 to make sure we can use three
    251   // register pairs for long operations.
    252   blocked_registers[R5] = true;
    253   blocked_registers[R8] = true;
    254   blocked_registers[R10] = true;
    255   blocked_registers[R11] = true;
    256 }
    257 
    258 size_t CodeGeneratorARM::GetNumberOfRegisters() const {
    259   return kNumberOfRegIds;
    260 }
    261 
    262 InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen)
    263       : HGraphVisitor(graph),
    264         assembler_(codegen->GetAssembler()),
    265         codegen_(codegen) {}
    266 
    267 void CodeGeneratorARM::GenerateFrameEntry() {
    268   bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
    269   if (!skip_overflow_check) {
    270     if (kExplicitStackOverflowCheck) {
    271       SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM();
    272       AddSlowPath(slow_path);
    273 
    274       __ LoadFromOffset(kLoadWord, IP, TR, Thread::StackEndOffset<kArmWordSize>().Int32Value());
    275       __ cmp(SP, ShifterOperand(IP));
    276       __ b(slow_path->GetEntryLabel(), CC);
    277     } else {
    278       __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
    279       __ ldr(IP, Address(IP, 0));
    280       RecordPcInfo(0);
    281     }
    282   }
    283 
    284   core_spill_mask_ |= (1 << LR | 1 << R6 | 1 << R7);
    285   __ PushList(1 << LR | 1 << R6 | 1 << R7);
    286 
    287   // The return PC has already been pushed on the stack.
    288   __ AddConstant(SP, -(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize));
    289   __ str(R0, Address(SP, 0));
    290 }
    291 
    292 void CodeGeneratorARM::GenerateFrameExit() {
    293   __ AddConstant(SP, GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize);
    294   __ PopList(1 << PC | 1 << R6 | 1 << R7);
    295 }
    296 
    297 void CodeGeneratorARM::Bind(Label* label) {
    298   __ Bind(label);
    299 }
    300 
    301 Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const {
    302   switch (load->GetType()) {
    303     case Primitive::kPrimLong:
    304       return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
    305       break;
    306 
    307     case Primitive::kPrimInt:
    308     case Primitive::kPrimNot:
    309       return Location::StackSlot(GetStackSlot(load->GetLocal()));
    310 
    311     case Primitive::kPrimFloat:
    312     case Primitive::kPrimDouble:
    313       LOG(FATAL) << "Unimplemented type " << load->GetType();
    314 
    315     case Primitive::kPrimBoolean:
    316     case Primitive::kPrimByte:
    317     case Primitive::kPrimChar:
    318     case Primitive::kPrimShort:
    319     case Primitive::kPrimVoid:
    320       LOG(FATAL) << "Unexpected type " << load->GetType();
    321   }
    322 
    323   LOG(FATAL) << "Unreachable";
    324   return Location();
    325 }
    326 
    327 Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) {
    328   switch (type) {
    329     case Primitive::kPrimBoolean:
    330     case Primitive::kPrimByte:
    331     case Primitive::kPrimChar:
    332     case Primitive::kPrimShort:
    333     case Primitive::kPrimInt:
    334     case Primitive::kPrimNot: {
    335       uint32_t index = gp_index_++;
    336       if (index < calling_convention.GetNumberOfRegisters()) {
    337         return ArmCoreLocation(calling_convention.GetRegisterAt(index));
    338       } else {
    339         return Location::StackSlot(calling_convention.GetStackOffsetOf(index));
    340       }
    341     }
    342 
    343     case Primitive::kPrimLong: {
    344       uint32_t index = gp_index_;
    345       gp_index_ += 2;
    346       if (index + 1 < calling_convention.GetNumberOfRegisters()) {
    347         return Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(
    348             calling_convention.GetRegisterPairAt(index)));
    349       } else if (index + 1 == calling_convention.GetNumberOfRegisters()) {
    350         return Location::QuickParameter(index);
    351       } else {
    352         return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(index));
    353       }
    354     }
    355 
    356     case Primitive::kPrimDouble:
    357     case Primitive::kPrimFloat:
    358       LOG(FATAL) << "Unimplemented parameter type " << type;
    359       break;
    360 
    361     case Primitive::kPrimVoid:
    362       LOG(FATAL) << "Unexpected parameter type " << type;
    363       break;
    364   }
    365   return Location();
    366 }
    367 
    368 void CodeGeneratorARM::Move32(Location destination, Location source) {
    369   if (source.Equals(destination)) {
    370     return;
    371   }
    372   if (destination.IsRegister()) {
    373     if (source.IsRegister()) {
    374       __ Mov(destination.AsArm().AsCoreRegister(), source.AsArm().AsCoreRegister());
    375     } else {
    376       __ ldr(destination.AsArm().AsCoreRegister(), Address(SP, source.GetStackIndex()));
    377     }
    378   } else {
    379     DCHECK(destination.IsStackSlot());
    380     if (source.IsRegister()) {
    381       __ str(source.AsArm().AsCoreRegister(), Address(SP, destination.GetStackIndex()));
    382     } else {
    383       __ ldr(IP, Address(SP, source.GetStackIndex()));
    384       __ str(IP, Address(SP, destination.GetStackIndex()));
    385     }
    386   }
    387 }
    388 
    389 void CodeGeneratorARM::Move64(Location destination, Location source) {
    390   if (source.Equals(destination)) {
    391     return;
    392   }
    393   if (destination.IsRegister()) {
    394     if (source.IsRegister()) {
    395       __ Mov(destination.AsArm().AsRegisterPairLow(), source.AsArm().AsRegisterPairLow());
    396       __ Mov(destination.AsArm().AsRegisterPairHigh(), source.AsArm().AsRegisterPairHigh());
    397     } else if (source.IsQuickParameter()) {
    398       uint32_t argument_index = source.GetQuickParameterIndex();
    399       InvokeDexCallingConvention calling_convention;
    400       __ Mov(destination.AsArm().AsRegisterPairLow(),
    401              calling_convention.GetRegisterAt(argument_index));
    402       __ ldr(destination.AsArm().AsRegisterPairHigh(),
    403              Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1) + GetFrameSize()));
    404     } else {
    405       DCHECK(source.IsDoubleStackSlot());
    406       if (destination.AsArm().AsRegisterPair() == R1_R2) {
    407         __ ldr(R1, Address(SP, source.GetStackIndex()));
    408         __ ldr(R2, Address(SP, source.GetHighStackIndex(kArmWordSize)));
    409       } else {
    410         __ LoadFromOffset(kLoadWordPair, destination.AsArm().AsRegisterPairLow(),
    411                           SP, source.GetStackIndex());
    412       }
    413     }
    414   } else if (destination.IsQuickParameter()) {
    415     InvokeDexCallingConvention calling_convention;
    416     uint32_t argument_index = destination.GetQuickParameterIndex();
    417     if (source.IsRegister()) {
    418       __ Mov(calling_convention.GetRegisterAt(argument_index), source.AsArm().AsRegisterPairLow());
    419       __ str(source.AsArm().AsRegisterPairHigh(),
    420              Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1)));
    421     } else {
    422       DCHECK(source.IsDoubleStackSlot());
    423       __ ldr(calling_convention.GetRegisterAt(argument_index), Address(SP, source.GetStackIndex()));
    424       __ ldr(R0, Address(SP, source.GetHighStackIndex(kArmWordSize)));
    425       __ str(R0, Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1)));
    426     }
    427   } else {
    428     DCHECK(destination.IsDoubleStackSlot());
    429     if (source.IsRegister()) {
    430       if (source.AsArm().AsRegisterPair() == R1_R2) {
    431         __ str(R1, Address(SP, destination.GetStackIndex()));
    432         __ str(R2, Address(SP, destination.GetHighStackIndex(kArmWordSize)));
    433       } else {
    434         __ StoreToOffset(kStoreWordPair, source.AsArm().AsRegisterPairLow(),
    435                          SP, destination.GetStackIndex());
    436       }
    437     } else if (source.IsQuickParameter()) {
    438       InvokeDexCallingConvention calling_convention;
    439       uint32_t argument_index = source.GetQuickParameterIndex();
    440       __ str(calling_convention.GetRegisterAt(argument_index),
    441              Address(SP, destination.GetStackIndex()));
    442       __ ldr(R0,
    443              Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1) + GetFrameSize()));
    444       __ str(R0, Address(SP, destination.GetHighStackIndex(kArmWordSize)));
    445     } else {
    446       DCHECK(source.IsDoubleStackSlot());
    447       __ ldr(IP, Address(SP, source.GetStackIndex()));
    448       __ str(IP, Address(SP, destination.GetStackIndex()));
    449       __ ldr(IP, Address(SP, source.GetHighStackIndex(kArmWordSize)));
    450       __ str(IP, Address(SP, destination.GetHighStackIndex(kArmWordSize)));
    451     }
    452   }
    453 }
    454 
    455 void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstruction* move_for) {
    456   LocationSummary* locations = instruction->GetLocations();
    457   if (locations != nullptr && locations->Out().Equals(location)) {
    458     return;
    459   }
    460 
    461   if (instruction->AsIntConstant() != nullptr) {
    462     int32_t value = instruction->AsIntConstant()->GetValue();
    463     if (location.IsRegister()) {
    464       __ LoadImmediate(location.AsArm().AsCoreRegister(), value);
    465     } else {
    466       DCHECK(location.IsStackSlot());
    467       __ LoadImmediate(IP, value);
    468       __ str(IP, Address(SP, location.GetStackIndex()));
    469     }
    470   } else if (instruction->AsLongConstant() != nullptr) {
    471     int64_t value = instruction->AsLongConstant()->GetValue();
    472     if (location.IsRegister()) {
    473       __ LoadImmediate(location.AsArm().AsRegisterPairLow(), Low32Bits(value));
    474       __ LoadImmediate(location.AsArm().AsRegisterPairHigh(), High32Bits(value));
    475     } else {
    476       DCHECK(location.IsDoubleStackSlot());
    477       __ LoadImmediate(IP, Low32Bits(value));
    478       __ str(IP, Address(SP, location.GetStackIndex()));
    479       __ LoadImmediate(IP, High32Bits(value));
    480       __ str(IP, Address(SP, location.GetHighStackIndex(kArmWordSize)));
    481     }
    482   } else if (instruction->AsLoadLocal() != nullptr) {
    483     uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
    484     switch (instruction->GetType()) {
    485       case Primitive::kPrimBoolean:
    486       case Primitive::kPrimByte:
    487       case Primitive::kPrimChar:
    488       case Primitive::kPrimShort:
    489       case Primitive::kPrimInt:
    490       case Primitive::kPrimNot:
    491         Move32(location, Location::StackSlot(stack_slot));
    492         break;
    493 
    494       case Primitive::kPrimLong:
    495         Move64(location, Location::DoubleStackSlot(stack_slot));
    496         break;
    497 
    498       default:
    499         LOG(FATAL) << "Unimplemented type " << instruction->GetType();
    500     }
    501   } else {
    502     DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
    503     switch (instruction->GetType()) {
    504       case Primitive::kPrimBoolean:
    505       case Primitive::kPrimByte:
    506       case Primitive::kPrimChar:
    507       case Primitive::kPrimShort:
    508       case Primitive::kPrimNot:
    509       case Primitive::kPrimInt:
    510         Move32(location, locations->Out());
    511         break;
    512 
    513       case Primitive::kPrimLong:
    514         Move64(location, locations->Out());
    515         break;
    516 
    517       default:
    518         LOG(FATAL) << "Unimplemented type " << instruction->GetType();
    519     }
    520   }
    521 }
    522 
    523 void LocationsBuilderARM::VisitGoto(HGoto* got) {
    524   got->SetLocations(nullptr);
    525 }
    526 
    527 void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) {
    528   HBasicBlock* successor = got->GetSuccessor();
    529   if (GetGraph()->GetExitBlock() == successor) {
    530     codegen_->GenerateFrameExit();
    531   } else if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
    532     __ b(codegen_->GetLabelOf(successor));
    533   }
    534 }
    535 
    536 void LocationsBuilderARM::VisitExit(HExit* exit) {
    537   exit->SetLocations(nullptr);
    538 }
    539 
    540 void InstructionCodeGeneratorARM::VisitExit(HExit* exit) {
    541   if (kIsDebugBuild) {
    542     __ Comment("Unreachable");
    543     __ bkpt(0);
    544   }
    545 }
    546 
    547 void LocationsBuilderARM::VisitIf(HIf* if_instr) {
    548   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
    549   HInstruction* cond = if_instr->InputAt(0);
    550   DCHECK(cond->IsCondition());
    551   HCondition* condition = cond->AsCondition();
    552   if (condition->NeedsMaterialization()) {
    553     locations->SetInAt(0, Location::Any());
    554   }
    555   if_instr->SetLocations(locations);
    556 }
    557 
    558 void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
    559   HInstruction* cond = if_instr->InputAt(0);
    560   DCHECK(cond->IsCondition());
    561   HCondition* condition = cond->AsCondition();
    562   if (condition->NeedsMaterialization()) {
    563     // Condition has been materialized, compare the output to 0
    564     DCHECK(if_instr->GetLocations()->InAt(0).IsRegister());
    565     __ cmp(if_instr->GetLocations()->InAt(0).AsArm().AsCoreRegister(),
    566            ShifterOperand(0));
    567     __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), EQ);
    568   } else {
    569     // Condition has not been materialized, use its inputs as the comparison and its
    570     // condition as the branch condition.
    571     LocationSummary* locations = condition->GetLocations();
    572     if (locations->InAt(1).IsRegister()) {
    573       __ cmp(locations->InAt(0).AsArm().AsCoreRegister(),
    574              ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
    575     } else {
    576       DCHECK(locations->InAt(1).IsConstant());
    577       int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
    578       ShifterOperand operand;
    579       if (ShifterOperand::CanHoldArm(value, &operand)) {
    580         __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(value));
    581       } else {
    582         Register temp = IP;
    583         __ LoadImmediate(temp, value);
    584         __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(temp));
    585       }
    586     }
    587     __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()),
    588          ARMCondition(condition->GetCondition()));
    589   }
    590 
    591   if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) {
    592     __ b(codegen_->GetLabelOf(if_instr->IfFalseSuccessor()));
    593   }
    594 }
    595 
    596 
    597 void LocationsBuilderARM::VisitCondition(HCondition* comp) {
    598   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
    599   locations->SetInAt(0, Location::RequiresRegister());
    600   locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1)));
    601   if (comp->NeedsMaterialization()) {
    602     locations->SetOut(Location::RequiresRegister());
    603   }
    604   comp->SetLocations(locations);
    605 }
    606 
    607 void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) {
    608   if (!comp->NeedsMaterialization()) return;
    609 
    610   LocationSummary* locations = comp->GetLocations();
    611   if (locations->InAt(1).IsRegister()) {
    612     __ cmp(locations->InAt(0).AsArm().AsCoreRegister(),
    613            ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
    614   } else {
    615     DCHECK(locations->InAt(1).IsConstant());
    616     int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
    617     ShifterOperand operand;
    618     if (ShifterOperand::CanHoldArm(value, &operand)) {
    619       __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(value));
    620     } else {
    621       Register temp = IP;
    622       __ LoadImmediate(temp, value);
    623       __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(temp));
    624     }
    625   }
    626   __ it(ARMCondition(comp->GetCondition()), kItElse);
    627   __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(1),
    628          ARMCondition(comp->GetCondition()));
    629   __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(0),
    630          ARMOppositeCondition(comp->GetCondition()));
    631 }
    632 
    633 void LocationsBuilderARM::VisitEqual(HEqual* comp) {
    634   VisitCondition(comp);
    635 }
    636 
    637 void InstructionCodeGeneratorARM::VisitEqual(HEqual* comp) {
    638   VisitCondition(comp);
    639 }
    640 
    641 void LocationsBuilderARM::VisitNotEqual(HNotEqual* comp) {
    642   VisitCondition(comp);
    643 }
    644 
    645 void InstructionCodeGeneratorARM::VisitNotEqual(HNotEqual* comp) {
    646   VisitCondition(comp);
    647 }
    648 
    649 void LocationsBuilderARM::VisitLessThan(HLessThan* comp) {
    650   VisitCondition(comp);
    651 }
    652 
    653 void InstructionCodeGeneratorARM::VisitLessThan(HLessThan* comp) {
    654   VisitCondition(comp);
    655 }
    656 
    657 void LocationsBuilderARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
    658   VisitCondition(comp);
    659 }
    660 
    661 void InstructionCodeGeneratorARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
    662   VisitCondition(comp);
    663 }
    664 
    665 void LocationsBuilderARM::VisitGreaterThan(HGreaterThan* comp) {
    666   VisitCondition(comp);
    667 }
    668 
    669 void InstructionCodeGeneratorARM::VisitGreaterThan(HGreaterThan* comp) {
    670   VisitCondition(comp);
    671 }
    672 
    673 void LocationsBuilderARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
    674   VisitCondition(comp);
    675 }
    676 
    677 void InstructionCodeGeneratorARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
    678   VisitCondition(comp);
    679 }
    680 
    681 void LocationsBuilderARM::VisitLocal(HLocal* local) {
    682   local->SetLocations(nullptr);
    683 }
    684 
    685 void InstructionCodeGeneratorARM::VisitLocal(HLocal* local) {
    686   DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
    687 }
    688 
    689 void LocationsBuilderARM::VisitLoadLocal(HLoadLocal* load) {
    690   load->SetLocations(nullptr);
    691 }
    692 
    693 void InstructionCodeGeneratorARM::VisitLoadLocal(HLoadLocal* load) {
    694   // Nothing to do, this is driven by the code generator.
    695 }
    696 
    697 void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) {
    698   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
    699   switch (store->InputAt(1)->GetType()) {
    700     case Primitive::kPrimBoolean:
    701     case Primitive::kPrimByte:
    702     case Primitive::kPrimChar:
    703     case Primitive::kPrimShort:
    704     case Primitive::kPrimInt:
    705     case Primitive::kPrimNot:
    706       locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
    707       break;
    708 
    709     case Primitive::kPrimLong:
    710       locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
    711       break;
    712 
    713     default:
    714       LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
    715   }
    716   store->SetLocations(locations);
    717 }
    718 
    719 void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) {
    720 }
    721 
    722 void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) {
    723   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
    724   locations->SetOut(Location::ConstantLocation(constant));
    725   constant->SetLocations(locations);
    726 }
    727 
    728 void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) {
    729 }
    730 
    731 void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) {
    732   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
    733   locations->SetOut(Location::ConstantLocation(constant));
    734   constant->SetLocations(locations);
    735 }
    736 
    737 void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant) {
    738   // Will be generated at use site.
    739 }
    740 
    741 void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) {
    742   ret->SetLocations(nullptr);
    743 }
    744 
    745 void InstructionCodeGeneratorARM::VisitReturnVoid(HReturnVoid* ret) {
    746   codegen_->GenerateFrameExit();
    747 }
    748 
    749 void LocationsBuilderARM::VisitReturn(HReturn* ret) {
    750   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
    751   switch (ret->InputAt(0)->GetType()) {
    752     case Primitive::kPrimBoolean:
    753     case Primitive::kPrimByte:
    754     case Primitive::kPrimChar:
    755     case Primitive::kPrimShort:
    756     case Primitive::kPrimInt:
    757     case Primitive::kPrimNot:
    758       locations->SetInAt(0, ArmCoreLocation(R0));
    759       break;
    760 
    761     case Primitive::kPrimLong:
    762       locations->SetInAt(
    763           0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
    764       break;
    765 
    766     default:
    767       LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
    768   }
    769 
    770   ret->SetLocations(locations);
    771 }
    772 
    773 void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) {
    774   if (kIsDebugBuild) {
    775     switch (ret->InputAt(0)->GetType()) {
    776       case Primitive::kPrimBoolean:
    777       case Primitive::kPrimByte:
    778       case Primitive::kPrimChar:
    779       case Primitive::kPrimShort:
    780       case Primitive::kPrimInt:
    781       case Primitive::kPrimNot:
    782         DCHECK_EQ(ret->GetLocations()->InAt(0).AsArm().AsCoreRegister(), R0);
    783         break;
    784 
    785       case Primitive::kPrimLong:
    786         DCHECK_EQ(ret->GetLocations()->InAt(0).AsArm().AsRegisterPair(), R0_R1);
    787         break;
    788 
    789       default:
    790         LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
    791     }
    792   }
    793   codegen_->GenerateFrameExit();
    794 }
    795 
    796 void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
    797   codegen_->MarkNotLeaf();
    798   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
    799   locations->AddTemp(ArmCoreLocation(R0));
    800 
    801   InvokeDexCallingConventionVisitor calling_convention_visitor;
    802   for (size_t i = 0; i < invoke->InputCount(); i++) {
    803     HInstruction* input = invoke->InputAt(i);
    804     locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
    805   }
    806 
    807   switch (invoke->GetType()) {
    808     case Primitive::kPrimBoolean:
    809     case Primitive::kPrimByte:
    810     case Primitive::kPrimChar:
    811     case Primitive::kPrimShort:
    812     case Primitive::kPrimInt:
    813     case Primitive::kPrimNot:
    814       locations->SetOut(ArmCoreLocation(R0));
    815       break;
    816 
    817     case Primitive::kPrimLong:
    818       locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
    819       break;
    820 
    821     case Primitive::kPrimVoid:
    822       break;
    823 
    824     case Primitive::kPrimDouble:
    825     case Primitive::kPrimFloat:
    826       LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
    827       break;
    828   }
    829 
    830   invoke->SetLocations(locations);
    831 }
    832 
    833 void InstructionCodeGeneratorARM::LoadCurrentMethod(Register reg) {
    834   __ ldr(reg, Address(SP, kCurrentMethodStackOffset));
    835 }
    836 
    837 void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) {
    838   Register temp = invoke->GetLocations()->GetTemp(0).AsArm().AsCoreRegister();
    839   uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>);
    840   size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() +
    841       invoke->GetIndexInDexCache() * kArmWordSize;
    842 
    843   // TODO: Implement all kinds of calls:
    844   // 1) boot -> boot
    845   // 2) app -> boot
    846   // 3) app -> app
    847   //
    848   // Currently we implement the app -> app logic, which looks up in the resolve cache.
    849 
    850   // temp = method;
    851   LoadCurrentMethod(temp);
    852   // temp = temp->dex_cache_resolved_methods_;
    853   __ ldr(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
    854   // temp = temp[index_in_cache]
    855   __ ldr(temp, Address(temp, index_in_cache));
    856   // LR = temp[offset_of_quick_compiled_code]
    857   __ ldr(LR, Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
    858       kArmPointerSize).Int32Value()));
    859   // LR()
    860   __ blx(LR);
    861 
    862   codegen_->RecordPcInfo(invoke->GetDexPc());
    863   DCHECK(!codegen_->IsLeafMethod());
    864 }
    865 
    866 void LocationsBuilderARM::VisitAdd(HAdd* add) {
    867   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
    868   switch (add->GetResultType()) {
    869     case Primitive::kPrimInt:
    870     case Primitive::kPrimLong: {
    871       locations->SetInAt(0, Location::RequiresRegister());
    872       locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
    873       locations->SetOut(Location::RequiresRegister());
    874       break;
    875     }
    876 
    877     case Primitive::kPrimBoolean:
    878     case Primitive::kPrimByte:
    879     case Primitive::kPrimChar:
    880     case Primitive::kPrimShort:
    881       LOG(FATAL) << "Unexpected add type " << add->GetResultType();
    882       break;
    883 
    884     default:
    885       LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
    886   }
    887   add->SetLocations(locations);
    888 }
    889 
    890 void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) {
    891   LocationSummary* locations = add->GetLocations();
    892   switch (add->GetResultType()) {
    893     case Primitive::kPrimInt:
    894       if (locations->InAt(1).IsRegister()) {
    895         __ add(locations->Out().AsArm().AsCoreRegister(),
    896                locations->InAt(0).AsArm().AsCoreRegister(),
    897                ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
    898       } else {
    899         __ AddConstant(locations->Out().AsArm().AsCoreRegister(),
    900                        locations->InAt(0).AsArm().AsCoreRegister(),
    901                        locations->InAt(1).GetConstant()->AsIntConstant()->GetValue());
    902       }
    903       break;
    904 
    905     case Primitive::kPrimLong:
    906       __ adds(locations->Out().AsArm().AsRegisterPairLow(),
    907               locations->InAt(0).AsArm().AsRegisterPairLow(),
    908               ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairLow()));
    909       __ adc(locations->Out().AsArm().AsRegisterPairHigh(),
    910              locations->InAt(0).AsArm().AsRegisterPairHigh(),
    911              ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairHigh()));
    912       break;
    913 
    914     case Primitive::kPrimBoolean:
    915     case Primitive::kPrimByte:
    916     case Primitive::kPrimChar:
    917     case Primitive::kPrimShort:
    918       LOG(FATAL) << "Unexpected add type " << add->GetResultType();
    919       break;
    920 
    921     default:
    922       LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
    923   }
    924 }
    925 
    926 void LocationsBuilderARM::VisitSub(HSub* sub) {
    927   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
    928   switch (sub->GetResultType()) {
    929     case Primitive::kPrimInt:
    930     case Primitive::kPrimLong: {
    931       locations->SetInAt(0, Location::RequiresRegister());
    932       locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
    933       locations->SetOut(Location::RequiresRegister());
    934       break;
    935     }
    936 
    937     case Primitive::kPrimBoolean:
    938     case Primitive::kPrimByte:
    939     case Primitive::kPrimChar:
    940     case Primitive::kPrimShort:
    941       LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
    942       break;
    943 
    944     default:
    945       LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
    946   }
    947   sub->SetLocations(locations);
    948 }
    949 
    950 void InstructionCodeGeneratorARM::VisitSub(HSub* sub) {
    951   LocationSummary* locations = sub->GetLocations();
    952   switch (sub->GetResultType()) {
    953     case Primitive::kPrimInt: {
    954       if (locations->InAt(1).IsRegister()) {
    955         __ sub(locations->Out().AsArm().AsCoreRegister(),
    956                locations->InAt(0).AsArm().AsCoreRegister(),
    957                ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
    958       } else {
    959         __ AddConstant(locations->Out().AsArm().AsCoreRegister(),
    960                        locations->InAt(0).AsArm().AsCoreRegister(),
    961                        -locations->InAt(1).GetConstant()->AsIntConstant()->GetValue());
    962       }
    963       break;
    964     }
    965 
    966     case Primitive::kPrimLong:
    967       __ subs(locations->Out().AsArm().AsRegisterPairLow(),
    968               locations->InAt(0).AsArm().AsRegisterPairLow(),
    969               ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairLow()));
    970       __ sbc(locations->Out().AsArm().AsRegisterPairHigh(),
    971              locations->InAt(0).AsArm().AsRegisterPairHigh(),
    972              ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairHigh()));
    973       break;
    974 
    975     case Primitive::kPrimBoolean:
    976     case Primitive::kPrimByte:
    977     case Primitive::kPrimChar:
    978     case Primitive::kPrimShort:
    979       LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
    980       break;
    981 
    982     default:
    983       LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
    984   }
    985 }
    986 
    987 void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
    988   codegen_->MarkNotLeaf();
    989   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
    990   InvokeRuntimeCallingConvention calling_convention;
    991   locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(0)));
    992   locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(1)));
    993   locations->SetOut(ArmCoreLocation(R0));
    994   instruction->SetLocations(locations);
    995 }
    996 
    997 void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
    998   InvokeRuntimeCallingConvention calling_convention;
    999   LoadCurrentMethod(calling_convention.GetRegisterAt(1));
   1000   __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
   1001 
   1002   int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocObjectWithAccessCheck).Int32Value();
   1003   __ ldr(LR, Address(TR, offset));
   1004   __ blx(LR);
   1005 
   1006   codegen_->RecordPcInfo(instruction->GetDexPc());
   1007   DCHECK(!codegen_->IsLeafMethod());
   1008 }
   1009 
   1010 void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
   1011   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   1012   Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
   1013   if (location.IsStackSlot()) {
   1014     location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
   1015   } else if (location.IsDoubleStackSlot()) {
   1016     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
   1017   }
   1018   locations->SetOut(location);
   1019   instruction->SetLocations(locations);
   1020 }
   1021 
   1022 void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instruction) {
   1023   // Nothing to do, the parameter is already at its location.
   1024 }
   1025 
   1026 void LocationsBuilderARM::VisitNot(HNot* instruction) {
   1027   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   1028   locations->SetInAt(0, Location::RequiresRegister());
   1029   locations->SetOut(Location::RequiresRegister());
   1030   instruction->SetLocations(locations);
   1031 }
   1032 
   1033 void InstructionCodeGeneratorARM::VisitNot(HNot* instruction) {
   1034   LocationSummary* locations = instruction->GetLocations();
   1035   __ eor(locations->Out().AsArm().AsCoreRegister(),
   1036          locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(1));
   1037 }
   1038 
   1039 void LocationsBuilderARM::VisitCompare(HCompare* compare) {
   1040   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
   1041   locations->SetInAt(0, Location::RequiresRegister());
   1042   locations->SetInAt(1, Location::RequiresRegister());
   1043   locations->SetOut(Location::RequiresRegister());
   1044   compare->SetLocations(locations);
   1045 }
   1046 
   1047 void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) {
   1048   Label greater, done;
   1049   LocationSummary* locations = compare->GetLocations();
   1050   switch (compare->InputAt(0)->GetType()) {
   1051     case Primitive::kPrimLong: {
   1052       Register output = locations->Out().AsArm().AsCoreRegister();
   1053       ArmManagedRegister left = locations->InAt(0).AsArm();
   1054       ArmManagedRegister right = locations->InAt(1).AsArm();
   1055       Label less, greater, done;
   1056       __ cmp(left.AsRegisterPairHigh(),
   1057              ShifterOperand(right.AsRegisterPairHigh()));  // Signed compare.
   1058       __ b(&less, LT);
   1059       __ b(&greater, GT);
   1060       // Do LoadImmediate before any `cmp`, as LoadImmediate might affect
   1061       // the status flags.
   1062       __ LoadImmediate(output, 0);
   1063       __ cmp(left.AsRegisterPairLow(),
   1064              ShifterOperand(right.AsRegisterPairLow()));  // Unsigned compare.
   1065       __ b(&done, EQ);
   1066       __ b(&less, CC);
   1067 
   1068       __ Bind(&greater);
   1069       __ LoadImmediate(output, 1);
   1070       __ b(&done);
   1071 
   1072       __ Bind(&less);
   1073       __ LoadImmediate(output, -1);
   1074 
   1075       __ Bind(&done);
   1076       break;
   1077     }
   1078     default:
   1079       LOG(FATAL) << "Unimplemented compare type " << compare->InputAt(0)->GetType();
   1080   }
   1081 }
   1082 
   1083 void LocationsBuilderARM::VisitPhi(HPhi* instruction) {
   1084   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   1085   for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
   1086     locations->SetInAt(i, Location::Any());
   1087   }
   1088   locations->SetOut(Location::Any());
   1089   instruction->SetLocations(locations);
   1090 }
   1091 
   1092 void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) {
   1093   LOG(FATAL) << "Unreachable";
   1094 }
   1095 
   1096 void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
   1097   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   1098   locations->SetInAt(0, Location::RequiresRegister());
   1099   locations->SetInAt(1, Location::RequiresRegister());
   1100   // Temporary registers for the write barrier.
   1101   if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
   1102     locations->AddTemp(Location::RequiresRegister());
   1103     locations->AddTemp(Location::RequiresRegister());
   1104   }
   1105   instruction->SetLocations(locations);
   1106 }
   1107 
   1108 void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
   1109   LocationSummary* locations = instruction->GetLocations();
   1110   Register obj = locations->InAt(0).AsArm().AsCoreRegister();
   1111   uint32_t offset = instruction->GetFieldOffset().Uint32Value();
   1112   Primitive::Type field_type = instruction->InputAt(1)->GetType();
   1113 
   1114   switch (field_type) {
   1115     case Primitive::kPrimBoolean:
   1116     case Primitive::kPrimByte: {
   1117       Register value = locations->InAt(1).AsArm().AsCoreRegister();
   1118       __ StoreToOffset(kStoreByte, value, obj, offset);
   1119       break;
   1120     }
   1121 
   1122     case Primitive::kPrimShort:
   1123     case Primitive::kPrimChar: {
   1124       Register value = locations->InAt(1).AsArm().AsCoreRegister();
   1125       __ StoreToOffset(kStoreHalfword, value, obj, offset);
   1126       break;
   1127     }
   1128 
   1129     case Primitive::kPrimInt:
   1130     case Primitive::kPrimNot: {
   1131       Register value = locations->InAt(1).AsArm().AsCoreRegister();
   1132       __ StoreToOffset(kStoreWord, value, obj, offset);
   1133       if (field_type == Primitive::kPrimNot) {
   1134         Register temp = locations->GetTemp(0).AsArm().AsCoreRegister();
   1135         Register card = locations->GetTemp(1).AsArm().AsCoreRegister();
   1136         codegen_->MarkGCCard(temp, card, obj, value);
   1137       }
   1138       break;
   1139     }
   1140 
   1141     case Primitive::kPrimLong: {
   1142       ArmManagedRegister value = locations->InAt(1).AsArm();
   1143       __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), obj, offset);
   1144       break;
   1145     }
   1146 
   1147     case Primitive::kPrimFloat:
   1148     case Primitive::kPrimDouble:
   1149       LOG(FATAL) << "Unimplemented register type " << field_type;
   1150 
   1151     case Primitive::kPrimVoid:
   1152       LOG(FATAL) << "Unreachable type " << field_type;
   1153   }
   1154 }
   1155 
   1156 void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
   1157   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   1158   locations->SetInAt(0, Location::RequiresRegister());
   1159   locations->SetOut(Location::RequiresRegister());
   1160   instruction->SetLocations(locations);
   1161 }
   1162 
   1163 void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
   1164   LocationSummary* locations = instruction->GetLocations();
   1165   Register obj = locations->InAt(0).AsArm().AsCoreRegister();
   1166   uint32_t offset = instruction->GetFieldOffset().Uint32Value();
   1167 
   1168   switch (instruction->GetType()) {
   1169     case Primitive::kPrimBoolean: {
   1170       Register out = locations->Out().AsArm().AsCoreRegister();
   1171       __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset);
   1172       break;
   1173     }
   1174 
   1175     case Primitive::kPrimByte: {
   1176       Register out = locations->Out().AsArm().AsCoreRegister();
   1177       __ LoadFromOffset(kLoadSignedByte, out, obj, offset);
   1178       break;
   1179     }
   1180 
   1181     case Primitive::kPrimShort: {
   1182       Register out = locations->Out().AsArm().AsCoreRegister();
   1183       __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset);
   1184       break;
   1185     }
   1186 
   1187     case Primitive::kPrimChar: {
   1188       Register out = locations->Out().AsArm().AsCoreRegister();
   1189       __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset);
   1190       break;
   1191     }
   1192 
   1193     case Primitive::kPrimInt:
   1194     case Primitive::kPrimNot: {
   1195       Register out = locations->Out().AsArm().AsCoreRegister();
   1196       __ LoadFromOffset(kLoadWord, out, obj, offset);
   1197       break;
   1198     }
   1199 
   1200     case Primitive::kPrimLong: {
   1201       // TODO: support volatile.
   1202       ArmManagedRegister out = locations->Out().AsArm();
   1203       __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), obj, offset);
   1204       break;
   1205     }
   1206 
   1207     case Primitive::kPrimFloat:
   1208     case Primitive::kPrimDouble:
   1209       LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
   1210 
   1211     case Primitive::kPrimVoid:
   1212       LOG(FATAL) << "Unreachable type " << instruction->GetType();
   1213   }
   1214 }
   1215 
   1216 void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
   1217   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   1218   locations->SetInAt(0, Location::RequiresRegister());
   1219   // TODO: Have a normalization phase that makes this instruction never used.
   1220   locations->SetOut(Location::SameAsFirstInput());
   1221   instruction->SetLocations(locations);
   1222 }
   1223 
   1224 void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
   1225   SlowPathCode* slow_path =
   1226       new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction->GetDexPc());
   1227   codegen_->AddSlowPath(slow_path);
   1228 
   1229   LocationSummary* locations = instruction->GetLocations();
   1230   Location obj = locations->InAt(0);
   1231   DCHECK(obj.Equals(locations->Out()));
   1232 
   1233   if (obj.IsRegister()) {
   1234     __ cmp(obj.AsArm().AsCoreRegister(), ShifterOperand(0));
   1235   }
   1236   __ b(slow_path->GetEntryLabel(), EQ);
   1237 }
   1238 
   1239 void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) {
   1240   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   1241   locations->SetInAt(0, Location::RequiresRegister());
   1242   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   1243   locations->SetOut(Location::RequiresRegister());
   1244   instruction->SetLocations(locations);
   1245 }
   1246 
   1247 void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
   1248   LocationSummary* locations = instruction->GetLocations();
   1249   Register obj = locations->InAt(0).AsArm().AsCoreRegister();
   1250   Location index = locations->InAt(1);
   1251 
   1252   switch (instruction->GetType()) {
   1253     case Primitive::kPrimBoolean: {
   1254       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
   1255       Register out = locations->Out().AsArm().AsCoreRegister();
   1256       if (index.IsConstant()) {
   1257         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
   1258         __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset);
   1259       } else {
   1260         __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister()));
   1261         __ LoadFromOffset(kLoadUnsignedByte, out, IP, data_offset);
   1262       }
   1263       break;
   1264     }
   1265 
   1266     case Primitive::kPrimByte: {
   1267       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
   1268       Register out = locations->Out().AsArm().AsCoreRegister();
   1269       if (index.IsConstant()) {
   1270         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
   1271         __ LoadFromOffset(kLoadSignedByte, out, obj, offset);
   1272       } else {
   1273         __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister()));
   1274         __ LoadFromOffset(kLoadSignedByte, out, IP, data_offset);
   1275       }
   1276       break;
   1277     }
   1278 
   1279     case Primitive::kPrimShort: {
   1280       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
   1281       Register out = locations->Out().AsArm().AsCoreRegister();
   1282       if (index.IsConstant()) {
   1283         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
   1284         __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset);
   1285       } else {
   1286         __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2));
   1287         __ LoadFromOffset(kLoadSignedHalfword, out, IP, data_offset);
   1288       }
   1289       break;
   1290     }
   1291 
   1292     case Primitive::kPrimChar: {
   1293       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
   1294       Register out = locations->Out().AsArm().AsCoreRegister();
   1295       if (index.IsConstant()) {
   1296         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
   1297         __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset);
   1298       } else {
   1299         __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2));
   1300         __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset);
   1301       }
   1302       break;
   1303     }
   1304 
   1305     case Primitive::kPrimInt:
   1306     case Primitive::kPrimNot: {
   1307       DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
   1308       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
   1309       Register out = locations->Out().AsArm().AsCoreRegister();
   1310       if (index.IsConstant()) {
   1311         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
   1312         __ LoadFromOffset(kLoadWord, out, obj, offset);
   1313       } else {
   1314         __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_4));
   1315         __ LoadFromOffset(kLoadWord, out, IP, data_offset);
   1316       }
   1317       break;
   1318     }
   1319 
   1320     case Primitive::kPrimLong: {
   1321       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
   1322       ArmManagedRegister out = locations->Out().AsArm();
   1323       if (index.IsConstant()) {
   1324         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
   1325         __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), obj, offset);
   1326       } else {
   1327         __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_8));
   1328         __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), IP, data_offset);
   1329       }
   1330       break;
   1331     }
   1332 
   1333     case Primitive::kPrimFloat:
   1334     case Primitive::kPrimDouble:
   1335       LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
   1336 
   1337     case Primitive::kPrimVoid:
   1338       LOG(FATAL) << "Unreachable type " << instruction->GetType();
   1339   }
   1340 }
   1341 
   1342 void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) {
   1343   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   1344   Primitive::Type value_type = instruction->InputAt(2)->GetType();
   1345   if (value_type == Primitive::kPrimNot) {
   1346     InvokeRuntimeCallingConvention calling_convention;
   1347     locations->SetInAt(0, ArmCoreLocation(calling_convention.GetRegisterAt(0)));
   1348     locations->SetInAt(1, ArmCoreLocation(calling_convention.GetRegisterAt(1)));
   1349     locations->SetInAt(2, ArmCoreLocation(calling_convention.GetRegisterAt(2)));
   1350     codegen_->MarkNotLeaf();
   1351   } else {
   1352     locations->SetInAt(0, Location::RequiresRegister());
   1353     locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   1354     locations->SetInAt(2, Location::RequiresRegister());
   1355   }
   1356   instruction->SetLocations(locations);
   1357 }
   1358 
   1359 void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
   1360   LocationSummary* locations = instruction->GetLocations();
   1361   Register obj = locations->InAt(0).AsArm().AsCoreRegister();
   1362   Location index = locations->InAt(1);
   1363   Primitive::Type value_type = instruction->InputAt(2)->GetType();
   1364 
   1365   switch (value_type) {
   1366     case Primitive::kPrimBoolean:
   1367     case Primitive::kPrimByte: {
   1368       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
   1369       Register value = locations->InAt(2).AsArm().AsCoreRegister();
   1370       if (index.IsConstant()) {
   1371         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
   1372         __ StoreToOffset(kStoreByte, value, obj, offset);
   1373       } else {
   1374         __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister()));
   1375         __ StoreToOffset(kStoreByte, value, IP, data_offset);
   1376       }
   1377       break;
   1378     }
   1379 
   1380     case Primitive::kPrimShort:
   1381     case Primitive::kPrimChar: {
   1382       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
   1383       Register value = locations->InAt(2).AsArm().AsCoreRegister();
   1384       if (index.IsConstant()) {
   1385         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
   1386         __ StoreToOffset(kStoreHalfword, value, obj, offset);
   1387       } else {
   1388         __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_2));
   1389         __ StoreToOffset(kStoreHalfword, value, IP, data_offset);
   1390       }
   1391       break;
   1392     }
   1393 
   1394     case Primitive::kPrimInt: {
   1395       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
   1396       Register value = locations->InAt(2).AsArm().AsCoreRegister();
   1397       if (index.IsConstant()) {
   1398         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
   1399         __ StoreToOffset(kStoreWord, value, obj, offset);
   1400       } else {
   1401         __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_4));
   1402         __ StoreToOffset(kStoreWord, value, IP, data_offset);
   1403       }
   1404       break;
   1405     }
   1406 
   1407     case Primitive::kPrimNot: {
   1408       int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAputObject).Int32Value();
   1409       __ ldr(LR, Address(TR, offset));
   1410       __ blx(LR);
   1411       codegen_->RecordPcInfo(instruction->GetDexPc());
   1412       DCHECK(!codegen_->IsLeafMethod());
   1413       break;
   1414     }
   1415 
   1416     case Primitive::kPrimLong: {
   1417       uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
   1418       ArmManagedRegister value = locations->InAt(2).AsArm();
   1419       if (index.IsConstant()) {
   1420         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
   1421         __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), obj, offset);
   1422       } else {
   1423         __ add(IP, obj, ShifterOperand(index.AsArm().AsCoreRegister(), LSL, TIMES_8));
   1424         __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), IP, data_offset);
   1425       }
   1426       break;
   1427     }
   1428 
   1429     case Primitive::kPrimFloat:
   1430     case Primitive::kPrimDouble:
   1431       LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
   1432 
   1433     case Primitive::kPrimVoid:
   1434       LOG(FATAL) << "Unreachable type " << instruction->GetType();
   1435   }
   1436 }
   1437 
   1438 void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) {
   1439   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   1440   locations->SetInAt(0, Location::RequiresRegister());
   1441   locations->SetOut(Location::RequiresRegister());
   1442   instruction->SetLocations(locations);
   1443 }
   1444 
   1445 void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) {
   1446   LocationSummary* locations = instruction->GetLocations();
   1447   uint32_t offset = mirror::Array::LengthOffset().Uint32Value();
   1448   Register obj = locations->InAt(0).AsArm().AsCoreRegister();
   1449   Register out = locations->Out().AsArm().AsCoreRegister();
   1450   __ LoadFromOffset(kLoadWord, out, obj, offset);
   1451 }
   1452 
   1453 void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) {
   1454   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   1455   locations->SetInAt(0, Location::RequiresRegister());
   1456   locations->SetInAt(1, Location::RequiresRegister());
   1457   // TODO: Have a normalization phase that makes this instruction never used.
   1458   locations->SetOut(Location::SameAsFirstInput());
   1459   instruction->SetLocations(locations);
   1460 }
   1461 
   1462 void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) {
   1463   LocationSummary* locations = instruction->GetLocations();
   1464   SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(
   1465       instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
   1466   codegen_->AddSlowPath(slow_path);
   1467 
   1468   Register index = locations->InAt(0).AsArm().AsCoreRegister();
   1469   Register length = locations->InAt(1).AsArm().AsCoreRegister();
   1470 
   1471   __ cmp(index, ShifterOperand(length));
   1472   __ b(slow_path->GetEntryLabel(), CS);
   1473 }
   1474 
   1475 void CodeGeneratorARM::MarkGCCard(Register temp, Register card, Register object, Register value) {
   1476   Label is_null;
   1477   __ CompareAndBranchIfZero(value, &is_null);
   1478   __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value());
   1479   __ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
   1480   __ strb(card, Address(card, temp));
   1481   __ Bind(&is_null);
   1482 }
   1483 
   1484 void LocationsBuilderARM::VisitTemporary(HTemporary* temp) {
   1485   temp->SetLocations(nullptr);
   1486 }
   1487 
   1488 void InstructionCodeGeneratorARM::VisitTemporary(HTemporary* temp) {
   1489   // Nothing to do, this is driven by the code generator.
   1490 }
   1491 
   1492 void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction) {
   1493   LOG(FATAL) << "Unreachable";
   1494 }
   1495 
   1496 void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) {
   1497   codegen_->GetMoveResolver()->EmitNativeCode(instruction);
   1498 }
   1499 
   1500 ArmAssembler* ParallelMoveResolverARM::GetAssembler() const {
   1501   return codegen_->GetAssembler();
   1502 }
   1503 
   1504 void ParallelMoveResolverARM::EmitMove(size_t index) {
   1505   MoveOperands* move = moves_.Get(index);
   1506   Location source = move->GetSource();
   1507   Location destination = move->GetDestination();
   1508 
   1509   if (source.IsRegister()) {
   1510     if (destination.IsRegister()) {
   1511       __ Mov(destination.AsArm().AsCoreRegister(), source.AsArm().AsCoreRegister());
   1512     } else {
   1513       DCHECK(destination.IsStackSlot());
   1514       __ StoreToOffset(kStoreWord, source.AsArm().AsCoreRegister(),
   1515                        SP, destination.GetStackIndex());
   1516     }
   1517   } else if (source.IsStackSlot()) {
   1518     if (destination.IsRegister()) {
   1519       __ LoadFromOffset(kLoadWord, destination.AsArm().AsCoreRegister(),
   1520                         SP, source.GetStackIndex());
   1521     } else {
   1522       DCHECK(destination.IsStackSlot());
   1523       __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex());
   1524       __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
   1525     }
   1526   } else {
   1527     DCHECK(source.IsConstant());
   1528     DCHECK(source.GetConstant()->AsIntConstant() != nullptr);
   1529     int32_t value = source.GetConstant()->AsIntConstant()->GetValue();
   1530     if (destination.IsRegister()) {
   1531       __ LoadImmediate(destination.AsArm().AsCoreRegister(), value);
   1532     } else {
   1533       DCHECK(destination.IsStackSlot());
   1534       __ LoadImmediate(IP, value);
   1535       __ str(IP, Address(SP, destination.GetStackIndex()));
   1536     }
   1537   }
   1538 }
   1539 
   1540 void ParallelMoveResolverARM::Exchange(Register reg, int mem) {
   1541   __ Mov(IP, reg);
   1542   __ LoadFromOffset(kLoadWord, reg, SP, mem);
   1543   __ StoreToOffset(kStoreWord, IP, SP, mem);
   1544 }
   1545 
   1546 void ParallelMoveResolverARM::Exchange(int mem1, int mem2) {
   1547   ScratchRegisterScope ensure_scratch(this, IP, R0, codegen_->GetNumberOfCoreRegisters());
   1548   int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0;
   1549   __ LoadFromOffset(kLoadWord, static_cast<Register>(ensure_scratch.GetRegister()),
   1550                     SP, mem1 + stack_offset);
   1551   __ LoadFromOffset(kLoadWord, IP, SP, mem2 + stack_offset);
   1552   __ StoreToOffset(kStoreWord, static_cast<Register>(ensure_scratch.GetRegister()),
   1553                    SP, mem2 + stack_offset);
   1554   __ StoreToOffset(kStoreWord, IP, SP, mem1 + stack_offset);
   1555 }
   1556 
   1557 void ParallelMoveResolverARM::EmitSwap(size_t index) {
   1558   MoveOperands* move = moves_.Get(index);
   1559   Location source = move->GetSource();
   1560   Location destination = move->GetDestination();
   1561 
   1562   if (source.IsRegister() && destination.IsRegister()) {
   1563     DCHECK_NE(source.AsArm().AsCoreRegister(), IP);
   1564     DCHECK_NE(destination.AsArm().AsCoreRegister(), IP);
   1565     __ Mov(IP, source.AsArm().AsCoreRegister());
   1566     __ Mov(source.AsArm().AsCoreRegister(), destination.AsArm().AsCoreRegister());
   1567     __ Mov(destination.AsArm().AsCoreRegister(), IP);
   1568   } else if (source.IsRegister() && destination.IsStackSlot()) {
   1569     Exchange(source.AsArm().AsCoreRegister(), destination.GetStackIndex());
   1570   } else if (source.IsStackSlot() && destination.IsRegister()) {
   1571     Exchange(destination.AsArm().AsCoreRegister(), source.GetStackIndex());
   1572   } else if (source.IsStackSlot() && destination.IsStackSlot()) {
   1573     Exchange(source.GetStackIndex(), destination.GetStackIndex());
   1574   } else {
   1575     LOG(FATAL) << "Unimplemented";
   1576   }
   1577 }
   1578 
   1579 void ParallelMoveResolverARM::SpillScratch(int reg) {
   1580   __ Push(static_cast<Register>(reg));
   1581 }
   1582 
   1583 void ParallelMoveResolverARM::RestoreScratch(int reg) {
   1584   __ Pop(static_cast<Register>(reg));
   1585 }
   1586 
   1587 }  // namespace arm
   1588 }  // namespace art
   1589