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 @@
V(TransitionAndStoreNumberElement) \
V(TransitionElementsKind) \
V(TypeOf) \
V(Unsigned32Divide) \
V(VerifyType)
#define SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(V) \
......
......@@ -767,7 +767,7 @@ class RepresentationSelector {
TurboJsonFile json_of(info, std::ios_base::app);
JSONGraphWriter writer(json_of, graph(), source_positions_,
node_origins_);
writer.PrintPhase("V8.TFSimplifiedLowering [after retype]");
writer.PrintPhase("V8.TFSimplifiedLowering [after lower]");
}
// Verify all nodes.
......@@ -797,7 +797,6 @@ class RepresentationSelector {
RunPropagatePhase();
RunRetypePhase();
RunLowerPhase(lowering);
if (verification_enabled()) {
RunVerifyPhase(lowering->info_);
}
......@@ -2582,6 +2581,14 @@ class RepresentationSelector {
if (lower<T>()) ChangeToPureOp(node, Float64Op(node));
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:
return VisitSpeculativeNumberModulus<T>(node, truncation, lowering);
case IrOpcode::kNumberModulus: {
......
......@@ -799,7 +799,8 @@ bool operator==(CheckMinusZeroParameters const& lhs,
V(StringLessThan, Operator::kNoProperties, 2, 0) \
V(StringLessThanOrEqual, Operator::kNoProperties, 2, 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) \
V(BigIntAdd, Operator::kNoProperties, 2, 1) \
......
......@@ -1072,6 +1072,11 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
#endif
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.
const Operator* FastApiCall(
......
......@@ -328,16 +328,14 @@ Reduction TypedOptimization::ReduceNumberFloor(Node* node) {
//
// 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
// value (note that {rhs} cannot be less than 1 due to the
// plain-number type constraint on the {node}).
NodeProperties::ChangeOp(node, simplified()->NumberToUint32());
NodeProperties::SetType(node,
Type::Range(0, lhs_type.Max(), graph()->zone()));
return Changed(node);
node = graph()->NewNode(simplified()->Unsigned32Divide(), lhs, rhs);
return Replace(node);
}
}
return NoChange();
......
......@@ -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::TypeUnsigned32Divide(Node* node) {
Type lhs = Operand(node, 0);
return Type::Range(0, lhs.Max(), zone());
}
Type Typer::Visitor::JSCallTyper(Type fun, Typer* t) {
if (!fun.IsHeapConstant() || !fun.AsHeapConstant()->Ref().IsJSFunction()) {
return Type::NonInternal();
......
......@@ -1096,6 +1096,11 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::Unsigned32());
break;
case IrOpcode::kUnsigned32Divide:
CheckValueInputIs(node, 0, Type::Unsigned32());
CheckValueInputIs(node, 1, Type::Unsigned32());
CheckTypeIs(node, Type::Unsigned32());
break;
case IrOpcode::kSpeculativeToNumber:
CheckValueInputIs(node, 0, Type::Any());
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