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