Skip to content

Commit 501eb1e

Browse files
authored
Rollup merge of rust-lang#147576 - Mark-Simulacrum:fix-offset-zst, r=nnethercote
Fix ICE on offsetted ZST pointer I'm not sure this is the *right* fix, but it's simple enough and does roughly what I'd expect. Like with the previous optimization to codegen usize rather than a zero-sized static, there's no guarantee that we continue returning a particular value from the offsetting. A grep for `const_usize.*align` found the same code copied to rustc_codegen_gcc and cranelift but a quick skim didn't find other cases of similar 'optimization'. That said, I'm not convinced I caught everything, it's not trivial to search for this. Closes rust-lang#147516
2 parents 93b79fc + 7a513dd commit 501eb1e

File tree

4 files changed

+28
-6
lines changed

4 files changed

+28
-6
lines changed

compiler/rustc_codegen_cranelift/src/constant.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,10 @@ pub(crate) fn codegen_const_value<'tcx>(
138138
let base_addr = match fx.tcx.global_alloc(alloc_id) {
139139
GlobalAlloc::Memory(alloc) => {
140140
if alloc.inner().len() == 0 {
141-
assert_eq!(offset, Size::ZERO);
142-
fx.bcx.ins().iconst(fx.pointer_type, alloc.inner().align.bytes() as i64)
141+
fx.bcx.ins().iconst(
142+
fx.pointer_type,
143+
alloc.inner().align.bytes().wrapping_add(offset.bytes()) as i64,
144+
)
143145
} else {
144146
let data_id = data_id_for_alloc_id(
145147
&mut fx.constants_cx,

compiler/rustc_codegen_gcc/src/common.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,9 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
247247
// This avoids generating a zero-sized constant value and actually needing a
248248
// real address at runtime.
249249
if alloc.inner().len() == 0 {
250-
assert_eq!(offset.bytes(), 0);
251-
let val = self.const_usize(alloc.inner().align.bytes());
250+
let val = self.const_usize(
251+
alloc.inner().align.bytes().wrapping_add(offset.bytes()),
252+
);
252253
return if matches!(layout.primitive(), Pointer(_)) {
253254
self.context.new_cast(None, val, ty)
254255
} else {

compiler/rustc_codegen_llvm/src/common.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,9 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
281281
// This avoids generating a zero-sized constant value and actually needing a
282282
// real address at runtime.
283283
if alloc.inner().len() == 0 {
284-
assert_eq!(offset.bytes(), 0);
285-
let llval = self.const_usize(alloc.inner().align.bytes());
284+
let llval = self.const_usize(
285+
alloc.inner().align.bytes().wrapping_add(offset.bytes()),
286+
);
286287
return if matches!(layout.primitive(), Pointer(_)) {
287288
unsafe { llvm::LLVMConstIntToPtr(llval, llty) }
288289
} else {

tests/ui/consts/zst_no_llvm_alloc.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
//@ run-pass
22

3+
// We need some non-1 alignment to test we use the alignment of the type in the compiler.
34
#[repr(align(4))]
45
struct Foo;
56

67
static FOO: Foo = Foo;
78

9+
// This tests for regression of https://github.com/rust-lang/rust/issues/147516
10+
//
11+
// THe compiler will codegen `&Zst` without creating a real allocation, just a properly aligned
12+
// `usize` (i.e., ptr::dangling). However, code can add an arbitrary offset from that base
13+
// allocation. We confirm here that we correctly codegen that offset combined with the necessary
14+
// alignment of the base &() as a 1-ZST and &Foo as a 4-ZST.
15+
const A: *const () = (&() as *const ()).wrapping_byte_add(2);
16+
const B: *const () = (&Foo as *const _ as *const ()).wrapping_byte_add(usize::MAX);
17+
const C: *const () = (&Foo as *const _ as *const ()).wrapping_byte_add(2);
18+
819
fn main() {
920
// There's no stable guarantee that these are true.
1021
// However, we want them to be true so that our LLVM IR and runtime are a bit faster:
@@ -15,6 +26,13 @@ fn main() {
1526
let x: &'static Foo = &Foo;
1627
assert_eq!(x as *const Foo as usize, 4);
1728

29+
// * A 1-aligned ZST (1-ZST) is placed at 0x1. Then offsetting that by 2 results in 3.
30+
// * Foo is a 4-aligned ZST, so is placed at 0x4. +2 = 6
31+
// * Foo is a 4-aligned ZST, so is placed at 0x4. +usize::MAX = -1 (same bit pattern) = 3
32+
assert_eq!(A.addr(), 3);
33+
assert_eq!(B.addr(), 3);
34+
assert_eq!(C.addr(), 6);
35+
1836
// The exact addresses returned by these library functions are not necessarily stable guarantees
1937
// but for now we assert that we're still matching.
2038
#[allow(dangling_pointers_from_temporaries)]

0 commit comments

Comments
 (0)