Skip to content

Commit

Permalink
Make sure global constants are recognized more reliably as such durin…
Browse files Browse the repository at this point in the history
…g GC lowering. (JuliaLang#37113)

Some of the pointer chasing duplicates the logic in the rest of the pass but are never the less
necessary to handle operation in the untracked address space.
  • Loading branch information
yuyichao authored Aug 23, 2020
1 parent 87bf13b commit 81e2006
Showing 1 changed file with 76 additions and 11 deletions.
87 changes: 76 additions & 11 deletions src/llvm-late-gc-lowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1121,17 +1121,77 @@ static bool isLoadFromImmut(LoadInst *LI)
return false;
}

// Check if this is a load from an constant global.
static bool isLoadFromConstGV(LoadInst *LI)
static bool isConstGV(GlobalVariable *gv)
{
return gv->isConstant() || gv->getMetadata("julia.constgv");
}

static bool isLoadFromConstGV(LoadInst *LI, bool &task_local);
static bool isLoadFromConstGV(Value *v, bool &task_local)
{
v = v->stripInBoundsOffsets();
if (auto LI = dyn_cast<LoadInst>(v))
return isLoadFromConstGV(LI, task_local);
if (auto gv = dyn_cast<GlobalVariable>(v))
return isConstGV(gv);
// null pointer
if (isa<ConstantData>(v))
return true;
// literal pointers
if (auto CE = dyn_cast<ConstantExpr>(v))
return (CE->getOpcode() == Instruction::IntToPtr &&
isa<ConstantData>(CE->getOperand(0)));
if (auto SL = dyn_cast<SelectInst>(v))
return (isLoadFromConstGV(SL->getTrueValue(), task_local) &&
isLoadFromConstGV(SL->getFalseValue(), task_local));
if (auto Phi = dyn_cast<PHINode>(v)) {
auto n = Phi->getNumIncomingValues();
for (unsigned i = 0; i < n; ++i) {
if (!isLoadFromConstGV(Phi->getIncomingValue(i), task_local)) {
return false;
}
}
return true;
}
if (auto call = dyn_cast<CallInst>(v)) {
auto callee = call->getCalledFunction();
if (callee && callee->getName() == "julia.typeof") {
return true;
}
if (callee && callee->getName() == "julia.ptls_states") {
task_local = true;
return true;
}
}
if (isa<Argument>(v)) {
task_local = true;
return true;
}
return false;
}

// Check if this is can be traced through constant loads to an constant global
// or otherwise globally rooted value.
// Almost all `tbaa_const` loads satisfies this with the exception of
// task local constants which are constant as far as the code is concerned but aren't
// global constants. For task local constant `task_local` will be true when this function
// returns.
//
// The white list implemented here and above in `isLoadFromConstGV(Value*)` should
// cover all the cases we and LLVM generates.
static bool isLoadFromConstGV(LoadInst *LI, bool &task_local)
{
// We only emit single slot GV in codegen
// but LLVM global merging can change the pointer operands to GEPs/bitcasts
if (auto gv = dyn_cast<GlobalVariable>(LI->getPointerOperand()->stripInBoundsOffsets())) {
MDNode *TBAA = LI->getMetadata(LLVMContext::MD_tbaa);
if (isTBAA(TBAA, {"jtbaa_const"}) || gv->getMetadata("julia.constgv")) {
auto load_base = LI->getPointerOperand()->stripInBoundsOffsets();
auto gv = dyn_cast<GlobalVariable>(load_base);
if (isTBAA(LI->getMetadata(LLVMContext::MD_tbaa), {"jtbaa_immut", "jtbaa_const"})) {
if (gv)
return true;
}
return isLoadFromConstGV(load_base, task_local);
}
if (gv)
return isConstGV(gv);
return false;
}

Expand Down Expand Up @@ -1466,6 +1526,7 @@ State LateLowerGCFrame::LocalScan(Function &F) {
// from.
SmallVector<int, 1> RefinedPtr{};
Type *Ty = LI->getType()->getScalarType();
bool task_local = false;
if (isLoadFromImmut(LI) && isSpecialPtr(LI->getPointerOperand()->getType())) {
RefinedPtr.push_back(Number(S, LI->getPointerOperand()));
} else if (LI->getType()->isPointerTy() &&
Expand All @@ -1474,10 +1535,12 @@ State LateLowerGCFrame::LocalScan(Function &F) {
// Loads from a jlcall argument array
RefinedPtr.push_back(-1);
}
else if (isLoadFromConstGV(LI)) {
else if (isLoadFromConstGV(LI, task_local)) {
// If this is a const load from a global,
// we know that the object is a constant as well and doesn't need rooting.
RefinedPtr.push_back(-2);
// If this is a task local constant, we don't need to root it within the
// task but we do need to issue write barriers for when the current task dies.
RefinedPtr.push_back(task_local ? -1 : -2);
}
if (!Ty->isPointerTy() || Ty->getPointerAddressSpace() != AddressSpace::Loaded) {
MaybeNoteDef(S, BBS, LI, BBS.Safepoints, std::move(RefinedPtr));
Expand Down Expand Up @@ -1534,10 +1597,11 @@ State LateLowerGCFrame::LocalScan(Function &F) {
} else if (auto *ASCI = dyn_cast<AddrSpaceCastInst>(&I)) {
if (isTrackedValue(ASCI)) {
SmallVector<int, 1> RefinedPtr{};
bool task_local = false;
auto origin = ASCI->getPointerOperand()->stripPointerCasts();
if (auto LI = dyn_cast<LoadInst>(origin)) {
if (isLoadFromConstGV(LI)) {
RefinedPtr.push_back(-2);
if (isLoadFromConstGV(LI, task_local)) {
RefinedPtr.push_back(task_local ? -1 : -2);
}
}
MaybeNoteDef(S, BBS, ASCI, BBS.Safepoints, std::move(RefinedPtr));
Expand Down Expand Up @@ -2170,7 +2234,8 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S) {
// a type in other branches.
// However, it should be safe for us to do this on const globals
// which should be the important cases as well.
if (isLoadFromConstGV(LI) && getLoadValueAlign(LI) < 16) {
bool task_local = false;
if (isLoadFromConstGV(LI, task_local) && getLoadValueAlign(LI) < 16) {
Type *T_int64 = Type::getInt64Ty(LI->getContext());
auto op = ConstantAsMetadata::get(ConstantInt::get(T_int64, 16));
LI->setMetadata(LLVMContext::MD_align,
Expand Down

0 comments on commit 81e2006

Please sign in to comment.