[turbofan] Introduce node regions for protection from scheduling.
This CL re-purposes ValueEffect and Finish as delimiters for regions that are scheduled atomically (renamed to BeginRegion, FinishRegion). The BeginRegion node takes and produces an effect. For the uses that do not care about the placement in the effect chain, it is ok to feed graph->start() as an effect input. The FinishRegion takes a value and an effect and produces a value and an effect. It is important that any value or effect produced inside the region is not used outside the region. The FinishRegion node is the only way to smuggle an effect and a value out. At the moment, this does not support control flow inside the region. Control flow would be hard. During scheduling we do some sanity check, but the checks are not exhaustive. Here is what we check: - the effect chain between begin and finish is linear (no splitting, single effect input and output). - any value produced is consumed by the FinishRegion node. - no control flow outputs. Review URL: https://codereview.chromium.org/1399423002 Cr-Commit-Position: refs/heads/master@{#31265}
This commit is contained in:
parent
1919fa38c2
commit
59c616ccd7
@ -66,7 +66,7 @@ Node* ChangeLowering::AllocateHeapNumberWithValue(Node* value, Node* control) {
|
||||
Callable callable = CodeFactory::AllocateHeapNumber(isolate());
|
||||
Node* target = jsgraph()->HeapConstant(callable.code());
|
||||
Node* context = jsgraph()->NoContextConstant();
|
||||
Node* effect = graph()->NewNode(common()->ValueEffect(1), value);
|
||||
Node* effect = graph()->NewNode(common()->BeginRegion(), graph()->start());
|
||||
if (!allocate_heap_number_operator_.is_set()) {
|
||||
CallDescriptor* descriptor = Linkage::GetStubCallDescriptor(
|
||||
isolate(), jsgraph()->zone(), callable.descriptor(), 0,
|
||||
@ -78,7 +78,7 @@ Node* ChangeLowering::AllocateHeapNumberWithValue(Node* value, Node* control) {
|
||||
Node* store = graph()->NewNode(
|
||||
machine()->Store(StoreRepresentation(kMachFloat64, kNoWriteBarrier)),
|
||||
heap_number, HeapNumberValueIndexConstant(), value, heap_number, control);
|
||||
return graph()->NewNode(common()->Finish(1), heap_number, store);
|
||||
return graph()->NewNode(common()->FinishRegion(), heap_number, store);
|
||||
}
|
||||
|
||||
|
||||
|
@ -125,7 +125,9 @@ std::ostream& operator<<(std::ostream& os, ParameterInfo const& i) {
|
||||
V(Deoptimize, Operator::kNoThrow, 1, 1, 1, 0, 0, 1) \
|
||||
V(Terminate, Operator::kKontrol, 0, 1, 1, 0, 0, 1) \
|
||||
V(OsrNormalEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1) \
|
||||
V(OsrLoopEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1)
|
||||
V(OsrLoopEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1) \
|
||||
V(BeginRegion, Operator::kNoThrow, 0, 1, 0, 0, 1, 0) \
|
||||
V(FinishRegion, Operator::kNoThrow, 1, 1, 0, 1, 1, 0)
|
||||
|
||||
|
||||
#define CACHED_RETURN_LIST(V) \
|
||||
@ -678,24 +680,6 @@ const Operator* CommonOperatorBuilder::EffectSet(int arguments) {
|
||||
}
|
||||
|
||||
|
||||
const Operator* CommonOperatorBuilder::ValueEffect(int arguments) {
|
||||
DCHECK(arguments > 0); // Disallow empty value effects.
|
||||
return new (zone()) Operator( // --
|
||||
IrOpcode::kValueEffect, Operator::kPure, // opcode
|
||||
"ValueEffect", // name
|
||||
arguments, 0, 0, 0, 1, 0); // counts
|
||||
}
|
||||
|
||||
|
||||
const Operator* CommonOperatorBuilder::Finish(int arguments) {
|
||||
DCHECK(arguments > 0); // Disallow empty finishes.
|
||||
return new (zone()) Operator( // --
|
||||
IrOpcode::kFinish, Operator::kPure, // opcode
|
||||
"Finish", // name
|
||||
1, arguments, 0, 1, 0, 0); // counts
|
||||
}
|
||||
|
||||
|
||||
const Operator* CommonOperatorBuilder::StateValues(int arguments) {
|
||||
switch (arguments) {
|
||||
#define CACHED_STATE_VALUES(arguments) \
|
||||
|
@ -145,8 +145,8 @@ class CommonOperatorBuilder final : public ZoneObject {
|
||||
const Operator* Phi(MachineType type, int value_input_count);
|
||||
const Operator* EffectPhi(int effect_input_count);
|
||||
const Operator* EffectSet(int arguments);
|
||||
const Operator* ValueEffect(int arguments);
|
||||
const Operator* Finish(int arguments);
|
||||
const Operator* BeginRegion();
|
||||
const Operator* FinishRegion();
|
||||
const Operator* StateValues(int arguments);
|
||||
const Operator* TypedStateValues(const ZoneVector<MachineType>* types);
|
||||
const Operator* FrameState(BailoutId bailout_id,
|
||||
|
@ -545,12 +545,13 @@ void InstructionSelector::VisitNode(Node* node) {
|
||||
case IrOpcode::kEffectPhi:
|
||||
case IrOpcode::kMerge:
|
||||
case IrOpcode::kTerminate:
|
||||
case IrOpcode::kBeginRegion:
|
||||
// No code needed for these graph artifacts.
|
||||
return;
|
||||
case IrOpcode::kIfException:
|
||||
return MarkAsReference(node), VisitIfException(node);
|
||||
case IrOpcode::kFinish:
|
||||
return MarkAsReference(node), VisitFinish(node);
|
||||
case IrOpcode::kFinishRegion:
|
||||
return MarkAsReference(node), VisitFinishRegion(node);
|
||||
case IrOpcode::kParameter: {
|
||||
MachineType type =
|
||||
linkage()->GetParameterType(ParameterIndexOf(node->op()));
|
||||
@ -925,7 +926,7 @@ void InstructionSelector::VisitBitcastInt64ToFloat64(Node* node) {
|
||||
#endif // V8_TARGET_ARCH_32_BIT
|
||||
|
||||
|
||||
void InstructionSelector::VisitFinish(Node* node) {
|
||||
void InstructionSelector::VisitFinishRegion(Node* node) {
|
||||
OperandGenerator g(this);
|
||||
Node* value = node->InputAt(0);
|
||||
Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value));
|
||||
|
@ -200,7 +200,7 @@ class InstructionSelector final {
|
||||
MACHINE_OP_LIST(DECLARE_GENERATOR)
|
||||
#undef DECLARE_GENERATOR
|
||||
|
||||
void VisitFinish(Node* node);
|
||||
void VisitFinishRegion(Node* node);
|
||||
void VisitParameter(Node* node);
|
||||
void VisitIfException(Node* node);
|
||||
void VisitOsrValue(Node* node);
|
||||
|
@ -46,6 +46,7 @@ class AllocationBuilder final {
|
||||
|
||||
// Primitive allocation of static size.
|
||||
void Allocate(int size) {
|
||||
effect_ = graph()->NewNode(jsgraph()->common()->BeginRegion(), effect_);
|
||||
allocation_ = graph()->NewNode(
|
||||
simplified()->Allocate(), jsgraph()->Constant(size), effect_, control_);
|
||||
effect_ = allocation_;
|
||||
@ -70,8 +71,13 @@ class AllocationBuilder final {
|
||||
Store(access, jsgraph()->Constant(value));
|
||||
}
|
||||
|
||||
Node* allocation() const { return allocation_; }
|
||||
Node* effect() const { return effect_; }
|
||||
void Finish(Node* node) {
|
||||
NodeProperties::SetType(allocation_, NodeProperties::GetType(node));
|
||||
node->ReplaceInput(0, allocation_);
|
||||
node->ReplaceInput(1, effect_);
|
||||
node->TrimInputCount(2);
|
||||
NodeProperties::ChangeOp(node, jsgraph()->common()->FinishRegion());
|
||||
}
|
||||
|
||||
protected:
|
||||
JSGraph* jsgraph() { return jsgraph_; }
|
||||
@ -1274,13 +1280,8 @@ Reduction JSTypedLowering::ReduceJSCreateFunctionContext(Node* node) {
|
||||
for (int i = Context::MIN_CONTEXT_SLOTS; i < context_length; ++i) {
|
||||
a.Store(AccessBuilder::ForContextSlot(i), jsgraph()->TheHoleConstant());
|
||||
}
|
||||
// TODO(mstarzinger): We could mutate {node} into the allocation instead.
|
||||
NodeProperties::SetType(a.allocation(), NodeProperties::GetType(node));
|
||||
ReplaceWithValue(node, node, a.effect());
|
||||
node->ReplaceInput(0, a.allocation());
|
||||
node->ReplaceInput(1, a.effect());
|
||||
node->TrimInputCount(2);
|
||||
NodeProperties::ChangeOp(node, common()->Finish(1));
|
||||
RelaxControls(node);
|
||||
a.Finish(node);
|
||||
return Changed(node);
|
||||
}
|
||||
|
||||
@ -1325,13 +1326,8 @@ Reduction JSTypedLowering::ReduceJSCreateWithContext(Node* node) {
|
||||
a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
|
||||
a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), input);
|
||||
a.Store(AccessBuilder::ForContextSlot(Context::GLOBAL_OBJECT_INDEX), load);
|
||||
// TODO(mstarzinger): We could mutate {node} into the allocation instead.
|
||||
NodeProperties::SetType(a.allocation(), NodeProperties::GetType(node));
|
||||
ReplaceWithValue(node, node, a.effect());
|
||||
node->ReplaceInput(0, a.allocation());
|
||||
node->ReplaceInput(1, a.effect());
|
||||
node->TrimInputCount(2);
|
||||
NodeProperties::ChangeOp(node, common()->Finish(1));
|
||||
RelaxControls(node);
|
||||
a.Finish(node);
|
||||
return Changed(node);
|
||||
}
|
||||
|
||||
@ -1366,13 +1362,8 @@ Reduction JSTypedLowering::ReduceJSCreateBlockContext(Node* node) {
|
||||
for (int i = Context::MIN_CONTEXT_SLOTS; i < context_length; ++i) {
|
||||
a.Store(AccessBuilder::ForContextSlot(i), jsgraph()->TheHoleConstant());
|
||||
}
|
||||
// TODO(mstarzinger): We could mutate {node} into the allocation instead.
|
||||
NodeProperties::SetType(a.allocation(), NodeProperties::GetType(node));
|
||||
ReplaceWithValue(node, node, a.effect());
|
||||
node->ReplaceInput(0, a.allocation());
|
||||
node->ReplaceInput(1, a.effect());
|
||||
node->TrimInputCount(2);
|
||||
NodeProperties::ChangeOp(node, common()->Finish(1));
|
||||
RelaxControls(node);
|
||||
a.Finish(node);
|
||||
return Changed(node);
|
||||
}
|
||||
|
||||
|
@ -44,8 +44,8 @@
|
||||
V(Phi) \
|
||||
V(EffectSet) \
|
||||
V(EffectPhi) \
|
||||
V(ValueEffect) \
|
||||
V(Finish) \
|
||||
V(BeginRegion) \
|
||||
V(FinishRegion) \
|
||||
V(FrameState) \
|
||||
V(StateValues) \
|
||||
V(TypedStateValues) \
|
||||
|
@ -1394,6 +1394,8 @@ class ScheduleLateNodeVisitor {
|
||||
// Schedule the node or a floating control structure.
|
||||
if (IrOpcode::IsMergeOpcode(node->opcode())) {
|
||||
ScheduleFloatingControl(block, node);
|
||||
} else if (node->opcode() == IrOpcode::kFinishRegion) {
|
||||
ScheduleRegion(block, node);
|
||||
} else {
|
||||
ScheduleNode(block, node);
|
||||
}
|
||||
@ -1572,6 +1574,34 @@ class ScheduleLateNodeVisitor {
|
||||
scheduler_->FuseFloatingControl(block, node);
|
||||
}
|
||||
|
||||
void ScheduleRegion(BasicBlock* block, Node* region_end) {
|
||||
// We only allow regions of instructions connected into a linear
|
||||
// effect chain. The only value allowed to be produced by a node
|
||||
// in the chain must be the value consumed by the FinishRegion node.
|
||||
|
||||
// We schedule back to front; we first schedule FinishRegion.
|
||||
CHECK_EQ(IrOpcode::kFinishRegion, region_end->opcode());
|
||||
ScheduleNode(block, region_end);
|
||||
|
||||
// Schedule the chain.
|
||||
Node* node = NodeProperties::GetEffectInput(region_end);
|
||||
while (node->opcode() != IrOpcode::kBeginRegion) {
|
||||
DCHECK_EQ(0, scheduler_->GetData(node)->unscheduled_count_);
|
||||
DCHECK_EQ(1, node->op()->EffectInputCount());
|
||||
DCHECK_EQ(1, node->op()->EffectOutputCount());
|
||||
DCHECK_EQ(0, node->op()->ControlOutputCount());
|
||||
// The value output (if there is any) must be consumed
|
||||
// by the EndRegion node.
|
||||
DCHECK(node->op()->ValueOutputCount() == 0 ||
|
||||
node == region_end->InputAt(0));
|
||||
ScheduleNode(block, node);
|
||||
node = NodeProperties::GetEffectInput(node);
|
||||
}
|
||||
// Schedule the BeginRegion node.
|
||||
DCHECK_EQ(0, scheduler_->GetData(node)->unscheduled_count_);
|
||||
ScheduleNode(block, node);
|
||||
}
|
||||
|
||||
void ScheduleNode(BasicBlock* block, Node* node) {
|
||||
schedule_->PlanNode(block, node);
|
||||
scheduler_->scheduled_nodes_[block->id().ToSize()].push_back(node);
|
||||
|
@ -554,13 +554,13 @@ Type* Typer::Visitor::TypeEffectSet(Node* node) {
|
||||
}
|
||||
|
||||
|
||||
Type* Typer::Visitor::TypeValueEffect(Node* node) {
|
||||
Type* Typer::Visitor::TypeBeginRegion(Node* node) {
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
Type* Typer::Visitor::TypeFinish(Node* node) { return Operand(node, 0); }
|
||||
Type* Typer::Visitor::TypeFinishRegion(Node* node) { return Operand(node, 0); }
|
||||
|
||||
|
||||
Type* Typer::Visitor::TypeFrameState(Node* node) {
|
||||
|
@ -413,10 +413,10 @@ void Verifier::Visitor::Check(Node* node) {
|
||||
CHECK_LT(1, effect_count);
|
||||
break;
|
||||
}
|
||||
case IrOpcode::kValueEffect:
|
||||
case IrOpcode::kBeginRegion:
|
||||
// TODO(rossberg): what are the constraints on these?
|
||||
break;
|
||||
case IrOpcode::kFinish: {
|
||||
case IrOpcode::kFinishRegion: {
|
||||
// TODO(rossberg): what are the constraints on these?
|
||||
// Type must be subsumed by input type.
|
||||
if (typing == TYPED) {
|
||||
|
@ -128,9 +128,10 @@ TARGET_TEST_P(ChangeLoweringCommonTest, ChangeFloat64ToTagged) {
|
||||
Capture<Node*> heap_number;
|
||||
EXPECT_THAT(
|
||||
r.replacement(),
|
||||
IsFinish(
|
||||
IsFinishRegion(
|
||||
AllOf(CaptureEq(&heap_number),
|
||||
IsAllocateHeapNumber(IsValueEffect(value), graph()->start())),
|
||||
IsAllocateHeapNumber(IsBeginRegion(graph()->start()),
|
||||
graph()->start())),
|
||||
IsStore(StoreRepresentation(kMachFloat64, kNoWriteBarrier),
|
||||
CaptureEq(&heap_number),
|
||||
IsIntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag),
|
||||
@ -218,14 +219,15 @@ TARGET_TEST_F(ChangeLowering32Test, ChangeInt32ToTagged) {
|
||||
EXPECT_THAT(
|
||||
r.replacement(),
|
||||
IsPhi(kMachAnyTagged,
|
||||
IsFinish(AllOf(CaptureEq(&heap_number),
|
||||
IsAllocateHeapNumber(_, CaptureEq(&if_true))),
|
||||
IsStore(StoreRepresentation(kMachFloat64, kNoWriteBarrier),
|
||||
CaptureEq(&heap_number),
|
||||
IsIntPtrConstant(HeapNumber::kValueOffset -
|
||||
kHeapObjectTag),
|
||||
IsChangeInt32ToFloat64(value),
|
||||
CaptureEq(&heap_number), CaptureEq(&if_true))),
|
||||
IsFinishRegion(
|
||||
AllOf(CaptureEq(&heap_number),
|
||||
IsAllocateHeapNumber(_, CaptureEq(&if_true))),
|
||||
IsStore(
|
||||
StoreRepresentation(kMachFloat64, kNoWriteBarrier),
|
||||
CaptureEq(&heap_number),
|
||||
IsIntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag),
|
||||
IsChangeInt32ToFloat64(value), CaptureEq(&heap_number),
|
||||
CaptureEq(&if_true))),
|
||||
IsProjection(0, AllOf(CaptureEq(&add),
|
||||
IsInt32AddWithOverflow(value, value))),
|
||||
IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))),
|
||||
@ -319,14 +321,15 @@ TARGET_TEST_F(ChangeLowering32Test, ChangeUint32ToTagged) {
|
||||
IsPhi(
|
||||
kMachAnyTagged,
|
||||
IsWord32Shl(value, IsInt32Constant(kSmiTagSize + kSmiShiftSize)),
|
||||
IsFinish(AllOf(CaptureEq(&heap_number),
|
||||
IsAllocateHeapNumber(_, CaptureEq(&if_false))),
|
||||
IsStore(StoreRepresentation(kMachFloat64, kNoWriteBarrier),
|
||||
CaptureEq(&heap_number),
|
||||
IsInt32Constant(HeapNumber::kValueOffset -
|
||||
kHeapObjectTag),
|
||||
IsChangeUint32ToFloat64(value),
|
||||
CaptureEq(&heap_number), CaptureEq(&if_false))),
|
||||
IsFinishRegion(
|
||||
AllOf(CaptureEq(&heap_number),
|
||||
IsAllocateHeapNumber(_, CaptureEq(&if_false))),
|
||||
IsStore(
|
||||
StoreRepresentation(kMachFloat64, kNoWriteBarrier),
|
||||
CaptureEq(&heap_number),
|
||||
IsInt32Constant(HeapNumber::kValueOffset - kHeapObjectTag),
|
||||
IsChangeUint32ToFloat64(value), CaptureEq(&heap_number),
|
||||
CaptureEq(&if_false))),
|
||||
IsMerge(IsIfTrue(AllOf(
|
||||
CaptureEq(&branch),
|
||||
IsBranch(IsUint32LessThanOrEqual(
|
||||
@ -443,14 +446,15 @@ TARGET_TEST_F(ChangeLowering64Test, ChangeUint32ToTagged) {
|
||||
kMachAnyTagged,
|
||||
IsWord64Shl(IsChangeUint32ToUint64(value),
|
||||
IsInt64Constant(kSmiTagSize + kSmiShiftSize)),
|
||||
IsFinish(AllOf(CaptureEq(&heap_number),
|
||||
IsAllocateHeapNumber(_, CaptureEq(&if_false))),
|
||||
IsStore(StoreRepresentation(kMachFloat64, kNoWriteBarrier),
|
||||
CaptureEq(&heap_number),
|
||||
IsInt64Constant(HeapNumber::kValueOffset -
|
||||
kHeapObjectTag),
|
||||
IsChangeUint32ToFloat64(value),
|
||||
CaptureEq(&heap_number), CaptureEq(&if_false))),
|
||||
IsFinishRegion(
|
||||
AllOf(CaptureEq(&heap_number),
|
||||
IsAllocateHeapNumber(_, CaptureEq(&if_false))),
|
||||
IsStore(
|
||||
StoreRepresentation(kMachFloat64, kNoWriteBarrier),
|
||||
CaptureEq(&heap_number),
|
||||
IsInt64Constant(HeapNumber::kValueOffset - kHeapObjectTag),
|
||||
IsChangeUint32ToFloat64(value), CaptureEq(&heap_number),
|
||||
CaptureEq(&if_false))),
|
||||
IsMerge(IsIfTrue(AllOf(
|
||||
CaptureEq(&branch),
|
||||
IsBranch(IsUint32LessThanOrEqual(
|
||||
|
@ -358,28 +358,24 @@ TEST_F(CommonOperatorTest, NumberConstant) {
|
||||
}
|
||||
|
||||
|
||||
TEST_F(CommonOperatorTest, ValueEffect) {
|
||||
TRACED_FOREACH(int, arguments, kArguments) {
|
||||
const Operator* op = common()->ValueEffect(arguments);
|
||||
EXPECT_EQ(arguments, op->ValueInputCount());
|
||||
EXPECT_EQ(arguments, OperatorProperties::GetTotalInputCount(op));
|
||||
EXPECT_EQ(0, op->ControlOutputCount());
|
||||
EXPECT_EQ(1, op->EffectOutputCount());
|
||||
EXPECT_EQ(0, op->ValueOutputCount());
|
||||
}
|
||||
TEST_F(CommonOperatorTest, BeginRegion) {
|
||||
const Operator* op = common()->BeginRegion();
|
||||
EXPECT_EQ(1, op->EffectInputCount());
|
||||
EXPECT_EQ(1, OperatorProperties::GetTotalInputCount(op));
|
||||
EXPECT_EQ(0, op->ControlOutputCount());
|
||||
EXPECT_EQ(1, op->EffectOutputCount());
|
||||
EXPECT_EQ(0, op->ValueOutputCount());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(CommonOperatorTest, Finish) {
|
||||
TRACED_FOREACH(int, arguments, kArguments) {
|
||||
const Operator* op = common()->Finish(arguments);
|
||||
EXPECT_EQ(1, op->ValueInputCount());
|
||||
EXPECT_EQ(arguments, op->EffectInputCount());
|
||||
EXPECT_EQ(arguments + 1, OperatorProperties::GetTotalInputCount(op));
|
||||
EXPECT_EQ(0, op->ControlOutputCount());
|
||||
EXPECT_EQ(0, op->EffectOutputCount());
|
||||
EXPECT_EQ(1, op->ValueOutputCount());
|
||||
}
|
||||
TEST_F(CommonOperatorTest, FinishRegion) {
|
||||
const Operator* op = common()->FinishRegion();
|
||||
EXPECT_EQ(1, op->ValueInputCount());
|
||||
EXPECT_EQ(1, op->EffectInputCount());
|
||||
EXPECT_EQ(2, OperatorProperties::GetTotalInputCount(op));
|
||||
EXPECT_EQ(0, op->ControlOutputCount());
|
||||
EXPECT_EQ(1, op->EffectOutputCount());
|
||||
EXPECT_EQ(1, op->ValueOutputCount());
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
|
@ -241,13 +241,14 @@ TARGET_TEST_F(InstructionSelectorTest, ReferenceParameter) {
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Finish.
|
||||
// FinishRegion.
|
||||
|
||||
|
||||
TARGET_TEST_F(InstructionSelectorTest, Finish) {
|
||||
TARGET_TEST_F(InstructionSelectorTest, FinishRegion) {
|
||||
StreamBuilder m(this, kMachAnyTagged, kMachAnyTagged);
|
||||
Node* param = m.Parameter(0);
|
||||
Node* finish = m.AddNode(m.common()->Finish(1), param, m.graph()->start());
|
||||
Node* finish =
|
||||
m.AddNode(m.common()->FinishRegion(), param, m.graph()->start());
|
||||
m.Return(finish);
|
||||
Stream s = m.Build(kAllInstructions);
|
||||
ASSERT_EQ(4U, s.size());
|
||||
@ -333,8 +334,9 @@ TARGET_TEST_F(InstructionSelectorTest, ValueEffect) {
|
||||
Stream s1 = m1.Build(kAllInstructions);
|
||||
StreamBuilder m2(this, kMachInt32, kMachPtr);
|
||||
Node* p2 = m2.Parameter(0);
|
||||
m2.Return(m2.AddNode(m2.machine()->Load(kMachInt32), p2, m2.Int32Constant(0),
|
||||
m2.AddNode(m2.common()->ValueEffect(1), p2)));
|
||||
m2.Return(
|
||||
m2.AddNode(m2.machine()->Load(kMachInt32), p2, m2.Int32Constant(0),
|
||||
m2.AddNode(m2.common()->BeginRegion(), m2.graph()->start())));
|
||||
Stream s2 = m2.Build(kAllInstructions);
|
||||
EXPECT_LE(3U, s1.size());
|
||||
ASSERT_EQ(s1.size(), s2.size());
|
||||
|
@ -1066,10 +1066,10 @@ TEST_F(JSTypedLoweringTest, JSCreateFunctionContextViaInlinedAllocation) {
|
||||
context, effect, control));
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(),
|
||||
IsFinish(IsAllocate(IsNumberConstant(Context::SizeFor(
|
||||
8 + Context::MIN_CONTEXT_SLOTS)),
|
||||
effect, control),
|
||||
_));
|
||||
IsFinishRegion(IsAllocate(IsNumberConstant(Context::SizeFor(
|
||||
8 + Context::MIN_CONTEXT_SLOTS)),
|
||||
IsBeginRegion(effect), control),
|
||||
_));
|
||||
}
|
||||
|
||||
|
||||
@ -1106,10 +1106,10 @@ TEST_F(JSTypedLoweringTest, JSCreateWithContext) {
|
||||
closure, context, frame_state, effect, control));
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(),
|
||||
IsFinish(IsAllocate(IsNumberConstant(Context::SizeFor(
|
||||
Context::MIN_CONTEXT_SLOTS)),
|
||||
effect, control),
|
||||
_));
|
||||
IsFinishRegion(IsAllocate(IsNumberConstant(Context::SizeFor(
|
||||
Context::MIN_CONTEXT_SLOTS)),
|
||||
IsBeginRegion(effect), control),
|
||||
_));
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
|
@ -261,11 +261,34 @@ class IsControl3Matcher final : public NodeMatcher {
|
||||
};
|
||||
|
||||
|
||||
class IsFinishMatcher final : public NodeMatcher {
|
||||
class IsBeginRegionMatcher final : public NodeMatcher {
|
||||
public:
|
||||
IsFinishMatcher(const Matcher<Node*>& value_matcher,
|
||||
const Matcher<Node*>& effect_matcher)
|
||||
: NodeMatcher(IrOpcode::kFinish),
|
||||
explicit IsBeginRegionMatcher(const Matcher<Node*>& effect_matcher)
|
||||
: NodeMatcher(IrOpcode::kBeginRegion), effect_matcher_(effect_matcher) {}
|
||||
|
||||
void DescribeTo(std::ostream* os) const final {
|
||||
NodeMatcher::DescribeTo(os);
|
||||
*os << " whose effect (";
|
||||
effect_matcher_.DescribeTo(os);
|
||||
*os << ")";
|
||||
}
|
||||
|
||||
bool MatchAndExplain(Node* node, MatchResultListener* listener) const final {
|
||||
return (NodeMatcher::MatchAndExplain(node, listener) &&
|
||||
PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect",
|
||||
effect_matcher_, listener));
|
||||
}
|
||||
|
||||
private:
|
||||
const Matcher<Node*> effect_matcher_;
|
||||
};
|
||||
|
||||
|
||||
class IsFinishRegionMatcher final : public NodeMatcher {
|
||||
public:
|
||||
IsFinishRegionMatcher(const Matcher<Node*>& value_matcher,
|
||||
const Matcher<Node*>& effect_matcher)
|
||||
: NodeMatcher(IrOpcode::kFinishRegion),
|
||||
value_matcher_(value_matcher),
|
||||
effect_matcher_(effect_matcher) {}
|
||||
|
||||
@ -1501,14 +1524,14 @@ Matcher<Node*> IsIfDefault(const Matcher<Node*>& control_matcher) {
|
||||
}
|
||||
|
||||
|
||||
Matcher<Node*> IsValueEffect(const Matcher<Node*>& value_matcher) {
|
||||
return MakeMatcher(new IsUnopMatcher(IrOpcode::kValueEffect, value_matcher));
|
||||
Matcher<Node*> IsBeginRegion(const Matcher<Node*>& effect_matcher) {
|
||||
return MakeMatcher(new IsBeginRegionMatcher(effect_matcher));
|
||||
}
|
||||
|
||||
|
||||
Matcher<Node*> IsFinish(const Matcher<Node*>& value_matcher,
|
||||
const Matcher<Node*>& effect_matcher) {
|
||||
return MakeMatcher(new IsFinishMatcher(value_matcher, effect_matcher));
|
||||
Matcher<Node*> IsFinishRegion(const Matcher<Node*>& value_matcher,
|
||||
const Matcher<Node*>& effect_matcher) {
|
||||
return MakeMatcher(new IsFinishRegionMatcher(value_matcher, effect_matcher));
|
||||
}
|
||||
|
||||
|
||||
|
@ -63,9 +63,9 @@ Matcher<Node*> IsSwitch(const Matcher<Node*>& value_matcher,
|
||||
Matcher<Node*> IsIfValue(const Matcher<int32_t>& value_matcher,
|
||||
const Matcher<Node*>& control_matcher);
|
||||
Matcher<Node*> IsIfDefault(const Matcher<Node*>& control_matcher);
|
||||
Matcher<Node*> IsValueEffect(const Matcher<Node*>& value_matcher);
|
||||
Matcher<Node*> IsFinish(const Matcher<Node*>& value_matcher,
|
||||
const Matcher<Node*>& effect_matcher);
|
||||
Matcher<Node*> IsBeginRegion(const Matcher<Node*>& effect_matcher);
|
||||
Matcher<Node*> IsFinishRegion(const Matcher<Node*>& value_matcher,
|
||||
const Matcher<Node*>& effect_matcher);
|
||||
Matcher<Node*> IsReturn(const Matcher<Node*>& value_matcher,
|
||||
const Matcher<Node*>& effect_matcher,
|
||||
const Matcher<Node*>& control_matcher);
|
||||
|
Loading…
Reference in New Issue
Block a user