Description
rustc seems to generate invalid/unsupported LLVM IR on macOS for release builds.
Linux and debug builds seem to be unaffected.
Code
#[allow(dead_code)] // There are warnings because some of RugInt's members are never accessed.
// This represents the arbitrary precision Integer type from the rug crate.
struct RugInt {
pub m: i32,
pub n: i32,
pub o: *const i32,
}
impl RugInt {
// Integer::new in the rug crate.
pub const fn new() -> Self {
const VALUE_REF: &i32 = &0x1D1E_6010;
Self {
m: 0,
n: 0,
o: VALUE_REF as *const i32,
}
}
}
fn main() {
unsafe {
// Allocate memory for an array of 32 RugInts.
let n = 32;
let layout = std::alloc::Layout::array::<RugInt>(n).unwrap();
let ptr = std::alloc::alloc(layout) as *mut RugInt;
// Initialize the array with empty RugInts.
for i in 0..n {
ptr.add(i).write(RugInt::new());
}
// Don't let the compiler remove the code by printing a value in the array.
println!("{}", (*ptr).m);
}
}
Meta
This issue occurs on the stable version
rustc --version --verbose
:
rustc 1.55.0 (c8dfcfe04 2021-09-06)
binary: rustc
commit-hash: c8dfcfe046a7680554bf4eb612bad840e7631c4b
commit-date: 2021-09-06
host: x86_64-apple-darwin
release: 1.55.0
LLVM version: 12.0.1
as well as the most recent nightly version:
rustc 1.57.0-nightly (2b862bed9 2021-09-23)
binary: rustc
commit-hash: 2b862bed9889808b69629fd7246317189b9517a5
commit-date: 2021-09-23
host: x86_64-apple-darwin
release: 1.57.0-nightly
LLVM version: 13.0.0
Error output
cargo build --release
Compiling bug v0.1.0 (/path/bug)
LLVM ERROR: Unsupported expression in static initializer: zext (i64 ptrtoint (<{ [4 x i8] }>* @alloc3 to i64) to i128)
error: could not compile `bug`
Initial investigation
By running cargo rustc --release -- --emit=llvm-ir
to emit the LLVM, I could narrow the issue down a bit.
The faulty initializer belongs to a constant called @.memset_pattern
:
@.memset_pattern = private unnamed_addr constant i128 zext (i64 ptrtoint (<{ [4 x i8] }>* @alloc3 to i64) to i128), align 16
This constant is used inside the main
function in a call to memset_pattern16
call void @memset_pattern16(i8* %0, i8* bitcast (i128* @.memset_pattern to i8*), i64 512)
This function is (as far as I know) not available on Linux which explains why the bug doesn't occur on Linux.
This function seems to replace the initialization loop in the code above which explains why the bug doesn't occur on debug builds.
The optimization seems to be very sensitive: When I removed the second member in the struct, the code compiles, because the compiler unrolls the loop instead of calling memset_pattern16.