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