• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Clearv8::internal::compiler::JumpThreadingState22   void Clear(size_t count) { result.assign(count, unvisited()); }
PushIfUnvisitedv8::internal::compiler::JumpThreadingState23   void PushIfUnvisited(RpoNumber num) {
24     if (result[num.ToInt()] == unvisited()) {
25       stack.push(num);
26       result[num.ToInt()] = onstack();
27     }
28   }
Forwardv8::internal::compiler::JumpThreadingState29   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   }
unvisitedv8::internal::compiler::JumpThreadingState52   RpoNumber unvisited() { return RpoNumber::FromInt(-1); }
onstackv8::internal::compiler::JumpThreadingState53   RpoNumber onstack() { return RpoNumber::FromInt(-2); }
54 };
55 
56 
ComputeForwarding(Zone * local_zone,ZoneVector<RpoNumber> & result,InstructionSequence * code)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 
ApplyForwarding(ZoneVector<RpoNumber> & result,InstructionSequence * code)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