[turbofan] Improve typing of ToNumeric and ToNumber.
The previous typing rules for ToNumeric and ToNumber didn't match on the NonBigIntPrimitive input set, which causes trouble when we morph ToNumeric nodes into ToNumber nodes, and generally lead to worse typings in the graph, and thus worse code generation. This change improves the existing typing rules and turns ToNumber into a chokepoint again. Bug: chromium:879898, v8:8015 Change-Id: I4a7ff0e9c420c5dcfdb2b96884e019a5943828a4 Reviewed-on: https://chromium-review.googlesource.com/1201522 Reviewed-by: Georg Neis <neis@chromium.org> Commit-Queue: Benedikt Meurer <bmeurer@chromium.org> Cr-Commit-Position: refs/heads/master@{#55595}
This commit is contained in:
parent
4a96850aeb
commit
b898112277
@ -265,59 +265,61 @@ Type OperationTyper::ConvertReceiver(Type type) {
|
||||
return type;
|
||||
}
|
||||
|
||||
// Returns the result type of converting {type} to number, if the
|
||||
// result does not depend on conversion options.
|
||||
base::Optional<Type> OperationTyper::ToNumberCommon(Type type) {
|
||||
Type OperationTyper::ToNumber(Type type) {
|
||||
if (type.Is(Type::Number())) return type;
|
||||
if (type.Is(Type::NullOrUndefined())) {
|
||||
if (type.Is(Type::Null())) return cache_.kSingletonZero;
|
||||
if (type.Is(Type::Undefined())) return Type::NaN();
|
||||
return Type::Union(Type::NaN(), cache_.kSingletonZero, zone());
|
||||
}
|
||||
if (type.Is(Type::Boolean())) {
|
||||
if (type.Is(singleton_false_)) return cache_.kSingletonZero;
|
||||
if (type.Is(singleton_true_)) return cache_.kSingletonOne;
|
||||
return cache_.kZeroOrOne;
|
||||
}
|
||||
if (type.Is(Type::NumberOrOddball())) {
|
||||
if (type.Is(Type::NumberOrUndefined())) {
|
||||
type = Type::Union(type, Type::NaN(), zone());
|
||||
} else if (type.Is(Type::NullOrNumber())) {
|
||||
|
||||
// If {type} includes any receivers, we cannot tell what kind of
|
||||
// Number their callbacks might produce. Similarly in the case
|
||||
// where {type} includes String, it's not possible at this point
|
||||
// to tell which exact numbers are going to be produced.
|
||||
if (type.Maybe(Type::StringOrReceiver())) return Type::Number();
|
||||
|
||||
// Both Symbol and BigInt primitives will cause exceptions
|
||||
// to be thrown from ToNumber conversions, so they don't
|
||||
// contribute to the resulting type anyways.
|
||||
type = Type::Intersect(type, Type::PlainPrimitive(), zone());
|
||||
|
||||
// This leaves us with Number\/Oddball, so deal with the individual
|
||||
// Oddball primitives below.
|
||||
DCHECK(type.Is(Type::NumberOrOddball()));
|
||||
if (type.Maybe(Type::Null())) {
|
||||
// ToNumber(null) => +0
|
||||
type = Type::Union(type, cache_.kSingletonZero, zone());
|
||||
} else if (type.Is(Type::BooleanOrNullOrNumber())) {
|
||||
type = Type::Union(type, cache_.kZeroOrOne, zone());
|
||||
} else {
|
||||
type = Type::Union(type, cache_.kZeroOrOneOrNaN, zone());
|
||||
}
|
||||
if (type.Maybe(Type::Undefined())) {
|
||||
// ToNumber(undefined) => NaN
|
||||
type = Type::Union(type, Type::NaN(), zone());
|
||||
}
|
||||
if (type.Maybe(singleton_false_)) {
|
||||
// ToNumber(false) => +0
|
||||
type = Type::Union(type, cache_.kSingletonZero, zone());
|
||||
}
|
||||
if (type.Maybe(singleton_true_)) {
|
||||
// ToNumber(true) => +1
|
||||
type = Type::Union(type, cache_.kSingletonOne, zone());
|
||||
}
|
||||
return Type::Intersect(type, Type::Number(), zone());
|
||||
}
|
||||
return base::Optional<Type>();
|
||||
}
|
||||
|
||||
Type OperationTyper::ToNumberOrNumeric(Object::Conversion mode, Type type) {
|
||||
if (base::Optional<Type> maybe_result_type = ToNumberCommon(type)) {
|
||||
return *maybe_result_type;
|
||||
}
|
||||
if (type.Is(Type::BigInt())) {
|
||||
return mode == Object::Conversion::kToNumber ? Type::None() : type;
|
||||
}
|
||||
return mode == Object::Conversion::kToNumber ? Type::Number()
|
||||
: Type::Numeric();
|
||||
}
|
||||
|
||||
Type OperationTyper::ToNumber(Type type) {
|
||||
return ToNumberOrNumeric(Object::Conversion::kToNumber, type);
|
||||
}
|
||||
|
||||
Type OperationTyper::ToNumberConvertBigInt(Type type) {
|
||||
if (base::Optional<Type> maybe_result_type = ToNumberCommon(type)) {
|
||||
return *maybe_result_type;
|
||||
}
|
||||
return Type::Number();
|
||||
// If the {type} includes any receivers, then the callbacks
|
||||
// might actually produce BigInt primitive values here.
|
||||
bool maybe_bigint =
|
||||
type.Maybe(Type::BigInt()) || type.Maybe(Type::Receiver());
|
||||
type = ToNumber(Type::Intersect(type, Type::NonBigInt(), zone()));
|
||||
|
||||
// Any BigInt is rounded to an integer Number in the range [-inf, inf].
|
||||
return maybe_bigint ? Type::Union(type, cache_.kInteger, zone()) : type;
|
||||
}
|
||||
|
||||
Type OperationTyper::ToNumeric(Type type) {
|
||||
return ToNumberOrNumeric(Object::Conversion::kToNumeric, type);
|
||||
// If the {type} includes any receivers, then the callbacks
|
||||
// might actually produce BigInt primitive values here.
|
||||
if (type.Maybe(Type::Receiver())) {
|
||||
type = Type::Union(type, Type::BigInt(), zone());
|
||||
}
|
||||
return Type::Union(ToNumber(Type::Intersect(type, Type::NonBigInt(), zone())),
|
||||
Type::Intersect(type, Type::BigInt(), zone()), zone());
|
||||
}
|
||||
|
||||
Type OperationTyper::NumberAbs(Type type) {
|
||||
|
@ -77,9 +77,6 @@ class V8_EXPORT_PRIVATE OperationTyper {
|
||||
private:
|
||||
typedef base::Flags<ComparisonOutcomeFlags> ComparisonOutcome;
|
||||
|
||||
Type ToNumberOrNumeric(Object::Conversion mode, Type type);
|
||||
base::Optional<Type> ToNumberCommon(Type type);
|
||||
|
||||
ComparisonOutcome Invert(ComparisonOutcome);
|
||||
Type Invert(Type);
|
||||
Type FalsifyUndefined(ComparisonOutcome);
|
||||
|
@ -194,7 +194,8 @@ namespace compiler {
|
||||
kUndefined | kReceiver) \
|
||||
V(Internal, kHole | kExternalPointer | kOtherInternal) \
|
||||
V(NonInternal, kPrimitive | kReceiver) \
|
||||
V(NonNumber, kUnique | kString | kInternal) \
|
||||
V(NonBigInt, kNonBigIntPrimitive | kReceiver) \
|
||||
V(NonNumber, kBigInt | kUnique | kString | kInternal) \
|
||||
V(Any, 0xfffffffeu)
|
||||
|
||||
// clang-format on
|
||||
|
12
test/mjsunit/regress/regress-crbug-879898.js
Normal file
12
test/mjsunit/regress/regress-crbug-879898.js
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright 2018 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
|
||||
|
||||
function foo() {
|
||||
return Symbol.toPrimitive++;
|
||||
}
|
||||
assertThrows(foo);
|
||||
%OptimizeFunctionOnNextCall(foo);
|
||||
assertThrows(foo);
|
Loading…
Reference in New Issue
Block a user