blob: 93e7367182bbfe1dc0be49941735ef0007c010ec [file] [log] [blame]
Nicolas Geoffrayd4dd2552014-02-28 10:23:58 +00001/*
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"
Nicolas Geoffrayd4dd2552014-02-28 10:23:58 +000018
Ian Rogersb0fa5dc2014-04-28 16:47:08 -070019#include "entrypoints/quick/quick_entrypoints.h"
Nicolas Geoffray1a43dd72014-07-17 15:15:34 +010020#include "gc/accounting/card_table.h"
Nicolas Geoffray8ccc3f52014-03-19 10:34:11 +000021#include "mirror/array.h"
22#include "mirror/art_method.h"
Ian Rogersb0fa5dc2014-04-28 16:47:08 -070023#include "thread.h"
Nicolas Geoffray9cf35522014-06-09 18:40:10 +010024#include "utils/assembler.h"
25#include "utils/arm/assembler_arm.h"
26#include "utils/arm/managed_register_arm.h"
Nicolas Geoffray8ccc3f52014-03-19 10:34:11 +000027
Nicolas Geoffrayd4dd2552014-02-28 10:23:58 +000028namespace art {
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +010029
30arm::ArmManagedRegister Location::AsArm() const {
31 return reg().AsArm();
32}
33
Nicolas Geoffrayd4dd2552014-02-28 10:23:58 +000034namespace arm {
35
Nicolas Geoffraye5038322014-07-04 09:41:32 +010036#define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())->
37
38class NullCheckSlowPathARM : public SlowPathCode {
39 public:
40 explicit NullCheckSlowPathARM(uint32_t dex_pc) : dex_pc_(dex_pc) {}
41
42 virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
43 __ Bind(GetEntryLabel());
44 int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowNullPointer).Int32Value();
45 __ ldr(LR, Address(TR, offset));
46 __ blx(LR);
47 codegen->RecordPcInfo(dex_pc_);
48 }
49
50 private:
51 const uint32_t dex_pc_;
52 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
53};
54
55#undef __
56#define __ reinterpret_cast<ArmAssembler*>(GetAssembler())->
Dave Allison20dfc792014-06-16 20:44:29 -070057
58inline Condition ARMCondition(IfCondition cond) {
59 switch (cond) {
60 case kCondEQ: return EQ;
61 case kCondNE: return NE;
62 case kCondLT: return LT;
63 case kCondLE: return LE;
64 case kCondGT: return GT;
65 case kCondGE: return GE;
66 default:
67 LOG(FATAL) << "Unknown if condition";
68 }
69 return EQ; // Unreachable.
70}
71
72inline Condition ARMOppositeCondition(IfCondition cond) {
73 switch (cond) {
74 case kCondEQ: return NE;
75 case kCondNE: return EQ;
76 case kCondLT: return GE;
77 case kCondLE: return GT;
78 case kCondGT: return LE;
79 case kCondGE: return LT;
80 default:
81 LOG(FATAL) << "Unknown if condition";
82 }
83 return EQ; // Unreachable.
84}
85
Nicolas Geoffraye5038322014-07-04 09:41:32 +010086static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2; // LR, R6, R7
Nicolas Geoffray4a34a422014-04-03 10:38:37 +010087static constexpr int kCurrentMethodStackOffset = 0;
88
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010089void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const {
90 stream << ArmManagedRegister::FromCoreRegister(Register(reg));
91}
92
93void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
94 stream << ArmManagedRegister::FromDRegister(DRegister(reg));
95}
96
Nicolas Geoffraya7aca372014-04-28 17:47:12 +010097CodeGeneratorARM::CodeGeneratorARM(HGraph* graph)
98 : CodeGenerator(graph, kNumberOfRegIds),
99 location_builder_(graph, this),
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +0100100 instruction_visitor_(graph, this),
Nicolas Geoffray8d486732014-07-16 16:23:40 +0100101 move_resolver_(graph->GetArena(), this),
102 assembler_(true) {}
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100103
Nicolas Geoffrayab032bc2014-07-15 12:55:21 +0100104size_t CodeGeneratorARM::FrameEntrySpillSize() const {
105 return kNumberOfPushedRegistersAtEntry * kArmWordSize;
106}
107
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100108static bool* GetBlockedRegisterPairs(bool* blocked_registers) {
109 return blocked_registers + kNumberOfAllocIds;
110}
111
112ManagedRegister CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type,
113 bool* blocked_registers) const {
114 switch (type) {
115 case Primitive::kPrimLong: {
Nicolas Geoffraye5038322014-07-04 09:41:32 +0100116 bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
117 size_t reg = AllocateFreeRegisterInternal(blocked_register_pairs, kNumberOfRegisterPairs);
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100118 ArmManagedRegister pair =
119 ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg));
120 blocked_registers[pair.AsRegisterPairLow()] = true;
121 blocked_registers[pair.AsRegisterPairHigh()] = true;
Nicolas Geoffraye5038322014-07-04 09:41:32 +0100122 // Block all other register pairs that share a register with `pair`.
123 for (int i = 0; i < kNumberOfRegisterPairs; i++) {
124 ArmManagedRegister current =
125 ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i));
126 if (current.AsRegisterPairLow() == pair.AsRegisterPairLow()
127 || current.AsRegisterPairLow() == pair.AsRegisterPairHigh()
128 || current.AsRegisterPairHigh() == pair.AsRegisterPairLow()
129 || current.AsRegisterPairHigh() == pair.AsRegisterPairHigh()) {
130 blocked_register_pairs[i] = true;
131 }
132 }
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100133 return pair;
134 }
135
136 case Primitive::kPrimByte:
137 case Primitive::kPrimBoolean:
138 case Primitive::kPrimChar:
139 case Primitive::kPrimShort:
140 case Primitive::kPrimInt:
141 case Primitive::kPrimNot: {
Nicolas Geoffraye5038322014-07-04 09:41:32 +0100142 int reg = AllocateFreeRegisterInternal(blocked_registers, kNumberOfCoreRegisters);
143 // Block all register pairs that contain `reg`.
144 bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
145 for (int i = 0; i < kNumberOfRegisterPairs; i++) {
146 ArmManagedRegister current =
147 ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i));
148 if (current.AsRegisterPairLow() == reg || current.AsRegisterPairHigh() == reg) {
149 blocked_register_pairs[i] = true;
150 }
151 }
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100152 return ArmManagedRegister::FromCoreRegister(static_cast<Register>(reg));
153 }
154
155 case Primitive::kPrimFloat:
156 case Primitive::kPrimDouble:
157 LOG(FATAL) << "Unimplemented register type " << type;
158
159 case Primitive::kPrimVoid:
160 LOG(FATAL) << "Unreachable type " << type;
161 }
162
163 return ManagedRegister::NoRegister();
164}
165
166void CodeGeneratorARM::SetupBlockedRegisters(bool* blocked_registers) const {
167 bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
168
169 // Don't allocate the dalvik style register pair passing.
170 blocked_register_pairs[R1_R2] = true;
171
172 // Stack register, LR and PC are always reserved.
173 blocked_registers[SP] = true;
174 blocked_registers[LR] = true;
175 blocked_registers[PC] = true;
176
177 // Reserve R4 for suspend check.
178 blocked_registers[R4] = true;
179 blocked_register_pairs[R4_R5] = true;
180
181 // Reserve thread register.
182 blocked_registers[TR] = true;
183
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +0100184 // Reserve temp register.
185 blocked_registers[IP] = true;
186
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100187 // TODO: We currently don't use Quick's callee saved registers.
Nicolas Geoffraye5038322014-07-04 09:41:32 +0100188 // We always save and restore R6 and R7 to make sure we can use three
189 // register pairs for long operations.
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100190 blocked_registers[R5] = true;
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100191 blocked_registers[R8] = true;
192 blocked_registers[R10] = true;
193 blocked_registers[R11] = true;
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100194}
195
196size_t CodeGeneratorARM::GetNumberOfRegisters() const {
197 return kNumberOfRegIds;
198}
199
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100200static Location ArmCoreLocation(Register reg) {
201 return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg));
202}
203
Nicolas Geoffray4a34a422014-04-03 10:38:37 +0100204InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen)
205 : HGraphVisitor(graph),
206 assembler_(codegen->GetAssembler()),
207 codegen_(codegen) {}
208
Nicolas Geoffrayd4dd2552014-02-28 10:23:58 +0000209void CodeGeneratorARM::GenerateFrameEntry() {
Nicolas Geoffraye5038322014-07-04 09:41:32 +0100210 core_spill_mask_ |= (1 << LR | 1 << R6 | 1 << R7);
211 __ PushList(1 << LR | 1 << R6 | 1 << R7);
Nicolas Geoffray8ccc3f52014-03-19 10:34:11 +0000212
Nicolas Geoffrayf583e592014-04-07 13:20:42 +0100213 // The return PC has already been pushed on the stack.
Nicolas Geoffray707c8092014-04-04 10:50:14 +0100214 __ AddConstant(SP, -(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize));
Nicolas Geoffray8ccc3f52014-03-19 10:34:11 +0000215 __ str(R0, Address(SP, 0));
Nicolas Geoffrayd4dd2552014-02-28 10:23:58 +0000216}
217
218void CodeGeneratorARM::GenerateFrameExit() {
Nicolas Geoffray707c8092014-04-04 10:50:14 +0100219 __ AddConstant(SP, GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize);
Nicolas Geoffraye5038322014-07-04 09:41:32 +0100220 __ PopList(1 << PC | 1 << R6 | 1 << R7);
Nicolas Geoffrayd4dd2552014-02-28 10:23:58 +0000221}
222
223void CodeGeneratorARM::Bind(Label* label) {
224 __ Bind(label);
225}
226
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100227Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const {
228 switch (load->GetType()) {
229 case Primitive::kPrimLong:
230 return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
231 break;
232
233 case Primitive::kPrimInt:
234 case Primitive::kPrimNot:
235 return Location::StackSlot(GetStackSlot(load->GetLocal()));
236
237 case Primitive::kPrimFloat:
238 case Primitive::kPrimDouble:
239 LOG(FATAL) << "Unimplemented type " << load->GetType();
240
241 case Primitive::kPrimBoolean:
242 case Primitive::kPrimByte:
243 case Primitive::kPrimChar:
244 case Primitive::kPrimShort:
245 case Primitive::kPrimVoid:
246 LOG(FATAL) << "Unexpected type " << load->GetType();
247 }
248
249 LOG(FATAL) << "Unreachable";
250 return Location();
251}
252
Nicolas Geoffraya747a392014-04-17 14:56:23 +0100253Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) {
254 switch (type) {
255 case Primitive::kPrimBoolean:
256 case Primitive::kPrimByte:
257 case Primitive::kPrimChar:
258 case Primitive::kPrimShort:
259 case Primitive::kPrimInt:
260 case Primitive::kPrimNot: {
261 uint32_t index = gp_index_++;
262 if (index < calling_convention.GetNumberOfRegisters()) {
263 return ArmCoreLocation(calling_convention.GetRegisterAt(index));
264 } else {
Nicolas Geoffray9cf35522014-06-09 18:40:10 +0100265 return Location::StackSlot(calling_convention.GetStackOffsetOf(index));
Nicolas Geoffraydb928fc2014-04-16 17:38:32 +0100266 }
Nicolas Geoffraydb928fc2014-04-16 17:38:32 +0100267 }
Nicolas Geoffraya747a392014-04-17 14:56:23 +0100268
269 case Primitive::kPrimLong: {
270 uint32_t index = gp_index_;
271 gp_index_ += 2;
272 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
273 return Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(
274 calling_convention.GetRegisterPairAt(index)));
275 } else if (index + 1 == calling_convention.GetNumberOfRegisters()) {
276 return Location::QuickParameter(index);
277 } else {
Nicolas Geoffray9cf35522014-06-09 18:40:10 +0100278 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(index));
Nicolas Geoffraya747a392014-04-17 14:56:23 +0100279 }
280 }
281
282 case Primitive::kPrimDouble:
283 case Primitive::kPrimFloat:
284 LOG(FATAL) << "Unimplemented parameter type " << type;
285 break;
286
287 case Primitive::kPrimVoid:
288 LOG(FATAL) << "Unexpected parameter type " << type;
289 break;
Nicolas Geoffraydb928fc2014-04-16 17:38:32 +0100290 }
Nicolas Geoffraya747a392014-04-17 14:56:23 +0100291 return Location();
292}
Nicolas Geoffraydb928fc2014-04-16 17:38:32 +0100293
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100294void CodeGeneratorARM::Move32(Location destination, Location source) {
295 if (source.Equals(destination)) {
296 return;
297 }
298 if (destination.IsRegister()) {
299 if (source.IsRegister()) {
300 __ Mov(destination.AsArm().AsCoreRegister(), source.AsArm().AsCoreRegister());
301 } else {
302 __ ldr(destination.AsArm().AsCoreRegister(), Address(SP, source.GetStackIndex()));
303 }
304 } else {
305 DCHECK(destination.IsStackSlot());
306 if (source.IsRegister()) {
307 __ str(source.AsArm().AsCoreRegister(), Address(SP, destination.GetStackIndex()));
308 } else {
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +0100309 __ ldr(IP, Address(SP, source.GetStackIndex()));
310 __ str(IP, Address(SP, destination.GetStackIndex()));
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100311 }
312 }
313}
314
315void CodeGeneratorARM::Move64(Location destination, Location source) {
316 if (source.Equals(destination)) {
317 return;
318 }
319 if (destination.IsRegister()) {
320 if (source.IsRegister()) {
321 __ Mov(destination.AsArm().AsRegisterPairLow(), source.AsArm().AsRegisterPairLow());
322 __ Mov(destination.AsArm().AsRegisterPairHigh(), source.AsArm().AsRegisterPairHigh());
323 } else if (source.IsQuickParameter()) {
324 uint32_t argument_index = source.GetQuickParameterIndex();
325 InvokeDexCallingConvention calling_convention;
326 __ Mov(destination.AsArm().AsRegisterPairLow(),
327 calling_convention.GetRegisterAt(argument_index));
328 __ ldr(destination.AsArm().AsRegisterPairHigh(),
Nicolas Geoffray9cf35522014-06-09 18:40:10 +0100329 Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1) + GetFrameSize()));
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100330 } else {
331 DCHECK(source.IsDoubleStackSlot());
332 if (destination.AsArm().AsRegisterPair() == R1_R2) {
333 __ ldr(R1, Address(SP, source.GetStackIndex()));
334 __ ldr(R2, Address(SP, source.GetHighStackIndex(kArmWordSize)));
335 } else {
336 __ LoadFromOffset(kLoadWordPair, destination.AsArm().AsRegisterPairLow(),
337 SP, source.GetStackIndex());
338 }
339 }
340 } else if (destination.IsQuickParameter()) {
341 InvokeDexCallingConvention calling_convention;
342 uint32_t argument_index = destination.GetQuickParameterIndex();
343 if (source.IsRegister()) {
344 __ Mov(calling_convention.GetRegisterAt(argument_index), source.AsArm().AsRegisterPairLow());
345 __ str(source.AsArm().AsRegisterPairHigh(),
Nicolas Geoffray9cf35522014-06-09 18:40:10 +0100346 Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1)));
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100347 } else {
348 DCHECK(source.IsDoubleStackSlot());
349 __ ldr(calling_convention.GetRegisterAt(argument_index), Address(SP, source.GetStackIndex()));
Nicolas Geoffray9cf35522014-06-09 18:40:10 +0100350 __ ldr(R0, Address(SP, source.GetHighStackIndex(kArmWordSize)));
351 __ str(R0, Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1)));
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100352 }
353 } else {
354 DCHECK(destination.IsDoubleStackSlot());
355 if (source.IsRegister()) {
356 if (source.AsArm().AsRegisterPair() == R1_R2) {
357 __ str(R1, Address(SP, destination.GetStackIndex()));
358 __ str(R2, Address(SP, destination.GetHighStackIndex(kArmWordSize)));
359 } else {
360 __ StoreToOffset(kStoreWordPair, source.AsArm().AsRegisterPairLow(),
361 SP, destination.GetStackIndex());
362 }
363 } else if (source.IsQuickParameter()) {
364 InvokeDexCallingConvention calling_convention;
365 uint32_t argument_index = source.GetQuickParameterIndex();
366 __ str(calling_convention.GetRegisterAt(argument_index),
367 Address(SP, destination.GetStackIndex()));
Nicolas Geoffray9cf35522014-06-09 18:40:10 +0100368 __ ldr(R0,
369 Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1) + GetFrameSize()));
370 __ str(R0, Address(SP, destination.GetHighStackIndex(kArmWordSize)));
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100371 } else {
372 DCHECK(source.IsDoubleStackSlot());
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +0100373 __ ldr(IP, Address(SP, source.GetStackIndex()));
374 __ str(IP, Address(SP, destination.GetStackIndex()));
375 __ ldr(IP, Address(SP, source.GetHighStackIndex(kArmWordSize)));
376 __ str(IP, Address(SP, destination.GetHighStackIndex(kArmWordSize)));
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100377 }
378 }
379}
380
Nicolas Geoffray4a34a422014-04-03 10:38:37 +0100381void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstruction* move_for) {
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100382 LocationSummary* locations = instruction->GetLocations();
383 if (locations != nullptr && locations->Out().Equals(location)) {
384 return;
385 }
386
Nicolas Geoffray4a34a422014-04-03 10:38:37 +0100387 if (instruction->AsIntConstant() != nullptr) {
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100388 int32_t value = instruction->AsIntConstant()->GetValue();
389 if (location.IsRegister()) {
390 __ LoadImmediate(location.AsArm().AsCoreRegister(), value);
391 } else {
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100392 DCHECK(location.IsStackSlot());
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +0100393 __ LoadImmediate(IP, value);
394 __ str(IP, Address(SP, location.GetStackIndex()));
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100395 }
396 } else if (instruction->AsLongConstant() != nullptr) {
397 int64_t value = instruction->AsLongConstant()->GetValue();
398 if (location.IsRegister()) {
399 __ LoadImmediate(location.AsArm().AsRegisterPairLow(), Low32Bits(value));
400 __ LoadImmediate(location.AsArm().AsRegisterPairHigh(), High32Bits(value));
401 } else {
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100402 DCHECK(location.IsDoubleStackSlot());
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +0100403 __ LoadImmediate(IP, Low32Bits(value));
404 __ str(IP, Address(SP, location.GetStackIndex()));
405 __ LoadImmediate(IP, High32Bits(value));
406 __ str(IP, Address(SP, location.GetHighStackIndex(kArmWordSize)));
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100407 }
Nicolas Geoffray4a34a422014-04-03 10:38:37 +0100408 } else if (instruction->AsLoadLocal() != nullptr) {
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100409 uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
410 switch (instruction->GetType()) {
411 case Primitive::kPrimBoolean:
412 case Primitive::kPrimByte:
413 case Primitive::kPrimChar:
414 case Primitive::kPrimShort:
415 case Primitive::kPrimInt:
416 case Primitive::kPrimNot:
417 Move32(location, Location::StackSlot(stack_slot));
418 break;
419
420 case Primitive::kPrimLong:
421 Move64(location, Location::DoubleStackSlot(stack_slot));
422 break;
423
424 default:
425 LOG(FATAL) << "Unimplemented type " << instruction->GetType();
426 }
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000427 } else {
Nicolas Geoffraye5038322014-07-04 09:41:32 +0100428 DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100429 switch (instruction->GetType()) {
430 case Primitive::kPrimBoolean:
431 case Primitive::kPrimByte:
432 case Primitive::kPrimChar:
433 case Primitive::kPrimShort:
434 case Primitive::kPrimNot:
435 case Primitive::kPrimInt:
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100436 Move32(location, locations->Out());
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100437 break;
438
439 case Primitive::kPrimLong:
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100440 Move64(location, locations->Out());
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100441 break;
442
443 default:
444 LOG(FATAL) << "Unimplemented type " << instruction->GetType();
445 }
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000446 }
447}
448
449void LocationsBuilderARM::VisitGoto(HGoto* got) {
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000450 got->SetLocations(nullptr);
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000451}
452
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000453void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) {
Nicolas Geoffrayd4dd2552014-02-28 10:23:58 +0000454 HBasicBlock* successor = got->GetSuccessor();
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000455 if (GetGraph()->GetExitBlock() == successor) {
456 codegen_->GenerateFrameExit();
457 } else if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
458 __ b(codegen_->GetLabelOf(successor));
Nicolas Geoffrayd4dd2552014-02-28 10:23:58 +0000459 }
460}
461
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000462void LocationsBuilderARM::VisitExit(HExit* exit) {
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000463 exit->SetLocations(nullptr);
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000464}
465
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000466void InstructionCodeGeneratorARM::VisitExit(HExit* exit) {
Nicolas Geoffrayd4dd2552014-02-28 10:23:58 +0000467 if (kIsDebugBuild) {
468 __ Comment("Unreachable");
469 __ bkpt(0);
470 }
471}
472
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000473void LocationsBuilderARM::VisitIf(HIf* if_instr) {
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000474 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
Nicolas Geoffraye5038322014-07-04 09:41:32 +0100475 HInstruction* cond = if_instr->InputAt(0);
476 DCHECK(cond->IsCondition());
477 HCondition* condition = cond->AsCondition();
478 if (condition->NeedsMaterialization()) {
479 locations->SetInAt(0, Location::Any());
480 }
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000481 if_instr->SetLocations(locations);
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000482}
483
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000484void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
Dave Allison20dfc792014-06-16 20:44:29 -0700485 HInstruction* cond = if_instr->InputAt(0);
486 DCHECK(cond->IsCondition());
487 HCondition* condition = cond->AsCondition();
488 if (condition->NeedsMaterialization()) {
489 // Condition has been materialized, compare the output to 0
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100490 DCHECK(if_instr->GetLocations()->InAt(0).IsRegister());
Dave Allison20dfc792014-06-16 20:44:29 -0700491 __ cmp(if_instr->GetLocations()->InAt(0).AsArm().AsCoreRegister(),
492 ShifterOperand(0));
493 __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), EQ);
494 } else {
495 // Condition has not been materialized, use its inputs as the comparison and its
496 // condition as the branch condition.
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100497 LocationSummary* locations = condition->GetLocations();
498 if (locations->InAt(1).IsRegister()) {
499 __ cmp(locations->InAt(0).AsArm().AsCoreRegister(),
500 ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
501 } else {
502 DCHECK(locations->InAt(1).IsConstant());
503 int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
504 ShifterOperand operand;
505 if (ShifterOperand::CanHoldArm(value, &operand)) {
506 __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(value));
507 } else {
508 Register temp = IP;
509 __ LoadImmediate(temp, value);
510 __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(temp));
511 }
512 }
Dave Allison20dfc792014-06-16 20:44:29 -0700513 __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()),
514 ARMCondition(condition->GetCondition()));
515 }
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100516
Dave Allison20dfc792014-06-16 20:44:29 -0700517 if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) {
518 __ b(codegen_->GetLabelOf(if_instr->IfFalseSuccessor()));
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000519 }
520}
521
Dave Allison20dfc792014-06-16 20:44:29 -0700522
523void LocationsBuilderARM::VisitCondition(HCondition* comp) {
524 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100525 locations->SetInAt(0, Location::RequiresRegister());
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100526 locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1)));
Nicolas Geoffraye5038322014-07-04 09:41:32 +0100527 if (comp->NeedsMaterialization()) {
528 locations->SetOut(Location::RequiresRegister());
529 }
Dave Allison20dfc792014-06-16 20:44:29 -0700530 comp->SetLocations(locations);
Nicolas Geoffrayd4dd2552014-02-28 10:23:58 +0000531}
532
Dave Allison20dfc792014-06-16 20:44:29 -0700533void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) {
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100534 if (!comp->NeedsMaterialization()) return;
535
536 LocationSummary* locations = comp->GetLocations();
537 if (locations->InAt(1).IsRegister()) {
Dave Allison20dfc792014-06-16 20:44:29 -0700538 __ cmp(locations->InAt(0).AsArm().AsCoreRegister(),
539 ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100540 } else {
541 DCHECK(locations->InAt(1).IsConstant());
542 int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
543 ShifterOperand operand;
544 if (ShifterOperand::CanHoldArm(value, &operand)) {
545 __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(value));
546 } else {
547 Register temp = IP;
548 __ LoadImmediate(temp, value);
549 __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(temp));
550 }
Dave Allison20dfc792014-06-16 20:44:29 -0700551 }
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100552 __ it(ARMCondition(comp->GetCondition()), kItElse);
553 __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(1),
554 ARMCondition(comp->GetCondition()));
555 __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(0),
556 ARMOppositeCondition(comp->GetCondition()));
Dave Allison20dfc792014-06-16 20:44:29 -0700557}
558
559void LocationsBuilderARM::VisitEqual(HEqual* comp) {
560 VisitCondition(comp);
561}
562
563void InstructionCodeGeneratorARM::VisitEqual(HEqual* comp) {
564 VisitCondition(comp);
565}
566
567void LocationsBuilderARM::VisitNotEqual(HNotEqual* comp) {
568 VisitCondition(comp);
569}
570
571void InstructionCodeGeneratorARM::VisitNotEqual(HNotEqual* comp) {
572 VisitCondition(comp);
573}
574
575void LocationsBuilderARM::VisitLessThan(HLessThan* comp) {
576 VisitCondition(comp);
577}
578
579void InstructionCodeGeneratorARM::VisitLessThan(HLessThan* comp) {
580 VisitCondition(comp);
581}
582
583void LocationsBuilderARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
584 VisitCondition(comp);
585}
586
587void InstructionCodeGeneratorARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
588 VisitCondition(comp);
589}
590
591void LocationsBuilderARM::VisitGreaterThan(HGreaterThan* comp) {
592 VisitCondition(comp);
593}
594
595void InstructionCodeGeneratorARM::VisitGreaterThan(HGreaterThan* comp) {
596 VisitCondition(comp);
597}
598
599void LocationsBuilderARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
600 VisitCondition(comp);
601}
602
603void InstructionCodeGeneratorARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
604 VisitCondition(comp);
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000605}
606
607void LocationsBuilderARM::VisitLocal(HLocal* local) {
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000608 local->SetLocations(nullptr);
Nicolas Geoffray3ff386a2014-03-04 14:46:47 +0000609}
610
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000611void InstructionCodeGeneratorARM::VisitLocal(HLocal* local) {
612 DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
Nicolas Geoffray3ff386a2014-03-04 14:46:47 +0000613}
614
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000615void LocationsBuilderARM::VisitLoadLocal(HLoadLocal* load) {
Nicolas Geoffray4a34a422014-04-03 10:38:37 +0100616 load->SetLocations(nullptr);
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000617}
618
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000619void InstructionCodeGeneratorARM::VisitLoadLocal(HLoadLocal* load) {
Nicolas Geoffray4a34a422014-04-03 10:38:37 +0100620 // Nothing to do, this is driven by the code generator.
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000621}
622
623void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) {
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000624 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100625 switch (store->InputAt(1)->GetType()) {
626 case Primitive::kPrimBoolean:
627 case Primitive::kPrimByte:
628 case Primitive::kPrimChar:
629 case Primitive::kPrimShort:
630 case Primitive::kPrimInt:
631 case Primitive::kPrimNot:
632 locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
633 break;
634
635 case Primitive::kPrimLong:
636 locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
637 break;
638
639 default:
640 LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
641 }
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000642 store->SetLocations(locations);
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000643}
644
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000645void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) {
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000646}
647
648void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) {
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100649 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100650 locations->SetOut(Location::ConstantLocation(constant));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100651 constant->SetLocations(locations);
Nicolas Geoffray3ff386a2014-03-04 14:46:47 +0000652}
653
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000654void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) {
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000655}
656
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100657void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) {
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100658 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100659 locations->SetOut(Location::ConstantLocation(constant));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100660 constant->SetLocations(locations);
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100661}
662
663void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant) {
664 // Will be generated at use site.
665}
666
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000667void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) {
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000668 ret->SetLocations(nullptr);
Nicolas Geoffray3ff386a2014-03-04 14:46:47 +0000669}
670
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000671void InstructionCodeGeneratorARM::VisitReturnVoid(HReturnVoid* ret) {
672 codegen_->GenerateFrameExit();
Nicolas Geoffrayd4dd2552014-02-28 10:23:58 +0000673}
674
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000675void LocationsBuilderARM::VisitReturn(HReturn* ret) {
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000676 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100677 switch (ret->InputAt(0)->GetType()) {
678 case Primitive::kPrimBoolean:
679 case Primitive::kPrimByte:
680 case Primitive::kPrimChar:
681 case Primitive::kPrimShort:
682 case Primitive::kPrimInt:
683 case Primitive::kPrimNot:
684 locations->SetInAt(0, ArmCoreLocation(R0));
685 break;
686
687 case Primitive::kPrimLong:
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100688 locations->SetInAt(
689 0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100690 break;
691
692 default:
693 LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
694 }
695
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000696 ret->SetLocations(locations);
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000697}
698
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000699void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) {
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100700 if (kIsDebugBuild) {
701 switch (ret->InputAt(0)->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 DCHECK_EQ(ret->GetLocations()->InAt(0).AsArm().AsCoreRegister(), R0);
709 break;
710
711 case Primitive::kPrimLong:
712 DCHECK_EQ(ret->GetLocations()->InAt(0).AsArm().AsRegisterPair(), R0_R1);
713 break;
714
715 default:
716 LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
717 }
718 }
Nicolas Geoffray787c3072014-03-17 10:20:19 +0000719 codegen_->GenerateFrameExit();
Nicolas Geoffraybab4ed72014-03-11 17:53:17 +0000720}
721
Nicolas Geoffray8ccc3f52014-03-19 10:34:11 +0000722void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
723 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +0100724 locations->AddTemp(ArmCoreLocation(R0));
Nicolas Geoffraydb928fc2014-04-16 17:38:32 +0100725
726 InvokeDexCallingConventionVisitor calling_convention_visitor;
Nicolas Geoffrayc32e7702014-04-24 12:43:16 +0100727 for (size_t i = 0; i < invoke->InputCount(); i++) {
Nicolas Geoffraydb928fc2014-04-16 17:38:32 +0100728 HInstruction* input = invoke->InputAt(i);
729 locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
730 }
731
732 switch (invoke->GetType()) {
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100733 case Primitive::kPrimBoolean:
734 case Primitive::kPrimByte:
735 case Primitive::kPrimChar:
736 case Primitive::kPrimShort:
737 case Primitive::kPrimInt:
738 case Primitive::kPrimNot:
739 locations->SetOut(ArmCoreLocation(R0));
740 break;
741
742 case Primitive::kPrimLong:
743 locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
744 break;
745
746 case Primitive::kPrimVoid:
747 break;
748
749 case Primitive::kPrimDouble:
750 case Primitive::kPrimFloat:
751 LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
752 break;
753 }
754
Nicolas Geoffray8ccc3f52014-03-19 10:34:11 +0000755 invoke->SetLocations(locations);
756}
757
758void InstructionCodeGeneratorARM::LoadCurrentMethod(Register reg) {
Nicolas Geoffray4a34a422014-04-03 10:38:37 +0100759 __ ldr(reg, Address(SP, kCurrentMethodStackOffset));
Nicolas Geoffray8ccc3f52014-03-19 10:34:11 +0000760}
761
762void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) {
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100763 Register temp = invoke->GetLocations()->GetTemp(0).AsArm().AsCoreRegister();
Nicolas Geoffrayf61b5372014-06-25 14:35:34 +0100764 uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>);
765 size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() +
Nicolas Geoffray707c8092014-04-04 10:50:14 +0100766 invoke->GetIndexInDexCache() * kArmWordSize;
Nicolas Geoffray8ccc3f52014-03-19 10:34:11 +0000767
768 // TODO: Implement all kinds of calls:
769 // 1) boot -> boot
770 // 2) app -> boot
771 // 3) app -> app
772 //
773 // Currently we implement the app -> app logic, which looks up in the resolve cache.
774
775 // temp = method;
776 LoadCurrentMethod(temp);
777 // temp = temp->dex_cache_resolved_methods_;
778 __ ldr(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
779 // temp = temp[index_in_cache]
780 __ ldr(temp, Address(temp, index_in_cache));
781 // LR = temp[offset_of_quick_compiled_code]
782 __ ldr(LR, Address(temp,
783 mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
784 // LR()
785 __ blx(LR);
786
787 codegen_->RecordPcInfo(invoke->GetDexPc());
788}
789
Nicolas Geoffrayd8ee7372014-03-28 15:43:40 +0000790void LocationsBuilderARM::VisitAdd(HAdd* add) {
791 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
792 switch (add->GetResultType()) {
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100793 case Primitive::kPrimInt:
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100794 case Primitive::kPrimLong: {
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100795 locations->SetInAt(0, Location::RequiresRegister());
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100796 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100797 locations->SetOut(Location::RequiresRegister());
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100798 break;
799 }
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
Nicolas Geoffrayd8ee7372014-03-28 15:43:40 +0000808 default:
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100809 LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
Nicolas Geoffrayd8ee7372014-03-28 15:43:40 +0000810 }
811 add->SetLocations(locations);
812}
813
814void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) {
815 LocationSummary* locations = add->GetLocations();
816 switch (add->GetResultType()) {
817 case Primitive::kPrimInt:
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100818 if (locations->InAt(1).IsRegister()) {
819 __ add(locations->Out().AsArm().AsCoreRegister(),
820 locations->InAt(0).AsArm().AsCoreRegister(),
821 ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
822 } else {
823 __ AddConstant(locations->Out().AsArm().AsCoreRegister(),
824 locations->InAt(0).AsArm().AsCoreRegister(),
825 locations->InAt(1).GetConstant()->AsIntConstant()->GetValue());
826 }
Nicolas Geoffrayd8ee7372014-03-28 15:43:40 +0000827 break;
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100828
829 case Primitive::kPrimLong:
830 __ adds(locations->Out().AsArm().AsRegisterPairLow(),
831 locations->InAt(0).AsArm().AsRegisterPairLow(),
832 ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairLow()));
833 __ adc(locations->Out().AsArm().AsRegisterPairHigh(),
834 locations->InAt(0).AsArm().AsRegisterPairHigh(),
835 ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairHigh()));
836 break;
837
838 case Primitive::kPrimBoolean:
839 case Primitive::kPrimByte:
840 case Primitive::kPrimChar:
841 case Primitive::kPrimShort:
842 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
843 break;
844
Nicolas Geoffrayd8ee7372014-03-28 15:43:40 +0000845 default:
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100846 LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
Nicolas Geoffrayd8ee7372014-03-28 15:43:40 +0000847 }
848}
849
Nicolas Geoffrayf583e592014-04-07 13:20:42 +0100850void LocationsBuilderARM::VisitSub(HSub* sub) {
851 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
852 switch (sub->GetResultType()) {
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100853 case Primitive::kPrimInt:
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100854 case Primitive::kPrimLong: {
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100855 locations->SetInAt(0, Location::RequiresRegister());
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100856 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100857 locations->SetOut(Location::RequiresRegister());
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100858 break;
859 }
860
861 case Primitive::kPrimBoolean:
862 case Primitive::kPrimByte:
863 case Primitive::kPrimChar:
864 case Primitive::kPrimShort:
865 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
866 break;
867
Nicolas Geoffrayf583e592014-04-07 13:20:42 +0100868 default:
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100869 LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
Nicolas Geoffrayf583e592014-04-07 13:20:42 +0100870 }
871 sub->SetLocations(locations);
872}
873
874void InstructionCodeGeneratorARM::VisitSub(HSub* sub) {
875 LocationSummary* locations = sub->GetLocations();
876 switch (sub->GetResultType()) {
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100877 case Primitive::kPrimInt: {
878 if (locations->InAt(1).IsRegister()) {
879 __ sub(locations->Out().AsArm().AsCoreRegister(),
880 locations->InAt(0).AsArm().AsCoreRegister(),
881 ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
882 } else {
883 __ AddConstant(locations->Out().AsArm().AsCoreRegister(),
884 locations->InAt(0).AsArm().AsCoreRegister(),
885 -locations->InAt(1).GetConstant()->AsIntConstant()->GetValue());
886 }
Nicolas Geoffrayf583e592014-04-07 13:20:42 +0100887 break;
Nicolas Geoffray96f89a22014-07-11 10:57:49 +0100888 }
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100889
890 case Primitive::kPrimLong:
891 __ subs(locations->Out().AsArm().AsRegisterPairLow(),
892 locations->InAt(0).AsArm().AsRegisterPairLow(),
893 ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairLow()));
894 __ sbc(locations->Out().AsArm().AsRegisterPairHigh(),
895 locations->InAt(0).AsArm().AsRegisterPairHigh(),
896 ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairHigh()));
897 break;
898
899 case Primitive::kPrimBoolean:
900 case Primitive::kPrimByte:
901 case Primitive::kPrimChar:
902 case Primitive::kPrimShort:
903 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
904 break;
905
Nicolas Geoffrayf583e592014-04-07 13:20:42 +0100906 default:
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100907 LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
Nicolas Geoffrayf583e592014-04-07 13:20:42 +0100908 }
909}
910
Nicolas Geoffray2e7038a2014-04-03 18:49:58 +0100911static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1 };
912static constexpr size_t kRuntimeParameterCoreRegistersLength =
913 arraysize(kRuntimeParameterCoreRegisters);
914
915class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
916 public:
917 InvokeRuntimeCallingConvention()
918 : CallingConvention(kRuntimeParameterCoreRegisters,
919 kRuntimeParameterCoreRegistersLength) {}
920
921 private:
922 DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
923};
924
925void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
926 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100927 InvokeRuntimeCallingConvention calling_convention;
928 locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(0)));
929 locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(1)));
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100930 locations->SetOut(ArmCoreLocation(R0));
Nicolas Geoffray2e7038a2014-04-03 18:49:58 +0100931 instruction->SetLocations(locations);
932}
933
934void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
935 InvokeRuntimeCallingConvention calling_convention;
936 LoadCurrentMethod(calling_convention.GetRegisterAt(1));
937 __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
938
Nicolas Geoffray707c8092014-04-04 10:50:14 +0100939 int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocObjectWithAccessCheck).Int32Value();
Nicolas Geoffray2e7038a2014-04-03 18:49:58 +0100940 __ ldr(LR, Address(TR, offset));
941 __ blx(LR);
942
943 codegen_->RecordPcInfo(instruction->GetDexPc());
944}
945
Nicolas Geoffrayf583e592014-04-07 13:20:42 +0100946void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
947 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
Nicolas Geoffraya747a392014-04-17 14:56:23 +0100948 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
949 if (location.IsStackSlot()) {
950 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
951 } else if (location.IsDoubleStackSlot()) {
952 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
Nicolas Geoffrayf583e592014-04-07 13:20:42 +0100953 }
Nicolas Geoffraya747a392014-04-17 14:56:23 +0100954 locations->SetOut(location);
Nicolas Geoffrayf583e592014-04-07 13:20:42 +0100955 instruction->SetLocations(locations);
956}
957
958void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instruction) {
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100959 // Nothing to do, the parameter is already at its location.
Nicolas Geoffrayf583e592014-04-07 13:20:42 +0100960}
961
Nicolas Geoffrayb55f8352014-04-07 15:26:35 +0100962void LocationsBuilderARM::VisitNot(HNot* instruction) {
963 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
Nicolas Geoffraya7aca372014-04-28 17:47:12 +0100964 locations->SetInAt(0, Location::RequiresRegister());
965 locations->SetOut(Location::RequiresRegister());
Nicolas Geoffrayb55f8352014-04-07 15:26:35 +0100966 instruction->SetLocations(locations);
967}
968
969void InstructionCodeGeneratorARM::VisitNot(HNot* instruction) {
970 LocationSummary* locations = instruction->GetLocations();
Nicolas Geoffray01bc96d2014-04-11 17:43:50 +0100971 __ eor(locations->Out().AsArm().AsCoreRegister(),
972 locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(1));
Nicolas Geoffrayb55f8352014-04-07 15:26:35 +0100973}
974
Nicolas Geoffray412f10c2014-06-19 10:00:34 +0100975void LocationsBuilderARM::VisitCompare(HCompare* compare) {
976 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
977 locations->SetInAt(0, Location::RequiresRegister());
978 locations->SetInAt(1, Location::RequiresRegister());
979 locations->SetOut(Location::RequiresRegister());
980 compare->SetLocations(locations);
981}
982
983void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) {
984 Label greater, done;
985 LocationSummary* locations = compare->GetLocations();
986 switch (compare->InputAt(0)->GetType()) {
987 case Primitive::kPrimLong: {
988 Register output = locations->Out().AsArm().AsCoreRegister();
989 ArmManagedRegister left = locations->InAt(0).AsArm();
990 ArmManagedRegister right = locations->InAt(1).AsArm();
991 Label less, greater, done;
992 __ cmp(left.AsRegisterPairHigh(),
993 ShifterOperand(right.AsRegisterPairHigh())); // Signed compare.
994 __ b(&less, LT);
995 __ b(&greater, GT);
Nicolas Geoffray8d486732014-07-16 16:23:40 +0100996 // Do LoadImmediate before any `cmp`, as LoadImmediate might affect
997 // the status flags.
998 __ LoadImmediate(output, 0);
Nicolas Geoffray412f10c2014-06-19 10:00:34 +0100999 __ cmp(left.AsRegisterPairLow(),
1000 ShifterOperand(right.AsRegisterPairLow())); // Unsigned compare.
Nicolas Geoffray412f10c2014-06-19 10:00:34 +01001001 __ b(&done, EQ);
1002 __ b(&less, CC);
1003
1004 __ Bind(&greater);
1005 __ LoadImmediate(output, 1);
1006 __ b(&done);
1007
1008 __ Bind(&less);
1009 __ LoadImmediate(output, -1);
1010
1011 __ Bind(&done);
1012 break;
1013 }
1014 default:
1015 LOG(FATAL) << "Unimplemented compare type " << compare->InputAt(0)->GetType();
1016 }
1017}
1018
Nicolas Geoffrayc32e7702014-04-24 12:43:16 +01001019void LocationsBuilderARM::VisitPhi(HPhi* instruction) {
Nicolas Geoffray31d76b42014-06-09 15:02:22 +01001020 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
1021 for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
1022 locations->SetInAt(i, Location::Any());
1023 }
1024 locations->SetOut(Location::Any());
1025 instruction->SetLocations(locations);
Nicolas Geoffrayc32e7702014-04-24 12:43:16 +01001026}
1027
1028void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) {
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +01001029 LOG(FATAL) << "Unreachable";
Nicolas Geoffrayc32e7702014-04-24 12:43:16 +01001030}
1031
Nicolas Geoffraye5038322014-07-04 09:41:32 +01001032void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
1033 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
1034 locations->SetInAt(0, Location::RequiresRegister());
1035 locations->SetInAt(1, Location::RequiresRegister());
Nicolas Geoffray1a43dd72014-07-17 15:15:34 +01001036 // Temporary registers for the write barrier.
1037 if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
1038 locations->AddTemp(Location::RequiresRegister());
1039 locations->AddTemp(Location::RequiresRegister());
1040 }
Nicolas Geoffraye5038322014-07-04 09:41:32 +01001041 instruction->SetLocations(locations);
1042}
1043
1044void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
1045 LocationSummary* locations = instruction->GetLocations();
1046 Register obj = locations->InAt(0).AsArm().AsCoreRegister();
1047 uint32_t offset = instruction->GetFieldOffset().Uint32Value();
1048 Primitive::Type field_type = instruction->InputAt(1)->GetType();
1049
1050 switch (field_type) {
1051 case Primitive::kPrimBoolean:
1052 case Primitive::kPrimByte: {
1053 Register value = locations->InAt(1).AsArm().AsCoreRegister();
1054 __ StoreToOffset(kStoreByte, value, obj, offset);
1055 break;
1056 }
1057
1058 case Primitive::kPrimShort:
1059 case Primitive::kPrimChar: {
1060 Register value = locations->InAt(1).AsArm().AsCoreRegister();
1061 __ StoreToOffset(kStoreHalfword, value, obj, offset);
1062 break;
1063 }
1064
Nicolas Geoffray1a43dd72014-07-17 15:15:34 +01001065 case Primitive::kPrimInt: {
1066 Register value = locations->InAt(1).AsArm().AsCoreRegister();
1067 __ StoreToOffset(kStoreWord, value, obj, offset);
1068 break;
1069 }
1070
Nicolas Geoffraye5038322014-07-04 09:41:32 +01001071 case Primitive::kPrimNot: {
1072 Register value = locations->InAt(1).AsArm().AsCoreRegister();
1073 __ StoreToOffset(kStoreWord, value, obj, offset);
Nicolas Geoffray1a43dd72014-07-17 15:15:34 +01001074
1075 Register temp = locations->GetTemp(0).AsArm().AsCoreRegister();
1076 Register card = locations->GetTemp(1).AsArm().AsCoreRegister();
1077 Label is_null;
1078 __ CompareAndBranchIfZero(value, &is_null);
1079 __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value());
1080 __ Lsr(temp, obj, gc::accounting::CardTable::kCardShift);
1081 __ strb(card, Address(card, temp));
1082 __ Bind(&is_null);
Nicolas Geoffraye5038322014-07-04 09:41:32 +01001083 break;
1084 }
1085
1086 case Primitive::kPrimLong: {
1087 ArmManagedRegister value = locations->InAt(1).AsArm();
1088 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), obj, offset);
1089 break;
1090 }
1091
1092 case Primitive::kPrimFloat:
1093 case Primitive::kPrimDouble:
1094 LOG(FATAL) << "Unimplemented register type " << field_type;
1095
1096 case Primitive::kPrimVoid:
1097 LOG(FATAL) << "Unreachable type " << field_type;
1098 }
1099}
1100
1101void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
1102 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
1103 locations->SetInAt(0, Location::RequiresRegister());
1104 locations->SetOut(Location::RequiresRegister());
1105 instruction->SetLocations(locations);
1106}
1107
1108void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
1109 LocationSummary* locations = instruction->GetLocations();
1110 Register obj = locations->InAt(0).AsArm().AsCoreRegister();
1111 uint32_t offset = instruction->GetFieldOffset().Uint32Value();
1112
1113 switch (instruction->GetType()) {
1114 case Primitive::kPrimBoolean: {
1115 Register out = locations->Out().AsArm().AsCoreRegister();
1116 __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset);
1117 break;
1118 }
1119
1120 case Primitive::kPrimByte: {
1121 Register out = locations->Out().AsArm().AsCoreRegister();
1122 __ LoadFromOffset(kLoadSignedByte, out, obj, offset);
1123 break;
1124 }
1125
1126 case Primitive::kPrimShort: {
1127 Register out = locations->Out().AsArm().AsCoreRegister();
1128 __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset);
1129 break;
1130 }
1131
1132 case Primitive::kPrimChar: {
1133 Register out = locations->Out().AsArm().AsCoreRegister();
1134 __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset);
1135 break;
1136 }
1137
1138 case Primitive::kPrimInt:
1139 case Primitive::kPrimNot: {
1140 Register out = locations->Out().AsArm().AsCoreRegister();
1141 __ LoadFromOffset(kLoadWord, out, obj, offset);
1142 break;
1143 }
1144
1145 case Primitive::kPrimLong: {
1146 // TODO: support volatile.
1147 ArmManagedRegister out = locations->Out().AsArm();
1148 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), obj, offset);
1149 break;
1150 }
1151
1152 case Primitive::kPrimFloat:
1153 case Primitive::kPrimDouble:
1154 LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
1155
1156 case Primitive::kPrimVoid:
1157 LOG(FATAL) << "Unreachable type " << instruction->GetType();
1158 }
1159}
1160
1161void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
1162 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
1163 locations->SetInAt(0, Location::RequiresRegister());
1164 // TODO: Have a normalization phase that makes this instruction never used.
1165 locations->SetOut(Location::SameAsFirstInput());
1166 instruction->SetLocations(locations);
1167}
1168
1169void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
1170 SlowPathCode* slow_path =
1171 new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction->GetDexPc());
1172 codegen_->AddSlowPath(slow_path);
1173
1174 LocationSummary* locations = instruction->GetLocations();
1175 Location obj = locations->InAt(0);
1176 DCHECK(obj.Equals(locations->Out()));
1177
1178 if (obj.IsRegister()) {
1179 __ cmp(obj.AsArm().AsCoreRegister(), ShifterOperand(0));
1180 }
1181 __ b(slow_path->GetEntryLabel(), EQ);
1182}
1183
1184void LocationsBuilderARM::VisitTemporary(HTemporary* temp) {
1185 temp->SetLocations(nullptr);
1186}
1187
1188void InstructionCodeGeneratorARM::VisitTemporary(HTemporary* temp) {
1189 // Nothing to do, this is driven by the code generator.
1190}
1191
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +01001192void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction) {
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +01001193 LOG(FATAL) << "Unreachable";
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +01001194}
1195
1196void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) {
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +01001197 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
1198}
1199
1200ArmAssembler* ParallelMoveResolverARM::GetAssembler() const {
1201 return codegen_->GetAssembler();
1202}
1203
1204void ParallelMoveResolverARM::EmitMove(size_t index) {
1205 MoveOperands* move = moves_.Get(index);
1206 Location source = move->GetSource();
1207 Location destination = move->GetDestination();
1208
1209 if (source.IsRegister()) {
1210 if (destination.IsRegister()) {
1211 __ Mov(destination.AsArm().AsCoreRegister(), source.AsArm().AsCoreRegister());
1212 } else {
1213 DCHECK(destination.IsStackSlot());
1214 __ StoreToOffset(kStoreWord, source.AsArm().AsCoreRegister(),
1215 SP, destination.GetStackIndex());
1216 }
1217 } else if (source.IsStackSlot()) {
1218 if (destination.IsRegister()) {
1219 __ LoadFromOffset(kLoadWord, destination.AsArm().AsCoreRegister(),
1220 SP, source.GetStackIndex());
1221 } else {
1222 DCHECK(destination.IsStackSlot());
1223 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex());
1224 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
1225 }
1226 } else {
Nicolas Geoffray96f89a22014-07-11 10:57:49 +01001227 DCHECK(source.IsConstant());
1228 DCHECK(source.GetConstant()->AsIntConstant() != nullptr);
1229 int32_t value = source.GetConstant()->AsIntConstant()->GetValue();
1230 if (destination.IsRegister()) {
1231 __ LoadImmediate(destination.AsArm().AsCoreRegister(), value);
1232 } else {
1233 DCHECK(destination.IsStackSlot());
1234 __ LoadImmediate(IP, value);
1235 __ str(IP, Address(SP, destination.GetStackIndex()));
1236 }
Nicolas Geoffraye27f31a2014-06-12 17:53:14 +01001237 }
1238}
1239
1240void ParallelMoveResolverARM::Exchange(Register reg, int mem) {
1241 __ Mov(IP, reg);
1242 __ LoadFromOffset(kLoadWord, reg, SP, mem);
1243 __ StoreToOffset(kStoreWord, IP, SP, mem);
1244}
1245
1246void ParallelMoveResolverARM::Exchange(int mem1, int mem2) {
1247 ScratchRegisterScope ensure_scratch(this, IP, R0, codegen_->GetNumberOfCoreRegisters());
1248 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0;
1249 __ LoadFromOffset(kLoadWord, static_cast<Register>(ensure_scratch.GetRegister()),
1250 SP, mem1 + stack_offset);
1251 __ LoadFromOffset(kLoadWord, IP, SP, mem2 + stack_offset);
1252 __ StoreToOffset(kStoreWord, static_cast<Register>(ensure_scratch.GetRegister()),
1253 SP, mem2 + stack_offset);
1254 __ StoreToOffset(kStoreWord, IP, SP, mem1 + stack_offset);
1255}
1256
1257void ParallelMoveResolverARM::EmitSwap(size_t index) {
1258 MoveOperands* move = moves_.Get(index);
1259 Location source = move->GetSource();
1260 Location destination = move->GetDestination();
1261
1262 if (source.IsRegister() && destination.IsRegister()) {
1263 DCHECK_NE(source.AsArm().AsCoreRegister(), IP);
1264 DCHECK_NE(destination.AsArm().AsCoreRegister(), IP);
1265 __ Mov(IP, source.AsArm().AsCoreRegister());
1266 __ Mov(source.AsArm().AsCoreRegister(), destination.AsArm().AsCoreRegister());
1267 __ Mov(destination.AsArm().AsCoreRegister(), IP);
1268 } else if (source.IsRegister() && destination.IsStackSlot()) {
1269 Exchange(source.AsArm().AsCoreRegister(), destination.GetStackIndex());
1270 } else if (source.IsStackSlot() && destination.IsRegister()) {
1271 Exchange(destination.AsArm().AsCoreRegister(), source.GetStackIndex());
1272 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
1273 Exchange(source.GetStackIndex(), destination.GetStackIndex());
1274 } else {
1275 LOG(FATAL) << "Unimplemented";
1276 }
1277}
1278
1279void ParallelMoveResolverARM::SpillScratch(int reg) {
1280 __ Push(static_cast<Register>(reg));
1281}
1282
1283void ParallelMoveResolverARM::RestoreScratch(int reg) {
1284 __ Pop(static_cast<Register>(reg));
Nicolas Geoffray4e3d23a2014-05-22 18:32:45 +01001285}
1286
Nicolas Geoffrayd4dd2552014-02-28 10:23:58 +00001287} // namespace arm
1288} // namespace art