-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Process gc preserve #58
Changes from 5 commits
fe1772d
4b36001
480b89d
ed66d4b
0558e64
c4c04d3
1b3d0de
83d551b
01f2f24
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2308,9 +2308,52 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { | |
continue; | ||
} | ||
Value *callee = CI->getCalledOperand(); | ||
if (callee && (callee == gc_flush_func || callee == gc_preserve_begin_func | ||
|| callee == gc_preserve_end_func)) { | ||
if (callee && (callee == gc_flush_func)) { | ||
/* No replacement */ | ||
} else if (callee && (callee == gc_preserve_begin_func)) { | ||
/* Replace with a call to the hook functions */ | ||
// Initialize an IR builder. | ||
IRBuilder<> builder(CI); | ||
|
||
builder.SetCurrentDebugLocation(CI->getDebugLoc()); | ||
size_t nargs = 0; | ||
State S2(F); | ||
|
||
std::vector<Value*> args; | ||
for (Use &U : CI->args()) { | ||
Value *V = U; | ||
if (isa<Constant>(V)) | ||
continue; | ||
if (isa<PointerType>(V->getType())) { | ||
if (isSpecialPtr(V->getType())) { | ||
int Num = Number(S2, V); | ||
if (Num >= 0) { | ||
nargs++; | ||
Value *Val = GetPtrForNumber(S2, Num, CI); | ||
args.push_back(Val); | ||
} | ||
} | ||
} else { | ||
std::vector<int> Nums = NumberAll(S2, V); | ||
for (int Num : Nums) { | ||
if (Num < 0) | ||
continue; | ||
Value *Val = GetPtrForNumber(S2, Num, CI); | ||
args.push_back(Val); | ||
nargs++; | ||
} | ||
} | ||
} | ||
args.insert(args.begin(), ConstantInt::get(T_size, nargs)); | ||
|
||
ArrayRef<Value*> args_llvm = ArrayRef<Value*>(args); | ||
builder.CreateCall(getOrDeclare(jl_well_known::GCPreserveBeginHook), args_llvm ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. It turns out that the stock build was already broken because of a file not being compiled in |
||
} else if (callee && (callee == gc_preserve_end_func)) { | ||
/* Replace with a call to the hook functions */ | ||
// Initialize an IR builder. | ||
IRBuilder<> builder(CI); | ||
builder.SetCurrentDebugLocation(CI->getDebugLoc()); | ||
builder.CreateCall(getOrDeclare(jl_well_known::GCPreserveEndHook), {}); | ||
} else if (pointer_from_objref_func != nullptr && callee == pointer_from_objref_func) { | ||
auto *obj = CI->getOperand(0); | ||
auto *ASCI = new AddrSpaceCastInst(obj, JuliaType::get_pjlvalue_ty(obj->getContext()), "", CI); | ||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -570,6 +570,45 @@ JL_DLLEXPORT void jl_gc_array_ptr_copy(jl_array_t *dest, void **dest_p, jl_array | |||
mmtk_memory_region_copy(&ptls->mmtk_mutator, jl_array_owner(src), src_p, jl_array_owner(dest), dest_p, n); | ||||
} | ||||
|
||||
#define jl_p_tpin_gcstack (jl_current_task->tpin_gcstack) | ||||
|
||||
#define JL_GC_PUSHARGS_TPIN_ROOT_OBJS(rts_var,n) \ | ||||
rts_var = ((jl_value_t**)malloc(((n)+2)*sizeof(jl_value_t*)))+2; \ | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These need to be clarified.
|
||||
((void**)rts_var)[-2] = (void*)JL_GC_ENCODE_PUSHARGS(n); \ | ||||
((void**)rts_var)[-1] = jl_p_tpin_gcstack; \ | ||||
memset((void*)rts_var, 0, (n)*sizeof(jl_value_t*)); \ | ||||
jl_p_tpin_gcstack = (jl_gcframe_t*)&(((void**)rts_var)[-2]); \ | ||||
|
||||
#define JL_GC_POP_TPIN_ROOT_OBJS() \ | ||||
jl_gcframe_t *curr = jl_p_tpin_gcstack; \ | ||||
if(curr) { \ | ||||
(jl_p_tpin_gcstack = jl_p_tpin_gcstack->prev); \ | ||||
free(curr); \ | ||||
} | ||||
|
||||
// Add each argument as a tpin root object. | ||||
// However, we cannot use JL_GC_PUSH and JL_GC_POP since the slots should live | ||||
// beyond this function. Instead, we maintain a tpin stack by mallocing/freeing | ||||
// the frames for each of the preserve regions we encounter | ||||
JL_DLLEXPORT void jl_gc_preserve_begin_hook(int n, ...) JL_NOTSAFEPOINT | ||||
{ | ||||
jl_value_t** frame; | ||||
JL_GC_PUSHARGS_TPIN_ROOT_OBJS(frame, n); | ||||
if (n == 0) return; | ||||
|
||||
va_list args; | ||||
va_start(args, n); | ||||
for (int i = 0; i < n; i++) { | ||||
frame[i] = va_arg(args, jl_value_t *); | ||||
} | ||||
va_end(args); | ||||
} | ||||
|
||||
JL_DLLEXPORT void jl_gc_preserve_end_hook(void) JL_NOTSAFEPOINT | ||||
{ | ||||
JL_GC_POP_TPIN_ROOT_OBJS(); | ||||
} | ||||
|
||||
// No inline write barrier -- only used for debugging | ||||
JL_DLLEXPORT void jl_gc_wb1_noinline(const void *parent) JL_NOTSAFEPOINT | ||||
{ | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this function removed? The compiler removes the GC preserve calls if the values to be preserved are removed, or moved to the stack allocation. I don't know why we want to remove this optimization. Furthermore, I am concerned that in those cases where GC preserve is kept, the preserved value is replaced with a null pointer -- preserving a null pointer sounds meaningless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I misunderstood the code twice.
(1) I thought I was deleting the gc preserve after it had been lowered, ie., after the necessary objects arguments to
preserve_begin
have been pushed to the shadow stack, but I don't think that is the case.(2) Since the if case only covered
pass.gc_preserve_begin_func
, I thought I'd end up with dangling hooks for thepreserve_end
and that would cause problems with the push and pop hook functions, but the matchingpop
functions are also removed.Can you explain what you meant by "moved to stack allocation"? I'm just trying to understand whether removing those cases could cause any correctness issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand right, this optimization tries to change heap allocation to stack allocation if possible.
julia/src/llvm-alloc-opt.cpp
Line 554 in 88fea47
If a value
x
that was heap allocated and is replaced with stack allocation by the optimization, all of its uses need to be fixed withreplace_inst
.julia/src/llvm-alloc-opt.cpp
Line 636 in 88fea47
If we have
gc_preserve(x)
,replace_inst
either removes thegc_preserve
(removeGCPreserve()
), or replacex
with the alloca buff on the stack.julia/src/llvm-alloc-opt.cpp
Line 656 in 88fea47
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you remove the code that checks the
gc_preserve_begin_func
case, it will fall into the default case.julia/src/llvm-alloc-opt.cpp
Line 678 in 88fea47
It either replaces
x
with the alloca buff (which is fine), or replacesx
with null. Then you may see null pointers in the preserve begin hook, and you will need to deal with null pointers in the hook.I don't think it causes correctness issues, but doing a gc preserve call for null pointers is meaningless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it! In that case, it might actually be problematic if we remove that particular gc preserve. The reason being the fact that we need to transitively pin everything in that object's transitive closure, independently of that object itself being allocated in the heap or in the stack.Okay, then it should be fine, since the gc preserve is removed only if it doesn't have anything referring to it (
has_ref == 0
, which I believe would also be considering any possible c call).