• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 <iostream>
18 #include <type_traits>
19 
20 #include "assembler_arm_vixl.h"
21 #include "base/bit_utils.h"
22 #include "base/bit_utils_iterator.h"
23 #include "entrypoints/quick/quick_entrypoints.h"
24 #include "heap_poisoning.h"
25 #include "thread.h"
26 
27 using namespace vixl::aarch32;  // NOLINT(build/namespaces)
28 
29 using vixl::ExactAssemblyScope;
30 using vixl::CodeBufferCheckScope;
31 
32 namespace art {
33 namespace arm {
34 
35 #ifdef ___
36 #error "ARM Assembler macro already defined."
37 #else
38 #define ___   vixl_masm_.
39 #endif
40 
41 // Thread register definition.
42 extern const vixl32::Register tr(TR);
43 // Marking register definition.
44 extern const vixl32::Register mr(MR);
45 
FinalizeCode()46 void ArmVIXLAssembler::FinalizeCode() {
47   vixl_masm_.FinalizeCode();
48 }
49 
CodeSize() const50 size_t ArmVIXLAssembler::CodeSize() const {
51   return vixl_masm_.GetSizeOfCodeGenerated();
52 }
53 
CodeBufferBaseAddress() const54 const uint8_t* ArmVIXLAssembler::CodeBufferBaseAddress() const {
55   return vixl_masm_.GetBuffer().GetStartAddress<const uint8_t*>();
56 }
57 
FinalizeInstructions(const MemoryRegion & region)58 void ArmVIXLAssembler::FinalizeInstructions(const MemoryRegion& region) {
59   // Copy the instructions from the buffer.
60   MemoryRegion from(vixl_masm_.GetBuffer()->GetStartAddress<void*>(), CodeSize());
61   region.CopyFrom(0, from);
62 }
63 
PoisonHeapReference(vixl::aarch32::Register reg)64 void ArmVIXLAssembler::PoisonHeapReference(vixl::aarch32::Register reg) {
65   // reg = -reg.
66   ___ Rsb(reg, reg, 0);
67 }
68 
UnpoisonHeapReference(vixl::aarch32::Register reg)69 void ArmVIXLAssembler::UnpoisonHeapReference(vixl::aarch32::Register reg) {
70   // reg = -reg.
71   ___ Rsb(reg, reg, 0);
72 }
73 
MaybePoisonHeapReference(vixl32::Register reg)74 void ArmVIXLAssembler::MaybePoisonHeapReference(vixl32::Register reg) {
75   if (kPoisonHeapReferences) {
76     PoisonHeapReference(reg);
77   }
78 }
79 
MaybeUnpoisonHeapReference(vixl32::Register reg)80 void ArmVIXLAssembler::MaybeUnpoisonHeapReference(vixl32::Register reg) {
81   if (kPoisonHeapReferences) {
82     UnpoisonHeapReference(reg);
83   }
84 }
85 
GenerateMarkingRegisterCheck(vixl32::Register temp,int code)86 void ArmVIXLAssembler::GenerateMarkingRegisterCheck(vixl32::Register temp, int code) {
87   // The Marking Register is only used in the Baker read barrier configuration.
88   DCHECK(kEmitCompilerReadBarrier);
89   DCHECK(kUseBakerReadBarrier);
90 
91   vixl32::Label mr_is_ok;
92 
93   // temp = self.tls32_.is.gc_marking
94   ___ Ldr(temp, MemOperand(tr, Thread::IsGcMarkingOffset<kArmPointerSize>().Int32Value()));
95   // Check that mr == self.tls32_.is.gc_marking.
96   ___ Cmp(mr, temp);
97   ___ B(eq, &mr_is_ok, /* far_target */ false);
98   ___ Bkpt(code);
99   ___ Bind(&mr_is_ok);
100 }
101 
LoadImmediate(vixl32::Register rd,int32_t value)102 void ArmVIXLAssembler::LoadImmediate(vixl32::Register rd, int32_t value) {
103   // TODO(VIXL): Implement this optimization in VIXL.
104   if (!ShifterOperandCanAlwaysHold(value) && ShifterOperandCanAlwaysHold(~value)) {
105     ___ Mvn(rd, ~value);
106   } else {
107     ___ Mov(rd, value);
108   }
109 }
110 
ShifterOperandCanAlwaysHold(uint32_t immediate)111 bool ArmVIXLAssembler::ShifterOperandCanAlwaysHold(uint32_t immediate) {
112   return vixl_masm_.IsModifiedImmediate(immediate);
113 }
114 
ShifterOperandCanHold(Opcode opcode,uint32_t immediate,vixl::aarch32::FlagsUpdate update_flags)115 bool ArmVIXLAssembler::ShifterOperandCanHold(Opcode opcode,
116                                              uint32_t immediate,
117                                              vixl::aarch32::FlagsUpdate update_flags) {
118   switch (opcode) {
119     case ADD:
120     case SUB:
121       // Less than (or equal to) 12 bits can be done if we don't need to set condition codes.
122       if (IsUint<12>(immediate) && update_flags != vixl::aarch32::SetFlags) {
123         return true;
124       }
125       return ShifterOperandCanAlwaysHold(immediate);
126 
127     case MOV:
128       // TODO: Support less than or equal to 12bits.
129       return ShifterOperandCanAlwaysHold(immediate);
130 
131     case MVN:
132     default:
133       return ShifterOperandCanAlwaysHold(immediate);
134   }
135 }
136 
CanSplitLoadStoreOffset(int32_t allowed_offset_bits,int32_t offset,int32_t * add_to_base,int32_t * offset_for_load_store)137 bool ArmVIXLAssembler::CanSplitLoadStoreOffset(int32_t allowed_offset_bits,
138                                                int32_t offset,
139                                                /*out*/ int32_t* add_to_base,
140                                                /*out*/ int32_t* offset_for_load_store) {
141   int32_t other_bits = offset & ~allowed_offset_bits;
142   if (ShifterOperandCanAlwaysHold(other_bits) || ShifterOperandCanAlwaysHold(-other_bits)) {
143     *add_to_base = offset & ~allowed_offset_bits;
144     *offset_for_load_store = offset & allowed_offset_bits;
145     return true;
146   }
147   return false;
148 }
149 
AdjustLoadStoreOffset(int32_t allowed_offset_bits,vixl32::Register temp,vixl32::Register base,int32_t offset)150 int32_t ArmVIXLAssembler::AdjustLoadStoreOffset(int32_t allowed_offset_bits,
151                                                 vixl32::Register temp,
152                                                 vixl32::Register base,
153                                                 int32_t offset) {
154   DCHECK_NE(offset & ~allowed_offset_bits, 0);
155   int32_t add_to_base, offset_for_load;
156   if (CanSplitLoadStoreOffset(allowed_offset_bits, offset, &add_to_base, &offset_for_load)) {
157     ___ Add(temp, base, add_to_base);
158     return offset_for_load;
159   } else {
160     ___ Mov(temp, offset);
161     ___ Add(temp, temp, base);
162     return 0;
163   }
164 }
165 
166 // TODO(VIXL): Implement this in VIXL.
GetAllowedLoadOffsetBits(LoadOperandType type)167 int32_t ArmVIXLAssembler::GetAllowedLoadOffsetBits(LoadOperandType type) {
168   switch (type) {
169     case kLoadSignedByte:
170     case kLoadSignedHalfword:
171     case kLoadUnsignedHalfword:
172     case kLoadUnsignedByte:
173     case kLoadWord:
174       // We can encode imm12 offset.
175       return 0xfff;
176     case kLoadSWord:
177     case kLoadDWord:
178     case kLoadWordPair:
179       // We can encode imm8:'00' offset.
180       return 0xff << 2;
181     default:
182       LOG(FATAL) << "UNREACHABLE";
183       UNREACHABLE();
184   }
185 }
186 
187 // TODO(VIXL): Implement this in VIXL.
GetAllowedStoreOffsetBits(StoreOperandType type)188 int32_t ArmVIXLAssembler::GetAllowedStoreOffsetBits(StoreOperandType type) {
189   switch (type) {
190     case kStoreHalfword:
191     case kStoreByte:
192     case kStoreWord:
193       // We can encode imm12 offset.
194       return 0xfff;
195     case kStoreSWord:
196     case kStoreDWord:
197     case kStoreWordPair:
198       // We can encode imm8:'00' offset.
199       return 0xff << 2;
200     default:
201       LOG(FATAL) << "UNREACHABLE";
202       UNREACHABLE();
203   }
204 }
205 
206 // TODO(VIXL): Implement this in VIXL.
CanHoldLoadOffsetThumb(LoadOperandType type,int offset)207 static bool CanHoldLoadOffsetThumb(LoadOperandType type, int offset) {
208   switch (type) {
209     case kLoadSignedByte:
210     case kLoadSignedHalfword:
211     case kLoadUnsignedHalfword:
212     case kLoadUnsignedByte:
213     case kLoadWord:
214       return IsAbsoluteUint<12>(offset);
215     case kLoadSWord:
216     case kLoadDWord:
217       return IsAbsoluteUint<10>(offset) && IsAligned<4>(offset);  // VFP addressing mode.
218     case kLoadWordPair:
219       return IsAbsoluteUint<10>(offset) && IsAligned<4>(offset);
220     default:
221       LOG(FATAL) << "UNREACHABLE";
222       UNREACHABLE();
223   }
224 }
225 
226 // TODO(VIXL): Implement this in VIXL.
CanHoldStoreOffsetThumb(StoreOperandType type,int offset)227 static bool CanHoldStoreOffsetThumb(StoreOperandType type, int offset) {
228   switch (type) {
229     case kStoreHalfword:
230     case kStoreByte:
231     case kStoreWord:
232       return IsAbsoluteUint<12>(offset);
233     case kStoreSWord:
234     case kStoreDWord:
235       return IsAbsoluteUint<10>(offset) && IsAligned<4>(offset);  // VFP addressing mode.
236     case kStoreWordPair:
237       return IsAbsoluteUint<10>(offset) && IsAligned<4>(offset);
238     default:
239       LOG(FATAL) << "UNREACHABLE";
240       UNREACHABLE();
241   }
242 }
243 
244 // Implementation note: this method must emit at most one instruction when
245 // Address::CanHoldStoreOffsetThumb.
246 // TODO(VIXL): Implement AdjustLoadStoreOffset logic in VIXL.
StoreToOffset(StoreOperandType type,vixl32::Register reg,vixl32::Register base,int32_t offset)247 void ArmVIXLAssembler::StoreToOffset(StoreOperandType type,
248                                      vixl32::Register reg,
249                                      vixl32::Register base,
250                                      int32_t offset) {
251   vixl32::Register tmp_reg;
252   UseScratchRegisterScope temps(&vixl_masm_);
253 
254   if (!CanHoldStoreOffsetThumb(type, offset)) {
255     CHECK_NE(base.GetCode(), kIpCode);
256     if ((reg.GetCode() != kIpCode) &&
257         (!vixl_masm_.GetScratchRegisterList()->IsEmpty()) &&
258         ((type != kStoreWordPair) || (reg.GetCode() + 1 != kIpCode))) {
259       tmp_reg = temps.Acquire();
260     } else {
261       // Be careful not to use ip twice (for `reg` (or `reg` + 1 in
262       // the case of a word-pair store) and `base`) to build the
263       // Address object used by the store instruction(s) below.
264       // Instead, save R5 on the stack (or R6 if R5 is already used by
265       // `base`), use it as secondary temporary register, and restore
266       // it after the store instruction has been emitted.
267       tmp_reg = (base.GetCode() != 5) ? r5 : r6;
268       ___ Push(tmp_reg);
269       if (base.GetCode() == kSpCode) {
270         offset += kRegisterSize;
271       }
272     }
273     // TODO: Implement indexed store (not available for STRD), inline AdjustLoadStoreOffset()
274     // and in the "unsplittable" path get rid of the "add" by using the store indexed instead.
275     offset = AdjustLoadStoreOffset(GetAllowedStoreOffsetBits(type), tmp_reg, base, offset);
276     base = tmp_reg;
277   }
278   DCHECK(CanHoldStoreOffsetThumb(type, offset));
279   switch (type) {
280     case kStoreByte:
281       ___ Strb(reg, MemOperand(base, offset));
282       break;
283     case kStoreHalfword:
284       ___ Strh(reg, MemOperand(base, offset));
285       break;
286     case kStoreWord:
287       ___ Str(reg, MemOperand(base, offset));
288       break;
289     case kStoreWordPair:
290       ___ Strd(reg, vixl32::Register(reg.GetCode() + 1), MemOperand(base, offset));
291       break;
292     default:
293       LOG(FATAL) << "UNREACHABLE";
294       UNREACHABLE();
295   }
296   if ((tmp_reg.IsValid()) && (tmp_reg.GetCode() != kIpCode)) {
297     CHECK(tmp_reg.Is(r5) || tmp_reg.Is(r6)) << tmp_reg;
298     ___ Pop(tmp_reg);
299   }
300 }
301 
302 // Implementation note: this method must emit at most one instruction when
303 // Address::CanHoldLoadOffsetThumb.
304 // TODO(VIXL): Implement AdjustLoadStoreOffset logic in VIXL.
LoadFromOffset(LoadOperandType type,vixl32::Register dest,vixl32::Register base,int32_t offset)305 void ArmVIXLAssembler::LoadFromOffset(LoadOperandType type,
306                                       vixl32::Register dest,
307                                       vixl32::Register base,
308                                       int32_t offset) {
309   if (!CanHoldLoadOffsetThumb(type, offset)) {
310     CHECK(!base.Is(ip));
311     // Inlined AdjustLoadStoreOffset() allows us to pull a few more tricks.
312     int32_t allowed_offset_bits = GetAllowedLoadOffsetBits(type);
313     DCHECK_NE(offset & ~allowed_offset_bits, 0);
314     int32_t add_to_base, offset_for_load;
315     if (CanSplitLoadStoreOffset(allowed_offset_bits, offset, &add_to_base, &offset_for_load)) {
316       // Use reg for the adjusted base. If it's low reg, we may end up using 16-bit load.
317       AddConstant(dest, base, add_to_base);
318       base = dest;
319       offset = offset_for_load;
320     } else {
321       UseScratchRegisterScope temps(&vixl_masm_);
322       vixl32::Register temp = (dest.Is(base)) ? temps.Acquire() : dest;
323       LoadImmediate(temp, offset);
324       // TODO: Implement indexed load (not available for LDRD) and use it here to avoid the ADD.
325       // Use reg for the adjusted base. If it's low reg, we may end up using 16-bit load.
326       ___ Add(dest, dest, (dest.Is(base)) ? temp : base);
327       base = dest;
328       offset = 0;
329     }
330   }
331 
332   DCHECK(CanHoldLoadOffsetThumb(type, offset));
333   switch (type) {
334     case kLoadSignedByte:
335       ___ Ldrsb(dest, MemOperand(base, offset));
336       break;
337     case kLoadUnsignedByte:
338       ___ Ldrb(dest, MemOperand(base, offset));
339       break;
340     case kLoadSignedHalfword:
341       ___ Ldrsh(dest, MemOperand(base, offset));
342       break;
343     case kLoadUnsignedHalfword:
344       ___ Ldrh(dest, MemOperand(base, offset));
345       break;
346     case kLoadWord:
347       CHECK(!dest.IsSP());
348       ___ Ldr(dest, MemOperand(base, offset));
349       break;
350     case kLoadWordPair:
351       ___ Ldrd(dest, vixl32::Register(dest.GetCode() + 1), MemOperand(base, offset));
352       break;
353     default:
354       LOG(FATAL) << "UNREACHABLE";
355       UNREACHABLE();
356   }
357 }
358 
StoreSToOffset(vixl32::SRegister source,vixl32::Register base,int32_t offset)359 void ArmVIXLAssembler::StoreSToOffset(vixl32::SRegister source,
360                                       vixl32::Register base,
361                                       int32_t offset) {
362   ___ Vstr(source, MemOperand(base, offset));
363 }
364 
StoreDToOffset(vixl32::DRegister source,vixl32::Register base,int32_t offset)365 void ArmVIXLAssembler::StoreDToOffset(vixl32::DRegister source,
366                                       vixl32::Register base,
367                                       int32_t offset) {
368   ___ Vstr(source, MemOperand(base, offset));
369 }
370 
LoadSFromOffset(vixl32::SRegister reg,vixl32::Register base,int32_t offset)371 void ArmVIXLAssembler::LoadSFromOffset(vixl32::SRegister reg,
372                                        vixl32::Register base,
373                                        int32_t offset) {
374   ___ Vldr(reg, MemOperand(base, offset));
375 }
376 
LoadDFromOffset(vixl32::DRegister reg,vixl32::Register base,int32_t offset)377 void ArmVIXLAssembler::LoadDFromOffset(vixl32::DRegister reg,
378                                        vixl32::Register base,
379                                        int32_t offset) {
380   ___ Vldr(reg, MemOperand(base, offset));
381 }
382 
383 // Prefer Str to Add/Stm in ArmVIXLAssembler::StoreRegisterList and
384 // ArmVIXLAssembler::LoadRegisterList where this generates less code (size).
385 static constexpr int kRegListThreshold = 4;
386 
StoreRegisterList(RegList regs,size_t stack_offset)387 void ArmVIXLAssembler::StoreRegisterList(RegList regs, size_t stack_offset) {
388   int number_of_regs = POPCOUNT(static_cast<uint32_t>(regs));
389   if (number_of_regs != 0) {
390     if (number_of_regs > kRegListThreshold) {
391       UseScratchRegisterScope temps(GetVIXLAssembler());
392       vixl32::Register base = sp;
393       if (stack_offset != 0) {
394         base = temps.Acquire();
395         DCHECK_EQ(regs & (1u << base.GetCode()), 0u);
396         ___ Add(base, sp, Operand::From(stack_offset));
397       }
398       ___ Stm(base, NO_WRITE_BACK, RegisterList(regs));
399     } else {
400       for (uint32_t i : LowToHighBits(static_cast<uint32_t>(regs))) {
401         ___ Str(vixl32::Register(i), MemOperand(sp, stack_offset));
402         stack_offset += kRegSizeInBytes;
403       }
404     }
405   }
406 }
407 
LoadRegisterList(RegList regs,size_t stack_offset)408 void ArmVIXLAssembler::LoadRegisterList(RegList regs, size_t stack_offset) {
409   int number_of_regs = POPCOUNT(static_cast<uint32_t>(regs));
410   if (number_of_regs != 0) {
411     if (number_of_regs > kRegListThreshold) {
412       UseScratchRegisterScope temps(GetVIXLAssembler());
413       vixl32::Register base = sp;
414       if (stack_offset != 0) {
415         base = temps.Acquire();
416         ___ Add(base, sp, Operand::From(stack_offset));
417       }
418       ___ Ldm(base, NO_WRITE_BACK, RegisterList(regs));
419     } else {
420       for (uint32_t i : LowToHighBits(static_cast<uint32_t>(regs))) {
421         ___ Ldr(vixl32::Register(i), MemOperand(sp, stack_offset));
422         stack_offset += kRegSizeInBytes;
423       }
424     }
425   }
426 }
427 
AddConstant(vixl32::Register rd,int32_t value)428 void ArmVIXLAssembler::AddConstant(vixl32::Register rd, int32_t value) {
429   AddConstant(rd, rd, value);
430 }
431 
432 // TODO(VIXL): think about using adds which updates flags where possible.
AddConstant(vixl32::Register rd,vixl32::Register rn,int32_t value)433 void ArmVIXLAssembler::AddConstant(vixl32::Register rd,
434                                    vixl32::Register rn,
435                                    int32_t value) {
436   DCHECK(vixl_masm_.OutsideITBlock());
437   // TODO(VIXL): implement this optimization in VIXL.
438   if (value == 0) {
439     if (!rd.Is(rn)) {
440       ___ Mov(rd, rn);
441     }
442     return;
443   }
444   ___ Add(rd, rn, value);
445 }
446 
447 // Inside IT block we must use assembler, macroassembler instructions are not permitted.
AddConstantInIt(vixl32::Register rd,vixl32::Register rn,int32_t value,vixl32::Condition cond)448 void ArmVIXLAssembler::AddConstantInIt(vixl32::Register rd,
449                                        vixl32::Register rn,
450                                        int32_t value,
451                                        vixl32::Condition cond) {
452   DCHECK(vixl_masm_.InITBlock());
453   if (value == 0) {
454     ___ mov(cond, rd, rn);
455   } else {
456     ___ add(cond, rd, rn, value);
457   }
458 }
459 
CompareAndBranchIfZero(vixl32::Register rn,vixl32::Label * label,bool is_far_target)460 void ArmVIXLMacroAssembler::CompareAndBranchIfZero(vixl32::Register rn,
461                                                    vixl32::Label* label,
462                                                    bool is_far_target) {
463   if (!is_far_target && rn.IsLow() && !label->IsBound()) {
464     // In T32, Cbz/Cbnz instructions have following limitations:
465     // - There are only 7 bits (i:imm5:0) to encode branch target address (cannot be far target).
466     // - Only low registers (i.e R0 .. R7) can be encoded.
467     // - Only forward branches (unbound labels) are supported.
468     Cbz(rn, label);
469     return;
470   }
471   Cmp(rn, 0);
472   B(eq, label, is_far_target);
473 }
474 
CompareAndBranchIfNonZero(vixl32::Register rn,vixl32::Label * label,bool is_far_target)475 void ArmVIXLMacroAssembler::CompareAndBranchIfNonZero(vixl32::Register rn,
476                                                       vixl32::Label* label,
477                                                       bool is_far_target) {
478   if (!is_far_target && rn.IsLow() && !label->IsBound()) {
479     Cbnz(rn, label);
480     return;
481   }
482   Cmp(rn, 0);
483   B(ne, label, is_far_target);
484 }
485 
B(vixl32::Label * label)486 void ArmVIXLMacroAssembler::B(vixl32::Label* label) {
487   if (!label->IsBound()) {
488     // Try to use a 16-bit encoding of the B instruction.
489     DCHECK(OutsideITBlock());
490     BPreferNear(label);
491     return;
492   }
493   MacroAssembler::B(label);
494 }
495 
B(vixl32::Condition cond,vixl32::Label * label,bool is_far_target)496 void ArmVIXLMacroAssembler::B(vixl32::Condition cond, vixl32::Label* label, bool is_far_target) {
497   if (!label->IsBound() && !is_far_target) {
498     // Try to use a 16-bit encoding of the B instruction.
499     DCHECK(OutsideITBlock());
500     BPreferNear(cond, label);
501     return;
502   }
503   MacroAssembler::B(cond, label);
504 }
505 
506 }  // namespace arm
507 }  // namespace art
508