OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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/effect-control-linearizer.h" | |
6 | |
7 #include "src/code-factory.h" | |
8 #include "src/compiler/access-builder.h" | |
9 #include "src/compiler/js-graph.h" | |
10 #include "src/compiler/linkage.h" | |
11 #include "src/compiler/node-properties.h" | |
12 #include "src/compiler/node.h" | |
13 #include "src/compiler/schedule.h" | |
14 | |
15 namespace v8 { | |
16 namespace internal { | |
17 namespace compiler { | |
18 | |
19 EffectControlLinearizer::EffectControlLinearizer(JSGraph* js_graph, | |
20 Schedule* schedule, | |
21 Zone* temp_zone) | |
22 : js_graph_(js_graph), schedule_(schedule), temp_zone_(temp_zone) {} | |
23 | |
24 Graph* EffectControlLinearizer::graph() const { return js_graph_->graph(); } | |
25 CommonOperatorBuilder* EffectControlLinearizer::common() const { | |
26 return js_graph_->common(); | |
27 } | |
28 SimplifiedOperatorBuilder* EffectControlLinearizer::simplified() const { | |
29 return js_graph_->simplified(); | |
30 } | |
31 MachineOperatorBuilder* EffectControlLinearizer::machine() const { | |
32 return js_graph_->machine(); | |
33 } | |
34 | |
35 namespace { | |
36 | |
37 struct BlockEffectData { | |
38 Node* current_effect = nullptr; // New effect. | |
39 }; | |
40 | |
41 // Effect phis that need to be updated after the first pass. | |
42 struct PendingEffectPhi { | |
43 Node* effect_phi; | |
44 BasicBlock* block; | |
45 | |
46 PendingEffectPhi(Node* effect_phi, BasicBlock* block) | |
47 : effect_phi(effect_phi), block(block) {} | |
48 }; | |
49 | |
50 void UpdateEffectPhi(Node* node, BasicBlock* block, | |
51 ZoneVector<BlockEffectData>* block_effects) { | |
52 // Update all inputs to an effect phi with the effects from the given | |
53 // block->effect map. | |
54 DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode()); | |
55 DCHECK_EQ(node->op()->EffectInputCount(), block->PredecessorCount()); | |
56 for (int i = 0; i < node->op()->EffectInputCount(); i++) { | |
57 Node* input = node->InputAt(i); | |
58 BasicBlock* predecessor = block->PredecessorAt(static_cast<size_t>(i)); | |
59 Node* input_effect = | |
60 (*block_effects)[predecessor->rpo_number()].current_effect; | |
61 if (input != input_effect) { | |
62 node->ReplaceInput(i, input_effect); | |
63 } | |
64 } | |
65 } | |
66 | |
67 bool HasIncomingBackEdges(BasicBlock* block) { | |
68 for (BasicBlock* pred : block->predecessors()) { | |
69 if (pred->rpo_number() >= block->rpo_number()) { | |
70 return true; | |
71 } | |
72 } | |
73 return false; | |
74 } | |
75 | |
76 void RemoveRegionNode(Node* node) { | |
77 DCHECK(IrOpcode::kFinishRegion == node->opcode() || | |
78 IrOpcode::kBeginRegion == node->opcode()); | |
79 // Update the value/context uses to the value input of the finish node and | |
80 // the effect uses to the effect input. | |
81 for (Edge edge : node->use_edges()) { | |
82 DCHECK(!edge.from()->IsDead()); | |
83 if (NodeProperties::IsEffectEdge(edge)) { | |
84 edge.UpdateTo(NodeProperties::GetEffectInput(node)); | |
85 } else { | |
86 DCHECK(!NodeProperties::IsControlEdge(edge)); | |
87 DCHECK(!NodeProperties::IsFrameStateEdge(edge)); | |
88 edge.UpdateTo(node->InputAt(0)); | |
89 } | |
90 } | |
91 node->Kill(); | |
92 } | |
93 | |
94 } // namespace | |
95 | |
96 void EffectControlLinearizer::Run() { | |
97 ZoneVector<BlockEffectData> block_effects(temp_zone()); | |
98 ZoneVector<PendingEffectPhi> pending_effect_phis(temp_zone()); | |
99 block_effects.resize(schedule()->RpoBlockCount()); | |
100 NodeVector inputs_buffer(temp_zone()); | |
101 | |
102 for (BasicBlock* block : *(schedule()->rpo_order())) { | |
103 size_t instr = 0; | |
104 | |
105 // The control node should be the first. | |
Benedikt Meurer
2016/04/18 04:34:18
DCHECK this assumption.
Jarin
2016/04/18 07:53:33
Done.
| |
106 Node* control = block->NodeAt(instr); | |
107 instr++; | |
108 | |
109 // Iterate over the phis and update the effect phis. | |
110 Node* effect = nullptr; | |
111 Node* terminate = nullptr; | |
112 for (; instr < block->NodeCount(); instr++) { | |
113 Node* node = block->NodeAt(instr); | |
114 // Only go through the phis and effect phis. | |
115 if (node->opcode() == IrOpcode::kEffectPhi) { | |
116 // There should be at most one effect phi in a block. | |
117 DCHECK_NULL(effect); | |
118 // IfException blocks should not have effect phis. | |
119 DCHECK_NE(IrOpcode::kIfException, control->opcode()); | |
120 effect = node; | |
121 | |
122 // Make sure we update the inputs to the incoming blocks' effects. | |
123 if (HasIncomingBackEdges(block)) { | |
124 // In case of loops, we do not update the effect phi immediately | |
125 // because the back predecessor has not been handled yet. We just | |
126 // record the effect phi for later processing. | |
127 pending_effect_phis.push_back(PendingEffectPhi(node, block)); | |
128 } else { | |
129 UpdateEffectPhi(node, block, &block_effects); | |
130 } | |
131 } else if (node->opcode() == IrOpcode::kPhi) { | |
132 // Just skip phis. | |
133 } else if (node->opcode() == IrOpcode::kTerminate) { | |
134 DCHECK(terminate == nullptr); | |
135 terminate = node; | |
136 } else { | |
137 break; | |
138 } | |
139 } | |
140 | |
141 if (effect == nullptr) { | |
142 // There was no effect phi. | |
143 DCHECK(!HasIncomingBackEdges(block)); | |
144 if (block == schedule()->start()) { | |
145 // Start block => effect is start. | |
146 DCHECK_EQ(graph()->start(), control); | |
147 effect = graph()->start(); | |
148 } else if (control->opcode() == IrOpcode::kEnd) { | |
149 // End block is a just dummy, no effect needed. | |
Benedikt Meurer
2016/04/18 04:34:18
a just dummy -> just a dummy
Jarin
2016/04/18 07:53:33
Done.
| |
150 DCHECK_EQ(BasicBlock::kNone, block->control()); | |
151 DCHECK_EQ(1, block->size()); | |
Benedikt Meurer
2016/04/18 04:34:18
DCHECK_EQ(1u, block->size())
Jarin
2016/04/18 07:53:33
Done.
| |
152 effect = nullptr; | |
153 } else { | |
154 // If all the predecessors have the same effect, we can use it | |
155 // as our current effect. | |
156 int rpo_number = block->PredecessorAt(0)->rpo_number(); | |
157 effect = block_effects[rpo_number].current_effect; | |
158 for (size_t i = 1; i < block->PredecessorCount(); i++) { | |
159 int rpo_number = block->PredecessorAt(i)->rpo_number(); | |
160 if (block_effects[rpo_number].current_effect != effect) { | |
161 effect = nullptr; | |
162 break; | |
163 } | |
164 } | |
165 if (effect == nullptr) { | |
166 DCHECK_NE(IrOpcode::kIfException, control->opcode()); | |
167 // The input blocks do not have the same effect. We have | |
168 // to create an effect phi node. | |
169 inputs_buffer.clear(); | |
170 inputs_buffer.resize(block->PredecessorCount(), graph()->start()); | |
171 inputs_buffer.push_back(control); | |
172 effect = graph()->NewNode( | |
173 common()->EffectPhi(static_cast<int>(block->PredecessorCount())), | |
174 static_cast<int>(inputs_buffer.size()), &(inputs_buffer.front())); | |
175 // Let us update the effect phi node later. | |
176 pending_effect_phis.push_back(PendingEffectPhi(effect, block)); | |
177 } else if (control->opcode() == IrOpcode::kIfException) { | |
178 // The IfException is connected into the effect chain, so we need | |
179 // to process it here. | |
180 ProcessNode(control, &effect, &control); | |
181 } | |
182 } | |
183 } | |
184 | |
185 // Fixup the Terminate node. | |
186 if (terminate != nullptr) { | |
187 NodeProperties::ReplaceEffectInput(terminate, effect); | |
188 } | |
189 | |
190 // Process the ordinary instructions. | |
191 for (; instr < block->NodeCount(); instr++) { | |
192 Node* node = block->NodeAt(instr); | |
193 ProcessNode(node, &effect, &control); | |
194 } | |
195 | |
196 switch (block->control()) { | |
197 case BasicBlock::kGoto: | |
198 case BasicBlock::kNone: | |
199 break; | |
200 | |
201 case BasicBlock::kCall: | |
202 case BasicBlock::kTailCall: | |
203 case BasicBlock::kBranch: | |
204 case BasicBlock::kSwitch: | |
205 case BasicBlock::kReturn: | |
206 case BasicBlock::kDeoptimize: | |
207 case BasicBlock::kThrow: | |
208 ProcessNode(block->control_input(), &effect, &control); | |
209 break; | |
210 } | |
211 | |
212 // Store the effect for later use. | |
213 block_effects[block->rpo_number()].current_effect = effect; | |
214 } | |
215 | |
216 // Update the incoming edges of the effect phis that could not be processed | |
217 // during the first pass (because they could have incoming back edges). | |
218 for (const PendingEffectPhi& pending_effect_phi : pending_effect_phis) { | |
219 UpdateEffectPhi(pending_effect_phi.effect_phi, pending_effect_phi.block, | |
220 &block_effects); | |
221 } | |
222 } | |
223 | |
224 void EffectControlLinearizer::ProcessNode(Node* node, Node** effect, | |
225 Node** control) { | |
226 // If the node needs to be wired into the effect/control chain, do this | |
227 // here. | |
228 if (TryWireInStateEffect(node, effect, control)) { | |
229 return; | |
230 } | |
231 | |
232 // Remove the end markers of 'atomic' allocation region because the | |
233 // region should be wired-in now. | |
234 if (node->opcode() == IrOpcode::kFinishRegion || | |
235 node->opcode() == IrOpcode::kBeginRegion) { | |
236 // Update the value uses to the value input of the finish node and | |
237 // the effect uses to the effect input. | |
238 | |
239 // TODO(jarin) Enable this once we make sure everything with side effects | |
240 // is marked as effectful. | |
241 if (false) { | |
242 return RemoveRegionNode(node); | |
243 } | |
244 } | |
245 | |
246 // If the node takes an effect, replace with the current one. | |
247 if (node->op()->EffectInputCount() > 0) { | |
248 DCHECK_EQ(1, node->op()->EffectInputCount()); | |
249 Node* input_effect = NodeProperties::GetEffectInput(node); | |
250 | |
251 if (input_effect != *effect) { | |
252 NodeProperties::ReplaceEffectInput(node, *effect); | |
253 } | |
254 | |
255 // If the node produces an effect, update our current effect. (However, | |
256 // ignore new effect chains started with ValueEffect.) | |
257 if (node->op()->EffectOutputCount() > 0) { | |
258 DCHECK_EQ(1, node->op()->EffectOutputCount()); | |
259 *effect = node; | |
260 } | |
261 } else { | |
262 // New effect chain is only started with a Start or ValueEffect node. | |
263 DCHECK(node->op()->EffectOutputCount() == 0 || | |
264 node->opcode() == IrOpcode::kStart); | |
265 } | |
266 } | |
267 | |
268 bool EffectControlLinearizer::TryWireInStateEffect(Node* node, Node** effect, | |
269 Node** control) { | |
270 ValueEffectControl state(nullptr, nullptr, nullptr); | |
271 switch (node->opcode()) { | |
272 case IrOpcode::kChangeInt32ToTagged: | |
273 state = LowerChangeInt32ToTagged(node, *effect, *control); | |
274 break; | |
275 case IrOpcode::kChangeUint32ToTagged: | |
276 state = LowerChangeUint32ToTagged(node, *effect, *control); | |
277 break; | |
278 case IrOpcode::kChangeFloat64ToTagged: | |
279 state = LowerChangeFloat64ToTagged(node, *effect, *control); | |
280 break; | |
281 default: | |
282 return false; | |
283 } | |
284 NodeProperties::ReplaceUses(node, state.value); | |
285 *effect = state.effect; | |
286 *control = state.control; | |
287 return true; | |
288 } | |
289 | |
290 EffectControlLinearizer::ValueEffectControl | |
291 EffectControlLinearizer::LowerChangeFloat64ToTagged(Node* node, Node* effect, | |
292 Node* control) { | |
293 Node* value = node->InputAt(0); | |
294 | |
295 Type* const value_type = NodeProperties::GetType(value); | |
296 Node* const value32 = graph()->NewNode( | |
297 machine()->TruncateFloat64ToInt32(TruncationMode::kRoundToZero), value); | |
298 // TODO(bmeurer): This fast case must be disabled until we kill the asm.js | |
299 // support in the generic JavaScript pipeline, because LoadBuffer is lying | |
300 // about its result. | |
301 // if (value_type->Is(Type::Signed32())) { | |
302 // return ChangeInt32ToTagged(value32, control); | |
303 // } | |
304 Node* check_same = graph()->NewNode( | |
305 machine()->Float64Equal(), value, | |
306 graph()->NewNode(machine()->ChangeInt32ToFloat64(), value32)); | |
307 Node* branch_same = graph()->NewNode(common()->Branch(), check_same, control); | |
308 | |
309 Node* if_smi = graph()->NewNode(common()->IfTrue(), branch_same); | |
310 Node* vsmi; | |
311 Node* if_box = graph()->NewNode(common()->IfFalse(), branch_same); | |
312 | |
313 // We only need to check for -0 if the {value} can potentially contain -0. | |
314 if (value_type->Maybe(Type::MinusZero())) { | |
315 Node* check_zero = graph()->NewNode(machine()->Word32Equal(), value32, | |
316 jsgraph()->Int32Constant(0)); | |
317 Node* branch_zero = graph()->NewNode(common()->Branch(BranchHint::kFalse), | |
318 check_zero, if_smi); | |
319 | |
320 Node* if_zero = graph()->NewNode(common()->IfTrue(), branch_zero); | |
321 Node* if_notzero = graph()->NewNode(common()->IfFalse(), branch_zero); | |
322 | |
323 // In case of 0, we need to check the high bits for the IEEE -0 pattern. | |
324 Node* check_negative = graph()->NewNode( | |
325 machine()->Int32LessThan(), | |
326 graph()->NewNode(machine()->Float64ExtractHighWord32(), value), | |
327 jsgraph()->Int32Constant(0)); | |
328 Node* branch_negative = graph()->NewNode( | |
329 common()->Branch(BranchHint::kFalse), check_negative, if_zero); | |
330 | |
331 Node* if_negative = graph()->NewNode(common()->IfTrue(), branch_negative); | |
332 Node* if_notnegative = | |
333 graph()->NewNode(common()->IfFalse(), branch_negative); | |
334 | |
335 // We need to create a box for negative 0. | |
336 if_smi = graph()->NewNode(common()->Merge(2), if_notzero, if_notnegative); | |
337 if_box = graph()->NewNode(common()->Merge(2), if_box, if_negative); | |
338 } | |
339 | |
340 // On 64-bit machines we can just wrap the 32-bit integer in a smi, for 32-bit | |
341 // machines we need to deal with potential overflow and fallback to boxing. | |
342 if (machine()->Is64() || value_type->Is(Type::SignedSmall())) { | |
343 vsmi = ChangeInt32ToSmi(value32); | |
344 } else { | |
345 Node* smi_tag = | |
346 graph()->NewNode(machine()->Int32AddWithOverflow(), value32, value32); | |
347 | |
348 Node* check_ovf = graph()->NewNode(common()->Projection(1), smi_tag); | |
349 Node* branch_ovf = graph()->NewNode(common()->Branch(BranchHint::kFalse), | |
350 check_ovf, if_smi); | |
351 | |
352 Node* if_ovf = graph()->NewNode(common()->IfTrue(), branch_ovf); | |
353 if_box = graph()->NewNode(common()->Merge(2), if_ovf, if_box); | |
354 | |
355 if_smi = graph()->NewNode(common()->IfFalse(), branch_ovf); | |
356 vsmi = graph()->NewNode(common()->Projection(0), smi_tag); | |
357 } | |
358 | |
359 // Allocate the box for the {value}. | |
360 ValueEffectControl box = AllocateHeapNumberWithValue(value, effect, if_box); | |
361 | |
362 control = graph()->NewNode(common()->Merge(2), if_smi, box.control); | |
363 value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | |
364 vsmi, box.value, control); | |
365 effect = | |
366 graph()->NewNode(common()->EffectPhi(2), effect, box.effect, control); | |
367 return ValueEffectControl(value, effect, control); | |
368 } | |
369 | |
370 EffectControlLinearizer::ValueEffectControl | |
371 EffectControlLinearizer::LowerChangeInt32ToTagged(Node* node, Node* effect, | |
372 Node* control) { | |
373 Node* value = node->InputAt(0); | |
374 | |
375 if (machine()->Is64() || | |
376 NodeProperties::GetType(value)->Is(Type::SignedSmall())) { | |
377 return ValueEffectControl(ChangeInt32ToSmi(value), effect, control); | |
378 } | |
379 | |
380 Node* add = graph()->NewNode(machine()->Int32AddWithOverflow(), value, value); | |
381 | |
382 Node* ovf = graph()->NewNode(common()->Projection(1), add); | |
383 Node* branch = | |
384 graph()->NewNode(common()->Branch(BranchHint::kFalse), ovf, control); | |
385 | |
386 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); | |
387 ValueEffectControl alloc = | |
388 AllocateHeapNumberWithValue(ChangeInt32ToFloat64(value), effect, if_true); | |
389 | |
390 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); | |
391 Node* vfalse = graph()->NewNode(common()->Projection(0), add); | |
392 | |
393 Node* merge = graph()->NewNode(common()->Merge(2), alloc.control, if_false); | |
394 Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | |
395 alloc.value, vfalse, merge); | |
396 Node* ephi = | |
397 graph()->NewNode(common()->EffectPhi(2), alloc.effect, effect, merge); | |
398 | |
399 return ValueEffectControl(phi, ephi, merge); | |
400 } | |
401 | |
402 EffectControlLinearizer::ValueEffectControl | |
403 EffectControlLinearizer::LowerChangeUint32ToTagged(Node* node, Node* effect, | |
404 Node* control) { | |
405 Node* value = node->InputAt(0); | |
406 | |
407 if (NodeProperties::GetType(value)->Is(Type::UnsignedSmall())) { | |
408 return ValueEffectControl(ChangeUint32ToSmi(value), effect, control); | |
409 } | |
410 | |
411 Node* check = graph()->NewNode(machine()->Uint32LessThanOrEqual(), value, | |
412 SmiMaxValueConstant()); | |
413 Node* branch = | |
414 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); | |
415 | |
416 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); | |
417 Node* vtrue = ChangeUint32ToSmi(value); | |
418 | |
419 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); | |
420 ValueEffectControl alloc = AllocateHeapNumberWithValue( | |
421 ChangeUint32ToFloat64(value), effect, if_false); | |
422 | |
423 Node* merge = graph()->NewNode(common()->Merge(2), if_true, alloc.control); | |
424 Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | |
425 vtrue, alloc.value, merge); | |
426 Node* ephi = | |
427 graph()->NewNode(common()->EffectPhi(2), effect, alloc.effect, merge); | |
428 | |
429 return ValueEffectControl(phi, ephi, merge); | |
430 } | |
431 | |
432 EffectControlLinearizer::ValueEffectControl | |
433 EffectControlLinearizer::AllocateHeapNumberWithValue(Node* value, Node* effect, | |
434 Node* control) { | |
435 // The AllocateHeapNumberStub does not use the context, so we can safely pass | |
436 // in Smi zero here. | |
437 Callable callable = CodeFactory::AllocateHeapNumber(jsgraph()->isolate()); | |
438 Node* target = jsgraph()->HeapConstant(callable.code()); | |
439 Node* context = jsgraph()->NoContextConstant(); | |
440 if (!allocate_heap_number_operator_.is_set()) { | |
441 CallDescriptor* descriptor = Linkage::GetStubCallDescriptor( | |
442 jsgraph()->isolate(), jsgraph()->zone(), callable.descriptor(), 0, | |
443 CallDescriptor::kNoFlags, Operator::kNoThrow); | |
444 allocate_heap_number_operator_.set(common()->Call(descriptor)); | |
445 } | |
446 Node* heap_number = graph()->NewNode(allocate_heap_number_operator_.get(), | |
447 target, context, effect, control); | |
448 Node* store = graph()->NewNode( | |
449 machine()->Store(StoreRepresentation(MachineRepresentation::kFloat64, | |
450 kNoWriteBarrier)), | |
451 heap_number, HeapNumberValueIndexConstant(), value, heap_number, control); | |
452 return ValueEffectControl(heap_number, store, control); | |
453 } | |
454 | |
455 Node* EffectControlLinearizer::ChangeInt32ToSmi(Node* value) { | |
456 if (machine()->Is64()) { | |
457 value = graph()->NewNode(machine()->ChangeInt32ToInt64(), value); | |
458 } | |
459 return graph()->NewNode(machine()->WordShl(), value, SmiShiftBitsConstant()); | |
460 } | |
461 | |
462 Node* EffectControlLinearizer::ChangeUint32ToSmi(Node* value) { | |
463 if (machine()->Is64()) { | |
464 value = graph()->NewNode(machine()->ChangeUint32ToUint64(), value); | |
465 } | |
466 return graph()->NewNode(machine()->WordShl(), value, SmiShiftBitsConstant()); | |
467 } | |
468 | |
469 Node* EffectControlLinearizer::ChangeInt32ToFloat64(Node* value) { | |
470 return graph()->NewNode(machine()->ChangeInt32ToFloat64(), value); | |
471 } | |
472 | |
473 Node* EffectControlLinearizer::ChangeUint32ToFloat64(Node* value) { | |
474 return graph()->NewNode(machine()->ChangeUint32ToFloat64(), value); | |
475 } | |
476 | |
477 Node* EffectControlLinearizer::HeapNumberValueIndexConstant() { | |
478 return jsgraph()->IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag); | |
479 } | |
480 | |
481 Node* EffectControlLinearizer::SmiMaxValueConstant() { | |
482 return jsgraph()->Int32Constant(Smi::kMaxValue); | |
483 } | |
484 | |
485 Node* EffectControlLinearizer::SmiShiftBitsConstant() { | |
486 return jsgraph()->IntPtrConstant(kSmiShiftSize + kSmiTagSize); | |
487 } | |
488 | |
489 } // namespace compiler | |
490 } // namespace internal | |
491 } // namespace v8 | |
OLD | NEW |