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 "arch/arm/instruction_set_features_arm.h"
20 #include "art_method.h"
21 #include "code_generator_utils.h"
22 #include "common_arm.h"
23 #include "compiled_method.h"
24 #include "entrypoints/quick/quick_entrypoints.h"
25 #include "gc/accounting/card_table.h"
26 #include "intrinsics.h"
27 #include "intrinsics_arm.h"
28 #include "mirror/array-inl.h"
29 #include "mirror/class-inl.h"
30 #include "thread.h"
31 #include "utils/arm/assembler_arm.h"
32 #include "utils/arm/managed_register_arm.h"
33 #include "utils/assembler.h"
34 #include "utils/stack_checks.h"
35
36 namespace art {
37
38 template<class MirrorType>
39 class GcRoot;
40
41 namespace arm {
42
ExpectedPairLayout(Location location)43 static bool ExpectedPairLayout(Location location) {
44 // We expected this for both core and fpu register pairs.
45 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
46 }
47
48 static constexpr int kCurrentMethodStackOffset = 0;
49 static constexpr Register kMethodRegisterArgument = R0;
50
51 static constexpr Register kCoreAlwaysSpillRegister = R5;
52 static constexpr Register kCoreCalleeSaves[] =
53 { R5, R6, R7, R8, R10, R11, LR };
54 static constexpr SRegister kFpuCalleeSaves[] =
55 { S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31 };
56
57 // D31 cannot be split into two S registers, and the register allocator only works on
58 // S registers. Therefore there is no need to block it.
59 static constexpr DRegister DTMP = D31;
60
61 static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
62
63 // NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
64 #define __ down_cast<ArmAssembler*>(codegen->GetAssembler())-> // NOLINT
65 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
66
67 static constexpr int kRegListThreshold = 4;
68
69 // SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
70 // for each live D registers they treat two corresponding S registers as live ones.
71 //
72 // Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
73 // from a list of contiguous S registers a list of contiguous D registers (processing first/last
74 // S registers corner cases) and save/restore this new list treating them as D registers.
75 // - decreasing code size
76 // - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
77 // restored and then used in regular non SlowPath code as D register.
78 //
79 // For the following example (v means the S register is live):
80 // D names: | D0 | D1 | D2 | D4 | ...
81 // S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
82 // Live? | | v | v | v | v | v | v | | ...
83 //
84 // S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
85 // as D registers.
SaveContiguousSRegisterList(size_t first,size_t last,CodeGenerator * codegen,size_t stack_offset)86 static size_t SaveContiguousSRegisterList(size_t first,
87 size_t last,
88 CodeGenerator* codegen,
89 size_t stack_offset) {
90 DCHECK_LE(first, last);
91 if ((first == last) && (first == 0)) {
92 stack_offset += codegen->SaveFloatingPointRegister(stack_offset, first);
93 return stack_offset;
94 }
95 if (first % 2 == 1) {
96 stack_offset += codegen->SaveFloatingPointRegister(stack_offset, first++);
97 }
98
99 bool save_last = false;
100 if (last % 2 == 0) {
101 save_last = true;
102 --last;
103 }
104
105 if (first < last) {
106 DRegister d_reg = static_cast<DRegister>(first / 2);
107 DCHECK_EQ((last - first + 1) % 2, 0u);
108 size_t number_of_d_regs = (last - first + 1) / 2;
109
110 if (number_of_d_regs == 1) {
111 __ StoreDToOffset(d_reg, SP, stack_offset);
112 } else if (number_of_d_regs > 1) {
113 __ add(IP, SP, ShifterOperand(stack_offset));
114 __ vstmiad(IP, d_reg, number_of_d_regs);
115 }
116 stack_offset += number_of_d_regs * kArmWordSize * 2;
117 }
118
119 if (save_last) {
120 stack_offset += codegen->SaveFloatingPointRegister(stack_offset, last + 1);
121 }
122
123 return stack_offset;
124 }
125
RestoreContiguousSRegisterList(size_t first,size_t last,CodeGenerator * codegen,size_t stack_offset)126 static size_t RestoreContiguousSRegisterList(size_t first,
127 size_t last,
128 CodeGenerator* codegen,
129 size_t stack_offset) {
130 DCHECK_LE(first, last);
131 if ((first == last) && (first == 0)) {
132 stack_offset += codegen->RestoreFloatingPointRegister(stack_offset, first);
133 return stack_offset;
134 }
135 if (first % 2 == 1) {
136 stack_offset += codegen->RestoreFloatingPointRegister(stack_offset, first++);
137 }
138
139 bool restore_last = false;
140 if (last % 2 == 0) {
141 restore_last = true;
142 --last;
143 }
144
145 if (first < last) {
146 DRegister d_reg = static_cast<DRegister>(first / 2);
147 DCHECK_EQ((last - first + 1) % 2, 0u);
148 size_t number_of_d_regs = (last - first + 1) / 2;
149 if (number_of_d_regs == 1) {
150 __ LoadDFromOffset(d_reg, SP, stack_offset);
151 } else if (number_of_d_regs > 1) {
152 __ add(IP, SP, ShifterOperand(stack_offset));
153 __ vldmiad(IP, d_reg, number_of_d_regs);
154 }
155 stack_offset += number_of_d_regs * kArmWordSize * 2;
156 }
157
158 if (restore_last) {
159 stack_offset += codegen->RestoreFloatingPointRegister(stack_offset, last + 1);
160 }
161
162 return stack_offset;
163 }
164
SaveLiveRegisters(CodeGenerator * codegen,LocationSummary * locations)165 void SlowPathCodeARM::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
166 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
167 size_t orig_offset = stack_offset;
168
169 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
170 for (uint32_t i : LowToHighBits(core_spills)) {
171 // If the register holds an object, update the stack mask.
172 if (locations->RegisterContainsObject(i)) {
173 locations->SetStackBit(stack_offset / kVRegSize);
174 }
175 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
176 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
177 saved_core_stack_offsets_[i] = stack_offset;
178 stack_offset += kArmWordSize;
179 }
180
181 int reg_num = POPCOUNT(core_spills);
182 if (reg_num != 0) {
183 if (reg_num > kRegListThreshold) {
184 __ StoreList(RegList(core_spills), orig_offset);
185 } else {
186 stack_offset = orig_offset;
187 for (uint32_t i : LowToHighBits(core_spills)) {
188 stack_offset += codegen->SaveCoreRegister(stack_offset, i);
189 }
190 }
191 }
192
193 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
194 orig_offset = stack_offset;
195 for (uint32_t i : LowToHighBits(fp_spills)) {
196 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
197 saved_fpu_stack_offsets_[i] = stack_offset;
198 stack_offset += kArmWordSize;
199 }
200
201 stack_offset = orig_offset;
202 while (fp_spills != 0u) {
203 uint32_t begin = CTZ(fp_spills);
204 uint32_t tmp = fp_spills + (1u << begin);
205 fp_spills &= tmp; // Clear the contiguous range of 1s.
206 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
207 stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
208 }
209 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
210 }
211
RestoreLiveRegisters(CodeGenerator * codegen,LocationSummary * locations)212 void SlowPathCodeARM::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
213 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
214 size_t orig_offset = stack_offset;
215
216 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
217 for (uint32_t i : LowToHighBits(core_spills)) {
218 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
219 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
220 stack_offset += kArmWordSize;
221 }
222
223 int reg_num = POPCOUNT(core_spills);
224 if (reg_num != 0) {
225 if (reg_num > kRegListThreshold) {
226 __ LoadList(RegList(core_spills), orig_offset);
227 } else {
228 stack_offset = orig_offset;
229 for (uint32_t i : LowToHighBits(core_spills)) {
230 stack_offset += codegen->RestoreCoreRegister(stack_offset, i);
231 }
232 }
233 }
234
235 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
236 while (fp_spills != 0u) {
237 uint32_t begin = CTZ(fp_spills);
238 uint32_t tmp = fp_spills + (1u << begin);
239 fp_spills &= tmp; // Clear the contiguous range of 1s.
240 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
241 stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
242 }
243 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
244 }
245
246 class NullCheckSlowPathARM : public SlowPathCodeARM {
247 public:
NullCheckSlowPathARM(HNullCheck * instruction)248 explicit NullCheckSlowPathARM(HNullCheck* instruction) : SlowPathCodeARM(instruction) {}
249
EmitNativeCode(CodeGenerator * codegen)250 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
251 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
252 __ Bind(GetEntryLabel());
253 if (instruction_->CanThrowIntoCatchBlock()) {
254 // Live registers will be restored in the catch block if caught.
255 SaveLiveRegisters(codegen, instruction_->GetLocations());
256 }
257 arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
258 instruction_,
259 instruction_->GetDexPc(),
260 this);
261 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
262 }
263
IsFatal() const264 bool IsFatal() const OVERRIDE { return true; }
265
GetDescription() const266 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM"; }
267
268 private:
269 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
270 };
271
272 class DivZeroCheckSlowPathARM : public SlowPathCodeARM {
273 public:
DivZeroCheckSlowPathARM(HDivZeroCheck * instruction)274 explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : SlowPathCodeARM(instruction) {}
275
EmitNativeCode(CodeGenerator * codegen)276 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
277 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
278 __ Bind(GetEntryLabel());
279 arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
280 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
281 }
282
IsFatal() const283 bool IsFatal() const OVERRIDE { return true; }
284
GetDescription() const285 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM"; }
286
287 private:
288 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM);
289 };
290
291 class SuspendCheckSlowPathARM : public SlowPathCodeARM {
292 public:
SuspendCheckSlowPathARM(HSuspendCheck * instruction,HBasicBlock * successor)293 SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor)
294 : SlowPathCodeARM(instruction), successor_(successor) {}
295
EmitNativeCode(CodeGenerator * codegen)296 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
297 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
298 __ Bind(GetEntryLabel());
299 arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
300 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
301 if (successor_ == nullptr) {
302 __ b(GetReturnLabel());
303 } else {
304 __ b(arm_codegen->GetLabelOf(successor_));
305 }
306 }
307
GetReturnLabel()308 Label* GetReturnLabel() {
309 DCHECK(successor_ == nullptr);
310 return &return_label_;
311 }
312
GetSuccessor() const313 HBasicBlock* GetSuccessor() const {
314 return successor_;
315 }
316
GetDescription() const317 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM"; }
318
319 private:
320 // If not null, the block to branch to after the suspend check.
321 HBasicBlock* const successor_;
322
323 // If `successor_` is null, the label to branch to after the suspend check.
324 Label return_label_;
325
326 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM);
327 };
328
329 class BoundsCheckSlowPathARM : public SlowPathCodeARM {
330 public:
BoundsCheckSlowPathARM(HBoundsCheck * instruction)331 explicit BoundsCheckSlowPathARM(HBoundsCheck* instruction)
332 : SlowPathCodeARM(instruction) {}
333
EmitNativeCode(CodeGenerator * codegen)334 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
335 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
336 LocationSummary* locations = instruction_->GetLocations();
337
338 __ Bind(GetEntryLabel());
339 if (instruction_->CanThrowIntoCatchBlock()) {
340 // Live registers will be restored in the catch block if caught.
341 SaveLiveRegisters(codegen, instruction_->GetLocations());
342 }
343 // We're moving two locations to locations that could overlap, so we need a parallel
344 // move resolver.
345 InvokeRuntimeCallingConvention calling_convention;
346 codegen->EmitParallelMoves(
347 locations->InAt(0),
348 Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
349 Primitive::kPrimInt,
350 locations->InAt(1),
351 Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
352 Primitive::kPrimInt);
353 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
354 ? kQuickThrowStringBounds
355 : kQuickThrowArrayBounds;
356 arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
357 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
358 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
359 }
360
IsFatal() const361 bool IsFatal() const OVERRIDE { return true; }
362
GetDescription() const363 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM"; }
364
365 private:
366 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM);
367 };
368
369 class LoadClassSlowPathARM : public SlowPathCodeARM {
370 public:
LoadClassSlowPathARM(HLoadClass * cls,HInstruction * at,uint32_t dex_pc,bool do_clinit)371 LoadClassSlowPathARM(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit)
372 : SlowPathCodeARM(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) {
373 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
374 }
375
EmitNativeCode(CodeGenerator * codegen)376 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
377 LocationSummary* locations = instruction_->GetLocations();
378 Location out = locations->Out();
379 constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
380
381 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
382 __ Bind(GetEntryLabel());
383 SaveLiveRegisters(codegen, locations);
384
385 InvokeRuntimeCallingConvention calling_convention;
386 // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry.
387 DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
388 bool is_load_class_bss_entry =
389 (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry);
390 Register entry_address = kNoRegister;
391 if (is_load_class_bss_entry && call_saves_everything_except_r0) {
392 Register temp = locations->GetTemp(0).AsRegister<Register>();
393 // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
394 // the kSaveEverything call.
395 bool temp_is_r0 = (temp == calling_convention.GetRegisterAt(0));
396 entry_address = temp_is_r0 ? out.AsRegister<Register>() : temp;
397 DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
398 if (temp_is_r0) {
399 __ mov(entry_address, ShifterOperand(temp));
400 }
401 }
402 dex::TypeIndex type_index = cls_->GetTypeIndex();
403 __ LoadImmediate(calling_convention.GetRegisterAt(0), type_index.index_);
404 QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
405 : kQuickInitializeType;
406 arm_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this);
407 if (do_clinit_) {
408 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
409 } else {
410 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
411 }
412
413 // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry.
414 if (is_load_class_bss_entry) {
415 if (call_saves_everything_except_r0) {
416 // The class entry address was preserved in `entry_address` thanks to kSaveEverything.
417 __ str(R0, Address(entry_address));
418 } else {
419 // For non-Baker read barrier, we need to re-calculate the address of the string entry.
420 Register temp = IP;
421 CodeGeneratorARM::PcRelativePatchInfo* labels =
422 arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
423 __ BindTrackedLabel(&labels->movw_label);
424 __ movw(temp, /* placeholder */ 0u);
425 __ BindTrackedLabel(&labels->movt_label);
426 __ movt(temp, /* placeholder */ 0u);
427 __ BindTrackedLabel(&labels->add_pc_label);
428 __ add(temp, temp, ShifterOperand(PC));
429 __ str(R0, Address(temp));
430 }
431 }
432 // Move the class to the desired location.
433 if (out.IsValid()) {
434 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
435 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
436 }
437 RestoreLiveRegisters(codegen, locations);
438 __ b(GetExitLabel());
439 }
440
GetDescription() const441 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARM"; }
442
443 private:
444 // The class this slow path will load.
445 HLoadClass* const cls_;
446
447 // The dex PC of `at_`.
448 const uint32_t dex_pc_;
449
450 // Whether to initialize the class.
451 const bool do_clinit_;
452
453 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM);
454 };
455
456 class LoadStringSlowPathARM : public SlowPathCodeARM {
457 public:
LoadStringSlowPathARM(HLoadString * instruction)458 explicit LoadStringSlowPathARM(HLoadString* instruction) : SlowPathCodeARM(instruction) {}
459
EmitNativeCode(CodeGenerator * codegen)460 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
461 DCHECK(instruction_->IsLoadString());
462 DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
463 LocationSummary* locations = instruction_->GetLocations();
464 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
465 HLoadString* load = instruction_->AsLoadString();
466 const dex::StringIndex string_index = load->GetStringIndex();
467 Register out = locations->Out().AsRegister<Register>();
468 constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
469
470 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
471 __ Bind(GetEntryLabel());
472 SaveLiveRegisters(codegen, locations);
473
474 InvokeRuntimeCallingConvention calling_convention;
475 // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
476 // the kSaveEverything call.
477 Register entry_address = kNoRegister;
478 if (call_saves_everything_except_r0) {
479 Register temp = locations->GetTemp(0).AsRegister<Register>();
480 bool temp_is_r0 = (temp == calling_convention.GetRegisterAt(0));
481 entry_address = temp_is_r0 ? out : temp;
482 DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
483 if (temp_is_r0) {
484 __ mov(entry_address, ShifterOperand(temp));
485 }
486 }
487
488 __ LoadImmediate(calling_convention.GetRegisterAt(0), string_index.index_);
489 arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
490 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
491
492 // Store the resolved String to the .bss entry.
493 if (call_saves_everything_except_r0) {
494 // The string entry address was preserved in `entry_address` thanks to kSaveEverything.
495 __ str(R0, Address(entry_address));
496 } else {
497 // For non-Baker read barrier, we need to re-calculate the address of the string entry.
498 Register temp = IP;
499 CodeGeneratorARM::PcRelativePatchInfo* labels =
500 arm_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
501 __ BindTrackedLabel(&labels->movw_label);
502 __ movw(temp, /* placeholder */ 0u);
503 __ BindTrackedLabel(&labels->movt_label);
504 __ movt(temp, /* placeholder */ 0u);
505 __ BindTrackedLabel(&labels->add_pc_label);
506 __ add(temp, temp, ShifterOperand(PC));
507 __ str(R0, Address(temp));
508 }
509
510 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
511 RestoreLiveRegisters(codegen, locations);
512
513 __ b(GetExitLabel());
514 }
515
GetDescription() const516 const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM"; }
517
518 private:
519 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM);
520 };
521
522 class TypeCheckSlowPathARM : public SlowPathCodeARM {
523 public:
TypeCheckSlowPathARM(HInstruction * instruction,bool is_fatal)524 TypeCheckSlowPathARM(HInstruction* instruction, bool is_fatal)
525 : SlowPathCodeARM(instruction), is_fatal_(is_fatal) {}
526
EmitNativeCode(CodeGenerator * codegen)527 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
528 LocationSummary* locations = instruction_->GetLocations();
529 DCHECK(instruction_->IsCheckCast()
530 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
531
532 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
533 __ Bind(GetEntryLabel());
534
535 if (!is_fatal_) {
536 SaveLiveRegisters(codegen, locations);
537 }
538
539 // We're moving two locations to locations that could overlap, so we need a parallel
540 // move resolver.
541 InvokeRuntimeCallingConvention calling_convention;
542 codegen->EmitParallelMoves(locations->InAt(0),
543 Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
544 Primitive::kPrimNot,
545 locations->InAt(1),
546 Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
547 Primitive::kPrimNot);
548 if (instruction_->IsInstanceOf()) {
549 arm_codegen->InvokeRuntime(kQuickInstanceofNonTrivial,
550 instruction_,
551 instruction_->GetDexPc(),
552 this);
553 CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
554 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
555 } else {
556 DCHECK(instruction_->IsCheckCast());
557 arm_codegen->InvokeRuntime(kQuickCheckInstanceOf,
558 instruction_,
559 instruction_->GetDexPc(),
560 this);
561 CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
562 }
563
564 if (!is_fatal_) {
565 RestoreLiveRegisters(codegen, locations);
566 __ b(GetExitLabel());
567 }
568 }
569
GetDescription() const570 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARM"; }
571
IsFatal() const572 bool IsFatal() const OVERRIDE { return is_fatal_; }
573
574 private:
575 const bool is_fatal_;
576
577 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM);
578 };
579
580 class DeoptimizationSlowPathARM : public SlowPathCodeARM {
581 public:
DeoptimizationSlowPathARM(HDeoptimize * instruction)582 explicit DeoptimizationSlowPathARM(HDeoptimize* instruction)
583 : SlowPathCodeARM(instruction) {}
584
EmitNativeCode(CodeGenerator * codegen)585 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
586 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
587 __ Bind(GetEntryLabel());
588 LocationSummary* locations = instruction_->GetLocations();
589 SaveLiveRegisters(codegen, locations);
590 InvokeRuntimeCallingConvention calling_convention;
591 __ LoadImmediate(calling_convention.GetRegisterAt(0),
592 static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
593 arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
594 CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
595 }
596
GetDescription() const597 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM"; }
598
599 private:
600 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM);
601 };
602
603 class ArraySetSlowPathARM : public SlowPathCodeARM {
604 public:
ArraySetSlowPathARM(HInstruction * instruction)605 explicit ArraySetSlowPathARM(HInstruction* instruction) : SlowPathCodeARM(instruction) {}
606
EmitNativeCode(CodeGenerator * codegen)607 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
608 LocationSummary* locations = instruction_->GetLocations();
609 __ Bind(GetEntryLabel());
610 SaveLiveRegisters(codegen, locations);
611
612 InvokeRuntimeCallingConvention calling_convention;
613 HParallelMove parallel_move(codegen->GetGraph()->GetArena());
614 parallel_move.AddMove(
615 locations->InAt(0),
616 Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
617 Primitive::kPrimNot,
618 nullptr);
619 parallel_move.AddMove(
620 locations->InAt(1),
621 Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
622 Primitive::kPrimInt,
623 nullptr);
624 parallel_move.AddMove(
625 locations->InAt(2),
626 Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
627 Primitive::kPrimNot,
628 nullptr);
629 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move);
630
631 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
632 arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
633 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
634 RestoreLiveRegisters(codegen, locations);
635 __ b(GetExitLabel());
636 }
637
GetDescription() const638 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM"; }
639
640 private:
641 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM);
642 };
643
644 // Abstract base class for read barrier slow paths marking a reference
645 // `ref`.
646 //
647 // Argument `entrypoint` must be a register location holding the read
648 // barrier marking runtime entry point to be invoked.
649 class ReadBarrierMarkSlowPathBaseARM : public SlowPathCodeARM {
650 protected:
ReadBarrierMarkSlowPathBaseARM(HInstruction * instruction,Location ref,Location entrypoint)651 ReadBarrierMarkSlowPathBaseARM(HInstruction* instruction, Location ref, Location entrypoint)
652 : SlowPathCodeARM(instruction), ref_(ref), entrypoint_(entrypoint) {
653 DCHECK(kEmitCompilerReadBarrier);
654 }
655
GetDescription() const656 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathBaseARM"; }
657
658 // Generate assembly code calling the read barrier marking runtime
659 // entry point (ReadBarrierMarkRegX).
GenerateReadBarrierMarkRuntimeCall(CodeGenerator * codegen)660 void GenerateReadBarrierMarkRuntimeCall(CodeGenerator* codegen) {
661 Register ref_reg = ref_.AsRegister<Register>();
662
663 // No need to save live registers; it's taken care of by the
664 // entrypoint. Also, there is no need to update the stack mask,
665 // as this runtime call will not trigger a garbage collection.
666 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
667 DCHECK_NE(ref_reg, SP);
668 DCHECK_NE(ref_reg, LR);
669 DCHECK_NE(ref_reg, PC);
670 // IP is used internally by the ReadBarrierMarkRegX entry point
671 // as a temporary, it cannot be the entry point's input/output.
672 DCHECK_NE(ref_reg, IP);
673 DCHECK(0 <= ref_reg && ref_reg < kNumberOfCoreRegisters) << ref_reg;
674 // "Compact" slow path, saving two moves.
675 //
676 // Instead of using the standard runtime calling convention (input
677 // and output in R0):
678 //
679 // R0 <- ref
680 // R0 <- ReadBarrierMark(R0)
681 // ref <- R0
682 //
683 // we just use rX (the register containing `ref`) as input and output
684 // of a dedicated entrypoint:
685 //
686 // rX <- ReadBarrierMarkRegX(rX)
687 //
688 if (entrypoint_.IsValid()) {
689 arm_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
690 __ blx(entrypoint_.AsRegister<Register>());
691 } else {
692 // Entrypoint is not already loaded, load from the thread.
693 int32_t entry_point_offset =
694 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg);
695 // This runtime call does not require a stack map.
696 arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
697 }
698 }
699
700 // The location (register) of the marked object reference.
701 const Location ref_;
702
703 // The location of the entrypoint if it is already loaded.
704 const Location entrypoint_;
705
706 private:
707 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathBaseARM);
708 };
709
710 // Slow path marking an object reference `ref` during a read
711 // barrier. The field `obj.field` in the object `obj` holding this
712 // reference does not get updated by this slow path after marking.
713 //
714 // This means that after the execution of this slow path, `ref` will
715 // always be up-to-date, but `obj.field` may not; i.e., after the
716 // flip, `ref` will be a to-space reference, but `obj.field` will
717 // probably still be a from-space reference (unless it gets updated by
718 // another thread, or if another thread installed another object
719 // reference (different from `ref`) in `obj.field`).
720 //
721 // If `entrypoint` is a valid location it is assumed to already be
722 // holding the entrypoint. The case where the entrypoint is passed in
723 // is when the decision to mark is based on whether the GC is marking.
724 class ReadBarrierMarkSlowPathARM : public ReadBarrierMarkSlowPathBaseARM {
725 public:
ReadBarrierMarkSlowPathARM(HInstruction * instruction,Location ref,Location entrypoint=Location::NoLocation ())726 ReadBarrierMarkSlowPathARM(HInstruction* instruction,
727 Location ref,
728 Location entrypoint = Location::NoLocation())
729 : ReadBarrierMarkSlowPathBaseARM(instruction, ref, entrypoint) {
730 DCHECK(kEmitCompilerReadBarrier);
731 }
732
GetDescription() const733 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARM"; }
734
EmitNativeCode(CodeGenerator * codegen)735 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
736 LocationSummary* locations = instruction_->GetLocations();
737 DCHECK(locations->CanCall());
738 if (kIsDebugBuild) {
739 Register ref_reg = ref_.AsRegister<Register>();
740 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
741 }
742 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
743 << "Unexpected instruction in read barrier marking slow path: "
744 << instruction_->DebugName();
745
746 __ Bind(GetEntryLabel());
747 GenerateReadBarrierMarkRuntimeCall(codegen);
748 __ b(GetExitLabel());
749 }
750
751 private:
752 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARM);
753 };
754
755 // Slow path loading `obj`'s lock word, loading a reference from
756 // object `*(obj + offset + (index << scale_factor))` into `ref`, and
757 // marking `ref` if `obj` is gray according to the lock word (Baker
758 // read barrier). The field `obj.field` in the object `obj` holding
759 // this reference does not get updated by this slow path after marking
760 // (see LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM
761 // below for that).
762 //
763 // This means that after the execution of this slow path, `ref` will
764 // always be up-to-date, but `obj.field` may not; i.e., after the
765 // flip, `ref` will be a to-space reference, but `obj.field` will
766 // probably still be a from-space reference (unless it gets updated by
767 // another thread, or if another thread installed another object
768 // reference (different from `ref`) in `obj.field`).
769 //
770 // Argument `entrypoint` must be a register location holding the read
771 // barrier marking runtime entry point to be invoked.
772 class LoadReferenceWithBakerReadBarrierSlowPathARM : public ReadBarrierMarkSlowPathBaseARM {
773 public:
LoadReferenceWithBakerReadBarrierSlowPathARM(HInstruction * instruction,Location ref,Register obj,uint32_t offset,Location index,ScaleFactor scale_factor,bool needs_null_check,Register temp,Location entrypoint)774 LoadReferenceWithBakerReadBarrierSlowPathARM(HInstruction* instruction,
775 Location ref,
776 Register obj,
777 uint32_t offset,
778 Location index,
779 ScaleFactor scale_factor,
780 bool needs_null_check,
781 Register temp,
782 Location entrypoint)
783 : ReadBarrierMarkSlowPathBaseARM(instruction, ref, entrypoint),
784 obj_(obj),
785 offset_(offset),
786 index_(index),
787 scale_factor_(scale_factor),
788 needs_null_check_(needs_null_check),
789 temp_(temp) {
790 DCHECK(kEmitCompilerReadBarrier);
791 DCHECK(kUseBakerReadBarrier);
792 }
793
GetDescription() const794 const char* GetDescription() const OVERRIDE {
795 return "LoadReferenceWithBakerReadBarrierSlowPathARM";
796 }
797
EmitNativeCode(CodeGenerator * codegen)798 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
799 LocationSummary* locations = instruction_->GetLocations();
800 Register ref_reg = ref_.AsRegister<Register>();
801 DCHECK(locations->CanCall());
802 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
803 DCHECK_NE(ref_reg, temp_);
804 DCHECK(instruction_->IsInstanceFieldGet() ||
805 instruction_->IsStaticFieldGet() ||
806 instruction_->IsArrayGet() ||
807 instruction_->IsArraySet() ||
808 instruction_->IsInstanceOf() ||
809 instruction_->IsCheckCast() ||
810 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) ||
811 (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified()))
812 << "Unexpected instruction in read barrier marking slow path: "
813 << instruction_->DebugName();
814 // The read barrier instrumentation of object ArrayGet
815 // instructions does not support the HIntermediateAddress
816 // instruction.
817 DCHECK(!(instruction_->IsArrayGet() &&
818 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
819
820 __ Bind(GetEntryLabel());
821
822 // When using MaybeGenerateReadBarrierSlow, the read barrier call is
823 // inserted after the original load. However, in fast path based
824 // Baker's read barriers, we need to perform the load of
825 // mirror::Object::monitor_ *before* the original reference load.
826 // This load-load ordering is required by the read barrier.
827 // The fast path/slow path (for Baker's algorithm) should look like:
828 //
829 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
830 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
831 // HeapReference<mirror::Object> ref = *src; // Original reference load.
832 // bool is_gray = (rb_state == ReadBarrier::GrayState());
833 // if (is_gray) {
834 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
835 // }
836 //
837 // Note: the original implementation in ReadBarrier::Barrier is
838 // slightly more complex as it performs additional checks that we do
839 // not do here for performance reasons.
840
841 // /* int32_t */ monitor = obj->monitor_
842 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
843 __ LoadFromOffset(kLoadWord, temp_, obj_, monitor_offset);
844 if (needs_null_check_) {
845 codegen->MaybeRecordImplicitNullCheck(instruction_);
846 }
847 // /* LockWord */ lock_word = LockWord(monitor)
848 static_assert(sizeof(LockWord) == sizeof(int32_t),
849 "art::LockWord and int32_t have different sizes.");
850
851 // Introduce a dependency on the lock_word including the rb_state,
852 // which shall prevent load-load reordering without using
853 // a memory barrier (which would be more expensive).
854 // `obj` is unchanged by this operation, but its value now depends
855 // on `temp`.
856 __ add(obj_, obj_, ShifterOperand(temp_, LSR, 32));
857
858 // The actual reference load.
859 // A possible implicit null check has already been handled above.
860 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
861 arm_codegen->GenerateRawReferenceLoad(
862 instruction_, ref_, obj_, offset_, index_, scale_factor_, /* needs_null_check */ false);
863
864 // Mark the object `ref` when `obj` is gray.
865 //
866 // if (rb_state == ReadBarrier::GrayState())
867 // ref = ReadBarrier::Mark(ref);
868 //
869 // Given the numeric representation, it's enough to check the low bit of the
870 // rb_state. We do that by shifting the bit out of the lock word with LSRS
871 // which can be a 16-bit instruction unlike the TST immediate.
872 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
873 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
874 __ Lsrs(temp_, temp_, LockWord::kReadBarrierStateShift + 1);
875 __ b(GetExitLabel(), CC); // Carry flag is the last bit shifted out by LSRS.
876 GenerateReadBarrierMarkRuntimeCall(codegen);
877
878 __ b(GetExitLabel());
879 }
880
881 private:
882 // The register containing the object holding the marked object reference field.
883 Register obj_;
884 // The offset, index and scale factor to access the reference in `obj_`.
885 uint32_t offset_;
886 Location index_;
887 ScaleFactor scale_factor_;
888 // Is a null check required?
889 bool needs_null_check_;
890 // A temporary register used to hold the lock word of `obj_`.
891 Register temp_;
892
893 DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierSlowPathARM);
894 };
895
896 // Slow path loading `obj`'s lock word, loading a reference from
897 // object `*(obj + offset + (index << scale_factor))` into `ref`, and
898 // marking `ref` if `obj` is gray according to the lock word (Baker
899 // read barrier). If needed, this slow path also atomically updates
900 // the field `obj.field` in the object `obj` holding this reference
901 // after marking (contrary to
902 // LoadReferenceWithBakerReadBarrierSlowPathARM above, which never
903 // tries to update `obj.field`).
904 //
905 // This means that after the execution of this slow path, both `ref`
906 // and `obj.field` will be up-to-date; i.e., after the flip, both will
907 // hold the same to-space reference (unless another thread installed
908 // another object reference (different from `ref`) in `obj.field`).
909 //
910 // Argument `entrypoint` must be a register location holding the read
911 // barrier marking runtime entry point to be invoked.
912 class LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM
913 : public ReadBarrierMarkSlowPathBaseARM {
914 public:
LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM(HInstruction * instruction,Location ref,Register obj,uint32_t offset,Location index,ScaleFactor scale_factor,bool needs_null_check,Register temp1,Register temp2,Location entrypoint)915 LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM(HInstruction* instruction,
916 Location ref,
917 Register obj,
918 uint32_t offset,
919 Location index,
920 ScaleFactor scale_factor,
921 bool needs_null_check,
922 Register temp1,
923 Register temp2,
924 Location entrypoint)
925 : ReadBarrierMarkSlowPathBaseARM(instruction, ref, entrypoint),
926 obj_(obj),
927 offset_(offset),
928 index_(index),
929 scale_factor_(scale_factor),
930 needs_null_check_(needs_null_check),
931 temp1_(temp1),
932 temp2_(temp2) {
933 DCHECK(kEmitCompilerReadBarrier);
934 DCHECK(kUseBakerReadBarrier);
935 }
936
GetDescription() const937 const char* GetDescription() const OVERRIDE {
938 return "LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM";
939 }
940
EmitNativeCode(CodeGenerator * codegen)941 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
942 LocationSummary* locations = instruction_->GetLocations();
943 Register ref_reg = ref_.AsRegister<Register>();
944 DCHECK(locations->CanCall());
945 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
946 DCHECK_NE(ref_reg, temp1_);
947
948 // This slow path is only used by the UnsafeCASObject intrinsic at the moment.
949 DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
950 << "Unexpected instruction in read barrier marking and field updating slow path: "
951 << instruction_->DebugName();
952 DCHECK(instruction_->GetLocations()->Intrinsified());
953 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject);
954 DCHECK_EQ(offset_, 0u);
955 DCHECK_EQ(scale_factor_, ScaleFactor::TIMES_1);
956 // The location of the offset of the marked reference field within `obj_`.
957 Location field_offset = index_;
958 DCHECK(field_offset.IsRegisterPair()) << field_offset;
959
960 __ Bind(GetEntryLabel());
961
962 // /* int32_t */ monitor = obj->monitor_
963 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
964 __ LoadFromOffset(kLoadWord, temp1_, obj_, monitor_offset);
965 if (needs_null_check_) {
966 codegen->MaybeRecordImplicitNullCheck(instruction_);
967 }
968 // /* LockWord */ lock_word = LockWord(monitor)
969 static_assert(sizeof(LockWord) == sizeof(int32_t),
970 "art::LockWord and int32_t have different sizes.");
971
972 // Introduce a dependency on the lock_word including the rb_state,
973 // which shall prevent load-load reordering without using
974 // a memory barrier (which would be more expensive).
975 // `obj` is unchanged by this operation, but its value now depends
976 // on `temp1`.
977 __ add(obj_, obj_, ShifterOperand(temp1_, LSR, 32));
978
979 // The actual reference load.
980 // A possible implicit null check has already been handled above.
981 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
982 arm_codegen->GenerateRawReferenceLoad(
983 instruction_, ref_, obj_, offset_, index_, scale_factor_, /* needs_null_check */ false);
984
985 // Mark the object `ref` when `obj` is gray.
986 //
987 // if (rb_state == ReadBarrier::GrayState())
988 // ref = ReadBarrier::Mark(ref);
989 //
990 // Given the numeric representation, it's enough to check the low bit of the
991 // rb_state. We do that by shifting the bit out of the lock word with LSRS
992 // which can be a 16-bit instruction unlike the TST immediate.
993 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
994 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
995 __ Lsrs(temp1_, temp1_, LockWord::kReadBarrierStateShift + 1);
996 __ b(GetExitLabel(), CC); // Carry flag is the last bit shifted out by LSRS.
997
998 // Save the old value of the reference before marking it.
999 // Note that we cannot use IP to save the old reference, as IP is
1000 // used internally by the ReadBarrierMarkRegX entry point, and we
1001 // need the old reference after the call to that entry point.
1002 DCHECK_NE(temp1_, IP);
1003 __ Mov(temp1_, ref_reg);
1004
1005 GenerateReadBarrierMarkRuntimeCall(codegen);
1006
1007 // If the new reference is different from the old reference,
1008 // update the field in the holder (`*(obj_ + field_offset)`).
1009 //
1010 // Note that this field could also hold a different object, if
1011 // another thread had concurrently changed it. In that case, the
1012 // LDREX/SUBS/ITNE sequence of instructions in the compare-and-set
1013 // (CAS) operation below would abort the CAS, leaving the field
1014 // as-is.
1015 __ cmp(temp1_, ShifterOperand(ref_reg));
1016 __ b(GetExitLabel(), EQ);
1017
1018 // Update the the holder's field atomically. This may fail if
1019 // mutator updates before us, but it's OK. This is achieved
1020 // using a strong compare-and-set (CAS) operation with relaxed
1021 // memory synchronization ordering, where the expected value is
1022 // the old reference and the desired value is the new reference.
1023
1024 // Convenience aliases.
1025 Register base = obj_;
1026 // The UnsafeCASObject intrinsic uses a register pair as field
1027 // offset ("long offset"), of which only the low part contains
1028 // data.
1029 Register offset = field_offset.AsRegisterPairLow<Register>();
1030 Register expected = temp1_;
1031 Register value = ref_reg;
1032 Register tmp_ptr = IP; // Pointer to actual memory.
1033 Register tmp = temp2_; // Value in memory.
1034
1035 __ add(tmp_ptr, base, ShifterOperand(offset));
1036
1037 if (kPoisonHeapReferences) {
1038 __ PoisonHeapReference(expected);
1039 if (value == expected) {
1040 // Do not poison `value`, as it is the same register as
1041 // `expected`, which has just been poisoned.
1042 } else {
1043 __ PoisonHeapReference(value);
1044 }
1045 }
1046
1047 // do {
1048 // tmp = [r_ptr] - expected;
1049 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
1050
1051 Label loop_head, exit_loop;
1052 __ Bind(&loop_head);
1053
1054 __ ldrex(tmp, tmp_ptr);
1055
1056 __ subs(tmp, tmp, ShifterOperand(expected));
1057
1058 __ it(NE);
1059 __ clrex(NE);
1060
1061 __ b(&exit_loop, NE);
1062
1063 __ strex(tmp, value, tmp_ptr);
1064 __ cmp(tmp, ShifterOperand(1));
1065 __ b(&loop_head, EQ);
1066
1067 __ Bind(&exit_loop);
1068
1069 if (kPoisonHeapReferences) {
1070 __ UnpoisonHeapReference(expected);
1071 if (value == expected) {
1072 // Do not unpoison `value`, as it is the same register as
1073 // `expected`, which has just been unpoisoned.
1074 } else {
1075 __ UnpoisonHeapReference(value);
1076 }
1077 }
1078
1079 __ b(GetExitLabel());
1080 }
1081
1082 private:
1083 // The register containing the object holding the marked object reference field.
1084 const Register obj_;
1085 // The offset, index and scale factor to access the reference in `obj_`.
1086 uint32_t offset_;
1087 Location index_;
1088 ScaleFactor scale_factor_;
1089 // Is a null check required?
1090 bool needs_null_check_;
1091 // A temporary register used to hold the lock word of `obj_`; and
1092 // also to hold the original reference value, when the reference is
1093 // marked.
1094 const Register temp1_;
1095 // A temporary register used in the implementation of the CAS, to
1096 // update the object's reference field.
1097 const Register temp2_;
1098
1099 DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM);
1100 };
1101
1102 // Slow path generating a read barrier for a heap reference.
1103 class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCodeARM {
1104 public:
ReadBarrierForHeapReferenceSlowPathARM(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)1105 ReadBarrierForHeapReferenceSlowPathARM(HInstruction* instruction,
1106 Location out,
1107 Location ref,
1108 Location obj,
1109 uint32_t offset,
1110 Location index)
1111 : SlowPathCodeARM(instruction),
1112 out_(out),
1113 ref_(ref),
1114 obj_(obj),
1115 offset_(offset),
1116 index_(index) {
1117 DCHECK(kEmitCompilerReadBarrier);
1118 // If `obj` is equal to `out` or `ref`, it means the initial object
1119 // has been overwritten by (or after) the heap object reference load
1120 // to be instrumented, e.g.:
1121 //
1122 // __ LoadFromOffset(kLoadWord, out, out, offset);
1123 // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
1124 //
1125 // In that case, we have lost the information about the original
1126 // object, and the emitted read barrier cannot work properly.
1127 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
1128 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
1129 }
1130
EmitNativeCode(CodeGenerator * codegen)1131 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1132 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
1133 LocationSummary* locations = instruction_->GetLocations();
1134 Register reg_out = out_.AsRegister<Register>();
1135 DCHECK(locations->CanCall());
1136 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
1137 DCHECK(instruction_->IsInstanceFieldGet() ||
1138 instruction_->IsStaticFieldGet() ||
1139 instruction_->IsArrayGet() ||
1140 instruction_->IsInstanceOf() ||
1141 instruction_->IsCheckCast() ||
1142 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
1143 << "Unexpected instruction in read barrier for heap reference slow path: "
1144 << instruction_->DebugName();
1145 // The read barrier instrumentation of object ArrayGet
1146 // instructions does not support the HIntermediateAddress
1147 // instruction.
1148 DCHECK(!(instruction_->IsArrayGet() &&
1149 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
1150
1151 __ Bind(GetEntryLabel());
1152 SaveLiveRegisters(codegen, locations);
1153
1154 // We may have to change the index's value, but as `index_` is a
1155 // constant member (like other "inputs" of this slow path),
1156 // introduce a copy of it, `index`.
1157 Location index = index_;
1158 if (index_.IsValid()) {
1159 // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
1160 if (instruction_->IsArrayGet()) {
1161 // Compute the actual memory offset and store it in `index`.
1162 Register index_reg = index_.AsRegister<Register>();
1163 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg));
1164 if (codegen->IsCoreCalleeSaveRegister(index_reg)) {
1165 // We are about to change the value of `index_reg` (see the
1166 // calls to art::arm::Thumb2Assembler::Lsl and
1167 // art::arm::Thumb2Assembler::AddConstant below), but it has
1168 // not been saved by the previous call to
1169 // art::SlowPathCode::SaveLiveRegisters, as it is a
1170 // callee-save register --
1171 // art::SlowPathCode::SaveLiveRegisters does not consider
1172 // callee-save registers, as it has been designed with the
1173 // assumption that callee-save registers are supposed to be
1174 // handled by the called function. So, as a callee-save
1175 // register, `index_reg` _would_ eventually be saved onto
1176 // the stack, but it would be too late: we would have
1177 // changed its value earlier. Therefore, we manually save
1178 // it here into another freely available register,
1179 // `free_reg`, chosen of course among the caller-save
1180 // registers (as a callee-save `free_reg` register would
1181 // exhibit the same problem).
1182 //
1183 // Note we could have requested a temporary register from
1184 // the register allocator instead; but we prefer not to, as
1185 // this is a slow path, and we know we can find a
1186 // caller-save register that is available.
1187 Register free_reg = FindAvailableCallerSaveRegister(codegen);
1188 __ Mov(free_reg, index_reg);
1189 index_reg = free_reg;
1190 index = Location::RegisterLocation(index_reg);
1191 } else {
1192 // The initial register stored in `index_` has already been
1193 // saved in the call to art::SlowPathCode::SaveLiveRegisters
1194 // (as it is not a callee-save register), so we can freely
1195 // use it.
1196 }
1197 // Shifting the index value contained in `index_reg` by the scale
1198 // factor (2) cannot overflow in practice, as the runtime is
1199 // unable to allocate object arrays with a size larger than
1200 // 2^26 - 1 (that is, 2^28 - 4 bytes).
1201 __ Lsl(index_reg, index_reg, TIMES_4);
1202 static_assert(
1203 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
1204 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
1205 __ AddConstant(index_reg, index_reg, offset_);
1206 } else {
1207 // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
1208 // intrinsics, `index_` is not shifted by a scale factor of 2
1209 // (as in the case of ArrayGet), as it is actually an offset
1210 // to an object field within an object.
1211 DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
1212 DCHECK(instruction_->GetLocations()->Intrinsified());
1213 DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
1214 (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
1215 << instruction_->AsInvoke()->GetIntrinsic();
1216 DCHECK_EQ(offset_, 0U);
1217 DCHECK(index_.IsRegisterPair());
1218 // UnsafeGet's offset location is a register pair, the low
1219 // part contains the correct offset.
1220 index = index_.ToLow();
1221 }
1222 }
1223
1224 // We're moving two or three locations to locations that could
1225 // overlap, so we need a parallel move resolver.
1226 InvokeRuntimeCallingConvention calling_convention;
1227 HParallelMove parallel_move(codegen->GetGraph()->GetArena());
1228 parallel_move.AddMove(ref_,
1229 Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
1230 Primitive::kPrimNot,
1231 nullptr);
1232 parallel_move.AddMove(obj_,
1233 Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
1234 Primitive::kPrimNot,
1235 nullptr);
1236 if (index.IsValid()) {
1237 parallel_move.AddMove(index,
1238 Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
1239 Primitive::kPrimInt,
1240 nullptr);
1241 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move);
1242 } else {
1243 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move);
1244 __ LoadImmediate(calling_convention.GetRegisterAt(2), offset_);
1245 }
1246 arm_codegen->InvokeRuntime(kQuickReadBarrierSlow, instruction_, instruction_->GetDexPc(), this);
1247 CheckEntrypointTypes<
1248 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
1249 arm_codegen->Move32(out_, Location::RegisterLocation(R0));
1250
1251 RestoreLiveRegisters(codegen, locations);
1252 __ b(GetExitLabel());
1253 }
1254
GetDescription() const1255 const char* GetDescription() const OVERRIDE { return "ReadBarrierForHeapReferenceSlowPathARM"; }
1256
1257 private:
FindAvailableCallerSaveRegister(CodeGenerator * codegen)1258 Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
1259 size_t ref = static_cast<int>(ref_.AsRegister<Register>());
1260 size_t obj = static_cast<int>(obj_.AsRegister<Register>());
1261 for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
1262 if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) {
1263 return static_cast<Register>(i);
1264 }
1265 }
1266 // We shall never fail to find a free caller-save register, as
1267 // there are more than two core caller-save registers on ARM
1268 // (meaning it is possible to find one which is different from
1269 // `ref` and `obj`).
1270 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
1271 LOG(FATAL) << "Could not find a free caller-save register";
1272 UNREACHABLE();
1273 }
1274
1275 const Location out_;
1276 const Location ref_;
1277 const Location obj_;
1278 const uint32_t offset_;
1279 // An additional location containing an index to an array.
1280 // Only used for HArrayGet and the UnsafeGetObject &
1281 // UnsafeGetObjectVolatile intrinsics.
1282 const Location index_;
1283
1284 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARM);
1285 };
1286
1287 // Slow path generating a read barrier for a GC root.
1288 class ReadBarrierForRootSlowPathARM : public SlowPathCodeARM {
1289 public:
ReadBarrierForRootSlowPathARM(HInstruction * instruction,Location out,Location root)1290 ReadBarrierForRootSlowPathARM(HInstruction* instruction, Location out, Location root)
1291 : SlowPathCodeARM(instruction), out_(out), root_(root) {
1292 DCHECK(kEmitCompilerReadBarrier);
1293 }
1294
EmitNativeCode(CodeGenerator * codegen)1295 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1296 LocationSummary* locations = instruction_->GetLocations();
1297 Register reg_out = out_.AsRegister<Register>();
1298 DCHECK(locations->CanCall());
1299 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
1300 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
1301 << "Unexpected instruction in read barrier for GC root slow path: "
1302 << instruction_->DebugName();
1303
1304 __ Bind(GetEntryLabel());
1305 SaveLiveRegisters(codegen, locations);
1306
1307 InvokeRuntimeCallingConvention calling_convention;
1308 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
1309 arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), root_);
1310 arm_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
1311 instruction_,
1312 instruction_->GetDexPc(),
1313 this);
1314 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
1315 arm_codegen->Move32(out_, Location::RegisterLocation(R0));
1316
1317 RestoreLiveRegisters(codegen, locations);
1318 __ b(GetExitLabel());
1319 }
1320
GetDescription() const1321 const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARM"; }
1322
1323 private:
1324 const Location out_;
1325 const Location root_;
1326
1327 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARM);
1328 };
1329
ARMCondition(IfCondition cond)1330 inline Condition ARMCondition(IfCondition cond) {
1331 switch (cond) {
1332 case kCondEQ: return EQ;
1333 case kCondNE: return NE;
1334 case kCondLT: return LT;
1335 case kCondLE: return LE;
1336 case kCondGT: return GT;
1337 case kCondGE: return GE;
1338 case kCondB: return LO;
1339 case kCondBE: return LS;
1340 case kCondA: return HI;
1341 case kCondAE: return HS;
1342 }
1343 LOG(FATAL) << "Unreachable";
1344 UNREACHABLE();
1345 }
1346
1347 // Maps signed condition to unsigned condition.
ARMUnsignedCondition(IfCondition cond)1348 inline Condition ARMUnsignedCondition(IfCondition cond) {
1349 switch (cond) {
1350 case kCondEQ: return EQ;
1351 case kCondNE: return NE;
1352 // Signed to unsigned.
1353 case kCondLT: return LO;
1354 case kCondLE: return LS;
1355 case kCondGT: return HI;
1356 case kCondGE: return HS;
1357 // Unsigned remain unchanged.
1358 case kCondB: return LO;
1359 case kCondBE: return LS;
1360 case kCondA: return HI;
1361 case kCondAE: return HS;
1362 }
1363 LOG(FATAL) << "Unreachable";
1364 UNREACHABLE();
1365 }
1366
ARMFPCondition(IfCondition cond,bool gt_bias)1367 inline Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
1368 // The ARM condition codes can express all the necessary branches, see the
1369 // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
1370 // There is no dex instruction or HIR that would need the missing conditions
1371 // "equal or unordered" or "not equal".
1372 switch (cond) {
1373 case kCondEQ: return EQ;
1374 case kCondNE: return NE /* unordered */;
1375 case kCondLT: return gt_bias ? CC : LT /* unordered */;
1376 case kCondLE: return gt_bias ? LS : LE /* unordered */;
1377 case kCondGT: return gt_bias ? HI /* unordered */ : GT;
1378 case kCondGE: return gt_bias ? CS /* unordered */ : GE;
1379 default:
1380 LOG(FATAL) << "UNREACHABLE";
1381 UNREACHABLE();
1382 }
1383 }
1384
ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind)1385 inline Shift ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
1386 switch (op_kind) {
1387 case HDataProcWithShifterOp::kASR: return ASR;
1388 case HDataProcWithShifterOp::kLSL: return LSL;
1389 case HDataProcWithShifterOp::kLSR: return LSR;
1390 default:
1391 LOG(FATAL) << "Unexpected op kind " << op_kind;
1392 UNREACHABLE();
1393 }
1394 }
1395
GenerateDataProcInstruction(HInstruction::InstructionKind kind,Register out,Register first,const ShifterOperand & second,CodeGeneratorARM * codegen)1396 static void GenerateDataProcInstruction(HInstruction::InstructionKind kind,
1397 Register out,
1398 Register first,
1399 const ShifterOperand& second,
1400 CodeGeneratorARM* codegen) {
1401 if (second.IsImmediate() && second.GetImmediate() == 0) {
1402 const ShifterOperand in = kind == HInstruction::kAnd
1403 ? ShifterOperand(0)
1404 : ShifterOperand(first);
1405
1406 __ mov(out, in);
1407 } else {
1408 switch (kind) {
1409 case HInstruction::kAdd:
1410 __ add(out, first, second);
1411 break;
1412 case HInstruction::kAnd:
1413 __ and_(out, first, second);
1414 break;
1415 case HInstruction::kOr:
1416 __ orr(out, first, second);
1417 break;
1418 case HInstruction::kSub:
1419 __ sub(out, first, second);
1420 break;
1421 case HInstruction::kXor:
1422 __ eor(out, first, second);
1423 break;
1424 default:
1425 LOG(FATAL) << "Unexpected instruction kind: " << kind;
1426 UNREACHABLE();
1427 }
1428 }
1429 }
1430
GenerateDataProc(HInstruction::InstructionKind kind,const Location & out,const Location & first,const ShifterOperand & second_lo,const ShifterOperand & second_hi,CodeGeneratorARM * codegen)1431 static void GenerateDataProc(HInstruction::InstructionKind kind,
1432 const Location& out,
1433 const Location& first,
1434 const ShifterOperand& second_lo,
1435 const ShifterOperand& second_hi,
1436 CodeGeneratorARM* codegen) {
1437 const Register first_hi = first.AsRegisterPairHigh<Register>();
1438 const Register first_lo = first.AsRegisterPairLow<Register>();
1439 const Register out_hi = out.AsRegisterPairHigh<Register>();
1440 const Register out_lo = out.AsRegisterPairLow<Register>();
1441
1442 if (kind == HInstruction::kAdd) {
1443 __ adds(out_lo, first_lo, second_lo);
1444 __ adc(out_hi, first_hi, second_hi);
1445 } else if (kind == HInstruction::kSub) {
1446 __ subs(out_lo, first_lo, second_lo);
1447 __ sbc(out_hi, first_hi, second_hi);
1448 } else {
1449 GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen);
1450 GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen);
1451 }
1452 }
1453
GetShifterOperand(Register rm,Shift shift,uint32_t shift_imm)1454 static ShifterOperand GetShifterOperand(Register rm, Shift shift, uint32_t shift_imm) {
1455 return shift_imm == 0 ? ShifterOperand(rm) : ShifterOperand(rm, shift, shift_imm);
1456 }
1457
GenerateLongDataProc(HDataProcWithShifterOp * instruction,CodeGeneratorARM * codegen)1458 static void GenerateLongDataProc(HDataProcWithShifterOp* instruction, CodeGeneratorARM* codegen) {
1459 DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong);
1460 DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind()));
1461
1462 const LocationSummary* const locations = instruction->GetLocations();
1463 const uint32_t shift_value = instruction->GetShiftAmount();
1464 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
1465 const Location first = locations->InAt(0);
1466 const Location second = locations->InAt(1);
1467 const Location out = locations->Out();
1468 const Register first_hi = first.AsRegisterPairHigh<Register>();
1469 const Register first_lo = first.AsRegisterPairLow<Register>();
1470 const Register out_hi = out.AsRegisterPairHigh<Register>();
1471 const Register out_lo = out.AsRegisterPairLow<Register>();
1472 const Register second_hi = second.AsRegisterPairHigh<Register>();
1473 const Register second_lo = second.AsRegisterPairLow<Register>();
1474 const Shift shift = ShiftFromOpKind(instruction->GetOpKind());
1475
1476 if (shift_value >= 32) {
1477 if (shift == LSL) {
1478 GenerateDataProcInstruction(kind,
1479 out_hi,
1480 first_hi,
1481 ShifterOperand(second_lo, LSL, shift_value - 32),
1482 codegen);
1483 GenerateDataProcInstruction(kind,
1484 out_lo,
1485 first_lo,
1486 ShifterOperand(0),
1487 codegen);
1488 } else if (shift == ASR) {
1489 GenerateDataProc(kind,
1490 out,
1491 first,
1492 GetShifterOperand(second_hi, ASR, shift_value - 32),
1493 ShifterOperand(second_hi, ASR, 31),
1494 codegen);
1495 } else {
1496 DCHECK_EQ(shift, LSR);
1497 GenerateDataProc(kind,
1498 out,
1499 first,
1500 GetShifterOperand(second_hi, LSR, shift_value - 32),
1501 ShifterOperand(0),
1502 codegen);
1503 }
1504 } else {
1505 DCHECK_GT(shift_value, 1U);
1506 DCHECK_LT(shift_value, 32U);
1507
1508 if (shift == LSL) {
1509 // We are not doing this for HInstruction::kAdd because the output will require
1510 // Location::kOutputOverlap; not applicable to other cases.
1511 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1512 GenerateDataProcInstruction(kind,
1513 out_hi,
1514 first_hi,
1515 ShifterOperand(second_hi, LSL, shift_value),
1516 codegen);
1517 GenerateDataProcInstruction(kind,
1518 out_hi,
1519 out_hi,
1520 ShifterOperand(second_lo, LSR, 32 - shift_value),
1521 codegen);
1522 GenerateDataProcInstruction(kind,
1523 out_lo,
1524 first_lo,
1525 ShifterOperand(second_lo, LSL, shift_value),
1526 codegen);
1527 } else {
1528 __ Lsl(IP, second_hi, shift_value);
1529 __ orr(IP, IP, ShifterOperand(second_lo, LSR, 32 - shift_value));
1530 GenerateDataProc(kind,
1531 out,
1532 first,
1533 ShifterOperand(second_lo, LSL, shift_value),
1534 ShifterOperand(IP),
1535 codegen);
1536 }
1537 } else {
1538 DCHECK(shift == ASR || shift == LSR);
1539
1540 // We are not doing this for HInstruction::kAdd because the output will require
1541 // Location::kOutputOverlap; not applicable to other cases.
1542 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1543 GenerateDataProcInstruction(kind,
1544 out_lo,
1545 first_lo,
1546 ShifterOperand(second_lo, LSR, shift_value),
1547 codegen);
1548 GenerateDataProcInstruction(kind,
1549 out_lo,
1550 out_lo,
1551 ShifterOperand(second_hi, LSL, 32 - shift_value),
1552 codegen);
1553 GenerateDataProcInstruction(kind,
1554 out_hi,
1555 first_hi,
1556 ShifterOperand(second_hi, shift, shift_value),
1557 codegen);
1558 } else {
1559 __ Lsr(IP, second_lo, shift_value);
1560 __ orr(IP, IP, ShifterOperand(second_hi, LSL, 32 - shift_value));
1561 GenerateDataProc(kind,
1562 out,
1563 first,
1564 ShifterOperand(IP),
1565 ShifterOperand(second_hi, shift, shift_value),
1566 codegen);
1567 }
1568 }
1569 }
1570 }
1571
GenerateVcmp(HInstruction * instruction,CodeGeneratorARM * codegen)1572 static void GenerateVcmp(HInstruction* instruction, CodeGeneratorARM* codegen) {
1573 Primitive::Type type = instruction->InputAt(0)->GetType();
1574 Location lhs_loc = instruction->GetLocations()->InAt(0);
1575 Location rhs_loc = instruction->GetLocations()->InAt(1);
1576 if (rhs_loc.IsConstant()) {
1577 // 0.0 is the only immediate that can be encoded directly in
1578 // a VCMP instruction.
1579 //
1580 // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
1581 // specify that in a floating-point comparison, positive zero
1582 // and negative zero are considered equal, so we can use the
1583 // literal 0.0 for both cases here.
1584 //
1585 // Note however that some methods (Float.equal, Float.compare,
1586 // Float.compareTo, Double.equal, Double.compare,
1587 // Double.compareTo, Math.max, Math.min, StrictMath.max,
1588 // StrictMath.min) consider 0.0 to be (strictly) greater than
1589 // -0.0. So if we ever translate calls to these methods into a
1590 // HCompare instruction, we must handle the -0.0 case with
1591 // care here.
1592 DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
1593 if (type == Primitive::kPrimFloat) {
1594 __ vcmpsz(lhs_loc.AsFpuRegister<SRegister>());
1595 } else {
1596 DCHECK_EQ(type, Primitive::kPrimDouble);
1597 __ vcmpdz(FromLowSToD(lhs_loc.AsFpuRegisterPairLow<SRegister>()));
1598 }
1599 } else {
1600 if (type == Primitive::kPrimFloat) {
1601 __ vcmps(lhs_loc.AsFpuRegister<SRegister>(), rhs_loc.AsFpuRegister<SRegister>());
1602 } else {
1603 DCHECK_EQ(type, Primitive::kPrimDouble);
1604 __ vcmpd(FromLowSToD(lhs_loc.AsFpuRegisterPairLow<SRegister>()),
1605 FromLowSToD(rhs_loc.AsFpuRegisterPairLow<SRegister>()));
1606 }
1607 }
1608 }
1609
GenerateLongTestConstant(HCondition * condition,bool invert,CodeGeneratorARM * codegen)1610 static std::pair<Condition, Condition> GenerateLongTestConstant(HCondition* condition,
1611 bool invert,
1612 CodeGeneratorARM* codegen) {
1613 DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
1614
1615 const LocationSummary* const locations = condition->GetLocations();
1616 IfCondition cond = condition->GetCondition();
1617 IfCondition opposite = condition->GetOppositeCondition();
1618
1619 if (invert) {
1620 std::swap(cond, opposite);
1621 }
1622
1623 std::pair<Condition, Condition> ret;
1624 const Location left = locations->InAt(0);
1625 const Location right = locations->InAt(1);
1626
1627 DCHECK(right.IsConstant());
1628
1629 const Register left_high = left.AsRegisterPairHigh<Register>();
1630 const Register left_low = left.AsRegisterPairLow<Register>();
1631 int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
1632
1633 switch (cond) {
1634 case kCondEQ:
1635 case kCondNE:
1636 case kCondB:
1637 case kCondBE:
1638 case kCondA:
1639 case kCondAE:
1640 __ CmpConstant(left_high, High32Bits(value));
1641 __ it(EQ);
1642 __ cmp(left_low, ShifterOperand(Low32Bits(value)), EQ);
1643 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
1644 break;
1645 case kCondLE:
1646 case kCondGT:
1647 // Trivially true or false.
1648 if (value == std::numeric_limits<int64_t>::max()) {
1649 __ cmp(left_low, ShifterOperand(left_low));
1650 ret = cond == kCondLE ? std::make_pair(EQ, NE) : std::make_pair(NE, EQ);
1651 break;
1652 }
1653
1654 if (cond == kCondLE) {
1655 DCHECK_EQ(opposite, kCondGT);
1656 cond = kCondLT;
1657 opposite = kCondGE;
1658 } else {
1659 DCHECK_EQ(cond, kCondGT);
1660 DCHECK_EQ(opposite, kCondLE);
1661 cond = kCondGE;
1662 opposite = kCondLT;
1663 }
1664
1665 value++;
1666 FALLTHROUGH_INTENDED;
1667 case kCondGE:
1668 case kCondLT:
1669 __ CmpConstant(left_low, Low32Bits(value));
1670 __ sbcs(IP, left_high, ShifterOperand(High32Bits(value)));
1671 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
1672 break;
1673 default:
1674 LOG(FATAL) << "Unreachable";
1675 UNREACHABLE();
1676 }
1677
1678 return ret;
1679 }
1680
GenerateLongTest(HCondition * condition,bool invert,CodeGeneratorARM * codegen)1681 static std::pair<Condition, Condition> GenerateLongTest(HCondition* condition,
1682 bool invert,
1683 CodeGeneratorARM* codegen) {
1684 DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
1685
1686 const LocationSummary* const locations = condition->GetLocations();
1687 IfCondition cond = condition->GetCondition();
1688 IfCondition opposite = condition->GetOppositeCondition();
1689
1690 if (invert) {
1691 std::swap(cond, opposite);
1692 }
1693
1694 std::pair<Condition, Condition> ret;
1695 Location left = locations->InAt(0);
1696 Location right = locations->InAt(1);
1697
1698 DCHECK(right.IsRegisterPair());
1699
1700 switch (cond) {
1701 case kCondEQ:
1702 case kCondNE:
1703 case kCondB:
1704 case kCondBE:
1705 case kCondA:
1706 case kCondAE:
1707 __ cmp(left.AsRegisterPairHigh<Register>(),
1708 ShifterOperand(right.AsRegisterPairHigh<Register>()));
1709 __ it(EQ);
1710 __ cmp(left.AsRegisterPairLow<Register>(),
1711 ShifterOperand(right.AsRegisterPairLow<Register>()),
1712 EQ);
1713 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
1714 break;
1715 case kCondLE:
1716 case kCondGT:
1717 if (cond == kCondLE) {
1718 DCHECK_EQ(opposite, kCondGT);
1719 cond = kCondGE;
1720 opposite = kCondLT;
1721 } else {
1722 DCHECK_EQ(cond, kCondGT);
1723 DCHECK_EQ(opposite, kCondLE);
1724 cond = kCondLT;
1725 opposite = kCondGE;
1726 }
1727
1728 std::swap(left, right);
1729 FALLTHROUGH_INTENDED;
1730 case kCondGE:
1731 case kCondLT:
1732 __ cmp(left.AsRegisterPairLow<Register>(),
1733 ShifterOperand(right.AsRegisterPairLow<Register>()));
1734 __ sbcs(IP,
1735 left.AsRegisterPairHigh<Register>(),
1736 ShifterOperand(right.AsRegisterPairHigh<Register>()));
1737 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
1738 break;
1739 default:
1740 LOG(FATAL) << "Unreachable";
1741 UNREACHABLE();
1742 }
1743
1744 return ret;
1745 }
1746
GenerateTest(HCondition * condition,bool invert,CodeGeneratorARM * codegen)1747 static std::pair<Condition, Condition> GenerateTest(HCondition* condition,
1748 bool invert,
1749 CodeGeneratorARM* codegen) {
1750 const LocationSummary* const locations = condition->GetLocations();
1751 const Primitive::Type type = condition->GetLeft()->GetType();
1752 IfCondition cond = condition->GetCondition();
1753 IfCondition opposite = condition->GetOppositeCondition();
1754 std::pair<Condition, Condition> ret;
1755 const Location right = locations->InAt(1);
1756
1757 if (invert) {
1758 std::swap(cond, opposite);
1759 }
1760
1761 if (type == Primitive::kPrimLong) {
1762 ret = locations->InAt(1).IsConstant()
1763 ? GenerateLongTestConstant(condition, invert, codegen)
1764 : GenerateLongTest(condition, invert, codegen);
1765 } else if (Primitive::IsFloatingPointType(type)) {
1766 GenerateVcmp(condition, codegen);
1767 __ vmstat();
1768 ret = std::make_pair(ARMFPCondition(cond, condition->IsGtBias()),
1769 ARMFPCondition(opposite, condition->IsGtBias()));
1770 } else {
1771 DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
1772
1773 const Register left = locations->InAt(0).AsRegister<Register>();
1774
1775 if (right.IsRegister()) {
1776 __ cmp(left, ShifterOperand(right.AsRegister<Register>()));
1777 } else {
1778 DCHECK(right.IsConstant());
1779 __ CmpConstant(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
1780 }
1781
1782 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
1783 }
1784
1785 return ret;
1786 }
1787
CanGenerateTest(HCondition * condition,ArmAssembler * assembler)1788 static bool CanGenerateTest(HCondition* condition, ArmAssembler* assembler) {
1789 if (condition->GetLeft()->GetType() == Primitive::kPrimLong) {
1790 const LocationSummary* const locations = condition->GetLocations();
1791 const IfCondition c = condition->GetCondition();
1792
1793 if (locations->InAt(1).IsConstant()) {
1794 const int64_t value = locations->InAt(1).GetConstant()->AsLongConstant()->GetValue();
1795 ShifterOperand so;
1796
1797 if (c < kCondLT || c > kCondGE) {
1798 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
1799 // we check that the least significant half of the first input to be compared
1800 // is in a low register (the other half is read outside an IT block), and
1801 // the constant fits in an 8-bit unsigned integer, so that a 16-bit CMP
1802 // encoding can be used.
1803 if (!ArmAssembler::IsLowRegister(locations->InAt(0).AsRegisterPairLow<Register>()) ||
1804 !IsUint<8>(Low32Bits(value))) {
1805 return false;
1806 }
1807 } else if (c == kCondLE || c == kCondGT) {
1808 if (value < std::numeric_limits<int64_t>::max() &&
1809 !assembler->ShifterOperandCanHold(kNoRegister,
1810 kNoRegister,
1811 SBC,
1812 High32Bits(value + 1),
1813 kCcSet,
1814 &so)) {
1815 return false;
1816 }
1817 } else if (!assembler->ShifterOperandCanHold(kNoRegister,
1818 kNoRegister,
1819 SBC,
1820 High32Bits(value),
1821 kCcSet,
1822 &so)) {
1823 return false;
1824 }
1825 }
1826 }
1827
1828 return true;
1829 }
1830
CanEncodeConstantAs8BitImmediate(HConstant * constant)1831 static bool CanEncodeConstantAs8BitImmediate(HConstant* constant) {
1832 const Primitive::Type type = constant->GetType();
1833 bool ret = false;
1834
1835 DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
1836
1837 if (type == Primitive::kPrimLong) {
1838 const uint64_t value = constant->AsLongConstant()->GetValueAsUint64();
1839
1840 ret = IsUint<8>(Low32Bits(value)) && IsUint<8>(High32Bits(value));
1841 } else {
1842 ret = IsUint<8>(CodeGenerator::GetInt32ValueOf(constant));
1843 }
1844
1845 return ret;
1846 }
1847
Arm8BitEncodableConstantOrRegister(HInstruction * constant)1848 static Location Arm8BitEncodableConstantOrRegister(HInstruction* constant) {
1849 DCHECK(!Primitive::IsFloatingPointType(constant->GetType()));
1850
1851 if (constant->IsConstant() && CanEncodeConstantAs8BitImmediate(constant->AsConstant())) {
1852 return Location::ConstantLocation(constant->AsConstant());
1853 }
1854
1855 return Location::RequiresRegister();
1856 }
1857
CanGenerateConditionalMove(const Location & out,const Location & src)1858 static bool CanGenerateConditionalMove(const Location& out, const Location& src) {
1859 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
1860 // we check that we are not dealing with floating-point output (there is no
1861 // 16-bit VMOV encoding).
1862 if (!out.IsRegister() && !out.IsRegisterPair()) {
1863 return false;
1864 }
1865
1866 // For constants, we also check that the output is in one or two low registers,
1867 // and that the constants fit in an 8-bit unsigned integer, so that a 16-bit
1868 // MOV encoding can be used.
1869 if (src.IsConstant()) {
1870 if (!CanEncodeConstantAs8BitImmediate(src.GetConstant())) {
1871 return false;
1872 }
1873
1874 if (out.IsRegister()) {
1875 if (!ArmAssembler::IsLowRegister(out.AsRegister<Register>())) {
1876 return false;
1877 }
1878 } else {
1879 DCHECK(out.IsRegisterPair());
1880
1881 if (!ArmAssembler::IsLowRegister(out.AsRegisterPairHigh<Register>())) {
1882 return false;
1883 }
1884 }
1885 }
1886
1887 return true;
1888 }
1889
1890 #undef __
1891 // NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
1892 #define __ down_cast<ArmAssembler*>(GetAssembler())-> // NOLINT
1893
GetFinalLabel(HInstruction * instruction,Label * final_label)1894 Label* CodeGeneratorARM::GetFinalLabel(HInstruction* instruction, Label* final_label) {
1895 DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck());
1896 DCHECK(!instruction->IsInvoke() || !instruction->GetLocations()->CanCall());
1897
1898 const HBasicBlock* const block = instruction->GetBlock();
1899 const HLoopInformation* const info = block->GetLoopInformation();
1900 HInstruction* const next = instruction->GetNext();
1901
1902 // Avoid a branch to a branch.
1903 if (next->IsGoto() && (info == nullptr ||
1904 !info->IsBackEdge(*block) ||
1905 !info->HasSuspendCheck())) {
1906 final_label = GetLabelOf(next->AsGoto()->GetSuccessor());
1907 }
1908
1909 return final_label;
1910 }
1911
DumpCoreRegister(std::ostream & stream,int reg) const1912 void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const {
1913 stream << Register(reg);
1914 }
1915
DumpFloatingPointRegister(std::ostream & stream,int reg) const1916 void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
1917 stream << SRegister(reg);
1918 }
1919
SaveCoreRegister(size_t stack_index,uint32_t reg_id)1920 size_t CodeGeneratorARM::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
1921 __ StoreToOffset(kStoreWord, static_cast<Register>(reg_id), SP, stack_index);
1922 return kArmWordSize;
1923 }
1924
RestoreCoreRegister(size_t stack_index,uint32_t reg_id)1925 size_t CodeGeneratorARM::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
1926 __ LoadFromOffset(kLoadWord, static_cast<Register>(reg_id), SP, stack_index);
1927 return kArmWordSize;
1928 }
1929
SaveFloatingPointRegister(size_t stack_index,uint32_t reg_id)1930 size_t CodeGeneratorARM::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
1931 __ StoreSToOffset(static_cast<SRegister>(reg_id), SP, stack_index);
1932 return kArmWordSize;
1933 }
1934
RestoreFloatingPointRegister(size_t stack_index,uint32_t reg_id)1935 size_t CodeGeneratorARM::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
1936 __ LoadSFromOffset(static_cast<SRegister>(reg_id), SP, stack_index);
1937 return kArmWordSize;
1938 }
1939
CodeGeneratorARM(HGraph * graph,const ArmInstructionSetFeatures & isa_features,const CompilerOptions & compiler_options,OptimizingCompilerStats * stats)1940 CodeGeneratorARM::CodeGeneratorARM(HGraph* graph,
1941 const ArmInstructionSetFeatures& isa_features,
1942 const CompilerOptions& compiler_options,
1943 OptimizingCompilerStats* stats)
1944 : CodeGenerator(graph,
1945 kNumberOfCoreRegisters,
1946 kNumberOfSRegisters,
1947 kNumberOfRegisterPairs,
1948 ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves),
1949 arraysize(kCoreCalleeSaves)),
1950 ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves),
1951 arraysize(kFpuCalleeSaves)),
1952 compiler_options,
1953 stats),
1954 block_labels_(nullptr),
1955 location_builder_(graph, this),
1956 instruction_visitor_(graph, this),
1957 move_resolver_(graph->GetArena(), this),
1958 assembler_(graph->GetArena()),
1959 isa_features_(isa_features),
1960 uint32_literals_(std::less<uint32_t>(),
1961 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1962 pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1963 boot_image_string_patches_(StringReferenceValueComparator(),
1964 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1965 pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1966 boot_image_type_patches_(TypeReferenceValueComparator(),
1967 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1968 pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1969 type_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1970 jit_string_patches_(StringReferenceValueComparator(),
1971 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1972 jit_class_patches_(TypeReferenceValueComparator(),
1973 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
1974 // Always save the LR register to mimic Quick.
1975 AddAllocatedRegister(Location::RegisterLocation(LR));
1976 }
1977
Finalize(CodeAllocator * allocator)1978 void CodeGeneratorARM::Finalize(CodeAllocator* allocator) {
1979 // Ensure that we fix up branches and literal loads and emit the literal pool.
1980 __ FinalizeCode();
1981
1982 // Adjust native pc offsets in stack maps.
1983 for (size_t i = 0, num = stack_map_stream_.GetNumberOfStackMaps(); i != num; ++i) {
1984 uint32_t old_position =
1985 stack_map_stream_.GetStackMap(i).native_pc_code_offset.Uint32Value(kThumb2);
1986 uint32_t new_position = __ GetAdjustedPosition(old_position);
1987 stack_map_stream_.SetStackMapNativePcOffset(i, new_position);
1988 }
1989 // Adjust pc offsets for the disassembly information.
1990 if (disasm_info_ != nullptr) {
1991 GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval();
1992 frame_entry_interval->start = __ GetAdjustedPosition(frame_entry_interval->start);
1993 frame_entry_interval->end = __ GetAdjustedPosition(frame_entry_interval->end);
1994 for (auto& it : *disasm_info_->GetInstructionIntervals()) {
1995 it.second.start = __ GetAdjustedPosition(it.second.start);
1996 it.second.end = __ GetAdjustedPosition(it.second.end);
1997 }
1998 for (auto& it : *disasm_info_->GetSlowPathIntervals()) {
1999 it.code_interval.start = __ GetAdjustedPosition(it.code_interval.start);
2000 it.code_interval.end = __ GetAdjustedPosition(it.code_interval.end);
2001 }
2002 }
2003
2004 CodeGenerator::Finalize(allocator);
2005 }
2006
SetupBlockedRegisters() const2007 void CodeGeneratorARM::SetupBlockedRegisters() const {
2008 // Stack register, LR and PC are always reserved.
2009 blocked_core_registers_[SP] = true;
2010 blocked_core_registers_[LR] = true;
2011 blocked_core_registers_[PC] = true;
2012
2013 // Reserve thread register.
2014 blocked_core_registers_[TR] = true;
2015
2016 // Reserve temp register.
2017 blocked_core_registers_[IP] = true;
2018
2019 if (GetGraph()->IsDebuggable()) {
2020 // Stubs do not save callee-save floating point registers. If the graph
2021 // is debuggable, we need to deal with these registers differently. For
2022 // now, just block them.
2023 for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
2024 blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
2025 }
2026 }
2027 }
2028
InstructionCodeGeneratorARM(HGraph * graph,CodeGeneratorARM * codegen)2029 InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen)
2030 : InstructionCodeGenerator(graph, codegen),
2031 assembler_(codegen->GetAssembler()),
2032 codegen_(codegen) {}
2033
ComputeSpillMask()2034 void CodeGeneratorARM::ComputeSpillMask() {
2035 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
2036 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
2037 // There is no easy instruction to restore just the PC on thumb2. We spill and
2038 // restore another arbitrary register.
2039 core_spill_mask_ |= (1 << kCoreAlwaysSpillRegister);
2040 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
2041 // We use vpush and vpop for saving and restoring floating point registers, which take
2042 // a SRegister and the number of registers to save/restore after that SRegister. We
2043 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
2044 // but in the range.
2045 if (fpu_spill_mask_ != 0) {
2046 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
2047 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
2048 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
2049 fpu_spill_mask_ |= (1 << i);
2050 }
2051 }
2052 }
2053
DWARFReg(Register reg)2054 static dwarf::Reg DWARFReg(Register reg) {
2055 return dwarf::Reg::ArmCore(static_cast<int>(reg));
2056 }
2057
DWARFReg(SRegister reg)2058 static dwarf::Reg DWARFReg(SRegister reg) {
2059 return dwarf::Reg::ArmFp(static_cast<int>(reg));
2060 }
2061
GenerateFrameEntry()2062 void CodeGeneratorARM::GenerateFrameEntry() {
2063 bool skip_overflow_check =
2064 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
2065 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
2066 __ Bind(&frame_entry_label_);
2067
2068 if (HasEmptyFrame()) {
2069 return;
2070 }
2071
2072 if (!skip_overflow_check) {
2073 __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
2074 __ LoadFromOffset(kLoadWord, IP, IP, 0);
2075 RecordPcInfo(nullptr, 0);
2076 }
2077
2078 __ PushList(core_spill_mask_);
2079 __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
2080 __ cfi().RelOffsetForMany(DWARFReg(kMethodRegisterArgument), 0, core_spill_mask_, kArmWordSize);
2081 if (fpu_spill_mask_ != 0) {
2082 SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_));
2083 __ vpushs(start_register, POPCOUNT(fpu_spill_mask_));
2084 __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
2085 __ cfi().RelOffsetForMany(DWARFReg(S0), 0, fpu_spill_mask_, kArmWordSize);
2086 }
2087
2088 if (GetGraph()->HasShouldDeoptimizeFlag()) {
2089 // Initialize should_deoptimize flag to 0.
2090 __ mov(IP, ShifterOperand(0));
2091 __ StoreToOffset(kStoreWord, IP, SP, -kShouldDeoptimizeFlagSize);
2092 }
2093
2094 int adjust = GetFrameSize() - FrameEntrySpillSize();
2095 __ AddConstant(SP, -adjust);
2096 __ cfi().AdjustCFAOffset(adjust);
2097
2098 // Save the current method if we need it. Note that we do not
2099 // do this in HCurrentMethod, as the instruction might have been removed
2100 // in the SSA graph.
2101 if (RequiresCurrentMethod()) {
2102 __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, 0);
2103 }
2104 }
2105
GenerateFrameExit()2106 void CodeGeneratorARM::GenerateFrameExit() {
2107 if (HasEmptyFrame()) {
2108 __ bx(LR);
2109 return;
2110 }
2111 __ cfi().RememberState();
2112 int adjust = GetFrameSize() - FrameEntrySpillSize();
2113 __ AddConstant(SP, adjust);
2114 __ cfi().AdjustCFAOffset(-adjust);
2115 if (fpu_spill_mask_ != 0) {
2116 SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_));
2117 __ vpops(start_register, POPCOUNT(fpu_spill_mask_));
2118 __ cfi().AdjustCFAOffset(-static_cast<int>(kArmPointerSize) * POPCOUNT(fpu_spill_mask_));
2119 __ cfi().RestoreMany(DWARFReg(SRegister(0)), fpu_spill_mask_);
2120 }
2121 // Pop LR into PC to return.
2122 DCHECK_NE(core_spill_mask_ & (1 << LR), 0U);
2123 uint32_t pop_mask = (core_spill_mask_ & (~(1 << LR))) | 1 << PC;
2124 __ PopList(pop_mask);
2125 __ cfi().RestoreState();
2126 __ cfi().DefCFAOffset(GetFrameSize());
2127 }
2128
Bind(HBasicBlock * block)2129 void CodeGeneratorARM::Bind(HBasicBlock* block) {
2130 Label* label = GetLabelOf(block);
2131 __ BindTrackedLabel(label);
2132 }
2133
GetNextLocation(Primitive::Type type)2134 Location InvokeDexCallingConventionVisitorARM::GetNextLocation(Primitive::Type type) {
2135 switch (type) {
2136 case Primitive::kPrimBoolean:
2137 case Primitive::kPrimByte:
2138 case Primitive::kPrimChar:
2139 case Primitive::kPrimShort:
2140 case Primitive::kPrimInt:
2141 case Primitive::kPrimNot: {
2142 uint32_t index = gp_index_++;
2143 uint32_t stack_index = stack_index_++;
2144 if (index < calling_convention.GetNumberOfRegisters()) {
2145 return Location::RegisterLocation(calling_convention.GetRegisterAt(index));
2146 } else {
2147 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2148 }
2149 }
2150
2151 case Primitive::kPrimLong: {
2152 uint32_t index = gp_index_;
2153 uint32_t stack_index = stack_index_;
2154 gp_index_ += 2;
2155 stack_index_ += 2;
2156 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2157 if (calling_convention.GetRegisterAt(index) == R1) {
2158 // Skip R1, and use R2_R3 instead.
2159 gp_index_++;
2160 index++;
2161 }
2162 }
2163 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2164 DCHECK_EQ(calling_convention.GetRegisterAt(index) + 1,
2165 calling_convention.GetRegisterAt(index + 1));
2166
2167 return Location::RegisterPairLocation(calling_convention.GetRegisterAt(index),
2168 calling_convention.GetRegisterAt(index + 1));
2169 } else {
2170 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2171 }
2172 }
2173
2174 case Primitive::kPrimFloat: {
2175 uint32_t stack_index = stack_index_++;
2176 if (float_index_ % 2 == 0) {
2177 float_index_ = std::max(double_index_, float_index_);
2178 }
2179 if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
2180 return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(float_index_++));
2181 } else {
2182 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2183 }
2184 }
2185
2186 case Primitive::kPrimDouble: {
2187 double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
2188 uint32_t stack_index = stack_index_;
2189 stack_index_ += 2;
2190 if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
2191 uint32_t index = double_index_;
2192 double_index_ += 2;
2193 Location result = Location::FpuRegisterPairLocation(
2194 calling_convention.GetFpuRegisterAt(index),
2195 calling_convention.GetFpuRegisterAt(index + 1));
2196 DCHECK(ExpectedPairLayout(result));
2197 return result;
2198 } else {
2199 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2200 }
2201 }
2202
2203 case Primitive::kPrimVoid:
2204 LOG(FATAL) << "Unexpected parameter type " << type;
2205 break;
2206 }
2207 return Location::NoLocation();
2208 }
2209
GetReturnLocation(Primitive::Type type) const2210 Location InvokeDexCallingConventionVisitorARM::GetReturnLocation(Primitive::Type type) const {
2211 switch (type) {
2212 case Primitive::kPrimBoolean:
2213 case Primitive::kPrimByte:
2214 case Primitive::kPrimChar:
2215 case Primitive::kPrimShort:
2216 case Primitive::kPrimInt:
2217 case Primitive::kPrimNot: {
2218 return Location::RegisterLocation(R0);
2219 }
2220
2221 case Primitive::kPrimFloat: {
2222 return Location::FpuRegisterLocation(S0);
2223 }
2224
2225 case Primitive::kPrimLong: {
2226 return Location::RegisterPairLocation(R0, R1);
2227 }
2228
2229 case Primitive::kPrimDouble: {
2230 return Location::FpuRegisterPairLocation(S0, S1);
2231 }
2232
2233 case Primitive::kPrimVoid:
2234 return Location::NoLocation();
2235 }
2236
2237 UNREACHABLE();
2238 }
2239
GetMethodLocation() const2240 Location InvokeDexCallingConventionVisitorARM::GetMethodLocation() const {
2241 return Location::RegisterLocation(kMethodRegisterArgument);
2242 }
2243
Move32(Location destination,Location source)2244 void CodeGeneratorARM::Move32(Location destination, Location source) {
2245 if (source.Equals(destination)) {
2246 return;
2247 }
2248 if (destination.IsRegister()) {
2249 if (source.IsRegister()) {
2250 __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>());
2251 } else if (source.IsFpuRegister()) {
2252 __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>());
2253 } else {
2254 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), SP, source.GetStackIndex());
2255 }
2256 } else if (destination.IsFpuRegister()) {
2257 if (source.IsRegister()) {
2258 __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>());
2259 } else if (source.IsFpuRegister()) {
2260 __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>());
2261 } else {
2262 __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex());
2263 }
2264 } else {
2265 DCHECK(destination.IsStackSlot()) << destination;
2266 if (source.IsRegister()) {
2267 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), SP, destination.GetStackIndex());
2268 } else if (source.IsFpuRegister()) {
2269 __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex());
2270 } else {
2271 DCHECK(source.IsStackSlot()) << source;
2272 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex());
2273 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
2274 }
2275 }
2276 }
2277
Move64(Location destination,Location source)2278 void CodeGeneratorARM::Move64(Location destination, Location source) {
2279 if (source.Equals(destination)) {
2280 return;
2281 }
2282 if (destination.IsRegisterPair()) {
2283 if (source.IsRegisterPair()) {
2284 EmitParallelMoves(
2285 Location::RegisterLocation(source.AsRegisterPairHigh<Register>()),
2286 Location::RegisterLocation(destination.AsRegisterPairHigh<Register>()),
2287 Primitive::kPrimInt,
2288 Location::RegisterLocation(source.AsRegisterPairLow<Register>()),
2289 Location::RegisterLocation(destination.AsRegisterPairLow<Register>()),
2290 Primitive::kPrimInt);
2291 } else if (source.IsFpuRegister()) {
2292 UNIMPLEMENTED(FATAL);
2293 } else if (source.IsFpuRegisterPair()) {
2294 __ vmovrrd(destination.AsRegisterPairLow<Register>(),
2295 destination.AsRegisterPairHigh<Register>(),
2296 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
2297 } else {
2298 DCHECK(source.IsDoubleStackSlot());
2299 DCHECK(ExpectedPairLayout(destination));
2300 __ LoadFromOffset(kLoadWordPair, destination.AsRegisterPairLow<Register>(),
2301 SP, source.GetStackIndex());
2302 }
2303 } else if (destination.IsFpuRegisterPair()) {
2304 if (source.IsDoubleStackSlot()) {
2305 __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
2306 SP,
2307 source.GetStackIndex());
2308 } else if (source.IsRegisterPair()) {
2309 __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
2310 source.AsRegisterPairLow<Register>(),
2311 source.AsRegisterPairHigh<Register>());
2312 } else {
2313 UNIMPLEMENTED(FATAL);
2314 }
2315 } else {
2316 DCHECK(destination.IsDoubleStackSlot());
2317 if (source.IsRegisterPair()) {
2318 // No conflict possible, so just do the moves.
2319 if (source.AsRegisterPairLow<Register>() == R1) {
2320 DCHECK_EQ(source.AsRegisterPairHigh<Register>(), R2);
2321 __ StoreToOffset(kStoreWord, R1, SP, destination.GetStackIndex());
2322 __ StoreToOffset(kStoreWord, R2, SP, destination.GetHighStackIndex(kArmWordSize));
2323 } else {
2324 __ StoreToOffset(kStoreWordPair, source.AsRegisterPairLow<Register>(),
2325 SP, destination.GetStackIndex());
2326 }
2327 } else if (source.IsFpuRegisterPair()) {
2328 __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
2329 SP,
2330 destination.GetStackIndex());
2331 } else {
2332 DCHECK(source.IsDoubleStackSlot());
2333 EmitParallelMoves(
2334 Location::StackSlot(source.GetStackIndex()),
2335 Location::StackSlot(destination.GetStackIndex()),
2336 Primitive::kPrimInt,
2337 Location::StackSlot(source.GetHighStackIndex(kArmWordSize)),
2338 Location::StackSlot(destination.GetHighStackIndex(kArmWordSize)),
2339 Primitive::kPrimInt);
2340 }
2341 }
2342 }
2343
MoveConstant(Location location,int32_t value)2344 void CodeGeneratorARM::MoveConstant(Location location, int32_t value) {
2345 DCHECK(location.IsRegister());
2346 __ LoadImmediate(location.AsRegister<Register>(), value);
2347 }
2348
MoveLocation(Location dst,Location src,Primitive::Type dst_type)2349 void CodeGeneratorARM::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
2350 HParallelMove move(GetGraph()->GetArena());
2351 move.AddMove(src, dst, dst_type, nullptr);
2352 GetMoveResolver()->EmitNativeCode(&move);
2353 }
2354
AddLocationAsTemp(Location location,LocationSummary * locations)2355 void CodeGeneratorARM::AddLocationAsTemp(Location location, LocationSummary* locations) {
2356 if (location.IsRegister()) {
2357 locations->AddTemp(location);
2358 } else if (location.IsRegisterPair()) {
2359 locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>()));
2360 locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>()));
2361 } else {
2362 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
2363 }
2364 }
2365
InvokeRuntime(QuickEntrypointEnum entrypoint,HInstruction * instruction,uint32_t dex_pc,SlowPathCode * slow_path)2366 void CodeGeneratorARM::InvokeRuntime(QuickEntrypointEnum entrypoint,
2367 HInstruction* instruction,
2368 uint32_t dex_pc,
2369 SlowPathCode* slow_path) {
2370 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
2371 GenerateInvokeRuntime(GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value());
2372 if (EntrypointRequiresStackMap(entrypoint)) {
2373 RecordPcInfo(instruction, dex_pc, slow_path);
2374 }
2375 }
2376
InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,HInstruction * instruction,SlowPathCode * slow_path)2377 void CodeGeneratorARM::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
2378 HInstruction* instruction,
2379 SlowPathCode* slow_path) {
2380 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
2381 GenerateInvokeRuntime(entry_point_offset);
2382 }
2383
GenerateInvokeRuntime(int32_t entry_point_offset)2384 void CodeGeneratorARM::GenerateInvokeRuntime(int32_t entry_point_offset) {
2385 __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset);
2386 __ blx(LR);
2387 }
2388
HandleGoto(HInstruction * got,HBasicBlock * successor)2389 void InstructionCodeGeneratorARM::HandleGoto(HInstruction* got, HBasicBlock* successor) {
2390 DCHECK(!successor->IsExitBlock());
2391
2392 HBasicBlock* block = got->GetBlock();
2393 HInstruction* previous = got->GetPrevious();
2394
2395 HLoopInformation* info = block->GetLoopInformation();
2396 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
2397 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
2398 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
2399 return;
2400 }
2401
2402 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
2403 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
2404 }
2405 if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
2406 __ b(codegen_->GetLabelOf(successor));
2407 }
2408 }
2409
VisitGoto(HGoto * got)2410 void LocationsBuilderARM::VisitGoto(HGoto* got) {
2411 got->SetLocations(nullptr);
2412 }
2413
VisitGoto(HGoto * got)2414 void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) {
2415 HandleGoto(got, got->GetSuccessor());
2416 }
2417
VisitTryBoundary(HTryBoundary * try_boundary)2418 void LocationsBuilderARM::VisitTryBoundary(HTryBoundary* try_boundary) {
2419 try_boundary->SetLocations(nullptr);
2420 }
2421
VisitTryBoundary(HTryBoundary * try_boundary)2422 void InstructionCodeGeneratorARM::VisitTryBoundary(HTryBoundary* try_boundary) {
2423 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
2424 if (!successor->IsExitBlock()) {
2425 HandleGoto(try_boundary, successor);
2426 }
2427 }
2428
VisitExit(HExit * exit)2429 void LocationsBuilderARM::VisitExit(HExit* exit) {
2430 exit->SetLocations(nullptr);
2431 }
2432
VisitExit(HExit * exit ATTRIBUTE_UNUSED)2433 void InstructionCodeGeneratorARM::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
2434 }
2435
GenerateLongComparesAndJumps(HCondition * cond,Label * true_label,Label * false_label)2436 void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond,
2437 Label* true_label,
2438 Label* false_label) {
2439 LocationSummary* locations = cond->GetLocations();
2440 Location left = locations->InAt(0);
2441 Location right = locations->InAt(1);
2442 IfCondition if_cond = cond->GetCondition();
2443
2444 Register left_high = left.AsRegisterPairHigh<Register>();
2445 Register left_low = left.AsRegisterPairLow<Register>();
2446 IfCondition true_high_cond = if_cond;
2447 IfCondition false_high_cond = cond->GetOppositeCondition();
2448 Condition final_condition = ARMUnsignedCondition(if_cond); // unsigned on lower part
2449
2450 // Set the conditions for the test, remembering that == needs to be
2451 // decided using the low words.
2452 switch (if_cond) {
2453 case kCondEQ:
2454 case kCondNE:
2455 // Nothing to do.
2456 break;
2457 case kCondLT:
2458 false_high_cond = kCondGT;
2459 break;
2460 case kCondLE:
2461 true_high_cond = kCondLT;
2462 break;
2463 case kCondGT:
2464 false_high_cond = kCondLT;
2465 break;
2466 case kCondGE:
2467 true_high_cond = kCondGT;
2468 break;
2469 case kCondB:
2470 false_high_cond = kCondA;
2471 break;
2472 case kCondBE:
2473 true_high_cond = kCondB;
2474 break;
2475 case kCondA:
2476 false_high_cond = kCondB;
2477 break;
2478 case kCondAE:
2479 true_high_cond = kCondA;
2480 break;
2481 }
2482 if (right.IsConstant()) {
2483 int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
2484 int32_t val_low = Low32Bits(value);
2485 int32_t val_high = High32Bits(value);
2486
2487 __ CmpConstant(left_high, val_high);
2488 if (if_cond == kCondNE) {
2489 __ b(true_label, ARMCondition(true_high_cond));
2490 } else if (if_cond == kCondEQ) {
2491 __ b(false_label, ARMCondition(false_high_cond));
2492 } else {
2493 __ b(true_label, ARMCondition(true_high_cond));
2494 __ b(false_label, ARMCondition(false_high_cond));
2495 }
2496 // Must be equal high, so compare the lows.
2497 __ CmpConstant(left_low, val_low);
2498 } else {
2499 Register right_high = right.AsRegisterPairHigh<Register>();
2500 Register right_low = right.AsRegisterPairLow<Register>();
2501
2502 __ cmp(left_high, ShifterOperand(right_high));
2503 if (if_cond == kCondNE) {
2504 __ b(true_label, ARMCondition(true_high_cond));
2505 } else if (if_cond == kCondEQ) {
2506 __ b(false_label, ARMCondition(false_high_cond));
2507 } else {
2508 __ b(true_label, ARMCondition(true_high_cond));
2509 __ b(false_label, ARMCondition(false_high_cond));
2510 }
2511 // Must be equal high, so compare the lows.
2512 __ cmp(left_low, ShifterOperand(right_low));
2513 }
2514 // The last comparison might be unsigned.
2515 // TODO: optimize cases where this is always true/false
2516 __ b(true_label, final_condition);
2517 }
2518
GenerateCompareTestAndBranch(HCondition * condition,Label * true_target_in,Label * false_target_in)2519 void InstructionCodeGeneratorARM::GenerateCompareTestAndBranch(HCondition* condition,
2520 Label* true_target_in,
2521 Label* false_target_in) {
2522 if (CanGenerateTest(condition, codegen_->GetAssembler())) {
2523 Label* non_fallthrough_target;
2524 bool invert;
2525 bool emit_both_branches;
2526
2527 if (true_target_in == nullptr) {
2528 // The true target is fallthrough.
2529 DCHECK(false_target_in != nullptr);
2530 non_fallthrough_target = false_target_in;
2531 invert = true;
2532 emit_both_branches = false;
2533 } else {
2534 // Either the false target is fallthrough, or there is no fallthrough
2535 // and both branches must be emitted.
2536 non_fallthrough_target = true_target_in;
2537 invert = false;
2538 emit_both_branches = (false_target_in != nullptr);
2539 }
2540
2541 const auto cond = GenerateTest(condition, invert, codegen_);
2542
2543 __ b(non_fallthrough_target, cond.first);
2544
2545 if (emit_both_branches) {
2546 // No target falls through, we need to branch.
2547 __ b(false_target_in);
2548 }
2549
2550 return;
2551 }
2552
2553 // Generated branching requires both targets to be explicit. If either of the
2554 // targets is nullptr (fallthrough) use and bind `fallthrough_target` instead.
2555 Label fallthrough_target;
2556 Label* true_target = true_target_in == nullptr ? &fallthrough_target : true_target_in;
2557 Label* false_target = false_target_in == nullptr ? &fallthrough_target : false_target_in;
2558
2559 DCHECK_EQ(condition->InputAt(0)->GetType(), Primitive::kPrimLong);
2560 GenerateLongComparesAndJumps(condition, true_target, false_target);
2561
2562 if (false_target != &fallthrough_target) {
2563 __ b(false_target);
2564 }
2565
2566 if (fallthrough_target.IsLinked()) {
2567 __ Bind(&fallthrough_target);
2568 }
2569 }
2570
GenerateTestAndBranch(HInstruction * instruction,size_t condition_input_index,Label * true_target,Label * false_target)2571 void InstructionCodeGeneratorARM::GenerateTestAndBranch(HInstruction* instruction,
2572 size_t condition_input_index,
2573 Label* true_target,
2574 Label* false_target) {
2575 HInstruction* cond = instruction->InputAt(condition_input_index);
2576
2577 if (true_target == nullptr && false_target == nullptr) {
2578 // Nothing to do. The code always falls through.
2579 return;
2580 } else if (cond->IsIntConstant()) {
2581 // Constant condition, statically compared against "true" (integer value 1).
2582 if (cond->AsIntConstant()->IsTrue()) {
2583 if (true_target != nullptr) {
2584 __ b(true_target);
2585 }
2586 } else {
2587 DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
2588 if (false_target != nullptr) {
2589 __ b(false_target);
2590 }
2591 }
2592 return;
2593 }
2594
2595 // The following code generates these patterns:
2596 // (1) true_target == nullptr && false_target != nullptr
2597 // - opposite condition true => branch to false_target
2598 // (2) true_target != nullptr && false_target == nullptr
2599 // - condition true => branch to true_target
2600 // (3) true_target != nullptr && false_target != nullptr
2601 // - condition true => branch to true_target
2602 // - branch to false_target
2603 if (IsBooleanValueOrMaterializedCondition(cond)) {
2604 // Condition has been materialized, compare the output to 0.
2605 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
2606 DCHECK(cond_val.IsRegister());
2607 if (true_target == nullptr) {
2608 __ CompareAndBranchIfZero(cond_val.AsRegister<Register>(), false_target);
2609 } else {
2610 __ CompareAndBranchIfNonZero(cond_val.AsRegister<Register>(), true_target);
2611 }
2612 } else {
2613 // Condition has not been materialized. Use its inputs as the comparison and
2614 // its condition as the branch condition.
2615 HCondition* condition = cond->AsCondition();
2616
2617 // If this is a long or FP comparison that has been folded into
2618 // the HCondition, generate the comparison directly.
2619 Primitive::Type type = condition->InputAt(0)->GetType();
2620 if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
2621 GenerateCompareTestAndBranch(condition, true_target, false_target);
2622 return;
2623 }
2624
2625 Label* non_fallthrough_target;
2626 Condition arm_cond;
2627 LocationSummary* locations = cond->GetLocations();
2628 DCHECK(locations->InAt(0).IsRegister());
2629 Register left = locations->InAt(0).AsRegister<Register>();
2630 Location right = locations->InAt(1);
2631
2632 if (true_target == nullptr) {
2633 arm_cond = ARMCondition(condition->GetOppositeCondition());
2634 non_fallthrough_target = false_target;
2635 } else {
2636 arm_cond = ARMCondition(condition->GetCondition());
2637 non_fallthrough_target = true_target;
2638 }
2639
2640 if (right.IsConstant() && (arm_cond == NE || arm_cond == EQ) &&
2641 CodeGenerator::GetInt32ValueOf(right.GetConstant()) == 0) {
2642 if (arm_cond == EQ) {
2643 __ CompareAndBranchIfZero(left, non_fallthrough_target);
2644 } else {
2645 DCHECK_EQ(arm_cond, NE);
2646 __ CompareAndBranchIfNonZero(left, non_fallthrough_target);
2647 }
2648 } else {
2649 if (right.IsRegister()) {
2650 __ cmp(left, ShifterOperand(right.AsRegister<Register>()));
2651 } else {
2652 DCHECK(right.IsConstant());
2653 __ CmpConstant(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
2654 }
2655
2656 __ b(non_fallthrough_target, arm_cond);
2657 }
2658 }
2659
2660 // If neither branch falls through (case 3), the conditional branch to `true_target`
2661 // was already emitted (case 2) and we need to emit a jump to `false_target`.
2662 if (true_target != nullptr && false_target != nullptr) {
2663 __ b(false_target);
2664 }
2665 }
2666
VisitIf(HIf * if_instr)2667 void LocationsBuilderARM::VisitIf(HIf* if_instr) {
2668 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
2669 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
2670 locations->SetInAt(0, Location::RequiresRegister());
2671 }
2672 }
2673
VisitIf(HIf * if_instr)2674 void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
2675 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
2676 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
2677 Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
2678 nullptr : codegen_->GetLabelOf(true_successor);
2679 Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
2680 nullptr : codegen_->GetLabelOf(false_successor);
2681 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
2682 }
2683
VisitDeoptimize(HDeoptimize * deoptimize)2684 void LocationsBuilderARM::VisitDeoptimize(HDeoptimize* deoptimize) {
2685 LocationSummary* locations = new (GetGraph()->GetArena())
2686 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
2687 InvokeRuntimeCallingConvention calling_convention;
2688 RegisterSet caller_saves = RegisterSet::Empty();
2689 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
2690 locations->SetCustomSlowPathCallerSaves(caller_saves);
2691 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
2692 locations->SetInAt(0, Location::RequiresRegister());
2693 }
2694 }
2695
VisitDeoptimize(HDeoptimize * deoptimize)2696 void InstructionCodeGeneratorARM::VisitDeoptimize(HDeoptimize* deoptimize) {
2697 SlowPathCodeARM* slow_path = deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARM>(deoptimize);
2698 GenerateTestAndBranch(deoptimize,
2699 /* condition_input_index */ 0,
2700 slow_path->GetEntryLabel(),
2701 /* false_target */ nullptr);
2702 }
2703
VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag * flag)2704 void LocationsBuilderARM::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
2705 LocationSummary* locations = new (GetGraph()->GetArena())
2706 LocationSummary(flag, LocationSummary::kNoCall);
2707 locations->SetOut(Location::RequiresRegister());
2708 }
2709
VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag * flag)2710 void InstructionCodeGeneratorARM::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
2711 __ LoadFromOffset(kLoadWord,
2712 flag->GetLocations()->Out().AsRegister<Register>(),
2713 SP,
2714 codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
2715 }
2716
VisitSelect(HSelect * select)2717 void LocationsBuilderARM::VisitSelect(HSelect* select) {
2718 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
2719 const bool is_floating_point = Primitive::IsFloatingPointType(select->GetType());
2720
2721 if (is_floating_point) {
2722 locations->SetInAt(0, Location::RequiresFpuRegister());
2723 locations->SetInAt(1, Location::FpuRegisterOrConstant(select->GetTrueValue()));
2724 } else {
2725 locations->SetInAt(0, Location::RequiresRegister());
2726 locations->SetInAt(1, Arm8BitEncodableConstantOrRegister(select->GetTrueValue()));
2727 }
2728
2729 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
2730 locations->SetInAt(2, Location::RegisterOrConstant(select->GetCondition()));
2731 // The code generator handles overlap with the values, but not with the condition.
2732 locations->SetOut(Location::SameAsFirstInput());
2733 } else if (is_floating_point) {
2734 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2735 } else {
2736 if (!locations->InAt(1).IsConstant()) {
2737 locations->SetInAt(0, Arm8BitEncodableConstantOrRegister(select->GetFalseValue()));
2738 }
2739
2740 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2741 }
2742 }
2743
VisitSelect(HSelect * select)2744 void InstructionCodeGeneratorARM::VisitSelect(HSelect* select) {
2745 HInstruction* const condition = select->GetCondition();
2746 const LocationSummary* const locations = select->GetLocations();
2747 const Primitive::Type type = select->GetType();
2748 const Location first = locations->InAt(0);
2749 const Location out = locations->Out();
2750 const Location second = locations->InAt(1);
2751 Location src;
2752
2753 if (condition->IsIntConstant()) {
2754 if (condition->AsIntConstant()->IsFalse()) {
2755 src = first;
2756 } else {
2757 src = second;
2758 }
2759
2760 codegen_->MoveLocation(out, src, type);
2761 return;
2762 }
2763
2764 if (!Primitive::IsFloatingPointType(type) &&
2765 (IsBooleanValueOrMaterializedCondition(condition) ||
2766 CanGenerateTest(condition->AsCondition(), codegen_->GetAssembler()))) {
2767 bool invert = false;
2768
2769 if (out.Equals(second)) {
2770 src = first;
2771 invert = true;
2772 } else if (out.Equals(first)) {
2773 src = second;
2774 } else if (second.IsConstant()) {
2775 DCHECK(CanEncodeConstantAs8BitImmediate(second.GetConstant()));
2776 src = second;
2777 } else if (first.IsConstant()) {
2778 DCHECK(CanEncodeConstantAs8BitImmediate(first.GetConstant()));
2779 src = first;
2780 invert = true;
2781 } else {
2782 src = second;
2783 }
2784
2785 if (CanGenerateConditionalMove(out, src)) {
2786 if (!out.Equals(first) && !out.Equals(second)) {
2787 codegen_->MoveLocation(out, src.Equals(first) ? second : first, type);
2788 }
2789
2790 std::pair<Condition, Condition> cond;
2791
2792 if (IsBooleanValueOrMaterializedCondition(condition)) {
2793 __ CmpConstant(locations->InAt(2).AsRegister<Register>(), 0);
2794 cond = invert ? std::make_pair(EQ, NE) : std::make_pair(NE, EQ);
2795 } else {
2796 cond = GenerateTest(condition->AsCondition(), invert, codegen_);
2797 }
2798
2799 if (out.IsRegister()) {
2800 ShifterOperand operand;
2801
2802 if (src.IsConstant()) {
2803 operand = ShifterOperand(CodeGenerator::GetInt32ValueOf(src.GetConstant()));
2804 } else {
2805 DCHECK(src.IsRegister());
2806 operand = ShifterOperand(src.AsRegister<Register>());
2807 }
2808
2809 __ it(cond.first);
2810 __ mov(out.AsRegister<Register>(), operand, cond.first);
2811 } else {
2812 DCHECK(out.IsRegisterPair());
2813
2814 ShifterOperand operand_high;
2815 ShifterOperand operand_low;
2816
2817 if (src.IsConstant()) {
2818 const int64_t value = src.GetConstant()->AsLongConstant()->GetValue();
2819
2820 operand_high = ShifterOperand(High32Bits(value));
2821 operand_low = ShifterOperand(Low32Bits(value));
2822 } else {
2823 DCHECK(src.IsRegisterPair());
2824 operand_high = ShifterOperand(src.AsRegisterPairHigh<Register>());
2825 operand_low = ShifterOperand(src.AsRegisterPairLow<Register>());
2826 }
2827
2828 __ it(cond.first);
2829 __ mov(out.AsRegisterPairLow<Register>(), operand_low, cond.first);
2830 __ it(cond.first);
2831 __ mov(out.AsRegisterPairHigh<Register>(), operand_high, cond.first);
2832 }
2833
2834 return;
2835 }
2836 }
2837
2838 Label* false_target = nullptr;
2839 Label* true_target = nullptr;
2840 Label select_end;
2841 Label* target = codegen_->GetFinalLabel(select, &select_end);
2842
2843 if (out.Equals(second)) {
2844 true_target = target;
2845 src = first;
2846 } else {
2847 false_target = target;
2848 src = second;
2849
2850 if (!out.Equals(first)) {
2851 codegen_->MoveLocation(out, first, type);
2852 }
2853 }
2854
2855 GenerateTestAndBranch(select, 2, true_target, false_target);
2856 codegen_->MoveLocation(out, src, type);
2857
2858 if (select_end.IsLinked()) {
2859 __ Bind(&select_end);
2860 }
2861 }
2862
VisitNativeDebugInfo(HNativeDebugInfo * info)2863 void LocationsBuilderARM::VisitNativeDebugInfo(HNativeDebugInfo* info) {
2864 new (GetGraph()->GetArena()) LocationSummary(info);
2865 }
2866
VisitNativeDebugInfo(HNativeDebugInfo *)2867 void InstructionCodeGeneratorARM::VisitNativeDebugInfo(HNativeDebugInfo*) {
2868 // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
2869 }
2870
GenerateNop()2871 void CodeGeneratorARM::GenerateNop() {
2872 __ nop();
2873 }
2874
HandleCondition(HCondition * cond)2875 void LocationsBuilderARM::HandleCondition(HCondition* cond) {
2876 LocationSummary* locations =
2877 new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
2878 // Handle the long/FP comparisons made in instruction simplification.
2879 switch (cond->InputAt(0)->GetType()) {
2880 case Primitive::kPrimLong:
2881 locations->SetInAt(0, Location::RequiresRegister());
2882 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
2883 if (!cond->IsEmittedAtUseSite()) {
2884 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2885 }
2886 break;
2887
2888 case Primitive::kPrimFloat:
2889 case Primitive::kPrimDouble:
2890 locations->SetInAt(0, Location::RequiresFpuRegister());
2891 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(cond->InputAt(1)));
2892 if (!cond->IsEmittedAtUseSite()) {
2893 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2894 }
2895 break;
2896
2897 default:
2898 locations->SetInAt(0, Location::RequiresRegister());
2899 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
2900 if (!cond->IsEmittedAtUseSite()) {
2901 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2902 }
2903 }
2904 }
2905
HandleCondition(HCondition * cond)2906 void InstructionCodeGeneratorARM::HandleCondition(HCondition* cond) {
2907 if (cond->IsEmittedAtUseSite()) {
2908 return;
2909 }
2910
2911 const Register out = cond->GetLocations()->Out().AsRegister<Register>();
2912
2913 if (ArmAssembler::IsLowRegister(out) && CanGenerateTest(cond, codegen_->GetAssembler())) {
2914 const auto condition = GenerateTest(cond, false, codegen_);
2915
2916 __ it(condition.first);
2917 __ mov(out, ShifterOperand(1), condition.first);
2918 __ it(condition.second);
2919 __ mov(out, ShifterOperand(0), condition.second);
2920 return;
2921 }
2922
2923 // Convert the jumps into the result.
2924 Label done_label;
2925 Label* const final_label = codegen_->GetFinalLabel(cond, &done_label);
2926
2927 if (cond->InputAt(0)->GetType() == Primitive::kPrimLong) {
2928 Label true_label, false_label;
2929
2930 GenerateLongComparesAndJumps(cond, &true_label, &false_label);
2931
2932 // False case: result = 0.
2933 __ Bind(&false_label);
2934 __ LoadImmediate(out, 0);
2935 __ b(final_label);
2936
2937 // True case: result = 1.
2938 __ Bind(&true_label);
2939 __ LoadImmediate(out, 1);
2940 } else {
2941 DCHECK(CanGenerateTest(cond, codegen_->GetAssembler()));
2942
2943 const auto condition = GenerateTest(cond, false, codegen_);
2944
2945 __ mov(out, ShifterOperand(0), AL, kCcKeep);
2946 __ b(final_label, condition.second);
2947 __ LoadImmediate(out, 1);
2948 }
2949
2950 if (done_label.IsLinked()) {
2951 __ Bind(&done_label);
2952 }
2953 }
2954
VisitEqual(HEqual * comp)2955 void LocationsBuilderARM::VisitEqual(HEqual* comp) {
2956 HandleCondition(comp);
2957 }
2958
VisitEqual(HEqual * comp)2959 void InstructionCodeGeneratorARM::VisitEqual(HEqual* comp) {
2960 HandleCondition(comp);
2961 }
2962
VisitNotEqual(HNotEqual * comp)2963 void LocationsBuilderARM::VisitNotEqual(HNotEqual* comp) {
2964 HandleCondition(comp);
2965 }
2966
VisitNotEqual(HNotEqual * comp)2967 void InstructionCodeGeneratorARM::VisitNotEqual(HNotEqual* comp) {
2968 HandleCondition(comp);
2969 }
2970
VisitLessThan(HLessThan * comp)2971 void LocationsBuilderARM::VisitLessThan(HLessThan* comp) {
2972 HandleCondition(comp);
2973 }
2974
VisitLessThan(HLessThan * comp)2975 void InstructionCodeGeneratorARM::VisitLessThan(HLessThan* comp) {
2976 HandleCondition(comp);
2977 }
2978
VisitLessThanOrEqual(HLessThanOrEqual * comp)2979 void LocationsBuilderARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
2980 HandleCondition(comp);
2981 }
2982
VisitLessThanOrEqual(HLessThanOrEqual * comp)2983 void InstructionCodeGeneratorARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
2984 HandleCondition(comp);
2985 }
2986
VisitGreaterThan(HGreaterThan * comp)2987 void LocationsBuilderARM::VisitGreaterThan(HGreaterThan* comp) {
2988 HandleCondition(comp);
2989 }
2990
VisitGreaterThan(HGreaterThan * comp)2991 void InstructionCodeGeneratorARM::VisitGreaterThan(HGreaterThan* comp) {
2992 HandleCondition(comp);
2993 }
2994
VisitGreaterThanOrEqual(HGreaterThanOrEqual * comp)2995 void LocationsBuilderARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
2996 HandleCondition(comp);
2997 }
2998
VisitGreaterThanOrEqual(HGreaterThanOrEqual * comp)2999 void InstructionCodeGeneratorARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3000 HandleCondition(comp);
3001 }
3002
VisitBelow(HBelow * comp)3003 void LocationsBuilderARM::VisitBelow(HBelow* comp) {
3004 HandleCondition(comp);
3005 }
3006
VisitBelow(HBelow * comp)3007 void InstructionCodeGeneratorARM::VisitBelow(HBelow* comp) {
3008 HandleCondition(comp);
3009 }
3010
VisitBelowOrEqual(HBelowOrEqual * comp)3011 void LocationsBuilderARM::VisitBelowOrEqual(HBelowOrEqual* comp) {
3012 HandleCondition(comp);
3013 }
3014
VisitBelowOrEqual(HBelowOrEqual * comp)3015 void InstructionCodeGeneratorARM::VisitBelowOrEqual(HBelowOrEqual* comp) {
3016 HandleCondition(comp);
3017 }
3018
VisitAbove(HAbove * comp)3019 void LocationsBuilderARM::VisitAbove(HAbove* comp) {
3020 HandleCondition(comp);
3021 }
3022
VisitAbove(HAbove * comp)3023 void InstructionCodeGeneratorARM::VisitAbove(HAbove* comp) {
3024 HandleCondition(comp);
3025 }
3026
VisitAboveOrEqual(HAboveOrEqual * comp)3027 void LocationsBuilderARM::VisitAboveOrEqual(HAboveOrEqual* comp) {
3028 HandleCondition(comp);
3029 }
3030
VisitAboveOrEqual(HAboveOrEqual * comp)3031 void InstructionCodeGeneratorARM::VisitAboveOrEqual(HAboveOrEqual* comp) {
3032 HandleCondition(comp);
3033 }
3034
VisitIntConstant(HIntConstant * constant)3035 void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) {
3036 LocationSummary* locations =
3037 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
3038 locations->SetOut(Location::ConstantLocation(constant));
3039 }
3040
VisitIntConstant(HIntConstant * constant ATTRIBUTE_UNUSED)3041 void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
3042 // Will be generated at use site.
3043 }
3044
VisitNullConstant(HNullConstant * constant)3045 void LocationsBuilderARM::VisitNullConstant(HNullConstant* constant) {
3046 LocationSummary* locations =
3047 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
3048 locations->SetOut(Location::ConstantLocation(constant));
3049 }
3050
VisitNullConstant(HNullConstant * constant ATTRIBUTE_UNUSED)3051 void InstructionCodeGeneratorARM::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
3052 // Will be generated at use site.
3053 }
3054
VisitLongConstant(HLongConstant * constant)3055 void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) {
3056 LocationSummary* locations =
3057 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
3058 locations->SetOut(Location::ConstantLocation(constant));
3059 }
3060
VisitLongConstant(HLongConstant * constant ATTRIBUTE_UNUSED)3061 void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
3062 // Will be generated at use site.
3063 }
3064
VisitFloatConstant(HFloatConstant * constant)3065 void LocationsBuilderARM::VisitFloatConstant(HFloatConstant* constant) {
3066 LocationSummary* locations =
3067 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
3068 locations->SetOut(Location::ConstantLocation(constant));
3069 }
3070
VisitFloatConstant(HFloatConstant * constant ATTRIBUTE_UNUSED)3071 void InstructionCodeGeneratorARM::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) {
3072 // Will be generated at use site.
3073 }
3074
VisitDoubleConstant(HDoubleConstant * constant)3075 void LocationsBuilderARM::VisitDoubleConstant(HDoubleConstant* constant) {
3076 LocationSummary* locations =
3077 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
3078 locations->SetOut(Location::ConstantLocation(constant));
3079 }
3080
VisitDoubleConstant(HDoubleConstant * constant ATTRIBUTE_UNUSED)3081 void InstructionCodeGeneratorARM::VisitDoubleConstant(HDoubleConstant* constant ATTRIBUTE_UNUSED) {
3082 // Will be generated at use site.
3083 }
3084
VisitMemoryBarrier(HMemoryBarrier * memory_barrier)3085 void LocationsBuilderARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3086 memory_barrier->SetLocations(nullptr);
3087 }
3088
VisitMemoryBarrier(HMemoryBarrier * memory_barrier)3089 void InstructionCodeGeneratorARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3090 codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
3091 }
3092
VisitReturnVoid(HReturnVoid * ret)3093 void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) {
3094 ret->SetLocations(nullptr);
3095 }
3096
VisitReturnVoid(HReturnVoid * ret ATTRIBUTE_UNUSED)3097 void InstructionCodeGeneratorARM::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
3098 codegen_->GenerateFrameExit();
3099 }
3100
VisitReturn(HReturn * ret)3101 void LocationsBuilderARM::VisitReturn(HReturn* ret) {
3102 LocationSummary* locations =
3103 new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
3104 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
3105 }
3106
VisitReturn(HReturn * ret ATTRIBUTE_UNUSED)3107 void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
3108 codegen_->GenerateFrameExit();
3109 }
3110
VisitInvokeUnresolved(HInvokeUnresolved * invoke)3111 void LocationsBuilderARM::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3112 // The trampoline uses the same calling convention as dex calling conventions,
3113 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
3114 // the method_idx.
3115 HandleInvoke(invoke);
3116 }
3117
VisitInvokeUnresolved(HInvokeUnresolved * invoke)3118 void InstructionCodeGeneratorARM::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3119 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
3120 }
3121
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)3122 void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3123 // Explicit clinit checks triggered by static invokes must have been pruned by
3124 // art::PrepareForRegisterAllocation.
3125 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3126
3127 IntrinsicLocationsBuilderARM intrinsic(codegen_);
3128 if (intrinsic.TryDispatch(invoke)) {
3129 if (invoke->GetLocations()->CanCall() && invoke->HasPcRelativeDexCache()) {
3130 invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::Any());
3131 }
3132 return;
3133 }
3134
3135 HandleInvoke(invoke);
3136
3137 // For PC-relative dex cache the invoke has an extra input, the PC-relative address base.
3138 if (invoke->HasPcRelativeDexCache()) {
3139 invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::RequiresRegister());
3140 }
3141 }
3142
TryGenerateIntrinsicCode(HInvoke * invoke,CodeGeneratorARM * codegen)3143 static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM* codegen) {
3144 if (invoke->GetLocations()->Intrinsified()) {
3145 IntrinsicCodeGeneratorARM intrinsic(codegen);
3146 intrinsic.Dispatch(invoke);
3147 return true;
3148 }
3149 return false;
3150 }
3151
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)3152 void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3153 // Explicit clinit checks triggered by static invokes must have been pruned by
3154 // art::PrepareForRegisterAllocation.
3155 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3156
3157 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
3158 return;
3159 }
3160
3161 LocationSummary* locations = invoke->GetLocations();
3162 codegen_->GenerateStaticOrDirectCall(
3163 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
3164 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
3165 }
3166
HandleInvoke(HInvoke * invoke)3167 void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) {
3168 InvokeDexCallingConventionVisitorARM calling_convention_visitor;
3169 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
3170 }
3171
VisitInvokeVirtual(HInvokeVirtual * invoke)3172 void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
3173 IntrinsicLocationsBuilderARM intrinsic(codegen_);
3174 if (intrinsic.TryDispatch(invoke)) {
3175 return;
3176 }
3177
3178 HandleInvoke(invoke);
3179 }
3180
VisitInvokeVirtual(HInvokeVirtual * invoke)3181 void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
3182 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
3183 return;
3184 }
3185
3186 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
3187 DCHECK(!codegen_->IsLeafMethod());
3188 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
3189 }
3190
VisitInvokeInterface(HInvokeInterface * invoke)3191 void LocationsBuilderARM::VisitInvokeInterface(HInvokeInterface* invoke) {
3192 HandleInvoke(invoke);
3193 // Add the hidden argument.
3194 invoke->GetLocations()->AddTemp(Location::RegisterLocation(R12));
3195 }
3196
VisitInvokeInterface(HInvokeInterface * invoke)3197 void InstructionCodeGeneratorARM::VisitInvokeInterface(HInvokeInterface* invoke) {
3198 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
3199 LocationSummary* locations = invoke->GetLocations();
3200 Register temp = locations->GetTemp(0).AsRegister<Register>();
3201 Register hidden_reg = locations->GetTemp(1).AsRegister<Register>();
3202 Location receiver = locations->InAt(0);
3203 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3204
3205 // Set the hidden argument. This is safe to do this here, as R12
3206 // won't be modified thereafter, before the `blx` (call) instruction.
3207 DCHECK_EQ(R12, hidden_reg);
3208 __ LoadImmediate(hidden_reg, invoke->GetDexMethodIndex());
3209
3210 if (receiver.IsStackSlot()) {
3211 __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex());
3212 // /* HeapReference<Class> */ temp = temp->klass_
3213 __ LoadFromOffset(kLoadWord, temp, temp, class_offset);
3214 } else {
3215 // /* HeapReference<Class> */ temp = receiver->klass_
3216 __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
3217 }
3218 codegen_->MaybeRecordImplicitNullCheck(invoke);
3219 // Instead of simply (possibly) unpoisoning `temp` here, we should
3220 // emit a read barrier for the previous class reference load.
3221 // However this is not required in practice, as this is an
3222 // intermediate/temporary reference and because the current
3223 // concurrent copying collector keeps the from-space memory
3224 // intact/accessible until the end of the marking phase (the
3225 // concurrent copying collector may not in the future).
3226 __ MaybeUnpoisonHeapReference(temp);
3227 __ LoadFromOffset(kLoadWord, temp, temp,
3228 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
3229 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
3230 invoke->GetImtIndex(), kArmPointerSize));
3231 // temp = temp->GetImtEntryAt(method_offset);
3232 __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
3233 uint32_t entry_point =
3234 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value();
3235 // LR = temp->GetEntryPoint();
3236 __ LoadFromOffset(kLoadWord, LR, temp, entry_point);
3237 // LR();
3238 __ blx(LR);
3239 DCHECK(!codegen_->IsLeafMethod());
3240 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
3241 }
3242
VisitInvokePolymorphic(HInvokePolymorphic * invoke)3243 void LocationsBuilderARM::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3244 HandleInvoke(invoke);
3245 }
3246
VisitInvokePolymorphic(HInvokePolymorphic * invoke)3247 void InstructionCodeGeneratorARM::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3248 codegen_->GenerateInvokePolymorphicCall(invoke);
3249 }
3250
VisitNeg(HNeg * neg)3251 void LocationsBuilderARM::VisitNeg(HNeg* neg) {
3252 LocationSummary* locations =
3253 new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
3254 switch (neg->GetResultType()) {
3255 case Primitive::kPrimInt: {
3256 locations->SetInAt(0, Location::RequiresRegister());
3257 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3258 break;
3259 }
3260 case Primitive::kPrimLong: {
3261 locations->SetInAt(0, Location::RequiresRegister());
3262 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3263 break;
3264 }
3265
3266 case Primitive::kPrimFloat:
3267 case Primitive::kPrimDouble:
3268 locations->SetInAt(0, Location::RequiresFpuRegister());
3269 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3270 break;
3271
3272 default:
3273 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3274 }
3275 }
3276
VisitNeg(HNeg * neg)3277 void InstructionCodeGeneratorARM::VisitNeg(HNeg* neg) {
3278 LocationSummary* locations = neg->GetLocations();
3279 Location out = locations->Out();
3280 Location in = locations->InAt(0);
3281 switch (neg->GetResultType()) {
3282 case Primitive::kPrimInt:
3283 DCHECK(in.IsRegister());
3284 __ rsb(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(0));
3285 break;
3286
3287 case Primitive::kPrimLong:
3288 DCHECK(in.IsRegisterPair());
3289 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
3290 __ rsbs(out.AsRegisterPairLow<Register>(),
3291 in.AsRegisterPairLow<Register>(),
3292 ShifterOperand(0));
3293 // We cannot emit an RSC (Reverse Subtract with Carry)
3294 // instruction here, as it does not exist in the Thumb-2
3295 // instruction set. We use the following approach
3296 // using SBC and SUB instead.
3297 //
3298 // out.hi = -C
3299 __ sbc(out.AsRegisterPairHigh<Register>(),
3300 out.AsRegisterPairHigh<Register>(),
3301 ShifterOperand(out.AsRegisterPairHigh<Register>()));
3302 // out.hi = out.hi - in.hi
3303 __ sub(out.AsRegisterPairHigh<Register>(),
3304 out.AsRegisterPairHigh<Register>(),
3305 ShifterOperand(in.AsRegisterPairHigh<Register>()));
3306 break;
3307
3308 case Primitive::kPrimFloat:
3309 DCHECK(in.IsFpuRegister());
3310 __ vnegs(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>());
3311 break;
3312
3313 case Primitive::kPrimDouble:
3314 DCHECK(in.IsFpuRegisterPair());
3315 __ vnegd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
3316 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
3317 break;
3318
3319 default:
3320 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3321 }
3322 }
3323
VisitTypeConversion(HTypeConversion * conversion)3324 void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
3325 Primitive::Type result_type = conversion->GetResultType();
3326 Primitive::Type input_type = conversion->GetInputType();
3327 DCHECK_NE(result_type, input_type);
3328
3329 // The float-to-long, double-to-long and long-to-float type conversions
3330 // rely on a call to the runtime.
3331 LocationSummary::CallKind call_kind =
3332 (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
3333 && result_type == Primitive::kPrimLong)
3334 || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat))
3335 ? LocationSummary::kCallOnMainOnly
3336 : LocationSummary::kNoCall;
3337 LocationSummary* locations =
3338 new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
3339
3340 // The Java language does not allow treating boolean as an integral type but
3341 // our bit representation makes it safe.
3342
3343 switch (result_type) {
3344 case Primitive::kPrimByte:
3345 switch (input_type) {
3346 case Primitive::kPrimLong:
3347 // Type conversion from long to byte is a result of code transformations.
3348 case Primitive::kPrimBoolean:
3349 // Boolean input is a result of code transformations.
3350 case Primitive::kPrimShort:
3351 case Primitive::kPrimInt:
3352 case Primitive::kPrimChar:
3353 // Processing a Dex `int-to-byte' instruction.
3354 locations->SetInAt(0, Location::RequiresRegister());
3355 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3356 break;
3357
3358 default:
3359 LOG(FATAL) << "Unexpected type conversion from " << input_type
3360 << " to " << result_type;
3361 }
3362 break;
3363
3364 case Primitive::kPrimShort:
3365 switch (input_type) {
3366 case Primitive::kPrimLong:
3367 // Type conversion from long to short is a result of code transformations.
3368 case Primitive::kPrimBoolean:
3369 // Boolean input is a result of code transformations.
3370 case Primitive::kPrimByte:
3371 case Primitive::kPrimInt:
3372 case Primitive::kPrimChar:
3373 // Processing a Dex `int-to-short' instruction.
3374 locations->SetInAt(0, Location::RequiresRegister());
3375 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3376 break;
3377
3378 default:
3379 LOG(FATAL) << "Unexpected type conversion from " << input_type
3380 << " to " << result_type;
3381 }
3382 break;
3383
3384 case Primitive::kPrimInt:
3385 switch (input_type) {
3386 case Primitive::kPrimLong:
3387 // Processing a Dex `long-to-int' instruction.
3388 locations->SetInAt(0, Location::Any());
3389 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3390 break;
3391
3392 case Primitive::kPrimFloat:
3393 // Processing a Dex `float-to-int' instruction.
3394 locations->SetInAt(0, Location::RequiresFpuRegister());
3395 locations->SetOut(Location::RequiresRegister());
3396 locations->AddTemp(Location::RequiresFpuRegister());
3397 break;
3398
3399 case Primitive::kPrimDouble:
3400 // Processing a Dex `double-to-int' instruction.
3401 locations->SetInAt(0, Location::RequiresFpuRegister());
3402 locations->SetOut(Location::RequiresRegister());
3403 locations->AddTemp(Location::RequiresFpuRegister());
3404 break;
3405
3406 default:
3407 LOG(FATAL) << "Unexpected type conversion from " << input_type
3408 << " to " << result_type;
3409 }
3410 break;
3411
3412 case Primitive::kPrimLong:
3413 switch (input_type) {
3414 case Primitive::kPrimBoolean:
3415 // Boolean input is a result of code transformations.
3416 case Primitive::kPrimByte:
3417 case Primitive::kPrimShort:
3418 case Primitive::kPrimInt:
3419 case Primitive::kPrimChar:
3420 // Processing a Dex `int-to-long' instruction.
3421 locations->SetInAt(0, Location::RequiresRegister());
3422 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3423 break;
3424
3425 case Primitive::kPrimFloat: {
3426 // Processing a Dex `float-to-long' instruction.
3427 InvokeRuntimeCallingConvention calling_convention;
3428 locations->SetInAt(0, Location::FpuRegisterLocation(
3429 calling_convention.GetFpuRegisterAt(0)));
3430 locations->SetOut(Location::RegisterPairLocation(R0, R1));
3431 break;
3432 }
3433
3434 case Primitive::kPrimDouble: {
3435 // Processing a Dex `double-to-long' instruction.
3436 InvokeRuntimeCallingConvention calling_convention;
3437 locations->SetInAt(0, Location::FpuRegisterPairLocation(
3438 calling_convention.GetFpuRegisterAt(0),
3439 calling_convention.GetFpuRegisterAt(1)));
3440 locations->SetOut(Location::RegisterPairLocation(R0, R1));
3441 break;
3442 }
3443
3444 default:
3445 LOG(FATAL) << "Unexpected type conversion from " << input_type
3446 << " to " << result_type;
3447 }
3448 break;
3449
3450 case Primitive::kPrimChar:
3451 switch (input_type) {
3452 case Primitive::kPrimLong:
3453 // Type conversion from long to char is a result of code transformations.
3454 case Primitive::kPrimBoolean:
3455 // Boolean input is a result of code transformations.
3456 case Primitive::kPrimByte:
3457 case Primitive::kPrimShort:
3458 case Primitive::kPrimInt:
3459 // Processing a Dex `int-to-char' instruction.
3460 locations->SetInAt(0, Location::RequiresRegister());
3461 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3462 break;
3463
3464 default:
3465 LOG(FATAL) << "Unexpected type conversion from " << input_type
3466 << " to " << result_type;
3467 }
3468 break;
3469
3470 case Primitive::kPrimFloat:
3471 switch (input_type) {
3472 case Primitive::kPrimBoolean:
3473 // Boolean input is a result of code transformations.
3474 case Primitive::kPrimByte:
3475 case Primitive::kPrimShort:
3476 case Primitive::kPrimInt:
3477 case Primitive::kPrimChar:
3478 // Processing a Dex `int-to-float' instruction.
3479 locations->SetInAt(0, Location::RequiresRegister());
3480 locations->SetOut(Location::RequiresFpuRegister());
3481 break;
3482
3483 case Primitive::kPrimLong: {
3484 // Processing a Dex `long-to-float' instruction.
3485 InvokeRuntimeCallingConvention calling_convention;
3486 locations->SetInAt(0, Location::RegisterPairLocation(
3487 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
3488 locations->SetOut(Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
3489 break;
3490 }
3491
3492 case Primitive::kPrimDouble:
3493 // Processing a Dex `double-to-float' instruction.
3494 locations->SetInAt(0, Location::RequiresFpuRegister());
3495 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3496 break;
3497
3498 default:
3499 LOG(FATAL) << "Unexpected type conversion from " << input_type
3500 << " to " << result_type;
3501 };
3502 break;
3503
3504 case Primitive::kPrimDouble:
3505 switch (input_type) {
3506 case Primitive::kPrimBoolean:
3507 // Boolean input is a result of code transformations.
3508 case Primitive::kPrimByte:
3509 case Primitive::kPrimShort:
3510 case Primitive::kPrimInt:
3511 case Primitive::kPrimChar:
3512 // Processing a Dex `int-to-double' instruction.
3513 locations->SetInAt(0, Location::RequiresRegister());
3514 locations->SetOut(Location::RequiresFpuRegister());
3515 break;
3516
3517 case Primitive::kPrimLong:
3518 // Processing a Dex `long-to-double' instruction.
3519 locations->SetInAt(0, Location::RequiresRegister());
3520 locations->SetOut(Location::RequiresFpuRegister());
3521 locations->AddTemp(Location::RequiresFpuRegister());
3522 locations->AddTemp(Location::RequiresFpuRegister());
3523 break;
3524
3525 case Primitive::kPrimFloat:
3526 // Processing a Dex `float-to-double' instruction.
3527 locations->SetInAt(0, Location::RequiresFpuRegister());
3528 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3529 break;
3530
3531 default:
3532 LOG(FATAL) << "Unexpected type conversion from " << input_type
3533 << " to " << result_type;
3534 };
3535 break;
3536
3537 default:
3538 LOG(FATAL) << "Unexpected type conversion from " << input_type
3539 << " to " << result_type;
3540 }
3541 }
3542
VisitTypeConversion(HTypeConversion * conversion)3543 void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversion) {
3544 LocationSummary* locations = conversion->GetLocations();
3545 Location out = locations->Out();
3546 Location in = locations->InAt(0);
3547 Primitive::Type result_type = conversion->GetResultType();
3548 Primitive::Type input_type = conversion->GetInputType();
3549 DCHECK_NE(result_type, input_type);
3550 switch (result_type) {
3551 case Primitive::kPrimByte:
3552 switch (input_type) {
3553 case Primitive::kPrimLong:
3554 // Type conversion from long to byte is a result of code transformations.
3555 __ sbfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 8);
3556 break;
3557 case Primitive::kPrimBoolean:
3558 // Boolean input is a result of code transformations.
3559 case Primitive::kPrimShort:
3560 case Primitive::kPrimInt:
3561 case Primitive::kPrimChar:
3562 // Processing a Dex `int-to-byte' instruction.
3563 __ sbfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 8);
3564 break;
3565
3566 default:
3567 LOG(FATAL) << "Unexpected type conversion from " << input_type
3568 << " to " << result_type;
3569 }
3570 break;
3571
3572 case Primitive::kPrimShort:
3573 switch (input_type) {
3574 case Primitive::kPrimLong:
3575 // Type conversion from long to short is a result of code transformations.
3576 __ sbfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 16);
3577 break;
3578 case Primitive::kPrimBoolean:
3579 // Boolean input is a result of code transformations.
3580 case Primitive::kPrimByte:
3581 case Primitive::kPrimInt:
3582 case Primitive::kPrimChar:
3583 // Processing a Dex `int-to-short' instruction.
3584 __ sbfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 16);
3585 break;
3586
3587 default:
3588 LOG(FATAL) << "Unexpected type conversion from " << input_type
3589 << " to " << result_type;
3590 }
3591 break;
3592
3593 case Primitive::kPrimInt:
3594 switch (input_type) {
3595 case Primitive::kPrimLong:
3596 // Processing a Dex `long-to-int' instruction.
3597 DCHECK(out.IsRegister());
3598 if (in.IsRegisterPair()) {
3599 __ Mov(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>());
3600 } else if (in.IsDoubleStackSlot()) {
3601 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), SP, in.GetStackIndex());
3602 } else {
3603 DCHECK(in.IsConstant());
3604 DCHECK(in.GetConstant()->IsLongConstant());
3605 int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
3606 __ LoadImmediate(out.AsRegister<Register>(), static_cast<int32_t>(value));
3607 }
3608 break;
3609
3610 case Primitive::kPrimFloat: {
3611 // Processing a Dex `float-to-int' instruction.
3612 SRegister temp = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
3613 __ vcvtis(temp, in.AsFpuRegister<SRegister>());
3614 __ vmovrs(out.AsRegister<Register>(), temp);
3615 break;
3616 }
3617
3618 case Primitive::kPrimDouble: {
3619 // Processing a Dex `double-to-int' instruction.
3620 SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
3621 __ vcvtid(temp_s, FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
3622 __ vmovrs(out.AsRegister<Register>(), temp_s);
3623 break;
3624 }
3625
3626 default:
3627 LOG(FATAL) << "Unexpected type conversion from " << input_type
3628 << " to " << result_type;
3629 }
3630 break;
3631
3632 case Primitive::kPrimLong:
3633 switch (input_type) {
3634 case Primitive::kPrimBoolean:
3635 // Boolean input is a result of code transformations.
3636 case Primitive::kPrimByte:
3637 case Primitive::kPrimShort:
3638 case Primitive::kPrimInt:
3639 case Primitive::kPrimChar:
3640 // Processing a Dex `int-to-long' instruction.
3641 DCHECK(out.IsRegisterPair());
3642 DCHECK(in.IsRegister());
3643 __ Mov(out.AsRegisterPairLow<Register>(), in.AsRegister<Register>());
3644 // Sign extension.
3645 __ Asr(out.AsRegisterPairHigh<Register>(),
3646 out.AsRegisterPairLow<Register>(),
3647 31);
3648 break;
3649
3650 case Primitive::kPrimFloat:
3651 // Processing a Dex `float-to-long' instruction.
3652 codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
3653 CheckEntrypointTypes<kQuickF2l, int64_t, float>();
3654 break;
3655
3656 case Primitive::kPrimDouble:
3657 // Processing a Dex `double-to-long' instruction.
3658 codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
3659 CheckEntrypointTypes<kQuickD2l, int64_t, double>();
3660 break;
3661
3662 default:
3663 LOG(FATAL) << "Unexpected type conversion from " << input_type
3664 << " to " << result_type;
3665 }
3666 break;
3667
3668 case Primitive::kPrimChar:
3669 switch (input_type) {
3670 case Primitive::kPrimLong:
3671 // Type conversion from long to char is a result of code transformations.
3672 __ ubfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 16);
3673 break;
3674 case Primitive::kPrimBoolean:
3675 // Boolean input is a result of code transformations.
3676 case Primitive::kPrimByte:
3677 case Primitive::kPrimShort:
3678 case Primitive::kPrimInt:
3679 // Processing a Dex `int-to-char' instruction.
3680 __ ubfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 16);
3681 break;
3682
3683 default:
3684 LOG(FATAL) << "Unexpected type conversion from " << input_type
3685 << " to " << result_type;
3686 }
3687 break;
3688
3689 case Primitive::kPrimFloat:
3690 switch (input_type) {
3691 case Primitive::kPrimBoolean:
3692 // Boolean input is a result of code transformations.
3693 case Primitive::kPrimByte:
3694 case Primitive::kPrimShort:
3695 case Primitive::kPrimInt:
3696 case Primitive::kPrimChar: {
3697 // Processing a Dex `int-to-float' instruction.
3698 __ vmovsr(out.AsFpuRegister<SRegister>(), in.AsRegister<Register>());
3699 __ vcvtsi(out.AsFpuRegister<SRegister>(), out.AsFpuRegister<SRegister>());
3700 break;
3701 }
3702
3703 case Primitive::kPrimLong:
3704 // Processing a Dex `long-to-float' instruction.
3705 codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
3706 CheckEntrypointTypes<kQuickL2f, float, int64_t>();
3707 break;
3708
3709 case Primitive::kPrimDouble:
3710 // Processing a Dex `double-to-float' instruction.
3711 __ vcvtsd(out.AsFpuRegister<SRegister>(),
3712 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
3713 break;
3714
3715 default:
3716 LOG(FATAL) << "Unexpected type conversion from " << input_type
3717 << " to " << result_type;
3718 };
3719 break;
3720
3721 case Primitive::kPrimDouble:
3722 switch (input_type) {
3723 case Primitive::kPrimBoolean:
3724 // Boolean input is a result of code transformations.
3725 case Primitive::kPrimByte:
3726 case Primitive::kPrimShort:
3727 case Primitive::kPrimInt:
3728 case Primitive::kPrimChar: {
3729 // Processing a Dex `int-to-double' instruction.
3730 __ vmovsr(out.AsFpuRegisterPairLow<SRegister>(), in.AsRegister<Register>());
3731 __ vcvtdi(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
3732 out.AsFpuRegisterPairLow<SRegister>());
3733 break;
3734 }
3735
3736 case Primitive::kPrimLong: {
3737 // Processing a Dex `long-to-double' instruction.
3738 Register low = in.AsRegisterPairLow<Register>();
3739 Register high = in.AsRegisterPairHigh<Register>();
3740 SRegister out_s = out.AsFpuRegisterPairLow<SRegister>();
3741 DRegister out_d = FromLowSToD(out_s);
3742 SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
3743 DRegister temp_d = FromLowSToD(temp_s);
3744 SRegister constant_s = locations->GetTemp(1).AsFpuRegisterPairLow<SRegister>();
3745 DRegister constant_d = FromLowSToD(constant_s);
3746
3747 // temp_d = int-to-double(high)
3748 __ vmovsr(temp_s, high);
3749 __ vcvtdi(temp_d, temp_s);
3750 // constant_d = k2Pow32EncodingForDouble
3751 __ LoadDImmediate(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
3752 // out_d = unsigned-to-double(low)
3753 __ vmovsr(out_s, low);
3754 __ vcvtdu(out_d, out_s);
3755 // out_d += temp_d * constant_d
3756 __ vmlad(out_d, temp_d, constant_d);
3757 break;
3758 }
3759
3760 case Primitive::kPrimFloat:
3761 // Processing a Dex `float-to-double' instruction.
3762 __ vcvtds(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
3763 in.AsFpuRegister<SRegister>());
3764 break;
3765
3766 default:
3767 LOG(FATAL) << "Unexpected type conversion from " << input_type
3768 << " to " << result_type;
3769 };
3770 break;
3771
3772 default:
3773 LOG(FATAL) << "Unexpected type conversion from " << input_type
3774 << " to " << result_type;
3775 }
3776 }
3777
VisitAdd(HAdd * add)3778 void LocationsBuilderARM::VisitAdd(HAdd* add) {
3779 LocationSummary* locations =
3780 new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
3781 switch (add->GetResultType()) {
3782 case Primitive::kPrimInt: {
3783 locations->SetInAt(0, Location::RequiresRegister());
3784 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
3785 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3786 break;
3787 }
3788
3789 case Primitive::kPrimLong: {
3790 locations->SetInAt(0, Location::RequiresRegister());
3791 locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
3792 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3793 break;
3794 }
3795
3796 case Primitive::kPrimFloat:
3797 case Primitive::kPrimDouble: {
3798 locations->SetInAt(0, Location::RequiresFpuRegister());
3799 locations->SetInAt(1, Location::RequiresFpuRegister());
3800 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3801 break;
3802 }
3803
3804 default:
3805 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
3806 }
3807 }
3808
VisitAdd(HAdd * add)3809 void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) {
3810 LocationSummary* locations = add->GetLocations();
3811 Location out = locations->Out();
3812 Location first = locations->InAt(0);
3813 Location second = locations->InAt(1);
3814 switch (add->GetResultType()) {
3815 case Primitive::kPrimInt:
3816 if (second.IsRegister()) {
3817 __ add(out.AsRegister<Register>(),
3818 first.AsRegister<Register>(),
3819 ShifterOperand(second.AsRegister<Register>()));
3820 } else {
3821 __ AddConstant(out.AsRegister<Register>(),
3822 first.AsRegister<Register>(),
3823 second.GetConstant()->AsIntConstant()->GetValue());
3824 }
3825 break;
3826
3827 case Primitive::kPrimLong: {
3828 if (second.IsConstant()) {
3829 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
3830 GenerateAddLongConst(out, first, value);
3831 } else {
3832 DCHECK(second.IsRegisterPair());
3833 __ adds(out.AsRegisterPairLow<Register>(),
3834 first.AsRegisterPairLow<Register>(),
3835 ShifterOperand(second.AsRegisterPairLow<Register>()));
3836 __ adc(out.AsRegisterPairHigh<Register>(),
3837 first.AsRegisterPairHigh<Register>(),
3838 ShifterOperand(second.AsRegisterPairHigh<Register>()));
3839 }
3840 break;
3841 }
3842
3843 case Primitive::kPrimFloat:
3844 __ vadds(out.AsFpuRegister<SRegister>(),
3845 first.AsFpuRegister<SRegister>(),
3846 second.AsFpuRegister<SRegister>());
3847 break;
3848
3849 case Primitive::kPrimDouble:
3850 __ vaddd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
3851 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()),
3852 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>()));
3853 break;
3854
3855 default:
3856 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
3857 }
3858 }
3859
VisitSub(HSub * sub)3860 void LocationsBuilderARM::VisitSub(HSub* sub) {
3861 LocationSummary* locations =
3862 new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
3863 switch (sub->GetResultType()) {
3864 case Primitive::kPrimInt: {
3865 locations->SetInAt(0, Location::RequiresRegister());
3866 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
3867 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3868 break;
3869 }
3870
3871 case Primitive::kPrimLong: {
3872 locations->SetInAt(0, Location::RequiresRegister());
3873 locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
3874 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3875 break;
3876 }
3877 case Primitive::kPrimFloat:
3878 case Primitive::kPrimDouble: {
3879 locations->SetInAt(0, Location::RequiresFpuRegister());
3880 locations->SetInAt(1, Location::RequiresFpuRegister());
3881 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3882 break;
3883 }
3884 default:
3885 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
3886 }
3887 }
3888
VisitSub(HSub * sub)3889 void InstructionCodeGeneratorARM::VisitSub(HSub* sub) {
3890 LocationSummary* locations = sub->GetLocations();
3891 Location out = locations->Out();
3892 Location first = locations->InAt(0);
3893 Location second = locations->InAt(1);
3894 switch (sub->GetResultType()) {
3895 case Primitive::kPrimInt: {
3896 if (second.IsRegister()) {
3897 __ sub(out.AsRegister<Register>(),
3898 first.AsRegister<Register>(),
3899 ShifterOperand(second.AsRegister<Register>()));
3900 } else {
3901 __ AddConstant(out.AsRegister<Register>(),
3902 first.AsRegister<Register>(),
3903 -second.GetConstant()->AsIntConstant()->GetValue());
3904 }
3905 break;
3906 }
3907
3908 case Primitive::kPrimLong: {
3909 if (second.IsConstant()) {
3910 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
3911 GenerateAddLongConst(out, first, -value);
3912 } else {
3913 DCHECK(second.IsRegisterPair());
3914 __ subs(out.AsRegisterPairLow<Register>(),
3915 first.AsRegisterPairLow<Register>(),
3916 ShifterOperand(second.AsRegisterPairLow<Register>()));
3917 __ sbc(out.AsRegisterPairHigh<Register>(),
3918 first.AsRegisterPairHigh<Register>(),
3919 ShifterOperand(second.AsRegisterPairHigh<Register>()));
3920 }
3921 break;
3922 }
3923
3924 case Primitive::kPrimFloat: {
3925 __ vsubs(out.AsFpuRegister<SRegister>(),
3926 first.AsFpuRegister<SRegister>(),
3927 second.AsFpuRegister<SRegister>());
3928 break;
3929 }
3930
3931 case Primitive::kPrimDouble: {
3932 __ vsubd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
3933 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()),
3934 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>()));
3935 break;
3936 }
3937
3938
3939 default:
3940 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
3941 }
3942 }
3943
VisitMul(HMul * mul)3944 void LocationsBuilderARM::VisitMul(HMul* mul) {
3945 LocationSummary* locations =
3946 new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
3947 switch (mul->GetResultType()) {
3948 case Primitive::kPrimInt:
3949 case Primitive::kPrimLong: {
3950 locations->SetInAt(0, Location::RequiresRegister());
3951 locations->SetInAt(1, Location::RequiresRegister());
3952 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3953 break;
3954 }
3955
3956 case Primitive::kPrimFloat:
3957 case Primitive::kPrimDouble: {
3958 locations->SetInAt(0, Location::RequiresFpuRegister());
3959 locations->SetInAt(1, Location::RequiresFpuRegister());
3960 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3961 break;
3962 }
3963
3964 default:
3965 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
3966 }
3967 }
3968
VisitMul(HMul * mul)3969 void InstructionCodeGeneratorARM::VisitMul(HMul* mul) {
3970 LocationSummary* locations = mul->GetLocations();
3971 Location out = locations->Out();
3972 Location first = locations->InAt(0);
3973 Location second = locations->InAt(1);
3974 switch (mul->GetResultType()) {
3975 case Primitive::kPrimInt: {
3976 __ mul(out.AsRegister<Register>(),
3977 first.AsRegister<Register>(),
3978 second.AsRegister<Register>());
3979 break;
3980 }
3981 case Primitive::kPrimLong: {
3982 Register out_hi = out.AsRegisterPairHigh<Register>();
3983 Register out_lo = out.AsRegisterPairLow<Register>();
3984 Register in1_hi = first.AsRegisterPairHigh<Register>();
3985 Register in1_lo = first.AsRegisterPairLow<Register>();
3986 Register in2_hi = second.AsRegisterPairHigh<Register>();
3987 Register in2_lo = second.AsRegisterPairLow<Register>();
3988
3989 // Extra checks to protect caused by the existence of R1_R2.
3990 // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
3991 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
3992 DCHECK_NE(out_hi, in1_lo);
3993 DCHECK_NE(out_hi, in2_lo);
3994
3995 // input: in1 - 64 bits, in2 - 64 bits
3996 // output: out
3997 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
3998 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
3999 // parts: out.lo = (in1.lo * in2.lo)[31:0]
4000
4001 // IP <- in1.lo * in2.hi
4002 __ mul(IP, in1_lo, in2_hi);
4003 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
4004 __ mla(out_hi, in1_hi, in2_lo, IP);
4005 // out.lo <- (in1.lo * in2.lo)[31:0];
4006 __ umull(out_lo, IP, in1_lo, in2_lo);
4007 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
4008 __ add(out_hi, out_hi, ShifterOperand(IP));
4009 break;
4010 }
4011
4012 case Primitive::kPrimFloat: {
4013 __ vmuls(out.AsFpuRegister<SRegister>(),
4014 first.AsFpuRegister<SRegister>(),
4015 second.AsFpuRegister<SRegister>());
4016 break;
4017 }
4018
4019 case Primitive::kPrimDouble: {
4020 __ vmuld(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
4021 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()),
4022 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>()));
4023 break;
4024 }
4025
4026 default:
4027 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4028 }
4029 }
4030
DivRemOneOrMinusOne(HBinaryOperation * instruction)4031 void InstructionCodeGeneratorARM::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
4032 DCHECK(instruction->IsDiv() || instruction->IsRem());
4033 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
4034
4035 LocationSummary* locations = instruction->GetLocations();
4036 Location second = locations->InAt(1);
4037 DCHECK(second.IsConstant());
4038
4039 Register out = locations->Out().AsRegister<Register>();
4040 Register dividend = locations->InAt(0).AsRegister<Register>();
4041 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
4042 DCHECK(imm == 1 || imm == -1);
4043
4044 if (instruction->IsRem()) {
4045 __ LoadImmediate(out, 0);
4046 } else {
4047 if (imm == 1) {
4048 __ Mov(out, dividend);
4049 } else {
4050 __ rsb(out, dividend, ShifterOperand(0));
4051 }
4052 }
4053 }
4054
DivRemByPowerOfTwo(HBinaryOperation * instruction)4055 void InstructionCodeGeneratorARM::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
4056 DCHECK(instruction->IsDiv() || instruction->IsRem());
4057 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
4058
4059 LocationSummary* locations = instruction->GetLocations();
4060 Location second = locations->InAt(1);
4061 DCHECK(second.IsConstant());
4062
4063 Register out = locations->Out().AsRegister<Register>();
4064 Register dividend = locations->InAt(0).AsRegister<Register>();
4065 Register temp = locations->GetTemp(0).AsRegister<Register>();
4066 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
4067 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
4068 int ctz_imm = CTZ(abs_imm);
4069
4070 if (ctz_imm == 1) {
4071 __ Lsr(temp, dividend, 32 - ctz_imm);
4072 } else {
4073 __ Asr(temp, dividend, 31);
4074 __ Lsr(temp, temp, 32 - ctz_imm);
4075 }
4076 __ add(out, temp, ShifterOperand(dividend));
4077
4078 if (instruction->IsDiv()) {
4079 __ Asr(out, out, ctz_imm);
4080 if (imm < 0) {
4081 __ rsb(out, out, ShifterOperand(0));
4082 }
4083 } else {
4084 __ ubfx(out, out, 0, ctz_imm);
4085 __ sub(out, out, ShifterOperand(temp));
4086 }
4087 }
4088
GenerateDivRemWithAnyConstant(HBinaryOperation * instruction)4089 void InstructionCodeGeneratorARM::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
4090 DCHECK(instruction->IsDiv() || instruction->IsRem());
4091 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
4092
4093 LocationSummary* locations = instruction->GetLocations();
4094 Location second = locations->InAt(1);
4095 DCHECK(second.IsConstant());
4096
4097 Register out = locations->Out().AsRegister<Register>();
4098 Register dividend = locations->InAt(0).AsRegister<Register>();
4099 Register temp1 = locations->GetTemp(0).AsRegister<Register>();
4100 Register temp2 = locations->GetTemp(1).AsRegister<Register>();
4101 int64_t imm = second.GetConstant()->AsIntConstant()->GetValue();
4102
4103 int64_t magic;
4104 int shift;
4105 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
4106
4107 __ LoadImmediate(temp1, magic);
4108 __ smull(temp2, temp1, dividend, temp1);
4109
4110 if (imm > 0 && magic < 0) {
4111 __ add(temp1, temp1, ShifterOperand(dividend));
4112 } else if (imm < 0 && magic > 0) {
4113 __ sub(temp1, temp1, ShifterOperand(dividend));
4114 }
4115
4116 if (shift != 0) {
4117 __ Asr(temp1, temp1, shift);
4118 }
4119
4120 if (instruction->IsDiv()) {
4121 __ sub(out, temp1, ShifterOperand(temp1, ASR, 31));
4122 } else {
4123 __ sub(temp1, temp1, ShifterOperand(temp1, ASR, 31));
4124 // TODO: Strength reduction for mls.
4125 __ LoadImmediate(temp2, imm);
4126 __ mls(out, temp1, temp2, dividend);
4127 }
4128 }
4129
GenerateDivRemConstantIntegral(HBinaryOperation * instruction)4130 void InstructionCodeGeneratorARM::GenerateDivRemConstantIntegral(HBinaryOperation* instruction) {
4131 DCHECK(instruction->IsDiv() || instruction->IsRem());
4132 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
4133
4134 LocationSummary* locations = instruction->GetLocations();
4135 Location second = locations->InAt(1);
4136 DCHECK(second.IsConstant());
4137
4138 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
4139 if (imm == 0) {
4140 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
4141 } else if (imm == 1 || imm == -1) {
4142 DivRemOneOrMinusOne(instruction);
4143 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
4144 DivRemByPowerOfTwo(instruction);
4145 } else {
4146 DCHECK(imm <= -2 || imm >= 2);
4147 GenerateDivRemWithAnyConstant(instruction);
4148 }
4149 }
4150
VisitDiv(HDiv * div)4151 void LocationsBuilderARM::VisitDiv(HDiv* div) {
4152 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
4153 if (div->GetResultType() == Primitive::kPrimLong) {
4154 // pLdiv runtime call.
4155 call_kind = LocationSummary::kCallOnMainOnly;
4156 } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) {
4157 // sdiv will be replaced by other instruction sequence.
4158 } else if (div->GetResultType() == Primitive::kPrimInt &&
4159 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4160 // pIdivmod runtime call.
4161 call_kind = LocationSummary::kCallOnMainOnly;
4162 }
4163
4164 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind);
4165
4166 switch (div->GetResultType()) {
4167 case Primitive::kPrimInt: {
4168 if (div->InputAt(1)->IsConstant()) {
4169 locations->SetInAt(0, Location::RequiresRegister());
4170 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
4171 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4172 int32_t value = div->InputAt(1)->AsIntConstant()->GetValue();
4173 if (value == 1 || value == 0 || value == -1) {
4174 // No temp register required.
4175 } else {
4176 locations->AddTemp(Location::RequiresRegister());
4177 if (!IsPowerOfTwo(AbsOrMin(value))) {
4178 locations->AddTemp(Location::RequiresRegister());
4179 }
4180 }
4181 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4182 locations->SetInAt(0, Location::RequiresRegister());
4183 locations->SetInAt(1, Location::RequiresRegister());
4184 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4185 } else {
4186 InvokeRuntimeCallingConvention calling_convention;
4187 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
4188 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
4189 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
4190 // we only need the former.
4191 locations->SetOut(Location::RegisterLocation(R0));
4192 }
4193 break;
4194 }
4195 case Primitive::kPrimLong: {
4196 InvokeRuntimeCallingConvention calling_convention;
4197 locations->SetInAt(0, Location::RegisterPairLocation(
4198 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4199 locations->SetInAt(1, Location::RegisterPairLocation(
4200 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4201 locations->SetOut(Location::RegisterPairLocation(R0, R1));
4202 break;
4203 }
4204 case Primitive::kPrimFloat:
4205 case Primitive::kPrimDouble: {
4206 locations->SetInAt(0, Location::RequiresFpuRegister());
4207 locations->SetInAt(1, Location::RequiresFpuRegister());
4208 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4209 break;
4210 }
4211
4212 default:
4213 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4214 }
4215 }
4216
VisitDiv(HDiv * div)4217 void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) {
4218 LocationSummary* locations = div->GetLocations();
4219 Location out = locations->Out();
4220 Location first = locations->InAt(0);
4221 Location second = locations->InAt(1);
4222
4223 switch (div->GetResultType()) {
4224 case Primitive::kPrimInt: {
4225 if (second.IsConstant()) {
4226 GenerateDivRemConstantIntegral(div);
4227 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4228 __ sdiv(out.AsRegister<Register>(),
4229 first.AsRegister<Register>(),
4230 second.AsRegister<Register>());
4231 } else {
4232 InvokeRuntimeCallingConvention calling_convention;
4233 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>());
4234 DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>());
4235 DCHECK_EQ(R0, out.AsRegister<Register>());
4236
4237 codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc());
4238 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
4239 }
4240 break;
4241 }
4242
4243 case Primitive::kPrimLong: {
4244 InvokeRuntimeCallingConvention calling_convention;
4245 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>());
4246 DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>());
4247 DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>());
4248 DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>());
4249 DCHECK_EQ(R0, out.AsRegisterPairLow<Register>());
4250 DCHECK_EQ(R1, out.AsRegisterPairHigh<Register>());
4251
4252 codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
4253 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
4254 break;
4255 }
4256
4257 case Primitive::kPrimFloat: {
4258 __ vdivs(out.AsFpuRegister<SRegister>(),
4259 first.AsFpuRegister<SRegister>(),
4260 second.AsFpuRegister<SRegister>());
4261 break;
4262 }
4263
4264 case Primitive::kPrimDouble: {
4265 __ vdivd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
4266 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()),
4267 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>()));
4268 break;
4269 }
4270
4271 default:
4272 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4273 }
4274 }
4275
VisitRem(HRem * rem)4276 void LocationsBuilderARM::VisitRem(HRem* rem) {
4277 Primitive::Type type = rem->GetResultType();
4278
4279 // Most remainders are implemented in the runtime.
4280 LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
4281 if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) {
4282 // sdiv will be replaced by other instruction sequence.
4283 call_kind = LocationSummary::kNoCall;
4284 } else if ((rem->GetResultType() == Primitive::kPrimInt)
4285 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4286 // Have hardware divide instruction for int, do it with three instructions.
4287 call_kind = LocationSummary::kNoCall;
4288 }
4289
4290 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
4291
4292 switch (type) {
4293 case Primitive::kPrimInt: {
4294 if (rem->InputAt(1)->IsConstant()) {
4295 locations->SetInAt(0, Location::RequiresRegister());
4296 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
4297 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4298 int32_t value = rem->InputAt(1)->AsIntConstant()->GetValue();
4299 if (value == 1 || value == 0 || value == -1) {
4300 // No temp register required.
4301 } else {
4302 locations->AddTemp(Location::RequiresRegister());
4303 if (!IsPowerOfTwo(AbsOrMin(value))) {
4304 locations->AddTemp(Location::RequiresRegister());
4305 }
4306 }
4307 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4308 locations->SetInAt(0, Location::RequiresRegister());
4309 locations->SetInAt(1, Location::RequiresRegister());
4310 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4311 locations->AddTemp(Location::RequiresRegister());
4312 } else {
4313 InvokeRuntimeCallingConvention calling_convention;
4314 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
4315 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
4316 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
4317 // we only need the latter.
4318 locations->SetOut(Location::RegisterLocation(R1));
4319 }
4320 break;
4321 }
4322 case Primitive::kPrimLong: {
4323 InvokeRuntimeCallingConvention calling_convention;
4324 locations->SetInAt(0, Location::RegisterPairLocation(
4325 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4326 locations->SetInAt(1, Location::RegisterPairLocation(
4327 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4328 // The runtime helper puts the output in R2,R3.
4329 locations->SetOut(Location::RegisterPairLocation(R2, R3));
4330 break;
4331 }
4332 case Primitive::kPrimFloat: {
4333 InvokeRuntimeCallingConvention calling_convention;
4334 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
4335 locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
4336 locations->SetOut(Location::FpuRegisterLocation(S0));
4337 break;
4338 }
4339
4340 case Primitive::kPrimDouble: {
4341 InvokeRuntimeCallingConvention calling_convention;
4342 locations->SetInAt(0, Location::FpuRegisterPairLocation(
4343 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
4344 locations->SetInAt(1, Location::FpuRegisterPairLocation(
4345 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
4346 locations->SetOut(Location::Location::FpuRegisterPairLocation(S0, S1));
4347 break;
4348 }
4349
4350 default:
4351 LOG(FATAL) << "Unexpected rem type " << type;
4352 }
4353 }
4354
VisitRem(HRem * rem)4355 void InstructionCodeGeneratorARM::VisitRem(HRem* rem) {
4356 LocationSummary* locations = rem->GetLocations();
4357 Location out = locations->Out();
4358 Location first = locations->InAt(0);
4359 Location second = locations->InAt(1);
4360
4361 Primitive::Type type = rem->GetResultType();
4362 switch (type) {
4363 case Primitive::kPrimInt: {
4364 if (second.IsConstant()) {
4365 GenerateDivRemConstantIntegral(rem);
4366 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4367 Register reg1 = first.AsRegister<Register>();
4368 Register reg2 = second.AsRegister<Register>();
4369 Register temp = locations->GetTemp(0).AsRegister<Register>();
4370
4371 // temp = reg1 / reg2 (integer division)
4372 // dest = reg1 - temp * reg2
4373 __ sdiv(temp, reg1, reg2);
4374 __ mls(out.AsRegister<Register>(), temp, reg2, reg1);
4375 } else {
4376 InvokeRuntimeCallingConvention calling_convention;
4377 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>());
4378 DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>());
4379 DCHECK_EQ(R1, out.AsRegister<Register>());
4380
4381 codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc());
4382 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
4383 }
4384 break;
4385 }
4386
4387 case Primitive::kPrimLong: {
4388 codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc());
4389 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
4390 break;
4391 }
4392
4393 case Primitive::kPrimFloat: {
4394 codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc());
4395 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
4396 break;
4397 }
4398
4399 case Primitive::kPrimDouble: {
4400 codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc());
4401 CheckEntrypointTypes<kQuickFmod, double, double, double>();
4402 break;
4403 }
4404
4405 default:
4406 LOG(FATAL) << "Unexpected rem type " << type;
4407 }
4408 }
4409
VisitDivZeroCheck(HDivZeroCheck * instruction)4410 void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) {
4411 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
4412 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
4413 }
4414
VisitDivZeroCheck(HDivZeroCheck * instruction)4415 void InstructionCodeGeneratorARM::VisitDivZeroCheck(HDivZeroCheck* instruction) {
4416 SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) DivZeroCheckSlowPathARM(instruction);
4417 codegen_->AddSlowPath(slow_path);
4418
4419 LocationSummary* locations = instruction->GetLocations();
4420 Location value = locations->InAt(0);
4421
4422 switch (instruction->GetType()) {
4423 case Primitive::kPrimBoolean:
4424 case Primitive::kPrimByte:
4425 case Primitive::kPrimChar:
4426 case Primitive::kPrimShort:
4427 case Primitive::kPrimInt: {
4428 if (value.IsRegister()) {
4429 __ CompareAndBranchIfZero(value.AsRegister<Register>(), slow_path->GetEntryLabel());
4430 } else {
4431 DCHECK(value.IsConstant()) << value;
4432 if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
4433 __ b(slow_path->GetEntryLabel());
4434 }
4435 }
4436 break;
4437 }
4438 case Primitive::kPrimLong: {
4439 if (value.IsRegisterPair()) {
4440 __ orrs(IP,
4441 value.AsRegisterPairLow<Register>(),
4442 ShifterOperand(value.AsRegisterPairHigh<Register>()));
4443 __ b(slow_path->GetEntryLabel(), EQ);
4444 } else {
4445 DCHECK(value.IsConstant()) << value;
4446 if (value.GetConstant()->AsLongConstant()->GetValue() == 0) {
4447 __ b(slow_path->GetEntryLabel());
4448 }
4449 }
4450 break;
4451 default:
4452 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
4453 }
4454 }
4455 }
4456
HandleIntegerRotate(LocationSummary * locations)4457 void InstructionCodeGeneratorARM::HandleIntegerRotate(LocationSummary* locations) {
4458 Register in = locations->InAt(0).AsRegister<Register>();
4459 Location rhs = locations->InAt(1);
4460 Register out = locations->Out().AsRegister<Register>();
4461
4462 if (rhs.IsConstant()) {
4463 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
4464 // so map all rotations to a +ve. equivalent in that range.
4465 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
4466 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
4467 if (rot) {
4468 // Rotate, mapping left rotations to right equivalents if necessary.
4469 // (e.g. left by 2 bits == right by 30.)
4470 __ Ror(out, in, rot);
4471 } else if (out != in) {
4472 __ Mov(out, in);
4473 }
4474 } else {
4475 __ Ror(out, in, rhs.AsRegister<Register>());
4476 }
4477 }
4478
4479 // Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
4480 // rotates by swapping input regs (effectively rotating by the first 32-bits of
4481 // a larger rotation) or flipping direction (thus treating larger right/left
4482 // rotations as sub-word sized rotations in the other direction) as appropriate.
HandleLongRotate(HRor * ror)4483 void InstructionCodeGeneratorARM::HandleLongRotate(HRor* ror) {
4484 LocationSummary* locations = ror->GetLocations();
4485 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
4486 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
4487 Location rhs = locations->InAt(1);
4488 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
4489 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
4490
4491 if (rhs.IsConstant()) {
4492 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
4493 // Map all rotations to +ve. equivalents on the interval [0,63].
4494 rot &= kMaxLongShiftDistance;
4495 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
4496 // logic below to a simple pair of binary orr.
4497 // (e.g. 34 bits == in_reg swap + 2 bits right.)
4498 if (rot >= kArmBitsPerWord) {
4499 rot -= kArmBitsPerWord;
4500 std::swap(in_reg_hi, in_reg_lo);
4501 }
4502 // Rotate, or mov to out for zero or word size rotations.
4503 if (rot != 0u) {
4504 __ Lsr(out_reg_hi, in_reg_hi, rot);
4505 __ orr(out_reg_hi, out_reg_hi, ShifterOperand(in_reg_lo, arm::LSL, kArmBitsPerWord - rot));
4506 __ Lsr(out_reg_lo, in_reg_lo, rot);
4507 __ orr(out_reg_lo, out_reg_lo, ShifterOperand(in_reg_hi, arm::LSL, kArmBitsPerWord - rot));
4508 } else {
4509 __ Mov(out_reg_lo, in_reg_lo);
4510 __ Mov(out_reg_hi, in_reg_hi);
4511 }
4512 } else {
4513 Register shift_right = locations->GetTemp(0).AsRegister<Register>();
4514 Register shift_left = locations->GetTemp(1).AsRegister<Register>();
4515 Label end;
4516 Label shift_by_32_plus_shift_right;
4517 Label* final_label = codegen_->GetFinalLabel(ror, &end);
4518
4519 __ and_(shift_right, rhs.AsRegister<Register>(), ShifterOperand(0x1F));
4520 __ Lsrs(shift_left, rhs.AsRegister<Register>(), 6);
4521 __ rsb(shift_left, shift_right, ShifterOperand(kArmBitsPerWord), AL, kCcKeep);
4522 __ b(&shift_by_32_plus_shift_right, CC);
4523
4524 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
4525 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
4526 __ Lsl(out_reg_hi, in_reg_hi, shift_left);
4527 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
4528 __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo));
4529 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
4530 __ Lsr(shift_left, in_reg_hi, shift_right);
4531 __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_left));
4532 __ b(final_label);
4533
4534 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right.
4535 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
4536 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
4537 __ Lsr(out_reg_hi, in_reg_hi, shift_right);
4538 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
4539 __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo));
4540 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
4541 __ Lsl(shift_right, in_reg_hi, shift_left);
4542 __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_right));
4543
4544 if (end.IsLinked()) {
4545 __ Bind(&end);
4546 }
4547 }
4548 }
4549
VisitRor(HRor * ror)4550 void LocationsBuilderARM::VisitRor(HRor* ror) {
4551 LocationSummary* locations =
4552 new (GetGraph()->GetArena()) LocationSummary(ror, LocationSummary::kNoCall);
4553 switch (ror->GetResultType()) {
4554 case Primitive::kPrimInt: {
4555 locations->SetInAt(0, Location::RequiresRegister());
4556 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
4557 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4558 break;
4559 }
4560 case Primitive::kPrimLong: {
4561 locations->SetInAt(0, Location::RequiresRegister());
4562 if (ror->InputAt(1)->IsConstant()) {
4563 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
4564 } else {
4565 locations->SetInAt(1, Location::RequiresRegister());
4566 locations->AddTemp(Location::RequiresRegister());
4567 locations->AddTemp(Location::RequiresRegister());
4568 }
4569 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4570 break;
4571 }
4572 default:
4573 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
4574 }
4575 }
4576
VisitRor(HRor * ror)4577 void InstructionCodeGeneratorARM::VisitRor(HRor* ror) {
4578 LocationSummary* locations = ror->GetLocations();
4579 Primitive::Type type = ror->GetResultType();
4580 switch (type) {
4581 case Primitive::kPrimInt: {
4582 HandleIntegerRotate(locations);
4583 break;
4584 }
4585 case Primitive::kPrimLong: {
4586 HandleLongRotate(ror);
4587 break;
4588 }
4589 default:
4590 LOG(FATAL) << "Unexpected operation type " << type;
4591 UNREACHABLE();
4592 }
4593 }
4594
HandleShift(HBinaryOperation * op)4595 void LocationsBuilderARM::HandleShift(HBinaryOperation* op) {
4596 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
4597
4598 LocationSummary* locations =
4599 new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall);
4600
4601 switch (op->GetResultType()) {
4602 case Primitive::kPrimInt: {
4603 locations->SetInAt(0, Location::RequiresRegister());
4604 if (op->InputAt(1)->IsConstant()) {
4605 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
4606 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4607 } else {
4608 locations->SetInAt(1, Location::RequiresRegister());
4609 // Make the output overlap, as it will be used to hold the masked
4610 // second input.
4611 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4612 }
4613 break;
4614 }
4615 case Primitive::kPrimLong: {
4616 locations->SetInAt(0, Location::RequiresRegister());
4617 if (op->InputAt(1)->IsConstant()) {
4618 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
4619 // For simplicity, use kOutputOverlap even though we only require that low registers
4620 // don't clash with high registers which the register allocator currently guarantees.
4621 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4622 } else {
4623 locations->SetInAt(1, Location::RequiresRegister());
4624 locations->AddTemp(Location::RequiresRegister());
4625 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4626 }
4627 break;
4628 }
4629 default:
4630 LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
4631 }
4632 }
4633
HandleShift(HBinaryOperation * op)4634 void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) {
4635 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
4636
4637 LocationSummary* locations = op->GetLocations();
4638 Location out = locations->Out();
4639 Location first = locations->InAt(0);
4640 Location second = locations->InAt(1);
4641
4642 Primitive::Type type = op->GetResultType();
4643 switch (type) {
4644 case Primitive::kPrimInt: {
4645 Register out_reg = out.AsRegister<Register>();
4646 Register first_reg = first.AsRegister<Register>();
4647 if (second.IsRegister()) {
4648 Register second_reg = second.AsRegister<Register>();
4649 // ARM doesn't mask the shift count so we need to do it ourselves.
4650 __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftDistance));
4651 if (op->IsShl()) {
4652 __ Lsl(out_reg, first_reg, out_reg);
4653 } else if (op->IsShr()) {
4654 __ Asr(out_reg, first_reg, out_reg);
4655 } else {
4656 __ Lsr(out_reg, first_reg, out_reg);
4657 }
4658 } else {
4659 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
4660 uint32_t shift_value = cst & kMaxIntShiftDistance;
4661 if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
4662 __ Mov(out_reg, first_reg);
4663 } else if (op->IsShl()) {
4664 __ Lsl(out_reg, first_reg, shift_value);
4665 } else if (op->IsShr()) {
4666 __ Asr(out_reg, first_reg, shift_value);
4667 } else {
4668 __ Lsr(out_reg, first_reg, shift_value);
4669 }
4670 }
4671 break;
4672 }
4673 case Primitive::kPrimLong: {
4674 Register o_h = out.AsRegisterPairHigh<Register>();
4675 Register o_l = out.AsRegisterPairLow<Register>();
4676
4677 Register high = first.AsRegisterPairHigh<Register>();
4678 Register low = first.AsRegisterPairLow<Register>();
4679
4680 if (second.IsRegister()) {
4681 Register temp = locations->GetTemp(0).AsRegister<Register>();
4682
4683 Register second_reg = second.AsRegister<Register>();
4684
4685 if (op->IsShl()) {
4686 __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftDistance));
4687 // Shift the high part
4688 __ Lsl(o_h, high, o_l);
4689 // Shift the low part and `or` what overflew on the high part
4690 __ rsb(temp, o_l, ShifterOperand(kArmBitsPerWord));
4691 __ Lsr(temp, low, temp);
4692 __ orr(o_h, o_h, ShifterOperand(temp));
4693 // If the shift is > 32 bits, override the high part
4694 __ subs(temp, o_l, ShifterOperand(kArmBitsPerWord));
4695 __ it(PL);
4696 __ Lsl(o_h, low, temp, PL);
4697 // Shift the low part
4698 __ Lsl(o_l, low, o_l);
4699 } else if (op->IsShr()) {
4700 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftDistance));
4701 // Shift the low part
4702 __ Lsr(o_l, low, o_h);
4703 // Shift the high part and `or` what underflew on the low part
4704 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord));
4705 __ Lsl(temp, high, temp);
4706 __ orr(o_l, o_l, ShifterOperand(temp));
4707 // If the shift is > 32 bits, override the low part
4708 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord));
4709 __ it(PL);
4710 __ Asr(o_l, high, temp, PL);
4711 // Shift the high part
4712 __ Asr(o_h, high, o_h);
4713 } else {
4714 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftDistance));
4715 // same as Shr except we use `Lsr`s and not `Asr`s
4716 __ Lsr(o_l, low, o_h);
4717 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord));
4718 __ Lsl(temp, high, temp);
4719 __ orr(o_l, o_l, ShifterOperand(temp));
4720 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord));
4721 __ it(PL);
4722 __ Lsr(o_l, high, temp, PL);
4723 __ Lsr(o_h, high, o_h);
4724 }
4725 } else {
4726 // Register allocator doesn't create partial overlap.
4727 DCHECK_NE(o_l, high);
4728 DCHECK_NE(o_h, low);
4729 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
4730 uint32_t shift_value = cst & kMaxLongShiftDistance;
4731 if (shift_value > 32) {
4732 if (op->IsShl()) {
4733 __ Lsl(o_h, low, shift_value - 32);
4734 __ LoadImmediate(o_l, 0);
4735 } else if (op->IsShr()) {
4736 __ Asr(o_l, high, shift_value - 32);
4737 __ Asr(o_h, high, 31);
4738 } else {
4739 __ Lsr(o_l, high, shift_value - 32);
4740 __ LoadImmediate(o_h, 0);
4741 }
4742 } else if (shift_value == 32) {
4743 if (op->IsShl()) {
4744 __ mov(o_h, ShifterOperand(low));
4745 __ LoadImmediate(o_l, 0);
4746 } else if (op->IsShr()) {
4747 __ mov(o_l, ShifterOperand(high));
4748 __ Asr(o_h, high, 31);
4749 } else {
4750 __ mov(o_l, ShifterOperand(high));
4751 __ LoadImmediate(o_h, 0);
4752 }
4753 } else if (shift_value == 1) {
4754 if (op->IsShl()) {
4755 __ Lsls(o_l, low, 1);
4756 __ adc(o_h, high, ShifterOperand(high));
4757 } else if (op->IsShr()) {
4758 __ Asrs(o_h, high, 1);
4759 __ Rrx(o_l, low);
4760 } else {
4761 __ Lsrs(o_h, high, 1);
4762 __ Rrx(o_l, low);
4763 }
4764 } else {
4765 DCHECK(2 <= shift_value && shift_value < 32) << shift_value;
4766 if (op->IsShl()) {
4767 __ Lsl(o_h, high, shift_value);
4768 __ orr(o_h, o_h, ShifterOperand(low, LSR, 32 - shift_value));
4769 __ Lsl(o_l, low, shift_value);
4770 } else if (op->IsShr()) {
4771 __ Lsr(o_l, low, shift_value);
4772 __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value));
4773 __ Asr(o_h, high, shift_value);
4774 } else {
4775 __ Lsr(o_l, low, shift_value);
4776 __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value));
4777 __ Lsr(o_h, high, shift_value);
4778 }
4779 }
4780 }
4781 break;
4782 }
4783 default:
4784 LOG(FATAL) << "Unexpected operation type " << type;
4785 UNREACHABLE();
4786 }
4787 }
4788
VisitShl(HShl * shl)4789 void LocationsBuilderARM::VisitShl(HShl* shl) {
4790 HandleShift(shl);
4791 }
4792
VisitShl(HShl * shl)4793 void InstructionCodeGeneratorARM::VisitShl(HShl* shl) {
4794 HandleShift(shl);
4795 }
4796
VisitShr(HShr * shr)4797 void LocationsBuilderARM::VisitShr(HShr* shr) {
4798 HandleShift(shr);
4799 }
4800
VisitShr(HShr * shr)4801 void InstructionCodeGeneratorARM::VisitShr(HShr* shr) {
4802 HandleShift(shr);
4803 }
4804
VisitUShr(HUShr * ushr)4805 void LocationsBuilderARM::VisitUShr(HUShr* ushr) {
4806 HandleShift(ushr);
4807 }
4808
VisitUShr(HUShr * ushr)4809 void InstructionCodeGeneratorARM::VisitUShr(HUShr* ushr) {
4810 HandleShift(ushr);
4811 }
4812
VisitNewInstance(HNewInstance * instruction)4813 void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
4814 LocationSummary* locations =
4815 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
4816 if (instruction->IsStringAlloc()) {
4817 locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument));
4818 } else {
4819 InvokeRuntimeCallingConvention calling_convention;
4820 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
4821 }
4822 locations->SetOut(Location::RegisterLocation(R0));
4823 }
4824
VisitNewInstance(HNewInstance * instruction)4825 void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
4826 // Note: if heap poisoning is enabled, the entry point takes cares
4827 // of poisoning the reference.
4828 if (instruction->IsStringAlloc()) {
4829 // String is allocated through StringFactory. Call NewEmptyString entry point.
4830 Register temp = instruction->GetLocations()->GetTemp(0).AsRegister<Register>();
4831 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
4832 __ LoadFromOffset(kLoadWord, temp, TR, QUICK_ENTRY_POINT(pNewEmptyString));
4833 __ LoadFromOffset(kLoadWord, LR, temp, code_offset.Int32Value());
4834 __ blx(LR);
4835 codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
4836 } else {
4837 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
4838 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
4839 }
4840 }
4841
VisitNewArray(HNewArray * instruction)4842 void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) {
4843 LocationSummary* locations =
4844 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
4845 InvokeRuntimeCallingConvention calling_convention;
4846 locations->SetOut(Location::RegisterLocation(R0));
4847 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
4848 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
4849 }
4850
VisitNewArray(HNewArray * instruction)4851 void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) {
4852 // Note: if heap poisoning is enabled, the entry point takes cares
4853 // of poisoning the reference.
4854 QuickEntrypointEnum entrypoint =
4855 CodeGenerator::GetArrayAllocationEntrypoint(instruction->GetLoadClass()->GetClass());
4856 codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
4857 CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
4858 DCHECK(!codegen_->IsLeafMethod());
4859 }
4860
VisitParameterValue(HParameterValue * instruction)4861 void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
4862 LocationSummary* locations =
4863 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4864 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
4865 if (location.IsStackSlot()) {
4866 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
4867 } else if (location.IsDoubleStackSlot()) {
4868 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
4869 }
4870 locations->SetOut(location);
4871 }
4872
VisitParameterValue(HParameterValue * instruction ATTRIBUTE_UNUSED)4873 void InstructionCodeGeneratorARM::VisitParameterValue(
4874 HParameterValue* instruction ATTRIBUTE_UNUSED) {
4875 // Nothing to do, the parameter is already at its location.
4876 }
4877
VisitCurrentMethod(HCurrentMethod * instruction)4878 void LocationsBuilderARM::VisitCurrentMethod(HCurrentMethod* instruction) {
4879 LocationSummary* locations =
4880 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4881 locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument));
4882 }
4883
VisitCurrentMethod(HCurrentMethod * instruction ATTRIBUTE_UNUSED)4884 void InstructionCodeGeneratorARM::VisitCurrentMethod(HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
4885 // Nothing to do, the method is already at its location.
4886 }
4887
VisitNot(HNot * not_)4888 void LocationsBuilderARM::VisitNot(HNot* not_) {
4889 LocationSummary* locations =
4890 new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
4891 locations->SetInAt(0, Location::RequiresRegister());
4892 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4893 }
4894
VisitNot(HNot * not_)4895 void InstructionCodeGeneratorARM::VisitNot(HNot* not_) {
4896 LocationSummary* locations = not_->GetLocations();
4897 Location out = locations->Out();
4898 Location in = locations->InAt(0);
4899 switch (not_->GetResultType()) {
4900 case Primitive::kPrimInt:
4901 __ mvn(out.AsRegister<Register>(), ShifterOperand(in.AsRegister<Register>()));
4902 break;
4903
4904 case Primitive::kPrimLong:
4905 __ mvn(out.AsRegisterPairLow<Register>(),
4906 ShifterOperand(in.AsRegisterPairLow<Register>()));
4907 __ mvn(out.AsRegisterPairHigh<Register>(),
4908 ShifterOperand(in.AsRegisterPairHigh<Register>()));
4909 break;
4910
4911 default:
4912 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
4913 }
4914 }
4915
VisitBooleanNot(HBooleanNot * bool_not)4916 void LocationsBuilderARM::VisitBooleanNot(HBooleanNot* bool_not) {
4917 LocationSummary* locations =
4918 new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall);
4919 locations->SetInAt(0, Location::RequiresRegister());
4920 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4921 }
4922
VisitBooleanNot(HBooleanNot * bool_not)4923 void InstructionCodeGeneratorARM::VisitBooleanNot(HBooleanNot* bool_not) {
4924 LocationSummary* locations = bool_not->GetLocations();
4925 Location out = locations->Out();
4926 Location in = locations->InAt(0);
4927 __ eor(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(1));
4928 }
4929
VisitCompare(HCompare * compare)4930 void LocationsBuilderARM::VisitCompare(HCompare* compare) {
4931 LocationSummary* locations =
4932 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
4933 switch (compare->InputAt(0)->GetType()) {
4934 case Primitive::kPrimBoolean:
4935 case Primitive::kPrimByte:
4936 case Primitive::kPrimShort:
4937 case Primitive::kPrimChar:
4938 case Primitive::kPrimInt:
4939 case Primitive::kPrimLong: {
4940 locations->SetInAt(0, Location::RequiresRegister());
4941 locations->SetInAt(1, Location::RequiresRegister());
4942 // Output overlaps because it is written before doing the low comparison.
4943 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4944 break;
4945 }
4946 case Primitive::kPrimFloat:
4947 case Primitive::kPrimDouble: {
4948 locations->SetInAt(0, Location::RequiresFpuRegister());
4949 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
4950 locations->SetOut(Location::RequiresRegister());
4951 break;
4952 }
4953 default:
4954 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
4955 }
4956 }
4957
VisitCompare(HCompare * compare)4958 void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) {
4959 LocationSummary* locations = compare->GetLocations();
4960 Register out = locations->Out().AsRegister<Register>();
4961 Location left = locations->InAt(0);
4962 Location right = locations->InAt(1);
4963
4964 Label less, greater, done;
4965 Label* final_label = codegen_->GetFinalLabel(compare, &done);
4966 Primitive::Type type = compare->InputAt(0)->GetType();
4967 Condition less_cond;
4968 switch (type) {
4969 case Primitive::kPrimBoolean:
4970 case Primitive::kPrimByte:
4971 case Primitive::kPrimShort:
4972 case Primitive::kPrimChar:
4973 case Primitive::kPrimInt: {
4974 __ LoadImmediate(out, 0);
4975 __ cmp(left.AsRegister<Register>(),
4976 ShifterOperand(right.AsRegister<Register>())); // Signed compare.
4977 less_cond = LT;
4978 break;
4979 }
4980 case Primitive::kPrimLong: {
4981 __ cmp(left.AsRegisterPairHigh<Register>(),
4982 ShifterOperand(right.AsRegisterPairHigh<Register>())); // Signed compare.
4983 __ b(&less, LT);
4984 __ b(&greater, GT);
4985 // Do LoadImmediate before the last `cmp`, as LoadImmediate might affect the status flags.
4986 __ LoadImmediate(out, 0);
4987 __ cmp(left.AsRegisterPairLow<Register>(),
4988 ShifterOperand(right.AsRegisterPairLow<Register>())); // Unsigned compare.
4989 less_cond = LO;
4990 break;
4991 }
4992 case Primitive::kPrimFloat:
4993 case Primitive::kPrimDouble: {
4994 __ LoadImmediate(out, 0);
4995 GenerateVcmp(compare, codegen_);
4996 __ vmstat(); // transfer FP status register to ARM APSR.
4997 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
4998 break;
4999 }
5000 default:
5001 LOG(FATAL) << "Unexpected compare type " << type;
5002 UNREACHABLE();
5003 }
5004
5005 __ b(final_label, EQ);
5006 __ b(&less, less_cond);
5007
5008 __ Bind(&greater);
5009 __ LoadImmediate(out, 1);
5010 __ b(final_label);
5011
5012 __ Bind(&less);
5013 __ LoadImmediate(out, -1);
5014
5015 if (done.IsLinked()) {
5016 __ Bind(&done);
5017 }
5018 }
5019
VisitPhi(HPhi * instruction)5020 void LocationsBuilderARM::VisitPhi(HPhi* instruction) {
5021 LocationSummary* locations =
5022 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5023 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
5024 locations->SetInAt(i, Location::Any());
5025 }
5026 locations->SetOut(Location::Any());
5027 }
5028
VisitPhi(HPhi * instruction ATTRIBUTE_UNUSED)5029 void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
5030 LOG(FATAL) << "Unreachable";
5031 }
5032
GenerateMemoryBarrier(MemBarrierKind kind)5033 void CodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) {
5034 // TODO (ported from quick): revisit ARM barrier kinds.
5035 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
5036 switch (kind) {
5037 case MemBarrierKind::kAnyStore:
5038 case MemBarrierKind::kLoadAny:
5039 case MemBarrierKind::kAnyAny: {
5040 flavor = DmbOptions::ISH;
5041 break;
5042 }
5043 case MemBarrierKind::kStoreStore: {
5044 flavor = DmbOptions::ISHST;
5045 break;
5046 }
5047 default:
5048 LOG(FATAL) << "Unexpected memory barrier " << kind;
5049 }
5050 __ dmb(flavor);
5051 }
5052
GenerateWideAtomicLoad(Register addr,uint32_t offset,Register out_lo,Register out_hi)5053 void InstructionCodeGeneratorARM::GenerateWideAtomicLoad(Register addr,
5054 uint32_t offset,
5055 Register out_lo,
5056 Register out_hi) {
5057 if (offset != 0) {
5058 // Ensure `out_lo` is different from `addr`, so that loading
5059 // `offset` into `out_lo` does not clutter `addr`.
5060 DCHECK_NE(out_lo, addr);
5061 __ LoadImmediate(out_lo, offset);
5062 __ add(IP, addr, ShifterOperand(out_lo));
5063 addr = IP;
5064 }
5065 __ ldrexd(out_lo, out_hi, addr);
5066 }
5067
GenerateWideAtomicStore(Register addr,uint32_t offset,Register value_lo,Register value_hi,Register temp1,Register temp2,HInstruction * instruction)5068 void InstructionCodeGeneratorARM::GenerateWideAtomicStore(Register addr,
5069 uint32_t offset,
5070 Register value_lo,
5071 Register value_hi,
5072 Register temp1,
5073 Register temp2,
5074 HInstruction* instruction) {
5075 Label fail;
5076 if (offset != 0) {
5077 __ LoadImmediate(temp1, offset);
5078 __ add(IP, addr, ShifterOperand(temp1));
5079 addr = IP;
5080 }
5081 __ Bind(&fail);
5082 // We need a load followed by store. (The address used in a STREX instruction must
5083 // be the same as the address in the most recently executed LDREX instruction.)
5084 __ ldrexd(temp1, temp2, addr);
5085 codegen_->MaybeRecordImplicitNullCheck(instruction);
5086 __ strexd(temp1, value_lo, value_hi, addr);
5087 __ CompareAndBranchIfNonZero(temp1, &fail);
5088 }
5089
HandleFieldSet(HInstruction * instruction,const FieldInfo & field_info)5090 void LocationsBuilderARM::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) {
5091 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5092
5093 LocationSummary* locations =
5094 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5095 locations->SetInAt(0, Location::RequiresRegister());
5096
5097 Primitive::Type field_type = field_info.GetFieldType();
5098 if (Primitive::IsFloatingPointType(field_type)) {
5099 locations->SetInAt(1, Location::RequiresFpuRegister());
5100 } else {
5101 locations->SetInAt(1, Location::RequiresRegister());
5102 }
5103
5104 bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble;
5105 bool generate_volatile = field_info.IsVolatile()
5106 && is_wide
5107 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5108 bool needs_write_barrier =
5109 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5110 // Temporary registers for the write barrier.
5111 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
5112 if (needs_write_barrier) {
5113 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
5114 locations->AddTemp(Location::RequiresRegister());
5115 } else if (generate_volatile) {
5116 // ARM encoding have some additional constraints for ldrexd/strexd:
5117 // - registers need to be consecutive
5118 // - the first register should be even but not R14.
5119 // We don't test for ARM yet, and the assertion makes sure that we
5120 // revisit this if we ever enable ARM encoding.
5121 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5122
5123 locations->AddTemp(Location::RequiresRegister());
5124 locations->AddTemp(Location::RequiresRegister());
5125 if (field_type == Primitive::kPrimDouble) {
5126 // For doubles we need two more registers to copy the value.
5127 locations->AddTemp(Location::RegisterLocation(R2));
5128 locations->AddTemp(Location::RegisterLocation(R3));
5129 }
5130 }
5131 }
5132
HandleFieldSet(HInstruction * instruction,const FieldInfo & field_info,bool value_can_be_null)5133 void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction,
5134 const FieldInfo& field_info,
5135 bool value_can_be_null) {
5136 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5137
5138 LocationSummary* locations = instruction->GetLocations();
5139 Register base = locations->InAt(0).AsRegister<Register>();
5140 Location value = locations->InAt(1);
5141
5142 bool is_volatile = field_info.IsVolatile();
5143 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5144 Primitive::Type field_type = field_info.GetFieldType();
5145 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
5146 bool needs_write_barrier =
5147 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5148
5149 if (is_volatile) {
5150 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
5151 }
5152
5153 switch (field_type) {
5154 case Primitive::kPrimBoolean:
5155 case Primitive::kPrimByte: {
5156 __ StoreToOffset(kStoreByte, value.AsRegister<Register>(), base, offset);
5157 break;
5158 }
5159
5160 case Primitive::kPrimShort:
5161 case Primitive::kPrimChar: {
5162 __ StoreToOffset(kStoreHalfword, value.AsRegister<Register>(), base, offset);
5163 break;
5164 }
5165
5166 case Primitive::kPrimInt:
5167 case Primitive::kPrimNot: {
5168 if (kPoisonHeapReferences && needs_write_barrier) {
5169 // Note that in the case where `value` is a null reference,
5170 // we do not enter this block, as a null reference does not
5171 // need poisoning.
5172 DCHECK_EQ(field_type, Primitive::kPrimNot);
5173 Register temp = locations->GetTemp(0).AsRegister<Register>();
5174 __ Mov(temp, value.AsRegister<Register>());
5175 __ PoisonHeapReference(temp);
5176 __ StoreToOffset(kStoreWord, temp, base, offset);
5177 } else {
5178 __ StoreToOffset(kStoreWord, value.AsRegister<Register>(), base, offset);
5179 }
5180 break;
5181 }
5182
5183 case Primitive::kPrimLong: {
5184 if (is_volatile && !atomic_ldrd_strd) {
5185 GenerateWideAtomicStore(base, offset,
5186 value.AsRegisterPairLow<Register>(),
5187 value.AsRegisterPairHigh<Register>(),
5188 locations->GetTemp(0).AsRegister<Register>(),
5189 locations->GetTemp(1).AsRegister<Register>(),
5190 instruction);
5191 } else {
5192 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), base, offset);
5193 codegen_->MaybeRecordImplicitNullCheck(instruction);
5194 }
5195 break;
5196 }
5197
5198 case Primitive::kPrimFloat: {
5199 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), base, offset);
5200 break;
5201 }
5202
5203 case Primitive::kPrimDouble: {
5204 DRegister value_reg = FromLowSToD(value.AsFpuRegisterPairLow<SRegister>());
5205 if (is_volatile && !atomic_ldrd_strd) {
5206 Register value_reg_lo = locations->GetTemp(0).AsRegister<Register>();
5207 Register value_reg_hi = locations->GetTemp(1).AsRegister<Register>();
5208
5209 __ vmovrrd(value_reg_lo, value_reg_hi, value_reg);
5210
5211 GenerateWideAtomicStore(base, offset,
5212 value_reg_lo,
5213 value_reg_hi,
5214 locations->GetTemp(2).AsRegister<Register>(),
5215 locations->GetTemp(3).AsRegister<Register>(),
5216 instruction);
5217 } else {
5218 __ StoreDToOffset(value_reg, base, offset);
5219 codegen_->MaybeRecordImplicitNullCheck(instruction);
5220 }
5221 break;
5222 }
5223
5224 case Primitive::kPrimVoid:
5225 LOG(FATAL) << "Unreachable type " << field_type;
5226 UNREACHABLE();
5227 }
5228
5229 // Longs and doubles are handled in the switch.
5230 if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) {
5231 codegen_->MaybeRecordImplicitNullCheck(instruction);
5232 }
5233
5234 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
5235 Register temp = locations->GetTemp(0).AsRegister<Register>();
5236 Register card = locations->GetTemp(1).AsRegister<Register>();
5237 codegen_->MarkGCCard(
5238 temp, card, base, value.AsRegister<Register>(), value_can_be_null);
5239 }
5240
5241 if (is_volatile) {
5242 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
5243 }
5244 }
5245
HandleFieldGet(HInstruction * instruction,const FieldInfo & field_info)5246 void LocationsBuilderARM::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) {
5247 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
5248
5249 bool object_field_get_with_read_barrier =
5250 kEmitCompilerReadBarrier && (field_info.GetFieldType() == Primitive::kPrimNot);
5251 LocationSummary* locations =
5252 new (GetGraph()->GetArena()) LocationSummary(instruction,
5253 object_field_get_with_read_barrier ?
5254 LocationSummary::kCallOnSlowPath :
5255 LocationSummary::kNoCall);
5256 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
5257 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
5258 }
5259 locations->SetInAt(0, Location::RequiresRegister());
5260
5261 bool volatile_for_double = field_info.IsVolatile()
5262 && (field_info.GetFieldType() == Primitive::kPrimDouble)
5263 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5264 // The output overlaps in case of volatile long: we don't want the
5265 // code generated by GenerateWideAtomicLoad to overwrite the
5266 // object's location. Likewise, in the case of an object field get
5267 // with read barriers enabled, we do not want the load to overwrite
5268 // the object's location, as we need it to emit the read barrier.
5269 bool overlap = (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) ||
5270 object_field_get_with_read_barrier;
5271
5272 if (Primitive::IsFloatingPointType(instruction->GetType())) {
5273 locations->SetOut(Location::RequiresFpuRegister());
5274 } else {
5275 locations->SetOut(Location::RequiresRegister(),
5276 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
5277 }
5278 if (volatile_for_double) {
5279 // ARM encoding have some additional constraints for ldrexd/strexd:
5280 // - registers need to be consecutive
5281 // - the first register should be even but not R14.
5282 // We don't test for ARM yet, and the assertion makes sure that we
5283 // revisit this if we ever enable ARM encoding.
5284 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5285 locations->AddTemp(Location::RequiresRegister());
5286 locations->AddTemp(Location::RequiresRegister());
5287 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
5288 // We need a temporary register for the read barrier marking slow
5289 // path in CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier.
5290 locations->AddTemp(Location::RequiresRegister());
5291 }
5292 }
5293
ArithmeticZeroOrFpuRegister(HInstruction * input)5294 Location LocationsBuilderARM::ArithmeticZeroOrFpuRegister(HInstruction* input) {
5295 DCHECK(input->GetType() == Primitive::kPrimDouble || input->GetType() == Primitive::kPrimFloat)
5296 << input->GetType();
5297 if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
5298 (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
5299 return Location::ConstantLocation(input->AsConstant());
5300 } else {
5301 return Location::RequiresFpuRegister();
5302 }
5303 }
5304
ArmEncodableConstantOrRegister(HInstruction * constant,Opcode opcode)5305 Location LocationsBuilderARM::ArmEncodableConstantOrRegister(HInstruction* constant,
5306 Opcode opcode) {
5307 DCHECK(!Primitive::IsFloatingPointType(constant->GetType()));
5308 if (constant->IsConstant() &&
5309 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
5310 return Location::ConstantLocation(constant->AsConstant());
5311 }
5312 return Location::RequiresRegister();
5313 }
5314
CanEncodeConstantAsImmediate(HConstant * input_cst,Opcode opcode)5315 bool LocationsBuilderARM::CanEncodeConstantAsImmediate(HConstant* input_cst,
5316 Opcode opcode) {
5317 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
5318 if (Primitive::Is64BitType(input_cst->GetType())) {
5319 Opcode high_opcode = opcode;
5320 SetCc low_set_cc = kCcDontCare;
5321 switch (opcode) {
5322 case SUB:
5323 // Flip the operation to an ADD.
5324 value = -value;
5325 opcode = ADD;
5326 FALLTHROUGH_INTENDED;
5327 case ADD:
5328 if (Low32Bits(value) == 0u) {
5329 return CanEncodeConstantAsImmediate(High32Bits(value), opcode, kCcDontCare);
5330 }
5331 high_opcode = ADC;
5332 low_set_cc = kCcSet;
5333 break;
5334 default:
5335 break;
5336 }
5337 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode, low_set_cc) &&
5338 CanEncodeConstantAsImmediate(High32Bits(value), high_opcode, kCcDontCare);
5339 } else {
5340 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode);
5341 }
5342 }
5343
CanEncodeConstantAsImmediate(uint32_t value,Opcode opcode,SetCc set_cc)5344 bool LocationsBuilderARM::CanEncodeConstantAsImmediate(uint32_t value,
5345 Opcode opcode,
5346 SetCc set_cc) {
5347 ShifterOperand so;
5348 ArmAssembler* assembler = codegen_->GetAssembler();
5349 if (assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, opcode, value, set_cc, &so)) {
5350 return true;
5351 }
5352 Opcode neg_opcode = kNoOperand;
5353 uint32_t neg_value = 0;
5354 switch (opcode) {
5355 case AND: neg_opcode = BIC; neg_value = ~value; break;
5356 case ORR: neg_opcode = ORN; neg_value = ~value; break;
5357 case ADD: neg_opcode = SUB; neg_value = -value; break;
5358 case ADC: neg_opcode = SBC; neg_value = ~value; break;
5359 case SUB: neg_opcode = ADD; neg_value = -value; break;
5360 case SBC: neg_opcode = ADC; neg_value = ~value; break;
5361 case MOV: neg_opcode = MVN; neg_value = ~value; break;
5362 default:
5363 return false;
5364 }
5365
5366 if (assembler->ShifterOperandCanHold(kNoRegister,
5367 kNoRegister,
5368 neg_opcode,
5369 neg_value,
5370 set_cc,
5371 &so)) {
5372 return true;
5373 }
5374
5375 return opcode == AND && IsPowerOfTwo(value + 1);
5376 }
5377
HandleFieldGet(HInstruction * instruction,const FieldInfo & field_info)5378 void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction,
5379 const FieldInfo& field_info) {
5380 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
5381
5382 LocationSummary* locations = instruction->GetLocations();
5383 Location base_loc = locations->InAt(0);
5384 Register base = base_loc.AsRegister<Register>();
5385 Location out = locations->Out();
5386 bool is_volatile = field_info.IsVolatile();
5387 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5388 Primitive::Type field_type = field_info.GetFieldType();
5389 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
5390
5391 switch (field_type) {
5392 case Primitive::kPrimBoolean:
5393 __ LoadFromOffset(kLoadUnsignedByte, out.AsRegister<Register>(), base, offset);
5394 break;
5395
5396 case Primitive::kPrimByte:
5397 __ LoadFromOffset(kLoadSignedByte, out.AsRegister<Register>(), base, offset);
5398 break;
5399
5400 case Primitive::kPrimShort:
5401 __ LoadFromOffset(kLoadSignedHalfword, out.AsRegister<Register>(), base, offset);
5402 break;
5403
5404 case Primitive::kPrimChar:
5405 __ LoadFromOffset(kLoadUnsignedHalfword, out.AsRegister<Register>(), base, offset);
5406 break;
5407
5408 case Primitive::kPrimInt:
5409 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset);
5410 break;
5411
5412 case Primitive::kPrimNot: {
5413 // /* HeapReference<Object> */ out = *(base + offset)
5414 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
5415 Location temp_loc = locations->GetTemp(0);
5416 // Note that a potential implicit null check is handled in this
5417 // CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier call.
5418 codegen_->GenerateFieldLoadWithBakerReadBarrier(
5419 instruction, out, base, offset, temp_loc, /* needs_null_check */ true);
5420 if (is_volatile) {
5421 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
5422 }
5423 } else {
5424 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset);
5425 codegen_->MaybeRecordImplicitNullCheck(instruction);
5426 if (is_volatile) {
5427 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
5428 }
5429 // If read barriers are enabled, emit read barriers other than
5430 // Baker's using a slow path (and also unpoison the loaded
5431 // reference, if heap poisoning is enabled).
5432 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, base_loc, offset);
5433 }
5434 break;
5435 }
5436
5437 case Primitive::kPrimLong:
5438 if (is_volatile && !atomic_ldrd_strd) {
5439 GenerateWideAtomicLoad(base, offset,
5440 out.AsRegisterPairLow<Register>(),
5441 out.AsRegisterPairHigh<Register>());
5442 } else {
5443 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), base, offset);
5444 }
5445 break;
5446
5447 case Primitive::kPrimFloat:
5448 __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), base, offset);
5449 break;
5450
5451 case Primitive::kPrimDouble: {
5452 DRegister out_reg = FromLowSToD(out.AsFpuRegisterPairLow<SRegister>());
5453 if (is_volatile && !atomic_ldrd_strd) {
5454 Register lo = locations->GetTemp(0).AsRegister<Register>();
5455 Register hi = locations->GetTemp(1).AsRegister<Register>();
5456 GenerateWideAtomicLoad(base, offset, lo, hi);
5457 codegen_->MaybeRecordImplicitNullCheck(instruction);
5458 __ vmovdrr(out_reg, lo, hi);
5459 } else {
5460 __ LoadDFromOffset(out_reg, base, offset);
5461 codegen_->MaybeRecordImplicitNullCheck(instruction);
5462 }
5463 break;
5464 }
5465
5466 case Primitive::kPrimVoid:
5467 LOG(FATAL) << "Unreachable type " << field_type;
5468 UNREACHABLE();
5469 }
5470
5471 if (field_type == Primitive::kPrimNot || field_type == Primitive::kPrimDouble) {
5472 // Potential implicit null checks, in the case of reference or
5473 // double fields, are handled in the previous switch statement.
5474 } else {
5475 codegen_->MaybeRecordImplicitNullCheck(instruction);
5476 }
5477
5478 if (is_volatile) {
5479 if (field_type == Primitive::kPrimNot) {
5480 // Memory barriers, in the case of references, are also handled
5481 // in the previous switch statement.
5482 } else {
5483 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
5484 }
5485 }
5486 }
5487
VisitInstanceFieldSet(HInstanceFieldSet * instruction)5488 void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
5489 HandleFieldSet(instruction, instruction->GetFieldInfo());
5490 }
5491
VisitInstanceFieldSet(HInstanceFieldSet * instruction)5492 void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
5493 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
5494 }
5495
VisitInstanceFieldGet(HInstanceFieldGet * instruction)5496 void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
5497 HandleFieldGet(instruction, instruction->GetFieldInfo());
5498 }
5499
VisitInstanceFieldGet(HInstanceFieldGet * instruction)5500 void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
5501 HandleFieldGet(instruction, instruction->GetFieldInfo());
5502 }
5503
VisitStaticFieldGet(HStaticFieldGet * instruction)5504 void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
5505 HandleFieldGet(instruction, instruction->GetFieldInfo());
5506 }
5507
VisitStaticFieldGet(HStaticFieldGet * instruction)5508 void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
5509 HandleFieldGet(instruction, instruction->GetFieldInfo());
5510 }
5511
VisitStaticFieldSet(HStaticFieldSet * instruction)5512 void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
5513 HandleFieldSet(instruction, instruction->GetFieldInfo());
5514 }
5515
VisitStaticFieldSet(HStaticFieldSet * instruction)5516 void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
5517 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
5518 }
5519
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)5520 void LocationsBuilderARM::VisitUnresolvedInstanceFieldGet(
5521 HUnresolvedInstanceFieldGet* instruction) {
5522 FieldAccessCallingConventionARM calling_convention;
5523 codegen_->CreateUnresolvedFieldLocationSummary(
5524 instruction, instruction->GetFieldType(), calling_convention);
5525 }
5526
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)5527 void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldGet(
5528 HUnresolvedInstanceFieldGet* instruction) {
5529 FieldAccessCallingConventionARM calling_convention;
5530 codegen_->GenerateUnresolvedFieldAccess(instruction,
5531 instruction->GetFieldType(),
5532 instruction->GetFieldIndex(),
5533 instruction->GetDexPc(),
5534 calling_convention);
5535 }
5536
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)5537 void LocationsBuilderARM::VisitUnresolvedInstanceFieldSet(
5538 HUnresolvedInstanceFieldSet* instruction) {
5539 FieldAccessCallingConventionARM calling_convention;
5540 codegen_->CreateUnresolvedFieldLocationSummary(
5541 instruction, instruction->GetFieldType(), calling_convention);
5542 }
5543
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)5544 void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldSet(
5545 HUnresolvedInstanceFieldSet* instruction) {
5546 FieldAccessCallingConventionARM calling_convention;
5547 codegen_->GenerateUnresolvedFieldAccess(instruction,
5548 instruction->GetFieldType(),
5549 instruction->GetFieldIndex(),
5550 instruction->GetDexPc(),
5551 calling_convention);
5552 }
5553
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)5554 void LocationsBuilderARM::VisitUnresolvedStaticFieldGet(
5555 HUnresolvedStaticFieldGet* instruction) {
5556 FieldAccessCallingConventionARM calling_convention;
5557 codegen_->CreateUnresolvedFieldLocationSummary(
5558 instruction, instruction->GetFieldType(), calling_convention);
5559 }
5560
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)5561 void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldGet(
5562 HUnresolvedStaticFieldGet* instruction) {
5563 FieldAccessCallingConventionARM calling_convention;
5564 codegen_->GenerateUnresolvedFieldAccess(instruction,
5565 instruction->GetFieldType(),
5566 instruction->GetFieldIndex(),
5567 instruction->GetDexPc(),
5568 calling_convention);
5569 }
5570
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)5571 void LocationsBuilderARM::VisitUnresolvedStaticFieldSet(
5572 HUnresolvedStaticFieldSet* instruction) {
5573 FieldAccessCallingConventionARM calling_convention;
5574 codegen_->CreateUnresolvedFieldLocationSummary(
5575 instruction, instruction->GetFieldType(), calling_convention);
5576 }
5577
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)5578 void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldSet(
5579 HUnresolvedStaticFieldSet* instruction) {
5580 FieldAccessCallingConventionARM calling_convention;
5581 codegen_->GenerateUnresolvedFieldAccess(instruction,
5582 instruction->GetFieldType(),
5583 instruction->GetFieldIndex(),
5584 instruction->GetDexPc(),
5585 calling_convention);
5586 }
5587
VisitNullCheck(HNullCheck * instruction)5588 void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
5589 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
5590 locations->SetInAt(0, Location::RequiresRegister());
5591 }
5592
GenerateImplicitNullCheck(HNullCheck * instruction)5593 void CodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) {
5594 if (CanMoveNullCheckToUser(instruction)) {
5595 return;
5596 }
5597 Location obj = instruction->GetLocations()->InAt(0);
5598
5599 __ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0);
5600 RecordPcInfo(instruction, instruction->GetDexPc());
5601 }
5602
GenerateExplicitNullCheck(HNullCheck * instruction)5603 void CodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) {
5604 SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction);
5605 AddSlowPath(slow_path);
5606
5607 LocationSummary* locations = instruction->GetLocations();
5608 Location obj = locations->InAt(0);
5609
5610 __ CompareAndBranchIfZero(obj.AsRegister<Register>(), slow_path->GetEntryLabel());
5611 }
5612
VisitNullCheck(HNullCheck * instruction)5613 void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
5614 codegen_->GenerateNullCheck(instruction);
5615 }
5616
GetLoadOperandType(Primitive::Type type)5617 static LoadOperandType GetLoadOperandType(Primitive::Type type) {
5618 switch (type) {
5619 case Primitive::kPrimNot:
5620 return kLoadWord;
5621 case Primitive::kPrimBoolean:
5622 return kLoadUnsignedByte;
5623 case Primitive::kPrimByte:
5624 return kLoadSignedByte;
5625 case Primitive::kPrimChar:
5626 return kLoadUnsignedHalfword;
5627 case Primitive::kPrimShort:
5628 return kLoadSignedHalfword;
5629 case Primitive::kPrimInt:
5630 return kLoadWord;
5631 case Primitive::kPrimLong:
5632 return kLoadWordPair;
5633 case Primitive::kPrimFloat:
5634 return kLoadSWord;
5635 case Primitive::kPrimDouble:
5636 return kLoadDWord;
5637 default:
5638 LOG(FATAL) << "Unreachable type " << type;
5639 UNREACHABLE();
5640 }
5641 }
5642
GetStoreOperandType(Primitive::Type type)5643 static StoreOperandType GetStoreOperandType(Primitive::Type type) {
5644 switch (type) {
5645 case Primitive::kPrimNot:
5646 return kStoreWord;
5647 case Primitive::kPrimBoolean:
5648 case Primitive::kPrimByte:
5649 return kStoreByte;
5650 case Primitive::kPrimChar:
5651 case Primitive::kPrimShort:
5652 return kStoreHalfword;
5653 case Primitive::kPrimInt:
5654 return kStoreWord;
5655 case Primitive::kPrimLong:
5656 return kStoreWordPair;
5657 case Primitive::kPrimFloat:
5658 return kStoreSWord;
5659 case Primitive::kPrimDouble:
5660 return kStoreDWord;
5661 default:
5662 LOG(FATAL) << "Unreachable type " << type;
5663 UNREACHABLE();
5664 }
5665 }
5666
LoadFromShiftedRegOffset(Primitive::Type type,Location out_loc,Register base,Register reg_offset,Condition cond)5667 void CodeGeneratorARM::LoadFromShiftedRegOffset(Primitive::Type type,
5668 Location out_loc,
5669 Register base,
5670 Register reg_offset,
5671 Condition cond) {
5672 uint32_t shift_count = Primitive::ComponentSizeShift(type);
5673 Address mem_address(base, reg_offset, Shift::LSL, shift_count);
5674
5675 switch (type) {
5676 case Primitive::kPrimByte:
5677 __ ldrsb(out_loc.AsRegister<Register>(), mem_address, cond);
5678 break;
5679 case Primitive::kPrimBoolean:
5680 __ ldrb(out_loc.AsRegister<Register>(), mem_address, cond);
5681 break;
5682 case Primitive::kPrimShort:
5683 __ ldrsh(out_loc.AsRegister<Register>(), mem_address, cond);
5684 break;
5685 case Primitive::kPrimChar:
5686 __ ldrh(out_loc.AsRegister<Register>(), mem_address, cond);
5687 break;
5688 case Primitive::kPrimNot:
5689 case Primitive::kPrimInt:
5690 __ ldr(out_loc.AsRegister<Register>(), mem_address, cond);
5691 break;
5692 // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
5693 case Primitive::kPrimLong:
5694 case Primitive::kPrimFloat:
5695 case Primitive::kPrimDouble:
5696 default:
5697 LOG(FATAL) << "Unreachable type " << type;
5698 UNREACHABLE();
5699 }
5700 }
5701
StoreToShiftedRegOffset(Primitive::Type type,Location loc,Register base,Register reg_offset,Condition cond)5702 void CodeGeneratorARM::StoreToShiftedRegOffset(Primitive::Type type,
5703 Location loc,
5704 Register base,
5705 Register reg_offset,
5706 Condition cond) {
5707 uint32_t shift_count = Primitive::ComponentSizeShift(type);
5708 Address mem_address(base, reg_offset, Shift::LSL, shift_count);
5709
5710 switch (type) {
5711 case Primitive::kPrimByte:
5712 case Primitive::kPrimBoolean:
5713 __ strb(loc.AsRegister<Register>(), mem_address, cond);
5714 break;
5715 case Primitive::kPrimShort:
5716 case Primitive::kPrimChar:
5717 __ strh(loc.AsRegister<Register>(), mem_address, cond);
5718 break;
5719 case Primitive::kPrimNot:
5720 case Primitive::kPrimInt:
5721 __ str(loc.AsRegister<Register>(), mem_address, cond);
5722 break;
5723 // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
5724 case Primitive::kPrimLong:
5725 case Primitive::kPrimFloat:
5726 case Primitive::kPrimDouble:
5727 default:
5728 LOG(FATAL) << "Unreachable type " << type;
5729 UNREACHABLE();
5730 }
5731 }
5732
VisitArrayGet(HArrayGet * instruction)5733 void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) {
5734 bool object_array_get_with_read_barrier =
5735 kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot);
5736 LocationSummary* locations =
5737 new (GetGraph()->GetArena()) LocationSummary(instruction,
5738 object_array_get_with_read_barrier ?
5739 LocationSummary::kCallOnSlowPath :
5740 LocationSummary::kNoCall);
5741 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
5742 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
5743 }
5744 locations->SetInAt(0, Location::RequiresRegister());
5745 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
5746 if (Primitive::IsFloatingPointType(instruction->GetType())) {
5747 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
5748 } else {
5749 // The output overlaps in the case of an object array get with
5750 // read barriers enabled: we do not want the move to overwrite the
5751 // array's location, as we need it to emit the read barrier.
5752 locations->SetOut(
5753 Location::RequiresRegister(),
5754 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
5755 }
5756 // We need a temporary register for the read barrier marking slow
5757 // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
5758 // Also need for String compression feature.
5759 if ((object_array_get_with_read_barrier && kUseBakerReadBarrier)
5760 || (mirror::kUseStringCompression && instruction->IsStringCharAt())) {
5761 locations->AddTemp(Location::RequiresRegister());
5762 }
5763 }
5764
VisitArrayGet(HArrayGet * instruction)5765 void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
5766 LocationSummary* locations = instruction->GetLocations();
5767 Location obj_loc = locations->InAt(0);
5768 Register obj = obj_loc.AsRegister<Register>();
5769 Location index = locations->InAt(1);
5770 Location out_loc = locations->Out();
5771 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
5772 Primitive::Type type = instruction->GetType();
5773 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
5774 instruction->IsStringCharAt();
5775 HInstruction* array_instr = instruction->GetArray();
5776 bool has_intermediate_address = array_instr->IsIntermediateAddress();
5777
5778 switch (type) {
5779 case Primitive::kPrimBoolean:
5780 case Primitive::kPrimByte:
5781 case Primitive::kPrimShort:
5782 case Primitive::kPrimChar:
5783 case Primitive::kPrimInt: {
5784 Register length;
5785 if (maybe_compressed_char_at) {
5786 length = locations->GetTemp(0).AsRegister<Register>();
5787 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
5788 __ LoadFromOffset(kLoadWord, length, obj, count_offset);
5789 codegen_->MaybeRecordImplicitNullCheck(instruction);
5790 }
5791 if (index.IsConstant()) {
5792 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
5793 if (maybe_compressed_char_at) {
5794 Label uncompressed_load, done;
5795 Label* final_label = codegen_->GetFinalLabel(instruction, &done);
5796 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
5797 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
5798 "Expecting 0=compressed, 1=uncompressed");
5799 __ b(&uncompressed_load, CS);
5800 __ LoadFromOffset(kLoadUnsignedByte,
5801 out_loc.AsRegister<Register>(),
5802 obj,
5803 data_offset + const_index);
5804 __ b(final_label);
5805 __ Bind(&uncompressed_load);
5806 __ LoadFromOffset(GetLoadOperandType(Primitive::kPrimChar),
5807 out_loc.AsRegister<Register>(),
5808 obj,
5809 data_offset + (const_index << 1));
5810 if (done.IsLinked()) {
5811 __ Bind(&done);
5812 }
5813 } else {
5814 uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type));
5815
5816 LoadOperandType load_type = GetLoadOperandType(type);
5817 __ LoadFromOffset(load_type, out_loc.AsRegister<Register>(), obj, full_offset);
5818 }
5819 } else {
5820 Register temp = IP;
5821
5822 if (has_intermediate_address) {
5823 // We do not need to compute the intermediate address from the array: the
5824 // input instruction has done it already. See the comment in
5825 // `TryExtractArrayAccessAddress()`.
5826 if (kIsDebugBuild) {
5827 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
5828 DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
5829 }
5830 temp = obj;
5831 } else {
5832 __ add(temp, obj, ShifterOperand(data_offset));
5833 }
5834 if (maybe_compressed_char_at) {
5835 Label uncompressed_load, done;
5836 Label* final_label = codegen_->GetFinalLabel(instruction, &done);
5837 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
5838 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
5839 "Expecting 0=compressed, 1=uncompressed");
5840 __ b(&uncompressed_load, CS);
5841 __ ldrb(out_loc.AsRegister<Register>(),
5842 Address(temp, index.AsRegister<Register>(), Shift::LSL, 0));
5843 __ b(final_label);
5844 __ Bind(&uncompressed_load);
5845 __ ldrh(out_loc.AsRegister<Register>(),
5846 Address(temp, index.AsRegister<Register>(), Shift::LSL, 1));
5847 if (done.IsLinked()) {
5848 __ Bind(&done);
5849 }
5850 } else {
5851 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, index.AsRegister<Register>());
5852 }
5853 }
5854 break;
5855 }
5856
5857 case Primitive::kPrimNot: {
5858 // The read barrier instrumentation of object ArrayGet
5859 // instructions does not support the HIntermediateAddress
5860 // instruction.
5861 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
5862
5863 static_assert(
5864 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
5865 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
5866 // /* HeapReference<Object> */ out =
5867 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
5868 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
5869 Location temp = locations->GetTemp(0);
5870 // Note that a potential implicit null check is handled in this
5871 // CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier call.
5872 codegen_->GenerateArrayLoadWithBakerReadBarrier(
5873 instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ true);
5874 } else {
5875 Register out = out_loc.AsRegister<Register>();
5876 if (index.IsConstant()) {
5877 size_t offset =
5878 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
5879 __ LoadFromOffset(kLoadWord, out, obj, offset);
5880 codegen_->MaybeRecordImplicitNullCheck(instruction);
5881 // If read barriers are enabled, emit read barriers other than
5882 // Baker's using a slow path (and also unpoison the loaded
5883 // reference, if heap poisoning is enabled).
5884 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
5885 } else {
5886 Register temp = IP;
5887
5888 if (has_intermediate_address) {
5889 // We do not need to compute the intermediate address from the array: the
5890 // input instruction has done it already. See the comment in
5891 // `TryExtractArrayAccessAddress()`.
5892 if (kIsDebugBuild) {
5893 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
5894 DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
5895 }
5896 temp = obj;
5897 } else {
5898 __ add(temp, obj, ShifterOperand(data_offset));
5899 }
5900 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, index.AsRegister<Register>());
5901
5902 codegen_->MaybeRecordImplicitNullCheck(instruction);
5903 // If read barriers are enabled, emit read barriers other than
5904 // Baker's using a slow path (and also unpoison the loaded
5905 // reference, if heap poisoning is enabled).
5906 codegen_->MaybeGenerateReadBarrierSlow(
5907 instruction, out_loc, out_loc, obj_loc, data_offset, index);
5908 }
5909 }
5910 break;
5911 }
5912
5913 case Primitive::kPrimLong: {
5914 if (index.IsConstant()) {
5915 size_t offset =
5916 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
5917 __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), obj, offset);
5918 } else {
5919 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
5920 __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), IP, data_offset);
5921 }
5922 break;
5923 }
5924
5925 case Primitive::kPrimFloat: {
5926 SRegister out = out_loc.AsFpuRegister<SRegister>();
5927 if (index.IsConstant()) {
5928 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
5929 __ LoadSFromOffset(out, obj, offset);
5930 } else {
5931 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
5932 __ LoadSFromOffset(out, IP, data_offset);
5933 }
5934 break;
5935 }
5936
5937 case Primitive::kPrimDouble: {
5938 SRegister out = out_loc.AsFpuRegisterPairLow<SRegister>();
5939 if (index.IsConstant()) {
5940 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
5941 __ LoadDFromOffset(FromLowSToD(out), obj, offset);
5942 } else {
5943 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
5944 __ LoadDFromOffset(FromLowSToD(out), IP, data_offset);
5945 }
5946 break;
5947 }
5948
5949 case Primitive::kPrimVoid:
5950 LOG(FATAL) << "Unreachable type " << type;
5951 UNREACHABLE();
5952 }
5953
5954 if (type == Primitive::kPrimNot) {
5955 // Potential implicit null checks, in the case of reference
5956 // arrays, are handled in the previous switch statement.
5957 } else if (!maybe_compressed_char_at) {
5958 codegen_->MaybeRecordImplicitNullCheck(instruction);
5959 }
5960 }
5961
VisitArraySet(HArraySet * instruction)5962 void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) {
5963 Primitive::Type value_type = instruction->GetComponentType();
5964
5965 bool needs_write_barrier =
5966 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
5967 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
5968
5969 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
5970 instruction,
5971 may_need_runtime_call_for_type_check ?
5972 LocationSummary::kCallOnSlowPath :
5973 LocationSummary::kNoCall);
5974
5975 locations->SetInAt(0, Location::RequiresRegister());
5976 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
5977 if (Primitive::IsFloatingPointType(value_type)) {
5978 locations->SetInAt(2, Location::RequiresFpuRegister());
5979 } else {
5980 locations->SetInAt(2, Location::RequiresRegister());
5981 }
5982 if (needs_write_barrier) {
5983 // Temporary registers for the write barrier.
5984 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
5985 locations->AddTemp(Location::RequiresRegister());
5986 }
5987 }
5988
VisitArraySet(HArraySet * instruction)5989 void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
5990 LocationSummary* locations = instruction->GetLocations();
5991 Location array_loc = locations->InAt(0);
5992 Register array = array_loc.AsRegister<Register>();
5993 Location index = locations->InAt(1);
5994 Primitive::Type value_type = instruction->GetComponentType();
5995 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
5996 bool needs_write_barrier =
5997 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
5998 uint32_t data_offset =
5999 mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
6000 Location value_loc = locations->InAt(2);
6001 HInstruction* array_instr = instruction->GetArray();
6002 bool has_intermediate_address = array_instr->IsIntermediateAddress();
6003
6004 switch (value_type) {
6005 case Primitive::kPrimBoolean:
6006 case Primitive::kPrimByte:
6007 case Primitive::kPrimShort:
6008 case Primitive::kPrimChar:
6009 case Primitive::kPrimInt: {
6010 if (index.IsConstant()) {
6011 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
6012 uint32_t full_offset =
6013 data_offset + (const_index << Primitive::ComponentSizeShift(value_type));
6014 StoreOperandType store_type = GetStoreOperandType(value_type);
6015 __ StoreToOffset(store_type, value_loc.AsRegister<Register>(), array, full_offset);
6016 } else {
6017 Register temp = IP;
6018
6019 if (has_intermediate_address) {
6020 // We do not need to compute the intermediate address from the array: the
6021 // input instruction has done it already. See the comment in
6022 // `TryExtractArrayAccessAddress()`.
6023 if (kIsDebugBuild) {
6024 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
6025 DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == data_offset);
6026 }
6027 temp = array;
6028 } else {
6029 __ add(temp, array, ShifterOperand(data_offset));
6030 }
6031 codegen_->StoreToShiftedRegOffset(value_type,
6032 value_loc,
6033 temp,
6034 index.AsRegister<Register>());
6035 }
6036 break;
6037 }
6038
6039 case Primitive::kPrimNot: {
6040 Register value = value_loc.AsRegister<Register>();
6041 // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
6042 // See the comment in instruction_simplifier_shared.cc.
6043 DCHECK(!has_intermediate_address);
6044
6045 if (instruction->InputAt(2)->IsNullConstant()) {
6046 // Just setting null.
6047 if (index.IsConstant()) {
6048 size_t offset =
6049 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
6050 __ StoreToOffset(kStoreWord, value, array, offset);
6051 } else {
6052 DCHECK(index.IsRegister()) << index;
6053 __ add(IP, array, ShifterOperand(data_offset));
6054 codegen_->StoreToShiftedRegOffset(value_type,
6055 value_loc,
6056 IP,
6057 index.AsRegister<Register>());
6058 }
6059 codegen_->MaybeRecordImplicitNullCheck(instruction);
6060 DCHECK(!needs_write_barrier);
6061 DCHECK(!may_need_runtime_call_for_type_check);
6062 break;
6063 }
6064
6065 DCHECK(needs_write_barrier);
6066 Location temp1_loc = locations->GetTemp(0);
6067 Register temp1 = temp1_loc.AsRegister<Register>();
6068 Location temp2_loc = locations->GetTemp(1);
6069 Register temp2 = temp2_loc.AsRegister<Register>();
6070 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
6071 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
6072 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
6073 Label done;
6074 Label* final_label = codegen_->GetFinalLabel(instruction, &done);
6075 SlowPathCodeARM* slow_path = nullptr;
6076
6077 if (may_need_runtime_call_for_type_check) {
6078 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARM(instruction);
6079 codegen_->AddSlowPath(slow_path);
6080 if (instruction->GetValueCanBeNull()) {
6081 Label non_zero;
6082 __ CompareAndBranchIfNonZero(value, &non_zero);
6083 if (index.IsConstant()) {
6084 size_t offset =
6085 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
6086 __ StoreToOffset(kStoreWord, value, array, offset);
6087 } else {
6088 DCHECK(index.IsRegister()) << index;
6089 __ add(IP, array, ShifterOperand(data_offset));
6090 codegen_->StoreToShiftedRegOffset(value_type,
6091 value_loc,
6092 IP,
6093 index.AsRegister<Register>());
6094 }
6095 codegen_->MaybeRecordImplicitNullCheck(instruction);
6096 __ b(final_label);
6097 __ Bind(&non_zero);
6098 }
6099
6100 // Note that when read barriers are enabled, the type checks
6101 // are performed without read barriers. This is fine, even in
6102 // the case where a class object is in the from-space after
6103 // the flip, as a comparison involving such a type would not
6104 // produce a false positive; it may of course produce a false
6105 // negative, in which case we would take the ArraySet slow
6106 // path.
6107
6108 // /* HeapReference<Class> */ temp1 = array->klass_
6109 __ LoadFromOffset(kLoadWord, temp1, array, class_offset);
6110 codegen_->MaybeRecordImplicitNullCheck(instruction);
6111 __ MaybeUnpoisonHeapReference(temp1);
6112
6113 // /* HeapReference<Class> */ temp1 = temp1->component_type_
6114 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
6115 // /* HeapReference<Class> */ temp2 = value->klass_
6116 __ LoadFromOffset(kLoadWord, temp2, value, class_offset);
6117 // If heap poisoning is enabled, no need to unpoison `temp1`
6118 // nor `temp2`, as we are comparing two poisoned references.
6119 __ cmp(temp1, ShifterOperand(temp2));
6120
6121 if (instruction->StaticTypeOfArrayIsObjectArray()) {
6122 Label do_put;
6123 __ b(&do_put, EQ);
6124 // If heap poisoning is enabled, the `temp1` reference has
6125 // not been unpoisoned yet; unpoison it now.
6126 __ MaybeUnpoisonHeapReference(temp1);
6127
6128 // /* HeapReference<Class> */ temp1 = temp1->super_class_
6129 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
6130 // If heap poisoning is enabled, no need to unpoison
6131 // `temp1`, as we are comparing against null below.
6132 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
6133 __ Bind(&do_put);
6134 } else {
6135 __ b(slow_path->GetEntryLabel(), NE);
6136 }
6137 }
6138
6139 Register source = value;
6140 if (kPoisonHeapReferences) {
6141 // Note that in the case where `value` is a null reference,
6142 // we do not enter this block, as a null reference does not
6143 // need poisoning.
6144 DCHECK_EQ(value_type, Primitive::kPrimNot);
6145 __ Mov(temp1, value);
6146 __ PoisonHeapReference(temp1);
6147 source = temp1;
6148 }
6149
6150 if (index.IsConstant()) {
6151 size_t offset =
6152 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
6153 __ StoreToOffset(kStoreWord, source, array, offset);
6154 } else {
6155 DCHECK(index.IsRegister()) << index;
6156
6157 __ add(IP, array, ShifterOperand(data_offset));
6158 codegen_->StoreToShiftedRegOffset(value_type,
6159 Location::RegisterLocation(source),
6160 IP,
6161 index.AsRegister<Register>());
6162 }
6163
6164 if (!may_need_runtime_call_for_type_check) {
6165 codegen_->MaybeRecordImplicitNullCheck(instruction);
6166 }
6167
6168 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull());
6169
6170 if (done.IsLinked()) {
6171 __ Bind(&done);
6172 }
6173
6174 if (slow_path != nullptr) {
6175 __ Bind(slow_path->GetExitLabel());
6176 }
6177
6178 break;
6179 }
6180
6181 case Primitive::kPrimLong: {
6182 Location value = locations->InAt(2);
6183 if (index.IsConstant()) {
6184 size_t offset =
6185 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
6186 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), array, offset);
6187 } else {
6188 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
6189 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), IP, data_offset);
6190 }
6191 break;
6192 }
6193
6194 case Primitive::kPrimFloat: {
6195 Location value = locations->InAt(2);
6196 DCHECK(value.IsFpuRegister());
6197 if (index.IsConstant()) {
6198 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
6199 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), array, offset);
6200 } else {
6201 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
6202 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), IP, data_offset);
6203 }
6204 break;
6205 }
6206
6207 case Primitive::kPrimDouble: {
6208 Location value = locations->InAt(2);
6209 DCHECK(value.IsFpuRegisterPair());
6210 if (index.IsConstant()) {
6211 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
6212 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), array, offset);
6213 } else {
6214 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
6215 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset);
6216 }
6217
6218 break;
6219 }
6220
6221 case Primitive::kPrimVoid:
6222 LOG(FATAL) << "Unreachable type " << value_type;
6223 UNREACHABLE();
6224 }
6225
6226 // Objects are handled in the switch.
6227 if (value_type != Primitive::kPrimNot) {
6228 codegen_->MaybeRecordImplicitNullCheck(instruction);
6229 }
6230 }
6231
VisitArrayLength(HArrayLength * instruction)6232 void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) {
6233 LocationSummary* locations =
6234 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
6235 locations->SetInAt(0, Location::RequiresRegister());
6236 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6237 }
6238
VisitArrayLength(HArrayLength * instruction)6239 void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) {
6240 LocationSummary* locations = instruction->GetLocations();
6241 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
6242 Register obj = locations->InAt(0).AsRegister<Register>();
6243 Register out = locations->Out().AsRegister<Register>();
6244 __ LoadFromOffset(kLoadWord, out, obj, offset);
6245 codegen_->MaybeRecordImplicitNullCheck(instruction);
6246 // Mask out compression flag from String's array length.
6247 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
6248 __ Lsr(out, out, 1u);
6249 }
6250 }
6251
VisitIntermediateAddress(HIntermediateAddress * instruction)6252 void LocationsBuilderARM::VisitIntermediateAddress(HIntermediateAddress* instruction) {
6253 LocationSummary* locations =
6254 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
6255
6256 locations->SetInAt(0, Location::RequiresRegister());
6257 locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
6258 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6259 }
6260
VisitIntermediateAddress(HIntermediateAddress * instruction)6261 void InstructionCodeGeneratorARM::VisitIntermediateAddress(HIntermediateAddress* instruction) {
6262 LocationSummary* locations = instruction->GetLocations();
6263 Location out = locations->Out();
6264 Location first = locations->InAt(0);
6265 Location second = locations->InAt(1);
6266
6267 if (second.IsRegister()) {
6268 __ add(out.AsRegister<Register>(),
6269 first.AsRegister<Register>(),
6270 ShifterOperand(second.AsRegister<Register>()));
6271 } else {
6272 __ AddConstant(out.AsRegister<Register>(),
6273 first.AsRegister<Register>(),
6274 second.GetConstant()->AsIntConstant()->GetValue());
6275 }
6276 }
6277
VisitBoundsCheck(HBoundsCheck * instruction)6278 void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) {
6279 RegisterSet caller_saves = RegisterSet::Empty();
6280 InvokeRuntimeCallingConvention calling_convention;
6281 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
6282 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
6283 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
6284
6285 HInstruction* index = instruction->InputAt(0);
6286 HInstruction* length = instruction->InputAt(1);
6287 // If both index and length are constants we can statically check the bounds. But if at least one
6288 // of them is not encodable ArmEncodableConstantOrRegister will create
6289 // Location::RequiresRegister() which is not desired to happen. Instead we create constant
6290 // locations.
6291 bool both_const = index->IsConstant() && length->IsConstant();
6292 locations->SetInAt(0, both_const
6293 ? Location::ConstantLocation(index->AsConstant())
6294 : ArmEncodableConstantOrRegister(index, CMP));
6295 locations->SetInAt(1, both_const
6296 ? Location::ConstantLocation(length->AsConstant())
6297 : ArmEncodableConstantOrRegister(length, CMP));
6298 }
6299
VisitBoundsCheck(HBoundsCheck * instruction)6300 void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) {
6301 LocationSummary* locations = instruction->GetLocations();
6302 Location index_loc = locations->InAt(0);
6303 Location length_loc = locations->InAt(1);
6304
6305 if (length_loc.IsConstant()) {
6306 int32_t length = helpers::Int32ConstantFrom(length_loc);
6307 if (index_loc.IsConstant()) {
6308 // BCE will remove the bounds check if we are guaranteed to pass.
6309 int32_t index = helpers::Int32ConstantFrom(index_loc);
6310 if (index < 0 || index >= length) {
6311 SlowPathCodeARM* slow_path =
6312 new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction);
6313 codegen_->AddSlowPath(slow_path);
6314 __ b(slow_path->GetEntryLabel());
6315 } else {
6316 // Some optimization after BCE may have generated this, and we should not
6317 // generate a bounds check if it is a valid range.
6318 }
6319 return;
6320 }
6321
6322 SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction);
6323 __ cmp(index_loc.AsRegister<Register>(), ShifterOperand(length));
6324 codegen_->AddSlowPath(slow_path);
6325 __ b(slow_path->GetEntryLabel(), HS);
6326 } else {
6327 SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction);
6328 if (index_loc.IsConstant()) {
6329 int32_t index = helpers::Int32ConstantFrom(index_loc);
6330 __ cmp(length_loc.AsRegister<Register>(), ShifterOperand(index));
6331 } else {
6332 __ cmp(length_loc.AsRegister<Register>(), ShifterOperand(index_loc.AsRegister<Register>()));
6333 }
6334 codegen_->AddSlowPath(slow_path);
6335 __ b(slow_path->GetEntryLabel(), LS);
6336 }
6337 }
6338
MarkGCCard(Register temp,Register card,Register object,Register value,bool can_be_null)6339 void CodeGeneratorARM::MarkGCCard(Register temp,
6340 Register card,
6341 Register object,
6342 Register value,
6343 bool can_be_null) {
6344 Label is_null;
6345 if (can_be_null) {
6346 __ CompareAndBranchIfZero(value, &is_null);
6347 }
6348 __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
6349 __ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
6350 __ strb(card, Address(card, temp));
6351 if (can_be_null) {
6352 __ Bind(&is_null);
6353 }
6354 }
6355
VisitParallelMove(HParallelMove * instruction ATTRIBUTE_UNUSED)6356 void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
6357 LOG(FATAL) << "Unreachable";
6358 }
6359
VisitParallelMove(HParallelMove * instruction)6360 void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) {
6361 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
6362 }
6363
VisitSuspendCheck(HSuspendCheck * instruction)6364 void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) {
6365 LocationSummary* locations =
6366 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
6367 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
6368 }
6369
VisitSuspendCheck(HSuspendCheck * instruction)6370 void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) {
6371 HBasicBlock* block = instruction->GetBlock();
6372 if (block->GetLoopInformation() != nullptr) {
6373 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
6374 // The back edge will generate the suspend check.
6375 return;
6376 }
6377 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
6378 // The goto will generate the suspend check.
6379 return;
6380 }
6381 GenerateSuspendCheck(instruction, nullptr);
6382 }
6383
GenerateSuspendCheck(HSuspendCheck * instruction,HBasicBlock * successor)6384 void InstructionCodeGeneratorARM::GenerateSuspendCheck(HSuspendCheck* instruction,
6385 HBasicBlock* successor) {
6386 SuspendCheckSlowPathARM* slow_path =
6387 down_cast<SuspendCheckSlowPathARM*>(instruction->GetSlowPath());
6388 if (slow_path == nullptr) {
6389 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor);
6390 instruction->SetSlowPath(slow_path);
6391 codegen_->AddSlowPath(slow_path);
6392 if (successor != nullptr) {
6393 DCHECK(successor->IsLoopHeader());
6394 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction);
6395 }
6396 } else {
6397 DCHECK_EQ(slow_path->GetSuccessor(), successor);
6398 }
6399
6400 __ LoadFromOffset(
6401 kLoadUnsignedHalfword, IP, TR, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
6402 if (successor == nullptr) {
6403 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel());
6404 __ Bind(slow_path->GetReturnLabel());
6405 } else {
6406 __ CompareAndBranchIfZero(IP, codegen_->GetLabelOf(successor));
6407 __ b(slow_path->GetEntryLabel());
6408 }
6409 }
6410
GetAssembler() const6411 ArmAssembler* ParallelMoveResolverARM::GetAssembler() const {
6412 return codegen_->GetAssembler();
6413 }
6414
EmitMove(size_t index)6415 void ParallelMoveResolverARM::EmitMove(size_t index) {
6416 MoveOperands* move = moves_[index];
6417 Location source = move->GetSource();
6418 Location destination = move->GetDestination();
6419
6420 if (source.IsRegister()) {
6421 if (destination.IsRegister()) {
6422 __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>());
6423 } else if (destination.IsFpuRegister()) {
6424 __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>());
6425 } else {
6426 DCHECK(destination.IsStackSlot());
6427 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(),
6428 SP, destination.GetStackIndex());
6429 }
6430 } else if (source.IsStackSlot()) {
6431 if (destination.IsRegister()) {
6432 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(),
6433 SP, source.GetStackIndex());
6434 } else if (destination.IsFpuRegister()) {
6435 __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex());
6436 } else {
6437 DCHECK(destination.IsStackSlot());
6438 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex());
6439 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
6440 }
6441 } else if (source.IsFpuRegister()) {
6442 if (destination.IsRegister()) {
6443 __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>());
6444 } else if (destination.IsFpuRegister()) {
6445 __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>());
6446 } else {
6447 DCHECK(destination.IsStackSlot());
6448 __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex());
6449 }
6450 } else if (source.IsDoubleStackSlot()) {
6451 if (destination.IsDoubleStackSlot()) {
6452 __ LoadDFromOffset(DTMP, SP, source.GetStackIndex());
6453 __ StoreDToOffset(DTMP, SP, destination.GetStackIndex());
6454 } else if (destination.IsRegisterPair()) {
6455 DCHECK(ExpectedPairLayout(destination));
6456 __ LoadFromOffset(
6457 kLoadWordPair, destination.AsRegisterPairLow<Register>(), SP, source.GetStackIndex());
6458 } else {
6459 DCHECK(destination.IsFpuRegisterPair()) << destination;
6460 __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
6461 SP,
6462 source.GetStackIndex());
6463 }
6464 } else if (source.IsRegisterPair()) {
6465 if (destination.IsRegisterPair()) {
6466 __ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>());
6467 __ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>());
6468 } else if (destination.IsFpuRegisterPair()) {
6469 __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
6470 source.AsRegisterPairLow<Register>(),
6471 source.AsRegisterPairHigh<Register>());
6472 } else {
6473 DCHECK(destination.IsDoubleStackSlot()) << destination;
6474 DCHECK(ExpectedPairLayout(source));
6475 __ StoreToOffset(
6476 kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex());
6477 }
6478 } else if (source.IsFpuRegisterPair()) {
6479 if (destination.IsRegisterPair()) {
6480 __ vmovrrd(destination.AsRegisterPairLow<Register>(),
6481 destination.AsRegisterPairHigh<Register>(),
6482 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
6483 } else if (destination.IsFpuRegisterPair()) {
6484 __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
6485 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
6486 } else {
6487 DCHECK(destination.IsDoubleStackSlot()) << destination;
6488 __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
6489 SP,
6490 destination.GetStackIndex());
6491 }
6492 } else {
6493 DCHECK(source.IsConstant()) << source;
6494 HConstant* constant = source.GetConstant();
6495 if (constant->IsIntConstant() || constant->IsNullConstant()) {
6496 int32_t value = CodeGenerator::GetInt32ValueOf(constant);
6497 if (destination.IsRegister()) {
6498 __ LoadImmediate(destination.AsRegister<Register>(), value);
6499 } else {
6500 DCHECK(destination.IsStackSlot());
6501 __ LoadImmediate(IP, value);
6502 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
6503 }
6504 } else if (constant->IsLongConstant()) {
6505 int64_t value = constant->AsLongConstant()->GetValue();
6506 if (destination.IsRegisterPair()) {
6507 __ LoadImmediate(destination.AsRegisterPairLow<Register>(), Low32Bits(value));
6508 __ LoadImmediate(destination.AsRegisterPairHigh<Register>(), High32Bits(value));
6509 } else {
6510 DCHECK(destination.IsDoubleStackSlot()) << destination;
6511 __ LoadImmediate(IP, Low32Bits(value));
6512 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
6513 __ LoadImmediate(IP, High32Bits(value));
6514 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize));
6515 }
6516 } else if (constant->IsDoubleConstant()) {
6517 double value = constant->AsDoubleConstant()->GetValue();
6518 if (destination.IsFpuRegisterPair()) {
6519 __ LoadDImmediate(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), value);
6520 } else {
6521 DCHECK(destination.IsDoubleStackSlot()) << destination;
6522 uint64_t int_value = bit_cast<uint64_t, double>(value);
6523 __ LoadImmediate(IP, Low32Bits(int_value));
6524 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
6525 __ LoadImmediate(IP, High32Bits(int_value));
6526 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize));
6527 }
6528 } else {
6529 DCHECK(constant->IsFloatConstant()) << constant->DebugName();
6530 float value = constant->AsFloatConstant()->GetValue();
6531 if (destination.IsFpuRegister()) {
6532 __ LoadSImmediate(destination.AsFpuRegister<SRegister>(), value);
6533 } else {
6534 DCHECK(destination.IsStackSlot());
6535 __ LoadImmediate(IP, bit_cast<int32_t, float>(value));
6536 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
6537 }
6538 }
6539 }
6540 }
6541
Exchange(Register reg,int mem)6542 void ParallelMoveResolverARM::Exchange(Register reg, int mem) {
6543 __ Mov(IP, reg);
6544 __ LoadFromOffset(kLoadWord, reg, SP, mem);
6545 __ StoreToOffset(kStoreWord, IP, SP, mem);
6546 }
6547
Exchange(int mem1,int mem2)6548 void ParallelMoveResolverARM::Exchange(int mem1, int mem2) {
6549 ScratchRegisterScope ensure_scratch(this, IP, R0, codegen_->GetNumberOfCoreRegisters());
6550 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0;
6551 __ LoadFromOffset(kLoadWord, static_cast<Register>(ensure_scratch.GetRegister()),
6552 SP, mem1 + stack_offset);
6553 __ LoadFromOffset(kLoadWord, IP, SP, mem2 + stack_offset);
6554 __ StoreToOffset(kStoreWord, static_cast<Register>(ensure_scratch.GetRegister()),
6555 SP, mem2 + stack_offset);
6556 __ StoreToOffset(kStoreWord, IP, SP, mem1 + stack_offset);
6557 }
6558
EmitSwap(size_t index)6559 void ParallelMoveResolverARM::EmitSwap(size_t index) {
6560 MoveOperands* move = moves_[index];
6561 Location source = move->GetSource();
6562 Location destination = move->GetDestination();
6563
6564 if (source.IsRegister() && destination.IsRegister()) {
6565 DCHECK_NE(source.AsRegister<Register>(), IP);
6566 DCHECK_NE(destination.AsRegister<Register>(), IP);
6567 __ Mov(IP, source.AsRegister<Register>());
6568 __ Mov(source.AsRegister<Register>(), destination.AsRegister<Register>());
6569 __ Mov(destination.AsRegister<Register>(), IP);
6570 } else if (source.IsRegister() && destination.IsStackSlot()) {
6571 Exchange(source.AsRegister<Register>(), destination.GetStackIndex());
6572 } else if (source.IsStackSlot() && destination.IsRegister()) {
6573 Exchange(destination.AsRegister<Register>(), source.GetStackIndex());
6574 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
6575 Exchange(source.GetStackIndex(), destination.GetStackIndex());
6576 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
6577 __ vmovrs(IP, source.AsFpuRegister<SRegister>());
6578 __ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>());
6579 __ vmovsr(destination.AsFpuRegister<SRegister>(), IP);
6580 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
6581 __ vmovdrr(DTMP, source.AsRegisterPairLow<Register>(), source.AsRegisterPairHigh<Register>());
6582 __ Mov(source.AsRegisterPairLow<Register>(), destination.AsRegisterPairLow<Register>());
6583 __ Mov(source.AsRegisterPairHigh<Register>(), destination.AsRegisterPairHigh<Register>());
6584 __ vmovrrd(destination.AsRegisterPairLow<Register>(),
6585 destination.AsRegisterPairHigh<Register>(),
6586 DTMP);
6587 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
6588 Register low_reg = source.IsRegisterPair()
6589 ? source.AsRegisterPairLow<Register>()
6590 : destination.AsRegisterPairLow<Register>();
6591 int mem = source.IsRegisterPair()
6592 ? destination.GetStackIndex()
6593 : source.GetStackIndex();
6594 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
6595 __ vmovdrr(DTMP, low_reg, static_cast<Register>(low_reg + 1));
6596 __ LoadFromOffset(kLoadWordPair, low_reg, SP, mem);
6597 __ StoreDToOffset(DTMP, SP, mem);
6598 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
6599 DRegister first = FromLowSToD(source.AsFpuRegisterPairLow<SRegister>());
6600 DRegister second = FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>());
6601 __ vmovd(DTMP, first);
6602 __ vmovd(first, second);
6603 __ vmovd(second, DTMP);
6604 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
6605 DRegister reg = source.IsFpuRegisterPair()
6606 ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())
6607 : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>());
6608 int mem = source.IsFpuRegisterPair()
6609 ? destination.GetStackIndex()
6610 : source.GetStackIndex();
6611 __ vmovd(DTMP, reg);
6612 __ LoadDFromOffset(reg, SP, mem);
6613 __ StoreDToOffset(DTMP, SP, mem);
6614 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
6615 SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>()
6616 : destination.AsFpuRegister<SRegister>();
6617 int mem = source.IsFpuRegister()
6618 ? destination.GetStackIndex()
6619 : source.GetStackIndex();
6620
6621 __ vmovrs(IP, reg);
6622 __ LoadSFromOffset(reg, SP, mem);
6623 __ StoreToOffset(kStoreWord, IP, SP, mem);
6624 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
6625 Exchange(source.GetStackIndex(), destination.GetStackIndex());
6626 Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize));
6627 } else {
6628 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
6629 }
6630 }
6631
SpillScratch(int reg)6632 void ParallelMoveResolverARM::SpillScratch(int reg) {
6633 __ Push(static_cast<Register>(reg));
6634 }
6635
RestoreScratch(int reg)6636 void ParallelMoveResolverARM::RestoreScratch(int reg) {
6637 __ Pop(static_cast<Register>(reg));
6638 }
6639
GetSupportedLoadClassKind(HLoadClass::LoadKind desired_class_load_kind)6640 HLoadClass::LoadKind CodeGeneratorARM::GetSupportedLoadClassKind(
6641 HLoadClass::LoadKind desired_class_load_kind) {
6642 switch (desired_class_load_kind) {
6643 case HLoadClass::LoadKind::kInvalid:
6644 LOG(FATAL) << "UNREACHABLE";
6645 UNREACHABLE();
6646 case HLoadClass::LoadKind::kReferrersClass:
6647 break;
6648 case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
6649 DCHECK(!GetCompilerOptions().GetCompilePic());
6650 break;
6651 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
6652 DCHECK(GetCompilerOptions().GetCompilePic());
6653 break;
6654 case HLoadClass::LoadKind::kBootImageAddress:
6655 break;
6656 case HLoadClass::LoadKind::kBssEntry:
6657 DCHECK(!Runtime::Current()->UseJitCompilation());
6658 break;
6659 case HLoadClass::LoadKind::kJitTableAddress:
6660 DCHECK(Runtime::Current()->UseJitCompilation());
6661 break;
6662 case HLoadClass::LoadKind::kDexCacheViaMethod:
6663 break;
6664 }
6665 return desired_class_load_kind;
6666 }
6667
VisitLoadClass(HLoadClass * cls)6668 void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) {
6669 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
6670 if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
6671 InvokeRuntimeCallingConvention calling_convention;
6672 CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
6673 cls,
6674 Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
6675 Location::RegisterLocation(R0));
6676 DCHECK_EQ(calling_convention.GetRegisterAt(0), R0);
6677 return;
6678 }
6679 DCHECK(!cls->NeedsAccessCheck());
6680
6681 const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
6682 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
6683 ? LocationSummary::kCallOnSlowPath
6684 : LocationSummary::kNoCall;
6685 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
6686 if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
6687 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
6688 }
6689
6690 if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
6691 locations->SetInAt(0, Location::RequiresRegister());
6692 }
6693 locations->SetOut(Location::RequiresRegister());
6694 if (load_kind == HLoadClass::LoadKind::kBssEntry) {
6695 if (!kUseReadBarrier || kUseBakerReadBarrier) {
6696 // Rely on the type resolution or initialization and marking to save everything we need.
6697 // Note that IP may be clobbered by saving/restoring the live register (only one thanks
6698 // to the custom calling convention) or by marking, so we request a different temp.
6699 locations->AddTemp(Location::RequiresRegister());
6700 RegisterSet caller_saves = RegisterSet::Empty();
6701 InvokeRuntimeCallingConvention calling_convention;
6702 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
6703 // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
6704 // that the the kPrimNot result register is the same as the first argument register.
6705 locations->SetCustomSlowPathCallerSaves(caller_saves);
6706 } else {
6707 // For non-Baker read barrier we have a temp-clobbering call.
6708 }
6709 }
6710 }
6711
6712 // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
6713 // move.
VisitLoadClass(HLoadClass * cls)6714 void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
6715 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
6716 if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
6717 codegen_->GenerateLoadClassRuntimeCall(cls);
6718 return;
6719 }
6720 DCHECK(!cls->NeedsAccessCheck());
6721
6722 LocationSummary* locations = cls->GetLocations();
6723 Location out_loc = locations->Out();
6724 Register out = out_loc.AsRegister<Register>();
6725
6726 const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
6727 ? kWithoutReadBarrier
6728 : kCompilerReadBarrierOption;
6729 bool generate_null_check = false;
6730 switch (load_kind) {
6731 case HLoadClass::LoadKind::kReferrersClass: {
6732 DCHECK(!cls->CanCallRuntime());
6733 DCHECK(!cls->MustGenerateClinitCheck());
6734 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
6735 Register current_method = locations->InAt(0).AsRegister<Register>();
6736 GenerateGcRootFieldLoad(cls,
6737 out_loc,
6738 current_method,
6739 ArtMethod::DeclaringClassOffset().Int32Value(),
6740 read_barrier_option);
6741 break;
6742 }
6743 case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
6744 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
6745 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
6746 __ LoadLiteral(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
6747 cls->GetTypeIndex()));
6748 break;
6749 }
6750 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
6751 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
6752 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
6753 CodeGeneratorARM::PcRelativePatchInfo* labels =
6754 codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
6755 __ BindTrackedLabel(&labels->movw_label);
6756 __ movw(out, /* placeholder */ 0u);
6757 __ BindTrackedLabel(&labels->movt_label);
6758 __ movt(out, /* placeholder */ 0u);
6759 __ BindTrackedLabel(&labels->add_pc_label);
6760 __ add(out, out, ShifterOperand(PC));
6761 break;
6762 }
6763 case HLoadClass::LoadKind::kBootImageAddress: {
6764 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
6765 uint32_t address = dchecked_integral_cast<uint32_t>(
6766 reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
6767 DCHECK_NE(address, 0u);
6768 __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
6769 break;
6770 }
6771 case HLoadClass::LoadKind::kBssEntry: {
6772 Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
6773 ? locations->GetTemp(0).AsRegister<Register>()
6774 : out;
6775 CodeGeneratorARM::PcRelativePatchInfo* labels =
6776 codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
6777 __ BindTrackedLabel(&labels->movw_label);
6778 __ movw(temp, /* placeholder */ 0u);
6779 __ BindTrackedLabel(&labels->movt_label);
6780 __ movt(temp, /* placeholder */ 0u);
6781 __ BindTrackedLabel(&labels->add_pc_label);
6782 __ add(temp, temp, ShifterOperand(PC));
6783 GenerateGcRootFieldLoad(cls, out_loc, temp, /* offset */ 0, read_barrier_option);
6784 generate_null_check = true;
6785 break;
6786 }
6787 case HLoadClass::LoadKind::kJitTableAddress: {
6788 __ LoadLiteral(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
6789 cls->GetTypeIndex(),
6790 cls->GetClass()));
6791 // /* GcRoot<mirror::Class> */ out = *out
6792 GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option);
6793 break;
6794 }
6795 case HLoadClass::LoadKind::kDexCacheViaMethod:
6796 case HLoadClass::LoadKind::kInvalid:
6797 LOG(FATAL) << "UNREACHABLE";
6798 UNREACHABLE();
6799 }
6800
6801 if (generate_null_check || cls->MustGenerateClinitCheck()) {
6802 DCHECK(cls->CanCallRuntime());
6803 SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM(
6804 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
6805 codegen_->AddSlowPath(slow_path);
6806 if (generate_null_check) {
6807 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
6808 }
6809 if (cls->MustGenerateClinitCheck()) {
6810 GenerateClassInitializationCheck(slow_path, out);
6811 } else {
6812 __ Bind(slow_path->GetExitLabel());
6813 }
6814 }
6815 }
6816
VisitClinitCheck(HClinitCheck * check)6817 void LocationsBuilderARM::VisitClinitCheck(HClinitCheck* check) {
6818 LocationSummary* locations =
6819 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
6820 locations->SetInAt(0, Location::RequiresRegister());
6821 if (check->HasUses()) {
6822 locations->SetOut(Location::SameAsFirstInput());
6823 }
6824 }
6825
VisitClinitCheck(HClinitCheck * check)6826 void InstructionCodeGeneratorARM::VisitClinitCheck(HClinitCheck* check) {
6827 // We assume the class is not null.
6828 SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM(
6829 check->GetLoadClass(), check, check->GetDexPc(), true);
6830 codegen_->AddSlowPath(slow_path);
6831 GenerateClassInitializationCheck(slow_path,
6832 check->GetLocations()->InAt(0).AsRegister<Register>());
6833 }
6834
GenerateClassInitializationCheck(SlowPathCodeARM * slow_path,Register class_reg)6835 void InstructionCodeGeneratorARM::GenerateClassInitializationCheck(
6836 SlowPathCodeARM* slow_path, Register class_reg) {
6837 __ LoadFromOffset(kLoadWord, IP, class_reg, mirror::Class::StatusOffset().Int32Value());
6838 __ cmp(IP, ShifterOperand(mirror::Class::kStatusInitialized));
6839 __ b(slow_path->GetEntryLabel(), LT);
6840 // Even if the initialized flag is set, we may be in a situation where caches are not synced
6841 // properly. Therefore, we do a memory fence.
6842 __ dmb(ISH);
6843 __ Bind(slow_path->GetExitLabel());
6844 }
6845
GetSupportedLoadStringKind(HLoadString::LoadKind desired_string_load_kind)6846 HLoadString::LoadKind CodeGeneratorARM::GetSupportedLoadStringKind(
6847 HLoadString::LoadKind desired_string_load_kind) {
6848 switch (desired_string_load_kind) {
6849 case HLoadString::LoadKind::kBootImageLinkTimeAddress:
6850 DCHECK(!GetCompilerOptions().GetCompilePic());
6851 break;
6852 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
6853 DCHECK(GetCompilerOptions().GetCompilePic());
6854 break;
6855 case HLoadString::LoadKind::kBootImageAddress:
6856 break;
6857 case HLoadString::LoadKind::kBssEntry:
6858 DCHECK(!Runtime::Current()->UseJitCompilation());
6859 break;
6860 case HLoadString::LoadKind::kJitTableAddress:
6861 DCHECK(Runtime::Current()->UseJitCompilation());
6862 break;
6863 case HLoadString::LoadKind::kDexCacheViaMethod:
6864 break;
6865 }
6866 return desired_string_load_kind;
6867 }
6868
VisitLoadString(HLoadString * load)6869 void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
6870 LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
6871 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
6872 HLoadString::LoadKind load_kind = load->GetLoadKind();
6873 if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
6874 locations->SetOut(Location::RegisterLocation(R0));
6875 } else {
6876 locations->SetOut(Location::RequiresRegister());
6877 if (load_kind == HLoadString::LoadKind::kBssEntry) {
6878 if (!kUseReadBarrier || kUseBakerReadBarrier) {
6879 // Rely on the pResolveString and marking to save everything we need, including temps.
6880 // Note that IP may be clobbered by saving/restoring the live register (only one thanks
6881 // to the custom calling convention) or by marking, so we request a different temp.
6882 locations->AddTemp(Location::RequiresRegister());
6883 RegisterSet caller_saves = RegisterSet::Empty();
6884 InvokeRuntimeCallingConvention calling_convention;
6885 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
6886 // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
6887 // that the the kPrimNot result register is the same as the first argument register.
6888 locations->SetCustomSlowPathCallerSaves(caller_saves);
6889 } else {
6890 // For non-Baker read barrier we have a temp-clobbering call.
6891 }
6892 }
6893 }
6894 }
6895
6896 // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
6897 // move.
VisitLoadString(HLoadString * load)6898 void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
6899 LocationSummary* locations = load->GetLocations();
6900 Location out_loc = locations->Out();
6901 Register out = out_loc.AsRegister<Register>();
6902 HLoadString::LoadKind load_kind = load->GetLoadKind();
6903
6904 switch (load_kind) {
6905 case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
6906 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
6907 __ LoadLiteral(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
6908 load->GetStringIndex()));
6909 return; // No dex cache slow path.
6910 }
6911 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
6912 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
6913 CodeGeneratorARM::PcRelativePatchInfo* labels =
6914 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
6915 __ BindTrackedLabel(&labels->movw_label);
6916 __ movw(out, /* placeholder */ 0u);
6917 __ BindTrackedLabel(&labels->movt_label);
6918 __ movt(out, /* placeholder */ 0u);
6919 __ BindTrackedLabel(&labels->add_pc_label);
6920 __ add(out, out, ShifterOperand(PC));
6921 return; // No dex cache slow path.
6922 }
6923 case HLoadString::LoadKind::kBootImageAddress: {
6924 uint32_t address = dchecked_integral_cast<uint32_t>(
6925 reinterpret_cast<uintptr_t>(load->GetString().Get()));
6926 DCHECK_NE(address, 0u);
6927 __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
6928 return; // No dex cache slow path.
6929 }
6930 case HLoadString::LoadKind::kBssEntry: {
6931 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
6932 Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
6933 ? locations->GetTemp(0).AsRegister<Register>()
6934 : out;
6935 CodeGeneratorARM::PcRelativePatchInfo* labels =
6936 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
6937 __ BindTrackedLabel(&labels->movw_label);
6938 __ movw(temp, /* placeholder */ 0u);
6939 __ BindTrackedLabel(&labels->movt_label);
6940 __ movt(temp, /* placeholder */ 0u);
6941 __ BindTrackedLabel(&labels->add_pc_label);
6942 __ add(temp, temp, ShifterOperand(PC));
6943 GenerateGcRootFieldLoad(load, out_loc, temp, /* offset */ 0, kCompilerReadBarrierOption);
6944 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
6945 codegen_->AddSlowPath(slow_path);
6946 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
6947 __ Bind(slow_path->GetExitLabel());
6948 return;
6949 }
6950 case HLoadString::LoadKind::kJitTableAddress: {
6951 __ LoadLiteral(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
6952 load->GetStringIndex(),
6953 load->GetString()));
6954 // /* GcRoot<mirror::String> */ out = *out
6955 GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
6956 return;
6957 }
6958 default:
6959 break;
6960 }
6961
6962 // TODO: Consider re-adding the compiler code to do string dex cache lookup again.
6963 DCHECK(load_kind == HLoadString::LoadKind::kDexCacheViaMethod);
6964 InvokeRuntimeCallingConvention calling_convention;
6965 DCHECK_EQ(calling_convention.GetRegisterAt(0), out);
6966 __ LoadImmediate(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
6967 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
6968 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
6969 }
6970
GetExceptionTlsOffset()6971 static int32_t GetExceptionTlsOffset() {
6972 return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
6973 }
6974
VisitLoadException(HLoadException * load)6975 void LocationsBuilderARM::VisitLoadException(HLoadException* load) {
6976 LocationSummary* locations =
6977 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
6978 locations->SetOut(Location::RequiresRegister());
6979 }
6980
VisitLoadException(HLoadException * load)6981 void InstructionCodeGeneratorARM::VisitLoadException(HLoadException* load) {
6982 Register out = load->GetLocations()->Out().AsRegister<Register>();
6983 __ LoadFromOffset(kLoadWord, out, TR, GetExceptionTlsOffset());
6984 }
6985
VisitClearException(HClearException * clear)6986 void LocationsBuilderARM::VisitClearException(HClearException* clear) {
6987 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
6988 }
6989
VisitClearException(HClearException * clear ATTRIBUTE_UNUSED)6990 void InstructionCodeGeneratorARM::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
6991 __ LoadImmediate(IP, 0);
6992 __ StoreToOffset(kStoreWord, IP, TR, GetExceptionTlsOffset());
6993 }
6994
VisitThrow(HThrow * instruction)6995 void LocationsBuilderARM::VisitThrow(HThrow* instruction) {
6996 LocationSummary* locations =
6997 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
6998 InvokeRuntimeCallingConvention calling_convention;
6999 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
7000 }
7001
VisitThrow(HThrow * instruction)7002 void InstructionCodeGeneratorARM::VisitThrow(HThrow* instruction) {
7003 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
7004 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
7005 }
7006
7007 // Temp is used for read barrier.
NumberOfInstanceOfTemps(TypeCheckKind type_check_kind)7008 static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
7009 if (kEmitCompilerReadBarrier &&
7010 (kUseBakerReadBarrier ||
7011 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
7012 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
7013 type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
7014 return 1;
7015 }
7016 return 0;
7017 }
7018
7019 // Interface case has 3 temps, one for holding the number of interfaces, one for the current
7020 // interface pointer, one for loading the current interface.
7021 // The other checks have one temp for loading the object's class.
NumberOfCheckCastTemps(TypeCheckKind type_check_kind)7022 static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
7023 if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
7024 return 3;
7025 }
7026 return 1 + NumberOfInstanceOfTemps(type_check_kind);
7027 }
7028
VisitInstanceOf(HInstanceOf * instruction)7029 void LocationsBuilderARM::VisitInstanceOf(HInstanceOf* instruction) {
7030 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
7031 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7032 bool baker_read_barrier_slow_path = false;
7033 switch (type_check_kind) {
7034 case TypeCheckKind::kExactCheck:
7035 case TypeCheckKind::kAbstractClassCheck:
7036 case TypeCheckKind::kClassHierarchyCheck:
7037 case TypeCheckKind::kArrayObjectCheck:
7038 call_kind =
7039 kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
7040 baker_read_barrier_slow_path = kUseBakerReadBarrier;
7041 break;
7042 case TypeCheckKind::kArrayCheck:
7043 case TypeCheckKind::kUnresolvedCheck:
7044 case TypeCheckKind::kInterfaceCheck:
7045 call_kind = LocationSummary::kCallOnSlowPath;
7046 break;
7047 }
7048
7049 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
7050 if (baker_read_barrier_slow_path) {
7051 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
7052 }
7053 locations->SetInAt(0, Location::RequiresRegister());
7054 locations->SetInAt(1, Location::RequiresRegister());
7055 // The "out" register is used as a temporary, so it overlaps with the inputs.
7056 // Note that TypeCheckSlowPathARM uses this register too.
7057 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
7058 locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
7059 }
7060
VisitInstanceOf(HInstanceOf * instruction)7061 void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) {
7062 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7063 LocationSummary* locations = instruction->GetLocations();
7064 Location obj_loc = locations->InAt(0);
7065 Register obj = obj_loc.AsRegister<Register>();
7066 Register cls = locations->InAt(1).AsRegister<Register>();
7067 Location out_loc = locations->Out();
7068 Register out = out_loc.AsRegister<Register>();
7069 const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
7070 DCHECK_LE(num_temps, 1u);
7071 Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
7072 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7073 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
7074 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
7075 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
7076 Label done;
7077 Label* const final_label = codegen_->GetFinalLabel(instruction, &done);
7078 SlowPathCodeARM* slow_path = nullptr;
7079
7080 // Return 0 if `obj` is null.
7081 // avoid null check if we know obj is not null.
7082 if (instruction->MustDoNullCheck()) {
7083 DCHECK_NE(out, obj);
7084 __ LoadImmediate(out, 0);
7085 __ CompareAndBranchIfZero(obj, final_label);
7086 }
7087
7088 switch (type_check_kind) {
7089 case TypeCheckKind::kExactCheck: {
7090 // /* HeapReference<Class> */ out = obj->klass_
7091 GenerateReferenceLoadTwoRegisters(instruction,
7092 out_loc,
7093 obj_loc,
7094 class_offset,
7095 maybe_temp_loc,
7096 kCompilerReadBarrierOption);
7097 // Classes must be equal for the instanceof to succeed.
7098 __ cmp(out, ShifterOperand(cls));
7099 // We speculatively set the result to false without changing the condition
7100 // flags, which allows us to avoid some branching later.
7101 __ mov(out, ShifterOperand(0), AL, kCcKeep);
7102
7103 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7104 // we check that the output is in a low register, so that a 16-bit MOV
7105 // encoding can be used.
7106 if (ArmAssembler::IsLowRegister(out)) {
7107 __ it(EQ);
7108 __ mov(out, ShifterOperand(1), EQ);
7109 } else {
7110 __ b(final_label, NE);
7111 __ LoadImmediate(out, 1);
7112 }
7113
7114 break;
7115 }
7116
7117 case TypeCheckKind::kAbstractClassCheck: {
7118 // /* HeapReference<Class> */ out = obj->klass_
7119 GenerateReferenceLoadTwoRegisters(instruction,
7120 out_loc,
7121 obj_loc,
7122 class_offset,
7123 maybe_temp_loc,
7124 kCompilerReadBarrierOption);
7125 // If the class is abstract, we eagerly fetch the super class of the
7126 // object to avoid doing a comparison we know will fail.
7127 Label loop;
7128 __ Bind(&loop);
7129 // /* HeapReference<Class> */ out = out->super_class_
7130 GenerateReferenceLoadOneRegister(instruction,
7131 out_loc,
7132 super_offset,
7133 maybe_temp_loc,
7134 kCompilerReadBarrierOption);
7135 // If `out` is null, we use it for the result, and jump to the final label.
7136 __ CompareAndBranchIfZero(out, final_label);
7137 __ cmp(out, ShifterOperand(cls));
7138 __ b(&loop, NE);
7139 __ LoadImmediate(out, 1);
7140 break;
7141 }
7142
7143 case TypeCheckKind::kClassHierarchyCheck: {
7144 // /* HeapReference<Class> */ out = obj->klass_
7145 GenerateReferenceLoadTwoRegisters(instruction,
7146 out_loc,
7147 obj_loc,
7148 class_offset,
7149 maybe_temp_loc,
7150 kCompilerReadBarrierOption);
7151 // Walk over the class hierarchy to find a match.
7152 Label loop, success;
7153 __ Bind(&loop);
7154 __ cmp(out, ShifterOperand(cls));
7155 __ b(&success, EQ);
7156 // /* HeapReference<Class> */ out = out->super_class_
7157 GenerateReferenceLoadOneRegister(instruction,
7158 out_loc,
7159 super_offset,
7160 maybe_temp_loc,
7161 kCompilerReadBarrierOption);
7162 // This is essentially a null check, but it sets the condition flags to the
7163 // proper value for the code that follows the loop, i.e. not `EQ`.
7164 __ cmp(out, ShifterOperand(1));
7165 __ b(&loop, HS);
7166
7167 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7168 // we check that the output is in a low register, so that a 16-bit MOV
7169 // encoding can be used.
7170 if (ArmAssembler::IsLowRegister(out)) {
7171 // If `out` is null, we use it for the result, and the condition flags
7172 // have already been set to `NE`, so the IT block that comes afterwards
7173 // (and which handles the successful case) turns into a NOP (instead of
7174 // overwriting `out`).
7175 __ Bind(&success);
7176 // There is only one branch to the `success` label (which is bound to this
7177 // IT block), and it has the same condition, `EQ`, so in that case the MOV
7178 // is executed.
7179 __ it(EQ);
7180 __ mov(out, ShifterOperand(1), EQ);
7181 } else {
7182 // If `out` is null, we use it for the result, and jump to the final label.
7183 __ b(final_label);
7184 __ Bind(&success);
7185 __ LoadImmediate(out, 1);
7186 }
7187
7188 break;
7189 }
7190
7191 case TypeCheckKind::kArrayObjectCheck: {
7192 // /* HeapReference<Class> */ out = obj->klass_
7193 GenerateReferenceLoadTwoRegisters(instruction,
7194 out_loc,
7195 obj_loc,
7196 class_offset,
7197 maybe_temp_loc,
7198 kCompilerReadBarrierOption);
7199 // Do an exact check.
7200 Label exact_check;
7201 __ cmp(out, ShifterOperand(cls));
7202 __ b(&exact_check, EQ);
7203 // Otherwise, we need to check that the object's class is a non-primitive array.
7204 // /* HeapReference<Class> */ out = out->component_type_
7205 GenerateReferenceLoadOneRegister(instruction,
7206 out_loc,
7207 component_offset,
7208 maybe_temp_loc,
7209 kCompilerReadBarrierOption);
7210 // If `out` is null, we use it for the result, and jump to the final label.
7211 __ CompareAndBranchIfZero(out, final_label);
7212 __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
7213 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
7214 __ cmp(out, ShifterOperand(0));
7215 // We speculatively set the result to false without changing the condition
7216 // flags, which allows us to avoid some branching later.
7217 __ mov(out, ShifterOperand(0), AL, kCcKeep);
7218
7219 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7220 // we check that the output is in a low register, so that a 16-bit MOV
7221 // encoding can be used.
7222 if (ArmAssembler::IsLowRegister(out)) {
7223 __ Bind(&exact_check);
7224 __ it(EQ);
7225 __ mov(out, ShifterOperand(1), EQ);
7226 } else {
7227 __ b(final_label, NE);
7228 __ Bind(&exact_check);
7229 __ LoadImmediate(out, 1);
7230 }
7231
7232 break;
7233 }
7234
7235 case TypeCheckKind::kArrayCheck: {
7236 // No read barrier since the slow path will retry upon failure.
7237 // /* HeapReference<Class> */ out = obj->klass_
7238 GenerateReferenceLoadTwoRegisters(instruction,
7239 out_loc,
7240 obj_loc,
7241 class_offset,
7242 maybe_temp_loc,
7243 kWithoutReadBarrier);
7244 __ cmp(out, ShifterOperand(cls));
7245 DCHECK(locations->OnlyCallsOnSlowPath());
7246 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction,
7247 /* is_fatal */ false);
7248 codegen_->AddSlowPath(slow_path);
7249 __ b(slow_path->GetEntryLabel(), NE);
7250 __ LoadImmediate(out, 1);
7251 break;
7252 }
7253
7254 case TypeCheckKind::kUnresolvedCheck:
7255 case TypeCheckKind::kInterfaceCheck: {
7256 // Note that we indeed only call on slow path, but we always go
7257 // into the slow path for the unresolved and interface check
7258 // cases.
7259 //
7260 // We cannot directly call the InstanceofNonTrivial runtime
7261 // entry point without resorting to a type checking slow path
7262 // here (i.e. by calling InvokeRuntime directly), as it would
7263 // require to assign fixed registers for the inputs of this
7264 // HInstanceOf instruction (following the runtime calling
7265 // convention), which might be cluttered by the potential first
7266 // read barrier emission at the beginning of this method.
7267 //
7268 // TODO: Introduce a new runtime entry point taking the object
7269 // to test (instead of its class) as argument, and let it deal
7270 // with the read barrier issues. This will let us refactor this
7271 // case of the `switch` code as it was previously (with a direct
7272 // call to the runtime not using a type checking slow path).
7273 // This should also be beneficial for the other cases above.
7274 DCHECK(locations->OnlyCallsOnSlowPath());
7275 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction,
7276 /* is_fatal */ false);
7277 codegen_->AddSlowPath(slow_path);
7278 __ b(slow_path->GetEntryLabel());
7279 break;
7280 }
7281 }
7282
7283 if (done.IsLinked()) {
7284 __ Bind(&done);
7285 }
7286
7287 if (slow_path != nullptr) {
7288 __ Bind(slow_path->GetExitLabel());
7289 }
7290 }
7291
VisitCheckCast(HCheckCast * instruction)7292 void LocationsBuilderARM::VisitCheckCast(HCheckCast* instruction) {
7293 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
7294 bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
7295
7296 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7297 switch (type_check_kind) {
7298 case TypeCheckKind::kExactCheck:
7299 case TypeCheckKind::kAbstractClassCheck:
7300 case TypeCheckKind::kClassHierarchyCheck:
7301 case TypeCheckKind::kArrayObjectCheck:
7302 call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ?
7303 LocationSummary::kCallOnSlowPath :
7304 LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path.
7305 break;
7306 case TypeCheckKind::kArrayCheck:
7307 case TypeCheckKind::kUnresolvedCheck:
7308 case TypeCheckKind::kInterfaceCheck:
7309 call_kind = LocationSummary::kCallOnSlowPath;
7310 break;
7311 }
7312
7313 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
7314 locations->SetInAt(0, Location::RequiresRegister());
7315 locations->SetInAt(1, Location::RequiresRegister());
7316 locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
7317 }
7318
VisitCheckCast(HCheckCast * instruction)7319 void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) {
7320 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7321 LocationSummary* locations = instruction->GetLocations();
7322 Location obj_loc = locations->InAt(0);
7323 Register obj = obj_loc.AsRegister<Register>();
7324 Register cls = locations->InAt(1).AsRegister<Register>();
7325 Location temp_loc = locations->GetTemp(0);
7326 Register temp = temp_loc.AsRegister<Register>();
7327 const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
7328 DCHECK_LE(num_temps, 3u);
7329 Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
7330 Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
7331 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7332 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
7333 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
7334 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
7335 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
7336 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
7337 const uint32_t object_array_data_offset =
7338 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
7339
7340 // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases
7341 // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding
7342 // read barriers is done for performance and code size reasons.
7343 bool is_type_check_slow_path_fatal = false;
7344 if (!kEmitCompilerReadBarrier) {
7345 is_type_check_slow_path_fatal =
7346 (type_check_kind == TypeCheckKind::kExactCheck ||
7347 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
7348 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
7349 type_check_kind == TypeCheckKind::kArrayObjectCheck) &&
7350 !instruction->CanThrowIntoCatchBlock();
7351 }
7352 SlowPathCodeARM* type_check_slow_path =
7353 new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction,
7354 is_type_check_slow_path_fatal);
7355 codegen_->AddSlowPath(type_check_slow_path);
7356
7357 Label done;
7358 Label* final_label = codegen_->GetFinalLabel(instruction, &done);
7359 // Avoid null check if we know obj is not null.
7360 if (instruction->MustDoNullCheck()) {
7361 __ CompareAndBranchIfZero(obj, final_label);
7362 }
7363
7364 switch (type_check_kind) {
7365 case TypeCheckKind::kExactCheck:
7366 case TypeCheckKind::kArrayCheck: {
7367 // /* HeapReference<Class> */ temp = obj->klass_
7368 GenerateReferenceLoadTwoRegisters(instruction,
7369 temp_loc,
7370 obj_loc,
7371 class_offset,
7372 maybe_temp2_loc,
7373 kWithoutReadBarrier);
7374
7375 __ cmp(temp, ShifterOperand(cls));
7376 // Jump to slow path for throwing the exception or doing a
7377 // more involved array check.
7378 __ b(type_check_slow_path->GetEntryLabel(), NE);
7379 break;
7380 }
7381
7382 case TypeCheckKind::kAbstractClassCheck: {
7383 // /* HeapReference<Class> */ temp = obj->klass_
7384 GenerateReferenceLoadTwoRegisters(instruction,
7385 temp_loc,
7386 obj_loc,
7387 class_offset,
7388 maybe_temp2_loc,
7389 kWithoutReadBarrier);
7390
7391 // If the class is abstract, we eagerly fetch the super class of the
7392 // object to avoid doing a comparison we know will fail.
7393 Label loop;
7394 __ Bind(&loop);
7395 // /* HeapReference<Class> */ temp = temp->super_class_
7396 GenerateReferenceLoadOneRegister(instruction,
7397 temp_loc,
7398 super_offset,
7399 maybe_temp2_loc,
7400 kWithoutReadBarrier);
7401
7402 // If the class reference currently in `temp` is null, jump to the slow path to throw the
7403 // exception.
7404 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
7405
7406 // Otherwise, compare the classes.
7407 __ cmp(temp, ShifterOperand(cls));
7408 __ b(&loop, NE);
7409 break;
7410 }
7411
7412 case TypeCheckKind::kClassHierarchyCheck: {
7413 // /* HeapReference<Class> */ temp = obj->klass_
7414 GenerateReferenceLoadTwoRegisters(instruction,
7415 temp_loc,
7416 obj_loc,
7417 class_offset,
7418 maybe_temp2_loc,
7419 kWithoutReadBarrier);
7420
7421 // Walk over the class hierarchy to find a match.
7422 Label loop;
7423 __ Bind(&loop);
7424 __ cmp(temp, ShifterOperand(cls));
7425 __ b(final_label, EQ);
7426
7427 // /* HeapReference<Class> */ temp = temp->super_class_
7428 GenerateReferenceLoadOneRegister(instruction,
7429 temp_loc,
7430 super_offset,
7431 maybe_temp2_loc,
7432 kWithoutReadBarrier);
7433
7434 // If the class reference currently in `temp` is null, jump to the slow path to throw the
7435 // exception.
7436 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
7437 // Otherwise, jump to the beginning of the loop.
7438 __ b(&loop);
7439 break;
7440 }
7441
7442 case TypeCheckKind::kArrayObjectCheck: {
7443 // /* HeapReference<Class> */ temp = obj->klass_
7444 GenerateReferenceLoadTwoRegisters(instruction,
7445 temp_loc,
7446 obj_loc,
7447 class_offset,
7448 maybe_temp2_loc,
7449 kWithoutReadBarrier);
7450
7451 // Do an exact check.
7452 __ cmp(temp, ShifterOperand(cls));
7453 __ b(final_label, EQ);
7454
7455 // Otherwise, we need to check that the object's class is a non-primitive array.
7456 // /* HeapReference<Class> */ temp = temp->component_type_
7457 GenerateReferenceLoadOneRegister(instruction,
7458 temp_loc,
7459 component_offset,
7460 maybe_temp2_loc,
7461 kWithoutReadBarrier);
7462 // If the component type is null, jump to the slow path to throw the exception.
7463 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
7464 // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
7465 // to further check that this component type is not a primitive type.
7466 __ LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
7467 static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot");
7468 __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
7469 break;
7470 }
7471
7472 case TypeCheckKind::kUnresolvedCheck:
7473 // We always go into the type check slow path for the unresolved check case.
7474 // We cannot directly call the CheckCast runtime entry point
7475 // without resorting to a type checking slow path here (i.e. by
7476 // calling InvokeRuntime directly), as it would require to
7477 // assign fixed registers for the inputs of this HInstanceOf
7478 // instruction (following the runtime calling convention), which
7479 // might be cluttered by the potential first read barrier
7480 // emission at the beginning of this method.
7481
7482 __ b(type_check_slow_path->GetEntryLabel());
7483 break;
7484
7485 case TypeCheckKind::kInterfaceCheck: {
7486 // Avoid read barriers to improve performance of the fast path. We can not get false
7487 // positives by doing this.
7488 // /* HeapReference<Class> */ temp = obj->klass_
7489 GenerateReferenceLoadTwoRegisters(instruction,
7490 temp_loc,
7491 obj_loc,
7492 class_offset,
7493 maybe_temp2_loc,
7494 kWithoutReadBarrier);
7495
7496 // /* HeapReference<Class> */ temp = temp->iftable_
7497 GenerateReferenceLoadTwoRegisters(instruction,
7498 temp_loc,
7499 temp_loc,
7500 iftable_offset,
7501 maybe_temp2_loc,
7502 kWithoutReadBarrier);
7503 // Iftable is never null.
7504 __ ldr(maybe_temp2_loc.AsRegister<Register>(), Address(temp, array_length_offset));
7505 // Loop through the iftable and check if any class matches.
7506 Label start_loop;
7507 __ Bind(&start_loop);
7508 __ CompareAndBranchIfZero(maybe_temp2_loc.AsRegister<Register>(),
7509 type_check_slow_path->GetEntryLabel());
7510 __ ldr(maybe_temp3_loc.AsRegister<Register>(), Address(temp, object_array_data_offset));
7511 __ MaybeUnpoisonHeapReference(maybe_temp3_loc.AsRegister<Register>());
7512 // Go to next interface.
7513 __ add(temp, temp, ShifterOperand(2 * kHeapReferenceSize));
7514 __ sub(maybe_temp2_loc.AsRegister<Register>(),
7515 maybe_temp2_loc.AsRegister<Register>(),
7516 ShifterOperand(2));
7517 // Compare the classes and continue the loop if they do not match.
7518 __ cmp(cls, ShifterOperand(maybe_temp3_loc.AsRegister<Register>()));
7519 __ b(&start_loop, NE);
7520 break;
7521 }
7522 }
7523
7524 if (done.IsLinked()) {
7525 __ Bind(&done);
7526 }
7527
7528 __ Bind(type_check_slow_path->GetExitLabel());
7529 }
7530
VisitMonitorOperation(HMonitorOperation * instruction)7531 void LocationsBuilderARM::VisitMonitorOperation(HMonitorOperation* instruction) {
7532 LocationSummary* locations =
7533 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
7534 InvokeRuntimeCallingConvention calling_convention;
7535 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
7536 }
7537
VisitMonitorOperation(HMonitorOperation * instruction)7538 void InstructionCodeGeneratorARM::VisitMonitorOperation(HMonitorOperation* instruction) {
7539 codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
7540 instruction,
7541 instruction->GetDexPc());
7542 if (instruction->IsEnter()) {
7543 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
7544 } else {
7545 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
7546 }
7547 }
7548
VisitAnd(HAnd * instruction)7549 void LocationsBuilderARM::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction, AND); }
VisitOr(HOr * instruction)7550 void LocationsBuilderARM::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction, ORR); }
VisitXor(HXor * instruction)7551 void LocationsBuilderARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction, EOR); }
7552
HandleBitwiseOperation(HBinaryOperation * instruction,Opcode opcode)7553 void LocationsBuilderARM::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
7554 LocationSummary* locations =
7555 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
7556 DCHECK(instruction->GetResultType() == Primitive::kPrimInt
7557 || instruction->GetResultType() == Primitive::kPrimLong);
7558 // Note: GVN reorders commutative operations to have the constant on the right hand side.
7559 locations->SetInAt(0, Location::RequiresRegister());
7560 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
7561 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
7562 }
7563
VisitAnd(HAnd * instruction)7564 void InstructionCodeGeneratorARM::VisitAnd(HAnd* instruction) {
7565 HandleBitwiseOperation(instruction);
7566 }
7567
VisitOr(HOr * instruction)7568 void InstructionCodeGeneratorARM::VisitOr(HOr* instruction) {
7569 HandleBitwiseOperation(instruction);
7570 }
7571
VisitXor(HXor * instruction)7572 void InstructionCodeGeneratorARM::VisitXor(HXor* instruction) {
7573 HandleBitwiseOperation(instruction);
7574 }
7575
7576
VisitBitwiseNegatedRight(HBitwiseNegatedRight * instruction)7577 void LocationsBuilderARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
7578 LocationSummary* locations =
7579 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
7580 DCHECK(instruction->GetResultType() == Primitive::kPrimInt
7581 || instruction->GetResultType() == Primitive::kPrimLong);
7582
7583 locations->SetInAt(0, Location::RequiresRegister());
7584 locations->SetInAt(1, Location::RequiresRegister());
7585 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
7586 }
7587
VisitBitwiseNegatedRight(HBitwiseNegatedRight * instruction)7588 void InstructionCodeGeneratorARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
7589 LocationSummary* locations = instruction->GetLocations();
7590 Location first = locations->InAt(0);
7591 Location second = locations->InAt(1);
7592 Location out = locations->Out();
7593
7594 if (instruction->GetResultType() == Primitive::kPrimInt) {
7595 Register first_reg = first.AsRegister<Register>();
7596 ShifterOperand second_reg(second.AsRegister<Register>());
7597 Register out_reg = out.AsRegister<Register>();
7598
7599 switch (instruction->GetOpKind()) {
7600 case HInstruction::kAnd:
7601 __ bic(out_reg, first_reg, second_reg);
7602 break;
7603 case HInstruction::kOr:
7604 __ orn(out_reg, first_reg, second_reg);
7605 break;
7606 // There is no EON on arm.
7607 case HInstruction::kXor:
7608 default:
7609 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
7610 UNREACHABLE();
7611 }
7612 return;
7613
7614 } else {
7615 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
7616 Register first_low = first.AsRegisterPairLow<Register>();
7617 Register first_high = first.AsRegisterPairHigh<Register>();
7618 ShifterOperand second_low(second.AsRegisterPairLow<Register>());
7619 ShifterOperand second_high(second.AsRegisterPairHigh<Register>());
7620 Register out_low = out.AsRegisterPairLow<Register>();
7621 Register out_high = out.AsRegisterPairHigh<Register>();
7622
7623 switch (instruction->GetOpKind()) {
7624 case HInstruction::kAnd:
7625 __ bic(out_low, first_low, second_low);
7626 __ bic(out_high, first_high, second_high);
7627 break;
7628 case HInstruction::kOr:
7629 __ orn(out_low, first_low, second_low);
7630 __ orn(out_high, first_high, second_high);
7631 break;
7632 // There is no EON on arm.
7633 case HInstruction::kXor:
7634 default:
7635 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
7636 UNREACHABLE();
7637 }
7638 }
7639 }
7640
VisitDataProcWithShifterOp(HDataProcWithShifterOp * instruction)7641 void LocationsBuilderARM::VisitDataProcWithShifterOp(
7642 HDataProcWithShifterOp* instruction) {
7643 DCHECK(instruction->GetType() == Primitive::kPrimInt ||
7644 instruction->GetType() == Primitive::kPrimLong);
7645 LocationSummary* locations =
7646 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
7647 const bool overlap = instruction->GetType() == Primitive::kPrimLong &&
7648 HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind());
7649
7650 locations->SetInAt(0, Location::RequiresRegister());
7651 locations->SetInAt(1, Location::RequiresRegister());
7652 locations->SetOut(Location::RequiresRegister(),
7653 overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap);
7654 }
7655
VisitDataProcWithShifterOp(HDataProcWithShifterOp * instruction)7656 void InstructionCodeGeneratorARM::VisitDataProcWithShifterOp(
7657 HDataProcWithShifterOp* instruction) {
7658 const LocationSummary* const locations = instruction->GetLocations();
7659 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
7660 const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
7661 const Location left = locations->InAt(0);
7662 const Location right = locations->InAt(1);
7663 const Location out = locations->Out();
7664
7665 if (instruction->GetType() == Primitive::kPrimInt) {
7666 DCHECK(!HDataProcWithShifterOp::IsExtensionOp(op_kind));
7667
7668 const Register second = instruction->InputAt(1)->GetType() == Primitive::kPrimLong
7669 ? right.AsRegisterPairLow<Register>()
7670 : right.AsRegister<Register>();
7671
7672 GenerateDataProcInstruction(kind,
7673 out.AsRegister<Register>(),
7674 left.AsRegister<Register>(),
7675 ShifterOperand(second,
7676 ShiftFromOpKind(op_kind),
7677 instruction->GetShiftAmount()),
7678 codegen_);
7679 } else {
7680 DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong);
7681
7682 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
7683 const Register second = right.AsRegister<Register>();
7684
7685 DCHECK_NE(out.AsRegisterPairLow<Register>(), second);
7686 GenerateDataProc(kind,
7687 out,
7688 left,
7689 ShifterOperand(second),
7690 ShifterOperand(second, ASR, 31),
7691 codegen_);
7692 } else {
7693 GenerateLongDataProc(instruction, codegen_);
7694 }
7695 }
7696 }
7697
GenerateAndConst(Register out,Register first,uint32_t value)7698 void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) {
7699 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
7700 if (value == 0xffffffffu) {
7701 if (out != first) {
7702 __ mov(out, ShifterOperand(first));
7703 }
7704 return;
7705 }
7706 if (value == 0u) {
7707 __ mov(out, ShifterOperand(0));
7708 return;
7709 }
7710 ShifterOperand so;
7711 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, AND, value, &so)) {
7712 __ and_(out, first, so);
7713 } else if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, BIC, ~value, &so)) {
7714 __ bic(out, first, ShifterOperand(~value));
7715 } else {
7716 DCHECK(IsPowerOfTwo(value + 1));
7717 __ ubfx(out, first, 0, WhichPowerOf2(value + 1));
7718 }
7719 }
7720
GenerateOrrConst(Register out,Register first,uint32_t value)7721 void InstructionCodeGeneratorARM::GenerateOrrConst(Register out, Register first, uint32_t value) {
7722 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
7723 if (value == 0u) {
7724 if (out != first) {
7725 __ mov(out, ShifterOperand(first));
7726 }
7727 return;
7728 }
7729 if (value == 0xffffffffu) {
7730 __ mvn(out, ShifterOperand(0));
7731 return;
7732 }
7733 ShifterOperand so;
7734 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORR, value, &so)) {
7735 __ orr(out, first, so);
7736 } else {
7737 DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORN, ~value, &so));
7738 __ orn(out, first, ShifterOperand(~value));
7739 }
7740 }
7741
GenerateEorConst(Register out,Register first,uint32_t value)7742 void InstructionCodeGeneratorARM::GenerateEorConst(Register out, Register first, uint32_t value) {
7743 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
7744 if (value == 0u) {
7745 if (out != first) {
7746 __ mov(out, ShifterOperand(first));
7747 }
7748 return;
7749 }
7750 __ eor(out, first, ShifterOperand(value));
7751 }
7752
GenerateAddLongConst(Location out,Location first,uint64_t value)7753 void InstructionCodeGeneratorARM::GenerateAddLongConst(Location out,
7754 Location first,
7755 uint64_t value) {
7756 Register out_low = out.AsRegisterPairLow<Register>();
7757 Register out_high = out.AsRegisterPairHigh<Register>();
7758 Register first_low = first.AsRegisterPairLow<Register>();
7759 Register first_high = first.AsRegisterPairHigh<Register>();
7760 uint32_t value_low = Low32Bits(value);
7761 uint32_t value_high = High32Bits(value);
7762 if (value_low == 0u) {
7763 if (out_low != first_low) {
7764 __ mov(out_low, ShifterOperand(first_low));
7765 }
7766 __ AddConstant(out_high, first_high, value_high);
7767 return;
7768 }
7769 __ AddConstantSetFlags(out_low, first_low, value_low);
7770 ShifterOperand so;
7771 if (__ ShifterOperandCanHold(out_high, first_high, ADC, value_high, kCcDontCare, &so)) {
7772 __ adc(out_high, first_high, so);
7773 } else if (__ ShifterOperandCanHold(out_low, first_low, SBC, ~value_high, kCcDontCare, &so)) {
7774 __ sbc(out_high, first_high, so);
7775 } else {
7776 LOG(FATAL) << "Unexpected constant " << value_high;
7777 UNREACHABLE();
7778 }
7779 }
7780
HandleBitwiseOperation(HBinaryOperation * instruction)7781 void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instruction) {
7782 LocationSummary* locations = instruction->GetLocations();
7783 Location first = locations->InAt(0);
7784 Location second = locations->InAt(1);
7785 Location out = locations->Out();
7786
7787 if (second.IsConstant()) {
7788 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
7789 uint32_t value_low = Low32Bits(value);
7790 if (instruction->GetResultType() == Primitive::kPrimInt) {
7791 Register first_reg = first.AsRegister<Register>();
7792 Register out_reg = out.AsRegister<Register>();
7793 if (instruction->IsAnd()) {
7794 GenerateAndConst(out_reg, first_reg, value_low);
7795 } else if (instruction->IsOr()) {
7796 GenerateOrrConst(out_reg, first_reg, value_low);
7797 } else {
7798 DCHECK(instruction->IsXor());
7799 GenerateEorConst(out_reg, first_reg, value_low);
7800 }
7801 } else {
7802 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
7803 uint32_t value_high = High32Bits(value);
7804 Register first_low = first.AsRegisterPairLow<Register>();
7805 Register first_high = first.AsRegisterPairHigh<Register>();
7806 Register out_low = out.AsRegisterPairLow<Register>();
7807 Register out_high = out.AsRegisterPairHigh<Register>();
7808 if (instruction->IsAnd()) {
7809 GenerateAndConst(out_low, first_low, value_low);
7810 GenerateAndConst(out_high, first_high, value_high);
7811 } else if (instruction->IsOr()) {
7812 GenerateOrrConst(out_low, first_low, value_low);
7813 GenerateOrrConst(out_high, first_high, value_high);
7814 } else {
7815 DCHECK(instruction->IsXor());
7816 GenerateEorConst(out_low, first_low, value_low);
7817 GenerateEorConst(out_high, first_high, value_high);
7818 }
7819 }
7820 return;
7821 }
7822
7823 if (instruction->GetResultType() == Primitive::kPrimInt) {
7824 Register first_reg = first.AsRegister<Register>();
7825 ShifterOperand second_reg(second.AsRegister<Register>());
7826 Register out_reg = out.AsRegister<Register>();
7827 if (instruction->IsAnd()) {
7828 __ and_(out_reg, first_reg, second_reg);
7829 } else if (instruction->IsOr()) {
7830 __ orr(out_reg, first_reg, second_reg);
7831 } else {
7832 DCHECK(instruction->IsXor());
7833 __ eor(out_reg, first_reg, second_reg);
7834 }
7835 } else {
7836 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
7837 Register first_low = first.AsRegisterPairLow<Register>();
7838 Register first_high = first.AsRegisterPairHigh<Register>();
7839 ShifterOperand second_low(second.AsRegisterPairLow<Register>());
7840 ShifterOperand second_high(second.AsRegisterPairHigh<Register>());
7841 Register out_low = out.AsRegisterPairLow<Register>();
7842 Register out_high = out.AsRegisterPairHigh<Register>();
7843 if (instruction->IsAnd()) {
7844 __ and_(out_low, first_low, second_low);
7845 __ and_(out_high, first_high, second_high);
7846 } else if (instruction->IsOr()) {
7847 __ orr(out_low, first_low, second_low);
7848 __ orr(out_high, first_high, second_high);
7849 } else {
7850 DCHECK(instruction->IsXor());
7851 __ eor(out_low, first_low, second_low);
7852 __ eor(out_high, first_high, second_high);
7853 }
7854 }
7855 }
7856
GenerateReferenceLoadOneRegister(HInstruction * instruction,Location out,uint32_t offset,Location maybe_temp,ReadBarrierOption read_barrier_option)7857 void InstructionCodeGeneratorARM::GenerateReferenceLoadOneRegister(
7858 HInstruction* instruction,
7859 Location out,
7860 uint32_t offset,
7861 Location maybe_temp,
7862 ReadBarrierOption read_barrier_option) {
7863 Register out_reg = out.AsRegister<Register>();
7864 if (read_barrier_option == kWithReadBarrier) {
7865 CHECK(kEmitCompilerReadBarrier);
7866 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
7867 if (kUseBakerReadBarrier) {
7868 // Load with fast path based Baker's read barrier.
7869 // /* HeapReference<Object> */ out = *(out + offset)
7870 codegen_->GenerateFieldLoadWithBakerReadBarrier(
7871 instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false);
7872 } else {
7873 // Load with slow path based read barrier.
7874 // Save the value of `out` into `maybe_temp` before overwriting it
7875 // in the following move operation, as we will need it for the
7876 // read barrier below.
7877 __ Mov(maybe_temp.AsRegister<Register>(), out_reg);
7878 // /* HeapReference<Object> */ out = *(out + offset)
7879 __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
7880 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
7881 }
7882 } else {
7883 // Plain load with no read barrier.
7884 // /* HeapReference<Object> */ out = *(out + offset)
7885 __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
7886 __ MaybeUnpoisonHeapReference(out_reg);
7887 }
7888 }
7889
GenerateReferenceLoadTwoRegisters(HInstruction * instruction,Location out,Location obj,uint32_t offset,Location maybe_temp,ReadBarrierOption read_barrier_option)7890 void InstructionCodeGeneratorARM::GenerateReferenceLoadTwoRegisters(
7891 HInstruction* instruction,
7892 Location out,
7893 Location obj,
7894 uint32_t offset,
7895 Location maybe_temp,
7896 ReadBarrierOption read_barrier_option) {
7897 Register out_reg = out.AsRegister<Register>();
7898 Register obj_reg = obj.AsRegister<Register>();
7899 if (read_barrier_option == kWithReadBarrier) {
7900 CHECK(kEmitCompilerReadBarrier);
7901 if (kUseBakerReadBarrier) {
7902 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
7903 // Load with fast path based Baker's read barrier.
7904 // /* HeapReference<Object> */ out = *(obj + offset)
7905 codegen_->GenerateFieldLoadWithBakerReadBarrier(
7906 instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false);
7907 } else {
7908 // Load with slow path based read barrier.
7909 // /* HeapReference<Object> */ out = *(obj + offset)
7910 __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
7911 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
7912 }
7913 } else {
7914 // Plain load with no read barrier.
7915 // /* HeapReference<Object> */ out = *(obj + offset)
7916 __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
7917 __ MaybeUnpoisonHeapReference(out_reg);
7918 }
7919 }
7920
GenerateGcRootFieldLoad(HInstruction * instruction,Location root,Register obj,uint32_t offset,ReadBarrierOption read_barrier_option)7921 void InstructionCodeGeneratorARM::GenerateGcRootFieldLoad(HInstruction* instruction,
7922 Location root,
7923 Register obj,
7924 uint32_t offset,
7925 ReadBarrierOption read_barrier_option) {
7926 Register root_reg = root.AsRegister<Register>();
7927 if (read_barrier_option == kWithReadBarrier) {
7928 DCHECK(kEmitCompilerReadBarrier);
7929 if (kUseBakerReadBarrier) {
7930 // Fast path implementation of art::ReadBarrier::BarrierForRoot when
7931 // Baker's read barrier are used.
7932 //
7933 // Note that we do not actually check the value of
7934 // `GetIsGcMarking()` to decide whether to mark the loaded GC
7935 // root or not. Instead, we load into `temp` the read barrier
7936 // mark entry point corresponding to register `root`. If `temp`
7937 // is null, it means that `GetIsGcMarking()` is false, and vice
7938 // versa.
7939 //
7940 // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
7941 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
7942 // if (temp != nullptr) { // <=> Thread::Current()->GetIsGcMarking()
7943 // // Slow path.
7944 // root = temp(root); // root = ReadBarrier::Mark(root); // Runtime entry point call.
7945 // }
7946
7947 // Slow path marking the GC root `root`. The entrypoint will already be loaded in `temp`.
7948 Location temp = Location::RegisterLocation(LR);
7949 SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(
7950 instruction, root, /* entrypoint */ temp);
7951 codegen_->AddSlowPath(slow_path);
7952
7953 // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
7954 const int32_t entry_point_offset =
7955 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(root.reg());
7956 // Loading the entrypoint does not require a load acquire since it is only changed when
7957 // threads are suspended or running a checkpoint.
7958 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, entry_point_offset);
7959
7960 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
7961 __ LoadFromOffset(kLoadWord, root_reg, obj, offset);
7962 static_assert(
7963 sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
7964 "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
7965 "have different sizes.");
7966 static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
7967 "art::mirror::CompressedReference<mirror::Object> and int32_t "
7968 "have different sizes.");
7969
7970 // The entrypoint is null when the GC is not marking, this prevents one load compared to
7971 // checking GetIsGcMarking.
7972 __ CompareAndBranchIfNonZero(temp.AsRegister<Register>(), slow_path->GetEntryLabel());
7973 __ Bind(slow_path->GetExitLabel());
7974 } else {
7975 // GC root loaded through a slow path for read barriers other
7976 // than Baker's.
7977 // /* GcRoot<mirror::Object>* */ root = obj + offset
7978 __ AddConstant(root_reg, obj, offset);
7979 // /* mirror::Object* */ root = root->Read()
7980 codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
7981 }
7982 } else {
7983 // Plain GC root load with no read barrier.
7984 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
7985 __ LoadFromOffset(kLoadWord, root_reg, obj, offset);
7986 // Note that GC roots are not affected by heap poisoning, thus we
7987 // do not have to unpoison `root_reg` here.
7988 }
7989 }
7990
GenerateFieldLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,Register obj,uint32_t offset,Location temp,bool needs_null_check)7991 void CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
7992 Location ref,
7993 Register obj,
7994 uint32_t offset,
7995 Location temp,
7996 bool needs_null_check) {
7997 DCHECK(kEmitCompilerReadBarrier);
7998 DCHECK(kUseBakerReadBarrier);
7999
8000 // /* HeapReference<Object> */ ref = *(obj + offset)
8001 Location no_index = Location::NoLocation();
8002 ScaleFactor no_scale_factor = TIMES_1;
8003 GenerateReferenceLoadWithBakerReadBarrier(
8004 instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check);
8005 }
8006
GenerateArrayLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,Register obj,uint32_t data_offset,Location index,Location temp,bool needs_null_check)8007 void CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
8008 Location ref,
8009 Register obj,
8010 uint32_t data_offset,
8011 Location index,
8012 Location temp,
8013 bool needs_null_check) {
8014 DCHECK(kEmitCompilerReadBarrier);
8015 DCHECK(kUseBakerReadBarrier);
8016
8017 static_assert(
8018 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
8019 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
8020 // /* HeapReference<Object> */ ref =
8021 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
8022 ScaleFactor scale_factor = TIMES_4;
8023 GenerateReferenceLoadWithBakerReadBarrier(
8024 instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check);
8025 }
8026
GenerateReferenceLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,Register obj,uint32_t offset,Location index,ScaleFactor scale_factor,Location temp,bool needs_null_check,bool always_update_field,Register * temp2)8027 void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
8028 Location ref,
8029 Register obj,
8030 uint32_t offset,
8031 Location index,
8032 ScaleFactor scale_factor,
8033 Location temp,
8034 bool needs_null_check,
8035 bool always_update_field,
8036 Register* temp2) {
8037 DCHECK(kEmitCompilerReadBarrier);
8038 DCHECK(kUseBakerReadBarrier);
8039
8040 // Query `art::Thread::Current()->GetIsGcMarking()` to decide
8041 // whether we need to enter the slow path to mark the reference.
8042 // Then, in the slow path, check the gray bit in the lock word of
8043 // the reference's holder (`obj`) to decide whether to mark `ref` or
8044 // not.
8045 //
8046 // Note that we do not actually check the value of `GetIsGcMarking()`;
8047 // instead, we load into `temp3` the read barrier mark entry point
8048 // corresponding to register `ref`. If `temp3` is null, it means
8049 // that `GetIsGcMarking()` is false, and vice versa.
8050 //
8051 // temp3 = Thread::Current()->pReadBarrierMarkReg ## root.reg()
8052 // if (temp3 != nullptr) { // <=> Thread::Current()->GetIsGcMarking()
8053 // // Slow path.
8054 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
8055 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
8056 // HeapReference<mirror::Object> ref = *src; // Original reference load.
8057 // bool is_gray = (rb_state == ReadBarrier::GrayState());
8058 // if (is_gray) {
8059 // ref = temp3(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
8060 // }
8061 // } else {
8062 // HeapReference<mirror::Object> ref = *src; // Original reference load.
8063 // }
8064
8065 Register temp_reg = temp.AsRegister<Register>();
8066
8067 // Slow path marking the object `ref` when the GC is marking. The
8068 // entrypoint will already be loaded in `temp3`.
8069 Location temp3 = Location::RegisterLocation(LR);
8070 SlowPathCodeARM* slow_path;
8071 if (always_update_field) {
8072 DCHECK(temp2 != nullptr);
8073 // LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM only
8074 // supports address of the form `obj + field_offset`, where `obj`
8075 // is a register and `field_offset` is a register pair (of which
8076 // only the lower half is used). Thus `offset` and `scale_factor`
8077 // above are expected to be null in this code path.
8078 DCHECK_EQ(offset, 0u);
8079 DCHECK_EQ(scale_factor, ScaleFactor::TIMES_1);
8080 Location field_offset = index;
8081 slow_path =
8082 new (GetGraph()->GetArena()) LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM(
8083 instruction,
8084 ref,
8085 obj,
8086 offset,
8087 /* index */ field_offset,
8088 scale_factor,
8089 needs_null_check,
8090 temp_reg,
8091 *temp2,
8092 /* entrypoint */ temp3);
8093 } else {
8094 slow_path = new (GetGraph()->GetArena()) LoadReferenceWithBakerReadBarrierSlowPathARM(
8095 instruction,
8096 ref,
8097 obj,
8098 offset,
8099 index,
8100 scale_factor,
8101 needs_null_check,
8102 temp_reg,
8103 /* entrypoint */ temp3);
8104 }
8105 AddSlowPath(slow_path);
8106
8107 // temp3 = Thread::Current()->pReadBarrierMarkReg ## ref.reg()
8108 const int32_t entry_point_offset =
8109 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref.reg());
8110 // Loading the entrypoint does not require a load acquire since it is only changed when
8111 // threads are suspended or running a checkpoint.
8112 __ LoadFromOffset(kLoadWord, temp3.AsRegister<Register>(), TR, entry_point_offset);
8113 // The entrypoint is null when the GC is not marking, this prevents one load compared to
8114 // checking GetIsGcMarking.
8115 __ CompareAndBranchIfNonZero(temp3.AsRegister<Register>(), slow_path->GetEntryLabel());
8116 // Fast path: just load the reference.
8117 GenerateRawReferenceLoad(instruction, ref, obj, offset, index, scale_factor, needs_null_check);
8118 __ Bind(slow_path->GetExitLabel());
8119 }
8120
GenerateRawReferenceLoad(HInstruction * instruction,Location ref,Register obj,uint32_t offset,Location index,ScaleFactor scale_factor,bool needs_null_check)8121 void CodeGeneratorARM::GenerateRawReferenceLoad(HInstruction* instruction,
8122 Location ref,
8123 Register obj,
8124 uint32_t offset,
8125 Location index,
8126 ScaleFactor scale_factor,
8127 bool needs_null_check) {
8128 Register ref_reg = ref.AsRegister<Register>();
8129
8130 if (index.IsValid()) {
8131 // Load types involving an "index": ArrayGet,
8132 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
8133 // intrinsics.
8134 // /* HeapReference<mirror::Object> */ ref = *(obj + offset + (index << scale_factor))
8135 if (index.IsConstant()) {
8136 size_t computed_offset =
8137 (index.GetConstant()->AsIntConstant()->GetValue() << scale_factor) + offset;
8138 __ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
8139 } else {
8140 // Handle the special case of the
8141 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
8142 // intrinsics, which use a register pair as index ("long
8143 // offset"), of which only the low part contains data.
8144 Register index_reg = index.IsRegisterPair()
8145 ? index.AsRegisterPairLow<Register>()
8146 : index.AsRegister<Register>();
8147 __ add(IP, obj, ShifterOperand(index_reg, LSL, scale_factor));
8148 __ LoadFromOffset(kLoadWord, ref_reg, IP, offset);
8149 }
8150 } else {
8151 // /* HeapReference<mirror::Object> */ ref = *(obj + offset)
8152 __ LoadFromOffset(kLoadWord, ref_reg, obj, offset);
8153 }
8154
8155 if (needs_null_check) {
8156 MaybeRecordImplicitNullCheck(instruction);
8157 }
8158
8159 // Object* ref = ref_addr->AsMirrorPtr()
8160 __ MaybeUnpoisonHeapReference(ref_reg);
8161 }
8162
GenerateReadBarrierSlow(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)8163 void CodeGeneratorARM::GenerateReadBarrierSlow(HInstruction* instruction,
8164 Location out,
8165 Location ref,
8166 Location obj,
8167 uint32_t offset,
8168 Location index) {
8169 DCHECK(kEmitCompilerReadBarrier);
8170
8171 // Insert a slow path based read barrier *after* the reference load.
8172 //
8173 // If heap poisoning is enabled, the unpoisoning of the loaded
8174 // reference will be carried out by the runtime within the slow
8175 // path.
8176 //
8177 // Note that `ref` currently does not get unpoisoned (when heap
8178 // poisoning is enabled), which is alright as the `ref` argument is
8179 // not used by the artReadBarrierSlow entry point.
8180 //
8181 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
8182 SlowPathCodeARM* slow_path = new (GetGraph()->GetArena())
8183 ReadBarrierForHeapReferenceSlowPathARM(instruction, out, ref, obj, offset, index);
8184 AddSlowPath(slow_path);
8185
8186 __ b(slow_path->GetEntryLabel());
8187 __ Bind(slow_path->GetExitLabel());
8188 }
8189
MaybeGenerateReadBarrierSlow(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)8190 void CodeGeneratorARM::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
8191 Location out,
8192 Location ref,
8193 Location obj,
8194 uint32_t offset,
8195 Location index) {
8196 if (kEmitCompilerReadBarrier) {
8197 // Baker's read barriers shall be handled by the fast path
8198 // (CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier).
8199 DCHECK(!kUseBakerReadBarrier);
8200 // If heap poisoning is enabled, unpoisoning will be taken care of
8201 // by the runtime within the slow path.
8202 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
8203 } else if (kPoisonHeapReferences) {
8204 __ UnpoisonHeapReference(out.AsRegister<Register>());
8205 }
8206 }
8207
GenerateReadBarrierForRootSlow(HInstruction * instruction,Location out,Location root)8208 void CodeGeneratorARM::GenerateReadBarrierForRootSlow(HInstruction* instruction,
8209 Location out,
8210 Location root) {
8211 DCHECK(kEmitCompilerReadBarrier);
8212
8213 // Insert a slow path based read barrier *after* the GC root load.
8214 //
8215 // Note that GC roots are not affected by heap poisoning, so we do
8216 // not need to do anything special for this here.
8217 SlowPathCodeARM* slow_path =
8218 new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARM(instruction, out, root);
8219 AddSlowPath(slow_path);
8220
8221 __ b(slow_path->GetEntryLabel());
8222 __ Bind(slow_path->GetExitLabel());
8223 }
8224
GetSupportedInvokeStaticOrDirectDispatch(const HInvokeStaticOrDirect::DispatchInfo & desired_dispatch_info,HInvokeStaticOrDirect * invoke ATTRIBUTE_UNUSED)8225 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch(
8226 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
8227 HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
8228 return desired_dispatch_info;
8229 }
8230
GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect * invoke,Register temp)8231 Register CodeGeneratorARM::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
8232 Register temp) {
8233 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
8234 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
8235 if (!invoke->GetLocations()->Intrinsified()) {
8236 return location.AsRegister<Register>();
8237 }
8238 // For intrinsics we allow any location, so it may be on the stack.
8239 if (!location.IsRegister()) {
8240 __ LoadFromOffset(kLoadWord, temp, SP, location.GetStackIndex());
8241 return temp;
8242 }
8243 // For register locations, check if the register was saved. If so, get it from the stack.
8244 // Note: There is a chance that the register was saved but not overwritten, so we could
8245 // save one load. However, since this is just an intrinsic slow path we prefer this
8246 // simple and more robust approach rather that trying to determine if that's the case.
8247 SlowPathCode* slow_path = GetCurrentSlowPath();
8248 if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) {
8249 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(location.AsRegister<Register>());
8250 __ LoadFromOffset(kLoadWord, temp, SP, stack_offset);
8251 return temp;
8252 }
8253 return location.AsRegister<Register>();
8254 }
8255
GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect * invoke,Location temp)8256 Location CodeGeneratorARM::GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
8257 Location temp) {
8258 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
8259 switch (invoke->GetMethodLoadKind()) {
8260 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
8261 uint32_t offset =
8262 GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
8263 // temp = thread->string_init_entrypoint
8264 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, offset);
8265 break;
8266 }
8267 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
8268 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
8269 break;
8270 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
8271 __ LoadImmediate(temp.AsRegister<Register>(), invoke->GetMethodAddress());
8272 break;
8273 case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
8274 HArmDexCacheArraysBase* base =
8275 invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase();
8276 Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke,
8277 temp.AsRegister<Register>());
8278 int32_t offset = invoke->GetDexCacheArrayOffset() - base->GetElementOffset();
8279 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), base_reg, offset);
8280 break;
8281 }
8282 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
8283 Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
8284 Register method_reg;
8285 Register reg = temp.AsRegister<Register>();
8286 if (current_method.IsRegister()) {
8287 method_reg = current_method.AsRegister<Register>();
8288 } else {
8289 DCHECK(invoke->GetLocations()->Intrinsified());
8290 DCHECK(!current_method.IsValid());
8291 method_reg = reg;
8292 __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset);
8293 }
8294 // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_;
8295 __ LoadFromOffset(kLoadWord,
8296 reg,
8297 method_reg,
8298 ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value());
8299 // temp = temp[index_in_cache];
8300 // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
8301 uint32_t index_in_cache = invoke->GetDexMethodIndex();
8302 __ LoadFromOffset(kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache));
8303 break;
8304 }
8305 }
8306 return callee_method;
8307 }
8308
GenerateStaticOrDirectCall(HInvokeStaticOrDirect * invoke,Location temp)8309 void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
8310 Location callee_method = GenerateCalleeMethodStaticOrDirectCall(invoke, temp);
8311
8312 switch (invoke->GetCodePtrLocation()) {
8313 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
8314 __ bl(GetFrameEntryLabel());
8315 break;
8316 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
8317 // LR = callee_method->entry_point_from_quick_compiled_code_
8318 __ LoadFromOffset(
8319 kLoadWord, LR, callee_method.AsRegister<Register>(),
8320 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
8321 // LR()
8322 __ blx(LR);
8323 break;
8324 }
8325
8326 DCHECK(!IsLeafMethod());
8327 }
8328
GenerateVirtualCall(HInvokeVirtual * invoke,Location temp_location)8329 void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) {
8330 Register temp = temp_location.AsRegister<Register>();
8331 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
8332 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
8333
8334 // Use the calling convention instead of the location of the receiver, as
8335 // intrinsics may have put the receiver in a different register. In the intrinsics
8336 // slow path, the arguments have been moved to the right place, so here we are
8337 // guaranteed that the receiver is the first register of the calling convention.
8338 InvokeDexCallingConvention calling_convention;
8339 Register receiver = calling_convention.GetRegisterAt(0);
8340 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
8341 // /* HeapReference<Class> */ temp = receiver->klass_
8342 __ LoadFromOffset(kLoadWord, temp, receiver, class_offset);
8343 MaybeRecordImplicitNullCheck(invoke);
8344 // Instead of simply (possibly) unpoisoning `temp` here, we should
8345 // emit a read barrier for the previous class reference load.
8346 // However this is not required in practice, as this is an
8347 // intermediate/temporary reference and because the current
8348 // concurrent copying collector keeps the from-space memory
8349 // intact/accessible until the end of the marking phase (the
8350 // concurrent copying collector may not in the future).
8351 __ MaybeUnpoisonHeapReference(temp);
8352 // temp = temp->GetMethodAt(method_offset);
8353 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
8354 kArmPointerSize).Int32Value();
8355 __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
8356 // LR = temp->GetEntryPoint();
8357 __ LoadFromOffset(kLoadWord, LR, temp, entry_point);
8358 // LR();
8359 __ blx(LR);
8360 }
8361
NewPcRelativeStringPatch(const DexFile & dex_file,dex::StringIndex string_index)8362 CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeStringPatch(
8363 const DexFile& dex_file, dex::StringIndex string_index) {
8364 return NewPcRelativePatch(dex_file, string_index.index_, &pc_relative_string_patches_);
8365 }
8366
NewPcRelativeTypePatch(const DexFile & dex_file,dex::TypeIndex type_index)8367 CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeTypePatch(
8368 const DexFile& dex_file, dex::TypeIndex type_index) {
8369 return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
8370 }
8371
NewTypeBssEntryPatch(const DexFile & dex_file,dex::TypeIndex type_index)8372 CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewTypeBssEntryPatch(
8373 const DexFile& dex_file, dex::TypeIndex type_index) {
8374 return NewPcRelativePatch(dex_file, type_index.index_, &type_bss_entry_patches_);
8375 }
8376
NewPcRelativeDexCacheArrayPatch(const DexFile & dex_file,uint32_t element_offset)8377 CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeDexCacheArrayPatch(
8378 const DexFile& dex_file, uint32_t element_offset) {
8379 return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_);
8380 }
8381
NewPcRelativePatch(const DexFile & dex_file,uint32_t offset_or_index,ArenaDeque<PcRelativePatchInfo> * patches)8382 CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativePatch(
8383 const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
8384 patches->emplace_back(dex_file, offset_or_index);
8385 return &patches->back();
8386 }
8387
DeduplicateBootImageStringLiteral(const DexFile & dex_file,dex::StringIndex string_index)8388 Literal* CodeGeneratorARM::DeduplicateBootImageStringLiteral(const DexFile& dex_file,
8389 dex::StringIndex string_index) {
8390 return boot_image_string_patches_.GetOrCreate(
8391 StringReference(&dex_file, string_index),
8392 [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
8393 }
8394
DeduplicateBootImageTypeLiteral(const DexFile & dex_file,dex::TypeIndex type_index)8395 Literal* CodeGeneratorARM::DeduplicateBootImageTypeLiteral(const DexFile& dex_file,
8396 dex::TypeIndex type_index) {
8397 return boot_image_type_patches_.GetOrCreate(
8398 TypeReference(&dex_file, type_index),
8399 [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
8400 }
8401
DeduplicateBootImageAddressLiteral(uint32_t address)8402 Literal* CodeGeneratorARM::DeduplicateBootImageAddressLiteral(uint32_t address) {
8403 return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), &uint32_literals_);
8404 }
8405
DeduplicateJitStringLiteral(const DexFile & dex_file,dex::StringIndex string_index,Handle<mirror::String> handle)8406 Literal* CodeGeneratorARM::DeduplicateJitStringLiteral(const DexFile& dex_file,
8407 dex::StringIndex string_index,
8408 Handle<mirror::String> handle) {
8409 jit_string_roots_.Overwrite(StringReference(&dex_file, string_index),
8410 reinterpret_cast64<uint64_t>(handle.GetReference()));
8411 return jit_string_patches_.GetOrCreate(
8412 StringReference(&dex_file, string_index),
8413 [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
8414 }
8415
DeduplicateJitClassLiteral(const DexFile & dex_file,dex::TypeIndex type_index,Handle<mirror::Class> handle)8416 Literal* CodeGeneratorARM::DeduplicateJitClassLiteral(const DexFile& dex_file,
8417 dex::TypeIndex type_index,
8418 Handle<mirror::Class> handle) {
8419 jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index),
8420 reinterpret_cast64<uint64_t>(handle.GetReference()));
8421 return jit_class_patches_.GetOrCreate(
8422 TypeReference(&dex_file, type_index),
8423 [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
8424 }
8425
8426 template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo> & infos,ArenaVector<LinkerPatch> * linker_patches)8427 inline void CodeGeneratorARM::EmitPcRelativeLinkerPatches(
8428 const ArenaDeque<PcRelativePatchInfo>& infos,
8429 ArenaVector<LinkerPatch>* linker_patches) {
8430 for (const PcRelativePatchInfo& info : infos) {
8431 const DexFile& dex_file = info.target_dex_file;
8432 size_t offset_or_index = info.offset_or_index;
8433 DCHECK(info.add_pc_label.IsBound());
8434 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position());
8435 // Add MOVW patch.
8436 DCHECK(info.movw_label.IsBound());
8437 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position());
8438 linker_patches->push_back(Factory(movw_offset, &dex_file, add_pc_offset, offset_or_index));
8439 // Add MOVT patch.
8440 DCHECK(info.movt_label.IsBound());
8441 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position());
8442 linker_patches->push_back(Factory(movt_offset, &dex_file, add_pc_offset, offset_or_index));
8443 }
8444 }
8445
EmitLinkerPatches(ArenaVector<LinkerPatch> * linker_patches)8446 void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
8447 DCHECK(linker_patches->empty());
8448 size_t size =
8449 /* MOVW+MOVT for each entry */ 2u * pc_relative_dex_cache_patches_.size() +
8450 boot_image_string_patches_.size() +
8451 /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() +
8452 boot_image_type_patches_.size() +
8453 /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size() +
8454 /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size();
8455 linker_patches->reserve(size);
8456 EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
8457 linker_patches);
8458 for (const auto& entry : boot_image_string_patches_) {
8459 const StringReference& target_string = entry.first;
8460 Literal* literal = entry.second;
8461 DCHECK(literal->GetLabel()->IsBound());
8462 uint32_t literal_offset = literal->GetLabel()->Position();
8463 linker_patches->push_back(LinkerPatch::StringPatch(literal_offset,
8464 target_string.dex_file,
8465 target_string.string_index.index_));
8466 }
8467 if (!GetCompilerOptions().IsBootImage()) {
8468 DCHECK(pc_relative_type_patches_.empty());
8469 EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_,
8470 linker_patches);
8471 } else {
8472 EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
8473 linker_patches);
8474 EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_,
8475 linker_patches);
8476 }
8477 EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_,
8478 linker_patches);
8479 for (const auto& entry : boot_image_type_patches_) {
8480 const TypeReference& target_type = entry.first;
8481 Literal* literal = entry.second;
8482 DCHECK(literal->GetLabel()->IsBound());
8483 uint32_t literal_offset = literal->GetLabel()->Position();
8484 linker_patches->push_back(LinkerPatch::TypePatch(literal_offset,
8485 target_type.dex_file,
8486 target_type.type_index.index_));
8487 }
8488 DCHECK_EQ(size, linker_patches->size());
8489 }
8490
DeduplicateUint32Literal(uint32_t value,Uint32ToLiteralMap * map)8491 Literal* CodeGeneratorARM::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) {
8492 return map->GetOrCreate(
8493 value,
8494 [this, value]() { return __ NewLiteral<uint32_t>(value); });
8495 }
8496
DeduplicateMethodLiteral(MethodReference target_method,MethodToLiteralMap * map)8497 Literal* CodeGeneratorARM::DeduplicateMethodLiteral(MethodReference target_method,
8498 MethodToLiteralMap* map) {
8499 return map->GetOrCreate(
8500 target_method,
8501 [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
8502 }
8503
VisitMultiplyAccumulate(HMultiplyAccumulate * instr)8504 void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
8505 LocationSummary* locations =
8506 new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
8507 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
8508 Location::RequiresRegister());
8509 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
8510 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
8511 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8512 }
8513
VisitMultiplyAccumulate(HMultiplyAccumulate * instr)8514 void InstructionCodeGeneratorARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
8515 LocationSummary* locations = instr->GetLocations();
8516 Register res = locations->Out().AsRegister<Register>();
8517 Register accumulator =
8518 locations->InAt(HMultiplyAccumulate::kInputAccumulatorIndex).AsRegister<Register>();
8519 Register mul_left =
8520 locations->InAt(HMultiplyAccumulate::kInputMulLeftIndex).AsRegister<Register>();
8521 Register mul_right =
8522 locations->InAt(HMultiplyAccumulate::kInputMulRightIndex).AsRegister<Register>();
8523
8524 if (instr->GetOpKind() == HInstruction::kAdd) {
8525 __ mla(res, mul_left, mul_right, accumulator);
8526 } else {
8527 __ mls(res, mul_left, mul_right, accumulator);
8528 }
8529 }
8530
VisitBoundType(HBoundType * instruction ATTRIBUTE_UNUSED)8531 void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
8532 // Nothing to do, this should be removed during prepare for register allocator.
8533 LOG(FATAL) << "Unreachable";
8534 }
8535
VisitBoundType(HBoundType * instruction ATTRIBUTE_UNUSED)8536 void InstructionCodeGeneratorARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
8537 // Nothing to do, this should be removed during prepare for register allocator.
8538 LOG(FATAL) << "Unreachable";
8539 }
8540
8541 // Simple implementation of packed switch - generate cascaded compare/jumps.
VisitPackedSwitch(HPackedSwitch * switch_instr)8542 void LocationsBuilderARM::VisitPackedSwitch(HPackedSwitch* switch_instr) {
8543 LocationSummary* locations =
8544 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall);
8545 locations->SetInAt(0, Location::RequiresRegister());
8546 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
8547 codegen_->GetAssembler()->IsThumb()) {
8548 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base.
8549 if (switch_instr->GetStartValue() != 0) {
8550 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias.
8551 }
8552 }
8553 }
8554
VisitPackedSwitch(HPackedSwitch * switch_instr)8555 void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr) {
8556 int32_t lower_bound = switch_instr->GetStartValue();
8557 uint32_t num_entries = switch_instr->GetNumEntries();
8558 LocationSummary* locations = switch_instr->GetLocations();
8559 Register value_reg = locations->InAt(0).AsRegister<Register>();
8560 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
8561
8562 if (num_entries <= kPackedSwitchCompareJumpThreshold || !codegen_->GetAssembler()->IsThumb()) {
8563 // Create a series of compare/jumps.
8564 Register temp_reg = IP;
8565 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
8566 // the immediate, because IP is used as the destination register. For the other
8567 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
8568 // and they can be encoded in the instruction without making use of IP register.
8569 __ AddConstantSetFlags(temp_reg, value_reg, -lower_bound);
8570
8571 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
8572 // Jump to successors[0] if value == lower_bound.
8573 __ b(codegen_->GetLabelOf(successors[0]), EQ);
8574 int32_t last_index = 0;
8575 for (; num_entries - last_index > 2; last_index += 2) {
8576 __ AddConstantSetFlags(temp_reg, temp_reg, -2);
8577 // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
8578 __ b(codegen_->GetLabelOf(successors[last_index + 1]), LO);
8579 // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
8580 __ b(codegen_->GetLabelOf(successors[last_index + 2]), EQ);
8581 }
8582 if (num_entries - last_index == 2) {
8583 // The last missing case_value.
8584 __ CmpConstant(temp_reg, 1);
8585 __ b(codegen_->GetLabelOf(successors[last_index + 1]), EQ);
8586 }
8587
8588 // And the default for any other value.
8589 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
8590 __ b(codegen_->GetLabelOf(default_block));
8591 }
8592 } else {
8593 // Create a table lookup.
8594 Register temp_reg = locations->GetTemp(0).AsRegister<Register>();
8595
8596 // Materialize a pointer to the switch table
8597 std::vector<Label*> labels(num_entries);
8598 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
8599 for (uint32_t i = 0; i < num_entries; i++) {
8600 labels[i] = codegen_->GetLabelOf(successors[i]);
8601 }
8602 JumpTable* table = __ CreateJumpTable(std::move(labels), temp_reg);
8603
8604 // Remove the bias.
8605 Register key_reg;
8606 if (lower_bound != 0) {
8607 key_reg = locations->GetTemp(1).AsRegister<Register>();
8608 __ AddConstant(key_reg, value_reg, -lower_bound);
8609 } else {
8610 key_reg = value_reg;
8611 }
8612
8613 // Check whether the value is in the table, jump to default block if not.
8614 __ CmpConstant(key_reg, num_entries - 1);
8615 __ b(codegen_->GetLabelOf(default_block), Condition::HI);
8616
8617 // Load the displacement from the table.
8618 __ ldr(temp_reg, Address(temp_reg, key_reg, Shift::LSL, 2));
8619
8620 // Dispatch is a direct add to the PC (for Thumb2).
8621 __ EmitJumpTableDispatch(table, temp_reg);
8622 }
8623 }
8624
VisitArmDexCacheArraysBase(HArmDexCacheArraysBase * base)8625 void LocationsBuilderARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
8626 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(base);
8627 locations->SetOut(Location::RequiresRegister());
8628 }
8629
VisitArmDexCacheArraysBase(HArmDexCacheArraysBase * base)8630 void InstructionCodeGeneratorARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
8631 Register base_reg = base->GetLocations()->Out().AsRegister<Register>();
8632 CodeGeneratorARM::PcRelativePatchInfo* labels =
8633 codegen_->NewPcRelativeDexCacheArrayPatch(base->GetDexFile(), base->GetElementOffset());
8634 __ BindTrackedLabel(&labels->movw_label);
8635 __ movw(base_reg, /* placeholder */ 0u);
8636 __ BindTrackedLabel(&labels->movt_label);
8637 __ movt(base_reg, /* placeholder */ 0u);
8638 __ BindTrackedLabel(&labels->add_pc_label);
8639 __ add(base_reg, base_reg, ShifterOperand(PC));
8640 }
8641
MoveFromReturnRegister(Location trg,Primitive::Type type)8642 void CodeGeneratorARM::MoveFromReturnRegister(Location trg, Primitive::Type type) {
8643 if (!trg.IsValid()) {
8644 DCHECK_EQ(type, Primitive::kPrimVoid);
8645 return;
8646 }
8647
8648 DCHECK_NE(type, Primitive::kPrimVoid);
8649
8650 Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type);
8651 if (return_loc.Equals(trg)) {
8652 return;
8653 }
8654
8655 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged
8656 // with the last branch.
8657 if (type == Primitive::kPrimLong) {
8658 HParallelMove parallel_move(GetGraph()->GetArena());
8659 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimInt, nullptr);
8660 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimInt, nullptr);
8661 GetMoveResolver()->EmitNativeCode(¶llel_move);
8662 } else if (type == Primitive::kPrimDouble) {
8663 HParallelMove parallel_move(GetGraph()->GetArena());
8664 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimFloat, nullptr);
8665 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimFloat, nullptr);
8666 GetMoveResolver()->EmitNativeCode(¶llel_move);
8667 } else {
8668 // Let the parallel move resolver take care of all of this.
8669 HParallelMove parallel_move(GetGraph()->GetArena());
8670 parallel_move.AddMove(return_loc, trg, type, nullptr);
8671 GetMoveResolver()->EmitNativeCode(¶llel_move);
8672 }
8673 }
8674
VisitClassTableGet(HClassTableGet * instruction)8675 void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) {
8676 LocationSummary* locations =
8677 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
8678 locations->SetInAt(0, Location::RequiresRegister());
8679 locations->SetOut(Location::RequiresRegister());
8680 }
8681
VisitClassTableGet(HClassTableGet * instruction)8682 void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) {
8683 LocationSummary* locations = instruction->GetLocations();
8684 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
8685 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
8686 instruction->GetIndex(), kArmPointerSize).SizeValue();
8687 __ LoadFromOffset(kLoadWord,
8688 locations->Out().AsRegister<Register>(),
8689 locations->InAt(0).AsRegister<Register>(),
8690 method_offset);
8691 } else {
8692 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
8693 instruction->GetIndex(), kArmPointerSize));
8694 __ LoadFromOffset(kLoadWord,
8695 locations->Out().AsRegister<Register>(),
8696 locations->InAt(0).AsRegister<Register>(),
8697 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
8698 __ LoadFromOffset(kLoadWord,
8699 locations->Out().AsRegister<Register>(),
8700 locations->Out().AsRegister<Register>(),
8701 method_offset);
8702 }
8703 }
8704
PatchJitRootUse(uint8_t * code,const uint8_t * roots_data,Literal * literal,uint64_t index_in_table)8705 static void PatchJitRootUse(uint8_t* code,
8706 const uint8_t* roots_data,
8707 Literal* literal,
8708 uint64_t index_in_table) {
8709 DCHECK(literal->GetLabel()->IsBound());
8710 uint32_t literal_offset = literal->GetLabel()->Position();
8711 uintptr_t address =
8712 reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
8713 uint8_t* data = code + literal_offset;
8714 reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
8715 }
8716
EmitJitRootPatches(uint8_t * code,const uint8_t * roots_data)8717 void CodeGeneratorARM::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
8718 for (const auto& entry : jit_string_patches_) {
8719 const auto& it = jit_string_roots_.find(entry.first);
8720 DCHECK(it != jit_string_roots_.end());
8721 PatchJitRootUse(code, roots_data, entry.second, it->second);
8722 }
8723 for (const auto& entry : jit_class_patches_) {
8724 const auto& it = jit_class_roots_.find(entry.first);
8725 DCHECK(it != jit_class_roots_.end());
8726 PatchJitRootUse(code, roots_data, entry.second, it->second);
8727 }
8728 }
8729
8730 #undef __
8731 #undef QUICK_ENTRY_POINT
8732
8733 } // namespace arm
8734 } // namespace art
8735