codegen: use new {re,de,}allocator annotations in llvm

This obviates the patch that teaches LLVM internals about
_rust_{re,de}alloc functions by putting annotations directly in the IR
for the optimizer.

The sole test change is required to anchor FileCheck to the body of the
`box_uninitialized` method, so it doesn't see the `allocalign` on
`__rust_alloc` and get mad about the string `alloca` showing up. Since I
was there anyway, I added some checks on the attributes to prove the
right attributes got set.

While we're here, we also emit allocator attributes on
__rust_alloc_zeroed. This should allow LLVM to perform more
optimizations for zeroed blocks, and probably fixes #90032. [This
comment](https://github.com/rust-lang/rust/issues/24194#issuecomment-308791157)
mentions "weird UB-like behaviour with bitvec iterators in
rustc_data_structures" so we may need to back this change out if things
go wrong.

The new test cases require LLVM 15, so we copy them into LLVM
14-supporting versions, which we can delete when we drop LLVM 14.
This commit is contained in:
Augie Fackler 2022-03-21 15:30:54 -04:00
parent 2fdbf075cf
commit 130a1df71e
14 changed files with 356 additions and 4 deletions

View file

@ -234,6 +234,14 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
return Attribute::SanitizeMemTag;
case ShadowCallStack:
return Attribute::ShadowCallStack;
case AllocSize:
return Attribute::AllocSize;
#if LLVM_VERSION_GE(15, 0)
case AllocatedPointer:
return Attribute::AllocatedPointer;
case AllocAlign:
return Attribute::AllocAlign;
#endif
}
report_fatal_error("bad AttributeKind");
}
@ -305,6 +313,67 @@ extern "C" LLVMAttributeRef LLVMRustCreateUWTableAttr(LLVMContextRef C, bool Asy
#endif
}
extern "C" LLVMAttributeRef LLVMRustCreateAllocSizeAttr(LLVMContextRef C, uint32_t ElementSizeArg) {
return wrap(Attribute::getWithAllocSizeArgs(*unwrap(C), ElementSizeArg, None));
}
#if LLVM_VERSION_GE(15, 0)
// These values **must** match ffi::AllocKindFlags.
// It _happens_ to match the LLVM values of llvm::AllocFnKind,
// but that's happenstance and we do explicit conversions before
// passing them to LLVM.
enum class LLVMRustAllocKindFlags : uint64_t {
Unknown = 0,
Alloc = 1,
Realloc = 1 << 1,
Free = 1 << 2,
Uninitialized = 1 << 3,
Zeroed = 1 << 4,
Aligned = 1 << 5,
};
static LLVMRustAllocKindFlags operator&(LLVMRustAllocKindFlags A, LLVMRustAllocKindFlags B) {
return static_cast<LLVMRustAllocKindFlags>(static_cast<uint64_t>(A) &
static_cast<uint64_t>(B));
}
static bool isSet(LLVMRustAllocKindFlags F) { return F != LLVMRustAllocKindFlags::Unknown; }
static llvm::AllocFnKind allocKindFromRust(LLVMRustAllocKindFlags F) {
llvm::AllocFnKind AFK = llvm::AllocFnKind::Unknown;
if (isSet(F & LLVMRustAllocKindFlags::Alloc)) {
AFK |= llvm::AllocFnKind::Alloc;
}
if (isSet(F & LLVMRustAllocKindFlags::Realloc)) {
AFK |= llvm::AllocFnKind::Realloc;
}
if (isSet(F & LLVMRustAllocKindFlags::Free)) {
AFK |= llvm::AllocFnKind::Free;
}
if (isSet(F & LLVMRustAllocKindFlags::Uninitialized)) {
AFK |= llvm::AllocFnKind::Uninitialized;
}
if (isSet(F & LLVMRustAllocKindFlags::Zeroed)) {
AFK |= llvm::AllocFnKind::Zeroed;
}
if (isSet(F & LLVMRustAllocKindFlags::Aligned)) {
AFK |= llvm::AllocFnKind::Aligned;
}
return AFK;
}
#endif
extern "C" LLVMAttributeRef LLVMRustCreateAllocKindAttr(LLVMContextRef C, uint64_t AllocKindArg) {
#if LLVM_VERSION_GE(15, 0)
return wrap(Attribute::get(*unwrap(C), Attribute::AllocKind,
static_cast<uint64_t>(allocKindFromRust(static_cast<LLVMRustAllocKindFlags>(AllocKindArg)))));
#else
report_fatal_error(
"allockind attributes are new in LLVM 15 and should not be used on older LLVMs");
#endif
}
// Enable a fast-math flag
//
// https://llvm.org/docs/LangRef.html#fast-math-flags