[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}
This commit is contained in:
bmeurer 2015-11-05 01:50:55 -08:00 committed by Commit bot
parent 82a54b38e7
commit 8d780560bd
10 changed files with 235 additions and 30 deletions

View File

@ -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();
}

View File

@ -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

View File

@ -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();
}

View File

@ -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:

View File

@ -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;
}

View File

@ -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") \

View File

@ -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());

View File

@ -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);

View File

@ -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),

View File

@ -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