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 "android-base/stringprintf.h"
20
21 #include "base/stl_util.h"
22 #include "utils/assembler_test.h"
23
24 namespace art {
25
26 using android::base::StringPrintf;
27
28 class AssemblerThumb2Test : public AssemblerTest<arm::Thumb2Assembler,
29 arm::Register, arm::SRegister,
30 uint32_t> {
31 protected:
GetArchitectureString()32 std::string GetArchitectureString() OVERRIDE {
33 return "arm";
34 }
35
GetAssemblerParameters()36 std::string GetAssemblerParameters() OVERRIDE {
37 return " -march=armv7-a -mcpu=cortex-a15 -mfpu=neon -mthumb";
38 }
39
GetAssemblyHeader()40 const char* GetAssemblyHeader() OVERRIDE {
41 return kThumb2AssemblyHeader;
42 }
43
GetDisassembleParameters()44 std::string GetDisassembleParameters() OVERRIDE {
45 return " -D -bbinary -marm --disassembler-options=force-thumb --no-show-raw-insn";
46 }
47
SetUpHelpers()48 void SetUpHelpers() OVERRIDE {
49 if (registers_.size() == 0) {
50 registers_.insert(end(registers_),
51 { // NOLINT(whitespace/braces)
52 new arm::Register(arm::R0),
53 new arm::Register(arm::R1),
54 new arm::Register(arm::R2),
55 new arm::Register(arm::R3),
56 new arm::Register(arm::R4),
57 new arm::Register(arm::R5),
58 new arm::Register(arm::R6),
59 new arm::Register(arm::R7),
60 new arm::Register(arm::R8),
61 new arm::Register(arm::R9),
62 new arm::Register(arm::R10),
63 new arm::Register(arm::R11),
64 new arm::Register(arm::R12),
65 new arm::Register(arm::R13),
66 new arm::Register(arm::R14),
67 new arm::Register(arm::R15)
68 });
69 }
70 }
71
TearDown()72 void TearDown() OVERRIDE {
73 AssemblerTest::TearDown();
74 STLDeleteElements(®isters_);
75 }
76
GetRegisters()77 std::vector<arm::Register*> GetRegisters() OVERRIDE {
78 return registers_;
79 }
80
CreateImmediate(int64_t imm_value)81 uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
82 return imm_value;
83 }
84
RepeatInsn(size_t count,const std::string & insn)85 std::string RepeatInsn(size_t count, const std::string& insn) {
86 std::string result;
87 for (; count != 0u; --count) {
88 result += insn;
89 }
90 return result;
91 }
92
93 private:
94 std::vector<arm::Register*> registers_;
95
96 static constexpr const char* kThumb2AssemblyHeader = ".syntax unified\n.thumb\n";
97 };
98
TEST_F(AssemblerThumb2Test,Toolchain)99 TEST_F(AssemblerThumb2Test, Toolchain) {
100 EXPECT_TRUE(CheckTools());
101 }
102
103 #define __ GetAssembler()->
104
TEST_F(AssemblerThumb2Test,Sbfx)105 TEST_F(AssemblerThumb2Test, Sbfx) {
106 __ sbfx(arm::R0, arm::R1, 0, 1);
107 __ sbfx(arm::R0, arm::R1, 0, 8);
108 __ sbfx(arm::R0, arm::R1, 0, 16);
109 __ sbfx(arm::R0, arm::R1, 0, 32);
110
111 __ sbfx(arm::R0, arm::R1, 8, 1);
112 __ sbfx(arm::R0, arm::R1, 8, 8);
113 __ sbfx(arm::R0, arm::R1, 8, 16);
114 __ sbfx(arm::R0, arm::R1, 8, 24);
115
116 __ sbfx(arm::R0, arm::R1, 16, 1);
117 __ sbfx(arm::R0, arm::R1, 16, 8);
118 __ sbfx(arm::R0, arm::R1, 16, 16);
119
120 __ sbfx(arm::R0, arm::R1, 31, 1);
121
122 const char* expected =
123 "sbfx r0, r1, #0, #1\n"
124 "sbfx r0, r1, #0, #8\n"
125 "sbfx r0, r1, #0, #16\n"
126 "sbfx r0, r1, #0, #32\n"
127
128 "sbfx r0, r1, #8, #1\n"
129 "sbfx r0, r1, #8, #8\n"
130 "sbfx r0, r1, #8, #16\n"
131 "sbfx r0, r1, #8, #24\n"
132
133 "sbfx r0, r1, #16, #1\n"
134 "sbfx r0, r1, #16, #8\n"
135 "sbfx r0, r1, #16, #16\n"
136
137 "sbfx r0, r1, #31, #1\n";
138 DriverStr(expected, "sbfx");
139 }
140
TEST_F(AssemblerThumb2Test,Ubfx)141 TEST_F(AssemblerThumb2Test, Ubfx) {
142 __ ubfx(arm::R0, arm::R1, 0, 1);
143 __ ubfx(arm::R0, arm::R1, 0, 8);
144 __ ubfx(arm::R0, arm::R1, 0, 16);
145 __ ubfx(arm::R0, arm::R1, 0, 32);
146
147 __ ubfx(arm::R0, arm::R1, 8, 1);
148 __ ubfx(arm::R0, arm::R1, 8, 8);
149 __ ubfx(arm::R0, arm::R1, 8, 16);
150 __ ubfx(arm::R0, arm::R1, 8, 24);
151
152 __ ubfx(arm::R0, arm::R1, 16, 1);
153 __ ubfx(arm::R0, arm::R1, 16, 8);
154 __ ubfx(arm::R0, arm::R1, 16, 16);
155
156 __ ubfx(arm::R0, arm::R1, 31, 1);
157
158 const char* expected =
159 "ubfx r0, r1, #0, #1\n"
160 "ubfx r0, r1, #0, #8\n"
161 "ubfx r0, r1, #0, #16\n"
162 "ubfx r0, r1, #0, #32\n"
163
164 "ubfx r0, r1, #8, #1\n"
165 "ubfx r0, r1, #8, #8\n"
166 "ubfx r0, r1, #8, #16\n"
167 "ubfx r0, r1, #8, #24\n"
168
169 "ubfx r0, r1, #16, #1\n"
170 "ubfx r0, r1, #16, #8\n"
171 "ubfx r0, r1, #16, #16\n"
172
173 "ubfx r0, r1, #31, #1\n";
174 DriverStr(expected, "ubfx");
175 }
176
TEST_F(AssemblerThumb2Test,Vmstat)177 TEST_F(AssemblerThumb2Test, Vmstat) {
178 __ vmstat();
179
180 const char* expected = "vmrs APSR_nzcv, FPSCR\n";
181
182 DriverStr(expected, "vmrs");
183 }
184
TEST_F(AssemblerThumb2Test,ldrexd)185 TEST_F(AssemblerThumb2Test, ldrexd) {
186 __ ldrexd(arm::R0, arm::R1, arm::R0);
187 __ ldrexd(arm::R0, arm::R1, arm::R1);
188 __ ldrexd(arm::R0, arm::R1, arm::R2);
189 __ ldrexd(arm::R5, arm::R3, arm::R7);
190
191 const char* expected =
192 "ldrexd r0, r1, [r0]\n"
193 "ldrexd r0, r1, [r1]\n"
194 "ldrexd r0, r1, [r2]\n"
195 "ldrexd r5, r3, [r7]\n";
196 DriverStr(expected, "ldrexd");
197 }
198
TEST_F(AssemblerThumb2Test,strexd)199 TEST_F(AssemblerThumb2Test, strexd) {
200 __ strexd(arm::R9, arm::R0, arm::R1, arm::R0);
201 __ strexd(arm::R9, arm::R0, arm::R1, arm::R1);
202 __ strexd(arm::R9, arm::R0, arm::R1, arm::R2);
203 __ strexd(arm::R9, arm::R5, arm::R3, arm::R7);
204
205 const char* expected =
206 "strexd r9, r0, r1, [r0]\n"
207 "strexd r9, r0, r1, [r1]\n"
208 "strexd r9, r0, r1, [r2]\n"
209 "strexd r9, r5, r3, [r7]\n";
210 DriverStr(expected, "strexd");
211 }
212
TEST_F(AssemblerThumb2Test,clrex)213 TEST_F(AssemblerThumb2Test, clrex) {
214 __ clrex();
215
216 const char* expected = "clrex\n";
217 DriverStr(expected, "clrex");
218 }
219
TEST_F(AssemblerThumb2Test,LdrdStrd)220 TEST_F(AssemblerThumb2Test, LdrdStrd) {
221 __ ldrd(arm::R0, arm::Address(arm::R2, 8));
222 __ ldrd(arm::R0, arm::Address(arm::R12));
223 __ strd(arm::R0, arm::Address(arm::R2, 8));
224
225 const char* expected =
226 "ldrd r0, r1, [r2, #8]\n"
227 "ldrd r0, r1, [r12]\n"
228 "strd r0, r1, [r2, #8]\n";
229 DriverStr(expected, "ldrdstrd");
230 }
231
TEST_F(AssemblerThumb2Test,eor)232 TEST_F(AssemblerThumb2Test, eor) {
233 __ eor(arm::R1, arm::R1, arm::ShifterOperand(arm::R0));
234 __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R1));
235 __ eor(arm::R1, arm::R8, arm::ShifterOperand(arm::R0));
236 __ eor(arm::R8, arm::R1, arm::ShifterOperand(arm::R0));
237 __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R8));
238
239 const char* expected =
240 "eors r1, r0\n"
241 "eor r1, r0, r1\n"
242 "eor r1, r8, r0\n"
243 "eor r8, r1, r0\n"
244 "eor r1, r0, r8\n";
245 DriverStr(expected, "abs");
246 }
247
TEST_F(AssemblerThumb2Test,sub)248 TEST_F(AssemblerThumb2Test, sub) {
249 __ subs(arm::R1, arm::R0, arm::ShifterOperand(42));
250 __ sub(arm::R1, arm::R0, arm::ShifterOperand(42));
251 __ subs(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
252 __ sub(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
253
254 const char* expected =
255 "subs r1, r0, #42\n"
256 "sub.w r1, r0, #42\n"
257 "subs r1, r0, r2, asr #31\n"
258 "sub r1, r0, r2, asr #31\n";
259 DriverStr(expected, "sub");
260 }
261
TEST_F(AssemblerThumb2Test,add)262 TEST_F(AssemblerThumb2Test, add) {
263 __ adds(arm::R1, arm::R0, arm::ShifterOperand(42));
264 __ add(arm::R1, arm::R0, arm::ShifterOperand(42));
265 __ adds(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
266 __ add(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
267
268 const char* expected =
269 "adds r1, r0, #42\n"
270 "add.w r1, r0, #42\n"
271 "adds r1, r0, r2, asr #31\n"
272 "add r1, r0, r2, asr #31\n";
273 DriverStr(expected, "add");
274 }
275
TEST_F(AssemblerThumb2Test,umull)276 TEST_F(AssemblerThumb2Test, umull) {
277 __ umull(arm::R0, arm::R1, arm::R2, arm::R3);
278
279 const char* expected =
280 "umull r0, r1, r2, r3\n";
281 DriverStr(expected, "umull");
282 }
283
TEST_F(AssemblerThumb2Test,smull)284 TEST_F(AssemblerThumb2Test, smull) {
285 __ smull(arm::R0, arm::R1, arm::R2, arm::R3);
286
287 const char* expected =
288 "smull r0, r1, r2, r3\n";
289 DriverStr(expected, "smull");
290 }
291
TEST_F(AssemblerThumb2Test,LoadByteFromThumbOffset)292 TEST_F(AssemblerThumb2Test, LoadByteFromThumbOffset) {
293 arm::LoadOperandType type = arm::kLoadUnsignedByte;
294
295 __ LoadFromOffset(type, arm::R0, arm::R7, 0);
296 __ LoadFromOffset(type, arm::R1, arm::R7, 31);
297 __ LoadFromOffset(type, arm::R2, arm::R7, 32);
298 __ LoadFromOffset(type, arm::R3, arm::R7, 4095);
299 __ LoadFromOffset(type, arm::R4, arm::SP, 0);
300
301 const char* expected =
302 "ldrb r0, [r7, #0]\n"
303 "ldrb r1, [r7, #31]\n"
304 "ldrb.w r2, [r7, #32]\n"
305 "ldrb.w r3, [r7, #4095]\n"
306 "ldrb.w r4, [sp, #0]\n";
307 DriverStr(expected, "LoadByteFromThumbOffset");
308 }
309
TEST_F(AssemblerThumb2Test,StoreByteToThumbOffset)310 TEST_F(AssemblerThumb2Test, StoreByteToThumbOffset) {
311 arm::StoreOperandType type = arm::kStoreByte;
312
313 __ StoreToOffset(type, arm::R0, arm::R7, 0);
314 __ StoreToOffset(type, arm::R1, arm::R7, 31);
315 __ StoreToOffset(type, arm::R2, arm::R7, 32);
316 __ StoreToOffset(type, arm::R3, arm::R7, 4095);
317 __ StoreToOffset(type, arm::R4, arm::SP, 0);
318
319 const char* expected =
320 "strb r0, [r7, #0]\n"
321 "strb r1, [r7, #31]\n"
322 "strb.w r2, [r7, #32]\n"
323 "strb.w r3, [r7, #4095]\n"
324 "strb.w r4, [sp, #0]\n";
325 DriverStr(expected, "StoreByteToThumbOffset");
326 }
327
TEST_F(AssemblerThumb2Test,LoadHalfFromThumbOffset)328 TEST_F(AssemblerThumb2Test, LoadHalfFromThumbOffset) {
329 arm::LoadOperandType type = arm::kLoadUnsignedHalfword;
330
331 __ LoadFromOffset(type, arm::R0, arm::R7, 0);
332 __ LoadFromOffset(type, arm::R1, arm::R7, 62);
333 __ LoadFromOffset(type, arm::R2, arm::R7, 64);
334 __ LoadFromOffset(type, arm::R3, arm::R7, 4094);
335 __ LoadFromOffset(type, arm::R4, arm::SP, 0);
336 __ LoadFromOffset(type, arm::R5, arm::R7, 1); // Unaligned
337
338 const char* expected =
339 "ldrh r0, [r7, #0]\n"
340 "ldrh r1, [r7, #62]\n"
341 "ldrh.w r2, [r7, #64]\n"
342 "ldrh.w r3, [r7, #4094]\n"
343 "ldrh.w r4, [sp, #0]\n"
344 "ldrh.w r5, [r7, #1]\n";
345 DriverStr(expected, "LoadHalfFromThumbOffset");
346 }
347
TEST_F(AssemblerThumb2Test,StoreHalfToThumbOffset)348 TEST_F(AssemblerThumb2Test, StoreHalfToThumbOffset) {
349 arm::StoreOperandType type = arm::kStoreHalfword;
350
351 __ StoreToOffset(type, arm::R0, arm::R7, 0);
352 __ StoreToOffset(type, arm::R1, arm::R7, 62);
353 __ StoreToOffset(type, arm::R2, arm::R7, 64);
354 __ StoreToOffset(type, arm::R3, arm::R7, 4094);
355 __ StoreToOffset(type, arm::R4, arm::SP, 0);
356 __ StoreToOffset(type, arm::R5, arm::R7, 1); // Unaligned
357
358 const char* expected =
359 "strh r0, [r7, #0]\n"
360 "strh r1, [r7, #62]\n"
361 "strh.w r2, [r7, #64]\n"
362 "strh.w r3, [r7, #4094]\n"
363 "strh.w r4, [sp, #0]\n"
364 "strh.w r5, [r7, #1]\n";
365 DriverStr(expected, "StoreHalfToThumbOffset");
366 }
367
TEST_F(AssemblerThumb2Test,LoadWordFromSpPlusOffset)368 TEST_F(AssemblerThumb2Test, LoadWordFromSpPlusOffset) {
369 arm::LoadOperandType type = arm::kLoadWord;
370
371 __ LoadFromOffset(type, arm::R0, arm::SP, 0);
372 __ LoadFromOffset(type, arm::R1, arm::SP, 124);
373 __ LoadFromOffset(type, arm::R2, arm::SP, 128);
374 __ LoadFromOffset(type, arm::R3, arm::SP, 1020);
375 __ LoadFromOffset(type, arm::R4, arm::SP, 1024);
376 __ LoadFromOffset(type, arm::R5, arm::SP, 4092);
377 __ LoadFromOffset(type, arm::R6, arm::SP, 1); // Unaligned
378
379 const char* expected =
380 "ldr r0, [sp, #0]\n"
381 "ldr r1, [sp, #124]\n"
382 "ldr r2, [sp, #128]\n"
383 "ldr r3, [sp, #1020]\n"
384 "ldr.w r4, [sp, #1024]\n"
385 "ldr.w r5, [sp, #4092]\n"
386 "ldr.w r6, [sp, #1]\n";
387 DriverStr(expected, "LoadWordFromSpPlusOffset");
388 }
389
TEST_F(AssemblerThumb2Test,StoreWordToSpPlusOffset)390 TEST_F(AssemblerThumb2Test, StoreWordToSpPlusOffset) {
391 arm::StoreOperandType type = arm::kStoreWord;
392
393 __ StoreToOffset(type, arm::R0, arm::SP, 0);
394 __ StoreToOffset(type, arm::R1, arm::SP, 124);
395 __ StoreToOffset(type, arm::R2, arm::SP, 128);
396 __ StoreToOffset(type, arm::R3, arm::SP, 1020);
397 __ StoreToOffset(type, arm::R4, arm::SP, 1024);
398 __ StoreToOffset(type, arm::R5, arm::SP, 4092);
399 __ StoreToOffset(type, arm::R6, arm::SP, 1); // Unaligned
400
401 const char* expected =
402 "str r0, [sp, #0]\n"
403 "str r1, [sp, #124]\n"
404 "str r2, [sp, #128]\n"
405 "str r3, [sp, #1020]\n"
406 "str.w r4, [sp, #1024]\n"
407 "str.w r5, [sp, #4092]\n"
408 "str.w r6, [sp, #1]\n";
409 DriverStr(expected, "StoreWordToSpPlusOffset");
410 }
411
TEST_F(AssemblerThumb2Test,LoadWordFromPcPlusOffset)412 TEST_F(AssemblerThumb2Test, LoadWordFromPcPlusOffset) {
413 arm::LoadOperandType type = arm::kLoadWord;
414
415 __ LoadFromOffset(type, arm::R0, arm::PC, 0);
416 __ LoadFromOffset(type, arm::R1, arm::PC, 124);
417 __ LoadFromOffset(type, arm::R2, arm::PC, 128);
418 __ LoadFromOffset(type, arm::R3, arm::PC, 1020);
419 __ LoadFromOffset(type, arm::R4, arm::PC, 1024);
420 __ LoadFromOffset(type, arm::R5, arm::PC, 4092);
421 __ LoadFromOffset(type, arm::R6, arm::PC, 1); // Unaligned
422
423 const char* expected =
424 "ldr r0, [pc, #0]\n"
425 "ldr r1, [pc, #124]\n"
426 "ldr r2, [pc, #128]\n"
427 "ldr r3, [pc, #1020]\n"
428 "ldr.w r4, [pc, #1024]\n"
429 "ldr.w r5, [pc, #4092]\n"
430 "ldr.w r6, [pc, #1]\n";
431 DriverStr(expected, "LoadWordFromPcPlusOffset");
432 }
433
TEST_F(AssemblerThumb2Test,StoreWordToThumbOffset)434 TEST_F(AssemblerThumb2Test, StoreWordToThumbOffset) {
435 arm::StoreOperandType type = arm::kStoreWord;
436 int32_t offset = 4092;
437 ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
438
439 __ StoreToOffset(type, arm::R0, arm::SP, offset);
440 __ StoreToOffset(type, arm::IP, arm::SP, offset);
441 __ StoreToOffset(type, arm::IP, arm::R5, offset);
442
443 const char* expected =
444 "str r0, [sp, #4092]\n"
445 "str ip, [sp, #4092]\n"
446 "str ip, [r5, #4092]\n";
447 DriverStr(expected, "StoreWordToThumbOffset");
448 }
449
TEST_F(AssemblerThumb2Test,StoreWordToNonThumbOffset)450 TEST_F(AssemblerThumb2Test, StoreWordToNonThumbOffset) {
451 arm::StoreOperandType type = arm::kStoreWord;
452 int32_t offset = 4096;
453 ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
454
455 __ StoreToOffset(type, arm::R0, arm::SP, offset);
456 __ StoreToOffset(type, arm::IP, arm::SP, offset);
457 __ StoreToOffset(type, arm::IP, arm::R5, offset);
458
459 const char* expected =
460 "add.w ip, sp, #4096\n" // AddConstant(ip, sp, 4096)
461 "str r0, [ip, #0]\n"
462
463 "str r5, [sp, #-4]!\n" // Push(r5)
464 "add.w r5, sp, #4096\n" // AddConstant(r5, 4100 & ~0xfff)
465 "str ip, [r5, #4]\n" // StoreToOffset(type, ip, r5, 4100 & 0xfff)
466 "ldr r5, [sp], #4\n" // Pop(r5)
467
468 "str r6, [sp, #-4]!\n" // Push(r6)
469 "add.w r6, r5, #4096\n" // AddConstant(r6, r5, 4096 & ~0xfff)
470 "str ip, [r6, #0]\n" // StoreToOffset(type, ip, r6, 4096 & 0xfff)
471 "ldr r6, [sp], #4\n"; // Pop(r6)
472 DriverStr(expected, "StoreWordToNonThumbOffset");
473 }
474
TEST_F(AssemblerThumb2Test,StoreWordPairToThumbOffset)475 TEST_F(AssemblerThumb2Test, StoreWordPairToThumbOffset) {
476 arm::StoreOperandType type = arm::kStoreWordPair;
477 int32_t offset = 1020;
478 ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
479
480 __ StoreToOffset(type, arm::R0, arm::SP, offset);
481 // We cannot use IP (i.e. R12) as first source register, as it would
482 // force us to use SP (i.e. R13) as second source register, which
483 // would have an "unpredictable" effect according to the ARMv7
484 // specification (the T1 encoding describes the result as
485 // UNPREDICTABLE when of the source registers is R13).
486 //
487 // So we use (R11, IP) (e.g. (R11, R12)) as source registers in the
488 // following instructions.
489 __ StoreToOffset(type, arm::R11, arm::SP, offset);
490 __ StoreToOffset(type, arm::R11, arm::R5, offset);
491
492 const char* expected =
493 "strd r0, r1, [sp, #1020]\n"
494 "strd r11, ip, [sp, #1020]\n"
495 "strd r11, ip, [r5, #1020]\n";
496 DriverStr(expected, "StoreWordPairToThumbOffset");
497 }
498
TEST_F(AssemblerThumb2Test,StoreWordPairToNonThumbOffset)499 TEST_F(AssemblerThumb2Test, StoreWordPairToNonThumbOffset) {
500 arm::StoreOperandType type = arm::kStoreWordPair;
501 int32_t offset = 1024;
502 ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
503
504 __ StoreToOffset(type, arm::R0, arm::SP, offset);
505 // Same comment as in AssemblerThumb2Test.StoreWordPairToThumbOffset
506 // regarding the use of (R11, IP) (e.g. (R11, R12)) as source
507 // registers in the following instructions.
508 __ StoreToOffset(type, arm::R11, arm::SP, offset);
509 __ StoreToOffset(type, arm::R11, arm::R5, offset);
510
511 const char* expected =
512 "add.w ip, sp, #1024\n" // AddConstant(ip, sp, 1024)
513 "strd r0, r1, [ip, #0]\n"
514
515 "str r5, [sp, #-4]!\n" // Push(r5)
516 "add.w r5, sp, #1024\n" // AddConstant(r5, sp, (1024 + kRegisterSize) & ~0x3fc)
517 "strd r11, ip, [r5, #4]\n" // StoreToOffset(type, r11, sp, (1024 + kRegisterSize) & 0x3fc)
518 "ldr r5, [sp], #4\n" // Pop(r5)
519
520 "str r6, [sp, #-4]!\n" // Push(r6)
521 "add.w r6, r5, #1024\n" // AddConstant(r6, r5, 1024 & ~0x3fc)
522 "strd r11, ip, [r6, #0]\n" // StoreToOffset(type, r11, r6, 1024 & 0x3fc)
523 "ldr r6, [sp], #4\n"; // Pop(r6)
524 DriverStr(expected, "StoreWordPairToNonThumbOffset");
525 }
526
TEST_F(AssemblerThumb2Test,DistantBackBranch)527 TEST_F(AssemblerThumb2Test, DistantBackBranch) {
528 Label start, end;
529 __ Bind(&start);
530 constexpr size_t kLdrR0R0Count1 = 256;
531 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
532 __ ldr(arm::R0, arm::Address(arm::R0));
533 }
534 __ b(&end, arm::EQ);
535 __ b(&start, arm::LT);
536 constexpr size_t kLdrR0R0Count2 = 256;
537 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
538 __ ldr(arm::R0, arm::Address(arm::R0));
539 }
540 __ Bind(&end);
541
542 std::string expected =
543 "0:\n" +
544 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
545 "beq 1f\n"
546 "blt 0b\n" +
547 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
548 "1:\n";
549 DriverStr(expected, "DistantBackBranch");
550 }
551
TEST_F(AssemblerThumb2Test,TwoCbzMaxOffset)552 TEST_F(AssemblerThumb2Test, TwoCbzMaxOffset) {
553 Label label0, label1, label2;
554 __ cbz(arm::R0, &label1);
555 constexpr size_t kLdrR0R0Count1 = 63;
556 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
557 __ ldr(arm::R0, arm::Address(arm::R0));
558 }
559 __ Bind(&label0);
560 __ cbz(arm::R0, &label2);
561 __ Bind(&label1);
562 constexpr size_t kLdrR0R0Count2 = 64;
563 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
564 __ ldr(arm::R0, arm::Address(arm::R0));
565 }
566 __ Bind(&label2);
567
568 std::string expected =
569 "cbz r0, 1f\n" + // cbz r0, label1
570 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
571 "0:\n"
572 "cbz r0, 2f\n" // cbz r0, label2
573 "1:\n" +
574 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
575 "2:\n";
576 DriverStr(expected, "TwoCbzMaxOffset");
577
578 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
579 __ GetAdjustedPosition(label0.Position()));
580 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 0u,
581 __ GetAdjustedPosition(label1.Position()));
582 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 0u,
583 __ GetAdjustedPosition(label2.Position()));
584 }
585
TEST_F(AssemblerThumb2Test,TwoCbzBeyondMaxOffset)586 TEST_F(AssemblerThumb2Test, TwoCbzBeyondMaxOffset) {
587 Label label0, label1, label2;
588 __ cbz(arm::R0, &label1);
589 constexpr size_t kLdrR0R0Count1 = 63;
590 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
591 __ ldr(arm::R0, arm::Address(arm::R0));
592 }
593 __ Bind(&label0);
594 __ cbz(arm::R0, &label2);
595 __ Bind(&label1);
596 constexpr size_t kLdrR0R0Count2 = 65;
597 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
598 __ ldr(arm::R0, arm::Address(arm::R0));
599 }
600 __ Bind(&label2);
601
602 std::string expected =
603 "cmp r0, #0\n" // cbz r0, label1
604 "beq.n 1f\n" +
605 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
606 "0:\n"
607 "cmp r0, #0\n" // cbz r0, label2
608 "beq.n 2f\n"
609 "1:\n" +
610 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
611 "2:\n";
612 DriverStr(expected, "TwoCbzBeyondMaxOffset");
613
614 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
615 __ GetAdjustedPosition(label0.Position()));
616 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 4u,
617 __ GetAdjustedPosition(label1.Position()));
618 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 4u,
619 __ GetAdjustedPosition(label2.Position()));
620 }
621
TEST_F(AssemblerThumb2Test,TwoCbzSecondAtMaxB16Offset)622 TEST_F(AssemblerThumb2Test, TwoCbzSecondAtMaxB16Offset) {
623 Label label0, label1, label2;
624 __ cbz(arm::R0, &label1);
625 constexpr size_t kLdrR0R0Count1 = 62;
626 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
627 __ ldr(arm::R0, arm::Address(arm::R0));
628 }
629 __ Bind(&label0);
630 __ cbz(arm::R0, &label2);
631 __ Bind(&label1);
632 constexpr size_t kLdrR0R0Count2 = 128;
633 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
634 __ ldr(arm::R0, arm::Address(arm::R0));
635 }
636 __ Bind(&label2);
637
638 std::string expected =
639 "cbz r0, 1f\n" + // cbz r0, label1
640 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
641 "0:\n"
642 "cmp r0, #0\n" // cbz r0, label2
643 "beq.n 2f\n"
644 "1:\n" +
645 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
646 "2:\n";
647 DriverStr(expected, "TwoCbzSecondAtMaxB16Offset");
648
649 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
650 __ GetAdjustedPosition(label0.Position()));
651 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
652 __ GetAdjustedPosition(label1.Position()));
653 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
654 __ GetAdjustedPosition(label2.Position()));
655 }
656
TEST_F(AssemblerThumb2Test,TwoCbzSecondBeyondMaxB16Offset)657 TEST_F(AssemblerThumb2Test, TwoCbzSecondBeyondMaxB16Offset) {
658 Label label0, label1, label2;
659 __ cbz(arm::R0, &label1);
660 constexpr size_t kLdrR0R0Count1 = 62;
661 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
662 __ ldr(arm::R0, arm::Address(arm::R0));
663 }
664 __ Bind(&label0);
665 __ cbz(arm::R0, &label2);
666 __ Bind(&label1);
667 constexpr size_t kLdrR0R0Count2 = 129;
668 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
669 __ ldr(arm::R0, arm::Address(arm::R0));
670 }
671 __ Bind(&label2);
672
673 std::string expected =
674 "cmp r0, #0\n" // cbz r0, label1
675 "beq.n 1f\n" +
676 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
677 "0:\n"
678 "cmp r0, #0\n" // cbz r0, label2
679 "beq.w 2f\n"
680 "1:\n" +
681 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
682 "2:\n";
683 DriverStr(expected, "TwoCbzSecondBeyondMaxB16Offset");
684
685 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
686 __ GetAdjustedPosition(label0.Position()));
687 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
688 __ GetAdjustedPosition(label1.Position()));
689 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
690 __ GetAdjustedPosition(label2.Position()));
691 }
692
TEST_F(AssemblerThumb2Test,TwoCbzFirstAtMaxB16Offset)693 TEST_F(AssemblerThumb2Test, TwoCbzFirstAtMaxB16Offset) {
694 Label label0, label1, label2;
695 __ cbz(arm::R0, &label1);
696 constexpr size_t kLdrR0R0Count1 = 127;
697 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
698 __ ldr(arm::R0, arm::Address(arm::R0));
699 }
700 __ Bind(&label0);
701 __ cbz(arm::R0, &label2);
702 __ Bind(&label1);
703 constexpr size_t kLdrR0R0Count2 = 64;
704 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
705 __ ldr(arm::R0, arm::Address(arm::R0));
706 }
707 __ Bind(&label2);
708
709 std::string expected =
710 "cmp r0, #0\n" // cbz r0, label1
711 "beq.n 1f\n" +
712 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
713 "0:\n"
714 "cbz r0, 2f\n" // cbz r0, label2
715 "1:\n" +
716 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
717 "2:\n";
718 DriverStr(expected, "TwoCbzFirstAtMaxB16Offset");
719
720 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
721 __ GetAdjustedPosition(label0.Position()));
722 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
723 __ GetAdjustedPosition(label1.Position()));
724 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
725 __ GetAdjustedPosition(label2.Position()));
726 }
727
TEST_F(AssemblerThumb2Test,TwoCbzFirstBeyondMaxB16Offset)728 TEST_F(AssemblerThumb2Test, TwoCbzFirstBeyondMaxB16Offset) {
729 Label label0, label1, label2;
730 __ cbz(arm::R0, &label1);
731 constexpr size_t kLdrR0R0Count1 = 127;
732 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
733 __ ldr(arm::R0, arm::Address(arm::R0));
734 }
735 __ Bind(&label0);
736 __ cbz(arm::R0, &label2);
737 __ Bind(&label1);
738 constexpr size_t kLdrR0R0Count2 = 65;
739 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
740 __ ldr(arm::R0, arm::Address(arm::R0));
741 }
742 __ Bind(&label2);
743
744 std::string expected =
745 "cmp r0, #0\n" // cbz r0, label1
746 "beq.w 1f\n" +
747 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
748 "0:\n"
749 "cmp r0, #0\n" // cbz r0, label2
750 "beq.n 2f\n"
751 "1:\n" +
752 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
753 "2:\n";
754 DriverStr(expected, "TwoCbzFirstBeyondMaxB16Offset");
755
756 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 4u,
757 __ GetAdjustedPosition(label0.Position()));
758 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
759 __ GetAdjustedPosition(label1.Position()));
760 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
761 __ GetAdjustedPosition(label2.Position()));
762 }
763
TEST_F(AssemblerThumb2Test,LoadLiteralMax1KiB)764 TEST_F(AssemblerThumb2Test, LoadLiteralMax1KiB) {
765 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
766 __ LoadLiteral(arm::R0, literal);
767 Label label;
768 __ Bind(&label);
769 constexpr size_t kLdrR0R0Count = 511;
770 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
771 __ ldr(arm::R0, arm::Address(arm::R0));
772 }
773
774 std::string expected =
775 "1:\n"
776 "ldr.n r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
777 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
778 ".align 2, 0\n"
779 "2:\n"
780 ".word 0x12345678\n";
781 DriverStr(expected, "LoadLiteralMax1KiB");
782
783 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
784 __ GetAdjustedPosition(label.Position()));
785 }
786
TEST_F(AssemblerThumb2Test,LoadLiteralBeyondMax1KiB)787 TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiB) {
788 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
789 __ LoadLiteral(arm::R0, literal);
790 Label label;
791 __ Bind(&label);
792 constexpr size_t kLdrR0R0Count = 512;
793 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
794 __ ldr(arm::R0, arm::Address(arm::R0));
795 }
796
797 std::string expected =
798 "1:\n"
799 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
800 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
801 ".align 2, 0\n"
802 "2:\n"
803 ".word 0x12345678\n";
804 DriverStr(expected, "LoadLiteralBeyondMax1KiB");
805
806 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
807 __ GetAdjustedPosition(label.Position()));
808 }
809
TEST_F(AssemblerThumb2Test,LoadLiteralMax4KiB)810 TEST_F(AssemblerThumb2Test, LoadLiteralMax4KiB) {
811 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
812 __ LoadLiteral(arm::R1, literal);
813 Label label;
814 __ Bind(&label);
815 constexpr size_t kLdrR0R0Count = 2046;
816 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
817 __ ldr(arm::R0, arm::Address(arm::R0));
818 }
819
820 std::string expected =
821 "1:\n"
822 "ldr.w r1, [pc, #((2f - 1b - 2) & ~2)]\n" +
823 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
824 ".align 2, 0\n"
825 "2:\n"
826 ".word 0x12345678\n";
827 DriverStr(expected, "LoadLiteralMax4KiB");
828
829 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
830 __ GetAdjustedPosition(label.Position()));
831 }
832
TEST_F(AssemblerThumb2Test,LoadLiteralBeyondMax4KiB)833 TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax4KiB) {
834 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
835 __ LoadLiteral(arm::R1, literal);
836 Label label;
837 __ Bind(&label);
838 constexpr size_t kLdrR0R0Count = 2047;
839 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
840 __ ldr(arm::R0, arm::Address(arm::R0));
841 }
842
843 std::string expected =
844 "movw r1, #4096\n" // "as" does not consider (2f - 1f - 4) a constant expression for movw.
845 "1:\n"
846 "add r1, pc\n"
847 "ldr r1, [r1, #0]\n" +
848 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
849 ".align 2, 0\n"
850 "2:\n"
851 ".word 0x12345678\n";
852 DriverStr(expected, "LoadLiteralBeyondMax4KiB");
853
854 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
855 __ GetAdjustedPosition(label.Position()));
856 }
857
TEST_F(AssemblerThumb2Test,LoadLiteralMax64KiB)858 TEST_F(AssemblerThumb2Test, LoadLiteralMax64KiB) {
859 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
860 __ LoadLiteral(arm::R1, literal);
861 Label label;
862 __ Bind(&label);
863 constexpr size_t kLdrR0R0Count = (1u << 15) - 2u;
864 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
865 __ ldr(arm::R0, arm::Address(arm::R0));
866 }
867
868 std::string expected =
869 "movw r1, #0xfffc\n" // "as" does not consider (2f - 1f - 4) a constant expression for movw.
870 "1:\n"
871 "add r1, pc\n"
872 "ldr r1, [r1, #0]\n" +
873 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
874 ".align 2, 0\n"
875 "2:\n"
876 ".word 0x12345678\n";
877 DriverStr(expected, "LoadLiteralMax64KiB");
878
879 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
880 __ GetAdjustedPosition(label.Position()));
881 }
882
TEST_F(AssemblerThumb2Test,LoadLiteralBeyondMax64KiB)883 TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax64KiB) {
884 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
885 __ LoadLiteral(arm::R1, literal);
886 Label label;
887 __ Bind(&label);
888 constexpr size_t kLdrR0R0Count = (1u << 15) - 1u;
889 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
890 __ ldr(arm::R0, arm::Address(arm::R0));
891 }
892
893 std::string expected =
894 "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n"
895 "1:\n"
896 "add r1, pc\n"
897 "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" +
898 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
899 ".align 2, 0\n"
900 "2:\n"
901 ".word 0x12345678\n";
902 DriverStr(expected, "LoadLiteralBeyondMax64KiB");
903
904 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
905 __ GetAdjustedPosition(label.Position()));
906 }
907
TEST_F(AssemblerThumb2Test,LoadLiteralMax1MiB)908 TEST_F(AssemblerThumb2Test, LoadLiteralMax1MiB) {
909 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
910 __ LoadLiteral(arm::R1, literal);
911 Label label;
912 __ Bind(&label);
913 constexpr size_t kLdrR0R0Count = (1u << 19) - 3u;
914 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
915 __ ldr(arm::R0, arm::Address(arm::R0));
916 }
917
918 std::string expected =
919 "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n"
920 "1:\n"
921 "add r1, pc\n"
922 "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" +
923 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
924 ".align 2, 0\n"
925 "2:\n"
926 ".word 0x12345678\n";
927 DriverStr(expected, "LoadLiteralMax1MiB");
928
929 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
930 __ GetAdjustedPosition(label.Position()));
931 }
932
TEST_F(AssemblerThumb2Test,LoadLiteralBeyondMax1MiB)933 TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1MiB) {
934 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
935 __ LoadLiteral(arm::R1, literal);
936 Label label;
937 __ Bind(&label);
938 constexpr size_t kLdrR0R0Count = (1u << 19) - 2u;
939 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
940 __ ldr(arm::R0, arm::Address(arm::R0));
941 }
942
943 std::string expected =
944 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
945 "movw r1, #(0x100000 & 0xffff)\n"
946 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
947 "movt r1, #(0x100000 >> 16)\n"
948 "1:\n"
949 "add r1, pc\n"
950 "ldr.w r1, [r1, #0]\n" +
951 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
952 ".align 2, 0\n"
953 "2:\n"
954 ".word 0x12345678\n";
955 DriverStr(expected, "LoadLiteralBeyondMax1MiB");
956
957 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
958 __ GetAdjustedPosition(label.Position()));
959 }
960
TEST_F(AssemblerThumb2Test,LoadLiteralFar)961 TEST_F(AssemblerThumb2Test, LoadLiteralFar) {
962 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
963 __ LoadLiteral(arm::R1, literal);
964 Label label;
965 __ Bind(&label);
966 constexpr size_t kLdrR0R0Count = (1u << 19) - 2u + 0x1234;
967 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
968 __ ldr(arm::R0, arm::Address(arm::R0));
969 }
970
971 std::string expected =
972 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
973 "movw r1, #((0x100000 + 2 * 0x1234) & 0xffff)\n"
974 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
975 "movt r1, #((0x100000 + 2 * 0x1234) >> 16)\n"
976 "1:\n"
977 "add r1, pc\n"
978 "ldr.w r1, [r1, #0]\n" +
979 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
980 ".align 2, 0\n"
981 "2:\n"
982 ".word 0x12345678\n";
983 DriverStr(expected, "LoadLiteralFar");
984
985 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
986 __ GetAdjustedPosition(label.Position()));
987 }
988
TEST_F(AssemblerThumb2Test,LoadLiteralWideMax1KiB)989 TEST_F(AssemblerThumb2Test, LoadLiteralWideMax1KiB) {
990 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
991 __ LoadLiteral(arm::R1, arm::R3, literal);
992 Label label;
993 __ Bind(&label);
994 constexpr size_t kLdrR0R0Count = 510;
995 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
996 __ ldr(arm::R0, arm::Address(arm::R0));
997 }
998
999 std::string expected =
1000 "1:\n"
1001 "ldrd r1, r3, [pc, #((2f - 1b - 2) & ~2)]\n" +
1002 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1003 ".align 2, 0\n"
1004 "2:\n"
1005 ".word 0x87654321\n"
1006 ".word 0x12345678\n";
1007 DriverStr(expected, "LoadLiteralWideMax1KiB");
1008
1009 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
1010 __ GetAdjustedPosition(label.Position()));
1011 }
1012
TEST_F(AssemblerThumb2Test,LoadLiteralWideBeyondMax1KiB)1013 TEST_F(AssemblerThumb2Test, LoadLiteralWideBeyondMax1KiB) {
1014 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
1015 __ LoadLiteral(arm::R1, arm::R3, literal);
1016 Label label;
1017 __ Bind(&label);
1018 constexpr size_t kLdrR0R0Count = 511;
1019 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1020 __ ldr(arm::R0, arm::Address(arm::R0));
1021 }
1022
1023 std::string expected =
1024 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
1025 "movw ip, #(0x408 - 0x4 - 4)\n"
1026 "1:\n"
1027 "add ip, pc\n"
1028 "ldrd r1, r3, [ip, #0]\n" +
1029 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1030 ".align 2, 0\n"
1031 "2:\n"
1032 ".word 0x87654321\n"
1033 ".word 0x12345678\n";
1034 DriverStr(expected, "LoadLiteralWideBeyondMax1KiB");
1035
1036 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
1037 __ GetAdjustedPosition(label.Position()));
1038 }
1039
TEST_F(AssemblerThumb2Test,LoadLiteralSingleMax64KiB)1040 TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax64KiB) {
1041 // The literal size must match but the type doesn't, so use an int32_t rather than float.
1042 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
1043 __ LoadLiteral(arm::S3, literal);
1044 Label label;
1045 __ Bind(&label);
1046 constexpr size_t kLdrR0R0Count = (1 << 15) - 3u;
1047 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1048 __ ldr(arm::R0, arm::Address(arm::R0));
1049 }
1050
1051 std::string expected =
1052 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
1053 "movw ip, #(0x10004 - 0x4 - 4)\n"
1054 "1:\n"
1055 "add ip, pc\n"
1056 "vldr s3, [ip, #0]\n" +
1057 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1058 ".align 2, 0\n"
1059 "2:\n"
1060 ".word 0x12345678\n";
1061 DriverStr(expected, "LoadLiteralSingleMax64KiB");
1062
1063 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
1064 __ GetAdjustedPosition(label.Position()));
1065 }
1066
TEST_F(AssemblerThumb2Test,LoadLiteralSingleMax64KiB_UnalignedPC)1067 TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax64KiB_UnalignedPC) {
1068 // The literal size must match but the type doesn't, so use an int32_t rather than float.
1069 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
1070 __ ldr(arm::R0, arm::Address(arm::R0));
1071 __ LoadLiteral(arm::S3, literal);
1072 Label label;
1073 __ Bind(&label);
1074 constexpr size_t kLdrR0R0Count = (1 << 15) - 4u;
1075 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1076 __ ldr(arm::R0, arm::Address(arm::R0));
1077 }
1078
1079 std::string expected =
1080 "ldr r0, [r0]\n"
1081 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
1082 "movw ip, #(0x10004 - 0x6 - 4)\n"
1083 "1:\n"
1084 "add ip, pc\n"
1085 "vldr s3, [ip, #0]\n" +
1086 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1087 ".align 2, 0\n"
1088 "2:\n"
1089 ".word 0x12345678\n";
1090 DriverStr(expected, "LoadLiteralSingleMax64KiB_UnalignedPC");
1091
1092 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
1093 __ GetAdjustedPosition(label.Position()));
1094 }
1095
TEST_F(AssemblerThumb2Test,LoadLiteralDoubleBeyondMax64KiB)1096 TEST_F(AssemblerThumb2Test, LoadLiteralDoubleBeyondMax64KiB) {
1097 // The literal size must match but the type doesn't, so use an int64_t rather than double.
1098 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
1099 __ LoadLiteral(arm::D3, literal);
1100 Label label;
1101 __ Bind(&label);
1102 constexpr size_t kLdrR0R0Count = (1 << 15) - 2u;
1103 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1104 __ ldr(arm::R0, arm::Address(arm::R0));
1105 }
1106
1107 std::string expected =
1108 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
1109 "movw ip, #((0x1000c - 0x8 - 4) & 0xffff)\n"
1110 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
1111 "movt ip, #((0x1000c - 0x8 - 4) >> 16)\n"
1112 "1:\n"
1113 "add ip, pc\n"
1114 "vldr d3, [ip, #0]\n" +
1115 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1116 ".align 2, 0\n"
1117 "2:\n"
1118 ".word 0x87654321\n"
1119 ".word 0x12345678\n";
1120 DriverStr(expected, "LoadLiteralDoubleBeyondMax64KiB");
1121
1122 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
1123 __ GetAdjustedPosition(label.Position()));
1124 }
1125
TEST_F(AssemblerThumb2Test,LoadLiteralDoubleFar)1126 TEST_F(AssemblerThumb2Test, LoadLiteralDoubleFar) {
1127 // The literal size must match but the type doesn't, so use an int64_t rather than double.
1128 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
1129 __ LoadLiteral(arm::D3, literal);
1130 Label label;
1131 __ Bind(&label);
1132 constexpr size_t kLdrR0R0Count = (1 << 15) - 2u + 0x1234;
1133 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1134 __ ldr(arm::R0, arm::Address(arm::R0));
1135 }
1136
1137 std::string expected =
1138 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
1139 "movw ip, #((0x1000c + 2 * 0x1234 - 0x8 - 4) & 0xffff)\n"
1140 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
1141 "movt ip, #((0x1000c + 2 * 0x1234 - 0x8 - 4) >> 16)\n"
1142 "1:\n"
1143 "add ip, pc\n"
1144 "vldr d3, [ip, #0]\n" +
1145 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1146 ".align 2, 0\n"
1147 "2:\n"
1148 ".word 0x87654321\n"
1149 ".word 0x12345678\n";
1150 DriverStr(expected, "LoadLiteralDoubleFar");
1151
1152 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
1153 __ GetAdjustedPosition(label.Position()));
1154 }
1155
TEST_F(AssemblerThumb2Test,LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass)1156 TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass) {
1157 // First part: as TwoCbzBeyondMaxOffset but add one 16-bit instruction to the end,
1158 // so that the size is not Aligned<4>(.). On the first pass, the assembler resizes
1159 // the second CBZ because it's out of range, then it will resize the first CBZ
1160 // which has been pushed out of range. Thus, after the first pass, the code size
1161 // will appear Aligned<4>(.) but the final size will not be.
1162 Label label0, label1, label2;
1163 __ cbz(arm::R0, &label1);
1164 constexpr size_t kLdrR0R0Count1 = 63;
1165 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
1166 __ ldr(arm::R0, arm::Address(arm::R0));
1167 }
1168 __ Bind(&label0);
1169 __ cbz(arm::R0, &label2);
1170 __ Bind(&label1);
1171 constexpr size_t kLdrR0R0Count2 = 65;
1172 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
1173 __ ldr(arm::R0, arm::Address(arm::R0));
1174 }
1175 __ Bind(&label2);
1176 __ ldr(arm::R0, arm::Address(arm::R0));
1177
1178 std::string expected_part1 =
1179 "cmp r0, #0\n" // cbz r0, label1
1180 "beq.n 1f\n" +
1181 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
1182 "0:\n"
1183 "cmp r0, #0\n" // cbz r0, label2
1184 "beq.n 2f\n"
1185 "1:\n" +
1186 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1187 "2:\n" // Here the offset is Aligned<4>(.).
1188 "ldr r0, [r0]\n"; // Make the first part
1189
1190 // Second part: as LoadLiteralMax1KiB with the caveat that the offset of the load
1191 // literal will not be Aligned<4>(.) but it will appear to be when we process the
1192 // instruction during the first pass, so the literal will need a padding and it
1193 // will push the literal out of range, so we shall end up with "ldr.w".
1194 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
1195 __ LoadLiteral(arm::R0, literal);
1196 Label label;
1197 __ Bind(&label);
1198 constexpr size_t kLdrR0R0Count = 511;
1199 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1200 __ ldr(arm::R0, arm::Address(arm::R0));
1201 }
1202
1203 std::string expected =
1204 expected_part1 +
1205 "1:\n"
1206 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
1207 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1208 ".align 2, 0\n"
1209 "2:\n"
1210 ".word 0x12345678\n";
1211 DriverStr(expected, "LoadLiteralMax1KiB");
1212
1213 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
1214 __ GetAdjustedPosition(label.Position()));
1215 }
1216
TEST_F(AssemblerThumb2Test,BindTrackedLabel)1217 TEST_F(AssemblerThumb2Test, BindTrackedLabel) {
1218 Label non_tracked, tracked, branch_target;
1219
1220 // A few dummy loads on entry.
1221 constexpr size_t kLdrR0R0Count = 5;
1222 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1223 __ ldr(arm::R0, arm::Address(arm::R0));
1224 }
1225
1226 // A branch that will need to be fixed up.
1227 __ cbz(arm::R0, &branch_target);
1228
1229 // Some more dummy loads.
1230 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1231 __ ldr(arm::R0, arm::Address(arm::R0));
1232 }
1233
1234 // Now insert tracked and untracked label.
1235 __ Bind(&non_tracked);
1236 __ BindTrackedLabel(&tracked);
1237
1238 // A lot of dummy loads, to ensure the branch needs resizing.
1239 constexpr size_t kLdrR0R0CountLong = 60;
1240 for (size_t i = 0; i != kLdrR0R0CountLong; ++i) {
1241 __ ldr(arm::R0, arm::Address(arm::R0));
1242 }
1243
1244 // Bind the branch target.
1245 __ Bind(&branch_target);
1246
1247 // One more load.
1248 __ ldr(arm::R0, arm::Address(arm::R0));
1249
1250 std::string expected =
1251 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1252 "cmp r0, #0\n" // cbz r0, 1f
1253 "beq.n 1f\n" +
1254 RepeatInsn(kLdrR0R0Count + kLdrR0R0CountLong, "ldr r0, [r0]\n") +
1255 "1:\n"
1256 "ldr r0, [r0]\n";
1257 DriverStr(expected, "BindTrackedLabel");
1258
1259 // Expectation is that the tracked label should have moved.
1260 EXPECT_LT(non_tracked.Position(), tracked.Position());
1261 }
1262
TEST_F(AssemblerThumb2Test,JumpTable)1263 TEST_F(AssemblerThumb2Test, JumpTable) {
1264 // The jump table. Use three labels.
1265 Label label1, label2, label3;
1266 std::vector<Label*> labels({ &label1, &label2, &label3 });
1267
1268 // A few dummy loads on entry, interspersed with 2 labels.
1269 constexpr size_t kLdrR0R0Count = 5;
1270 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1271 __ ldr(arm::R0, arm::Address(arm::R0));
1272 }
1273 __ BindTrackedLabel(&label1);
1274 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1275 __ ldr(arm::R0, arm::Address(arm::R0));
1276 }
1277 __ BindTrackedLabel(&label2);
1278 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1279 __ ldr(arm::R0, arm::Address(arm::R0));
1280 }
1281
1282 // Create the jump table, emit the base load.
1283 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1284
1285 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1286 // it's being used.
1287 __ ldr(arm::R0, arm::Address(arm::R0));
1288
1289 // Emit the jump
1290 __ EmitJumpTableDispatch(jump_table, arm::R1);
1291
1292 // Some more dummy instructions.
1293 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1294 __ ldr(arm::R0, arm::Address(arm::R0));
1295 }
1296 __ BindTrackedLabel(&label3);
1297 for (size_t i = 0; i != kLdrR0R0Count; ++i) { // Note: odd so there's no alignment
1298 __ ldr(arm::R0, arm::Address(arm::R0)); // necessary, as gcc as emits nops,
1299 } // whereas we emit 0 != nop.
1300
1301 static_assert((kLdrR0R0Count + 3) * 2 < 1 * KB, "Too much offset");
1302
1303 std::string expected =
1304 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1305 ".L1:\n" +
1306 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1307 ".L2:\n" +
1308 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1309 "adr r1, .Ljump_table\n"
1310 "ldr r0, [r0]\n"
1311 ".Lbase:\n"
1312 "add pc, r1\n" +
1313 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1314 ".L3:\n" +
1315 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1316 ".align 2\n"
1317 ".Ljump_table:\n"
1318 ".4byte (.L1 - .Lbase - 4)\n"
1319 ".4byte (.L2 - .Lbase - 4)\n"
1320 ".4byte (.L3 - .Lbase - 4)\n";
1321 DriverStr(expected, "JumpTable");
1322 }
1323
1324 // Test for >1K fixup.
TEST_F(AssemblerThumb2Test,JumpTable4K)1325 TEST_F(AssemblerThumb2Test, JumpTable4K) {
1326 // The jump table. Use three labels.
1327 Label label1, label2, label3;
1328 std::vector<Label*> labels({ &label1, &label2, &label3 });
1329
1330 // A few dummy loads on entry, interspersed with 2 labels.
1331 constexpr size_t kLdrR0R0Count = 5;
1332 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1333 __ ldr(arm::R0, arm::Address(arm::R0));
1334 }
1335 __ BindTrackedLabel(&label1);
1336 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1337 __ ldr(arm::R0, arm::Address(arm::R0));
1338 }
1339 __ BindTrackedLabel(&label2);
1340 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1341 __ ldr(arm::R0, arm::Address(arm::R0));
1342 }
1343
1344 // Create the jump table, emit the base load.
1345 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1346
1347 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1348 // it's being used.
1349 __ ldr(arm::R0, arm::Address(arm::R0));
1350
1351 // Emit the jump
1352 __ EmitJumpTableDispatch(jump_table, arm::R1);
1353
1354 // Some more dummy instructions.
1355 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1356 __ ldr(arm::R0, arm::Address(arm::R0));
1357 }
1358 __ BindTrackedLabel(&label3);
1359 constexpr size_t kLdrR0R0Count2 = 600; // Note: even so there's no alignment
1360 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1361 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1362 }
1363
1364 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 1 * KB, "Not enough offset");
1365 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 4 * KB, "Too much offset");
1366
1367 std::string expected =
1368 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1369 ".L1:\n" +
1370 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1371 ".L2:\n" +
1372 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1373 "adr r1, .Ljump_table\n"
1374 "ldr r0, [r0]\n"
1375 ".Lbase:\n"
1376 "add pc, r1\n" +
1377 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1378 ".L3:\n" +
1379 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1380 ".align 2\n"
1381 ".Ljump_table:\n"
1382 ".4byte (.L1 - .Lbase - 4)\n"
1383 ".4byte (.L2 - .Lbase - 4)\n"
1384 ".4byte (.L3 - .Lbase - 4)\n";
1385 DriverStr(expected, "JumpTable4K");
1386 }
1387
1388 // Test for >4K fixup.
TEST_F(AssemblerThumb2Test,JumpTable64K)1389 TEST_F(AssemblerThumb2Test, JumpTable64K) {
1390 // The jump table. Use three labels.
1391 Label label1, label2, label3;
1392 std::vector<Label*> labels({ &label1, &label2, &label3 });
1393
1394 // A few dummy loads on entry, interspersed with 2 labels.
1395 constexpr size_t kLdrR0R0Count = 5;
1396 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1397 __ ldr(arm::R0, arm::Address(arm::R0));
1398 }
1399 __ BindTrackedLabel(&label1);
1400 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1401 __ ldr(arm::R0, arm::Address(arm::R0));
1402 }
1403 __ BindTrackedLabel(&label2);
1404 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1405 __ ldr(arm::R0, arm::Address(arm::R0));
1406 }
1407
1408 // Create the jump table, emit the base load.
1409 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1410
1411 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1412 // it's being used.
1413 __ ldr(arm::R0, arm::Address(arm::R0));
1414
1415 // Emit the jump
1416 __ EmitJumpTableDispatch(jump_table, arm::R1);
1417
1418 // Some more dummy instructions.
1419 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1420 __ ldr(arm::R0, arm::Address(arm::R0));
1421 }
1422 __ BindTrackedLabel(&label3);
1423 constexpr size_t kLdrR0R0Count2 = 2601; // Note: odd so there's no alignment
1424 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1425 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1426 }
1427
1428 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 4 * KB, "Not enough offset");
1429 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 64 * KB, "Too much offset");
1430
1431 std::string expected =
1432 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1433 ".L1:\n" +
1434 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1435 ".L2:\n" +
1436 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1437 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
1438 // (Note: have to use constants, as labels aren't accepted.
1439 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1440 ") * 2 - 4) & 0xFFFF)\n"
1441 "add r1, pc\n"
1442 "ldr r0, [r0]\n"
1443 ".Lbase:\n"
1444 "add pc, r1\n" +
1445 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1446 ".L3:\n" +
1447 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1448 ".align 2\n"
1449 ".Ljump_table:\n"
1450 ".4byte (.L1 - .Lbase - 4)\n"
1451 ".4byte (.L2 - .Lbase - 4)\n"
1452 ".4byte (.L3 - .Lbase - 4)\n";
1453 DriverStr(expected, "JumpTable64K");
1454 }
1455
1456 // Test for >64K fixup.
TEST_F(AssemblerThumb2Test,JumpTableFar)1457 TEST_F(AssemblerThumb2Test, JumpTableFar) {
1458 // The jump table. Use three labels.
1459 Label label1, label2, label3;
1460 std::vector<Label*> labels({ &label1, &label2, &label3 });
1461
1462 // A few dummy loads on entry, interspersed with 2 labels.
1463 constexpr size_t kLdrR0R0Count = 5;
1464 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1465 __ ldr(arm::R0, arm::Address(arm::R0));
1466 }
1467 __ BindTrackedLabel(&label1);
1468 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1469 __ ldr(arm::R0, arm::Address(arm::R0));
1470 }
1471 __ BindTrackedLabel(&label2);
1472 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1473 __ ldr(arm::R0, arm::Address(arm::R0));
1474 }
1475
1476 // Create the jump table, emit the base load.
1477 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1478
1479 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1480 // it's being used.
1481 __ ldr(arm::R0, arm::Address(arm::R0));
1482
1483 // Emit the jump
1484 __ EmitJumpTableDispatch(jump_table, arm::R1);
1485
1486 // Some more dummy instructions.
1487 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1488 __ ldr(arm::R0, arm::Address(arm::R0));
1489 }
1490 __ BindTrackedLabel(&label3);
1491 constexpr size_t kLdrR0R0Count2 = 70001; // Note: odd so there's no alignment
1492 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1493 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1494 }
1495
1496 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 64 * KB, "Not enough offset");
1497
1498 std::string expected =
1499 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1500 ".L1:\n" +
1501 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1502 ".L2:\n" +
1503 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1504 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
1505 // (Note: have to use constants, as labels aren't accepted.
1506 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1507 ") * 2 - 4) & 0xFFFF)\n"
1508 "movt r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1509 ") * 2 - 4) >> 16)\n"
1510 ".Lhelp:"
1511 "add r1, pc\n"
1512 "ldr r0, [r0]\n"
1513 ".Lbase:\n"
1514 "add pc, r1\n" +
1515 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1516 ".L3:\n" +
1517 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1518 ".align 2\n"
1519 ".Ljump_table:\n"
1520 ".4byte (.L1 - .Lbase - 4)\n"
1521 ".4byte (.L2 - .Lbase - 4)\n"
1522 ".4byte (.L3 - .Lbase - 4)\n";
1523 DriverStr(expected, "JumpTableFar");
1524 }
1525
TEST_F(AssemblerThumb2Test,Clz)1526 TEST_F(AssemblerThumb2Test, Clz) {
1527 __ clz(arm::R0, arm::R1);
1528
1529 const char* expected = "clz r0, r1\n";
1530
1531 DriverStr(expected, "clz");
1532 }
1533
TEST_F(AssemblerThumb2Test,rbit)1534 TEST_F(AssemblerThumb2Test, rbit) {
1535 __ rbit(arm::R1, arm::R0);
1536
1537 const char* expected = "rbit r1, r0\n";
1538
1539 DriverStr(expected, "rbit");
1540 }
1541
TEST_F(AssemblerThumb2Test,rev)1542 TEST_F(AssemblerThumb2Test, rev) {
1543 __ rev(arm::R1, arm::R0);
1544
1545 const char* expected = "rev r1, r0\n";
1546
1547 DriverStr(expected, "rev");
1548 }
1549
TEST_F(AssemblerThumb2Test,rev16)1550 TEST_F(AssemblerThumb2Test, rev16) {
1551 __ rev16(arm::R1, arm::R0);
1552
1553 const char* expected = "rev16 r1, r0\n";
1554
1555 DriverStr(expected, "rev16");
1556 }
1557
TEST_F(AssemblerThumb2Test,revsh)1558 TEST_F(AssemblerThumb2Test, revsh) {
1559 __ revsh(arm::R1, arm::R0);
1560
1561 const char* expected = "revsh r1, r0\n";
1562
1563 DriverStr(expected, "revsh");
1564 }
1565
TEST_F(AssemblerThumb2Test,vcnt)1566 TEST_F(AssemblerThumb2Test, vcnt) {
1567 // Different D register numbers are used here, to test register encoding.
1568 // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
1569 // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
1570 // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
1571 __ vcntd(arm::D0, arm::D1);
1572 __ vcntd(arm::D19, arm::D20);
1573 __ vcntd(arm::D0, arm::D9);
1574 __ vcntd(arm::D16, arm::D20);
1575
1576 std::string expected =
1577 "vcnt.8 d0, d1\n"
1578 "vcnt.8 d19, d20\n"
1579 "vcnt.8 d0, d9\n"
1580 "vcnt.8 d16, d20\n";
1581
1582 DriverStr(expected, "vcnt");
1583 }
1584
TEST_F(AssemblerThumb2Test,vpaddl)1585 TEST_F(AssemblerThumb2Test, vpaddl) {
1586 // Different D register numbers are used here, to test register encoding.
1587 // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
1588 // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
1589 // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
1590 // Different data types (signed and unsigned) are also tested.
1591 __ vpaddld(arm::D0, arm::D0, 8, true);
1592 __ vpaddld(arm::D20, arm::D20, 8, false);
1593 __ vpaddld(arm::D0, arm::D20, 16, false);
1594 __ vpaddld(arm::D20, arm::D0, 32, true);
1595
1596 std::string expected =
1597 "vpaddl.u8 d0, d0\n"
1598 "vpaddl.s8 d20, d20\n"
1599 "vpaddl.s16 d0, d20\n"
1600 "vpaddl.u32 d20, d0\n";
1601
1602 DriverStr(expected, "vpaddl");
1603 }
1604
TEST_F(AssemblerThumb2Test,LoadFromShiftedRegOffset)1605 TEST_F(AssemblerThumb2Test, LoadFromShiftedRegOffset) {
1606 arm::Address mem_address(arm::R0, arm::R1, arm::Shift::LSL, 2);
1607
1608 __ ldrsb(arm::R2, mem_address);
1609 __ ldrb(arm::R2, mem_address);
1610 __ ldrsh(arm::R2, mem_address);
1611 __ ldrh(arm::R2, mem_address);
1612 __ ldr(arm::R2, mem_address);
1613
1614 std::string expected =
1615 "ldrsb r2, [r0, r1, LSL #2]\n"
1616 "ldrb r2, [r0, r1, LSL #2]\n"
1617 "ldrsh r2, [r0, r1, LSL #2]\n"
1618 "ldrh r2, [r0, r1, LSL #2]\n"
1619 "ldr r2, [r0, r1, LSL #2]\n";
1620
1621 DriverStr(expected, "LoadFromShiftedRegOffset");
1622 }
1623
TEST_F(AssemblerThumb2Test,VStmLdmPushPop)1624 TEST_F(AssemblerThumb2Test, VStmLdmPushPop) {
1625 // Different D register numbers are used here, to test register encoding.
1626 // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
1627 // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
1628 // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
1629 // Different data types (signed and unsigned) are also tested.
1630 __ vstmiad(arm::R0, arm::D0, 4);
1631 __ vldmiad(arm::R1, arm::D9, 5);
1632 __ vpopd(arm::D0, 4);
1633 __ vpushd(arm::D9, 5);
1634 __ vpops(arm::S0, 4);
1635 __ vpushs(arm::S9, 5);
1636 __ vpushs(arm::S16, 5);
1637 __ vpushd(arm::D0, 16);
1638 __ vpushd(arm::D1, 15);
1639 __ vpushd(arm::D8, 16);
1640 __ vpushd(arm::D31, 1);
1641 __ vpushs(arm::S0, 32);
1642 __ vpushs(arm::S1, 31);
1643 __ vpushs(arm::S16, 16);
1644 __ vpushs(arm::S31, 1);
1645
1646 std::string expected =
1647 "vstmia r0, {d0 - d3}\n"
1648 "vldmia r1, {d9 - d13}\n"
1649 "vpop {d0 - d3}\n"
1650 "vpush {d9 - d13}\n"
1651 "vpop {s0 - s3}\n"
1652 "vpush {s9 - s13}\n"
1653 "vpush {s16 - s20}\n"
1654 "vpush {d0 - d15}\n"
1655 "vpush {d1 - d15}\n"
1656 "vpush {d8 - d23}\n"
1657 "vpush {d31}\n"
1658 "vpush {s0 - s31}\n"
1659 "vpush {s1 - s31}\n"
1660 "vpush {s16 - s31}\n"
1661 "vpush {s31}\n";
1662
1663 DriverStr(expected, "VStmLdmPushPop");
1664 }
1665
1666 } // namespace art
1667