Commit 9eca23e9 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbofan] Inline Number constructor in certain cases

This CL adds inlining for the Number constructor if new.target is not
present. The lowering is BigInt compatible, i.e. it converts BigInts to
numbers.

Bug: v8:7904
Change-Id: If03b9f872d82e50b6ded7709069181c33dc44e82
Reviewed-on: https://chromium-review.googlesource.com/1118557
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54454}
parent efaece9c
......@@ -142,6 +142,14 @@ TF_BUILTIN(ToNumber, CodeStubAssembler) {
Return(ToNumber(context, input));
}
// Like ToNumber, but also converts BigInts.
TF_BUILTIN(ToNumberConvertBigInt, CodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* input = Parameter(Descriptor::kArgument);
Return(ToNumber(context, input, BigIntHandling::kConvertToNumber));
}
// ES section #sec-tostring-applied-to-the-number-type
TF_BUILTIN(NumberToString, CodeStubAssembler) {
TNode<Number> input = CAST(Parameter(Descriptor::kArgument));
......
......@@ -186,6 +186,7 @@ namespace internal {
TFC(NonNumberToNumber, TypeConversion, 1) \
TFC(NonNumberToNumeric, TypeConversion, 1) \
TFC(ToNumber, TypeConversion, 1) \
TFC(ToNumberConvertBigInt, TypeConversion, 1) \
TFC(ToNumeric, TypeConversion, 1) \
TFC(NumberToString, TypeConversion, 1) \
TFC(ToString, TypeConversion, 1) \
......
......@@ -3665,6 +3665,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
return ReduceDatePrototypeGetTime(node);
case Builtins::kDateNow:
return ReduceDateNow(node);
case Builtins::kNumberConstructor:
return ReduceNumberConstructor(node);
default:
break;
}
......@@ -7157,6 +7159,26 @@ Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) {
return Changed(node);
}
// ES section #sec-number-constructor
Reduction JSCallReducer::ReduceNumberConstructor(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
if (p.arity() <= 2) {
ReplaceWithValue(node, jsgraph()->ZeroConstant());
}
// We don't have a new.target argument, so we can convert to number,
// but must also convert BigInts.
if (p.arity() == 3) {
Node* value = NodeProperties::GetValueInput(node, 2);
NodeProperties::ReplaceValueInputs(node, value);
NodeProperties::ChangeOp(node, javascript()->ToNumberConvertBigInt());
return Changed(node);
}
return NoChange();
}
Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
......
......@@ -190,6 +190,8 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
Reduction ReduceDateNow(Node* node);
Reduction ReduceNumberParseInt(Node* node);
Reduction ReduceNumberConstructor(Node* node);
// Returns the updated {to} node, and updates control and effect along the
// way.
Node* DoFilterPostCallbackWork(ElementsKind kind, Node** control,
......
......@@ -82,6 +82,7 @@ REPLACE_STUB_CALL(Equal)
REPLACE_STUB_CALL(ToInteger)
REPLACE_STUB_CALL(ToLength)
REPLACE_STUB_CALL(ToNumber)
REPLACE_STUB_CALL(ToNumberConvertBigInt)
REPLACE_STUB_CALL(ToNumeric)
REPLACE_STUB_CALL(ToName)
REPLACE_STUB_CALL(ToObject)
......
......@@ -589,6 +589,7 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(ToLength, Operator::kNoProperties, 1, 1) \
V(ToName, Operator::kNoProperties, 1, 1) \
V(ToNumber, Operator::kNoProperties, 1, 1) \
V(ToNumberConvertBigInt, Operator::kNoProperties, 1, 1) \
V(ToNumeric, Operator::kNoProperties, 1, 1) \
V(ToObject, Operator::kFoldable, 1, 1) \
V(ToString, Operator::kNoProperties, 1, 1) \
......
......@@ -685,6 +685,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* ToLength();
const Operator* ToName();
const Operator* ToNumber();
const Operator* ToNumberConvertBigInt();
const Operator* ToNumeric();
const Operator* ToObject();
const Operator* ToString();
......
......@@ -2329,6 +2329,7 @@ Reduction JSTypedLowering::Reduce(Node* node) {
case IrOpcode::kJSToName:
return ReduceJSToName(node);
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToNumberConvertBigInt:
case IrOpcode::kJSToNumeric:
return ReduceJSToNumberOrNumeric(node);
case IrOpcode::kJSToString:
......
......@@ -240,7 +240,8 @@ InductionVariable* LoopVariableOptimizer::TryGetInductionVariable(Node* phi) {
// TODO(jarin) Support both sides.
Node* input = arith->InputAt(0);
if (input->opcode() == IrOpcode::kSpeculativeToNumber ||
input->opcode() == IrOpcode::kJSToNumber) {
input->opcode() == IrOpcode::kJSToNumber ||
input->opcode() == IrOpcode::kJSToNumberConvertBigInt) {
input = input->InputAt(0);
}
if (input != phi) return nullptr;
......
......@@ -602,6 +602,7 @@ bool NodeProperties::CanBeNullOrUndefined(Isolate* isolate, Node* receiver,
case IrOpcode::kJSToLength:
case IrOpcode::kJSToName:
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToNumberConvertBigInt:
case IrOpcode::kJSToNumeric:
case IrOpcode::kJSToString:
case IrOpcode::kToBoolean:
......
......@@ -121,6 +121,7 @@
V(JSToLength) \
V(JSToName) \
V(JSToNumber) \
V(JSToNumberConvertBigInt) \
V(JSToNumeric) \
V(JSToObject) \
V(JSToString) \
......
......@@ -265,7 +265,9 @@ Type OperationTyper::ConvertReceiver(Type type) {
return type;
}
Type OperationTyper::ToNumberOrNumeric(Object::Conversion mode, Type 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) {
if (type.Is(Type::Number())) return type;
if (type.Is(Type::NullOrUndefined())) {
if (type.Is(Type::Null())) return cache_.kSingletonZero;
......@@ -289,6 +291,13 @@ Type OperationTyper::ToNumberOrNumeric(Object::Conversion mode, Type type) {
}
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;
}
......@@ -300,6 +309,13 @@ 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();
}
Type OperationTyper::ToNumeric(Type type) {
return ToNumberOrNumeric(Object::Conversion::kToNumeric, type);
}
......
......@@ -35,6 +35,7 @@ class V8_EXPORT_PRIVATE OperationTyper {
Type ToPrimitive(Type type);
Type ToNumber(Type type);
Type ToNumberConvertBigInt(Type type);
Type ToNumeric(Type type);
Type ToBoolean(Type type);
......@@ -78,6 +79,7 @@ class V8_EXPORT_PRIVATE OperationTyper {
typedef base::Flags<ComparisonOutcomeFlags> ComparisonOutcome;
Type ToNumberOrNumeric(Object::Conversion mode, Type type);
base::Optional<Type> ToNumberCommon(Type type);
ComparisonOutcome Invert(ComparisonOutcome);
Type Invert(Type);
......
......@@ -93,6 +93,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
case IrOpcode::kJSToLength:
case IrOpcode::kJSToName:
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToNumberConvertBigInt:
case IrOpcode::kJSToNumeric:
case IrOpcode::kJSToObject:
case IrOpcode::kJSToString:
......
......@@ -1563,6 +1563,7 @@ class RepresentationSelector {
return;
}
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToNumberConvertBigInt:
case IrOpcode::kJSToNumeric: {
VisitInputs(node);
// TODO(bmeurer): Optimize somewhat based on input type?
......@@ -3297,6 +3298,7 @@ void SimplifiedLowering::LowerAllNodes() {
void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToFloat64(
Node* node, RepresentationSelector* selector) {
DCHECK(node->opcode() == IrOpcode::kJSToNumber ||
node->opcode() == IrOpcode::kJSToNumberConvertBigInt ||
node->opcode() == IrOpcode::kJSToNumeric);
Node* value = node->InputAt(0);
Node* context = node->InputAt(1);
......@@ -3320,11 +3322,17 @@ void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToFloat64(
Node* efalse0 = effect;
Node* vfalse0;
{
Operator const* op = node->opcode() == IrOpcode::kJSToNumber
? ToNumberOperator()
: ToNumericOperator();
Node* code = node->opcode() == IrOpcode::kJSToNumber ? ToNumberCode()
: ToNumericCode();
Operator const* op =
node->opcode() == IrOpcode::kJSToNumber
? (node->opcode() == IrOpcode::kJSToNumberConvertBigInt
? ToNumberConvertBigIntOperator()
: ToNumberOperator())
: ToNumericOperator();
Node* code = node->opcode() == IrOpcode::kJSToNumber
? ToNumberCode()
: (node->opcode() == IrOpcode::kJSToNumberConvertBigInt
? ToNumberConvertBigIntCode()
: ToNumericCode());
vfalse0 = efalse0 = if_false0 = graph()->NewNode(
op, code, value, context, frame_state, efalse0, if_false0);
......@@ -3392,6 +3400,7 @@ void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToFloat64(
void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToWord32(
Node* node, RepresentationSelector* selector) {
DCHECK(node->opcode() == IrOpcode::kJSToNumber ||
node->opcode() == IrOpcode::kJSToNumberConvertBigInt ||
node->opcode() == IrOpcode::kJSToNumeric);
Node* value = node->InputAt(0);
Node* context = node->InputAt(1);
......@@ -3412,11 +3421,17 @@ void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToWord32(
Node* efalse0 = effect;
Node* vfalse0;
{
Operator const* op = node->opcode() == IrOpcode::kJSToNumber
? ToNumberOperator()
: ToNumericOperator();
Node* code = node->opcode() == IrOpcode::kJSToNumber ? ToNumberCode()
: ToNumericCode();
Operator const* op =
node->opcode() == IrOpcode::kJSToNumber
? (node->opcode() == IrOpcode::kJSToNumberConvertBigInt
? ToNumberConvertBigIntOperator()
: ToNumberOperator())
: ToNumericOperator();
Node* code = node->opcode() == IrOpcode::kJSToNumber
? ToNumberCode()
: (node->opcode() == IrOpcode::kJSToNumberConvertBigInt
? ToNumberConvertBigIntCode()
: ToNumericCode());
vfalse0 = efalse0 = if_false0 = graph()->NewNode(
op, code, value, context, frame_state, efalse0, if_false0);
......@@ -3922,6 +3937,16 @@ Node* SimplifiedLowering::ToNumberCode() {
return to_number_code_.get();
}
Node* SimplifiedLowering::ToNumberConvertBigIntCode() {
if (!to_number_convert_big_int_code_.is_set()) {
Callable callable =
Builtins::CallableFor(isolate(), Builtins::kToNumberConvertBigInt);
to_number_convert_big_int_code_.set(
jsgraph()->HeapConstant(callable.code()));
}
return to_number_convert_big_int_code_.get();
}
Node* SimplifiedLowering::ToNumericCode() {
if (!to_numeric_code_.is_set()) {
Callable callable = Builtins::CallableFor(isolate(), Builtins::kToNumeric);
......@@ -3942,6 +3967,19 @@ Operator const* SimplifiedLowering::ToNumberOperator() {
return to_number_operator_.get();
}
Operator const* SimplifiedLowering::ToNumberConvertBigIntOperator() {
if (!to_number_convert_big_int_operator_.is_set()) {
Callable callable =
Builtins::CallableFor(isolate(), Builtins::kToNumberConvertBigInt);
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
auto call_descriptor =
Linkage::GetStubCallDescriptor(graph()->zone(), callable.descriptor(),
0, flags, Operator::kNoProperties);
to_number_convert_big_int_operator_.set(common()->Call(call_descriptor));
}
return to_number_convert_big_int_operator_.get();
}
Operator const* SimplifiedLowering::ToNumericOperator() {
if (!to_numeric_operator_.is_set()) {
Callable callable = Builtins::CallableFor(isolate(), Builtins::kToNumeric);
......
......@@ -52,8 +52,10 @@ class V8_EXPORT_PRIVATE SimplifiedLowering final {
Zone* const zone_;
TypeCache const& type_cache_;
SetOncePointer<Node> to_number_code_;
SetOncePointer<Node> to_number_convert_big_int_code_;
SetOncePointer<Node> to_numeric_code_;
SetOncePointer<Operator const> to_number_operator_;
SetOncePointer<Operator const> to_number_convert_big_int_operator_;
SetOncePointer<Operator const> to_numeric_operator_;
// TODO(danno): SimplifiedLowering shouldn't know anything about the source
......@@ -76,8 +78,10 @@ class V8_EXPORT_PRIVATE SimplifiedLowering final {
Node* Uint32Mod(Node* const node);
Node* ToNumberCode();
Node* ToNumberConvertBigIntCode();
Node* ToNumericCode();
Operator const* ToNumberOperator();
Operator const* ToNumberConvertBigIntOperator();
Operator const* ToNumericOperator();
friend class RepresentationSelector;
......
......@@ -266,6 +266,7 @@ class Typer::Visitor : public Reducer {
static Type ToLength(Type, Typer*);
static Type ToName(Type, Typer*);
static Type ToNumber(Type, Typer*);
static Type ToNumberConvertBigInt(Type, Typer*);
static Type ToNumeric(Type, Typer*);
static Type ToObject(Type, Typer*);
static Type ToString(Type, Typer*);
......@@ -529,6 +530,11 @@ Type Typer::Visitor::ToNumber(Type type, Typer* t) {
return t->operation_typer_.ToNumber(type);
}
// static
Type Typer::Visitor::ToNumberConvertBigInt(Type type, Typer* t) {
return t->operation_typer_.ToNumberConvertBigInt(type);
}
// static
Type Typer::Visitor::ToNumeric(Type type, Typer* t) {
return t->operation_typer_.ToNumeric(type);
......@@ -1125,6 +1131,10 @@ Type Typer::Visitor::TypeJSToNumber(Node* node) {
return TypeUnaryOp(node, ToNumber);
}
Type Typer::Visitor::TypeJSToNumberConvertBigInt(Node* node) {
return TypeUnaryOp(node, ToNumberConvertBigInt);
}
Type Typer::Visitor::TypeJSToNumeric(Node* node) {
return TypeUnaryOp(node, ToNumeric);
}
......
......@@ -629,6 +629,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckTypeIs(node, Type::Name());
break;
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToNumberConvertBigInt:
// Type is Number.
CheckTypeIs(node, Type::Number());
break;
......
......@@ -605,7 +605,8 @@ static void CheckIsConvertedToNumber(Node* val, Node* converted) {
CHECK_EQ(val, converted);
} else {
if (converted->opcode() == IrOpcode::kNumberConstant) return;
CHECK_EQ(IrOpcode::kJSToNumber, converted->opcode());
CHECK(IrOpcode::kJSToNumber == converted->opcode() ||
IrOpcode::kJSToNumberConvertBigInt == converted->opcode());
CHECK_EQ(val, converted->InputAt(0));
}
}
......
......@@ -1039,6 +1039,19 @@
{"name": "OneLineComments"},
{"name": "MultiLineComment"}
]
},
{
"name": "Numbers",
"path": ["Numbers"],
"main": "run.js",
"flags": ["--allow-natives-syntax"],
"resources": [ "toNumber.js"],
"results_regexp": "^%s\\-Numbers\\(Score\\): (.+)$",
"tests": [
{"name": "Constructor"},
{"name": "UnaryPlus"},
{"name": "ParseFloat"}
]
}
]
}
// 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.
load('../base.js');
load('toNumber.js');
function PrintResult(name, result) {
console.log(name);
console.log(name + '-Numbers(Score): ' + result);
}
function PrintError(name, error) {
PrintResult(name, error);
}
BenchmarkSuite.config.doWarmup = undefined;
BenchmarkSuite.config.doDeterministic = undefined;
BenchmarkSuite.RunSuites({ NotifyResult: PrintResult,
NotifyError: PrintError });
// 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.
const A = [undefined, 12, "123"];
function NumberConstructor() {
Number.isNaN(Number(A[0]))
Number.isNaN(Number(A[1]))
Number.isNaN(Number(A[2]))
}
createSuite('Constructor', 1000, NumberConstructor, ()=>{});
function NumberPlus() {
Number.isNaN(+(A[0]))
Number.isNaN(+(A[1]))
Number.isNaN(+(A[2]))
}
createSuite('UnaryPlus', 1000, NumberPlus, ()=>{});
function NumberParseFloat() {
Number.isNaN(parseFloat(A[0]))
Number.isNaN(parseFloat(A[1]))
Number.isNaN(parseFloat(A[2]))
}
createSuite('ParseFloat', 1000, NumberParseFloat, ()=>{});
// 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 --harmony-bigint
function f(x, b) {
if (b) return Math.trunc(+(x))
else return Math.trunc(Number(x))
}
f("1", true);
f("2", true);
f("2", false);
%OptimizeFunctionOnNextCall(f);
f(3n);
......@@ -31,7 +31,7 @@ function Test(f, ...cases) {
function V(input, expected_value) {
function check(result) {
assertFalse(result.exception, input);
assertFalse(result.exception, `unexpected exception ${result.value} on input ${input}`);
assertEquals(expected_value, result.value);
}
return {input, check};
......@@ -39,7 +39,7 @@ function V(input, expected_value) {
function E(input, expected_exception) {
function check(result) {
assertTrue(result.exception, input);
assertTrue(result.exception, `expected exception ${expected_exception.name} on input ${input}`);
assertInstanceof(result.value, expected_exception);
}
return {input, check};
......@@ -56,10 +56,15 @@ const six = {[Symbol.toPrimitive]() {return 6n}};
// inputs.
////////////////////////////////////////////////////////////////////////////////
Test(x => Number(x),
V(1n, 1), V(1, 1), V("", 0), V(1.4, 1.4), V(null, 0), V(six, 6));
Test(x => Math.trunc(+x),
E(1n, TypeError), V(1, 1), V("", 0), V(1.4, 1), V(null, 0), E(six, TypeError));
Test(x => Math.trunc(Number(x)),
V(1n, 1), V(1, 1), V("", 0), V(1.4, 1), V(null, 0), V(six, 6));
Test(x => String(x),
V(1n, "1"), V(1, "1"), V(1.4, "1.4"), V(null, "null"), V(six, "6"));
......
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