assembler_thumb2_test.cc revision c257da7b0fb6737f65aba426add8831e45404755
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "assembler_thumb2.h" 18 19#include "base/stl_util.h" 20#include "base/stringprintf.h" 21#include "utils/assembler_test.h" 22 23namespace art { 24 25class AssemblerThumb2Test : public AssemblerTest<arm::Thumb2Assembler, 26 arm::Register, arm::SRegister, 27 uint32_t> { 28 protected: 29 std::string GetArchitectureString() OVERRIDE { 30 return "arm"; 31 } 32 33 std::string GetAssemblerParameters() OVERRIDE { 34 return " -march=armv7-a -mcpu=cortex-a15 -mfpu=neon -mthumb"; 35 } 36 37 const char* GetAssemblyHeader() OVERRIDE { 38 return kThumb2AssemblyHeader; 39 } 40 41 std::string GetDisassembleParameters() OVERRIDE { 42 return " -D -bbinary -marm --disassembler-options=force-thumb --no-show-raw-insn"; 43 } 44 45 void SetUpHelpers() OVERRIDE { 46 if (registers_.size() == 0) { 47 registers_.insert(end(registers_), 48 { // NOLINT(whitespace/braces) 49 new arm::Register(arm::R0), 50 new arm::Register(arm::R1), 51 new arm::Register(arm::R2), 52 new arm::Register(arm::R3), 53 new arm::Register(arm::R4), 54 new arm::Register(arm::R5), 55 new arm::Register(arm::R6), 56 new arm::Register(arm::R7), 57 new arm::Register(arm::R8), 58 new arm::Register(arm::R9), 59 new arm::Register(arm::R10), 60 new arm::Register(arm::R11), 61 new arm::Register(arm::R12), 62 new arm::Register(arm::R13), 63 new arm::Register(arm::R14), 64 new arm::Register(arm::R15) 65 }); 66 } 67 } 68 69 void TearDown() OVERRIDE { 70 AssemblerTest::TearDown(); 71 STLDeleteElements(®isters_); 72 } 73 74 std::vector<arm::Register*> GetRegisters() OVERRIDE { 75 return registers_; 76 } 77 78 uint32_t CreateImmediate(int64_t imm_value) OVERRIDE { 79 return imm_value; 80 } 81 82 std::string RepeatInsn(size_t count, const std::string& insn) { 83 std::string result; 84 for (; count != 0u; --count) { 85 result += insn; 86 } 87 return result; 88 } 89 90 private: 91 std::vector<arm::Register*> registers_; 92 93 static constexpr const char* kThumb2AssemblyHeader = ".syntax unified\n.thumb\n"; 94}; 95 96TEST_F(AssemblerThumb2Test, Toolchain) { 97 EXPECT_TRUE(CheckTools()); 98} 99 100#define __ GetAssembler()-> 101 102TEST_F(AssemblerThumb2Test, Sbfx) { 103 __ sbfx(arm::R0, arm::R1, 0, 1); 104 __ sbfx(arm::R0, arm::R1, 0, 8); 105 __ sbfx(arm::R0, arm::R1, 0, 16); 106 __ sbfx(arm::R0, arm::R1, 0, 32); 107 108 __ sbfx(arm::R0, arm::R1, 8, 1); 109 __ sbfx(arm::R0, arm::R1, 8, 8); 110 __ sbfx(arm::R0, arm::R1, 8, 16); 111 __ sbfx(arm::R0, arm::R1, 8, 24); 112 113 __ sbfx(arm::R0, arm::R1, 16, 1); 114 __ sbfx(arm::R0, arm::R1, 16, 8); 115 __ sbfx(arm::R0, arm::R1, 16, 16); 116 117 __ sbfx(arm::R0, arm::R1, 31, 1); 118 119 const char* expected = 120 "sbfx r0, r1, #0, #1\n" 121 "sbfx r0, r1, #0, #8\n" 122 "sbfx r0, r1, #0, #16\n" 123 "sbfx r0, r1, #0, #32\n" 124 125 "sbfx r0, r1, #8, #1\n" 126 "sbfx r0, r1, #8, #8\n" 127 "sbfx r0, r1, #8, #16\n" 128 "sbfx r0, r1, #8, #24\n" 129 130 "sbfx r0, r1, #16, #1\n" 131 "sbfx r0, r1, #16, #8\n" 132 "sbfx r0, r1, #16, #16\n" 133 134 "sbfx r0, r1, #31, #1\n"; 135 DriverStr(expected, "sbfx"); 136} 137 138TEST_F(AssemblerThumb2Test, Ubfx) { 139 __ ubfx(arm::R0, arm::R1, 0, 1); 140 __ ubfx(arm::R0, arm::R1, 0, 8); 141 __ ubfx(arm::R0, arm::R1, 0, 16); 142 __ ubfx(arm::R0, arm::R1, 0, 32); 143 144 __ ubfx(arm::R0, arm::R1, 8, 1); 145 __ ubfx(arm::R0, arm::R1, 8, 8); 146 __ ubfx(arm::R0, arm::R1, 8, 16); 147 __ ubfx(arm::R0, arm::R1, 8, 24); 148 149 __ ubfx(arm::R0, arm::R1, 16, 1); 150 __ ubfx(arm::R0, arm::R1, 16, 8); 151 __ ubfx(arm::R0, arm::R1, 16, 16); 152 153 __ ubfx(arm::R0, arm::R1, 31, 1); 154 155 const char* expected = 156 "ubfx r0, r1, #0, #1\n" 157 "ubfx r0, r1, #0, #8\n" 158 "ubfx r0, r1, #0, #16\n" 159 "ubfx r0, r1, #0, #32\n" 160 161 "ubfx r0, r1, #8, #1\n" 162 "ubfx r0, r1, #8, #8\n" 163 "ubfx r0, r1, #8, #16\n" 164 "ubfx r0, r1, #8, #24\n" 165 166 "ubfx r0, r1, #16, #1\n" 167 "ubfx r0, r1, #16, #8\n" 168 "ubfx r0, r1, #16, #16\n" 169 170 "ubfx r0, r1, #31, #1\n"; 171 DriverStr(expected, "ubfx"); 172} 173 174TEST_F(AssemblerThumb2Test, Vmstat) { 175 __ vmstat(); 176 177 const char* expected = "vmrs APSR_nzcv, FPSCR\n"; 178 179 DriverStr(expected, "vmrs"); 180} 181 182TEST_F(AssemblerThumb2Test, ldrexd) { 183 __ ldrexd(arm::R0, arm::R1, arm::R0); 184 __ ldrexd(arm::R0, arm::R1, arm::R1); 185 __ ldrexd(arm::R0, arm::R1, arm::R2); 186 __ ldrexd(arm::R5, arm::R3, arm::R7); 187 188 const char* expected = 189 "ldrexd r0, r1, [r0]\n" 190 "ldrexd r0, r1, [r1]\n" 191 "ldrexd r0, r1, [r2]\n" 192 "ldrexd r5, r3, [r7]\n"; 193 DriverStr(expected, "ldrexd"); 194} 195 196TEST_F(AssemblerThumb2Test, strexd) { 197 __ strexd(arm::R9, arm::R0, arm::R1, arm::R0); 198 __ strexd(arm::R9, arm::R0, arm::R1, arm::R1); 199 __ strexd(arm::R9, arm::R0, arm::R1, arm::R2); 200 __ strexd(arm::R9, arm::R5, arm::R3, arm::R7); 201 202 const char* expected = 203 "strexd r9, r0, r1, [r0]\n" 204 "strexd r9, r0, r1, [r1]\n" 205 "strexd r9, r0, r1, [r2]\n" 206 "strexd r9, r5, r3, [r7]\n"; 207 DriverStr(expected, "strexd"); 208} 209 210TEST_F(AssemblerThumb2Test, LdrdStrd) { 211 __ ldrd(arm::R0, arm::Address(arm::R2, 8)); 212 __ ldrd(arm::R0, arm::Address(arm::R12)); 213 __ strd(arm::R0, arm::Address(arm::R2, 8)); 214 215 const char* expected = 216 "ldrd r0, r1, [r2, #8]\n" 217 "ldrd r0, r1, [r12]\n" 218 "strd r0, r1, [r2, #8]\n"; 219 DriverStr(expected, "ldrdstrd"); 220} 221 222TEST_F(AssemblerThumb2Test, eor) { 223 __ eor(arm::R1, arm::R1, arm::ShifterOperand(arm::R0)); 224 __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R1)); 225 __ eor(arm::R1, arm::R8, arm::ShifterOperand(arm::R0)); 226 __ eor(arm::R8, arm::R1, arm::ShifterOperand(arm::R0)); 227 __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R8)); 228 229 const char* expected = 230 "eors r1, r0\n" 231 "eor r1, r0, r1\n" 232 "eor r1, r8, r0\n" 233 "eor r8, r1, r0\n" 234 "eor r1, r0, r8\n"; 235 DriverStr(expected, "abs"); 236} 237 238TEST_F(AssemblerThumb2Test, sub) { 239 __ subs(arm::R1, arm::R0, arm::ShifterOperand(42)); 240 __ sub(arm::R1, arm::R0, arm::ShifterOperand(42)); 241 __ subs(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31)); 242 __ sub(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31)); 243 244 const char* expected = 245 "subs r1, r0, #42\n" 246 "sub.w r1, r0, #42\n" 247 "subs r1, r0, r2, asr #31\n" 248 "sub r1, r0, r2, asr #31\n"; 249 DriverStr(expected, "sub"); 250} 251 252TEST_F(AssemblerThumb2Test, add) { 253 __ adds(arm::R1, arm::R0, arm::ShifterOperand(42)); 254 __ add(arm::R1, arm::R0, arm::ShifterOperand(42)); 255 __ adds(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31)); 256 __ add(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31)); 257 258 const char* expected = 259 "adds r1, r0, #42\n" 260 "add.w r1, r0, #42\n" 261 "adds r1, r0, r2, asr #31\n" 262 "add r1, r0, r2, asr #31\n"; 263 DriverStr(expected, "add"); 264} 265 266TEST_F(AssemblerThumb2Test, umull) { 267 __ umull(arm::R0, arm::R1, arm::R2, arm::R3); 268 269 const char* expected = 270 "umull r0, r1, r2, r3\n"; 271 DriverStr(expected, "umull"); 272} 273 274TEST_F(AssemblerThumb2Test, smull) { 275 __ smull(arm::R0, arm::R1, arm::R2, arm::R3); 276 277 const char* expected = 278 "smull r0, r1, r2, r3\n"; 279 DriverStr(expected, "smull"); 280} 281 282TEST_F(AssemblerThumb2Test, StoreWordToThumbOffset) { 283 arm::StoreOperandType type = arm::kStoreWord; 284 int32_t offset = 4092; 285 ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset)); 286 287 __ StoreToOffset(type, arm::R0, arm::SP, offset); 288 __ StoreToOffset(type, arm::IP, arm::SP, offset); 289 __ StoreToOffset(type, arm::IP, arm::R5, offset); 290 291 const char* expected = 292 "str r0, [sp, #4092]\n" 293 "str ip, [sp, #4092]\n" 294 "str ip, [r5, #4092]\n"; 295 DriverStr(expected, "StoreWordToThumbOffset"); 296} 297 298TEST_F(AssemblerThumb2Test, StoreWordToNonThumbOffset) { 299 arm::StoreOperandType type = arm::kStoreWord; 300 int32_t offset = 4096; 301 ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset)); 302 303 __ StoreToOffset(type, arm::R0, arm::SP, offset); 304 __ StoreToOffset(type, arm::IP, arm::SP, offset); 305 __ StoreToOffset(type, arm::IP, arm::R5, offset); 306 307 const char* expected = 308 "add.w ip, sp, #4096\n" // AddConstant(ip, sp, 4096) 309 "str r0, [ip, #0]\n" 310 311 "str r5, [sp, #-4]!\n" // Push(r5) 312 "add.w r5, sp, #4096\n" // AddConstant(r5, 4100 & ~0xfff) 313 "str ip, [r5, #4]\n" // StoreToOffset(type, ip, r5, 4100 & 0xfff) 314 "ldr r5, [sp], #4\n" // Pop(r5) 315 316 "str r6, [sp, #-4]!\n" // Push(r6) 317 "add.w r6, r5, #4096\n" // AddConstant(r6, r5, 4096 & ~0xfff) 318 "str ip, [r6, #0]\n" // StoreToOffset(type, ip, r6, 4096 & 0xfff) 319 "ldr r6, [sp], #4\n"; // Pop(r6) 320 DriverStr(expected, "StoreWordToNonThumbOffset"); 321} 322 323TEST_F(AssemblerThumb2Test, StoreWordPairToThumbOffset) { 324 arm::StoreOperandType type = arm::kStoreWordPair; 325 int32_t offset = 1020; 326 ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset)); 327 328 __ StoreToOffset(type, arm::R0, arm::SP, offset); 329 // We cannot use IP (i.e. R12) as first source register, as it would 330 // force us to use SP (i.e. R13) as second source register, which 331 // would have an "unpredictable" effect according to the ARMv7 332 // specification (the T1 encoding describes the result as 333 // UNPREDICTABLE when of the source registers is R13). 334 // 335 // So we use (R11, IP) (e.g. (R11, R12)) as source registers in the 336 // following instructions. 337 __ StoreToOffset(type, arm::R11, arm::SP, offset); 338 __ StoreToOffset(type, arm::R11, arm::R5, offset); 339 340 const char* expected = 341 "strd r0, r1, [sp, #1020]\n" 342 "strd r11, ip, [sp, #1020]\n" 343 "strd r11, ip, [r5, #1020]\n"; 344 DriverStr(expected, "StoreWordPairToThumbOffset"); 345} 346 347TEST_F(AssemblerThumb2Test, StoreWordPairToNonThumbOffset) { 348 arm::StoreOperandType type = arm::kStoreWordPair; 349 int32_t offset = 1024; 350 ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset)); 351 352 __ StoreToOffset(type, arm::R0, arm::SP, offset); 353 // Same comment as in AssemblerThumb2Test.StoreWordPairToThumbOffset 354 // regarding the use of (R11, IP) (e.g. (R11, R12)) as source 355 // registers in the following instructions. 356 __ StoreToOffset(type, arm::R11, arm::SP, offset); 357 __ StoreToOffset(type, arm::R11, arm::R5, offset); 358 359 const char* expected = 360 "add.w ip, sp, #1024\n" // AddConstant(ip, sp, 1024) 361 "strd r0, r1, [ip, #0]\n" 362 363 "str r5, [sp, #-4]!\n" // Push(r5) 364 "add.w r5, sp, #1024\n" // AddConstant(r5, sp, (1024 + kRegisterSize) & ~0x3fc) 365 "strd r11, ip, [r5, #4]\n" // StoreToOffset(type, r11, sp, (1024 + kRegisterSize) & 0x3fc) 366 "ldr r5, [sp], #4\n" // Pop(r5) 367 368 "str r6, [sp, #-4]!\n" // Push(r6) 369 "add.w r6, r5, #1024\n" // AddConstant(r6, r5, 1024 & ~0x3fc) 370 "strd r11, ip, [r6, #0]\n" // StoreToOffset(type, r11, r6, 1024 & 0x3fc) 371 "ldr r6, [sp], #4\n"; // Pop(r6) 372 DriverStr(expected, "StoreWordPairToNonThumbOffset"); 373} 374 375TEST_F(AssemblerThumb2Test, TwoCbzMaxOffset) { 376 Label label0, label1, label2; 377 __ cbz(arm::R0, &label1); 378 constexpr size_t kLdrR0R0Count1 = 63; 379 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 380 __ ldr(arm::R0, arm::Address(arm::R0)); 381 } 382 __ Bind(&label0); 383 __ cbz(arm::R0, &label2); 384 __ Bind(&label1); 385 constexpr size_t kLdrR0R0Count2 = 64; 386 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 387 __ ldr(arm::R0, arm::Address(arm::R0)); 388 } 389 __ Bind(&label2); 390 391 std::string expected = 392 "cbz r0, 1f\n" + // cbz r0, label1 393 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 394 "0:\n" 395 "cbz r0, 2f\n" // cbz r0, label2 396 "1:\n" + 397 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 398 "2:\n"; 399 DriverStr(expected, "TwoCbzMaxOffset"); 400 401 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u, 402 __ GetAdjustedPosition(label0.Position())); 403 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 0u, 404 __ GetAdjustedPosition(label1.Position())); 405 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 0u, 406 __ GetAdjustedPosition(label2.Position())); 407} 408 409TEST_F(AssemblerThumb2Test, TwoCbzBeyondMaxOffset) { 410 Label label0, label1, label2; 411 __ cbz(arm::R0, &label1); 412 constexpr size_t kLdrR0R0Count1 = 63; 413 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 414 __ ldr(arm::R0, arm::Address(arm::R0)); 415 } 416 __ Bind(&label0); 417 __ cbz(arm::R0, &label2); 418 __ Bind(&label1); 419 constexpr size_t kLdrR0R0Count2 = 65; 420 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 421 __ ldr(arm::R0, arm::Address(arm::R0)); 422 } 423 __ Bind(&label2); 424 425 std::string expected = 426 "cmp r0, #0\n" // cbz r0, label1 427 "beq.n 1f\n" + 428 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 429 "0:\n" 430 "cmp r0, #0\n" // cbz r0, label2 431 "beq.n 2f\n" 432 "1:\n" + 433 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 434 "2:\n"; 435 DriverStr(expected, "TwoCbzBeyondMaxOffset"); 436 437 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u, 438 __ GetAdjustedPosition(label0.Position())); 439 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 4u, 440 __ GetAdjustedPosition(label1.Position())); 441 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 4u, 442 __ GetAdjustedPosition(label2.Position())); 443} 444 445TEST_F(AssemblerThumb2Test, TwoCbzSecondAtMaxB16Offset) { 446 Label label0, label1, label2; 447 __ cbz(arm::R0, &label1); 448 constexpr size_t kLdrR0R0Count1 = 62; 449 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 450 __ ldr(arm::R0, arm::Address(arm::R0)); 451 } 452 __ Bind(&label0); 453 __ cbz(arm::R0, &label2); 454 __ Bind(&label1); 455 constexpr size_t kLdrR0R0Count2 = 128; 456 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 457 __ ldr(arm::R0, arm::Address(arm::R0)); 458 } 459 __ Bind(&label2); 460 461 std::string expected = 462 "cbz r0, 1f\n" + // cbz r0, label1 463 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 464 "0:\n" 465 "cmp r0, #0\n" // cbz r0, label2 466 "beq.n 2f\n" 467 "1:\n" + 468 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 469 "2:\n"; 470 DriverStr(expected, "TwoCbzSecondAtMaxB16Offset"); 471 472 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u, 473 __ GetAdjustedPosition(label0.Position())); 474 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u, 475 __ GetAdjustedPosition(label1.Position())); 476 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u, 477 __ GetAdjustedPosition(label2.Position())); 478} 479 480TEST_F(AssemblerThumb2Test, TwoCbzSecondBeyondMaxB16Offset) { 481 Label label0, label1, label2; 482 __ cbz(arm::R0, &label1); 483 constexpr size_t kLdrR0R0Count1 = 62; 484 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 485 __ ldr(arm::R0, arm::Address(arm::R0)); 486 } 487 __ Bind(&label0); 488 __ cbz(arm::R0, &label2); 489 __ Bind(&label1); 490 constexpr size_t kLdrR0R0Count2 = 129; 491 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 492 __ ldr(arm::R0, arm::Address(arm::R0)); 493 } 494 __ Bind(&label2); 495 496 std::string expected = 497 "cmp r0, #0\n" // cbz r0, label1 498 "beq.n 1f\n" + 499 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 500 "0:\n" 501 "cmp r0, #0\n" // cbz r0, label2 502 "beq.w 2f\n" 503 "1:\n" + 504 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 505 "2:\n"; 506 DriverStr(expected, "TwoCbzSecondBeyondMaxB16Offset"); 507 508 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u, 509 __ GetAdjustedPosition(label0.Position())); 510 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u, 511 __ GetAdjustedPosition(label1.Position())); 512 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u, 513 __ GetAdjustedPosition(label2.Position())); 514} 515 516TEST_F(AssemblerThumb2Test, TwoCbzFirstAtMaxB16Offset) { 517 Label label0, label1, label2; 518 __ cbz(arm::R0, &label1); 519 constexpr size_t kLdrR0R0Count1 = 127; 520 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 521 __ ldr(arm::R0, arm::Address(arm::R0)); 522 } 523 __ Bind(&label0); 524 __ cbz(arm::R0, &label2); 525 __ Bind(&label1); 526 constexpr size_t kLdrR0R0Count2 = 64; 527 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 528 __ ldr(arm::R0, arm::Address(arm::R0)); 529 } 530 __ Bind(&label2); 531 532 std::string expected = 533 "cmp r0, #0\n" // cbz r0, label1 534 "beq.n 1f\n" + 535 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 536 "0:\n" 537 "cbz r0, 2f\n" // cbz r0, label2 538 "1:\n" + 539 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 540 "2:\n"; 541 DriverStr(expected, "TwoCbzFirstAtMaxB16Offset"); 542 543 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u, 544 __ GetAdjustedPosition(label0.Position())); 545 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u, 546 __ GetAdjustedPosition(label1.Position())); 547 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u, 548 __ GetAdjustedPosition(label2.Position())); 549} 550 551TEST_F(AssemblerThumb2Test, TwoCbzFirstBeyondMaxB16Offset) { 552 Label label0, label1, label2; 553 __ cbz(arm::R0, &label1); 554 constexpr size_t kLdrR0R0Count1 = 127; 555 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 556 __ ldr(arm::R0, arm::Address(arm::R0)); 557 } 558 __ Bind(&label0); 559 __ cbz(arm::R0, &label2); 560 __ Bind(&label1); 561 constexpr size_t kLdrR0R0Count2 = 65; 562 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 563 __ ldr(arm::R0, arm::Address(arm::R0)); 564 } 565 __ Bind(&label2); 566 567 std::string expected = 568 "cmp r0, #0\n" // cbz r0, label1 569 "beq.w 1f\n" + 570 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 571 "0:\n" 572 "cmp r0, #0\n" // cbz r0, label2 573 "beq.n 2f\n" 574 "1:\n" + 575 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 576 "2:\n"; 577 DriverStr(expected, "TwoCbzFirstBeyondMaxB16Offset"); 578 579 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 4u, 580 __ GetAdjustedPosition(label0.Position())); 581 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u, 582 __ GetAdjustedPosition(label1.Position())); 583 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u, 584 __ GetAdjustedPosition(label2.Position())); 585} 586 587TEST_F(AssemblerThumb2Test, LoadLiteralMax1KiB) { 588 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 589 __ LoadLiteral(arm::R0, literal); 590 Label label; 591 __ Bind(&label); 592 constexpr size_t kLdrR0R0Count = 511; 593 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 594 __ ldr(arm::R0, arm::Address(arm::R0)); 595 } 596 597 std::string expected = 598 "1:\n" 599 "ldr.n r0, [pc, #((2f - 1b - 2) & ~2)]\n" + 600 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 601 ".align 2, 0\n" 602 "2:\n" 603 ".word 0x12345678\n"; 604 DriverStr(expected, "LoadLiteralMax1KiB"); 605 606 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u, 607 __ GetAdjustedPosition(label.Position())); 608} 609 610TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiB) { 611 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 612 __ LoadLiteral(arm::R0, literal); 613 Label label; 614 __ Bind(&label); 615 constexpr size_t kLdrR0R0Count = 512; 616 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 617 __ ldr(arm::R0, arm::Address(arm::R0)); 618 } 619 620 std::string expected = 621 "1:\n" 622 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" + 623 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 624 ".align 2, 0\n" 625 "2:\n" 626 ".word 0x12345678\n"; 627 DriverStr(expected, "LoadLiteralBeyondMax1KiB"); 628 629 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u, 630 __ GetAdjustedPosition(label.Position())); 631} 632 633TEST_F(AssemblerThumb2Test, LoadLiteralMax4KiB) { 634 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 635 __ LoadLiteral(arm::R1, literal); 636 Label label; 637 __ Bind(&label); 638 constexpr size_t kLdrR0R0Count = 2046; 639 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 640 __ ldr(arm::R0, arm::Address(arm::R0)); 641 } 642 643 std::string expected = 644 "1:\n" 645 "ldr.w r1, [pc, #((2f - 1b - 2) & ~2)]\n" + 646 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 647 ".align 2, 0\n" 648 "2:\n" 649 ".word 0x12345678\n"; 650 DriverStr(expected, "LoadLiteralMax4KiB"); 651 652 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u, 653 __ GetAdjustedPosition(label.Position())); 654} 655 656TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax4KiB) { 657 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 658 __ LoadLiteral(arm::R1, literal); 659 Label label; 660 __ Bind(&label); 661 constexpr size_t kLdrR0R0Count = 2047; 662 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 663 __ ldr(arm::R0, arm::Address(arm::R0)); 664 } 665 666 std::string expected = 667 "movw r1, #4096\n" // "as" does not consider (2f - 1f - 4) a constant expression for movw. 668 "1:\n" 669 "add r1, pc\n" 670 "ldr r1, [r1, #0]\n" + 671 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 672 ".align 2, 0\n" 673 "2:\n" 674 ".word 0x12345678\n"; 675 DriverStr(expected, "LoadLiteralBeyondMax4KiB"); 676 677 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u, 678 __ GetAdjustedPosition(label.Position())); 679} 680 681TEST_F(AssemblerThumb2Test, LoadLiteralMax64KiB) { 682 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 683 __ LoadLiteral(arm::R1, literal); 684 Label label; 685 __ Bind(&label); 686 constexpr size_t kLdrR0R0Count = (1u << 15) - 2u; 687 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 688 __ ldr(arm::R0, arm::Address(arm::R0)); 689 } 690 691 std::string expected = 692 "movw r1, #0xfffc\n" // "as" does not consider (2f - 1f - 4) a constant expression for movw. 693 "1:\n" 694 "add r1, pc\n" 695 "ldr r1, [r1, #0]\n" + 696 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 697 ".align 2, 0\n" 698 "2:\n" 699 ".word 0x12345678\n"; 700 DriverStr(expected, "LoadLiteralMax64KiB"); 701 702 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u, 703 __ GetAdjustedPosition(label.Position())); 704} 705 706TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax64KiB) { 707 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 708 __ LoadLiteral(arm::R1, literal); 709 Label label; 710 __ Bind(&label); 711 constexpr size_t kLdrR0R0Count = (1u << 15) - 1u; 712 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 713 __ ldr(arm::R0, arm::Address(arm::R0)); 714 } 715 716 std::string expected = 717 "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n" 718 "1:\n" 719 "add r1, pc\n" 720 "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" + 721 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 722 ".align 2, 0\n" 723 "2:\n" 724 ".word 0x12345678\n"; 725 DriverStr(expected, "LoadLiteralBeyondMax64KiB"); 726 727 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u, 728 __ GetAdjustedPosition(label.Position())); 729} 730 731TEST_F(AssemblerThumb2Test, LoadLiteralMax1MiB) { 732 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 733 __ LoadLiteral(arm::R1, literal); 734 Label label; 735 __ Bind(&label); 736 constexpr size_t kLdrR0R0Count = (1u << 19) - 3u; 737 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 738 __ ldr(arm::R0, arm::Address(arm::R0)); 739 } 740 741 std::string expected = 742 "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n" 743 "1:\n" 744 "add r1, pc\n" 745 "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" + 746 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 747 ".align 2, 0\n" 748 "2:\n" 749 ".word 0x12345678\n"; 750 DriverStr(expected, "LoadLiteralMax1MiB"); 751 752 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u, 753 __ GetAdjustedPosition(label.Position())); 754} 755 756TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1MiB) { 757 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 758 __ LoadLiteral(arm::R1, literal); 759 Label label; 760 __ Bind(&label); 761 constexpr size_t kLdrR0R0Count = (1u << 19) - 2u; 762 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 763 __ ldr(arm::R0, arm::Address(arm::R0)); 764 } 765 766 std::string expected = 767 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw. 768 "movw r1, #(0x100000 & 0xffff)\n" 769 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt. 770 "movt r1, #(0x100000 >> 16)\n" 771 "1:\n" 772 "add r1, pc\n" 773 "ldr.w r1, [r1, #0]\n" + 774 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 775 ".align 2, 0\n" 776 "2:\n" 777 ".word 0x12345678\n"; 778 DriverStr(expected, "LoadLiteralBeyondMax1MiB"); 779 780 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u, 781 __ GetAdjustedPosition(label.Position())); 782} 783 784TEST_F(AssemblerThumb2Test, LoadLiteralFar) { 785 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 786 __ LoadLiteral(arm::R1, literal); 787 Label label; 788 __ Bind(&label); 789 constexpr size_t kLdrR0R0Count = (1u << 19) - 2u + 0x1234; 790 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 791 __ ldr(arm::R0, arm::Address(arm::R0)); 792 } 793 794 std::string expected = 795 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw. 796 "movw r1, #((0x100000 + 2 * 0x1234) & 0xffff)\n" 797 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt. 798 "movt r1, #((0x100000 + 2 * 0x1234) >> 16)\n" 799 "1:\n" 800 "add r1, pc\n" 801 "ldr.w r1, [r1, #0]\n" + 802 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 803 ".align 2, 0\n" 804 "2:\n" 805 ".word 0x12345678\n"; 806 DriverStr(expected, "LoadLiteralFar"); 807 808 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u, 809 __ GetAdjustedPosition(label.Position())); 810} 811 812TEST_F(AssemblerThumb2Test, LoadLiteralWideMax1KiB) { 813 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321)); 814 __ LoadLiteral(arm::R1, arm::R3, literal); 815 Label label; 816 __ Bind(&label); 817 constexpr size_t kLdrR0R0Count = 510; 818 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 819 __ ldr(arm::R0, arm::Address(arm::R0)); 820 } 821 822 std::string expected = 823 "1:\n" 824 "ldrd r1, r3, [pc, #((2f - 1b - 2) & ~2)]\n" + 825 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 826 ".align 2, 0\n" 827 "2:\n" 828 ".word 0x87654321\n" 829 ".word 0x12345678\n"; 830 DriverStr(expected, "LoadLiteralWideMax1KiB"); 831 832 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u, 833 __ GetAdjustedPosition(label.Position())); 834} 835 836TEST_F(AssemblerThumb2Test, LoadLiteralWideBeyondMax1KiB) { 837 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321)); 838 __ LoadLiteral(arm::R1, arm::R3, literal); 839 Label label; 840 __ Bind(&label); 841 constexpr size_t kLdrR0R0Count = 511; 842 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 843 __ ldr(arm::R0, arm::Address(arm::R0)); 844 } 845 846 std::string expected = 847 "mov.w ip, #((2f - 1f - 4) & ~0x3ff)\n" 848 "1:\n" 849 "add ip, pc\n" 850 "ldrd r1, r3, [ip, #((2f - 1b - 4) & 0x3ff)]\n" + 851 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 852 ".align 2, 0\n" 853 "2:\n" 854 ".word 0x87654321\n" 855 ".word 0x12345678\n"; 856 DriverStr(expected, "LoadLiteralWideBeyondMax1KiB"); 857 858 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u, 859 __ GetAdjustedPosition(label.Position())); 860} 861 862TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax256KiB) { 863 // The literal size must match but the type doesn't, so use an int32_t rather than float. 864 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 865 __ LoadLiteral(arm::S3, literal); 866 Label label; 867 __ Bind(&label); 868 constexpr size_t kLdrR0R0Count = (1 << 17) - 3u; 869 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 870 __ ldr(arm::R0, arm::Address(arm::R0)); 871 } 872 873 std::string expected = 874 "mov.w ip, #((2f - 1f - 4) & ~0x3ff)\n" 875 "1:\n" 876 "add ip, pc\n" 877 "vldr s3, [ip, #((2f - 1b - 4) & 0x3ff)]\n" + 878 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 879 ".align 2, 0\n" 880 "2:\n" 881 ".word 0x12345678\n"; 882 DriverStr(expected, "LoadLiteralSingleMax256KiB"); 883 884 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u, 885 __ GetAdjustedPosition(label.Position())); 886} 887 888TEST_F(AssemblerThumb2Test, LoadLiteralDoubleBeyondMax256KiB) { 889 // The literal size must match but the type doesn't, so use an int64_t rather than double. 890 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321)); 891 __ LoadLiteral(arm::D3, literal); 892 Label label; 893 __ Bind(&label); 894 constexpr size_t kLdrR0R0Count = (1 << 17) - 2u; 895 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 896 __ ldr(arm::R0, arm::Address(arm::R0)); 897 } 898 899 std::string expected = 900 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw. 901 "movw ip, #(0x40000 & 0xffff)\n" 902 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt. 903 "movt ip, #(0x40000 >> 16)\n" 904 "1:\n" 905 "add ip, pc\n" 906 "vldr d3, [ip, #0]\n" + 907 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 908 ".align 2, 0\n" 909 "2:\n" 910 ".word 0x87654321\n" 911 ".word 0x12345678\n"; 912 DriverStr(expected, "LoadLiteralDoubleBeyondMax256KiB"); 913 914 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u, 915 __ GetAdjustedPosition(label.Position())); 916} 917 918TEST_F(AssemblerThumb2Test, LoadLiteralDoubleFar) { 919 // The literal size must match but the type doesn't, so use an int64_t rather than double. 920 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321)); 921 __ LoadLiteral(arm::D3, literal); 922 Label label; 923 __ Bind(&label); 924 constexpr size_t kLdrR0R0Count = (1 << 17) - 2u + 0x1234; 925 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 926 __ ldr(arm::R0, arm::Address(arm::R0)); 927 } 928 929 std::string expected = 930 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw. 931 "movw ip, #((0x40000 + 2 * 0x1234) & 0xffff)\n" 932 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt. 933 "movt ip, #((0x40000 + 2 * 0x1234) >> 16)\n" 934 "1:\n" 935 "add ip, pc\n" 936 "vldr d3, [ip, #0]\n" + 937 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 938 ".align 2, 0\n" 939 "2:\n" 940 ".word 0x87654321\n" 941 ".word 0x12345678\n"; 942 DriverStr(expected, "LoadLiteralDoubleFar"); 943 944 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u, 945 __ GetAdjustedPosition(label.Position())); 946} 947 948TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass) { 949 // First part: as TwoCbzBeyondMaxOffset but add one 16-bit instruction to the end, 950 // so that the size is not Aligned<4>(.). On the first pass, the assembler resizes 951 // the second CBZ because it's out of range, then it will resize the first CBZ 952 // which has been pushed out of range. Thus, after the first pass, the code size 953 // will appear Aligned<4>(.) but the final size will not be. 954 Label label0, label1, label2; 955 __ cbz(arm::R0, &label1); 956 constexpr size_t kLdrR0R0Count1 = 63; 957 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 958 __ ldr(arm::R0, arm::Address(arm::R0)); 959 } 960 __ Bind(&label0); 961 __ cbz(arm::R0, &label2); 962 __ Bind(&label1); 963 constexpr size_t kLdrR0R0Count2 = 65; 964 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 965 __ ldr(arm::R0, arm::Address(arm::R0)); 966 } 967 __ Bind(&label2); 968 __ ldr(arm::R0, arm::Address(arm::R0)); 969 970 std::string expected_part1 = 971 "cmp r0, #0\n" // cbz r0, label1 972 "beq.n 1f\n" + 973 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 974 "0:\n" 975 "cmp r0, #0\n" // cbz r0, label2 976 "beq.n 2f\n" 977 "1:\n" + 978 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 979 "2:\n" // Here the offset is Aligned<4>(.). 980 "ldr r0, [r0]\n"; // Make the first part 981 982 // Second part: as LoadLiteralMax1KiB with the caveat that the offset of the load 983 // literal will not be Aligned<4>(.) but it will appear to be when we process the 984 // instruction during the first pass, so the literal will need a padding and it 985 // will push the literal out of range, so we shall end up with "ldr.w". 986 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 987 __ LoadLiteral(arm::R0, literal); 988 Label label; 989 __ Bind(&label); 990 constexpr size_t kLdrR0R0Count = 511; 991 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 992 __ ldr(arm::R0, arm::Address(arm::R0)); 993 } 994 995 std::string expected = 996 expected_part1 + 997 "1:\n" 998 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" + 999 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1000 ".align 2, 0\n" 1001 "2:\n" 1002 ".word 0x12345678\n"; 1003 DriverStr(expected, "LoadLiteralMax1KiB"); 1004 1005 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u, 1006 __ GetAdjustedPosition(label.Position())); 1007} 1008 1009TEST_F(AssemblerThumb2Test, BindTrackedLabel) { 1010 Label non_tracked, tracked, branch_target; 1011 1012 // A few dummy loads on entry. 1013 constexpr size_t kLdrR0R0Count = 5; 1014 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1015 __ ldr(arm::R0, arm::Address(arm::R0)); 1016 } 1017 1018 // A branch that will need to be fixed up. 1019 __ cbz(arm::R0, &branch_target); 1020 1021 // Some more dummy loads. 1022 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1023 __ ldr(arm::R0, arm::Address(arm::R0)); 1024 } 1025 1026 // Now insert tracked and untracked label. 1027 __ Bind(&non_tracked); 1028 __ BindTrackedLabel(&tracked); 1029 1030 // A lot of dummy loads, to ensure the branch needs resizing. 1031 constexpr size_t kLdrR0R0CountLong = 60; 1032 for (size_t i = 0; i != kLdrR0R0CountLong; ++i) { 1033 __ ldr(arm::R0, arm::Address(arm::R0)); 1034 } 1035 1036 // Bind the branch target. 1037 __ Bind(&branch_target); 1038 1039 // One more load. 1040 __ ldr(arm::R0, arm::Address(arm::R0)); 1041 1042 std::string expected = 1043 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1044 "cmp r0, #0\n" // cbz r0, 1f 1045 "beq.n 1f\n" + 1046 RepeatInsn(kLdrR0R0Count + kLdrR0R0CountLong, "ldr r0, [r0]\n") + 1047 "1:\n" 1048 "ldr r0, [r0]\n"; 1049 DriverStr(expected, "BindTrackedLabel"); 1050 1051 // Expectation is that the tracked label should have moved. 1052 EXPECT_LT(non_tracked.Position(), tracked.Position()); 1053} 1054 1055TEST_F(AssemblerThumb2Test, JumpTable) { 1056 // The jump table. Use three labels. 1057 Label label1, label2, label3; 1058 std::vector<Label*> labels({ &label1, &label2, &label3 }); 1059 1060 // A few dummy loads on entry, interspersed with 2 labels. 1061 constexpr size_t kLdrR0R0Count = 5; 1062 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1063 __ ldr(arm::R0, arm::Address(arm::R0)); 1064 } 1065 __ BindTrackedLabel(&label1); 1066 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1067 __ ldr(arm::R0, arm::Address(arm::R0)); 1068 } 1069 __ BindTrackedLabel(&label2); 1070 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1071 __ ldr(arm::R0, arm::Address(arm::R0)); 1072 } 1073 1074 // Create the jump table, emit the base load. 1075 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1); 1076 1077 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how 1078 // it's being used. 1079 __ ldr(arm::R0, arm::Address(arm::R0)); 1080 1081 // Emit the jump 1082 __ EmitJumpTableDispatch(jump_table, arm::R1); 1083 1084 // Some more dummy instructions. 1085 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1086 __ ldr(arm::R0, arm::Address(arm::R0)); 1087 } 1088 __ BindTrackedLabel(&label3); 1089 for (size_t i = 0; i != kLdrR0R0Count; ++i) { // Note: odd so there's no alignment 1090 __ ldr(arm::R0, arm::Address(arm::R0)); // necessary, as gcc as emits nops, 1091 } // whereas we emit 0 != nop. 1092 1093 static_assert((kLdrR0R0Count + 3) * 2 < 1 * KB, "Too much offset"); 1094 1095 std::string expected = 1096 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1097 ".L1:\n" + 1098 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1099 ".L2:\n" + 1100 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1101 "adr r1, .Ljump_table\n" 1102 "ldr r0, [r0]\n" 1103 ".Lbase:\n" 1104 "add pc, r1\n" + 1105 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1106 ".L3:\n" + 1107 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1108 ".align 2\n" 1109 ".Ljump_table:\n" 1110 ".4byte (.L1 - .Lbase - 4)\n" 1111 ".4byte (.L2 - .Lbase - 4)\n" 1112 ".4byte (.L3 - .Lbase - 4)\n"; 1113 DriverStr(expected, "JumpTable"); 1114} 1115 1116// Test for >1K fixup. 1117TEST_F(AssemblerThumb2Test, JumpTable4K) { 1118 // The jump table. Use three labels. 1119 Label label1, label2, label3; 1120 std::vector<Label*> labels({ &label1, &label2, &label3 }); 1121 1122 // A few dummy loads on entry, interspersed with 2 labels. 1123 constexpr size_t kLdrR0R0Count = 5; 1124 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1125 __ ldr(arm::R0, arm::Address(arm::R0)); 1126 } 1127 __ BindTrackedLabel(&label1); 1128 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1129 __ ldr(arm::R0, arm::Address(arm::R0)); 1130 } 1131 __ BindTrackedLabel(&label2); 1132 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1133 __ ldr(arm::R0, arm::Address(arm::R0)); 1134 } 1135 1136 // Create the jump table, emit the base load. 1137 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1); 1138 1139 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how 1140 // it's being used. 1141 __ ldr(arm::R0, arm::Address(arm::R0)); 1142 1143 // Emit the jump 1144 __ EmitJumpTableDispatch(jump_table, arm::R1); 1145 1146 // Some more dummy instructions. 1147 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1148 __ ldr(arm::R0, arm::Address(arm::R0)); 1149 } 1150 __ BindTrackedLabel(&label3); 1151 constexpr size_t kLdrR0R0Count2 = 600; // Note: even so there's no alignment 1152 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops, 1153 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop. 1154 } 1155 1156 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 1 * KB, "Not enough offset"); 1157 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 4 * KB, "Too much offset"); 1158 1159 std::string expected = 1160 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1161 ".L1:\n" + 1162 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1163 ".L2:\n" + 1164 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1165 "adr r1, .Ljump_table\n" 1166 "ldr r0, [r0]\n" 1167 ".Lbase:\n" 1168 "add pc, r1\n" + 1169 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1170 ".L3:\n" + 1171 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 1172 ".align 2\n" 1173 ".Ljump_table:\n" 1174 ".4byte (.L1 - .Lbase - 4)\n" 1175 ".4byte (.L2 - .Lbase - 4)\n" 1176 ".4byte (.L3 - .Lbase - 4)\n"; 1177 DriverStr(expected, "JumpTable4K"); 1178} 1179 1180// Test for >4K fixup. 1181TEST_F(AssemblerThumb2Test, JumpTable64K) { 1182 // The jump table. Use three labels. 1183 Label label1, label2, label3; 1184 std::vector<Label*> labels({ &label1, &label2, &label3 }); 1185 1186 // A few dummy loads on entry, interspersed with 2 labels. 1187 constexpr size_t kLdrR0R0Count = 5; 1188 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1189 __ ldr(arm::R0, arm::Address(arm::R0)); 1190 } 1191 __ BindTrackedLabel(&label1); 1192 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1193 __ ldr(arm::R0, arm::Address(arm::R0)); 1194 } 1195 __ BindTrackedLabel(&label2); 1196 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1197 __ ldr(arm::R0, arm::Address(arm::R0)); 1198 } 1199 1200 // Create the jump table, emit the base load. 1201 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1); 1202 1203 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how 1204 // it's being used. 1205 __ ldr(arm::R0, arm::Address(arm::R0)); 1206 1207 // Emit the jump 1208 __ EmitJumpTableDispatch(jump_table, arm::R1); 1209 1210 // Some more dummy instructions. 1211 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1212 __ ldr(arm::R0, arm::Address(arm::R0)); 1213 } 1214 __ BindTrackedLabel(&label3); 1215 constexpr size_t kLdrR0R0Count2 = 2601; // Note: odd so there's no alignment 1216 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops, 1217 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop. 1218 } 1219 1220 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 4 * KB, "Not enough offset"); 1221 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 64 * KB, "Too much offset"); 1222 1223 std::string expected = 1224 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1225 ".L1:\n" + 1226 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1227 ".L2:\n" + 1228 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1229 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself. 1230 // (Note: have to use constants, as labels aren't accepted. 1231 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) + 1232 ") * 2 - 4) & 0xFFFF)\n" 1233 "add r1, pc\n" 1234 "ldr r0, [r0]\n" 1235 ".Lbase:\n" 1236 "add pc, r1\n" + 1237 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1238 ".L3:\n" + 1239 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 1240 ".align 2\n" 1241 ".Ljump_table:\n" 1242 ".4byte (.L1 - .Lbase - 4)\n" 1243 ".4byte (.L2 - .Lbase - 4)\n" 1244 ".4byte (.L3 - .Lbase - 4)\n"; 1245 DriverStr(expected, "JumpTable64K"); 1246} 1247 1248// Test for >64K fixup. 1249TEST_F(AssemblerThumb2Test, JumpTableFar) { 1250 // The jump table. Use three labels. 1251 Label label1, label2, label3; 1252 std::vector<Label*> labels({ &label1, &label2, &label3 }); 1253 1254 // A few dummy loads on entry, interspersed with 2 labels. 1255 constexpr size_t kLdrR0R0Count = 5; 1256 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1257 __ ldr(arm::R0, arm::Address(arm::R0)); 1258 } 1259 __ BindTrackedLabel(&label1); 1260 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1261 __ ldr(arm::R0, arm::Address(arm::R0)); 1262 } 1263 __ BindTrackedLabel(&label2); 1264 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1265 __ ldr(arm::R0, arm::Address(arm::R0)); 1266 } 1267 1268 // Create the jump table, emit the base load. 1269 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1); 1270 1271 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how 1272 // it's being used. 1273 __ ldr(arm::R0, arm::Address(arm::R0)); 1274 1275 // Emit the jump 1276 __ EmitJumpTableDispatch(jump_table, arm::R1); 1277 1278 // Some more dummy instructions. 1279 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1280 __ ldr(arm::R0, arm::Address(arm::R0)); 1281 } 1282 __ BindTrackedLabel(&label3); 1283 constexpr size_t kLdrR0R0Count2 = 70001; // Note: odd so there's no alignment 1284 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops, 1285 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop. 1286 } 1287 1288 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 64 * KB, "Not enough offset"); 1289 1290 std::string expected = 1291 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1292 ".L1:\n" + 1293 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1294 ".L2:\n" + 1295 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1296 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself. 1297 // (Note: have to use constants, as labels aren't accepted. 1298 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) + 1299 ") * 2 - 4) & 0xFFFF)\n" 1300 "movt r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) + 1301 ") * 2 - 4) >> 16)\n" 1302 ".Lhelp:" 1303 "add r1, pc\n" 1304 "ldr r0, [r0]\n" 1305 ".Lbase:\n" 1306 "add pc, r1\n" + 1307 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1308 ".L3:\n" + 1309 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 1310 ".align 2\n" 1311 ".Ljump_table:\n" 1312 ".4byte (.L1 - .Lbase - 4)\n" 1313 ".4byte (.L2 - .Lbase - 4)\n" 1314 ".4byte (.L3 - .Lbase - 4)\n"; 1315 DriverStr(expected, "JumpTableFar"); 1316} 1317 1318TEST_F(AssemblerThumb2Test, Clz) { 1319 __ clz(arm::R0, arm::R1); 1320 1321 const char* expected = "clz r0, r1\n"; 1322 1323 DriverStr(expected, "clz"); 1324} 1325 1326TEST_F(AssemblerThumb2Test, rbit) { 1327 __ rbit(arm::R1, arm::R0); 1328 1329 const char* expected = "rbit r1, r0\n"; 1330 1331 DriverStr(expected, "rbit"); 1332} 1333 1334TEST_F(AssemblerThumb2Test, rev) { 1335 __ rev(arm::R1, arm::R0); 1336 1337 const char* expected = "rev r1, r0\n"; 1338 1339 DriverStr(expected, "rev"); 1340} 1341 1342TEST_F(AssemblerThumb2Test, rev16) { 1343 __ rev16(arm::R1, arm::R0); 1344 1345 const char* expected = "rev16 r1, r0\n"; 1346 1347 DriverStr(expected, "rev16"); 1348} 1349 1350TEST_F(AssemblerThumb2Test, revsh) { 1351 __ revsh(arm::R1, arm::R0); 1352 1353 const char* expected = "revsh r1, r0\n"; 1354 1355 DriverStr(expected, "revsh"); 1356} 1357 1358} // namespace art 1359