Skip to content

Commit

Permalink
codegen: cleanup gcstack call frames somewhat earlier
Browse files Browse the repository at this point in the history
Slightly reduces the amount of work these optimization passes need to do
later, in most typical cases, and avoids putting an unknown call at the
top of all of our functions, which can inhibit some optimizations of
otherwise trivial functions.

Fixes #57400
  • Loading branch information
vtjnash committed Feb 14, 2025
1 parent be574cd commit 7e7f74f
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 71 deletions.
110 changes: 61 additions & 49 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7143,8 +7143,9 @@ static void allocate_gc_frame(jl_codectx_t &ctx, BasicBlock *b0, bool or_new=fal
// allocate a placeholder gc instruction
// this will require the runtime, but it gets deleted later if unused
ctx.topalloca = ctx.builder.CreateCall(prepare_call(or_new ? jladoptthread_func : jlpgcstack_func));
ctx.pgcstack = ctx.topalloca;
ctx.pgcstack->setName("pgcstack");
ctx.topalloca->setName("pgcstack");
if (ctx.pgcstack == nullptr)
ctx.pgcstack = ctx.topalloca;
}

static Value *get_current_task(jl_codectx_t &ctx)
Expand Down Expand Up @@ -7291,16 +7292,17 @@ static void emit_specsig_to_specsig(
ctx.builder.SetInsertPoint(b0);
DebugLoc noDbg;
ctx.builder.SetCurrentDebugLocation(noDbg);
allocate_gc_frame(ctx, b0);
Function::arg_iterator AI = gf_thunk->arg_begin();
SmallVector<jl_cgval_t, 0> myargs(nargs);
if (cc == jl_returninfo_t::SRet || cc == jl_returninfo_t::Union)
++AI;
if (return_roots)
++AI;
if (JL_FEAT_TEST(ctx,gcstack_arg)) {
ctx.pgcstack = AI;
++AI; // gcstack_arg
}
allocate_gc_frame(ctx, b0);
for (size_t i = 0; i < nargs; i++) {
if (i == 0 && is_for_opaque_closure) {
// `jt` would be wrong here (it is the captures type), so is not used used for
Expand Down Expand Up @@ -7407,6 +7409,10 @@ static void emit_specsig_to_specsig(
break;
}
}
if (ctx.topalloca != ctx.pgcstack && ctx.topalloca->use_empty()) {
ctx.topalloca->eraseFromParent();
ctx.topalloca = nullptr;
}
}

void emit_specsig_to_fptr1(
Expand Down Expand Up @@ -8263,6 +8269,10 @@ static void gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *abi, jl_va
CreateTrap(ctx.builder, false);
else
ctx.builder.CreateRet(boxed(ctx, retval));
if (ctx.topalloca != ctx.pgcstack && ctx.topalloca->use_empty()) {
ctx.topalloca->eraseFromParent();
ctx.topalloca = nullptr;
}
}

static jl_returninfo_t get_specsig_function(jl_codegen_params_t &params, Module *M, Value *fval, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure,
Expand Down Expand Up @@ -8919,7 +8929,53 @@ static jl_llvm_functions_t
ctx.spvals_ptr = &*AI++;
}
}
// step 6. set up GC frame
// step 6. set up GC frame and special arguments
Function::arg_iterator AI = f->arg_begin();
SmallVector<AttributeSet, 0> attrs(f->arg_size()); // function declaration attributes

if (has_sret) {
Argument *Arg = &*AI;
++AI;
AttrBuilder param(ctx.builder.getContext(), f->getAttributes().getParamAttrs(Arg->getArgNo()));
if (returninfo.cc == jl_returninfo_t::Union) {
param.addAttribute(Attribute::NonNull);
// The `dereferenceable` below does not imply `nonnull` for non addrspace(0) pointers.
param.addDereferenceableAttr(returninfo.union_bytes);
param.addAlignmentAttr(returninfo.union_align);
}
else {
const DataLayout &DL = jl_Module->getDataLayout();
Type *RT = Arg->getParamStructRetType();
TypeSize sz = DL.getTypeAllocSize(RT);
Align al = DL.getPrefTypeAlign(RT);
if (al > MAX_ALIGN)
al = Align(MAX_ALIGN);
param.addAttribute(Attribute::NonNull);
// The `dereferenceable` below does not imply `nonnull` for non addrspace(0) pointers.
param.addDereferenceableAttr(sz);
param.addAlignmentAttr(al);
}
attrs[Arg->getArgNo()] = AttributeSet::get(Arg->getContext(), param); // function declaration attributes
}
if (returninfo.return_roots) {
Argument *Arg = &*AI;
++AI;
AttrBuilder param(ctx.builder.getContext(), f->getAttributes().getParamAttrs(Arg->getArgNo()));
param.addAttribute(Attribute::NonNull);
// The `dereferenceable` below does not imply `nonnull` for non addrspace(0) pointers.
size_t size = returninfo.return_roots * sizeof(jl_value_t*);
param.addDereferenceableAttr(size);
param.addAlignmentAttr(Align(sizeof(jl_value_t*)));
attrs[Arg->getArgNo()] = AttributeSet::get(Arg->getContext(), param); // function declaration attributes
}
if (specsig && JL_FEAT_TEST(ctx, gcstack_arg)) {
Argument *Arg = &*AI;
ctx.pgcstack = Arg;
++AI;
AttrBuilder param(ctx.builder.getContext());
attrs[Arg->getArgNo()] = AttributeSet::get(Arg->getContext(), param);
}

allocate_gc_frame(ctx, b0);
Value *last_age = NULL;
Value *world_age_field = NULL;
Expand Down Expand Up @@ -9062,9 +9118,6 @@ static jl_llvm_functions_t
}

// step 8. move args into local variables
Function::arg_iterator AI = f->arg_begin();
SmallVector<AttributeSet, 0> attrs(f->arg_size()); // function declaration attributes

auto get_specsig_arg = [&](jl_value_t *argType, Type *llvmArgType, bool isboxed) {
if (type_is_ghost(llvmArgType)) { // this argument is not actually passed
return ghostValue(ctx, argType);
Expand Down Expand Up @@ -9097,47 +9150,6 @@ static jl_llvm_functions_t
return theArg;
};

if (has_sret) {
Argument *Arg = &*AI;
++AI;
AttrBuilder param(ctx.builder.getContext(), f->getAttributes().getParamAttrs(Arg->getArgNo()));
if (returninfo.cc == jl_returninfo_t::Union) {
param.addAttribute(Attribute::NonNull);
// The `dereferenceable` below does not imply `nonnull` for non addrspace(0) pointers.
param.addDereferenceableAttr(returninfo.union_bytes);
param.addAlignmentAttr(returninfo.union_align);
}
else {
const DataLayout &DL = jl_Module->getDataLayout();
Type *RT = Arg->getParamStructRetType();
TypeSize sz = DL.getTypeAllocSize(RT);
Align al = DL.getPrefTypeAlign(RT);
if (al > MAX_ALIGN)
al = Align(MAX_ALIGN);
param.addAttribute(Attribute::NonNull);
// The `dereferenceable` below does not imply `nonnull` for non addrspace(0) pointers.
param.addDereferenceableAttr(sz);
param.addAlignmentAttr(al);
}
attrs[Arg->getArgNo()] = AttributeSet::get(Arg->getContext(), param); // function declaration attributes
}
if (returninfo.return_roots) {
Argument *Arg = &*AI;
++AI;
AttrBuilder param(ctx.builder.getContext(), f->getAttributes().getParamAttrs(Arg->getArgNo()));
param.addAttribute(Attribute::NonNull);
// The `dereferenceable` below does not imply `nonnull` for non addrspace(0) pointers.
size_t size = returninfo.return_roots * sizeof(jl_value_t*);
param.addDereferenceableAttr(size);
param.addAlignmentAttr(Align(sizeof(jl_value_t*)));
attrs[Arg->getArgNo()] = AttributeSet::get(Arg->getContext(), param); // function declaration attributes
}
if (specsig && JL_FEAT_TEST(ctx, gcstack_arg)){
Argument *Arg = &*AI;
++AI;
AttrBuilder param(ctx.builder.getContext());
attrs[Arg->getArgNo()] = AttributeSet::get(Arg->getContext(), param);
}
for (i = 0; i < nreq && i < vinfoslen; i++) {
jl_sym_t *s = slot_symbol(ctx, i);
jl_varinfo_t &vi = ctx.slots[i];
Expand Down Expand Up @@ -10105,7 +10117,7 @@ static jl_llvm_functions_t
}
}

if (ctx.topalloca->use_empty()) {
if (ctx.topalloca != ctx.pgcstack && ctx.topalloca->use_empty()) {
ctx.topalloca->eraseFromParent();
ctx.topalloca = nullptr;
}
Expand Down
4 changes: 0 additions & 4 deletions src/llvm-final-gc-lowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,6 @@ void FinalLowerGC::lowerGCAllocBytes(CallInst *target, Function &F)
bool FinalLowerGC::runOnFunction(Function &F)
{
initAll(*F.getParent());
if (!pgcstack_getter && !adoptthread_func) {
LLVM_DEBUG(dbgs() << "FINAL GC LOWERING: Skipping function " << F.getName() << "\n");
return false;
}

// Look for a call to 'julia.get_pgcstack'.
pgcstack = getPGCstack(F);
Expand Down
4 changes: 2 additions & 2 deletions src/llvm-gc-interface-passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ struct LateLowerGCFrame: private JuliaPassContext {
bool runOnFunction(Function &F, bool *CFGModified = nullptr);

private:
CallInst *pgcstack;
Value *pgcstack;
Function *smallAllocFunc;

void MaybeNoteDef(State &S, BBState &BBS, Value *Def, const ArrayRef<int> &SafepointsSoFar,
Expand Down Expand Up @@ -388,7 +388,7 @@ struct FinalLowerGC: private JuliaPassContext {
Function *smallAllocFunc;
Function *bigAllocFunc;
Function *allocTypedFunc;
Instruction *pgcstack;
Value *pgcstack;
Type *T_size;

// Lowers a `julia.new_gc_frame` intrinsic.
Expand Down
7 changes: 4 additions & 3 deletions src/llvm-late-gc-lowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2330,7 +2330,10 @@ void LateLowerGCFrame::PlaceRootsAndUpdateCalls(ArrayRef<int> Colors, int PreAss
auto pushGcframe = CallInst::Create(
getOrDeclare(jl_intrinsics::pushGCFrame),
{gcframe, ConstantInt::get(T_int32, 0)});
pushGcframe->insertAfter(pgcstack);
if (isa<Argument>(pgcstack))
pushGcframe->insertAfter(gcframe);
else
pushGcframe->insertAfter(cast<Instruction>(pgcstack));

// we don't run memsetopt after this, so run a basic approximation of it
// that removes any redundant memset calls in the prologue since getGCFrameSlot already includes the null store
Expand Down Expand Up @@ -2448,8 +2451,6 @@ bool LateLowerGCFrame::runOnFunction(Function &F, bool *CFGModified) {
initAll(*F.getParent());
smallAllocFunc = getOrDeclare(jl_well_known::GCSmallAlloc);
LLVM_DEBUG(dbgs() << "GC ROOT PLACEMENT: Processing function " << F.getName() << "\n");
if (!pgcstack_getter && !adoptthread_func)
return CleanupIR(F, nullptr, CFGModified);

pgcstack = getPGCstack(F);
if (!pgcstack)
Expand Down
24 changes: 15 additions & 9 deletions src/llvm-pass-helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,25 @@ void JuliaPassContext::initAll(Module &M)
T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx);
}

llvm::CallInst *JuliaPassContext::getPGCstack(llvm::Function &F) const
llvm::Value *JuliaPassContext::getPGCstack(llvm::Function &F) const
{
if (!pgcstack_getter && !adoptthread_func)
return nullptr;
for (auto &I : F.getEntryBlock()) {
if (CallInst *callInst = dyn_cast<CallInst>(&I)) {
Value *callee = callInst->getCalledOperand();
if ((pgcstack_getter && callee == pgcstack_getter) ||
(adoptthread_func && callee == adoptthread_func)) {
return callInst;
if (pgcstack_getter || adoptthread_func) {
for (auto &I : F.getEntryBlock()) {
if (CallInst *callInst = dyn_cast<CallInst>(&I)) {
Value *callee = callInst->getCalledOperand();
if ((pgcstack_getter && callee == pgcstack_getter) ||
(adoptthread_func && callee == adoptthread_func)) {
return callInst;
}
}
}
}
if (F.getCallingConv() == CallingConv::Swift) {
for (auto &arg : F.args()) {
if (arg.hasSwiftSelfAttr())
return &arg;
}
}
return nullptr;
}

Expand Down
3 changes: 2 additions & 1 deletion src/llvm-pass-helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ struct JuliaPassContext {

// Gets a call to the `julia.get_pgcstack' intrinsic in the entry
// point of the given function, if there exists such a call.
// Otherwise, gets a swiftself argument, if there exists such an argument.
// Otherwise, `nullptr` is returned.
llvm::CallInst *getPGCstack(llvm::Function &F) const;
llvm::Value *getPGCstack(llvm::Function &F) const;

// Gets the intrinsic or well-known function that conforms to
// the given description if it exists in the module. If not,
Expand Down
6 changes: 3 additions & 3 deletions src/llvm-ptls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,13 +317,13 @@ bool LowerPTLS::run(bool *CFGModified)
need_init = false;
}

for (auto it = pgcstack_getter->user_begin(); it != pgcstack_getter->user_end();) {
for (auto it = pgcstack_getter->user_begin(); it != pgcstack_getter->user_end(); ) {
auto call = cast<CallInst>(*it);
++it;
auto f = call->getCaller();
Value *pgcstack = NULL;
for (Function::arg_iterator arg = f->arg_begin(); arg != f->arg_end();++arg) {
if (arg->hasSwiftSelfAttr()){
for (Function::arg_iterator arg = f->arg_begin(); arg != f->arg_end(); ++arg) {
if (arg->hasSwiftSelfAttr()) {
pgcstack = &*arg;
break;
}
Expand Down

0 comments on commit 7e7f74f

Please sign in to comment.