Commit 46ed47e6 authored by Nico Hartmann's avatar Nico Hartmann Committed by V8 LUCI CQ

[turbofan] Fix inconsistent typing of NumberFloor(NumberDivide(...))

In typed-optimization, Turbofan optimized NumberFloor(NumberDivide(...))
patterns where both inputs are known to be of Unsigned32 type, but the
replacement couldn't be typed consistently. This CL introduces a new
operator Unsigned32Divide, which has the same semantics, but can be
typed consistently and thus allows the simplified lowering verifier to
validate the graph correctly.

Bug: v8:12619
Change-Id: Iad77154d3d840c94edfd3ab91ffa37c840da0bc9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3644790
Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80967}
parent e50d19cb
...@@ -493,6 +493,7 @@ ...@@ -493,6 +493,7 @@
V(TransitionAndStoreNumberElement) \ V(TransitionAndStoreNumberElement) \
V(TransitionElementsKind) \ V(TransitionElementsKind) \
V(TypeOf) \ V(TypeOf) \
V(Unsigned32Divide) \
V(VerifyType) V(VerifyType)
#define SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(V) \ #define SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(V) \
......
...@@ -767,7 +767,7 @@ class RepresentationSelector { ...@@ -767,7 +767,7 @@ class RepresentationSelector {
TurboJsonFile json_of(info, std::ios_base::app); TurboJsonFile json_of(info, std::ios_base::app);
JSONGraphWriter writer(json_of, graph(), source_positions_, JSONGraphWriter writer(json_of, graph(), source_positions_,
node_origins_); node_origins_);
writer.PrintPhase("V8.TFSimplifiedLowering [after retype]"); writer.PrintPhase("V8.TFSimplifiedLowering [after lower]");
} }
// Verify all nodes. // Verify all nodes.
...@@ -797,7 +797,6 @@ class RepresentationSelector { ...@@ -797,7 +797,6 @@ class RepresentationSelector {
RunPropagatePhase(); RunPropagatePhase();
RunRetypePhase(); RunRetypePhase();
RunLowerPhase(lowering); RunLowerPhase(lowering);
if (verification_enabled()) { if (verification_enabled()) {
RunVerifyPhase(lowering->info_); RunVerifyPhase(lowering->info_);
} }
...@@ -2582,6 +2581,14 @@ class RepresentationSelector { ...@@ -2582,6 +2581,14 @@ class RepresentationSelector {
if (lower<T>()) ChangeToPureOp(node, Float64Op(node)); if (lower<T>()) ChangeToPureOp(node, Float64Op(node));
return; return;
} }
case IrOpcode::kUnsigned32Divide: {
CHECK(TypeOf(node->InputAt(0)).Is(Type::Unsigned32()));
CHECK(TypeOf(node->InputAt(1)).Is(Type::Unsigned32()));
// => unsigned Uint32Div
VisitWord32TruncatingBinop<T>(node);
if (lower<T>()) DeferReplacement(node, lowering->Uint32Div(node));
return;
}
case IrOpcode::kSpeculativeNumberModulus: case IrOpcode::kSpeculativeNumberModulus:
return VisitSpeculativeNumberModulus<T>(node, truncation, lowering); return VisitSpeculativeNumberModulus<T>(node, truncation, lowering);
case IrOpcode::kNumberModulus: { case IrOpcode::kNumberModulus: {
......
...@@ -799,7 +799,8 @@ bool operator==(CheckMinusZeroParameters const& lhs, ...@@ -799,7 +799,8 @@ bool operator==(CheckMinusZeroParameters const& lhs,
V(StringLessThan, Operator::kNoProperties, 2, 0) \ V(StringLessThan, Operator::kNoProperties, 2, 0) \
V(StringLessThanOrEqual, Operator::kNoProperties, 2, 0) \ V(StringLessThanOrEqual, Operator::kNoProperties, 2, 0) \
V(ToBoolean, Operator::kNoProperties, 1, 0) \ V(ToBoolean, Operator::kNoProperties, 1, 0) \
V(NewConsString, Operator::kNoProperties, 3, 0) V(NewConsString, Operator::kNoProperties, 3, 0) \
V(Unsigned32Divide, Operator::kNoProperties, 2, 0)
#define EFFECT_DEPENDENT_OP_LIST(V) \ #define EFFECT_DEPENDENT_OP_LIST(V) \
V(BigIntAdd, Operator::kNoProperties, 2, 1) \ V(BigIntAdd, Operator::kNoProperties, 2, 1) \
......
...@@ -1072,6 +1072,11 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -1072,6 +1072,11 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
#endif #endif
const Operator* DateNow(); const Operator* DateNow();
// Unsigned32Divide is a special operator to express the division of two
// Unsigned32 inputs and truncating the result to Unsigned32. It's semantics
// is equivalent to NumberFloor(NumberDivide(x:Unsigned32, y:Unsigned32)) but
// is required to allow consistent typing of the graph.
const Operator* Unsigned32Divide();
// Represents the inputs necessary to construct a fast and a slow API call. // Represents the inputs necessary to construct a fast and a slow API call.
const Operator* FastApiCall( const Operator* FastApiCall(
......
...@@ -328,16 +328,14 @@ Reduction TypedOptimization::ReduceNumberFloor(Node* node) { ...@@ -328,16 +328,14 @@ Reduction TypedOptimization::ReduceNumberFloor(Node* node) {
// //
// with // with
// //
// NumberToUint32(NumberDivide(lhs, rhs)) // Unsigned32Divide(lhs, rhs)
// //
// and just smash the type [0...lhs.Max] on the {node}, // and have the new node typed to [0...lhs.Max],
// as the truncated result must be lower than {lhs}'s maximum // as the truncated result must be lower than {lhs}'s maximum
// value (note that {rhs} cannot be less than 1 due to the // value (note that {rhs} cannot be less than 1 due to the
// plain-number type constraint on the {node}). // plain-number type constraint on the {node}).
NodeProperties::ChangeOp(node, simplified()->NumberToUint32()); node = graph()->NewNode(simplified()->Unsigned32Divide(), lhs, rhs);
NodeProperties::SetType(node, return Replace(node);
Type::Range(0, lhs_type.Max(), graph()->zone()));
return Changed(node);
} }
} }
return NoChange(); return NoChange();
......
...@@ -1525,6 +1525,11 @@ Type Typer::Visitor::TypeJSObjectIsArray(Node* node) { return Type::Boolean(); } ...@@ -1525,6 +1525,11 @@ Type Typer::Visitor::TypeJSObjectIsArray(Node* node) { return Type::Boolean(); }
Type Typer::Visitor::TypeDateNow(Node* node) { return Type::Number(); } Type Typer::Visitor::TypeDateNow(Node* node) { return Type::Number(); }
Type Typer::Visitor::TypeUnsigned32Divide(Node* node) {
Type lhs = Operand(node, 0);
return Type::Range(0, lhs.Max(), zone());
}
Type Typer::Visitor::JSCallTyper(Type fun, Typer* t) { Type Typer::Visitor::JSCallTyper(Type fun, Typer* t) {
if (!fun.IsHeapConstant() || !fun.AsHeapConstant()->Ref().IsJSFunction()) { if (!fun.IsHeapConstant() || !fun.AsHeapConstant()->Ref().IsJSFunction()) {
return Type::NonInternal(); return Type::NonInternal();
......
...@@ -1096,6 +1096,11 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { ...@@ -1096,6 +1096,11 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::Number()); CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::Unsigned32()); CheckTypeIs(node, Type::Unsigned32());
break; break;
case IrOpcode::kUnsigned32Divide:
CheckValueInputIs(node, 0, Type::Unsigned32());
CheckValueInputIs(node, 1, Type::Unsigned32());
CheckTypeIs(node, Type::Unsigned32());
break;
case IrOpcode::kSpeculativeToNumber: case IrOpcode::kSpeculativeToNumber:
CheckValueInputIs(node, 0, Type::Any()); CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::Number()); CheckTypeIs(node, Type::Number());
......
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --turbofan
let g;
function test() {
const ten = 10;
const x = 10 / ten;
const y = Math.floor(x);
g = x;
return y + 1;
}
%PrepareFunctionForOptimization(test);
assertEquals(2, test());
%OptimizeFunctionOnNextCall(test);
assertEquals(2, test());
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