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