Commit d9fe1e71 authored by bmeurer@chromium.org's avatar bmeurer@chromium.org

[turbofan] Add new ControlEffect and Finish operators.

Fix the ChangeLowering to properly use ControlEffect nodes
to turn the control output of IfTrue nodes into an effect
input for the Load nodes, and to properly use Finish nodes
to ensure that allocation and store were both performed
prior to actually using the allocated heap number.

TEST=compiler-unittests
R=jarin@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23149 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent e4db78e7
......@@ -94,7 +94,6 @@ Reduction ChangeLowering::ChangeInt32ToTagged(Node* val, Node* effect,
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* number = graph()->NewNode(machine()->ChangeInt32ToFloat64(), val);
// TODO(bmeurer): Inline allocation if possible.
const Runtime::Function* fn =
Runtime::FunctionForId(Runtime::kAllocateHeapNumber);
DCHECK_EQ(0, fn->nargs);
......@@ -103,17 +102,17 @@ Reduction ChangeLowering::ChangeInt32ToTagged(Node* val, Node* effect,
Node* heap_number = graph()->NewNode(
common()->Call(desc), jsgraph()->CEntryStubConstant(),
jsgraph()->ExternalConstant(ExternalReference(fn, isolate())),
jsgraph()->ZeroConstant(), context, effect, if_true);
jsgraph()->Int32Constant(fn->nargs), context, effect, if_true);
Node* store = graph()->NewNode(
machine()->Store(kMachFloat64, kNoWriteBarrier), heap_number,
HeapNumberValueIndexConstant(), number, effect, heap_number);
HeapNumberValueIndexConstant(), number, heap_number, if_true);
Node* finish = graph()->NewNode(common()->Finish(1), heap_number, store);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* smi = graph()->NewNode(common()->Projection(0), add);
Node* merge = graph()->NewNode(common()->Merge(2), store, if_false);
Node* phi = graph()->NewNode(common()->Phi(2), heap_number, smi, merge);
Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* phi = graph()->NewNode(common()->Phi(2), finish, smi, merge);
return Replace(phi);
}
......@@ -128,8 +127,9 @@ Reduction ChangeLowering::ChangeTaggedToFloat64(Node* val, Node* effect,
Node* branch = graph()->NewNode(common()->Branch(), tag, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* load = graph()->NewNode(machine()->Load(kMachFloat64), val,
HeapNumberValueIndexConstant(), if_true);
Node* load = graph()->NewNode(
machine()->Load(kMachFloat64), val, HeapNumberValueIndexConstant(),
graph()->NewNode(common()->ControlEffect(), if_true));
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* integer =
......
......@@ -130,6 +130,15 @@ class CommonOperatorBuilder {
return new (zone_) Operator1<int>(IrOpcode::kEffectPhi, Operator::kPure, 0,
0, "EffectPhi", arguments);
}
Operator* ControlEffect() {
return new (zone_) SimpleOperator(IrOpcode::kControlEffect, Operator::kPure,
0, 0, "ControlEffect");
}
Operator* Finish(int arguments) {
DCHECK(arguments > 0); // Disallow empty finishes.
return new (zone_) Operator1<int>(IrOpcode::kFinish, Operator::kPure, 1, 1,
"Finish", arguments);
}
Operator* StateValues(int arguments) {
return new (zone_) Operator1<int>(IrOpcode::kStateValues, Operator::kPure,
arguments, 1, "StateValues", arguments);
......
......@@ -33,6 +33,8 @@
#define INNER_OP_LIST(V) \
V(Phi) \
V(EffectPhi) \
V(ControlEffect) \
V(Finish) \
V(FrameState) \
V(StateValues) \
V(Call) \
......
......@@ -42,7 +42,8 @@ inline int OperatorProperties::GetContextInputCount(Operator* op) {
}
inline int OperatorProperties::GetEffectInputCount(Operator* op) {
if (op->opcode() == IrOpcode::kEffectPhi) {
if (op->opcode() == IrOpcode::kEffectPhi ||
op->opcode() == IrOpcode::kFinish) {
return static_cast<Operator1<int>*>(op)->parameter();
}
if (op->HasProperty(Operator::kNoRead) && op->HasProperty(Operator::kNoWrite))
......@@ -54,6 +55,7 @@ inline int OperatorProperties::GetControlInputCount(Operator* op) {
switch (op->opcode()) {
case IrOpcode::kPhi:
case IrOpcode::kEffectPhi:
case IrOpcode::kControlEffect:
return 1;
#define OPCODE_CASE(x) case IrOpcode::k##x:
CONTROL_OP_LIST(OPCODE_CASE)
......@@ -87,7 +89,9 @@ inline bool OperatorProperties::HasValueOutput(Operator* op) {
}
inline bool OperatorProperties::HasEffectOutput(Operator* op) {
return op->opcode() == IrOpcode::kStart || GetEffectInputCount(op) > 0;
return op->opcode() == IrOpcode::kStart ||
op->opcode() == IrOpcode::kControlEffect ||
(op->opcode() != IrOpcode::kFinish && GetEffectInputCount(op) > 0);
}
inline bool OperatorProperties::HasControlOutput(Operator* op) {
......
......@@ -267,6 +267,14 @@ Bounds Typer::Visitor::TypeEffectPhi(Node* node) {
}
Bounds Typer::Visitor::TypeControlEffect(Node* node) {
return Bounds(Type::None(zone()));
}
Bounds Typer::Visitor::TypeFinish(Node* node) { return OperandType(node, 0); }
Bounds Typer::Visitor::TypeFrameState(Node* node) {
return Bounds(Type::None(zone()));
}
......
......@@ -117,34 +117,33 @@ TARGET_TEST_F(ChangeLowering32Test, ChangeInt32ToTagged) {
ASSERT_TRUE(reduction.Changed());
Node* phi = reduction.replacement();
ASSERT_EQ(IrOpcode::kPhi, phi->opcode());
Node* smi = NodeProperties::GetValueInput(phi, 1);
ASSERT_THAT(smi, IsProjection(0, IsInt32AddWithOverflow(val, val)));
Node* heap_number = NodeProperties::GetValueInput(phi, 0);
ASSERT_EQ(IrOpcode::kCall, heap_number->opcode());
Node* merge = NodeProperties::GetControlInput(phi);
ASSERT_EQ(IrOpcode::kMerge, merge->opcode());
Capture<Node*> add, branch, heap_number, if_true;
const int32_t kValueOffset = kHeapNumberValueOffset - kHeapObjectTag;
EXPECT_THAT(NodeProperties::GetControlInput(merge, 0),
IsStore(kMachFloat64, kNoWriteBarrier, heap_number,
EXPECT_THAT(
phi,
IsPhi(
IsFinish(
AllOf(
CaptureEq(&heap_number),
IsCall(
_, IsHeapConstant(
PrintableUnique<HeapObject>::CreateImmovable(
zone(), CEntryStub(isolate(), 1).GetCode())),
IsExternalConstant(ExternalReference(
Runtime::FunctionForId(Runtime::kAllocateHeapNumber),
isolate())),
IsInt32Constant(0), IsNumberConstant(0.0),
graph()->start(), CaptureEq(&if_true))),
IsStore(kMachFloat64, kNoWriteBarrier, CaptureEq(&heap_number),
IsInt32Constant(kValueOffset),
IsChangeInt32ToFloat64(val), _, heap_number));
Node* if_true = NodeProperties::GetControlInput(heap_number);
ASSERT_EQ(IrOpcode::kIfTrue, if_true->opcode());
Node* if_false = NodeProperties::GetControlInput(merge, 1);
ASSERT_EQ(IrOpcode::kIfFalse, if_false->opcode());
Node* branch = NodeProperties::GetControlInput(if_true);
EXPECT_EQ(branch, NodeProperties::GetControlInput(if_false));
EXPECT_THAT(branch,
IsBranch(IsProjection(1, IsInt32AddWithOverflow(val, val)),
graph()->start()));
IsChangeInt32ToFloat64(val), CaptureEq(&heap_number),
CaptureEq(&if_true))),
IsProjection(
0, AllOf(CaptureEq(&add), IsInt32AddWithOverflow(val, val))),
IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))),
IsIfFalse(AllOf(CaptureEq(&branch),
IsBranch(IsProjection(1, CaptureEq(&add)),
graph()->start()))))));
}
......@@ -161,17 +160,21 @@ TARGET_TEST_F(ChangeLowering32Test, ChangeTaggedToFloat64) {
kSmiTagSize + SmiTagging<kPointerSize>::kSmiShiftSize;
const int32_t kValueOffset = kHeapNumberValueOffset - kHeapObjectTag;
Node* phi = reduction.replacement();
Capture<Node*> branch;
Capture<Node*> branch, if_true;
EXPECT_THAT(
phi,
IsPhi(IsLoad(kMachFloat64, val, IsInt32Constant(kValueOffset), _),
IsChangeInt32ToFloat64(
IsWord32Sar(val, IsInt32Constant(kShiftAmount))),
IsMerge(IsIfTrue(AllOf(
IsPhi(
IsLoad(kMachFloat64, val, IsInt32Constant(kValueOffset),
IsControlEffect(CaptureEq(&if_true))),
IsChangeInt32ToFloat64(
IsWord32Sar(val, IsInt32Constant(kShiftAmount))),
IsMerge(
AllOf(CaptureEq(&if_true),
IsIfTrue(AllOf(
CaptureEq(&branch),
IsBranch(IsWord32And(val, IsInt32Constant(kSmiTagMask)),
graph()->start()))),
IsIfFalse(CaptureEq(&branch)))));
graph()->start())))),
IsIfFalse(CaptureEq(&branch)))));
}
......@@ -218,17 +221,21 @@ TARGET_TEST_F(ChangeLowering64Test, ChangeTaggedToFloat64) {
kSmiTagSize + SmiTagging<kPointerSize>::kSmiShiftSize;
const int32_t kValueOffset = kHeapNumberValueOffset - kHeapObjectTag;
Node* phi = reduction.replacement();
Capture<Node*> branch;
Capture<Node*> branch, if_true;
EXPECT_THAT(
phi,
IsPhi(IsLoad(kMachFloat64, val, IsInt32Constant(kValueOffset), _),
IsChangeInt32ToFloat64(IsConvertInt64ToInt32(
IsWord64Sar(val, IsInt32Constant(kShiftAmount)))),
IsMerge(IsIfTrue(AllOf(
IsPhi(
IsLoad(kMachFloat64, val, IsInt32Constant(kValueOffset),
IsControlEffect(CaptureEq(&if_true))),
IsChangeInt32ToFloat64(IsConvertInt64ToInt32(
IsWord64Sar(val, IsInt32Constant(kShiftAmount)))),
IsMerge(
AllOf(CaptureEq(&if_true),
IsIfTrue(AllOf(
CaptureEq(&branch),
IsBranch(IsWord64And(val, IsInt32Constant(kSmiTagMask)),
graph()->start()))),
IsIfFalse(CaptureEq(&branch)))));
graph()->start())))),
IsIfFalse(CaptureEq(&branch)))));
}
} // namespace compiler
......
......@@ -15,6 +15,30 @@ CommonOperatorTest::CommonOperatorTest() : common_(zone()) {}
CommonOperatorTest::~CommonOperatorTest() {}
TEST_F(CommonOperatorTest, ControlEffect) {
Operator* op = common()->ControlEffect();
EXPECT_EQ(1, OperatorProperties::GetControlInputCount(op));
EXPECT_EQ(1, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(0, OperatorProperties::GetControlOutputCount(op));
EXPECT_EQ(1, OperatorProperties::GetEffectOutputCount(op));
EXPECT_EQ(0, OperatorProperties::GetValueOutputCount(op));
}
TEST_F(CommonOperatorTest, Finish) {
static const int kArguments[] = {1, 5, 6, 42, 100, 10000, kMaxInt};
TRACED_FOREACH(int, arguments, kArguments) {
Operator* op = common()->Finish(arguments);
EXPECT_EQ(1, OperatorProperties::GetValueInputCount(op));
EXPECT_EQ(arguments, OperatorProperties::GetEffectInputCount(op));
EXPECT_EQ(arguments + 1, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(0, OperatorProperties::GetControlOutputCount(op));
EXPECT_EQ(0, OperatorProperties::GetEffectOutputCount(op));
EXPECT_EQ(1, OperatorProperties::GetValueOutputCount(op));
}
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -22,6 +22,12 @@ inline std::ostream& operator<<(std::ostream& os,
const PrintableUnique<T>& value) {
return os << value.string();
}
inline std::ostream& operator<<(std::ostream& os,
const ExternalReference& value) {
OStringStream ost;
compiler::StaticParameterTraits<ExternalReference>::PrintTo(ost, value);
return os << ost.c_str();
}
namespace compiler {
......@@ -66,7 +72,8 @@ class NodeMatcher : public MatcherInterface<Node*> {
return false;
}
if (node->opcode() != opcode_) {
*listener << "whose opcode is " << IrOpcode::Mnemonic(node->opcode());
*listener << "whose opcode is " << IrOpcode::Mnemonic(node->opcode())
<< " but should have been " << IrOpcode::Mnemonic(opcode_);
return false;
}
return true;
......@@ -141,10 +148,11 @@ class IsMergeMatcher V8_FINAL : public NodeMatcher {
};
class IsIfTrueMatcher V8_FINAL : public NodeMatcher {
class IsControl1Matcher V8_FINAL : public NodeMatcher {
public:
explicit IsIfTrueMatcher(const Matcher<Node*>& control_matcher)
: NodeMatcher(IrOpcode::kIfTrue), control_matcher_(control_matcher) {}
IsControl1Matcher(IrOpcode::Value opcode,
const Matcher<Node*>& control_matcher)
: NodeMatcher(opcode), control_matcher_(control_matcher) {}
virtual void DescribeTo(std::ostream* os) const V8_OVERRIDE {
NodeMatcher::DescribeTo(os);
......@@ -165,27 +173,35 @@ class IsIfTrueMatcher V8_FINAL : public NodeMatcher {
};
class IsIfFalseMatcher V8_FINAL : public NodeMatcher {
class IsFinishMatcher V8_FINAL : public NodeMatcher {
public:
explicit IsIfFalseMatcher(const Matcher<Node*>& control_matcher)
: NodeMatcher(IrOpcode::kIfFalse), control_matcher_(control_matcher) {}
IsFinishMatcher(const Matcher<Node*>& value_matcher,
const Matcher<Node*>& effect_matcher)
: NodeMatcher(IrOpcode::kFinish),
value_matcher_(value_matcher),
effect_matcher_(effect_matcher) {}
virtual void DescribeTo(std::ostream* os) const V8_OVERRIDE {
NodeMatcher::DescribeTo(os);
*os << " whose control (";
control_matcher_.DescribeTo(os);
*os << " whose value (";
value_matcher_.DescribeTo(os);
*os << ") and effect (";
effect_matcher_.DescribeTo(os);
*os << ")";
}
virtual bool MatchAndExplain(Node* node, MatchResultListener* listener) const
V8_OVERRIDE {
return (NodeMatcher::MatchAndExplain(node, listener) &&
PrintMatchAndExplain(NodeProperties::GetControlInput(node),
"control", control_matcher_, listener));
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0),
"value", value_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect",
effect_matcher_, listener));
}
private:
const Matcher<Node*> control_matcher_;
const Matcher<Node*> value_matcher_;
const Matcher<Node*> effect_matcher_;
};
......@@ -285,6 +301,71 @@ class IsProjectionMatcher V8_FINAL : public NodeMatcher {
};
class IsCallMatcher V8_FINAL : public NodeMatcher {
public:
IsCallMatcher(const Matcher<CallDescriptor*>& descriptor_matcher,
const Matcher<Node*>& value0_matcher,
const Matcher<Node*>& value1_matcher,
const Matcher<Node*>& value2_matcher,
const Matcher<Node*>& value3_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher)
: NodeMatcher(IrOpcode::kCall),
descriptor_matcher_(descriptor_matcher),
value0_matcher_(value0_matcher),
value1_matcher_(value1_matcher),
value2_matcher_(value2_matcher),
value3_matcher_(value3_matcher),
effect_matcher_(effect_matcher),
control_matcher_(control_matcher) {}
virtual void DescribeTo(std::ostream* os) const V8_OVERRIDE {
NodeMatcher::DescribeTo(os);
*os << " whose value0 (";
value0_matcher_.DescribeTo(os);
*os << ") and value1 (";
value1_matcher_.DescribeTo(os);
*os << ") and value2 (";
value2_matcher_.DescribeTo(os);
*os << ") and value3 (";
value3_matcher_.DescribeTo(os);
*os << ") and effect (";
effect_matcher_.DescribeTo(os);
*os << ") and control (";
control_matcher_.DescribeTo(os);
*os << ")";
}
virtual bool MatchAndExplain(Node* node, MatchResultListener* listener) const
V8_OVERRIDE {
return (NodeMatcher::MatchAndExplain(node, listener) &&
PrintMatchAndExplain(OpParameter<CallDescriptor*>(node),
"descriptor", descriptor_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0),
"value0", value0_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1),
"value1", value1_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 2),
"value2", value2_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 3),
"value3", value3_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect",
effect_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetControlInput(node),
"control", control_matcher_, listener));
}
private:
const Matcher<CallDescriptor*> descriptor_matcher_;
const Matcher<Node*> value0_matcher_;
const Matcher<Node*> value1_matcher_;
const Matcher<Node*> value2_matcher_;
const Matcher<Node*> value3_matcher_;
const Matcher<Node*> effect_matcher_;
const Matcher<Node*> control_matcher_;
};
class IsLoadMatcher V8_FINAL : public NodeMatcher {
public:
IsLoadMatcher(const Matcher<MachineType>& type_matcher,
......@@ -470,18 +551,32 @@ Matcher<Node*> IsMerge(const Matcher<Node*>& control0_matcher,
Matcher<Node*> IsIfTrue(const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsIfTrueMatcher(control_matcher));
return MakeMatcher(new IsControl1Matcher(IrOpcode::kIfTrue, control_matcher));
}
Matcher<Node*> IsIfFalse(const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsIfFalseMatcher(control_matcher));
return MakeMatcher(
new IsControl1Matcher(IrOpcode::kIfFalse, control_matcher));
}
Matcher<Node*> IsInt32Constant(const Matcher<int32_t>& value_matcher) {
Matcher<Node*> IsControlEffect(const Matcher<Node*>& control_matcher) {
return MakeMatcher(
new IsConstantMatcher<int32_t>(IrOpcode::kInt32Constant, value_matcher));
new IsControl1Matcher(IrOpcode::kControlEffect, control_matcher));
}
Matcher<Node*> IsFinish(const Matcher<Node*>& value_matcher,
const Matcher<Node*>& effect_matcher) {
return MakeMatcher(new IsFinishMatcher(value_matcher, effect_matcher));
}
Matcher<Node*> IsExternalConstant(
const Matcher<ExternalReference>& value_matcher) {
return MakeMatcher(new IsConstantMatcher<ExternalReference>(
IrOpcode::kExternalConstant, value_matcher));
}
......@@ -492,6 +587,18 @@ Matcher<Node*> IsHeapConstant(
}
Matcher<Node*> IsInt32Constant(const Matcher<int32_t>& value_matcher) {
return MakeMatcher(
new IsConstantMatcher<int32_t>(IrOpcode::kInt32Constant, value_matcher));
}
Matcher<Node*> IsNumberConstant(const Matcher<double>& value_matcher) {
return MakeMatcher(
new IsConstantMatcher<double>(IrOpcode::kNumberConstant, value_matcher));
}
Matcher<Node*> IsPhi(const Matcher<Node*>& value0_matcher,
const Matcher<Node*>& value1_matcher,
const Matcher<Node*>& merge_matcher) {
......@@ -506,6 +613,19 @@ Matcher<Node*> IsProjection(const Matcher<int32_t>& index_matcher,
}
Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
const Matcher<Node*>& value0_matcher,
const Matcher<Node*>& value1_matcher,
const Matcher<Node*>& value2_matcher,
const Matcher<Node*>& value3_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsCallMatcher(
descriptor_matcher, value0_matcher, value1_matcher, value2_matcher,
value3_matcher, effect_matcher, control_matcher));
}
Matcher<Node*> IsLoad(const Matcher<MachineType>& type_matcher,
const Matcher<Node*>& base_matcher,
const Matcher<Node*>& index_matcher,
......
......@@ -41,14 +41,27 @@ Matcher<Node*> IsMerge(const Matcher<Node*>& control0_matcher,
const Matcher<Node*>& control1_matcher);
Matcher<Node*> IsIfTrue(const Matcher<Node*>& control_matcher);
Matcher<Node*> IsIfFalse(const Matcher<Node*>& control_matcher);
Matcher<Node*> IsControlEffect(const Matcher<Node*>& control_matcher);
Matcher<Node*> IsFinish(const Matcher<Node*>& value_matcher,
const Matcher<Node*>& effect_matcher);
Matcher<Node*> IsExternalConstant(
const Matcher<ExternalReference>& value_matcher);
Matcher<Node*> IsHeapConstant(
const Matcher<PrintableUnique<HeapObject> >& value_matcher);
Matcher<Node*> IsInt32Constant(const Matcher<int32_t>& value_matcher);
Matcher<Node*> IsNumberConstant(const Matcher<double>& value_matcher);
Matcher<Node*> IsPhi(const Matcher<Node*>& value0_matcher,
const Matcher<Node*>& value1_matcher,
const Matcher<Node*>& merge_matcher);
Matcher<Node*> IsProjection(const Matcher<int32_t>& index_matcher,
const Matcher<Node*>& base_matcher);
Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
const Matcher<Node*>& value0_matcher,
const Matcher<Node*>& value1_matcher,
const Matcher<Node*>& value2_matcher,
const Matcher<Node*>& value3_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsLoad(const Matcher<MachineType>& type_matcher,
const Matcher<Node*>& base_matcher,
......
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