mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[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:
parent
48b45db358
commit
bfbcfa6dc5
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue