Skip to content

Commit b4c8f5d

Browse files
authored
Add GC preserve hook (#70)
This PR ports #58 to `dev`. This PR is mostly the same as #58 except that 1. this PR does not remove transitive pinning of shadow stack roots (we know it is unsound to remove the transitive pinning at this stage), and 2. this PR includes minor refactoring for GC codegen interface.
1 parent 97828e0 commit b4c8f5d

13 files changed

+190
-14
lines changed

src/Makefile

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ ifeq ($(USECLANG),1)
2929
FLAGS += -Wno-return-type-c-linkage -Wno-atomic-alignment
3030
endif
3131

32-
ifeq ($(WITH_MMTK), 1)
33-
FLAGS += -I$(MMTK_API_INC)
34-
endif
35-
3632
FLAGS += -DJL_BUILD_ARCH='"$(ARCH)"'
3733
ifeq ($(OS),WINNT)
3834
FLAGS += -DJL_BUILD_UNAME='"NT"'
@@ -65,7 +61,14 @@ RT_LLVMLINK :=
6561
CG_LLVMLINK :=
6662

6763
ifeq ($(JULIACODEGEN),LLVM)
64+
# Currently these files are used by both GCs. But we should make the list specific to stock, and MMTk should have its own implementation.
6865
GC_CODEGEN_SRCS := llvm-final-gc-lowering llvm-late-gc-lowering llvm-gc-invariant-verifier
66+
ifeq ($(WITH_MMTK), 1)
67+
FLAGS += -I$(MMTK_API_INC)
68+
GC_CODEGEN_SRCS += llvm-late-gc-lowering-mmtk
69+
else
70+
GC_CODEGEN_SRCS += llvm-late-gc-lowering-stock
71+
endif
6972
CODEGEN_SRCS := codegen jitlayers aotcompile debuginfo disasm llvm-simdloop \
7073
llvm-pass-helpers llvm-ptls \
7174
llvm-lower-handlers llvm-propagate-addrspaces \

src/gc-interface.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ JL_DLLEXPORT unsigned char jl_gc_pin_object(void* obj);
103103
// Returns the version of which GC implementation is being used according to the list of supported GCs
104104
JL_DLLEXPORT const char* jl_active_gc_impl(void);
105105

106+
// TODO: The preserve hook functions may be temporary. We should see the performance impact of the change.
107+
108+
// 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.
109+
JL_DLLEXPORT void jl_gc_preserve_begin_hook(int n, ...) JL_NOTSAFEPOINT;
110+
// 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.
111+
JL_DLLEXPORT void jl_gc_preserve_end_hook(void) JL_NOTSAFEPOINT;
112+
106113
// ========================================================================= //
107114
// Metrics
108115
// ========================================================================= //

src/gc-mmtk.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,53 @@ JL_DLLEXPORT jl_value_t *jl_gc_internal_obj_base_ptr(void *p)
12401240
return NULL;
12411241
}
12421242

1243+
#define jl_p_gcpreserve_stack (jl_current_task->gcpreserve_stack)
1244+
1245+
// This macro currently uses malloc instead of alloca because this function will exit
1246+
// after pushing the roots into the gc_preserve_stack, which means that the preserve_begin function's
1247+
// stack frame will be destroyed (together with its alloca variables). When we support lowering this code
1248+
// inside the same function that is doing the preserve_begin/preserve_end calls we should be able to simple use allocas.
1249+
// Note also that we use a separate stack for gc preserve roots to avoid the possibility of calling free
1250+
// on a stack that has been allocated with alloca instead of malloc, which could happen depending on the order in which
1251+
// JL_GC_POP() and jl_gc_preserve_end_hook() occurs.
1252+
1253+
#define JL_GC_PUSHARGS_PRESERVE_ROOT_OBJS(rts_var,n) \
1254+
rts_var = ((jl_value_t**)malloc(((n)+2)*sizeof(jl_value_t*)))+2; \
1255+
((void**)rts_var)[-2] = (void*)JL_GC_ENCODE_PUSHARGS(n); \
1256+
((void**)rts_var)[-1] = jl_p_gcpreserve_stack; \
1257+
memset((void*)rts_var, 0, (n)*sizeof(jl_value_t*)); \
1258+
jl_p_gcpreserve_stack = (jl_gcframe_t*)&(((void**)rts_var)[-2]); \
1259+
1260+
#define JL_GC_POP_PRESERVE_ROOT_OBJS() \
1261+
jl_gcframe_t *curr = jl_p_gcpreserve_stack; \
1262+
if(curr) { \
1263+
(jl_p_gcpreserve_stack = jl_p_gcpreserve_stack->prev); \
1264+
free(curr); \
1265+
}
1266+
1267+
// Add each argument as a tpin root object.
1268+
// However, we cannot use JL_GC_PUSH and JL_GC_POP since the slots should live
1269+
// beyond this function. Instead, we maintain a tpin stack by mallocing/freeing
1270+
// the frames for each of the preserve regions we encounter
1271+
JL_DLLEXPORT void jl_gc_preserve_begin_hook(int n, ...) JL_NOTSAFEPOINT
1272+
{
1273+
jl_value_t** frame;
1274+
JL_GC_PUSHARGS_PRESERVE_ROOT_OBJS(frame, n);
1275+
if (n == 0) return;
1276+
1277+
va_list args;
1278+
va_start(args, n);
1279+
for (int i = 0; i < n; i++) {
1280+
frame[i] = va_arg(args, jl_value_t *);
1281+
}
1282+
va_end(args);
1283+
}
1284+
1285+
JL_DLLEXPORT void jl_gc_preserve_end_hook(void) JL_NOTSAFEPOINT
1286+
{
1287+
JL_GC_POP_PRESERVE_ROOT_OBJS();
1288+
}
1289+
12431290
#ifdef __cplusplus
12441291
}
12451292
#endif

src/gc-stock.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3999,6 +3999,16 @@ JL_DLLEXPORT void jl_gc_wb2_slow(const void *parent, const void* ptr) JL_NOTSAFE
39993999
{
40004000
}
40014001

4002+
JL_DLLEXPORT void jl_gc_preserve_begin_hook(int n, ...) JL_NOTSAFEPOINT
4003+
{
4004+
jl_unreachable();
4005+
}
4006+
4007+
JL_DLLEXPORT void jl_gc_preserve_end_hook(void) JL_NOTSAFEPOINT
4008+
{
4009+
jl_unreachable();
4010+
}
4011+
40024012
JL_DLLEXPORT const char* jl_active_gc_impl(void) {
40034013
return "";
40044014
}

src/jl_exported_funcs.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@
186186
XX(jl_gc_set_max_memory) \
187187
XX(jl_gc_sync_total_bytes) \
188188
XX(jl_gc_total_hrtime) \
189+
XX(jl_gc_preserve_begin_hook) \
190+
XX(jl_gc_preserve_end_hook) \
189191
XX(jl_gdblookup) \
190192
XX(jl_generating_output) \
191193
XX(jl_declare_const_gf) \

src/julia.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2367,6 +2367,9 @@ typedef struct _jl_task_t {
23672367
// uint48_t padding2_64;
23682368
// saved gc stack top for context switches
23692369
jl_gcframe_t *gcstack;
2370+
// GC stack of objects from gc preserve regions
2371+
// These must always be transitively pinned. Only used by MMTK.
2372+
jl_gcframe_t *gcpreserve_stack;
23702373
size_t world_age;
23712374
// quick lookup for current ptls
23722375
jl_ptls_t ptls; // == jl_all_tls_states[tid]

src/llvm-final-gc-lowering.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ void FinalLowerGC::lowerPushGCFrame(CallInst *target, Function &F)
4242

4343
IRBuilder<> builder(target);
4444
StoreInst *inst = builder.CreateAlignedStore(
45+
// FIXME: We should use JL_GC_ENCODE_PUSHARGS_NO_TPIN here.
46+
// We need to make sure things are properly pinned before turning this into a non TPIN push.
4547
ConstantInt::get(T_size, JL_GC_ENCODE_PUSHARGS(nRoots)),
4648
builder.CreateConstInBoundsGEP1_32(T_prjlvalue, gcframe, 0, "frame.nroots"),// GEP of 0 becomes a noop and eats the name
4749
Align(sizeof(void*)));

src/llvm-gc-interface-passes.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ struct LateLowerGCFrame: private JuliaPassContext {
360360
void PlaceGCFrameStores(State &S, unsigned MinColorRoot, ArrayRef<int> Colors, Value *GCFrame);
361361
void PlaceRootsAndUpdateCalls(SmallVectorImpl<int> &Colors, State &S, std::map<Value *, std::pair<int, int>>);
362362
void CleanupWriteBarriers(Function &F, State *S, const SmallVector<CallInst*, 0> &WriteBarriers, bool *CFGModified);
363+
void CleanupGCPreserve(Function &F, CallInst *CI, Value *callee, Type *T_size);
363364
bool CleanupIR(Function &F, State *S, bool *CFGModified);
364365
void NoteUseChain(State &S, BBState &BBS, User *TheUser);
365366
SmallVector<int, 1> GetPHIRefinements(PHINode *phi, State &S);
@@ -427,4 +428,12 @@ struct FinalLowerGC: private JuliaPassContext {
427428
#endif
428429
};
429430

431+
inline bool isSpecialPtr(Type *Ty) {
432+
PointerType *PTy = dyn_cast<PointerType>(Ty);
433+
if (!PTy)
434+
return false;
435+
unsigned AS = PTy->getAddressSpace();
436+
return AddressSpace::FirstSpecial <= AS && AS <= AddressSpace::LastSpecial;
437+
}
438+
430439
#endif // LLVM_GC_PASSES_H

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include "llvm-gc-interface-passes.h"
2+
3+
void LateLowerGCFrame::CleanupGCPreserve(Function &F, CallInst *CI, Value *callee, Type *T_size) {
4+
if (callee == gc_preserve_begin_func) {
5+
// Initialize an IR builder.
6+
IRBuilder<> builder(CI);
7+
8+
builder.SetCurrentDebugLocation(CI->getDebugLoc());
9+
size_t nargs = 0;
10+
State S2(F);
11+
12+
std::vector<Value*> args;
13+
for (Use &U : CI->args()) {
14+
Value *V = U;
15+
if (isa<Constant>(V))
16+
continue;
17+
if (isa<PointerType>(V->getType())) {
18+
if (isSpecialPtr(V->getType())) {
19+
int Num = Number(S2, V);
20+
if (Num >= 0) {
21+
nargs++;
22+
Value *Val = GetPtrForNumber(S2, Num, CI);
23+
args.push_back(Val);
24+
}
25+
}
26+
} else {
27+
auto Nums = NumberAll(S2, V);
28+
for (int Num : Nums) {
29+
if (Num < 0)
30+
continue;
31+
Value *Val = GetPtrForNumber(S2, Num, CI);
32+
args.push_back(Val);
33+
nargs++;
34+
}
35+
}
36+
}
37+
args.insert(args.begin(), ConstantInt::get(T_size, nargs));
38+
39+
ArrayRef<Value*> args_llvm = ArrayRef<Value*>(args);
40+
builder.CreateCall(getOrDeclare(jl_well_known::GCPreserveBeginHook), args_llvm );
41+
} else if (callee == gc_preserve_end_func) {
42+
// Initialize an IR builder.
43+
IRBuilder<> builder(CI);
44+
builder.SetCurrentDebugLocation(CI->getDebugLoc());
45+
builder.CreateCall(getOrDeclare(jl_well_known::GCPreserveEndHook), {});
46+
}
47+
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#include "llvm-gc-interface-passes.h"
2+
3+
void LateLowerGCFrame::CleanupGCPreserve(Function &F, CallInst *CI, Value *callee, Type *T_size) {
4+
// Do nothing for the stock GC
5+
}

0 commit comments

Comments
 (0)