diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 2461f70a86e35..b40dcb7db6cfa 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1881,12 +1881,15 @@ unsafe extern "C" { C: &Context, effects: MemoryEffects, ) -> &Attribute; - pub(crate) fn LLVMRustCreateRangeAttribute( - C: &Context, - num_bits: c_uint, - lower_words: *const u64, - upper_words: *const u64, - ) -> &Attribute; + /// Lower and upper bounds are each passed as a `u128` broken into low and high 64-bit parts. + pub(crate) safe fn LLVMRustCreateRangeAttribute<'ll>( + C: &'ll Context, + NumBits: c_uint, + LowerBoundLo: u64, + LowerBoundHi: u64, + UpperBoundLo: u64, + UpperBoundHi: u64, + ) -> &'ll Attribute; // Operations on functions pub(crate) fn LLVMRustGetOrInsertFunction<'a>( diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 7fea7b79a8cfb..d7c11aacadaac 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -112,17 +112,27 @@ pub(crate) fn CreateAllocKindAttr(llcx: &Context, kind_arg: AllocKindFlags) -> & pub(crate) fn CreateRangeAttr(llcx: &Context, size: Size, range: WrappingRange) -> &Attribute { let lower = range.start; + // LLVM treats the upper bound as exclusive, but allows wrapping. let upper = range.end.wrapping_add(1); - let lower_words = [lower as u64, (lower >> 64) as u64]; - let upper_words = [upper as u64, (upper >> 64) as u64]; - unsafe { - LLVMRustCreateRangeAttribute( - llcx, - size.bits().try_into().unwrap(), - lower_words.as_ptr(), - upper_words.as_ptr(), - ) - } + + // Split each endpoint into a pair of u64 values to make FFI easier. + let as_lo_hi_pair = |x: u128| (x as u64, (x >> 64) as u64); + let (lower_bound_lo, lower_bound_hi) = as_lo_hi_pair(lower); + let (upper_bound_lo, upper_bound_hi) = as_lo_hi_pair(upper); + + // Endpoints are given as u128, so make sure the given size isn't larger than that. + let size_bits = size.bits(); + assert!(size_bits <= 128); + let size_bits = size_bits.try_into().unwrap(); + + LLVMRustCreateRangeAttribute( + llcx, + size_bits, + lower_bound_lo, + lower_bound_hi, + upper_bound_lo, + upper_bound_hi, + ) } #[derive(Copy, Clone)] diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index e699e4b9c13f8..85e141cc24f49 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -486,10 +486,15 @@ LLVMRustCreateAllocSizeAttr(LLVMContextRef C, uint32_t ElementSizeArg) { extern "C" LLVMAttributeRef LLVMRustCreateRangeAttribute(LLVMContextRef C, unsigned NumBits, - const uint64_t LowerWords[], - const uint64_t UpperWords[]) { - return LLVMCreateConstantRangeAttribute(C, Attribute::Range, NumBits, - LowerWords, UpperWords); + uint64_t LowerBoundLo, uint64_t LowerBoundHi, + uint64_t UpperBoundLo, uint64_t UpperBoundHi) { + // For both endpoints, reassemble the lo/hi parts into into APInt values. + // APInt will automatically discard any excess bits. + ConstantRange RangeValue = { + APInt(NumBits, ArrayRef{LowerBoundLo, LowerBoundHi}), + APInt(NumBits, ArrayRef{UpperBoundLo, UpperBoundHi}), + }; + return wrap(Attribute::get(*unwrap(C), llvm::Attribute::Range, RangeValue)); } // These values **must** match ffi::AllocKindFlags. diff --git a/tests/codegen-llvm/range-attr/edge-cases.rs b/tests/codegen-llvm/range-attr/edge-cases.rs new file mode 100644 index 0000000000000..634d0b1de1ff9 --- /dev/null +++ b/tests/codegen-llvm/range-attr/edge-cases.rs @@ -0,0 +1,39 @@ +#![feature(rustc_attrs)] +#![crate_type = "lib"] +//@ edition: 2024 + +// Edge-case tests for the conversion from `rustc_abi::WrappingRange` to +// LLVM range attributes. + +#[rustc_layout_scalar_valid_range_start(1)] +pub struct LowNiche8(u8); +// CHECK: define void @low_niche_8(i8 noundef range(i8 1, 0) %_1) +#[unsafe(no_mangle)] +pub fn low_niche_8(_: LowNiche8) {} + +#[rustc_layout_scalar_valid_range_end(254)] +pub struct HighNiche8(u8); +// CHECK: define void @high_niche_8(i8 noundef range(i8 0, -1) %_1) +#[unsafe(no_mangle)] +pub fn high_niche_8(_: HighNiche8) {} + +#[rustc_layout_scalar_valid_range_start(1)] +#[rustc_layout_scalar_valid_range_end(254)] +pub struct Niches8(u8); +// CHECK: define void @niches_8(i8 noundef range(i8 1, -1) %_1) +#[unsafe(no_mangle)] +pub fn niches_8(_: Niches8) {} + +#[rustc_layout_scalar_valid_range_start(255)] +#[rustc_layout_scalar_valid_range_end(255)] +pub struct SoloHigh8(u8); +// CHECK: define void @solo_high_8(i8 noundef range(i8 -1, 0) %_1) +#[unsafe(no_mangle)] +pub fn solo_high_8(_: SoloHigh8) {} + +#[rustc_layout_scalar_valid_range_start(1)] +#[rustc_layout_scalar_valid_range_end(340282366920938463463374607431768211454)] // (u128::MAX - 1) +pub struct Niches128(u128); +// CHECK: define void @niches_128(i128 noundef range(i128 1, -1) %_1) +#[unsafe(no_mangle)] +pub fn niches_128(_: Niches128) {} diff --git a/tests/codegen-llvm/range-attribute.rs b/tests/codegen-llvm/range-attr/range-attribute.rs similarity index 100% rename from tests/codegen-llvm/range-attribute.rs rename to tests/codegen-llvm/range-attr/range-attribute.rs