Skip to content

Commit 66b1fa9

Browse files
committed
Merge branch 'dev' into merging-master-into-dev
2 parents 2d3d545 + dfd0a0e commit 66b1fa9

10 files changed

+177
-4
lines changed

src/gc-interface.h

+25
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,26 @@ JL_DLLEXPORT void jl_gc_set_max_memory(uint64_t max_mem);
101101
JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection);
102102
// Returns whether the thread with `tid` is a collector thread
103103
JL_DLLEXPORT int gc_is_collector_thread(int tid) JL_NOTSAFEPOINT;
104+
// Pinning objects; Returns whether the object has been pinned by this call.
105+
JL_DLLEXPORT unsigned char jl_gc_pin_object(void* obj);
104106
// Returns which GC implementation is being used and possibly its version according to the list of supported GCs
105107
// NB: it should clearly identify the GC by including e.g. ‘stock’ or ‘mmtk’ as a substring.
106108
JL_DLLEXPORT const char* jl_gc_active_impl(void);
107109
// Sweep Julia's stack pools and mtarray buffers. Note that this function has been added to the interface as
108110
// each GC should implement it but it will most likely not be used by other code in the runtime.
109111
// It still needs to be annotated with JL_DLLEXPORT since it is called from Rust by MMTk.
110112
JL_DLLEXPORT void jl_gc_sweep_stack_pools_and_mtarraylist_buffers(jl_ptls_t ptls) JL_NOTSAFEPOINT;
113+
// Notifies the GC that the given thread is about to yield for a GC. ctx is the ucontext for the thread
114+
// if it is already fetched by the caller, otherwise it is NULL.
115+
JL_DLLEXPORT void jl_gc_notify_thread_yield(jl_ptls_t ptls, void* ctx);
116+
117+
// TODO: The preserve hook functions may be temporary. We should see the performance impact of the change.
118+
119+
// Runtime hook for gc preserve begin. The GC needs to make sure that the preserved objects and its children stay alive and won't move.
120+
JL_DLLEXPORT void jl_gc_preserve_begin_hook(int n, ...) JL_NOTSAFEPOINT;
121+
// Runtime hook for gc preserve end. The GC needs to make sure that the preserved objects and its children stay alive and won't move.
122+
JL_DLLEXPORT void jl_gc_preserve_end_hook(void) JL_NOTSAFEPOINT;
123+
111124

112125
// ========================================================================= //
113126
// Metrics
@@ -207,10 +220,22 @@ JL_DLLEXPORT void *jl_gc_perm_alloc(size_t sz, int zero, unsigned align,
207220
// the allocated object. All objects stored in fields of this object
208221
// must be either permanently allocated or have other roots.
209222
struct _jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT;
223+
// permanently allocates a symbol (jl_sym_t). The object needs to be word aligned,
224+
// and tagged with jl_sym_tag.
225+
// FIXME: Ideally we should merge this with jl_gc_permobj, as symbol is an object.
226+
// Currently there are a few differences between the two functions, and refactoring is needed.
227+
// 1. sz for this function includes the object header, and sz for jl_gc_permobj excludes the header size.
228+
// 2. align for this function is word align, and align for jl_gc_permobj depends on the allocation size.
229+
// 3. ty for this function is jl_symbol_tag << 4, and ty for jl_gc_permobj is a datatype pointer.
230+
struct _jl_value_t *jl_gc_permsymbol(size_t sz) JL_NOTSAFEPOINT;
210231
// This function notifies the GC about memory addresses that are set when loading the boot image.
211232
// The GC may use that information to, for instance, determine that such objects should
212233
// be treated as marked and belonged to the old generation in nursery collections.
213234
void jl_gc_notify_image_load(const char* img_data, size_t len);
235+
// This function notifies the GC about memory addresses that are set when allocating the boot image.
236+
// The GC may use that information to, for instance, determine that all objects in that chunk of memory should
237+
// be treated as marked and belonged to the old generation in nursery collections.
238+
void jl_gc_notify_image_alloc(const char* img_data, size_t len);
214239

215240
// ========================================================================= //
216241
// Runtime Write-Barriers

src/gc-mmtk.c

+33
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,24 @@ JL_DLLEXPORT void jl_gc_prepare_to_collect(void)
323323
errno = last_errno;
324324
}
325325

326+
JL_DLLEXPORT unsigned char jl_gc_pin_object(void* obj) {
327+
return mmtk_pin_object(obj);
328+
}
329+
330+
JL_DLLEXPORT void jl_gc_notify_thread_yield(jl_ptls_t ptls, void* ctx) {
331+
if (ctx == NULL) {
332+
// Save the context for the thread as it was running at the time of the call
333+
int r = getcontext(&ptls->gc_tls.ctx_at_the_time_gc_started);
334+
if (r == -1) {
335+
jl_safe_printf("Failed to save context for conservative scanning\n");
336+
abort();
337+
}
338+
return;
339+
}
340+
memcpy(&ptls->gc_tls.ctx_at_the_time_gc_started, ctx, sizeof(ucontext_t));
341+
}
342+
343+
326344
// ========================================================================= //
327345
// GC Statistics
328346
// ========================================================================= //
@@ -1042,6 +1060,16 @@ jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT
10421060
return jl_valueof(o);
10431061
}
10441062

1063+
jl_value_t *jl_gc_permsymbol(size_t sz) JL_NOTSAFEPOINT
1064+
{
1065+
jl_taggedvalue_t *tag = (jl_taggedvalue_t*)jl_gc_perm_alloc(sz, 0, sizeof(void*), 0);
1066+
jl_value_t *sym = jl_valueof(tag);
1067+
jl_ptls_t ptls = jl_current_task->ptls;
1068+
jl_set_typetagof(sym, jl_symbol_tag, 0); // We need to set symbol tag. The GC tag doesnt matter.
1069+
mmtk_immortal_post_alloc_fast(&ptls->gc_tls.mmtk_mutator, sym, sz);
1070+
return sym;
1071+
}
1072+
10451073
JL_DLLEXPORT void *jl_gc_managed_malloc(size_t sz)
10461074
{
10471075
jl_ptls_t ptls = jl_current_task->ptls;
@@ -1079,6 +1107,11 @@ void jl_gc_notify_image_load(const char* img_data, size_t len)
10791107
mmtk_set_vm_space((void*)img_data, len);
10801108
}
10811109

1110+
void jl_gc_notify_image_alloc(const char* img_data, size_t len)
1111+
{
1112+
mmtk_immortal_region_post_alloc((void*)img_data, len);
1113+
}
1114+
10821115
// ========================================================================= //
10831116
// Code specific to stock that is not supported by MMTk
10841117
// ========================================================================= //

src/gc-stock.c

+55
Original file line numberDiff line numberDiff line change
@@ -3458,6 +3458,11 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection)
34583458
gc_cblist_pre_gc, (collection));
34593459

34603460
if (!jl_atomic_load_acquire(&jl_gc_disable_counter)) {
3461+
// This thread will yield.
3462+
// jl_gc_notify_thread_yield does nothing for the stock GC at the point, but it may be non empty in the future,
3463+
// and this is a place where we should call jl_gc_notify_thread_yield.
3464+
// TODO: This call can be removed if requested.
3465+
jl_gc_notify_thread_yield(ptls, NULL);
34613466
JL_LOCK_NOGC(&finalizers_lock); // all the other threads are stopped, so this does not make sense, right? otherwise, failing that, this seems like plausibly a deadlock
34623467
#ifndef __clang_gcanalyzer__
34633468
if (_jl_gc_collect(ptls, collection)) {
@@ -3940,6 +3945,15 @@ jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT
39403945
return jl_valueof(o);
39413946
}
39423947

3948+
jl_value_t *jl_gc_permsymbol(size_t sz) JL_NOTSAFEPOINT
3949+
{
3950+
jl_taggedvalue_t *tag = (jl_taggedvalue_t*)jl_gc_perm_alloc(sz, 0, sizeof(void*), 0);
3951+
jl_value_t *sym = jl_valueof(tag);
3952+
// set to old marked so that we won't look at it in the GC or write barrier.
3953+
jl_set_typetagof(sym, jl_symbol_tag, GC_OLD_MARKED);
3954+
return sym;
3955+
}
3956+
39433957
JL_DLLEXPORT int jl_gc_enable_conservative_gc_support(void)
39443958
{
39453959
if (jl_is_initialized()) {
@@ -4076,6 +4090,47 @@ void jl_gc_notify_image_load(const char* img_data, size_t len)
40764090
// Do nothing
40774091
}
40784092

4093+
void jl_gc_notify_image_alloc(const char* img_data, size_t len)
4094+
{
4095+
// Do nothing
4096+
}
4097+
4098+
JL_DLLEXPORT unsigned char jl_gc_pin_object(void* obj) {
4099+
return 0;
4100+
}
4101+
4102+
// added for MMTk integration
4103+
4104+
JL_DLLEXPORT void jl_gc_wb1_noinline(const void *parent) JL_NOTSAFEPOINT
4105+
{
4106+
}
4107+
4108+
JL_DLLEXPORT void jl_gc_wb2_noinline(const void *parent, const void *ptr) JL_NOTSAFEPOINT
4109+
{
4110+
}
4111+
4112+
JL_DLLEXPORT void jl_gc_wb1_slow(const void *parent) JL_NOTSAFEPOINT
4113+
{
4114+
}
4115+
4116+
JL_DLLEXPORT void jl_gc_wb2_slow(const void *parent, const void* ptr) JL_NOTSAFEPOINT
4117+
{
4118+
}
4119+
4120+
JL_DLLEXPORT void jl_gc_preserve_begin_hook(int n, ...) JL_NOTSAFEPOINT
4121+
{
4122+
jl_unreachable();
4123+
}
4124+
4125+
JL_DLLEXPORT void jl_gc_preserve_end_hook(void) JL_NOTSAFEPOINT
4126+
{
4127+
jl_unreachable();
4128+
}
4129+
4130+
JL_DLLEXPORT void jl_gc_notify_thread_yield(jl_ptls_t ptls, void* ctx) {
4131+
// Do nothing before a thread yields
4132+
}
4133+
40794134
JL_DLLEXPORT const char* jl_gc_active_impl(void) {
40804135
return "Built with stock GC";
40814136
}

src/genericmemory.c

+7
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ JL_DLLEXPORT jl_genericmemory_t *jl_string_to_genericmemory(jl_value_t *str)
111111
m->length = jl_string_len(str);
112112
m->ptr = jl_string_data(str);
113113
jl_genericmemory_data_owner_field(m) = str;
114+
PTR_PIN(str);
114115
return m;
115116
}
116117

@@ -166,6 +167,7 @@ JL_DLLEXPORT jl_genericmemory_t *jl_ptr_to_genericmemory(jl_value_t *mtype, void
166167
m->length = nel;
167168
jl_genericmemory_data_owner_field(m) = own_buffer ? (jl_value_t*)m : NULL;
168169
if (own_buffer) {
170+
PTR_PIN(m);
169171
int isaligned = 0; // TODO: allow passing memalign'd buffers
170172
jl_gc_track_malloced_genericmemory(ct->ptls, m, isaligned);
171173
size_t allocated_bytes = memory_block_usable_size(data, isaligned);
@@ -235,6 +237,10 @@ JL_DLLEXPORT void jl_genericmemory_copyto(jl_genericmemory_t *dest, char* destda
235237
_Atomic(void*) * dest_p = (_Atomic(void*)*)destdata;
236238
_Atomic(void*) * src_p = (_Atomic(void*)*)srcdata;
237239
jl_value_t *owner = jl_genericmemory_owner(dest);
240+
// FIXME: The following should be a write barrier impl provided by the GC.
241+
#ifdef MMTK_GC
242+
jl_gc_wb(owner, NULL);
243+
#else
238244
if (__unlikely(jl_astaggedvalue(owner)->bits.gc == GC_OLD_MARKED)) {
239245
jl_value_t *src_owner = jl_genericmemory_owner(src);
240246
ssize_t done = 0;
@@ -265,6 +271,7 @@ JL_DLLEXPORT void jl_genericmemory_copyto(jl_genericmemory_t *dest, char* destda
265271
n -= done;
266272
}
267273
}
274+
#endif
268275
return memmove_refs(dest_p, src_p, n);
269276
}
270277
size_t elsz = layout->size;

src/interpreter.c

+2
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,7 @@ jl_value_t *NOINLINE jl_interpret_toplevel_thunk(jl_module_t *m, jl_code_info_t
891891
unsigned nroots = jl_source_nslots(src) + jl_source_nssavalues(src);
892892
JL_GC_PUSHFRAME(s, s->locals, nroots);
893893
jl_array_t *stmts = src->code;
894+
JL_GC_PUSH1(&stmts);
894895
assert(jl_typetagis(stmts, jl_array_any_type));
895896
s->src = src;
896897
s->module = m;
@@ -901,6 +902,7 @@ jl_value_t *NOINLINE jl_interpret_toplevel_thunk(jl_module_t *m, jl_code_info_t
901902
JL_GC_ENABLEFRAME(s);
902903
jl_value_t *r = eval_body(stmts, s, 0, 1);
903904
JL_GC_POP();
905+
JL_GC_POP();
904906
return r;
905907
}
906908

src/julia.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1179,7 +1179,7 @@ JL_DLLEXPORT void jl_free_stack(void *stkbuf, size_t bufsz);
11791179
JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref(jl_value_t *value);
11801180

11811181
// GC write barriers
1182-
1182+
#ifndef MMTK_GC
11831183
STATIC_INLINE void jl_gc_wb(const void *parent, const void *ptr) JL_NOTSAFEPOINT
11841184
{
11851185
// parent and ptr isa jl_value_t*

src/llvm-gc-interface-passes.h

+1
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ struct LateLowerGCFrame: private JuliaPassContext {
361361
void PlaceGCFrameReset(State &S, unsigned R, unsigned MinColorRoot, ArrayRef<int> Colors, Value *GCFrame, Instruction *InsertBefore);
362362
void PlaceRootsAndUpdateCalls(ArrayRef<int> Colors, int PreAssignedColors, State &S, std::map<Value *, std::pair<int, int>>);
363363
void CleanupWriteBarriers(Function &F, State *S, const SmallVector<CallInst*, 0> &WriteBarriers, bool *CFGModified);
364+
void CleanupGCPreserve(Function &F, CallInst *CI, Value *callee, Type *T_size);
364365
bool CleanupIR(Function &F, State *S, bool *CFGModified);
365366
void NoteUseChain(State &S, BBState &BBS, User *TheUser);
366367
SmallVector<int, 1> GetPHIRefinements(PHINode *phi, State &S);

src/llvm-late-gc-lowering-mmtk.cpp

+46
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,52 @@
22

33
#include "llvm-gc-interface-passes.h"
44

5+
void LateLowerGCFrame::CleanupGCPreserve(Function &F, CallInst *CI, Value *callee, Type *T_size) {
6+
if (callee == gc_preserve_begin_func) {
7+
// Initialize an IR builder.
8+
IRBuilder<> builder(CI);
9+
10+
builder.SetCurrentDebugLocation(CI->getDebugLoc());
11+
size_t nargs = 0;
12+
State S2(F);
13+
14+
std::vector<Value*> args;
15+
for (Use &U : CI->args()) {
16+
Value *V = U;
17+
if (isa<Constant>(V))
18+
continue;
19+
if (isa<PointerType>(V->getType())) {
20+
if (isSpecialPtr(V->getType())) {
21+
int Num = Number(S2, V);
22+
if (Num >= 0) {
23+
nargs++;
24+
Value *Val = GetPtrForNumber(S2, Num, CI);
25+
args.push_back(Val);
26+
}
27+
}
28+
} else {
29+
auto Nums = NumberAll(S2, V);
30+
for (int Num : Nums) {
31+
if (Num < 0)
32+
continue;
33+
Value *Val = GetPtrForNumber(S2, Num, CI);
34+
args.push_back(Val);
35+
nargs++;
36+
}
37+
}
38+
}
39+
args.insert(args.begin(), ConstantInt::get(T_size, nargs));
40+
41+
ArrayRef<Value*> args_llvm = ArrayRef<Value*>(args);
42+
builder.CreateCall(getOrDeclare(jl_well_known::GCPreserveBeginHook), args_llvm );
43+
} else if (callee == gc_preserve_end_func) {
44+
// Initialize an IR builder.
45+
IRBuilder<> builder(CI);
46+
builder.SetCurrentDebugLocation(CI->getDebugLoc());
47+
builder.CreateCall(getOrDeclare(jl_well_known::GCPreserveEndHook), {});
48+
}
49+
}
50+
551
Value* LateLowerGCFrame::lowerGCAllocBytesLate(CallInst *target, Function &F)
652
{
753
assert(target->arg_size() == 3);

src/llvm-late-gc-lowering-stock.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
#include "llvm-gc-interface-passes.h"
44

5+
void LateLowerGCFrame::CleanupGCPreserve(Function &F, CallInst *CI, Value *callee, Type *T_size) {
6+
// Do nothing for the stock GC
7+
}
8+
59
Value* LateLowerGCFrame::lowerGCAllocBytesLate(CallInst *target, Function &F)
610
{
711
// Do nothing for the stock GC

src/subtype.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ static void re_save_env(jl_stenv_t *e, jl_savedenv_t *se, int root)
274274
}
275275
else {
276276
roots = se->roots;
277-
nroots = se->gcframe.nroots >> 2;
277+
nroots = JL_GC_DECODE_NROOTS(se->gcframe.nroots);
278278
}
279279
}
280280
jl_varbinding_t *v = e->vars;
@@ -367,7 +367,7 @@ static void restore_env(jl_stenv_t *e, jl_savedenv_t *se, int root) JL_NOTSAFEPO
367367
}
368368
else {
369369
roots = se->roots;
370-
nroots = se->gcframe.nroots >> 2;
370+
nroots = JL_GC_DECODE_NROOTS(se->gcframe.nroots);
371371
}
372372
}
373373
jl_varbinding_t *v = e->vars;
@@ -4193,7 +4193,7 @@ static int merge_env(jl_stenv_t *e, jl_savedenv_t *me, jl_savedenv_t *se, int co
41934193
else {
41944194
saved = se->roots;
41954195
merged = me->roots;
4196-
nroots = se->gcframe.nroots >> 2;
4196+
nroots = JL_GC_DECODE_NROOTS(se->gcframe.nroots);
41974197
}
41984198
assert(nroots == current_env_length(e) * 3);
41994199
assert(nroots % 3 == 0);

0 commit comments

Comments
 (0)