| 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
|
|
|