code_generator_arm.cc revision 8d486731559ba0c5e12c27b4a507181333702b7e
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 "mirror/array.h"
21#include "mirror/art_method.h"
22#include "thread.h"
23#include "utils/assembler.h"
24#include "utils/arm/assembler_arm.h"
25#include "utils/arm/managed_register_arm.h"
26
27namespace art {
28
29arm::ArmManagedRegister Location::AsArm() const {
30  return reg().AsArm();
31}
32
33namespace arm {
34
35#define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())->
36
37class NullCheckSlowPathARM : public SlowPathCode {
38 public:
39  explicit NullCheckSlowPathARM(uint32_t dex_pc) : dex_pc_(dex_pc) {}
40
41  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
42    __ Bind(GetEntryLabel());
43    int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowNullPointer).Int32Value();
44    __ ldr(LR, Address(TR, offset));
45    __ blx(LR);
46    codegen->RecordPcInfo(dex_pc_);
47  }
48
49 private:
50  const uint32_t dex_pc_;
51  DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
52};
53
54#undef __
55#define __ reinterpret_cast<ArmAssembler*>(GetAssembler())->
56
57inline Condition ARMCondition(IfCondition cond) {
58  switch (cond) {
59    case kCondEQ: return EQ;
60    case kCondNE: return NE;
61    case kCondLT: return LT;
62    case kCondLE: return LE;
63    case kCondGT: return GT;
64    case kCondGE: return GE;
65    default:
66      LOG(FATAL) << "Unknown if condition";
67  }
68  return EQ;        // Unreachable.
69}
70
71inline Condition ARMOppositeCondition(IfCondition cond) {
72  switch (cond) {
73    case kCondEQ: return NE;
74    case kCondNE: return EQ;
75    case kCondLT: return GE;
76    case kCondLE: return GT;
77    case kCondGT: return LE;
78    case kCondGE: return LT;
79    default:
80      LOG(FATAL) << "Unknown if condition";
81  }
82  return EQ;        // Unreachable.
83}
84
85static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2;  // LR, R6, R7
86static constexpr int kCurrentMethodStackOffset = 0;
87
88void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const {
89  stream << ArmManagedRegister::FromCoreRegister(Register(reg));
90}
91
92void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
93  stream << ArmManagedRegister::FromDRegister(DRegister(reg));
94}
95
96CodeGeneratorARM::CodeGeneratorARM(HGraph* graph)
97    : CodeGenerator(graph, kNumberOfRegIds),
98      location_builder_(graph, this),
99      instruction_visitor_(graph, this),
100      move_resolver_(graph->GetArena(), this),
101      assembler_(true) {}
102
103size_t CodeGeneratorARM::FrameEntrySpillSize() const {
104  return kNumberOfPushedRegistersAtEntry * kArmWordSize;
105}
106
107static bool* GetBlockedRegisterPairs(bool* blocked_registers) {
108  return blocked_registers + kNumberOfAllocIds;
109}
110
111ManagedRegister CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type,
112                                                       bool* blocked_registers) const {
113  switch (type) {
114    case Primitive::kPrimLong: {
115      bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
116      size_t reg = AllocateFreeRegisterInternal(blocked_register_pairs, kNumberOfRegisterPairs);
117      ArmManagedRegister pair =
118          ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg));
119      blocked_registers[pair.AsRegisterPairLow()] = true;
120      blocked_registers[pair.AsRegisterPairHigh()] = true;
121       // Block all other register pairs that share a register with `pair`.
122      for (int i = 0; i < kNumberOfRegisterPairs; i++) {
123        ArmManagedRegister current =
124            ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i));
125        if (current.AsRegisterPairLow() == pair.AsRegisterPairLow()
126            || current.AsRegisterPairLow() == pair.AsRegisterPairHigh()
127            || current.AsRegisterPairHigh() == pair.AsRegisterPairLow()
128            || current.AsRegisterPairHigh() == pair.AsRegisterPairHigh()) {
129          blocked_register_pairs[i] = true;
130        }
131      }
132      return pair;
133    }
134
135    case Primitive::kPrimByte:
136    case Primitive::kPrimBoolean:
137    case Primitive::kPrimChar:
138    case Primitive::kPrimShort:
139    case Primitive::kPrimInt:
140    case Primitive::kPrimNot: {
141      int reg = AllocateFreeRegisterInternal(blocked_registers, kNumberOfCoreRegisters);
142      // Block all register pairs that contain `reg`.
143      bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
144      for (int i = 0; i < kNumberOfRegisterPairs; i++) {
145        ArmManagedRegister current =
146            ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i));
147        if (current.AsRegisterPairLow() == reg || current.AsRegisterPairHigh() == reg) {
148          blocked_register_pairs[i] = true;
149        }
150      }
151      return ArmManagedRegister::FromCoreRegister(static_cast<Register>(reg));
152    }
153
154    case Primitive::kPrimFloat:
155    case Primitive::kPrimDouble:
156      LOG(FATAL) << "Unimplemented register type " << type;
157
158    case Primitive::kPrimVoid:
159      LOG(FATAL) << "Unreachable type " << type;
160  }
161
162  return ManagedRegister::NoRegister();
163}
164
165void CodeGeneratorARM::SetupBlockedRegisters(bool* blocked_registers) const {
166  bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
167
168  // Don't allocate the dalvik style register pair passing.
169  blocked_register_pairs[R1_R2] = true;
170
171  // Stack register, LR and PC are always reserved.
172  blocked_registers[SP] = true;
173  blocked_registers[LR] = true;
174  blocked_registers[PC] = true;
175
176  // Reserve R4 for suspend check.
177  blocked_registers[R4] = true;
178  blocked_register_pairs[R4_R5] = true;
179
180  // Reserve thread register.
181  blocked_registers[TR] = true;
182
183  // Reserve temp register.
184  blocked_registers[IP] = true;
185
186  // TODO: We currently don't use Quick's callee saved registers.
187  // We always save and restore R6 and R7 to make sure we can use three
188  // register pairs for long operations.
189  blocked_registers[R5] = true;
190  blocked_registers[R8] = true;
191  blocked_registers[R10] = true;
192  blocked_registers[R11] = true;
193}
194
195size_t CodeGeneratorARM::GetNumberOfRegisters() const {
196  return kNumberOfRegIds;
197}
198
199static Location ArmCoreLocation(Register reg) {
200  return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg));
201}
202
203InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen)
204      : HGraphVisitor(graph),
205        assembler_(codegen->GetAssembler()),
206        codegen_(codegen) {}
207
208void CodeGeneratorARM::GenerateFrameEntry() {
209  core_spill_mask_ |= (1 << LR | 1 << R6 | 1 << R7);
210  __ PushList(1 << LR | 1 << R6 | 1 << R7);
211
212  // The return PC has already been pushed on the stack.
213  __ AddConstant(SP, -(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize));
214  __ str(R0, Address(SP, 0));
215}
216
217void CodeGeneratorARM::GenerateFrameExit() {
218  __ AddConstant(SP, GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize);
219  __ PopList(1 << PC | 1 << R6 | 1 << R7);
220}
221
222void CodeGeneratorARM::Bind(Label* label) {
223  __ Bind(label);
224}
225
226Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const {
227  switch (load->GetType()) {
228    case Primitive::kPrimLong:
229      return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
230      break;
231
232    case Primitive::kPrimInt:
233    case Primitive::kPrimNot:
234      return Location::StackSlot(GetStackSlot(load->GetLocal()));
235
236    case Primitive::kPrimFloat:
237    case Primitive::kPrimDouble:
238      LOG(FATAL) << "Unimplemented type " << load->GetType();
239
240    case Primitive::kPrimBoolean:
241    case Primitive::kPrimByte:
242    case Primitive::kPrimChar:
243    case Primitive::kPrimShort:
244    case Primitive::kPrimVoid:
245      LOG(FATAL) << "Unexpected type " << load->GetType();
246  }
247
248  LOG(FATAL) << "Unreachable";
249  return Location();
250}
251
252Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) {
253  switch (type) {
254    case Primitive::kPrimBoolean:
255    case Primitive::kPrimByte:
256    case Primitive::kPrimChar:
257    case Primitive::kPrimShort:
258    case Primitive::kPrimInt:
259    case Primitive::kPrimNot: {
260      uint32_t index = gp_index_++;
261      if (index < calling_convention.GetNumberOfRegisters()) {
262        return ArmCoreLocation(calling_convention.GetRegisterAt(index));
263      } else {
264        return Location::StackSlot(calling_convention.GetStackOffsetOf(index));
265      }
266    }
267
268    case Primitive::kPrimLong: {
269      uint32_t index = gp_index_;
270      gp_index_ += 2;
271      if (index + 1 < calling_convention.GetNumberOfRegisters()) {
272        return Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(
273            calling_convention.GetRegisterPairAt(index)));
274      } else if (index + 1 == calling_convention.GetNumberOfRegisters()) {
275        return Location::QuickParameter(index);
276      } else {
277        return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(index));
278      }
279    }
280
281    case Primitive::kPrimDouble:
282    case Primitive::kPrimFloat:
283      LOG(FATAL) << "Unimplemented parameter type " << type;
284      break;
285
286    case Primitive::kPrimVoid:
287      LOG(FATAL) << "Unexpected parameter type " << type;
288      break;
289  }
290  return Location();
291}
292
293void CodeGeneratorARM::Move32(Location destination, Location source) {
294  if (source.Equals(destination)) {
295    return;
296  }
297  if (destination.IsRegister()) {
298    if (source.IsRegister()) {
299      __ Mov(destination.AsArm().AsCoreRegister(), source.AsArm().AsCoreRegister());
300    } else {
301      __ ldr(destination.AsArm().AsCoreRegister(), Address(SP, source.GetStackIndex()));
302    }
303  } else {
304    DCHECK(destination.IsStackSlot());
305    if (source.IsRegister()) {
306      __ str(source.AsArm().AsCoreRegister(), Address(SP, destination.GetStackIndex()));
307    } else {
308      __ ldr(IP, Address(SP, source.GetStackIndex()));
309      __ str(IP, Address(SP, destination.GetStackIndex()));
310    }
311  }
312}
313
314void CodeGeneratorARM::Move64(Location destination, Location source) {
315  if (source.Equals(destination)) {
316    return;
317  }
318  if (destination.IsRegister()) {
319    if (source.IsRegister()) {
320      __ Mov(destination.AsArm().AsRegisterPairLow(), source.AsArm().AsRegisterPairLow());
321      __ Mov(destination.AsArm().AsRegisterPairHigh(), source.AsArm().AsRegisterPairHigh());
322    } else if (source.IsQuickParameter()) {
323      uint32_t argument_index = source.GetQuickParameterIndex();
324      InvokeDexCallingConvention calling_convention;
325      __ Mov(destination.AsArm().AsRegisterPairLow(),
326             calling_convention.GetRegisterAt(argument_index));
327      __ ldr(destination.AsArm().AsRegisterPairHigh(),
328             Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1) + GetFrameSize()));
329    } else {
330      DCHECK(source.IsDoubleStackSlot());
331      if (destination.AsArm().AsRegisterPair() == R1_R2) {
332        __ ldr(R1, Address(SP, source.GetStackIndex()));
333        __ ldr(R2, Address(SP, source.GetHighStackIndex(kArmWordSize)));
334      } else {
335        __ LoadFromOffset(kLoadWordPair, destination.AsArm().AsRegisterPairLow(),
336                          SP, source.GetStackIndex());
337      }
338    }
339  } else if (destination.IsQuickParameter()) {
340    InvokeDexCallingConvention calling_convention;
341    uint32_t argument_index = destination.GetQuickParameterIndex();
342    if (source.IsRegister()) {
343      __ Mov(calling_convention.GetRegisterAt(argument_index), source.AsArm().AsRegisterPairLow());
344      __ str(source.AsArm().AsRegisterPairHigh(),
345             Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1)));
346    } else {
347      DCHECK(source.IsDoubleStackSlot());
348      __ ldr(calling_convention.GetRegisterAt(argument_index), Address(SP, source.GetStackIndex()));
349      __ ldr(R0, Address(SP, source.GetHighStackIndex(kArmWordSize)));
350      __ str(R0, Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1)));
351    }
352  } else {
353    DCHECK(destination.IsDoubleStackSlot());
354    if (source.IsRegister()) {
355      if (source.AsArm().AsRegisterPair() == R1_R2) {
356        __ str(R1, Address(SP, destination.GetStackIndex()));
357        __ str(R2, Address(SP, destination.GetHighStackIndex(kArmWordSize)));
358      } else {
359        __ StoreToOffset(kStoreWordPair, source.AsArm().AsRegisterPairLow(),
360                         SP, destination.GetStackIndex());
361      }
362    } else if (source.IsQuickParameter()) {
363      InvokeDexCallingConvention calling_convention;
364      uint32_t argument_index = source.GetQuickParameterIndex();
365      __ str(calling_convention.GetRegisterAt(argument_index),
366             Address(SP, destination.GetStackIndex()));
367      __ ldr(R0,
368             Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1) + GetFrameSize()));
369      __ str(R0, Address(SP, destination.GetHighStackIndex(kArmWordSize)));
370    } else {
371      DCHECK(source.IsDoubleStackSlot());
372      __ ldr(IP, Address(SP, source.GetStackIndex()));
373      __ str(IP, Address(SP, destination.GetStackIndex()));
374      __ ldr(IP, Address(SP, source.GetHighStackIndex(kArmWordSize)));
375      __ str(IP, Address(SP, destination.GetHighStackIndex(kArmWordSize)));
376    }
377  }
378}
379
380void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstruction* move_for) {
381  if (instruction->AsIntConstant() != nullptr) {
382    int32_t value = instruction->AsIntConstant()->GetValue();
383    if (location.IsRegister()) {
384      __ LoadImmediate(location.AsArm().AsCoreRegister(), value);
385    } else {
386      __ LoadImmediate(IP, value);
387      __ str(IP, Address(SP, location.GetStackIndex()));
388    }
389  } else if (instruction->AsLongConstant() != nullptr) {
390    int64_t value = instruction->AsLongConstant()->GetValue();
391    if (location.IsRegister()) {
392      __ LoadImmediate(location.AsArm().AsRegisterPairLow(), Low32Bits(value));
393      __ LoadImmediate(location.AsArm().AsRegisterPairHigh(), High32Bits(value));
394    } else {
395      __ LoadImmediate(IP, Low32Bits(value));
396      __ str(IP, Address(SP, location.GetStackIndex()));
397      __ LoadImmediate(IP, High32Bits(value));
398      __ str(IP, Address(SP, location.GetHighStackIndex(kArmWordSize)));
399    }
400  } else if (instruction->AsLoadLocal() != nullptr) {
401    uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
402    switch (instruction->GetType()) {
403      case Primitive::kPrimBoolean:
404      case Primitive::kPrimByte:
405      case Primitive::kPrimChar:
406      case Primitive::kPrimShort:
407      case Primitive::kPrimInt:
408      case Primitive::kPrimNot:
409        Move32(location, Location::StackSlot(stack_slot));
410        break;
411
412      case Primitive::kPrimLong:
413        Move64(location, Location::DoubleStackSlot(stack_slot));
414        break;
415
416      default:
417        LOG(FATAL) << "Unimplemented type " << instruction->GetType();
418    }
419  } else {
420    DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
421    switch (instruction->GetType()) {
422      case Primitive::kPrimBoolean:
423      case Primitive::kPrimByte:
424      case Primitive::kPrimChar:
425      case Primitive::kPrimShort:
426      case Primitive::kPrimNot:
427      case Primitive::kPrimInt:
428        Move32(location, instruction->GetLocations()->Out());
429        break;
430
431      case Primitive::kPrimLong:
432        Move64(location, instruction->GetLocations()->Out());
433        break;
434
435      default:
436        LOG(FATAL) << "Unimplemented type " << instruction->GetType();
437    }
438  }
439}
440
441void LocationsBuilderARM::VisitGoto(HGoto* got) {
442  got->SetLocations(nullptr);
443}
444
445void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) {
446  HBasicBlock* successor = got->GetSuccessor();
447  if (GetGraph()->GetExitBlock() == successor) {
448    codegen_->GenerateFrameExit();
449  } else if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
450    __ b(codegen_->GetLabelOf(successor));
451  }
452}
453
454void LocationsBuilderARM::VisitExit(HExit* exit) {
455  exit->SetLocations(nullptr);
456}
457
458void InstructionCodeGeneratorARM::VisitExit(HExit* exit) {
459  if (kIsDebugBuild) {
460    __ Comment("Unreachable");
461    __ bkpt(0);
462  }
463}
464
465void LocationsBuilderARM::VisitIf(HIf* if_instr) {
466  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
467  HInstruction* cond = if_instr->InputAt(0);
468  DCHECK(cond->IsCondition());
469  HCondition* condition = cond->AsCondition();
470  if (condition->NeedsMaterialization()) {
471    locations->SetInAt(0, Location::Any());
472  }
473  if_instr->SetLocations(locations);
474}
475
476void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
477  HInstruction* cond = if_instr->InputAt(0);
478  DCHECK(cond->IsCondition());
479  HCondition* condition = cond->AsCondition();
480  if (condition->NeedsMaterialization()) {
481    // Condition has been materialized, compare the output to 0
482    if (!if_instr->GetLocations()->InAt(0).IsRegister()) {
483      LOG(FATAL) << "Materialized condition is not in an ARM register";
484    }
485    __ cmp(if_instr->GetLocations()->InAt(0).AsArm().AsCoreRegister(),
486           ShifterOperand(0));
487    __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), EQ);
488  } else {
489    // Condition has not been materialized, use its inputs as the comparison and its
490    // condition as the branch condition.
491    __ cmp(condition->GetLocations()->InAt(0).AsArm().AsCoreRegister(),
492           ShifterOperand(condition->GetLocations()->InAt(1).AsArm().AsCoreRegister()));
493    __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()),
494         ARMCondition(condition->GetCondition()));
495  }
496  if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) {
497    __ b(codegen_->GetLabelOf(if_instr->IfFalseSuccessor()));
498  }
499}
500
501
502void LocationsBuilderARM::VisitCondition(HCondition* comp) {
503  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
504  locations->SetInAt(0, Location::RequiresRegister());
505  locations->SetInAt(1, Location::RequiresRegister());
506  if (comp->NeedsMaterialization()) {
507    locations->SetOut(Location::RequiresRegister());
508  }
509  comp->SetLocations(locations);
510}
511
512void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) {
513  if (comp->NeedsMaterialization()) {
514    LocationSummary* locations = comp->GetLocations();
515    __ cmp(locations->InAt(0).AsArm().AsCoreRegister(),
516           ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
517    __ it(ARMCondition(comp->GetCondition()), kItElse);
518    __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(1),
519           ARMCondition(comp->GetCondition()));
520    __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(0),
521           ARMOppositeCondition(comp->GetCondition()));
522  }
523}
524
525void LocationsBuilderARM::VisitEqual(HEqual* comp) {
526  VisitCondition(comp);
527}
528
529void InstructionCodeGeneratorARM::VisitEqual(HEqual* comp) {
530  VisitCondition(comp);
531}
532
533void LocationsBuilderARM::VisitNotEqual(HNotEqual* comp) {
534  VisitCondition(comp);
535}
536
537void InstructionCodeGeneratorARM::VisitNotEqual(HNotEqual* comp) {
538  VisitCondition(comp);
539}
540
541void LocationsBuilderARM::VisitLessThan(HLessThan* comp) {
542  VisitCondition(comp);
543}
544
545void InstructionCodeGeneratorARM::VisitLessThan(HLessThan* comp) {
546  VisitCondition(comp);
547}
548
549void LocationsBuilderARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
550  VisitCondition(comp);
551}
552
553void InstructionCodeGeneratorARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
554  VisitCondition(comp);
555}
556
557void LocationsBuilderARM::VisitGreaterThan(HGreaterThan* comp) {
558  VisitCondition(comp);
559}
560
561void InstructionCodeGeneratorARM::VisitGreaterThan(HGreaterThan* comp) {
562  VisitCondition(comp);
563}
564
565void LocationsBuilderARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
566  VisitCondition(comp);
567}
568
569void InstructionCodeGeneratorARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
570  VisitCondition(comp);
571}
572
573void LocationsBuilderARM::VisitLocal(HLocal* local) {
574  local->SetLocations(nullptr);
575}
576
577void InstructionCodeGeneratorARM::VisitLocal(HLocal* local) {
578  DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
579}
580
581void LocationsBuilderARM::VisitLoadLocal(HLoadLocal* load) {
582  load->SetLocations(nullptr);
583}
584
585void InstructionCodeGeneratorARM::VisitLoadLocal(HLoadLocal* load) {
586  // Nothing to do, this is driven by the code generator.
587}
588
589void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) {
590  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
591  switch (store->InputAt(1)->GetType()) {
592    case Primitive::kPrimBoolean:
593    case Primitive::kPrimByte:
594    case Primitive::kPrimChar:
595    case Primitive::kPrimShort:
596    case Primitive::kPrimInt:
597    case Primitive::kPrimNot:
598      locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
599      break;
600
601    case Primitive::kPrimLong:
602      locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
603      break;
604
605    default:
606      LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
607  }
608  store->SetLocations(locations);
609}
610
611void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) {
612}
613
614void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) {
615  // TODO: Support constant locations.
616  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
617  locations->SetOut(Location::RequiresRegister());
618  constant->SetLocations(locations);
619}
620
621void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) {
622  codegen_->Move(constant, constant->GetLocations()->Out(), nullptr);
623}
624
625void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) {
626  // TODO: Support constant locations.
627  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
628  locations->SetOut(Location::RequiresRegister());
629  constant->SetLocations(locations);
630}
631
632void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant) {
633  // Will be generated at use site.
634}
635
636void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) {
637  ret->SetLocations(nullptr);
638}
639
640void InstructionCodeGeneratorARM::VisitReturnVoid(HReturnVoid* ret) {
641  codegen_->GenerateFrameExit();
642}
643
644void LocationsBuilderARM::VisitReturn(HReturn* ret) {
645  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
646  switch (ret->InputAt(0)->GetType()) {
647    case Primitive::kPrimBoolean:
648    case Primitive::kPrimByte:
649    case Primitive::kPrimChar:
650    case Primitive::kPrimShort:
651    case Primitive::kPrimInt:
652    case Primitive::kPrimNot:
653      locations->SetInAt(0, ArmCoreLocation(R0));
654      break;
655
656    case Primitive::kPrimLong:
657      locations->SetInAt(
658          0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
659      break;
660
661    default:
662      LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
663  }
664
665  ret->SetLocations(locations);
666}
667
668void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) {
669  if (kIsDebugBuild) {
670    switch (ret->InputAt(0)->GetType()) {
671      case Primitive::kPrimBoolean:
672      case Primitive::kPrimByte:
673      case Primitive::kPrimChar:
674      case Primitive::kPrimShort:
675      case Primitive::kPrimInt:
676      case Primitive::kPrimNot:
677        DCHECK_EQ(ret->GetLocations()->InAt(0).AsArm().AsCoreRegister(), R0);
678        break;
679
680      case Primitive::kPrimLong:
681        DCHECK_EQ(ret->GetLocations()->InAt(0).AsArm().AsRegisterPair(), R0_R1);
682        break;
683
684      default:
685        LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
686    }
687  }
688  codegen_->GenerateFrameExit();
689}
690
691void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
692  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
693  locations->AddTemp(ArmCoreLocation(R0));
694
695  InvokeDexCallingConventionVisitor calling_convention_visitor;
696  for (size_t i = 0; i < invoke->InputCount(); i++) {
697    HInstruction* input = invoke->InputAt(i);
698    locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
699  }
700
701  switch (invoke->GetType()) {
702    case Primitive::kPrimBoolean:
703    case Primitive::kPrimByte:
704    case Primitive::kPrimChar:
705    case Primitive::kPrimShort:
706    case Primitive::kPrimInt:
707    case Primitive::kPrimNot:
708      locations->SetOut(ArmCoreLocation(R0));
709      break;
710
711    case Primitive::kPrimLong:
712      locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
713      break;
714
715    case Primitive::kPrimVoid:
716      break;
717
718    case Primitive::kPrimDouble:
719    case Primitive::kPrimFloat:
720      LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
721      break;
722  }
723
724  invoke->SetLocations(locations);
725}
726
727void InstructionCodeGeneratorARM::LoadCurrentMethod(Register reg) {
728  __ ldr(reg, Address(SP, kCurrentMethodStackOffset));
729}
730
731void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) {
732  Register temp = invoke->GetLocations()->GetTemp(0).AsArm().AsCoreRegister();
733  uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>);
734  size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() +
735      invoke->GetIndexInDexCache() * kArmWordSize;
736
737  // TODO: Implement all kinds of calls:
738  // 1) boot -> boot
739  // 2) app -> boot
740  // 3) app -> app
741  //
742  // Currently we implement the app -> app logic, which looks up in the resolve cache.
743
744  // temp = method;
745  LoadCurrentMethod(temp);
746  // temp = temp->dex_cache_resolved_methods_;
747  __ ldr(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
748  // temp = temp[index_in_cache]
749  __ ldr(temp, Address(temp, index_in_cache));
750  // LR = temp[offset_of_quick_compiled_code]
751  __ ldr(LR, Address(temp,
752                     mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
753  // LR()
754  __ blx(LR);
755
756  codegen_->RecordPcInfo(invoke->GetDexPc());
757}
758
759void LocationsBuilderARM::VisitAdd(HAdd* add) {
760  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
761  switch (add->GetResultType()) {
762    case Primitive::kPrimInt:
763    case Primitive::kPrimLong: {
764      locations->SetInAt(0, Location::RequiresRegister());
765      locations->SetInAt(1, Location::RequiresRegister());
766      locations->SetOut(Location::RequiresRegister());
767      break;
768    }
769
770    case Primitive::kPrimBoolean:
771    case Primitive::kPrimByte:
772    case Primitive::kPrimChar:
773    case Primitive::kPrimShort:
774      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
775      break;
776
777    default:
778      LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
779  }
780  add->SetLocations(locations);
781}
782
783void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) {
784  LocationSummary* locations = add->GetLocations();
785  switch (add->GetResultType()) {
786    case Primitive::kPrimInt:
787      __ add(locations->Out().AsArm().AsCoreRegister(),
788             locations->InAt(0).AsArm().AsCoreRegister(),
789             ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
790      break;
791
792    case Primitive::kPrimLong:
793      __ adds(locations->Out().AsArm().AsRegisterPairLow(),
794              locations->InAt(0).AsArm().AsRegisterPairLow(),
795              ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairLow()));
796      __ adc(locations->Out().AsArm().AsRegisterPairHigh(),
797             locations->InAt(0).AsArm().AsRegisterPairHigh(),
798             ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairHigh()));
799      break;
800
801    case Primitive::kPrimBoolean:
802    case Primitive::kPrimByte:
803    case Primitive::kPrimChar:
804    case Primitive::kPrimShort:
805      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
806      break;
807
808    default:
809      LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
810  }
811}
812
813void LocationsBuilderARM::VisitSub(HSub* sub) {
814  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
815  switch (sub->GetResultType()) {
816    case Primitive::kPrimInt:
817    case Primitive::kPrimLong: {
818      locations->SetInAt(0, Location::RequiresRegister());
819      locations->SetInAt(1, Location::RequiresRegister());
820      locations->SetOut(Location::RequiresRegister());
821      break;
822    }
823
824    case Primitive::kPrimBoolean:
825    case Primitive::kPrimByte:
826    case Primitive::kPrimChar:
827    case Primitive::kPrimShort:
828      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
829      break;
830
831    default:
832      LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
833  }
834  sub->SetLocations(locations);
835}
836
837void InstructionCodeGeneratorARM::VisitSub(HSub* sub) {
838  LocationSummary* locations = sub->GetLocations();
839  switch (sub->GetResultType()) {
840    case Primitive::kPrimInt:
841      __ sub(locations->Out().AsArm().AsCoreRegister(),
842             locations->InAt(0).AsArm().AsCoreRegister(),
843             ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
844      break;
845
846    case Primitive::kPrimLong:
847      __ subs(locations->Out().AsArm().AsRegisterPairLow(),
848              locations->InAt(0).AsArm().AsRegisterPairLow(),
849              ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairLow()));
850      __ sbc(locations->Out().AsArm().AsRegisterPairHigh(),
851             locations->InAt(0).AsArm().AsRegisterPairHigh(),
852             ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairHigh()));
853      break;
854
855    case Primitive::kPrimBoolean:
856    case Primitive::kPrimByte:
857    case Primitive::kPrimChar:
858    case Primitive::kPrimShort:
859      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
860      break;
861
862    default:
863      LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
864  }
865}
866
867static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1 };
868static constexpr size_t kRuntimeParameterCoreRegistersLength =
869    arraysize(kRuntimeParameterCoreRegisters);
870
871class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
872 public:
873  InvokeRuntimeCallingConvention()
874      : CallingConvention(kRuntimeParameterCoreRegisters,
875                          kRuntimeParameterCoreRegistersLength) {}
876
877 private:
878  DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
879};
880
881void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
882  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
883  InvokeRuntimeCallingConvention calling_convention;
884  locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(0)));
885  locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(1)));
886  locations->SetOut(ArmCoreLocation(R0));
887  instruction->SetLocations(locations);
888}
889
890void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
891  InvokeRuntimeCallingConvention calling_convention;
892  LoadCurrentMethod(calling_convention.GetRegisterAt(1));
893  __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
894
895  int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocObjectWithAccessCheck).Int32Value();
896  __ ldr(LR, Address(TR, offset));
897  __ blx(LR);
898
899  codegen_->RecordPcInfo(instruction->GetDexPc());
900}
901
902void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
903  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
904  Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
905  if (location.IsStackSlot()) {
906    location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
907  } else if (location.IsDoubleStackSlot()) {
908    location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
909  }
910  locations->SetOut(location);
911  instruction->SetLocations(locations);
912}
913
914void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instruction) {
915  // Nothing to do, the parameter is already at its location.
916}
917
918void LocationsBuilderARM::VisitNot(HNot* instruction) {
919  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
920  locations->SetInAt(0, Location::RequiresRegister());
921  locations->SetOut(Location::RequiresRegister());
922  instruction->SetLocations(locations);
923}
924
925void InstructionCodeGeneratorARM::VisitNot(HNot* instruction) {
926  LocationSummary* locations = instruction->GetLocations();
927  __ eor(locations->Out().AsArm().AsCoreRegister(),
928         locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(1));
929}
930
931void LocationsBuilderARM::VisitCompare(HCompare* compare) {
932  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
933  locations->SetInAt(0, Location::RequiresRegister());
934  locations->SetInAt(1, Location::RequiresRegister());
935  locations->SetOut(Location::RequiresRegister());
936  compare->SetLocations(locations);
937}
938
939void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) {
940  Label greater, done;
941  LocationSummary* locations = compare->GetLocations();
942  switch (compare->InputAt(0)->GetType()) {
943    case Primitive::kPrimLong: {
944      Register output = locations->Out().AsArm().AsCoreRegister();
945      ArmManagedRegister left = locations->InAt(0).AsArm();
946      ArmManagedRegister right = locations->InAt(1).AsArm();
947      Label less, greater, done;
948      __ cmp(left.AsRegisterPairHigh(),
949             ShifterOperand(right.AsRegisterPairHigh()));  // Signed compare.
950      __ b(&less, LT);
951      __ b(&greater, GT);
952      // Do LoadImmediate before any `cmp`, as LoadImmediate might affect
953      // the status flags.
954      __ LoadImmediate(output, 0);
955      __ cmp(left.AsRegisterPairLow(),
956             ShifterOperand(right.AsRegisterPairLow()));  // Unsigned compare.
957      __ b(&done, EQ);
958      __ b(&less, CC);
959
960      __ Bind(&greater);
961      __ LoadImmediate(output, 1);
962      __ b(&done);
963
964      __ Bind(&less);
965      __ LoadImmediate(output, -1);
966
967      __ Bind(&done);
968      break;
969    }
970    default:
971      LOG(FATAL) << "Unimplemented compare type " << compare->InputAt(0)->GetType();
972  }
973}
974
975void LocationsBuilderARM::VisitPhi(HPhi* instruction) {
976  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
977  for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
978    locations->SetInAt(i, Location::Any());
979  }
980  locations->SetOut(Location::Any());
981  instruction->SetLocations(locations);
982}
983
984void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) {
985  LOG(FATAL) << "Unreachable";
986}
987
988void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
989  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
990  locations->SetInAt(0, Location::RequiresRegister());
991  locations->SetInAt(1, Location::RequiresRegister());
992  instruction->SetLocations(locations);
993}
994
995void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
996  LocationSummary* locations = instruction->GetLocations();
997  Register obj = locations->InAt(0).AsArm().AsCoreRegister();
998  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
999  Primitive::Type field_type = instruction->InputAt(1)->GetType();
1000
1001  switch (field_type) {
1002    case Primitive::kPrimBoolean:
1003    case Primitive::kPrimByte: {
1004      Register value = locations->InAt(1).AsArm().AsCoreRegister();
1005      __ StoreToOffset(kStoreByte, value, obj, offset);
1006      break;
1007    }
1008
1009    case Primitive::kPrimShort:
1010    case Primitive::kPrimChar: {
1011      Register value = locations->InAt(1).AsArm().AsCoreRegister();
1012      __ StoreToOffset(kStoreHalfword, value, obj, offset);
1013      break;
1014    }
1015
1016    case Primitive::kPrimInt:
1017    case Primitive::kPrimNot: {
1018      Register value = locations->InAt(1).AsArm().AsCoreRegister();
1019      __ StoreToOffset(kStoreWord, value, obj, offset);
1020      break;
1021    }
1022
1023    case Primitive::kPrimLong: {
1024      ArmManagedRegister value = locations->InAt(1).AsArm();
1025      __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), obj, offset);
1026      break;
1027    }
1028
1029    case Primitive::kPrimFloat:
1030    case Primitive::kPrimDouble:
1031      LOG(FATAL) << "Unimplemented register type " << field_type;
1032
1033    case Primitive::kPrimVoid:
1034      LOG(FATAL) << "Unreachable type " << field_type;
1035  }
1036}
1037
1038void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
1039  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
1040  locations->SetInAt(0, Location::RequiresRegister());
1041  locations->SetOut(Location::RequiresRegister());
1042  instruction->SetLocations(locations);
1043}
1044
1045void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
1046  LocationSummary* locations = instruction->GetLocations();
1047  Register obj = locations->InAt(0).AsArm().AsCoreRegister();
1048  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
1049
1050  switch (instruction->GetType()) {
1051    case Primitive::kPrimBoolean: {
1052      Register out = locations->Out().AsArm().AsCoreRegister();
1053      __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset);
1054      break;
1055    }
1056
1057    case Primitive::kPrimByte: {
1058      Register out = locations->Out().AsArm().AsCoreRegister();
1059      __ LoadFromOffset(kLoadSignedByte, out, obj, offset);
1060      break;
1061    }
1062
1063    case Primitive::kPrimShort: {
1064      Register out = locations->Out().AsArm().AsCoreRegister();
1065      __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset);
1066      break;
1067    }
1068
1069    case Primitive::kPrimChar: {
1070      Register out = locations->Out().AsArm().AsCoreRegister();
1071      __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset);
1072      break;
1073    }
1074
1075    case Primitive::kPrimInt:
1076    case Primitive::kPrimNot: {
1077      Register out = locations->Out().AsArm().AsCoreRegister();
1078      __ LoadFromOffset(kLoadWord, out, obj, offset);
1079      break;
1080    }
1081
1082    case Primitive::kPrimLong: {
1083      // TODO: support volatile.
1084      ArmManagedRegister out = locations->Out().AsArm();
1085      __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), obj, offset);
1086      break;
1087    }
1088
1089    case Primitive::kPrimFloat:
1090    case Primitive::kPrimDouble:
1091      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
1092
1093    case Primitive::kPrimVoid:
1094      LOG(FATAL) << "Unreachable type " << instruction->GetType();
1095  }
1096}
1097
1098void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
1099  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
1100  locations->SetInAt(0, Location::RequiresRegister());
1101  // TODO: Have a normalization phase that makes this instruction never used.
1102  locations->SetOut(Location::SameAsFirstInput());
1103  instruction->SetLocations(locations);
1104}
1105
1106void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
1107  SlowPathCode* slow_path =
1108      new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction->GetDexPc());
1109  codegen_->AddSlowPath(slow_path);
1110
1111  LocationSummary* locations = instruction->GetLocations();
1112  Location obj = locations->InAt(0);
1113  DCHECK(obj.Equals(locations->Out()));
1114
1115  if (obj.IsRegister()) {
1116    __ cmp(obj.AsArm().AsCoreRegister(), ShifterOperand(0));
1117  }
1118  __ b(slow_path->GetEntryLabel(), EQ);
1119}
1120
1121void LocationsBuilderARM::VisitTemporary(HTemporary* temp) {
1122  temp->SetLocations(nullptr);
1123}
1124
1125void InstructionCodeGeneratorARM::VisitTemporary(HTemporary* temp) {
1126  // Nothing to do, this is driven by the code generator.
1127}
1128
1129void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction) {
1130  LOG(FATAL) << "Unreachable";
1131}
1132
1133void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) {
1134  codegen_->GetMoveResolver()->EmitNativeCode(instruction);
1135}
1136
1137ArmAssembler* ParallelMoveResolverARM::GetAssembler() const {
1138  return codegen_->GetAssembler();
1139}
1140
1141void ParallelMoveResolverARM::EmitMove(size_t index) {
1142  MoveOperands* move = moves_.Get(index);
1143  Location source = move->GetSource();
1144  Location destination = move->GetDestination();
1145
1146  if (source.IsRegister()) {
1147    if (destination.IsRegister()) {
1148      __ Mov(destination.AsArm().AsCoreRegister(), source.AsArm().AsCoreRegister());
1149    } else {
1150      DCHECK(destination.IsStackSlot());
1151      __ StoreToOffset(kStoreWord, source.AsArm().AsCoreRegister(),
1152                       SP, destination.GetStackIndex());
1153    }
1154  } else if (source.IsStackSlot()) {
1155    if (destination.IsRegister()) {
1156      __ LoadFromOffset(kLoadWord, destination.AsArm().AsCoreRegister(),
1157                        SP, source.GetStackIndex());
1158    } else {
1159      DCHECK(destination.IsStackSlot());
1160      __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex());
1161      __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
1162    }
1163  } else {
1164    LOG(FATAL) << "Unimplemented";
1165  }
1166}
1167
1168void ParallelMoveResolverARM::Exchange(Register reg, int mem) {
1169  __ Mov(IP, reg);
1170  __ LoadFromOffset(kLoadWord, reg, SP, mem);
1171  __ StoreToOffset(kStoreWord, IP, SP, mem);
1172}
1173
1174void ParallelMoveResolverARM::Exchange(int mem1, int mem2) {
1175  ScratchRegisterScope ensure_scratch(this, IP, R0, codegen_->GetNumberOfCoreRegisters());
1176  int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0;
1177  __ LoadFromOffset(kLoadWord, static_cast<Register>(ensure_scratch.GetRegister()),
1178                    SP, mem1 + stack_offset);
1179  __ LoadFromOffset(kLoadWord, IP, SP, mem2 + stack_offset);
1180  __ StoreToOffset(kStoreWord, static_cast<Register>(ensure_scratch.GetRegister()),
1181                   SP, mem2 + stack_offset);
1182  __ StoreToOffset(kStoreWord, IP, SP, mem1 + stack_offset);
1183}
1184
1185void ParallelMoveResolverARM::EmitSwap(size_t index) {
1186  MoveOperands* move = moves_.Get(index);
1187  Location source = move->GetSource();
1188  Location destination = move->GetDestination();
1189
1190  if (source.IsRegister() && destination.IsRegister()) {
1191    DCHECK_NE(source.AsArm().AsCoreRegister(), IP);
1192    DCHECK_NE(destination.AsArm().AsCoreRegister(), IP);
1193    __ Mov(IP, source.AsArm().AsCoreRegister());
1194    __ Mov(source.AsArm().AsCoreRegister(), destination.AsArm().AsCoreRegister());
1195    __ Mov(destination.AsArm().AsCoreRegister(), IP);
1196  } else if (source.IsRegister() && destination.IsStackSlot()) {
1197    Exchange(source.AsArm().AsCoreRegister(), destination.GetStackIndex());
1198  } else if (source.IsStackSlot() && destination.IsRegister()) {
1199    Exchange(destination.AsArm().AsCoreRegister(), source.GetStackIndex());
1200  } else if (source.IsStackSlot() && destination.IsStackSlot()) {
1201    Exchange(source.GetStackIndex(), destination.GetStackIndex());
1202  } else {
1203    LOG(FATAL) << "Unimplemented";
1204  }
1205}
1206
1207void ParallelMoveResolverARM::SpillScratch(int reg) {
1208  __ Push(static_cast<Register>(reg));
1209}
1210
1211void ParallelMoveResolverARM::RestoreScratch(int reg) {
1212  __ Pop(static_cast<Register>(reg));
1213}
1214
1215}  // namespace arm
1216}  // namespace art
1217