assembler_thumb2_test.cc revision 7cffc3b0004d32faffc552c0a59286f369b21504
1/*
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"
20#include "base/stringprintf.h"
21#include "utils/assembler_test.h"
22
23namespace art {
24
25class AssemblerThumb2Test : public AssemblerTest<arm::Thumb2Assembler,
26                                                 arm::Register, arm::SRegister,
27                                                 uint32_t> {
28 protected:
29  std::string GetArchitectureString() OVERRIDE {
30    return "arm";
31  }
32
33  std::string GetAssemblerParameters() OVERRIDE {
34    return " -march=armv7-a -mcpu=cortex-a15 -mfpu=neon -mthumb";
35  }
36
37  const char* GetAssemblyHeader() OVERRIDE {
38    return kThumb2AssemblyHeader;
39  }
40
41  std::string GetDisassembleParameters() OVERRIDE {
42    return " -D -bbinary -marm --disassembler-options=force-thumb --no-show-raw-insn";
43  }
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
82  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
90 private:
91  std::vector<arm::Register*> registers_;
92
93  static constexpr const char* kThumb2AssemblyHeader = ".syntax unified\n.thumb\n";
94};
95
96TEST_F(AssemblerThumb2Test, Toolchain) {
97  EXPECT_TRUE(CheckTools());
98}
99
100#define __ GetAssembler()->
101
102TEST_F(AssemblerThumb2Test, Sbfx) {
103  __ 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);
107
108  __ 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);
112
113  __ sbfx(arm::R0, arm::R1, 16, 1);
114  __ sbfx(arm::R0, arm::R1, 16, 8);
115  __ sbfx(arm::R0, arm::R1, 16, 16);
116
117  __ sbfx(arm::R0, arm::R1, 31, 1);
118
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
138TEST_F(AssemblerThumb2Test, Ubfx) {
139  __ 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);
143
144  __ 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);
148
149  __ ubfx(arm::R0, arm::R1, 16, 1);
150  __ ubfx(arm::R0, arm::R1, 16, 8);
151  __ ubfx(arm::R0, arm::R1, 16, 16);
152
153  __ ubfx(arm::R0, arm::R1, 31, 1);
154
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
174TEST_F(AssemblerThumb2Test, Vmstat) {
175  __ vmstat();
176
177  const char* expected = "vmrs APSR_nzcv, FPSCR\n";
178
179  DriverStr(expected, "vmrs");
180}
181
182TEST_F(AssemblerThumb2Test, ldrexd) {
183  __ 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);
187
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) {
197  __ 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);
201
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
210TEST_F(AssemblerThumb2Test, LdrdStrd) {
211  __ 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));
214
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
222TEST_F(AssemblerThumb2Test, eor) {
223  __ 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
238TEST_F(AssemblerThumb2Test, sub) {
239  __ subs(arm::R1, arm::R0, arm::ShifterOperand(42));
240  __ sub(arm::R1, arm::R0, arm::ShifterOperand(42));
241  __ 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));
243
244  const char* expected =
245      "subs r1, r0, #42\n"
246      "subw r1, r0, #42\n"
247      "subs r1, r0, r2, asr #31\n"
248      "sub r1, r0, r2, asr #31\n";
249  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));
255  __ 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));
257
258  const char* expected =
259      "adds r1, r0, #42\n"
260      "addw r1, r0, #42\n"
261      "adds r1, r0, r2, asr #31\n"
262      "add r1, r0, r2, asr #31\n";
263  DriverStr(expected, "add");
264}
265
266TEST_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
282TEST_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
326TEST_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
381TEST_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
954TEST_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
1015TEST_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
1324TEST_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
1332TEST_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
1340}  // namespace art
1341