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