Index: src/compiler/effect-control-linearizer.cc |
diff --git a/src/compiler/effect-control-linearizer.cc b/src/compiler/effect-control-linearizer.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e3d0498087f9c20c44bd6a2d6b0d71f34e3f1966 |
--- /dev/null |
+++ b/src/compiler/effect-control-linearizer.cc |
@@ -0,0 +1,492 @@ |
+// Copyright 2015 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/compiler/effect-control-linearizer.h" |
+ |
+#include "src/code-factory.h" |
+#include "src/compiler/access-builder.h" |
+#include "src/compiler/js-graph.h" |
+#include "src/compiler/linkage.h" |
+#include "src/compiler/node-properties.h" |
+#include "src/compiler/node.h" |
+#include "src/compiler/schedule.h" |
+ |
+namespace v8 { |
+namespace internal { |
+namespace compiler { |
+ |
+EffectControlLinearizer::EffectControlLinearizer(JSGraph* js_graph, |
+ Schedule* schedule, |
+ Zone* temp_zone) |
+ : js_graph_(js_graph), schedule_(schedule), temp_zone_(temp_zone) {} |
+ |
+Graph* EffectControlLinearizer::graph() const { return js_graph_->graph(); } |
+CommonOperatorBuilder* EffectControlLinearizer::common() const { |
+ return js_graph_->common(); |
+} |
+SimplifiedOperatorBuilder* EffectControlLinearizer::simplified() const { |
+ return js_graph_->simplified(); |
+} |
+MachineOperatorBuilder* EffectControlLinearizer::machine() const { |
+ return js_graph_->machine(); |
+} |
+ |
+namespace { |
+ |
+struct BlockEffectData { |
+ Node* current_effect = nullptr; // New effect. |
+}; |
+ |
+// Effect phis that need to be updated after the first pass. |
+struct PendingEffectPhi { |
+ Node* effect_phi; |
+ BasicBlock* block; |
+ |
+ PendingEffectPhi(Node* effect_phi, BasicBlock* block) |
+ : effect_phi(effect_phi), block(block) {} |
+}; |
+ |
+void UpdateEffectPhi(Node* node, BasicBlock* block, |
+ ZoneVector<BlockEffectData>* block_effects) { |
+ // Update all inputs to an effect phi with the effects from the given |
+ // block->effect map. |
+ DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode()); |
+ DCHECK_EQ(node->op()->EffectInputCount(), block->PredecessorCount()); |
+ for (int i = 0; i < node->op()->EffectInputCount(); i++) { |
+ Node* input = node->InputAt(i); |
+ BasicBlock* predecessor = block->PredecessorAt(static_cast<size_t>(i)); |
+ Node* input_effect = |
+ (*block_effects)[predecessor->rpo_number()].current_effect; |
+ if (input != input_effect) { |
+ node->ReplaceInput(i, input_effect); |
+ } |
+ } |
+} |
+ |
+bool HasIncomingBackEdges(BasicBlock* block) { |
+ for (BasicBlock* pred : block->predecessors()) { |
+ if (pred->rpo_number() >= block->rpo_number()) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+void RemoveRegionNode(Node* node) { |
+ DCHECK(IrOpcode::kFinishRegion == node->opcode() || |
+ IrOpcode::kBeginRegion == node->opcode()); |
+ // Update the value/context uses to the value input of the finish node and |
+ // the effect uses to the effect input. |
+ for (Edge edge : node->use_edges()) { |
+ DCHECK(!edge.from()->IsDead()); |
+ if (NodeProperties::IsEffectEdge(edge)) { |
+ edge.UpdateTo(NodeProperties::GetEffectInput(node)); |
+ } else { |
+ DCHECK(!NodeProperties::IsControlEdge(edge)); |
+ DCHECK(!NodeProperties::IsFrameStateEdge(edge)); |
+ edge.UpdateTo(node->InputAt(0)); |
+ } |
+ } |
+ node->Kill(); |
+} |
+ |
+} // namespace |
+ |
+void EffectControlLinearizer::Run() { |
+ ZoneVector<BlockEffectData> block_effects(temp_zone()); |
+ ZoneVector<PendingEffectPhi> pending_effect_phis(temp_zone()); |
+ block_effects.resize(schedule()->RpoBlockCount()); |
+ NodeVector inputs_buffer(temp_zone()); |
+ |
+ for (BasicBlock* block : *(schedule()->rpo_order())) { |
+ size_t instr = 0; |
+ |
+ // The control node should be the first. |
+ Node* control = block->NodeAt(instr); |
+ DCHECK(NodeProperties::IsControl(control)); |
+ instr++; |
+ |
+ // Iterate over the phis and update the effect phis. |
+ Node* effect = nullptr; |
+ Node* terminate = nullptr; |
+ for (; instr < block->NodeCount(); instr++) { |
+ Node* node = block->NodeAt(instr); |
+ // Only go through the phis and effect phis. |
+ if (node->opcode() == IrOpcode::kEffectPhi) { |
+ // There should be at most one effect phi in a block. |
+ DCHECK_NULL(effect); |
+ // IfException blocks should not have effect phis. |
+ DCHECK_NE(IrOpcode::kIfException, control->opcode()); |
+ effect = node; |
+ |
+ // Make sure we update the inputs to the incoming blocks' effects. |
+ if (HasIncomingBackEdges(block)) { |
+ // In case of loops, we do not update the effect phi immediately |
+ // because the back predecessor has not been handled yet. We just |
+ // record the effect phi for later processing. |
+ pending_effect_phis.push_back(PendingEffectPhi(node, block)); |
+ } else { |
+ UpdateEffectPhi(node, block, &block_effects); |
+ } |
+ } else if (node->opcode() == IrOpcode::kPhi) { |
+ // Just skip phis. |
+ } else if (node->opcode() == IrOpcode::kTerminate) { |
+ DCHECK(terminate == nullptr); |
+ terminate = node; |
+ } else { |
+ break; |
+ } |
+ } |
+ |
+ if (effect == nullptr) { |
+ // There was no effect phi. |
+ DCHECK(!HasIncomingBackEdges(block)); |
+ if (block == schedule()->start()) { |
+ // Start block => effect is start. |
+ DCHECK_EQ(graph()->start(), control); |
+ effect = graph()->start(); |
+ } else if (control->opcode() == IrOpcode::kEnd) { |
+ // End block is just a dummy, no effect needed. |
+ DCHECK_EQ(BasicBlock::kNone, block->control()); |
+ DCHECK_EQ(1u, block->size()); |
+ effect = nullptr; |
+ } else { |
+ // If all the predecessors have the same effect, we can use it |
+ // as our current effect. |
+ int rpo_number = block->PredecessorAt(0)->rpo_number(); |
+ effect = block_effects[rpo_number].current_effect; |
+ for (size_t i = 1; i < block->PredecessorCount(); i++) { |
+ int rpo_number = block->PredecessorAt(i)->rpo_number(); |
+ if (block_effects[rpo_number].current_effect != effect) { |
+ effect = nullptr; |
+ break; |
+ } |
+ } |
+ if (effect == nullptr) { |
+ DCHECK_NE(IrOpcode::kIfException, control->opcode()); |
+ // The input blocks do not have the same effect. We have |
+ // to create an effect phi node. |
+ inputs_buffer.clear(); |
+ inputs_buffer.resize(block->PredecessorCount(), graph()->start()); |
+ inputs_buffer.push_back(control); |
+ effect = graph()->NewNode( |
+ common()->EffectPhi(static_cast<int>(block->PredecessorCount())), |
+ static_cast<int>(inputs_buffer.size()), &(inputs_buffer.front())); |
+ // Let us update the effect phi node later. |
+ pending_effect_phis.push_back(PendingEffectPhi(effect, block)); |
+ } else if (control->opcode() == IrOpcode::kIfException) { |
+ // The IfException is connected into the effect chain, so we need |
+ // to process it here. |
+ ProcessNode(control, &effect, &control); |
+ } |
+ } |
+ } |
+ |
+ // Fixup the Terminate node. |
+ if (terminate != nullptr) { |
+ NodeProperties::ReplaceEffectInput(terminate, effect); |
+ } |
+ |
+ // Process the ordinary instructions. |
+ for (; instr < block->NodeCount(); instr++) { |
+ Node* node = block->NodeAt(instr); |
+ ProcessNode(node, &effect, &control); |
+ } |
+ |
+ switch (block->control()) { |
+ case BasicBlock::kGoto: |
+ case BasicBlock::kNone: |
+ break; |
+ |
+ case BasicBlock::kCall: |
+ case BasicBlock::kTailCall: |
+ case BasicBlock::kBranch: |
+ case BasicBlock::kSwitch: |
+ case BasicBlock::kReturn: |
+ case BasicBlock::kDeoptimize: |
+ case BasicBlock::kThrow: |
+ ProcessNode(block->control_input(), &effect, &control); |
+ break; |
+ } |
+ |
+ // Store the effect for later use. |
+ block_effects[block->rpo_number()].current_effect = effect; |
+ } |
+ |
+ // Update the incoming edges of the effect phis that could not be processed |
+ // during the first pass (because they could have incoming back edges). |
+ for (const PendingEffectPhi& pending_effect_phi : pending_effect_phis) { |
+ UpdateEffectPhi(pending_effect_phi.effect_phi, pending_effect_phi.block, |
+ &block_effects); |
+ } |
+} |
+ |
+void EffectControlLinearizer::ProcessNode(Node* node, Node** effect, |
+ Node** control) { |
+ // If the node needs to be wired into the effect/control chain, do this |
+ // here. |
+ if (TryWireInStateEffect(node, effect, control)) { |
+ return; |
+ } |
+ |
+ // Remove the end markers of 'atomic' allocation region because the |
+ // region should be wired-in now. |
+ if (node->opcode() == IrOpcode::kFinishRegion || |
+ node->opcode() == IrOpcode::kBeginRegion) { |
+ // Update the value uses to the value input of the finish node and |
+ // the effect uses to the effect input. |
+ |
+ // TODO(jarin) Enable this once we make sure everything with side effects |
+ // is marked as effectful. |
+ if (false) { |
+ return RemoveRegionNode(node); |
+ } |
+ } |
+ |
+ // If the node takes an effect, replace with the current one. |
+ if (node->op()->EffectInputCount() > 0) { |
+ DCHECK_EQ(1, node->op()->EffectInputCount()); |
+ Node* input_effect = NodeProperties::GetEffectInput(node); |
+ |
+ if (input_effect != *effect) { |
+ NodeProperties::ReplaceEffectInput(node, *effect); |
+ } |
+ |
+ // If the node produces an effect, update our current effect. (However, |
+ // ignore new effect chains started with ValueEffect.) |
+ if (node->op()->EffectOutputCount() > 0) { |
+ DCHECK_EQ(1, node->op()->EffectOutputCount()); |
+ *effect = node; |
+ } |
+ } else { |
+ // New effect chain is only started with a Start or ValueEffect node. |
+ DCHECK(node->op()->EffectOutputCount() == 0 || |
+ node->opcode() == IrOpcode::kStart); |
+ } |
+} |
+ |
+bool EffectControlLinearizer::TryWireInStateEffect(Node* node, Node** effect, |
+ Node** control) { |
+ ValueEffectControl state(nullptr, nullptr, nullptr); |
+ switch (node->opcode()) { |
+ case IrOpcode::kChangeInt32ToTagged: |
+ state = LowerChangeInt32ToTagged(node, *effect, *control); |
+ break; |
+ case IrOpcode::kChangeUint32ToTagged: |
+ state = LowerChangeUint32ToTagged(node, *effect, *control); |
+ break; |
+ case IrOpcode::kChangeFloat64ToTagged: |
+ state = LowerChangeFloat64ToTagged(node, *effect, *control); |
+ break; |
+ default: |
+ return false; |
+ } |
+ NodeProperties::ReplaceUses(node, state.value); |
+ *effect = state.effect; |
+ *control = state.control; |
+ return true; |
+} |
+ |
+EffectControlLinearizer::ValueEffectControl |
+EffectControlLinearizer::LowerChangeFloat64ToTagged(Node* node, Node* effect, |
+ Node* control) { |
+ Node* value = node->InputAt(0); |
+ |
+ Type* const value_type = NodeProperties::GetType(value); |
+ Node* const value32 = graph()->NewNode( |
+ machine()->TruncateFloat64ToInt32(TruncationMode::kRoundToZero), value); |
+ // TODO(bmeurer): This fast case must be disabled until we kill the asm.js |
+ // support in the generic JavaScript pipeline, because LoadBuffer is lying |
+ // about its result. |
+ // if (value_type->Is(Type::Signed32())) { |
+ // return ChangeInt32ToTagged(value32, control); |
+ // } |
+ Node* check_same = graph()->NewNode( |
+ machine()->Float64Equal(), value, |
+ graph()->NewNode(machine()->ChangeInt32ToFloat64(), value32)); |
+ Node* branch_same = graph()->NewNode(common()->Branch(), check_same, control); |
+ |
+ Node* if_smi = graph()->NewNode(common()->IfTrue(), branch_same); |
+ Node* vsmi; |
+ Node* if_box = graph()->NewNode(common()->IfFalse(), branch_same); |
+ |
+ // We only need to check for -0 if the {value} can potentially contain -0. |
+ if (value_type->Maybe(Type::MinusZero())) { |
+ Node* check_zero = graph()->NewNode(machine()->Word32Equal(), value32, |
+ jsgraph()->Int32Constant(0)); |
+ Node* branch_zero = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
+ check_zero, if_smi); |
+ |
+ Node* if_zero = graph()->NewNode(common()->IfTrue(), branch_zero); |
+ Node* if_notzero = graph()->NewNode(common()->IfFalse(), branch_zero); |
+ |
+ // In case of 0, we need to check the high bits for the IEEE -0 pattern. |
+ Node* check_negative = graph()->NewNode( |
+ machine()->Int32LessThan(), |
+ graph()->NewNode(machine()->Float64ExtractHighWord32(), value), |
+ jsgraph()->Int32Constant(0)); |
+ Node* branch_negative = graph()->NewNode( |
+ common()->Branch(BranchHint::kFalse), check_negative, if_zero); |
+ |
+ Node* if_negative = graph()->NewNode(common()->IfTrue(), branch_negative); |
+ Node* if_notnegative = |
+ graph()->NewNode(common()->IfFalse(), branch_negative); |
+ |
+ // We need to create a box for negative 0. |
+ if_smi = graph()->NewNode(common()->Merge(2), if_notzero, if_notnegative); |
+ if_box = graph()->NewNode(common()->Merge(2), if_box, if_negative); |
+ } |
+ |
+ // On 64-bit machines we can just wrap the 32-bit integer in a smi, for 32-bit |
+ // machines we need to deal with potential overflow and fallback to boxing. |
+ if (machine()->Is64() || value_type->Is(Type::SignedSmall())) { |
+ vsmi = ChangeInt32ToSmi(value32); |
+ } else { |
+ Node* smi_tag = |
+ graph()->NewNode(machine()->Int32AddWithOverflow(), value32, value32); |
+ |
+ Node* check_ovf = graph()->NewNode(common()->Projection(1), smi_tag); |
+ Node* branch_ovf = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
+ check_ovf, if_smi); |
+ |
+ Node* if_ovf = graph()->NewNode(common()->IfTrue(), branch_ovf); |
+ if_box = graph()->NewNode(common()->Merge(2), if_ovf, if_box); |
+ |
+ if_smi = graph()->NewNode(common()->IfFalse(), branch_ovf); |
+ vsmi = graph()->NewNode(common()->Projection(0), smi_tag); |
+ } |
+ |
+ // Allocate the box for the {value}. |
+ ValueEffectControl box = AllocateHeapNumberWithValue(value, effect, if_box); |
+ |
+ control = graph()->NewNode(common()->Merge(2), if_smi, box.control); |
+ value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
+ vsmi, box.value, control); |
+ effect = |
+ graph()->NewNode(common()->EffectPhi(2), effect, box.effect, control); |
+ return ValueEffectControl(value, effect, control); |
+} |
+ |
+EffectControlLinearizer::ValueEffectControl |
+EffectControlLinearizer::LowerChangeInt32ToTagged(Node* node, Node* effect, |
+ Node* control) { |
+ Node* value = node->InputAt(0); |
+ |
+ if (machine()->Is64() || |
+ NodeProperties::GetType(value)->Is(Type::SignedSmall())) { |
+ return ValueEffectControl(ChangeInt32ToSmi(value), effect, control); |
+ } |
+ |
+ Node* add = graph()->NewNode(machine()->Int32AddWithOverflow(), value, value); |
+ |
+ Node* ovf = graph()->NewNode(common()->Projection(1), add); |
+ Node* branch = |
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), ovf, control); |
+ |
+ Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
+ ValueEffectControl alloc = |
+ AllocateHeapNumberWithValue(ChangeInt32ToFloat64(value), effect, if_true); |
+ |
+ Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
+ Node* vfalse = graph()->NewNode(common()->Projection(0), add); |
+ |
+ Node* merge = graph()->NewNode(common()->Merge(2), alloc.control, if_false); |
+ Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
+ alloc.value, vfalse, merge); |
+ Node* ephi = |
+ graph()->NewNode(common()->EffectPhi(2), alloc.effect, effect, merge); |
+ |
+ return ValueEffectControl(phi, ephi, merge); |
+} |
+ |
+EffectControlLinearizer::ValueEffectControl |
+EffectControlLinearizer::LowerChangeUint32ToTagged(Node* node, Node* effect, |
+ Node* control) { |
+ Node* value = node->InputAt(0); |
+ |
+ if (NodeProperties::GetType(value)->Is(Type::UnsignedSmall())) { |
+ return ValueEffectControl(ChangeUint32ToSmi(value), effect, control); |
+ } |
+ |
+ Node* check = graph()->NewNode(machine()->Uint32LessThanOrEqual(), value, |
+ SmiMaxValueConstant()); |
+ Node* branch = |
+ graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
+ |
+ Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
+ Node* vtrue = ChangeUint32ToSmi(value); |
+ |
+ Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
+ ValueEffectControl alloc = AllocateHeapNumberWithValue( |
+ ChangeUint32ToFloat64(value), effect, if_false); |
+ |
+ Node* merge = graph()->NewNode(common()->Merge(2), if_true, alloc.control); |
+ Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
+ vtrue, alloc.value, merge); |
+ Node* ephi = |
+ graph()->NewNode(common()->EffectPhi(2), effect, alloc.effect, merge); |
+ |
+ return ValueEffectControl(phi, ephi, merge); |
+} |
+ |
+EffectControlLinearizer::ValueEffectControl |
+EffectControlLinearizer::AllocateHeapNumberWithValue(Node* value, Node* effect, |
+ Node* control) { |
+ // The AllocateHeapNumberStub does not use the context, so we can safely pass |
+ // in Smi zero here. |
+ Callable callable = CodeFactory::AllocateHeapNumber(jsgraph()->isolate()); |
+ Node* target = jsgraph()->HeapConstant(callable.code()); |
+ Node* context = jsgraph()->NoContextConstant(); |
+ if (!allocate_heap_number_operator_.is_set()) { |
+ CallDescriptor* descriptor = Linkage::GetStubCallDescriptor( |
+ jsgraph()->isolate(), jsgraph()->zone(), callable.descriptor(), 0, |
+ CallDescriptor::kNoFlags, Operator::kNoThrow); |
+ allocate_heap_number_operator_.set(common()->Call(descriptor)); |
+ } |
+ Node* heap_number = graph()->NewNode(allocate_heap_number_operator_.get(), |
+ target, context, effect, control); |
+ Node* store = graph()->NewNode( |
+ machine()->Store(StoreRepresentation(MachineRepresentation::kFloat64, |
+ kNoWriteBarrier)), |
+ heap_number, HeapNumberValueIndexConstant(), value, heap_number, control); |
+ return ValueEffectControl(heap_number, store, control); |
+} |
+ |
+Node* EffectControlLinearizer::ChangeInt32ToSmi(Node* value) { |
+ if (machine()->Is64()) { |
+ value = graph()->NewNode(machine()->ChangeInt32ToInt64(), value); |
+ } |
+ return graph()->NewNode(machine()->WordShl(), value, SmiShiftBitsConstant()); |
+} |
+ |
+Node* EffectControlLinearizer::ChangeUint32ToSmi(Node* value) { |
+ if (machine()->Is64()) { |
+ value = graph()->NewNode(machine()->ChangeUint32ToUint64(), value); |
+ } |
+ return graph()->NewNode(machine()->WordShl(), value, SmiShiftBitsConstant()); |
+} |
+ |
+Node* EffectControlLinearizer::ChangeInt32ToFloat64(Node* value) { |
+ return graph()->NewNode(machine()->ChangeInt32ToFloat64(), value); |
+} |
+ |
+Node* EffectControlLinearizer::ChangeUint32ToFloat64(Node* value) { |
+ return graph()->NewNode(machine()->ChangeUint32ToFloat64(), value); |
+} |
+ |
+Node* EffectControlLinearizer::HeapNumberValueIndexConstant() { |
+ return jsgraph()->IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag); |
+} |
+ |
+Node* EffectControlLinearizer::SmiMaxValueConstant() { |
+ return jsgraph()->Int32Constant(Smi::kMaxValue); |
+} |
+ |
+Node* EffectControlLinearizer::SmiShiftBitsConstant() { |
+ return jsgraph()->IntPtrConstant(kSmiShiftSize + kSmiTagSize); |
+} |
+ |
+} // namespace compiler |
+} // namespace internal |
+} // namespace v8 |