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