blob: 3c5973ebe64d3c6b537b5614c88024951db74923 [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 {
46 return vixl_masm_.GetStartAddress<uint8_t*>();
47}
48
49void ArmVIXLAssembler::FinalizeInstructions(const MemoryRegion& region) {
50 // Copy the instructions from the buffer.
51 MemoryRegion from(vixl_masm_.GetStartAddress<void*>(), CodeSize());
52 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
65void ArmVIXLAssembler::MaybeUnpoisonHeapReference(vixl32::Register reg) {
66 if (kPoisonHeapReferences) {
67 UnpoisonHeapReference(reg);
68 }
69}
70
71void ArmVIXLAssembler::LoadImmediate(vixl32::Register rd, int32_t value) {
72 // TODO(VIXL): Implement this optimization in VIXL.
73 if (!ShifterOperandCanAlwaysHold(value) && ShifterOperandCanAlwaysHold(~value)) {
74 ___ Mvn(rd, ~value);
75 } else {
76 ___ Mov(rd, value);
77 }
78}
79
80bool ArmVIXLAssembler::ShifterOperandCanAlwaysHold(uint32_t immediate) {
81 return vixl_masm_.IsModifiedImmediate(immediate);
82}
83
84bool ArmVIXLAssembler::ShifterOperandCanHold(Opcode opcode, uint32_t immediate, SetCc set_cc) {
85 switch (opcode) {
86 case ADD:
87 case SUB:
88 // Less than (or equal to) 12 bits can be done if we don't need to set condition codes.
89 if (IsUint<12>(immediate) && set_cc != kCcSet) {
90 return true;
91 }
92 return ShifterOperandCanAlwaysHold(immediate);
93
94 case MOV:
95 // TODO: Support less than or equal to 12bits.
96 return ShifterOperandCanAlwaysHold(immediate);
97
98 case MVN:
99 default:
100 return ShifterOperandCanAlwaysHold(immediate);
101 }
102}
103
104bool ArmVIXLAssembler::CanSplitLoadStoreOffset(int32_t allowed_offset_bits,
105 int32_t offset,
106 /*out*/ int32_t* add_to_base,
107 /*out*/ int32_t* offset_for_load_store) {
108 int32_t other_bits = offset & ~allowed_offset_bits;
109 if (ShifterOperandCanAlwaysHold(other_bits) || ShifterOperandCanAlwaysHold(-other_bits)) {
110 *add_to_base = offset & ~allowed_offset_bits;
111 *offset_for_load_store = offset & allowed_offset_bits;
112 return true;
113 }
114 return false;
115}
116
117int32_t ArmVIXLAssembler::AdjustLoadStoreOffset(int32_t allowed_offset_bits,
118 vixl32::Register temp,
119 vixl32::Register base,
120 int32_t offset) {
121 DCHECK_NE(offset & ~allowed_offset_bits, 0);
122 int32_t add_to_base, offset_for_load;
123 if (CanSplitLoadStoreOffset(allowed_offset_bits, offset, &add_to_base, &offset_for_load)) {
124 ___ Add(temp, base, add_to_base);
125 return offset_for_load;
126 } else {
127 ___ Mov(temp, offset);
128 ___ Add(temp, temp, base);
129 return 0;
130 }
131}
132
133// TODO(VIXL): Implement this in VIXL.
134int32_t ArmVIXLAssembler::GetAllowedLoadOffsetBits(LoadOperandType type) {
135 switch (type) {
136 case kLoadSignedByte:
137 case kLoadSignedHalfword:
138 case kLoadUnsignedHalfword:
139 case kLoadUnsignedByte:
140 case kLoadWord:
141 // We can encode imm12 offset.
142 return 0xfff;
143 case kLoadSWord:
144 case kLoadDWord:
145 case kLoadWordPair:
146 // We can encode imm8:'00' offset.
147 return 0xff << 2;
148 default:
149 LOG(FATAL) << "UNREACHABLE";
150 UNREACHABLE();
151 }
152}
153
154// TODO(VIXL): Implement this in VIXL.
155int32_t ArmVIXLAssembler::GetAllowedStoreOffsetBits(StoreOperandType type) {
156 switch (type) {
157 case kStoreHalfword:
158 case kStoreByte:
159 case kStoreWord:
160 // We can encode imm12 offset.
161 return 0xfff;
162 case kStoreSWord:
163 case kStoreDWord:
164 case kStoreWordPair:
165 // We can encode imm8:'00' offset.
166 return 0xff << 2;
167 default:
168 LOG(FATAL) << "UNREACHABLE";
169 UNREACHABLE();
170 }
171}
172
173// TODO(VIXL): Implement this in VIXL.
174static bool CanHoldLoadOffsetThumb(LoadOperandType type, int offset) {
175 switch (type) {
176 case kLoadSignedByte:
177 case kLoadSignedHalfword:
178 case kLoadUnsignedHalfword:
179 case kLoadUnsignedByte:
180 case kLoadWord:
181 return IsAbsoluteUint<12>(offset);
182 case kLoadSWord:
183 case kLoadDWord:
184 return IsAbsoluteUint<10>(offset) && IsAligned<4>(offset); // VFP addressing mode.
185 case kLoadWordPair:
186 return IsAbsoluteUint<10>(offset) && IsAligned<4>(offset);
187 default:
188 LOG(FATAL) << "UNREACHABLE";
189 UNREACHABLE();
190 }
191}
192
193// TODO(VIXL): Implement this in VIXL.
194static bool CanHoldStoreOffsetThumb(StoreOperandType type, int offset) {
195 switch (type) {
196 case kStoreHalfword:
197 case kStoreByte:
198 case kStoreWord:
199 return IsAbsoluteUint<12>(offset);
200 case kStoreSWord:
201 case kStoreDWord:
202 return IsAbsoluteUint<10>(offset) && IsAligned<4>(offset); // VFP addressing mode.
203 case kStoreWordPair:
204 return IsAbsoluteUint<10>(offset) && IsAligned<4>(offset);
205 default:
206 LOG(FATAL) << "UNREACHABLE";
207 UNREACHABLE();
208 }
209}
210
211// Implementation note: this method must emit at most one instruction when
212// Address::CanHoldStoreOffsetThumb.
213// TODO(VIXL): Implement AdjustLoadStoreOffset logic in VIXL.
214void ArmVIXLAssembler::StoreToOffset(StoreOperandType type,
215 vixl32::Register reg,
216 vixl32::Register base,
217 int32_t offset) {
218 vixl32::Register tmp_reg;
219 UseScratchRegisterScope temps(&vixl_masm_);
220
221 if (!CanHoldStoreOffsetThumb(type, offset)) {
222 CHECK_NE(base.GetCode(), kIpCode);
223 if ((reg.GetCode() != kIpCode) &&
224 ((type != kStoreWordPair) || (reg.GetCode() + 1 != kIpCode))) {
225 tmp_reg = temps.Acquire();
226 } else {
227 // Be careful not to use ip twice (for `reg` (or `reg` + 1 in
228 // the case of a word-pair store) and `base`) to build the
229 // Address object used by the store instruction(s) below.
230 // Instead, save R5 on the stack (or R6 if R5 is already used by
231 // `base`), use it as secondary temporary register, and restore
232 // it after the store instruction has been emitted.
233 tmp_reg = (base.GetCode() != 5) ? r5 : r6;
234 ___ Push(tmp_reg);
235 if (base.GetCode() == kSpCode) {
236 offset += kRegisterSize;
237 }
238 }
239 // TODO: Implement indexed store (not available for STRD), inline AdjustLoadStoreOffset()
240 // and in the "unsplittable" path get rid of the "add" by using the store indexed instead.
241 offset = AdjustLoadStoreOffset(GetAllowedStoreOffsetBits(type), tmp_reg, base, offset);
242 base = tmp_reg;
243 }
244 DCHECK(CanHoldStoreOffsetThumb(type, offset));
245 switch (type) {
246 case kStoreByte:
247 ___ Strb(reg, MemOperand(base, offset));
248 break;
249 case kStoreHalfword:
250 ___ Strh(reg, MemOperand(base, offset));
251 break;
252 case kStoreWord:
253 ___ Str(reg, MemOperand(base, offset));
254 break;
255 case kStoreWordPair:
256 ___ Strd(reg, vixl32::Register(reg.GetCode() + 1), MemOperand(base, offset));
257 break;
258 default:
259 LOG(FATAL) << "UNREACHABLE";
260 UNREACHABLE();
261 }
262 if ((tmp_reg.IsValid()) && (tmp_reg.GetCode() != kIpCode)) {
263 CHECK(tmp_reg.Is(r5) || tmp_reg.Is(r6)) << tmp_reg;
264 ___ Pop(tmp_reg);
265 }
266}
267
268// Implementation note: this method must emit at most one instruction when
269// Address::CanHoldLoadOffsetThumb.
270// TODO(VIXL): Implement AdjustLoadStoreOffset logic in VIXL.
271void ArmVIXLAssembler::LoadFromOffset(LoadOperandType type,
272 vixl32::Register dest,
273 vixl32::Register base,
274 int32_t offset) {
275 if (!CanHoldLoadOffsetThumb(type, offset)) {
276 CHECK(!base.Is(ip));
277 // Inlined AdjustLoadStoreOffset() allows us to pull a few more tricks.
278 int32_t allowed_offset_bits = GetAllowedLoadOffsetBits(type);
279 DCHECK_NE(offset & ~allowed_offset_bits, 0);
280 int32_t add_to_base, offset_for_load;
281 if (CanSplitLoadStoreOffset(allowed_offset_bits, offset, &add_to_base, &offset_for_load)) {
282 // Use reg for the adjusted base. If it's low reg, we may end up using 16-bit load.
283 AddConstant(dest, base, add_to_base);
284 base = dest;
285 offset = offset_for_load;
286 } else {
287 UseScratchRegisterScope temps(&vixl_masm_);
288 vixl32::Register temp = (dest.Is(base)) ? temps.Acquire() : dest;
289 LoadImmediate(temp, offset);
290 // TODO: Implement indexed load (not available for LDRD) and use it here to avoid the ADD.
291 // Use reg for the adjusted base. If it's low reg, we may end up using 16-bit load.
292 ___ Add(dest, dest, (dest.Is(base)) ? temp : base);
293 base = dest;
294 offset = 0;
295 }
296 }
297
298 DCHECK(CanHoldLoadOffsetThumb(type, offset));
299 switch (type) {
300 case kLoadSignedByte:
301 ___ Ldrsb(dest, MemOperand(base, offset));
302 break;
303 case kLoadUnsignedByte:
304 ___ Ldrb(dest, MemOperand(base, offset));
305 break;
306 case kLoadSignedHalfword:
307 ___ Ldrsh(dest, MemOperand(base, offset));
308 break;
309 case kLoadUnsignedHalfword:
310 ___ Ldrh(dest, MemOperand(base, offset));
311 break;
312 case kLoadWord:
313 CHECK(!dest.IsSP());
314 ___ Ldr(dest, MemOperand(base, offset));
315 break;
316 case kLoadWordPair:
317 ___ Ldrd(dest, vixl32::Register(dest.GetCode() + 1), MemOperand(base, offset));
318 break;
319 default:
320 LOG(FATAL) << "UNREACHABLE";
321 UNREACHABLE();
322 }
323}
324
325void ArmVIXLAssembler::StoreSToOffset(vixl32::SRegister source,
326 vixl32::Register base,
327 int32_t offset) {
328 ___ Vstr(source, MemOperand(base, offset));
329}
330
331void ArmVIXLAssembler::StoreDToOffset(vixl32::DRegister source,
332 vixl32::Register base,
333 int32_t offset) {
334 ___ Vstr(source, MemOperand(base, offset));
335}
336
337void ArmVIXLAssembler::LoadSFromOffset(vixl32::SRegister reg,
338 vixl32::Register base,
339 int32_t offset) {
340 ___ Vldr(reg, MemOperand(base, offset));
341}
342
343void ArmVIXLAssembler::LoadDFromOffset(vixl32::DRegister reg,
344 vixl32::Register base,
345 int32_t offset) {
346 ___ Vldr(reg, MemOperand(base, offset));
347}
348
349void ArmVIXLAssembler::AddConstant(vixl32::Register rd, int32_t value) {
350 AddConstant(rd, rd, value);
351}
352
353// TODO(VIXL): think about using adds which updates flags where possible.
354void ArmVIXLAssembler::AddConstant(vixl32::Register rd,
355 vixl32::Register rn,
356 int32_t value) {
357 DCHECK(vixl_masm_.OutsideITBlock());
358 // TODO(VIXL): implement this optimization in VIXL.
359 if (value == 0) {
360 if (!rd.Is(rn)) {
361 ___ Mov(rd, rn);
362 }
363 return;
364 }
365 ___ Add(rd, rn, value);
366}
367
368// Inside IT block we must use assembler, macroassembler instructions are not permitted.
369void ArmVIXLAssembler::AddConstantInIt(vixl32::Register rd,
370 vixl32::Register rn,
371 int32_t value,
372 vixl32::Condition cond) {
373 DCHECK(vixl_masm_.InITBlock());
374 if (value == 0) {
375 ___ mov(cond, rd, rn);
376 } else {
377 ___ add(cond, rd, rn, value);
378 }
379}
380
381} // namespace arm
382} // namespace art