Skip to content
This repository was archived by the owner on Apr 12, 2019. It is now read-only.

Commit 3cfc131

Browse files
authored
Add a special GC AS for array ptrs (JuliaLang#28251)
The array data pointer is somewhat special. It points to a chunk for memory that is effectively managed by the GC, but is not itself a GC-tracked value. However, it is also not quite an interior pointer into the array, since it may be an external allocation (or at the more immediate IR level it is derived using a load rather than a gep). We could try to make Derived do both, but the semantics turn out to be rather different, so add a new kind of AS `Loaded`, that handles precisely this situation: It roots the object that it was loaded from while it is live. Fixes JuliaLang#27955
1 parent bf74a57 commit 3cfc131

9 files changed

+99
-22
lines changed

doc/src/devdocs/llvm.md

+5
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@ three different address spaces (their numbers are defined in `src/codegen_shared
211211
future), but unlike the other pointers need not be rooted if passed to a
212212
call (they do still need to be rooted if they are live across another safepoint
213213
between the definition and the call).
214+
- Pointers loaded from tracked object (currently 13): This is used by arrays,
215+
which themselves contain a pointer to the managed data. This data area is owned
216+
by the array, but is not a GC-tracked object by itself. The compiler guarantees
217+
that as long as this pointer is live, the object that this pointer was loaded
218+
from will keep being live.
214219

215220
### Invariants
216221

src/ccall.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -1638,9 +1638,8 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs)
16381638
assert(!isVa && !llvmcall && nargt == 1);
16391639
assert(!addressOf.at(0));
16401640
const jl_cgval_t &ary = argv[0];
1641-
jl_value_t *aryex = ccallarg(0);
16421641
JL_GC_POP();
1643-
return mark_or_box_ccall_result(ctx, ctx.builder.CreatePtrToInt(emit_arrayptr(ctx, ary, aryex), lrt),
1642+
return mark_or_box_ccall_result(ctx, ctx.builder.CreatePtrToInt(emit_unsafe_arrayptr(ctx, ary), lrt),
16441643
retboxed, rt, unionall, static_rt);
16451644
}
16461645
else if (is_libjulia_func(jl_value_ptr)) {

src/cgutils.cpp

+42-14
Original file line numberDiff line numberDiff line change
@@ -238,20 +238,28 @@ static DIType *julia_type_to_di(jl_value_t *jt, DIBuilder *dbuilder, bool isboxe
238238
return (llvm::DIType*)jdt->ditype;
239239
}
240240

241-
static Value *emit_pointer_from_objref(jl_codectx_t &ctx, Value *V)
241+
static Value *emit_pointer_from_objref_internal(jl_codectx_t &ctx, Value *V)
242242
{
243-
unsigned AS = cast<PointerType>(V->getType())->getAddressSpace();
244-
if (AS != AddressSpace::Tracked && AS != AddressSpace::Derived)
245-
return ctx.builder.CreatePtrToInt(V, T_size);
246-
V = ctx.builder.CreateBitCast(decay_derived(V),
247-
PointerType::get(T_jlvalue, AddressSpace::Derived));
248243
CallInst *Call = ctx.builder.CreateCall(prepare_call(pointer_from_objref_func), V);
249244
#if JL_LLVM_VERSION >= 50000
250245
Call->addAttribute(AttributeList::FunctionIndex, Attribute::ReadNone);
251246
#else
252247
Call->addAttribute(AttributeSet::FunctionIndex, Attribute::ReadNone);
253248
#endif
254-
return ctx.builder.CreatePtrToInt(Call, T_size);
249+
return Call;
250+
}
251+
252+
static Value *emit_pointer_from_objref(jl_codectx_t &ctx, Value *V)
253+
{
254+
unsigned AS = cast<PointerType>(V->getType())->getAddressSpace();
255+
if (AS != AddressSpace::Tracked && AS != AddressSpace::Derived)
256+
return ctx.builder.CreatePtrToInt(V, T_size);
257+
V = ctx.builder.CreateBitCast(decay_derived(V),
258+
PointerType::get(T_jlvalue, AddressSpace::Derived));
259+
260+
return ctx.builder.CreatePtrToInt(
261+
emit_pointer_from_objref_internal(ctx, V),
262+
T_size);
255263
}
256264

257265
// --- emitting pointers directly into code ---
@@ -1705,24 +1713,44 @@ static Value *emit_arraylen(jl_codectx_t &ctx, const jl_cgval_t &tinfo)
17051713
return emit_arraylen_prim(ctx, tinfo);
17061714
}
17071715

1708-
static Value *emit_arrayptr(jl_codectx_t &ctx, const jl_cgval_t &tinfo, bool isboxed = false)
1716+
static Value *emit_arrayptr_internal(jl_codectx_t &ctx, const jl_cgval_t &tinfo, Value *t, unsigned AS, bool isboxed)
17091717
{
1710-
Value *t = boxed(ctx, tinfo);
1711-
Value *addr = ctx.builder.CreateStructGEP(jl_array_llvmt,
1712-
emit_bitcast(ctx, decay_derived(t), jl_parray_llvmt),
1713-
0); //index (not offset) of data field in jl_parray_llvmt
1714-
1718+
Value *addr =
1719+
ctx.builder.CreateStructGEP(jl_array_llvmt,
1720+
emit_bitcast(ctx, t, jl_parray_llvmt),
1721+
0); // index (not offset) of data field in jl_parray_llvmt
17151722
MDNode *tbaa = arraytype_constshape(tinfo.typ) ? tbaa_const : tbaa_arrayptr;
1723+
PointerType *PT = cast<PointerType>(addr->getType());
1724+
PointerType *PPT = cast<PointerType>(PT->getElementType());
17161725
if (isboxed) {
17171726
addr = ctx.builder.CreateBitCast(addr,
1718-
PointerType::get(T_pprjlvalue, cast<PointerType>(addr->getType())->getAddressSpace()));
1727+
PointerType::get(PointerType::get(T_prjlvalue, AS),
1728+
PT->getAddressSpace()));
1729+
} else if (AS != PPT->getAddressSpace()) {
1730+
addr = ctx.builder.CreateBitCast(addr,
1731+
PointerType::get(
1732+
PointerType::get(PPT->getElementType(), AS),
1733+
PT->getAddressSpace()));
17191734
}
17201735
auto LI = ctx.builder.CreateLoad(addr);
17211736
LI->setMetadata(LLVMContext::MD_nonnull, MDNode::get(jl_LLVMContext, None));
17221737
tbaa_decorate(tbaa, LI);
17231738
return LI;
17241739
}
17251740

1741+
static Value *emit_arrayptr(jl_codectx_t &ctx, const jl_cgval_t &tinfo, bool isboxed = false)
1742+
{
1743+
Value *t = boxed(ctx, tinfo);
1744+
return emit_arrayptr_internal(ctx, tinfo, decay_derived(t), AddressSpace::Loaded, isboxed);
1745+
}
1746+
1747+
static Value *emit_unsafe_arrayptr(jl_codectx_t &ctx, const jl_cgval_t &tinfo, bool isboxed = false)
1748+
{
1749+
Value *t = boxed(ctx, tinfo);
1750+
t = emit_pointer_from_objref_internal(ctx, decay_derived(t));
1751+
return emit_arrayptr_internal(ctx, tinfo, t, 0, isboxed);
1752+
}
1753+
17261754
static Value *emit_arrayptr(jl_codectx_t &ctx, const jl_cgval_t &tinfo, jl_value_t *ex, bool isboxed = false)
17271755
{
17281756
return emit_arrayptr(ctx, tinfo, isboxed);

src/codegen.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -6830,7 +6830,7 @@ static void init_julia_llvm_env(Module *m)
68306830
jl_func_sig = FunctionType::get(T_prjlvalue, ftargs, false);
68316831
assert(jl_func_sig != NULL);
68326832

6833-
Type *vaelts[] = {T_pint8
6833+
Type *vaelts[] = {PointerType::get(T_int8, AddressSpace::Loaded)
68346834
#ifdef STORE_ARRAY_LEN
68356835
, T_size
68366836
#endif
@@ -7476,7 +7476,7 @@ extern "C" void *jl_init_llvm(void)
74767476
// Mark our address spaces as non-integral
74777477
#if JL_LLVM_VERSION >= 40000
74787478
jl_data_layout = jl_ExecutionEngine->getDataLayout();
7479-
std::string DL = jl_data_layout.getStringRepresentation() + "-ni:10:11:12";
7479+
std::string DL = jl_data_layout.getStringRepresentation() + "-ni:10:11:12:13";
74807480
jl_data_layout.reset(DL);
74817481
#endif
74827482

src/codegen_shared.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55

66
enum AddressSpace {
77
Generic = 0,
8-
Tracked = 10, Derived = 11, CalleeRooted = 12,
8+
Tracked = 10,
9+
Derived = 11,
10+
CalleeRooted = 12,
11+
Loaded = 13,
912
FirstSpecial = Tracked,
10-
LastSpecial = CalleeRooted,
13+
LastSpecial = Loaded,
1114
};
1215

1316
#define JLCALL_CC (CallingConv::ID)36

src/jitlayers.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1160,7 +1160,7 @@ void jl_dump_native(const char *bc_fname, const char *unopt_bc_fname, const char
11601160
shadow_output->setTargetTriple(TM->getTargetTriple().str());
11611161
#if JL_LLVM_VERSION >= 40000
11621162
DataLayout DL = TM->createDataLayout();
1163-
DL.reset(DL.getStringRepresentation() + "-ni:10:11:12");
1163+
DL.reset(DL.getStringRepresentation() + "-ni:10:11:12:13");
11641164
shadow_output->setDataLayout(DL);
11651165
#else
11661166
shadow_output->setDataLayout(TM->createDataLayout());

src/llvm-gc-invariant-verifier.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ void GCInvariantVerifier::visitAddrSpaceCastInst(AddrSpaceCastInst &I) {
6565
unsigned ToAS = cast<PointerType>(I.getDestTy())->getAddressSpace();
6666
if (FromAS == 0)
6767
return;
68+
Check(ToAS != AddressSpace::Loaded && FromAS != AddressSpace::Loaded,
69+
"Illegal address space cast involving loaded ptr", &I);
6870
Check(FromAS != AddressSpace::Tracked ||
6971
ToAS == AddressSpace::CalleeRooted ||
7072
ToAS == AddressSpace::Derived,

src/llvm-late-gc-lowering.cpp

+25-1
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,23 @@ static std::pair<Value*,int> FindBaseValue(const State &S, Value *V, bool UseCac
461461
fld_idx = IdxOp->getLimitedValue(INT_MAX);
462462
CurrentV = EEI->getVectorOperand();
463463
}
464+
else if (auto LI = dyn_cast<LoadInst>(CurrentV)) {
465+
if (auto PtrT = dyn_cast<PointerType>(LI->getType())) {
466+
if (PtrT->getAddressSpace() == AddressSpace::Loaded) {
467+
CurrentV = LI->getPointerOperand();
468+
if (!isSpecialPtr(CurrentV->getType())) {
469+
// Special case to bypass the check below.
470+
// This could really be anything, but it's not loaded
471+
// from a tracked pointer, so it doesn't matter what
472+
// it is.
473+
return std::make_pair(CurrentV, fld_idx);
474+
}
475+
continue;
476+
}
477+
}
478+
// In general a load terminates a walk
479+
break;
480+
}
464481
else {
465482
break;
466483
}
@@ -538,6 +555,10 @@ int LateLowerGCFrame::NumberBase(State &S, Value *V, Value *CurrentV)
538555
getValueAddrSpace(CurrentV) != AddressSpace::Tracked)) {
539556
// We know this is rooted in the parent
540557
Number = -1;
558+
} else if (!isSpecialPtr(CurrentV->getType()) && !isUnion) {
559+
// Externally rooted somehow hopefully (otherwise there's a bug in the
560+
// input IR)
561+
Number = -1;
541562
} else if (isa<SelectInst>(CurrentV) && !isUnion && getValueAddrSpace(CurrentV) != AddressSpace::Tracked) {
542563
int Number = LiftSelect(S, cast<SelectInst>(CurrentV));
543564
S.AllPtrNumbering[V] = Number;
@@ -1089,7 +1110,10 @@ State LateLowerGCFrame::LocalScan(Function &F) {
10891110
// we know that the object is a constant as well and doesn't need rooting.
10901111
RefinedPtr.push_back(-2);
10911112
}
1092-
MaybeNoteDef(S, BBS, LI, BBS.Safepoints, std::move(RefinedPtr));
1113+
if (!LI->getType()->isPointerTy() ||
1114+
cast<PointerType>(LI->getType())->getAddressSpace() != AddressSpace::Loaded) {
1115+
MaybeNoteDef(S, BBS, LI, BBS.Safepoints, std::move(RefinedPtr));
1116+
}
10931117
NoteOperandUses(S, BBS, I, BBS.UpExposedUsesUnrooted);
10941118
} else if (SelectInst *SI = dyn_cast<SelectInst>(&I)) {
10951119
// We need to insert an extra select for the GC root

test/llvmpasses/gcroots.ll

+16
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,22 @@ top:
385385
ret %jl_value_t addrspace(10)* %rval
386386
}
387387

388+
define i8 @simple_arrayptr() {
389+
; CHECK-LABEL: @simple_arrayptr
390+
; CHECK: %gcframe = alloca %jl_value_t addrspace(10)*, i32 4
391+
top:
392+
%ptls = call %jl_value_t*** @julia.ptls_states()
393+
%obj1 = call %jl_value_t addrspace(10) *@alloc()
394+
%obj2 = call %jl_value_t addrspace(10) *@alloc()
395+
%decayed = addrspacecast %jl_value_t addrspace(10) *%obj1 to %jl_value_t addrspace(11) *
396+
%arrayptrptr = bitcast %jl_value_t addrspace(11) *%decayed to i8 addrspace(13)* addrspace(11)*
397+
%arrayptr = load i8 addrspace(13)*, i8 addrspace(13)* addrspace(11)* %arrayptrptr
398+
call void @jl_safepoint()
399+
call void @one_arg_boxed(%jl_value_t addrspace(10) *%obj2)
400+
%val = load i8, i8 addrspace(13)* %arrayptr
401+
ret i8 %val
402+
}
403+
388404
!0 = !{!"jtbaa"}
389405
!1 = !{!"jtbaa_const", !0, i64 0}
390406
!2 = !{!1, !1, i64 0, i64 1}

0 commit comments

Comments
 (0)