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 23 namespace art { 24 25 class 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 96 TEST_F(AssemblerThumb2Test, Toolchain) { 97 EXPECT_TRUE(CheckTools()); 98 } 99 100 #define __ GetAssembler()-> 101 102 TEST_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 138 TEST_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 174 TEST_F(AssemblerThumb2Test, Vmstat) { 175 __ vmstat(); 176 177 const char* expected = "vmrs APSR_nzcv, FPSCR\n"; 178 179 DriverStr(expected, "vmrs"); 180 } 181 182 TEST_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 196 TEST_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 210 TEST_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 222 TEST_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 238 TEST_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 252 TEST_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 266 TEST_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 274 TEST_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 282 TEST_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 298 TEST_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 323 TEST_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 347 TEST_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 375 TEST_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 400 TEST_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 434 TEST_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 470 TEST_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 505 TEST_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 541 TEST_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 576 TEST_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 612 TEST_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 635 TEST_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 658 TEST_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 681 TEST_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 706 TEST_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 731 TEST_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 756 TEST_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 781 TEST_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 809 TEST_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 837 TEST_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 861 TEST_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 = 872 "mov.w ip, #((2f - 1f - 4) & ~0x3ff)\n" 873 "1:\n" 874 "add ip, pc\n" 875 "ldrd r1, r3, [ip, #((2f - 1b - 4) & 0x3ff)]\n" + 876 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 877 ".align 2, 0\n" 878 "2:\n" 879 ".word 0x87654321\n" 880 ".word 0x12345678\n"; 881 DriverStr(expected, "LoadLiteralWideBeyondMax1KiB"); 882 883 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u, 884 __ GetAdjustedPosition(label.Position())); 885 } 886 887 TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax256KiB) { 888 // The literal size must match but the type doesn't, so use an int32_t rather than float. 889 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 890 __ LoadLiteral(arm::S3, literal); 891 Label label; 892 __ Bind(&label); 893 constexpr size_t kLdrR0R0Count = (1 << 17) - 3u; 894 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 895 __ ldr(arm::R0, arm::Address(arm::R0)); 896 } 897 898 std::string expected = 899 "mov.w ip, #((2f - 1f - 4) & ~0x3ff)\n" 900 "1:\n" 901 "add ip, pc\n" 902 "vldr s3, [ip, #((2f - 1b - 4) & 0x3ff)]\n" + 903 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 904 ".align 2, 0\n" 905 "2:\n" 906 ".word 0x12345678\n"; 907 DriverStr(expected, "LoadLiteralSingleMax256KiB"); 908 909 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u, 910 __ GetAdjustedPosition(label.Position())); 911 } 912 913 TEST_F(AssemblerThumb2Test, LoadLiteralDoubleBeyondMax256KiB) { 914 // The literal size must match but the type doesn't, so use an int64_t rather than double. 915 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321)); 916 __ LoadLiteral(arm::D3, literal); 917 Label label; 918 __ Bind(&label); 919 constexpr size_t kLdrR0R0Count = (1 << 17) - 2u; 920 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 921 __ ldr(arm::R0, arm::Address(arm::R0)); 922 } 923 924 std::string expected = 925 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw. 926 "movw ip, #(0x40000 & 0xffff)\n" 927 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt. 928 "movt ip, #(0x40000 >> 16)\n" 929 "1:\n" 930 "add ip, pc\n" 931 "vldr d3, [ip, #0]\n" + 932 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 933 ".align 2, 0\n" 934 "2:\n" 935 ".word 0x87654321\n" 936 ".word 0x12345678\n"; 937 DriverStr(expected, "LoadLiteralDoubleBeyondMax256KiB"); 938 939 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u, 940 __ GetAdjustedPosition(label.Position())); 941 } 942 943 TEST_F(AssemblerThumb2Test, LoadLiteralDoubleFar) { 944 // The literal size must match but the type doesn't, so use an int64_t rather than double. 945 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321)); 946 __ LoadLiteral(arm::D3, literal); 947 Label label; 948 __ Bind(&label); 949 constexpr size_t kLdrR0R0Count = (1 << 17) - 2u + 0x1234; 950 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 951 __ ldr(arm::R0, arm::Address(arm::R0)); 952 } 953 954 std::string expected = 955 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw. 956 "movw ip, #((0x40000 + 2 * 0x1234) & 0xffff)\n" 957 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt. 958 "movt ip, #((0x40000 + 2 * 0x1234) >> 16)\n" 959 "1:\n" 960 "add ip, pc\n" 961 "vldr d3, [ip, #0]\n" + 962 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 963 ".align 2, 0\n" 964 "2:\n" 965 ".word 0x87654321\n" 966 ".word 0x12345678\n"; 967 DriverStr(expected, "LoadLiteralDoubleFar"); 968 969 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u, 970 __ GetAdjustedPosition(label.Position())); 971 } 972 973 TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass) { 974 // First part: as TwoCbzBeyondMaxOffset but add one 16-bit instruction to the end, 975 // so that the size is not Aligned<4>(.). On the first pass, the assembler resizes 976 // the second CBZ because it's out of range, then it will resize the first CBZ 977 // which has been pushed out of range. Thus, after the first pass, the code size 978 // will appear Aligned<4>(.) but the final size will not be. 979 Label label0, label1, label2; 980 __ cbz(arm::R0, &label1); 981 constexpr size_t kLdrR0R0Count1 = 63; 982 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 983 __ ldr(arm::R0, arm::Address(arm::R0)); 984 } 985 __ Bind(&label0); 986 __ cbz(arm::R0, &label2); 987 __ Bind(&label1); 988 constexpr size_t kLdrR0R0Count2 = 65; 989 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 990 __ ldr(arm::R0, arm::Address(arm::R0)); 991 } 992 __ Bind(&label2); 993 __ ldr(arm::R0, arm::Address(arm::R0)); 994 995 std::string expected_part1 = 996 "cmp r0, #0\n" // cbz r0, label1 997 "beq.n 1f\n" + 998 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 999 "0:\n" 1000 "cmp r0, #0\n" // cbz r0, label2 1001 "beq.n 2f\n" 1002 "1:\n" + 1003 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 1004 "2:\n" // Here the offset is Aligned<4>(.). 1005 "ldr r0, [r0]\n"; // Make the first part 1006 1007 // Second part: as LoadLiteralMax1KiB with the caveat that the offset of the load 1008 // literal will not be Aligned<4>(.) but it will appear to be when we process the 1009 // instruction during the first pass, so the literal will need a padding and it 1010 // will push the literal out of range, so we shall end up with "ldr.w". 1011 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 1012 __ LoadLiteral(arm::R0, literal); 1013 Label label; 1014 __ Bind(&label); 1015 constexpr size_t kLdrR0R0Count = 511; 1016 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1017 __ ldr(arm::R0, arm::Address(arm::R0)); 1018 } 1019 1020 std::string expected = 1021 expected_part1 + 1022 "1:\n" 1023 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" + 1024 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1025 ".align 2, 0\n" 1026 "2:\n" 1027 ".word 0x12345678\n"; 1028 DriverStr(expected, "LoadLiteralMax1KiB"); 1029 1030 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u, 1031 __ GetAdjustedPosition(label.Position())); 1032 } 1033 1034 TEST_F(AssemblerThumb2Test, BindTrackedLabel) { 1035 Label non_tracked, tracked, branch_target; 1036 1037 // A few dummy loads on entry. 1038 constexpr size_t kLdrR0R0Count = 5; 1039 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1040 __ ldr(arm::R0, arm::Address(arm::R0)); 1041 } 1042 1043 // A branch that will need to be fixed up. 1044 __ cbz(arm::R0, &branch_target); 1045 1046 // Some more dummy loads. 1047 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1048 __ ldr(arm::R0, arm::Address(arm::R0)); 1049 } 1050 1051 // Now insert tracked and untracked label. 1052 __ Bind(&non_tracked); 1053 __ BindTrackedLabel(&tracked); 1054 1055 // A lot of dummy loads, to ensure the branch needs resizing. 1056 constexpr size_t kLdrR0R0CountLong = 60; 1057 for (size_t i = 0; i != kLdrR0R0CountLong; ++i) { 1058 __ ldr(arm::R0, arm::Address(arm::R0)); 1059 } 1060 1061 // Bind the branch target. 1062 __ Bind(&branch_target); 1063 1064 // One more load. 1065 __ ldr(arm::R0, arm::Address(arm::R0)); 1066 1067 std::string expected = 1068 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1069 "cmp r0, #0\n" // cbz r0, 1f 1070 "beq.n 1f\n" + 1071 RepeatInsn(kLdrR0R0Count + kLdrR0R0CountLong, "ldr r0, [r0]\n") + 1072 "1:\n" 1073 "ldr r0, [r0]\n"; 1074 DriverStr(expected, "BindTrackedLabel"); 1075 1076 // Expectation is that the tracked label should have moved. 1077 EXPECT_LT(non_tracked.Position(), tracked.Position()); 1078 } 1079 1080 TEST_F(AssemblerThumb2Test, JumpTable) { 1081 // The jump table. Use three labels. 1082 Label label1, label2, label3; 1083 std::vector<Label*> labels({ &label1, &label2, &label3 }); 1084 1085 // A few dummy loads on entry, interspersed with 2 labels. 1086 constexpr size_t kLdrR0R0Count = 5; 1087 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1088 __ ldr(arm::R0, arm::Address(arm::R0)); 1089 } 1090 __ BindTrackedLabel(&label1); 1091 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1092 __ ldr(arm::R0, arm::Address(arm::R0)); 1093 } 1094 __ BindTrackedLabel(&label2); 1095 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1096 __ ldr(arm::R0, arm::Address(arm::R0)); 1097 } 1098 1099 // Create the jump table, emit the base load. 1100 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1); 1101 1102 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how 1103 // it's being used. 1104 __ ldr(arm::R0, arm::Address(arm::R0)); 1105 1106 // Emit the jump 1107 __ EmitJumpTableDispatch(jump_table, arm::R1); 1108 1109 // Some more dummy instructions. 1110 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1111 __ ldr(arm::R0, arm::Address(arm::R0)); 1112 } 1113 __ BindTrackedLabel(&label3); 1114 for (size_t i = 0; i != kLdrR0R0Count; ++i) { // Note: odd so there's no alignment 1115 __ ldr(arm::R0, arm::Address(arm::R0)); // necessary, as gcc as emits nops, 1116 } // whereas we emit 0 != nop. 1117 1118 static_assert((kLdrR0R0Count + 3) * 2 < 1 * KB, "Too much offset"); 1119 1120 std::string expected = 1121 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1122 ".L1:\n" + 1123 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1124 ".L2:\n" + 1125 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1126 "adr r1, .Ljump_table\n" 1127 "ldr r0, [r0]\n" 1128 ".Lbase:\n" 1129 "add pc, r1\n" + 1130 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1131 ".L3:\n" + 1132 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1133 ".align 2\n" 1134 ".Ljump_table:\n" 1135 ".4byte (.L1 - .Lbase - 4)\n" 1136 ".4byte (.L2 - .Lbase - 4)\n" 1137 ".4byte (.L3 - .Lbase - 4)\n"; 1138 DriverStr(expected, "JumpTable"); 1139 } 1140 1141 // Test for >1K fixup. 1142 TEST_F(AssemblerThumb2Test, JumpTable4K) { 1143 // The jump table. Use three labels. 1144 Label label1, label2, label3; 1145 std::vector<Label*> labels({ &label1, &label2, &label3 }); 1146 1147 // A few dummy loads on entry, interspersed with 2 labels. 1148 constexpr size_t kLdrR0R0Count = 5; 1149 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1150 __ ldr(arm::R0, arm::Address(arm::R0)); 1151 } 1152 __ BindTrackedLabel(&label1); 1153 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1154 __ ldr(arm::R0, arm::Address(arm::R0)); 1155 } 1156 __ BindTrackedLabel(&label2); 1157 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1158 __ ldr(arm::R0, arm::Address(arm::R0)); 1159 } 1160 1161 // Create the jump table, emit the base load. 1162 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1); 1163 1164 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how 1165 // it's being used. 1166 __ ldr(arm::R0, arm::Address(arm::R0)); 1167 1168 // Emit the jump 1169 __ EmitJumpTableDispatch(jump_table, arm::R1); 1170 1171 // Some more dummy instructions. 1172 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1173 __ ldr(arm::R0, arm::Address(arm::R0)); 1174 } 1175 __ BindTrackedLabel(&label3); 1176 constexpr size_t kLdrR0R0Count2 = 600; // Note: even so there's no alignment 1177 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops, 1178 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop. 1179 } 1180 1181 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 1 * KB, "Not enough offset"); 1182 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 4 * KB, "Too much offset"); 1183 1184 std::string expected = 1185 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1186 ".L1:\n" + 1187 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1188 ".L2:\n" + 1189 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1190 "adr r1, .Ljump_table\n" 1191 "ldr r0, [r0]\n" 1192 ".Lbase:\n" 1193 "add pc, r1\n" + 1194 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1195 ".L3:\n" + 1196 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 1197 ".align 2\n" 1198 ".Ljump_table:\n" 1199 ".4byte (.L1 - .Lbase - 4)\n" 1200 ".4byte (.L2 - .Lbase - 4)\n" 1201 ".4byte (.L3 - .Lbase - 4)\n"; 1202 DriverStr(expected, "JumpTable4K"); 1203 } 1204 1205 // Test for >4K fixup. 1206 TEST_F(AssemblerThumb2Test, JumpTable64K) { 1207 // The jump table. Use three labels. 1208 Label label1, label2, label3; 1209 std::vector<Label*> labels({ &label1, &label2, &label3 }); 1210 1211 // A few dummy loads on entry, interspersed with 2 labels. 1212 constexpr size_t kLdrR0R0Count = 5; 1213 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1214 __ ldr(arm::R0, arm::Address(arm::R0)); 1215 } 1216 __ BindTrackedLabel(&label1); 1217 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1218 __ ldr(arm::R0, arm::Address(arm::R0)); 1219 } 1220 __ BindTrackedLabel(&label2); 1221 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1222 __ ldr(arm::R0, arm::Address(arm::R0)); 1223 } 1224 1225 // Create the jump table, emit the base load. 1226 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1); 1227 1228 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how 1229 // it's being used. 1230 __ ldr(arm::R0, arm::Address(arm::R0)); 1231 1232 // Emit the jump 1233 __ EmitJumpTableDispatch(jump_table, arm::R1); 1234 1235 // Some more dummy instructions. 1236 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1237 __ ldr(arm::R0, arm::Address(arm::R0)); 1238 } 1239 __ BindTrackedLabel(&label3); 1240 constexpr size_t kLdrR0R0Count2 = 2601; // Note: odd so there's no alignment 1241 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops, 1242 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop. 1243 } 1244 1245 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 4 * KB, "Not enough offset"); 1246 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 64 * KB, "Too much offset"); 1247 1248 std::string expected = 1249 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1250 ".L1:\n" + 1251 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1252 ".L2:\n" + 1253 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1254 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself. 1255 // (Note: have to use constants, as labels aren't accepted. 1256 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) + 1257 ") * 2 - 4) & 0xFFFF)\n" 1258 "add r1, pc\n" 1259 "ldr r0, [r0]\n" 1260 ".Lbase:\n" 1261 "add pc, r1\n" + 1262 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1263 ".L3:\n" + 1264 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 1265 ".align 2\n" 1266 ".Ljump_table:\n" 1267 ".4byte (.L1 - .Lbase - 4)\n" 1268 ".4byte (.L2 - .Lbase - 4)\n" 1269 ".4byte (.L3 - .Lbase - 4)\n"; 1270 DriverStr(expected, "JumpTable64K"); 1271 } 1272 1273 // Test for >64K fixup. 1274 TEST_F(AssemblerThumb2Test, JumpTableFar) { 1275 // The jump table. Use three labels. 1276 Label label1, label2, label3; 1277 std::vector<Label*> labels({ &label1, &label2, &label3 }); 1278 1279 // A few dummy loads on entry, interspersed with 2 labels. 1280 constexpr size_t kLdrR0R0Count = 5; 1281 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1282 __ ldr(arm::R0, arm::Address(arm::R0)); 1283 } 1284 __ BindTrackedLabel(&label1); 1285 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1286 __ ldr(arm::R0, arm::Address(arm::R0)); 1287 } 1288 __ BindTrackedLabel(&label2); 1289 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1290 __ ldr(arm::R0, arm::Address(arm::R0)); 1291 } 1292 1293 // Create the jump table, emit the base load. 1294 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1); 1295 1296 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how 1297 // it's being used. 1298 __ ldr(arm::R0, arm::Address(arm::R0)); 1299 1300 // Emit the jump 1301 __ EmitJumpTableDispatch(jump_table, arm::R1); 1302 1303 // Some more dummy instructions. 1304 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1305 __ ldr(arm::R0, arm::Address(arm::R0)); 1306 } 1307 __ BindTrackedLabel(&label3); 1308 constexpr size_t kLdrR0R0Count2 = 70001; // Note: odd so there's no alignment 1309 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops, 1310 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop. 1311 } 1312 1313 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 64 * KB, "Not enough offset"); 1314 1315 std::string expected = 1316 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1317 ".L1:\n" + 1318 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1319 ".L2:\n" + 1320 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1321 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself. 1322 // (Note: have to use constants, as labels aren't accepted. 1323 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) + 1324 ") * 2 - 4) & 0xFFFF)\n" 1325 "movt r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) + 1326 ") * 2 - 4) >> 16)\n" 1327 ".Lhelp:" 1328 "add r1, pc\n" 1329 "ldr r0, [r0]\n" 1330 ".Lbase:\n" 1331 "add pc, r1\n" + 1332 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1333 ".L3:\n" + 1334 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 1335 ".align 2\n" 1336 ".Ljump_table:\n" 1337 ".4byte (.L1 - .Lbase - 4)\n" 1338 ".4byte (.L2 - .Lbase - 4)\n" 1339 ".4byte (.L3 - .Lbase - 4)\n"; 1340 DriverStr(expected, "JumpTableFar"); 1341 } 1342 1343 TEST_F(AssemblerThumb2Test, Clz) { 1344 __ clz(arm::R0, arm::R1); 1345 1346 const char* expected = "clz r0, r1\n"; 1347 1348 DriverStr(expected, "clz"); 1349 } 1350 1351 TEST_F(AssemblerThumb2Test, rbit) { 1352 __ rbit(arm::R1, arm::R0); 1353 1354 const char* expected = "rbit r1, r0\n"; 1355 1356 DriverStr(expected, "rbit"); 1357 } 1358 1359 TEST_F(AssemblerThumb2Test, rev) { 1360 __ rev(arm::R1, arm::R0); 1361 1362 const char* expected = "rev r1, r0\n"; 1363 1364 DriverStr(expected, "rev"); 1365 } 1366 1367 TEST_F(AssemblerThumb2Test, rev16) { 1368 __ rev16(arm::R1, arm::R0); 1369 1370 const char* expected = "rev16 r1, r0\n"; 1371 1372 DriverStr(expected, "rev16"); 1373 } 1374 1375 TEST_F(AssemblerThumb2Test, revsh) { 1376 __ revsh(arm::R1, arm::R0); 1377 1378 const char* expected = "revsh r1, r0\n"; 1379 1380 DriverStr(expected, "revsh"); 1381 } 1382 1383 } // namespace art 1384