Skip to content

Commit 03d4299

Browse files
authored
Rollup merge of #111318 - scottmcm:operand-value-poison, r=compiler-errors
Add a distinct `OperandValue::ZeroSized` variant for ZSTs These tend to have special handling in a bunch of places anyway, so the variant helps remember that. And I think it's easier to grok than `Aggregate`s sometimes being `Immediates` (after all, I previously got that wrong and caused #109992). As a minor bonus, it means we don't need to generate poison LLVM values for ZSTs to pass around in `OperandValue::Immediate`s. Inspired by #110021 (comment), so r? `@compiler-errors`
2 parents 0baa301 + bf36193 commit 03d4299

File tree

11 files changed

+157
-76
lines changed

11 files changed

+157
-76
lines changed

compiler/rustc_codegen_gcc/src/builder.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
758758
assert_eq!(place.llextra.is_some(), place.layout.is_unsized());
759759

760760
if place.layout.is_zst() {
761-
return OperandRef::new_zst(self, place.layout);
761+
return OperandRef::zero_sized(place.layout);
762762
}
763763

764764
fn scalar_load_metadata<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, load: RValue<'gcc>, scalar: &abi::Scalar) {

compiler/rustc_codegen_gcc/src/type_of.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
159159
fn is_gcc_immediate(&self) -> bool {
160160
match self.abi {
161161
Abi::Scalar(_) | Abi::Vector { .. } => true,
162-
Abi::ScalarPair(..) => false,
163-
Abi::Uninhabited | Abi::Aggregate { .. } => self.is_zst(),
162+
Abi::ScalarPair(..) | Abi::Uninhabited | Abi::Aggregate { .. } => false,
164163
}
165164
}
166165

compiler/rustc_codegen_llvm/src/builder.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
486486
assert_eq!(place.llextra.is_some(), place.layout.is_unsized());
487487

488488
if place.layout.is_zst() {
489-
return OperandRef::new_zst(self, place.layout);
489+
return OperandRef::zero_sized(place.layout);
490490
}
491491

492492
#[instrument(level = "trace", skip(bx))]

compiler/rustc_codegen_llvm/src/type_of.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
198198
fn is_llvm_immediate(&self) -> bool {
199199
match self.abi {
200200
Abi::Scalar(_) | Abi::Vector { .. } => true,
201-
Abi::ScalarPair(..) => false,
202-
Abi::Uninhabited | Abi::Aggregate { .. } => self.is_zst(),
201+
Abi::ScalarPair(..) | Abi::Uninhabited | Abi::Aggregate { .. } => false,
203202
}
204203
}
205204

compiler/rustc_codegen_ssa/src/base.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
295295
let (base, info) = match bx.load_operand(src).val {
296296
OperandValue::Pair(base, info) => unsize_ptr(bx, base, src_ty, dst_ty, Some(info)),
297297
OperandValue::Immediate(base) => unsize_ptr(bx, base, src_ty, dst_ty, None),
298-
OperandValue::Ref(..) => bug!(),
298+
OperandValue::Ref(..) | OperandValue::ZeroSized => bug!(),
299299
};
300300
OperandValue::Pair(base, info).store(bx, dst);
301301
}

compiler/rustc_codegen_ssa/src/mir/block.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::operand::OperandRef;
2-
use super::operand::OperandValue::{Immediate, Pair, Ref};
2+
use super::operand::OperandValue::{Immediate, Pair, Ref, ZeroSized};
33
use super::place::PlaceRef;
44
use super::{CachedLlbb, FunctionCx, LocalRef};
55

@@ -427,6 +427,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
427427
assert_eq!(align, op.layout.align.abi, "return place is unaligned!");
428428
llval
429429
}
430+
ZeroSized => bug!("ZST return value shouldn't be in PassMode::Cast"),
430431
};
431432
let ty = bx.cast_backend_type(cast_ty);
432433
let addr = bx.pointercast(llslot, bx.type_ptr_to(ty));
@@ -1386,6 +1387,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
13861387
(llval, align, true)
13871388
}
13881389
}
1390+
ZeroSized => match arg.mode {
1391+
PassMode::Indirect { .. } => {
1392+
// Though `extern "Rust"` doesn't pass ZSTs, some ABIs pass
1393+
// a pointer for `repr(C)` structs even when empty, so get
1394+
// one from an `alloca` (which can be left uninitialized).
1395+
let scratch = PlaceRef::alloca(bx, arg.layout);
1396+
(scratch.llval, scratch.align, true)
1397+
}
1398+
_ => bug!("ZST {op:?} wasn't ignored, but was passed with abi {arg:?}"),
1399+
},
13891400
};
13901401

13911402
if by_ref && !arg.is_indirect() {

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

+3
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
352352
bx.set_var_name(a, &(name.clone() + ".0"));
353353
bx.set_var_name(b, &(name.clone() + ".1"));
354354
}
355+
OperandValue::ZeroSized => {
356+
// These never have a value to talk about
357+
}
355358
},
356359
LocalRef::PendingOperand => {}
357360
}

compiler/rustc_codegen_ssa/src/mir/mod.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,13 @@ enum LocalRef<'tcx, V> {
129129
PendingOperand,
130130
}
131131

132-
impl<'a, 'tcx, V: CodegenObject> LocalRef<'tcx, V> {
133-
fn new_operand<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
134-
bx: &mut Bx,
135-
layout: TyAndLayout<'tcx>,
136-
) -> LocalRef<'tcx, V> {
132+
impl<'tcx, V: CodegenObject> LocalRef<'tcx, V> {
133+
fn new_operand(layout: TyAndLayout<'tcx>) -> LocalRef<'tcx, V> {
137134
if layout.is_zst() {
138135
// Zero-size temporaries aren't always initialized, which
139136
// doesn't matter because they don't contain data, but
140137
// we need something in the operand.
141-
LocalRef::Operand(OperandRef::new_zst(bx, layout))
138+
LocalRef::Operand(OperandRef::zero_sized(layout))
142139
} else {
143140
LocalRef::PendingOperand
144141
}
@@ -249,7 +246,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
249246
}
250247
} else {
251248
debug!("alloc: {:?} -> operand", local);
252-
LocalRef::new_operand(&mut start_bx, layout)
249+
LocalRef::new_operand(layout)
253250
}
254251
};
255252

@@ -355,7 +352,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
355352
let local = |op| LocalRef::Operand(op);
356353
match arg.mode {
357354
PassMode::Ignore => {
358-
return local(OperandRef::new_zst(bx, arg.layout));
355+
return local(OperandRef::zero_sized(arg.layout));
359356
}
360357
PassMode::Direct(_) => {
361358
let llarg = bx.get_param(llarg_idx);

compiler/rustc_codegen_ssa/src/mir/operand.rs

+26-23
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ pub enum OperandValue<V> {
4545
/// as returned by [`LayoutTypeMethods::scalar_pair_element_backend_type`]
4646
/// with `immediate: true`.
4747
Pair(V, V),
48+
/// A value taking no bytes, and which therefore needs no LLVM value at all.
49+
///
50+
/// If you ever need a `V` to pass to something, get a fresh poison value
51+
/// from [`ConstMethods::const_poison`].
52+
///
53+
/// An `OperandValue` *must* be this variant for any type for which
54+
/// `is_zst` on its `Layout` returns `true`.
55+
ZeroSized,
4856
}
4957

5058
/// An `OperandRef` is an "SSA" reference to a Rust value, along with
@@ -71,15 +79,9 @@ impl<V: CodegenObject> fmt::Debug for OperandRef<'_, V> {
7179
}
7280

7381
impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
74-
pub fn new_zst<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
75-
bx: &mut Bx,
76-
layout: TyAndLayout<'tcx>,
77-
) -> OperandRef<'tcx, V> {
82+
pub fn zero_sized(layout: TyAndLayout<'tcx>) -> OperandRef<'tcx, V> {
7883
assert!(layout.is_zst());
79-
OperandRef {
80-
val: OperandValue::Immediate(bx.const_poison(bx.immediate_backend_type(layout))),
81-
layout,
82-
}
84+
OperandRef { val: OperandValue::ZeroSized, layout }
8385
}
8486

8587
pub fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
@@ -97,7 +99,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
9799
let llval = bx.scalar_to_backend(x, scalar, bx.immediate_backend_type(layout));
98100
OperandValue::Immediate(llval)
99101
}
100-
ConstValue::ZeroSized => return OperandRef::new_zst(bx, layout),
102+
ConstValue::ZeroSized => return OperandRef::zero_sized(layout),
101103
ConstValue::Slice { data, start, end } => {
102104
let Abi::ScalarPair(a_scalar, _) = layout.abi else {
103105
bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
@@ -178,7 +180,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
178180
);
179181
OperandRef { val: OperandValue::Pair(a_val, b_val), layout }
180182
}
181-
_ if layout.is_zst() => OperandRef::new_zst(bx, layout),
183+
_ if layout.is_zst() => OperandRef::zero_sized(layout),
182184
_ => {
183185
// Neither a scalar nor scalar pair. Load from a place
184186
let init = bx.const_data_from_alloc(alloc);
@@ -216,6 +218,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
216218
OperandValue::Immediate(llptr) => (llptr, None),
217219
OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)),
218220
OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self),
221+
OperandValue::ZeroSized => bug!("Deref of ZST operand {:?}", self),
219222
};
220223
let layout = cx.layout_of(projected_ty);
221224
PlaceRef { llval: llptr, llextra, layout, align: layout.align.abi }
@@ -273,9 +276,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
273276

274277
let mut val = match (self.val, self.layout.abi) {
275278
// If the field is ZST, it has no data.
276-
_ if field.is_zst() => {
277-
return OperandRef::new_zst(bx, field);
278-
}
279+
_ if field.is_zst() => OperandValue::ZeroSized,
279280

280281
// Newtype of a scalar, scalar pair or vector.
281282
(OperandValue::Immediate(_) | OperandValue::Pair(..), _)
@@ -306,6 +307,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
306307
};
307308

308309
match (&mut val, field.abi) {
310+
(OperandValue::ZeroSized, _) => {}
309311
(
310312
OperandValue::Immediate(llval),
311313
Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. },
@@ -359,16 +361,18 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
359361
impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
360362
/// Returns an `OperandValue` that's generally UB to use in any way.
361363
///
362-
/// Depending on the `layout`, returns an `Immediate` or `Pair` containing
363-
/// poison value(s), or a `Ref` containing a poison pointer.
364+
/// Depending on the `layout`, returns `ZeroSized` for ZSTs, an `Immediate` or
365+
/// `Pair` containing poison value(s), or a `Ref` containing a poison pointer.
364366
///
365367
/// Supports sized types only.
366368
pub fn poison<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
367369
bx: &mut Bx,
368370
layout: TyAndLayout<'tcx>,
369371
) -> OperandValue<V> {
370372
assert!(layout.is_sized());
371-
if bx.cx().is_backend_immediate(layout) {
373+
if layout.is_zst() {
374+
OperandValue::ZeroSized
375+
} else if bx.cx().is_backend_immediate(layout) {
372376
let ibty = bx.cx().immediate_backend_type(layout);
373377
OperandValue::Immediate(bx.const_poison(ibty))
374378
} else if bx.cx().is_backend_scalar_pair(layout) {
@@ -421,12 +425,11 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
421425
flags: MemFlags,
422426
) {
423427
debug!("OperandRef::store: operand={:?}, dest={:?}", self, dest);
424-
// Avoid generating stores of zero-sized values, because the only way to have a zero-sized
425-
// value is through `undef`, and store itself is useless.
426-
if dest.layout.is_zst() {
427-
return;
428-
}
429428
match self {
429+
OperandValue::ZeroSized => {
430+
// Avoid generating stores of zero-sized values, because the only way to have a zero-sized
431+
// value is through `undef`/`poison`, and the store itself is useless.
432+
}
430433
OperandValue::Ref(r, None, source_align) => {
431434
if flags.contains(MemFlags::NONTEMPORAL) {
432435
// HACK(nox): This is inefficient but there is no nontemporal memcpy.
@@ -527,7 +530,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
527530
// checks in `codegen_consume` and `extract_field`.
528531
let elem = o.layout.field(bx.cx(), 0);
529532
if elem.is_zst() {
530-
o = OperandRef::new_zst(bx, elem);
533+
o = OperandRef::zero_sized(elem);
531534
} else {
532535
return None;
533536
}
@@ -561,7 +564,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
561564

562565
// ZSTs don't require any actual memory access.
563566
if layout.is_zst() {
564-
return OperandRef::new_zst(bx, layout);
567+
return OperandRef::zero_sized(layout);
565568
}
566569

567570
if let Some(o) = self.maybe_codegen_consume_direct(bx, place_ref) {

0 commit comments

Comments
 (0)