Skip to content

Commit ccffdd7

Browse files
authored
Rollup merge of rust-lang#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 rust-lang#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 rust-lang#110021 (comment), so r? ``@compiler-errors``
2 parents 5da322f + 9495095 commit ccffdd7

File tree

11 files changed

+167
-86
lines changed

11 files changed

+167
-86
lines changed

compiler/rustc_codegen_gcc/src/builder.rs

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 2 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 2 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 12 additions & 1 deletion
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 5 additions & 8 deletions
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

Lines changed: 25 additions & 22 deletions
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);
@@ -147,6 +149,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
147149
OperandValue::Immediate(llptr) => (llptr, None),
148150
OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)),
149151
OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self),
152+
OperandValue::ZeroSized => bug!("Deref of ZST operand {:?}", self),
150153
};
151154
let layout = cx.layout_of(projected_ty);
152155
PlaceRef { llval: llptr, llextra, layout, align: layout.align.abi }
@@ -204,9 +207,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
204207

205208
let mut val = match (self.val, self.layout.abi) {
206209
// If the field is ZST, it has no data.
207-
_ if field.is_zst() => {
208-
return OperandRef::new_zst(bx, field);
209-
}
210+
_ if field.is_zst() => OperandValue::ZeroSized,
210211

211212
// Newtype of a scalar, scalar pair or vector.
212213
(OperandValue::Immediate(_) | OperandValue::Pair(..), _)
@@ -237,6 +238,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
237238
};
238239

239240
match (&mut val, field.abi) {
241+
(OperandValue::ZeroSized, _) => {}
240242
(
241243
OperandValue::Immediate(llval),
242244
Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. },
@@ -290,16 +292,18 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
290292
impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
291293
/// Returns an `OperandValue` that's generally UB to use in any way.
292294
///
293-
/// Depending on the `layout`, returns an `Immediate` or `Pair` containing
294-
/// poison value(s), or a `Ref` containing a poison pointer.
295+
/// Depending on the `layout`, returns `ZeroSized` for ZSTs, an `Immediate` or
296+
/// `Pair` containing poison value(s), or a `Ref` containing a poison pointer.
295297
///
296298
/// Supports sized types only.
297299
pub fn poison<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
298300
bx: &mut Bx,
299301
layout: TyAndLayout<'tcx>,
300302
) -> OperandValue<V> {
301303
assert!(layout.is_sized());
302-
if bx.cx().is_backend_immediate(layout) {
304+
if layout.is_zst() {
305+
OperandValue::ZeroSized
306+
} else if bx.cx().is_backend_immediate(layout) {
303307
let ibty = bx.cx().immediate_backend_type(layout);
304308
OperandValue::Immediate(bx.const_poison(ibty))
305309
} else if bx.cx().is_backend_scalar_pair(layout) {
@@ -352,12 +356,11 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
352356
flags: MemFlags,
353357
) {
354358
debug!("OperandRef::store: operand={:?}, dest={:?}", self, dest);
355-
// Avoid generating stores of zero-sized values, because the only way to have a zero-sized
356-
// value is through `undef`, and store itself is useless.
357-
if dest.layout.is_zst() {
358-
return;
359-
}
360359
match self {
360+
OperandValue::ZeroSized => {
361+
// Avoid generating stores of zero-sized values, because the only way to have a zero-sized
362+
// value is through `undef`/`poison`, and the store itself is useless.
363+
}
361364
OperandValue::Ref(r, None, source_align) => {
362365
if flags.contains(MemFlags::NONTEMPORAL) {
363366
// HACK(nox): This is inefficient but there is no nontemporal memcpy.
@@ -458,7 +461,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
458461
// checks in `codegen_consume` and `extract_field`.
459462
let elem = o.layout.field(bx.cx(), 0);
460463
if elem.is_zst() {
461-
o = OperandRef::new_zst(bx, elem);
464+
o = OperandRef::zero_sized(elem);
462465
} else {
463466
return None;
464467
}
@@ -492,7 +495,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
492495

493496
// ZSTs don't require any actual memory access.
494497
if layout.is_zst() {
495-
return OperandRef::new_zst(bx, layout);
498+
return OperandRef::zero_sized(layout);
496499
}
497500

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

0 commit comments

Comments
 (0)