Commit 02dbef14 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Introduce NumberToString operator.

This adds a new simplified operator NumberToString, which just lowers to
a call to the NumberToString builtin, and hooks that up to the typed
lowering (addressing a long-standing TODO).

Drive-by-fix: Also remove the %NumberToString runtime entry, and just
always use the %NumberToStringSkipCache entry from CSA, since we only
go there if the cache lookup already failed.

Bug: v8:5267, v8:7109
Change-Id: I5ca698c98679653813088a404f1fd38903a73c0e
Reviewed-on: https://chromium-review.googlesource.com/779099
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarMichael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50636}
parent 6505d9b5
......@@ -5265,7 +5265,8 @@ Node* CodeStubAssembler::NumberToString(Node* context, Node* argument) {
BIND(&runtime);
{
// No cache entry, go to the runtime.
result.Bind(CallRuntime(Runtime::kNumberToString, context, argument));
result.Bind(
CallRuntime(Runtime::kNumberToStringSkipCache, context, argument));
}
Goto(&done);
......
......@@ -746,6 +746,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kCheckedTruncateTaggedToWord32:
result = LowerCheckedTruncateTaggedToWord32(node, frame_state);
break;
case IrOpcode::kNumberToString:
result = LowerNumberToString(node);
break;
case IrOpcode::kObjectIsArrayBufferView:
result = LowerObjectIsArrayBufferView(node);
break;
......@@ -2028,6 +2031,19 @@ Node* EffectControlLinearizer::LowerAllocate(Node* node) {
return new_node;
}
Node* EffectControlLinearizer::LowerNumberToString(Node* node) {
Node* argument = node->InputAt(0);
Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kNumberToString);
Operator::Properties properties = Operator::kEliminatable;
CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties);
return __ Call(desc, __ HeapConstant(callable.code()), argument,
__ NoContextConstant());
}
Node* EffectControlLinearizer::LowerObjectIsArrayBufferView(Node* node) {
Node* value = node->InputAt(0);
......
......@@ -90,6 +90,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerTruncateTaggedToWord32(Node* node);
Node* LowerCheckedTruncateTaggedToWord32(Node* node, Node* frame_state);
Node* LowerAllocate(Node* node);
Node* LowerNumberToString(Node* node);
Node* LowerObjectIsArrayBufferView(Node* node);
Node* LowerObjectIsBigInt(Node* node);
Node* LowerObjectIsCallable(Node* node);
......
......@@ -1045,7 +1045,9 @@ Reduction JSTypedLowering::ReduceJSToStringInput(Node* input) {
return Replace(jsgraph()->HeapConstant(
factory()->NumberToString(factory()->NewNumber(input_type->Min()))));
}
// TODO(turbofan): js-typed-lowering of ToString(x:number)
if (input_type->Is(Type::Number())) {
return Replace(graph()->NewNode(simplified()->NumberToString(), input));
}
return NoChange();
}
......
......@@ -318,6 +318,7 @@
V(NumberTrunc) \
V(NumberToBoolean) \
V(NumberToInt32) \
V(NumberToString) \
V(NumberToUint32) \
V(NumberToUint8Clamped) \
V(NumberSilenceNaN)
......
......@@ -24,6 +24,8 @@ OperationTyper::OperationTyper(Isolate* isolate, Zone* zone)
Type* truncating_to_zero = Type::MinusZeroOrNaN();
DCHECK(!truncating_to_zero->Maybe(Type::Integral32()));
singleton_NaN_string_ = Type::HeapConstant(factory->NaN_string(), zone);
singleton_zero_string_ = Type::HeapConstant(factory->zero_string(), zone);
singleton_false_ = Type::HeapConstant(factory->false_value(), zone);
singleton_true_ = Type::HeapConstant(factory->true_value(), zone);
singleton_the_hole_ = Type::HeapConstant(factory->the_hole_value(), zone);
......@@ -503,6 +505,14 @@ Type* OperationTyper::NumberToInt32(Type* type) {
return Type::Signed32();
}
Type* OperationTyper::NumberToString(Type* type) {
DCHECK(type->Is(Type::Number()));
if (type->Is(Type::NaN())) return singleton_NaN_string_;
if (type->Is(cache_.kZeroOrMinusZero)) return singleton_zero_string_;
return Type::SeqString();
}
Type* OperationTyper::NumberToUint32(Type* type) {
DCHECK(type->Is(Type::Number()));
......
......@@ -94,6 +94,8 @@ class V8_EXPORT_PRIVATE OperationTyper {
Type* infinity_;
Type* minus_infinity_;
Type* singleton_NaN_string_;
Type* singleton_zero_string_;
Type* singleton_false_;
Type* singleton_true_;
Type* singleton_the_hole_;
......
......@@ -2270,6 +2270,11 @@ class RepresentationSelector {
if (lower()) DeferReplacement(node, node->InputAt(0));
return;
}
case IrOpcode::kNumberToString: {
VisitUnop(node, UseInfo::AnyTagged(),
MachineRepresentation::kTaggedPointer);
return;
}
case IrOpcode::kNumberToUint32: {
// Just change representation if necessary.
VisitUnop(node, UseInfo::TruncatingWord32(),
......
......@@ -650,6 +650,7 @@ bool operator==(CheckMinusZeroParameters const& lhs,
V(NumberTrunc, Operator::kNoProperties, 1, 0) \
V(NumberToBoolean, Operator::kNoProperties, 1, 0) \
V(NumberToInt32, Operator::kNoProperties, 1, 0) \
V(NumberToString, Operator::kNoProperties, 1, 0) \
V(NumberToUint32, Operator::kNoProperties, 1, 0) \
V(NumberToUint8Clamped, Operator::kNoProperties, 1, 0) \
V(NumberSilenceNaN, Operator::kNoProperties, 1, 0) \
......
......@@ -465,6 +465,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* NumberTrunc();
const Operator* NumberToBoolean();
const Operator* NumberToInt32();
const Operator* NumberToString();
const Operator* NumberToUint32();
const Operator* NumberToUint8Clamped();
......
......@@ -44,6 +44,8 @@ class TypeCache final {
Type* const kSingletonOne = CreateRange(1.0, 1.0);
Type* const kSingletonTen = CreateRange(10.0, 10.0);
Type* const kSingletonMinusOne = CreateRange(-1.0, -1.0);
Type* const kZeroOrMinusZero =
Type::Union(kSingletonZero, Type::MinusZero(), zone());
Type* const kZeroOrUndefined =
Type::Union(kSingletonZero, Type::Undefined(), zone());
Type* const kTenOrUndefined =
......
......@@ -997,6 +997,11 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::Signed32());
break;
case IrOpcode::kNumberToString:
// Number -> String
CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::String());
break;
case IrOpcode::kNumberToUint32:
case IrOpcode::kNumberToUint8Clamped:
// Number -> Unsigned32
......
......@@ -278,7 +278,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(ToString) \
V(ToLength) \
V(ToNumber) \
V(NumberToString) \
V(NumberToStringSkipCache) \
/* Type checks */ \
V(IsJSReceiver) \
V(IsSmi) \
......
......@@ -569,7 +569,7 @@ inherits(NumberMirror, ValueMirror);
NumberMirror.prototype.toText = function() {
return %NumberToString(this.value_);
return '' + this.value_;
};
......
......@@ -69,16 +69,6 @@ RUNTIME_FUNCTION(Runtime_StringParseFloat) {
return *isolate->factory()->NewNumber(value);
}
RUNTIME_FUNCTION(Runtime_NumberToString) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_NUMBER_ARG_HANDLE_CHECKED(number, 0);
return *isolate->factory()->NumberToString(number);
}
RUNTIME_FUNCTION(Runtime_NumberToStringSkipCache) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
......
......@@ -375,7 +375,6 @@ namespace internal {
F(StringToNumber, 1, 1) \
F(StringParseInt, 2, 1) \
F(StringParseFloat, 1, 1) \
F(NumberToString, 1, 1) \
F(NumberToStringSkipCache, 1, 1) \
F(NumberToSmi, 1, 1) \
F(SmiLexicographicCompare, 2, 1) \
......
......@@ -520,7 +520,7 @@ TEST(JSToString1) {
{ // ToString(number)
Node* r = R.ReduceUnop(op, Type::Number());
CHECK_EQ(IrOpcode::kJSToString, r->opcode());
CHECK_EQ(IrOpcode::kNumberToString, r->opcode());
}
{ // ToString(string)
......
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