assembler_thumb2_test.cc revision 9ee23f4273efed8d6378f6ad8e63c65e30a17139
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 "utils/assembler_test.h"
21
22namespace art {
23
24class AssemblerThumb2Test : public AssemblerTest<arm::Thumb2Assembler,
25                                                 arm::Register, arm::SRegister,
26                                                 uint32_t> {
27 protected:
28  std::string GetArchitectureString() OVERRIDE {
29    return "arm";
30  }
31
32  std::string GetAssemblerParameters() OVERRIDE {
33    return " -march=armv7-a -mcpu=cortex-a15 -mfpu=neon -mthumb";
34  }
35
36  const char* GetAssemblyHeader() OVERRIDE {
37    return kThumb2AssemblyHeader;
38  }
39
40  std::string GetDisassembleParameters() OVERRIDE {
41    return " -D -bbinary -marm --disassembler-options=force-thumb --no-show-raw-insn";
42  }
43
44  void SetUpHelpers() OVERRIDE {
45    if (registers_.size() == 0) {
46      registers_.insert(end(registers_),
47                        {  // NOLINT(whitespace/braces)
48                          new arm::Register(arm::R0),
49                          new arm::Register(arm::R1),
50                          new arm::Register(arm::R2),
51                          new arm::Register(arm::R3),
52                          new arm::Register(arm::R4),
53                          new arm::Register(arm::R5),
54                          new arm::Register(arm::R6),
55                          new arm::Register(arm::R7),
56                          new arm::Register(arm::R8),
57                          new arm::Register(arm::R9),
58                          new arm::Register(arm::R10),
59                          new arm::Register(arm::R11),
60                          new arm::Register(arm::R12),
61                          new arm::Register(arm::R13),
62                          new arm::Register(arm::R14),
63                          new arm::Register(arm::R15)
64                        });
65    }
66  }
67
68  void TearDown() OVERRIDE {
69    AssemblerTest::TearDown();
70    STLDeleteElements(&registers_);
71  }
72
73  std::vector<arm::Register*> GetRegisters() OVERRIDE {
74    return registers_;
75  }
76
77  uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
78    return imm_value;
79  }
80
81  std::string RepeatInsn(size_t count, const std::string& insn) {
82    std::string result;
83    for (; count != 0u; --count) {
84      result += insn;
85    }
86    return result;
87  }
88
89 private:
90  std::vector<arm::Register*> registers_;
91
92  static constexpr const char* kThumb2AssemblyHeader = ".syntax unified\n.thumb\n";
93};
94
95TEST_F(AssemblerThumb2Test, Toolchain) {
96  EXPECT_TRUE(CheckTools());
97}
98
99#define __ GetAssembler()->
100
101TEST_F(AssemblerThumb2Test, Sbfx) {
102  __ sbfx(arm::R0, arm::R1, 0, 1);
103  __ sbfx(arm::R0, arm::R1, 0, 8);
104  __ sbfx(arm::R0, arm::R1, 0, 16);
105  __ sbfx(arm::R0, arm::R1, 0, 32);
106
107  __ sbfx(arm::R0, arm::R1, 8, 1);
108  __ sbfx(arm::R0, arm::R1, 8, 8);
109  __ sbfx(arm::R0, arm::R1, 8, 16);
110  __ sbfx(arm::R0, arm::R1, 8, 24);
111
112  __ sbfx(arm::R0, arm::R1, 16, 1);
113  __ sbfx(arm::R0, arm::R1, 16, 8);
114  __ sbfx(arm::R0, arm::R1, 16, 16);
115
116  __ sbfx(arm::R0, arm::R1, 31, 1);
117
118  const char* expected =
119      "sbfx r0, r1, #0, #1\n"
120      "sbfx r0, r1, #0, #8\n"
121      "sbfx r0, r1, #0, #16\n"
122      "sbfx r0, r1, #0, #32\n"
123
124      "sbfx r0, r1, #8, #1\n"
125      "sbfx r0, r1, #8, #8\n"
126      "sbfx r0, r1, #8, #16\n"
127      "sbfx r0, r1, #8, #24\n"
128
129      "sbfx r0, r1, #16, #1\n"
130      "sbfx r0, r1, #16, #8\n"
131      "sbfx r0, r1, #16, #16\n"
132
133      "sbfx r0, r1, #31, #1\n";
134  DriverStr(expected, "sbfx");
135}
136
137TEST_F(AssemblerThumb2Test, Ubfx) {
138  __ ubfx(arm::R0, arm::R1, 0, 1);
139  __ ubfx(arm::R0, arm::R1, 0, 8);
140  __ ubfx(arm::R0, arm::R1, 0, 16);
141  __ ubfx(arm::R0, arm::R1, 0, 32);
142
143  __ ubfx(arm::R0, arm::R1, 8, 1);
144  __ ubfx(arm::R0, arm::R1, 8, 8);
145  __ ubfx(arm::R0, arm::R1, 8, 16);
146  __ ubfx(arm::R0, arm::R1, 8, 24);
147
148  __ ubfx(arm::R0, arm::R1, 16, 1);
149  __ ubfx(arm::R0, arm::R1, 16, 8);
150  __ ubfx(arm::R0, arm::R1, 16, 16);
151
152  __ ubfx(arm::R0, arm::R1, 31, 1);
153
154  const char* expected =
155      "ubfx r0, r1, #0, #1\n"
156      "ubfx r0, r1, #0, #8\n"
157      "ubfx r0, r1, #0, #16\n"
158      "ubfx r0, r1, #0, #32\n"
159
160      "ubfx r0, r1, #8, #1\n"
161      "ubfx r0, r1, #8, #8\n"
162      "ubfx r0, r1, #8, #16\n"
163      "ubfx r0, r1, #8, #24\n"
164
165      "ubfx r0, r1, #16, #1\n"
166      "ubfx r0, r1, #16, #8\n"
167      "ubfx r0, r1, #16, #16\n"
168
169      "ubfx r0, r1, #31, #1\n";
170  DriverStr(expected, "ubfx");
171}
172
173TEST_F(AssemblerThumb2Test, Vmstat) {
174  __ vmstat();
175
176  const char* expected = "vmrs APSR_nzcv, FPSCR\n";
177
178  DriverStr(expected, "vmrs");
179}
180
181TEST_F(AssemblerThumb2Test, ldrexd) {
182  __ ldrexd(arm::R0, arm::R1, arm::R0);
183  __ ldrexd(arm::R0, arm::R1, arm::R1);
184  __ ldrexd(arm::R0, arm::R1, arm::R2);
185  __ ldrexd(arm::R5, arm::R3, arm::R7);
186
187  const char* expected =
188      "ldrexd r0, r1, [r0]\n"
189      "ldrexd r0, r1, [r1]\n"
190      "ldrexd r0, r1, [r2]\n"
191      "ldrexd r5, r3, [r7]\n";
192  DriverStr(expected, "ldrexd");
193}
194
195TEST_F(AssemblerThumb2Test, strexd) {
196  __ strexd(arm::R9, arm::R0, arm::R1, arm::R0);
197  __ strexd(arm::R9, arm::R0, arm::R1, arm::R1);
198  __ strexd(arm::R9, arm::R0, arm::R1, arm::R2);
199  __ strexd(arm::R9, arm::R5, arm::R3, arm::R7);
200
201  const char* expected =
202      "strexd r9, r0, r1, [r0]\n"
203      "strexd r9, r0, r1, [r1]\n"
204      "strexd r9, r0, r1, [r2]\n"
205      "strexd r9, r5, r3, [r7]\n";
206  DriverStr(expected, "strexd");
207}
208
209TEST_F(AssemblerThumb2Test, LdrdStrd) {
210  __ ldrd(arm::R0, arm::Address(arm::R2, 8));
211  __ ldrd(arm::R0, arm::Address(arm::R12));
212  __ strd(arm::R0, arm::Address(arm::R2, 8));
213
214  const char* expected =
215      "ldrd r0, r1, [r2, #8]\n"
216      "ldrd r0, r1, [r12]\n"
217      "strd r0, r1, [r2, #8]\n";
218  DriverStr(expected, "ldrdstrd");
219}
220
221TEST_F(AssemblerThumb2Test, eor) {
222  __ eor(arm::R1, arm::R1, arm::ShifterOperand(arm::R0));
223  __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R1));
224  __ eor(arm::R1, arm::R8, arm::ShifterOperand(arm::R0));
225  __ eor(arm::R8, arm::R1, arm::ShifterOperand(arm::R0));
226  __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R8));
227
228  const char* expected =
229      "eors r1, r0\n"
230      "eor r1, r0, r1\n"
231      "eor r1, r8, r0\n"
232      "eor r8, r1, r0\n"
233      "eor r1, r0, r8\n";
234  DriverStr(expected, "abs");
235}
236
237TEST_F(AssemblerThumb2Test, sub) {
238  __ subs(arm::R1, arm::R0, arm::ShifterOperand(42));
239  __ sub(arm::R1, arm::R0, arm::ShifterOperand(42));
240  __ subs(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
241  __ sub(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
242
243  const char* expected =
244      "subs r1, r0, #42\n"
245      "subw r1, r0, #42\n"
246      "subs r1, r0, r2, asr #31\n"
247      "sub r1, r0, r2, asr #31\n";
248  DriverStr(expected, "sub");
249}
250
251TEST_F(AssemblerThumb2Test, add) {
252  __ adds(arm::R1, arm::R0, arm::ShifterOperand(42));
253  __ add(arm::R1, arm::R0, arm::ShifterOperand(42));
254  __ adds(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
255  __ add(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
256
257  const char* expected =
258      "adds r1, r0, #42\n"
259      "addw r1, r0, #42\n"
260      "adds r1, r0, r2, asr #31\n"
261      "add r1, r0, r2, asr #31\n";
262  DriverStr(expected, "add");
263}
264
265TEST_F(AssemblerThumb2Test, umull) {
266  __ umull(arm::R0, arm::R1, arm::R2, arm::R3);
267
268  const char* expected =
269      "umull r0, r1, r2, r3\n";
270  DriverStr(expected, "umull");
271}
272
273TEST_F(AssemblerThumb2Test, smull) {
274  __ smull(arm::R0, arm::R1, arm::R2, arm::R3);
275
276  const char* expected =
277      "smull r0, r1, r2, r3\n";
278  DriverStr(expected, "smull");
279}
280
281TEST_F(AssemblerThumb2Test, StoreWordToThumbOffset) {
282  arm::StoreOperandType type = arm::kStoreWord;
283  int32_t offset = 4092;
284  ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
285
286  __ StoreToOffset(type, arm::R0, arm::SP, offset);
287  __ StoreToOffset(type, arm::IP, arm::SP, offset);
288  __ StoreToOffset(type, arm::IP, arm::R5, offset);
289
290  const char* expected =
291      "str r0, [sp, #4092]\n"
292      "str ip, [sp, #4092]\n"
293      "str ip, [r5, #4092]\n";
294  DriverStr(expected, "StoreWordToThumbOffset");
295}
296
297TEST_F(AssemblerThumb2Test, StoreWordToNonThumbOffset) {
298  arm::StoreOperandType type = arm::kStoreWord;
299  int32_t offset = 4096;
300  ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
301
302  __ StoreToOffset(type, arm::R0, arm::SP, offset);
303  __ StoreToOffset(type, arm::IP, arm::SP, offset);
304  __ StoreToOffset(type, arm::IP, arm::R5, offset);
305
306  const char* expected =
307      "mov ip, #4096\n"       // LoadImmediate(ip, 4096)
308      "add ip, ip, sp\n"
309      "str r0, [ip, #0]\n"
310
311      "str r5, [sp, #-4]!\n"  // Push(r5)
312      "movw r5, #4100\n"      // LoadImmediate(r5, 4096 + kRegisterSize)
313      "add r5, r5, sp\n"
314      "str ip, [r5, #0]\n"
315      "ldr r5, [sp], #4\n"    // Pop(r5)
316
317      "str r6, [sp, #-4]!\n"  // Push(r6)
318      "mov r6, #4096\n"       // LoadImmediate(r6, 4096)
319      "add r6, r6, r5\n"
320      "str ip, [r6, #0]\n"
321      "ldr r6, [sp], #4\n";   // Pop(r6)
322  DriverStr(expected, "StoreWordToNonThumbOffset");
323}
324
325TEST_F(AssemblerThumb2Test, StoreWordPairToThumbOffset) {
326  arm::StoreOperandType type = arm::kStoreWordPair;
327  int32_t offset = 1020;
328  ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
329
330  __ StoreToOffset(type, arm::R0, arm::SP, offset);
331  // We cannot use IP (i.e. R12) as first source register, as it would
332  // force us to use SP (i.e. R13) as second source register, which
333  // would have an "unpredictable" effect according to the ARMv7
334  // specification (the T1 encoding describes the result as
335  // UNPREDICTABLE when of the source registers is R13).
336  //
337  // So we use (R11, IP) (e.g. (R11, R12)) as source registers in the
338  // following instructions.
339  __ StoreToOffset(type, arm::R11, arm::SP, offset);
340  __ StoreToOffset(type, arm::R11, arm::R5, offset);
341
342  const char* expected =
343      "strd r0, r1, [sp, #1020]\n"
344      "strd r11, ip, [sp, #1020]\n"
345      "strd r11, ip, [r5, #1020]\n";
346  DriverStr(expected, "StoreWordPairToThumbOffset");
347}
348
349TEST_F(AssemblerThumb2Test, StoreWordPairToNonThumbOffset) {
350  arm::StoreOperandType type = arm::kStoreWordPair;
351  int32_t offset = 1024;
352  ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
353
354  __ StoreToOffset(type, arm::R0, arm::SP, offset);
355  // Same comment as in AssemblerThumb2Test.StoreWordPairToThumbOffset
356  // regarding the use of (R11, IP) (e.g. (R11, R12)) as source
357  // registers in the following instructions.
358  __ StoreToOffset(type, arm::R11, arm::SP, offset);
359  __ StoreToOffset(type, arm::R11, arm::R5, offset);
360
361  const char* expected =
362      "mov ip, #1024\n"           // LoadImmediate(ip, 1024)
363      "add ip, ip, sp\n"
364      "strd r0, r1, [ip, #0]\n"
365
366      "str r5, [sp, #-4]!\n"      // Push(r5)
367      "movw r5, #1028\n"          // LoadImmediate(r5, 1024 + kRegisterSize)
368      "add r5, r5, sp\n"
369      "strd r11, ip, [r5, #0]\n"
370      "ldr r5, [sp], #4\n"        // Pop(r5)
371
372      "str r6, [sp, #-4]!\n"      // Push(r6)
373      "mov r6, #1024\n"           // LoadImmediate(r6, 1024)
374      "add r6, r6, r5\n"
375      "strd r11, ip, [r6, #0]\n"
376      "ldr r6, [sp], #4\n";       // Pop(r6)
377  DriverStr(expected, "StoreWordPairToNonThumbOffset");
378}
379
380TEST_F(AssemblerThumb2Test, TwoCbzMaxOffset) {
381  Label label0, label1, label2;
382  __ cbz(arm::R0, &label1);
383  constexpr size_t kLdrR0R0Count1 = 63;
384  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
385    __ ldr(arm::R0, arm::Address(arm::R0));
386  }
387  __ Bind(&label0);
388  __ cbz(arm::R0, &label2);
389  __ Bind(&label1);
390  constexpr size_t kLdrR0R0Count2 = 64;
391  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
392    __ ldr(arm::R0, arm::Address(arm::R0));
393  }
394  __ Bind(&label2);
395
396  std::string expected =
397      "cbz r0, 1f\n" +            // cbz r0, label1
398      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
399      "0:\n"
400      "cbz r0, 2f\n"              // cbz r0, label2
401      "1:\n" +
402      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
403      "2:\n";
404  DriverStr(expected, "TwoCbzMaxOffset");
405
406  EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
407            __ GetAdjustedPosition(label0.Position()));
408  EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 0u,
409            __ GetAdjustedPosition(label1.Position()));
410  EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 0u,
411            __ GetAdjustedPosition(label2.Position()));
412}
413
414TEST_F(AssemblerThumb2Test, TwoCbzBeyondMaxOffset) {
415  Label label0, label1, label2;
416  __ cbz(arm::R0, &label1);
417  constexpr size_t kLdrR0R0Count1 = 63;
418  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
419    __ ldr(arm::R0, arm::Address(arm::R0));
420  }
421  __ Bind(&label0);
422  __ cbz(arm::R0, &label2);
423  __ Bind(&label1);
424  constexpr size_t kLdrR0R0Count2 = 65;
425  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
426    __ ldr(arm::R0, arm::Address(arm::R0));
427  }
428  __ Bind(&label2);
429
430  std::string expected =
431      "cmp r0, #0\n"              // cbz r0, label1
432      "beq.n 1f\n" +
433      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
434      "0:\n"
435      "cmp r0, #0\n"              // cbz r0, label2
436      "beq.n 2f\n"
437      "1:\n" +
438      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
439      "2:\n";
440  DriverStr(expected, "TwoCbzBeyondMaxOffset");
441
442  EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
443            __ GetAdjustedPosition(label0.Position()));
444  EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 4u,
445            __ GetAdjustedPosition(label1.Position()));
446  EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 4u,
447            __ GetAdjustedPosition(label2.Position()));
448}
449
450TEST_F(AssemblerThumb2Test, TwoCbzSecondAtMaxB16Offset) {
451  Label label0, label1, label2;
452  __ cbz(arm::R0, &label1);
453  constexpr size_t kLdrR0R0Count1 = 62;
454  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
455    __ ldr(arm::R0, arm::Address(arm::R0));
456  }
457  __ Bind(&label0);
458  __ cbz(arm::R0, &label2);
459  __ Bind(&label1);
460  constexpr size_t kLdrR0R0Count2 = 128;
461  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
462    __ ldr(arm::R0, arm::Address(arm::R0));
463  }
464  __ Bind(&label2);
465
466  std::string expected =
467      "cbz r0, 1f\n" +            // cbz r0, label1
468      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
469      "0:\n"
470      "cmp r0, #0\n"              // cbz r0, label2
471      "beq.n 2f\n"
472      "1:\n" +
473      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
474      "2:\n";
475  DriverStr(expected, "TwoCbzSecondAtMaxB16Offset");
476
477  EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
478            __ GetAdjustedPosition(label0.Position()));
479  EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
480            __ GetAdjustedPosition(label1.Position()));
481  EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
482            __ GetAdjustedPosition(label2.Position()));
483}
484
485TEST_F(AssemblerThumb2Test, TwoCbzSecondBeyondMaxB16Offset) {
486  Label label0, label1, label2;
487  __ cbz(arm::R0, &label1);
488  constexpr size_t kLdrR0R0Count1 = 62;
489  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
490    __ ldr(arm::R0, arm::Address(arm::R0));
491  }
492  __ Bind(&label0);
493  __ cbz(arm::R0, &label2);
494  __ Bind(&label1);
495  constexpr size_t kLdrR0R0Count2 = 129;
496  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
497    __ ldr(arm::R0, arm::Address(arm::R0));
498  }
499  __ Bind(&label2);
500
501  std::string expected =
502      "cmp r0, #0\n"              // cbz r0, label1
503      "beq.n 1f\n" +
504      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
505      "0:\n"
506      "cmp r0, #0\n"              // cbz r0, label2
507      "beq.w 2f\n"
508      "1:\n" +
509      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
510      "2:\n";
511  DriverStr(expected, "TwoCbzSecondBeyondMaxB16Offset");
512
513  EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
514            __ GetAdjustedPosition(label0.Position()));
515  EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
516            __ GetAdjustedPosition(label1.Position()));
517  EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
518            __ GetAdjustedPosition(label2.Position()));
519}
520
521TEST_F(AssemblerThumb2Test, TwoCbzFirstAtMaxB16Offset) {
522  Label label0, label1, label2;
523  __ cbz(arm::R0, &label1);
524  constexpr size_t kLdrR0R0Count1 = 127;
525  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
526    __ ldr(arm::R0, arm::Address(arm::R0));
527  }
528  __ Bind(&label0);
529  __ cbz(arm::R0, &label2);
530  __ Bind(&label1);
531  constexpr size_t kLdrR0R0Count2 = 64;
532  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
533    __ ldr(arm::R0, arm::Address(arm::R0));
534  }
535  __ Bind(&label2);
536
537  std::string expected =
538      "cmp r0, #0\n"              // cbz r0, label1
539      "beq.n 1f\n" +
540      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
541      "0:\n"
542      "cbz r0, 2f\n"              // cbz r0, label2
543      "1:\n" +
544      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
545      "2:\n";
546  DriverStr(expected, "TwoCbzFirstAtMaxB16Offset");
547
548  EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
549            __ GetAdjustedPosition(label0.Position()));
550  EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
551            __ GetAdjustedPosition(label1.Position()));
552  EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
553            __ GetAdjustedPosition(label2.Position()));
554}
555
556TEST_F(AssemblerThumb2Test, TwoCbzFirstBeyondMaxB16Offset) {
557  Label label0, label1, label2;
558  __ cbz(arm::R0, &label1);
559  constexpr size_t kLdrR0R0Count1 = 127;
560  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
561    __ ldr(arm::R0, arm::Address(arm::R0));
562  }
563  __ Bind(&label0);
564  __ cbz(arm::R0, &label2);
565  __ Bind(&label1);
566  constexpr size_t kLdrR0R0Count2 = 65;
567  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
568    __ ldr(arm::R0, arm::Address(arm::R0));
569  }
570  __ Bind(&label2);
571
572  std::string expected =
573      "cmp r0, #0\n"              // cbz r0, label1
574      "beq.w 1f\n" +
575      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
576      "0:\n"
577      "cmp r0, #0\n"              // cbz r0, label2
578      "beq.n 2f\n"
579      "1:\n" +
580      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
581      "2:\n";
582  DriverStr(expected, "TwoCbzFirstBeyondMaxB16Offset");
583
584  EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 4u,
585            __ GetAdjustedPosition(label0.Position()));
586  EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
587            __ GetAdjustedPosition(label1.Position()));
588  EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
589            __ GetAdjustedPosition(label2.Position()));
590}
591
592TEST_F(AssemblerThumb2Test, LoadLiteralMax1KiB) {
593  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
594  __ LoadLiteral(arm::R0, literal);
595  Label label;
596  __ Bind(&label);
597  constexpr size_t kLdrR0R0Count = 511;
598  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
599    __ ldr(arm::R0, arm::Address(arm::R0));
600  }
601
602  std::string expected =
603      "1:\n"
604      "ldr.n r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
605      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
606      ".align 2, 0\n"
607      "2:\n"
608      ".word 0x12345678\n";
609  DriverStr(expected, "LoadLiteralMax1KiB");
610
611  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
612            __ GetAdjustedPosition(label.Position()));
613}
614
615TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiB) {
616  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
617  __ LoadLiteral(arm::R0, literal);
618  Label label;
619  __ Bind(&label);
620  constexpr size_t kLdrR0R0Count = 512;
621  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
622    __ ldr(arm::R0, arm::Address(arm::R0));
623  }
624
625  std::string expected =
626      "1:\n"
627      "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
628      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
629      ".align 2, 0\n"
630      "2:\n"
631      ".word 0x12345678\n";
632  DriverStr(expected, "LoadLiteralBeyondMax1KiB");
633
634  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
635            __ GetAdjustedPosition(label.Position()));
636}
637
638TEST_F(AssemblerThumb2Test, LoadLiteralMax4KiB) {
639  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
640  __ LoadLiteral(arm::R1, literal);
641  Label label;
642  __ Bind(&label);
643  constexpr size_t kLdrR0R0Count = 2046;
644  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
645    __ ldr(arm::R0, arm::Address(arm::R0));
646  }
647
648  std::string expected =
649      "1:\n"
650      "ldr.w r1, [pc, #((2f - 1b - 2) & ~2)]\n" +
651      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
652      ".align 2, 0\n"
653      "2:\n"
654      ".word 0x12345678\n";
655  DriverStr(expected, "LoadLiteralMax4KiB");
656
657  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
658            __ GetAdjustedPosition(label.Position()));
659}
660
661TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax4KiB) {
662  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
663  __ LoadLiteral(arm::R1, literal);
664  Label label;
665  __ Bind(&label);
666  constexpr size_t kLdrR0R0Count = 2047;
667  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
668    __ ldr(arm::R0, arm::Address(arm::R0));
669  }
670
671  std::string expected =
672      "movw r1, #4096\n"  // "as" does not consider (2f - 1f - 4) a constant expression for movw.
673      "1:\n"
674      "add r1, pc\n"
675      "ldr r1, [r1, #0]\n" +
676      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
677      ".align 2, 0\n"
678      "2:\n"
679      ".word 0x12345678\n";
680  DriverStr(expected, "LoadLiteralBeyondMax4KiB");
681
682  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
683            __ GetAdjustedPosition(label.Position()));
684}
685
686TEST_F(AssemblerThumb2Test, LoadLiteralMax64KiB) {
687  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
688  __ LoadLiteral(arm::R1, literal);
689  Label label;
690  __ Bind(&label);
691  constexpr size_t kLdrR0R0Count = (1u << 15) - 2u;
692  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
693    __ ldr(arm::R0, arm::Address(arm::R0));
694  }
695
696  std::string expected =
697      "movw r1, #0xfffc\n"  // "as" does not consider (2f - 1f - 4) a constant expression for movw.
698      "1:\n"
699      "add r1, pc\n"
700      "ldr r1, [r1, #0]\n" +
701      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
702      ".align 2, 0\n"
703      "2:\n"
704      ".word 0x12345678\n";
705  DriverStr(expected, "LoadLiteralMax64KiB");
706
707  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
708            __ GetAdjustedPosition(label.Position()));
709}
710
711TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax64KiB) {
712  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
713  __ LoadLiteral(arm::R1, literal);
714  Label label;
715  __ Bind(&label);
716  constexpr size_t kLdrR0R0Count = (1u << 15) - 1u;
717  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
718    __ ldr(arm::R0, arm::Address(arm::R0));
719  }
720
721  std::string expected =
722      "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n"
723      "1:\n"
724      "add r1, pc\n"
725      "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" +
726      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
727      ".align 2, 0\n"
728      "2:\n"
729      ".word 0x12345678\n";
730  DriverStr(expected, "LoadLiteralBeyondMax64KiB");
731
732  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
733            __ GetAdjustedPosition(label.Position()));
734}
735
736TEST_F(AssemblerThumb2Test, LoadLiteralMax1MiB) {
737  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
738  __ LoadLiteral(arm::R1, literal);
739  Label label;
740  __ Bind(&label);
741  constexpr size_t kLdrR0R0Count = (1u << 19) - 3u;
742  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
743    __ ldr(arm::R0, arm::Address(arm::R0));
744  }
745
746  std::string expected =
747      "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n"
748      "1:\n"
749      "add r1, pc\n"
750      "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" +
751      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
752      ".align 2, 0\n"
753      "2:\n"
754      ".word 0x12345678\n";
755  DriverStr(expected, "LoadLiteralMax1MiB");
756
757  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
758            __ GetAdjustedPosition(label.Position()));
759}
760
761TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1MiB) {
762  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
763  __ LoadLiteral(arm::R1, literal);
764  Label label;
765  __ Bind(&label);
766  constexpr size_t kLdrR0R0Count = (1u << 19) - 2u;
767  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
768    __ ldr(arm::R0, arm::Address(arm::R0));
769  }
770
771  std::string expected =
772      // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
773      "movw r1, #(0x100000 & 0xffff)\n"
774      // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
775      "movt r1, #(0x100000 >> 16)\n"
776      "1:\n"
777      "add r1, pc\n"
778      "ldr.w r1, [r1, #0]\n" +
779      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
780      ".align 2, 0\n"
781      "2:\n"
782      ".word 0x12345678\n";
783  DriverStr(expected, "LoadLiteralBeyondMax1MiB");
784
785  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
786            __ GetAdjustedPosition(label.Position()));
787}
788
789TEST_F(AssemblerThumb2Test, LoadLiteralFar) {
790  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
791  __ LoadLiteral(arm::R1, literal);
792  Label label;
793  __ Bind(&label);
794  constexpr size_t kLdrR0R0Count = (1u << 19) - 2u + 0x1234;
795  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
796    __ ldr(arm::R0, arm::Address(arm::R0));
797  }
798
799  std::string expected =
800      // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
801      "movw r1, #((0x100000 + 2 * 0x1234) & 0xffff)\n"
802      // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
803      "movt r1, #((0x100000 + 2 * 0x1234) >> 16)\n"
804      "1:\n"
805      "add r1, pc\n"
806      "ldr.w r1, [r1, #0]\n" +
807      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
808      ".align 2, 0\n"
809      "2:\n"
810      ".word 0x12345678\n";
811  DriverStr(expected, "LoadLiteralFar");
812
813  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
814            __ GetAdjustedPosition(label.Position()));
815}
816
817TEST_F(AssemblerThumb2Test, LoadLiteralWideMax1KiB) {
818  arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
819  __ LoadLiteral(arm::R1, arm::R3, literal);
820  Label label;
821  __ Bind(&label);
822  constexpr size_t kLdrR0R0Count = 510;
823  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
824    __ ldr(arm::R0, arm::Address(arm::R0));
825  }
826
827  std::string expected =
828      "1:\n"
829      "ldrd r1, r3, [pc, #((2f - 1b - 2) & ~2)]\n" +
830      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
831      ".align 2, 0\n"
832      "2:\n"
833      ".word 0x87654321\n"
834      ".word 0x12345678\n";
835  DriverStr(expected, "LoadLiteralWideMax1KiB");
836
837  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
838            __ GetAdjustedPosition(label.Position()));
839}
840
841TEST_F(AssemblerThumb2Test, LoadLiteralWideBeyondMax1KiB) {
842  arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
843  __ LoadLiteral(arm::R1, arm::R3, literal);
844  Label label;
845  __ Bind(&label);
846  constexpr size_t kLdrR0R0Count = 511;
847  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
848    __ ldr(arm::R0, arm::Address(arm::R0));
849  }
850
851  std::string expected =
852      "mov.w ip, #((2f - 1f - 4) & ~0x3ff)\n"
853      "1:\n"
854      "add ip, pc\n"
855      "ldrd r1, r3, [ip, #((2f - 1b - 4) & 0x3ff)]\n" +
856      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
857      ".align 2, 0\n"
858      "2:\n"
859      ".word 0x87654321\n"
860      ".word 0x12345678\n";
861  DriverStr(expected, "LoadLiteralWideBeyondMax1KiB");
862
863  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
864            __ GetAdjustedPosition(label.Position()));
865}
866
867TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax256KiB) {
868  // The literal size must match but the type doesn't, so use an int32_t rather than float.
869  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
870  __ LoadLiteral(arm::S3, literal);
871  Label label;
872  __ Bind(&label);
873  constexpr size_t kLdrR0R0Count = (1 << 17) - 3u;
874  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
875    __ ldr(arm::R0, arm::Address(arm::R0));
876  }
877
878  std::string expected =
879      "mov.w ip, #((2f - 1f - 4) & ~0x3ff)\n"
880      "1:\n"
881      "add ip, pc\n"
882      "vldr s3, [ip, #((2f - 1b - 4) & 0x3ff)]\n" +
883      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
884      ".align 2, 0\n"
885      "2:\n"
886      ".word 0x12345678\n";
887  DriverStr(expected, "LoadLiteralSingleMax256KiB");
888
889  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
890            __ GetAdjustedPosition(label.Position()));
891}
892
893TEST_F(AssemblerThumb2Test, LoadLiteralDoubleBeyondMax256KiB) {
894  // The literal size must match but the type doesn't, so use an int64_t rather than double.
895  arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
896  __ LoadLiteral(arm::D3, literal);
897  Label label;
898  __ Bind(&label);
899  constexpr size_t kLdrR0R0Count = (1 << 17) - 2u;
900  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
901    __ ldr(arm::R0, arm::Address(arm::R0));
902  }
903
904  std::string expected =
905      // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
906      "movw ip, #(0x40000 & 0xffff)\n"
907      // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
908      "movt ip, #(0x40000 >> 16)\n"
909      "1:\n"
910      "add ip, pc\n"
911      "vldr d3, [ip, #0]\n" +
912      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
913      ".align 2, 0\n"
914      "2:\n"
915      ".word 0x87654321\n"
916      ".word 0x12345678\n";
917  DriverStr(expected, "LoadLiteralDoubleBeyondMax256KiB");
918
919  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
920            __ GetAdjustedPosition(label.Position()));
921}
922
923TEST_F(AssemblerThumb2Test, LoadLiteralDoubleFar) {
924  // The literal size must match but the type doesn't, so use an int64_t rather than double.
925  arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
926  __ LoadLiteral(arm::D3, literal);
927  Label label;
928  __ Bind(&label);
929  constexpr size_t kLdrR0R0Count = (1 << 17) - 2u + 0x1234;
930  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
931    __ ldr(arm::R0, arm::Address(arm::R0));
932  }
933
934  std::string expected =
935      // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
936      "movw ip, #((0x40000 + 2 * 0x1234) & 0xffff)\n"
937      // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
938      "movt ip, #((0x40000 + 2 * 0x1234) >> 16)\n"
939      "1:\n"
940      "add ip, pc\n"
941      "vldr d3, [ip, #0]\n" +
942      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
943      ".align 2, 0\n"
944      "2:\n"
945      ".word 0x87654321\n"
946      ".word 0x12345678\n";
947  DriverStr(expected, "LoadLiteralDoubleFar");
948
949  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
950            __ GetAdjustedPosition(label.Position()));
951}
952
953TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass) {
954  // First part: as TwoCbzBeyondMaxOffset but add one 16-bit instruction to the end,
955  // so that the size is not Aligned<4>(.). On the first pass, the assembler resizes
956  // the second CBZ because it's out of range, then it will resize the first CBZ
957  // which has been pushed out of range. Thus, after the first pass, the code size
958  // will appear Aligned<4>(.) but the final size will not be.
959  Label label0, label1, label2;
960  __ cbz(arm::R0, &label1);
961  constexpr size_t kLdrR0R0Count1 = 63;
962  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
963    __ ldr(arm::R0, arm::Address(arm::R0));
964  }
965  __ Bind(&label0);
966  __ cbz(arm::R0, &label2);
967  __ Bind(&label1);
968  constexpr size_t kLdrR0R0Count2 = 65;
969  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
970    __ ldr(arm::R0, arm::Address(arm::R0));
971  }
972  __ Bind(&label2);
973  __ ldr(arm::R0, arm::Address(arm::R0));
974
975  std::string expected_part1 =
976      "cmp r0, #0\n"              // cbz r0, label1
977      "beq.n 1f\n" +
978      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
979      "0:\n"
980      "cmp r0, #0\n"              // cbz r0, label2
981      "beq.n 2f\n"
982      "1:\n" +
983      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
984      "2:\n"                      // Here the offset is Aligned<4>(.).
985      "ldr r0, [r0]\n";           // Make the first part
986
987  // Second part: as LoadLiteralMax1KiB with the caveat that the offset of the load
988  // literal will not be Aligned<4>(.) but it will appear to be when we process the
989  // instruction during the first pass, so the literal will need a padding and it
990  // will push the literal out of range, so we shall end up with "ldr.w".
991  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
992  __ LoadLiteral(arm::R0, literal);
993  Label label;
994  __ Bind(&label);
995  constexpr size_t kLdrR0R0Count = 511;
996  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
997    __ ldr(arm::R0, arm::Address(arm::R0));
998  }
999
1000  std::string expected =
1001      expected_part1 +
1002      "1:\n"
1003      "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
1004      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1005      ".align 2, 0\n"
1006      "2:\n"
1007      ".word 0x12345678\n";
1008  DriverStr(expected, "LoadLiteralMax1KiB");
1009
1010  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
1011            __ GetAdjustedPosition(label.Position()));
1012}
1013
1014TEST_F(AssemblerThumb2Test, Clz) {
1015  __ clz(arm::R0, arm::R1);
1016
1017  const char* expected = "clz r0, r1\n";
1018
1019  DriverStr(expected, "clz");
1020}
1021
1022TEST_F(AssemblerThumb2Test, rbit) {
1023  __ rbit(arm::R1, arm::R0);
1024
1025  const char* expected = "rbit r1, r0\n";
1026
1027  DriverStr(expected, "rbit");
1028}
1029
1030}  // namespace art
1031