blob: f3fa72ccc636f6a633a9280ba62486cc247d3812 [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
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100282TEST_F(AssemblerThumb2Test, StoreWordToThumbOffset) {
283 arm::StoreOperandType type = arm::kStoreWord;
284 int32_t offset = 4092;
285 ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
286
287 __ StoreToOffset(type, arm::R0, arm::SP, offset);
288 __ StoreToOffset(type, arm::IP, arm::SP, offset);
289 __ StoreToOffset(type, arm::IP, arm::R5, offset);
290
291 const char* expected =
292 "str r0, [sp, #4092]\n"
293 "str ip, [sp, #4092]\n"
294 "str ip, [r5, #4092]\n";
295 DriverStr(expected, "StoreWordToThumbOffset");
296}
297
298TEST_F(AssemblerThumb2Test, StoreWordToNonThumbOffset) {
299 arm::StoreOperandType type = arm::kStoreWord;
300 int32_t offset = 4096;
301 ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
302
303 __ StoreToOffset(type, arm::R0, arm::SP, offset);
304 __ StoreToOffset(type, arm::IP, arm::SP, offset);
305 __ StoreToOffset(type, arm::IP, arm::R5, offset);
306
307 const char* expected =
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000308 "add.w ip, sp, #4096\n" // AddConstant(ip, sp, 4096)
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100309 "str r0, [ip, #0]\n"
310
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000311 "str r5, [sp, #-4]!\n" // Push(r5)
312 "add.w r5, sp, #4096\n" // AddConstant(r5, 4100 & ~0xfff)
313 "str ip, [r5, #4]\n" // StoreToOffset(type, ip, r5, 4100 & 0xfff)
314 "ldr r5, [sp], #4\n" // Pop(r5)
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100315
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000316 "str r6, [sp, #-4]!\n" // Push(r6)
317 "add.w r6, r5, #4096\n" // AddConstant(r6, r5, 4096 & ~0xfff)
318 "str ip, [r6, #0]\n" // StoreToOffset(type, ip, r6, 4096 & 0xfff)
319 "ldr r6, [sp], #4\n"; // Pop(r6)
Roland Levillainc5a5ac62015-04-02 15:49:24 +0100320 DriverStr(expected, "StoreWordToNonThumbOffset");
321}
322
Roland Levillain4af147e2015-04-07 13:54:49 +0100323TEST_F(AssemblerThumb2Test, StoreWordPairToThumbOffset) {
324 arm::StoreOperandType type = arm::kStoreWordPair;
325 int32_t offset = 1020;
326 ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
327
328 __ StoreToOffset(type, arm::R0, arm::SP, offset);
329 // We cannot use IP (i.e. R12) as first source register, as it would
330 // force us to use SP (i.e. R13) as second source register, which
331 // would have an "unpredictable" effect according to the ARMv7
332 // specification (the T1 encoding describes the result as
333 // UNPREDICTABLE when of the source registers is R13).
334 //
335 // So we use (R11, IP) (e.g. (R11, R12)) as source registers in the
336 // following instructions.
337 __ StoreToOffset(type, arm::R11, arm::SP, offset);
338 __ StoreToOffset(type, arm::R11, arm::R5, offset);
339
340 const char* expected =
341 "strd r0, r1, [sp, #1020]\n"
342 "strd r11, ip, [sp, #1020]\n"
343 "strd r11, ip, [r5, #1020]\n";
344 DriverStr(expected, "StoreWordPairToThumbOffset");
345}
346
347TEST_F(AssemblerThumb2Test, StoreWordPairToNonThumbOffset) {
348 arm::StoreOperandType type = arm::kStoreWordPair;
349 int32_t offset = 1024;
350 ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
351
352 __ StoreToOffset(type, arm::R0, arm::SP, offset);
353 // Same comment as in AssemblerThumb2Test.StoreWordPairToThumbOffset
354 // regarding the use of (R11, IP) (e.g. (R11, R12)) as source
355 // registers in the following instructions.
356 __ StoreToOffset(type, arm::R11, arm::SP, offset);
357 __ StoreToOffset(type, arm::R11, arm::R5, offset);
358
359 const char* expected =
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000360 "add.w ip, sp, #1024\n" // AddConstant(ip, sp, 1024)
Roland Levillain4af147e2015-04-07 13:54:49 +0100361 "strd r0, r1, [ip, #0]\n"
362
363 "str r5, [sp, #-4]!\n" // Push(r5)
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000364 "add.w r5, sp, #1024\n" // AddConstant(r5, sp, (1024 + kRegisterSize) & ~0x3fc)
365 "strd r11, ip, [r5, #4]\n" // StoreToOffset(type, r11, sp, (1024 + kRegisterSize) & 0x3fc)
Roland Levillain4af147e2015-04-07 13:54:49 +0100366 "ldr r5, [sp], #4\n" // Pop(r5)
367
368 "str r6, [sp, #-4]!\n" // Push(r6)
Vladimir Marko6fd0ffe2015-11-19 21:13:52 +0000369 "add.w r6, r5, #1024\n" // AddConstant(r6, r5, 1024 & ~0x3fc)
370 "strd r11, ip, [r6, #0]\n" // StoreToOffset(type, r11, r6, 1024 & 0x3fc)
Roland Levillain4af147e2015-04-07 13:54:49 +0100371 "ldr r6, [sp], #4\n"; // Pop(r6)
372 DriverStr(expected, "StoreWordPairToNonThumbOffset");
373}
374
Vladimir Markoa64f2492016-04-25 12:43:50 +0000375TEST_F(AssemblerThumb2Test, DistantBackBranch) {
376 Label start, end;
377 __ Bind(&start);
378 constexpr size_t kLdrR0R0Count1 = 256;
379 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
380 __ ldr(arm::R0, arm::Address(arm::R0));
381 }
382 __ b(&end, arm::EQ);
383 __ b(&start, arm::LT);
384 constexpr size_t kLdrR0R0Count2 = 256;
385 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
386 __ ldr(arm::R0, arm::Address(arm::R0));
387 }
388 __ Bind(&end);
389
390 std::string expected =
391 "0:\n" +
392 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
393 "beq 1f\n"
394 "blt 0b\n" +
395 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
396 "1:\n";
397 DriverStr(expected, "DistantBackBranch");
398}
399
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000400TEST_F(AssemblerThumb2Test, TwoCbzMaxOffset) {
401 Label label0, label1, label2;
402 __ cbz(arm::R0, &label1);
403 constexpr size_t kLdrR0R0Count1 = 63;
404 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
405 __ ldr(arm::R0, arm::Address(arm::R0));
406 }
407 __ Bind(&label0);
408 __ cbz(arm::R0, &label2);
409 __ Bind(&label1);
410 constexpr size_t kLdrR0R0Count2 = 64;
411 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
412 __ ldr(arm::R0, arm::Address(arm::R0));
413 }
414 __ Bind(&label2);
415
416 std::string expected =
417 "cbz r0, 1f\n" + // cbz r0, label1
418 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
419 "0:\n"
420 "cbz r0, 2f\n" // cbz r0, label2
421 "1:\n" +
422 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
423 "2:\n";
424 DriverStr(expected, "TwoCbzMaxOffset");
425
426 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
427 __ GetAdjustedPosition(label0.Position()));
428 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 0u,
429 __ GetAdjustedPosition(label1.Position()));
430 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 0u,
431 __ GetAdjustedPosition(label2.Position()));
432}
433
434TEST_F(AssemblerThumb2Test, TwoCbzBeyondMaxOffset) {
435 Label label0, label1, label2;
436 __ cbz(arm::R0, &label1);
437 constexpr size_t kLdrR0R0Count1 = 63;
438 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
439 __ ldr(arm::R0, arm::Address(arm::R0));
440 }
441 __ Bind(&label0);
442 __ cbz(arm::R0, &label2);
443 __ Bind(&label1);
444 constexpr size_t kLdrR0R0Count2 = 65;
445 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
446 __ ldr(arm::R0, arm::Address(arm::R0));
447 }
448 __ Bind(&label2);
449
450 std::string expected =
451 "cmp r0, #0\n" // cbz r0, label1
452 "beq.n 1f\n" +
453 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
454 "0:\n"
455 "cmp r0, #0\n" // cbz r0, label2
456 "beq.n 2f\n"
457 "1:\n" +
458 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
459 "2:\n";
460 DriverStr(expected, "TwoCbzBeyondMaxOffset");
461
462 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
463 __ GetAdjustedPosition(label0.Position()));
464 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 4u,
465 __ GetAdjustedPosition(label1.Position()));
466 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 4u,
467 __ GetAdjustedPosition(label2.Position()));
468}
469
470TEST_F(AssemblerThumb2Test, TwoCbzSecondAtMaxB16Offset) {
471 Label label0, label1, label2;
472 __ cbz(arm::R0, &label1);
473 constexpr size_t kLdrR0R0Count1 = 62;
474 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
475 __ ldr(arm::R0, arm::Address(arm::R0));
476 }
477 __ Bind(&label0);
478 __ cbz(arm::R0, &label2);
479 __ Bind(&label1);
480 constexpr size_t kLdrR0R0Count2 = 128;
481 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
482 __ ldr(arm::R0, arm::Address(arm::R0));
483 }
484 __ Bind(&label2);
485
486 std::string expected =
487 "cbz r0, 1f\n" + // cbz r0, label1
488 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
489 "0:\n"
490 "cmp r0, #0\n" // cbz r0, label2
491 "beq.n 2f\n"
492 "1:\n" +
493 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
494 "2:\n";
495 DriverStr(expected, "TwoCbzSecondAtMaxB16Offset");
496
497 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u,
498 __ GetAdjustedPosition(label0.Position()));
499 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
500 __ GetAdjustedPosition(label1.Position()));
501 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
502 __ GetAdjustedPosition(label2.Position()));
503}
504
505TEST_F(AssemblerThumb2Test, TwoCbzSecondBeyondMaxB16Offset) {
506 Label label0, label1, label2;
507 __ cbz(arm::R0, &label1);
508 constexpr size_t kLdrR0R0Count1 = 62;
509 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
510 __ ldr(arm::R0, arm::Address(arm::R0));
511 }
512 __ Bind(&label0);
513 __ cbz(arm::R0, &label2);
514 __ Bind(&label1);
515 constexpr size_t kLdrR0R0Count2 = 129;
516 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
517 __ ldr(arm::R0, arm::Address(arm::R0));
518 }
519 __ Bind(&label2);
520
521 std::string expected =
522 "cmp r0, #0\n" // cbz r0, label1
523 "beq.n 1f\n" +
524 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
525 "0:\n"
526 "cmp r0, #0\n" // cbz r0, label2
527 "beq.w 2f\n"
528 "1:\n" +
529 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
530 "2:\n";
531 DriverStr(expected, "TwoCbzSecondBeyondMaxB16Offset");
532
533 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
534 __ GetAdjustedPosition(label0.Position()));
535 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
536 __ GetAdjustedPosition(label1.Position()));
537 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
538 __ GetAdjustedPosition(label2.Position()));
539}
540
541TEST_F(AssemblerThumb2Test, TwoCbzFirstAtMaxB16Offset) {
542 Label label0, label1, label2;
543 __ cbz(arm::R0, &label1);
544 constexpr size_t kLdrR0R0Count1 = 127;
545 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
546 __ ldr(arm::R0, arm::Address(arm::R0));
547 }
548 __ Bind(&label0);
549 __ cbz(arm::R0, &label2);
550 __ Bind(&label1);
551 constexpr size_t kLdrR0R0Count2 = 64;
552 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
553 __ ldr(arm::R0, arm::Address(arm::R0));
554 }
555 __ Bind(&label2);
556
557 std::string expected =
558 "cmp r0, #0\n" // cbz r0, label1
559 "beq.n 1f\n" +
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, "TwoCbzFirstAtMaxB16Offset");
567
568 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u,
569 __ GetAdjustedPosition(label0.Position()));
570 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u,
571 __ GetAdjustedPosition(label1.Position()));
572 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u,
573 __ GetAdjustedPosition(label2.Position()));
574}
575
576TEST_F(AssemblerThumb2Test, TwoCbzFirstBeyondMaxB16Offset) {
577 Label label0, label1, label2;
578 __ cbz(arm::R0, &label1);
579 constexpr size_t kLdrR0R0Count1 = 127;
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.w 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, "TwoCbzFirstBeyondMaxB16Offset");
603
604 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 4u,
605 __ GetAdjustedPosition(label0.Position()));
606 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u,
607 __ GetAdjustedPosition(label1.Position()));
608 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u,
609 __ GetAdjustedPosition(label2.Position()));
610}
611
612TEST_F(AssemblerThumb2Test, LoadLiteralMax1KiB) {
613 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
614 __ LoadLiteral(arm::R0, literal);
615 Label label;
616 __ Bind(&label);
617 constexpr size_t kLdrR0R0Count = 511;
618 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
619 __ ldr(arm::R0, arm::Address(arm::R0));
620 }
621
622 std::string expected =
623 "1:\n"
624 "ldr.n r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
625 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
626 ".align 2, 0\n"
627 "2:\n"
628 ".word 0x12345678\n";
629 DriverStr(expected, "LoadLiteralMax1KiB");
630
631 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
632 __ GetAdjustedPosition(label.Position()));
633}
634
635TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiB) {
636 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
637 __ LoadLiteral(arm::R0, literal);
638 Label label;
639 __ Bind(&label);
640 constexpr size_t kLdrR0R0Count = 512;
641 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
642 __ ldr(arm::R0, arm::Address(arm::R0));
643 }
644
645 std::string expected =
646 "1:\n"
647 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
648 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
649 ".align 2, 0\n"
650 "2:\n"
651 ".word 0x12345678\n";
652 DriverStr(expected, "LoadLiteralBeyondMax1KiB");
653
654 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
655 __ GetAdjustedPosition(label.Position()));
656}
657
658TEST_F(AssemblerThumb2Test, LoadLiteralMax4KiB) {
659 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
660 __ LoadLiteral(arm::R1, literal);
661 Label label;
662 __ Bind(&label);
663 constexpr size_t kLdrR0R0Count = 2046;
664 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
665 __ ldr(arm::R0, arm::Address(arm::R0));
666 }
667
668 std::string expected =
669 "1:\n"
670 "ldr.w r1, [pc, #((2f - 1b - 2) & ~2)]\n" +
671 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
672 ".align 2, 0\n"
673 "2:\n"
674 ".word 0x12345678\n";
675 DriverStr(expected, "LoadLiteralMax4KiB");
676
677 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u,
678 __ GetAdjustedPosition(label.Position()));
679}
680
681TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax4KiB) {
682 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
683 __ LoadLiteral(arm::R1, literal);
684 Label label;
685 __ Bind(&label);
686 constexpr size_t kLdrR0R0Count = 2047;
687 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
688 __ ldr(arm::R0, arm::Address(arm::R0));
689 }
690
691 std::string expected =
692 "movw r1, #4096\n" // "as" does not consider (2f - 1f - 4) a constant expression for movw.
693 "1:\n"
694 "add r1, pc\n"
695 "ldr r1, [r1, #0]\n" +
696 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
697 ".align 2, 0\n"
698 "2:\n"
699 ".word 0x12345678\n";
700 DriverStr(expected, "LoadLiteralBeyondMax4KiB");
701
702 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
703 __ GetAdjustedPosition(label.Position()));
704}
705
706TEST_F(AssemblerThumb2Test, LoadLiteralMax64KiB) {
707 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
708 __ LoadLiteral(arm::R1, literal);
709 Label label;
710 __ Bind(&label);
711 constexpr size_t kLdrR0R0Count = (1u << 15) - 2u;
712 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
713 __ ldr(arm::R0, arm::Address(arm::R0));
714 }
715
716 std::string expected =
717 "movw r1, #0xfffc\n" // "as" does not consider (2f - 1f - 4) a constant expression for movw.
718 "1:\n"
719 "add r1, pc\n"
720 "ldr r1, [r1, #0]\n" +
721 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
722 ".align 2, 0\n"
723 "2:\n"
724 ".word 0x12345678\n";
725 DriverStr(expected, "LoadLiteralMax64KiB");
726
727 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
728 __ GetAdjustedPosition(label.Position()));
729}
730
731TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax64KiB) {
732 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
733 __ LoadLiteral(arm::R1, literal);
734 Label label;
735 __ Bind(&label);
736 constexpr size_t kLdrR0R0Count = (1u << 15) - 1u;
737 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
738 __ ldr(arm::R0, arm::Address(arm::R0));
739 }
740
741 std::string expected =
742 "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n"
743 "1:\n"
744 "add r1, pc\n"
745 "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" +
746 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
747 ".align 2, 0\n"
748 "2:\n"
749 ".word 0x12345678\n";
750 DriverStr(expected, "LoadLiteralBeyondMax64KiB");
751
752 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
753 __ GetAdjustedPosition(label.Position()));
754}
755
756TEST_F(AssemblerThumb2Test, LoadLiteralMax1MiB) {
757 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
758 __ LoadLiteral(arm::R1, literal);
759 Label label;
760 __ Bind(&label);
761 constexpr size_t kLdrR0R0Count = (1u << 19) - 3u;
762 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
763 __ ldr(arm::R0, arm::Address(arm::R0));
764 }
765
766 std::string expected =
767 "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n"
768 "1:\n"
769 "add r1, pc\n"
770 "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" +
771 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
772 ".align 2, 0\n"
773 "2:\n"
774 ".word 0x12345678\n";
775 DriverStr(expected, "LoadLiteralMax1MiB");
776
777 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u,
778 __ GetAdjustedPosition(label.Position()));
779}
780
781TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1MiB) {
782 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
783 __ LoadLiteral(arm::R1, literal);
784 Label label;
785 __ Bind(&label);
786 constexpr size_t kLdrR0R0Count = (1u << 19) - 2u;
787 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
788 __ ldr(arm::R0, arm::Address(arm::R0));
789 }
790
791 std::string expected =
792 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
793 "movw r1, #(0x100000 & 0xffff)\n"
794 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
795 "movt r1, #(0x100000 >> 16)\n"
796 "1:\n"
797 "add r1, pc\n"
798 "ldr.w r1, [r1, #0]\n" +
799 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
800 ".align 2, 0\n"
801 "2:\n"
802 ".word 0x12345678\n";
803 DriverStr(expected, "LoadLiteralBeyondMax1MiB");
804
805 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
806 __ GetAdjustedPosition(label.Position()));
807}
808
809TEST_F(AssemblerThumb2Test, LoadLiteralFar) {
810 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
811 __ LoadLiteral(arm::R1, literal);
812 Label label;
813 __ Bind(&label);
814 constexpr size_t kLdrR0R0Count = (1u << 19) - 2u + 0x1234;
815 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
816 __ ldr(arm::R0, arm::Address(arm::R0));
817 }
818
819 std::string expected =
820 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
821 "movw r1, #((0x100000 + 2 * 0x1234) & 0xffff)\n"
822 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
823 "movt r1, #((0x100000 + 2 * 0x1234) >> 16)\n"
824 "1:\n"
825 "add r1, pc\n"
826 "ldr.w r1, [r1, #0]\n" +
827 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
828 ".align 2, 0\n"
829 "2:\n"
830 ".word 0x12345678\n";
831 DriverStr(expected, "LoadLiteralFar");
832
833 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u,
834 __ GetAdjustedPosition(label.Position()));
835}
836
837TEST_F(AssemblerThumb2Test, LoadLiteralWideMax1KiB) {
838 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
839 __ LoadLiteral(arm::R1, arm::R3, literal);
840 Label label;
841 __ Bind(&label);
842 constexpr size_t kLdrR0R0Count = 510;
843 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
844 __ ldr(arm::R0, arm::Address(arm::R0));
845 }
846
847 std::string expected =
848 "1:\n"
849 "ldrd r1, r3, [pc, #((2f - 1b - 2) & ~2)]\n" +
850 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
851 ".align 2, 0\n"
852 "2:\n"
853 ".word 0x87654321\n"
854 ".word 0x12345678\n";
855 DriverStr(expected, "LoadLiteralWideMax1KiB");
856
857 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u,
858 __ GetAdjustedPosition(label.Position()));
859}
860
861TEST_F(AssemblerThumb2Test, LoadLiteralWideBeyondMax1KiB) {
862 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
863 __ LoadLiteral(arm::R1, arm::R3, literal);
864 Label label;
865 __ Bind(&label);
866 constexpr size_t kLdrR0R0Count = 511;
867 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
868 __ ldr(arm::R0, arm::Address(arm::R0));
869 }
870
871 std::string expected =
Vladimir Markoebdbf4b2016-07-07 15:37:02 +0100872 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
873 "movw ip, #(0x408 - 0x4 - 4)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000874 "1:\n"
875 "add ip, pc\n"
Vladimir Markoebdbf4b2016-07-07 15:37:02 +0100876 "ldrd r1, r3, [ip, #0]\n" +
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000877 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
878 ".align 2, 0\n"
879 "2:\n"
880 ".word 0x87654321\n"
881 ".word 0x12345678\n";
882 DriverStr(expected, "LoadLiteralWideBeyondMax1KiB");
883
884 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
885 __ GetAdjustedPosition(label.Position()));
886}
887
Vladimir Markoebdbf4b2016-07-07 15:37:02 +0100888TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax64KiB) {
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000889 // The literal size must match but the type doesn't, so use an int32_t rather than float.
890 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
891 __ LoadLiteral(arm::S3, literal);
892 Label label;
893 __ Bind(&label);
Vladimir Markoebdbf4b2016-07-07 15:37:02 +0100894 constexpr size_t kLdrR0R0Count = (1 << 15) - 3u;
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000895 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
896 __ ldr(arm::R0, arm::Address(arm::R0));
897 }
898
899 std::string expected =
900 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
Vladimir Markoebdbf4b2016-07-07 15:37:02 +0100901 "movw ip, #(0x10004 - 0x4 - 4)\n"
902 "1:\n"
903 "add ip, pc\n"
904 "vldr s3, [ip, #0]\n" +
905 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
906 ".align 2, 0\n"
907 "2:\n"
908 ".word 0x12345678\n";
909 DriverStr(expected, "LoadLiteralSingleMax64KiB");
910
911 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
912 __ GetAdjustedPosition(label.Position()));
913}
914
915TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax64KiB_UnalignedPC) {
916 // The literal size must match but the type doesn't, so use an int32_t rather than float.
917 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
918 __ ldr(arm::R0, arm::Address(arm::R0));
919 __ LoadLiteral(arm::S3, literal);
920 Label label;
921 __ Bind(&label);
922 constexpr size_t kLdrR0R0Count = (1 << 15) - 4u;
923 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
924 __ ldr(arm::R0, arm::Address(arm::R0));
925 }
926
927 std::string expected =
928 "ldr r0, [r0]\n"
929 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
930 "movw ip, #(0x10004 - 0x6 - 4)\n"
931 "1:\n"
932 "add ip, pc\n"
933 "vldr s3, [ip, #0]\n" +
934 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
935 ".align 2, 0\n"
936 "2:\n"
937 ".word 0x12345678\n";
938 DriverStr(expected, "LoadLiteralSingleMax64KiB_UnalignedPC");
939
940 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
941 __ GetAdjustedPosition(label.Position()));
942}
943
944TEST_F(AssemblerThumb2Test, LoadLiteralDoubleBeyondMax64KiB) {
945 // The literal size must match but the type doesn't, so use an int64_t rather than double.
946 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
947 __ LoadLiteral(arm::D3, literal);
948 Label label;
949 __ Bind(&label);
950 constexpr size_t kLdrR0R0Count = (1 << 15) - 2u;
951 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
952 __ ldr(arm::R0, arm::Address(arm::R0));
953 }
954
955 std::string expected =
956 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
957 "movw ip, #((0x1000c - 0x8 - 4) & 0xffff)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000958 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
Vladimir Markoebdbf4b2016-07-07 15:37:02 +0100959 "movt ip, #((0x1000c - 0x8 - 4) >> 16)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000960 "1:\n"
961 "add ip, pc\n"
962 "vldr d3, [ip, #0]\n" +
963 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
964 ".align 2, 0\n"
965 "2:\n"
966 ".word 0x87654321\n"
967 ".word 0x12345678\n";
Vladimir Markoebdbf4b2016-07-07 15:37:02 +0100968 DriverStr(expected, "LoadLiteralDoubleBeyondMax64KiB");
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000969
970 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
971 __ GetAdjustedPosition(label.Position()));
972}
973
974TEST_F(AssemblerThumb2Test, LoadLiteralDoubleFar) {
975 // The literal size must match but the type doesn't, so use an int64_t rather than double.
976 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321));
977 __ LoadLiteral(arm::D3, literal);
978 Label label;
979 __ Bind(&label);
Vladimir Markoebdbf4b2016-07-07 15:37:02 +0100980 constexpr size_t kLdrR0R0Count = (1 << 15) - 2u + 0x1234;
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000981 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
982 __ ldr(arm::R0, arm::Address(arm::R0));
983 }
984
985 std::string expected =
986 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw.
Vladimir Markoebdbf4b2016-07-07 15:37:02 +0100987 "movw ip, #((0x1000c + 2 * 0x1234 - 0x8 - 4) & 0xffff)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000988 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt.
Vladimir Markoebdbf4b2016-07-07 15:37:02 +0100989 "movt ip, #((0x1000c + 2 * 0x1234 - 0x8 - 4) >> 16)\n"
Vladimir Markocf93a5c2015-06-16 11:33:24 +0000990 "1:\n"
991 "add ip, pc\n"
992 "vldr d3, [ip, #0]\n" +
993 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
994 ".align 2, 0\n"
995 "2:\n"
996 ".word 0x87654321\n"
997 ".word 0x12345678\n";
998 DriverStr(expected, "LoadLiteralDoubleFar");
999
1000 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u,
1001 __ GetAdjustedPosition(label.Position()));
1002}
1003
Vladimir Marko663c9342015-07-22 11:28:14 +01001004TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass) {
1005 // First part: as TwoCbzBeyondMaxOffset but add one 16-bit instruction to the end,
1006 // so that the size is not Aligned<4>(.). On the first pass, the assembler resizes
1007 // the second CBZ because it's out of range, then it will resize the first CBZ
1008 // which has been pushed out of range. Thus, after the first pass, the code size
1009 // will appear Aligned<4>(.) but the final size will not be.
1010 Label label0, label1, label2;
1011 __ cbz(arm::R0, &label1);
1012 constexpr size_t kLdrR0R0Count1 = 63;
1013 for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
1014 __ ldr(arm::R0, arm::Address(arm::R0));
1015 }
1016 __ Bind(&label0);
1017 __ cbz(arm::R0, &label2);
1018 __ Bind(&label1);
1019 constexpr size_t kLdrR0R0Count2 = 65;
1020 for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
1021 __ ldr(arm::R0, arm::Address(arm::R0));
1022 }
1023 __ Bind(&label2);
1024 __ ldr(arm::R0, arm::Address(arm::R0));
1025
1026 std::string expected_part1 =
1027 "cmp r0, #0\n" // cbz r0, label1
1028 "beq.n 1f\n" +
1029 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
1030 "0:\n"
1031 "cmp r0, #0\n" // cbz r0, label2
1032 "beq.n 2f\n"
1033 "1:\n" +
1034 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1035 "2:\n" // Here the offset is Aligned<4>(.).
1036 "ldr r0, [r0]\n"; // Make the first part
1037
1038 // Second part: as LoadLiteralMax1KiB with the caveat that the offset of the load
1039 // literal will not be Aligned<4>(.) but it will appear to be when we process the
1040 // instruction during the first pass, so the literal will need a padding and it
1041 // will push the literal out of range, so we shall end up with "ldr.w".
1042 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
1043 __ LoadLiteral(arm::R0, literal);
1044 Label label;
1045 __ Bind(&label);
1046 constexpr size_t kLdrR0R0Count = 511;
1047 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1048 __ ldr(arm::R0, arm::Address(arm::R0));
1049 }
1050
1051 std::string expected =
1052 expected_part1 +
1053 "1:\n"
1054 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
1055 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1056 ".align 2, 0\n"
1057 "2:\n"
1058 ".word 0x12345678\n";
1059 DriverStr(expected, "LoadLiteralMax1KiB");
1060
1061 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
1062 __ GetAdjustedPosition(label.Position()));
1063}
1064
Andreas Gampe7cffc3b2015-10-19 21:31:53 -07001065TEST_F(AssemblerThumb2Test, BindTrackedLabel) {
1066 Label non_tracked, tracked, branch_target;
1067
1068 // A few dummy loads on entry.
1069 constexpr size_t kLdrR0R0Count = 5;
1070 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1071 __ ldr(arm::R0, arm::Address(arm::R0));
1072 }
1073
1074 // A branch that will need to be fixed up.
1075 __ cbz(arm::R0, &branch_target);
1076
1077 // Some more dummy loads.
1078 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1079 __ ldr(arm::R0, arm::Address(arm::R0));
1080 }
1081
1082 // Now insert tracked and untracked label.
1083 __ Bind(&non_tracked);
1084 __ BindTrackedLabel(&tracked);
1085
1086 // A lot of dummy loads, to ensure the branch needs resizing.
1087 constexpr size_t kLdrR0R0CountLong = 60;
1088 for (size_t i = 0; i != kLdrR0R0CountLong; ++i) {
1089 __ ldr(arm::R0, arm::Address(arm::R0));
1090 }
1091
1092 // Bind the branch target.
1093 __ Bind(&branch_target);
1094
1095 // One more load.
1096 __ ldr(arm::R0, arm::Address(arm::R0));
1097
1098 std::string expected =
1099 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1100 "cmp r0, #0\n" // cbz r0, 1f
1101 "beq.n 1f\n" +
1102 RepeatInsn(kLdrR0R0Count + kLdrR0R0CountLong, "ldr r0, [r0]\n") +
1103 "1:\n"
1104 "ldr r0, [r0]\n";
1105 DriverStr(expected, "BindTrackedLabel");
1106
1107 // Expectation is that the tracked label should have moved.
1108 EXPECT_LT(non_tracked.Position(), tracked.Position());
1109}
1110
1111TEST_F(AssemblerThumb2Test, JumpTable) {
1112 // The jump table. Use three labels.
1113 Label label1, label2, label3;
1114 std::vector<Label*> labels({ &label1, &label2, &label3 });
1115
1116 // A few dummy loads on entry, interspersed with 2 labels.
1117 constexpr size_t kLdrR0R0Count = 5;
1118 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1119 __ ldr(arm::R0, arm::Address(arm::R0));
1120 }
1121 __ BindTrackedLabel(&label1);
1122 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1123 __ ldr(arm::R0, arm::Address(arm::R0));
1124 }
1125 __ BindTrackedLabel(&label2);
1126 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1127 __ ldr(arm::R0, arm::Address(arm::R0));
1128 }
1129
1130 // Create the jump table, emit the base load.
1131 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1132
1133 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1134 // it's being used.
1135 __ ldr(arm::R0, arm::Address(arm::R0));
1136
1137 // Emit the jump
1138 __ EmitJumpTableDispatch(jump_table, arm::R1);
1139
1140 // Some more dummy instructions.
1141 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1142 __ ldr(arm::R0, arm::Address(arm::R0));
1143 }
1144 __ BindTrackedLabel(&label3);
1145 for (size_t i = 0; i != kLdrR0R0Count; ++i) { // Note: odd so there's no alignment
1146 __ ldr(arm::R0, arm::Address(arm::R0)); // necessary, as gcc as emits nops,
1147 } // whereas we emit 0 != nop.
1148
1149 static_assert((kLdrR0R0Count + 3) * 2 < 1 * KB, "Too much offset");
1150
1151 std::string expected =
1152 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1153 ".L1:\n" +
1154 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1155 ".L2:\n" +
1156 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1157 "adr r1, .Ljump_table\n"
1158 "ldr r0, [r0]\n"
1159 ".Lbase:\n"
1160 "add pc, r1\n" +
1161 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1162 ".L3:\n" +
1163 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1164 ".align 2\n"
1165 ".Ljump_table:\n"
1166 ".4byte (.L1 - .Lbase - 4)\n"
1167 ".4byte (.L2 - .Lbase - 4)\n"
1168 ".4byte (.L3 - .Lbase - 4)\n";
1169 DriverStr(expected, "JumpTable");
1170}
1171
1172// Test for >1K fixup.
1173TEST_F(AssemblerThumb2Test, JumpTable4K) {
1174 // The jump table. Use three labels.
1175 Label label1, label2, label3;
1176 std::vector<Label*> labels({ &label1, &label2, &label3 });
1177
1178 // A few dummy loads on entry, interspersed with 2 labels.
1179 constexpr size_t kLdrR0R0Count = 5;
1180 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1181 __ ldr(arm::R0, arm::Address(arm::R0));
1182 }
1183 __ BindTrackedLabel(&label1);
1184 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1185 __ ldr(arm::R0, arm::Address(arm::R0));
1186 }
1187 __ BindTrackedLabel(&label2);
1188 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1189 __ ldr(arm::R0, arm::Address(arm::R0));
1190 }
1191
1192 // Create the jump table, emit the base load.
1193 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1194
1195 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1196 // it's being used.
1197 __ ldr(arm::R0, arm::Address(arm::R0));
1198
1199 // Emit the jump
1200 __ EmitJumpTableDispatch(jump_table, arm::R1);
1201
1202 // Some more dummy instructions.
1203 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1204 __ ldr(arm::R0, arm::Address(arm::R0));
1205 }
1206 __ BindTrackedLabel(&label3);
1207 constexpr size_t kLdrR0R0Count2 = 600; // Note: even so there's no alignment
1208 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1209 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1210 }
1211
1212 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 1 * KB, "Not enough offset");
1213 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 4 * KB, "Too much offset");
1214
1215 std::string expected =
1216 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1217 ".L1:\n" +
1218 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1219 ".L2:\n" +
1220 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1221 "adr r1, .Ljump_table\n"
1222 "ldr r0, [r0]\n"
1223 ".Lbase:\n"
1224 "add pc, r1\n" +
1225 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1226 ".L3:\n" +
1227 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1228 ".align 2\n"
1229 ".Ljump_table:\n"
1230 ".4byte (.L1 - .Lbase - 4)\n"
1231 ".4byte (.L2 - .Lbase - 4)\n"
1232 ".4byte (.L3 - .Lbase - 4)\n";
1233 DriverStr(expected, "JumpTable4K");
1234}
1235
1236// Test for >4K fixup.
1237TEST_F(AssemblerThumb2Test, JumpTable64K) {
1238 // The jump table. Use three labels.
1239 Label label1, label2, label3;
1240 std::vector<Label*> labels({ &label1, &label2, &label3 });
1241
1242 // A few dummy loads on entry, interspersed with 2 labels.
1243 constexpr size_t kLdrR0R0Count = 5;
1244 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1245 __ ldr(arm::R0, arm::Address(arm::R0));
1246 }
1247 __ BindTrackedLabel(&label1);
1248 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1249 __ ldr(arm::R0, arm::Address(arm::R0));
1250 }
1251 __ BindTrackedLabel(&label2);
1252 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1253 __ ldr(arm::R0, arm::Address(arm::R0));
1254 }
1255
1256 // Create the jump table, emit the base load.
1257 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1258
1259 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1260 // it's being used.
1261 __ ldr(arm::R0, arm::Address(arm::R0));
1262
1263 // Emit the jump
1264 __ EmitJumpTableDispatch(jump_table, arm::R1);
1265
1266 // Some more dummy instructions.
1267 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1268 __ ldr(arm::R0, arm::Address(arm::R0));
1269 }
1270 __ BindTrackedLabel(&label3);
1271 constexpr size_t kLdrR0R0Count2 = 2601; // Note: odd so there's no alignment
1272 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1273 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1274 }
1275
1276 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 4 * KB, "Not enough offset");
1277 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 64 * KB, "Too much offset");
1278
1279 std::string expected =
1280 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1281 ".L1:\n" +
1282 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1283 ".L2:\n" +
1284 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1285 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
1286 // (Note: have to use constants, as labels aren't accepted.
1287 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1288 ") * 2 - 4) & 0xFFFF)\n"
1289 "add r1, pc\n"
1290 "ldr r0, [r0]\n"
1291 ".Lbase:\n"
1292 "add pc, r1\n" +
1293 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1294 ".L3:\n" +
1295 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1296 ".align 2\n"
1297 ".Ljump_table:\n"
1298 ".4byte (.L1 - .Lbase - 4)\n"
1299 ".4byte (.L2 - .Lbase - 4)\n"
1300 ".4byte (.L3 - .Lbase - 4)\n";
1301 DriverStr(expected, "JumpTable64K");
1302}
1303
1304// Test for >64K fixup.
1305TEST_F(AssemblerThumb2Test, JumpTableFar) {
1306 // The jump table. Use three labels.
1307 Label label1, label2, label3;
1308 std::vector<Label*> labels({ &label1, &label2, &label3 });
1309
1310 // A few dummy loads on entry, interspersed with 2 labels.
1311 constexpr size_t kLdrR0R0Count = 5;
1312 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1313 __ ldr(arm::R0, arm::Address(arm::R0));
1314 }
1315 __ BindTrackedLabel(&label1);
1316 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1317 __ ldr(arm::R0, arm::Address(arm::R0));
1318 }
1319 __ BindTrackedLabel(&label2);
1320 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1321 __ ldr(arm::R0, arm::Address(arm::R0));
1322 }
1323
1324 // Create the jump table, emit the base load.
1325 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
1326
1327 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
1328 // it's being used.
1329 __ ldr(arm::R0, arm::Address(arm::R0));
1330
1331 // Emit the jump
1332 __ EmitJumpTableDispatch(jump_table, arm::R1);
1333
1334 // Some more dummy instructions.
1335 for (size_t i = 0; i != kLdrR0R0Count; ++i) {
1336 __ ldr(arm::R0, arm::Address(arm::R0));
1337 }
1338 __ BindTrackedLabel(&label3);
1339 constexpr size_t kLdrR0R0Count2 = 70001; // Note: odd so there's no alignment
1340 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
1341 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
1342 }
1343
1344 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 64 * KB, "Not enough offset");
1345
1346 std::string expected =
1347 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1348 ".L1:\n" +
1349 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1350 ".L2:\n" +
1351 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1352 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
1353 // (Note: have to use constants, as labels aren't accepted.
1354 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1355 ") * 2 - 4) & 0xFFFF)\n"
1356 "movt r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
1357 ") * 2 - 4) >> 16)\n"
1358 ".Lhelp:"
1359 "add r1, pc\n"
1360 "ldr r0, [r0]\n"
1361 ".Lbase:\n"
1362 "add pc, r1\n" +
1363 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
1364 ".L3:\n" +
1365 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
1366 ".align 2\n"
1367 ".Ljump_table:\n"
1368 ".4byte (.L1 - .Lbase - 4)\n"
1369 ".4byte (.L2 - .Lbase - 4)\n"
1370 ".4byte (.L3 - .Lbase - 4)\n";
1371 DriverStr(expected, "JumpTableFar");
1372}
1373
Scott Wakeling611d3392015-07-10 11:42:06 +01001374TEST_F(AssemblerThumb2Test, Clz) {
1375 __ clz(arm::R0, arm::R1);
1376
1377 const char* expected = "clz r0, r1\n";
1378
1379 DriverStr(expected, "clz");
1380}
1381
Scott Wakeling9ee23f42015-07-23 10:44:35 +01001382TEST_F(AssemblerThumb2Test, rbit) {
1383 __ rbit(arm::R1, arm::R0);
1384
1385 const char* expected = "rbit r1, r0\n";
1386
1387 DriverStr(expected, "rbit");
1388}
1389
Artem Serovc257da72016-02-02 13:49:43 +00001390TEST_F(AssemblerThumb2Test, rev) {
1391 __ rev(arm::R1, arm::R0);
1392
1393 const char* expected = "rev r1, r0\n";
1394
1395 DriverStr(expected, "rev");
1396}
1397
1398TEST_F(AssemblerThumb2Test, rev16) {
1399 __ rev16(arm::R1, arm::R0);
1400
1401 const char* expected = "rev16 r1, r0\n";
1402
1403 DriverStr(expected, "rev16");
1404}
1405
1406TEST_F(AssemblerThumb2Test, revsh) {
1407 __ revsh(arm::R1, arm::R0);
1408
1409 const char* expected = "revsh r1, r0\n";
1410
1411 DriverStr(expected, "revsh");
1412}
1413
xueliang.zhonge652c122016-06-13 14:42:27 +01001414TEST_F(AssemblerThumb2Test, vcnt) {
1415 // Different D register numbers are used here, to test register encoding.
1416 // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
1417 // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
1418 // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
1419 __ vcntd(arm::D0, arm::D1);
1420 __ vcntd(arm::D19, arm::D20);
1421 __ vcntd(arm::D0, arm::D9);
1422 __ vcntd(arm::D16, arm::D20);
1423
1424 std::string expected =
1425 "vcnt.8 d0, d1\n"
1426 "vcnt.8 d19, d20\n"
1427 "vcnt.8 d0, d9\n"
1428 "vcnt.8 d16, d20\n";
1429
1430 DriverStr(expected, "vcnt");
1431}
1432
1433TEST_F(AssemblerThumb2Test, vpaddl) {
1434 // Different D register numbers are used here, to test register encoding.
1435 // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
1436 // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
1437 // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
1438 // Different data types (signed and unsigned) are also tested.
1439 __ vpaddld(arm::D0, arm::D0, 8, true);
1440 __ vpaddld(arm::D20, arm::D20, 8, false);
1441 __ vpaddld(arm::D0, arm::D20, 16, false);
1442 __ vpaddld(arm::D20, arm::D0, 32, true);
1443
1444 std::string expected =
1445 "vpaddl.u8 d0, d0\n"
1446 "vpaddl.s8 d20, d20\n"
1447 "vpaddl.s16 d0, d20\n"
1448 "vpaddl.u32 d20, d0\n";
1449
1450 DriverStr(expected, "vpaddl");
1451}
1452
Roland Levillain1a28fc42014-11-13 18:03:06 +00001453} // namespace art