Skip to content

Commit 6a6a854

Browse files
author
Dart CI
committed
Version 2.12.0-200.0.dev
Merge commit '5cd0770c9cd9a4c1e525854f4ff9b34f940afc07' into 'dev'
2 parents f1be9c1 + 5cd0770 commit 6a6a854

19 files changed

+386
-243
lines changed
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
#include "vm/closure_functions_cache.h"
6+
7+
#include "vm/compiler/jit/compiler.h"
8+
#include "vm/object.h"
9+
#include "vm/object_store.h"
10+
11+
namespace dart {
12+
13+
FunctionPtr ClosureFunctionsCache::LookupClosureFunction(
14+
const Class& owner,
15+
TokenPosition token_pos) {
16+
auto thread = Thread::Current();
17+
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
18+
return LookupClosureFunctionLocked(owner, token_pos);
19+
}
20+
21+
FunctionPtr ClosureFunctionsCache::LookupClosureFunctionLocked(
22+
const Class& owner,
23+
TokenPosition token_pos) {
24+
auto thread = Thread::Current();
25+
auto zone = thread->zone();
26+
auto object_store = thread->isolate_group()->object_store();
27+
28+
DEBUG_ASSERT(
29+
thread->isolate_group()->program_lock()->IsCurrentThreadReader());
30+
31+
const auto& closures =
32+
GrowableObjectArray::Handle(zone, object_store->closure_functions());
33+
auto& closure = Function::Handle(zone);
34+
intptr_t num_closures = closures.Length();
35+
for (intptr_t i = 0; i < num_closures; i++) {
36+
closure ^= closures.At(i);
37+
if (closure.token_pos() == token_pos && closure.Owner() == owner.raw()) {
38+
return closure.raw();
39+
}
40+
}
41+
return Function::null();
42+
}
43+
44+
FunctionPtr ClosureFunctionsCache::LookupClosureFunction(
45+
const Function& parent,
46+
TokenPosition token_pos) {
47+
auto thread = Thread::Current();
48+
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
49+
return LookupClosureFunctionLocked(parent, token_pos);
50+
}
51+
52+
FunctionPtr ClosureFunctionsCache::LookupClosureFunctionLocked(
53+
const Function& parent,
54+
TokenPosition token_pos) {
55+
auto thread = Thread::Current();
56+
auto zone = thread->zone();
57+
auto object_store = thread->isolate_group()->object_store();
58+
59+
DEBUG_ASSERT(
60+
thread->isolate_group()->program_lock()->IsCurrentThreadReader());
61+
62+
const auto& closures =
63+
GrowableObjectArray::Handle(zone, object_store->closure_functions());
64+
auto& closure = Function::Handle(zone);
65+
intptr_t num_closures = closures.Length();
66+
for (intptr_t i = 0; i < num_closures; i++) {
67+
closure ^= closures.At(i);
68+
if (closure.token_pos() == token_pos &&
69+
closure.parent_function() == parent.raw()) {
70+
return closure.raw();
71+
}
72+
}
73+
return Function::null();
74+
}
75+
76+
void ClosureFunctionsCache::AddClosureFunctionLocked(const Function& function) {
77+
ASSERT(!Compiler::IsBackgroundCompilation());
78+
79+
auto thread = Thread::Current();
80+
auto zone = thread->zone();
81+
auto object_store = thread->isolate_group()->object_store();
82+
83+
DEBUG_ASSERT(
84+
thread->isolate_group()->program_lock()->IsCurrentThreadWriter());
85+
86+
const auto& closures =
87+
GrowableObjectArray::Handle(zone, object_store->closure_functions());
88+
ASSERT(!closures.IsNull());
89+
ASSERT(function.IsNonImplicitClosureFunction());
90+
closures.Add(function, Heap::kOld);
91+
}
92+
93+
intptr_t ClosureFunctionsCache::FindClosureIndex(const Function& needle) {
94+
auto thread = Thread::Current();
95+
auto zone = thread->zone();
96+
auto object_store = thread->isolate_group()->object_store();
97+
98+
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
99+
100+
const auto& closures_array =
101+
GrowableObjectArray::Handle(zone, object_store->closure_functions());
102+
intptr_t num_closures = closures_array.Length();
103+
for (intptr_t i = 0; i < num_closures; i++) {
104+
if (closures_array.At(i) == needle.raw()) {
105+
return i;
106+
}
107+
}
108+
return -1;
109+
}
110+
111+
FunctionPtr ClosureFunctionsCache::ClosureFunctionFromIndex(intptr_t idx) {
112+
auto thread = Thread::Current();
113+
auto zone = thread->zone();
114+
auto object_store = thread->isolate_group()->object_store();
115+
116+
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
117+
118+
const auto& closures_array =
119+
GrowableObjectArray::Handle(zone, object_store->closure_functions());
120+
if (idx < 0 || idx >= closures_array.Length()) {
121+
return Function::null();
122+
}
123+
return Function::RawCast(closures_array.At(idx));
124+
}
125+
126+
FunctionPtr ClosureFunctionsCache::GetUniqueInnerClosure(
127+
const Function& outer) {
128+
auto thread = Thread::Current();
129+
auto zone = thread->zone();
130+
auto object_store = thread->isolate_group()->object_store();
131+
132+
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
133+
134+
const auto& closures =
135+
GrowableObjectArray::Handle(zone, object_store->closure_functions());
136+
auto& entry = Function::Handle(zone);
137+
for (intptr_t i = (closures.Length() - 1); i >= 0; i--) {
138+
entry ^= closures.At(i);
139+
if (entry.parent_function() == outer.raw()) {
140+
#if defined(DEBUG)
141+
auto& other = Function::Handle(zone);
142+
for (intptr_t j = i - 1; j >= 0; j--) {
143+
other ^= closures.At(j);
144+
ASSERT(other.parent_function() != outer.raw());
145+
}
146+
#endif
147+
return entry.raw();
148+
}
149+
}
150+
return Function::null();
151+
}
152+
153+
void ClosureFunctionsCache::ForAllClosureFunctions(
154+
std::function<bool(const Function&)> callback) {
155+
auto thread = Thread::Current();
156+
auto zone = thread->zone();
157+
auto object_store = thread->isolate_group()->object_store();
158+
159+
auto& current_data = Array::Handle(zone);
160+
auto& entry = Function::Handle(zone);
161+
162+
// NOTE: Inner functions may get added to the closures array while iterating -
163+
// we guarantee that any closure functions added on this thread by a
164+
// [callback] call will be visited as well.
165+
//
166+
// We avoid holding a lock while accessing the closures array, since often
167+
// times [callback] will do very heavy things (e.g. compiling the function).
168+
//
169+
// This means we can possibly miss a concurrently added closure function -
170+
// which the caller should be ok with (or it guarantees that this cannot
171+
// happen).
172+
const auto& closures =
173+
GrowableObjectArray::Handle(zone, object_store->closure_functions());
174+
175+
{
176+
// The empty read locker scope will implicitly issue an acquire memory
177+
// fence, which means any closure functions added so far will be visible and
178+
// iterated further down.
179+
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
180+
}
181+
182+
// We have an outer loop to ensure any new closure functions added by
183+
// [callback] will be iterated as well.
184+
intptr_t i = 0;
185+
while (true) {
186+
intptr_t current_length = closures.Length();
187+
if (i == current_length) break;
188+
189+
current_data = closures.data();
190+
if (current_data.Length() < current_length) {
191+
current_length = current_data.Length();
192+
}
193+
194+
for (; i < current_length; ++i) {
195+
entry ^= current_data.At(i);
196+
if (!callback(entry)) {
197+
return; // Stop iteration.
198+
}
199+
}
200+
}
201+
}
202+
203+
} // namespace dart
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
#ifndef RUNTIME_VM_CLOSURE_FUNCTIONS_CACHE_H_
6+
#define RUNTIME_VM_CLOSURE_FUNCTIONS_CACHE_H_
7+
8+
#include <functional>
9+
10+
#include "vm/allocation.h"
11+
#include "vm/token_position.h"
12+
13+
namespace dart {
14+
15+
class Class;
16+
class Function;
17+
class FunctionPtr;
18+
19+
// Implementation of cache for inner closure functions.
20+
//
21+
// This cache is populated lazily by the compiler: When compiling a function,
22+
// the flow graph builder will recursively traverse the kernel AST for the
23+
// function and any inner functions. This will cause the lazy-creation of inner
24+
// closure functions.
25+
//
26+
// The cache is currently implemented as O(n) lookup in a growable list.
27+
//
28+
// Parts of the VM have certain requirements that are maintained:
29+
//
30+
// * parent functions need to come before inner functions
31+
// * closure functions list can grow while iterating
32+
// * the index of closure function must be stable
33+
//
34+
// If the linear lookup turns out to be too expensive, the list of closures
35+
// could be maintained in a hash map, with the key being the token position of
36+
// the closure. There are almost no collisions with this simple hash value.
37+
// However, iterating over all closure functions becomes more difficult,
38+
// especially when the list/map changes while iterating over it (see
39+
// requirements above).
40+
class ClosureFunctionsCache : public AllStatic {
41+
public:
42+
static FunctionPtr LookupClosureFunction(const Class& owner,
43+
TokenPosition pos);
44+
static FunctionPtr LookupClosureFunctionLocked(const Class& owner,
45+
TokenPosition pos);
46+
47+
static FunctionPtr LookupClosureFunction(const Function& parent,
48+
TokenPosition token_pos);
49+
static FunctionPtr LookupClosureFunctionLocked(const Function& parent,
50+
TokenPosition token_pos);
51+
52+
static void AddClosureFunctionLocked(const Function& function);
53+
54+
static intptr_t FindClosureIndex(const Function& needle);
55+
static FunctionPtr ClosureFunctionFromIndex(intptr_t idx);
56+
57+
static FunctionPtr GetUniqueInnerClosure(const Function& outer);
58+
59+
// Visits all closure functions registered in the object store.
60+
//
61+
// Iterates in-order, thereby allowing new closures being added during the
62+
// iteration.
63+
//
64+
// The iteration continues until either [callback] returns `false` or all
65+
// closure functions have been visited.
66+
static void ForAllClosureFunctions(
67+
std::function<bool(const Function&)> callback);
68+
};
69+
70+
} // namespace dart
71+
72+
#endif // RUNTIME_VM_CLOSURE_FUNCTIONS_CACHE_H_

runtime/vm/compilation_trace.cc

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "vm/compilation_trace.h"
66

7+
#include "vm/closure_functions_cache.h"
78
#include "vm/compiler/jit/compiler.h"
89
#include "vm/globals.h"
910
#include "vm/log.h"
@@ -131,20 +132,21 @@ ObjectPtr CompilationTraceLoader::CompileTrace(uint8_t* buffer, intptr_t size) {
131132
}
132133
}
133134

134-
// Finally, compile closures in all compiled functions. Don't cache the
135-
// length since compiling may append to this list.
136-
const GrowableObjectArray& closure_functions = GrowableObjectArray::Handle(
137-
zone_, thread_->isolate_group()->object_store()->closure_functions());
138-
for (intptr_t i = 0; i < closure_functions.Length(); i++) {
139-
function_ ^= closure_functions.At(i);
140-
function2_ = function_.parent_function();
135+
// Finally, compile closures in all compiled functions. Note: We rely on the
136+
// fact that parent functions are visited before children.
137+
error_ = Object::null();
138+
auto& result = Object::Handle(zone_);
139+
ClosureFunctionsCache::ForAllClosureFunctions([&](const Function& func) {
140+
function2_ = func.parent_function();
141141
if (function2_.HasCode()) {
142-
error_ = CompileFunction(function_);
143-
if (error_.IsError()) {
144-
return error_.raw();
142+
result = CompileFunction(function_);
143+
if (result.IsError()) {
144+
error_ = result.raw();
145+
return false; // Stop iteration.
145146
}
146147
}
147-
}
148+
return true;
149+
});
148150

149151
return Object::null();
150152
}
@@ -965,19 +967,7 @@ FunctionPtr TypeFeedbackLoader::FindFunction(FunctionLayout::Kind kind,
965967
// functions in the serialized feedback, so the parent will have already
966968
// been unoptimized compilated and the child function created and added to
967969
// ObjectStore::closure_functions_.
968-
const GrowableObjectArray& closure_functions = GrowableObjectArray::Handle(
969-
zone_, thread_->isolate_group()->object_store()->closure_functions());
970-
bool found = false;
971-
for (intptr_t i = 0; i < closure_functions.Length(); i++) {
972-
func_ ^= closure_functions.At(i);
973-
if (func_.Owner() == cls_.raw() && func_.token_pos() == token_pos) {
974-
found = true;
975-
break;
976-
}
977-
}
978-
if (!found) {
979-
func_ = Function::null();
980-
}
970+
func_ = ClosureFunctionsCache::LookupClosureFunction(cls_, token_pos);
981971
} else {
982972
// This leaves unhandled:
983973
// - kInvokeFieldDispatcher (how to get a valid args descriptor?)

0 commit comments

Comments
 (0)