blob: 30e8f4e60412f5c677b329203836a659a520290c [file] [log] [blame]
Roland Levillain1a28fc42014-11-13 18:03:06 +00001/*
2 * Copyright (C) 2014 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 "assembler_thumb2.h"
18
19#include "base/stl_util.h"
Andreas Gampe7cffc3b2015-10-19 21:31:53 -070020#include "base/stringprintf.h"
Roland Levillain1a28fc42014-11-13 18:03:06 +000021#include "utils/assembler_test.h"
22
23namespace art {
24
25class AssemblerThumb2Test : public AssemblerTest<arm::Thumb2Assembler,
Andreas Gampe851df202014-11-12 14:05:46 -080026 arm::Register, arm::SRegister,
27 uint32_t> {
Roland Levillain1a28fc42014-11-13 18:03:06 +000028 protected:
29 std::string GetArchitectureString() OVERRIDE {
30 return "arm";
31 }
32
33 std::string GetAssemblerParameters() OVERRIDE {
Andreas Gampe513ea0c2015-02-02 13:17:52 -080034 return " -march=armv7-a -mcpu=cortex-a15 -mfpu=neon -mthumb";
35 }
36
37 const char* GetAssemblyHeader() OVERRIDE {
38 return kThumb2AssemblyHeader;
Roland Levillain1a28fc42014-11-13 18:03:06 +000039 }
40
41 std::string GetDisassembleParameters() OVERRIDE {
Andreas Gampe513ea0c2015-02-02 13:17:52 -080042 return " -D -bbinary -marm --disassembler-options=force-thumb --no-show-raw-insn";
Roland Levillain1a28fc42014-11-13 18:03:06 +000043 }
44
45 void SetUpHelpers() OVERRIDE {
46 if (registers_.size() == 0) {
47 registers_.insert(end(registers_),
48 { // NOLINT(whitespace/braces)
49 new arm::Register(arm::R0),
50 new arm::Register(arm::R1),
51 new arm::Register(arm::R2),
52 new arm::Register(arm::R3),
53 new arm::Register(arm::R4),
54 new arm::Register(arm::R5),
55 new arm::Register(arm::R6),
56 new arm::Register(arm::R7),
57 new arm::Register(arm::R8),
58 new arm::Register(arm::R9),
59 new arm::Register(arm::R10),
60 new arm::Register(arm::R11),
61 new arm::Register(arm::R12),
62 new arm::Register(arm::R13),
63 new arm::Register(arm::R14),
64 new arm::Register(arm::R15)
65 });
66 }
67 }
68
69 void TearDown() OVERRIDE {
70 AssemblerTest::TearDown();
71 STLDeleteElements(&registers_);
72 }
73
74 std::vector<arm::Register*> GetRegisters() OVERRIDE {
75 return registers_;
76 }
77
78 uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
79 return imm_value;
80 }
81
Vladimir Markocf93a5c2015-06-16 11:33:24 +000082 std::string RepeatInsn(size_t count, const std::string& insn) {
83 std::string result;
84 for (; count != 0u; --count) {
85 result += insn;
86 }
87 return result;
88 }
89
Roland Levillain1a28fc42014-11-13 18:03:06 +000090 private:
91 std::vector<arm::Register*> registers_;
Andreas Gampe513ea0c2015-02-02 13:17:52 -080092
93 static constexpr const char* kThumb2AssemblyHeader = ".syntax unified\n.thumb\n";
Roland Levillain1a28fc42014-11-13 18:03:06 +000094};
95
Roland Levillain1a28fc42014-11-13 18:03:06 +000096TEST_F(AssemblerThumb2Test, Toolchain) {
97 EXPECT_TRUE(CheckTools());
98}
99
Zheng Xuc6667102015-05-15 16:08:45 +0800100#define __ GetAssembler()->
Roland Levillain1a28fc42014-11-13 18:03:06 +0000101
102TEST_F(AssemblerThumb2Test, Sbfx) {
Zheng Xuc6667102015-05-15 16:08:45 +0800103 __ sbfx(arm::R0, arm::R1, 0, 1);
104 __ sbfx(arm::R0, arm::R1, 0, 8);
105 __ sbfx(arm::R0, arm::R1, 0, 16);
106 __ sbfx(arm::R0, arm::R1, 0, 32);
Roland Levillain1a28fc42014-11-13 18:03:06 +0000107
Zheng Xuc6667102015-05-15 16:08:45 +0800108 __ sbfx(arm::R0, arm::R1, 8, 1);
109 __ sbfx(arm::R0, arm::R1, 8, 8);
110 __ sbfx(arm::R0, arm::R1, 8, 16);
111 __ sbfx(arm::R0, arm::R1, 8, 24);
Roland Levillain1a28fc42014-11-13 18:03:06 +0000112
Zheng Xuc6667102015-05-15 16:08:45 +0800113 __ sbfx(arm::R0, arm::R1, 16, 1);
114 __ sbfx(arm::R0, arm::R1, 16, 8);
115 __ sbfx(arm::R0, arm::R1, 16, 16);
Roland Levillain1a28fc42014-11-13 18:03:06 +0000116
Zheng Xuc6667102015-05-15 16:08:45 +0800117 __ sbfx(arm::R0, arm::R1, 31, 1);
Roland Levillain1a28fc42014-11-13 18:03:06 +0000118
119 const char* expected =
120 "sbfx r0, r1, #0, #1\n"
121 "sbfx r0, r1, #0, #8\n"
122 "sbfx r0, r1, #0, #16\n"
123 "sbfx r0, r1, #0, #32\n"
124
125 "sbfx r0, r1, #8, #1\n"
126 "sbfx r0, r1, #8, #8\n"
127 "sbfx r0, r1, #8, #16\n"
128 "sbfx r0, r1, #8, #24\n"
129
130 "sbfx r0, r1, #16, #1\n"
131 "sbfx r0, r1, #16, #8\n"
132 "sbfx r0, r1, #16, #16\n"
133
134 "sbfx r0, r1, #31, #1\n";
135 DriverStr(expected, "sbfx");
136}
137
Roland Levillain981e4542014-11-14 11:47:14 +0000138TEST_F(AssemblerThumb2Test, Ubfx) {
Zheng Xuc6667102015-05-15 16:08:45 +0800139 __ ubfx(arm::R0, arm::R1, 0, 1);
140 __ ubfx(arm::R0, arm::R1, 0, 8);
141 __ ubfx(arm::R0, arm::R1, 0, 16);
142 __ ubfx(arm::R0, arm::R1, 0, 32);
Roland Levillain981e4542014-11-14 11:47:14 +0000143
Zheng Xuc6667102015-05-15 16:08:45 +0800144 __ ubfx(arm::R0, arm::R1, 8, 1);
145 __ ubfx(arm::R0, arm::R1, 8, 8);
146 __ ubfx(arm::R0, arm::R1, 8, 16);
147 __ ubfx(arm::R0, arm::R1, 8, 24);
Roland Levillain981e4542014-11-14 11:47:14 +0000148
Zheng Xuc6667102015-05-15 16:08:45 +0800149 __ ubfx(arm::R0, arm::R1, 16, 1);
150 __ ubfx(arm::R0, arm::R1, 16, 8);
151 __ ubfx(arm::R0, arm::R1, 16, 16);
Roland Levillain981e4542014-11-14 11:47:14 +0000152
Zheng Xuc6667102015-05-15 16:08:45 +0800153 __ ubfx(arm::R0, arm::R1, 31, 1);
Roland Levillain981e4542014-11-14 11:47:14 +0000154
155 const char* expected =
156 "ubfx r0, r1, #0, #1\n"
157 "ubfx r0, r1, #0, #8\n"
158 "ubfx r0, r1, #0, #16\n"
159 "ubfx r0, r1, #0, #32\n"
160
161 "ubfx r0, r1, #8, #1\n"
162 "ubfx r0, r1, #8, #8\n"
163 "ubfx r0, r1, #8, #16\n"
164 "ubfx r0, r1, #8, #24\n"
165
166 "ubfx r0, r1, #16, #1\n"
167 "ubfx r0, r1, #16, #8\n"
168 "ubfx r0, r1, #16, #16\n"
169
170 "ubfx r0, r1, #31, #1\n";
171 DriverStr(expected, "ubfx");
172}
173
Calin Juravleddb7df22014-11-25 20:56:51 +0000174TEST_F(AssemblerThumb2Test, Vmstat) {
Zheng Xuc6667102015-05-15 16:08:45 +0800175 __ vmstat();
Calin Juravleddb7df22014-11-25 20:56:51 +0000176
177 const char* expected = "vmrs APSR_nzcv, FPSCR\n";
178
179 DriverStr(expected, "vmrs");
180}
181
Calin Juravle52c48962014-12-16 17:02:57 +0000182TEST_F(AssemblerThumb2Test, ldrexd) {
Zheng Xuc6667102015-05-15 16:08:45 +0800183 __ ldrexd(arm::R0, arm::R1, arm::R0);
184 __ ldrexd(arm::R0, arm::R1, arm::R1);
185 __ ldrexd(arm::R0, arm::R1, arm::R2);
186 __ ldrexd(arm::R5, arm::R3, arm::R7);
Calin Juravle52c48962014-12-16 17:02:57 +0000187
188 const char* expected =
189 "ldrexd r0, r1, [r0]\n"
190 "ldrexd r0, r1, [r1]\n"
191 "ldrexd r0, r1, [r2]\n"
192 "ldrexd r5, r3, [r7]\n";
193 DriverStr(expected, "ldrexd");
194}
195
196TEST_F(AssemblerThumb2Test, strexd) {
Zheng Xuc6667102015-05-15 16:08:45 +0800197 __ strexd(arm::R9, arm::R0, arm::R1, arm::R0);
198 __ strexd(arm::R9, arm::R0, arm::R1, arm::R1);
199 __ strexd(arm::R9, arm::R0, arm::R1, arm::R2);
200 __ strexd(arm::R9, arm::R5, arm::R3, arm::R7);
Calin Juravle52c48962014-12-16 17:02:57 +0000201
202 const char* expected =
203 "strexd r9, r0, r1, [r0]\n"
204 "strexd r9, r0, r1, [r1]\n"
205 "strexd r9, r0, r1, [r2]\n"
206 "strexd r9, r5, r3, [r7]\n";
207 DriverStr(expected, "strexd");
208}
209
Roland Levillain188edb32016-10-24 16:31:16 +0100210TEST_F(AssemblerThumb2Test, clrex) {
211 __ clrex();
212
213 const char* expected = "clrex\n";
214 DriverStr(expected, "clrex");
215}
216
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800217TEST_F(AssemblerThumb2Test, LdrdStrd) {
Zheng Xuc6667102015-05-15 16:08:45 +0800218 __ ldrd(arm::R0, arm::Address(arm::R2, 8));
219 __ ldrd(arm::R0, arm::Address(arm::R12));
220 __ strd(arm::R0, arm::Address(arm::R2, 8));
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800221
222 const char* expected =
223 "ldrd r0, r1, [r2, #8]\n"
224 "ldrd r0, r1, [r12]\n"
225 "strd r0, r1, [r2, #8]\n";
226 DriverStr(expected, "ldrdstrd");
227}
228
Andreas Gampe513ea0c2015-02-02 13:17:52 -0800229TEST_F(AssemblerThumb2Test, eor) {
Andreas Gampe513ea0c2015-02-02 13:17:52 -0800230 __ eor(arm::R1, arm::R1, arm::ShifterOperand(arm::R0));
231 __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R1));
232 __ eor(arm::R1, arm::R8, arm::ShifterOperand(arm::R0));
233 __ eor(arm::R8, arm::R1, arm::ShifterOperand(arm::R0));
234 __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R8));
235
236 const char* expected =
237 "eors r1, r0\n"
238 "eor r1, r0, r1\n"
239 "eor r1, r8, r0\n"
240 "eor r8, r1, r0\n"
241 "eor r1, r0, r8\n";
242 DriverStr(expected, "abs");
243}
244
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000245TEST_F(AssemblerThumb2Test, sub) {
246 __ subs(arm::R1, arm::R0, arm::ShifterOperand(42));
247 __ sub(arm::R1, arm::R0, arm::ShifterOperand(42));
Zheng Xuc6667102015-05-15 16:08:45 +0800248 __ subs(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
249 __ sub(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000250
251 const char* expected =
252 "subs r1, r0, #42\n"
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000253 "sub.w r1, r0, #42\n"
Zheng Xuc6667102015-05-15 16:08:45 +0800254 "subs r1, r0, r2, asr #31\n"
255 "sub r1, r0, r2, asr #31\n";
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000256 DriverStr(expected, "sub");
257}
258
259TEST_F(AssemblerThumb2Test, add) {
260 __ adds(arm::R1, arm::R0, arm::ShifterOperand(42));
261 __ add(arm::R1, arm::R0, arm::ShifterOperand(42));
Zheng Xuc6667102015-05-15 16:08:45 +0800262 __ adds(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
263 __ add(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000264
265 const char* expected =
266 "adds r1, r0, #42\n"
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000267 "add.w r1, r0, #42\n"
Zheng Xuc6667102015-05-15 16:08:45 +0800268 "adds r1, r0, r2, asr #31\n"
269 "add r1, r0, r2, asr #31\n";
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000270 DriverStr(expected, "add");
271}
272
Zheng Xuc6667102015-05-15 16:08:45 +0800273TEST_F(AssemblerThumb2Test, umull) {
274 __ umull(arm::R0, arm::R1, arm::R2, arm::R3);
275
276 const char* expected =
277 "umull r0, r1, r2, r3\n";
278 DriverStr(expected, "umull");
279}
280
281TEST_F(AssemblerThumb2Test, smull) {
282 __ smull(arm::R0, arm::R1, arm::R2, arm::R3);
283
284 const char* expected =
285 "smull r0, r1, r2, r3\n";
286 DriverStr(expected, "smull");
287}
288
Vladimir Marko3a656e12016-08-02 14:57:56 +0100289TEST_F(AssemblerThumb2Test, LoadByteFromThumbOffset) {
290 arm::LoadOperandType type = arm::kLoadUnsignedByte;
291
292 __ LoadFromOffset(type, arm::R0, arm::R7, 0);
293 __ LoadFromOffset(type, arm::R1, arm::R7, 31);
294 __ LoadFromOffset(type, arm::R2, arm::R7, 32);
295 __ LoadFromOffset(type, arm::R3, arm::R7, 4095);
296 __ LoadFromOffset(type, arm::R4, arm::SP, 0);
297
298 const char* expected =
299 "ldrb r0, [r7, #0]\n"
300 "ldrb r1, [r7, #31]\n"
301 "ldrb.w r2, [r7, #32]\n"
302 "ldrb.w r3, [r7, #4095]\n"
303 "ldrb.w r4, [sp, #0]\n";
304 DriverStr(expected, "LoadByteFromThumbOffset");
305}
306
307TEST_F(AssemblerThumb2Test, StoreByteToThumbOffset) {
308 arm::StoreOperandType type = arm::kStoreByte;
309
310 __ StoreToOffset(type, arm::R0, arm::R7, 0);
311 __ StoreToOffset(type, arm::R1, arm::R7, 31);
312 __ StoreToOffset(type, arm::R2, arm::R7, 32);
313 __ StoreToOffset(type, arm::R3, arm::R7, 4095);
314 __ StoreToOffset(type, arm::R4, arm::SP, 0);
315
316 const char* expected =
317 "strb r0, [r7, #0]\n"
318 "strb r1, [r7, #31]\n"
319 "strb.w r2, [r7, #32]\n"
320 "strb.w r3, [r7, #4095]\n"
321 "strb.w r4, [sp, #0]\n";
322 DriverStr(expected, "StoreByteToThumbOffset");
323}
324
325TEST_F(AssemblerThumb2Test, LoadHalfFromThumbOffset) {
326 arm::LoadOperandType type = arm::kLoadUnsignedHalfword;
327
328 __ LoadFromOffset(type, arm::R0, arm::R7, 0);
329 __ LoadFromOffset(type, arm::R1, arm::R7, 62);
330 __ LoadFromOffset(type, arm::R2, arm::R7, 64);
331 __ LoadFromOffset(type, arm::R3, arm::R7, 4094);
332 __ LoadFromOffset(type, arm::R4, arm::SP, 0);
333 __ LoadFromOffset(type, arm::R5, arm::R7, 1); // Unaligned
334
335 const char* expected =
336 "ldrh r0, [r7, #0]\n"
337 "ldrh r1, [r7, #62]\n"
338 "ldrh.w r2, [r7, #64]\n"
339 "ldrh.w r3, [r7, #4094]\n"
340 "ldrh.w r4, [sp, #0]\n"
341 "ldrh.w r5, [r7, #1]\n";
342 DriverStr(expected, "LoadHalfFromThumbOffset");
343}
344
345TEST_F(AssemblerThumb2Test, StoreHalfToThumbOffset) {
346 arm::StoreOperandType type = arm::kStoreHalfword;
347
348 __ StoreToOffset(type, arm::R0, arm::R7, 0);
349 __ StoreToOffset(type, arm::R1, arm::R7, 62);
350 __ StoreToOffset(type, arm::R2, arm::R7, 64);
351 __ StoreToOffset(type, arm::R3, arm::R7, 4094);
352 __ StoreToOffset(type, arm::R4, arm::SP, 0);
353 __ StoreToOffset(type, arm::R5, arm::R7, 1); // Unaligned
354
355 const char* expected =
356 "strh r0, [r7, #0]\n"
357 "strh r1, [r7, #62]\n"
358 "strh.w r2, [r7, #64]\n"
359 "strh.w r3, [r7, #4094]\n"
360 "strh.w r4, [sp, #0]\n"
361 "strh.w r5, [r7, #1]\n";
362 DriverStr(expected, "StoreHalfToThumbOffset");
363}
364
365TEST_F(AssemblerThumb2Test, LoadWordFromSpPlusOffset) {
366 arm::LoadOperandType type = arm::kLoadWord;
367
368 __ LoadFromOffset(type, arm::R0, arm::SP, 0);
369 __ LoadFromOffset(type, arm::R1, arm::SP, 124);
370 __ LoadFromOffset(type, arm::R2, arm::SP, 128);
371 __ LoadFromOffset(type, arm::R3, arm::SP, 1020);
372 __ LoadFromOffset(type, arm::R4, arm::SP, 1024);
373 __ LoadFromOffset(type, arm::R5, arm::SP, 4092);
374 __ LoadFromOffset(type, arm::R6, arm::SP, 1); // Unaligned
375
376 const char* expected =
377 "ldr r0, [sp, #0]\n"
378 "ldr r1, [sp, #124]\n"
379 "ldr r2, [sp, #128]\n"
380 "ldr r3, [sp, #1020]\n"
381 "ldr.w r4, [sp, #1024]\n"
382 "ldr.w r5, [sp, #4092]\n"
383 "ldr.w r6, [sp, #1]\n";
384 DriverStr(expected, "LoadWordFromSpPlusOffset");
385}
386
387TEST_F(AssemblerThumb2Test, StoreWordToSpPlusOffset) {
388 arm::StoreOperandType type = arm::kStoreWord;
389
390 __ StoreToOffset(type, arm::R0, arm::SP, 0);
391 __ StoreToOffset(type, arm::R1, arm::SP, 124);
392 __ StoreToOffset(type, arm::R2, arm::SP, 128);
393 __ StoreToOffset(type, arm::R3, arm::SP, 1020);
394 __ StoreToOffset(type, arm::R4, arm::SP, 1024);
395 __ StoreToOffset(type, arm::R5, arm::SP, 4092);
396 __ StoreToOffset(type, arm::R6, arm::SP, 1); // Unaligned
397
398 const char* expected =
399 "str r0, [sp, #0]\n"
400 "str r1, [sp, #124]\n"
401 "str r2, [sp, #128]\n"
402 "str r3, [sp, #1020]\n"
403 "str.w r4, [sp, #1024]\n"
404 "str.w r5, [sp, #4092]\n"
405 "str.w r6, [sp, #1]\n";
406 DriverStr(expected, "StoreWordToSpPlusOffset");
407}
408
409TEST_F(AssemblerThumb2Test, LoadWordFromPcPlusOffset) {
410 arm::LoadOperandType type = arm::kLoadWord;
411
412 __ LoadFromOffset(type, arm::R0, arm::PC, 0);
413 __ LoadFromOffset(type, arm::R1, arm::PC, 124);
414 __ LoadFromOffset(type, arm::R2, arm::PC, 128);
415 __ LoadFromOffset(type, arm::R3, arm::PC, 1020);
416 __ LoadFromOffset(type, arm::R4, arm::PC, 1024);
417 __ LoadFromOffset(type, arm::R5, arm::PC, 4092);
418 __ LoadFromOffset(type, arm::R6, arm::PC, 1); // Unaligned
419
420 const char* expected =
421 "ldr r0, [pc, #0]\n"
422 "ldr r1, [pc, #124]\n"
423 "ldr r2, [pc, #128]\n"
424 "ldr r3, [pc, #1020]\n"
425 "ldr.w r4, [pc, #1024]\n"
426 "ldr.w r5, [pc, #4092]\n"
427 "ldr.w r6, [pc, #1]\n";
428 DriverStr(expected, "LoadWordFromPcPlusOffset");
429}
430
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100431TEST_F(AssemblerThumb2Test, StoreWordToThumbOffset) {
432 arm::StoreOperandType type = arm::kStoreWord;
433 int32_t offset = 4092;
434 ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
435
436 __ StoreToOffset(type, arm::R0, arm::SP, offset);
437 __ StoreToOffset(type, arm::IP, arm::SP, offset);
438 __ StoreToOffset(type, arm::IP, arm::R5, offset);
439
440 const char* expected =
441 "str r0, [sp, #4092]\n"
442 "str ip, [sp, #4092]\n"
443 "str ip, [r5, #4092]\n";
444 DriverStr(expected, "StoreWordToThumbOffset");
445}
446
447TEST_F(AssemblerThumb2Test, StoreWordToNonThumbOffset) {
448 arm::StoreOperandType type = arm::kStoreWord;
449 int32_t offset = 4096;
450 ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
451
452 __ StoreToOffset(type, arm::R0, arm::SP, offset);
453 __ StoreToOffset(type, arm::IP, arm::SP, offset);
454 __ StoreToOffset(type, arm::IP, arm::R5, offset);
455
456 const char* expected =
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000457 "add.w ip, sp, #4096\n" // AddConstant(ip, sp, 4096)
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100458 "str r0, [ip, #0]\n"
459
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000460 "str r5, [sp, #-4]!\n" // Push(r5)
461 "add.w r5, sp, #4096\n" // AddConstant(r5, 4100 & ~0xfff)
462 "str ip, [r5, #4]\n" // StoreToOffset(type, ip, r5, 4100 & 0xfff)
463 "ldr r5, [sp], #4\n" // Pop(r5)
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100464
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000465 "str r6, [sp, #-4]!\n" // Push(r6)
466 "add.w r6, r5, #4096\n" // AddConstant(r6, r5, 4096 & ~0xfff)
467 "str ip, [r6, #0]\n" // StoreToOffset(type, ip, r6, 4096 & 0xfff)
468 "ldr r6, [sp], #4\n"; // Pop(r6)
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100469 DriverStr(expected, "StoreWordToNonThumbOffset");
470}
471
Roland Levillain4af147e2015-04-07 13:54:49 +0100472TEST_F(AssemblerThumb2Test, StoreWordPairToThumbOffset) {
473 arm::StoreOperandType type = arm::kStoreWordPair;
474 int32_t offset = 1020;
475 ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
476
477 __ StoreToOffset(type, arm::R0, arm::SP, offset);
478 // We cannot use IP (i.e. R12) as first source register, as it would
479 // force us to use SP (i.e. R13) as second source register, which
480 // would have an "unpredictable" effect according to the ARMv7
481 // specification (the T1 encoding describes the result as
482 // UNPREDICTABLE when of the source registers is R13).
483 //
484 // So we use (R11, IP) (e.g. (R11, R12)) as source registers in the
485 // following instructions.
486 __ StoreToOffset(type, arm::R11, arm::SP, offset);
487 __ StoreToOffset(type, arm::R11, arm::R5, offset);
488
489 const char* expected =
490 "strd r0, r1, [sp, #1020]\n"
491 "strd r11, ip, [sp, #1020]\n"
492 "strd r11, ip, [r5, #1020]\n";
493 DriverStr(expected, "StoreWordPairToThumbOffset");
494}
495
496TEST_F(AssemblerThumb2Test, StoreWordPairToNonThumbOffset) {
497 arm::StoreOperandType type = arm::kStoreWordPair;
498 int32_t offset = 1024;
499 ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
500
501 __ StoreToOffset(type, arm::R0, arm::SP, offset);
502 // Same comment as in AssemblerThumb2Test.StoreWordPairToThumbOffset
503 // regarding the use of (R11, IP) (e.g. (R11, R12)) as source
504 // registers in the following instructions.
505 __ StoreToOffset(type, arm::R11, arm::SP, offset);
506 __ StoreToOffset(type, arm::R11, arm::R5, offset);
507
508 const char* expected =
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000509 "add.w ip, sp, #1024\n" // AddConstant(ip, sp, 1024)
Roland Levillain4af147e2015-04-07 13:54:49 +0100510 "strd r0, r1, [ip, #0]\n"
511
512 "str r5, [sp, #-4]!\n" // Push(r5)
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000513 "add.w r5, sp, #1024\n" // AddConstant(r5, sp, (1024 + kRegisterSize) & ~0x3fc)
514 "strd r11, ip, [r5, #4]\n" // StoreToOffset(type, r11, sp, (1024 + kRegisterSize) & 0x3fc)
Roland Levillain4af147e2015-04-07 13:54:49 +0100515 "ldr r5, [sp], #4\n" // Pop(r5)
516
517 "str r6, [sp, #-4]!\n" // Push(r6)
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000518 "add.w r6, r5, #1024\n" // AddConstant(r6, r5, 1024 & ~0x3fc)
519 "strd r11, ip, [r6, #0]\n" // StoreToOffset(type, r11, r6, 1024 & 0x3fc)
Roland Levillain4af147e2015-04-07 13:54:49 +0100520 "ldr r6, [sp], #4\n"; // Pop(r6)
521 DriverStr(expected, "StoreWordPairToNonThumbOffset");
522}
523
Vladimir Markoa64f2492016-04-25 12:43:50 +0000524TEST_F(AssemblerThumb2Test, DistantBackBranch) {
525 Label start, end;
526 __ Bind(&start);
527 constexpr size_t kLdrR0R0Count1 = 256;
528 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
529 __ ldr(arm::R0, arm::Address(arm::R0));
530 }
531 __ b(&end, arm::EQ);
532 __ b(&start, arm::LT);
533 constexpr size_t kLdrR0R0Count2 = 256;
534 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
535 __ ldr(arm::R0, arm::Address(arm::R0));
536 }
537 __ Bind(&end);
538
539 std::string expected =
540 "0:\n" +
541 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
542 "beq 1f\n"
543 "blt 0b\n" +
544 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
545 "1:\n";
546 DriverStr(expected, "DistantBackBranch");
547}
548
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000549TEST_F(AssemblerThumb2Test, TwoCbzMaxOffset) {
550 Label label0, label1, label2;
551 __ cbz(arm::R0, &label1);
552 constexpr size_t kLdrR0R0Count1 = 63;
553 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
554 __ ldr(arm::R0, arm::Address(arm::R0));
555 }
556 __ Bind(&label0);
557 __ cbz(arm::R0, &label2);
558 __ Bind(&label1);
559 constexpr size_t kLdrR0R0Count2 = 64;
560 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
561 __ ldr(arm::R0, arm::Address(arm::R0));
562 }
563 __ Bind(&label2);
564
565 std::string expected =
566 "cbz r0, 1f\n" + // cbz r0, label1
567 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
568 "0:\n"
569 "cbz r0, 2f\n" // cbz r0, label2
570 "1:\n" +
571 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
572 "2:\n";
573 DriverStr(expected, "TwoCbzMaxOffset");
574
575 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
576 __ GetAdjustedPosition(label0.Position()));
577 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 0u,
578 __ GetAdjustedPosition(label1.Position()));
579 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 0u,
580 __ GetAdjustedPosition(label2.Position()));
581}
582
583TEST_F(AssemblerThumb2Test, TwoCbzBeyondMaxOffset) {
584 Label label0, label1, label2;
585 __ cbz(arm::R0, &label1);
586 constexpr size_t kLdrR0R0Count1 = 63;
587 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
588 __ ldr(arm::R0, arm::Address(arm::R0));
589 }
590 __ Bind(&label0);
591 __ cbz(arm::R0, &label2);
592 __ Bind(&label1);
593 constexpr size_t kLdrR0R0Count2 = 65;
594 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
595 __ ldr(arm::R0, arm::Address(arm::R0));
596 }
597 __ Bind(&label2);
598
599 std::string expected =
600 "cmp r0, #0\n" // cbz r0, label1
601 "beq.n 1f\n" +
602 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
603 "0:\n"
604 "cmp r0, #0\n" // cbz r0, label2
605 "beq.n 2f\n"
606 "1:\n" +
607 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
608 "2:\n";
609 DriverStr(expected, "TwoCbzBeyondMaxOffset");
610
611 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
612 __ GetAdjustedPosition(label0.Position()));
613 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 4u,
614 __ GetAdjustedPosition(label1.Position()));
615 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 4u,
616 __ GetAdjustedPosition(label2.Position()));
617}
618
619TEST_F(AssemblerThumb2Test, TwoCbzSecondAtMaxB16Offset) {
620 Label label0, label1, label2;
621 __ cbz(arm::R0, &label1);
622 constexpr size_t kLdrR0R0Count1 = 62;
623 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
624 __ ldr(arm::R0, arm::Address(arm::R0));
625 }
626 __ Bind(&label0);
627 __ cbz(arm::R0, &label2);
628 __ Bind(&label1);
629 constexpr size_t kLdrR0R0Count2 = 128;
630 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
631 __ ldr(arm::R0, arm::Address(arm::R0));
632 }
633 __ Bind(&label2);
634
635 std::string expected =
636 "cbz r0, 1f\n" + // cbz r0, label1
637 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
638 "0:\n"
639 "cmp r0, #0\n" // cbz r0, label2
640 "beq.n 2f\n"
641 "1:\n" +
642 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
643 "2:\n";
644 DriverStr(expected, "TwoCbzSecondAtMaxB16Offset");
645
646 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
647 __ GetAdjustedPosition(label0.Position()));
648 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
649 __ GetAdjustedPosition(label1.Position()));
650 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
651 __ GetAdjustedPosition(label2.Position()));
652}
653
654TEST_F(AssemblerThumb2Test, TwoCbzSecondBeyondMaxB16Offset) {
655 Label label0, label1, label2;
656 __ cbz(arm::R0, &label1);
657 constexpr size_t kLdrR0R0Count1 = 62;
658 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
659 __ ldr(arm::R0, arm::Address(arm::R0));
660 }
661 __ Bind(&label0);
662 __ cbz(arm::R0, &label2);
663 __ Bind(&label1);
664 constexpr size_t kLdrR0R0Count2 = 129;
665 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
666 __ ldr(arm::R0, arm::Address(arm::R0));
667 }
668 __ Bind(&label2);
669
670 std::string expected =
671 "cmp r0, #0\n" // cbz r0, label1
672 "beq.n 1f\n" +
673 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
674 "0:\n"
675 "cmp r0, #0\n" // cbz r0, label2
676 "beq.w 2f\n"
677 "1:\n" +
678 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
679 "2:\n";
680 DriverStr(expected, "TwoCbzSecondBeyondMaxB16Offset");
681
682 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
683 __ GetAdjustedPosition(label0.Position()));
684 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
685 __ GetAdjustedPosition(label1.Position()));
686 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
687 __ GetAdjustedPosition(label2.Position()));
688}
689
690TEST_F(AssemblerThumb2Test, TwoCbzFirstAtMaxB16Offset) {
691 Label label0, label1, label2;
692 __ cbz(arm::R0, &label1);
693 constexpr size_t kLdrR0R0Count1 = 127;
694 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
695 __ ldr(arm::R0, arm::Address(arm::R0));
696 }
697 __ Bind(&label0);
698 __ cbz(arm::R0, &label2);
699 __ Bind(&label1);
700 constexpr size_t kLdrR0R0Count2 = 64;
701 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
702 __ ldr(arm::R0, arm::Address(arm::R0));
703 }
704 __ Bind(&label2);
705
706 std::string expected =
707 "cmp r0, #0\n" // cbz r0, label1
708 "beq.n 1f\n" +
709 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
710 "0:\n"
711 "cbz r0, 2f\n" // cbz r0, label2
712 "1:\n" +
713 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
714 "2:\n";
715 DriverStr(expected, "TwoCbzFirstAtMaxB16Offset");
716
717 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
718 __ GetAdjustedPosition(label0.Position()));
719 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
720 __ GetAdjustedPosition(label1.Position()));
721 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
722 __ GetAdjustedPosition(label2.Position()));
723}
724
725TEST_F(AssemblerThumb2Test, TwoCbzFirstBeyondMaxB16Offset) {
726 Label label0, label1, label2;
727 __ cbz(arm::R0, &label1);
728 constexpr size_t kLdrR0R0Count1 = 127;
729 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
730 __ ldr(arm::R0, arm::Address(arm::R0));
731 }
732 __ Bind(&label0);
733 __ cbz(arm::R0, &label2);
734 __ Bind(&label1);
735 constexpr size_t kLdrR0R0Count2 = 65;
736 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
737 __ ldr(arm::R0, arm::Address(arm::R0));
738 }
739 __ Bind(&label2);
740
741 std::string expected =
742 "cmp r0, #0\n" // cbz r0, label1
743 "beq.w 1f\n" +
744 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
745 "0:\n"
746 "cmp r0, #0\n" // cbz r0, label2
747 "beq.n 2f\n"
748 "1:\n" +
749 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
750 "2:\n";
751 DriverStr(expected, "TwoCbzFirstBeyondMaxB16Offset");
752
753 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 4u,
754 __ GetAdjustedPosition(label0.Position()));
755 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
756 __ GetAdjustedPosition(label1.Position()));
757 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
758 __ GetAdjustedPosition(label2.Position()));
759}
760
761TEST_F(AssemblerThumb2Test, LoadLiteralMax1KiB) {
762 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
763 __ LoadLiteral(arm::R0, literal);
764 Label label;
765 __ Bind(&label);
766 constexpr size_t kLdrR0R0Count = 511;
767 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
768 __ ldr(arm::R0, arm::Address(arm::R0));
769 }
770
771 std::string expected =
772 "1:\n"
773 "ldr.n r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
774 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
775 ".align 2, 0\n"
776 "2:\n"
777 ".word 0x12345678\n";
778 DriverStr(expected, "LoadLiteralMax1KiB");
779
780 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
781 __ GetAdjustedPosition(label.Position()));
782}
783
784TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiB) {
785 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
786 __ LoadLiteral(arm::R0, literal);
787 Label label;
788 __ Bind(&label);
789 constexpr size_t kLdrR0R0Count = 512;
790 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
791 __ ldr(arm::R0, arm::Address(arm::R0));
792 }
793
794 std::string expected =
795 "1:\n"
796 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
797 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
798 ".align 2, 0\n"
799 "2:\n"
800 ".word 0x12345678\n";
801 DriverStr(expected, "LoadLiteralBeyondMax1KiB");
802
803 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
804 __ GetAdjustedPosition(label.Position()));
805}
806
807TEST_F(AssemblerThumb2Test, LoadLiteralMax4KiB) {
808 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
809 __ LoadLiteral(arm::R1, literal);
810 Label label;
811 __ Bind(&label);
812 constexpr size_t kLdrR0R0Count = 2046;
813 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
814 __ ldr(arm::R0, arm::Address(arm::R0));
815 }
816
817 std::string expected =
818 "1:\n"
819 "ldr.w r1, [pc, #((2f - 1b - 2) & ~2)]\n" +
820 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
821 ".align 2, 0\n"
822 "2:\n"
823 ".word 0x12345678\n";
824 DriverStr(expected, "LoadLiteralMax4KiB");
825
826 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
827 __ GetAdjustedPosition(label.Position()));
828}
829
830TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax4KiB) {
831 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
832 __ LoadLiteral(arm::R1, literal);
833 Label label;
834 __ Bind(&label);
835 constexpr size_t kLdrR0R0Count = 2047;
836 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
837 __ ldr(arm::R0, arm::Address(arm::R0));
838 }
839
840 std::string expected =
841 "movw r1, #4096\n" // "as" does not consider (2f - 1f - 4) a constant expression for movw.
842 "1:\n"
843 "add r1, pc\n"
844 "ldr r1, [r1, #0]\n" +
845 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
846 ".align 2, 0\n"
847 "2:\n"
848 ".word 0x12345678\n";
849 DriverStr(expected, "LoadLiteralBeyondMax4KiB");
850
851 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
852 __ GetAdjustedPosition(label.Position()));
853}
854
855TEST_F(AssemblerThumb2Test, LoadLiteralMax64KiB) {
856 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
857 __ LoadLiteral(arm::R1, literal);
858 Label label;
859 __ Bind(&label);
860 constexpr size_t kLdrR0R0Count = (1u << 15) - 2u;
861 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
862 __ ldr(arm::R0, arm::Address(arm::R0));
863 }
864
865 std::string expected =
866 "movw r1, #0xfffc\n" // "as" does not consider (2f - 1f - 4) a constant expression for movw.
867 "1:\n"
868 "add r1, pc\n"
869 "ldr r1, [r1, #0]\n" +
870 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
871 ".align 2, 0\n"
872 "2:\n"
873 ".word 0x12345678\n";
874 DriverStr(expected, "LoadLiteralMax64KiB");
875
876 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
877 __ GetAdjustedPosition(label.Position()));
878}
879
880TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax64KiB) {
881 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
882 __ LoadLiteral(arm::R1, literal);
883 Label label;
884 __ Bind(&label);
885 constexpr size_t kLdrR0R0Count = (1u << 15) - 1u;
886 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
887 __ ldr(arm::R0, arm::Address(arm::R0));
888 }
889
890 std::string expected =
891 "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n"
892 "1:\n"
893 "add r1, pc\n"
894 "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" +
895 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
896 ".align 2, 0\n"
897 "2:\n"
898 ".word 0x12345678\n";
899 DriverStr(expected, "LoadLiteralBeyondMax64KiB");
900
901 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
902 __ GetAdjustedPosition(label.Position()));
903}
904
905TEST_F(AssemblerThumb2Test, LoadLiteralMax1MiB) {
906 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
907 __ LoadLiteral(arm::R1, literal);
908 Label label;
909 __ Bind(&label);
910 constexpr size_t kLdrR0R0Count = (1u << 19) - 3u;
911 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
912 __ ldr(arm::R0, arm::Address(arm::R0));
913 }
914
915 std::string expected =
916 "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n"
917 "1:\n"
918 "add r1, pc\n"
919 "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" +
920 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
921 ".align 2, 0\n"
922 "2:\n"
923 ".word 0x12345678\n";
924 DriverStr(expected, "LoadLiteralMax1MiB");
925
926 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
927 __ GetAdjustedPosition(label.Position()));
928}
929
930TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1MiB) {
931 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
932 __ LoadLiteral(arm::R1, literal);
933 Label label;
934 __ Bind(&label);
935 constexpr size_t kLdrR0R0Count = (1u << 19) - 2u;
936 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
937 __ ldr(arm::R0, arm::Address(arm::R0));
938 }
939
940 std::string expected =
941 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
942 "movw r1, #(0x100000 & 0xffff)\n"
943 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
944 "movt r1, #(0x100000 >> 16)\n"
945 "1:\n"
946 "add r1, pc\n"
947 "ldr.w r1, [r1, #0]\n" +
948 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
949 ".align 2, 0\n"
950 "2:\n"
951 ".word 0x12345678\n";
952 DriverStr(expected, "LoadLiteralBeyondMax1MiB");
953
954 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
955 __ GetAdjustedPosition(label.Position()));
956}
957
958TEST_F(AssemblerThumb2Test, LoadLiteralFar) {
959 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
960 __ LoadLiteral(arm::R1, literal);
961 Label label;
962 __ Bind(&label);
963 constexpr size_t kLdrR0R0Count = (1u << 19) - 2u + 0x1234;
964 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
965 __ ldr(arm::R0, arm::Address(arm::R0));
966 }
967
968 std::string expected =
969 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
970 "movw r1, #((0x100000 + 2 * 0x1234) & 0xffff)\n"
971 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
972 "movt r1, #((0x100000 + 2 * 0x1234) >> 16)\n"
973 "1:\n"
974 "add r1, pc\n"
975 "ldr.w r1, [r1, #0]\n" +
976 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
977 ".align 2, 0\n"
978 "2:\n"
979 ".word 0x12345678\n";
980 DriverStr(expected, "LoadLiteralFar");
981
982 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
983 __ GetAdjustedPosition(label.Position()));
984}
985
986TEST_F(AssemblerThumb2Test, LoadLiteralWideMax1KiB) {
987 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
988 __ LoadLiteral(arm::R1, arm::R3, literal);
989 Label label;
990 __ Bind(&label);
991 constexpr size_t kLdrR0R0Count = 510;
992 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
993 __ ldr(arm::R0, arm::Address(arm::R0));
994 }
995
996 std::string expected =
997 "1:\n"
998 "ldrd r1, r3, [pc, #((2f - 1b - 2) & ~2)]\n" +
999 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1000 ".align 2, 0\n"
1001 "2:\n"
1002 ".word 0x87654321\n"
1003 ".word 0x12345678\n";
1004 DriverStr(expected, "LoadLiteralWideMax1KiB");
1005
1006 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
1007 __ GetAdjustedPosition(label.Position()));
1008}
1009
1010TEST_F(AssemblerThumb2Test, LoadLiteralWideBeyondMax1KiB) {
1011 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
1012 __ LoadLiteral(arm::R1, arm::R3, literal);
1013 Label label;
1014 __ Bind(&label);
1015 constexpr size_t kLdrR0R0Count = 511;
1016 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1017 __ ldr(arm::R0, arm::Address(arm::R0));
1018 }
1019
1020 std::string expected =
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001021 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
1022 "movw ip, #(0x408 - 0x4 - 4)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001023 "1:\n"
1024 "add ip, pc\n"
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001025 "ldrd r1, r3, [ip, #0]\n" +
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001026 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1027 ".align 2, 0\n"
1028 "2:\n"
1029 ".word 0x87654321\n"
1030 ".word 0x12345678\n";
1031 DriverStr(expected, "LoadLiteralWideBeyondMax1KiB");
1032
1033 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
1034 __ GetAdjustedPosition(label.Position()));
1035}
1036
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001037TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax64KiB) {
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001038 // The literal size must match but the type doesn't, so use an int32_t rather than float.
1039 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
1040 __ LoadLiteral(arm::S3, literal);
1041 Label label;
1042 __ Bind(&label);
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001043 constexpr size_t kLdrR0R0Count = (1 << 15) - 3u;
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001044 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1045 __ ldr(arm::R0, arm::Address(arm::R0));
1046 }
1047
1048 std::string expected =
1049 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001050 "movw ip, #(0x10004 - 0x4 - 4)\n"
1051 "1:\n"
1052 "add ip, pc\n"
1053 "vldr s3, [ip, #0]\n" +
1054 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1055 ".align 2, 0\n"
1056 "2:\n"
1057 ".word 0x12345678\n";
1058 DriverStr(expected, "LoadLiteralSingleMax64KiB");
1059
1060 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
1061 __ GetAdjustedPosition(label.Position()));
1062}
1063
1064TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax64KiB_UnalignedPC) {
1065 // The literal size must match but the type doesn't, so use an int32_t rather than float.
1066 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
1067 __ ldr(arm::R0, arm::Address(arm::R0));
1068 __ LoadLiteral(arm::S3, literal);
1069 Label label;
1070 __ Bind(&label);
1071 constexpr size_t kLdrR0R0Count = (1 << 15) - 4u;
1072 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1073 __ ldr(arm::R0, arm::Address(arm::R0));
1074 }
1075
1076 std::string expected =
1077 "ldr r0, [r0]\n"
1078 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
1079 "movw ip, #(0x10004 - 0x6 - 4)\n"
1080 "1:\n"
1081 "add ip, pc\n"
1082 "vldr s3, [ip, #0]\n" +
1083 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1084 ".align 2, 0\n"
1085 "2:\n"
1086 ".word 0x12345678\n";
1087 DriverStr(expected, "LoadLiteralSingleMax64KiB_UnalignedPC");
1088
1089 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
1090 __ GetAdjustedPosition(label.Position()));
1091}
1092
1093TEST_F(AssemblerThumb2Test, LoadLiteralDoubleBeyondMax64KiB) {
1094 // The literal size must match but the type doesn't, so use an int64_t rather than double.
1095 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
1096 __ LoadLiteral(arm::D3, literal);
1097 Label label;
1098 __ Bind(&label);
1099 constexpr size_t kLdrR0R0Count = (1 << 15) - 2u;
1100 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1101 __ ldr(arm::R0, arm::Address(arm::R0));
1102 }
1103
1104 std::string expected =
1105 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
1106 "movw ip, #((0x1000c - 0x8 - 4) & 0xffff)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001107 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001108 "movt ip, #((0x1000c - 0x8 - 4) >> 16)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001109 "1:\n"
1110 "add ip, pc\n"
1111 "vldr d3, [ip, #0]\n" +
1112 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1113 ".align 2, 0\n"
1114 "2:\n"
1115 ".word 0x87654321\n"
1116 ".word 0x12345678\n";
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001117 DriverStr(expected, "LoadLiteralDoubleBeyondMax64KiB");
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001118
1119 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
1120 __ GetAdjustedPosition(label.Position()));
1121}
1122
1123TEST_F(AssemblerThumb2Test, LoadLiteralDoubleFar) {
1124 // The literal size must match but the type doesn't, so use an int64_t rather than double.
1125 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
1126 __ LoadLiteral(arm::D3, literal);
1127 Label label;
1128 __ Bind(&label);
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001129 constexpr size_t kLdrR0R0Count = (1 << 15) - 2u + 0x1234;
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001130 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1131 __ ldr(arm::R0, arm::Address(arm::R0));
1132 }
1133
1134 std::string expected =
1135 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001136 "movw ip, #((0x1000c + 2 * 0x1234 - 0x8 - 4) & 0xffff)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001137 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001138 "movt ip, #((0x1000c + 2 * 0x1234 - 0x8 - 4) >> 16)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001139 "1:\n"
1140 "add ip, pc\n"
1141 "vldr d3, [ip, #0]\n" +
1142 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1143 ".align 2, 0\n"
1144 "2:\n"
1145 ".word 0x87654321\n"
1146 ".word 0x12345678\n";
1147 DriverStr(expected, "LoadLiteralDoubleFar");
1148
1149 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
1150 __ GetAdjustedPosition(label.Position()));
1151}
1152
Vladimir Marko663c9342015-07-22 11:28:14 +01001153TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass) {
1154 // First part: as TwoCbzBeyondMaxOffset but add one 16-bit instruction to the end,
1155 // so that the size is not Aligned<4>(.). On the first pass, the assembler resizes
1156 // the second CBZ because it's out of range, then it will resize the first CBZ
1157 // which has been pushed out of range. Thus, after the first pass, the code size
1158 // will appear Aligned<4>(.) but the final size will not be.
1159 Label label0, label1, label2;
1160 __ cbz(arm::R0, &label1);
1161 constexpr size_t kLdrR0R0Count1 = 63;
1162 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
1163 __ ldr(arm::R0, arm::Address(arm::R0));
1164 }
1165 __ Bind(&label0);
1166 __ cbz(arm::R0, &label2);
1167 __ Bind(&label1);
1168 constexpr size_t kLdrR0R0Count2 = 65;
1169 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
1170 __ ldr(arm::R0, arm::Address(arm::R0));
1171 }
1172 __ Bind(&label2);
1173 __ ldr(arm::R0, arm::Address(arm::R0));
1174
1175 std::string expected_part1 =
1176 "cmp r0, #0\n" // cbz r0, label1
1177 "beq.n 1f\n" +
1178 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
1179 "0:\n"
1180 "cmp r0, #0\n" // cbz r0, label2
1181 "beq.n 2f\n"
1182 "1:\n" +
1183 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1184 "2:\n" // Here the offset is Aligned<4>(.).
1185 "ldr r0, [r0]\n"; // Make the first part
1186
1187 // Second part: as LoadLiteralMax1KiB with the caveat that the offset of the load
1188 // literal will not be Aligned<4>(.) but it will appear to be when we process the
1189 // instruction during the first pass, so the literal will need a padding and it
1190 // will push the literal out of range, so we shall end up with "ldr.w".
1191 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
1192 __ LoadLiteral(arm::R0, literal);
1193 Label label;
1194 __ Bind(&label);
1195 constexpr size_t kLdrR0R0Count = 511;
1196 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1197 __ ldr(arm::R0, arm::Address(arm::R0));
1198 }
1199
1200 std::string expected =
1201 expected_part1 +
1202 "1:\n"
1203 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
1204 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1205 ".align 2, 0\n"
1206 "2:\n"
1207 ".word 0x12345678\n";
1208 DriverStr(expected, "LoadLiteralMax1KiB");
1209
1210 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
1211 __ GetAdjustedPosition(label.Position()));
1212}
1213
Andreas Gampe7cffc3b2015-10-19 21:31:53 -07001214TEST_F(AssemblerThumb2Test, BindTrackedLabel) {
1215 Label non_tracked, tracked, branch_target;
1216
1217 // A few dummy loads on entry.
1218 constexpr size_t kLdrR0R0Count = 5;
1219 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1220 __ ldr(arm::R0, arm::Address(arm::R0));
1221 }
1222
1223 // A branch that will need to be fixed up.
1224 __ cbz(arm::R0, &branch_target);
1225
1226 // Some more dummy loads.
1227 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1228 __ ldr(arm::R0, arm::Address(arm::R0));
1229 }
1230
1231 // Now insert tracked and untracked label.
1232 __ Bind(&non_tracked);
1233 __ BindTrackedLabel(&tracked);
1234
1235 // A lot of dummy loads, to ensure the branch needs resizing.
1236 constexpr size_t kLdrR0R0CountLong = 60;
1237 for (size_t i = 0; i != kLdrR0R0CountLong; ++i) {
1238 __ ldr(arm::R0, arm::Address(arm::R0));
1239 }
1240
1241 // Bind the branch target.
1242 __ Bind(&branch_target);
1243
1244 // One more load.
1245 __ ldr(arm::R0, arm::Address(arm::R0));
1246
1247 std::string expected =
1248 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1249 "cmp r0, #0\n" // cbz r0, 1f
1250 "beq.n 1f\n" +
1251 RepeatInsn(kLdrR0R0Count + kLdrR0R0CountLong, "ldr r0, [r0]\n") +
1252 "1:\n"
1253 "ldr r0, [r0]\n";
1254 DriverStr(expected, "BindTrackedLabel");
1255
1256 // Expectation is that the tracked label should have moved.
1257 EXPECT_LT(non_tracked.Position(), tracked.Position());
1258}
1259
1260TEST_F(AssemblerThumb2Test, JumpTable) {
1261 // The jump table. Use three labels.
1262 Label label1, label2, label3;
1263 std::vector<Label*> labels({ &label1, &label2, &label3 });
1264
1265 // A few dummy loads on entry, interspersed with 2 labels.
1266 constexpr size_t kLdrR0R0Count = 5;
1267 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1268 __ ldr(arm::R0, arm::Address(arm::R0));
1269 }
1270 __ BindTrackedLabel(&label1);
1271 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1272 __ ldr(arm::R0, arm::Address(arm::R0));
1273 }
1274 __ BindTrackedLabel(&label2);
1275 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1276 __ ldr(arm::R0, arm::Address(arm::R0));
1277 }
1278
1279 // Create the jump table, emit the base load.
1280 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1281
1282 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1283 // it's being used.
1284 __ ldr(arm::R0, arm::Address(arm::R0));
1285
1286 // Emit the jump
1287 __ EmitJumpTableDispatch(jump_table, arm::R1);
1288
1289 // Some more dummy instructions.
1290 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1291 __ ldr(arm::R0, arm::Address(arm::R0));
1292 }
1293 __ BindTrackedLabel(&label3);
1294 for (size_t i = 0; i != kLdrR0R0Count; ++i) { // Note: odd so there's no alignment
1295 __ ldr(arm::R0, arm::Address(arm::R0)); // necessary, as gcc as emits nops,
1296 } // whereas we emit 0 != nop.
1297
1298 static_assert((kLdrR0R0Count + 3) * 2 < 1 * KB, "Too much offset");
1299
1300 std::string expected =
1301 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1302 ".L1:\n" +
1303 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1304 ".L2:\n" +
1305 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1306 "adr r1, .Ljump_table\n"
1307 "ldr r0, [r0]\n"
1308 ".Lbase:\n"
1309 "add pc, r1\n" +
1310 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1311 ".L3:\n" +
1312 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1313 ".align 2\n"
1314 ".Ljump_table:\n"
1315 ".4byte (.L1 - .Lbase - 4)\n"
1316 ".4byte (.L2 - .Lbase - 4)\n"
1317 ".4byte (.L3 - .Lbase - 4)\n";
1318 DriverStr(expected, "JumpTable");
1319}
1320
1321// Test for >1K fixup.
1322TEST_F(AssemblerThumb2Test, JumpTable4K) {
1323 // The jump table. Use three labels.
1324 Label label1, label2, label3;
1325 std::vector<Label*> labels({ &label1, &label2, &label3 });
1326
1327 // A few dummy loads on entry, interspersed with 2 labels.
1328 constexpr size_t kLdrR0R0Count = 5;
1329 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1330 __ ldr(arm::R0, arm::Address(arm::R0));
1331 }
1332 __ BindTrackedLabel(&label1);
1333 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1334 __ ldr(arm::R0, arm::Address(arm::R0));
1335 }
1336 __ BindTrackedLabel(&label2);
1337 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1338 __ ldr(arm::R0, arm::Address(arm::R0));
1339 }
1340
1341 // Create the jump table, emit the base load.
1342 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1343
1344 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1345 // it's being used.
1346 __ ldr(arm::R0, arm::Address(arm::R0));
1347
1348 // Emit the jump
1349 __ EmitJumpTableDispatch(jump_table, arm::R1);
1350
1351 // Some more dummy instructions.
1352 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1353 __ ldr(arm::R0, arm::Address(arm::R0));
1354 }
1355 __ BindTrackedLabel(&label3);
1356 constexpr size_t kLdrR0R0Count2 = 600; // Note: even so there's no alignment
1357 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1358 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1359 }
1360
1361 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 1 * KB, "Not enough offset");
1362 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 4 * KB, "Too much offset");
1363
1364 std::string expected =
1365 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1366 ".L1:\n" +
1367 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1368 ".L2:\n" +
1369 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1370 "adr r1, .Ljump_table\n"
1371 "ldr r0, [r0]\n"
1372 ".Lbase:\n"
1373 "add pc, r1\n" +
1374 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1375 ".L3:\n" +
1376 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1377 ".align 2\n"
1378 ".Ljump_table:\n"
1379 ".4byte (.L1 - .Lbase - 4)\n"
1380 ".4byte (.L2 - .Lbase - 4)\n"
1381 ".4byte (.L3 - .Lbase - 4)\n";
1382 DriverStr(expected, "JumpTable4K");
1383}
1384
1385// Test for >4K fixup.
1386TEST_F(AssemblerThumb2Test, JumpTable64K) {
1387 // The jump table. Use three labels.
1388 Label label1, label2, label3;
1389 std::vector<Label*> labels({ &label1, &label2, &label3 });
1390
1391 // A few dummy loads on entry, interspersed with 2 labels.
1392 constexpr size_t kLdrR0R0Count = 5;
1393 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1394 __ ldr(arm::R0, arm::Address(arm::R0));
1395 }
1396 __ BindTrackedLabel(&label1);
1397 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1398 __ ldr(arm::R0, arm::Address(arm::R0));
1399 }
1400 __ BindTrackedLabel(&label2);
1401 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1402 __ ldr(arm::R0, arm::Address(arm::R0));
1403 }
1404
1405 // Create the jump table, emit the base load.
1406 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1407
1408 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1409 // it's being used.
1410 __ ldr(arm::R0, arm::Address(arm::R0));
1411
1412 // Emit the jump
1413 __ EmitJumpTableDispatch(jump_table, arm::R1);
1414
1415 // Some more dummy instructions.
1416 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1417 __ ldr(arm::R0, arm::Address(arm::R0));
1418 }
1419 __ BindTrackedLabel(&label3);
1420 constexpr size_t kLdrR0R0Count2 = 2601; // Note: odd so there's no alignment
1421 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1422 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1423 }
1424
1425 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 4 * KB, "Not enough offset");
1426 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 64 * KB, "Too much offset");
1427
1428 std::string expected =
1429 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1430 ".L1:\n" +
1431 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1432 ".L2:\n" +
1433 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1434 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
1435 // (Note: have to use constants, as labels aren't accepted.
1436 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1437 ") * 2 - 4) & 0xFFFF)\n"
1438 "add r1, pc\n"
1439 "ldr r0, [r0]\n"
1440 ".Lbase:\n"
1441 "add pc, r1\n" +
1442 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1443 ".L3:\n" +
1444 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1445 ".align 2\n"
1446 ".Ljump_table:\n"
1447 ".4byte (.L1 - .Lbase - 4)\n"
1448 ".4byte (.L2 - .Lbase - 4)\n"
1449 ".4byte (.L3 - .Lbase - 4)\n";
1450 DriverStr(expected, "JumpTable64K");
1451}
1452
1453// Test for >64K fixup.
1454TEST_F(AssemblerThumb2Test, JumpTableFar) {
1455 // The jump table. Use three labels.
1456 Label label1, label2, label3;
1457 std::vector<Label*> labels({ &label1, &label2, &label3 });
1458
1459 // A few dummy loads on entry, interspersed with 2 labels.
1460 constexpr size_t kLdrR0R0Count = 5;
1461 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1462 __ ldr(arm::R0, arm::Address(arm::R0));
1463 }
1464 __ BindTrackedLabel(&label1);
1465 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1466 __ ldr(arm::R0, arm::Address(arm::R0));
1467 }
1468 __ BindTrackedLabel(&label2);
1469 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1470 __ ldr(arm::R0, arm::Address(arm::R0));
1471 }
1472
1473 // Create the jump table, emit the base load.
1474 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1475
1476 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1477 // it's being used.
1478 __ ldr(arm::R0, arm::Address(arm::R0));
1479
1480 // Emit the jump
1481 __ EmitJumpTableDispatch(jump_table, arm::R1);
1482
1483 // Some more dummy instructions.
1484 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1485 __ ldr(arm::R0, arm::Address(arm::R0));
1486 }
1487 __ BindTrackedLabel(&label3);
1488 constexpr size_t kLdrR0R0Count2 = 70001; // Note: odd so there's no alignment
1489 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1490 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1491 }
1492
1493 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 64 * KB, "Not enough offset");
1494
1495 std::string expected =
1496 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1497 ".L1:\n" +
1498 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1499 ".L2:\n" +
1500 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1501 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
1502 // (Note: have to use constants, as labels aren't accepted.
1503 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1504 ") * 2 - 4) & 0xFFFF)\n"
1505 "movt r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1506 ") * 2 - 4) >> 16)\n"
1507 ".Lhelp:"
1508 "add r1, pc\n"
1509 "ldr r0, [r0]\n"
1510 ".Lbase:\n"
1511 "add pc, r1\n" +
1512 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1513 ".L3:\n" +
1514 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1515 ".align 2\n"
1516 ".Ljump_table:\n"
1517 ".4byte (.L1 - .Lbase - 4)\n"
1518 ".4byte (.L2 - .Lbase - 4)\n"
1519 ".4byte (.L3 - .Lbase - 4)\n";
1520 DriverStr(expected, "JumpTableFar");
1521}
1522
Scott Wakeling611d3392015-07-10 11:42:06 +01001523TEST_F(AssemblerThumb2Test, Clz) {
1524 __ clz(arm::R0, arm::R1);
1525
1526 const char* expected = "clz r0, r1\n";
1527
1528 DriverStr(expected, "clz");
1529}
1530
Scott Wakeling9ee23f42015-07-23 10:44:35 +01001531TEST_F(AssemblerThumb2Test, rbit) {
1532 __ rbit(arm::R1, arm::R0);
1533
1534 const char* expected = "rbit r1, r0\n";
1535
1536 DriverStr(expected, "rbit");
1537}
1538
Artem Serovc257da72016-02-02 13:49:43 +00001539TEST_F(AssemblerThumb2Test, rev) {
1540 __ rev(arm::R1, arm::R0);
1541
1542 const char* expected = "rev r1, r0\n";
1543
1544 DriverStr(expected, "rev");
1545}
1546
1547TEST_F(AssemblerThumb2Test, rev16) {
1548 __ rev16(arm::R1, arm::R0);
1549
1550 const char* expected = "rev16 r1, r0\n";
1551
1552 DriverStr(expected, "rev16");
1553}
1554
1555TEST_F(AssemblerThumb2Test, revsh) {
1556 __ revsh(arm::R1, arm::R0);
1557
1558 const char* expected = "revsh r1, r0\n";
1559
1560 DriverStr(expected, "revsh");
1561}
1562
xueliang.zhonge652c122016-06-13 14:42:27 +01001563TEST_F(AssemblerThumb2Test, vcnt) {
1564 // Different D register numbers are used here, to test register encoding.
1565 // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
1566 // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
1567 // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
1568 __ vcntd(arm::D0, arm::D1);
1569 __ vcntd(arm::D19, arm::D20);
1570 __ vcntd(arm::D0, arm::D9);
1571 __ vcntd(arm::D16, arm::D20);
1572
1573 std::string expected =
1574 "vcnt.8 d0, d1\n"
1575 "vcnt.8 d19, d20\n"
1576 "vcnt.8 d0, d9\n"
1577 "vcnt.8 d16, d20\n";
1578
1579 DriverStr(expected, "vcnt");
1580}
1581
1582TEST_F(AssemblerThumb2Test, vpaddl) {
1583 // Different D register numbers are used here, to test register encoding.
1584 // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
1585 // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
1586 // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
1587 // Different data types (signed and unsigned) are also tested.
1588 __ vpaddld(arm::D0, arm::D0, 8, true);
1589 __ vpaddld(arm::D20, arm::D20, 8, false);
1590 __ vpaddld(arm::D0, arm::D20, 16, false);
1591 __ vpaddld(arm::D20, arm::D0, 32, true);
1592
1593 std::string expected =
1594 "vpaddl.u8 d0, d0\n"
1595 "vpaddl.s8 d20, d20\n"
1596 "vpaddl.s16 d0, d20\n"
1597 "vpaddl.u32 d20, d0\n";
1598
1599 DriverStr(expected, "vpaddl");
1600}
1601
Artem Serov2e4fcc92016-07-11 14:00:46 +01001602TEST_F(AssemblerThumb2Test, LoadFromShiftedRegOffset) {
1603 arm::Address mem_address(arm::R0, arm::R1, arm::Shift::LSL, 2);
1604
1605 __ ldrsb(arm::R2, mem_address);
1606 __ ldrb(arm::R2, mem_address);
1607 __ ldrsh(arm::R2, mem_address);
1608 __ ldrh(arm::R2, mem_address);
1609 __ ldr(arm::R2, mem_address);
1610
1611 std::string expected =
1612 "ldrsb r2, [r0, r1, LSL #2]\n"
1613 "ldrb r2, [r0, r1, LSL #2]\n"
1614 "ldrsh r2, [r0, r1, LSL #2]\n"
1615 "ldrh r2, [r0, r1, LSL #2]\n"
1616 "ldr r2, [r0, r1, LSL #2]\n";
1617
1618 DriverStr(expected, "LoadFromShiftedRegOffset");
1619}
1620
Artem Serovcb3cf4a2016-07-15 15:01:13 +01001621TEST_F(AssemblerThumb2Test, VStmLdmPushPop) {
1622 // Different D register numbers are used here, to test register encoding.
1623 // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
1624 // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
1625 // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
1626 // Different data types (signed and unsigned) are also tested.
1627 __ vstmiad(arm::R0, arm::D0, 4);
1628 __ vldmiad(arm::R1, arm::D9, 5);
1629 __ vpopd(arm::D0, 4);
1630 __ vpushd(arm::D9, 5);
1631 __ vpops(arm::S0, 4);
1632 __ vpushs(arm::S9, 5);
1633 __ vpushs(arm::S16, 5);
1634 __ vpushd(arm::D0, 16);
1635 __ vpushd(arm::D1, 15);
1636 __ vpushd(arm::D8, 16);
1637 __ vpushd(arm::D31, 1);
1638 __ vpushs(arm::S0, 32);
1639 __ vpushs(arm::S1, 31);
1640 __ vpushs(arm::S16, 16);
1641 __ vpushs(arm::S31, 1);
1642
1643 std::string expected =
1644 "vstmia r0, {d0 - d3}\n"
1645 "vldmia r1, {d9 - d13}\n"
1646 "vpop {d0 - d3}\n"
1647 "vpush {d9 - d13}\n"
1648 "vpop {s0 - s3}\n"
1649 "vpush {s9 - s13}\n"
1650 "vpush {s16 - s20}\n"
1651 "vpush {d0 - d15}\n"
1652 "vpush {d1 - d15}\n"
1653 "vpush {d8 - d23}\n"
1654 "vpush {d31}\n"
1655 "vpush {s0 - s31}\n"
1656 "vpush {s1 - s31}\n"
1657 "vpush {s16 - s31}\n"
1658 "vpush {s31}\n";
1659
1660 DriverStr(expected, "VStmLdmPushPop");
1661}
1662
Roland Levillain1a28fc42014-11-13 18:03:06 +00001663} // namespace art