From 9709741a5a30fd500c8134bec09ed32560f20992 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Fri, 11 Oct 2024 13:48:02 +1300 Subject: [PATCH] Pin more references and expose some globally rooted symbols (#64) This PR adds a few more pining for types in the native heap, and exposes globally rooted symbols so we can trace them in the MMTk binding. With this PR, we can run moving Immix without transitively pinning `global_roots_table`. See https://github.com/mmtk/mmtk-julia/pull/170. * Add `jl_gc_pin` * Use `jl_gc_pin` to pin `BigFloat` for MPFR * Rename `PTR_PIN` to `OBJ_PIN`. Additionally add `PTR_PIN` which handles cases for internal pointers. * Pin objects that are used as key in `jl_codectx_t.global_targets` * Pin objects that are stored in `jl_codectx_t.PhiNodes` * Pin objects that are referred to by `jl_cgval_t` * Disable GC before doing perm alloc in `jl_get_layout` (see https://github.com/mmtk/mmtk-julia/issues/172) * Pin weak references and the referee (see https://github.com/mmtk/mmtk-julia/issues/176) * Pin objects that are used in `libuv`'s handle. * Pin `jl_task_t.completion_future` (see https://github.com/mmtk/mmtk-julia/issues/179) * Pin all `jl_codeinst_t` objects. * Pin all `jl_method_instance_t` objects. * Pin all `jl_module_t` objects. * Pin all `jl_task_t` objects. * Expose some `JL_GLOBALLY_ROOTED` symbols, such as `newly_inferred`, `task_done_hook_func`. --- base/mpfr.jl | 1 + src/aotcompile.cpp | 1 + src/array.c | 6 +++--- src/ast.c | 2 +- src/builtins.c | 6 +++--- src/cgutils.cpp | 7 +++++-- src/codegen.cpp | 15 +++++++++++++++ src/datatype.c | 18 ++++++++++++++++-- src/gc-common.c | 5 +++++ src/gc.c | 5 +++++ src/gc.h | 2 ++ src/gf.c | 2 ++ src/jitlayers.h | 3 ++- src/jl_exported_funcs.inc | 1 + src/jl_uv.c | 2 ++ src/julia.h | 9 +++++++-- src/julia_internal.h | 18 +++++++++++++++--- src/method.c | 2 ++ src/mmtk-gc.c | 12 +++++++++++- src/module.c | 1 + src/staticdata_utils.c | 2 +- src/task.c | 12 +++++++++++- test/clangsa/MissingPinning.c | 20 ++++++++++---------- test/clangsa/MissingRoots.c | 16 ++++++++-------- 24 files changed, 130 insertions(+), 38 deletions(-) diff --git a/base/mpfr.jl b/base/mpfr.jl index 47c4c37e43f5d..75f1761f9841c 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -122,6 +122,7 @@ mutable struct BigFloat <: AbstractFloat nb = (nb + Core.sizeof(Limb) - 1) รท Core.sizeof(Limb) # align to number of Limb allocations required for this #d = Vector{Limb}(undef, nb) d = _string_n(nb * Core.sizeof(Limb)) + ccall(:jl_gc_pin, Cvoid, (Any,), d); # Pin the Julia object EXP_NAN = Clong(1) - Clong(typemax(Culong) >> 1) return _BigFloat(Clong(precision), one(Cint), EXP_NAN, d) # +NAN end diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 785398fa3ec33..f8139d21ac401 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -95,6 +95,7 @@ typedef struct { std::vector jl_sysimg_fvars; std::vector jl_sysimg_gvars; std::map> jl_fvar_map; + // This holds references to the heap. Need to be pinned. std::vector jl_value_to_llvm; std::vector jl_external_to_llvm; } jl_native_code_desc_t; diff --git a/src/array.c b/src/array.c index 9ed61a4ae144c..21702c44a354c 100644 --- a/src/array.c +++ b/src/array.c @@ -243,7 +243,7 @@ JL_DLLEXPORT jl_array_t *jl_reshape_array(jl_value_t *atype, jl_array_t *data, // introspect the object to update the a->data field. To avoid doing that and // making scan_object much more complex we simply enforce that both owner and // buffers are always pinned - PTR_PIN(owner); + OBJ_PIN(owner); a->flags.how = 3; a->data = data->data; a->flags.isshared = 1; @@ -296,7 +296,7 @@ JL_DLLEXPORT jl_array_t *jl_string_to_array(jl_value_t *str) // introspect the object to update the a->data field. To avoid doing that and // making scan_object much more complex we simply enforce that both owner and // buffers are always pinned - PTR_PIN(str); + OBJ_PIN(str); a->flags.how = 3; a->flags.isshared = 1; size_t l = jl_string_len(str); @@ -695,7 +695,7 @@ static int NOINLINE array_resize_buffer(jl_array_t *a, size_t newlen) // introspect the object to update the a->data field. To avoid doing that and // making scan_object much more complex we simply enforce that both owner and // buffers are always pinned - PTR_PIN(s); + OBJ_PIN(s); jl_array_data_owner(a) = s; jl_gc_wb(a, s); a->data = jl_string_data(s); diff --git a/src/ast.c b/src/ast.c index 028ea4d097172..c64f6686c03e8 100644 --- a/src/ast.c +++ b/src/ast.c @@ -722,7 +722,7 @@ static value_t julia_to_list2_noalloc(fl_context_t *fl_ctx, jl_value_t *a, jl_va static value_t julia_to_scm_(fl_context_t *fl_ctx, jl_value_t *v, int check_valid) { - PTR_PIN(v); + OBJ_PIN(v); value_t retval; if (julia_to_scm_noalloc1(fl_ctx, v, &retval)) return retval; diff --git a/src/builtins.c b/src/builtins.c index 157ad0257a95d..88a1e83da87a1 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -314,7 +314,7 @@ static uintptr_t type_object_id_(jl_value_t *v, jl_varidx_t *env) JL_NOTSAFEPOIN } // FIXME: Pinning objects that get hashed // until we implement address space hashing. - PTR_PIN(v); + OBJ_PIN(v); return inthash((uintptr_t)v); } if (tv == jl_uniontype_type) { @@ -364,7 +364,7 @@ static uintptr_t immut_id_(jl_datatype_t *dt, jl_value_t *v, uintptr_t h) JL_NOT // FIXME: Pinning objects that get hashed // until we implement address space hashing. - PTR_PIN(v); + PTR_PIN(v); // This has to be a pointer pin -- v could be an internal pointer // operate element-wise if there are unused bits inside, // otherwise just take the whole data block at once // a few select pointers (notably symbol) also have special hash values @@ -424,7 +424,7 @@ static uintptr_t NOINLINE jl_object_id__cold(jl_datatype_t *dt, jl_value_t *v) J if (dt->name->mutabl) { // FIXME: Pinning objects that get hashed // until we implement address space hashing. - PTR_PIN(v); + OBJ_PIN(v); return inthash((uintptr_t)v); } return immut_id_(dt, v, dt->hash); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index a302d3e9c1cf5..3d9502b0b01a7 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -295,6 +295,9 @@ static Value *julia_pgv(jl_codectx_t &ctx, const char *cname, void *addr) // emit a GlobalVariable for a jl_value_t named "cname" // store the name given so we can reuse it (facilitating merging later) // so first see if there already is a GlobalVariable for this address + + // This will be stored in the native heap. We need to pin it. + OBJ_PIN(addr); GlobalVariable* &gv = ctx.global_targets[addr]; Module *M = jl_Module; StringRef localname; @@ -474,7 +477,7 @@ static Value *literal_pointer_val(jl_codectx_t &ctx, jl_value_t *p) if (!ctx.emission_context.imaging) // literal_static_pointer_val will pin p. return literal_static_pointer_val(p, ctx.types().T_pjlvalue); - PTR_PIN(p); + OBJ_PIN(p); Value *pgv = literal_pointer_val_slot(ctx, p); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); return ai.decorateInst(maybe_mark_load_dereferenceable( @@ -491,7 +494,7 @@ static Value *literal_pointer_val(jl_codectx_t &ctx, jl_binding_t *p) if (!ctx.emission_context.imaging) // literal_static_pointer_val will pin p. return literal_static_pointer_val(p, ctx.types().T_pjlvalue); - PTR_PIN(p); + OBJ_PIN(p); // bindings are prefixed with jl_bnd# Value *pgv = julia_pgv(ctx, "jl_bnd#", p->name, p->owner, p); jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_const); diff --git a/src/codegen.cpp b/src/codegen.cpp index a4773acb3fbea..ddc2d20ed9aa0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1441,6 +1441,8 @@ struct jl_cgval_t { promotion_point(nullptr), promotion_ssa(-1) { + // jl_cgval_t could be in the native heap. We have to pin the object references in it. + OBJ_PIN(typ); assert(TIndex == NULL || TIndex->getType() == getInt8Ty(TIndex->getContext())); } jl_cgval_t(Value *Vptr, bool isboxed, jl_value_t *typ, Value *tindex, MDNode *tbaa) : // general pointer constructor @@ -1457,6 +1459,8 @@ struct jl_cgval_t { { if (Vboxed) assert(Vboxed->getType() == JuliaType::get_prjlvalue_ty(Vboxed->getContext())); + // jl_cgval_t could be in the native heap. We have to pin the object references in it. + OBJ_PIN(typ); assert(tbaa != NULL); assert(!(isboxed && TIndex != NULL)); assert(TIndex == NULL || TIndex->getType() == getInt8Ty(TIndex->getContext())); @@ -1474,6 +1478,9 @@ struct jl_cgval_t { promotion_point(nullptr), promotion_ssa(-1) { + // jl_cgval_t could be in the native heap. We have to pin the object references in it. + OBJ_PIN(typ); + OBJ_PIN(constant); assert(jl_is_datatype(typ)); assert(constant); } @@ -1489,6 +1496,9 @@ struct jl_cgval_t { promotion_point(v.promotion_point), promotion_ssa(v.promotion_ssa) { + // jl_cgval_t could be in the native heap. We have to pin the object references in it. + OBJ_PIN(typ); + OBJ_PIN(constant); if (Vboxed) assert(Vboxed->getType() == JuliaType::get_prjlvalue_ty(Vboxed->getContext())); // this constructor expects we had a badly or equivalently typed version @@ -1551,6 +1561,7 @@ class jl_codectx_t { IRBuilder<> builder; jl_codegen_params_t &emission_context; llvm::MapVector call_targets; + // This map may hold Julia obj ref in the native heap. We need to pin the void*. std::map &global_targets; std::map, GlobalVariable*> &external_calls; Function *f = NULL; @@ -1558,6 +1569,7 @@ class jl_codectx_t { std::vector slots; std::map phic_slots; std::vector SAvalues; + // The vector holds reference to Julia obj ref. We need to pin jl_value_t*. std::vector> PhiNodes; std::vector ssavalue_assigned; std::vector ssavalue_usecount; @@ -4851,6 +4863,7 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) maybe_bitcast(ctx, decay_derived(ctx, phi), getInt8PtrTy(ctx.builder.getContext()))); jl_cgval_t val = mark_julia_slot(ptr, phiType, Tindex_phi, ctx.tbaa().tbaa_stack); // XXX: this TBAA is wrong for ptr_phi val.Vboxed = ptr_phi; + OBJ_PIN(r); ctx.PhiNodes.push_back(std::make_tuple(val, BB, dest, ptr_phi, r)); ctx.SAvalues.at(idx) = val; ctx.ssavalue_assigned.at(idx) = true; @@ -4860,6 +4873,7 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) PHINode *Tindex_phi = PHINode::Create(getInt8Ty(ctx.builder.getContext()), jl_array_len(edges), "tindex_phi"); BB->getInstList().insert(InsertPt, Tindex_phi); jl_cgval_t val = mark_julia_slot(NULL, phiType, Tindex_phi, ctx.tbaa().tbaa_stack); + OBJ_PIN(r); ctx.PhiNodes.push_back(std::make_tuple(val, BB, dest, (PHINode*)NULL, r)); ctx.SAvalues.at(idx) = val; ctx.ssavalue_assigned.at(idx) = true; @@ -4895,6 +4909,7 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) BB->getInstList().insert(InsertPt, value_phi); slot = mark_julia_type(ctx, value_phi, isboxed, phiType); } + OBJ_PIN(r); ctx.PhiNodes.push_back(std::make_tuple(slot, BB, dest, value_phi, r)); ctx.SAvalues.at(idx) = slot; ctx.ssavalue_assigned.at(idx) = true; diff --git a/src/datatype.c b/src/datatype.c index 8667d245462ad..e5c01d2aa44c8 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -67,7 +67,7 @@ JL_DLLEXPORT jl_typename_t *jl_new_typename_in(jl_sym_t *name, jl_module_t *modu jl_typename_type); // Typenames should be pinned since they are used as metadata, and are // read during scan_object - PTR_PIN(tn); + OBJ_PIN(tn); tn->name = name; tn->module = module; tn->wrapper = NULL; @@ -101,7 +101,7 @@ jl_datatype_t *jl_new_uninitialized_datatype(void) jl_datatype_t *t = (jl_datatype_t*)jl_gc_alloc(ct->ptls, sizeof(jl_datatype_t), jl_datatype_type); // Types should be pinned since they are used as metadata, and are // read during scan_object - PTR_PIN(t); + OBJ_PIN(t); t->hash = 0; t->hasfreetypevars = 0; t->isdispatchtuple = 0; @@ -643,7 +643,14 @@ void jl_compute_field_offsets(jl_datatype_t *st) } } assert(ptr_i == npointers); + // Work around mmtk-julia #172: Julia assumes perm alloc won't trigger GC. +#ifdef MMTK_GC + int en = jl_gc_enable(0); +#endif st->layout = jl_get_layout(sz, nfields, npointers, alignm, haspadding, desc, pointers); +#ifdef MMTK_GC + jl_gc_enable(en); +#endif if (should_malloc) { free(desc); if (npointers) @@ -801,7 +808,14 @@ JL_DLLEXPORT jl_datatype_t *jl_new_primitivetype(jl_value_t *name, jl_module_t * // and we easily have a free bit for it in the DataType flags bt->isprimitivetype = 1; bt->isbitstype = (parameters == jl_emptysvec); + // Work around mmtk-julia #172: Julia assumes perm alloc won't trigger GC. +#ifdef MMTK_GC + int en = jl_gc_enable(0); +#endif bt->layout = jl_get_layout(nbytes, 0, 0, alignm, 0, NULL, NULL); +#ifdef MMTK_GC + jl_gc_enable(en); +#endif bt->instance = NULL; return bt; } diff --git a/src/gc-common.c b/src/gc-common.c index b10d003b19d65..589172d95bdf6 100644 --- a/src/gc-common.c +++ b/src/gc-common.c @@ -859,6 +859,11 @@ JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref_th(jl_ptls_t ptls, jl_weakref_t *wr = (jl_weakref_t*)jl_gc_alloc(ptls, sizeof(void*), jl_weakref_type); wr->value = value; // NOTE: wb not needed here + // We need this pin. The weak refs are stored in an arraylist. + OBJ_PIN(wr); + // This pin is not necessary. We can deal with object forwarding in weak ref processing. + // Pinning is simpler. + OBJ_PIN(value); small_arraylist_push(&ptls->heap.weak_refs, wr); return wr; } diff --git a/src/gc.c b/src/gc.c index a42a1354ffc20..1785d970df2e3 100644 --- a/src/gc.c +++ b/src/gc.c @@ -3619,6 +3619,11 @@ JL_DLLEXPORT void jl_gc_wb2_slow(const void *parent, const void* ptr) JL_NOTSAFE jl_unreachable(); } +JL_DLLEXPORT void jl_gc_pin(jl_value_t* obj) +{ + // Nothing needs to be done +} + #ifdef __cplusplus } #endif diff --git a/src/gc.h b/src/gc.h index c35faa3123b48..cad3e63fc8d41 100644 --- a/src/gc.h +++ b/src/gc.h @@ -744,6 +744,8 @@ void gc_stats_big_obj(void); #define gc_stats_big_obj() #endif +JL_DLLEXPORT void jl_gc_pin(jl_value_t* obj); + // For debugging void gc_count_pool(void); diff --git a/src/gf.c b/src/gf.c index 33849dd5aa387..6aece29fb8276 100644 --- a/src/gf.c +++ b/src/gf.c @@ -430,6 +430,8 @@ JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst( jl_atomic_store_relaxed(&codeinst->purity_bits, effects); codeinst->argescapes = argescapes; codeinst->relocatability = relocatability; + // Pin codeinst, as they are referenced by vectors and maps in _jl_codegen_params_t + OBJ_PIN(codeinst); return codeinst; } diff --git a/src/jitlayers.h b/src/jitlayers.h index c1061e727db79..5a8850c730a7c 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -175,6 +175,7 @@ typedef struct _jl_codegen_params_t { typedef StringMap SymMapGV; // outputs std::vector> workqueue; + // This may hold references to Julia heap. void* needs to be pinned. std::map globals; std::map, GlobalVariable*> external_fns; std::map ditypes; @@ -242,7 +243,7 @@ void add_named_global(StringRef name, void *addr); static inline Constant *literal_static_pointer_val(const void *p, Type *T) { - PTR_PIN((void*)p); + PTR_PIN((void*)p); // This may point to non-mmtk heap memory. // this function will emit a static pointer into the generated code // the generated code will only be valid during the current session, // and thus, this should typically be avoided in new API's diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 292c71ab54b2e..46dfdf8310dae 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -210,6 +210,7 @@ XX(jl_gc_set_max_memory) \ XX(jl_gc_sync_total_bytes) \ XX(jl_gc_total_hrtime) \ + XX(jl_gc_pin) \ XX(jl_gdblookup) \ XX(jl_generating_output) \ XX(jl_generic_function_def) \ diff --git a/src/jl_uv.c b/src/jl_uv.c index 97a6602c19d7a..664b18bd53873 100644 --- a/src/jl_uv.c +++ b/src/jl_uv.c @@ -275,11 +275,13 @@ JL_DLLEXPORT void jl_forceclose_uv(uv_handle_t *handle) JL_DLLEXPORT void jl_uv_associate_julia_struct(uv_handle_t *handle, jl_value_t *data) { + OBJ_PIN(data); handle->data = data; } JL_DLLEXPORT void jl_uv_disassociate_julia_struct(uv_handle_t *handle) { + // TODO: unpin here -- we need to implement pin count before we can unpin objects. handle->data = NULL; } diff --git a/src/julia.h b/src/julia.h index 3027591c5be7a..fa72793975592 100644 --- a/src/julia.h +++ b/src/julia.h @@ -27,6 +27,10 @@ extern "C" { #endif extern int mmtk_object_is_managed_by_mmtk(void* addr); +// Pointers include off-heap pointers, internal pointers, and normal object reference +extern unsigned char mmtk_pin_pointer(void* ptr); +extern unsigned char mmtk_unpin_pointer(void* ptr); +// Object can only be in-heap object references. extern unsigned char mmtk_pin_object(void* obj); extern unsigned char mmtk_unpin_object(void* obj); #ifdef __clang_gcanalyzer__ @@ -36,8 +40,9 @@ extern void PTRHASH_UNPIN(void* key) JL_NOTSAFEPOINT; // FIXME: Pinning objects that get hashed in the ptrhash table // until we implement address space hashing. #ifdef MMTK_GC -#define PTRHASH_PIN(key) mmtk_pin_object(key); -#define PTRHASH_UNPIN(key) mmtk_unpin_object(key); +// Can we use pin objects? +#define PTRHASH_PIN(key) if (key) mmtk_pin_pointer(key); +#define PTRHASH_UNPIN(key) if (key) mmtk_unpin_pointer(key); #else #define PTRHASH_PIN(key) #define PTRHASH_UNPIN(key) diff --git a/src/julia_internal.h b/src/julia_internal.h index 19199928a6e11..c0f66b71771ba 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -8,20 +8,32 @@ extern "C" { #endif extern int mmtk_object_is_managed_by_mmtk(void* addr); +// Pointers include off-heap pointers, internal pointers, and normal object reference +extern unsigned char mmtk_pin_pointer(void* ptr); +extern unsigned char mmtk_unpin_pointer(void* ptr); +extern unsigned char mmtk_is_pointer_pinned(void* ptr); +// Object can only be in-heap object references. extern unsigned char mmtk_pin_object(void* obj); extern unsigned char mmtk_unpin_object(void* obj); +extern unsigned char mmtk_is_object_pinned(void* obj); #ifdef __clang_gcanalyzer__ +extern void OBJ_PIN(void* key) JL_NOTSAFEPOINT; +extern void OBJ_UNPIN(void* key) JL_NOTSAFEPOINT; extern void PTR_PIN(void* key) JL_NOTSAFEPOINT; extern void PTR_UNPIN(void* key) JL_NOTSAFEPOINT; #else #ifdef MMTK_GC -#define PTR_PIN(key) mmtk_pin_object(key); -#define PTR_UNPIN(key) mmtk_unpin_object(key); +#define PTR_PIN(key) if (key) mmtk_pin_pointer(key); +#define PTR_UNPIN(key) if (key) mmtk_unpin_pointer(key); +#define OBJ_PIN(key) if (key) mmtk_pin_object(key); +#define OBJ_UNPIN(key) if (key) mmtk_unpin_object(key); #else #define PTR_PIN(key) #define PTR_UNPIN(key) +#define OBJ_PIN(key) +#define OBJ_UNPIN(key) #endif #endif @@ -588,7 +600,7 @@ STATIC_INLINE jl_gc_tracked_buffer_t *jl_gc_alloc_buf(jl_ptls_t ptls, size_t sz) // introspect the object to update the a->data field. To avoid doing that and // making scan_object much more complex we simply enforce that both owner and // buffers are always pinned - PTR_PIN(buf); + OBJ_PIN(buf); return buf; } diff --git a/src/method.c b/src/method.c index 4e48ef9122d79..a573dc36977f3 100644 --- a/src/method.c +++ b/src/method.c @@ -449,6 +449,8 @@ JL_DLLEXPORT jl_method_instance_t *jl_new_method_instance_uninit(void) li->inInference = 0; li->cache_with_orig = 0; li->precompiled = 0; + // jl_method_instance_t needs to be pinned, as it is referenced in a map in JITDebugInfoRegistry + OBJ_PIN(li); return li; } diff --git a/src/mmtk-gc.c b/src/mmtk-gc.c index d18e61db819fc..491a9ba4790db 100644 --- a/src/mmtk-gc.c +++ b/src/mmtk-gc.c @@ -29,6 +29,9 @@ JL_DLLEXPORT void jl_gc_set_cb_notify_external_alloc(jl_gc_cb_notify_external_al JL_DLLEXPORT void jl_gc_set_cb_notify_external_free(jl_gc_cb_notify_external_free_t cb, int enable) { } +JL_DLLEXPORT void jl_gc_set_cb_notify_gc_pressure(jl_gc_cb_notify_gc_pressure_t cb, int enable) +{ +} JL_DLLEXPORT int64_t jl_gc_pool_live_bytes(void) { mmtk_unreachable(); @@ -489,7 +492,7 @@ jl_value_t *jl_gc_realloc_string(jl_value_t *s, size_t sz) size_t len = jl_string_len(s); jl_value_t *snew = jl_alloc_string(sz); memcpy(jl_string_data(snew), jl_string_data(s), sz <= len ? sz : len); - if(mmtk_is_pinned(s)) { + if(mmtk_is_object_pinned(s)) { // if the source string was pinned, we also pin the new one mmtk_pin_object(snew); } @@ -676,6 +679,13 @@ void jl_gc_notify_image_alloc(char* img_data, size_t len) mmtk_immortal_region_post_alloc((void*)img_data, len); } +JL_DLLEXPORT void jl_gc_pin(jl_value_t* obj) +{ + if (obj) { + mmtk_pin_object(obj); + } +} + #ifdef __cplusplus } #endif diff --git a/src/module.c b/src/module.c index 605bcd3c2b773..3bc10a2202a7f 100644 --- a/src/module.c +++ b/src/module.c @@ -47,6 +47,7 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, uint8_t default_names) } jl_module_export(m, name); JL_GC_POP(); + OBJ_PIN(m); // modules are referenced in jl_current_modules (htable). They cannot move. return m; } diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 0333074b57571..e606ac5f26201 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -83,7 +83,7 @@ static uint64_t jl_worklist_key(jl_array_t *worklist) JL_NOTSAFEPOINT return 0; } -static jl_array_t *newly_inferred JL_GLOBALLY_ROOTED /*FIXME*/; +jl_array_t *newly_inferred JL_GLOBALLY_ROOTED /*FIXME*/; // Mutex for newly_inferred static jl_mutex_t newly_inferred_mutex; diff --git a/src/task.c b/src/task.c index 7093b8625b5c4..65e29470d95f5 100644 --- a/src/task.c +++ b/src/task.c @@ -288,7 +288,7 @@ JL_NO_ASAN static void restore_stack2(jl_task_t *t, jl_ptls_t ptls, jl_task_t *l #endif /* Rooted by the base module */ -static _Atomic(jl_function_t*) task_done_hook_func JL_GLOBALLY_ROOTED = NULL; +_Atomic(jl_function_t*) task_done_hook_func JL_GLOBALLY_ROOTED = NULL; void JL_NORETURN jl_finish_task(jl_task_t *t) { @@ -893,6 +893,8 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion { jl_task_t *ct = jl_current_task; jl_task_t *t = (jl_task_t*)jl_gc_alloc(ct->ptls, sizeof(jl_task_t), jl_task_type); + // Task cannot be moved, as jl_mutex_t (as globals) references tasks + OBJ_PIN(t); JL_PROBE_RT_NEW_TASK(ct, t); t->copy_stack = 0; if (ssize == 0) { @@ -922,6 +924,12 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion t->start = start; t->result = jl_nothing; t->donenotify = completion_future; + // completion_future is a GenericCondition with SpinLock. + // I am not sure why we have to pin this. But, if we don't pin it, + // it may get moved, and we still use the invalid old reference somehow. + // See https://github.com/mmtk/mmtk-julia/issues/179. + // TODO: We should understand where we get the invalid reference from. + OBJ_PIN(completion_future); jl_atomic_store_relaxed(&t->_isexception, 0); // Inherit logger state from parent task t->logstate = ct->logstate; @@ -1491,6 +1499,8 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) if (jl_nothing == NULL) // make a placeholder jl_nothing = jl_gc_permobj(0, jl_nothing_type); jl_task_t *ct = (jl_task_t*)jl_gc_alloc(ptls, sizeof(jl_task_t), jl_task_type); + // Task cannot be moved, as jl_mutex_t (as globals) references tasks + OBJ_PIN(ct); memset(ct, 0, sizeof(jl_task_t)); void *stack = stack_lo; size_t ssize = (char*)stack_hi - (char*)stack_lo; diff --git a/test/clangsa/MissingPinning.c b/test/clangsa/MissingPinning.c index 2fa0a814a2b94..c574d7677677f 100644 --- a/test/clangsa/MissingPinning.c +++ b/test/clangsa/MissingPinning.c @@ -23,7 +23,7 @@ int allow_unpinned() { void pinned_argument() { jl_svec_t *val = jl_svec1(NULL); JL_GC_PROMISE_ROOTED(val); - PTR_PIN(val); + OBJ_PIN(val); look_at_value((jl_value_t*) val); PTR_UNPIN(val); } @@ -40,7 +40,7 @@ void pin_after_safepoint() { jl_svec_t *val = jl_svec1(NULL); JL_GC_PROMISE_ROOTED(val); jl_gc_safepoint(); - PTR_PIN(val); // expected-warning{{Attempt to PIN a value that is already moved}} + OBJ_PIN(val); // expected-warning{{Attempt to PIN a value that is already moved}} // expected-note@-1{{Attempt to PIN a value that is already moved}} look_at_value((jl_value_t*) val); } @@ -48,7 +48,7 @@ void pin_after_safepoint() { void proper_pin_before_safepoint() { jl_svec_t *val = jl_svec1(NULL); JL_GC_PROMISE_ROOTED(val); - PTR_PIN(val); + OBJ_PIN(val); jl_gc_safepoint(); look_at_value((jl_value_t*) val); PTR_UNPIN(val); @@ -87,7 +87,7 @@ void pointer_to_pointer2(jl_value_t* u, jl_value_t **v) { extern jl_value_t *first_array_elem(jl_array_t *a JL_PROPAGATES_ROOT); void root_propagation(jl_expr_t *expr) { - PTR_PIN(expr->args); + OBJ_PIN(expr->args); jl_value_t *val = first_array_elem(expr->args); // expected-note{{Started tracking value here}} PTR_UNPIN(expr->args); jl_gc_safepoint(); @@ -98,13 +98,13 @@ void root_propagation(jl_expr_t *expr) { void derive_ptr_alias(jl_method_instance_t *mi) { jl_value_t* a = mi->specTypes; jl_value_t* b = mi->specTypes; - PTR_PIN(a); + OBJ_PIN(a); look_at_value(b); PTR_UNPIN(a); } void derive_ptr_alias2(jl_method_instance_t *mi) { - PTR_PIN(mi->specTypes); + OBJ_PIN(mi->specTypes); look_at_value(mi->specTypes); PTR_UNPIN(mi->specTypes); } @@ -113,13 +113,13 @@ void derive_ptr_alias2(jl_method_instance_t *mi) { // It pins the first return value, but cannot see the second return value is an alias of the first. // However, we could rewrite the code so the checker can check it. // void mtable(jl_value_t *f) { -// PTR_PIN((jl_value_t*)jl_gf_mtable(f)); +// OBJ_PIN((jl_value_t*)jl_gf_mtable(f)); // look_at_value((jl_value_t*)jl_gf_mtable(f)); // } void mtable(jl_value_t *f) { jl_value_t* mtable = (jl_value_t*)jl_gf_mtable(f); - PTR_PIN(mtable); + OBJ_PIN(mtable); look_at_value(mtable); PTR_UNPIN(mtable); } @@ -152,8 +152,8 @@ void rebind_tpin(jl_method_instance_t *mi, size_t world) { jl_value_t *ci = jl_rettype_inferred(mi, world, world); jl_code_instance_t *codeinst = (ci == jl_nothing ? NULL : (jl_code_instance_t*)ci); if (codeinst) { - PTR_PIN(mi->def.method); - PTR_PIN(codeinst); + OBJ_PIN(mi->def.method); + OBJ_PIN(codeinst); src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred); src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src); PTR_UNPIN(codeinst); diff --git a/test/clangsa/MissingRoots.c b/test/clangsa/MissingRoots.c index 87d234096bee4..e207374988e8f 100644 --- a/test/clangsa/MissingRoots.c +++ b/test/clangsa/MissingRoots.c @@ -183,10 +183,10 @@ void globally_rooted() { extern jl_value_t *first_array_elem(jl_array_t *a JL_PROPAGATES_ROOT); void root_propagation(jl_expr_t *expr) { - PTR_PIN(expr->args); + OBJ_PIN(expr->args); jl_value_t *val = first_array_elem(expr->args); PTR_UNPIN(expr->args); - PTR_PIN(val); + OBJ_PIN(val); jl_gc_safepoint(); look_at_value(val); PTR_UNPIN(val); @@ -206,7 +206,7 @@ void argument_propagation(jl_value_t *a) { void arg_array(jl_value_t **args) { jl_gc_safepoint(); jl_value_t *val = args[1]; - PTR_PIN(val); + OBJ_PIN(val); look_at_value(val); PTR_UNPIN(val); jl_value_t *val2 = NULL; @@ -267,7 +267,7 @@ void nonconst_loads(jl_svec_t *v) size_t i = jl_svec_len(v); jl_method_instance_t **data = (jl_method_instance_t**)jl_svec_data(v); jl_method_instance_t *mi = data[i]; - PTR_PIN(mi->specTypes); + OBJ_PIN(mi->specTypes); look_at_value(mi->specTypes); PTR_UNPIN(mi->specTypes); } @@ -288,7 +288,7 @@ static inline void look_at_value2(jl_value_t *v) { } void mtable(jl_value_t *f) { jl_value_t* mtable = (jl_value_t*)jl_gf_mtable(f); - PTR_PIN(mtable); + OBJ_PIN(mtable); look_at_value2(mtable); jl_value_t *val = NULL; JL_GC_PUSH1(&val); @@ -305,7 +305,7 @@ void mtable2(jl_value_t **v) { void tparam0(jl_value_t *atype) { jl_value_t *param0 = jl_tparam0(atype); - PTR_PIN(param0); + OBJ_PIN(param0); look_at_value(param0); PTR_UNPIN(param0); } @@ -344,11 +344,11 @@ void module_member(jl_module_t *m) { for(int i=(int)m->usings.len-1; i >= 0; --i) { jl_module_t *imp = propagation(m); - PTR_PIN(imp); + OBJ_PIN(imp); jl_gc_safepoint(); look_at_value((jl_value_t*)imp); jl_module_t *prop = propagation(imp); - PTR_PIN(prop); + OBJ_PIN(prop); look_at_value((jl_value_t*)prop); PTR_UNPIN(prop); PTR_UNPIN(imp);