Implement control reducer, which reduces branches and phis together in a single fixpoint.
R=bmeurer@chromium.org BUG= Review URL: https://codereview.chromium.org/665223006 Cr-Commit-Position: refs/heads/master@{#24891} git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24891 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
76bc15b5d2
commit
82581534ae
@ -14,7 +14,8 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
enum VisitState { kUnvisited, kOnStack, kRevisit, kVisited };
|
||||
enum VisitState { kUnvisited = 0, kOnStack = 1, kRevisit = 2, kVisited = 3 };
|
||||
enum Reachability { kFromStart = 8 };
|
||||
|
||||
#define TRACE(x) \
|
||||
if (FLAG_trace_turbo) PrintF x
|
||||
@ -39,23 +40,169 @@ class ControlReducerImpl {
|
||||
ZoneDeque<Node*> revisit_;
|
||||
Node* dead_;
|
||||
|
||||
void Trim() {
|
||||
// Mark all nodes reachable from end.
|
||||
NodeVector nodes(zone_);
|
||||
state_.assign(jsgraph_->graph()->NodeCount(), kUnvisited);
|
||||
Push(jsgraph_->graph()->end());
|
||||
while (!stack_.empty()) {
|
||||
Node* node = stack_[stack_.size() - 1];
|
||||
stack_.pop_back();
|
||||
state_[node->id()] = kVisited;
|
||||
nodes.push_back(node);
|
||||
for (InputIter i = node->inputs().begin(); i != node->inputs().end();
|
||||
++i) {
|
||||
Recurse(*i); // pushes node onto the stack if necessary.
|
||||
void Reduce() {
|
||||
Push(graph()->end());
|
||||
do {
|
||||
// Process the node on the top of the stack, potentially pushing more
|
||||
// or popping the node off the stack.
|
||||
ReduceTop();
|
||||
// If the stack becomes empty, revisit any nodes in the revisit queue.
|
||||
// If no nodes in the revisit queue, try removing dead loops.
|
||||
// If no dead loops, then finish.
|
||||
} while (!stack_.empty() || TryRevisit() || RepairAndRemoveLoops());
|
||||
}
|
||||
|
||||
bool TryRevisit() {
|
||||
while (!revisit_.empty()) {
|
||||
Node* n = revisit_.back();
|
||||
revisit_.pop_back();
|
||||
if (state_[n->id()] == kRevisit) { // state can change while in queue.
|
||||
Push(n);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Repair the graph after the possible creation of non-terminating or dead
|
||||
// loops. Removing dead loops can produce more opportunities for reduction.
|
||||
bool RepairAndRemoveLoops() {
|
||||
// TODO(turbofan): we can skip this if the graph has no loops, but
|
||||
// we have to be careful about proper loop detection during reduction.
|
||||
|
||||
// Gather all nodes backwards-reachable from end (through inputs).
|
||||
state_.assign(graph()->NodeCount(), kUnvisited);
|
||||
NodeVector nodes(zone_);
|
||||
AddNodesReachableFromEnd(nodes);
|
||||
|
||||
// Walk forward through control nodes, looking for back edges to nodes
|
||||
// that are not connected to end. Those are non-terminating loops (NTLs).
|
||||
Node* start = graph()->start();
|
||||
ZoneVector<byte> fw_reachability(graph()->NodeCount(), 0, zone_);
|
||||
fw_reachability[start->id()] = kFromStart | kOnStack;
|
||||
stack_.push_back(start);
|
||||
|
||||
while (!stack_.empty()) {
|
||||
Node* node = stack_.back();
|
||||
TRACE(("ControlFw: #%d:%s\n", node->id(), node->op()->mnemonic()));
|
||||
bool pop = true;
|
||||
for (Node* const succ : node->uses()) {
|
||||
byte reach = fw_reachability[succ->id()];
|
||||
if ((reach & kOnStack) != 0 && state_[succ->id()] != kVisited) {
|
||||
// {succ} is on stack and not reachable from end.
|
||||
ConnectNTL(nodes, succ);
|
||||
fw_reachability.resize(graph()->NodeCount(), 0);
|
||||
pop = false; // continue traversing inputs to this node.
|
||||
break;
|
||||
}
|
||||
if ((reach & kFromStart) == 0 &&
|
||||
IrOpcode::IsControlOpcode(succ->opcode())) {
|
||||
// {succ} is a control node and not yet reached from start.
|
||||
fw_reachability[succ->id()] |= kFromStart | kOnStack;
|
||||
stack_.push_back(succ);
|
||||
pop = false; // "recurse" into successor control node.
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pop) {
|
||||
fw_reachability[node->id()] &= ~kOnStack;
|
||||
stack_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// Trim references from dead nodes to live nodes first.
|
||||
jsgraph_->GetCachedNodes(&nodes);
|
||||
TrimNodes(nodes);
|
||||
|
||||
// Any control nodes not reachable from start are dead, even loops.
|
||||
for (size_t i = 0; i < nodes.size(); i++) {
|
||||
Node* node = nodes[i];
|
||||
byte reach = fw_reachability[node->id()];
|
||||
if ((reach & kFromStart) == 0 &&
|
||||
IrOpcode::IsControlOpcode(node->opcode())) {
|
||||
ReplaceNode(node, dead()); // uses will be added to revisit queue.
|
||||
}
|
||||
}
|
||||
return TryRevisit(); // try to push a node onto the stack.
|
||||
}
|
||||
|
||||
// Connect {loop}, the header of a non-terminating loop, to the end node.
|
||||
void ConnectNTL(NodeVector& nodes, Node* loop) {
|
||||
TRACE(("ConnectNTL: #%d:%s\n", loop->id(), loop->op()->mnemonic()));
|
||||
|
||||
if (loop->opcode() != IrOpcode::kTerminate) {
|
||||
// Insert a {Terminate} node if the loop has effects.
|
||||
ZoneDeque<Node*> effects(zone_);
|
||||
for (Node* const use : loop->uses()) {
|
||||
if (use->opcode() == IrOpcode::kEffectPhi) effects.push_back(use);
|
||||
}
|
||||
int count = static_cast<int>(effects.size());
|
||||
if (count > 0) {
|
||||
Node** inputs = zone_->NewArray<Node*>(1 + count);
|
||||
for (int i = 0; i < count; i++) inputs[i] = effects[i];
|
||||
inputs[count] = loop;
|
||||
loop = graph()->NewNode(common_->Terminate(count), 1 + count, inputs);
|
||||
TRACE(("AddTerminate: #%d:%s[%d]\n", loop->id(), loop->op()->mnemonic(),
|
||||
count));
|
||||
}
|
||||
}
|
||||
|
||||
Node* to_add = loop;
|
||||
Node* end = graph()->end();
|
||||
CHECK_EQ(IrOpcode::kEnd, end->opcode());
|
||||
Node* merge = end->InputAt(0);
|
||||
if (merge == NULL || merge->opcode() == IrOpcode::kDead) {
|
||||
// The end node died; just connect end to {loop}.
|
||||
end->ReplaceInput(0, loop);
|
||||
} else if (merge->opcode() != IrOpcode::kMerge) {
|
||||
// Introduce a final merge node for {end->InputAt(0)} and {loop}.
|
||||
merge = graph()->NewNode(common_->Merge(2), merge, loop);
|
||||
end->ReplaceInput(0, merge);
|
||||
to_add = merge;
|
||||
} else {
|
||||
// Append a new input to the final merge at the end.
|
||||
merge->AppendInput(graph()->zone(), loop);
|
||||
merge->set_op(common_->Merge(merge->InputCount()));
|
||||
}
|
||||
nodes.push_back(to_add);
|
||||
state_.resize(graph()->NodeCount(), kUnvisited);
|
||||
state_[to_add->id()] = kVisited;
|
||||
AddBackwardsReachableNodes(nodes, nodes.size() - 1);
|
||||
}
|
||||
|
||||
void AddNodesReachableFromEnd(NodeVector& nodes) {
|
||||
Node* end = graph()->end();
|
||||
state_[end->id()] = kVisited;
|
||||
if (!end->IsDead()) {
|
||||
nodes.push_back(end);
|
||||
AddBackwardsReachableNodes(nodes, nodes.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void AddBackwardsReachableNodes(NodeVector& nodes, size_t cursor) {
|
||||
while (cursor < nodes.size()) {
|
||||
Node* node = nodes[cursor++];
|
||||
for (Node* const input : node->inputs()) {
|
||||
if (state_[input->id()] != kVisited) {
|
||||
state_[input->id()] = kVisited;
|
||||
nodes.push_back(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trim() {
|
||||
// Gather all nodes backwards-reachable from end through inputs.
|
||||
state_.assign(graph()->NodeCount(), kUnvisited);
|
||||
NodeVector nodes(zone_);
|
||||
AddNodesReachableFromEnd(nodes);
|
||||
|
||||
// Process cached nodes in the JSGraph too.
|
||||
jsgraph_->GetCachedNodes(&nodes);
|
||||
TrimNodes(nodes);
|
||||
}
|
||||
|
||||
void TrimNodes(NodeVector& nodes) {
|
||||
// Remove dead->live edges.
|
||||
for (size_t j = 0; j < nodes.size(); j++) {
|
||||
Node* node = nodes[j];
|
||||
@ -75,18 +222,46 @@ class ControlReducerImpl {
|
||||
// Verify that no inputs to live nodes are NULL.
|
||||
for (size_t j = 0; j < nodes.size(); j++) {
|
||||
Node* node = nodes[j];
|
||||
for (InputIter i = node->inputs().begin(); i != node->inputs().end();
|
||||
++i) {
|
||||
CHECK_NE(NULL, *i);
|
||||
for (Node* const input : node->inputs()) {
|
||||
CHECK_NE(NULL, input);
|
||||
}
|
||||
for (UseIter i = node->uses().begin(); i != node->uses().end(); ++i) {
|
||||
size_t id = static_cast<size_t>((*i)->id());
|
||||
for (Node* const use : node->uses()) {
|
||||
size_t id = static_cast<size_t>(use->id());
|
||||
CHECK_EQ(kVisited, state_[id]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Reduce the node on the top of the stack.
|
||||
// If an input {i} is not yet visited or needs to be revisited, push {i} onto
|
||||
// the stack and return. Otherwise, all inputs are visited, so apply
|
||||
// reductions for {node} and pop it off the stack.
|
||||
void ReduceTop() {
|
||||
size_t height = stack_.size();
|
||||
Node* node = stack_.back();
|
||||
|
||||
if (node->IsDead()) return Pop(); // Node was killed while on stack.
|
||||
|
||||
TRACE(("ControlReduce: #%d:%s\n", node->id(), node->op()->mnemonic()));
|
||||
|
||||
// Recurse on an input if necessary.
|
||||
for (Node* const input : node->inputs()) {
|
||||
if (Recurse(input)) return;
|
||||
}
|
||||
|
||||
// All inputs should be visited or on stack. Apply reductions to node.
|
||||
Node* replacement = ReduceNode(node);
|
||||
if (replacement != node) ReplaceNode(node, replacement);
|
||||
|
||||
// After reducing the node, pop it off the stack.
|
||||
CHECK_EQ(static_cast<int>(height), static_cast<int>(stack_.size()));
|
||||
Pop();
|
||||
|
||||
// If there was a replacement, reduce it after popping {node}.
|
||||
if (replacement != node) Recurse(replacement);
|
||||
}
|
||||
|
||||
// Push a node onto the stack if its state is {kUnvisited} or {kRevisit}.
|
||||
bool Recurse(Node* node) {
|
||||
size_t id = static_cast<size_t>(node->id());
|
||||
@ -103,13 +278,223 @@ class ControlReducerImpl {
|
||||
state_[node->id()] = kOnStack;
|
||||
stack_.push_back(node);
|
||||
}
|
||||
|
||||
void Pop() {
|
||||
int pos = static_cast<int>(stack_.size()) - 1;
|
||||
DCHECK_GE(pos, 0);
|
||||
DCHECK_EQ(kOnStack, state_[stack_[pos]->id()]);
|
||||
state_[stack_[pos]->id()] = kVisited;
|
||||
stack_.pop_back();
|
||||
}
|
||||
|
||||
// Queue a node to be revisited if it has been visited once already.
|
||||
void Revisit(Node* node) {
|
||||
size_t id = static_cast<size_t>(node->id());
|
||||
if (id < state_.size() && state_[id] == kVisited) {
|
||||
TRACE((" Revisit #%d:%s\n", node->id(), node->op()->mnemonic()));
|
||||
state_[id] = kRevisit;
|
||||
revisit_.push_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
Node* dead() {
|
||||
if (dead_ == NULL) dead_ = graph()->NewNode(common_->Dead());
|
||||
return dead_;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Reducer implementation: perform reductions on a node.
|
||||
//===========================================================================
|
||||
Node* ReduceNode(Node* node) {
|
||||
if (OperatorProperties::GetControlInputCount(node->op()) == 1) {
|
||||
// If a node has only one control input and it is dead, replace with dead.
|
||||
Node* control = NodeProperties::GetControlInput(node);
|
||||
if (control->opcode() == IrOpcode::kDead) {
|
||||
TRACE(("ControlDead: #%d:%s\n", node->id(), node->op()->mnemonic()));
|
||||
return control;
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce branches, phis, and merges.
|
||||
switch (node->opcode()) {
|
||||
case IrOpcode::kBranch:
|
||||
return ReduceBranch(node);
|
||||
case IrOpcode::kLoop:
|
||||
case IrOpcode::kMerge:
|
||||
return ReduceMerge(node);
|
||||
case IrOpcode::kPhi:
|
||||
case IrOpcode::kEffectPhi:
|
||||
return ReducePhi(node);
|
||||
default:
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce redundant phis.
|
||||
Node* ReducePhi(Node* node) {
|
||||
int n = node->InputCount();
|
||||
if (n <= 1) return dead(); // No non-control inputs.
|
||||
if (n == 2) return node->InputAt(0); // Only one non-control input.
|
||||
|
||||
Node* replacement = NULL;
|
||||
Node::Inputs inputs = node->inputs();
|
||||
for (InputIter it = inputs.begin(); n > 1; --n, ++it) {
|
||||
Node* input = *it;
|
||||
if (input->opcode() == IrOpcode::kDead) continue; // ignore dead inputs.
|
||||
if (input != node && input != replacement) { // non-redundant input.
|
||||
if (replacement != NULL) return node;
|
||||
replacement = input;
|
||||
}
|
||||
}
|
||||
return replacement == NULL ? dead() : replacement;
|
||||
}
|
||||
|
||||
// Reduce merges by trimming away dead inputs from the merge and phis.
|
||||
Node* ReduceMerge(Node* node) {
|
||||
// Count the number of live inputs.
|
||||
int live = 0;
|
||||
int index = 0;
|
||||
int live_index = 0;
|
||||
for (Node* const input : node->inputs()) {
|
||||
if (input->opcode() != IrOpcode::kDead) {
|
||||
live++;
|
||||
live_index = index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if (live > 1 && live == node->InputCount()) return node; // nothing to do.
|
||||
|
||||
TRACE(("ReduceMerge: #%d:%s (%d live)\n", node->id(),
|
||||
node->op()->mnemonic(), live));
|
||||
|
||||
if (live == 0) return dead(); // no remaining inputs.
|
||||
|
||||
// Gather phis and effect phis to be edited.
|
||||
ZoneVector<Node*> phis(zone_);
|
||||
for (Node* const use : node->uses()) {
|
||||
if (use->opcode() == IrOpcode::kPhi ||
|
||||
use->opcode() == IrOpcode::kEffectPhi) {
|
||||
phis.push_back(use);
|
||||
}
|
||||
}
|
||||
|
||||
if (live == 1) {
|
||||
// All phis are redundant. Replace them with their live input.
|
||||
for (Node* const phi : phis) ReplaceNode(phi, phi->InputAt(live_index));
|
||||
// The merge itself is redundant.
|
||||
return node->InputAt(live_index);
|
||||
}
|
||||
|
||||
// Edit phis in place, removing dead inputs and revisiting them.
|
||||
for (Node* const phi : phis) {
|
||||
TRACE((" PhiInMerge: #%d:%s (%d live)\n", phi->id(),
|
||||
phi->op()->mnemonic(), live));
|
||||
RemoveDeadInputs(node, phi);
|
||||
Revisit(phi);
|
||||
}
|
||||
// Edit the merge in place, removing dead inputs.
|
||||
RemoveDeadInputs(node, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
// Reduce branches if they have constant inputs.
|
||||
Node* ReduceBranch(Node* node) {
|
||||
Node* cond = node->InputAt(0);
|
||||
bool is_true;
|
||||
switch (cond->opcode()) {
|
||||
case IrOpcode::kInt32Constant:
|
||||
is_true = !Int32Matcher(cond).Is(0);
|
||||
break;
|
||||
case IrOpcode::kNumberConstant:
|
||||
is_true = !NumberMatcher(cond).Is(0);
|
||||
break;
|
||||
case IrOpcode::kHeapConstant: {
|
||||
Handle<Object> object =
|
||||
HeapObjectMatcher<Object>(cond).Value().handle();
|
||||
if (object->IsTrue())
|
||||
is_true = true;
|
||||
else if (object->IsFalse())
|
||||
is_true = false;
|
||||
else
|
||||
return node; // TODO(turbofan): fold branches on strings, objects.
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return node;
|
||||
}
|
||||
|
||||
TRACE(("BranchReduce: #%d:%s = %s\n", node->id(), node->op()->mnemonic(),
|
||||
is_true ? "true" : "false"));
|
||||
|
||||
// Replace IfTrue and IfFalse projections from this branch.
|
||||
Node* control = NodeProperties::GetControlInput(node);
|
||||
for (UseIter i = node->uses().begin(); i != node->uses().end();) {
|
||||
Node* to = *i;
|
||||
if (to->opcode() == IrOpcode::kIfTrue) {
|
||||
TRACE((" IfTrue: #%d:%s\n", to->id(), to->op()->mnemonic()));
|
||||
i.UpdateToAndIncrement(NULL);
|
||||
ReplaceNode(to, is_true ? control : dead());
|
||||
} else if (to->opcode() == IrOpcode::kIfFalse) {
|
||||
TRACE((" IfFalse: #%d:%s\n", to->id(), to->op()->mnemonic()));
|
||||
i.UpdateToAndIncrement(NULL);
|
||||
ReplaceNode(to, is_true ? dead() : control);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return control;
|
||||
}
|
||||
|
||||
// Remove inputs to {node} corresponding to the dead inputs to {merge}
|
||||
// and compact the remaining inputs, updating the operator.
|
||||
void RemoveDeadInputs(Node* merge, Node* node) {
|
||||
int pos = 0;
|
||||
for (int i = 0; i < node->InputCount(); i++) {
|
||||
// skip dead inputs.
|
||||
if (i < merge->InputCount() &&
|
||||
merge->InputAt(i)->opcode() == IrOpcode::kDead)
|
||||
continue;
|
||||
// compact live inputs.
|
||||
if (pos != i) node->ReplaceInput(pos, node->InputAt(i));
|
||||
pos++;
|
||||
}
|
||||
node->TrimInputCount(pos);
|
||||
if (node->opcode() == IrOpcode::kPhi) {
|
||||
node->set_op(common_->Phi(OpParameter<MachineType>(node->op()), pos - 1));
|
||||
} else if (node->opcode() == IrOpcode::kEffectPhi) {
|
||||
node->set_op(common_->EffectPhi(pos - 1));
|
||||
} else if (node->opcode() == IrOpcode::kMerge) {
|
||||
node->set_op(common_->Merge(pos));
|
||||
} else if (node->opcode() == IrOpcode::kLoop) {
|
||||
node->set_op(common_->Loop(pos));
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
// Replace uses of {node} with {replacement} and revisit the uses.
|
||||
void ReplaceNode(Node* node, Node* replacement) {
|
||||
if (node == replacement) return;
|
||||
TRACE((" Replace: #%d:%s with #%d:%s\n", node->id(),
|
||||
node->op()->mnemonic(), replacement->id(),
|
||||
replacement->op()->mnemonic()));
|
||||
for (Node* const use : node->uses()) {
|
||||
// Don't revisit this node if it refers to itself.
|
||||
if (use != node) Revisit(use);
|
||||
}
|
||||
node->ReplaceUses(replacement);
|
||||
node->Kill();
|
||||
}
|
||||
|
||||
Graph* graph() { return jsgraph_->graph(); }
|
||||
};
|
||||
|
||||
|
||||
void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph,
|
||||
CommonOperatorBuilder* common) {
|
||||
ControlReducerImpl impl(zone, jsgraph, NULL);
|
||||
// Only trim the graph for now. Control reduction can reduce non-terminating
|
||||
// loops to graphs that are unschedulable at the moment.
|
||||
ControlReducerImpl impl(zone, jsgraph, common);
|
||||
impl.Reduce();
|
||||
impl.Trim();
|
||||
}
|
||||
|
||||
@ -118,6 +503,33 @@ void ControlReducer::TrimGraph(Zone* zone, JSGraph* jsgraph) {
|
||||
ControlReducerImpl impl(zone, jsgraph, NULL);
|
||||
impl.Trim();
|
||||
}
|
||||
|
||||
|
||||
Node* ControlReducer::ReducePhiForTesting(JSGraph* jsgraph,
|
||||
CommonOperatorBuilder* common,
|
||||
Node* node) {
|
||||
Zone zone(jsgraph->graph()->zone()->isolate());
|
||||
ControlReducerImpl impl(&zone, jsgraph, common);
|
||||
return impl.ReducePhi(node);
|
||||
}
|
||||
|
||||
|
||||
Node* ControlReducer::ReduceMergeForTesting(JSGraph* jsgraph,
|
||||
CommonOperatorBuilder* common,
|
||||
Node* node) {
|
||||
Zone zone(jsgraph->graph()->zone()->isolate());
|
||||
ControlReducerImpl impl(&zone, jsgraph, common);
|
||||
return impl.ReduceMerge(node);
|
||||
}
|
||||
|
||||
|
||||
Node* ControlReducer::ReduceBranchForTesting(JSGraph* jsgraph,
|
||||
CommonOperatorBuilder* common,
|
||||
Node* node) {
|
||||
Zone zone(jsgraph->graph()->zone()->isolate());
|
||||
ControlReducerImpl impl(&zone, jsgraph, common);
|
||||
return impl.ReduceBranch(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace v8::internal::compiler
|
||||
|
@ -11,6 +11,7 @@ namespace compiler {
|
||||
|
||||
class JSGraph;
|
||||
class CommonOperatorBuilder;
|
||||
class Node;
|
||||
|
||||
class ControlReducer {
|
||||
public:
|
||||
@ -20,6 +21,16 @@ class ControlReducer {
|
||||
|
||||
// Trim nodes in the graph that are not reachable from end.
|
||||
static void TrimGraph(Zone* zone, JSGraph* graph);
|
||||
|
||||
// Testing interface.
|
||||
static Node* ReducePhiForTesting(JSGraph* graph,
|
||||
CommonOperatorBuilder* builder, Node* node);
|
||||
static Node* ReduceBranchForTesting(JSGraph* graph,
|
||||
CommonOperatorBuilder* builder,
|
||||
Node* node);
|
||||
static Node* ReduceMergeForTesting(JSGraph* graph,
|
||||
CommonOperatorBuilder* builder,
|
||||
Node* node);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -117,6 +117,7 @@ inline int OperatorProperties::GetEffectInputCount(const Operator* op) {
|
||||
}
|
||||
|
||||
inline int OperatorProperties::GetControlInputCount(const Operator* op) {
|
||||
// TODO(titzer): fix this mess; just make them a count on the operator.
|
||||
switch (op->opcode()) {
|
||||
case IrOpcode::kPhi:
|
||||
case IrOpcode::kEffectPhi:
|
||||
@ -127,8 +128,8 @@ inline int OperatorProperties::GetControlInputCount(const Operator* op) {
|
||||
#define OPCODE_CASE(x) case IrOpcode::k##x:
|
||||
CONTROL_OP_LIST(OPCODE_CASE)
|
||||
#undef OPCODE_CASE
|
||||
// Branch operator is special
|
||||
if (op->opcode() == IrOpcode::kBranch) return 1;
|
||||
if (op->opcode() == IrOpcode::kTerminate) return 1;
|
||||
// Control operators are Operator1<int>.
|
||||
return OpParameter<int>(op);
|
||||
default:
|
||||
|
@ -382,7 +382,8 @@ class CFGBuilder {
|
||||
}
|
||||
|
||||
bool IsFinalMerge(Node* node) {
|
||||
return (node == scheduler_->graph_->end()->InputAt(0));
|
||||
return (node->opcode() == IrOpcode::kMerge &&
|
||||
node == scheduler_->graph_->end()->InputAt(0));
|
||||
}
|
||||
};
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user