blob: 05250a41575f1a70a66999d52de846f89e52b7f7 [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
Vladimir Markof0a6a1d2018-01-08 14:23:56 +0000115bool ArmVIXLAssembler::ShifterOperandCanHold(Opcode opcode,
116 uint32_t immediate,
117 vixl::aarch32::FlagsUpdate update_flags) {
Artem Serov12e097c2016-08-08 15:13:26 +0100118 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.
Vladimir Markof0a6a1d2018-01-08 14:23:56 +0000122 if (IsUint<12>(immediate) && update_flags != vixl::aarch32::SetFlags) {
Artem Serov12e097c2016-08-08 15:13:26 +0100123 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
137bool 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
150int32_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.
167int32_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.
188int32_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.
207static 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.
227static 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.
247void 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) &&
Nicolas Geoffray13a797b2017-03-15 16:41:31 +0000257 (!vixl_masm_.GetScratchRegisterList()->IsEmpty()) &&
Artem Serov12e097c2016-08-08 15:13:26 +0100258 ((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.
305void 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
359void ArmVIXLAssembler::StoreSToOffset(vixl32::SRegister source,
360 vixl32::Register base,
361 int32_t offset) {
362 ___ Vstr(source, MemOperand(base, offset));
363}
364
365void ArmVIXLAssembler::StoreDToOffset(vixl32::DRegister source,
366 vixl32::Register base,
367 int32_t offset) {
368 ___ Vstr(source, MemOperand(base, offset));
369}
370
371void ArmVIXLAssembler::LoadSFromOffset(vixl32::SRegister reg,
372 vixl32::Register base,
373 int32_t offset) {
374 ___ Vldr(reg, MemOperand(base, offset));
375}
376
377void ArmVIXLAssembler::LoadDFromOffset(vixl32::DRegister reg,
378 vixl32::Register base,
379 int32_t offset) {
380 ___ Vldr(reg, MemOperand(base, offset));
381}
382
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100383// Prefer Str to Add/Stm in ArmVIXLAssembler::StoreRegisterList and
384// ArmVIXLAssembler::LoadRegisterList where this generates less code (size).
385static constexpr int kRegListThreshold = 4;
386
387void 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);
Scott Wakelingb77051e2016-11-21 19:46:00 +0000396 ___ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100397 }
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
408void 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();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000416 ___ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100417 }
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
Artem Serov12e097c2016-08-08 15:13:26 +0100428void 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.
433void 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.
448void 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
xueliang.zhongf51bc622016-11-04 09:23:32 +0000460void 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);
Artem Serov517d9f62016-12-12 15:51:15 +0000472 B(eq, label, is_far_target);
xueliang.zhongf51bc622016-11-04 09:23:32 +0000473}
474
475void 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);
Artem Serov517d9f62016-12-12 15:51:15 +0000483 B(ne, label, is_far_target);
xueliang.zhongf51bc622016-11-04 09:23:32 +0000484}
485
Scott Wakelingbffdc702016-12-07 17:46:03 +0000486void ArmVIXLMacroAssembler::B(vixl32::Label* label) {
487 if (!label->IsBound()) {
Artem Serov672b9c12017-12-05 18:04:07 +0000488 // Try to use a 16-bit encoding of the B instruction.
Scott Wakelingbffdc702016-12-07 17:46:03 +0000489 DCHECK(OutsideITBlock());
Artem Serov672b9c12017-12-05 18:04:07 +0000490 BPreferNear(label);
Scott Wakelingbffdc702016-12-07 17:46:03 +0000491 return;
492 }
493 MacroAssembler::B(label);
494}
495
Artem Serov517d9f62016-12-12 15:51:15 +0000496void ArmVIXLMacroAssembler::B(vixl32::Condition cond, vixl32::Label* label, bool is_far_target) {
497 if (!label->IsBound() && !is_far_target) {
Artem Serov672b9c12017-12-05 18:04:07 +0000498 // Try to use a 16-bit encoding of the B instruction.
Artem Serov517d9f62016-12-12 15:51:15 +0000499 DCHECK(OutsideITBlock());
Artem Serov672b9c12017-12-05 18:04:07 +0000500 BPreferNear(cond, label);
Artem Serov517d9f62016-12-12 15:51:15 +0000501 return;
502 }
Scott Wakelingbffdc702016-12-07 17:46:03 +0000503 MacroAssembler::B(cond, label);
504}
505
Artem Serov12e097c2016-08-08 15:13:26 +0100506} // namespace arm
507} // namespace art