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