Commit 8d780560 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Add support for relevant ES6 type conversion intrinsics.

TurboFan didn't fully support the relevant ES6 type conversion
intrinsics like %_ToNumber, %_ToLength, %_ToName, %_ToString and
%_ToInteger until now, we always went to the runtime instead.  These
intrinsics are now well supported in TurboFan, and we are even able to
generate quite decent code in some cases.

R=jarin@chromium.org

Review URL: https://codereview.chromium.org/1428243003

Cr-Commit-Position: refs/heads/master@{#31820}
parent 82a54b38
......@@ -6,13 +6,16 @@
#include <stack>
#include "src/code-factory.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"
#include "src/counters.h"
#include "src/objects-inl.h"
#include "src/type-cache.h"
namespace v8 {
namespace internal {
......@@ -20,7 +23,10 @@ namespace compiler {
JSIntrinsicLowering::JSIntrinsicLowering(Editor* editor, JSGraph* jsgraph,
DeoptimizationMode mode)
: AdvancedReducer(editor), jsgraph_(jsgraph), mode_(mode) {}
: AdvancedReducer(editor),
jsgraph_(jsgraph),
mode_(mode),
type_cache_(TypeCache::Get()) {}
Reduction JSIntrinsicLowering::Reduce(Node* node) {
......@@ -91,8 +97,20 @@ Reduction JSIntrinsicLowering::Reduce(Node* node) {
return ReduceGetTypeFeedbackVector(node);
case Runtime::kInlineGetCallerJSFunction:
return ReduceGetCallerJSFunction(node);
case Runtime::kInlineToInteger:
return ReduceToInteger(node);
case Runtime::kInlineToLength:
return ReduceToLength(node);
case Runtime::kInlineToName:
return ReduceToName(node);
case Runtime::kInlineToNumber:
return ReduceToNumber(node);
case Runtime::kInlineToObject:
return ReduceToObject(node);
case Runtime::kInlineToPrimitive:
return ReduceToPrimitive(node);
case Runtime::kInlineToString:
return ReduceToString(node);
case Runtime::kInlineThrowNotDateError:
return ReduceThrowNotDateError(node);
case Runtime::kInlineCallFunction:
......@@ -523,12 +541,95 @@ Reduction JSIntrinsicLowering::ReduceThrowNotDateError(Node* node) {
}
Reduction JSIntrinsicLowering::ReduceToInteger(Node* node) {
Node* value = NodeProperties::GetValueInput(node, 0);
Type* value_type = NodeProperties::GetType(value);
if (value_type->Is(type_cache().kIntegerOrMinusZero)) {
ReplaceWithValue(node, value);
return Replace(value);
}
return NoChange();
}
Reduction JSIntrinsicLowering::ReduceToName(Node* node) {
NodeProperties::ChangeOp(node, javascript()->ToName());
return Changed(node);
}
Reduction JSIntrinsicLowering::ReduceToNumber(Node* node) {
NodeProperties::ChangeOp(node, javascript()->ToNumber());
return Changed(node);
}
Reduction JSIntrinsicLowering::ReduceToLength(Node* node) {
Node* value = NodeProperties::GetValueInput(node, 0);
Type* value_type = NodeProperties::GetType(value);
if (value_type->Is(type_cache().kIntegerOrMinusZero)) {
if (value_type->Max() <= 0.0) {
value = jsgraph()->ZeroConstant();
} else if (value_type->Min() >= kMaxSafeInteger) {
value = jsgraph()->Constant(kMaxSafeInteger);
} else {
if (value_type->Min() <= 0.0) {
value = graph()->NewNode(
common()->Select(kMachAnyTagged),
graph()->NewNode(simplified()->NumberLessThanOrEqual(), value,
jsgraph()->ZeroConstant()),
jsgraph()->ZeroConstant(), value);
value_type = Type::Range(0.0, value_type->Max(), graph()->zone());
NodeProperties::SetType(value, value_type);
}
if (value_type->Max() > kMaxSafeInteger) {
value = graph()->NewNode(
common()->Select(kMachAnyTagged),
graph()->NewNode(simplified()->NumberLessThanOrEqual(),
jsgraph()->Constant(kMaxSafeInteger), value),
jsgraph()->Constant(kMaxSafeInteger), value);
value_type =
Type::Range(value_type->Min(), kMaxSafeInteger, graph()->zone());
NodeProperties::SetType(value, value_type);
}
}
ReplaceWithValue(node, value);
return Replace(value);
}
Callable callable = CodeFactory::ToLength(isolate());
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0,
CallDescriptor::kNeedsFrameState, node->op()->properties());
node->InsertInput(graph()->zone(), 0,
jsgraph()->HeapConstant(callable.code()));
NodeProperties::ChangeOp(node, common()->Call(desc));
return Changed(node);
}
Reduction JSIntrinsicLowering::ReduceToObject(Node* node) {
NodeProperties::ChangeOp(node, javascript()->ToObject());
return Changed(node);
}
Reduction JSIntrinsicLowering::ReduceToPrimitive(Node* node) {
Node* value = NodeProperties::GetValueInput(node, 0);
Type* value_type = NodeProperties::GetType(value);
if (value_type->Is(Type::Primitive())) {
ReplaceWithValue(node, value);
return Replace(value);
}
return NoChange();
}
Reduction JSIntrinsicLowering::ReduceToString(Node* node) {
NodeProperties::ChangeOp(node, javascript()->ToString());
return Changed(node);
}
Reduction JSIntrinsicLowering::ReduceCallFunction(Node* node) {
CallRuntimeParameters params = CallRuntimeParametersOf(node->op());
size_t arity = params.arity();
......@@ -591,6 +692,9 @@ Reduction JSIntrinsicLowering::ChangeToUndefined(Node* node, Node* effect) {
Graph* JSIntrinsicLowering::graph() const { return jsgraph()->graph(); }
Isolate* JSIntrinsicLowering::isolate() const { return jsgraph()->isolate(); }
CommonOperatorBuilder* JSIntrinsicLowering::common() const {
return jsgraph()->common();
}
......
......@@ -10,6 +10,11 @@
namespace v8 {
namespace internal {
// Forward declarations.
class TypeCache;
namespace compiler {
// Forward declarations.
......@@ -57,7 +62,13 @@ class JSIntrinsicLowering final : public AdvancedReducer {
Reduction ReduceGetTypeFeedbackVector(Node* node);
Reduction ReduceGetCallerJSFunction(Node* node);
Reduction ReduceThrowNotDateError(Node* node);
Reduction ReduceToInteger(Node* node);
Reduction ReduceToLength(Node* node);
Reduction ReduceToName(Node* node);
Reduction ReduceToNumber(Node* node);
Reduction ReduceToObject(Node* node);
Reduction ReduceToPrimitive(Node* node);
Reduction ReduceToString(Node* node);
Reduction ReduceCallFunction(Node* node);
Reduction Change(Node* node, const Operator* op);
......@@ -69,14 +80,17 @@ class JSIntrinsicLowering final : public AdvancedReducer {
Graph* graph() const;
JSGraph* jsgraph() const { return jsgraph_; }
Isolate* isolate() const;
CommonOperatorBuilder* common() const;
JSOperatorBuilder* javascript() const;
MachineOperatorBuilder* machine() const;
SimplifiedOperatorBuilder* simplified() const;
DeoptimizationMode mode() const { return mode_; }
TypeCache const& type_cache() const { return type_cache_; }
JSGraph* const jsgraph_;
DeoptimizationMode const mode_;
TypeCache const& type_cache_;
};
} // namespace compiler
......
......@@ -143,11 +143,6 @@ class JSBinopReduction final {
node_->ReplaceInput(1, ConvertToUI32(right(), right_signedness));
}
void ConvertInputsToString() {
node_->ReplaceInput(0, ConvertToString(left()));
node_->ReplaceInput(1, ConvertToString(right()));
}
void SwapInputs() {
Node* l = left();
Node* r = right();
......@@ -257,16 +252,6 @@ class JSBinopReduction final {
JSTypedLowering* lowering_; // The containing lowering instance.
Node* node_; // The original node.
Node* ConvertToString(Node* node) {
// Avoid introducing too many eager ToString() operations.
Reduction reduced = lowering_->ReduceJSToStringInput(node);
if (reduced.Changed()) return reduced.replacement();
Node* n = graph()->NewNode(javascript()->ToString(), node, context(),
effect(), control());
update_effect(n);
return n;
}
Node* CreateFrameStateForLeftInput(Node* frame_state) {
FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
......@@ -820,13 +805,18 @@ Reduction JSTypedLowering::ReduceJSToStringInput(Node* input) {
if (input_type->Is(Type::String())) {
return Changed(input); // JSToString(x:string) => x
}
if (input_type->Is(Type::Boolean())) {
return Replace(
graph()->NewNode(common()->Select(kMachAnyTagged), input,
jsgraph()->HeapConstant(factory()->true_string()),
jsgraph()->HeapConstant(factory()->false_string())));
}
if (input_type->Is(Type::Undefined())) {
return Replace(jsgraph()->HeapConstant(factory()->undefined_string()));
}
if (input_type->Is(Type::Null())) {
return Replace(jsgraph()->HeapConstant(factory()->null_string()));
}
// TODO(turbofan): js-typed-lowering of ToString(x:boolean)
// TODO(turbofan): js-typed-lowering of ToString(x:number)
return NoChange();
}
......
......@@ -59,9 +59,10 @@ int OperatorProperties::GetFrameStateInputCount(const Operator* op) {
case IrOpcode::kJSCreateWithContext:
// Conversions
case IrOpcode::kJSToObject:
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToName:
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToObject:
case IrOpcode::kJSToString:
// Misc operations
case IrOpcode::kJSConvertReceiver:
......
......@@ -231,7 +231,11 @@ class Typer::Visitor : public Reducer {
static Type* ToPrimitive(Type*, Typer*);
static Type* ToBoolean(Type*, Typer*);
static Type* ToInteger(Type*, Typer*);
static Type* ToLength(Type*, Typer*);
static Type* ToName(Type*, Typer*);
static Type* ToNumber(Type*, Typer*);
static Type* ToObject(Type*, Typer*);
static Type* ToString(Type*, Typer*);
static Type* NumberToInt32(Type*, Typer*);
static Type* NumberToUint32(Type*, Typer*);
......@@ -402,6 +406,39 @@ Type* Typer::Visitor::ToBoolean(Type* type, Typer* t) {
}
// static
Type* Typer::Visitor::ToInteger(Type* type, Typer* t) {
// ES6 section 7.1.4 ToInteger ( argument )
type = ToNumber(type, t);
if (type->Is(t->cache_.kIntegerOrMinusZero)) return type;
return t->cache_.kIntegerOrMinusZero;
}
// static
Type* Typer::Visitor::ToLength(Type* type, Typer* t) {
// ES6 section 7.1.15 ToLength ( argument )
type = ToInteger(type, t);
double min = type->Min();
double max = type->Max();
if (min <= 0.0) min = 0.0;
if (max > kMaxSafeInteger) max = kMaxSafeInteger;
if (max <= min) max = min;
return Type::Range(min, max, t->zone());
}
// static
Type* Typer::Visitor::ToName(Type* type, Typer* t) {
// ES6 section 7.1.14 ToPropertyKey ( argument )
type = ToPrimitive(type, t);
if (type->Is(Type::Name())) return type;
if (type->Maybe(Type::Symbol())) return Type::Name();
return ToString(type, t);
}
// static
Type* Typer::Visitor::ToNumber(Type* type, Typer* t) {
if (type->Is(Type::Number())) return type;
if (type->Is(Type::NullOrUndefined())) {
......@@ -424,7 +461,20 @@ Type* Typer::Visitor::ToNumber(Type* type, Typer* t) {
}
// static
Type* Typer::Visitor::ToObject(Type* type, Typer* t) {
// ES6 section 7.1.13 ToObject ( argument )
if (type->Is(Type::Receiver())) return type;
if (type->Is(Type::Primitive())) return Type::OtherObject();
if (!type->Maybe(Type::Undetectable())) return Type::DetectableReceiver();
return Type::Receiver();
}
// static
Type* Typer::Visitor::ToString(Type* type, Typer* t) {
// ES6 section 7.1.12 ToString ( argument )
type = ToPrimitive(type, t);
if (type->Is(Type::String())) return type;
return Type::String();
}
......@@ -995,7 +1045,7 @@ Type* Typer::Visitor::JSMultiplyRanger(Type::RangeType* lhs,
(rmin == -V8_INFINITY || rmax == +V8_INFINITY)) ||
(rhs->Maybe(t->cache_.kSingletonZero) &&
(lmin == -V8_INFINITY || lmax == +V8_INFINITY));
if (maybe_nan) return t->cache_.kWeakint; // Giving up.
if (maybe_nan) return t->cache_.kIntegerOrMinusZeroOrNaN; // Giving up.
bool maybe_minuszero = (lhs->Maybe(t->cache_.kSingletonZero) && rmin < 0) ||
(rhs->Maybe(t->cache_.kSingletonZero) && lmin < 0);
Type* range =
......@@ -1142,10 +1192,14 @@ Type* Typer::Visitor::TypeJSToString(Node* node) {
}
Type* Typer::Visitor::TypeJSToName(Node* node) { return Type::Name(); }
Type* Typer::Visitor::TypeJSToName(Node* node) {
return TypeUnaryOp(node, ToName);
}
Type* Typer::Visitor::TypeJSToObject(Node* node) { return Type::Receiver(); }
Type* Typer::Visitor::TypeJSToObject(Node* node) {
return TypeUnaryOp(node, ToObject);
}
// JS object operators.
......@@ -1394,7 +1448,7 @@ Type* Typer::Visitor::JSCallFunctionTyper(Type* fun, Typer* t) {
case kMathFloor:
case kMathRound:
case kMathCeil:
return t->cache_.kWeakint;
return t->cache_.kIntegerOrMinusZeroOrNaN;
// Unary math functions.
case kMathAbs:
case kMathLog:
......@@ -1468,8 +1522,22 @@ Type* Typer::Visitor::TypeJSCallRuntime(Node* node) {
return Type::Range(0, 32, zone());
case Runtime::kInlineStringGetLength:
return Type::Range(0, String::kMaxLength, zone());
case Runtime::kInlineToInteger:
return TypeUnaryOp(node, ToInteger);
case Runtime::kInlineToLength:
return TypeUnaryOp(node, ToLength);
case Runtime::kInlineToName:
return TypeUnaryOp(node, ToName);
case Runtime::kInlineToNumber:
return TypeUnaryOp(node, ToNumber);
case Runtime::kInlineToObject:
return Type::Receiver();
return TypeUnaryOp(node, ToObject);
case Runtime::kInlineToPrimitive:
case Runtime::kInlineToPrimitive_Number:
case Runtime::kInlineToPrimitive_String:
return TypeUnaryOp(node, ToPrimitive);
case Runtime::kInlineToString:
return TypeUnaryOp(node, ToString);
default:
break;
}
......
......@@ -240,6 +240,7 @@ namespace internal {
V(enumerable_string, "enumerable") \
V(Error_string, "Error") \
V(eval_string, "eval") \
V(false_string, "false") \
V(float32x4_string, "float32x4") \
V(Float32x4_string, "Float32x4") \
V(for_api_string, "for_api") \
......@@ -298,6 +299,7 @@ namespace internal {
V(throw_string, "throw") \
V(toJSON_string, "toJSON") \
V(toString_string, "toString") \
V(true_string, "true") \
V(uint16x8_string, "uint16x8") \
V(Uint16x8_string, "Uint16x8") \
V(uint32x4_string, "uint32x4") \
......
......@@ -46,7 +46,13 @@ class TypeCache final {
Type* const kZeroish =
Type::Union(kSingletonZero, Type::MinusZeroOrNaN(), zone());
Type* const kInteger = CreateRange(-V8_INFINITY, V8_INFINITY);
Type* const kWeakint = Type::Union(kInteger, Type::MinusZeroOrNaN(), zone());
Type* const kPositiveInteger = CreateRange(0.0, V8_INFINITY);
Type* const kIntegerOrMinusZero =
Type::Union(kInteger, Type::MinusZero(), zone());
Type* const kIntegerOrMinusZeroOrNaN =
Type::Union(kIntegerOrMinusZero, Type::NaN(), zone());
Type* const kPositiveSafeInteger = CreateRange(0.0, kMaxSafeInteger);
Type* const kIntegral32 = Type::Union(kInt32, kUint32, zone());
......
......@@ -544,8 +544,7 @@ TEST(JSToString1) {
{ // ToString(boolean)
Node* r = R.ReduceUnop(op, Type::Boolean());
// TODO(titzer): could be a branch
CHECK_EQ(IrOpcode::kJSToString, r->opcode());
CHECK_EQ(IrOpcode::kSelect, r->opcode());
}
{ // ToString(number)
......@@ -573,8 +572,9 @@ TEST(JSToString_replacement) {
for (size_t i = 0; i < arraysize(types); i++) {
Node* n = R.Parameter(types[i]);
Node* c = R.graph.NewNode(R.javascript.ToString(), n, R.context(),
R.start(), R.start());
Node* c =
R.graph.NewNode(R.javascript.ToString(), n, R.context(),
R.EmptyFrameState(R.context()), R.start(), R.start());
Node* effect_use = R.UseForEffect(c);
Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
......
......@@ -74,7 +74,7 @@ const SharedOperator kSharedOperators[] = {
SHARED(UnaryNot, Operator::kEliminatable, 1, 0, 1, 0, 1, 1, 0),
SHARED(ToBoolean, Operator::kEliminatable, 1, 0, 1, 0, 1, 1, 0),
SHARED(ToNumber, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToString, Operator::kNoProperties, 1, 0, 1, 1, 1, 1, 2),
SHARED(ToString, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToName, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToObject, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(Yield, Operator::kNoProperties, 1, 0, 1, 1, 1, 1, 2),
......
......@@ -463,6 +463,26 @@ TEST_F(JSTypedLoweringTest, JSToObjectWithReceiver) {
}
// -----------------------------------------------------------------------------
// JSToString
TEST_F(JSTypedLoweringTest, JSToStringWithBoolean) {
Node* const input = Parameter(Type::Boolean(), 0);
Node* const context = Parameter(Type::Any(), 1);
Node* const frame_state = EmptyFrameState();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction r = Reduce(graph()->NewNode(javascript()->ToString(), input,
context, frame_state, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsSelect(kMachAnyTagged, input, IsHeapConstant(factory()->true_string()),
IsHeapConstant(factory()->false_string())));
}
// -----------------------------------------------------------------------------
// JSStrictEqual
......
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