1 // Copyright 2014 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/compiler/jump-threading.h" 6 #include "src/compiler/code-generator-impl.h" 7 8 namespace v8 { 9 namespace internal { 10 namespace compiler { 11 12 #define TRACE(...) \ 13 do { \ 14 if (FLAG_trace_turbo_jt) PrintF(__VA_ARGS__); \ 15 } while (false) 16 17 struct JumpThreadingState { 18 bool forwarded; 19 ZoneVector<RpoNumber>& result; 20 ZoneStack<RpoNumber>& stack; 21 22 void Clear(size_t count) { result.assign(count, unvisited()); } 23 void PushIfUnvisited(RpoNumber num) { 24 if (result[num.ToInt()] == unvisited()) { 25 stack.push(num); 26 result[num.ToInt()] = onstack(); 27 } 28 } 29 void Forward(RpoNumber to) { 30 RpoNumber from = stack.top(); 31 RpoNumber to_to = result[to.ToInt()]; 32 bool pop = true; 33 if (to == from) { 34 TRACE(" xx %d\n", from.ToInt()); 35 result[from.ToInt()] = from; 36 } else if (to_to == unvisited()) { 37 TRACE(" fw %d -> %d (recurse)\n", from.ToInt(), to.ToInt()); 38 stack.push(to); 39 result[to.ToInt()] = onstack(); 40 pop = false; // recurse. 41 } else if (to_to == onstack()) { 42 TRACE(" fw %d -> %d (cycle)\n", from.ToInt(), to.ToInt()); 43 result[from.ToInt()] = to; // break the cycle. 44 forwarded = true; 45 } else { 46 TRACE(" fw %d -> %d (forward)\n", from.ToInt(), to.ToInt()); 47 result[from.ToInt()] = to_to; // forward the block. 48 forwarded = true; 49 } 50 if (pop) stack.pop(); 51 } 52 RpoNumber unvisited() { return RpoNumber::FromInt(-1); } 53 RpoNumber onstack() { return RpoNumber::FromInt(-2); } 54 }; 55 56 57 bool JumpThreading::ComputeForwarding(Zone* local_zone, 58 ZoneVector<RpoNumber>& result, 59 InstructionSequence* code) { 60 ZoneStack<RpoNumber> stack(local_zone); 61 JumpThreadingState state = {false, result, stack}; 62 state.Clear(code->InstructionBlockCount()); 63 64 // Iterate over the blocks forward, pushing the blocks onto the stack. 65 for (auto const block : code->instruction_blocks()) { 66 RpoNumber current = block->rpo_number(); 67 state.PushIfUnvisited(current); 68 69 // Process the stack, which implements DFS through empty blocks. 70 while (!state.stack.empty()) { 71 InstructionBlock* block = code->InstructionBlockAt(state.stack.top()); 72 // Process the instructions in a block up to a non-empty instruction. 73 TRACE("jt [%d] B%d\n", static_cast<int>(stack.size()), 74 block->rpo_number().ToInt()); 75 bool fallthru = true; 76 RpoNumber fw = block->rpo_number(); 77 for (int i = block->code_start(); i < block->code_end(); ++i) { 78 Instruction* instr = code->InstructionAt(i); 79 if (!instr->AreMovesRedundant()) { 80 // can't skip instructions with non redundant moves. 81 TRACE(" parallel move\n"); 82 fallthru = false; 83 } else if (FlagsModeField::decode(instr->opcode()) != kFlags_none) { 84 // can't skip instructions with flags continuations. 85 TRACE(" flags\n"); 86 fallthru = false; 87 } else if (instr->IsNop()) { 88 // skip nops. 89 TRACE(" nop\n"); 90 continue; 91 } else if (instr->arch_opcode() == kArchJmp) { 92 // try to forward the jump instruction. 93 TRACE(" jmp\n"); 94 fw = code->InputRpo(instr, 0); 95 fallthru = false; 96 } else { 97 // can't skip other instructions. 98 TRACE(" other\n"); 99 fallthru = false; 100 } 101 break; 102 } 103 if (fallthru) { 104 int next = 1 + block->rpo_number().ToInt(); 105 if (next < code->InstructionBlockCount()) fw = RpoNumber::FromInt(next); 106 } 107 state.Forward(fw); 108 } 109 } 110 111 #ifdef DEBUG 112 for (RpoNumber num : result) { 113 CHECK(num.IsValid()); 114 } 115 #endif 116 117 if (FLAG_trace_turbo_jt) { 118 for (int i = 0; i < static_cast<int>(result.size()); i++) { 119 TRACE("B%d ", i); 120 int to = result[i].ToInt(); 121 if (i != to) { 122 TRACE("-> B%d\n", to); 123 } else { 124 TRACE("\n"); 125 } 126 } 127 } 128 129 return state.forwarded; 130 } 131 132 133 void JumpThreading::ApplyForwarding(ZoneVector<RpoNumber>& result, 134 InstructionSequence* code) { 135 if (!FLAG_turbo_jt) return; 136 137 Zone local_zone; 138 ZoneVector<bool> skip(static_cast<int>(result.size()), false, &local_zone); 139 140 // Skip empty blocks when the previous block doesn't fall through. 141 bool prev_fallthru = true; 142 for (auto const block : code->instruction_blocks()) { 143 int block_num = block->rpo_number().ToInt(); 144 skip[block_num] = !prev_fallthru && result[block_num].ToInt() != block_num; 145 146 bool fallthru = true; 147 for (int i = block->code_start(); i < block->code_end(); ++i) { 148 Instruction* instr = code->InstructionAt(i); 149 if (FlagsModeField::decode(instr->opcode()) == kFlags_branch) { 150 fallthru = false; // branches don't fall through to the next block. 151 } else if (instr->arch_opcode() == kArchJmp) { 152 if (skip[block_num]) { 153 // Overwrite a redundant jump with a nop. 154 TRACE("jt-fw nop @%d\n", i); 155 instr->OverwriteWithNop(); 156 } 157 fallthru = false; // jumps don't fall through to the next block. 158 } 159 } 160 prev_fallthru = fallthru; 161 } 162 163 // Patch RPO immediates. 164 InstructionSequence::Immediates& immediates = code->immediates(); 165 for (size_t i = 0; i < immediates.size(); i++) { 166 Constant constant = immediates[i]; 167 if (constant.type() == Constant::kRpoNumber) { 168 RpoNumber rpo = constant.ToRpoNumber(); 169 RpoNumber fw = result[rpo.ToInt()]; 170 if (!(fw == rpo)) immediates[i] = Constant(fw); 171 } 172 } 173 174 // Recompute assembly order numbers. 175 int ao = 0; 176 for (auto const block : code->instruction_blocks()) { 177 if (!block->IsDeferred()) { 178 block->set_ao_number(RpoNumber::FromInt(ao)); 179 if (!skip[block->rpo_number().ToInt()]) ao++; 180 } 181 } 182 for (auto const block : code->instruction_blocks()) { 183 if (block->IsDeferred()) { 184 block->set_ao_number(RpoNumber::FromInt(ao)); 185 if (!skip[block->rpo_number().ToInt()]) ao++; 186 } 187 } 188 } 189 190 } // namespace compiler 191 } // namespace internal 192 } // namespace v8 193