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