Index: test/cctest/test-assembler-a64.cc |
diff --git a/test/cctest/test-assembler-a64.cc b/test/cctest/test-assembler-a64.cc |
index 547de68749602a9150c9a94dde230e4bd5710f96..656f3691caa8b0363afb36a9cbbd75ccd704f1fd 100644 |
--- a/test/cctest/test-assembler-a64.cc |
+++ b/test/cctest/test-assembler-a64.cc |
@@ -1947,6 +1947,335 @@ TEST(test_branch) { |
} |
+TEST(far_branch_backward) { |
+ INIT_V8(); |
+ |
+ // Test that the MacroAssembler correctly resolves backward branches to labels |
+ // that are outside the immediate range of branch instructions. |
+ int max_range = |
+ std::max(Instruction::ImmBranchRange(TestBranchType), |
+ std::max(Instruction::ImmBranchRange(CompareBranchType), |
+ Instruction::ImmBranchRange(CondBranchType))); |
+ |
+ SETUP_SIZE(max_range + 1000 * kInstructionSize); |
+ |
+ START(); |
+ |
+ Label done, fail; |
+ Label test_tbz, test_cbz, test_bcond; |
+ Label success_tbz, success_cbz, success_bcond; |
+ |
+ __ Mov(x0, 0); |
+ __ Mov(x1, 1); |
+ __ Mov(x10, 0); |
+ |
+ __ B(&test_tbz); |
+ __ Bind(&success_tbz); |
+ __ Orr(x0, x0, 1 << 0); |
+ __ B(&test_cbz); |
+ __ Bind(&success_cbz); |
+ __ Orr(x0, x0, 1 << 1); |
+ __ B(&test_bcond); |
+ __ Bind(&success_bcond); |
+ __ Orr(x0, x0, 1 << 2); |
+ |
+ __ B(&done); |
+ |
+ // Generate enough code to overflow the immediate range of the three types of |
+ // branches below. |
+ for (unsigned i = 0; i < max_range / kInstructionSize + 1; ++i) { |
+ if (i % 100 == 0) { |
+ // If we do land in this code, we do not want to execute so many nops |
+ // before reaching the end of test (especially if tracing is activated). |
+ __ B(&fail); |
+ } else { |
+ __ Nop(); |
+ } |
+ } |
+ __ B(&fail); |
+ |
+ __ Bind(&test_tbz); |
+ __ Tbz(x10, 7, &success_tbz); |
+ __ Bind(&test_cbz); |
+ __ Cbz(x10, &success_cbz); |
+ __ Bind(&test_bcond); |
+ __ Cmp(x10, 0); |
+ __ B(eq, &success_bcond); |
+ |
+ // For each out-of-range branch instructions, at least two instructions should |
+ // have been generated. |
+ CHECK_GE(7 * kInstructionSize, __ SizeOfCodeGeneratedSince(&test_tbz)); |
+ |
+ __ Bind(&fail); |
+ __ Mov(x1, 0); |
+ __ Bind(&done); |
+ |
+ END(); |
+ |
+ RUN(); |
+ |
+ ASSERT_EQUAL_64(0x7, x0); |
+ ASSERT_EQUAL_64(0x1, x1); |
+ |
+ TEARDOWN(); |
+} |
+ |
+ |
+TEST(far_branch_simple_veneer) { |
+ INIT_V8(); |
+ |
+ // Test that the MacroAssembler correctly emits veneers for forward branches |
+ // to labels that are outside the immediate range of branch instructions. |
+ int max_range = |
+ std::max(Instruction::ImmBranchRange(TestBranchType), |
+ std::max(Instruction::ImmBranchRange(CompareBranchType), |
+ Instruction::ImmBranchRange(CondBranchType))); |
+ |
+ SETUP_SIZE(max_range + 1000 * kInstructionSize); |
+ |
+ START(); |
+ |
+ Label done, fail; |
+ Label test_tbz, test_cbz, test_bcond; |
+ Label success_tbz, success_cbz, success_bcond; |
+ |
+ __ Mov(x0, 0); |
+ __ Mov(x1, 1); |
+ __ Mov(x10, 0); |
+ |
+ __ Bind(&test_tbz); |
+ __ Tbz(x10, 7, &success_tbz); |
+ __ Bind(&test_cbz); |
+ __ Cbz(x10, &success_cbz); |
+ __ Bind(&test_bcond); |
+ __ Cmp(x10, 0); |
+ __ B(eq, &success_bcond); |
+ |
+ // Generate enough code to overflow the immediate range of the three types of |
+ // branches below. |
+ for (unsigned i = 0; i < max_range / kInstructionSize + 1; ++i) { |
+ if (i % 100 == 0) { |
+ // If we do land in this code, we do not want to execute so many nops |
+ // before reaching the end of test (especially if tracing is activated). |
+ // Also, the branches give the MacroAssembler the opportunity to emit the |
+ // veneers. |
+ __ B(&fail); |
+ } else { |
+ __ Nop(); |
+ } |
+ } |
+ __ B(&fail); |
+ |
+ __ Bind(&success_tbz); |
+ __ Orr(x0, x0, 1 << 0); |
+ __ B(&test_cbz); |
+ __ Bind(&success_cbz); |
+ __ Orr(x0, x0, 1 << 1); |
+ __ B(&test_bcond); |
+ __ Bind(&success_bcond); |
+ __ Orr(x0, x0, 1 << 2); |
+ |
+ __ B(&done); |
+ __ Bind(&fail); |
+ __ Mov(x1, 0); |
+ __ Bind(&done); |
+ |
+ END(); |
+ |
+ RUN(); |
+ |
+ ASSERT_EQUAL_64(0x7, x0); |
+ ASSERT_EQUAL_64(0x1, x1); |
+ |
+ TEARDOWN(); |
+} |
+ |
+ |
+TEST(far_branch_veneer_link_chain) { |
+ INIT_V8(); |
+ |
+ // Test that the MacroAssembler correctly emits veneers for forward branches |
+ // that target out-of-range labels and are part of multiple instructions |
+ // jumping to that label. |
+ // |
+ // We test the three situations with the different types of instruction: |
+ // (1)- When the branch is at the start of the chain with tbz. |
+ // (2)- When the branch is in the middle of the chain with cbz. |
+ // (3)- When the branch is at the end of the chain with bcond. |
+ int max_range = |
+ std::max(Instruction::ImmBranchRange(TestBranchType), |
+ std::max(Instruction::ImmBranchRange(CompareBranchType), |
+ Instruction::ImmBranchRange(CondBranchType))); |
+ |
+ SETUP_SIZE(max_range + 1000 * kInstructionSize); |
+ |
+ START(); |
+ |
+ Label skip, fail, done; |
+ Label test_tbz, test_cbz, test_bcond; |
+ Label success_tbz, success_cbz, success_bcond; |
+ |
+ __ Mov(x0, 0); |
+ __ Mov(x1, 1); |
+ __ Mov(x10, 0); |
+ |
+ __ B(&skip); |
+ // Branches at the start of the chain for situations (2) and (3). |
+ __ B(&success_cbz); |
+ __ B(&success_bcond); |
+ __ Nop(); |
+ __ B(&success_bcond); |
+ __ B(&success_cbz); |
+ __ Bind(&skip); |
+ |
+ __ Bind(&test_tbz); |
+ __ Tbz(x10, 7, &success_tbz); |
+ __ Bind(&test_cbz); |
+ __ Cbz(x10, &success_cbz); |
+ __ Bind(&test_bcond); |
+ __ Cmp(x10, 0); |
+ __ B(eq, &success_bcond); |
+ |
+ skip.Unuse(); |
+ __ B(&skip); |
+ // Branches at the end of the chain for situations (1) and (2). |
+ __ B(&success_cbz); |
+ __ B(&success_tbz); |
+ __ Nop(); |
+ __ B(&success_tbz); |
+ __ B(&success_cbz); |
+ __ Bind(&skip); |
+ |
+ // Generate enough code to overflow the immediate range of the three types of |
+ // branches below. |
+ for (unsigned i = 0; i < max_range / kInstructionSize + 1; ++i) { |
+ if (i % 100 == 0) { |
+ // If we do land in this code, we do not want to execute so many nops |
+ // before reaching the end of test (especially if tracing is activated). |
+ // Also, the branches give the MacroAssembler the opportunity to emit the |
+ // veneers. |
+ __ B(&fail); |
+ } else { |
+ __ Nop(); |
+ } |
+ } |
+ __ B(&fail); |
+ |
+ __ Bind(&success_tbz); |
+ __ Orr(x0, x0, 1 << 0); |
+ __ B(&test_cbz); |
+ __ Bind(&success_cbz); |
+ __ Orr(x0, x0, 1 << 1); |
+ __ B(&test_bcond); |
+ __ Bind(&success_bcond); |
+ __ Orr(x0, x0, 1 << 2); |
+ |
+ __ B(&done); |
+ __ Bind(&fail); |
+ __ Mov(x1, 0); |
+ __ Bind(&done); |
+ |
+ END(); |
+ |
+ RUN(); |
+ |
+ ASSERT_EQUAL_64(0x7, x0); |
+ ASSERT_EQUAL_64(0x1, x1); |
+ |
+ TEARDOWN(); |
+} |
+ |
+ |
+TEST(far_branch_veneer_broken_link_chain) { |
+ INIT_V8(); |
+ |
+ // Check that the MacroAssembler correctly handles the situation when removing |
+ // a branch from the link chain of a label and the two links on each side of |
+ // the removed branch cannot be linked together (out of range). |
+ // |
+ // We test with tbz because it has a small range. |
+ int max_range = Instruction::ImmBranchRange(TestBranchType); |
+ int inter_range = max_range / 2 + max_range / 10; |
+ |
+ SETUP_SIZE(3 * inter_range + 1000 * kInstructionSize); |
+ |
+ START(); |
+ |
+ Label skip, fail, done; |
+ Label test_1, test_2, test_3; |
+ Label far_target; |
+ |
+ __ Mov(x0, 0); // Indicates the origin of the branch. |
+ __ Mov(x1, 1); |
+ __ Mov(x10, 0); |
+ |
+ // First instruction in the label chain. |
+ __ Bind(&test_1); |
+ __ Mov(x0, 1); |
+ __ B(&far_target); |
+ |
+ for (unsigned i = 0; i < inter_range / kInstructionSize; ++i) { |
+ if (i % 100 == 0) { |
+ // Do not allow generating veneers. They should not be needed. |
+ __ b(&fail); |
+ } else { |
+ __ Nop(); |
+ } |
+ } |
+ |
+ // Will need a veneer to point to reach the target. |
+ __ Bind(&test_2); |
+ __ Mov(x0, 2); |
+ __ Tbz(x10, 7, &far_target); |
+ |
+ for (unsigned i = 0; i < inter_range / kInstructionSize; ++i) { |
+ if (i % 100 == 0) { |
+ // Do not allow generating veneers. They should not be needed. |
+ __ b(&fail); |
+ } else { |
+ __ Nop(); |
+ } |
+ } |
+ |
+ // Does not need a veneer to reach the target, but the initial branch |
+ // instruction is out of range. |
+ __ Bind(&test_3); |
+ __ Mov(x0, 3); |
+ __ Tbz(x10, 7, &far_target); |
+ |
+ for (unsigned i = 0; i < inter_range / kInstructionSize; ++i) { |
+ if (i % 100 == 0) { |
+ // Allow generating veneers. |
+ __ B(&fail); |
+ } else { |
+ __ Nop(); |
+ } |
+ } |
+ |
+ __ B(&fail); |
+ |
+ __ Bind(&far_target); |
+ __ Cmp(x0, 1); |
+ __ B(eq, &test_2); |
+ __ Cmp(x0, 2); |
+ __ B(eq, &test_3); |
+ |
+ __ B(&done); |
+ __ Bind(&fail); |
+ __ Mov(x1, 0); |
+ __ Bind(&done); |
+ |
+ END(); |
+ |
+ RUN(); |
+ |
+ ASSERT_EQUAL_64(0x3, x0); |
+ ASSERT_EQUAL_64(0x1, x1); |
+ |
+ TEARDOWN(); |
+} |
+ |
+ |
TEST(ldr_str_offset) { |
INIT_V8(); |
SETUP(); |