Skip to content

Commit 21d2de8

Browse files
committed
Workaround possible GC dead loop
This is necessary before the gc safepoint/transition support in codegen.
1 parent 4a13500 commit 21d2de8

File tree

9 files changed

+124
-60
lines changed

9 files changed

+124
-60
lines changed

base/atomics.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ for op in [:+, :-, :max, :min]
186186
cmp = old
187187
old = atomic_cas!(var, cmp, new)
188188
reinterpret(IT, old) == reinterpret(IT, cmp) && return new
189+
# Temporary solution before we have gc transition support in codegen.
190+
ccall(:jl_gc_safepoint, Void, ())
189191
end
190192
end
191193
end

base/locks.jl

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,7 @@ function lock!(l::TatasLock)
3232
end
3333
ccall(:jl_cpu_pause, Void, ())
3434
# Temporary solution before we have gc transition support in codegen.
35-
# This could mess up gc state when we add codegen support.
36-
# Use these as a safe point
37-
gc_state = ccall(:jl_gc_safe_enter, Int8, ())
38-
ccall(:jl_gc_safe_leave, Void, (Int8,), gc_state)
35+
ccall(:jl_gc_safepoint, Void, ())
3936
end
4037
end
4138

@@ -76,10 +73,7 @@ function lock!(l::RecursiveTatasLock)
7673
end
7774
ccall(:jl_cpu_pause, Void, ())
7875
# Temporary solution before we have gc transition support in codegen.
79-
# This could mess up gc state when we add codegen support.
80-
# Use these as a safe point
81-
gc_state = ccall(:jl_gc_safe_enter, Int8, ())
82-
ccall(:jl_gc_safe_leave, Void, (Int8,), gc_state)
76+
ccall(:jl_gc_safepoint, Void, ())
8377
end
8478
end
8579

src/ccall.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,29 @@ static jl_cgval_t emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx)
10841084
JL_GC_POP();
10851085
return ghostValue(jl_void_type);
10861086
}
1087+
if (fptr == &jl_gc_safepoint ||
1088+
((!f_lib || (intptr_t)f_lib == 2) && f_name &&
1089+
strcmp(f_name, "jl_gc_safepoint") == 0)) {
1090+
assert(lrt == T_void);
1091+
assert(!isVa);
1092+
assert(nargt == 0);
1093+
JL_GC_POP();
1094+
#ifdef JULIA_ENABLE_THREADING
1095+
builder.CreateFence(SequentiallyConsistent, SingleThread);
1096+
Value *addr;
1097+
if (imaging_mode) {
1098+
assert(ctx->signalPage);
1099+
addr = ctx->signalPage;
1100+
}
1101+
else {
1102+
addr = builder.CreateIntToPtr(
1103+
ConstantInt::get(T_size, (uintptr_t)jl_gc_signal_page), T_pint8);
1104+
}
1105+
builder.CreateLoad(addr, true);
1106+
builder.CreateFence(SequentiallyConsistent, SingleThread);
1107+
#endif
1108+
return ghostValue(jl_void_type);
1109+
}
10871110
if (fptr == (void(*)(void))&jl_is_leaf_type ||
10881111
((f_lib==NULL || (intptr_t)f_lib==2)
10891112
&& f_name && !strcmp(f_name, "jl_is_leaf_type"))) {

src/codegen.cpp

Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,8 @@ static GlobalVariable *jltls_states_var;
337337
// Imaging mode only
338338
static GlobalVariable *jltls_states_func_ptr = NULL;
339339
static size_t jltls_states_func_idx = 0;
340+
static GlobalVariable *jl_gc_signal_page_ptr = NULL;
341+
static size_t jl_gc_signal_page_idx = 0;
340342
#endif
341343

342344
// important functions
@@ -567,6 +569,9 @@ typedef struct {
567569
std::vector<bool> inbounds;
568570

569571
CallInst *ptlsStates;
572+
#ifdef JULIA_ENABLE_THREADING
573+
Value *signalPage;
574+
#endif
570575

571576
llvm::DIBuilder *dbuilder;
572577
bool debug_enabled;
@@ -3490,6 +3495,12 @@ static void allocate_gc_frame(BasicBlock *b0, jl_codectx_t *ctx)
34903495
{
34913496
// allocate a placeholder gc instruction
34923497
ctx->ptlsStates = builder.CreateCall(prepare_call(jltls_states_func));
3498+
#ifdef JULIA_ENABLE_THREADING
3499+
if (imaging_mode) {
3500+
ctx->signalPage =
3501+
tbaa_decorate(tbaa_const, builder.CreateLoad(jl_declare_global(jl_Module, jl_gc_signal_page_ptr)));
3502+
}
3503+
#endif
34933504
}
34943505

34953506
void jl_codegen_finalize_temp_arg(CallInst *ptlsStates, Type *T_pjlvalue);
@@ -3516,35 +3527,16 @@ static void finalize_gc_frame(Function *F)
35163527

35173528
jl_codegen_finalize_temp_arg(ptlsStates, T_pjlvalue);
35183529

3519-
GlobalVariable *oldGV = NULL;
3520-
#ifdef JULIA_ENABLE_THREADING
3521-
if (imaging_mode)
3522-
oldGV = jltls_states_func_ptr;
3523-
#else
3524-
oldGV = jltls_states_var;
3525-
#endif
3526-
3527-
GlobalVariable *GV = NULL;
3528-
if (oldGV) {
3529-
GV = M->getGlobalVariable(oldGV->getName(), true /* AllowLocal */);
3530-
if (GV == NULL) {
3531-
GV = new GlobalVariable(*M, oldGV->getType()->getElementType(),
3532-
oldGV->isConstant(),
3533-
GlobalValue::ExternalLinkage, NULL,
3534-
oldGV->getName());
3535-
GV->copyAttributesFrom(oldGV);
3536-
}
3537-
}
3538-
35393530
#ifdef JULIA_ENABLE_THREADING
3540-
if (GV) {
3531+
if (imaging_mode) {
3532+
GlobalVariable *GV = jl_declare_global(M, jltls_states_func_ptr);
35413533
Value *getter = tbaa_decorate(tbaa_const,
35423534
new LoadInst(GV, "", ptlsStates));
35433535
ptlsStates->setCalledFunction(getter);
35443536
}
35453537
ptlsStates->setAttributes(jltls_states_func->getAttributes());
35463538
#else
3547-
ptlsStates->replaceAllUsesWith(GV);
3539+
ptlsStates->replaceAllUsesWith(jl_declare_global(M, jltls_states_var));
35483540
ptlsStates->eraseFromParent();
35493541
#endif
35503542
}
@@ -5119,27 +5111,14 @@ static void init_julia_llvm_env(Module *m)
51195111
add_named_global(jltls_states_func, jl_get_ptls_states_getter());
51205112
if (imaging_mode) {
51215113
PointerType *pfunctype = jltls_states_func->getFunctionType()->getPointerTo();
5122-
// This is **NOT** a external variable or a normal global variable
5123-
// This is a special internal global slot with a special index
5124-
// in the global variable table.
51255114
jltls_states_func_ptr =
5126-
new GlobalVariable(*m, pfunctype,
5127-
false, GlobalVariable::InternalLinkage,
5128-
ConstantPointerNull::get(pfunctype),
5129-
"jl_get_ptls_states.ptr");
5130-
addComdat(jltls_states_func_ptr);
5131-
// make the pointer valid for this session
5132-
#if defined(USE_MCJIT) || defined(USE_ORCJIT)
5133-
auto p = new uintptr_t(0);
5134-
jl_ExecutionEngine->addGlobalMapping(jltls_states_func_ptr->getName(),
5135-
(uintptr_t)p);
5136-
#else
5137-
uintptr_t *p = (uintptr_t*)jl_ExecutionEngine->getPointerToGlobal(jltls_states_func_ptr);
5138-
#endif
5139-
*p = (uintptr_t)jl_get_ptls_states_getter();
5140-
jl_sysimg_gvars.push_back(ConstantExpr::getBitCast(jltls_states_func_ptr,
5141-
T_psize));
5142-
jltls_states_func_idx = jl_sysimg_gvars.size();
5115+
jl_emit_sysimg_slot(m, pfunctype, "jl_get_ptls_states.ptr",
5116+
(uintptr_t)jl_get_ptls_states_getter(),
5117+
jltls_states_func_idx);
5118+
jl_gc_signal_page_ptr =
5119+
jl_emit_sysimg_slot(m, T_pint8, "jl_gc_signal_page.ptr",
5120+
(uintptr_t)jl_gc_signal_page,
5121+
jl_gc_signal_page_idx);
51435122
}
51445123

51455124
#endif

src/dump.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ static int jl_load_sysimg_so(void)
236236
"jl_ptls_states_getter_idx");
237237
*sysimg_gvars[tls_getter_idx - 1] =
238238
(jl_value_t*)jl_get_ptls_states_getter();
239+
size_t signal_page_idx = *(size_t*)jl_dlsym(jl_sysimg_handle,
240+
"jl_gc_signal_page_idx");
241+
*sysimg_gvars[signal_page_idx - 1] = (jl_value_t*)jl_gc_signal_page;
239242
#endif
240243
const char *cpu_target = (const char*)jl_dlsym(jl_sysimg_handle, "jl_sysimg_cpu_target");
241244
if (strcmp(cpu_target,jl_options.cpu_target) != 0)

src/jitlayers.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,52 @@ static void* jl_emit_and_add_to_shadow(GlobalVariable *gv, void *gvarinit = NULL
776776
#endif
777777
}
778778

779+
// Emit a slot in the system image to be filled at sysimg init time.
780+
// Returns the global var. Fill `idx` with 1-base index in the sysimg gv.
781+
// Use as an optimization for runtime constant addresses to have one less
782+
// load.
783+
static GlobalVariable *jl_emit_sysimg_slot(Module *m, Type *typ, const char *name,
784+
uintptr_t init, size_t &idx)
785+
{
786+
assert(imaging_mode);
787+
// This is **NOT** a external variable or a normal global variable
788+
// This is a special internal global slot with a special index
789+
// in the global variable table.
790+
GlobalVariable *gv = new GlobalVariable(*m, typ, false,
791+
GlobalVariable::InternalLinkage,
792+
ConstantPointerNull::get((PointerType*)typ), name);
793+
addComdat(gv);
794+
// make the pointer valid for this session
795+
#if defined(USE_MCJIT) || defined(USE_ORCJIT)
796+
auto p = new uintptr_t(init);
797+
jl_ExecutionEngine->addGlobalMapping(gv->getName(), (uintptr_t)p);
798+
#else
799+
uintptr_t *p = (uintptr_t*)jl_ExecutionEngine->getPointerToGlobal(gv);
800+
*p = init;
801+
#endif
802+
jl_sysimg_gvars.push_back(ConstantExpr::getBitCast(gv, T_psize));
803+
idx = jl_sysimg_gvars.size();
804+
return gv;
805+
}
806+
807+
// Make sure `GV` belongs to `M` or create a declaration of `GV` in `M` (to
808+
// be linked later) that has the same properties.
809+
static GlobalVariable *jl_declare_global(Module *M, GlobalVariable *oldGV)
810+
{
811+
if (!oldGV)
812+
return NULL;
813+
GlobalVariable *GV = M->getGlobalVariable(oldGV->getName(),
814+
true /* AllowLocal */);
815+
if (GV)
816+
return GV;
817+
GV = new GlobalVariable(*M, oldGV->getType()->getElementType(),
818+
oldGV->isConstant(),
819+
GlobalValue::ExternalLinkage, NULL,
820+
oldGV->getName());
821+
GV->copyAttributesFrom(oldGV);
822+
return GV;
823+
}
824+
779825
static void* jl_get_global(GlobalVariable *gv)
780826
{
781827
#if defined(USE_MCJIT) || defined(USE_ORCJIT)
@@ -848,6 +894,12 @@ static void jl_gen_llvm_globaldata(llvm::Module *mod, ValueToValueMapTy &VMap,
848894
GlobalVariable::ExternalLinkage,
849895
ConstantInt::get(T_size, jltls_states_func_idx),
850896
"jl_ptls_states_getter_idx"));
897+
addComdat(new GlobalVariable(*mod,
898+
T_size,
899+
true,
900+
GlobalVariable::ExternalLinkage,
901+
ConstantInt::get(T_size, jl_gc_signal_page_idx),
902+
"jl_gc_signal_page_idx"));
851903
#endif
852904

853905
Constant *feature_string = ConstantDataArray::getString(jl_LLVMContext, jl_options.cpu_target);

src/jlapi.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,11 @@ JL_DLLEXPORT void (jl_gc_safe_leave)(int8_t state)
342342
jl_gc_safe_leave(state);
343343
}
344344

345+
JL_DLLEXPORT void (jl_gc_safepoint)(void)
346+
{
347+
jl_gc_safepoint();
348+
}
349+
345350
JL_DLLEXPORT void (jl_cpu_pause)(void)
346351
{
347352
jl_cpu_pause();

src/julia_threads.h

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -256,16 +256,15 @@ JL_DLLEXPORT void jl_set_ptls_states_getter(jl_get_ptls_states_func f);
256256
// Make sure jl_gc_state() is always a rvalue
257257
#define jl_gc_state() ((int8_t)(jl_get_ptls_states()->gc_state))
258258
JL_DLLEXPORT extern volatile size_t *jl_gc_signal_page;
259-
STATIC_INLINE void jl_gc_safepoint(void)
260-
{
261-
// This triggers a SegFault when we are in GC
262-
// Assign it to a variable to make sure the compiler emit the load
263-
// and to avoid Clang warning for -Wunused-volatile-lvalue
264-
jl_signal_fence();
265-
size_t v = *jl_gc_signal_page;
266-
jl_signal_fence();
267-
(void)v;
268-
}
259+
// This triggers a SegFault when we are in GC
260+
// Assign it to a variable to make sure the compiler emit the load
261+
// and to avoid Clang warning for -Wunused-volatile-lvalue
262+
#define jl_gc_safepoint() do { \
263+
jl_signal_fence(); \
264+
size_t safepoint_load = *jl_gc_signal_page; \
265+
jl_signal_fence(); \
266+
(void)safepoint_load; \
267+
} while (0)
269268
STATIC_INLINE int8_t jl_gc_state_set(int8_t state, int8_t old_state)
270269
{
271270
jl_get_ptls_states()->gc_state = state;
@@ -284,6 +283,7 @@ STATIC_INLINE int8_t jl_gc_state_save_and_set(int8_t state)
284283
#define jl_gc_unsafe_leave(state) ((void)jl_gc_state_set((state), 0))
285284
#define jl_gc_safe_enter() jl_gc_state_save_and_set(JL_GC_STATE_SAFE)
286285
#define jl_gc_safe_leave(state) ((void)jl_gc_state_set((state), JL_GC_STATE_SAFE))
286+
JL_DLLEXPORT void (jl_gc_safepoint)(void);
287287

288288
// Locks
289289
#ifdef JULIA_ENABLE_THREADING

test/threads.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ function test_atomic_read(commbuf::CommBuf, n::Int)
166166
var1 = commbuf.var1[]
167167
correct &= var1 >= var2
168168
var1 == n && break
169+
# Temporary solution before we have gc transition support in codegen.
170+
ccall(:jl_gc_safepoint, Void, ())
169171
end
170172
commbuf.correct_read = correct
171173
end
@@ -209,6 +211,8 @@ function test_fence(p::Peterson, id::Int, n::Int)
209211
atomic_fence()
210212
while p.flag[otherid][] != 0 && p.turn[] == otherid
211213
# busy wait
214+
# Temporary solution before we have gc transition support in codegen.
215+
ccall(:jl_gc_safepoint, Void, ())
212216
end
213217
# critical section
214218
p.critical[id][] = 1
@@ -260,6 +264,8 @@ function test_atomic_cas!{T}(var::Atomic{T}, range::StepRange{Int,Int})
260264
while true
261265
old = atomic_cas!(var, T(i-1), T(i))
262266
old == T(i-1) && break
267+
# Temporary solution before we have gc transition support in codegen.
268+
ccall(:jl_gc_safepoint, Void, ())
263269
end
264270
end
265271
end

0 commit comments

Comments
 (0)