Commit cfcb3597 authored by ishell's avatar ishell Committed by Commit bot

[ic] Let LoadGlobalIC load the variable name from TypeFeedbackMetadata.

This simplifies the calling convention of LoadGlobalIC.

Currently we do a linear search to get the name but I'll address this in a follow-up CL.

BUG=chromium:576312
TBR=rossberg@chromium.org

Review-Url: https://codereview.chromium.org/2084913006
Cr-Commit-Position: refs/heads/master@{#37253}
parent a7a9ac37
...@@ -134,7 +134,7 @@ void VariableProxy::AssignFeedbackVectorSlots(Isolate* isolate, ...@@ -134,7 +134,7 @@ void VariableProxy::AssignFeedbackVectorSlots(Isolate* isolate,
static_cast<int>(reinterpret_cast<intptr_t>(entry->value))); static_cast<int>(reinterpret_cast<intptr_t>(entry->value)));
return; return;
} }
variable_feedback_slot_ = spec->AddLoadGlobalICSlot(); variable_feedback_slot_ = spec->AddLoadGlobalICSlot(var()->name());
cache->Put(var(), variable_feedback_slot_); cache->Put(var(), variable_feedback_slot_);
} else { } else {
variable_feedback_slot_ = spec->AddLoadICSlot(); variable_feedback_slot_ = spec->AddLoadICSlot();
......
...@@ -5487,12 +5487,11 @@ void Generate_LoadIC_Miss(CodeStubAssembler* assembler) { ...@@ -5487,12 +5487,11 @@ void Generate_LoadIC_Miss(CodeStubAssembler* assembler) {
void Generate_LoadGlobalIC_Miss(CodeStubAssembler* assembler) { void Generate_LoadGlobalIC_Miss(CodeStubAssembler* assembler) {
typedef compiler::Node Node; typedef compiler::Node Node;
Node* name = assembler->Parameter(0); Node* slot = assembler->Parameter(0);
Node* slot = assembler->Parameter(1); Node* vector = assembler->Parameter(1);
Node* vector = assembler->Parameter(2); Node* context = assembler->Parameter(2);
Node* context = assembler->Parameter(3);
assembler->TailCallRuntime(Runtime::kLoadGlobalIC_Miss, context, name, slot, assembler->TailCallRuntime(Runtime::kLoadGlobalIC_Miss, context, slot,
vector); vector);
} }
...@@ -5519,23 +5518,23 @@ void Generate_LoadIC_Slow(CodeStubAssembler* assembler) { ...@@ -5519,23 +5518,23 @@ void Generate_LoadIC_Slow(CodeStubAssembler* assembler) {
void Generate_LoadGlobalIC_SlowInsideTypeof(CodeStubAssembler* assembler) { void Generate_LoadGlobalIC_SlowInsideTypeof(CodeStubAssembler* assembler) {
typedef compiler::Node Node; typedef compiler::Node Node;
Node* name = assembler->Parameter(0); Node* slot = assembler->Parameter(0);
// Node* slot = assembler->Parameter(1); Node* vector = assembler->Parameter(1);
// Node* vector = assembler->Parameter(2); Node* context = assembler->Parameter(2);
Node* context = assembler->Parameter(3);
assembler->TailCallRuntime(Runtime::kGetGlobalInsideTypeof, context, name); assembler->TailCallRuntime(Runtime::kGetGlobalInsideTypeof, context, slot,
vector);
} }
void Generate_LoadGlobalIC_SlowNotInsideTypeof(CodeStubAssembler* assembler) { void Generate_LoadGlobalIC_SlowNotInsideTypeof(CodeStubAssembler* assembler) {
typedef compiler::Node Node; typedef compiler::Node Node;
Node* name = assembler->Parameter(0); Node* slot = assembler->Parameter(0);
// Node* slot = assembler->Parameter(1); Node* vector = assembler->Parameter(1);
// Node* vector = assembler->Parameter(2); Node* context = assembler->Parameter(2);
Node* context = assembler->Parameter(3);
assembler->TailCallRuntime(Runtime::kGetGlobalNotInsideTypeof, context, name); assembler->TailCallRuntime(Runtime::kGetGlobalNotInsideTypeof, context, slot,
vector);
} }
void Generate_KeyedLoadIC_Slow(MacroAssembler* masm) { void Generate_KeyedLoadIC_Slow(MacroAssembler* masm) {
......
...@@ -2350,12 +2350,13 @@ void CodeStubAssembler::LoadGlobalIC(const LoadICParameters* p) { ...@@ -2350,12 +2350,13 @@ void CodeStubAssembler::LoadGlobalIC(const LoadICParameters* p) {
Node* native_context = LoadNativeContext(p->context); Node* native_context = LoadNativeContext(p->context);
Node* receiver = LoadFixedArrayElement( Node* receiver = LoadFixedArrayElement(
native_context, Int32Constant(Context::EXTENSION_INDEX)); native_context, Int32Constant(Context::EXTENSION_INDEX));
TailCallStub(descriptor, handler, p->context, receiver, p->name, p->slot, Node* fake_name = IntPtrConstant(0);
TailCallStub(descriptor, handler, p->context, receiver, fake_name, p->slot,
p->vector); p->vector);
} }
Bind(&miss); Bind(&miss);
{ {
TailCallRuntime(Runtime::kLoadGlobalIC_Miss, p->context, p->name, p->slot, TailCallRuntime(Runtime::kLoadGlobalIC_Miss, p->context, p->slot,
p->vector); p->vector);
} }
} }
......
...@@ -448,24 +448,24 @@ void LoadGlobalICTrampolineStub::GenerateAssembly( ...@@ -448,24 +448,24 @@ void LoadGlobalICTrampolineStub::GenerateAssembly(
CodeStubAssembler* assembler) const { CodeStubAssembler* assembler) const {
typedef compiler::Node Node; typedef compiler::Node Node;
Node* name = assembler->Parameter(0); Node* slot = assembler->Parameter(0);
Node* slot = assembler->Parameter(1); Node* context = assembler->Parameter(1);
Node* context = assembler->Parameter(2);
Node* vector = assembler->LoadTypeFeedbackVectorForStub(); Node* vector = assembler->LoadTypeFeedbackVectorForStub();
CodeStubAssembler::LoadICParameters p(context, nullptr, name, slot, vector); CodeStubAssembler::LoadICParameters p(context, nullptr, nullptr, slot,
vector);
assembler->LoadGlobalIC(&p); assembler->LoadGlobalIC(&p);
} }
void LoadGlobalICStub::GenerateAssembly(CodeStubAssembler* assembler) const { void LoadGlobalICStub::GenerateAssembly(CodeStubAssembler* assembler) const {
typedef compiler::Node Node; typedef compiler::Node Node;
Node* name = assembler->Parameter(0); Node* slot = assembler->Parameter(0);
Node* slot = assembler->Parameter(1); Node* vector = assembler->Parameter(1);
Node* vector = assembler->Parameter(2); Node* context = assembler->Parameter(2);
Node* context = assembler->Parameter(3);
CodeStubAssembler::LoadICParameters p(context, nullptr, name, slot, vector); CodeStubAssembler::LoadICParameters p(context, nullptr, nullptr, slot,
vector);
assembler->LoadGlobalIC(&p); assembler->LoadGlobalIC(&p);
} }
......
...@@ -2262,7 +2262,6 @@ RUNTIME_FUNCTION(Runtime_LoadIC_Miss) { ...@@ -2262,7 +2262,6 @@ RUNTIME_FUNCTION(Runtime_LoadIC_Miss) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8"), "V8.IcMiss"); TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8"), "V8.IcMiss");
HandleScope scope(isolate); HandleScope scope(isolate);
Handle<Object> receiver = args.at<Object>(0); Handle<Object> receiver = args.at<Object>(0);
Handle<Name> key = args.at<Name>(1);
DCHECK_EQ(4, args.length()); DCHECK_EQ(4, args.length());
Handle<Smi> slot = args.at<Smi>(2); Handle<Smi> slot = args.at<Smi>(2);
...@@ -2273,12 +2272,15 @@ RUNTIME_FUNCTION(Runtime_LoadIC_Miss) { ...@@ -2273,12 +2272,15 @@ RUNTIME_FUNCTION(Runtime_LoadIC_Miss) {
// set up outside the IC, handle that here. // set up outside the IC, handle that here.
FeedbackVectorSlotKind kind = vector->GetKind(vector_slot); FeedbackVectorSlotKind kind = vector->GetKind(vector_slot);
if (kind == FeedbackVectorSlotKind::LOAD_IC) { if (kind == FeedbackVectorSlotKind::LOAD_IC) {
Handle<Name> key = args.at<Name>(1);
LoadICNexus nexus(vector, vector_slot); LoadICNexus nexus(vector, vector_slot);
LoadIC ic(IC::NO_EXTRA_FRAME, isolate, &nexus); LoadIC ic(IC::NO_EXTRA_FRAME, isolate, &nexus);
ic.UpdateState(receiver, key); ic.UpdateState(receiver, key);
RETURN_RESULT_OR_FAILURE(isolate, ic.Load(receiver, key)); RETURN_RESULT_OR_FAILURE(isolate, ic.Load(receiver, key));
} else if (kind == FeedbackVectorSlotKind::LOAD_GLOBAL_IC) { } else if (kind == FeedbackVectorSlotKind::LOAD_GLOBAL_IC) {
Handle<Name> key(vector->GetName(vector_slot), isolate);
DCHECK_NE(*key, *isolate->factory()->empty_string());
DCHECK_EQ(*isolate->global_object(), *receiver); DCHECK_EQ(*isolate->global_object(), *receiver);
LoadGlobalICNexus nexus(vector, vector_slot); LoadGlobalICNexus nexus(vector, vector_slot);
LoadGlobalIC ic(IC::NO_EXTRA_FRAME, isolate, &nexus); LoadGlobalIC ic(IC::NO_EXTRA_FRAME, isolate, &nexus);
...@@ -2286,6 +2288,7 @@ RUNTIME_FUNCTION(Runtime_LoadIC_Miss) { ...@@ -2286,6 +2288,7 @@ RUNTIME_FUNCTION(Runtime_LoadIC_Miss) {
RETURN_RESULT_OR_FAILURE(isolate, ic.Load(key)); RETURN_RESULT_OR_FAILURE(isolate, ic.Load(key));
} else { } else {
Handle<Name> key = args.at<Name>(1);
DCHECK_EQ(FeedbackVectorSlotKind::KEYED_LOAD_IC, kind); DCHECK_EQ(FeedbackVectorSlotKind::KEYED_LOAD_IC, kind);
KeyedLoadICNexus nexus(vector, vector_slot); KeyedLoadICNexus nexus(vector, vector_slot);
KeyedLoadIC ic(IC::NO_EXTRA_FRAME, isolate, &nexus); KeyedLoadIC ic(IC::NO_EXTRA_FRAME, isolate, &nexus);
...@@ -2298,14 +2301,15 @@ RUNTIME_FUNCTION(Runtime_LoadIC_Miss) { ...@@ -2298,14 +2301,15 @@ RUNTIME_FUNCTION(Runtime_LoadIC_Miss) {
RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Miss) { RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Miss) {
TimerEventScope<TimerEventIcMiss> timer(isolate); TimerEventScope<TimerEventIcMiss> timer(isolate);
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(3, args.length()); DCHECK_EQ(2, args.length());
Handle<JSGlobalObject> global = isolate->global_object(); Handle<JSGlobalObject> global = isolate->global_object();
Handle<Name> name = args.at<Name>(0); Handle<Smi> slot = args.at<Smi>(0);
Handle<Smi> slot = args.at<Smi>(1); Handle<TypeFeedbackVector> vector = args.at<TypeFeedbackVector>(1);
Handle<TypeFeedbackVector> vector = args.at<TypeFeedbackVector>(2);
FeedbackVectorSlot vector_slot = vector->ToSlot(slot->value()); FeedbackVectorSlot vector_slot = vector->ToSlot(slot->value());
DCHECK_EQ(FeedbackVectorSlotKind::LOAD_GLOBAL_IC, DCHECK_EQ(FeedbackVectorSlotKind::LOAD_GLOBAL_IC,
vector->GetKind(vector_slot)); vector->GetKind(vector_slot));
Handle<String> name(vector->GetName(vector_slot), isolate);
DCHECK_NE(*name, *isolate->factory()->empty_string());
LoadGlobalICNexus nexus(vector, vector_slot); LoadGlobalICNexus nexus(vector, vector_slot);
LoadGlobalIC ic(IC::NO_EXTRA_FRAME, isolate, &nexus); LoadGlobalIC ic(IC::NO_EXTRA_FRAME, isolate, &nexus);
......
...@@ -101,16 +101,14 @@ FunctionType* LoadGlobalDescriptor::BuildCallInterfaceDescriptorFunctionType( ...@@ -101,16 +101,14 @@ FunctionType* LoadGlobalDescriptor::BuildCallInterfaceDescriptorFunctionType(
Isolate* isolate, int paramater_count) { Isolate* isolate, int paramater_count) {
Zone* zone = isolate->interface_descriptor_zone(); Zone* zone = isolate->interface_descriptor_zone();
FunctionType* function = FunctionType* function =
Type::Function(AnyTagged(zone), Type::Undefined(), 2, zone)->AsFunction(); Type::Function(AnyTagged(zone), Type::Undefined(), 1, zone)->AsFunction();
function->InitParameter(0, AnyTagged(zone)); function->InitParameter(0, SmiType(zone));
function->InitParameter(1, SmiType(zone));
return function; return function;
} }
void LoadGlobalDescriptor::InitializePlatformSpecific( void LoadGlobalDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
Register registers[] = {LoadWithVectorDescriptor::NameRegister(), Register registers[] = {LoadWithVectorDescriptor::SlotRegister()};
LoadWithVectorDescriptor::SlotRegister()};
data->InitializePlatformSpecific(arraysize(registers), registers); data->InitializePlatformSpecific(arraysize(registers), registers);
} }
...@@ -119,17 +117,15 @@ LoadGlobalWithVectorDescriptor::BuildCallInterfaceDescriptorFunctionType( ...@@ -119,17 +117,15 @@ LoadGlobalWithVectorDescriptor::BuildCallInterfaceDescriptorFunctionType(
Isolate* isolate, int paramater_count) { Isolate* isolate, int paramater_count) {
Zone* zone = isolate->interface_descriptor_zone(); Zone* zone = isolate->interface_descriptor_zone();
FunctionType* function = FunctionType* function =
Type::Function(AnyTagged(zone), Type::Undefined(), 3, zone)->AsFunction(); Type::Function(AnyTagged(zone), Type::Undefined(), 2, zone)->AsFunction();
function->InitParameter(0, AnyTagged(zone)); function->InitParameter(0, SmiType(zone));
function->InitParameter(1, SmiType(zone)); function->InitParameter(1, AnyTagged(zone));
function->InitParameter(2, AnyTagged(zone));
return function; return function;
} }
void LoadGlobalWithVectorDescriptor::InitializePlatformSpecific( void LoadGlobalWithVectorDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
Register registers[] = {LoadWithVectorDescriptor::NameRegister(), Register registers[] = {LoadWithVectorDescriptor::SlotRegister(),
LoadWithVectorDescriptor::SlotRegister(),
LoadWithVectorDescriptor::VectorRegister()}; LoadWithVectorDescriptor::VectorRegister()};
data->InitializePlatformSpecific(arraysize(registers), registers); data->InitializePlatformSpecific(arraysize(registers), registers);
} }
......
...@@ -374,7 +374,7 @@ class LoadGlobalDescriptor : public CallInterfaceDescriptor { ...@@ -374,7 +374,7 @@ class LoadGlobalDescriptor : public CallInterfaceDescriptor {
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(LoadGlobalDescriptor, DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(LoadGlobalDescriptor,
CallInterfaceDescriptor) CallInterfaceDescriptor)
enum ParameterIndices { kNameIndex, kSlotIndex }; enum ParameterIndices { kSlotIndex };
}; };
class LoadGlobalWithVectorDescriptor : public CallInterfaceDescriptor { class LoadGlobalWithVectorDescriptor : public CallInterfaceDescriptor {
...@@ -382,7 +382,7 @@ class LoadGlobalWithVectorDescriptor : public CallInterfaceDescriptor { ...@@ -382,7 +382,7 @@ class LoadGlobalWithVectorDescriptor : public CallInterfaceDescriptor {
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(LoadGlobalWithVectorDescriptor, DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(LoadGlobalWithVectorDescriptor,
CallInterfaceDescriptor) CallInterfaceDescriptor)
enum ParameterIndices { kNameIndex, kSlotIndex, kVectorIndex }; enum ParameterIndices { kSlotIndex, kVectorIndex };
}; };
class StoreDescriptor : public CallInterfaceDescriptor { class StoreDescriptor : public CallInterfaceDescriptor {
......
...@@ -578,6 +578,40 @@ void TransitionArray::TransitionArrayPrint(std::ostream& os) { // NOLINT ...@@ -578,6 +578,40 @@ void TransitionArray::TransitionArrayPrint(std::ostream& os) { // NOLINT
os << "\n"; os << "\n";
} }
template void FeedbackVectorSpecBase<StaticFeedbackVectorSpec>::Print();
template void FeedbackVectorSpecBase<FeedbackVectorSpec>::Print();
template <typename Derived>
void FeedbackVectorSpecBase<Derived>::Print() {
OFStream os(stdout);
FeedbackVectorSpecPrint(os);
os << std::flush;
}
template <typename Derived>
void FeedbackVectorSpecBase<Derived>::FeedbackVectorSpecPrint(
std::ostream& os) { // NOLINT
int slot_count = This()->slots();
os << " - slot_count: " << slot_count;
if (slot_count == 0) {
os << " (empty)\n";
return;
}
for (int slot = 0, name_index = 0; slot < slot_count;) {
FeedbackVectorSlotKind kind = This()->GetKind(slot);
int entry_size = TypeFeedbackMetadata::GetSlotSize(kind);
DCHECK_LT(0, entry_size);
os << "\n Slot #" << slot << " " << kind;
if (TypeFeedbackMetadata::SlotRequiresName(kind)) {
os << ", " << Brief(*This()->GetName(name_index++));
}
slot += entry_size;
}
os << "\n";
}
void TypeFeedbackMetadata::Print() { void TypeFeedbackMetadata::Print() {
OFStream os(stdout); OFStream os(stdout);
...@@ -594,12 +628,16 @@ void TypeFeedbackMetadata::TypeFeedbackMetadataPrint( ...@@ -594,12 +628,16 @@ void TypeFeedbackMetadata::TypeFeedbackMetadataPrint(
os << " (empty)\n"; os << " (empty)\n";
return; return;
} }
os << "\n - slot_count: " << slot_count();
TypeFeedbackMetadataIterator iter(this); TypeFeedbackMetadataIterator iter(this);
while (iter.HasNext()) { while (iter.HasNext()) {
FeedbackVectorSlot slot = iter.Next(); FeedbackVectorSlot slot = iter.Next();
FeedbackVectorSlotKind kind = iter.kind(); FeedbackVectorSlotKind kind = iter.kind();
os << "\n Slot " << slot << " " << kind; os << "\n Slot " << slot << " " << kind;
if (TypeFeedbackMetadata::SlotRequiresName(kind)) {
os << ", " << Brief(iter.name());
}
} }
os << "\n"; os << "\n";
} }
...@@ -625,7 +663,11 @@ void TypeFeedbackVector::TypeFeedbackVectorPrint(std::ostream& os) { // NOLINT ...@@ -625,7 +663,11 @@ void TypeFeedbackVector::TypeFeedbackVectorPrint(std::ostream& os) { // NOLINT
FeedbackVectorSlot slot = iter.Next(); FeedbackVectorSlot slot = iter.Next();
FeedbackVectorSlotKind kind = iter.kind(); FeedbackVectorSlotKind kind = iter.kind();
os << "\n Slot " << slot << " " << kind << " "; os << "\n Slot " << slot << " " << kind;
if (TypeFeedbackMetadata::SlotRequiresName(kind)) {
os << ", " << Brief(iter.name());
}
os << " ";
switch (kind) { switch (kind) {
case FeedbackVectorSlotKind::LOAD_IC: { case FeedbackVectorSlotKind::LOAD_IC: {
LoadICNexus nexus(this, slot); LoadICNexus nexus(this, slot);
......
...@@ -350,8 +350,14 @@ RUNTIME_FUNCTION(Runtime_GetProperty) { ...@@ -350,8 +350,14 @@ RUNTIME_FUNCTION(Runtime_GetProperty) {
namespace { namespace {
Object* GetGlobal(Isolate* isolate, Handle<String> name, Object* GetGlobal(Isolate* isolate, int slot, Handle<TypeFeedbackVector> vector,
bool should_throw_reference_error) { bool should_throw_reference_error) {
FeedbackVectorSlot vector_slot = vector->ToSlot(slot);
DCHECK_EQ(FeedbackVectorSlotKind::LOAD_GLOBAL_IC,
vector->GetKind(vector_slot));
Handle<String> name(vector->GetName(vector_slot), isolate);
DCHECK_NE(*name, *isolate->factory()->empty_string());
Handle<JSGlobalObject> global = isolate->global_object(); Handle<JSGlobalObject> global = isolate->global_object();
Handle<ScriptContextTable> script_contexts( Handle<ScriptContextTable> script_contexts(
...@@ -382,16 +388,18 @@ Object* GetGlobal(Isolate* isolate, Handle<String> name, ...@@ -382,16 +388,18 @@ Object* GetGlobal(Isolate* isolate, Handle<String> name,
RUNTIME_FUNCTION(Runtime_GetGlobalInsideTypeof) { RUNTIME_FUNCTION(Runtime_GetGlobalInsideTypeof) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(1, args.length()); DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, name, 0); CONVERT_SMI_ARG_CHECKED(slot, 0);
return GetGlobal(isolate, name, false); CONVERT_ARG_HANDLE_CHECKED(TypeFeedbackVector, vector, 1);
return GetGlobal(isolate, slot, vector, false);
} }
RUNTIME_FUNCTION(Runtime_GetGlobalNotInsideTypeof) { RUNTIME_FUNCTION(Runtime_GetGlobalNotInsideTypeof) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(1, args.length()); DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, name, 0); CONVERT_SMI_ARG_CHECKED(slot, 0);
return GetGlobal(isolate, name, true); CONVERT_ARG_HANDLE_CHECKED(TypeFeedbackVector, vector, 1);
return GetGlobal(isolate, slot, vector, true);
} }
// KeyedGetProperty is called from KeyedLoadIC::GenerateGeneric. // KeyedGetProperty is called from KeyedLoadIC::GenerateGeneric.
......
...@@ -384,8 +384,8 @@ namespace internal { ...@@ -384,8 +384,8 @@ namespace internal {
F(SetPrototype, 2, 1) \ F(SetPrototype, 2, 1) \
F(OptimizeObjectForAddingMultipleProperties, 2, 1) \ F(OptimizeObjectForAddingMultipleProperties, 2, 1) \
F(GetProperty, 2, 1) \ F(GetProperty, 2, 1) \
F(GetGlobalInsideTypeof, 1, 1) \ F(GetGlobalInsideTypeof, 2, 1) \
F(GetGlobalNotInsideTypeof, 1, 1) \ F(GetGlobalNotInsideTypeof, 2, 1) \
F(KeyedGetProperty, 2, 1) \ F(KeyedGetProperty, 2, 1) \
F(StoreGlobalViaContext_Sloppy, 2, 1) \ F(StoreGlobalViaContext_Sloppy, 2, 1) \
F(StoreGlobalViaContext_Strict, 2, 1) \ F(StoreGlobalViaContext_Strict, 2, 1) \
...@@ -953,7 +953,7 @@ namespace internal { ...@@ -953,7 +953,7 @@ namespace internal {
F(KeyedStoreIC_MissFromStubFailure, 5, 1) \ F(KeyedStoreIC_MissFromStubFailure, 5, 1) \
F(KeyedStoreIC_Slow, 5, 1) \ F(KeyedStoreIC_Slow, 5, 1) \
F(LoadElementWithInterceptor, 2, 1) \ F(LoadElementWithInterceptor, 2, 1) \
F(LoadGlobalIC_Miss, 3, 1) \ F(LoadGlobalIC_Miss, 2, 1) \
F(LoadIC_Miss, 4, 1) \ F(LoadIC_Miss, 4, 1) \
F(LoadIC_MissFromStubFailure, 4, 1) \ F(LoadIC_MissFromStubFailure, 4, 1) \
F(LoadPropertyWithInterceptor, 3, 1) \ F(LoadPropertyWithInterceptor, 3, 1) \
......
...@@ -14,13 +14,11 @@ namespace internal { ...@@ -14,13 +14,11 @@ namespace internal {
template <typename Derived> template <typename Derived>
FeedbackVectorSlot FeedbackVectorSpecBase<Derived>::AddSlot( FeedbackVectorSlot FeedbackVectorSpecBase<Derived>::AddSlot(
FeedbackVectorSlotKind kind) { FeedbackVectorSlotKind kind) {
Derived* derived = static_cast<Derived*>(this); int slot = This()->slots();
int slot = derived->slots();
int entries_per_slot = TypeFeedbackMetadata::GetSlotSize(kind); int entries_per_slot = TypeFeedbackMetadata::GetSlotSize(kind);
derived->append(kind); This()->append(kind);
for (int i = 1; i < entries_per_slot; i++) { for (int i = 1; i < entries_per_slot; i++) {
derived->append(FeedbackVectorSlotKind::INVALID); This()->append(FeedbackVectorSlotKind::INVALID);
} }
return FeedbackVectorSlot(slot); return FeedbackVectorSlot(slot);
} }
...@@ -57,6 +55,26 @@ int TypeFeedbackMetadata::GetSlotSize(FeedbackVectorSlotKind kind) { ...@@ -57,6 +55,26 @@ int TypeFeedbackMetadata::GetSlotSize(FeedbackVectorSlotKind kind) {
return kind == FeedbackVectorSlotKind::GENERAL ? 1 : 2; return kind == FeedbackVectorSlotKind::GENERAL ? 1 : 2;
} }
bool TypeFeedbackMetadata::SlotRequiresName(FeedbackVectorSlotKind kind) {
switch (kind) {
case FeedbackVectorSlotKind::LOAD_GLOBAL_IC:
return true;
case FeedbackVectorSlotKind::CALL_IC:
case FeedbackVectorSlotKind::LOAD_IC:
case FeedbackVectorSlotKind::KEYED_LOAD_IC:
case FeedbackVectorSlotKind::STORE_IC:
case FeedbackVectorSlotKind::KEYED_STORE_IC:
case FeedbackVectorSlotKind::GENERAL:
case FeedbackVectorSlotKind::INVALID:
return false;
case FeedbackVectorSlotKind::KINDS_NUMBER:
break;
}
UNREACHABLE();
return false;
}
bool TypeFeedbackVector::is_empty() const { bool TypeFeedbackVector::is_empty() const {
if (length() == 0) return true; if (length() == 0) return true;
...@@ -140,15 +158,15 @@ Symbol* TypeFeedbackVector::RawUninitializedSentinel(Isolate* isolate) { ...@@ -140,15 +158,15 @@ Symbol* TypeFeedbackVector::RawUninitializedSentinel(Isolate* isolate) {
} }
bool TypeFeedbackMetadataIterator::HasNext() const { bool TypeFeedbackMetadataIterator::HasNext() const {
return slot_.ToInt() < metadata()->slot_count(); return next_slot_.ToInt() < metadata()->slot_count();
} }
FeedbackVectorSlot TypeFeedbackMetadataIterator::Next() { FeedbackVectorSlot TypeFeedbackMetadataIterator::Next() {
DCHECK(HasNext()); DCHECK(HasNext());
FeedbackVectorSlot slot = slot_; cur_slot_ = next_slot_;
slot_kind_ = metadata()->GetKind(slot); slot_kind_ = metadata()->GetKind(cur_slot_);
slot_ = FeedbackVectorSlot(slot_.ToInt() + entry_size()); next_slot_ = FeedbackVectorSlot(next_slot_.ToInt() + entry_size());
return slot; return cur_slot_;
} }
int TypeFeedbackMetadataIterator::entry_size() const { int TypeFeedbackMetadataIterator::entry_size() const {
......
...@@ -37,6 +37,23 @@ FeedbackVectorSlotKind TypeFeedbackMetadata::GetKind( ...@@ -37,6 +37,23 @@ FeedbackVectorSlotKind TypeFeedbackMetadata::GetKind(
return VectorICComputer::decode(data, slot.ToInt()); return VectorICComputer::decode(data, slot.ToInt());
} }
String* TypeFeedbackMetadata::GetName(FeedbackVectorSlot slot) const {
DCHECK(SlotRequiresName(GetKind(slot)));
FixedArray* names = FixedArray::cast(get(kNamesTableIndex));
// TODO(ishell): consider using binary search here or even Dictionary when we
// have more ICs with names.
Smi* key = Smi::FromInt(slot.ToInt());
for (int entry = 0; entry < names->length(); entry += kNameTableEntrySize) {
Object* current_key = names->get(entry + kNameTableSlotIndex);
if (current_key == key) {
Object* name = names->get(entry + kNameTableNameIndex);
DCHECK(name->IsString());
return String::cast(name);
}
}
UNREACHABLE();
return nullptr;
}
void TypeFeedbackMetadata::SetKind(FeedbackVectorSlot slot, void TypeFeedbackMetadata::SetKind(FeedbackVectorSlot slot,
FeedbackVectorSlotKind kind) { FeedbackVectorSlotKind kind) {
...@@ -57,12 +74,13 @@ template Handle<TypeFeedbackMetadata> TypeFeedbackMetadata::New( ...@@ -57,12 +74,13 @@ template Handle<TypeFeedbackMetadata> TypeFeedbackMetadata::New(
template <typename Spec> template <typename Spec>
Handle<TypeFeedbackMetadata> TypeFeedbackMetadata::New(Isolate* isolate, Handle<TypeFeedbackMetadata> TypeFeedbackMetadata::New(Isolate* isolate,
const Spec* spec) { const Spec* spec) {
Factory* factory = isolate->factory();
const int slot_count = spec->slots(); const int slot_count = spec->slots();
const int slot_kinds_length = VectorICComputer::word_count(slot_count); const int slot_kinds_length = VectorICComputer::word_count(slot_count);
const int length = slot_kinds_length + kReservedIndexCount; const int length = slot_kinds_length + kReservedIndexCount;
if (length == kReservedIndexCount) { if (length == kReservedIndexCount) {
return Handle<TypeFeedbackMetadata>::cast( return Handle<TypeFeedbackMetadata>::cast(factory->empty_fixed_array());
isolate->factory()->empty_fixed_array());
} }
#ifdef DEBUG #ifdef DEBUG
for (int i = 0; i < slot_count;) { for (int i = 0; i < slot_count;) {
...@@ -76,7 +94,7 @@ Handle<TypeFeedbackMetadata> TypeFeedbackMetadata::New(Isolate* isolate, ...@@ -76,7 +94,7 @@ Handle<TypeFeedbackMetadata> TypeFeedbackMetadata::New(Isolate* isolate,
} }
#endif #endif
Handle<FixedArray> array = isolate->factory()->NewFixedArray(length, TENURED); Handle<FixedArray> array = factory->NewFixedArray(length, TENURED);
array->set(kSlotsCountIndex, Smi::FromInt(slot_count)); array->set(kSlotsCountIndex, Smi::FromInt(slot_count));
// Fill the bit-vector part with zeros. // Fill the bit-vector part with zeros.
for (int i = 0; i < slot_kinds_length; i++) { for (int i = 0; i < slot_kinds_length; i++) {
...@@ -85,9 +103,29 @@ Handle<TypeFeedbackMetadata> TypeFeedbackMetadata::New(Isolate* isolate, ...@@ -85,9 +103,29 @@ Handle<TypeFeedbackMetadata> TypeFeedbackMetadata::New(Isolate* isolate,
Handle<TypeFeedbackMetadata> metadata = Handle<TypeFeedbackMetadata> metadata =
Handle<TypeFeedbackMetadata>::cast(array); Handle<TypeFeedbackMetadata>::cast(array);
// Add names to NamesTable.
const int name_count = spec->name_count();
Handle<FixedArray> names =
name_count == 0
? factory->empty_fixed_array()
: factory->NewFixedArray(name_count * kNameTableEntrySize);
int name_index = 0;
for (int i = 0; i < slot_count; i++) { for (int i = 0; i < slot_count; i++) {
metadata->SetKind(FeedbackVectorSlot(i), spec->GetKind(i)); FeedbackVectorSlotKind kind = spec->GetKind(i);
metadata->SetKind(FeedbackVectorSlot(i), kind);
if (SlotRequiresName(kind)) {
Handle<String> name = spec->GetName(name_index);
DCHECK(!name.is_null());
int entry = name_index * kNameTableEntrySize;
names->set(entry + kNameTableSlotIndex, Smi::FromInt(i));
names->set(entry + kNameTableNameIndex, *name);
name_index++;
}
} }
DCHECK_EQ(name_count, name_index);
metadata->set(kNamesTableIndex, *names);
// It's important that the TypeFeedbackMetadata have a COW map, since it's // It's important that the TypeFeedbackMetadata have a COW map, since it's
// pointed to by both a SharedFunctionInfo and indirectly by closures through // pointed to by both a SharedFunctionInfo and indirectly by closures through
...@@ -107,10 +145,24 @@ bool TypeFeedbackMetadata::SpecDiffersFrom( ...@@ -107,10 +145,24 @@ bool TypeFeedbackMetadata::SpecDiffersFrom(
} }
int slots = slot_count(); int slots = slot_count();
for (int i = 0; i < slots; i++) { int name_index = 0;
if (GetKind(FeedbackVectorSlot(i)) != other_spec->GetKind(i)) { for (int i = 0; i < slots;) {
FeedbackVectorSlot slot(i);
FeedbackVectorSlotKind kind = GetKind(slot);
int entry_size = TypeFeedbackMetadata::GetSlotSize(kind);
if (kind != other_spec->GetKind(i)) {
return true; return true;
} }
if (SlotRequiresName(kind)) {
String* name = GetName(slot);
DCHECK(name != GetHeap()->empty_string());
String* other_name = *other_spec->GetName(name_index++);
if (name != other_name) {
return true;
}
}
i += entry_size;
} }
return false; return false;
} }
...@@ -122,11 +174,19 @@ bool TypeFeedbackMetadata::DiffersFrom( ...@@ -122,11 +174,19 @@ bool TypeFeedbackMetadata::DiffersFrom(
} }
int slots = slot_count(); int slots = slot_count();
for (int i = 0; i < slots; i++) { for (int i = 0; i < slots;) {
FeedbackVectorSlot slot(i); FeedbackVectorSlot slot(i);
FeedbackVectorSlotKind kind = GetKind(slot);
int entry_size = TypeFeedbackMetadata::GetSlotSize(kind);
if (GetKind(slot) != other_metadata->GetKind(slot)) { if (GetKind(slot) != other_metadata->GetKind(slot)) {
return true; return true;
} }
if (SlotRequiresName(kind)) {
if (GetName(slot) != other_metadata->GetName(slot)) {
return true;
}
}
i += entry_size;
} }
return false; return false;
} }
...@@ -162,6 +222,11 @@ FeedbackVectorSlotKind TypeFeedbackVector::GetKind( ...@@ -162,6 +222,11 @@ FeedbackVectorSlotKind TypeFeedbackVector::GetKind(
return metadata()->GetKind(slot); return metadata()->GetKind(slot);
} }
String* TypeFeedbackVector::GetName(FeedbackVectorSlot slot) const {
DCHECK(!is_empty());
return metadata()->GetName(slot);
}
// static // static
Handle<TypeFeedbackVector> TypeFeedbackVector::New( Handle<TypeFeedbackVector> TypeFeedbackVector::New(
Isolate* isolate, Handle<TypeFeedbackMetadata> metadata) { Isolate* isolate, Handle<TypeFeedbackMetadata> metadata) {
...@@ -176,6 +241,8 @@ Handle<TypeFeedbackVector> TypeFeedbackVector::New( ...@@ -176,6 +241,8 @@ Handle<TypeFeedbackVector> TypeFeedbackVector::New(
Handle<FixedArray> array = factory->NewFixedArray(length, TENURED); Handle<FixedArray> array = factory->NewFixedArray(length, TENURED);
array->set(kMetadataIndex, *metadata); array->set(kMetadataIndex, *metadata);
DisallowHeapAllocation no_gc;
// Ensure we can skip the write barrier // Ensure we can skip the write barrier
Handle<Object> uninitialized_sentinel = UninitializedSentinel(isolate); Handle<Object> uninitialized_sentinel = UninitializedSentinel(isolate);
DCHECK_EQ(*factory->uninitialized_symbol(), *uninitialized_sentinel); DCHECK_EQ(*factory->uninitialized_symbol(), *uninitialized_sentinel);
......
...@@ -50,7 +50,8 @@ class FeedbackVectorSpecBase { ...@@ -50,7 +50,8 @@ class FeedbackVectorSpecBase {
return AddSlot(FeedbackVectorSlotKind::LOAD_IC); return AddSlot(FeedbackVectorSlotKind::LOAD_IC);
} }
FeedbackVectorSlot AddLoadGlobalICSlot() { FeedbackVectorSlot AddLoadGlobalICSlot(Handle<String> name) {
This()->append_name(name);
return AddSlot(FeedbackVectorSlotKind::LOAD_GLOBAL_IC); return AddSlot(FeedbackVectorSlotKind::LOAD_GLOBAL_IC);
} }
...@@ -69,40 +70,65 @@ class FeedbackVectorSpecBase { ...@@ -69,40 +70,65 @@ class FeedbackVectorSpecBase {
FeedbackVectorSlot AddGeneralSlot() { FeedbackVectorSlot AddGeneralSlot() {
return AddSlot(FeedbackVectorSlotKind::GENERAL); return AddSlot(FeedbackVectorSlotKind::GENERAL);
} }
#ifdef OBJECT_PRINT
// For gdb debugging.
void Print();
#endif // OBJECT_PRINT
DECLARE_PRINTER(FeedbackVectorSpec)
private:
Derived* This() { return static_cast<Derived*>(this); }
}; };
class StaticFeedbackVectorSpec class StaticFeedbackVectorSpec
: public FeedbackVectorSpecBase<StaticFeedbackVectorSpec> { : public FeedbackVectorSpecBase<StaticFeedbackVectorSpec> {
public: public:
StaticFeedbackVectorSpec() : slots_(0) {} StaticFeedbackVectorSpec() : slot_count_(0), name_count_(0) {}
int slots() const { return slots_; } int slots() const { return slot_count_; }
FeedbackVectorSlotKind GetKind(int slot) const { FeedbackVectorSlotKind GetKind(int slot) const {
DCHECK(slot >= 0 && slot < slots_); DCHECK(slot >= 0 && slot < slot_count_);
return kinds_[slot]; return kinds_[slot];
} }
int name_count() const { return name_count_; }
Handle<String> GetName(int index) const {
DCHECK(index >= 0 && index < name_count_);
return names_[index];
}
private: private:
friend class FeedbackVectorSpecBase<StaticFeedbackVectorSpec>; friend class FeedbackVectorSpecBase<StaticFeedbackVectorSpec>;
void append(FeedbackVectorSlotKind kind) { void append(FeedbackVectorSlotKind kind) {
DCHECK(slots_ < kMaxLength); DCHECK(slot_count_ < kMaxLength);
kinds_[slots_++] = kind; kinds_[slot_count_++] = kind;
}
void append_name(Handle<String> name) {
DCHECK(name_count_ < kMaxLength);
names_[name_count_++] = name;
} }
static const int kMaxLength = 12; static const int kMaxLength = 12;
int slots_; int slot_count_;
FeedbackVectorSlotKind kinds_[kMaxLength]; FeedbackVectorSlotKind kinds_[kMaxLength];
int name_count_;
Handle<String> names_[kMaxLength];
}; };
class FeedbackVectorSpec : public FeedbackVectorSpecBase<FeedbackVectorSpec> { class FeedbackVectorSpec : public FeedbackVectorSpecBase<FeedbackVectorSpec> {
public: public:
explicit FeedbackVectorSpec(Zone* zone) : slot_kinds_(zone) { explicit FeedbackVectorSpec(Zone* zone) : slot_kinds_(zone), names_(zone) {
slot_kinds_.reserve(16); slot_kinds_.reserve(16);
names_.reserve(8);
} }
int slots() const { return static_cast<int>(slot_kinds_.size()); } int slots() const { return static_cast<int>(slot_kinds_.size()); }
...@@ -111,6 +137,10 @@ class FeedbackVectorSpec : public FeedbackVectorSpecBase<FeedbackVectorSpec> { ...@@ -111,6 +137,10 @@ class FeedbackVectorSpec : public FeedbackVectorSpecBase<FeedbackVectorSpec> {
return static_cast<FeedbackVectorSlotKind>(slot_kinds_.at(slot)); return static_cast<FeedbackVectorSlotKind>(slot_kinds_.at(slot));
} }
int name_count() const { return static_cast<int>(names_.size()); }
Handle<String> GetName(int index) const { return names_.at(index); }
private: private:
friend class FeedbackVectorSpecBase<FeedbackVectorSpec>; friend class FeedbackVectorSpecBase<FeedbackVectorSpec>;
...@@ -118,13 +148,17 @@ class FeedbackVectorSpec : public FeedbackVectorSpecBase<FeedbackVectorSpec> { ...@@ -118,13 +148,17 @@ class FeedbackVectorSpec : public FeedbackVectorSpecBase<FeedbackVectorSpec> {
slot_kinds_.push_back(static_cast<unsigned char>(kind)); slot_kinds_.push_back(static_cast<unsigned char>(kind));
} }
void append_name(Handle<String> name) { names_.push_back(name); }
ZoneVector<unsigned char> slot_kinds_; ZoneVector<unsigned char> slot_kinds_;
ZoneVector<Handle<String>> names_;
}; };
// The shape of the TypeFeedbackMetadata is an array with: // The shape of the TypeFeedbackMetadata is an array with:
// 0: slot_count // 0: slot_count
// 1..N: slot kinds packed into a bit vector // 1: names table
// 2..N: slot kinds packed into a bit vector
// //
class TypeFeedbackMetadata : public FixedArray { class TypeFeedbackMetadata : public FixedArray {
public: public:
...@@ -132,11 +166,19 @@ class TypeFeedbackMetadata : public FixedArray { ...@@ -132,11 +166,19 @@ class TypeFeedbackMetadata : public FixedArray {
static inline TypeFeedbackMetadata* cast(Object* obj); static inline TypeFeedbackMetadata* cast(Object* obj);
static const int kSlotsCountIndex = 0; static const int kSlotsCountIndex = 0;
static const int kReservedIndexCount = 1; static const int kNamesTableIndex = 1;
static const int kReservedIndexCount = 2;
static const int kNameTableEntrySize = 2;
static const int kNameTableSlotIndex = 0;
static const int kNameTableNameIndex = 1;
// Returns number of feedback vector elements used by given slot kind. // Returns number of feedback vector elements used by given slot kind.
static inline int GetSlotSize(FeedbackVectorSlotKind kind); static inline int GetSlotSize(FeedbackVectorSlotKind kind);
// Defines if slots of given kind require "name".
static inline bool SlotRequiresName(FeedbackVectorSlotKind kind);
bool SpecDiffersFrom(const FeedbackVectorSpec* other_spec) const; bool SpecDiffersFrom(const FeedbackVectorSpec* other_spec) const;
bool DiffersFrom(const TypeFeedbackMetadata* other_metadata) const; bool DiffersFrom(const TypeFeedbackMetadata* other_metadata) const;
...@@ -149,6 +191,9 @@ class TypeFeedbackMetadata : public FixedArray { ...@@ -149,6 +191,9 @@ class TypeFeedbackMetadata : public FixedArray {
// Returns slot kind for given slot. // Returns slot kind for given slot.
FeedbackVectorSlotKind GetKind(FeedbackVectorSlot slot) const; FeedbackVectorSlotKind GetKind(FeedbackVectorSlot slot) const;
// Returns name for given slot.
String* GetName(FeedbackVectorSlot slot) const;
template <typename Spec> template <typename Spec>
static Handle<TypeFeedbackMetadata> New(Isolate* isolate, const Spec* spec); static Handle<TypeFeedbackMetadata> New(Isolate* isolate, const Spec* spec);
...@@ -179,9 +224,9 @@ class TypeFeedbackMetadata : public FixedArray { ...@@ -179,9 +224,9 @@ class TypeFeedbackMetadata : public FixedArray {
// 0: feedback metadata // 0: feedback metadata
// 1: ics_with_types // 1: ics_with_types
// 2: ics_with_generic_info // 2: ics_with_generic_info
// 3: feedback slot #0 (N >= 3) // 3: feedback slot #0
// ... // ...
// N + slot_count - 1: feedback slot #(slot_count-1) // 3 + slot_count - 1: feedback slot #(slot_count-1)
// //
class TypeFeedbackVector : public FixedArray { class TypeFeedbackVector : public FixedArray {
public: public:
...@@ -215,6 +260,8 @@ class TypeFeedbackVector : public FixedArray { ...@@ -215,6 +260,8 @@ class TypeFeedbackVector : public FixedArray {
// Returns slot kind for given slot. // Returns slot kind for given slot.
FeedbackVectorSlotKind GetKind(FeedbackVectorSlot slot) const; FeedbackVectorSlotKind GetKind(FeedbackVectorSlot slot) const;
// Returns name corresponding to given slot or an empty string.
String* GetName(FeedbackVectorSlot slot) const;
static Handle<TypeFeedbackVector> New(Isolate* isolate, static Handle<TypeFeedbackVector> New(Isolate* isolate,
Handle<TypeFeedbackMetadata> metadata); Handle<TypeFeedbackMetadata> metadata);
...@@ -289,12 +336,12 @@ class TypeFeedbackMetadataIterator { ...@@ -289,12 +336,12 @@ class TypeFeedbackMetadataIterator {
public: public:
explicit TypeFeedbackMetadataIterator(Handle<TypeFeedbackMetadata> metadata) explicit TypeFeedbackMetadataIterator(Handle<TypeFeedbackMetadata> metadata)
: metadata_handle_(metadata), : metadata_handle_(metadata),
slot_(FeedbackVectorSlot(0)), next_slot_(FeedbackVectorSlot(0)),
slot_kind_(FeedbackVectorSlotKind::INVALID) {} slot_kind_(FeedbackVectorSlotKind::INVALID) {}
explicit TypeFeedbackMetadataIterator(TypeFeedbackMetadata* metadata) explicit TypeFeedbackMetadataIterator(TypeFeedbackMetadata* metadata)
: metadata_(metadata), : metadata_(metadata),
slot_(FeedbackVectorSlot(0)), next_slot_(FeedbackVectorSlot(0)),
slot_kind_(FeedbackVectorSlotKind::INVALID) {} slot_kind_(FeedbackVectorSlotKind::INVALID) {}
inline bool HasNext() const; inline bool HasNext() const;
...@@ -311,6 +358,11 @@ class TypeFeedbackMetadataIterator { ...@@ -311,6 +358,11 @@ class TypeFeedbackMetadataIterator {
// Returns entry size of the last slot returned by Next(). // Returns entry size of the last slot returned by Next().
inline int entry_size() const; inline int entry_size() const;
String* name() const {
DCHECK(TypeFeedbackMetadata::SlotRequiresName(kind()));
return metadata()->GetName(cur_slot_);
}
private: private:
TypeFeedbackMetadata* metadata() const { TypeFeedbackMetadata* metadata() const {
return !metadata_handle_.is_null() ? *metadata_handle_ : metadata_; return !metadata_handle_.is_null() ? *metadata_handle_ : metadata_;
...@@ -321,7 +373,8 @@ class TypeFeedbackMetadataIterator { ...@@ -321,7 +373,8 @@ class TypeFeedbackMetadataIterator {
// pointer use cases. // pointer use cases.
Handle<TypeFeedbackMetadata> metadata_handle_; Handle<TypeFeedbackMetadata> metadata_handle_;
TypeFeedbackMetadata* metadata_; TypeFeedbackMetadata* metadata_;
FeedbackVectorSlot slot_; FeedbackVectorSlot cur_slot_;
FeedbackVectorSlot next_slot_;
FeedbackVectorSlotKind slot_kind_; FeedbackVectorSlotKind slot_kind_;
}; };
......
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