assembler_thumb2_test.cc revision 7cffc3b0004d32faffc552c0a59286f369b21504
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 "subw 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 "addw 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 "mov ip, #4096\n" // LoadImmediate(ip, 4096) 309 "add ip, ip, sp\n" 310 "str r0, [ip, #0]\n" 311 312 "str r5, [sp, #-4]!\n" // Push(r5) 313 "movw r5, #4100\n" // LoadImmediate(r5, 4096 + kRegisterSize) 314 "add r5, r5, sp\n" 315 "str ip, [r5, #0]\n" 316 "ldr r5, [sp], #4\n" // Pop(r5) 317 318 "str r6, [sp, #-4]!\n" // Push(r6) 319 "mov r6, #4096\n" // LoadImmediate(r6, 4096) 320 "add r6, r6, r5\n" 321 "str ip, [r6, #0]\n" 322 "ldr r6, [sp], #4\n"; // Pop(r6) 323 DriverStr(expected, "StoreWordToNonThumbOffset"); 324} 325 326TEST_F(AssemblerThumb2Test, StoreWordPairToThumbOffset) { 327 arm::StoreOperandType type = arm::kStoreWordPair; 328 int32_t offset = 1020; 329 ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset)); 330 331 __ StoreToOffset(type, arm::R0, arm::SP, offset); 332 // We cannot use IP (i.e. R12) as first source register, as it would 333 // force us to use SP (i.e. R13) as second source register, which 334 // would have an "unpredictable" effect according to the ARMv7 335 // specification (the T1 encoding describes the result as 336 // UNPREDICTABLE when of the source registers is R13). 337 // 338 // So we use (R11, IP) (e.g. (R11, R12)) as source registers in the 339 // following instructions. 340 __ StoreToOffset(type, arm::R11, arm::SP, offset); 341 __ StoreToOffset(type, arm::R11, arm::R5, offset); 342 343 const char* expected = 344 "strd r0, r1, [sp, #1020]\n" 345 "strd r11, ip, [sp, #1020]\n" 346 "strd r11, ip, [r5, #1020]\n"; 347 DriverStr(expected, "StoreWordPairToThumbOffset"); 348} 349 350TEST_F(AssemblerThumb2Test, StoreWordPairToNonThumbOffset) { 351 arm::StoreOperandType type = arm::kStoreWordPair; 352 int32_t offset = 1024; 353 ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset)); 354 355 __ StoreToOffset(type, arm::R0, arm::SP, offset); 356 // Same comment as in AssemblerThumb2Test.StoreWordPairToThumbOffset 357 // regarding the use of (R11, IP) (e.g. (R11, R12)) as source 358 // registers in the following instructions. 359 __ StoreToOffset(type, arm::R11, arm::SP, offset); 360 __ StoreToOffset(type, arm::R11, arm::R5, offset); 361 362 const char* expected = 363 "mov ip, #1024\n" // LoadImmediate(ip, 1024) 364 "add ip, ip, sp\n" 365 "strd r0, r1, [ip, #0]\n" 366 367 "str r5, [sp, #-4]!\n" // Push(r5) 368 "movw r5, #1028\n" // LoadImmediate(r5, 1024 + kRegisterSize) 369 "add r5, r5, sp\n" 370 "strd r11, ip, [r5, #0]\n" 371 "ldr r5, [sp], #4\n" // Pop(r5) 372 373 "str r6, [sp, #-4]!\n" // Push(r6) 374 "mov r6, #1024\n" // LoadImmediate(r6, 1024) 375 "add r6, r6, r5\n" 376 "strd r11, ip, [r6, #0]\n" 377 "ldr r6, [sp], #4\n"; // Pop(r6) 378 DriverStr(expected, "StoreWordPairToNonThumbOffset"); 379} 380 381TEST_F(AssemblerThumb2Test, TwoCbzMaxOffset) { 382 Label label0, label1, label2; 383 __ cbz(arm::R0, &label1); 384 constexpr size_t kLdrR0R0Count1 = 63; 385 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 386 __ ldr(arm::R0, arm::Address(arm::R0)); 387 } 388 __ Bind(&label0); 389 __ cbz(arm::R0, &label2); 390 __ Bind(&label1); 391 constexpr size_t kLdrR0R0Count2 = 64; 392 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 393 __ ldr(arm::R0, arm::Address(arm::R0)); 394 } 395 __ Bind(&label2); 396 397 std::string expected = 398 "cbz r0, 1f\n" + // cbz r0, label1 399 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 400 "0:\n" 401 "cbz r0, 2f\n" // cbz r0, label2 402 "1:\n" + 403 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 404 "2:\n"; 405 DriverStr(expected, "TwoCbzMaxOffset"); 406 407 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u, 408 __ GetAdjustedPosition(label0.Position())); 409 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 0u, 410 __ GetAdjustedPosition(label1.Position())); 411 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 0u, 412 __ GetAdjustedPosition(label2.Position())); 413} 414 415TEST_F(AssemblerThumb2Test, TwoCbzBeyondMaxOffset) { 416 Label label0, label1, label2; 417 __ cbz(arm::R0, &label1); 418 constexpr size_t kLdrR0R0Count1 = 63; 419 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 420 __ ldr(arm::R0, arm::Address(arm::R0)); 421 } 422 __ Bind(&label0); 423 __ cbz(arm::R0, &label2); 424 __ Bind(&label1); 425 constexpr size_t kLdrR0R0Count2 = 65; 426 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 427 __ ldr(arm::R0, arm::Address(arm::R0)); 428 } 429 __ Bind(&label2); 430 431 std::string expected = 432 "cmp r0, #0\n" // cbz r0, label1 433 "beq.n 1f\n" + 434 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 435 "0:\n" 436 "cmp r0, #0\n" // cbz r0, label2 437 "beq.n 2f\n" 438 "1:\n" + 439 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 440 "2:\n"; 441 DriverStr(expected, "TwoCbzBeyondMaxOffset"); 442 443 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u, 444 __ GetAdjustedPosition(label0.Position())); 445 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 4u, 446 __ GetAdjustedPosition(label1.Position())); 447 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 4u, 448 __ GetAdjustedPosition(label2.Position())); 449} 450 451TEST_F(AssemblerThumb2Test, TwoCbzSecondAtMaxB16Offset) { 452 Label label0, label1, label2; 453 __ cbz(arm::R0, &label1); 454 constexpr size_t kLdrR0R0Count1 = 62; 455 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 456 __ ldr(arm::R0, arm::Address(arm::R0)); 457 } 458 __ Bind(&label0); 459 __ cbz(arm::R0, &label2); 460 __ Bind(&label1); 461 constexpr size_t kLdrR0R0Count2 = 128; 462 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 463 __ ldr(arm::R0, arm::Address(arm::R0)); 464 } 465 __ Bind(&label2); 466 467 std::string expected = 468 "cbz r0, 1f\n" + // cbz r0, label1 469 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 470 "0:\n" 471 "cmp r0, #0\n" // cbz r0, label2 472 "beq.n 2f\n" 473 "1:\n" + 474 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 475 "2:\n"; 476 DriverStr(expected, "TwoCbzSecondAtMaxB16Offset"); 477 478 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 0u, 479 __ GetAdjustedPosition(label0.Position())); 480 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u, 481 __ GetAdjustedPosition(label1.Position())); 482 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u, 483 __ GetAdjustedPosition(label2.Position())); 484} 485 486TEST_F(AssemblerThumb2Test, TwoCbzSecondBeyondMaxB16Offset) { 487 Label label0, label1, label2; 488 __ cbz(arm::R0, &label1); 489 constexpr size_t kLdrR0R0Count1 = 62; 490 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 491 __ ldr(arm::R0, arm::Address(arm::R0)); 492 } 493 __ Bind(&label0); 494 __ cbz(arm::R0, &label2); 495 __ Bind(&label1); 496 constexpr size_t kLdrR0R0Count2 = 129; 497 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 498 __ ldr(arm::R0, arm::Address(arm::R0)); 499 } 500 __ Bind(&label2); 501 502 std::string expected = 503 "cmp r0, #0\n" // cbz r0, label1 504 "beq.n 1f\n" + 505 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 506 "0:\n" 507 "cmp r0, #0\n" // cbz r0, label2 508 "beq.w 2f\n" 509 "1:\n" + 510 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 511 "2:\n"; 512 DriverStr(expected, "TwoCbzSecondBeyondMaxB16Offset"); 513 514 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u, 515 __ GetAdjustedPosition(label0.Position())); 516 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u, 517 __ GetAdjustedPosition(label1.Position())); 518 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u, 519 __ GetAdjustedPosition(label2.Position())); 520} 521 522TEST_F(AssemblerThumb2Test, TwoCbzFirstAtMaxB16Offset) { 523 Label label0, label1, label2; 524 __ cbz(arm::R0, &label1); 525 constexpr size_t kLdrR0R0Count1 = 127; 526 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 527 __ ldr(arm::R0, arm::Address(arm::R0)); 528 } 529 __ Bind(&label0); 530 __ cbz(arm::R0, &label2); 531 __ Bind(&label1); 532 constexpr size_t kLdrR0R0Count2 = 64; 533 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 534 __ ldr(arm::R0, arm::Address(arm::R0)); 535 } 536 __ Bind(&label2); 537 538 std::string expected = 539 "cmp r0, #0\n" // cbz r0, label1 540 "beq.n 1f\n" + 541 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 542 "0:\n" 543 "cbz r0, 2f\n" // cbz r0, label2 544 "1:\n" + 545 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 546 "2:\n"; 547 DriverStr(expected, "TwoCbzFirstAtMaxB16Offset"); 548 549 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 2u, 550 __ GetAdjustedPosition(label0.Position())); 551 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 2u, 552 __ GetAdjustedPosition(label1.Position())); 553 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 2u, 554 __ GetAdjustedPosition(label2.Position())); 555} 556 557TEST_F(AssemblerThumb2Test, TwoCbzFirstBeyondMaxB16Offset) { 558 Label label0, label1, label2; 559 __ cbz(arm::R0, &label1); 560 constexpr size_t kLdrR0R0Count1 = 127; 561 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 562 __ ldr(arm::R0, arm::Address(arm::R0)); 563 } 564 __ Bind(&label0); 565 __ cbz(arm::R0, &label2); 566 __ Bind(&label1); 567 constexpr size_t kLdrR0R0Count2 = 65; 568 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 569 __ ldr(arm::R0, arm::Address(arm::R0)); 570 } 571 __ Bind(&label2); 572 573 std::string expected = 574 "cmp r0, #0\n" // cbz r0, label1 575 "beq.w 1f\n" + 576 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 577 "0:\n" 578 "cmp r0, #0\n" // cbz r0, label2 579 "beq.n 2f\n" 580 "1:\n" + 581 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 582 "2:\n"; 583 DriverStr(expected, "TwoCbzFirstBeyondMaxB16Offset"); 584 585 EXPECT_EQ(static_cast<uint32_t>(label0.Position()) + 4u, 586 __ GetAdjustedPosition(label0.Position())); 587 EXPECT_EQ(static_cast<uint32_t>(label1.Position()) + 6u, 588 __ GetAdjustedPosition(label1.Position())); 589 EXPECT_EQ(static_cast<uint32_t>(label2.Position()) + 6u, 590 __ GetAdjustedPosition(label2.Position())); 591} 592 593TEST_F(AssemblerThumb2Test, LoadLiteralMax1KiB) { 594 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 595 __ LoadLiteral(arm::R0, literal); 596 Label label; 597 __ Bind(&label); 598 constexpr size_t kLdrR0R0Count = 511; 599 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 600 __ ldr(arm::R0, arm::Address(arm::R0)); 601 } 602 603 std::string expected = 604 "1:\n" 605 "ldr.n r0, [pc, #((2f - 1b - 2) & ~2)]\n" + 606 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 607 ".align 2, 0\n" 608 "2:\n" 609 ".word 0x12345678\n"; 610 DriverStr(expected, "LoadLiteralMax1KiB"); 611 612 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u, 613 __ GetAdjustedPosition(label.Position())); 614} 615 616TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiB) { 617 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 618 __ LoadLiteral(arm::R0, literal); 619 Label label; 620 __ Bind(&label); 621 constexpr size_t kLdrR0R0Count = 512; 622 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 623 __ ldr(arm::R0, arm::Address(arm::R0)); 624 } 625 626 std::string expected = 627 "1:\n" 628 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" + 629 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 630 ".align 2, 0\n" 631 "2:\n" 632 ".word 0x12345678\n"; 633 DriverStr(expected, "LoadLiteralBeyondMax1KiB"); 634 635 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u, 636 __ GetAdjustedPosition(label.Position())); 637} 638 639TEST_F(AssemblerThumb2Test, LoadLiteralMax4KiB) { 640 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 641 __ LoadLiteral(arm::R1, literal); 642 Label label; 643 __ Bind(&label); 644 constexpr size_t kLdrR0R0Count = 2046; 645 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 646 __ ldr(arm::R0, arm::Address(arm::R0)); 647 } 648 649 std::string expected = 650 "1:\n" 651 "ldr.w r1, [pc, #((2f - 1b - 2) & ~2)]\n" + 652 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 653 ".align 2, 0\n" 654 "2:\n" 655 ".word 0x12345678\n"; 656 DriverStr(expected, "LoadLiteralMax4KiB"); 657 658 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 2u, 659 __ GetAdjustedPosition(label.Position())); 660} 661 662TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax4KiB) { 663 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 664 __ LoadLiteral(arm::R1, literal); 665 Label label; 666 __ Bind(&label); 667 constexpr size_t kLdrR0R0Count = 2047; 668 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 669 __ ldr(arm::R0, arm::Address(arm::R0)); 670 } 671 672 std::string expected = 673 "movw r1, #4096\n" // "as" does not consider (2f - 1f - 4) a constant expression for movw. 674 "1:\n" 675 "add r1, pc\n" 676 "ldr r1, [r1, #0]\n" + 677 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 678 ".align 2, 0\n" 679 "2:\n" 680 ".word 0x12345678\n"; 681 DriverStr(expected, "LoadLiteralBeyondMax4KiB"); 682 683 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u, 684 __ GetAdjustedPosition(label.Position())); 685} 686 687TEST_F(AssemblerThumb2Test, LoadLiteralMax64KiB) { 688 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 689 __ LoadLiteral(arm::R1, literal); 690 Label label; 691 __ Bind(&label); 692 constexpr size_t kLdrR0R0Count = (1u << 15) - 2u; 693 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 694 __ ldr(arm::R0, arm::Address(arm::R0)); 695 } 696 697 std::string expected = 698 "movw r1, #0xfffc\n" // "as" does not consider (2f - 1f - 4) a constant expression for movw. 699 "1:\n" 700 "add r1, pc\n" 701 "ldr r1, [r1, #0]\n" + 702 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 703 ".align 2, 0\n" 704 "2:\n" 705 ".word 0x12345678\n"; 706 DriverStr(expected, "LoadLiteralMax64KiB"); 707 708 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u, 709 __ GetAdjustedPosition(label.Position())); 710} 711 712TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax64KiB) { 713 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 714 __ LoadLiteral(arm::R1, literal); 715 Label label; 716 __ Bind(&label); 717 constexpr size_t kLdrR0R0Count = (1u << 15) - 1u; 718 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 719 __ ldr(arm::R0, arm::Address(arm::R0)); 720 } 721 722 std::string expected = 723 "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n" 724 "1:\n" 725 "add r1, pc\n" 726 "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" + 727 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 728 ".align 2, 0\n" 729 "2:\n" 730 ".word 0x12345678\n"; 731 DriverStr(expected, "LoadLiteralBeyondMax64KiB"); 732 733 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u, 734 __ GetAdjustedPosition(label.Position())); 735} 736 737TEST_F(AssemblerThumb2Test, LoadLiteralMax1MiB) { 738 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 739 __ LoadLiteral(arm::R1, literal); 740 Label label; 741 __ Bind(&label); 742 constexpr size_t kLdrR0R0Count = (1u << 19) - 3u; 743 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 744 __ ldr(arm::R0, arm::Address(arm::R0)); 745 } 746 747 std::string expected = 748 "mov.w r1, #((2f - 1f - 4) & ~0xfff)\n" 749 "1:\n" 750 "add r1, pc\n" 751 "ldr r1, [r1, #((2f - 1b - 4) & 0xfff)]\n" + 752 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 753 ".align 2, 0\n" 754 "2:\n" 755 ".word 0x12345678\n"; 756 DriverStr(expected, "LoadLiteralMax1MiB"); 757 758 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 8u, 759 __ GetAdjustedPosition(label.Position())); 760} 761 762TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1MiB) { 763 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 764 __ LoadLiteral(arm::R1, literal); 765 Label label; 766 __ Bind(&label); 767 constexpr size_t kLdrR0R0Count = (1u << 19) - 2u; 768 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 769 __ ldr(arm::R0, arm::Address(arm::R0)); 770 } 771 772 std::string expected = 773 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw. 774 "movw r1, #(0x100000 & 0xffff)\n" 775 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt. 776 "movt r1, #(0x100000 >> 16)\n" 777 "1:\n" 778 "add r1, pc\n" 779 "ldr.w r1, [r1, #0]\n" + 780 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 781 ".align 2, 0\n" 782 "2:\n" 783 ".word 0x12345678\n"; 784 DriverStr(expected, "LoadLiteralBeyondMax1MiB"); 785 786 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u, 787 __ GetAdjustedPosition(label.Position())); 788} 789 790TEST_F(AssemblerThumb2Test, LoadLiteralFar) { 791 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 792 __ LoadLiteral(arm::R1, literal); 793 Label label; 794 __ Bind(&label); 795 constexpr size_t kLdrR0R0Count = (1u << 19) - 2u + 0x1234; 796 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 797 __ ldr(arm::R0, arm::Address(arm::R0)); 798 } 799 800 std::string expected = 801 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw. 802 "movw r1, #((0x100000 + 2 * 0x1234) & 0xffff)\n" 803 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt. 804 "movt r1, #((0x100000 + 2 * 0x1234) >> 16)\n" 805 "1:\n" 806 "add r1, pc\n" 807 "ldr.w r1, [r1, #0]\n" + 808 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 809 ".align 2, 0\n" 810 "2:\n" 811 ".word 0x12345678\n"; 812 DriverStr(expected, "LoadLiteralFar"); 813 814 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 12u, 815 __ GetAdjustedPosition(label.Position())); 816} 817 818TEST_F(AssemblerThumb2Test, LoadLiteralWideMax1KiB) { 819 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321)); 820 __ LoadLiteral(arm::R1, arm::R3, literal); 821 Label label; 822 __ Bind(&label); 823 constexpr size_t kLdrR0R0Count = 510; 824 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 825 __ ldr(arm::R0, arm::Address(arm::R0)); 826 } 827 828 std::string expected = 829 "1:\n" 830 "ldrd r1, r3, [pc, #((2f - 1b - 2) & ~2)]\n" + 831 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 832 ".align 2, 0\n" 833 "2:\n" 834 ".word 0x87654321\n" 835 ".word 0x12345678\n"; 836 DriverStr(expected, "LoadLiteralWideMax1KiB"); 837 838 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 0u, 839 __ GetAdjustedPosition(label.Position())); 840} 841 842TEST_F(AssemblerThumb2Test, LoadLiteralWideBeyondMax1KiB) { 843 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321)); 844 __ LoadLiteral(arm::R1, arm::R3, literal); 845 Label label; 846 __ Bind(&label); 847 constexpr size_t kLdrR0R0Count = 511; 848 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 849 __ ldr(arm::R0, arm::Address(arm::R0)); 850 } 851 852 std::string expected = 853 "mov.w ip, #((2f - 1f - 4) & ~0x3ff)\n" 854 "1:\n" 855 "add ip, pc\n" 856 "ldrd r1, r3, [ip, #((2f - 1b - 4) & 0x3ff)]\n" + 857 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 858 ".align 2, 0\n" 859 "2:\n" 860 ".word 0x87654321\n" 861 ".word 0x12345678\n"; 862 DriverStr(expected, "LoadLiteralWideBeyondMax1KiB"); 863 864 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u, 865 __ GetAdjustedPosition(label.Position())); 866} 867 868TEST_F(AssemblerThumb2Test, LoadLiteralSingleMax256KiB) { 869 // The literal size must match but the type doesn't, so use an int32_t rather than float. 870 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 871 __ LoadLiteral(arm::S3, literal); 872 Label label; 873 __ Bind(&label); 874 constexpr size_t kLdrR0R0Count = (1 << 17) - 3u; 875 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 876 __ ldr(arm::R0, arm::Address(arm::R0)); 877 } 878 879 std::string expected = 880 "mov.w ip, #((2f - 1f - 4) & ~0x3ff)\n" 881 "1:\n" 882 "add ip, pc\n" 883 "vldr s3, [ip, #((2f - 1b - 4) & 0x3ff)]\n" + 884 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 885 ".align 2, 0\n" 886 "2:\n" 887 ".word 0x12345678\n"; 888 DriverStr(expected, "LoadLiteralSingleMax256KiB"); 889 890 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u, 891 __ GetAdjustedPosition(label.Position())); 892} 893 894TEST_F(AssemblerThumb2Test, LoadLiteralDoubleBeyondMax256KiB) { 895 // The literal size must match but the type doesn't, so use an int64_t rather than double. 896 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321)); 897 __ LoadLiteral(arm::D3, literal); 898 Label label; 899 __ Bind(&label); 900 constexpr size_t kLdrR0R0Count = (1 << 17) - 2u; 901 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 902 __ ldr(arm::R0, arm::Address(arm::R0)); 903 } 904 905 std::string expected = 906 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw. 907 "movw ip, #(0x40000 & 0xffff)\n" 908 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt. 909 "movt ip, #(0x40000 >> 16)\n" 910 "1:\n" 911 "add ip, pc\n" 912 "vldr d3, [ip, #0]\n" + 913 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 914 ".align 2, 0\n" 915 "2:\n" 916 ".word 0x87654321\n" 917 ".word 0x12345678\n"; 918 DriverStr(expected, "LoadLiteralDoubleBeyondMax256KiB"); 919 920 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u, 921 __ GetAdjustedPosition(label.Position())); 922} 923 924TEST_F(AssemblerThumb2Test, LoadLiteralDoubleFar) { 925 // The literal size must match but the type doesn't, so use an int64_t rather than double. 926 arm::Literal* literal = __ NewLiteral<int64_t>(INT64_C(0x1234567887654321)); 927 __ LoadLiteral(arm::D3, literal); 928 Label label; 929 __ Bind(&label); 930 constexpr size_t kLdrR0R0Count = (1 << 17) - 2u + 0x1234; 931 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 932 __ ldr(arm::R0, arm::Address(arm::R0)); 933 } 934 935 std::string expected = 936 // "as" does not consider ((2f - 1f - 4) & 0xffff) a constant expression for movw. 937 "movw ip, #((0x40000 + 2 * 0x1234) & 0xffff)\n" 938 // "as" does not consider ((2f - 1f - 4) >> 16) a constant expression for movt. 939 "movt ip, #((0x40000 + 2 * 0x1234) >> 16)\n" 940 "1:\n" 941 "add ip, pc\n" 942 "vldr d3, [ip, #0]\n" + 943 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 944 ".align 2, 0\n" 945 "2:\n" 946 ".word 0x87654321\n" 947 ".word 0x12345678\n"; 948 DriverStr(expected, "LoadLiteralDoubleFar"); 949 950 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 10u, 951 __ GetAdjustedPosition(label.Position())); 952} 953 954TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass) { 955 // First part: as TwoCbzBeyondMaxOffset but add one 16-bit instruction to the end, 956 // so that the size is not Aligned<4>(.). On the first pass, the assembler resizes 957 // the second CBZ because it's out of range, then it will resize the first CBZ 958 // which has been pushed out of range. Thus, after the first pass, the code size 959 // will appear Aligned<4>(.) but the final size will not be. 960 Label label0, label1, label2; 961 __ cbz(arm::R0, &label1); 962 constexpr size_t kLdrR0R0Count1 = 63; 963 for (size_t i = 0; i != kLdrR0R0Count1; ++i) { 964 __ ldr(arm::R0, arm::Address(arm::R0)); 965 } 966 __ Bind(&label0); 967 __ cbz(arm::R0, &label2); 968 __ Bind(&label1); 969 constexpr size_t kLdrR0R0Count2 = 65; 970 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { 971 __ ldr(arm::R0, arm::Address(arm::R0)); 972 } 973 __ Bind(&label2); 974 __ ldr(arm::R0, arm::Address(arm::R0)); 975 976 std::string expected_part1 = 977 "cmp r0, #0\n" // cbz r0, label1 978 "beq.n 1f\n" + 979 RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") + 980 "0:\n" 981 "cmp r0, #0\n" // cbz r0, label2 982 "beq.n 2f\n" 983 "1:\n" + 984 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 985 "2:\n" // Here the offset is Aligned<4>(.). 986 "ldr r0, [r0]\n"; // Make the first part 987 988 // Second part: as LoadLiteralMax1KiB with the caveat that the offset of the load 989 // literal will not be Aligned<4>(.) but it will appear to be when we process the 990 // instruction during the first pass, so the literal will need a padding and it 991 // will push the literal out of range, so we shall end up with "ldr.w". 992 arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678); 993 __ LoadLiteral(arm::R0, literal); 994 Label label; 995 __ Bind(&label); 996 constexpr size_t kLdrR0R0Count = 511; 997 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 998 __ ldr(arm::R0, arm::Address(arm::R0)); 999 } 1000 1001 std::string expected = 1002 expected_part1 + 1003 "1:\n" 1004 "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" + 1005 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1006 ".align 2, 0\n" 1007 "2:\n" 1008 ".word 0x12345678\n"; 1009 DriverStr(expected, "LoadLiteralMax1KiB"); 1010 1011 EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u, 1012 __ GetAdjustedPosition(label.Position())); 1013} 1014 1015TEST_F(AssemblerThumb2Test, BindTrackedLabel) { 1016 Label non_tracked, tracked, branch_target; 1017 1018 // A few dummy loads on entry. 1019 constexpr size_t kLdrR0R0Count = 5; 1020 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1021 __ ldr(arm::R0, arm::Address(arm::R0)); 1022 } 1023 1024 // A branch that will need to be fixed up. 1025 __ cbz(arm::R0, &branch_target); 1026 1027 // Some more dummy loads. 1028 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1029 __ ldr(arm::R0, arm::Address(arm::R0)); 1030 } 1031 1032 // Now insert tracked and untracked label. 1033 __ Bind(&non_tracked); 1034 __ BindTrackedLabel(&tracked); 1035 1036 // A lot of dummy loads, to ensure the branch needs resizing. 1037 constexpr size_t kLdrR0R0CountLong = 60; 1038 for (size_t i = 0; i != kLdrR0R0CountLong; ++i) { 1039 __ ldr(arm::R0, arm::Address(arm::R0)); 1040 } 1041 1042 // Bind the branch target. 1043 __ Bind(&branch_target); 1044 1045 // One more load. 1046 __ ldr(arm::R0, arm::Address(arm::R0)); 1047 1048 std::string expected = 1049 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1050 "cmp r0, #0\n" // cbz r0, 1f 1051 "beq.n 1f\n" + 1052 RepeatInsn(kLdrR0R0Count + kLdrR0R0CountLong, "ldr r0, [r0]\n") + 1053 "1:\n" 1054 "ldr r0, [r0]\n"; 1055 DriverStr(expected, "BindTrackedLabel"); 1056 1057 // Expectation is that the tracked label should have moved. 1058 EXPECT_LT(non_tracked.Position(), tracked.Position()); 1059} 1060 1061TEST_F(AssemblerThumb2Test, JumpTable) { 1062 // The jump table. Use three labels. 1063 Label label1, label2, label3; 1064 std::vector<Label*> labels({ &label1, &label2, &label3 }); 1065 1066 // A few dummy loads on entry, interspersed with 2 labels. 1067 constexpr size_t kLdrR0R0Count = 5; 1068 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1069 __ ldr(arm::R0, arm::Address(arm::R0)); 1070 } 1071 __ BindTrackedLabel(&label1); 1072 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1073 __ ldr(arm::R0, arm::Address(arm::R0)); 1074 } 1075 __ BindTrackedLabel(&label2); 1076 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1077 __ ldr(arm::R0, arm::Address(arm::R0)); 1078 } 1079 1080 // Create the jump table, emit the base load. 1081 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1); 1082 1083 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how 1084 // it's being used. 1085 __ ldr(arm::R0, arm::Address(arm::R0)); 1086 1087 // Emit the jump 1088 __ EmitJumpTableDispatch(jump_table, arm::R1); 1089 1090 // Some more dummy instructions. 1091 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1092 __ ldr(arm::R0, arm::Address(arm::R0)); 1093 } 1094 __ BindTrackedLabel(&label3); 1095 for (size_t i = 0; i != kLdrR0R0Count; ++i) { // Note: odd so there's no alignment 1096 __ ldr(arm::R0, arm::Address(arm::R0)); // necessary, as gcc as emits nops, 1097 } // whereas we emit 0 != nop. 1098 1099 static_assert((kLdrR0R0Count + 3) * 2 < 1 * KB, "Too much offset"); 1100 1101 std::string expected = 1102 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1103 ".L1:\n" + 1104 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1105 ".L2:\n" + 1106 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1107 "adr r1, .Ljump_table\n" 1108 "ldr r0, [r0]\n" 1109 ".Lbase:\n" 1110 "add pc, r1\n" + 1111 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1112 ".L3:\n" + 1113 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1114 ".align 2\n" 1115 ".Ljump_table:\n" 1116 ".4byte (.L1 - .Lbase - 4)\n" 1117 ".4byte (.L2 - .Lbase - 4)\n" 1118 ".4byte (.L3 - .Lbase - 4)\n"; 1119 DriverStr(expected, "JumpTable"); 1120} 1121 1122// Test for >1K fixup. 1123TEST_F(AssemblerThumb2Test, JumpTable4K) { 1124 // The jump table. Use three labels. 1125 Label label1, label2, label3; 1126 std::vector<Label*> labels({ &label1, &label2, &label3 }); 1127 1128 // A few dummy loads on entry, interspersed with 2 labels. 1129 constexpr size_t kLdrR0R0Count = 5; 1130 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1131 __ ldr(arm::R0, arm::Address(arm::R0)); 1132 } 1133 __ BindTrackedLabel(&label1); 1134 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1135 __ ldr(arm::R0, arm::Address(arm::R0)); 1136 } 1137 __ BindTrackedLabel(&label2); 1138 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1139 __ ldr(arm::R0, arm::Address(arm::R0)); 1140 } 1141 1142 // Create the jump table, emit the base load. 1143 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1); 1144 1145 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how 1146 // it's being used. 1147 __ ldr(arm::R0, arm::Address(arm::R0)); 1148 1149 // Emit the jump 1150 __ EmitJumpTableDispatch(jump_table, arm::R1); 1151 1152 // Some more dummy instructions. 1153 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1154 __ ldr(arm::R0, arm::Address(arm::R0)); 1155 } 1156 __ BindTrackedLabel(&label3); 1157 constexpr size_t kLdrR0R0Count2 = 600; // Note: even so there's no alignment 1158 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops, 1159 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop. 1160 } 1161 1162 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 1 * KB, "Not enough offset"); 1163 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 4 * KB, "Too much offset"); 1164 1165 std::string expected = 1166 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1167 ".L1:\n" + 1168 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1169 ".L2:\n" + 1170 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1171 "adr r1, .Ljump_table\n" 1172 "ldr r0, [r0]\n" 1173 ".Lbase:\n" 1174 "add pc, r1\n" + 1175 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1176 ".L3:\n" + 1177 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 1178 ".align 2\n" 1179 ".Ljump_table:\n" 1180 ".4byte (.L1 - .Lbase - 4)\n" 1181 ".4byte (.L2 - .Lbase - 4)\n" 1182 ".4byte (.L3 - .Lbase - 4)\n"; 1183 DriverStr(expected, "JumpTable4K"); 1184} 1185 1186// Test for >4K fixup. 1187TEST_F(AssemblerThumb2Test, JumpTable64K) { 1188 // The jump table. Use three labels. 1189 Label label1, label2, label3; 1190 std::vector<Label*> labels({ &label1, &label2, &label3 }); 1191 1192 // A few dummy loads on entry, interspersed with 2 labels. 1193 constexpr size_t kLdrR0R0Count = 5; 1194 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1195 __ ldr(arm::R0, arm::Address(arm::R0)); 1196 } 1197 __ BindTrackedLabel(&label1); 1198 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1199 __ ldr(arm::R0, arm::Address(arm::R0)); 1200 } 1201 __ BindTrackedLabel(&label2); 1202 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1203 __ ldr(arm::R0, arm::Address(arm::R0)); 1204 } 1205 1206 // Create the jump table, emit the base load. 1207 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1); 1208 1209 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how 1210 // it's being used. 1211 __ ldr(arm::R0, arm::Address(arm::R0)); 1212 1213 // Emit the jump 1214 __ EmitJumpTableDispatch(jump_table, arm::R1); 1215 1216 // Some more dummy instructions. 1217 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1218 __ ldr(arm::R0, arm::Address(arm::R0)); 1219 } 1220 __ BindTrackedLabel(&label3); 1221 constexpr size_t kLdrR0R0Count2 = 2601; // Note: odd so there's no alignment 1222 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops, 1223 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop. 1224 } 1225 1226 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 4 * KB, "Not enough offset"); 1227 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 64 * KB, "Too much offset"); 1228 1229 std::string expected = 1230 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1231 ".L1:\n" + 1232 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1233 ".L2:\n" + 1234 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1235 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself. 1236 // (Note: have to use constants, as labels aren't accepted. 1237 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) + 1238 ") * 2 - 4) & 0xFFFF)\n" 1239 "add r1, pc\n" 1240 "ldr r0, [r0]\n" 1241 ".Lbase:\n" 1242 "add pc, r1\n" + 1243 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1244 ".L3:\n" + 1245 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 1246 ".align 2\n" 1247 ".Ljump_table:\n" 1248 ".4byte (.L1 - .Lbase - 4)\n" 1249 ".4byte (.L2 - .Lbase - 4)\n" 1250 ".4byte (.L3 - .Lbase - 4)\n"; 1251 DriverStr(expected, "JumpTable64K"); 1252} 1253 1254// Test for >64K fixup. 1255TEST_F(AssemblerThumb2Test, JumpTableFar) { 1256 // The jump table. Use three labels. 1257 Label label1, label2, label3; 1258 std::vector<Label*> labels({ &label1, &label2, &label3 }); 1259 1260 // A few dummy loads on entry, interspersed with 2 labels. 1261 constexpr size_t kLdrR0R0Count = 5; 1262 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1263 __ ldr(arm::R0, arm::Address(arm::R0)); 1264 } 1265 __ BindTrackedLabel(&label1); 1266 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1267 __ ldr(arm::R0, arm::Address(arm::R0)); 1268 } 1269 __ BindTrackedLabel(&label2); 1270 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1271 __ ldr(arm::R0, arm::Address(arm::R0)); 1272 } 1273 1274 // Create the jump table, emit the base load. 1275 arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1); 1276 1277 // Dummy computation, stand-in for the address. We're only testing the jump table here, not how 1278 // it's being used. 1279 __ ldr(arm::R0, arm::Address(arm::R0)); 1280 1281 // Emit the jump 1282 __ EmitJumpTableDispatch(jump_table, arm::R1); 1283 1284 // Some more dummy instructions. 1285 for (size_t i = 0; i != kLdrR0R0Count; ++i) { 1286 __ ldr(arm::R0, arm::Address(arm::R0)); 1287 } 1288 __ BindTrackedLabel(&label3); 1289 constexpr size_t kLdrR0R0Count2 = 70001; // Note: odd so there's no alignment 1290 for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops, 1291 __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop. 1292 } 1293 1294 static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 64 * KB, "Not enough offset"); 1295 1296 std::string expected = 1297 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1298 ".L1:\n" + 1299 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1300 ".L2:\n" + 1301 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1302 // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself. 1303 // (Note: have to use constants, as labels aren't accepted. 1304 "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) + 1305 ") * 2 - 4) & 0xFFFF)\n" 1306 "movt r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) + 1307 ") * 2 - 4) >> 16)\n" 1308 ".Lhelp:" 1309 "add r1, pc\n" 1310 "ldr r0, [r0]\n" 1311 ".Lbase:\n" 1312 "add pc, r1\n" + 1313 RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") + 1314 ".L3:\n" + 1315 RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") + 1316 ".align 2\n" 1317 ".Ljump_table:\n" 1318 ".4byte (.L1 - .Lbase - 4)\n" 1319 ".4byte (.L2 - .Lbase - 4)\n" 1320 ".4byte (.L3 - .Lbase - 4)\n"; 1321 DriverStr(expected, "JumpTableFar"); 1322} 1323 1324TEST_F(AssemblerThumb2Test, Clz) { 1325 __ clz(arm::R0, arm::R1); 1326 1327 const char* expected = "clz r0, r1\n"; 1328 1329 DriverStr(expected, "clz"); 1330} 1331 1332TEST_F(AssemblerThumb2Test, rbit) { 1333 __ rbit(arm::R1, arm::R0); 1334 1335 const char* expected = "rbit r1, r0\n"; 1336 1337 DriverStr(expected, "rbit"); 1338} 1339 1340} // namespace art 1341