blob: 7b32b0fd26e9ad23d106f1b37e46d18581c6b025 [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 Markocf93a5c2015-06-16 11:33:24 +0000375TEST_F(AssemblerThumb2Test, TwoCbzMaxOffset) {
376 Label label0, label1, label2;
377 __ cbz(arm::R0, &label1);
378 constexpr size_t kLdrR0R0Count1 = 63;
379 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
380 __ ldr(arm::R0, arm::Address(arm::R0));
381 }
382 __ Bind(&label0);
383 __ cbz(arm::R0, &label2);
384 __ Bind(&label1);
385 constexpr size_t kLdrR0R0Count2 = 64;
386 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
387 __ ldr(arm::R0, arm::Address(arm::R0));
388 }
389 __ Bind(&label2);
390
391 std::string expected =
392 "cbz r0, 1f\n" + // cbz r0, label1
393 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
394 "0:\n"
395 "cbz r0, 2f\n" // cbz r0, label2
396 "1:\n" +
397 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
398 "2:\n";
399 DriverStr(expected, "TwoCbzMaxOffset");
400
401 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
402 __ GetAdjustedPosition(label0.Position()));
403 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 0u,
404 __ GetAdjustedPosition(label1.Position()));
405 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 0u,
406 __ GetAdjustedPosition(label2.Position()));
407}
408
409TEST_F(AssemblerThumb2Test, TwoCbzBeyondMaxOffset) {
410 Label label0, label1, label2;
411 __ cbz(arm::R0, &label1);
412 constexpr size_t kLdrR0R0Count1 = 63;
413 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
414 __ ldr(arm::R0, arm::Address(arm::R0));
415 }
416 __ Bind(&label0);
417 __ cbz(arm::R0, &label2);
418 __ Bind(&label1);
419 constexpr size_t kLdrR0R0Count2 = 65;
420 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
421 __ ldr(arm::R0, arm::Address(arm::R0));
422 }
423 __ Bind(&label2);
424
425 std::string expected =
426 "cmp r0, #0\n" // cbz r0, label1
427 "beq.n 1f\n" +
428 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
429 "0:\n"
430 "cmp r0, #0\n" // cbz r0, label2
431 "beq.n 2f\n"
432 "1:\n" +
433 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
434 "2:\n";
435 DriverStr(expected, "TwoCbzBeyondMaxOffset");
436
437 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
438 __ GetAdjustedPosition(label0.Position()));
439 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 4u,
440 __ GetAdjustedPosition(label1.Position()));
441 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 4u,
442 __ GetAdjustedPosition(label2.Position()));
443}
444
445TEST_F(AssemblerThumb2Test, TwoCbzSecondAtMaxB16Offset) {
446 Label label0, label1, label2;
447 __ cbz(arm::R0, &label1);
448 constexpr size_t kLdrR0R0Count1 = 62;
449 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
450 __ ldr(arm::R0, arm::Address(arm::R0));
451 }
452 __ Bind(&label0);
453 __ cbz(arm::R0, &label2);
454 __ Bind(&label1);
455 constexpr size_t kLdrR0R0Count2 = 128;
456 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
457 __ ldr(arm::R0, arm::Address(arm::R0));
458 }
459 __ Bind(&label2);
460
461 std::string expected =
462 "cbz r0, 1f\n" + // cbz r0, label1
463 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
464 "0:\n"
465 "cmp r0, #0\n" // cbz r0, label2
466 "beq.n 2f\n"
467 "1:\n" +
468 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
469 "2:\n";
470 DriverStr(expected, "TwoCbzSecondAtMaxB16Offset");
471
472 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
473 __ GetAdjustedPosition(label0.Position()));
474 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
475 __ GetAdjustedPosition(label1.Position()));
476 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
477 __ GetAdjustedPosition(label2.Position()));
478}
479
480TEST_F(AssemblerThumb2Test, TwoCbzSecondBeyondMaxB16Offset) {
481 Label label0, label1, label2;
482 __ cbz(arm::R0, &label1);
483 constexpr size_t kLdrR0R0Count1 = 62;
484 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
485 __ ldr(arm::R0, arm::Address(arm::R0));
486 }
487 __ Bind(&label0);
488 __ cbz(arm::R0, &label2);
489 __ Bind(&label1);
490 constexpr size_t kLdrR0R0Count2 = 129;
491 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
492 __ ldr(arm::R0, arm::Address(arm::R0));
493 }
494 __ Bind(&label2);
495
496 std::string expected =
497 "cmp r0, #0\n" // cbz r0, label1
498 "beq.n 1f\n" +
499 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
500 "0:\n"
501 "cmp r0, #0\n" // cbz r0, label2
502 "beq.w 2f\n"
503 "1:\n" +
504 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
505 "2:\n";
506 DriverStr(expected, "TwoCbzSecondBeyondMaxB16Offset");
507
508 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
509 __ GetAdjustedPosition(label0.Position()));
510 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
511 __ GetAdjustedPosition(label1.Position()));
512 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
513 __ GetAdjustedPosition(label2.Position()));
514}
515
516TEST_F(AssemblerThumb2Test, TwoCbzFirstAtMaxB16Offset) {
517 Label label0, label1, label2;
518 __ cbz(arm::R0, &label1);
519 constexpr size_t kLdrR0R0Count1 = 127;
520 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
521 __ ldr(arm::R0, arm::Address(arm::R0));
522 }
523 __ Bind(&label0);
524 __ cbz(arm::R0, &label2);
525 __ Bind(&label1);
526 constexpr size_t kLdrR0R0Count2 = 64;
527 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
528 __ ldr(arm::R0, arm::Address(arm::R0));
529 }
530 __ Bind(&label2);
531
532 std::string expected =
533 "cmp r0, #0\n" // cbz r0, label1
534 "beq.n 1f\n" +
535 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
536 "0:\n"
537 "cbz r0, 2f\n" // cbz r0, label2
538 "1:\n" +
539 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
540 "2:\n";
541 DriverStr(expected, "TwoCbzFirstAtMaxB16Offset");
542
543 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
544 __ GetAdjustedPosition(label0.Position()));
545 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
546 __ GetAdjustedPosition(label1.Position()));
547 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
548 __ GetAdjustedPosition(label2.Position()));
549}
550
551TEST_F(AssemblerThumb2Test, TwoCbzFirstBeyondMaxB16Offset) {
552 Label label0, label1, label2;
553 __ cbz(arm::R0, &label1);
554 constexpr size_t kLdrR0R0Count1 = 127;
555 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
556 __ ldr(arm::R0, arm::Address(arm::R0));
557 }
558 __ Bind(&label0);
559 __ cbz(arm::R0, &label2);
560 __ Bind(&label1);
561 constexpr size_t kLdrR0R0Count2 = 65;
562 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
563 __ ldr(arm::R0, arm::Address(arm::R0));
564 }
565 __ Bind(&label2);
566
567 std::string expected =
568 "cmp r0, #0\n" // cbz r0, label1
569 "beq.w 1f\n" +
570 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
571 "0:\n"
572 "cmp r0, #0\n" // cbz r0, label2
573 "beq.n 2f\n"
574 "1:\n" +
575 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
576 "2:\n";
577 DriverStr(expected, "TwoCbzFirstBeyondMaxB16Offset");
578
579 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 4u,
580 __ GetAdjustedPosition(label0.Position()));
581 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
582 __ GetAdjustedPosition(label1.Position()));
583 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
584 __ GetAdjustedPosition(label2.Position()));
585}
586
587TEST_F(AssemblerThumb2Test, LoadLiteralMax1KiB) {
588 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
589 __ LoadLiteral(arm::R0, literal);
590 Label label;
591 __ Bind(&label);
592 constexpr size_t kLdrR0R0Count = 511;
593 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
594 __ ldr(arm::R0, arm::Address(arm::R0));
595 }
596
597 std::string expected =
598 "1:\n"
599 "ldr.n r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
600 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
601 ".align 2, 0\n"
602 "2:\n"
603 ".word 0x12345678\n";
604 DriverStr(expected, "LoadLiteralMax1KiB");
605
606 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
607 __ GetAdjustedPosition(label.Position()));
608}
609
610TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiB) {
611 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
612 __ LoadLiteral(arm::R0, literal);
613 Label label;
614 __ Bind(&label);
615 constexpr size_t kLdrR0R0Count = 512;
616 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
617 __ ldr(arm::R0, arm::Address(arm::R0));
618 }
619
620 std::string expected =
621 "1:\n"
622 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
623 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
624 ".align 2, 0\n"
625 "2:\n"
626 ".word 0x12345678\n";
627 DriverStr(expected, "LoadLiteralBeyondMax1KiB");
628
629 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
630 __ GetAdjustedPosition(label.Position()));
631}
632
633TEST_F(AssemblerThumb2Test, LoadLiteralMax4KiB) {
634 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
635 __ LoadLiteral(arm::R1, literal);
636 Label label;
637 __ Bind(&label);
638 constexpr size_t kLdrR0R0Count = 2046;
639 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
640 __ ldr(arm::R0, arm::Address(arm::R0));
641 }
642
643 std::string expected =
644 "1:\n"
645 "ldr.w r1, [pc, #((2f - 1b - 2) & ~2)]\n" +
646 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
647 ".align 2, 0\n"
648 "2:\n"
649 ".word 0x12345678\n";
650 DriverStr(expected, "LoadLiteralMax4KiB");
651
652 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
653 __ GetAdjustedPosition(label.Position()));
654}
655
656TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax4KiB) {
657 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
658 __ LoadLiteral(arm::R1, literal);
659 Label label;
660 __ Bind(&label);
661 constexpr size_t kLdrR0R0Count = 2047;
662 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
663 __ ldr(arm::R0, arm::Address(arm::R0));
664 }
665
666 std::string expected =
667 "movw r1, #4096\n" // "as" does not consider (2f - 1f - 4) a constant expression for movw.
668 "1:\n"
669 "add r1, pc\n"
670 "ldr r1, [r1, #0]\n" +
671 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
672 ".align 2, 0\n"
673 "2:\n"
674 ".word 0x12345678\n";
675 DriverStr(expected, "LoadLiteralBeyondMax4KiB");
676
677 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
678 __ GetAdjustedPosition(label.Position()));
679}
680
681TEST_F(AssemblerThumb2Test, LoadLiteralMax64KiB) {
682 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
683 __ LoadLiteral(arm::R1, literal);
684 Label label;
685 __ Bind(&label);
686 constexpr size_t kLdrR0R0Count = (1u << 15) - 2u;
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, #0xfffc\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, "LoadLiteralMax64KiB");
701
702 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
703 __ GetAdjustedPosition(label.Position()));
704}
705
706TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax64KiB) {
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) - 1u;
712 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
713 __ ldr(arm::R0, arm::Address(arm::R0));
714 }
715
716 std::string expected =
717 "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n"
718 "1:\n"
719 "add r1, pc\n"
720 "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" +
721 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
722 ".align 2, 0\n"
723 "2:\n"
724 ".word 0x12345678\n";
725 DriverStr(expected, "LoadLiteralBeyondMax64KiB");
726
727 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
728 __ GetAdjustedPosition(label.Position()));
729}
730
731TEST_F(AssemblerThumb2Test, LoadLiteralMax1MiB) {
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 << 19) - 3u;
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, "LoadLiteralMax1MiB");
751
752 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
753 __ GetAdjustedPosition(label.Position()));
754}
755
756TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1MiB) {
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) - 2u;
762 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
763 __ ldr(arm::R0, arm::Address(arm::R0));
764 }
765
766 std::string expected =
767 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
768 "movw r1, #(0x100000 & 0xffff)\n"
769 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
770 "movt r1, #(0x100000 >> 16)\n"
771 "1:\n"
772 "add r1, pc\n"
773 "ldr.w r1, [r1, #0]\n" +
774 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
775 ".align 2, 0\n"
776 "2:\n"
777 ".word 0x12345678\n";
778 DriverStr(expected, "LoadLiteralBeyondMax1MiB");
779
780 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
781 __ GetAdjustedPosition(label.Position()));
782}
783
784TEST_F(AssemblerThumb2Test, LoadLiteralFar) {
785 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
786 __ LoadLiteral(arm::R1, literal);
787 Label label;
788 __ Bind(&label);
789 constexpr size_t kLdrR0R0Count = (1u << 19) - 2u + 0x1234;
790 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
791 __ ldr(arm::R0, arm::Address(arm::R0));
792 }
793
794 std::string expected =
795 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
796 "movw r1, #((0x100000 + 2 * 0x1234) & 0xffff)\n"
797 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
798 "movt r1, #((0x100000 + 2 * 0x1234) >> 16)\n"
799 "1:\n"
800 "add r1, pc\n"
801 "ldr.w r1, [r1, #0]\n" +
802 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
803 ".align 2, 0\n"
804 "2:\n"
805 ".word 0x12345678\n";
806 DriverStr(expected, "LoadLiteralFar");
807
808 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
809 __ GetAdjustedPosition(label.Position()));
810}
811
812TEST_F(AssemblerThumb2Test, LoadLiteralWideMax1KiB) {
813 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
814 __ LoadLiteral(arm::R1, arm::R3, literal);
815 Label label;
816 __ Bind(&label);
817 constexpr size_t kLdrR0R0Count = 510;
818 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
819 __ ldr(arm::R0, arm::Address(arm::R0));
820 }
821
822 std::string expected =
823 "1:\n"
824 "ldrd r1, r3, [pc, #((2f - 1b - 2) & ~2)]\n" +
825 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
826 ".align 2, 0\n"
827 "2:\n"
828 ".word 0x87654321\n"
829 ".word 0x12345678\n";
830 DriverStr(expected, "LoadLiteralWideMax1KiB");
831
832 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
833 __ GetAdjustedPosition(label.Position()));
834}
835
836TEST_F(AssemblerThumb2Test, LoadLiteralWideBeyondMax1KiB) {
837 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
838 __ LoadLiteral(arm::R1, arm::R3, literal);
839 Label label;
840 __ Bind(&label);
841 constexpr size_t kLdrR0R0Count = 511;
842 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
843 __ ldr(arm::R0, arm::Address(arm::R0));
844 }
845
846 std::string expected =
847 "mov.w ip, #((2f - 1f - 4) & ~0x3ff)\n"
848 "1:\n"
849 "add ip, pc\n"
850 "ldrd r1, r3, [ip, #((2f - 1b - 4) & 0x3ff)]\n" +
851 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
852 ".align 2, 0\n"
853 "2:\n"
854 ".word 0x87654321\n"
855 ".word 0x12345678\n";
856 DriverStr(expected, "LoadLiteralWideBeyondMax1KiB");
857
858 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
859 __ GetAdjustedPosition(label.Position()));
860}
861
862TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax256KiB) {
863 // The literal size must match but the type doesn't, so use an int32_t rather than float.
864 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
865 __ LoadLiteral(arm::S3, literal);
866 Label label;
867 __ Bind(&label);
868 constexpr size_t kLdrR0R0Count = (1 << 17) - 3u;
869 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
870 __ ldr(arm::R0, arm::Address(arm::R0));
871 }
872
873 std::string expected =
874 "mov.w ip, #((2f - 1f - 4) & ~0x3ff)\n"
875 "1:\n"
876 "add ip, pc\n"
877 "vldr s3, [ip, #((2f - 1b - 4) & 0x3ff)]\n" +
878 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
879 ".align 2, 0\n"
880 "2:\n"
881 ".word 0x12345678\n";
882 DriverStr(expected, "LoadLiteralSingleMax256KiB");
883
884 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
885 __ GetAdjustedPosition(label.Position()));
886}
887
888TEST_F(AssemblerThumb2Test, LoadLiteralDoubleBeyondMax256KiB) {
889 // The literal size must match but the type doesn't, so use an int64_t rather than double.
890 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
891 __ LoadLiteral(arm::D3, literal);
892 Label label;
893 __ Bind(&label);
894 constexpr size_t kLdrR0R0Count = (1 << 17) - 2u;
895 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
896 __ ldr(arm::R0, arm::Address(arm::R0));
897 }
898
899 std::string expected =
900 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
901 "movw ip, #(0x40000 & 0xffff)\n"
902 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
903 "movt ip, #(0x40000 >> 16)\n"
904 "1:\n"
905 "add ip, pc\n"
906 "vldr d3, [ip, #0]\n" +
907 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
908 ".align 2, 0\n"
909 "2:\n"
910 ".word 0x87654321\n"
911 ".word 0x12345678\n";
912 DriverStr(expected, "LoadLiteralDoubleBeyondMax256KiB");
913
914 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
915 __ GetAdjustedPosition(label.Position()));
916}
917
918TEST_F(AssemblerThumb2Test, LoadLiteralDoubleFar) {
919 // The literal size must match but the type doesn't, so use an int64_t rather than double.
920 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
921 __ LoadLiteral(arm::D3, literal);
922 Label label;
923 __ Bind(&label);
924 constexpr size_t kLdrR0R0Count = (1 << 17) - 2u + 0x1234;
925 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
926 __ ldr(arm::R0, arm::Address(arm::R0));
927 }
928
929 std::string expected =
930 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
931 "movw ip, #((0x40000 + 2 * 0x1234) & 0xffff)\n"
932 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
933 "movt ip, #((0x40000 + 2 * 0x1234) >> 16)\n"
934 "1:\n"
935 "add ip, pc\n"
936 "vldr d3, [ip, #0]\n" +
937 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
938 ".align 2, 0\n"
939 "2:\n"
940 ".word 0x87654321\n"
941 ".word 0x12345678\n";
942 DriverStr(expected, "LoadLiteralDoubleFar");
943
944 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
945 __ GetAdjustedPosition(label.Position()));
946}
947
Vladimir Marko663c9342015-07-22 11:28:14 +0100948TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass) {
949 // First part: as TwoCbzBeyondMaxOffset but add one 16-bit instruction to the end,
950 // so that the size is not Aligned<4>(.). On the first pass, the assembler resizes
951 // the second CBZ because it's out of range, then it will resize the first CBZ
952 // which has been pushed out of range. Thus, after the first pass, the code size
953 // will appear Aligned<4>(.) but the final size will not be.
954 Label label0, label1, label2;
955 __ cbz(arm::R0, &label1);
956 constexpr size_t kLdrR0R0Count1 = 63;
957 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
958 __ ldr(arm::R0, arm::Address(arm::R0));
959 }
960 __ Bind(&label0);
961 __ cbz(arm::R0, &label2);
962 __ Bind(&label1);
963 constexpr size_t kLdrR0R0Count2 = 65;
964 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
965 __ ldr(arm::R0, arm::Address(arm::R0));
966 }
967 __ Bind(&label2);
968 __ ldr(arm::R0, arm::Address(arm::R0));
969
970 std::string expected_part1 =
971 "cmp r0, #0\n" // cbz r0, label1
972 "beq.n 1f\n" +
973 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
974 "0:\n"
975 "cmp r0, #0\n" // cbz r0, label2
976 "beq.n 2f\n"
977 "1:\n" +
978 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
979 "2:\n" // Here the offset is Aligned<4>(.).
980 "ldr r0, [r0]\n"; // Make the first part
981
982 // Second part: as LoadLiteralMax1KiB with the caveat that the offset of the load
983 // literal will not be Aligned<4>(.) but it will appear to be when we process the
984 // instruction during the first pass, so the literal will need a padding and it
985 // will push the literal out of range, so we shall end up with "ldr.w".
986 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
987 __ LoadLiteral(arm::R0, literal);
988 Label label;
989 __ Bind(&label);
990 constexpr size_t kLdrR0R0Count = 511;
991 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
992 __ ldr(arm::R0, arm::Address(arm::R0));
993 }
994
995 std::string expected =
996 expected_part1 +
997 "1:\n"
998 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
999 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1000 ".align 2, 0\n"
1001 "2:\n"
1002 ".word 0x12345678\n";
1003 DriverStr(expected, "LoadLiteralMax1KiB");
1004
1005 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
1006 __ GetAdjustedPosition(label.Position()));
1007}
1008
Andreas Gampe7cffc3b2015-10-19 21:31:53 -07001009TEST_F(AssemblerThumb2Test, BindTrackedLabel) {
1010 Label non_tracked, tracked, branch_target;
1011
1012 // A few dummy loads on entry.
1013 constexpr size_t kLdrR0R0Count = 5;
1014 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1015 __ ldr(arm::R0, arm::Address(arm::R0));
1016 }
1017
1018 // A branch that will need to be fixed up.
1019 __ cbz(arm::R0, &branch_target);
1020
1021 // Some more dummy loads.
1022 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1023 __ ldr(arm::R0, arm::Address(arm::R0));
1024 }
1025
1026 // Now insert tracked and untracked label.
1027 __ Bind(&non_tracked);
1028 __ BindTrackedLabel(&tracked);
1029
1030 // A lot of dummy loads, to ensure the branch needs resizing.
1031 constexpr size_t kLdrR0R0CountLong = 60;
1032 for (size_t i = 0; i != kLdrR0R0CountLong; ++i) {
1033 __ ldr(arm::R0, arm::Address(arm::R0));
1034 }
1035
1036 // Bind the branch target.
1037 __ Bind(&branch_target);
1038
1039 // One more load.
1040 __ ldr(arm::R0, arm::Address(arm::R0));
1041
1042 std::string expected =
1043 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1044 "cmp r0, #0\n" // cbz r0, 1f
1045 "beq.n 1f\n" +
1046 RepeatInsn(kLdrR0R0Count + kLdrR0R0CountLong, "ldr r0, [r0]\n") +
1047 "1:\n"
1048 "ldr r0, [r0]\n";
1049 DriverStr(expected, "BindTrackedLabel");
1050
1051 // Expectation is that the tracked label should have moved.
1052 EXPECT_LT(non_tracked.Position(), tracked.Position());
1053}
1054
1055TEST_F(AssemblerThumb2Test, JumpTable) {
1056 // The jump table. Use three labels.
1057 Label label1, label2, label3;
1058 std::vector<Label*> labels({ &label1, &label2, &label3 });
1059
1060 // A few dummy loads on entry, interspersed with 2 labels.
1061 constexpr size_t kLdrR0R0Count = 5;
1062 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1063 __ ldr(arm::R0, arm::Address(arm::R0));
1064 }
1065 __ BindTrackedLabel(&label1);
1066 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1067 __ ldr(arm::R0, arm::Address(arm::R0));
1068 }
1069 __ BindTrackedLabel(&label2);
1070 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1071 __ ldr(arm::R0, arm::Address(arm::R0));
1072 }
1073
1074 // Create the jump table, emit the base load.
1075 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1076
1077 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1078 // it's being used.
1079 __ ldr(arm::R0, arm::Address(arm::R0));
1080
1081 // Emit the jump
1082 __ EmitJumpTableDispatch(jump_table, arm::R1);
1083
1084 // Some more dummy instructions.
1085 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1086 __ ldr(arm::R0, arm::Address(arm::R0));
1087 }
1088 __ BindTrackedLabel(&label3);
1089 for (size_t i = 0; i != kLdrR0R0Count; ++i) { // Note: odd so there's no alignment
1090 __ ldr(arm::R0, arm::Address(arm::R0)); // necessary, as gcc as emits nops,
1091 } // whereas we emit 0 != nop.
1092
1093 static_assert((kLdrR0R0Count + 3) * 2 < 1 * KB, "Too much offset");
1094
1095 std::string expected =
1096 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1097 ".L1:\n" +
1098 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1099 ".L2:\n" +
1100 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1101 "adr r1, .Ljump_table\n"
1102 "ldr r0, [r0]\n"
1103 ".Lbase:\n"
1104 "add pc, r1\n" +
1105 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1106 ".L3:\n" +
1107 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1108 ".align 2\n"
1109 ".Ljump_table:\n"
1110 ".4byte (.L1 - .Lbase - 4)\n"
1111 ".4byte (.L2 - .Lbase - 4)\n"
1112 ".4byte (.L3 - .Lbase - 4)\n";
1113 DriverStr(expected, "JumpTable");
1114}
1115
1116// Test for >1K fixup.
1117TEST_F(AssemblerThumb2Test, JumpTable4K) {
1118 // The jump table. Use three labels.
1119 Label label1, label2, label3;
1120 std::vector<Label*> labels({ &label1, &label2, &label3 });
1121
1122 // A few dummy loads on entry, interspersed with 2 labels.
1123 constexpr size_t kLdrR0R0Count = 5;
1124 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1125 __ ldr(arm::R0, arm::Address(arm::R0));
1126 }
1127 __ BindTrackedLabel(&label1);
1128 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1129 __ ldr(arm::R0, arm::Address(arm::R0));
1130 }
1131 __ BindTrackedLabel(&label2);
1132 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1133 __ ldr(arm::R0, arm::Address(arm::R0));
1134 }
1135
1136 // Create the jump table, emit the base load.
1137 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1138
1139 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1140 // it's being used.
1141 __ ldr(arm::R0, arm::Address(arm::R0));
1142
1143 // Emit the jump
1144 __ EmitJumpTableDispatch(jump_table, arm::R1);
1145
1146 // Some more dummy instructions.
1147 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1148 __ ldr(arm::R0, arm::Address(arm::R0));
1149 }
1150 __ BindTrackedLabel(&label3);
1151 constexpr size_t kLdrR0R0Count2 = 600; // Note: even so there's no alignment
1152 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1153 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1154 }
1155
1156 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 1 * KB, "Not enough offset");
1157 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 4 * KB, "Too much offset");
1158
1159 std::string expected =
1160 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1161 ".L1:\n" +
1162 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1163 ".L2:\n" +
1164 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1165 "adr r1, .Ljump_table\n"
1166 "ldr r0, [r0]\n"
1167 ".Lbase:\n"
1168 "add pc, r1\n" +
1169 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1170 ".L3:\n" +
1171 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1172 ".align 2\n"
1173 ".Ljump_table:\n"
1174 ".4byte (.L1 - .Lbase - 4)\n"
1175 ".4byte (.L2 - .Lbase - 4)\n"
1176 ".4byte (.L3 - .Lbase - 4)\n";
1177 DriverStr(expected, "JumpTable4K");
1178}
1179
1180// Test for >4K fixup.
1181TEST_F(AssemblerThumb2Test, JumpTable64K) {
1182 // The jump table. Use three labels.
1183 Label label1, label2, label3;
1184 std::vector<Label*> labels({ &label1, &label2, &label3 });
1185
1186 // A few dummy loads on entry, interspersed with 2 labels.
1187 constexpr size_t kLdrR0R0Count = 5;
1188 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1189 __ ldr(arm::R0, arm::Address(arm::R0));
1190 }
1191 __ BindTrackedLabel(&label1);
1192 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1193 __ ldr(arm::R0, arm::Address(arm::R0));
1194 }
1195 __ BindTrackedLabel(&label2);
1196 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1197 __ ldr(arm::R0, arm::Address(arm::R0));
1198 }
1199
1200 // Create the jump table, emit the base load.
1201 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1202
1203 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1204 // it's being used.
1205 __ ldr(arm::R0, arm::Address(arm::R0));
1206
1207 // Emit the jump
1208 __ EmitJumpTableDispatch(jump_table, arm::R1);
1209
1210 // Some more dummy instructions.
1211 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1212 __ ldr(arm::R0, arm::Address(arm::R0));
1213 }
1214 __ BindTrackedLabel(&label3);
1215 constexpr size_t kLdrR0R0Count2 = 2601; // Note: odd so there's no alignment
1216 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1217 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1218 }
1219
1220 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 4 * KB, "Not enough offset");
1221 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 64 * KB, "Too much offset");
1222
1223 std::string expected =
1224 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1225 ".L1:\n" +
1226 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1227 ".L2:\n" +
1228 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1229 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
1230 // (Note: have to use constants, as labels aren't accepted.
1231 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1232 ") * 2 - 4) & 0xFFFF)\n"
1233 "add r1, pc\n"
1234 "ldr r0, [r0]\n"
1235 ".Lbase:\n"
1236 "add pc, r1\n" +
1237 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1238 ".L3:\n" +
1239 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1240 ".align 2\n"
1241 ".Ljump_table:\n"
1242 ".4byte (.L1 - .Lbase - 4)\n"
1243 ".4byte (.L2 - .Lbase - 4)\n"
1244 ".4byte (.L3 - .Lbase - 4)\n";
1245 DriverStr(expected, "JumpTable64K");
1246}
1247
1248// Test for >64K fixup.
1249TEST_F(AssemblerThumb2Test, JumpTableFar) {
1250 // The jump table. Use three labels.
1251 Label label1, label2, label3;
1252 std::vector<Label*> labels({ &label1, &label2, &label3 });
1253
1254 // A few dummy loads on entry, interspersed with 2 labels.
1255 constexpr size_t kLdrR0R0Count = 5;
1256 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1257 __ ldr(arm::R0, arm::Address(arm::R0));
1258 }
1259 __ BindTrackedLabel(&label1);
1260 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1261 __ ldr(arm::R0, arm::Address(arm::R0));
1262 }
1263 __ BindTrackedLabel(&label2);
1264 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1265 __ ldr(arm::R0, arm::Address(arm::R0));
1266 }
1267
1268 // Create the jump table, emit the base load.
1269 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1270
1271 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1272 // it's being used.
1273 __ ldr(arm::R0, arm::Address(arm::R0));
1274
1275 // Emit the jump
1276 __ EmitJumpTableDispatch(jump_table, arm::R1);
1277
1278 // Some more dummy instructions.
1279 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1280 __ ldr(arm::R0, arm::Address(arm::R0));
1281 }
1282 __ BindTrackedLabel(&label3);
1283 constexpr size_t kLdrR0R0Count2 = 70001; // Note: odd so there's no alignment
1284 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1285 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1286 }
1287
1288 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 64 * KB, "Not enough offset");
1289
1290 std::string expected =
1291 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1292 ".L1:\n" +
1293 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1294 ".L2:\n" +
1295 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1296 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
1297 // (Note: have to use constants, as labels aren't accepted.
1298 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1299 ") * 2 - 4) & 0xFFFF)\n"
1300 "movt r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1301 ") * 2 - 4) >> 16)\n"
1302 ".Lhelp:"
1303 "add r1, pc\n"
1304 "ldr r0, [r0]\n"
1305 ".Lbase:\n"
1306 "add pc, r1\n" +
1307 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1308 ".L3:\n" +
1309 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1310 ".align 2\n"
1311 ".Ljump_table:\n"
1312 ".4byte (.L1 - .Lbase - 4)\n"
1313 ".4byte (.L2 - .Lbase - 4)\n"
1314 ".4byte (.L3 - .Lbase - 4)\n";
1315 DriverStr(expected, "JumpTableFar");
1316}
1317
Scott Wakeling611d3392015-07-10 11:42:06 +01001318TEST_F(AssemblerThumb2Test, Clz) {
1319 __ clz(arm::R0, arm::R1);
1320
1321 const char* expected = "clz r0, r1\n";
1322
1323 DriverStr(expected, "clz");
1324}
1325
Scott Wakeling9ee23f42015-07-23 10:44:35 +01001326TEST_F(AssemblerThumb2Test, rbit) {
1327 __ rbit(arm::R1, arm::R0);
1328
1329 const char* expected = "rbit r1, r0\n";
1330
1331 DriverStr(expected, "rbit");
1332}
1333
Roland Levillain1a28fc42014-11-13 18:03:06 +00001334} // namespace art