Commit 6881d760 authored by mstarzinger's avatar mstarzinger Committed by Commit bot

Model exceptional edges from call nodes in TurboFan.

R=titzer@chromium.org,bmeurer@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#26766}
parent 726f3801
......@@ -328,7 +328,12 @@ class AstGraphBuilder::ControlScopeForIteration : public ControlScope {
class AstGraphBuilder::ControlScopeForCatch : public ControlScope {
public:
ControlScopeForCatch(AstGraphBuilder* owner, TryCatchBuilder* control)
: ControlScope(owner, 0), control_(control) {}
: ControlScope(owner, 0), control_(control) {
builder()->try_nesting_level_++; // Increment nesting.
}
~ControlScopeForCatch() {
builder()->try_nesting_level_--; // Decrement nesting.
}
protected:
virtual bool Execute(Command cmd, Statement* target, Node* value) OVERRIDE {
......@@ -354,7 +359,12 @@ class AstGraphBuilder::ControlScopeForFinally : public ControlScope {
public:
ControlScopeForFinally(AstGraphBuilder* owner, DeferredCommands* commands,
TryFinallyBuilder* control)
: ControlScope(owner, 0), commands_(commands), control_(control) {}
: ControlScope(owner, 0), commands_(commands), control_(control) {
builder()->try_nesting_level_++; // Increment nesting.
}
~ControlScopeForFinally() {
builder()->try_nesting_level_--; // Decrement nesting.
}
protected:
virtual bool Execute(Command cmd, Statement* target, Node* value) OVERRIDE {
......@@ -379,6 +389,7 @@ AstGraphBuilder::AstGraphBuilder(Zone* local_zone, CompilationInfo* info,
globals_(0, local_zone),
execution_control_(nullptr),
execution_context_(nullptr),
try_nesting_level_(0),
input_buffer_size_(0),
input_buffer_(nullptr),
exit_control_(nullptr),
......@@ -2974,6 +2985,7 @@ Node* AstGraphBuilder::MakeNode(const Operator* op, int value_input_count,
if (!has_context && !has_framestate && !has_control && !has_effect) {
result = graph()->NewNode(op, value_input_count, value_inputs, incomplete);
} else {
bool inside_try_scope = try_nesting_level_ > 0;
int input_count_with_deps = value_input_count;
if (has_context) ++input_count_with_deps;
if (has_framestate) ++input_count_with_deps;
......@@ -3001,10 +3013,25 @@ Node* AstGraphBuilder::MakeNode(const Operator* op, int value_input_count,
if (has_effect) {
environment_->UpdateEffectDependency(result);
}
if (result->op()->ControlOutputCount() > 0 &&
!environment()->IsMarkedAsUnreachable()) {
if (!environment()->IsMarkedAsUnreachable()) {
// Update the current control dependency for control-producing nodes.
if (NodeProperties::IsControl(result)) {
environment_->UpdateControlDependency(result);
}
// Add implicit exception continuation for throwing nodes.
if (!result->op()->HasProperty(Operator::kNoThrow) && inside_try_scope) {
Node* on_exception = graph()->NewNode(common()->IfException(), result);
environment_->UpdateControlDependency(on_exception);
if (FLAG_turbo_exceptions) {
execution_control()->ThrowValue(jsgraph()->UndefinedConstant());
}
}
// Add implicit success continuation for throwing nodes.
if (!result->op()->HasProperty(Operator::kNoThrow)) {
Node* on_success = graph()->NewNode(common()->IfSuccess(), result);
environment_->UpdateControlDependency(on_success);
}
}
}
return result;
......
......@@ -84,6 +84,9 @@ class AstGraphBuilder : public AstVisitor {
SetOncePointer<Node> function_closure_;
SetOncePointer<Node> function_context_;
// Tracks how many try-blocks are currently entered.
int try_nesting_level_;
// Temporary storage for building node input lists.
int input_buffer_size_;
Node** input_buffer_;
......
......@@ -202,6 +202,7 @@ bool CanCover(Node* value, IrOpcode::Value opcode) {
if (value->opcode() != opcode) return false;
bool first = true;
for (Edge const edge : value->use_edges()) {
if (NodeProperties::IsControlEdge(edge)) continue;
if (NodeProperties::IsEffectEdge(edge)) continue;
DCHECK(NodeProperties::IsValueEdge(edge));
if (!first) return false;
......
......@@ -115,6 +115,8 @@ size_t ProjectionIndexOf(const Operator* const op) {
V(End, Operator::kKontrol, 0, 0, 1, 0, 0, 0) \
V(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfException, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfDefault, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(Throw, Operator::kFoldable, 1, 1, 1, 0, 0, 1) \
V(Return, Operator::kNoThrow, 1, 1, 1, 0, 0, 1) \
......@@ -495,7 +497,8 @@ const Operator* CommonOperatorBuilder::Call(const CallDescriptor* descriptor) {
Operator::ZeroIfPure(descriptor->properties()),
Operator::ZeroIfPure(descriptor->properties()),
descriptor->ReturnCount(),
Operator::ZeroIfPure(descriptor->properties()), 0, descriptor) {}
Operator::ZeroIfPure(descriptor->properties()),
Operator::ZeroIfNoThrow(descriptor->properties()), descriptor) {}
void PrintParameter(std::ostream& os) const OVERRIDE {
os << "[" << *parameter() << "]";
......
......@@ -172,6 +172,8 @@ class CommonOperatorBuilder FINAL : public ZoneObject {
const Operator* Branch(BranchHint = BranchHint::kNone);
const Operator* IfTrue();
const Operator* IfFalse();
const Operator* IfSuccess();
const Operator* IfException();
const Operator* Switch(size_t control_output_count);
const Operator* IfValue(int32_t value);
const Operator* IfDefault();
......
......@@ -131,7 +131,7 @@ class ControlReducerImpl {
pop = false; // restart traversing successors of this node.
break;
}
if (NodeProperties::IsControl(succ) &&
if (succ->op()->ControlOutputCount() > 0 &&
!marked.IsReachableFromStart(succ)) {
// {succ} is a control node and not yet reached from start.
marked.Push(succ);
......@@ -155,7 +155,7 @@ class ControlReducerImpl {
// Any control nodes not reachable from start are dead, even loops.
for (size_t i = 0; i < nodes.size(); i++) {
Node* node = nodes[i];
if (NodeProperties::IsControl(node) &&
if (node->op()->ControlOutputCount() > 0 &&
!marked.IsReachableFromStart(node)) {
ReplaceNode(node, dead()); // uses will be added to revisit queue.
}
......@@ -190,16 +190,14 @@ class ControlReducerImpl {
DCHECK(NodeProperties::IsControlEdge(edge));
if (edge.from() == branch) continue;
switch (edge.from()->opcode()) {
#define CASE(Opcode) case IrOpcode::k##Opcode:
CONTROL_OP_LIST(CASE)
#undef CASE
// Update all control nodes (except {branch}) pointing to the {loop}.
edge.UpdateTo(if_true);
case IrOpcode::kPhi:
break;
case IrOpcode::kEffectPhi:
effects.push_back(edge.from());
break;
default:
// Update all control edges (except {branch}) pointing to the {loop}.
edge.UpdateTo(if_true);
break;
}
}
......
......@@ -485,34 +485,34 @@ void InstructionSelector::VisitBlock(BasicBlock* block) {
}
namespace {
V8_INLINE void CheckNoPhis(const BasicBlock* block) {
void InstructionSelector::VisitControl(BasicBlock* block) {
#ifdef DEBUG
// Branch targets should not have phis.
for (BasicBlock::const_iterator i = block->begin(); i != block->end(); ++i) {
const Node* node = *i;
CHECK_NE(IrOpcode::kPhi, node->opcode());
// SSA deconstruction requires targets of branches not to have phis.
// Edge split form guarantees this property, but is more strict.
if (block->SuccessorCount() > 1) {
for (BasicBlock* const successor : block->successors()) {
for (Node* const node : *successor) {
CHECK(!IrOpcode::IsPhiOpcode(node->opcode()));
}
}
}
#endif
}
} // namespace
void InstructionSelector::VisitControl(BasicBlock* block) {
Node* input = block->control_input();
switch (block->control()) {
case BasicBlock::kGoto:
return VisitGoto(block->SuccessorAt(0));
case BasicBlock::kCall: {
DCHECK_EQ(IrOpcode::kCall, input->opcode());
BasicBlock* success = block->SuccessorAt(0);
// TODO(mstarzinger): Record location of {exception} in {handler_table}.
// BasicBlock* exception = block->SuccessorAt(1);
return VisitCall(input), VisitGoto(success);
}
case BasicBlock::kBranch: {
DCHECK_EQ(IrOpcode::kBranch, input->opcode());
BasicBlock* tbranch = block->SuccessorAt(0);
BasicBlock* fbranch = block->SuccessorAt(1);
// SSA deconstruction requires targets of branches not to have phis.
// Edge split form guarantees this property, but is more strict.
CheckNoPhis(tbranch);
CheckNoPhis(fbranch);
if (tbranch == fbranch) return VisitGoto(tbranch);
// Treat special Branch(Always, IfTrue, IfFalse) as Goto(IfTrue).
Node* const condition = input->InputAt(0);
......@@ -524,9 +524,6 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
// Last successor must be Default.
BasicBlock* default_branch = block->successors().back();
DCHECK_EQ(IrOpcode::kIfDefault, default_branch->front()->opcode());
// SSA deconstruction requires targets of branches not to have phis.
// Edge split form guarantees this property, but is more strict.
CheckNoPhis(default_branch);
// All other successors must be cases.
size_t case_count = block->SuccessorCount() - 1;
DCHECK_LE(1u, case_count);
......@@ -541,9 +538,6 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
case_values[index] = value;
if (min_value > value) min_value = value;
if (max_value < value) max_value = value;
// SSA deconstruction requires targets of branches not to have phis.
// Edge split form guarantees this property, but is more strict.
CheckNoPhis(branch);
}
DCHECK_LE(min_value, max_value);
return VisitSwitch(input, default_branch, case_branches, case_values,
......@@ -721,6 +715,8 @@ void InstructionSelector::VisitNode(Node* node) {
case IrOpcode::kBranch:
case IrOpcode::kIfTrue:
case IrOpcode::kIfFalse:
case IrOpcode::kIfSuccess:
case IrOpcode::kIfException:
case IrOpcode::kSwitch:
case IrOpcode::kIfValue:
case IrOpcode::kIfDefault:
......
......@@ -145,7 +145,6 @@ Reduction JSIntrinsicLowering::ReduceInlineValueOf(Node* node) {
Node* merge0 = graph()->NewNode(merge_op, if_true0, if_false0);
// Replace all effect uses of {node} with the {ephi0}.
Node* ephi0 = graph()->NewNode(ephi_op, etrue0, efalse0, merge0);
NodeProperties::ReplaceWithValue(node, node, ephi0);
......@@ -156,7 +155,7 @@ Reduction JSIntrinsicLowering::ReduceInlineValueOf(Node* node) {
Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op) {
// Remove the effects from the node and update its effect usages.
// Replace all effect uses of {node} with the effect dependency.
NodeProperties::ReplaceWithValue(node, node);
// Remove the inputs corresponding to context, effect and control.
NodeProperties::RemoveNonValueInputs(node);
......
......@@ -254,7 +254,8 @@ struct JSOperatorGlobalCache FINAL {
: Operator(IrOpcode::kJS##Name, properties, "JS" #Name, \
value_input_count, Operator::ZeroIfPure(properties), \
Operator::ZeroIfPure(properties), value_output_count, \
Operator::ZeroIfPure(properties), 0) {} \
Operator::ZeroIfPure(properties), \
Operator::ZeroIfNoThrow(properties)) {} \
}; \
Name##Operator k##Name##Operator;
CACHED_OP_LIST(CACHED)
......@@ -265,7 +266,7 @@ struct JSOperatorGlobalCache FINAL {
StorePropertyOperator()
: Operator1<LanguageMode>(IrOpcode::kJSStoreProperty,
Operator::kNoProperties, "JSStoreProperty", 3,
1, 1, 0, 1, 0, kLanguageMode) {}
1, 1, 0, 1, 2, kLanguageMode) {}
};
StorePropertyOperator<SLOPPY> kStorePropertySloppyOperator;
StorePropertyOperator<STRICT> kStorePropertyStrictOperator;
......@@ -294,7 +295,7 @@ const Operator* JSOperatorBuilder::CallFunction(size_t arity,
return new (zone()) Operator1<CallFunctionParameters>( // --
IrOpcode::kJSCallFunction, Operator::kNoProperties, // opcode
"JSCallFunction", // name
parameters.arity(), 1, 1, 1, 1, 0, // inputs/outputs
parameters.arity(), 1, 1, 1, 1, 2, // inputs/outputs
parameters); // parameter
}
......@@ -307,7 +308,7 @@ const Operator* JSOperatorBuilder::CallRuntime(Runtime::FunctionId id,
return new (zone()) Operator1<CallRuntimeParameters>( // --
IrOpcode::kJSCallRuntime, Operator::kNoProperties, // opcode
"JSCallRuntime", // name
parameters.arity(), 1, 1, f->result_size, 1, 0, // inputs/outputs
parameters.arity(), 1, 1, f->result_size, 1, 2, // inputs/outputs
parameters); // parameter
}
......@@ -316,7 +317,7 @@ const Operator* JSOperatorBuilder::CallConstruct(int arguments) {
return new (zone()) Operator1<int>( // --
IrOpcode::kJSCallConstruct, Operator::kNoProperties, // opcode
"JSCallConstruct", // name
arguments, 1, 1, 1, 1, 0, // counts
arguments, 1, 1, 1, 1, 2, // counts
arguments); // parameter
}
......@@ -328,7 +329,7 @@ const Operator* JSOperatorBuilder::LoadNamed(const Unique<Name>& name,
return new (zone()) Operator1<LoadNamedParameters>( // --
IrOpcode::kJSLoadNamed, Operator::kNoProperties, // opcode
"JSLoadNamed", // name
1, 1, 1, 1, 1, 0, // counts
1, 1, 1, 1, 1, 2, // counts
parameters); // parameter
}
......@@ -339,7 +340,7 @@ const Operator* JSOperatorBuilder::LoadProperty(
return new (zone()) Operator1<LoadPropertyParameters>( // --
IrOpcode::kJSLoadProperty, Operator::kNoProperties, // opcode
"JSLoadProperty", // name
2, 1, 1, 1, 1, 0, // counts
2, 1, 1, 1, 1, 2, // counts
parameters); // parameter
}
......@@ -361,7 +362,7 @@ const Operator* JSOperatorBuilder::StoreNamed(LanguageMode language_mode,
return new (zone()) Operator1<StoreNamedParameters>( // --
IrOpcode::kJSStoreNamed, Operator::kNoProperties, // opcode
"JSStoreNamed", // name
2, 1, 1, 0, 1, 0, // counts
2, 1, 1, 0, 1, 2, // counts
parameters); // parameter
}
......@@ -370,7 +371,7 @@ const Operator* JSOperatorBuilder::DeleteProperty(LanguageMode language_mode) {
return new (zone()) Operator1<LanguageMode>( // --
IrOpcode::kJSDeleteProperty, Operator::kNoProperties, // opcode
"JSDeleteProperty", // name
2, 1, 1, 1, 1, 0, // counts
2, 1, 1, 1, 1, 2, // counts
language_mode); // parameter
}
......@@ -379,7 +380,8 @@ const Operator* JSOperatorBuilder::LoadContext(size_t depth, size_t index,
bool immutable) {
ContextAccess access(depth, index, immutable);
return new (zone()) Operator1<ContextAccess>( // --
IrOpcode::kJSLoadContext, Operator::kNoWrite, // opcode
IrOpcode::kJSLoadContext, // opcode
Operator::kNoWrite | Operator::kNoThrow, // flags
"JSLoadContext", // name
1, 1, 0, 1, 1, 0, // counts
access); // parameter
......@@ -389,7 +391,8 @@ const Operator* JSOperatorBuilder::LoadContext(size_t depth, size_t index,
const Operator* JSOperatorBuilder::StoreContext(size_t depth, size_t index) {
ContextAccess access(depth, index, false);
return new (zone()) Operator1<ContextAccess>( // --
IrOpcode::kJSStoreContext, Operator::kNoRead, // opcode
IrOpcode::kJSStoreContext, // opcode
Operator::kNoRead | Operator::kNoThrow, // flags
"JSStoreContext", // name
2, 1, 1, 0, 1, 0, // counts
access); // parameter
......@@ -401,7 +404,7 @@ const Operator* JSOperatorBuilder::CreateCatchContext(
return new (zone()) Operator1<Unique<String>>( // --
IrOpcode::kJSCreateCatchContext, Operator::kNoProperties, // opcode
"JSCreateCatchContext", // name
2, 1, 1, 1, 1, 0, // counts
2, 1, 1, 1, 1, 2, // counts
name); // parameter
}
......
......@@ -19,15 +19,23 @@ namespace compiler {
// - relax effects from generic but not-side-effecting operations
// Relax the effects of {node} by immediately replacing effect uses of {node}
// with the effect input to {node}.
// Relax the effects of {node} by immediately replacing effect and control uses
// of {node} with the effect and control input to {node}.
// TODO(turbofan): replace the effect input to {node} with {graph->start()}.
// TODO(titzer): move into a GraphEditor?
static void RelaxEffects(Node* node) {
static void RelaxEffectsAndControls(Node* node) {
NodeProperties::ReplaceWithValue(node, node, NULL);
}
// Relax the control uses of {node} by immediately replacing them with the
// control input to {node}.
// TODO(titzer): move into a GraphEditor?
static void RelaxControls(Node* node) {
NodeProperties::ReplaceWithValue(node, node, node);
}
JSTypedLowering::JSTypedLowering(JSGraph* jsgraph, Zone* zone)
: jsgraph_(jsgraph), simplified_(graph()->zone()), conversions_(zone) {
zero_range_ = Type::Range(0.0, 1.0, graph()->zone());
......@@ -116,9 +124,9 @@ class JSBinopReduction FINAL {
DCHECK_EQ(0, op->ControlInputCount());
DCHECK_EQ(2, op->ValueInputCount());
// Remove the effects from the node, if any, and update its effect usages.
// Remove the effects from the node, and update its effect/control usages.
if (node_->op()->EffectInputCount() > 0) {
RelaxEffects(node_);
RelaxEffectsAndControls(node_);
}
// Remove the inputs corresponding to context, effect, and control.
NodeProperties::RemoveNonValueInputs(node_);
......@@ -584,7 +592,7 @@ Reduction JSTypedLowering::ReduceJSToNumber(Node* node) {
DCHECK(NodeProperties::IsControl(control));
DCHECK(NodeProperties::GetBounds(node).upper->Is(Type::Number()));
DCHECK(!NodeProperties::GetBounds(input).upper->Is(Type::Number()));
RelaxEffects(node);
RelaxEffectsAndControls(node);
node->set_op(common()->Phi(kMachAnyTagged, input_count));
for (int i = 0; i < input_count; ++i) {
// We must be very careful not to introduce cycles when pushing
......@@ -616,7 +624,7 @@ Reduction JSTypedLowering::ReduceJSToNumber(Node* node) {
DCHECK_EQ(3, input_count);
DCHECK(NodeProperties::GetBounds(node).upper->Is(Type::Number()));
DCHECK(!NodeProperties::GetBounds(input).upper->Is(Type::Number()));
RelaxEffects(node);
RelaxEffectsAndControls(node);
node->set_op(common()->Select(kMachAnyTagged, input_hint));
node->ReplaceInput(0, input->InputAt(0));
for (int i = 1; i < input_count; ++i) {
......@@ -640,7 +648,7 @@ Reduction JSTypedLowering::ReduceJSToNumber(Node* node) {
NodeProperties::GetControlInput(node) != graph()->start()) {
// JSToNumber(x:plain-primitive,context,effect,control)
// => JSToNumber(x,no-context,start,start)
RelaxEffects(node);
RelaxEffectsAndControls(node);
NodeProperties::ReplaceContextInput(node, jsgraph()->NoContextConstant());
NodeProperties::ReplaceControlInput(node, graph()->start());
NodeProperties::ReplaceEffectInput(node, graph()->start());
......@@ -796,6 +804,7 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
node->ReplaceInput(3, effect);
node->ReplaceInput(4, control);
node->TrimInputCount(5);
RelaxControls(node);
return Changed(node);
}
// Compute byte offset.
......@@ -809,6 +818,7 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
node->ReplaceInput(4, effect);
node->ReplaceInput(5, control);
node->TrimInputCount(6);
RelaxControls(node);
return Changed(node);
}
}
......
......@@ -152,14 +152,18 @@ void NodeProperties::RemoveNonValueInputs(Node* node) {
// static
void NodeProperties::ReplaceWithValue(Node* node, Node* value, Node* effect) {
DCHECK(node->op()->ControlOutputCount() == 0);
if (!effect && node->op()->EffectInputCount() > 0) {
effect = NodeProperties::GetEffectInput(node);
}
// Requires distinguishing between value and effect edges.
// Requires distinguishing between value, effect and control edges.
for (Edge edge : node->use_edges()) {
if (IsEffectEdge(edge)) {
if (IsControlEdge(edge)) {
DCHECK_EQ(IrOpcode::kIfSuccess, edge.from()->opcode());
Node* control = NodeProperties::GetControlInput(node);
edge.from()->ReplaceUses(control);
edge.UpdateTo(NULL);
} else if (IsEffectEdge(edge)) {
DCHECK_NOT_NULL(effect);
edge.UpdateTo(effect);
} else {
......@@ -185,16 +189,13 @@ Node* NodeProperties::FindProjection(Node* node, size_t projection_index) {
void NodeProperties::CollectControlProjections(Node* node, Node** projections,
size_t projection_count) {
#ifdef DEBUG
DCHECK_EQ(static_cast<int>(projection_count), node->UseCount());
DCHECK_LE(static_cast<int>(projection_count), node->UseCount());
std::memset(projections, 0, sizeof(*projections) * projection_count);
#endif
size_t if_value_index = 0;
for (Node* const use : node->uses()) {
size_t index;
switch (use->opcode()) {
default:
UNREACHABLE();
// Fall through.
case IrOpcode::kIfTrue:
DCHECK_EQ(IrOpcode::kBranch, node->opcode());
index = 0;
......@@ -203,6 +204,14 @@ void NodeProperties::CollectControlProjections(Node* node, Node** projections,
DCHECK_EQ(IrOpcode::kBranch, node->opcode());
index = 1;
break;
case IrOpcode::kIfSuccess:
DCHECK_EQ(IrOpcode::kCall, node->opcode());
index = 0;
break;
case IrOpcode::kIfException:
DCHECK_EQ(IrOpcode::kCall, node->opcode());
index = 1;
break;
case IrOpcode::kIfValue:
DCHECK_EQ(IrOpcode::kSwitch, node->opcode());
index = if_value_index++;
......@@ -211,6 +220,8 @@ void NodeProperties::CollectControlProjections(Node* node, Node** projections,
DCHECK_EQ(IrOpcode::kSwitch, node->opcode());
index = projection_count - 1;
break;
default:
continue;
}
DCHECK_LT(if_value_index, projection_count);
DCHECK_LT(index, projection_count);
......
......@@ -81,7 +81,8 @@ class NodeProperties FINAL {
static void RemoveNonValueInputs(Node* node);
// Replace value uses of {node} with {value} and effect uses of {node} with
// {effect}. If {effect == NULL}, then use the effect input to {node}.
// {effect}. If {effect == NULL}, then use the effect input to {node}. All
// control uses will be relaxed assuming {node} cannot throw.
static void ReplaceWithValue(Node* node, Node* value, Node* effect = nullptr);
......@@ -91,8 +92,9 @@ class NodeProperties FINAL {
static Node* FindProjection(Node* node, size_t projection_index);
// Collect the branch-related projections from a node, such as IfTrue,
// IfFalse, IfValue and IfDefault.
// IfFalse, IfSuccess, IfException, IfValue and IfDefault.
// - Branch: [ IfTrue, IfFalse ]
// - Call : [ IfSuccess, IfException ]
// - Switch: [ IfValue, ..., IfDefault ]
static void CollectControlProjections(Node* node, Node** proj, size_t count);
......
......@@ -12,6 +12,8 @@
V(Branch) \
V(IfTrue) \
V(IfFalse) \
V(IfSuccess) \
V(IfException) \
V(Switch) \
V(IfValue) \
V(IfDefault) \
......@@ -299,8 +301,12 @@ class IrOpcode {
return kInt32Constant <= value && value <= kHeapConstant;
}
static bool IsPhiOpcode(Value val) {
return val == kPhi || val == kEffectPhi;
static bool IsPhiOpcode(Value value) {
return value == kPhi || value == kEffectPhi;
}
static bool IsMergeOpcode(Value value) {
return value == kMerge || value == kLoop;
}
};
......
......@@ -100,7 +100,8 @@ bool OperatorProperties::IsBasicBlockBegin(const Operator* op) {
return opcode == IrOpcode::kStart || opcode == IrOpcode::kEnd ||
opcode == IrOpcode::kDead || opcode == IrOpcode::kLoop ||
opcode == IrOpcode::kMerge || opcode == IrOpcode::kIfTrue ||
opcode == IrOpcode::kIfFalse || opcode == IrOpcode::kIfValue ||
opcode == IrOpcode::kIfFalse || opcode == IrOpcode::kIfSuccess ||
opcode == IrOpcode::kIfException || opcode == IrOpcode::kIfValue ||
opcode == IrOpcode::kIfDefault;
}
......
......@@ -97,6 +97,10 @@ class Operator : public ZoneObject {
int EffectOutputCount() const { return effect_out_; }
int ControlOutputCount() const { return control_out_; }
static size_t ZeroIfNoThrow(Properties properties) {
return (properties & kNoThrow) == kNoThrow ? 0 : 2;
}
static size_t ZeroIfPure(Properties properties) {
return (properties & kPure) == kPure ? 0 : 1;
}
......
......@@ -100,6 +100,8 @@ std::ostream& operator<<(std::ostream& os, const BasicBlock::Control& c) {
return os << "none";
case BasicBlock::kGoto:
return os << "goto";
case BasicBlock::kCall:
return os << "call";
case BasicBlock::kBranch:
return os << "branch";
case BasicBlock::kSwitch:
......@@ -194,16 +196,27 @@ void Schedule::AddNode(BasicBlock* block, Node* node) {
void Schedule::AddGoto(BasicBlock* block, BasicBlock* succ) {
DCHECK(block->control() == BasicBlock::kNone);
DCHECK_EQ(BasicBlock::kNone, block->control());
block->set_control(BasicBlock::kGoto);
AddSuccessor(block, succ);
}
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());
block->set_control(BasicBlock::kCall);
AddSuccessor(block, success_block);
AddSuccessor(block, exception_block);
SetControlInput(block, call);
}
void Schedule::AddBranch(BasicBlock* block, Node* branch, BasicBlock* tblock,
BasicBlock* fblock) {
DCHECK(block->control() == BasicBlock::kNone);
DCHECK(branch->opcode() == IrOpcode::kBranch);
DCHECK_EQ(BasicBlock::kNone, block->control());
DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
block->set_control(BasicBlock::kBranch);
AddSuccessor(block, tblock);
AddSuccessor(block, fblock);
......@@ -224,7 +237,7 @@ void Schedule::AddSwitch(BasicBlock* block, Node* sw, BasicBlock** succ_blocks,
void Schedule::AddReturn(BasicBlock* block, Node* input) {
DCHECK(block->control() == BasicBlock::kNone);
DCHECK_EQ(BasicBlock::kNone, block->control());
block->set_control(BasicBlock::kReturn);
SetControlInput(block, input);
if (block != end()) AddSuccessor(block, end());
......@@ -232,7 +245,7 @@ void Schedule::AddReturn(BasicBlock* block, Node* input) {
void Schedule::AddThrow(BasicBlock* block, Node* input) {
DCHECK(block->control() == BasicBlock::kNone);
DCHECK_EQ(BasicBlock::kNone, block->control());
block->set_control(BasicBlock::kThrow);
SetControlInput(block, input);
if (block != end()) AddSuccessor(block, end());
......@@ -241,8 +254,8 @@ void Schedule::AddThrow(BasicBlock* block, Node* input) {
void Schedule::InsertBranch(BasicBlock* block, BasicBlock* end, Node* branch,
BasicBlock* tblock, BasicBlock* fblock) {
DCHECK(block->control() != BasicBlock::kNone);
DCHECK(end->control() == BasicBlock::kNone);
DCHECK_NE(BasicBlock::kNone, block->control());
DCHECK_EQ(BasicBlock::kNone, end->control());
end->set_control(block->control());
block->set_control(BasicBlock::kBranch);
MoveSuccessors(block, end);
......
......@@ -32,6 +32,7 @@ class BasicBlock FINAL : public ZoneObject {
enum Control {
kNone, // Control not initialized yet.
kGoto, // Goto a single successor block.
kCall, // Call with continuation as first successor, exception second.
kBranch, // Branch if true to first successor, otherwise second.
kSwitch, // Table dispatch to one of the successor blocks.
kReturn, // Return a value from this method.
......@@ -233,6 +234,10 @@ class Schedule FINAL : public ZoneObject {
// BasicBlock building: add a goto to the end of {block}.
void AddGoto(BasicBlock* block, BasicBlock* succ);
// BasicBlock building: add a call at the end of {block}.
void AddCall(BasicBlock* block, Node* call, BasicBlock* success_block,
BasicBlock* exception_block);
// BasicBlock building: add a branch at the end of {block}.
void AddBranch(BasicBlock* block, Node* branch, BasicBlock* tblock,
BasicBlock* fblock);
......
......@@ -286,7 +286,7 @@ class CFGBuilder : public ZoneObject {
}
private:
// TODO(mstarzinger): Only for Scheduler::FuseFloatingControl.
friend class ScheduleLateNodeVisitor;
friend class Scheduler;
void FixNode(BasicBlock* block, Node* node) {
......@@ -320,6 +320,11 @@ class CFGBuilder : public ZoneObject {
case IrOpcode::kSwitch:
BuildBlocksForSuccessors(node);
break;
case IrOpcode::kCall:
if (IsExceptionalCall(node)) {
BuildBlocksForSuccessors(node);
}
break;
default:
break;
}
......@@ -347,6 +352,12 @@ class CFGBuilder : public ZoneObject {
scheduler_->UpdatePlacement(node, Scheduler::kFixed);
ConnectThrow(node);
break;
case IrOpcode::kCall:
if (IsExceptionalCall(node)) {
scheduler_->UpdatePlacement(node, Scheduler::kFixed);
ConnectCall(node);
}
break;
default:
break;
}
......@@ -381,6 +392,31 @@ class CFGBuilder : public ZoneObject {
}
}
BasicBlock* FindPredecessorBlock(Node* node) {
BasicBlock* predecessor_block = nullptr;
while (true) {
predecessor_block = schedule_->block(node);
if (predecessor_block != nullptr) break;
node = NodeProperties::GetControlInput(node);
}
return predecessor_block;
}
void ConnectCall(Node* call) {
BasicBlock* successor_blocks[2];
CollectSuccessorBlocks(call, successor_blocks, arraysize(successor_blocks));
// Consider the exception continuation to be deferred.
successor_blocks[1]->set_deferred(true);
Node* call_control = NodeProperties::GetControlInput(call);
BasicBlock* call_block = FindPredecessorBlock(call_control);
TraceConnect(call, call_block, successor_blocks[0]);
TraceConnect(call, call_block, successor_blocks[1]);
schedule_->AddCall(call_block, call, successor_blocks[0],
successor_blocks[1]);
}
void ConnectBranch(Node* branch) {
BasicBlock* successor_blocks[2];
CollectSuccessorBlocks(branch, successor_blocks,
......@@ -404,10 +440,8 @@ class CFGBuilder : public ZoneObject {
schedule_->InsertBranch(component_start_, component_end_, branch,
successor_blocks[0], successor_blocks[1]);
} else {
Node* branch_block_node = NodeProperties::GetControlInput(branch);
BasicBlock* branch_block = schedule_->block(branch_block_node);
DCHECK_NOT_NULL(branch_block);
Node* branch_control = NodeProperties::GetControlInput(branch);
BasicBlock* branch_block = FindPredecessorBlock(branch_control);
TraceConnect(branch, branch_block, successor_blocks[0]);
TraceConnect(branch, branch_block, successor_blocks[1]);
schedule_->AddBranch(branch_block, branch, successor_blocks[0],
......@@ -428,14 +462,12 @@ class CFGBuilder : public ZoneObject {
schedule_->InsertSwitch(component_start_, component_end_, sw,
successor_blocks, successor_count);
} else {
Node* sw_block_node = NodeProperties::GetControlInput(sw);
BasicBlock* sw_block = schedule_->block(sw_block_node);
DCHECK_NOT_NULL(sw_block);
Node* switch_control = NodeProperties::GetControlInput(sw);
BasicBlock* switch_block = FindPredecessorBlock(switch_control);
for (size_t index = 0; index < successor_count; ++index) {
TraceConnect(sw, sw_block, successor_blocks[index]);
TraceConnect(sw, switch_block, successor_blocks[index]);
}
schedule_->AddSwitch(sw_block, sw, successor_blocks, successor_count);
schedule_->AddSwitch(switch_block, sw, successor_blocks, successor_count);
}
}
......@@ -448,22 +480,22 @@ class CFGBuilder : public ZoneObject {
// For all of the merge's control inputs, add a goto at the end to the
// merge's basic block.
for (Node* const input : merge->inputs()) {
BasicBlock* predecessor_block = schedule_->block(input);
BasicBlock* predecessor_block = FindPredecessorBlock(input);
TraceConnect(merge, predecessor_block, block);
schedule_->AddGoto(predecessor_block, block);
}
}
void ConnectReturn(Node* ret) {
Node* return_block_node = NodeProperties::GetControlInput(ret);
BasicBlock* return_block = schedule_->block(return_block_node);
Node* return_control = NodeProperties::GetControlInput(ret);
BasicBlock* return_block = FindPredecessorBlock(return_control);
TraceConnect(ret, return_block, NULL);
schedule_->AddReturn(return_block, ret);
}
void ConnectThrow(Node* thr) {
Node* throw_block_node = NodeProperties::GetControlInput(thr);
BasicBlock* throw_block = schedule_->block(throw_block_node);
Node* throw_control = NodeProperties::GetControlInput(thr);
BasicBlock* throw_block = FindPredecessorBlock(throw_control);
TraceConnect(thr, throw_block, NULL);
schedule_->AddThrow(throw_block, thr);
}
......@@ -479,6 +511,13 @@ class CFGBuilder : public ZoneObject {
}
}
bool IsExceptionalCall(Node* node) {
for (Node* const use : node->uses()) {
if (use->opcode() == IrOpcode::kIfException) return true;
}
return false;
}
bool IsFinalMerge(Node* node) {
return (node->opcode() == IrOpcode::kMerge &&
node == scheduler_->graph_->end()->InputAt(0));
......@@ -1324,7 +1363,7 @@ class ScheduleLateNodeVisitor {
}
// Schedule the node or a floating control structure.
if (NodeProperties::IsControl(node)) {
if (IrOpcode::IsMergeOpcode(node->opcode())) {
ScheduleFloatingControl(block, node);
} else {
ScheduleNode(block, node);
......@@ -1448,10 +1487,13 @@ class ScheduleLateNodeVisitor {
return block;
}
BasicBlock* FindPredecessorBlock(Node* node) {
return scheduler_->control_flow_builder_->FindPredecessorBlock(node);
}
BasicBlock* GetBlockForUse(Edge edge) {
Node* use = edge.from();
IrOpcode::Value opcode = use->opcode();
if (IrOpcode::IsPhiOpcode(opcode)) {
if (IrOpcode::IsPhiOpcode(use->opcode())) {
// If the use is from a coupled (i.e. floating) phi, compute the common
// dominator of its uses. This will not recurse more than one level.
if (scheduler_->GetPlacement(use) == Scheduler::kCoupled) {
......@@ -1460,15 +1502,23 @@ class ScheduleLateNodeVisitor {
DCHECK_EQ(edge.to(), NodeProperties::GetControlInput(use));
return GetCommonDominatorOfUses(use);
}
// If the use is from a fixed (i.e. non-floating) phi, use the block
// of the corresponding control input to the merge.
// If the use is from a fixed (i.e. non-floating) phi, we use the
// predecessor block of the corresponding control input to the merge.
if (scheduler_->GetPlacement(use) == Scheduler::kFixed) {
Trace(" input@%d into a fixed phi #%d:%s\n", edge.index(), use->id(),
use->op()->mnemonic());
Node* merge = NodeProperties::GetControlInput(use, 0);
opcode = merge->opcode();
DCHECK(opcode == IrOpcode::kMerge || opcode == IrOpcode::kLoop);
use = NodeProperties::GetControlInput(merge, edge.index());
DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
Node* input = NodeProperties::GetControlInput(merge, edge.index());
return FindPredecessorBlock(input);
}
} else if (IrOpcode::IsMergeOpcode(use->opcode())) {
// If the use is from a fixed (i.e. non-floating) merge, we use the
// predecessor block of the current input to the merge.
if (scheduler_->GetPlacement(use) == Scheduler::kFixed) {
Trace(" input@%d into a fixed merge #%d:%s\n", edge.index(), use->id(),
use->op()->mnemonic());
return FindPredecessorBlock(edge.to());
}
}
BasicBlock* result = schedule_->block(use);
......
......@@ -228,6 +228,15 @@ void Verifier::Visitor::Check(Node* node) {
// Type is empty.
CheckNotTyped(node);
break;
case IrOpcode::kIfSuccess:
case IrOpcode::kIfException: {
// IfSuccess and IfException continuation only on throwing nodes.
Node* input = NodeProperties::GetControlInput(node, 0);
CHECK(!input->op()->HasProperty(Operator::kNoThrow));
// Type is empty.
CheckNotTyped(node);
break;
}
case IrOpcode::kSwitch: {
// Switch uses are Case and Default.
int count_case = 0, count_default = 0;
......
......@@ -118,6 +118,28 @@ TEST(CatchBreak) {
}
TEST(CatchCall) {
i::FLAG_turbo_exceptions = true;
const char* src =
"(function(fun) {"
" var r = '-';"
" try {"
" r += 'A-';"
" fun();"
" } catch (e) {"
" r += e;"
" }"
" return r;"
"})";
FunctionTester T(src);
CompileRun("function thrower() { throw 'T-'; }");
#if 0 // TODO(mstarzinger): Enable once we have exception handlers.
T.CheckCall(T.Val("-A-T-"), T.NewFunction("thrower"));
#endif
}
TEST(Finally) {
i::FLAG_turbo_exceptions = true;
const char* src =
......
......@@ -53,6 +53,8 @@ const SharedOperator kSharedOperators[] = {
SHARED(End, Operator::kKontrol, 0, 0, 1, 0, 0, 0),
SHARED(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1),
SHARED(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1),
SHARED(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1),
SHARED(IfException, Operator::kKontrol, 0, 0, 1, 0, 0, 1),
SHARED(Throw, Operator::kFoldable, 1, 1, 1, 0, 0, 1),
SHARED(Return, Operator::kNoThrow, 1, 1, 1, 0, 0, 1)
#undef SHARED
......
......@@ -28,6 +28,7 @@ struct SharedOperator {
int control_input_count;
int value_output_count;
int effect_output_count;
int control_output_count;
};
......@@ -39,48 +40,49 @@ std::ostream& operator<<(std::ostream& os, const SharedOperator& sop) {
const SharedOperator kSharedOperators[] = {
#define SHARED(Name, properties, value_input_count, frame_state_input_count, \
effect_input_count, control_input_count, value_output_count, \
effect_output_count) \
effect_output_count, control_output_count) \
{ \
&JSOperatorBuilder::Name, IrOpcode::kJS##Name, properties, \
value_input_count, frame_state_input_count, effect_input_count, \
control_input_count, value_output_count, effect_output_count \
control_input_count, value_output_count, effect_output_count, \
control_output_count \
}
SHARED(Equal, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(NotEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(StrictEqual, Operator::kPure, 2, 0, 0, 0, 1, 0),
SHARED(StrictNotEqual, Operator::kPure, 2, 0, 0, 0, 1, 0),
SHARED(LessThan, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(GreaterThan, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(LessThanOrEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(GreaterThanOrEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(BitwiseOr, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(BitwiseXor, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(BitwiseAnd, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(ShiftLeft, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(ShiftRight, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(ShiftRightLogical, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(Add, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(Subtract, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(Multiply, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(Divide, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(Modulus, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(UnaryNot, Operator::kPure, 1, 0, 0, 0, 1, 0),
SHARED(ToBoolean, Operator::kPure, 1, 0, 0, 0, 1, 0),
SHARED(ToNumber, Operator::kNoProperties, 1, 1, 1, 1, 1, 1),
SHARED(ToString, Operator::kNoProperties, 1, 0, 1, 1, 1, 1),
SHARED(ToName, Operator::kNoProperties, 1, 1, 1, 1, 1, 1),
SHARED(ToObject, Operator::kNoProperties, 1, 1, 1, 1, 1, 1),
SHARED(Yield, Operator::kNoProperties, 1, 0, 1, 1, 1, 1),
SHARED(Create, Operator::kEliminatable, 0, 0, 1, 1, 1, 1),
SHARED(HasProperty, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(TypeOf, Operator::kPure, 1, 0, 0, 0, 1, 0),
SHARED(InstanceOf, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(Debugger, Operator::kNoProperties, 0, 0, 1, 1, 0, 1),
SHARED(CreateFunctionContext, Operator::kNoProperties, 1, 0, 1, 1, 1, 1),
SHARED(CreateWithContext, Operator::kNoProperties, 2, 1, 1, 1, 1, 1),
SHARED(CreateBlockContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1),
SHARED(CreateModuleContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1),
SHARED(CreateScriptContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1)
SHARED(Equal, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(NotEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(StrictEqual, Operator::kPure, 2, 0, 0, 0, 1, 0, 0),
SHARED(StrictNotEqual, Operator::kPure, 2, 0, 0, 0, 1, 0, 0),
SHARED(LessThan, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(GreaterThan, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(LessThanOrEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(GreaterThanOrEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(BitwiseOr, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(BitwiseXor, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(BitwiseAnd, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(ShiftLeft, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(ShiftRight, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(ShiftRightLogical, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(Add, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(Subtract, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(Multiply, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(Divide, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(Modulus, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(UnaryNot, Operator::kPure, 1, 0, 0, 0, 1, 0, 0),
SHARED(ToBoolean, Operator::kPure, 1, 0, 0, 0, 1, 0, 0),
SHARED(ToNumber, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToString, Operator::kNoProperties, 1, 0, 1, 1, 1, 1, 2),
SHARED(ToName, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToObject, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(Yield, Operator::kNoProperties, 1, 0, 1, 1, 1, 1, 2),
SHARED(Create, Operator::kEliminatable, 0, 0, 1, 1, 1, 1, 0),
SHARED(HasProperty, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(TypeOf, Operator::kPure, 1, 0, 0, 0, 1, 0, 0),
SHARED(InstanceOf, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(Debugger, Operator::kNoProperties, 0, 0, 1, 1, 0, 1, 2),
SHARED(CreateFunctionContext, Operator::kNoProperties, 1, 0, 1, 1, 1, 1, 2),
SHARED(CreateWithContext, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(CreateBlockContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1, 2),
SHARED(CreateModuleContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1, 2),
SHARED(CreateScriptContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1, 2)
#undef SHARED
};
......@@ -122,7 +124,7 @@ TEST_P(JSSharedOperatorTest, NumberOfInputsAndOutputs) {
EXPECT_EQ(sop.value_output_count, op->ValueOutputCount());
EXPECT_EQ(sop.effect_output_count, op->EffectOutputCount());
EXPECT_EQ(0, op->ControlOutputCount());
EXPECT_EQ(sop.control_output_count, op->ControlOutputCount());
}
......@@ -181,7 +183,7 @@ TEST_P(JSStorePropertyOperatorTest, NumberOfInputsAndOutputs) {
EXPECT_EQ(0, op->ValueOutputCount());
EXPECT_EQ(1, op->EffectOutputCount());
EXPECT_EQ(0, op->ControlOutputCount());
EXPECT_EQ(2, op->ControlOutputCount());
}
......
......@@ -8,6 +8,7 @@
#include "testing/gmock/include/gmock/gmock.h"
using testing::AnyOf;
using testing::ElementsAre;
using testing::IsNull;
namespace v8 {
......@@ -17,6 +18,62 @@ namespace compiler {
typedef TestWithZone NodePropertiesTest;
namespace {
const Operator kMockOperator(IrOpcode::kDead, Operator::kNoProperties,
"MockOperator", 0, 0, 0, 1, 0, 0);
const Operator kMockOpEffect(IrOpcode::kDead, Operator::kNoProperties,
"MockOpEffect", 0, 1, 0, 1, 1, 0);
const Operator kMockOpControl(IrOpcode::kDead, Operator::kNoProperties,
"MockOpControl", 0, 0, 1, 1, 0, 1);
} // namespace
TEST_F(NodePropertiesTest, ReplaceWithValue_ValueUse) {
CommonOperatorBuilder common(zone());
Node* node = Node::New(zone(), 0, &kMockOperator, 0, nullptr, false);
Node* use_value = Node::New(zone(), 0, common.Return(), 1, &node, false);
Node* replacement = Node::New(zone(), 0, &kMockOperator, 0, nullptr, false);
NodeProperties::ReplaceWithValue(node, replacement);
EXPECT_EQ(replacement, use_value->InputAt(0));
EXPECT_EQ(0, node->UseCount());
EXPECT_EQ(1, replacement->UseCount());
EXPECT_THAT(replacement->uses(), ElementsAre(use_value));
}
TEST_F(NodePropertiesTest, ReplaceWithValue_EffectUse) {
CommonOperatorBuilder common(zone());
Node* start = Node::New(zone(), 0, common.Start(1), 0, nullptr, false);
Node* node = Node::New(zone(), 0, &kMockOpEffect, 1, &start, false);
Node* use_effect = Node::New(zone(), 0, common.EffectPhi(1), 1, &node, false);
Node* replacement = Node::New(zone(), 0, &kMockOperator, 0, nullptr, false);
NodeProperties::ReplaceWithValue(node, replacement);
EXPECT_EQ(start, use_effect->InputAt(0));
EXPECT_EQ(0, node->UseCount());
EXPECT_EQ(2, start->UseCount());
EXPECT_EQ(0, replacement->UseCount());
EXPECT_THAT(start->uses(), ElementsAre(node, use_effect));
}
TEST_F(NodePropertiesTest, ReplaceWithValue_ControlUse) {
CommonOperatorBuilder common(zone());
Node* start = Node::New(zone(), 0, common.Start(1), 0, nullptr, false);
Node* node = Node::New(zone(), 0, &kMockOpControl, 1, &start, false);
Node* success = Node::New(zone(), 0, common.IfSuccess(), 1, &node, false);
Node* use_control = Node::New(zone(), 0, common.Merge(1), 1, &success, false);
Node* replacement = Node::New(zone(), 0, &kMockOperator, 0, nullptr, false);
NodeProperties::ReplaceWithValue(node, replacement);
EXPECT_EQ(start, use_control->InputAt(0));
EXPECT_EQ(0, node->UseCount());
EXPECT_EQ(2, start->UseCount());
EXPECT_EQ(0, replacement->UseCount());
EXPECT_THAT(start->uses(), ElementsAre(node, use_control));
}
TEST_F(NodePropertiesTest, FindProjection) {
CommonOperatorBuilder common(zone());
Node* start = Node::New(zone(), 0, common.Start(1), 0, nullptr, false);
......
......@@ -73,8 +73,10 @@ typedef TestWithZone ScheduleTest;
namespace {
const Operator kCallOperator(IrOpcode::kCall, Operator::kNoProperties,
"MockCall", 0, 0, 0, 0, 0, 0);
const Operator kBranchOperator(IrOpcode::kBranch, Operator::kNoProperties,
"Branch", 0, 0, 0, 0, 0, 0);
"MockBranch", 0, 0, 0, 0, 0, 0);
const Operator kDummyOperator(IrOpcode::kParameter, Operator::kNoProperties,
"Dummy", 0, 0, 0, 0, 0, 0);
......@@ -135,6 +137,35 @@ TEST_F(ScheduleTest, AddGoto) {
}
TEST_F(ScheduleTest, AddCall) {
Schedule schedule(zone());
BasicBlock* start = schedule.start();
Node* call = Node::New(zone(), 0, &kCallOperator, 0, nullptr, false);
BasicBlock* sblock = schedule.NewBasicBlock();
BasicBlock* eblock = schedule.NewBasicBlock();
schedule.AddCall(start, call, sblock, eblock);
EXPECT_EQ(start, schedule.block(call));
EXPECT_EQ(0u, start->PredecessorCount());
EXPECT_EQ(2u, start->SuccessorCount());
EXPECT_EQ(sblock, start->SuccessorAt(0));
EXPECT_EQ(eblock, start->SuccessorAt(1));
EXPECT_THAT(start->successors(), ElementsAre(sblock, eblock));
EXPECT_EQ(1u, sblock->PredecessorCount());
EXPECT_EQ(0u, sblock->SuccessorCount());
EXPECT_EQ(start, sblock->PredecessorAt(0));
EXPECT_THAT(sblock->predecessors(), ElementsAre(start));
EXPECT_EQ(1u, eblock->PredecessorCount());
EXPECT_EQ(0u, eblock->SuccessorCount());
EXPECT_EQ(start, eblock->PredecessorAt(0));
EXPECT_THAT(eblock->predecessors(), ElementsAre(start));
}
TEST_F(ScheduleTest, AddBranch) {
Schedule schedule(zone());
BasicBlock* start = schedule.start();
......
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