[turbofan] Introduce PlainPrimitiveToNumber.

This should solve the problem with missing checkpoints after JSToNumber
(PlainPrimitiveToNumber is marked no-write, so the frame-state
propagation should see through it.)

Unfortunately, this also duplicates the word32- and float64-truncation
magic that we have for JSToNumber in "simplified lowering".

Review-Url: https://codereview.chromium.org/2059653002
Cr-Commit-Position: refs/heads/master@{#36881}
This commit is contained in:
jarin 2016-06-10 00:41:45 -07:00 committed by Commit bot
parent d0c7775d7c
commit 2890137bdc
15 changed files with 222 additions and 22 deletions

View File

@ -454,6 +454,14 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
break;
case IrOpcode::kCheckIf:
state = LowerCheckIf(node, frame_state, *effect, *control);
case IrOpcode::kPlainPrimitiveToNumber:
state = LowerPlainPrimitiveToNumber(node, *effect, *control);
break;
case IrOpcode::kPlainPrimitiveToWord32:
state = LowerPlainPrimitiveToWord32(node, *effect, *control);
break;
case IrOpcode::kPlainPrimitiveToFloat64:
state = LowerPlainPrimitiveToFloat64(node, *effect, *control);
break;
default:
return false;
@ -1330,7 +1338,6 @@ Node* EffectControlLinearizer::ChangeSmiToInt32(Node* value) {
}
return value;
}
Node* EffectControlLinearizer::ObjectIsSmi(Node* value) {
return graph()->NewNode(
machine()->WordEqual(),
@ -1347,6 +1354,128 @@ Node* EffectControlLinearizer::SmiShiftBitsConstant() {
return jsgraph()->IntPtrConstant(kSmiShiftSize + kSmiTagSize);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerPlainPrimitiveToNumber(Node* node, Node* effect,
Node* control) {
Node* value = node->InputAt(0);
Node* result = effect =
graph()->NewNode(ToNumberOperator(), jsgraph()->ToNumberBuiltinConstant(),
value, jsgraph()->NoContextConstant(), effect, control);
return ValueEffectControl(result, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerPlainPrimitiveToWord32(Node* node, Node* effect,
Node* control) {
Node* value = node->InputAt(0);
Node* check0 = ObjectIsSmi(value);
Node* branch0 =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
Node* etrue0 = effect;
Node* vtrue0 = ChangeSmiToInt32(value);
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
Node* efalse0 = effect;
Node* vfalse0;
{
vfalse0 = efalse0 = graph()->NewNode(
ToNumberOperator(), jsgraph()->ToNumberBuiltinConstant(), value,
jsgraph()->NoContextConstant(), efalse0, if_false0);
Node* check1 = ObjectIsSmi(vfalse0);
Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0);
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
Node* etrue1 = efalse0;
Node* vtrue1 = ChangeSmiToInt32(vfalse0);
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
Node* efalse1 = efalse0;
Node* vfalse1;
{
vfalse1 = efalse1 = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), efalse0,
efalse1, if_false1);
vfalse1 = graph()->NewNode(machine()->TruncateFloat64ToWord32(), vfalse1);
}
if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
efalse0 =
graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0);
vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
vtrue1, vfalse1, if_false0);
}
control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
value = graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
vtrue0, vfalse0, control);
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerPlainPrimitiveToFloat64(Node* node, Node* effect,
Node* control) {
Node* value = node->InputAt(0);
Node* check0 = ObjectIsSmi(value);
Node* branch0 =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
Node* etrue0 = effect;
Node* vtrue0;
{
vtrue0 = ChangeSmiToInt32(value);
vtrue0 = graph()->NewNode(machine()->ChangeInt32ToFloat64(), vtrue0);
}
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
Node* efalse0 = effect;
Node* vfalse0;
{
vfalse0 = efalse0 = graph()->NewNode(
ToNumberOperator(), jsgraph()->ToNumberBuiltinConstant(), value,
jsgraph()->NoContextConstant(), efalse0, if_false0);
Node* check1 = ObjectIsSmi(vfalse0);
Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0);
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
Node* etrue1 = efalse0;
Node* vtrue1;
{
vtrue1 = ChangeSmiToInt32(vfalse0);
vtrue1 = graph()->NewNode(machine()->ChangeInt32ToFloat64(), vtrue1);
}
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
Node* efalse1 = efalse0;
Node* vfalse1;
{
vfalse1 = efalse1 = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), efalse0,
efalse1, if_false1);
}
if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
efalse0 =
graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0);
vfalse0 =
graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2),
vtrue1, vfalse1, if_false0);
}
control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
value = graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2),
vtrue0, vfalse0, control);
return ValueEffectControl(value, effect, control);
}
Factory* EffectControlLinearizer::factory() const {
return isolate()->factory();
}
@ -1355,6 +1484,18 @@ Isolate* EffectControlLinearizer::isolate() const {
return jsgraph()->isolate();
}
Operator const* EffectControlLinearizer::ToNumberOperator() {
if (!to_number_operator_.is_set()) {
Callable callable = CodeFactory::ToNumber(isolate());
CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags,
Operator::kNoThrow);
to_number_operator_.set(common()->Call(desc));
}
return to_number_operator_.get();
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -91,6 +91,13 @@ class EffectControlLinearizer {
Node* control);
ValueEffectControl LowerCheckIf(Node* node, Node* frame_state, Node* effect,
Node* control);
ValueEffectControl LowerPlainPrimitiveToNumber(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerPlainPrimitiveToWord32(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerPlainPrimitiveToFloat64(Node* node, Node* effect,
Node* control);
ValueEffectControl AllocateHeapNumberWithValue(Node* node, Node* effect,
Node* control);
ValueEffectControl BuildCheckedFloat64ToInt32(Node* value, Node* frame_state,
@ -99,6 +106,7 @@ class EffectControlLinearizer {
Node* frame_state,
Node* effect,
Node* control);
Node* ChangeInt32ToSmi(Node* value);
Node* ChangeUint32ToSmi(Node* value);
Node* ChangeInt32ToFloat64(Node* value);
@ -119,9 +127,13 @@ class EffectControlLinearizer {
SimplifiedOperatorBuilder* simplified() const;
MachineOperatorBuilder* machine() const;
Operator const* ToNumberOperator();
JSGraph* js_graph_;
Schedule* schedule_;
Zone* temp_zone_;
SetOncePointer<Operator const> to_number_operator_;
};
} // namespace compiler

View File

@ -24,6 +24,11 @@ Node* JSGraph::AllocateInOldSpaceStubConstant() {
HeapConstant(isolate()->builtins()->AllocateInOldSpace()));
}
Node* JSGraph::ToNumberBuiltinConstant() {
return CACHED(kToNumberBuiltinConstant,
HeapConstant(isolate()->builtins()->ToNumber()));
}
Node* JSGraph::CEntryStubConstant(int result_size) {
if (result_size == 1) {
return CACHED(kCEntryStubConstant,

View File

@ -41,6 +41,7 @@ class JSGraph : public ZoneObject {
// Canonicalized global constants.
Node* AllocateInNewSpaceStubConstant();
Node* AllocateInOldSpaceStubConstant();
Node* ToNumberBuiltinConstant();
Node* CEntryStubConstant(int result_size);
Node* EmptyFixedArrayConstant();
Node* EmptyLiteralsArrayConstant();
@ -146,6 +147,7 @@ class JSGraph : public ZoneObject {
enum CachedNode {
kAllocateInNewSpaceStubConstant,
kAllocateInOldSpaceStubConstant,
kToNumberBuiltinConstant,
kCEntryStubConstant,
kEmptyFixedArrayConstant,
kEmptyLiteralsArrayConstant,

View File

@ -289,10 +289,7 @@ class JSBinopReduction final {
if (NodeProperties::GetType(node)->Is(Type::NumberOrUndefined())) {
return node;
}
// TODO(bmeurer): Introduce PlainPrimitiveToNumber here.
return graph()->NewNode(
javascript()->ToNumber(), node, jsgraph()->NoContextConstant(),
lowering_->EmptyFrameState(), graph()->start(), graph()->start());
return graph()->NewNode(simplified()->PlainPrimitiveToNumber(), node);
}
Node* ConvertSingleInputToNumber(Node* node, Node* frame_state) {
@ -869,20 +866,10 @@ Reduction JSTypedLowering::ReduceJSToNumber(Node* node) {
}
Type* const input_type = NodeProperties::GetType(input);
if (input_type->Is(Type::PlainPrimitive())) {
if (NodeProperties::GetContextInput(node) !=
jsgraph()->NoContextConstant() ||
NodeProperties::GetEffectInput(node) != graph()->start() ||
NodeProperties::GetControlInput(node) != graph()->start()) {
// JSToNumber(x:plain-primitive,context,effect,control)
// => JSToNumber(x,no-context,start,start)
RelaxEffectsAndControls(node);
NodeProperties::ReplaceContextInput(node, jsgraph()->NoContextConstant());
NodeProperties::ReplaceControlInput(node, graph()->start());
NodeProperties::ReplaceEffectInput(node, graph()->start());
DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
NodeProperties::ReplaceFrameStateInput(node, 0, EmptyFrameState());
return Changed(node);
}
RelaxEffectsAndControls(node);
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->PlainPrimitiveToNumber());
return Changed(node);
}
return NoChange();
}

View File

@ -175,6 +175,9 @@
#define SIMPLIFIED_OP_LIST(V) \
SIMPLIFIED_COMPARE_BINOP_LIST(V) \
V(PlainPrimitiveToNumber) \
V(PlainPrimitiveToWord32) \
V(PlainPrimitiveToFloat64) \
V(BooleanNot) \
V(BooleanToNumber) \
V(SpeculativeNumberAdd) \

View File

@ -1639,6 +1639,24 @@ class RepresentationSelector {
}
return;
}
case IrOpcode::kPlainPrimitiveToNumber:
ProcessInput(node, 0, UseInfo::AnyTagged());
if (truncation.TruncatesToWord32()) {
SetOutput(node, MachineRepresentation::kWord32);
if (lower()) {
NodeProperties::ChangeOp(node,
simplified()->PlainPrimitiveToWord32());
}
} else if (truncation.TruncatesToFloat64()) {
SetOutput(node, MachineRepresentation::kFloat64);
if (lower()) {
NodeProperties::ChangeOp(node,
simplified()->PlainPrimitiveToFloat64());
}
} else {
SetOutput(node, MachineRepresentation::kTagged);
}
return;
case IrOpcode::kObjectIsCallable:
case IrOpcode::kObjectIsNumber:
case IrOpcode::kObjectIsReceiver:
@ -1808,6 +1826,8 @@ class RepresentationSelector {
case IrOpcode::kCheckedFloat64ToInt32:
case IrOpcode::kCheckedTaggedToInt32:
case IrOpcode::kCheckedTaggedToFloat64:
case IrOpcode::kPlainPrimitiveToWord32:
case IrOpcode::kPlainPrimitiveToFloat64:
FATAL("Representation inference: unsupported opcodes.");
break;

View File

@ -212,6 +212,9 @@ BinaryOperationHints::Hint BinaryOperationHintOf(const Operator* op) {
V(NumberIsHoleNaN, Operator::kNoProperties, 1) \
V(StringFromCharCode, Operator::kNoProperties, 1) \
V(StringToNumber, Operator::kNoProperties, 1) \
V(PlainPrimitiveToNumber, Operator::kNoWrite, 1) \
V(PlainPrimitiveToWord32, Operator::kNoWrite, 1) \
V(PlainPrimitiveToFloat64, Operator::kNoWrite, 1) \
V(ChangeTaggedSignedToInt32, Operator::kNoProperties, 1) \
V(ChangeTaggedToInt32, Operator::kNoProperties, 1) \
V(ChangeTaggedToUint32, Operator::kNoProperties, 1) \

View File

@ -172,6 +172,10 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* StringFromCharCode();
const Operator* StringToNumber();
const Operator* PlainPrimitiveToNumber();
const Operator* PlainPrimitiveToWord32();
const Operator* PlainPrimitiveToFloat64();
const Operator* ChangeTaggedSignedToInt32();
const Operator* ChangeTaggedToInt32();
const Operator* ChangeTaggedToUint32();

View File

@ -1759,6 +1759,18 @@ Type* Typer::Visitor::TypeNumberShiftRightLogical(Node* node) {
return Type::Unsigned32();
}
Type* Typer::Visitor::TypePlainPrimitiveToNumber(Node* node) {
return TypeUnaryOp(node, ToNumber);
}
Type* Typer::Visitor::TypePlainPrimitiveToWord32(Node* node) {
return Type::Integral32();
}
Type* Typer::Visitor::TypePlainPrimitiveToFloat64(Node* node) {
return Type::Number();
}
Type* Typer::Visitor::TypeNumberImul(Node* node) { return Type::Signed32(); }
Type* Typer::Visitor::TypeNumberClz32(Node* node) {

View File

@ -762,6 +762,16 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 0, Type::Number());
CheckUpperIs(node, Type::Boolean());
break;
case IrOpcode::kPlainPrimitiveToNumber:
// Type is Number.
CheckUpperIs(node, Type::Number());
break;
case IrOpcode::kPlainPrimitiveToWord32:
CheckUpperIs(node, Type::Number());
break;
case IrOpcode::kPlainPrimitiveToFloat64:
CheckUpperIs(node, Type::Number());
break;
case IrOpcode::kStringEqual:
case IrOpcode::kStringLessThan:
case IrOpcode::kStringLessThanOrEqual:

View File

@ -492,7 +492,7 @@ TEST(JSToNumberOfNumberOrOtherPrimitive) {
for (size_t i = 0; i < arraysize(others); i++) {
Type* t = Type::Union(Type::Number(), others[i], R.main_zone());
Node* r = R.ReduceUnop(R.javascript.ToNumber(), t);
CHECK_EQ(IrOpcode::kJSToNumber, r->opcode());
CHECK_EQ(IrOpcode::kPlainPrimitiveToNumber, r->opcode());
}
}

View File

@ -321,8 +321,7 @@ TEST_F(JSTypedLoweringTest, JSToNumberWithPlainPrimitive) {
Reduce(graph()->NewNode(javascript()->ToNumber(), input, context,
EmptyFrameState(), effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsToNumber(input, IsNumberConstant(BitEq(0.0)),
graph()->start(), control));
EXPECT_THAT(r.replacement(), IsPlainPrimitiveToNumber(input));
}

View File

@ -2311,6 +2311,7 @@ IS_UNOP_MATCHER(Float64ExtractLowWord32)
IS_UNOP_MATCHER(Float64ExtractHighWord32)
IS_UNOP_MATCHER(NumberToInt32)
IS_UNOP_MATCHER(NumberToUint32)
IS_UNOP_MATCHER(PlainPrimitiveToNumber)
IS_UNOP_MATCHER(ObjectIsReceiver)
IS_UNOP_MATCHER(ObjectIsSmi)
IS_UNOP_MATCHER(StringFromCharCode)

View File

@ -375,6 +375,7 @@ Matcher<Node*> IsNumberToUint32(const Matcher<Node*>& input_matcher);
Matcher<Node*> IsParameter(const Matcher<int> index_matcher);
Matcher<Node*> IsLoadFramePointer();
Matcher<Node*> IsLoadParentFramePointer();
Matcher<Node*> IsPlainPrimitiveToNumber(const Matcher<Node*>& input_matcher);
Matcher<Node*> IsInt32PairAdd(const Matcher<Node*>& a_matcher,
const Matcher<Node*>& b_matcher,