Skip to content
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

AllocOpt: Fix stack lowering where alloca continas boxed and unboxed data #55306

Merged
merged 9 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/llvm-alloc-helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@
memop.isaggr = isa<StructType>(elty) || isa<ArrayType>(elty) || isa<VectorType>(elty);
memop.isobjref = hasObjref(elty);
auto &field = getField(offset, size, elty);

Check warning on line 91 in src/llvm-alloc-helpers.cpp

View workflow job for this annotation

GitHub Actions / Check whitespace

Whitespace check

trailing whitespace
vchuravy marked this conversation as resolved.
Show resolved Hide resolved
field.second.hasunboxed |= !hasObjref(elty) || (hasObjref(elty) && !isa<PointerType>(elty));

if (field.second.hasobjref != memop.isobjref)
field.second.multiloc = true; // can't split this field, since it contains a mix of references and bits
if (!isstore)
Expand Down Expand Up @@ -198,6 +201,7 @@
auto elty = inst->getType();
required.use_info.has_unknown_objref |= hasObjref(elty);
required.use_info.has_unknown_objrefaggr |= hasObjref(elty) && !isa<PointerType>(elty);
required.use_info.has_unknown_unboxed |= !hasObjref(elty) || (hasObjref(elty) && !isa<PointerType>(elty));
required.use_info.hasunknownmem = true;
} else if (!required.use_info.addMemOp(inst, 0, cur.offset,
inst->getType(),
Expand Down Expand Up @@ -289,6 +293,7 @@
auto elty = storev->getType();
required.use_info.has_unknown_objref |= hasObjref(elty);
required.use_info.has_unknown_objrefaggr |= hasObjref(elty) && !isa<PointerType>(elty);
required.use_info.has_unknown_unboxed |= !hasObjref(elty) || (hasObjref(elty) && !isa<PointerType>(elty));
required.use_info.hasunknownmem = true;
} else if (!required.use_info.addMemOp(inst, use->getOperandNo(),
cur.offset, storev->getType(),
Expand All @@ -310,10 +315,14 @@
}
required.use_info.hasload = true;
auto storev = isa<AtomicCmpXchgInst>(inst) ? cast<AtomicCmpXchgInst>(inst)->getNewValOperand() : cast<AtomicRMWInst>(inst)->getValOperand();
Type *elty = storev->getType();
if (cur.offset == UINT32_MAX || !required.use_info.addMemOp(inst, use->getOperandNo(),
cur.offset, storev->getType(),
cur.offset, elty,
true, required.DL)) {
LLVM_DEBUG(dbgs() << "Atomic inst has unknown offset\n");
required.use_info.has_unknown_objref |= hasObjref(elty);
required.use_info.has_unknown_objrefaggr |= hasObjref(elty) && !isa<PointerType>(elty);
required.use_info.has_unknown_unboxed |= !hasObjref(elty) || (hasObjref(elty) && !isa<PointerType>(elty));
required.use_info.hasunknownmem = true;
}
required.use_info.refload = true;
Expand Down
7 changes: 7 additions & 0 deletions src/llvm-alloc-helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ namespace jl_alloc {
bool hasaggr:1;
bool multiloc:1;
bool hasload:1;
// The alloc has a unboxed object at this offset.
bool hasunboxed:1;
llvm::Type *elty;
llvm::SmallVector<MemOp,4> accesses;
Field(uint32_t size, llvm::Type *elty)
Expand All @@ -54,6 +56,7 @@ namespace jl_alloc {
hasaggr(false),
multiloc(false),
hasload(false),
hasunboxed(false),
elty(elty)
{
}
Expand Down Expand Up @@ -95,6 +98,9 @@ namespace jl_alloc {
// The alloc has an aggregate Julia object reference not in an explicit field.
bool has_unknown_objrefaggr:1;

// The alloc has an unboxed object at an unknown offset.
bool has_unknown_unboxed:1;

void reset()
{
escaped = false;
Expand All @@ -110,6 +116,7 @@ namespace jl_alloc {
allockind = llvm::AllocFnKind::Unknown;
has_unknown_objref = false;
has_unknown_objrefaggr = false;
has_unknown_unboxed = false;
uses.clear();
preserves.clear();
memops.clear();
Expand Down
15 changes: 15 additions & 0 deletions src/llvm-alloc-opt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,12 @@ void Optimizer::optimizeAll()
removeAlloc(orig);
continue;
}
bool has_unboxed = use_info.has_unknown_bits;
bool has_ref = use_info.has_unknown_objref;
bool has_refaggr = use_info.has_unknown_objrefaggr;
for (auto memop: use_info.memops) {
auto &field = memop.second;
has_unboxed |= field.hasbits;
if (field.hasobjref) {
has_ref = true;
// This can be relaxed a little based on hasload
Expand Down Expand Up @@ -284,6 +286,19 @@ void Optimizer::optimizeAll()
splitOnStack(orig);
continue;
}
// The move to stack code below, if has_ref is set, changes the allocation to an array of jlvalue_t's. This is fine
// if all objects are jlvalue_t's. However, if part of the allocation is not a julia object (e.g. it is a { float, jlvaluet }),
// then the moveToStackCAll will create a [2 x jlvaluet] bitcast to { float, jlvaluet }. The float clearly is not a GC
// value and will result in segfaults and other problems.
wsmoses marked this conversation as resolved.
Show resolved Hide resolved
if (has_unboxed && has_ref) {
REMARK([&]() {
return OptimizationRemarkMissed(DEBUG_TYPE, "Escaped", orig)
<< "GC allocation could not be split and contained both julia object and non-julia-object data, unable to move to stack " << ore::NV("GC Allocation", orig);
wsmoses marked this conversation as resolved.
Show resolved Hide resolved
});
if (use_info.hastypeof)
optimizeTag(orig);
continue;
}
REMARK([&](){
return OptimizationRemark(DEBUG_TYPE, "Stack Move Allocation", orig)
<< "GC allocation moved to stack " << ore::NV("GC Allocation", orig);
Expand Down
37 changes: 37 additions & 0 deletions test/llvmpasses/alloc-opt-bits.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
; This file is a part of Julia. License is MIT: https://julialang.org/license

; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='function(AllocOpt)' -S %s | FileCheck %s


@tag = external addrspace(10) global {}

@glob = external addrspace(10) global {}

; Test that the gc_preserve intrinsics are deleted directly.

; CHECK-LABEL: @ptr_and_bits
; CHECK-NOT: alloca
; CHECK: call noalias ptr addrspace(10) @julia.gc_alloc_obj

define void @ptr_and_bits(ptr %fptr, i1 %b, i1 %b2, i32 %idx) {
%pgcstack = call ptr @julia.get_pgcstack()
%ptls = call ptr @julia.ptls_states()
%ptls_i8 = bitcast ptr %ptls to ptr
%v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 16, ptr addrspace(10) @tag)

%g0 = getelementptr { i64, ptr addrspace(10) }, ptr addrspace(10) %v, i32 %idx, i32 1
store ptr addrspace(10) @glob, ptr addrspace(10) %g0

%g1 = getelementptr { i64, ptr addrspace(10) }, ptr addrspace(10) %v, i32 %idx, i32 0
store i64 7, ptr addrspace(10) %g1

%res = load ptr addrspace(10), ptr addrspace(10) %g0
%res2 = load i64, ptr addrspace(10) %g1
ret void
}

declare noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr, i64, ptr addrspace(10))

declare ptr @julia.ptls_states()

declare ptr @julia.get_pgcstack()
Loading