blob: b5cafcbf666236aec0b38577274d919c2451bfce [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
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800210TEST_F(AssemblerThumb2Test, LdrdStrd) {
Zheng Xuc6667102015-05-15 16:08:45 +0800211 __ ldrd(arm::R0, arm::Address(arm::R2, 8));
212 __ ldrd(arm::R0, arm::Address(arm::R12));
213 __ strd(arm::R0, arm::Address(arm::R2, 8));
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800214
215 const char* expected =
216 "ldrd r0, r1, [r2, #8]\n"
217 "ldrd r0, r1, [r12]\n"
218 "strd r0, r1, [r2, #8]\n";
219 DriverStr(expected, "ldrdstrd");
220}
221
Andreas Gampe513ea0c2015-02-02 13:17:52 -0800222TEST_F(AssemblerThumb2Test, eor) {
Andreas Gampe513ea0c2015-02-02 13:17:52 -0800223 __ eor(arm::R1, arm::R1, arm::ShifterOperand(arm::R0));
224 __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R1));
225 __ eor(arm::R1, arm::R8, arm::ShifterOperand(arm::R0));
226 __ eor(arm::R8, arm::R1, arm::ShifterOperand(arm::R0));
227 __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R8));
228
229 const char* expected =
230 "eors r1, r0\n"
231 "eor r1, r0, r1\n"
232 "eor r1, r8, r0\n"
233 "eor r8, r1, r0\n"
234 "eor r1, r0, r8\n";
235 DriverStr(expected, "abs");
236}
237
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000238TEST_F(AssemblerThumb2Test, sub) {
239 __ subs(arm::R1, arm::R0, arm::ShifterOperand(42));
240 __ sub(arm::R1, arm::R0, arm::ShifterOperand(42));
Zheng Xuc6667102015-05-15 16:08:45 +0800241 __ subs(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
242 __ sub(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000243
244 const char* expected =
245 "subs r1, r0, #42\n"
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000246 "sub.w r1, r0, #42\n"
Zheng Xuc6667102015-05-15 16:08:45 +0800247 "subs r1, r0, r2, asr #31\n"
248 "sub r1, r0, r2, asr #31\n";
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000249 DriverStr(expected, "sub");
250}
251
252TEST_F(AssemblerThumb2Test, add) {
253 __ adds(arm::R1, arm::R0, arm::ShifterOperand(42));
254 __ add(arm::R1, arm::R0, arm::ShifterOperand(42));
Zheng Xuc6667102015-05-15 16:08:45 +0800255 __ adds(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
256 __ add(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000257
258 const char* expected =
259 "adds r1, r0, #42\n"
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000260 "add.w r1, r0, #42\n"
Zheng Xuc6667102015-05-15 16:08:45 +0800261 "adds r1, r0, r2, asr #31\n"
262 "add r1, r0, r2, asr #31\n";
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000263 DriverStr(expected, "add");
264}
265
Zheng Xuc6667102015-05-15 16:08:45 +0800266TEST_F(AssemblerThumb2Test, umull) {
267 __ umull(arm::R0, arm::R1, arm::R2, arm::R3);
268
269 const char* expected =
270 "umull r0, r1, r2, r3\n";
271 DriverStr(expected, "umull");
272}
273
274TEST_F(AssemblerThumb2Test, smull) {
275 __ smull(arm::R0, arm::R1, arm::R2, arm::R3);
276
277 const char* expected =
278 "smull r0, r1, r2, r3\n";
279 DriverStr(expected, "smull");
280}
281
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100282TEST_F(AssemblerThumb2Test, StoreWordToThumbOffset) {
283 arm::StoreOperandType type = arm::kStoreWord;
284 int32_t offset = 4092;
285 ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
286
287 __ StoreToOffset(type, arm::R0, arm::SP, offset);
288 __ StoreToOffset(type, arm::IP, arm::SP, offset);
289 __ StoreToOffset(type, arm::IP, arm::R5, offset);
290
291 const char* expected =
292 "str r0, [sp, #4092]\n"
293 "str ip, [sp, #4092]\n"
294 "str ip, [r5, #4092]\n";
295 DriverStr(expected, "StoreWordToThumbOffset");
296}
297
298TEST_F(AssemblerThumb2Test, StoreWordToNonThumbOffset) {
299 arm::StoreOperandType type = arm::kStoreWord;
300 int32_t offset = 4096;
301 ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
302
303 __ StoreToOffset(type, arm::R0, arm::SP, offset);
304 __ StoreToOffset(type, arm::IP, arm::SP, offset);
305 __ StoreToOffset(type, arm::IP, arm::R5, offset);
306
307 const char* expected =
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000308 "add.w ip, sp, #4096\n" // AddConstant(ip, sp, 4096)
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100309 "str r0, [ip, #0]\n"
310
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000311 "str r5, [sp, #-4]!\n" // Push(r5)
312 "add.w r5, sp, #4096\n" // AddConstant(r5, 4100 & ~0xfff)
313 "str ip, [r5, #4]\n" // StoreToOffset(type, ip, r5, 4100 & 0xfff)
314 "ldr r5, [sp], #4\n" // Pop(r5)
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100315
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000316 "str r6, [sp, #-4]!\n" // Push(r6)
317 "add.w r6, r5, #4096\n" // AddConstant(r6, r5, 4096 & ~0xfff)
318 "str ip, [r6, #0]\n" // StoreToOffset(type, ip, r6, 4096 & 0xfff)
319 "ldr r6, [sp], #4\n"; // Pop(r6)
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100320 DriverStr(expected, "StoreWordToNonThumbOffset");
321}
322
Roland Levillain4af147e2015-04-07 13:54:49 +0100323TEST_F(AssemblerThumb2Test, StoreWordPairToThumbOffset) {
324 arm::StoreOperandType type = arm::kStoreWordPair;
325 int32_t offset = 1020;
326 ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
327
328 __ StoreToOffset(type, arm::R0, arm::SP, offset);
329 // We cannot use IP (i.e. R12) as first source register, as it would
330 // force us to use SP (i.e. R13) as second source register, which
331 // would have an "unpredictable" effect according to the ARMv7
332 // specification (the T1 encoding describes the result as
333 // UNPREDICTABLE when of the source registers is R13).
334 //
335 // So we use (R11, IP) (e.g. (R11, R12)) as source registers in the
336 // following instructions.
337 __ StoreToOffset(type, arm::R11, arm::SP, offset);
338 __ StoreToOffset(type, arm::R11, arm::R5, offset);
339
340 const char* expected =
341 "strd r0, r1, [sp, #1020]\n"
342 "strd r11, ip, [sp, #1020]\n"
343 "strd r11, ip, [r5, #1020]\n";
344 DriverStr(expected, "StoreWordPairToThumbOffset");
345}
346
347TEST_F(AssemblerThumb2Test, StoreWordPairToNonThumbOffset) {
348 arm::StoreOperandType type = arm::kStoreWordPair;
349 int32_t offset = 1024;
350 ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
351
352 __ StoreToOffset(type, arm::R0, arm::SP, offset);
353 // Same comment as in AssemblerThumb2Test.StoreWordPairToThumbOffset
354 // regarding the use of (R11, IP) (e.g. (R11, R12)) as source
355 // registers in the following instructions.
356 __ StoreToOffset(type, arm::R11, arm::SP, offset);
357 __ StoreToOffset(type, arm::R11, arm::R5, offset);
358
359 const char* expected =
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000360 "add.w ip, sp, #1024\n" // AddConstant(ip, sp, 1024)
Roland Levillain4af147e2015-04-07 13:54:49 +0100361 "strd r0, r1, [ip, #0]\n"
362
363 "str r5, [sp, #-4]!\n" // Push(r5)
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000364 "add.w r5, sp, #1024\n" // AddConstant(r5, sp, (1024 + kRegisterSize) & ~0x3fc)
365 "strd r11, ip, [r5, #4]\n" // StoreToOffset(type, r11, sp, (1024 + kRegisterSize) & 0x3fc)
Roland Levillain4af147e2015-04-07 13:54:49 +0100366 "ldr r5, [sp], #4\n" // Pop(r5)
367
368 "str r6, [sp, #-4]!\n" // Push(r6)
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000369 "add.w r6, r5, #1024\n" // AddConstant(r6, r5, 1024 & ~0x3fc)
370 "strd r11, ip, [r6, #0]\n" // StoreToOffset(type, r11, r6, 1024 & 0x3fc)
Roland Levillain4af147e2015-04-07 13:54:49 +0100371 "ldr r6, [sp], #4\n"; // Pop(r6)
372 DriverStr(expected, "StoreWordPairToNonThumbOffset");
373}
374
Vladimir Markof2bb0432016-04-25 12:43:50 +0000375TEST_F(AssemblerThumb2Test, DistantBackBranch) {
376 Label start, end;
377 __ Bind(&start);
378 constexpr size_t kLdrR0R0Count1 = 256;
379 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
380 __ ldr(arm::R0, arm::Address(arm::R0));
381 }
382 __ b(&end, arm::EQ);
383 __ b(&start, arm::LT);
384 constexpr size_t kLdrR0R0Count2 = 256;
385 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
386 __ ldr(arm::R0, arm::Address(arm::R0));
387 }
388 __ Bind(&end);
389
390 std::string expected =
391 "0:\n" +
392 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
393 "beq 1f\n"
394 "blt 0b\n" +
395 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
396 "1:\n";
397 DriverStr(expected, "DistantBackBranch");
398}
399
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000400TEST_F(AssemblerThumb2Test, TwoCbzMaxOffset) {
401 Label label0, label1, label2;
402 __ cbz(arm::R0, &label1);
403 constexpr size_t kLdrR0R0Count1 = 63;
404 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
405 __ ldr(arm::R0, arm::Address(arm::R0));
406 }
407 __ Bind(&label0);
408 __ cbz(arm::R0, &label2);
409 __ Bind(&label1);
410 constexpr size_t kLdrR0R0Count2 = 64;
411 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
412 __ ldr(arm::R0, arm::Address(arm::R0));
413 }
414 __ Bind(&label2);
415
416 std::string expected =
417 "cbz r0, 1f\n" + // cbz r0, label1
418 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
419 "0:\n"
420 "cbz r0, 2f\n" // cbz r0, label2
421 "1:\n" +
422 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
423 "2:\n";
424 DriverStr(expected, "TwoCbzMaxOffset");
425
426 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
427 __ GetAdjustedPosition(label0.Position()));
428 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 0u,
429 __ GetAdjustedPosition(label1.Position()));
430 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 0u,
431 __ GetAdjustedPosition(label2.Position()));
432}
433
434TEST_F(AssemblerThumb2Test, TwoCbzBeyondMaxOffset) {
435 Label label0, label1, label2;
436 __ cbz(arm::R0, &label1);
437 constexpr size_t kLdrR0R0Count1 = 63;
438 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
439 __ ldr(arm::R0, arm::Address(arm::R0));
440 }
441 __ Bind(&label0);
442 __ cbz(arm::R0, &label2);
443 __ Bind(&label1);
444 constexpr size_t kLdrR0R0Count2 = 65;
445 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
446 __ ldr(arm::R0, arm::Address(arm::R0));
447 }
448 __ Bind(&label2);
449
450 std::string expected =
451 "cmp r0, #0\n" // cbz r0, label1
452 "beq.n 1f\n" +
453 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
454 "0:\n"
455 "cmp r0, #0\n" // cbz r0, label2
456 "beq.n 2f\n"
457 "1:\n" +
458 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
459 "2:\n";
460 DriverStr(expected, "TwoCbzBeyondMaxOffset");
461
462 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
463 __ GetAdjustedPosition(label0.Position()));
464 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 4u,
465 __ GetAdjustedPosition(label1.Position()));
466 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 4u,
467 __ GetAdjustedPosition(label2.Position()));
468}
469
470TEST_F(AssemblerThumb2Test, TwoCbzSecondAtMaxB16Offset) {
471 Label label0, label1, label2;
472 __ cbz(arm::R0, &label1);
473 constexpr size_t kLdrR0R0Count1 = 62;
474 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
475 __ ldr(arm::R0, arm::Address(arm::R0));
476 }
477 __ Bind(&label0);
478 __ cbz(arm::R0, &label2);
479 __ Bind(&label1);
480 constexpr size_t kLdrR0R0Count2 = 128;
481 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
482 __ ldr(arm::R0, arm::Address(arm::R0));
483 }
484 __ Bind(&label2);
485
486 std::string expected =
487 "cbz r0, 1f\n" + // cbz r0, label1
488 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
489 "0:\n"
490 "cmp r0, #0\n" // cbz r0, label2
491 "beq.n 2f\n"
492 "1:\n" +
493 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
494 "2:\n";
495 DriverStr(expected, "TwoCbzSecondAtMaxB16Offset");
496
497 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
498 __ GetAdjustedPosition(label0.Position()));
499 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
500 __ GetAdjustedPosition(label1.Position()));
501 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
502 __ GetAdjustedPosition(label2.Position()));
503}
504
505TEST_F(AssemblerThumb2Test, TwoCbzSecondBeyondMaxB16Offset) {
506 Label label0, label1, label2;
507 __ cbz(arm::R0, &label1);
508 constexpr size_t kLdrR0R0Count1 = 62;
509 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
510 __ ldr(arm::R0, arm::Address(arm::R0));
511 }
512 __ Bind(&label0);
513 __ cbz(arm::R0, &label2);
514 __ Bind(&label1);
515 constexpr size_t kLdrR0R0Count2 = 129;
516 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
517 __ ldr(arm::R0, arm::Address(arm::R0));
518 }
519 __ Bind(&label2);
520
521 std::string expected =
522 "cmp r0, #0\n" // cbz r0, label1
523 "beq.n 1f\n" +
524 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
525 "0:\n"
526 "cmp r0, #0\n" // cbz r0, label2
527 "beq.w 2f\n"
528 "1:\n" +
529 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
530 "2:\n";
531 DriverStr(expected, "TwoCbzSecondBeyondMaxB16Offset");
532
533 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
534 __ GetAdjustedPosition(label0.Position()));
535 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
536 __ GetAdjustedPosition(label1.Position()));
537 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
538 __ GetAdjustedPosition(label2.Position()));
539}
540
541TEST_F(AssemblerThumb2Test, TwoCbzFirstAtMaxB16Offset) {
542 Label label0, label1, label2;
543 __ cbz(arm::R0, &label1);
544 constexpr size_t kLdrR0R0Count1 = 127;
545 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
546 __ ldr(arm::R0, arm::Address(arm::R0));
547 }
548 __ Bind(&label0);
549 __ cbz(arm::R0, &label2);
550 __ Bind(&label1);
551 constexpr size_t kLdrR0R0Count2 = 64;
552 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
553 __ ldr(arm::R0, arm::Address(arm::R0));
554 }
555 __ Bind(&label2);
556
557 std::string expected =
558 "cmp r0, #0\n" // cbz r0, label1
559 "beq.n 1f\n" +
560 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
561 "0:\n"
562 "cbz r0, 2f\n" // cbz r0, label2
563 "1:\n" +
564 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
565 "2:\n";
566 DriverStr(expected, "TwoCbzFirstAtMaxB16Offset");
567
568 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
569 __ GetAdjustedPosition(label0.Position()));
570 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
571 __ GetAdjustedPosition(label1.Position()));
572 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
573 __ GetAdjustedPosition(label2.Position()));
574}
575
576TEST_F(AssemblerThumb2Test, TwoCbzFirstBeyondMaxB16Offset) {
577 Label label0, label1, label2;
578 __ cbz(arm::R0, &label1);
579 constexpr size_t kLdrR0R0Count1 = 127;
580 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
581 __ ldr(arm::R0, arm::Address(arm::R0));
582 }
583 __ Bind(&label0);
584 __ cbz(arm::R0, &label2);
585 __ Bind(&label1);
586 constexpr size_t kLdrR0R0Count2 = 65;
587 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
588 __ ldr(arm::R0, arm::Address(arm::R0));
589 }
590 __ Bind(&label2);
591
592 std::string expected =
593 "cmp r0, #0\n" // cbz r0, label1
594 "beq.w 1f\n" +
595 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
596 "0:\n"
597 "cmp r0, #0\n" // cbz r0, label2
598 "beq.n 2f\n"
599 "1:\n" +
600 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
601 "2:\n";
602 DriverStr(expected, "TwoCbzFirstBeyondMaxB16Offset");
603
604 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 4u,
605 __ GetAdjustedPosition(label0.Position()));
606 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
607 __ GetAdjustedPosition(label1.Position()));
608 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
609 __ GetAdjustedPosition(label2.Position()));
610}
611
612TEST_F(AssemblerThumb2Test, LoadLiteralMax1KiB) {
613 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
614 __ LoadLiteral(arm::R0, literal);
615 Label label;
616 __ Bind(&label);
617 constexpr size_t kLdrR0R0Count = 511;
618 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
619 __ ldr(arm::R0, arm::Address(arm::R0));
620 }
621
622 std::string expected =
623 "1:\n"
624 "ldr.n r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
625 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
626 ".align 2, 0\n"
627 "2:\n"
628 ".word 0x12345678\n";
629 DriverStr(expected, "LoadLiteralMax1KiB");
630
631 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
632 __ GetAdjustedPosition(label.Position()));
633}
634
635TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiB) {
636 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
637 __ LoadLiteral(arm::R0, literal);
638 Label label;
639 __ Bind(&label);
640 constexpr size_t kLdrR0R0Count = 512;
641 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
642 __ ldr(arm::R0, arm::Address(arm::R0));
643 }
644
645 std::string expected =
646 "1:\n"
647 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
648 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
649 ".align 2, 0\n"
650 "2:\n"
651 ".word 0x12345678\n";
652 DriverStr(expected, "LoadLiteralBeyondMax1KiB");
653
654 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
655 __ GetAdjustedPosition(label.Position()));
656}
657
658TEST_F(AssemblerThumb2Test, LoadLiteralMax4KiB) {
659 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
660 __ LoadLiteral(arm::R1, literal);
661 Label label;
662 __ Bind(&label);
663 constexpr size_t kLdrR0R0Count = 2046;
664 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
665 __ ldr(arm::R0, arm::Address(arm::R0));
666 }
667
668 std::string expected =
669 "1:\n"
670 "ldr.w r1, [pc, #((2f - 1b - 2) & ~2)]\n" +
671 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
672 ".align 2, 0\n"
673 "2:\n"
674 ".word 0x12345678\n";
675 DriverStr(expected, "LoadLiteralMax4KiB");
676
677 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
678 __ GetAdjustedPosition(label.Position()));
679}
680
681TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax4KiB) {
682 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
683 __ LoadLiteral(arm::R1, literal);
684 Label label;
685 __ Bind(&label);
686 constexpr size_t kLdrR0R0Count = 2047;
687 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
688 __ ldr(arm::R0, arm::Address(arm::R0));
689 }
690
691 std::string expected =
692 "movw r1, #4096\n" // "as" does not consider (2f - 1f - 4) a constant expression for movw.
693 "1:\n"
694 "add r1, pc\n"
695 "ldr r1, [r1, #0]\n" +
696 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
697 ".align 2, 0\n"
698 "2:\n"
699 ".word 0x12345678\n";
700 DriverStr(expected, "LoadLiteralBeyondMax4KiB");
701
702 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
703 __ GetAdjustedPosition(label.Position()));
704}
705
706TEST_F(AssemblerThumb2Test, LoadLiteralMax64KiB) {
707 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
708 __ LoadLiteral(arm::R1, literal);
709 Label label;
710 __ Bind(&label);
711 constexpr size_t kLdrR0R0Count = (1u << 15) - 2u;
712 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
713 __ ldr(arm::R0, arm::Address(arm::R0));
714 }
715
716 std::string expected =
717 "movw r1, #0xfffc\n" // "as" does not consider (2f - 1f - 4) a constant expression for movw.
718 "1:\n"
719 "add r1, pc\n"
720 "ldr r1, [r1, #0]\n" +
721 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
722 ".align 2, 0\n"
723 "2:\n"
724 ".word 0x12345678\n";
725 DriverStr(expected, "LoadLiteralMax64KiB");
726
727 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
728 __ GetAdjustedPosition(label.Position()));
729}
730
731TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax64KiB) {
732 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
733 __ LoadLiteral(arm::R1, literal);
734 Label label;
735 __ Bind(&label);
736 constexpr size_t kLdrR0R0Count = (1u << 15) - 1u;
737 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
738 __ ldr(arm::R0, arm::Address(arm::R0));
739 }
740
741 std::string expected =
742 "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n"
743 "1:\n"
744 "add r1, pc\n"
745 "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" +
746 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
747 ".align 2, 0\n"
748 "2:\n"
749 ".word 0x12345678\n";
750 DriverStr(expected, "LoadLiteralBeyondMax64KiB");
751
752 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
753 __ GetAdjustedPosition(label.Position()));
754}
755
756TEST_F(AssemblerThumb2Test, LoadLiteralMax1MiB) {
757 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
758 __ LoadLiteral(arm::R1, literal);
759 Label label;
760 __ Bind(&label);
761 constexpr size_t kLdrR0R0Count = (1u << 19) - 3u;
762 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
763 __ ldr(arm::R0, arm::Address(arm::R0));
764 }
765
766 std::string expected =
767 "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n"
768 "1:\n"
769 "add r1, pc\n"
770 "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" +
771 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
772 ".align 2, 0\n"
773 "2:\n"
774 ".word 0x12345678\n";
775 DriverStr(expected, "LoadLiteralMax1MiB");
776
777 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
778 __ GetAdjustedPosition(label.Position()));
779}
780
781TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1MiB) {
782 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
783 __ LoadLiteral(arm::R1, literal);
784 Label label;
785 __ Bind(&label);
786 constexpr size_t kLdrR0R0Count = (1u << 19) - 2u;
787 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
788 __ ldr(arm::R0, arm::Address(arm::R0));
789 }
790
791 std::string expected =
792 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
793 "movw r1, #(0x100000 & 0xffff)\n"
794 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
795 "movt r1, #(0x100000 >> 16)\n"
796 "1:\n"
797 "add r1, pc\n"
798 "ldr.w r1, [r1, #0]\n" +
799 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
800 ".align 2, 0\n"
801 "2:\n"
802 ".word 0x12345678\n";
803 DriverStr(expected, "LoadLiteralBeyondMax1MiB");
804
805 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
806 __ GetAdjustedPosition(label.Position()));
807}
808
809TEST_F(AssemblerThumb2Test, LoadLiteralFar) {
810 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
811 __ LoadLiteral(arm::R1, literal);
812 Label label;
813 __ Bind(&label);
814 constexpr size_t kLdrR0R0Count = (1u << 19) - 2u + 0x1234;
815 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
816 __ ldr(arm::R0, arm::Address(arm::R0));
817 }
818
819 std::string expected =
820 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
821 "movw r1, #((0x100000 + 2 * 0x1234) & 0xffff)\n"
822 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
823 "movt r1, #((0x100000 + 2 * 0x1234) >> 16)\n"
824 "1:\n"
825 "add r1, pc\n"
826 "ldr.w r1, [r1, #0]\n" +
827 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
828 ".align 2, 0\n"
829 "2:\n"
830 ".word 0x12345678\n";
831 DriverStr(expected, "LoadLiteralFar");
832
833 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
834 __ GetAdjustedPosition(label.Position()));
835}
836
837TEST_F(AssemblerThumb2Test, LoadLiteralWideMax1KiB) {
838 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
839 __ LoadLiteral(arm::R1, arm::R3, literal);
840 Label label;
841 __ Bind(&label);
842 constexpr size_t kLdrR0R0Count = 510;
843 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
844 __ ldr(arm::R0, arm::Address(arm::R0));
845 }
846
847 std::string expected =
848 "1:\n"
849 "ldrd r1, r3, [pc, #((2f - 1b - 2) & ~2)]\n" +
850 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
851 ".align 2, 0\n"
852 "2:\n"
853 ".word 0x87654321\n"
854 ".word 0x12345678\n";
855 DriverStr(expected, "LoadLiteralWideMax1KiB");
856
857 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
858 __ GetAdjustedPosition(label.Position()));
859}
860
861TEST_F(AssemblerThumb2Test, LoadLiteralWideBeyondMax1KiB) {
862 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
863 __ LoadLiteral(arm::R1, arm::R3, literal);
864 Label label;
865 __ Bind(&label);
866 constexpr size_t kLdrR0R0Count = 511;
867 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
868 __ ldr(arm::R0, arm::Address(arm::R0));
869 }
870
871 std::string expected =
872 "mov.w ip, #((2f - 1f - 4) & ~0x3ff)\n"
873 "1:\n"
874 "add ip, pc\n"
875 "ldrd r1, r3, [ip, #((2f - 1b - 4) & 0x3ff)]\n" +
876 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
877 ".align 2, 0\n"
878 "2:\n"
879 ".word 0x87654321\n"
880 ".word 0x12345678\n";
881 DriverStr(expected, "LoadLiteralWideBeyondMax1KiB");
882
883 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
884 __ GetAdjustedPosition(label.Position()));
885}
886
887TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax256KiB) {
888 // The literal size must match but the type doesn't, so use an int32_t rather than float.
889 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
890 __ LoadLiteral(arm::S3, literal);
891 Label label;
892 __ Bind(&label);
893 constexpr size_t kLdrR0R0Count = (1 << 17) - 3u;
894 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
895 __ ldr(arm::R0, arm::Address(arm::R0));
896 }
897
898 std::string expected =
899 "mov.w ip, #((2f - 1f - 4) & ~0x3ff)\n"
900 "1:\n"
901 "add ip, pc\n"
902 "vldr s3, [ip, #((2f - 1b - 4) & 0x3ff)]\n" +
903 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
904 ".align 2, 0\n"
905 "2:\n"
906 ".word 0x12345678\n";
907 DriverStr(expected, "LoadLiteralSingleMax256KiB");
908
909 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
910 __ GetAdjustedPosition(label.Position()));
911}
912
913TEST_F(AssemblerThumb2Test, LoadLiteralDoubleBeyondMax256KiB) {
914 // The literal size must match but the type doesn't, so use an int64_t rather than double.
915 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
916 __ LoadLiteral(arm::D3, literal);
917 Label label;
918 __ Bind(&label);
919 constexpr size_t kLdrR0R0Count = (1 << 17) - 2u;
920 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
921 __ ldr(arm::R0, arm::Address(arm::R0));
922 }
923
924 std::string expected =
925 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
926 "movw ip, #(0x40000 & 0xffff)\n"
927 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
928 "movt ip, #(0x40000 >> 16)\n"
929 "1:\n"
930 "add ip, pc\n"
931 "vldr d3, [ip, #0]\n" +
932 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
933 ".align 2, 0\n"
934 "2:\n"
935 ".word 0x87654321\n"
936 ".word 0x12345678\n";
937 DriverStr(expected, "LoadLiteralDoubleBeyondMax256KiB");
938
939 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
940 __ GetAdjustedPosition(label.Position()));
941}
942
943TEST_F(AssemblerThumb2Test, LoadLiteralDoubleFar) {
944 // The literal size must match but the type doesn't, so use an int64_t rather than double.
945 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
946 __ LoadLiteral(arm::D3, literal);
947 Label label;
948 __ Bind(&label);
949 constexpr size_t kLdrR0R0Count = (1 << 17) - 2u + 0x1234;
950 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
951 __ ldr(arm::R0, arm::Address(arm::R0));
952 }
953
954 std::string expected =
955 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
956 "movw ip, #((0x40000 + 2 * 0x1234) & 0xffff)\n"
957 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
958 "movt ip, #((0x40000 + 2 * 0x1234) >> 16)\n"
959 "1:\n"
960 "add ip, pc\n"
961 "vldr d3, [ip, #0]\n" +
962 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
963 ".align 2, 0\n"
964 "2:\n"
965 ".word 0x87654321\n"
966 ".word 0x12345678\n";
967 DriverStr(expected, "LoadLiteralDoubleFar");
968
969 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
970 __ GetAdjustedPosition(label.Position()));
971}
972
Vladimir Marko663c9342015-07-22 11:28:14 +0100973TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass) {
974 // First part: as TwoCbzBeyondMaxOffset but add one 16-bit instruction to the end,
975 // so that the size is not Aligned<4>(.). On the first pass, the assembler resizes
976 // the second CBZ because it's out of range, then it will resize the first CBZ
977 // which has been pushed out of range. Thus, after the first pass, the code size
978 // will appear Aligned<4>(.) but the final size will not be.
979 Label label0, label1, label2;
980 __ cbz(arm::R0, &label1);
981 constexpr size_t kLdrR0R0Count1 = 63;
982 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
983 __ ldr(arm::R0, arm::Address(arm::R0));
984 }
985 __ Bind(&label0);
986 __ cbz(arm::R0, &label2);
987 __ Bind(&label1);
988 constexpr size_t kLdrR0R0Count2 = 65;
989 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
990 __ ldr(arm::R0, arm::Address(arm::R0));
991 }
992 __ Bind(&label2);
993 __ ldr(arm::R0, arm::Address(arm::R0));
994
995 std::string expected_part1 =
996 "cmp r0, #0\n" // cbz r0, label1
997 "beq.n 1f\n" +
998 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
999 "0:\n"
1000 "cmp r0, #0\n" // cbz r0, label2
1001 "beq.n 2f\n"
1002 "1:\n" +
1003 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1004 "2:\n" // Here the offset is Aligned<4>(.).
1005 "ldr r0, [r0]\n"; // Make the first part
1006
1007 // Second part: as LoadLiteralMax1KiB with the caveat that the offset of the load
1008 // literal will not be Aligned<4>(.) but it will appear to be when we process the
1009 // instruction during the first pass, so the literal will need a padding and it
1010 // will push the literal out of range, so we shall end up with "ldr.w".
1011 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
1012 __ LoadLiteral(arm::R0, 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 =
1021 expected_part1 +
1022 "1:\n"
1023 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
1024 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1025 ".align 2, 0\n"
1026 "2:\n"
1027 ".word 0x12345678\n";
1028 DriverStr(expected, "LoadLiteralMax1KiB");
1029
1030 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
1031 __ GetAdjustedPosition(label.Position()));
1032}
1033
Andreas Gampe7cffc3b2015-10-19 21:31:53 -07001034TEST_F(AssemblerThumb2Test, BindTrackedLabel) {
1035 Label non_tracked, tracked, branch_target;
1036
1037 // A few dummy loads on entry.
1038 constexpr size_t kLdrR0R0Count = 5;
1039 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1040 __ ldr(arm::R0, arm::Address(arm::R0));
1041 }
1042
1043 // A branch that will need to be fixed up.
1044 __ cbz(arm::R0, &branch_target);
1045
1046 // Some more dummy loads.
1047 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1048 __ ldr(arm::R0, arm::Address(arm::R0));
1049 }
1050
1051 // Now insert tracked and untracked label.
1052 __ Bind(&non_tracked);
1053 __ BindTrackedLabel(&tracked);
1054
1055 // A lot of dummy loads, to ensure the branch needs resizing.
1056 constexpr size_t kLdrR0R0CountLong = 60;
1057 for (size_t i = 0; i != kLdrR0R0CountLong; ++i) {
1058 __ ldr(arm::R0, arm::Address(arm::R0));
1059 }
1060
1061 // Bind the branch target.
1062 __ Bind(&branch_target);
1063
1064 // One more load.
1065 __ ldr(arm::R0, arm::Address(arm::R0));
1066
1067 std::string expected =
1068 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1069 "cmp r0, #0\n" // cbz r0, 1f
1070 "beq.n 1f\n" +
1071 RepeatInsn(kLdrR0R0Count + kLdrR0R0CountLong, "ldr r0, [r0]\n") +
1072 "1:\n"
1073 "ldr r0, [r0]\n";
1074 DriverStr(expected, "BindTrackedLabel");
1075
1076 // Expectation is that the tracked label should have moved.
1077 EXPECT_LT(non_tracked.Position(), tracked.Position());
1078}
1079
1080TEST_F(AssemblerThumb2Test, JumpTable) {
1081 // The jump table. Use three labels.
1082 Label label1, label2, label3;
1083 std::vector<Label*> labels({ &label1, &label2, &label3 });
1084
1085 // A few dummy loads on entry, interspersed with 2 labels.
1086 constexpr size_t kLdrR0R0Count = 5;
1087 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1088 __ ldr(arm::R0, arm::Address(arm::R0));
1089 }
1090 __ BindTrackedLabel(&label1);
1091 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1092 __ ldr(arm::R0, arm::Address(arm::R0));
1093 }
1094 __ BindTrackedLabel(&label2);
1095 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1096 __ ldr(arm::R0, arm::Address(arm::R0));
1097 }
1098
1099 // Create the jump table, emit the base load.
1100 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1101
1102 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1103 // it's being used.
1104 __ ldr(arm::R0, arm::Address(arm::R0));
1105
1106 // Emit the jump
1107 __ EmitJumpTableDispatch(jump_table, arm::R1);
1108
1109 // Some more dummy instructions.
1110 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1111 __ ldr(arm::R0, arm::Address(arm::R0));
1112 }
1113 __ BindTrackedLabel(&label3);
1114 for (size_t i = 0; i != kLdrR0R0Count; ++i) { // Note: odd so there's no alignment
1115 __ ldr(arm::R0, arm::Address(arm::R0)); // necessary, as gcc as emits nops,
1116 } // whereas we emit 0 != nop.
1117
1118 static_assert((kLdrR0R0Count + 3) * 2 < 1 * KB, "Too much offset");
1119
1120 std::string expected =
1121 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1122 ".L1:\n" +
1123 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1124 ".L2:\n" +
1125 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1126 "adr r1, .Ljump_table\n"
1127 "ldr r0, [r0]\n"
1128 ".Lbase:\n"
1129 "add pc, r1\n" +
1130 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1131 ".L3:\n" +
1132 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1133 ".align 2\n"
1134 ".Ljump_table:\n"
1135 ".4byte (.L1 - .Lbase - 4)\n"
1136 ".4byte (.L2 - .Lbase - 4)\n"
1137 ".4byte (.L3 - .Lbase - 4)\n";
1138 DriverStr(expected, "JumpTable");
1139}
1140
1141// Test for >1K fixup.
1142TEST_F(AssemblerThumb2Test, JumpTable4K) {
1143 // The jump table. Use three labels.
1144 Label label1, label2, label3;
1145 std::vector<Label*> labels({ &label1, &label2, &label3 });
1146
1147 // A few dummy loads on entry, interspersed with 2 labels.
1148 constexpr size_t kLdrR0R0Count = 5;
1149 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1150 __ ldr(arm::R0, arm::Address(arm::R0));
1151 }
1152 __ BindTrackedLabel(&label1);
1153 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1154 __ ldr(arm::R0, arm::Address(arm::R0));
1155 }
1156 __ BindTrackedLabel(&label2);
1157 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1158 __ ldr(arm::R0, arm::Address(arm::R0));
1159 }
1160
1161 // Create the jump table, emit the base load.
1162 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1163
1164 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1165 // it's being used.
1166 __ ldr(arm::R0, arm::Address(arm::R0));
1167
1168 // Emit the jump
1169 __ EmitJumpTableDispatch(jump_table, arm::R1);
1170
1171 // Some more dummy instructions.
1172 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1173 __ ldr(arm::R0, arm::Address(arm::R0));
1174 }
1175 __ BindTrackedLabel(&label3);
1176 constexpr size_t kLdrR0R0Count2 = 600; // Note: even so there's no alignment
1177 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1178 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1179 }
1180
1181 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 1 * KB, "Not enough offset");
1182 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 4 * KB, "Too much offset");
1183
1184 std::string expected =
1185 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1186 ".L1:\n" +
1187 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1188 ".L2:\n" +
1189 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1190 "adr r1, .Ljump_table\n"
1191 "ldr r0, [r0]\n"
1192 ".Lbase:\n"
1193 "add pc, r1\n" +
1194 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1195 ".L3:\n" +
1196 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1197 ".align 2\n"
1198 ".Ljump_table:\n"
1199 ".4byte (.L1 - .Lbase - 4)\n"
1200 ".4byte (.L2 - .Lbase - 4)\n"
1201 ".4byte (.L3 - .Lbase - 4)\n";
1202 DriverStr(expected, "JumpTable4K");
1203}
1204
1205// Test for >4K fixup.
1206TEST_F(AssemblerThumb2Test, JumpTable64K) {
1207 // The jump table. Use three labels.
1208 Label label1, label2, label3;
1209 std::vector<Label*> labels({ &label1, &label2, &label3 });
1210
1211 // A few dummy loads on entry, interspersed with 2 labels.
1212 constexpr size_t kLdrR0R0Count = 5;
1213 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1214 __ ldr(arm::R0, arm::Address(arm::R0));
1215 }
1216 __ BindTrackedLabel(&label1);
1217 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1218 __ ldr(arm::R0, arm::Address(arm::R0));
1219 }
1220 __ BindTrackedLabel(&label2);
1221 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1222 __ ldr(arm::R0, arm::Address(arm::R0));
1223 }
1224
1225 // Create the jump table, emit the base load.
1226 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1227
1228 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1229 // it's being used.
1230 __ ldr(arm::R0, arm::Address(arm::R0));
1231
1232 // Emit the jump
1233 __ EmitJumpTableDispatch(jump_table, arm::R1);
1234
1235 // Some more dummy instructions.
1236 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1237 __ ldr(arm::R0, arm::Address(arm::R0));
1238 }
1239 __ BindTrackedLabel(&label3);
1240 constexpr size_t kLdrR0R0Count2 = 2601; // Note: odd so there's no alignment
1241 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1242 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1243 }
1244
1245 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 4 * KB, "Not enough offset");
1246 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 64 * KB, "Too much offset");
1247
1248 std::string expected =
1249 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1250 ".L1:\n" +
1251 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1252 ".L2:\n" +
1253 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1254 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
1255 // (Note: have to use constants, as labels aren't accepted.
1256 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1257 ") * 2 - 4) & 0xFFFF)\n"
1258 "add r1, pc\n"
1259 "ldr r0, [r0]\n"
1260 ".Lbase:\n"
1261 "add pc, r1\n" +
1262 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1263 ".L3:\n" +
1264 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1265 ".align 2\n"
1266 ".Ljump_table:\n"
1267 ".4byte (.L1 - .Lbase - 4)\n"
1268 ".4byte (.L2 - .Lbase - 4)\n"
1269 ".4byte (.L3 - .Lbase - 4)\n";
1270 DriverStr(expected, "JumpTable64K");
1271}
1272
1273// Test for >64K fixup.
1274TEST_F(AssemblerThumb2Test, JumpTableFar) {
1275 // The jump table. Use three labels.
1276 Label label1, label2, label3;
1277 std::vector<Label*> labels({ &label1, &label2, &label3 });
1278
1279 // A few dummy loads on entry, interspersed with 2 labels.
1280 constexpr size_t kLdrR0R0Count = 5;
1281 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1282 __ ldr(arm::R0, arm::Address(arm::R0));
1283 }
1284 __ BindTrackedLabel(&label1);
1285 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1286 __ ldr(arm::R0, arm::Address(arm::R0));
1287 }
1288 __ BindTrackedLabel(&label2);
1289 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1290 __ ldr(arm::R0, arm::Address(arm::R0));
1291 }
1292
1293 // Create the jump table, emit the base load.
1294 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1295
1296 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1297 // it's being used.
1298 __ ldr(arm::R0, arm::Address(arm::R0));
1299
1300 // Emit the jump
1301 __ EmitJumpTableDispatch(jump_table, arm::R1);
1302
1303 // Some more dummy instructions.
1304 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1305 __ ldr(arm::R0, arm::Address(arm::R0));
1306 }
1307 __ BindTrackedLabel(&label3);
1308 constexpr size_t kLdrR0R0Count2 = 70001; // Note: odd so there's no alignment
1309 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1310 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1311 }
1312
1313 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 64 * KB, "Not enough offset");
1314
1315 std::string expected =
1316 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1317 ".L1:\n" +
1318 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1319 ".L2:\n" +
1320 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1321 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
1322 // (Note: have to use constants, as labels aren't accepted.
1323 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1324 ") * 2 - 4) & 0xFFFF)\n"
1325 "movt r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1326 ") * 2 - 4) >> 16)\n"
1327 ".Lhelp:"
1328 "add r1, pc\n"
1329 "ldr r0, [r0]\n"
1330 ".Lbase:\n"
1331 "add pc, r1\n" +
1332 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1333 ".L3:\n" +
1334 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1335 ".align 2\n"
1336 ".Ljump_table:\n"
1337 ".4byte (.L1 - .Lbase - 4)\n"
1338 ".4byte (.L2 - .Lbase - 4)\n"
1339 ".4byte (.L3 - .Lbase - 4)\n";
1340 DriverStr(expected, "JumpTableFar");
1341}
1342
Scott Wakeling611d3392015-07-10 11:42:06 +01001343TEST_F(AssemblerThumb2Test, Clz) {
1344 __ clz(arm::R0, arm::R1);
1345
1346 const char* expected = "clz r0, r1\n";
1347
1348 DriverStr(expected, "clz");
1349}
1350
Scott Wakeling9ee23f42015-07-23 10:44:35 +01001351TEST_F(AssemblerThumb2Test, rbit) {
1352 __ rbit(arm::R1, arm::R0);
1353
1354 const char* expected = "rbit r1, r0\n";
1355
1356 DriverStr(expected, "rbit");
1357}
1358
Artem Serovc257da72016-02-02 13:49:43 +00001359TEST_F(AssemblerThumb2Test, rev) {
1360 __ rev(arm::R1, arm::R0);
1361
1362 const char* expected = "rev r1, r0\n";
1363
1364 DriverStr(expected, "rev");
1365}
1366
1367TEST_F(AssemblerThumb2Test, rev16) {
1368 __ rev16(arm::R1, arm::R0);
1369
1370 const char* expected = "rev16 r1, r0\n";
1371
1372 DriverStr(expected, "rev16");
1373}
1374
1375TEST_F(AssemblerThumb2Test, revsh) {
1376 __ revsh(arm::R1, arm::R0);
1377
1378 const char* expected = "revsh r1, r0\n";
1379
1380 DriverStr(expected, "revsh");
1381}
1382
Roland Levillain1a28fc42014-11-13 18:03:06 +00001383} // namespace art