Commit b8981122 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[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/1201522Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55595}
parent 4a96850a
...@@ -265,59 +265,61 @@ Type OperationTyper::ConvertReceiver(Type type) { ...@@ -265,59 +265,61 @@ Type OperationTyper::ConvertReceiver(Type type) {
return type; return type;
} }
// Returns the result type of converting {type} to number, if the Type OperationTyper::ToNumber(Type type) {
// result does not depend on conversion options.
base::Optional<Type> OperationTyper::ToNumberCommon(Type type) {
if (type.Is(Type::Number())) return type; if (type.Is(Type::Number())) return type;
if (type.Is(Type::NullOrUndefined())) {
if (type.Is(Type::Null())) return cache_.kSingletonZero; // If {type} includes any receivers, we cannot tell what kind of
if (type.Is(Type::Undefined())) return Type::NaN(); // Number their callbacks might produce. Similarly in the case
return Type::Union(Type::NaN(), cache_.kSingletonZero, zone()); // where {type} includes String, it's not possible at this point
} // to tell which exact numbers are going to be produced.
if (type.Is(Type::Boolean())) { if (type.Maybe(Type::StringOrReceiver())) return Type::Number();
if (type.Is(singleton_false_)) return cache_.kSingletonZero;
if (type.Is(singleton_true_)) return cache_.kSingletonOne; // Both Symbol and BigInt primitives will cause exceptions
return cache_.kZeroOrOne; // to be thrown from ToNumber conversions, so they don't
} // contribute to the resulting type anyways.
if (type.Is(Type::NumberOrOddball())) { type = Type::Intersect(type, Type::PlainPrimitive(), zone());
if (type.Is(Type::NumberOrUndefined())) {
type = Type::Union(type, Type::NaN(), zone()); // This leaves us with Number\/Oddball, so deal with the individual
} else if (type.Is(Type::NullOrNumber())) { // Oddball primitives below.
DCHECK(type.Is(Type::NumberOrOddball()));
if (type.Maybe(Type::Null())) {
// ToNumber(null) => +0
type = Type::Union(type, cache_.kSingletonZero, zone()); 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());
} }
return Type::Intersect(type, Type::Number(), zone()); if (type.Maybe(Type::Undefined())) {
// ToNumber(undefined) => NaN
type = Type::Union(type, Type::NaN(), zone());
} }
return base::Optional<Type>(); if (type.Maybe(singleton_false_)) {
} // ToNumber(false) => +0
type = Type::Union(type, cache_.kSingletonZero, zone());
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())) { if (type.Maybe(singleton_true_)) {
return mode == Object::Conversion::kToNumber ? Type::None() : type; // ToNumber(true) => +1
type = Type::Union(type, cache_.kSingletonOne, zone());
} }
return mode == Object::Conversion::kToNumber ? Type::Number() return Type::Intersect(type, Type::Number(), zone());
: Type::Numeric();
}
Type OperationTyper::ToNumber(Type type) {
return ToNumberOrNumeric(Object::Conversion::kToNumber, type);
} }
Type OperationTyper::ToNumberConvertBigInt(Type type) { Type OperationTyper::ToNumberConvertBigInt(Type type) {
if (base::Optional<Type> maybe_result_type = ToNumberCommon(type)) { // If the {type} includes any receivers, then the callbacks
return *maybe_result_type; // might actually produce BigInt primitive values here.
} bool maybe_bigint =
return Type::Number(); 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) { 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) { Type OperationTyper::NumberAbs(Type type) {
......
...@@ -77,9 +77,6 @@ class V8_EXPORT_PRIVATE OperationTyper { ...@@ -77,9 +77,6 @@ class V8_EXPORT_PRIVATE OperationTyper {
private: private:
typedef base::Flags<ComparisonOutcomeFlags> ComparisonOutcome; typedef base::Flags<ComparisonOutcomeFlags> ComparisonOutcome;
Type ToNumberOrNumeric(Object::Conversion mode, Type type);
base::Optional<Type> ToNumberCommon(Type type);
ComparisonOutcome Invert(ComparisonOutcome); ComparisonOutcome Invert(ComparisonOutcome);
Type Invert(Type); Type Invert(Type);
Type FalsifyUndefined(ComparisonOutcome); Type FalsifyUndefined(ComparisonOutcome);
......
...@@ -194,7 +194,8 @@ namespace compiler { ...@@ -194,7 +194,8 @@ namespace compiler {
kUndefined | kReceiver) \ kUndefined | kReceiver) \
V(Internal, kHole | kExternalPointer | kOtherInternal) \ V(Internal, kHole | kExternalPointer | kOtherInternal) \
V(NonInternal, kPrimitive | kReceiver) \ V(NonInternal, kPrimitive | kReceiver) \
V(NonNumber, kUnique | kString | kInternal) \ V(NonBigInt, kNonBigIntPrimitive | kReceiver) \
V(NonNumber, kBigInt | kUnique | kString | kInternal) \
V(Any, 0xfffffffeu) V(Any, 0xfffffffeu)
// clang-format on // clang-format on
......
// 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);
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