[turbofan] Make ConvertReceiver a simplified operator.

This enables proper wiring into ithe control flow chain.

Bug: v8:7002,chromium:777574
Change-Id: Idba59944ff6ab3c10c204bb74ace61d812e6297c
Reviewed-on: https://chromium-review.googlesource.com/738183
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Jaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48990}
This commit is contained in:
Jaroslav Sevcik 2017-10-26 09:12:38 +02:00 committed by Commit Bot
parent b2fcb649d5
commit 70b1f15e5a
26 changed files with 225 additions and 214 deletions

View File

@ -710,6 +710,16 @@ FieldAccess AccessBuilder::ForJSGlobalObjectNativeContext() {
return access;
}
// static
FieldAccess AccessBuilder::ForJSGlobalProxyNativeContext() {
FieldAccess access = {
kTaggedBase, JSGlobalProxy::kNativeContextOffset,
Handle<Name>(), MaybeHandle<Map>(),
Type::Internal(), MachineType::TaggedPointer(),
kPointerWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForJSArrayIteratorObject() {
FieldAccess access = {kTaggedBase,

View File

@ -235,6 +235,9 @@ class V8_EXPORT_PRIVATE AccessBuilder final
// Provides access to JSGlobalObject::native_context() field.
static FieldAccess ForJSGlobalObjectNativeContext();
// Provides access to JSGlobalProxy::native_context() field.
static FieldAccess ForJSGlobalProxyNativeContext();
// Provides access to JSArrayIterator::object() field.
static FieldAccess ForJSArrayIteratorObject();

View File

@ -877,6 +877,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kRuntimeAbort:
LowerRuntimeAbort(node);
break;
case IrOpcode::kConvertReceiver:
result = LowerConvertReceiver(node);
break;
case IrOpcode::kFloat64RoundUp:
if (!LowerFloat64RoundUp(node).To(&result)) {
return false;
@ -3632,6 +3635,93 @@ void EffectControlLinearizer::LowerRuntimeAbort(Node* node) {
__ Int32Constant(1), __ NoContextConstant());
}
Node* EffectControlLinearizer::LowerConvertReceiver(Node* node) {
ConvertReceiverMode const mode = ConvertReceiverModeOf(node->op());
Node* value = node->InputAt(0);
Node* global_proxy = node->InputAt(1);
switch (mode) {
case ConvertReceiverMode::kNullOrUndefined: {
return global_proxy;
}
case ConvertReceiverMode::kNotNullOrUndefined: {
auto convert_to_object = __ MakeDeferredLabel();
auto done_convert = __ MakeLabel(MachineRepresentation::kTagged);
// Check if {value} is already a JSReceiver.
__ GotoIf(ObjectIsSmi(value), &convert_to_object);
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
Node* value_instance_type =
__ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
Node* check = __ Uint32LessThan(
value_instance_type, __ Uint32Constant(FIRST_JS_RECEIVER_TYPE));
__ GotoIf(check, &convert_to_object);
__ Goto(&done_convert, value);
// Wrap the primitive {value} into a JSValue.
__ Bind(&convert_to_object);
Operator::Properties properties = Operator::kEliminatable;
Callable callable = Builtins::CallableFor(isolate(), Builtins::kToObject);
CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags,
properties);
Node* native_context = __ LoadField(
AccessBuilder::ForJSGlobalProxyNativeContext(), global_proxy);
Node* result = __ Call(desc, __ HeapConstant(callable.code()), value,
native_context);
__ Goto(&done_convert, result);
__ Bind(&done_convert);
return done_convert.PhiAt(0);
}
case ConvertReceiverMode::kAny: {
auto convert_to_object = __ MakeDeferredLabel();
auto convert_global_proxy = __ MakeDeferredLabel();
auto done_convert = __ MakeLabel(MachineRepresentation::kTagged);
// Check if {value} is already a JSReceiver, or null/undefined.
__ GotoIf(ObjectIsSmi(value), &convert_to_object);
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
Node* value_instance_type =
__ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
Node* check = __ Uint32LessThan(
value_instance_type, __ Uint32Constant(FIRST_JS_RECEIVER_TYPE));
__ GotoIf(check, &convert_to_object);
__ Goto(&done_convert, value);
// Wrap the primitive {value} into a JSValue.
__ Bind(&convert_to_object);
__ GotoIf(__ WordEqual(value, __ UndefinedConstant()),
&convert_global_proxy);
__ GotoIf(__ WordEqual(value, __ NullConstant()), &convert_global_proxy);
Operator::Properties properties = Operator::kEliminatable;
Callable callable = Builtins::CallableFor(isolate(), Builtins::kToObject);
CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags,
properties);
Node* native_context = __ LoadField(
AccessBuilder::ForJSGlobalProxyNativeContext(), global_proxy);
Node* result = __ Call(desc, __ HeapConstant(callable.code()), value,
native_context);
__ Goto(&done_convert, result);
// Replace the {value} with the {global_proxy}.
__ Bind(&convert_global_proxy);
__ Goto(&done_convert, global_proxy);
__ Bind(&done_convert);
return done_convert.PhiAt(0);
}
}
UNREACHABLE();
return nullptr;
}
Maybe<Node*> EffectControlLinearizer::LowerFloat64RoundUp(Node* node) {
// Nothing to be done if a fast hardware instruction is available.
if (machine()->Float64RoundUp().IsSupported()) {

View File

@ -139,6 +139,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
void LowerTransitionAndStoreNumberElement(Node* node);
void LowerTransitionAndStoreNonNumberElement(Node* node);
void LowerRuntimeAbort(Node* node);
Node* LowerConvertReceiver(Node* node);
// Lowering of optional operators.
Maybe<Node*> LowerFloat64RoundUp(Node* node);

View File

@ -74,6 +74,7 @@ namespace compiler {
#define JSGRAPH_SINGLETON_CONSTANT_LIST(V) \
V(TrueConstant) \
V(FalseConstant) \
V(NullConstant) \
V(HeapNumberMapConstant) \
V(NoContextConstant) \
V(EmptyStringConstant) \

View File

@ -26,6 +26,7 @@ namespace {
bool CanBePrimitive(Node* node) {
switch (node->opcode()) {
case IrOpcode::kConvertReceiver:
case IrOpcode::kJSCreate:
case IrOpcode::kJSCreateArguments:
case IrOpcode::kJSCreateArray:
@ -42,7 +43,6 @@ bool CanBePrimitive(Node* node) {
case IrOpcode::kJSConstruct:
case IrOpcode::kJSConstructWithArrayLike:
case IrOpcode::kJSConstructWithSpread:
case IrOpcode::kJSConvertReceiver:
case IrOpcode::kJSGetSuperConstructor:
case IrOpcode::kJSToObject:
return false;

View File

@ -675,10 +675,6 @@ void JSGenericLowering::LowerJSCallRuntime(Node* node) {
ReplaceWithRuntimeCall(node, p.id(), static_cast<int>(p.arity()));
}
void JSGenericLowering::LowerJSConvertReceiver(Node* node) {
ReplaceWithRuntimeCall(node, Runtime::kConvertReceiver);
}
void JSGenericLowering::LowerJSForInNext(Node* node) {
UNREACHABLE(); // Eliminated in typed lowering.
}

View File

@ -272,6 +272,7 @@ namespace {
bool NeedsConvertReceiver(Node* receiver, Node* effect) {
// Check if the {receiver} is already a JSReceiver.
switch (receiver->opcode()) {
case IrOpcode::kConvertReceiver:
case IrOpcode::kJSConstruct:
case IrOpcode::kJSConstructWithSpread:
case IrOpcode::kJSCreate:
@ -284,7 +285,6 @@ bool NeedsConvertReceiver(Node* receiver, Node* effect) {
case IrOpcode::kJSCreateLiteralArray:
case IrOpcode::kJSCreateLiteralObject:
case IrOpcode::kJSCreateLiteralRegExp:
case IrOpcode::kJSConvertReceiver:
case IrOpcode::kJSGetSuperConstructor:
case IrOpcode::kJSToObject: {
return false;
@ -709,11 +709,13 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
is_sloppy(shared_info->language_mode()) && !shared_info->native()) {
Node* effect = NodeProperties::GetEffectInput(node);
if (NeedsConvertReceiver(call.receiver(), effect)) {
const CallParameters& p = CallParametersOf(node->op());
Node* convert = effect =
graph()->NewNode(javascript()->ConvertReceiver(p.convert_mode()),
call.receiver(), context, effect, start);
NodeProperties::ReplaceValueInput(node, convert, 1);
CallParameters const& p = CallParametersOf(node->op());
Node* global_proxy = jsgraph()->HeapConstant(
handle(info_->native_context()->global_proxy()));
Node* receiver = effect =
graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()),
call.receiver(), global_proxy, effect, start);
NodeProperties::ReplaceValueInput(node, receiver, 1);
NodeProperties::ReplaceEffectInput(node, effect);
}
}

View File

@ -52,12 +52,6 @@ size_t hash_value(VectorSlotPair const& p) {
}
ConvertReceiverMode ConvertReceiverModeOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kJSConvertReceiver, op->opcode());
return OpParameter<ConvertReceiverMode>(op);
}
std::ostream& operator<<(std::ostream& os,
ConstructForwardVarargsParameters const& p) {
return os << p.arity() << ", " << p.start_index();
@ -856,15 +850,6 @@ const Operator* JSOperatorBuilder::ConstructWithSpread(
parameters); // parameter
}
const Operator* JSOperatorBuilder::ConvertReceiver(
ConvertReceiverMode convert_mode) {
return new (zone()) Operator1<ConvertReceiverMode>( // --
IrOpcode::kJSConvertReceiver, Operator::kEliminatable, // opcode
"JSConvertReceiver", // name
1, 1, 1, 1, 1, 0, // counts
convert_mode); // parameter
}
const Operator* JSOperatorBuilder::LoadNamed(Handle<Name> name,
const VectorSlotPair& feedback) {
NamedAccess access(LanguageMode::kSloppy, name, feedback);

View File

@ -85,10 +85,6 @@ bool operator!=(VectorSlotPair const&, VectorSlotPair const&);
size_t hash_value(VectorSlotPair const&);
// The ConvertReceiverMode is used as parameter by JSConvertReceiver operators.
ConvertReceiverMode ConvertReceiverModeOf(Operator const* op);
// Defines the flags for a JavaScript call forwarding parameters. This
// is used as parameter by JSConstructForwardVarargs operators.
class ConstructForwardVarargsParameters final {
@ -708,8 +704,6 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
uint32_t arity, CallFrequency frequency = CallFrequency(),
VectorSlotPair const& feedback = VectorSlotPair());
const Operator* ConvertReceiver(ConvertReceiverMode convert_mode);
const Operator* LoadProperty(VectorSlotPair const& feedback);
const Operator* LoadNamed(Handle<Name> name, VectorSlotPair const& feedback);

View File

@ -1362,165 +1362,6 @@ Reduction JSTypedLowering::ReduceJSStoreModule(Node* node) {
return Changed(value);
}
Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) {
DCHECK_EQ(IrOpcode::kJSConvertReceiver, node->opcode());
ConvertReceiverMode mode = ConvertReceiverModeOf(node->op());
Node* receiver = NodeProperties::GetValueInput(node, 0);
Type* receiver_type = NodeProperties::GetType(receiver);
Node* context = NodeProperties::GetContextInput(node);
Type* context_type = NodeProperties::GetType(context);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Check if {receiver} is known to be a receiver.
if (receiver_type->Is(Type::Receiver())) {
ReplaceWithValue(node, receiver, effect, control);
return Replace(receiver);
}
// If the {receiver} is known to be null or undefined, we can just replace it
// with the global proxy unconditionally.
if (receiver_type->Is(Type::NullOrUndefined()) ||
mode == ConvertReceiverMode::kNullOrUndefined) {
if (context_type->IsHeapConstant()) {
Handle<JSObject> global_proxy(
Handle<Context>::cast(context_type->AsHeapConstant()->Value())
->global_proxy(),
isolate());
receiver = jsgraph()->Constant(global_proxy);
} else {
Node* native_context = effect = graph()->NewNode(
javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
context, effect);
receiver = effect = graph()->NewNode(
javascript()->LoadContext(0, Context::GLOBAL_PROXY_INDEX, true),
native_context, effect);
}
ReplaceWithValue(node, receiver, effect, control);
return Replace(receiver);
}
// If {receiver} cannot be null or undefined we can skip a few checks.
if (!receiver_type->Maybe(Type::NullOrUndefined()) ||
mode == ConvertReceiverMode::kNotNullOrUndefined) {
Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), receiver);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* etrue = effect;
Node* rtrue = receiver;
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* efalse = effect;
Node* rfalse;
{
// Convert {receiver} using the ToObjectStub. The call does not require a
// frame-state in this case, because neither null nor undefined is passed.
Callable callable = Builtins::CallableFor(isolate(), Builtins::kToObject);
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0,
CallDescriptor::kNoFlags, node->op()->properties());
rfalse = efalse = graph()->NewNode(
common()->Call(desc), jsgraph()->HeapConstant(callable.code()),
receiver, context, efalse);
}
control = graph()->NewNode(common()->Merge(2), if_true, if_false);
effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
// Morph the {node} into an appropriate Phi.
ReplaceWithValue(node, node, effect, control);
node->ReplaceInput(0, rtrue);
node->ReplaceInput(1, rfalse);
node->ReplaceInput(2, control);
node->TrimInputCount(3);
NodeProperties::ChangeOp(node,
common()->Phi(MachineRepresentation::kTagged, 2));
return Changed(node);
}
// Check if {receiver} is already a JSReceiver.
Node* check0 = graph()->NewNode(simplified()->ObjectIsReceiver(), receiver);
Node* branch0 =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
// Check {receiver} for undefined.
Node* check1 = graph()->NewNode(simplified()->ReferenceEqual(), receiver,
jsgraph()->UndefinedConstant());
Node* branch1 =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check1, if_false0);
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
// Check {receiver} for null.
Node* check2 = graph()->NewNode(simplified()->ReferenceEqual(), receiver,
jsgraph()->NullConstant());
Node* branch2 =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check2, if_false1);
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
// We just use {receiver} directly.
Node* if_noop = if_true0;
Node* enoop = effect;
Node* rnoop = receiver;
// Convert {receiver} using ToObject.
Node* if_convert = if_false2;
Node* econvert = effect;
Node* rconvert;
{
// Convert {receiver} using the ToObjectStub. The call does not require a
// frame-state in this case, because neither null nor undefined is passed.
Callable callable = Builtins::CallableFor(isolate(), Builtins::kToObject);
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0,
CallDescriptor::kNoFlags, node->op()->properties());
rconvert = econvert = graph()->NewNode(
common()->Call(desc), jsgraph()->HeapConstant(callable.code()),
receiver, context, econvert);
}
// Replace {receiver} with global proxy of {context}.
Node* if_global = graph()->NewNode(common()->Merge(2), if_true1, if_true2);
Node* eglobal = effect;
Node* rglobal;
{
if (context_type->IsHeapConstant()) {
Handle<JSObject> global_proxy(
Handle<Context>::cast(context_type->AsHeapConstant()->Value())
->global_proxy(),
isolate());
rglobal = jsgraph()->Constant(global_proxy);
} else {
Node* native_context = eglobal = graph()->NewNode(
javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
context, eglobal);
rglobal = eglobal = graph()->NewNode(
javascript()->LoadContext(0, Context::GLOBAL_PROXY_INDEX, true),
native_context, eglobal);
}
}
control =
graph()->NewNode(common()->Merge(3), if_noop, if_convert, if_global);
effect = graph()->NewNode(common()->EffectPhi(3), enoop, econvert, eglobal,
control);
// Morph the {node} into an appropriate Phi.
ReplaceWithValue(node, node, effect, control);
node->ReplaceInput(0, rnoop);
node->ReplaceInput(1, rconvert);
node->ReplaceInput(2, rglobal);
node->ReplaceInput(3, control);
node->TrimInputCount(4);
NodeProperties::ChangeOp(node,
common()->Phi(MachineRepresentation::kTagged, 3));
return Changed(node);
}
namespace {
void ReduceBuiltin(Isolate* isolate, JSGraph* jsgraph, Node* node,
@ -1781,9 +1622,11 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
// Check if we need to convert the {receiver}.
if (is_sloppy(shared->language_mode()) && !shared->native() &&
!receiver_type->Is(Type::Receiver())) {
Node* global_proxy =
jsgraph()->HeapConstant(handle(function->global_proxy()));
receiver = effect =
graph()->NewNode(javascript()->ConvertReceiver(convert_mode),
receiver, context, effect, control);
graph()->NewNode(simplified()->ConvertReceiver(convert_mode),
receiver, global_proxy, effect, control);
NodeProperties::ReplaceValueInput(node, receiver, 1);
}
@ -2241,8 +2084,6 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSLoadModule(node);
case IrOpcode::kJSStoreModule:
return ReduceJSStoreModule(node);
case IrOpcode::kJSConvertReceiver:
return ReduceJSConvertReceiver(node);
case IrOpcode::kJSConstructForwardVarargs:
return ReduceJSConstructForwardVarargs(node);
case IrOpcode::kJSConstruct:

View File

@ -58,7 +58,6 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
Reduction ReduceJSToStringInput(Node* input);
Reduction ReduceJSToString(Node* node);
Reduction ReduceJSToObject(Node* node);
Reduction ReduceJSConvertReceiver(Node* node);
Reduction ReduceJSConstructForwardVarargs(Node* node);
Reduction ReduceJSConstruct(Node* node);
Reduction ReduceJSCallForwardVarargs(Node* node);

View File

@ -144,7 +144,6 @@ bool Linkage::NeedsFrameStateInput(Runtime::FunctionId function) {
// deoptimize are whitelisted here and can be called without a FrameState.
case Runtime::kAbort:
case Runtime::kAllocateInTargetSpace:
case Runtime::kConvertReceiver:
case Runtime::kCreateIterResultObject:
case Runtime::kGeneratorGetContinuation:
case Runtime::kIncBlockCounter:

View File

@ -168,7 +168,6 @@
V(JSCallWithArrayLike) \
V(JSCallWithSpread) \
V(JSCallRuntime) \
V(JSConvertReceiver) \
V(JSForInEnumerate) \
V(JSForInNext) \
V(JSForInPrepare) \
@ -339,6 +338,7 @@
V(CheckEqualsInternalizedString) \
V(CheckEqualsSymbol) \
V(CompareMaps) \
V(ConvertReceiver) \
V(ConvertTaggedHoleToUndefined) \
V(TypeOf) \
V(ClassOf) \

View File

@ -241,6 +241,18 @@ Type* OperationTyper::MultiplyRanger(Type* lhs, Type* rhs) {
: range;
}
Type* OperationTyper::ConvertReceiver(Type* type) {
if (type->Is(Type::Receiver())) return type;
bool const maybe_primitive = type->Maybe(Type::Primitive());
type = Type::Intersect(type, Type::Receiver(), zone());
if (maybe_primitive) {
// ConvertReceiver maps null and undefined to the JSGlobalProxy of the
// target function, and all other primitives are wrapped into a JSValue.
type = Type::Union(type, Type::OtherObject(), zone());
}
return type;
}
Type* OperationTyper::ToNumber(Type* type) {
if (type->Is(Type::Number())) return type;
if (type->Is(Type::NullOrUndefined())) {

View File

@ -40,6 +40,7 @@ class V8_EXPORT_PRIVATE OperationTyper {
#define DECLARE_METHOD(Name) Type* Name(Type* type);
SIMPLIFIED_NUMBER_UNOP_LIST(DECLARE_METHOD)
SIMPLIFIED_SPECULATIVE_NUMBER_UNOP_LIST(DECLARE_METHOD)
DECLARE_METHOD(ConvertReceiver)
#undef DECLARE_METHOD
// Number binary operators.

View File

@ -93,6 +93,7 @@ namespace {
bool NeedsCheckHeapObject(Node* receiver) {
switch (receiver->opcode()) {
case IrOpcode::kConvertReceiver:
case IrOpcode::kHeapConstant:
case IrOpcode::kJSCreate:
case IrOpcode::kJSCreateArguments:
@ -105,7 +106,6 @@ bool NeedsCheckHeapObject(Node* receiver) {
case IrOpcode::kJSCreateEmptyLiteralObject:
case IrOpcode::kJSCreateLiteralRegExp:
case IrOpcode::kJSCreateGeneratorObject:
case IrOpcode::kJSConvertReceiver:
case IrOpcode::kJSConstructForwardVarargs:
case IrOpcode::kJSConstruct:
case IrOpcode::kJSConstructWithArrayLike:

View File

@ -457,6 +457,10 @@ class RepresentationSelector {
SIMPLIFIED_SPECULATIVE_NUMBER_UNOP_LIST(DECLARE_CASE)
#undef DECLARE_CASE
case IrOpcode::kConvertReceiver:
new_type = op_typer_.ConvertReceiver(FeedbackTypeOf(node->InputAt(0)));
break;
case IrOpcode::kPlainPrimitiveToNumber:
new_type = op_typer_.ToNumber(FeedbackTypeOf(node->InputAt(0)));
break;
@ -2640,6 +2644,24 @@ class RepresentationSelector {
SetOutput(node, MachineRepresentation::kNone);
return;
}
case IrOpcode::kConvertReceiver: {
Type* input_type = TypeOf(node->InputAt(0));
VisitBinop(node, UseInfo::AnyTagged(),
MachineRepresentation::kTaggedPointer);
if (lower()) {
// Try to optimize the {node} based on the input type.
if (input_type->Is(Type::Receiver())) {
DeferReplacement(node, node->InputAt(0));
} else if (input_type->Is(Type::NullOrUndefined())) {
DeferReplacement(node, node->InputAt(1));
} else if (!input_type->Maybe(Type::NullOrUndefined())) {
NodeProperties::ChangeOp(
node, lowering->simplified()->ConvertReceiver(
ConvertReceiverMode::kNotNullOrUndefined));
}
}
return;
}
case IrOpcode::kPlainPrimitiveToNumber: {
if (InputIs(node, Type::Boolean())) {
VisitUnop(node, UseInfo::Bool(), MachineRepresentation::kWord32);

View File

@ -128,6 +128,11 @@ ExternalArrayType ExternalArrayTypeOf(const Operator* op) {
return OpParameter<ExternalArrayType>(op);
}
ConvertReceiverMode ConvertReceiverModeOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kConvertReceiver, op->opcode());
return OpParameter<ConvertReceiverMode>(op);
}
size_t hash_value(CheckFloat64HoleMode mode) {
return static_cast<size_t>(mode);
}
@ -821,6 +826,23 @@ struct SimplifiedOperatorGlobalCache final {
CheckedTruncateTaggedToWord32Operator<CheckTaggedInputMode::kNumberOrOddball>
kCheckedTruncateTaggedToWord32NumberOrOddballOperator;
template <ConvertReceiverMode kMode>
struct ConvertReceiverOperator final : public Operator1<ConvertReceiverMode> {
ConvertReceiverOperator()
: Operator1<ConvertReceiverMode>( // --
IrOpcode::kConvertReceiver, // opcode
Operator::kEliminatable, // flags
"ConvertReceiver", // name
2, 1, 1, 1, 1, 0, // counts
kMode) {} // param
};
ConvertReceiverOperator<ConvertReceiverMode::kAny>
kConvertReceiverAnyOperator;
ConvertReceiverOperator<ConvertReceiverMode::kNullOrUndefined>
kConvertReceiverNullOrUndefinedOperator;
ConvertReceiverOperator<ConvertReceiverMode::kNotNullOrUndefined>
kConvertReceiverNotNullOrUndefinedOperator;
template <CheckFloat64HoleMode kMode>
struct CheckFloat64HoleNaNOperator final
: public Operator1<CheckFloat64HoleMode> {
@ -1027,6 +1049,20 @@ const Operator* SimplifiedOperatorBuilder::CompareMaps(
MapsParameterInfo(maps)); // parameter
}
const Operator* SimplifiedOperatorBuilder::ConvertReceiver(
ConvertReceiverMode mode) {
switch (mode) {
case ConvertReceiverMode::kAny:
return &cache_.kConvertReceiverAnyOperator;
case ConvertReceiverMode::kNullOrUndefined:
return &cache_.kConvertReceiverNullOrUndefinedOperator;
case ConvertReceiverMode::kNotNullOrUndefined:
return &cache_.kConvertReceiverNotNullOrUndefinedOperator;
}
UNREACHABLE();
return nullptr;
}
const Operator* SimplifiedOperatorBuilder::CheckFloat64Hole(
CheckFloat64HoleMode mode) {
switch (mode) {

View File

@ -88,6 +88,9 @@ V8_EXPORT_PRIVATE ElementAccess const& ElementAccessOf(const Operator* op)
ExternalArrayType ExternalArrayTypeOf(const Operator* op) WARN_UNUSED_RESULT;
// The ConvertReceiverMode is used as parameter by ConvertReceiver operators.
ConvertReceiverMode ConvertReceiverModeOf(Operator const* op);
enum class CheckFloat64HoleMode : uint8_t {
kNeverReturnHole, // Never return the hole (deoptimize instead).
kAllowReturnHole // Allow to return the hole (signaling NaN).
@ -467,6 +470,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* CheckedTaggedToTaggedPointer();
const Operator* CheckedTruncateTaggedToWord32(CheckTaggedInputMode);
const Operator* ConvertReceiver(ConvertReceiverMode);
const Operator* CheckFloat64Hole(CheckFloat64HoleMode);
const Operator* CheckNotTaggedHole();
const Operator* ConvertTaggedHoleToUndefined();

View File

@ -73,6 +73,8 @@ Reduction TypedOptimization::Reduce(Node* node) {
}
}
switch (node->opcode()) {
case IrOpcode::kConvertReceiver:
return ReduceConvertReceiver(node);
case IrOpcode::kCheckHeapObject:
return ReduceCheckHeapObject(node);
case IrOpcode::kCheckNotTaggedHole:
@ -129,6 +131,20 @@ MaybeHandle<Map> GetStableMapFromObjectType(Type* object_type) {
} // namespace
Reduction TypedOptimization::ReduceConvertReceiver(Node* node) {
Node* const value = NodeProperties::GetValueInput(node, 0);
Type* const value_type = NodeProperties::GetType(value);
Node* const global_proxy = NodeProperties::GetValueInput(node, 1);
if (value_type->Is(Type::Receiver())) {
ReplaceWithValue(node, value);
return Replace(value);
} else if (value_type->Is(Type::NullOrUndefined())) {
ReplaceWithValue(node, global_proxy);
return Replace(global_proxy);
}
return NoChange();
}
Reduction TypedOptimization::ReduceCheckHeapObject(Node* node) {
Node* const input = NodeProperties::GetValueInput(node, 0);
Type* const input_type = NodeProperties::GetType(input);

View File

@ -36,6 +36,7 @@ class V8_EXPORT_PRIVATE TypedOptimization final
Reduction Reduce(Node* node) final;
private:
Reduction ReduceConvertReceiver(Node* node);
Reduction ReduceCheckHeapObject(Node* node);
Reduction ReduceCheckMaps(Node* node);
Reduction ReduceCheckNumber(Node* node);

View File

@ -1699,10 +1699,6 @@ Type* Typer::Visitor::TypeJSCallRuntime(Node* node) {
}
Type* Typer::Visitor::TypeJSConvertReceiver(Node* node) {
return Type::Receiver();
}
Type* Typer::Visitor::TypeJSForInEnumerate(Node* node) {
return Type::OtherInternal();
}
@ -1939,6 +1935,11 @@ Type* Typer::Visitor::TypeCheckNotTaggedHole(Node* node) {
return type;
}
Type* Typer::Visitor::TypeConvertReceiver(Node* node) {
Type* arg = Operand(node, 0);
return typer_->operation_typer_.ConvertReceiver(arg);
}
Type* Typer::Visitor::TypeConvertTaggedHoleToUndefined(Node* node) {
Type* type = Operand(node, 0);
return typer_->operation_typer()->ConvertTaggedHoleToUndefined(type);

View File

@ -735,7 +735,6 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kJSConstruct:
case IrOpcode::kJSConstructWithArrayLike:
case IrOpcode::kJSConstructWithSpread:
case IrOpcode::kJSConvertReceiver:
// Type is Receiver.
CheckTypeIs(node, Type::Receiver());
break;
@ -1262,6 +1261,13 @@ void Verifier::Visitor::Check(Node* node) {
CheckTypeIs(node, Type::Symbol());
break;
case IrOpcode::kConvertReceiver:
// (Any, Any) -> Receiver
CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Any());
CheckTypeIs(node, Type::Receiver());
break;
case IrOpcode::kCheckedInt32Add:
case IrOpcode::kCheckedInt32Sub:
case IrOpcode::kCheckedInt32Div:

View File

@ -220,15 +220,6 @@ RUNTIME_FUNCTION(Runtime_Call) {
}
// ES6 section 9.2.1.2, OrdinaryCallBindThis for sloppy callee.
RUNTIME_FUNCTION(Runtime_ConvertReceiver) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0);
return *Object::ConvertReceiver(isolate, receiver).ToHandleChecked();
}
RUNTIME_FUNCTION(Runtime_IsFunction) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());

View File

@ -240,7 +240,6 @@ namespace internal {
F(SetNativeFlag, 1, 1) \
F(IsConstructor, 1, 1) \
F(Call, -1 /* >= 2 */, 1) \
F(ConvertReceiver, 1, 1) \
F(IsFunction, 1, 1) \
F(FunctionToString, 1, 1)