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