Commit 2fffa81d authored by Victor Gomes's avatar Victor Gomes Committed by Commit Bot

[compiler] Check if context has extension slot statically

We know statically if a context has an extension slot or not, but that
was dynamically checked.

The CL lifts the ScopeInfo chain to the compiler and does the check
statically, it only generates the undefined check if the context
has an extension slot.

Bug: v8:9744
Change-Id: I169d05bb11b36501e97af00d30ae44bedcd6be83
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1876051
Commit-Queue: Victor Gomes <victorgomes@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Auto-Submit: Victor Gomes <victorgomes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64599}
parent 44788bfa
......@@ -233,6 +233,13 @@ class BytecodeGraphBuilder {
// Check the context chain for extensions, for lookup fast paths.
Environment* CheckContextExtensions(uint32_t depth);
// Slow path taken when we cannot figure out the current scope info.
Environment* CheckContextExtensionsSlowPath(uint32_t depth);
// Helper function that tries to get the current scope info.
base::Optional<ScopeInfoRef> TryGetScopeInfo();
// Helper function to create a context extension check.
Environment* CheckContextExtensionAtDepth(Environment* slow_environment,
uint32_t depth);
// Helper function to create binary operation hint from the recorded
// type feedback.
......@@ -1565,12 +1572,95 @@ void BytecodeGraphBuilder::VisitLdaLookupSlotInsideTypeof() {
BuildLdaLookupSlot(TypeofMode::INSIDE_TYPEOF);
}
BytecodeGraphBuilder::Environment*
BytecodeGraphBuilder::CheckContextExtensionAtDepth(
Environment* slow_environment, uint32_t depth) {
Node* extension_slot = NewNode(
javascript()->LoadContext(depth, Context::EXTENSION_INDEX, false));
Node* check_no_extension =
NewNode(simplified()->ReferenceEqual(), extension_slot,
jsgraph()->UndefinedConstant());
NewBranch(check_no_extension);
{
SubEnvironment sub_environment(this);
NewIfFalse();
// If there is an extension, merge into the slow path.
if (slow_environment == nullptr) {
slow_environment = environment();
NewMerge();
} else {
slow_environment->Merge(environment(),
bytecode_analysis().GetInLivenessFor(
bytecode_iterator().current_offset()));
}
}
NewIfTrue();
// Do nothing on if there is no extension, eventually falling through to
// the fast path.
DCHECK_NOT_NULL(slow_environment);
return slow_environment;
}
base::Optional<ScopeInfoRef> BytecodeGraphBuilder::TryGetScopeInfo() {
Node* context = environment()->Context();
switch (context->opcode()) {
case IrOpcode::kJSCreateFunctionContext:
return ScopeInfoRef(
broker(),
CreateFunctionContextParametersOf(context->op()).scope_info());
case IrOpcode::kJSCreateBlockContext:
case IrOpcode::kJSCreateCatchContext:
case IrOpcode::kJSCreateWithContext:
return ScopeInfoRef(broker(), ScopeInfoOf(context->op()));
case IrOpcode::kParameter: {
ScopeInfoRef scope_info = shared_info_.scope_info();
if (scope_info.HasOuterScopeInfo()) {
scope_info = scope_info.OuterScopeInfo();
}
return scope_info;
}
case IrOpcode::kJSGeneratorRestoreContext:
case IrOpcode::kOsrValue:
case IrOpcode::kPhi:
return base::nullopt;
default:
UNREACHABLE();
}
}
BytecodeGraphBuilder::Environment* BytecodeGraphBuilder::CheckContextExtensions(
uint32_t depth) {
base::Optional<ScopeInfoRef> maybe_scope_info = TryGetScopeInfo();
if (!maybe_scope_info.has_value()) {
return CheckContextExtensionsSlowPath(depth);
}
ScopeInfoRef scope_info = maybe_scope_info.value();
// We only need to check up to the last-but-one depth, because an eval
// in the same scope as the variable itself has no way of shadowing it.
Environment* slow_environment = nullptr;
for (uint32_t d = 0; d < depth; d++) {
if (scope_info.HasContextExtension()) {
slow_environment = CheckContextExtensionAtDepth(slow_environment, d);
}
DCHECK_IMPLIES(!scope_info.HasOuterScopeInfo(), d + 1 == depth);
if (scope_info.HasOuterScopeInfo()) {
scope_info = scope_info.OuterScopeInfo();
}
}
// The depth can be zero, in which case no slow-path checks are built, and
// the slow path environment can be null.
DCHECK_IMPLIES(slow_environment == nullptr, depth == 0);
return slow_environment;
}
BytecodeGraphBuilder::Environment*
BytecodeGraphBuilder::CheckContextExtensionsSlowPath(uint32_t depth) {
// Output environment where the context has an extension
Environment* slow_environment = nullptr;
// We only need to check up to the last-but-one depth, because the an eval
// We only need to check up to the last-but-one depth, because an eval
// in the same scope as the variable itself has no way of shadowing it.
for (uint32_t d = 0; d < depth; d++) {
Node* has_extension = NewNode(javascript()->HasContextExtension(d));
......@@ -1580,30 +1670,7 @@ BytecodeGraphBuilder::Environment* BytecodeGraphBuilder::CheckContextExtensions(
{
SubEnvironment sub_environment(this);
NewIfTrue();
Node* extension_slot = NewNode(
javascript()->LoadContext(d, Context::EXTENSION_INDEX, false));
Node* check_no_extension =
NewNode(simplified()->ReferenceEqual(), extension_slot,
jsgraph()->UndefinedConstant());
NewBranch(check_no_extension);
{
SubEnvironment sub_environment(this);
NewIfFalse();
// If there is an extension, merge into the slow path.
if (slow_environment == nullptr) {
slow_environment = environment();
NewMerge();
} else {
slow_environment->Merge(environment(),
bytecode_analysis().GetInLivenessFor(
bytecode_iterator().current_offset()));
}
}
NewIfTrue();
slow_environment = CheckContextExtensionAtDepth(slow_environment, d);
undefined_extension_env = environment();
}
NewIfFalse();
......@@ -1617,8 +1684,7 @@ BytecodeGraphBuilder::Environment* BytecodeGraphBuilder::CheckContextExtensions(
// The depth can be zero, in which case no slow-path checks are built, and
// the slow path environment can be null.
DCHECK(depth == 0 || slow_environment != nullptr);
DCHECK_IMPLIES(slow_environment == nullptr, depth == 0);
return slow_environment;
}
......
......@@ -759,6 +759,13 @@ class ScopeInfoRef : public HeapObjectRef {
Handle<ScopeInfo> object() const;
int ContextLength() const;
bool HasOuterScopeInfo() const;
int Flags() const;
bool HasContextExtension() const;
// Only serialized via SerializeScopeInfoChain.
ScopeInfoRef OuterScopeInfo() const;
void SerializeScopeInfoChain();
};
#define BROKER_SFI_FIELDS(V) \
......@@ -808,6 +815,9 @@ class V8_EXPORT_PRIVATE SharedFunctionInfoRef : public HeapObjectRef {
void SerializeFunctionTemplateInfo();
base::Optional<FunctionTemplateInfoRef> function_template_info() const;
void SerializeScopeInfoChain();
ScopeInfoRef scope_info() const;
};
class StringRef : public NameRef {
......
......@@ -1613,15 +1613,38 @@ class ScopeInfoData : public HeapObjectData {
Handle<ScopeInfo> object);
int context_length() const { return context_length_; }
bool has_outer_scope_info() const { return has_outer_scope_info_; }
int flags() const { return flags_; }
ScopeInfoData* outer_scope_info() const { return outer_scope_info_; }
void SerializeScopeInfoChain(JSHeapBroker* broker);
private:
int const context_length_;
bool const has_outer_scope_info_;
int const flags_;
// Only serialized via SerializeScopeInfoChain.
ScopeInfoData* outer_scope_info_;
};
ScopeInfoData::ScopeInfoData(JSHeapBroker* broker, ObjectData** storage,
Handle<ScopeInfo> object)
: HeapObjectData(broker, storage, object),
context_length_(object->ContextLength()) {}
context_length_(object->ContextLength()),
has_outer_scope_info_(object->HasOuterScopeInfo()),
flags_(object->Flags()),
outer_scope_info_(nullptr) {}
void ScopeInfoData::SerializeScopeInfoChain(JSHeapBroker* broker) {
if (outer_scope_info_) return;
if (!has_outer_scope_info_) return;
outer_scope_info_ =
broker
->GetOrCreateData(Handle<ScopeInfo>::cast(object())->OuterScopeInfo())
->AsScopeInfo();
outer_scope_info_->SerializeScopeInfoChain(broker);
}
class SharedFunctionInfoData : public HeapObjectData {
public:
......@@ -1635,6 +1658,8 @@ class SharedFunctionInfoData : public HeapObjectData {
FeedbackVectorRef feedback);
bool IsSerializedForCompilation(FeedbackVectorRef feedback) const;
void SerializeFunctionTemplateInfo(JSHeapBroker* broker);
ScopeInfoData* scope_info() const { return scope_info_; }
void SerializeScopeInfoChain(JSHeapBroker* broker);
FunctionTemplateInfoData* function_template_info() const {
return function_template_info_;
}
......@@ -1667,6 +1692,7 @@ class SharedFunctionInfoData : public HeapObjectData {
#undef DECL_MEMBER
FunctionTemplateInfoData* function_template_info_;
ZoneMap<int, JSArrayData*> template_objects_;
ScopeInfoData* scope_info_;
};
SharedFunctionInfoData::SharedFunctionInfoData(
......@@ -1687,7 +1713,8 @@ SharedFunctionInfoData::SharedFunctionInfoData(
#undef INIT_MEMBER
,
function_template_info_(nullptr),
template_objects_(broker->zone()) {
template_objects_(broker->zone()),
scope_info_(nullptr) {
DCHECK_EQ(HasBuiltinId_, builtin_id_ != Builtins::kNoBuiltinId);
DCHECK_EQ(HasBytecodeArray_, GetBytecodeArray_ != nullptr);
}
......@@ -1711,6 +1738,16 @@ void SharedFunctionInfoData::SerializeFunctionTemplateInfo(
->AsFunctionTemplateInfo();
}
void SharedFunctionInfoData::SerializeScopeInfoChain(JSHeapBroker* broker) {
if (scope_info_) return;
scope_info_ =
broker
->GetOrCreateData(
Handle<SharedFunctionInfo>::cast(object())->scope_info())
->AsScopeInfo();
scope_info_->SerializeScopeInfoChain(broker);
}
bool SharedFunctionInfoData::IsSerializedForCompilation(
FeedbackVectorRef feedback) const {
return serialized_for_compilation_.find(feedback.object()) !=
......@@ -3494,6 +3531,35 @@ int ScopeInfoRef::ContextLength() const {
return data()->AsScopeInfo()->context_length();
}
int ScopeInfoRef::Flags() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(ScopeInfo, Flags);
return data()->AsScopeInfo()->flags();
}
bool ScopeInfoRef::HasContextExtension() const {
return ScopeInfo::HasContextExtensionSlotField::decode(Flags());
}
bool ScopeInfoRef::HasOuterScopeInfo() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(ScopeInfo, HasOuterScopeInfo);
return data()->AsScopeInfo()->has_outer_scope_info();
}
ScopeInfoRef ScopeInfoRef::OuterScopeInfo() const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
return ScopeInfoRef(
broker(), handle(object()->OuterScopeInfo(), broker()->isolate()));
}
return ScopeInfoRef(broker(), data()->AsScopeInfo()->outer_scope_info());
}
void ScopeInfoRef::SerializeScopeInfoChain() {
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsScopeInfo()->SerializeScopeInfoChain(broker());
}
bool StringRef::IsExternalString() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(String, IsExternalString);
return data()->AsString()->is_external_string();
......@@ -4010,6 +4076,11 @@ void SharedFunctionInfoRef::SerializeFunctionTemplateInfo() {
data()->AsSharedFunctionInfo()->SerializeFunctionTemplateInfo(broker());
}
void SharedFunctionInfoRef::SerializeScopeInfoChain() {
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsSharedFunctionInfo()->SerializeScopeInfoChain(broker());
}
base::Optional<FunctionTemplateInfoRef>
SharedFunctionInfoRef::function_template_info() const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
......@@ -4037,6 +4108,16 @@ int SharedFunctionInfoRef::context_header_size() const {
return data()->AsSharedFunctionInfo()->context_header_size();
}
ScopeInfoRef SharedFunctionInfoRef::scope_info() const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
return ScopeInfoRef(broker(),
handle(object()->scope_info(), broker()->isolate()));
}
return ScopeInfoRef(broker(), data()->AsSharedFunctionInfo()->scope_info());
}
void JSObjectRef::SerializeObjectCreateMap() {
if (broker()->mode() == JSHeapBroker::kDisabled) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
......
......@@ -1721,6 +1721,7 @@ void SerializerForBackgroundCompilation::ProcessCreateContext(
Handle<ScopeInfo>::cast(iterator->GetConstantForIndexOperand(
scopeinfo_operand_index, broker()->isolate()));
ScopeInfoRef scope_info_ref(broker(), scope_info);
scope_info_ref.SerializeScopeInfoChain();
Hints const& current_context_hints = environment()->current_context_hints();
Hints& accumulator_hints = environment()->accumulator_hints();
......@@ -2701,6 +2702,8 @@ void SerializerForBackgroundCompilation::ProcessCheckContextExtensions(
ProcessContextAccess(context_hints, Context::EXTENSION_INDEX, i,
kSerializeSlot);
}
SharedFunctionInfoRef shared(broker(), environment()->function().shared());
shared.SerializeScopeInfoChain();
}
void SerializerForBackgroundCompilation::ProcessLdaLookupGlobalSlot(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment