Index: test/cctest/compiler/test-jump-threading.cc |
diff --git a/test/cctest/compiler/test-jump-threading.cc b/test/cctest/compiler/test-jump-threading.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b40023cfbb89bc997147e60cf5d6b29c91ef2c8d |
--- /dev/null |
+++ b/test/cctest/compiler/test-jump-threading.cc |
@@ -0,0 +1,765 @@ |
+// Copyright 2014 the V8 project authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "src/v8.h" |
+#include "test/cctest/cctest.h" |
+ |
+#include "src/compiler/instruction.h" |
+#include "src/compiler/instruction-codes.h" |
+#include "src/compiler/jump-threading.h" |
+ |
+namespace v8 { |
+namespace internal { |
+namespace compiler { |
+ |
+typedef BasicBlock::RpoNumber RpoNumber; |
+ |
+class TestCode : public HandleAndZoneScope { |
+ public: |
+ TestCode() |
+ : HandleAndZoneScope(), |
+ blocks_(main_zone()), |
+ sequence_(main_zone(), &blocks_), |
+ rpo_number_(RpoNumber::FromInt(0)), |
+ current_(NULL) {} |
+ |
+ ZoneVector<InstructionBlock*> blocks_; |
+ InstructionSequence sequence_; |
+ RpoNumber rpo_number_; |
+ InstructionBlock* current_; |
+ |
+ int Jump(int target) { |
+ Start(); |
+ InstructionOperand* ops[] = {UseRpo(target)}; |
+ sequence_.AddInstruction(Instruction::New(main_zone(), kArchJmp, 0, NULL, 1, |
+ ops, 0, NULL)->MarkAsControl()); |
+ int pos = static_cast<int>(sequence_.instructions().size() - 1); |
+ End(); |
+ return pos; |
+ } |
+ void Fallthru() { |
+ Start(); |
+ End(); |
+ } |
+ int Branch(int ttarget, int ftarget) { |
+ Start(); |
+ InstructionOperand* ops[] = {UseRpo(ttarget), UseRpo(ftarget)}; |
+ InstructionCode code = 119 | FlagsModeField::encode(kFlags_branch) | |
+ FlagsConditionField::encode(kEqual); |
+ sequence_.AddInstruction(Instruction::New(main_zone(), code, 0, NULL, 2, |
+ ops, 0, NULL)->MarkAsControl()); |
+ int pos = static_cast<int>(sequence_.instructions().size() - 1); |
+ End(); |
+ return pos; |
+ } |
+ void Nop() { |
+ Start(); |
+ sequence_.AddInstruction(Instruction::New(main_zone(), kArchNop)); |
+ } |
+ void RedundantMoves() { |
+ Start(); |
+ sequence_.AddInstruction(Instruction::New(main_zone(), kArchNop)); |
+ int index = static_cast<int>(sequence_.instructions().size()) - 1; |
+ sequence_.AddGapMove(index, RegisterOperand::Create(13, main_zone()), |
+ RegisterOperand::Create(13, main_zone())); |
+ } |
+ void NonRedundantMoves() { |
+ Start(); |
+ sequence_.AddInstruction(Instruction::New(main_zone(), kArchNop)); |
+ int index = static_cast<int>(sequence_.instructions().size()) - 1; |
+ sequence_.AddGapMove(index, ImmediateOperand::Create(11, main_zone()), |
+ RegisterOperand::Create(11, main_zone())); |
+ } |
+ void Other() { |
+ Start(); |
+ sequence_.AddInstruction(Instruction::New(main_zone(), 155)); |
+ } |
+ void End() { |
+ Start(); |
+ sequence_.EndBlock(current_->rpo_number()); |
+ current_ = NULL; |
+ rpo_number_ = RpoNumber::FromInt(rpo_number_.ToInt() + 1); |
+ } |
+ InstructionOperand* UseRpo(int num) { |
+ int index = sequence_.AddImmediate(Constant(RpoNumber::FromInt(num))); |
+ return ImmediateOperand::Create(index, main_zone()); |
+ } |
+ void Start(bool deferred = false) { |
+ if (current_ == NULL) { |
+ current_ = new (main_zone()) InstructionBlock( |
+ main_zone(), BasicBlock::Id::FromInt(rpo_number_.ToInt()), |
+ rpo_number_, rpo_number_, RpoNumber::Invalid(), RpoNumber::Invalid(), |
+ deferred); |
+ blocks_.push_back(current_); |
+ sequence_.StartBlock(rpo_number_); |
+ } |
+ } |
+ void Defer() { |
+ CHECK(current_ == NULL); |
+ Start(true); |
+ } |
+}; |
+ |
+ |
+void VerifyForwarding(TestCode& code, int count, int* expected) { |
+ Zone local_zone(code.main_isolate()); |
+ ZoneVector<RpoNumber> result(&local_zone); |
+ JumpThreading::ComputeForwarding(&local_zone, result, &code.sequence_); |
+ |
+ CHECK(count == static_cast<int>(result.size())); |
+ for (int i = 0; i < count; i++) { |
+ CHECK(expected[i] == result[i].ToInt()); |
+ } |
+} |
+ |
+ |
+TEST(FwEmpty1) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Jump(1); |
+ // B1 |
+ code.Jump(2); |
+ // B2 |
+ code.End(); |
+ |
+ static int expected[] = {2, 2, 2}; |
+ VerifyForwarding(code, 3, expected); |
+} |
+ |
+ |
+TEST(FwEmptyN) { |
+ for (int i = 0; i < 9; i++) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Jump(1); |
+ // B1 |
+ for (int j = 0; j < i; j++) code.Nop(); |
+ code.Jump(2); |
+ // B2 |
+ code.End(); |
+ |
+ static int expected[] = {2, 2, 2}; |
+ VerifyForwarding(code, 3, expected); |
+ } |
+} |
+ |
+ |
+TEST(FwNone1) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.End(); |
+ |
+ static int expected[] = {0}; |
+ VerifyForwarding(code, 1, expected); |
+} |
+ |
+ |
+TEST(FwMoves1) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.RedundantMoves(); |
+ code.End(); |
+ |
+ static int expected[] = {0}; |
+ VerifyForwarding(code, 1, expected); |
+} |
+ |
+ |
+TEST(FwMoves2) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.RedundantMoves(); |
+ code.Fallthru(); |
+ // B1 |
+ code.End(); |
+ |
+ static int expected[] = {1, 1}; |
+ VerifyForwarding(code, 2, expected); |
+} |
+ |
+ |
+TEST(FwMoves2b) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.NonRedundantMoves(); |
+ code.Fallthru(); |
+ // B1 |
+ code.End(); |
+ |
+ static int expected[] = {0, 1}; |
+ VerifyForwarding(code, 2, expected); |
+} |
+ |
+ |
+TEST(FwOther2) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Other(); |
+ code.Fallthru(); |
+ // B1 |
+ code.End(); |
+ |
+ static int expected[] = {0, 1}; |
+ VerifyForwarding(code, 2, expected); |
+} |
+ |
+ |
+TEST(FwNone2a) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Fallthru(); |
+ // B1 |
+ code.End(); |
+ |
+ static int expected[] = {1, 1}; |
+ VerifyForwarding(code, 2, expected); |
+} |
+ |
+ |
+TEST(FwNone2b) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Jump(1); |
+ // B1 |
+ code.End(); |
+ |
+ static int expected[] = {1, 1}; |
+ VerifyForwarding(code, 2, expected); |
+} |
+ |
+ |
+TEST(FwLoop1) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Jump(0); |
+ |
+ static int expected[] = {0}; |
+ VerifyForwarding(code, 1, expected); |
+} |
+ |
+ |
+TEST(FwLoop2) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Fallthru(); |
+ // B1 |
+ code.Jump(0); |
+ |
+ static int expected[] = {0, 0}; |
+ VerifyForwarding(code, 2, expected); |
+} |
+ |
+ |
+TEST(FwLoop3) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Fallthru(); |
+ // B1 |
+ code.Fallthru(); |
+ // B2 |
+ code.Jump(0); |
+ |
+ static int expected[] = {0, 0, 0}; |
+ VerifyForwarding(code, 3, expected); |
+} |
+ |
+ |
+TEST(FwLoop1b) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Fallthru(); |
+ // B1 |
+ code.Jump(1); |
+ |
+ static int expected[] = {1, 1}; |
+ VerifyForwarding(code, 2, expected); |
+} |
+ |
+ |
+TEST(FwLoop2b) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Fallthru(); |
+ // B1 |
+ code.Fallthru(); |
+ // B2 |
+ code.Jump(1); |
+ |
+ static int expected[] = {1, 1, 1}; |
+ VerifyForwarding(code, 3, expected); |
+} |
+ |
+ |
+TEST(FwLoop3b) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Fallthru(); |
+ // B1 |
+ code.Fallthru(); |
+ // B2 |
+ code.Fallthru(); |
+ // B3 |
+ code.Jump(1); |
+ |
+ static int expected[] = {1, 1, 1, 1}; |
+ VerifyForwarding(code, 4, expected); |
+} |
+ |
+ |
+TEST(FwLoop2_1a) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Fallthru(); |
+ // B1 |
+ code.Fallthru(); |
+ // B2 |
+ code.Fallthru(); |
+ // B3 |
+ code.Jump(1); |
+ // B4 |
+ code.Jump(2); |
+ |
+ static int expected[] = {1, 1, 1, 1, 1}; |
+ VerifyForwarding(code, 5, expected); |
+} |
+ |
+ |
+TEST(FwLoop2_1b) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Fallthru(); |
+ // B1 |
+ code.Fallthru(); |
+ // B2 |
+ code.Jump(4); |
+ // B3 |
+ code.Jump(1); |
+ // B4 |
+ code.Jump(2); |
+ |
+ static int expected[] = {2, 2, 2, 2, 2}; |
+ VerifyForwarding(code, 5, expected); |
+} |
+ |
+ |
+TEST(FwLoop2_1c) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Fallthru(); |
+ // B1 |
+ code.Fallthru(); |
+ // B2 |
+ code.Jump(4); |
+ // B3 |
+ code.Jump(2); |
+ // B4 |
+ code.Jump(1); |
+ |
+ static int expected[] = {1, 1, 1, 1, 1}; |
+ VerifyForwarding(code, 5, expected); |
+} |
+ |
+ |
+TEST(FwLoop2_1d) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Fallthru(); |
+ // B1 |
+ code.Fallthru(); |
+ // B2 |
+ code.Jump(1); |
+ // B3 |
+ code.Jump(1); |
+ // B4 |
+ code.Jump(1); |
+ |
+ static int expected[] = {1, 1, 1, 1, 1}; |
+ VerifyForwarding(code, 5, expected); |
+} |
+ |
+ |
+TEST(FwLoop3_1a) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Fallthru(); |
+ // B1 |
+ code.Fallthru(); |
+ // B2 |
+ code.Fallthru(); |
+ // B3 |
+ code.Jump(2); |
+ // B4 |
+ code.Jump(1); |
+ // B5 |
+ code.Jump(0); |
+ |
+ static int expected[] = {2, 2, 2, 2, 2, 2}; |
+ VerifyForwarding(code, 6, expected); |
+} |
+ |
+ |
+TEST(FwDiamonds) { |
+ for (int i = 0; i < 2; i++) { |
+ for (int j = 0; j < 2; j++) { |
+ TestCode code; |
+ // B0 |
+ code.Branch(1, 2); |
+ // B1 |
+ if (i) code.Other(); |
+ code.Jump(3); |
+ // B2 |
+ if (j) code.Other(); |
+ code.Jump(3); |
+ // B3 |
+ code.End(); |
+ |
+ int expected[] = {0, i ? 1 : 3, j ? 2 : 3, 3}; |
+ VerifyForwarding(code, 4, expected); |
+ } |
+ } |
+} |
+ |
+ |
+TEST(FwDiamonds2) { |
+ for (int i = 0; i < 2; i++) { |
+ for (int j = 0; j < 2; j++) { |
+ for (int k = 0; k < 2; k++) { |
+ TestCode code; |
+ // B0 |
+ code.Branch(1, 2); |
+ // B1 |
+ if (i) code.Other(); |
+ code.Jump(3); |
+ // B2 |
+ if (j) code.Other(); |
+ code.Jump(3); |
+ // B3 |
+ if (k) code.NonRedundantMoves(); |
+ code.Jump(4); |
+ // B4 |
+ code.End(); |
+ |
+ int merge = k ? 3 : 4; |
+ int expected[] = {0, i ? 1 : merge, j ? 2 : merge, merge, 4}; |
+ VerifyForwarding(code, 5, expected); |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+TEST(FwDoubleDiamonds) { |
+ for (int i = 0; i < 2; i++) { |
+ for (int j = 0; j < 2; j++) { |
+ for (int x = 0; x < 2; x++) { |
+ for (int y = 0; y < 2; y++) { |
+ TestCode code; |
+ // B0 |
+ code.Branch(1, 2); |
+ // B1 |
+ if (i) code.Other(); |
+ code.Jump(3); |
+ // B2 |
+ if (j) code.Other(); |
+ code.Jump(3); |
+ // B3 |
+ code.Branch(4, 5); |
+ // B4 |
+ if (x) code.Other(); |
+ code.Jump(6); |
+ // B5 |
+ if (y) code.Other(); |
+ code.Jump(6); |
+ // B6 |
+ code.End(); |
+ |
+ int expected[] = {0, i ? 1 : 3, j ? 2 : 3, 3, |
+ x ? 4 : 6, y ? 5 : 6, 6}; |
+ VerifyForwarding(code, 7, expected); |
+ } |
+ } |
+ } |
+ } |
+} |
+ |
+template <int kSize> |
+void RunPermutationsRecursive(int outer[kSize], int start, |
+ void (*run)(int*, int)) { |
+ int permutation[kSize]; |
+ |
+ for (int i = 0; i < kSize; i++) permutation[i] = outer[i]; |
+ |
+ int count = kSize - start; |
+ if (count == 0) return run(permutation, kSize); |
+ for (int i = start; i < kSize; i++) { |
+ permutation[start] = outer[i]; |
+ permutation[i] = outer[start]; |
+ RunPermutationsRecursive<kSize>(permutation, start + 1, run); |
+ permutation[i] = outer[i]; |
+ permutation[start] = outer[start]; |
+ } |
+} |
+ |
+ |
+template <int kSize> |
+void RunAllPermutations(void (*run)(int*, int)) { |
+ int permutation[kSize]; |
+ for (int i = 0; i < kSize; i++) permutation[i] = i; |
+ RunPermutationsRecursive<kSize>(permutation, 0, run); |
+} |
+ |
+ |
+void PrintPermutation(int* permutation, int size) { |
+ printf("{ "); |
+ for (int i = 0; i < size; i++) { |
+ if (i > 0) printf(", "); |
+ printf("%d", permutation[i]); |
+ } |
+ printf(" }\n"); |
+} |
+ |
+ |
+int find(int x, int* permutation, int size) { |
+ for (int i = 0; i < size; i++) { |
+ if (permutation[i] == x) return i; |
+ } |
+ return size; |
+} |
+ |
+ |
+void RunPermutedChain(int* permutation, int size) { |
+ TestCode code; |
+ int cur = -1; |
+ for (int i = 0; i < size; i++) { |
+ code.Jump(find(cur + 1, permutation, size) + 1); |
+ cur = permutation[i]; |
+ } |
+ code.Jump(find(cur + 1, permutation, size) + 1); |
+ code.End(); |
+ |
+ int expected[] = {size + 1, size + 1, size + 1, size + 1, |
+ size + 1, size + 1, size + 1}; |
+ VerifyForwarding(code, size + 2, expected); |
+} |
+ |
+ |
+TEST(FwPermuted_chain) { |
+ RunAllPermutations<3>(RunPermutedChain); |
+ RunAllPermutations<4>(RunPermutedChain); |
+ RunAllPermutations<5>(RunPermutedChain); |
+} |
+ |
+ |
+void RunPermutedDiamond(int* permutation, int size) { |
+ TestCode code; |
+ int br = 1 + find(0, permutation, size); |
+ code.Jump(br); |
+ for (int i = 0; i < size; i++) { |
+ switch (permutation[i]) { |
+ case 0: |
+ code.Branch(1 + find(1, permutation, size), |
+ 1 + find(2, permutation, size)); |
+ break; |
+ case 1: |
+ code.Jump(1 + find(3, permutation, size)); |
+ break; |
+ case 2: |
+ code.Jump(1 + find(3, permutation, size)); |
+ break; |
+ case 3: |
+ code.Jump(5); |
+ break; |
+ } |
+ } |
+ code.End(); |
+ |
+ int expected[] = {br, 5, 5, 5, 5, 5}; |
+ expected[br] = br; |
+ VerifyForwarding(code, 6, expected); |
+} |
+ |
+ |
+TEST(FwPermuted_diamond) { RunAllPermutations<4>(RunPermutedDiamond); } |
+ |
+ |
+void ApplyForwarding(TestCode& code, int size, int* forward) { |
+ ZoneVector<RpoNumber> vector(code.main_zone()); |
+ for (int i = 0; i < size; i++) { |
+ vector.push_back(RpoNumber::FromInt(forward[i])); |
+ } |
+ JumpThreading::ApplyForwarding(vector, &code.sequence_); |
+} |
+ |
+ |
+void CheckJump(TestCode& code, int pos, int target) { |
+ Instruction* instr = code.sequence_.InstructionAt(pos); |
+ CHECK_EQ(kArchJmp, instr->arch_opcode()); |
+ CHECK_EQ(1, static_cast<int>(instr->InputCount())); |
+ CHECK_EQ(0, static_cast<int>(instr->OutputCount())); |
+ CHECK_EQ(0, static_cast<int>(instr->TempCount())); |
+ CHECK_EQ(target, code.sequence_.InputRpo(instr, 0).ToInt()); |
+} |
+ |
+ |
+void CheckNop(TestCode& code, int pos) { |
+ Instruction* instr = code.sequence_.InstructionAt(pos); |
+ CHECK_EQ(kArchNop, instr->arch_opcode()); |
+ CHECK_EQ(0, static_cast<int>(instr->InputCount())); |
+ CHECK_EQ(0, static_cast<int>(instr->OutputCount())); |
+ CHECK_EQ(0, static_cast<int>(instr->TempCount())); |
+} |
+ |
+ |
+void CheckBranch(TestCode& code, int pos, int t1, int t2) { |
+ Instruction* instr = code.sequence_.InstructionAt(pos); |
+ CHECK_EQ(2, static_cast<int>(instr->InputCount())); |
+ CHECK_EQ(0, static_cast<int>(instr->OutputCount())); |
+ CHECK_EQ(0, static_cast<int>(instr->TempCount())); |
+ CHECK_EQ(t1, code.sequence_.InputRpo(instr, 0).ToInt()); |
+ CHECK_EQ(t2, code.sequence_.InputRpo(instr, 1).ToInt()); |
+} |
+ |
+ |
+void CheckAssemblyOrder(TestCode& code, int size, int* expected) { |
+ int i = 0; |
+ for (auto const block : code.sequence_.instruction_blocks()) { |
+ CHECK_EQ(expected[i++], block->ao_number().ToInt()); |
+ } |
+} |
+ |
+ |
+TEST(Rewire1) { |
+ TestCode code; |
+ |
+ // B0 |
+ int j1 = code.Jump(1); |
+ // B1 |
+ int j2 = code.Jump(2); |
+ // B2 |
+ code.End(); |
+ |
+ static int forward[] = {2, 2, 2}; |
+ ApplyForwarding(code, 3, forward); |
+ CheckJump(code, j1, 2); |
+ CheckNop(code, j2); |
+ |
+ static int assembly[] = {0, 1, 1}; |
+ CheckAssemblyOrder(code, 3, assembly); |
+} |
+ |
+ |
+TEST(Rewire1_deferred) { |
+ TestCode code; |
+ |
+ // B0 |
+ int j1 = code.Jump(1); |
+ // B1 |
+ int j2 = code.Jump(2); |
+ // B2 |
+ code.Defer(); |
+ int j3 = code.Jump(3); |
+ // B3 |
+ code.End(); |
+ |
+ static int forward[] = {3, 3, 3, 3}; |
+ ApplyForwarding(code, 4, forward); |
+ CheckJump(code, j1, 3); |
+ CheckNop(code, j2); |
+ CheckNop(code, j3); |
+ |
+ static int assembly[] = {0, 1, 2, 1}; |
+ CheckAssemblyOrder(code, 4, assembly); |
+} |
+ |
+ |
+TEST(Rewire2_deferred) { |
+ TestCode code; |
+ |
+ // B0 |
+ code.Other(); |
+ int j1 = code.Jump(1); |
+ // B1 |
+ code.Defer(); |
+ code.Fallthru(); |
+ // B2 |
+ code.Defer(); |
+ int j2 = code.Jump(3); |
+ // B3 |
+ code.End(); |
+ |
+ static int forward[] = {0, 1, 2, 3}; |
+ ApplyForwarding(code, 4, forward); |
+ CheckJump(code, j1, 1); |
+ CheckJump(code, j2, 3); |
+ |
+ static int assembly[] = {0, 2, 3, 1}; |
+ CheckAssemblyOrder(code, 4, assembly); |
+} |
+ |
+ |
+TEST(Rewire_diamond) { |
+ for (int i = 0; i < 2; i++) { |
+ for (int j = 0; j < 2; j++) { |
+ TestCode code; |
+ // B0 |
+ int j1 = code.Jump(1); |
+ // B1 |
+ int b1 = code.Branch(2, 3); |
+ // B2 |
+ int j2 = code.Jump(4); |
+ // B3 |
+ int j3 = code.Jump(4); |
+ // B5 |
+ code.End(); |
+ |
+ int forward[] = {0, 1, i ? 4 : 2, j ? 4 : 3, 4}; |
+ ApplyForwarding(code, 5, forward); |
+ CheckJump(code, j1, 1); |
+ CheckBranch(code, b1, i ? 4 : 2, j ? 4 : 3); |
+ if (i) { |
+ CheckNop(code, j2); |
+ } else { |
+ CheckJump(code, j2, 4); |
+ } |
+ if (j) { |
+ CheckNop(code, j3); |
+ } else { |
+ CheckJump(code, j3, 4); |
+ } |
+ |
+ int assembly[] = {0, 1, 2, 3, 4}; |
+ if (i) { |
+ for (int k = 3; k < 5; k++) assembly[k]--; |
+ } |
+ if (j) { |
+ for (int k = 4; k < 5; k++) assembly[k]--; |
+ } |
+ CheckAssemblyOrder(code, 5, assembly); |
+ } |
+ } |
+} |
+ |
+} // namespace compiler |
+} // namespace internal |
+} // namespace v8 |