[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:
parent
b2fcb649d5
commit
70b1f15e5a
@ -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,
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
@ -74,6 +74,7 @@ namespace compiler {
|
||||
#define JSGRAPH_SINGLETON_CONSTANT_LIST(V) \
|
||||
V(TrueConstant) \
|
||||
V(FalseConstant) \
|
||||
V(NullConstant) \
|
||||
V(HeapNumberMapConstant) \
|
||||
V(NoContextConstant) \
|
||||
V(EmptyStringConstant) \
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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) \
|
||||
|
@ -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())) {
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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());
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user