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 6 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 @@
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 @@
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 an unboxed value (e.g. it is a { float, jlvaluet }),
// then moveToStack will create a [2 x jlvaluet] bitcast to { float, jlvaluet }.

Check warning on line 291 in src/llvm-alloc-opt.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
// This later causes the GC rooting pass, to miss-characterize the float as a pointer to a GC value
if (has_unboxed && has_ref) {
REMARK([&]() {
return OptimizationRemarkMissed(DEBUG_TYPE, "Escaped", orig)
<< "GC allocation could not be split since it contains both boxed and unboxed values, unable to move to stack " << ore::NV("GC Allocation", orig);
});
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