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