blob: 0147a76744f735c7b386c9e287b45cc97b8d5f1e [file] [log] [blame]
Roland Levillain1a28fc42014-11-13 18:03:06 +00001/*
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
Andreas Gampe46ee31b2016-12-14 10:11:49 -080019#include "android-base/stringprintf.h"
20
Roland Levillain1a28fc42014-11-13 18:03:06 +000021#include "base/stl_util.h"
22#include "utils/assembler_test.h"
23
24namespace art {
25
Andreas Gampe46ee31b2016-12-14 10:11:49 -080026using android::base::StringPrintf;
27
Roland Levillain1a28fc42014-11-13 18:03:06 +000028class AssemblerThumb2Test : public AssemblerTest<arm::Thumb2Assembler,
Andreas Gampe851df202014-11-12 14:05:46 -080029 arm::Register, arm::SRegister,
30 uint32_t> {
Roland Levillain1a28fc42014-11-13 18:03:06 +000031 protected:
32 std::string GetArchitectureString() OVERRIDE {
33 return "arm";
34 }
35
36 std::string GetAssemblerParameters() OVERRIDE {
Andreas Gampe513ea0c2015-02-02 13:17:52 -080037 return " -march=armv7-a -mcpu=cortex-a15 -mfpu=neon -mthumb";
38 }
39
40 const char* GetAssemblyHeader() OVERRIDE {
41 return kThumb2AssemblyHeader;
Roland Levillain1a28fc42014-11-13 18:03:06 +000042 }
43
44 std::string GetDisassembleParameters() OVERRIDE {
Andreas Gampe513ea0c2015-02-02 13:17:52 -080045 return " -D -bbinary -marm --disassembler-options=force-thumb --no-show-raw-insn";
Roland Levillain1a28fc42014-11-13 18:03:06 +000046 }
47
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
72 void TearDown() OVERRIDE {
73 AssemblerTest::TearDown();
74 STLDeleteElements(&registers_);
75 }
76
77 std::vector<arm::Register*> GetRegisters() OVERRIDE {
78 return registers_;
79 }
80
81 uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
82 return imm_value;
83 }
84
Vladimir Markocf93a5c2015-06-16 11:33:24 +000085 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
Roland Levillain1a28fc42014-11-13 18:03:06 +000093 private:
94 std::vector<arm::Register*> registers_;
Andreas Gampe513ea0c2015-02-02 13:17:52 -080095
96 static constexpr const char* kThumb2AssemblyHeader = ".syntax unified\n.thumb\n";
Roland Levillain1a28fc42014-11-13 18:03:06 +000097};
98
Roland Levillain1a28fc42014-11-13 18:03:06 +000099TEST_F(AssemblerThumb2Test, Toolchain) {
100 EXPECT_TRUE(CheckTools());
101}
102
Zheng Xuc6667102015-05-15 16:08:45 +0800103#define __ GetAssembler()->
Roland Levillain1a28fc42014-11-13 18:03:06 +0000104
105TEST_F(AssemblerThumb2Test, Sbfx) {
Zheng Xuc6667102015-05-15 16:08:45 +0800106 __ 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);
Roland Levillain1a28fc42014-11-13 18:03:06 +0000110
Zheng Xuc6667102015-05-15 16:08:45 +0800111 __ 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);
Roland Levillain1a28fc42014-11-13 18:03:06 +0000115
Zheng Xuc6667102015-05-15 16:08:45 +0800116 __ sbfx(arm::R0, arm::R1, 16, 1);
117 __ sbfx(arm::R0, arm::R1, 16, 8);
118 __ sbfx(arm::R0, arm::R1, 16, 16);
Roland Levillain1a28fc42014-11-13 18:03:06 +0000119
Zheng Xuc6667102015-05-15 16:08:45 +0800120 __ sbfx(arm::R0, arm::R1, 31, 1);
Roland Levillain1a28fc42014-11-13 18:03:06 +0000121
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
Roland Levillain981e4542014-11-14 11:47:14 +0000141TEST_F(AssemblerThumb2Test, Ubfx) {
Zheng Xuc6667102015-05-15 16:08:45 +0800142 __ 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);
Roland Levillain981e4542014-11-14 11:47:14 +0000146
Zheng Xuc6667102015-05-15 16:08:45 +0800147 __ 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);
Roland Levillain981e4542014-11-14 11:47:14 +0000151
Zheng Xuc6667102015-05-15 16:08:45 +0800152 __ ubfx(arm::R0, arm::R1, 16, 1);
153 __ ubfx(arm::R0, arm::R1, 16, 8);
154 __ ubfx(arm::R0, arm::R1, 16, 16);
Roland Levillain981e4542014-11-14 11:47:14 +0000155
Zheng Xuc6667102015-05-15 16:08:45 +0800156 __ ubfx(arm::R0, arm::R1, 31, 1);
Roland Levillain981e4542014-11-14 11:47:14 +0000157
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
Calin Juravleddb7df22014-11-25 20:56:51 +0000177TEST_F(AssemblerThumb2Test, Vmstat) {
Zheng Xuc6667102015-05-15 16:08:45 +0800178 __ vmstat();
Calin Juravleddb7df22014-11-25 20:56:51 +0000179
180 const char* expected = "vmrs APSR_nzcv, FPSCR\n";
181
182 DriverStr(expected, "vmrs");
183}
184
Calin Juravle52c48962014-12-16 17:02:57 +0000185TEST_F(AssemblerThumb2Test, ldrexd) {
Zheng Xuc6667102015-05-15 16:08:45 +0800186 __ 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);
Calin Juravle52c48962014-12-16 17:02:57 +0000190
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
199TEST_F(AssemblerThumb2Test, strexd) {
Zheng Xuc6667102015-05-15 16:08:45 +0800200 __ 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);
Calin Juravle52c48962014-12-16 17:02:57 +0000204
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
Roland Levillain188edb32016-10-24 16:31:16 +0100213TEST_F(AssemblerThumb2Test, clrex) {
214 __ clrex();
215
216 const char* expected = "clrex\n";
217 DriverStr(expected, "clrex");
218}
219
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800220TEST_F(AssemblerThumb2Test, LdrdStrd) {
Zheng Xuc6667102015-05-15 16:08:45 +0800221 __ 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));
Andreas Gampe2bcf9bf2015-01-29 09:56:07 -0800224
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
Andreas Gampe513ea0c2015-02-02 13:17:52 -0800232TEST_F(AssemblerThumb2Test, eor) {
Andreas Gampe513ea0c2015-02-02 13:17:52 -0800233 __ 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
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000248TEST_F(AssemblerThumb2Test, sub) {
249 __ subs(arm::R1, arm::R0, arm::ShifterOperand(42));
250 __ sub(arm::R1, arm::R0, arm::ShifterOperand(42));
Zheng Xuc6667102015-05-15 16:08:45 +0800251 __ 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));
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000253
254 const char* expected =
255 "subs r1, r0, #42\n"
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000256 "sub.w r1, r0, #42\n"
Zheng Xuc6667102015-05-15 16:08:45 +0800257 "subs r1, r0, r2, asr #31\n"
258 "sub r1, r0, r2, asr #31\n";
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000259 DriverStr(expected, "sub");
260}
261
262TEST_F(AssemblerThumb2Test, add) {
263 __ adds(arm::R1, arm::R0, arm::ShifterOperand(42));
264 __ add(arm::R1, arm::R0, arm::ShifterOperand(42));
Zheng Xuc6667102015-05-15 16:08:45 +0800265 __ 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));
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000267
268 const char* expected =
269 "adds r1, r0, #42\n"
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000270 "add.w r1, r0, #42\n"
Zheng Xuc6667102015-05-15 16:08:45 +0800271 "adds r1, r0, r2, asr #31\n"
272 "add r1, r0, r2, asr #31\n";
Guillaume "Vermeille" Sanchezdc62c482015-03-11 14:30:31 +0000273 DriverStr(expected, "add");
274}
275
Zheng Xuc6667102015-05-15 16:08:45 +0800276TEST_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
284TEST_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
Vladimir Marko3a656e12016-08-02 14:57:56 +0100292TEST_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
310TEST_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
328TEST_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
348TEST_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
368TEST_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
390TEST_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
412TEST_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
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100434TEST_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
450TEST_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 =
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000460 "add.w ip, sp, #4096\n" // AddConstant(ip, sp, 4096)
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100461 "str r0, [ip, #0]\n"
462
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000463 "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)
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100467
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000468 "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)
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100472 DriverStr(expected, "StoreWordToNonThumbOffset");
473}
474
Roland Levillain4af147e2015-04-07 13:54:49 +0100475TEST_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
499TEST_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 =
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000512 "add.w ip, sp, #1024\n" // AddConstant(ip, sp, 1024)
Roland Levillain4af147e2015-04-07 13:54:49 +0100513 "strd r0, r1, [ip, #0]\n"
514
515 "str r5, [sp, #-4]!\n" // Push(r5)
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000516 "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)
Roland Levillain4af147e2015-04-07 13:54:49 +0100518 "ldr r5, [sp], #4\n" // Pop(r5)
519
520 "str r6, [sp, #-4]!\n" // Push(r6)
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000521 "add.w r6, r5, #1024\n" // AddConstant(r6, r5, 1024 & ~0x3fc)
522 "strd r11, ip, [r6, #0]\n" // StoreToOffset(type, r11, r6, 1024 & 0x3fc)
Roland Levillain4af147e2015-04-07 13:54:49 +0100523 "ldr r6, [sp], #4\n"; // Pop(r6)
524 DriverStr(expected, "StoreWordPairToNonThumbOffset");
525}
526
Vladimir Markoa64f2492016-04-25 12:43:50 +0000527TEST_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
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000552TEST_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
586TEST_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
622TEST_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
657TEST_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
693TEST_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
728TEST_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
764TEST_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
787TEST_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
810TEST_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
833TEST_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
858TEST_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
883TEST_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
908TEST_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
933TEST_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
961TEST_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
989TEST_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
1013TEST_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 =
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001024 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
1025 "movw ip, #(0x408 - 0x4 - 4)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001026 "1:\n"
1027 "add ip, pc\n"
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001028 "ldrd r1, r3, [ip, #0]\n" +
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001029 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
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001040TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax64KiB) {
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001041 // 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);
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001046 constexpr size_t kLdrR0R0Count = (1 << 15) - 3u;
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001047 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.
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001053 "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
1067TEST_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
1096TEST_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"
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001110 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001111 "movt ip, #((0x1000c - 0x8 - 4) >> 16)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001112 "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";
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001120 DriverStr(expected, "LoadLiteralDoubleBeyondMax64KiB");
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001121
1122 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
1123 __ GetAdjustedPosition(label.Position()));
1124}
1125
1126TEST_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);
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001132 constexpr size_t kLdrR0R0Count = (1 << 15) - 2u + 0x1234;
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001133 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.
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001139 "movw ip, #((0x1000c + 2 * 0x1234 - 0x8 - 4) & 0xffff)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001140 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
Vladimir Markoebdbf4b2016-07-07 15:37:02 +01001141 "movt ip, #((0x1000c + 2 * 0x1234 - 0x8 - 4) >> 16)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +00001142 "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
Vladimir Marko663c9342015-07-22 11:28:14 +01001156TEST_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
Andreas Gampe7cffc3b2015-10-19 21:31:53 -07001217TEST_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
1263TEST_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.
1325TEST_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.
1389TEST_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.
1457TEST_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
Scott Wakeling611d3392015-07-10 11:42:06 +01001526TEST_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
Scott Wakeling9ee23f42015-07-23 10:44:35 +01001534TEST_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
Artem Serovc257da72016-02-02 13:49:43 +00001542TEST_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
1550TEST_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
1558TEST_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
xueliang.zhonge652c122016-06-13 14:42:27 +01001566TEST_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
1585TEST_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
Artem Serov2e4fcc92016-07-11 14:00:46 +01001605TEST_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
Artem Serovcb3cf4a2016-07-15 15:01:13 +01001624TEST_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
Roland Levillain1a28fc42014-11-13 18:03:06 +00001666} // namespace art