[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:
parent
82a54b38e7
commit
8d780560bd
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user