Commit c500a3d3 authored by jarin's avatar jarin Committed by Commit bot

[turbofan] Numeric type feedback for mul, div and mod.

This introduces handling number feedback for multiplication, division
and modulus. At the moment, the only effect is using deoptimizing
number conversion instead of ToNumber call.

Review-Url: https://codereview.chromium.org/2074903002
Cr-Commit-Position: refs/heads/master@{#37081}
parent a8b5e235
......@@ -473,13 +473,17 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
Reduction JSTypedLowering::ReduceJSModulus(Node* node) {
if (flags() & kDisableBinaryOpReduction) return NoChange();
JSBinopReduction r(this, node);
if (r.BothInputsAre(Type::Number())) {
// JSModulus(x:number, x:number) => NumberModulus(x, y)
return r.ChangeToPureOperator(simplified()->NumberModulus(),
Type::Number());
}
BinaryOperationHints::Hint feedback = r.GetNumberBinaryOperationFeedback();
if (feedback != BinaryOperationHints::kAny) {
return r.ChangeToSpeculativeOperator(
simplified()->SpeculativeNumberModulus(feedback), Type::Number());
}
return NoChange();
}
......@@ -509,6 +513,13 @@ Reduction JSTypedLowering::ReduceJSSubtract(Node* node) {
Reduction JSTypedLowering::ReduceJSMultiply(Node* node) {
if (flags() & kDisableBinaryOpReduction) return NoChange();
JSBinopReduction r(this, node);
BinaryOperationHints::Hint feedback = r.GetNumberBinaryOperationFeedback();
if (feedback != BinaryOperationHints::kAny) {
return r.ChangeToSpeculativeOperator(
simplified()->SpeculativeNumberMultiply(feedback), Type::Number());
}
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
r.ConvertInputsToNumber(frame_state);
return r.ChangeToPureOperator(simplified()->NumberMultiply(), Type::Number());
......@@ -517,6 +528,11 @@ Reduction JSTypedLowering::ReduceJSMultiply(Node* node) {
Reduction JSTypedLowering::ReduceJSDivide(Node* node) {
if (flags() & kDisableBinaryOpReduction) return NoChange();
JSBinopReduction r(this, node);
BinaryOperationHints::Hint feedback = r.GetNumberBinaryOperationFeedback();
if (feedback != BinaryOperationHints::kAny) {
return r.ChangeToSpeculativeOperator(
simplified()->SpeculativeNumberDivide(feedback), Type::Number());
}
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
r.ConvertInputsToNumber(frame_state);
return r.ChangeToPureOperator(simplified()->NumberDivide(), Type::Number());
......
......@@ -182,6 +182,9 @@
V(BooleanToNumber) \
V(SpeculativeNumberAdd) \
V(SpeculativeNumberSubtract) \
V(SpeculativeNumberMultiply) \
V(SpeculativeNumberDivide) \
V(SpeculativeNumberModulus) \
V(SpeculativeNumberEqual) \
V(SpeculativeNumberLessThan) \
V(SpeculativeNumberLessThanOrEqual) \
......
......@@ -233,6 +233,36 @@ Type* OperationTyper::ModulusRanger(RangeType* lhs, RangeType* rhs) {
return result;
}
Type* OperationTyper::MultiplyRanger(Type* lhs, Type* rhs) {
double results[4];
double lmin = lhs->AsRange()->Min();
double lmax = lhs->AsRange()->Max();
double rmin = rhs->AsRange()->Min();
double rmax = rhs->AsRange()->Max();
results[0] = lmin * rmin;
results[1] = lmin * rmax;
results[2] = lmax * rmin;
results[3] = lmax * rmax;
// If the result may be nan, we give up on calculating a precise type,
// because
// the discontinuity makes it too complicated. Note that even if none of
// the
// "results" above is nan, the actual result may still be, so we have to do
// a
// different check:
bool maybe_nan = (lhs->Maybe(cache_.kSingletonZero) &&
(rmin == -V8_INFINITY || rmax == +V8_INFINITY)) ||
(rhs->Maybe(cache_.kSingletonZero) &&
(lmin == -V8_INFINITY || lmax == +V8_INFINITY));
if (maybe_nan) return cache_.kIntegerOrMinusZeroOrNaN; // Giving up.
bool maybe_minuszero = (lhs->Maybe(cache_.kSingletonZero) && rmin < 0) ||
(rhs->Maybe(cache_.kSingletonZero) && lmin < 0);
Type* range =
Type::Range(array_min(results, 4), array_max(results, 4), zone());
return maybe_minuszero ? Type::Union(range, Type::MinusZero(), zone())
: range;
}
Type* OperationTyper::ToNumber(Type* type) {
if (type->Is(Type::Number())) return type;
if (type->Is(Type::NullOrUndefined())) {
......@@ -282,6 +312,50 @@ Type* OperationTyper::NumericSubtract(Type* lhs, Type* rhs) {
return Type::Number();
}
Type* OperationTyper::NumericMultiply(Type* lhs, Type* rhs) {
DCHECK(lhs->Is(Type::Number()));
DCHECK(rhs->Is(Type::Number()));
lhs = Rangify(lhs);
rhs = Rangify(rhs);
if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
if (lhs->IsRange() && rhs->IsRange()) {
return MultiplyRanger(lhs, rhs);
}
return Type::Number();
}
Type* OperationTyper::NumericDivide(Type* lhs, Type* rhs) {
DCHECK(lhs->Is(Type::Number()));
DCHECK(rhs->Is(Type::Number()));
if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
// Division is tricky, so all we do is try ruling out nan.
bool maybe_nan =
lhs->Maybe(Type::NaN()) || rhs->Maybe(cache_.kZeroish) ||
((lhs->Min() == -V8_INFINITY || lhs->Max() == +V8_INFINITY) &&
(rhs->Min() == -V8_INFINITY || rhs->Max() == +V8_INFINITY));
return maybe_nan ? Type::Number() : Type::OrderedNumber();
}
Type* OperationTyper::NumericModulus(Type* lhs, Type* rhs) {
DCHECK(lhs->Is(Type::Number()));
DCHECK(rhs->Is(Type::Number()));
if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
if (lhs->Maybe(Type::NaN()) || rhs->Maybe(cache_.kZeroish) ||
lhs->Min() == -V8_INFINITY || lhs->Max() == +V8_INFINITY) {
// Result maybe NaN.
return Type::Number();
}
lhs = Rangify(lhs);
rhs = Rangify(rhs);
if (lhs->IsRange() && rhs->IsRange()) {
return ModulusRanger(lhs->AsRange(), rhs->AsRange());
}
return Type::OrderedNumber();
}
Type* OperationTyper::ToPrimitive(Type* type) {
if (type->Is(Type::Primitive()) && !type->Maybe(Type::Receiver())) {
return type;
......
......@@ -34,6 +34,9 @@ class OperationTyper {
Type* NumericAdd(Type* lhs, Type* rhs);
Type* NumericSubtract(Type* lhs, Type* rhs);
Type* NumericMultiply(Type* lhs, Type* rhs);
Type* NumericDivide(Type* lhs, Type* rhs);
Type* NumericModulus(Type* lhs, Type* rhs);
enum ComparisonOutcomeFlags {
kComparisonTrue = 1,
......@@ -60,6 +63,7 @@ class OperationTyper {
Type* Rangify(Type*);
Type* AddRanger(RangeType* lhs, RangeType* rhs);
Type* SubtractRanger(RangeType* lhs, RangeType* rhs);
Type* MultiplyRanger(Type* lhs, Type* rhs);
Type* ModulusRanger(RangeType* lhs, RangeType* rhs);
Zone* zone() { return zone_; }
......
......@@ -570,10 +570,13 @@ const Operator* RepresentationChanger::Int32OperatorFor(
case IrOpcode::kSpeculativeNumberSubtract: // Fall through.
case IrOpcode::kNumberSubtract:
return machine()->Int32Sub();
case IrOpcode::kSpeculativeNumberMultiply:
case IrOpcode::kNumberMultiply:
return machine()->Int32Mul();
case IrOpcode::kSpeculativeNumberDivide:
case IrOpcode::kNumberDivide:
return machine()->Int32Div();
case IrOpcode::kSpeculativeNumberModulus:
case IrOpcode::kNumberModulus:
return machine()->Int32Mod();
case IrOpcode::kNumberBitwiseOr:
......@@ -617,10 +620,13 @@ const Operator* RepresentationChanger::Uint32OperatorFor(
return machine()->Int32Add();
case IrOpcode::kNumberSubtract:
return machine()->Int32Sub();
case IrOpcode::kSpeculativeNumberMultiply:
case IrOpcode::kNumberMultiply:
return machine()->Int32Mul();
case IrOpcode::kSpeculativeNumberDivide:
case IrOpcode::kNumberDivide:
return machine()->Uint32Div();
case IrOpcode::kSpeculativeNumberModulus:
case IrOpcode::kNumberModulus:
return machine()->Uint32Mod();
case IrOpcode::kNumberEqual:
......@@ -652,10 +658,13 @@ const Operator* RepresentationChanger::Float64OperatorFor(
case IrOpcode::kSpeculativeNumberSubtract:
case IrOpcode::kNumberSubtract:
return machine()->Float64Sub();
case IrOpcode::kSpeculativeNumberMultiply:
case IrOpcode::kNumberMultiply:
return machine()->Float64Mul();
case IrOpcode::kSpeculativeNumberDivide:
case IrOpcode::kNumberDivide:
return machine()->Float64Div();
case IrOpcode::kSpeculativeNumberModulus:
case IrOpcode::kNumberModulus:
return machine()->Float64Mod();
case IrOpcode::kNumberEqual:
......
......@@ -398,6 +398,66 @@ class RepresentationSelector {
break;
}
case IrOpcode::kSpeculativeNumberMultiply: {
Type* lhs = FeedbackTypeOf(node->InputAt(0));
Type* rhs = FeedbackTypeOf(node->InputAt(1));
if (lhs->Is(Type::None()) || rhs->Is(Type::None())) return false;
// TODO(jarin) The ToNumber conversion is too conservative here,
// e.g. it will treat true as 1 even though the number check will
// fail on a boolean. OperationTyper should have a function that
// computes a more precise type.
lhs = op_typer_.ToNumber(lhs);
rhs = op_typer_.ToNumber(rhs);
Type* static_type = op_typer_.NumericMultiply(lhs, rhs);
if (info->type_check() == TypeCheckKind::kNone) {
new_type = static_type;
} else {
Type* feedback_type = TypeOfSpeculativeOp(info->type_check());
new_type = Type::Intersect(static_type, feedback_type, graph_zone());
}
break;
}
case IrOpcode::kSpeculativeNumberDivide: {
Type* lhs = FeedbackTypeOf(node->InputAt(0));
Type* rhs = FeedbackTypeOf(node->InputAt(1));
if (lhs->Is(Type::None()) || rhs->Is(Type::None())) return false;
// TODO(jarin) The ToNumber conversion is too conservative here,
// e.g. it will treat true as 1 even though the number check will
// fail on a boolean. OperationTyper should have a function that
// computes a more precise type.
lhs = op_typer_.ToNumber(lhs);
rhs = op_typer_.ToNumber(rhs);
Type* static_type = op_typer_.NumericDivide(lhs, rhs);
if (info->type_check() == TypeCheckKind::kNone) {
new_type = static_type;
} else {
Type* feedback_type = TypeOfSpeculativeOp(info->type_check());
new_type = Type::Intersect(static_type, feedback_type, graph_zone());
}
break;
}
case IrOpcode::kSpeculativeNumberModulus: {
Type* lhs = FeedbackTypeOf(node->InputAt(0));
Type* rhs = FeedbackTypeOf(node->InputAt(1));
if (lhs->Is(Type::None()) || rhs->Is(Type::None())) return false;
// TODO(jarin) The ToNumber conversion is too conservative here,
// e.g. it will treat true as 1 even though the number check will
// fail on a boolean. OperationTyper should have a function that
// computes a more precise type.
lhs = op_typer_.ToNumber(lhs);
rhs = op_typer_.ToNumber(rhs);
Type* static_type = op_typer_.NumericModulus(lhs, rhs);
if (info->type_check() == TypeCheckKind::kNone) {
new_type = static_type;
} else {
Type* feedback_type = TypeOfSpeculativeOp(info->type_check());
new_type = Type::Intersect(static_type, feedback_type, graph_zone());
}
break;
}
case IrOpcode::kPhi: {
new_type = TypePhi(node);
if (type != nullptr) {
......@@ -1003,10 +1063,7 @@ class RepresentationSelector {
return jsgraph_->simplified();
}
void ChangeToPureOp(Node* node, const Operator* new_op) {
// Disconnect the node from effect and control chains.
Node* control = NodeProperties::GetControlInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
void ReplaceEffectControlUses(Node* node, Node* effect, Node* control) {
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsControlEdge(edge)) {
edge.UpdateTo(control);
......@@ -1016,21 +1073,21 @@ class RepresentationSelector {
DCHECK(NodeProperties::IsValueEdge(edge));
}
}
node->TrimInputCount(new_op->ValueInputCount());
NodeProperties::ChangeOp(node, new_op);
}
void ReplaceEffectControlUses(Node* node, Node* effect, Node* control) {
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsControlEdge(edge)) {
edge.UpdateTo(control);
} else if (NodeProperties::IsEffectEdge(edge)) {
edge.UpdateTo(effect);
} else {
DCHECK(NodeProperties::IsValueEdge(edge));
}
void ChangeToPureOp(Node* node, const Operator* new_op) {
if (node->op()->EffectInputCount() > 0) {
DCHECK_LT(0, node->op()->ControlInputCount());
// Disconnect the node from effect and control chains.
Node* control = NodeProperties::GetControlInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
ReplaceEffectControlUses(node, effect, control);
node->TrimInputCount(new_op->ValueInputCount());
} else {
DCHECK_EQ(0, node->op()->ControlInputCount());
}
NodeProperties::ChangeOp(node, new_op);
}
void ChangeToInt32OverflowOp(Node* node, const Operator* op) {
......@@ -1296,13 +1353,14 @@ class RepresentationSelector {
}
return;
}
case IrOpcode::kSpeculativeNumberMultiply:
case IrOpcode::kNumberMultiply: {
if (BothInputsAreSigned32(node)) {
if (NodeProperties::GetType(node)->Is(Type::Signed32())) {
// Multiply reduces to Int32Mul if the inputs and the output
// are integers.
VisitInt32Binop(node);
if (lower()) NodeProperties::ChangeOp(node, Int32Op(node));
if (lower()) ChangeToPureOp(node, Int32Op(node));
return;
}
if (truncation.TruncatesToWord32() &&
......@@ -1312,15 +1370,24 @@ class RepresentationSelector {
// the uses are truncating and the result is in the safe
// integer range.
VisitWord32TruncatingBinop(node);
if (lower()) NodeProperties::ChangeOp(node, Int32Op(node));
if (lower()) ChangeToPureOp(node, Int32Op(node));
return;
}
}
// => Float64Mul
VisitFloat64Binop(node);
if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
// Number x Number => Float64Mul
if (BothInputsAre(node, Type::NumberOrUndefined())) {
VisitFloat64Binop(node);
if (lower()) ChangeToPureOp(node, Float64Op(node));
return;
}
// Checked float64 x float64 => float64
DCHECK_EQ(IrOpcode::kSpeculativeNumberMultiply, node->opcode());
VisitBinop(node, UseInfo::CheckedNumberOrUndefinedAsFloat64(),
MachineRepresentation::kFloat64, TypeCheckKind::kNumber);
if (lower()) ChangeToPureOp(node, Float64Op(node));
return;
}
case IrOpcode::kSpeculativeNumberDivide:
case IrOpcode::kNumberDivide: {
if (BothInputsAreSigned32(node)) {
if (NodeProperties::GetType(node)->Is(Type::Signed32())) {
......@@ -1342,11 +1409,20 @@ class RepresentationSelector {
if (lower()) DeferReplacement(node, lowering->Uint32Div(node));
return;
}
// => Float64Div
VisitFloat64Binop(node);
if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
// Number x Number => Float64Div
if (BothInputsAre(node, Type::NumberOrUndefined())) {
VisitFloat64Binop(node);
if (lower()) ChangeToPureOp(node, Float64Op(node));
return;
}
// Checked float64 x float64 => float64
DCHECK_EQ(IrOpcode::kSpeculativeNumberDivide, node->opcode());
VisitBinop(node, UseInfo::CheckedNumberOrUndefinedAsFloat64(),
MachineRepresentation::kFloat64, TypeCheckKind::kNumber);
if (lower()) ChangeToPureOp(node, Float64Op(node));
return;
}
case IrOpcode::kSpeculativeNumberModulus:
case IrOpcode::kNumberModulus: {
if (BothInputsAreSigned32(node)) {
if (NodeProperties::GetType(node)->Is(Type::Signed32())) {
......@@ -1368,9 +1444,18 @@ class RepresentationSelector {
if (lower()) DeferReplacement(node, lowering->Uint32Mod(node));
return;
}
// => Float64Mod
VisitFloat64Binop(node);
if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
// Number x Number => Float64Mod
if (BothInputsAre(node, Type::NumberOrUndefined())) {
// => Float64Mod
VisitFloat64Binop(node);
if (lower()) ChangeToPureOp(node, Float64Op(node));
return;
}
// Checked float64 x float64 => float64
DCHECK_EQ(IrOpcode::kSpeculativeNumberModulus, node->opcode());
VisitBinop(node, UseInfo::CheckedNumberOrUndefinedAsFloat64(),
MachineRepresentation::kFloat64, TypeCheckKind::kNumber);
if (lower()) ChangeToPureOp(node, Float64Op(node));
return;
}
case IrOpcode::kNumberBitwiseOr:
......@@ -1937,6 +2022,17 @@ class RepresentationSelector {
node->op()->mnemonic(), replacement->id(),
replacement->op()->mnemonic());
// Disconnect the node from effect and control chains, if necessary.
if (node->op()->EffectInputCount() > 0) {
DCHECK_LT(0, node->op()->ControlInputCount());
// Disconnect the node from effect and control chains.
Node* control = NodeProperties::GetControlInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
ReplaceEffectControlUses(node, effect, control);
} else {
DCHECK_EQ(0, node->op()->ControlInputCount());
}
if (replacement->id() < count_ &&
GetUpperBound(node)->Is(GetUpperBound(replacement)) &&
TypeOf(node)->Is(TypeOf(replacement))) {
......
......@@ -219,7 +219,10 @@ Type* TypeOf(const Operator* op) {
BinaryOperationHints::Hint BinaryOperationHintOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kSpeculativeNumberAdd ||
op->opcode() == IrOpcode::kSpeculativeNumberSubtract);
op->opcode() == IrOpcode::kSpeculativeNumberSubtract ||
op->opcode() == IrOpcode::kSpeculativeNumberMultiply ||
op->opcode() == IrOpcode::kSpeculativeNumberDivide ||
op->opcode() == IrOpcode::kSpeculativeNumberModulus);
return OpParameter<BinaryOperationHints::Hint>(op);
}
......@@ -297,6 +300,13 @@ CompareOperationHints::Hint CompareOperationHintOf(const Operator* op) {
V(StringLessThan, Operator::kNoProperties, 2) \
V(StringLessThanOrEqual, Operator::kNoProperties, 2)
#define SPECULATIVE_BINOP_LIST(V) \
V(SpeculativeNumberAdd) \
V(SpeculativeNumberSubtract) \
V(SpeculativeNumberDivide) \
V(SpeculativeNumberMultiply) \
V(SpeculativeNumberModulus)
#define CHECKED_OP_LIST(V) \
V(CheckedUint32ToInt32) \
V(CheckedFloat64ToInt32) \
......@@ -498,19 +508,14 @@ const Operator* SimplifiedOperatorBuilder::StoreBuffer(BufferAccess access) {
return nullptr;
}
const Operator* SimplifiedOperatorBuilder::SpeculativeNumberAdd(
BinaryOperationHints::Hint hint) {
return new (zone()) Operator1<BinaryOperationHints::Hint>(
IrOpcode::kSpeculativeNumberAdd, Operator::kPure, "SpeculativeNumberAdd",
2, 1, 1, 1, 1, 1, hint);
}
const Operator* SimplifiedOperatorBuilder::SpeculativeNumberSubtract(
BinaryOperationHints::Hint hint) {
return new (zone()) Operator1<BinaryOperationHints::Hint>(
IrOpcode::kSpeculativeNumberSubtract, Operator::kPure,
"SpeculativeNumberSubtract", 2, 1, 1, 1, 1, 1, hint);
}
#define SPECULATIVE_BINOP_DEF(Name) \
const Operator* SimplifiedOperatorBuilder::Name( \
BinaryOperationHints::Hint hint) { \
return new (zone()) Operator1<BinaryOperationHints::Hint>( \
IrOpcode::k##Name, Operator::kPure, #Name, 2, 1, 1, 1, 1, 1, hint); \
}
SPECULATIVE_BINOP_LIST(SPECULATIVE_BINOP_DEF)
#undef SPECULATIVE_BINOP_DEF
const Operator* SimplifiedOperatorBuilder::SpeculativeNumberEqual(
CompareOperationHints::Hint hint) {
......
......@@ -201,6 +201,9 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* SpeculativeNumberAdd(BinaryOperationHints::Hint hint);
const Operator* SpeculativeNumberSubtract(BinaryOperationHints::Hint hint);
const Operator* SpeculativeNumberMultiply(BinaryOperationHints::Hint hint);
const Operator* SpeculativeNumberDivide(BinaryOperationHints::Hint hint);
const Operator* SpeculativeNumberModulus(BinaryOperationHints::Hint hint);
const Operator* SpeculativeNumberLessThan(CompareOperationHints::Hint hint);
const Operator* SpeculativeNumberLessThanOrEqual(
......
......@@ -1734,6 +1734,18 @@ Type* Typer::Visitor::TypeSpeculativeNumberSubtract(Node* node) {
return Type::Number();
}
Type* Typer::Visitor::TypeSpeculativeNumberMultiply(Node* node) {
return Type::Number();
}
Type* Typer::Visitor::TypeSpeculativeNumberDivide(Node* node) {
return Type::Number();
}
Type* Typer::Visitor::TypeSpeculativeNumberModulus(Node* node) {
return Type::Number();
}
Type* Typer::Visitor::TypeNumberMultiply(Node* node) { return Type::Number(); }
Type* Typer::Visitor::TypeNumberDivide(Node* node) { return Type::Number(); }
......@@ -1924,7 +1936,9 @@ Type* Typer::Visitor::TypeChangeTaggedToFloat64(Node* node) {
Type* Typer::Visitor::TypeTruncateTaggedToFloat64(Node* node) {
Type* arg = Operand(node, 0);
DCHECK(arg->Is(Type::NumberOrOddball()));
// TODO(jarin) This DCHECK does not work because of speculative feedback.
// Re-enable once we record the speculative feedback in types.
// DCHECK(arg->Is(Type::NumberOrOddball()));
return ChangeRepresentation(arg, Type::UntaggedFloat64(), zone());
}
......
......@@ -689,6 +689,9 @@ void Verifier::Visitor::Check(Node* node) {
break;
case IrOpcode::kSpeculativeNumberAdd:
case IrOpcode::kSpeculativeNumberSubtract:
case IrOpcode::kSpeculativeNumberMultiply:
case IrOpcode::kSpeculativeNumberDivide:
case IrOpcode::kSpeculativeNumberModulus:
CheckUpperIs(node, Type::Number());
break;
case IrOpcode::kSpeculativeNumberEqual:
......
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