[vm/aot] Eliminate InlinedIntoICF retain reason

We should not need to retain functions which do not have any code,
the only reason to retain them would be for runtime to be able to
find the implicit closure (when performing lookup through API).

Additionally drop closure_functions array when producing
PRODUCT snapshot - because this array is not going to be used.

TEST=ci

Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-dwarf-linux-product-x64-try
Change-Id: I29110ce613fdb347e2627d857f790e82602926d3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/219481
Commit-Queue: Slava Egorov <vegorov@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Vyacheslav Egorov 2021-11-17 11:02:01 +00:00 committed by commit-bot@chromium.org
parent 48b45db358
commit bfbcfa6dc5
7 changed files with 119 additions and 90 deletions

View file

@ -5686,43 +5686,48 @@ static const char* const kObjectStoreFieldNames[] = {
class ProgramSerializationRoots : public SerializationRoots {
public:
#define RESET_ROOT_LIST(V) \
V(symbol_table, Array, HashTables::New<CanonicalStringSet>(4)) \
V(canonical_types, Array, HashTables::New<CanonicalTypeSet>(4)) \
V(canonical_function_types, Array, \
HashTables::New<CanonicalFunctionTypeSet>(4)) \
V(canonical_type_arguments, Array, \
HashTables::New<CanonicalTypeArgumentsSet>(4)) \
V(canonical_type_parameters, Array, \
HashTables::New<CanonicalTypeParameterSet>(4)) \
ONLY_IN_PRODUCT(ONLY_IN_AOT( \
V(closure_functions, GrowableObjectArray, GrowableObjectArray::null())))
ProgramSerializationRoots(ZoneGrowableArray<Object*>* base_objects,
ObjectStore* object_store,
Snapshot::Kind snapshot_kind)
: base_objects_(base_objects),
object_store_(object_store),
dispatch_table_entries_(Array::Handle()),
saved_symbol_table_(Array::Handle()),
saved_canonical_types_(Array::Handle()),
saved_canonical_function_types_(Array::Handle()),
saved_canonical_type_arguments_(Array::Handle()),
saved_canonical_type_parameters_(Array::Handle()) {
saved_symbol_table_ = object_store->symbol_table();
object_store->set_symbol_table(
Array::Handle(HashTables::New<CanonicalStringSet>(4)));
saved_canonical_types_ = object_store->canonical_types();
object_store->set_canonical_types(
Array::Handle(HashTables::New<CanonicalTypeSet>(4)));
saved_canonical_function_types_ = object_store->canonical_function_types();
object_store->set_canonical_function_types(
Array::Handle(HashTables::New<CanonicalFunctionTypeSet>(4)));
saved_canonical_type_arguments_ = object_store->canonical_type_arguments();
object_store->set_canonical_type_arguments(
Array::Handle(HashTables::New<CanonicalTypeArgumentsSet>(4)));
saved_canonical_type_parameters_ =
object_store->canonical_type_parameters();
object_store->set_canonical_type_parameters(
Array::Handle(HashTables::New<CanonicalTypeParameterSet>(4)));
snapshot_kind_(snapshot_kind) {
#define ONLY_IN_AOT(code) \
if (snapshot_kind_ == Snapshot::kFullAOT) { \
code \
}
#define SAVE_AND_RESET_ROOT(name, Type, init) \
do { \
saved_##name##_ = object_store->name(); \
object_store->set_##name(Type::Handle(init)); \
} while (0);
RESET_ROOT_LIST(SAVE_AND_RESET_ROOT)
#undef SAVE_AND_RESET_ROOT
#undef ONLY_IN_AOT
}
~ProgramSerializationRoots() {
object_store_->set_symbol_table(saved_symbol_table_);
object_store_->set_canonical_types(saved_canonical_types_);
object_store_->set_canonical_function_types(
saved_canonical_function_types_);
object_store_->set_canonical_type_arguments(
saved_canonical_type_arguments_);
object_store_->set_canonical_type_parameters(
saved_canonical_type_parameters_);
#define ONLY_IN_AOT(code) \
if (snapshot_kind_ == Snapshot::kFullAOT) { \
code \
}
#define RESTORE_ROOT(name, Type, init) \
object_store_->set_##name(saved_##name##_);
RESET_ROOT_LIST(RESTORE_ROOT)
#undef RESTORE_ROOT
#undef ONLY_IN_AOT
}
void AddBaseObjects(Serializer* s) {
@ -5776,14 +5781,16 @@ class ProgramSerializationRoots : public SerializationRoots {
}
private:
ZoneGrowableArray<Object*>* base_objects_;
ObjectStore* object_store_;
Array& dispatch_table_entries_;
Array& saved_symbol_table_;
Array& saved_canonical_types_;
Array& saved_canonical_function_types_;
Array& saved_canonical_type_arguments_;
Array& saved_canonical_type_parameters_;
ZoneGrowableArray<Object*>* const base_objects_;
ObjectStore* const object_store_;
const Snapshot::Kind snapshot_kind_;
Array& dispatch_table_entries_ = Array::Handle();
#define ONLY_IN_AOT(code) code
#define DECLARE_FIELD(name, Type, init) Type& saved_##name##_ = Type::Handle();
RESET_ROOT_LIST(DECLARE_FIELD)
#undef DECLARE_FIELD
#undef ONLY_IN_AOT
};
#endif // !DART_PRECOMPILED_RUNTIME

View file

@ -73,7 +73,9 @@ FunctionPtr ClosureFunctionsCache::LookupClosureFunctionLocked(
return Function::null();
}
void ClosureFunctionsCache::AddClosureFunctionLocked(const Function& function) {
void ClosureFunctionsCache::AddClosureFunctionLocked(
const Function& function,
bool allow_implicit_closure_functions /* = false */) {
ASSERT(!Compiler::IsBackgroundCompilation());
auto thread = Thread::Current();
@ -86,7 +88,8 @@ void ClosureFunctionsCache::AddClosureFunctionLocked(const Function& function) {
const auto& closures =
GrowableObjectArray::Handle(zone, object_store->closure_functions());
ASSERT(!closures.IsNull());
ASSERT(function.IsNonImplicitClosureFunction());
ASSERT(allow_implicit_closure_functions ||
function.IsNonImplicitClosureFunction());
closures.Add(function, Heap::kOld);
}

View file

@ -49,7 +49,13 @@ class ClosureFunctionsCache : public AllStatic {
static FunctionPtr LookupClosureFunctionLocked(const Function& parent,
TokenPosition token_pos);
static void AddClosureFunctionLocked(const Function& function);
// Normally implicit closure functions are not added to this cache, however
// during AOT compilation we might add those implicit closure functions
// that have their original functions shaken to allow ProgramWalker to
// discover them.
static void AddClosureFunctionLocked(
const Function& function,
bool allow_implicit_closure_functions = false);
static intptr_t FindClosureIndex(const Function& needle);
static FunctionPtr ClosureFunctionFromIndex(intptr_t idx);

View file

@ -141,12 +141,11 @@ struct RetainReasons : public AllStatic {
// The object is a function and symbolic stack traces are enabled.
static constexpr const char* kSymbolicStackTraces =
"needed for symbolic stack traces";
// The object is a function that is only used via its implicit closure
// function, into which it was inlined.
static constexpr const char* kInlinedIntoICF =
"inlined into implicit closure function";
// The object is a parent function function of a non-inlined local function.
static constexpr const char* kLocalParent = "parent of a local function";
// The object is a main function of the root library.
static constexpr const char* kMainFunction =
"this is main function of the root library";
// The object has an entry point pragma that requires it be retained.
static constexpr const char* kEntryPointPragma = "entry point pragma";
// The function is a target of FFI callback.
@ -724,13 +723,10 @@ void Precompiler::PrecompileConstructors() {
void Precompiler::AddRoots() {
HANDLESCOPE(T);
// Note that <rootlibrary>.main is not a root. The appropriate main will be
// discovered through _getMainClosure.
AddSelector(Symbols::NoSuchMethod());
AddSelector(Symbols::Call()); // For speed, not correctness.
// Add main as an entry point.
const Library& lib = Library::Handle(IG->object_store()->root_library());
if (lib.IsNull()) {
const String& msg = String::Handle(
@ -740,22 +736,26 @@ void Precompiler::AddRoots() {
}
const String& name = String::Handle(String::New("main"));
const Object& main_closure = Object::Handle(lib.GetFunctionClosure(name));
if (main_closure.IsClosure()) {
if (lib.LookupLocalFunction(name) == Function::null()) {
// Check whether the function is in exported namespace of library, in
// this case we have to retain the root library caches.
if (lib.LookupFunctionAllowPrivate(name) != Function::null() ||
lib.LookupReExport(name) != Object::null()) {
retain_root_library_caches_ = true;
}
Function& main = Function::Handle(lib.LookupFunctionAllowPrivate(name));
if (main.IsNull()) {
const Object& obj = Object::Handle(lib.LookupReExport(name));
if (obj.IsFunction()) {
main ^= obj.ptr();
}
AddConstObject(Closure::Cast(main_closure));
} else if (main_closure.IsError()) {
const Error& error = Error::Cast(main_closure);
String& msg =
String::Handle(Z, String::NewFormatted("Cannot find main closure %s\n",
error.ToErrorCString()));
}
if (!main.IsNull()) {
if (lib.LookupLocalFunction(name) == Function::null()) {
retain_root_library_caches_ = true;
}
AddRetainReason(main, RetainReasons::kMainFunction);
AddTypesOf(main);
// Create closure object from main.
main = main.ImplicitClosureFunction();
AddConstObject(Closure::Handle(main.ImplicitStaticClosure()));
} else {
String& msg = String::Handle(
Z, String::NewFormatted("Cannot find main in library %s\n",
lib.ToCString()));
Jump(Error::Handle(Z, ApiError::New(msg)));
UNREACHABLE();
}
@ -1930,25 +1930,25 @@ void Precompiler::TraceForRetainedFunctions() {
for (intptr_t j = 0; j < functions.Length(); j++) {
SafepointWriteRwLocker ml(T, T->isolate_group()->program_lock());
function ^= functions.At(j);
bool retain = possibly_retained_functions_.ContainsKey(function);
if (!retain && function.HasImplicitClosureFunction()) {
// It can happen that all uses of an implicit closure inline their
// target function, leaving the target function uncompiled. Keep
// the target function anyway so we can enumerate it to bind its
// static calls, etc.
function2 = function.ImplicitClosureFunction();
retain = function2.HasCode();
if (retain) {
AddRetainReason(function, RetainReasons::kInlinedIntoICF);
}
}
if (retain) {
function.DropUncompiledImplicitClosureFunction();
function.DropUncompiledImplicitClosureFunction();
const bool retained =
possibly_retained_functions_.ContainsKey(function);
if (retained) {
AddTypesOf(function);
if (function.HasImplicitClosureFunction()) {
function2 = function.ImplicitClosureFunction();
if (possibly_retained_functions_.ContainsKey(function2)) {
AddTypesOf(function2);
}
if (function.HasImplicitClosureFunction()) {
function2 = function.ImplicitClosureFunction();
if (possibly_retained_functions_.ContainsKey(function2)) {
AddTypesOf(function2);
// If function has @pragma('vm:entry-point', 'get') we need to keep
// the function itself around so that runtime could find it and
// get to the implicit closure through it.
if (!retained &&
functions_with_entry_point_pragmas_.ContainsKey(function2)) {
AddRetainReason(function, RetainReasons::kEntryPointPragma);
AddTypesOf(function);
}
}
}
@ -2129,6 +2129,7 @@ void Precompiler::DropFunctions() {
Array& functions = Array::Handle(Z);
Function& function = Function::Handle(Z);
Function& target = Function::Handle(Z);
Function& implicit_closure = Function::Handle(Z);
Code& code = Code::Handle(Z);
Object& owner = Object::Handle(Z);
GrowableObjectArray& retained_functions = GrowableObjectArray::Handle(Z);
@ -2207,6 +2208,17 @@ void Precompiler::DropFunctions() {
owner, Smi::Handle(Smi::New(owner.GetClassId())));
code.set_owner(owner);
}
if (function.HasImplicitClosureFunction()) {
// If we are going to drop the function which has a compiled
// implicit closure move the closure itself to the list of closures
// attached to the object store so that ProgramVisitor could find it.
// The list of closures is going to be dropped during PRODUCT snapshotting
// so there is no overhead in doing so.
implicit_closure = function.ImplicitClosureFunction();
RELEASE_ASSERT(functions_to_retain_.ContainsKey(implicit_closure));
ClosureFunctionsCache::AddClosureFunctionLocked(
implicit_closure, /*allow_implicit_closure_functions=*/true);
}
dropped_function_count_++;
if (FLAG_trace_precompiler) {
THR_Print("Dropping function %s\n",
@ -2289,6 +2301,9 @@ void Precompiler::DropFunctions() {
}
return true; // Continue iteration.
});
// Note: in PRODUCT mode snapshotter will drop this field when serializing.
// This is done in ProgramSerializationRoots.
IG->object_store()->set_closure_functions(retained_functions);
}

View file

@ -73,8 +73,10 @@ const intptr_t kDefaultNewGenSemiMaxSize = (kWordSize <= 4) ? 8 : 16;
#if defined(PRODUCT)
#define NOT_IN_PRODUCT(code)
#define ONLY_IN_PRODUCT(code) code
#else // defined(PRODUCT)
#define NOT_IN_PRODUCT(code) code
#define ONLY_IN_PRODUCT(code)
#endif // defined(PRODUCT)
#if defined(DART_PRECOMPILED_RUNTIME) && defined(DART_PRECOMPILER)
@ -87,13 +89,9 @@ const intptr_t kDefaultNewGenSemiMaxSize = (kWordSize <= 4) ? 8 : 16;
#if defined(DART_PRECOMPILED_RUNTIME)
#define NOT_IN_PRECOMPILED(code)
#else
#define NOT_IN_PRECOMPILED(code) code
#endif // defined(DART_PRECOMPILED_RUNTIME)
#if defined(DART_PRECOMPILED_RUNTIME)
#define ONLY_IN_PRECOMPILED(code) code
#else
#define NOT_IN_PRECOMPILED(code) code
#define ONLY_IN_PRECOMPILED(code)
#endif // defined(DART_PRECOMPILED_RUNTIME)

View file

@ -7483,7 +7483,7 @@ FunctionPtr Function::GetOutermostFunction() const {
FunctionPtr Function::implicit_closure_function() const {
if (IsClosureFunction() || IsDispatcherOrImplicitAccessor() ||
IsFieldInitializer() || IsFfiTrampoline()) {
IsFieldInitializer() || IsFfiTrampoline() || IsMethodExtractor()) {
return Function::null();
}
const Object& obj = Object::Handle(data());

View file

@ -103,8 +103,8 @@ ObjectStore::ObjectStore()
EMIT_FIELD_INIT,
EMIT_FIELD_INIT)
#undef EMIT_FIELD_INIT
unused_field_(0) // Just to prevent a trailing comma.
{
// Just to prevent a trailing comma.
unused_field_(0) {
for (ObjectPtr* current = from(); current <= to(); current++) {
*current = Object::null();
}