Commit b9e287c6 authored by jarin's avatar jarin Committed by Commit bot

[turbofan] Effect linearization after representation inference.

This introduces a compiler pass that schedules the graph and re-wires effect chain according to the schedule. It also connects allocating representation changes to the effect chain, and removes the BeginRegion and EndRegion nodes - they should not be needed anymore because all effectful nodes should be already wired-in.

This is an intermediate CL - the next step is to move lowering of the Change*ToTaggedEffect nodes to StateEffectIntroduction so that we do not have to introduce the effectful versions of nodes.

Review URL: https://codereview.chromium.org/1849603002

Cr-Commit-Position: refs/heads/master@{#35565}
parent 0b638f0a
......@@ -812,6 +812,8 @@ source_set("v8_base") {
"src/compiler/dead-code-elimination.cc",
"src/compiler/dead-code-elimination.h",
"src/compiler/diamond.h",
"src/compiler/effect-control-linearizer.cc",
"src/compiler/effect-control-linearizer.h",
"src/compiler/escape-analysis-reducer.cc",
"src/compiler/escape-analysis-reducer.h",
"src/compiler/escape-analysis.cc",
......
......@@ -158,7 +158,8 @@ class CompilationInfo {
kSplittingEnabled = 1 << 13,
kDeoptimizationEnabled = 1 << 14,
kSourcePositionsEnabled = 1 << 15,
kBailoutOnUninitialized = 1 << 16,
kEffectSchedulingEnabled = 1 << 16,
kBailoutOnUninitialized = 1 << 17,
};
CompilationInfo(ParseInfo* parse_info, Handle<JSFunction> closure);
......@@ -277,6 +278,12 @@ class CompilationInfo {
return GetFlag(kDeoptimizationEnabled);
}
void MarkAsEffectSchedulingEnabled() { SetFlag(kEffectSchedulingEnabled); }
bool is_effect_scheduling_enabled() const {
return GetFlag(kEffectSchedulingEnabled);
}
void MarkAsSourcePositionsEnabled() { SetFlag(kSourcePositionsEnabled); }
bool is_source_positions_enabled() const {
......
This diff is collapsed.
// Copyright 2016 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.
#ifndef V8_COMPILER_EFFECT_CONTROL_LINEARIZER_H_
#define V8_COMPILER_EFFECT_CONTROL_LINEARIZER_H_
#include "src/compiler/common-operator.h"
#include "src/compiler/node.h"
#include "src/compiler/simplified-operator.h"
namespace v8 {
namespace internal {
class Zone;
namespace compiler {
class CommonOperatorBuilder;
class SimplifiedOperatorBuilder;
class MachineOperatorBuilder;
class JSGraph;
class Graph;
class Schedule;
class EffectControlLinearizer {
public:
EffectControlLinearizer(JSGraph* graph, Schedule* schedule, Zone* temp_zone);
void Run();
private:
void ProcessNode(Node* node, Node** current_effect, Node** control);
struct ValueEffectControl {
Node* value;
Node* effect;
Node* control;
ValueEffectControl(Node* value, Node* effect, Node* control)
: value(value), effect(effect), control(control) {}
};
bool TryWireInStateEffect(Node* node, Node** effect, Node** control);
ValueEffectControl LowerChangeInt32ToTagged(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerChangeUint32ToTagged(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerChangeFloat64ToTagged(Node* node, Node* effect,
Node* control);
ValueEffectControl AllocateHeapNumberWithValue(Node* node, Node* effect,
Node* control);
Node* ChangeInt32ToSmi(Node* value);
Node* ChangeUint32ToSmi(Node* value);
Node* ChangeInt32ToFloat64(Node* value);
Node* ChangeUint32ToFloat64(Node* value);
Node* HeapNumberValueIndexConstant();
Node* SmiMaxValueConstant();
Node* SmiShiftBitsConstant();
JSGraph* jsgraph() const { return js_graph_; }
Graph* graph() const;
Schedule* schedule() const { return schedule_; }
Zone* temp_zone() const { return temp_zone_; }
CommonOperatorBuilder* common() const;
SimplifiedOperatorBuilder* simplified() const;
MachineOperatorBuilder* machine() const;
JSGraph* js_graph_;
Schedule* schedule_;
Zone* temp_zone_;
SetOncePointer<const Operator> allocate_heap_number_operator_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_EFFECT_CONTROL_LINEARIZER_H_
......@@ -222,7 +222,11 @@ void GraphReducer::ReplaceWithValue(Node* node, Node* value, Node* effect,
edge.UpdateTo(dead_);
Revisit(user);
} else {
UNREACHABLE();
DCHECK_NOT_NULL(control);
edge.UpdateTo(control);
Revisit(user);
// TODO(jarin) Check that the node cannot throw (otherwise, it
// would have to be connected via IfSuccess/IfException).
}
} else if (NodeProperties::IsEffectEdge(edge)) {
DCHECK_NOT_NULL(effect);
......
......@@ -19,6 +19,7 @@
#include "src/compiler/common-operator-reducer.h"
#include "src/compiler/control-flow-optimizer.h"
#include "src/compiler/dead-code-elimination.h"
#include "src/compiler/effect-control-linearizer.h"
#include "src/compiler/escape-analysis-reducer.h"
#include "src/compiler/escape-analysis.h"
#include "src/compiler/frame-elider.h"
......@@ -225,6 +226,7 @@ class PipelineData {
DCHECK(!schedule_);
schedule_ = schedule;
}
void reset_schedule() { schedule_ = nullptr; }
Zone* instruction_zone() const { return instruction_zone_; }
InstructionSequence* sequence() const { return sequence_; }
......@@ -503,6 +505,9 @@ PipelineCompilationJob::Status PipelineCompilationJob::CreateGraphImpl() {
if (!info()->shared_info()->asm_function() || FLAG_turbo_asm_deoptimization) {
info()->MarkAsDeoptimizationEnabled();
}
if (!info()->shared_info()->asm_function()) {
info()->MarkAsEffectSchedulingEnabled();
}
if (!info()->shared_info()->HasBytecodeArray()) {
if (!Compiler::EnsureDeoptimizationSupport(info())) return FAILED;
......@@ -799,6 +804,35 @@ struct ControlFlowOptimizationPhase {
}
};
struct EffectControlLinearizationPhase {
static const char* phase_name() { return "effect linearization"; }
void Run(PipelineData* data, Zone* temp_zone) {
// The scheduler requires the graphs to be trimmed, so trim now.
// TODO(jarin) Remove the trimming once the scheduler can handle untrimmed
// graphs.
GraphTrimmer trimmer(temp_zone, data->graph());
NodeVector roots(temp_zone);
data->jsgraph()->GetCachedNodes(&roots);
trimmer.TrimGraph(roots.begin(), roots.end());
// Schedule the graph without node splitting so that we can
// fix the effect and control flow for nodes with low-level side
// effects (such as changing representation to tagged or
// 'floating' allocation regions.)
Schedule* schedule = Scheduler::ComputeSchedule(temp_zone, data->graph(),
Scheduler::kNoFlags);
if (FLAG_turbo_verify) ScheduleVerifier::Run(schedule);
// Post-pass for wiring the control/effects
// - connect allocating representation changes into the control&effect
// chains and lower them,
// - get rid of the region markers,
// - introduce effect phis and rewire effects to get SSA again.
EffectControlLinearizer introducer(data->jsgraph(), schedule, temp_zone);
introducer.Run();
}
};
struct ChangeLoweringPhase {
static const char* phase_name() { return "change lowering"; }
......@@ -823,6 +857,26 @@ struct ChangeLoweringPhase {
}
};
struct ComputeEffectSchedulePhase {
static const char* phase_name() { return "effect scheduling"; }
void Run(PipelineData* data, Zone* temp_zone) {
Schedule* schedule = Scheduler::ComputeSchedule(temp_zone, data->graph(),
Scheduler::kNoFlags);
if (FLAG_turbo_verify) ScheduleVerifier::Run(schedule);
data->set_schedule(schedule);
}
};
struct EffectScheduleTrimmingPhase {
static const char* phase_name() { return "effect schedule graph trimming"; }
void Run(PipelineData* data, Zone* temp_zone) {
GraphTrimmer trimmer(temp_zone, data->graph());
NodeVector roots(temp_zone);
data->jsgraph()->GetCachedNodes(&roots);
trimmer.TrimGraph(roots.begin(), roots.end());
}
};
struct EarlyGraphTrimmingPhase {
static const char* phase_name() { return "early graph trimming"; }
......@@ -1251,6 +1305,12 @@ Handle<Code> Pipeline::GenerateCode() {
Run<SimplifiedLoweringPhase>();
RunPrintAndVerify("Lowered simplified");
if (info()->is_effect_scheduling_enabled()) {
// TODO(jarin) Run value numbering for the representation changes.
Run<EffectControlLinearizationPhase>();
RunPrintAndVerify("Effect and control linearized");
}
Run<BranchEliminationPhase>();
RunPrintAndVerify("Branch conditions eliminated");
......
......@@ -199,11 +199,28 @@ void Schedule::AddGoto(BasicBlock* block, BasicBlock* succ) {
AddSuccessor(block, succ);
}
#if DEBUG
namespace {
bool IsPotentiallyThrowingCall(IrOpcode::Value opcode) {
switch (opcode) {
#define BUILD_BLOCK_JS_CASE(Name) case IrOpcode::k##Name:
JS_OP_LIST(BUILD_BLOCK_JS_CASE)
#undef BUILD_BLOCK_JS_CASE
case IrOpcode::kCall:
return true;
default:
return false;
}
}
} // namespace
#endif // DEBUG
void Schedule::AddCall(BasicBlock* block, Node* call, BasicBlock* success_block,
BasicBlock* exception_block) {
DCHECK_EQ(BasicBlock::kNone, block->control());
DCHECK_EQ(IrOpcode::kCall, call->opcode());
DCHECK(IsPotentiallyThrowingCall(call->opcode()));
block->set_control(BasicBlock::kCall);
AddSuccessor(block, success_block);
AddSuccessor(block, exception_block);
......
......@@ -324,6 +324,10 @@ class CFGBuilder : public ZoneObject {
case IrOpcode::kSwitch:
BuildBlocksForSuccessors(node);
break;
#define BUILD_BLOCK_JS_CASE(Name) case IrOpcode::k##Name:
JS_OP_LIST(BUILD_BLOCK_JS_CASE)
// JS opcodes are just like calls => fall through.
#undef BUILD_BLOCK_JS_CASE
case IrOpcode::kCall:
if (NodeProperties::IsExceptionalCall(node)) {
BuildBlocksForSuccessors(node);
......@@ -364,6 +368,10 @@ class CFGBuilder : public ZoneObject {
scheduler_->UpdatePlacement(node, Scheduler::kFixed);
ConnectThrow(node);
break;
#define CONNECT_BLOCK_JS_CASE(Name) case IrOpcode::k##Name:
JS_OP_LIST(CONNECT_BLOCK_JS_CASE)
// JS opcodes are just like calls => fall through.
#undef CONNECT_BLOCK_JS_CASE
case IrOpcode::kCall:
if (NodeProperties::IsExceptionalCall(node)) {
scheduler_->UpdatePlacement(node, Scheduler::kFixed);
......
// 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/compiler/access-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/schedule.h"
#include "src/compiler/simplified-operator.h"
#include "test/unittests/compiler/graph-unittest.h"
#include "test/unittests/compiler/node-test-utils.h"
#include "test/unittests/test-utils.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace v8 {
namespace internal {
namespace compiler {
class EffectControlLinearizerTest : public TypedGraphTest {
public:
EffectControlLinearizerTest()
: TypedGraphTest(3),
machine_(zone()),
javascript_(zone()),
simplified_(zone()),
jsgraph_(isolate(), graph(), common(), &javascript_, &simplified_,
&machine_) {}
JSGraph* jsgraph() { return &jsgraph_; }
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
private:
MachineOperatorBuilder machine_;
JSOperatorBuilder javascript_;
SimplifiedOperatorBuilder simplified_;
JSGraph jsgraph_;
};
namespace {
BasicBlock* AddBlockToSchedule(Schedule* schedule) {
BasicBlock* block = schedule->NewBasicBlock();
block->set_rpo_number(static_cast<int32_t>(schedule->rpo_order()->size()));
schedule->rpo_order()->push_back(block);
return block;
}
} // namespace
TEST_F(EffectControlLinearizerTest, SimpleLoad) {
Schedule schedule(zone());
// Create the graph.
Node* heap_number = NumberConstant(0.5);
Node* load = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), heap_number,
graph()->start(), graph()->start());
Node* ret = graph()->NewNode(common()->Return(), load, graph()->start(),
graph()->start());
// Build the basic block structure.
BasicBlock* start = schedule.start();
schedule.rpo_order()->push_back(start);
start->set_rpo_number(0);
// Populate the basic blocks with nodes.
schedule.AddNode(start, graph()->start());
schedule.AddNode(start, heap_number);
schedule.AddNode(start, load);
schedule.AddReturn(start, ret);
// Run the state effect introducer.
EffectControlLinearizer introducer(jsgraph(), &schedule, zone());
introducer.Run();
EXPECT_THAT(load,
IsLoadField(AccessBuilder::ForHeapNumberValue(), heap_number,
graph()->start(), graph()->start()));
// The return should have reconnected effect edge to the load.
EXPECT_THAT(ret, IsReturn(load, load, graph()->start()));
}
TEST_F(EffectControlLinearizerTest, DiamondLoad) {
Schedule schedule(zone());
// Create the graph.
Node* branch =
graph()->NewNode(common()->Branch(), Int32Constant(0), graph()->start());
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* heap_number = NumberConstant(0.5);
Node* vtrue = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), heap_number,
graph()->start(), if_true);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse = Float64Constant(2);
Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* phi = graph()->NewNode(
common()->Phi(MachineRepresentation::kFloat64, 2), vtrue, vfalse, merge);
Node* ret =
graph()->NewNode(common()->Return(), phi, graph()->start(), merge);
// Build the basic block structure.
BasicBlock* start = schedule.start();
schedule.rpo_order()->push_back(start);
start->set_rpo_number(0);
BasicBlock* tblock = AddBlockToSchedule(&schedule);
BasicBlock* fblock = AddBlockToSchedule(&schedule);
BasicBlock* mblock = AddBlockToSchedule(&schedule);
// Populate the basic blocks with nodes.
schedule.AddNode(start, graph()->start());
schedule.AddBranch(start, branch, tblock, fblock);
schedule.AddNode(tblock, if_true);
schedule.AddNode(tblock, heap_number);
schedule.AddNode(tblock, vtrue);
schedule.AddGoto(tblock, mblock);
schedule.AddNode(fblock, if_false);
schedule.AddNode(fblock, vfalse);
schedule.AddGoto(fblock, mblock);
schedule.AddNode(mblock, merge);
schedule.AddNode(mblock, phi);
schedule.AddReturn(mblock, ret);
// Run the state effect introducer.
EffectControlLinearizer introducer(jsgraph(), &schedule, zone());
introducer.Run();
// The effect input to the return should be an effect phi with the
// newly introduced effectful change operators.
ASSERT_THAT(
ret, IsReturn(phi, IsEffectPhi(vtrue, graph()->start(), merge), merge));
}
TEST_F(EffectControlLinearizerTest, LoopLoad) {
Schedule schedule(zone());
// Create the graph.
Node* loop = graph()->NewNode(common()->Loop(1), graph()->start());
Node* effect_phi =
graph()->NewNode(common()->EffectPhi(1), graph()->start(), loop);
Node* cond = Int32Constant(0);
Node* branch = graph()->NewNode(common()->Branch(), cond, loop);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
loop->AppendInput(zone(), if_false);
NodeProperties::ChangeOp(loop, common()->Loop(2));
effect_phi->InsertInput(zone(), 1, effect_phi);
NodeProperties::ChangeOp(effect_phi, common()->EffectPhi(2));
Node* heap_number = NumberConstant(0.5);
Node* load = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), heap_number,
graph()->start(), loop);
Node* ret = graph()->NewNode(common()->Return(), load, effect_phi, if_true);
// Build the basic block structure.
BasicBlock* start = schedule.start();
schedule.rpo_order()->push_back(start);
start->set_rpo_number(0);
BasicBlock* lblock = AddBlockToSchedule(&schedule);
BasicBlock* fblock = AddBlockToSchedule(&schedule);
BasicBlock* rblock = AddBlockToSchedule(&schedule);
// Populate the basic blocks with nodes.
schedule.AddNode(start, graph()->start());
schedule.AddGoto(start, lblock);
schedule.AddNode(lblock, loop);
schedule.AddNode(lblock, effect_phi);
schedule.AddNode(lblock, heap_number);
schedule.AddNode(lblock, load);
schedule.AddNode(lblock, cond);
schedule.AddBranch(lblock, branch, rblock, fblock);
schedule.AddNode(fblock, if_false);
schedule.AddGoto(fblock, lblock);
schedule.AddNode(rblock, if_true);
schedule.AddReturn(rblock, ret);
// Run the state effect introducer.
EffectControlLinearizer introducer(jsgraph(), &schedule, zone());
introducer.Run();
ASSERT_THAT(ret, IsReturn(load, load, if_true));
EXPECT_THAT(load, IsLoadField(AccessBuilder::ForHeapNumberValue(),
heap_number, effect_phi, loop));
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -50,6 +50,7 @@
'compiler/control-flow-optimizer-unittest.cc',
'compiler/dead-code-elimination-unittest.cc',
'compiler/diamond-unittest.cc',
'compiler/effect-control-linearizer-unittest.cc',
'compiler/escape-analysis-unittest.cc',
'compiler/graph-reducer-unittest.cc',
'compiler/graph-reducer-unittest.h',
......
......@@ -510,6 +510,8 @@
'../../src/compiler/dead-code-elimination.cc',
'../../src/compiler/dead-code-elimination.h',
'../../src/compiler/diamond.h',
'../../src/compiler/effect-control-linearizer.cc',
'../../src/compiler/effect-control-linearizer.h',
'../../src/compiler/escape-analysis.cc',
'../../src/compiler/escape-analysis.h',
"../../src/compiler/escape-analysis-reducer.cc",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment