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