rustc_codegen_ssa/mir/operand.rs
1use std::fmt;
2
3use itertools::Either;
4use rustc_abi as abi;
5use rustc_abi::{
6 Align, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, TagEncoding, VariantIdx, Variants,
7};
8use rustc_middle::mir::interpret::{Pointer, Scalar, alloc_range};
9use rustc_middle::mir::{self, ConstValue};
10use rustc_middle::ty::Ty;
11use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
12use rustc_middle::{bug, span_bug};
13use rustc_session::config::OptLevel;
14use tracing::{debug, instrument};
15
16use super::place::{PlaceRef, PlaceValue};
17use super::rvalue::transmute_scalar;
18use super::{FunctionCx, LocalRef};
19use crate::MemFlags;
20use crate::common::IntPredicate;
21use crate::traits::*;
22
23/// The representation of a Rust value. The enum variant is in fact
24/// uniquely determined by the value's type, but is kept as a
25/// safety check.
26#[derive(Copy, Clone, Debug)]
27pub enum OperandValue<V> {
28 /// A reference to the actual operand. The data is guaranteed
29 /// to be valid for the operand's lifetime.
30 /// The second value, if any, is the extra data (vtable or length)
31 /// which indicates that it refers to an unsized rvalue.
32 ///
33 /// An `OperandValue` *must* be this variant for any type for which
34 /// [`LayoutTypeCodegenMethods::is_backend_ref`] returns `true`.
35 /// (That basically amounts to "isn't one of the other variants".)
36 ///
37 /// This holds a [`PlaceValue`] (like a [`PlaceRef`] does) with a pointer
38 /// to the location holding the value. The type behind that pointer is the
39 /// one returned by [`LayoutTypeCodegenMethods::backend_type`].
40 Ref(PlaceValue<V>),
41 /// A single LLVM immediate value.
42 ///
43 /// An `OperandValue` *must* be this variant for any type for which
44 /// [`LayoutTypeCodegenMethods::is_backend_immediate`] returns `true`.
45 /// The backend value in this variant must be the *immediate* backend type,
46 /// as returned by [`LayoutTypeCodegenMethods::immediate_backend_type`].
47 Immediate(V),
48 /// A pair of immediate LLVM values. Used by wide pointers too.
49 ///
50 /// # Invariants
51 /// - For `Pair(a, b)`, `a` is always at offset 0, but may have `FieldIdx(1..)`
52 /// - `b` is not at offset 0, because `V` is not a 1ZST type.
53 /// - `a` and `b` will have a different FieldIdx, but otherwise `b`'s may be lower
54 /// or they may not be adjacent, due to arbitrary numbers of 1ZST fields that
55 /// will not affect the shape of the data which determines if `Pair` will be used.
56 /// - An `OperandValue` *must* be this variant for any type for which
57 /// [`LayoutTypeCodegenMethods::is_backend_scalar_pair`] returns `true`.
58 /// - The backend values in this variant must be the *immediate* backend types,
59 /// as returned by [`LayoutTypeCodegenMethods::scalar_pair_element_backend_type`]
60 /// with `immediate: true`.
61 Pair(V, V),
62 /// A value taking no bytes, and which therefore needs no LLVM value at all.
63 ///
64 /// If you ever need a `V` to pass to something, get a fresh poison value
65 /// from [`ConstCodegenMethods::const_poison`].
66 ///
67 /// An `OperandValue` *must* be this variant for any type for which
68 /// `is_zst` on its `Layout` returns `true`. Note however that
69 /// these values can still require alignment.
70 ZeroSized,
71}
72
73impl<V: CodegenObject> OperandValue<V> {
74 /// Treat this value as a pointer and return the data pointer and
75 /// optional metadata as backend values.
76 ///
77 /// If you're making a place, use [`Self::deref`] instead.
78 pub(crate) fn pointer_parts(self) -> (V, Option<V>) {
79 match self {
80 OperandValue::Immediate(llptr) => (llptr, None),
81 OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)),
82 _ => bug!("OperandValue cannot be a pointer: {self:?}"),
83 }
84 }
85
86 /// Treat this value as a pointer and return the place to which it points.
87 ///
88 /// The pointer immediate doesn't inherently know its alignment,
89 /// so you need to pass it in. If you want to get it from a type's ABI
90 /// alignment, then maybe you want [`OperandRef::deref`] instead.
91 ///
92 /// This is the inverse of [`PlaceValue::address`].
93 pub(crate) fn deref(self, align: Align) -> PlaceValue<V> {
94 let (llval, llextra) = self.pointer_parts();
95 PlaceValue { llval, llextra, align }
96 }
97
98 pub(crate) fn is_expected_variant_for_type<'tcx, Cx: LayoutTypeCodegenMethods<'tcx>>(
99 &self,
100 cx: &Cx,
101 ty: TyAndLayout<'tcx>,
102 ) -> bool {
103 match self {
104 OperandValue::ZeroSized => ty.is_zst(),
105 OperandValue::Immediate(_) => cx.is_backend_immediate(ty),
106 OperandValue::Pair(_, _) => cx.is_backend_scalar_pair(ty),
107 OperandValue::Ref(_) => cx.is_backend_ref(ty),
108 }
109 }
110}
111
112/// An `OperandRef` is an "SSA" reference to a Rust value, along with
113/// its type.
114///
115/// NOTE: unless you know a value's type exactly, you should not
116/// generate LLVM opcodes acting on it and instead act via methods,
117/// to avoid nasty edge cases. In particular, using `Builder::store`
118/// directly is sure to cause problems -- use `OperandRef::store`
119/// instead.
120#[derive(Copy, Clone)]
121pub struct OperandRef<'tcx, V> {
122 /// The value.
123 pub val: OperandValue<V>,
124
125 /// The layout of value, based on its Rust type.
126 pub layout: TyAndLayout<'tcx>,
127}
128
129impl<V: CodegenObject> fmt::Debug for OperandRef<'_, V> {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 write!(f, "OperandRef({:?} @ {:?})", self.val, self.layout)
132 }
133}
134
135impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
136 pub fn zero_sized(layout: TyAndLayout<'tcx>) -> OperandRef<'tcx, V> {
137 assert!(layout.is_zst());
138 OperandRef { val: OperandValue::ZeroSized, layout }
139 }
140
141 pub(crate) fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
142 bx: &mut Bx,
143 val: mir::ConstValue<'tcx>,
144 ty: Ty<'tcx>,
145 ) -> Self {
146 let layout = bx.layout_of(ty);
147
148 let val = match val {
149 ConstValue::Scalar(x) => {
150 let BackendRepr::Scalar(scalar) = layout.backend_repr else {
151 bug!("from_const: invalid ByVal layout: {:#?}", layout);
152 };
153 let llval = bx.scalar_to_backend(x, scalar, bx.immediate_backend_type(layout));
154 OperandValue::Immediate(llval)
155 }
156 ConstValue::ZeroSized => return OperandRef::zero_sized(layout),
157 ConstValue::Slice { data, meta } => {
158 let BackendRepr::ScalarPair(a_scalar, _) = layout.backend_repr else {
159 bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
160 };
161 let a = Scalar::from_pointer(
162 Pointer::new(bx.tcx().reserve_and_set_memory_alloc(data).into(), Size::ZERO),
163 &bx.tcx(),
164 );
165 let a_llval = bx.scalar_to_backend(
166 a,
167 a_scalar,
168 bx.scalar_pair_element_backend_type(layout, 0, true),
169 );
170 let b_llval = bx.const_usize(meta);
171 OperandValue::Pair(a_llval, b_llval)
172 }
173 ConstValue::Indirect { alloc_id, offset } => {
174 let alloc = bx.tcx().global_alloc(alloc_id).unwrap_memory();
175 return Self::from_const_alloc(bx, layout, alloc, offset);
176 }
177 };
178
179 OperandRef { val, layout }
180 }
181
182 fn from_const_alloc<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
183 bx: &mut Bx,
184 layout: TyAndLayout<'tcx>,
185 alloc: rustc_middle::mir::interpret::ConstAllocation<'tcx>,
186 offset: Size,
187 ) -> Self {
188 let alloc_align = alloc.inner().align;
189 assert!(alloc_align >= layout.align.abi, "{alloc_align:?} < {:?}", layout.align.abi);
190
191 let read_scalar = |start, size, s: abi::Scalar, ty| {
192 match alloc.0.read_scalar(
193 bx,
194 alloc_range(start, size),
195 /*read_provenance*/ matches!(s.primitive(), abi::Primitive::Pointer(_)),
196 ) {
197 Ok(val) => bx.scalar_to_backend(val, s, ty),
198 Err(_) => bx.const_poison(ty),
199 }
200 };
201
202 // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point.
203 // However, `MaybeUninit<u64>` is considered a `Scalar` as far as its layout is concerned --
204 // and yet cannot be represented by an interpreter `Scalar`, since we have to handle the
205 // case where some of the bytes are initialized and others are not. So, we need an extra
206 // check that walks over the type of `mplace` to make sure it is truly correct to treat this
207 // like a `Scalar` (or `ScalarPair`).
208 match layout.backend_repr {
209 BackendRepr::Scalar(s @ abi::Scalar::Initialized { .. }) => {
210 let size = s.size(bx);
211 assert_eq!(size, layout.size, "abi::Scalar size does not match layout size");
212 let val = read_scalar(offset, size, s, bx.immediate_backend_type(layout));
213 OperandRef { val: OperandValue::Immediate(val), layout }
214 }
215 BackendRepr::ScalarPair(
216 a @ abi::Scalar::Initialized { .. },
217 b @ abi::Scalar::Initialized { .. },
218 ) => {
219 let (a_size, b_size) = (a.size(bx), b.size(bx));
220 let b_offset = (offset + a_size).align_to(b.align(bx).abi);
221 assert!(b_offset.bytes() > 0);
222 let a_val = read_scalar(
223 offset,
224 a_size,
225 a,
226 bx.scalar_pair_element_backend_type(layout, 0, true),
227 );
228 let b_val = read_scalar(
229 b_offset,
230 b_size,
231 b,
232 bx.scalar_pair_element_backend_type(layout, 1, true),
233 );
234 OperandRef { val: OperandValue::Pair(a_val, b_val), layout }
235 }
236 _ if layout.is_zst() => OperandRef::zero_sized(layout),
237 _ => {
238 // Neither a scalar nor scalar pair. Load from a place
239 // FIXME: should we cache `const_data_from_alloc` to avoid repeating this for the
240 // same `ConstAllocation`?
241 let init = bx.const_data_from_alloc(alloc);
242 let base_addr = bx.static_addr_of(init, alloc_align, None);
243
244 let llval = bx.const_ptr_byte_offset(base_addr, offset);
245 bx.load_operand(PlaceRef::new_sized(llval, layout))
246 }
247 }
248 }
249
250 /// Asserts that this operand refers to a scalar and returns
251 /// a reference to its value.
252 pub fn immediate(self) -> V {
253 match self.val {
254 OperandValue::Immediate(s) => s,
255 _ => bug!("not immediate: {:?}", self),
256 }
257 }
258
259 /// Asserts that this operand is a pointer (or reference) and returns
260 /// the place to which it points. (This requires no code to be emitted
261 /// as we represent places using the pointer to the place.)
262 ///
263 /// This uses [`Ty::builtin_deref`] to include the type of the place and
264 /// assumes the place is aligned to the pointee's usual ABI alignment.
265 ///
266 /// If you don't need the type, see [`OperandValue::pointer_parts`]
267 /// or [`OperandValue::deref`].
268 pub fn deref<Cx: CodegenMethods<'tcx>>(self, cx: &Cx) -> PlaceRef<'tcx, V> {
269 if self.layout.ty.is_box() {
270 // Derefer should have removed all Box derefs
271 bug!("dereferencing {:?} in codegen", self.layout.ty);
272 }
273
274 let projected_ty = self
275 .layout
276 .ty
277 .builtin_deref(true)
278 .unwrap_or_else(|| bug!("deref of non-pointer {:?}", self));
279
280 let layout = cx.layout_of(projected_ty);
281 self.val.deref(layout.align.abi).with_type(layout)
282 }
283
284 /// If this operand is a `Pair`, we return an aggregate with the two values.
285 /// For other cases, see `immediate`.
286 pub fn immediate_or_packed_pair<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
287 self,
288 bx: &mut Bx,
289 ) -> V {
290 if let OperandValue::Pair(a, b) = self.val {
291 let llty = bx.cx().immediate_backend_type(self.layout);
292 debug!("Operand::immediate_or_packed_pair: packing {:?} into {:?}", self, llty);
293 // Reconstruct the immediate aggregate.
294 let mut llpair = bx.cx().const_poison(llty);
295 llpair = bx.insert_value(llpair, a, 0);
296 llpair = bx.insert_value(llpair, b, 1);
297 llpair
298 } else {
299 self.immediate()
300 }
301 }
302
303 /// If the type is a pair, we return a `Pair`, otherwise, an `Immediate`.
304 pub fn from_immediate_or_packed_pair<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
305 bx: &mut Bx,
306 llval: V,
307 layout: TyAndLayout<'tcx>,
308 ) -> Self {
309 let val = if let BackendRepr::ScalarPair(..) = layout.backend_repr {
310 debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}", llval, layout);
311
312 // Deconstruct the immediate aggregate.
313 let a_llval = bx.extract_value(llval, 0);
314 let b_llval = bx.extract_value(llval, 1);
315 OperandValue::Pair(a_llval, b_llval)
316 } else {
317 OperandValue::Immediate(llval)
318 };
319 OperandRef { val, layout }
320 }
321
322 pub(crate) fn extract_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
323 &self,
324 fx: &mut FunctionCx<'a, 'tcx, Bx>,
325 bx: &mut Bx,
326 i: usize,
327 ) -> Self {
328 let field = self.layout.field(bx.cx(), i);
329 let offset = self.layout.fields.offset(i);
330
331 if !bx.is_backend_ref(self.layout) && bx.is_backend_ref(field) {
332 if let BackendRepr::SimdVector { count, .. } = self.layout.backend_repr
333 && let BackendRepr::Memory { sized: true } = field.backend_repr
334 && count.is_power_of_two()
335 {
336 assert_eq!(field.size, self.layout.size);
337 // This is being deprecated, but for now stdarch still needs it for
338 // Newtype vector of array, e.g. #[repr(simd)] struct S([i32; 4]);
339 let place = PlaceRef::alloca(bx, field);
340 self.val.store(bx, place.val.with_type(self.layout));
341 return bx.load_operand(place);
342 } else {
343 // Part of https://github.com/rust-lang/compiler-team/issues/838
344 bug!("Non-ref type {self:?} cannot project to ref field type {field:?}");
345 }
346 }
347
348 let val = if field.is_zst() {
349 OperandValue::ZeroSized
350 } else if let BackendRepr::SimdVector { .. } = self.layout.backend_repr {
351 // codegen_transmute_operand doesn't support SIMD, but since the previous
352 // check handled ZSTs, the only possible field access into something SIMD
353 // is to the `non_1zst_field` that's the same SIMD. (Other things, even
354 // just padding, would change the wrapper's representation type.)
355 assert_eq!(field.size, self.layout.size);
356 self.val
357 } else if field.size == self.layout.size {
358 assert_eq!(offset.bytes(), 0);
359 fx.codegen_transmute_operand(bx, *self, field)
360 } else {
361 let (in_scalar, imm) = match (self.val, self.layout.backend_repr) {
362 // Extract a scalar component from a pair.
363 (OperandValue::Pair(a_llval, b_llval), BackendRepr::ScalarPair(a, b)) => {
364 if offset.bytes() == 0 {
365 assert_eq!(field.size, a.size(bx.cx()));
366 (Some(a), a_llval)
367 } else {
368 assert_eq!(offset, a.size(bx.cx()).align_to(b.align(bx.cx()).abi));
369 assert_eq!(field.size, b.size(bx.cx()));
370 (Some(b), b_llval)
371 }
372 }
373
374 _ => {
375 span_bug!(fx.mir.span, "OperandRef::extract_field({:?}): not applicable", self)
376 }
377 };
378 OperandValue::Immediate(match field.backend_repr {
379 BackendRepr::SimdVector { .. } => imm,
380 BackendRepr::Scalar(out_scalar) => {
381 let Some(in_scalar) = in_scalar else {
382 span_bug!(
383 fx.mir.span,
384 "OperandRef::extract_field({:?}): missing input scalar for output scalar",
385 self
386 )
387 };
388 if in_scalar != out_scalar {
389 // If the backend and backend_immediate types might differ,
390 // flip back to the backend type then to the new immediate.
391 // This avoids nop truncations, but still handles things like
392 // Bools in union fields needs to be truncated.
393 let backend = bx.from_immediate(imm);
394 bx.to_immediate_scalar(backend, out_scalar)
395 } else {
396 imm
397 }
398 }
399 BackendRepr::ScalarPair(_, _) | BackendRepr::Memory { .. } => bug!(),
400 })
401 };
402
403 OperandRef { val, layout: field }
404 }
405
406 /// Obtain the actual discriminant of a value.
407 #[instrument(level = "trace", skip(fx, bx))]
408 pub fn codegen_get_discr<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
409 self,
410 fx: &mut FunctionCx<'a, 'tcx, Bx>,
411 bx: &mut Bx,
412 cast_to: Ty<'tcx>,
413 ) -> V {
414 let dl = &bx.tcx().data_layout;
415 let cast_to_layout = bx.cx().layout_of(cast_to);
416 let cast_to = bx.cx().immediate_backend_type(cast_to_layout);
417
418 // We check uninhabitedness separately because a type like
419 // `enum Foo { Bar(i32, !) }` is still reported as `Variants::Single`,
420 // *not* as `Variants::Empty`.
421 if self.layout.is_uninhabited() {
422 return bx.cx().const_poison(cast_to);
423 }
424
425 let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants {
426 Variants::Empty => unreachable!("we already handled uninhabited types"),
427 Variants::Single { index } => {
428 let discr_val =
429 if let Some(discr) = self.layout.ty.discriminant_for_variant(bx.tcx(), index) {
430 discr.val
431 } else {
432 // This arm is for types which are neither enums nor coroutines,
433 // and thus for which the only possible "variant" should be the first one.
434 assert_eq!(index, FIRST_VARIANT);
435 // There's thus no actual discriminant to return, so we return
436 // what it would have been if this was a single-variant enum.
437 0
438 };
439 return bx.cx().const_uint_big(cast_to, discr_val);
440 }
441 Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => {
442 (tag, tag_encoding, tag_field)
443 }
444 };
445
446 // Read the tag/niche-encoded discriminant from memory.
447 let tag_op = match self.val {
448 OperandValue::ZeroSized => bug!(),
449 OperandValue::Immediate(_) | OperandValue::Pair(_, _) => {
450 self.extract_field(fx, bx, tag_field.as_usize())
451 }
452 OperandValue::Ref(place) => {
453 let tag = place.with_type(self.layout).project_field(bx, tag_field.as_usize());
454 bx.load_operand(tag)
455 }
456 };
457 let tag_imm = tag_op.immediate();
458
459 // Decode the discriminant (specifically if it's niche-encoded).
460 match *tag_encoding {
461 TagEncoding::Direct => {
462 let signed = match tag_scalar.primitive() {
463 // We use `i1` for bytes that are always `0` or `1`,
464 // e.g., `#[repr(i8)] enum E { A, B }`, but we can't
465 // let LLVM interpret the `i1` as signed, because
466 // then `i1 1` (i.e., `E::B`) is effectively `i8 -1`.
467 Primitive::Int(_, signed) => !tag_scalar.is_bool() && signed,
468 _ => false,
469 };
470 bx.intcast(tag_imm, cast_to, signed)
471 }
472 TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => {
473 // Cast to an integer so we don't have to treat a pointer as a
474 // special case.
475 let (tag, tag_llty) = match tag_scalar.primitive() {
476 // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
477 Primitive::Pointer(_) => {
478 let t = bx.type_from_integer(dl.ptr_sized_integer());
479 let tag = bx.ptrtoint(tag_imm, t);
480 (tag, t)
481 }
482 _ => (tag_imm, bx.cx().immediate_backend_type(tag_op.layout)),
483 };
484
485 // `layout_sanity_check` ensures that we only get here for cases where the discriminant
486 // value and the variant index match, since that's all `Niche` can encode.
487
488 let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
489 let niche_start_const = bx.cx().const_uint_big(tag_llty, niche_start);
490
491 // We have a subrange `niche_start..=niche_end` inside `range`.
492 // If the value of the tag is inside this subrange, it's a
493 // "niche value", an increment of the discriminant. Otherwise it
494 // indicates the untagged variant.
495 // A general algorithm to extract the discriminant from the tag
496 // is:
497 // relative_tag = tag - niche_start
498 // is_niche = relative_tag <= (ule) relative_max
499 // discr = if is_niche {
500 // cast(relative_tag) + niche_variants.start()
501 // } else {
502 // untagged_variant
503 // }
504 // However, we will likely be able to emit simpler code.
505 let (is_niche, tagged_discr, delta) = if relative_max == 0 {
506 // Best case scenario: only one tagged variant. This will
507 // likely become just a comparison and a jump.
508 // The algorithm is:
509 // is_niche = tag == niche_start
510 // discr = if is_niche {
511 // niche_start
512 // } else {
513 // untagged_variant
514 // }
515 let is_niche = bx.icmp(IntPredicate::IntEQ, tag, niche_start_const);
516 let tagged_discr =
517 bx.cx().const_uint(cast_to, niche_variants.start().as_u32() as u64);
518 (is_niche, tagged_discr, 0)
519 } else {
520 // With multiple niched variants we'll have to actually compute
521 // the variant index from the stored tag.
522 //
523 // However, there's still one small optimization we can often do for
524 // determining *whether* a tag value is a natural value or a niched
525 // variant. The general algorithm involves a subtraction that often
526 // wraps in practice, making it tricky to analyse. However, in cases
527 // where there are few enough possible values of the tag that it doesn't
528 // need to wrap around, we can instead just look for the contiguous
529 // tag values on the end of the range with a single comparison.
530 //
531 // For example, take the type `enum Demo { A, B, Untagged(bool) }`.
532 // The `bool` is {0, 1}, and the two other variants are given the
533 // tags {2, 3} respectively. That means the `tag_range` is
534 // `[0, 3]`, which doesn't wrap as unsigned (nor as signed), so
535 // we can test for the niched variants with just `>= 2`.
536 //
537 // That means we're looking either for the niche values *above*
538 // the natural values of the untagged variant:
539 //
540 // niche_start niche_end
541 // | |
542 // v v
543 // MIN -------------+---------------------------+---------- MAX
544 // ^ | is niche |
545 // | +---------------------------+
546 // | |
547 // tag_range.start tag_range.end
548 //
549 // Or *below* the natural values:
550 //
551 // niche_start niche_end
552 // | |
553 // v v
554 // MIN ----+-----------------------+---------------------- MAX
555 // | is niche | ^
556 // +-----------------------+ |
557 // | |
558 // tag_range.start tag_range.end
559 //
560 // With those two options and having the flexibility to choose
561 // between a signed or unsigned comparison on the tag, that
562 // covers most realistic scenarios. The tests have a (contrived)
563 // example of a 1-byte enum with over 128 niched variants which
564 // wraps both as signed as unsigned, though, and for something
565 // like that we're stuck with the general algorithm.
566
567 let tag_range = tag_scalar.valid_range(&dl);
568 let tag_size = tag_scalar.size(&dl);
569 let niche_end = u128::from(relative_max).wrapping_add(niche_start);
570 let niche_end = tag_size.truncate(niche_end);
571
572 let relative_discr = bx.sub(tag, niche_start_const);
573 let cast_tag = bx.intcast(relative_discr, cast_to, false);
574 let is_niche = if tag_range.no_unsigned_wraparound(tag_size) == Ok(true) {
575 if niche_start == tag_range.start {
576 let niche_end_const = bx.cx().const_uint_big(tag_llty, niche_end);
577 bx.icmp(IntPredicate::IntULE, tag, niche_end_const)
578 } else {
579 assert_eq!(niche_end, tag_range.end);
580 bx.icmp(IntPredicate::IntUGE, tag, niche_start_const)
581 }
582 } else if tag_range.no_signed_wraparound(tag_size) == Ok(true) {
583 if niche_start == tag_range.start {
584 let niche_end_const = bx.cx().const_uint_big(tag_llty, niche_end);
585 bx.icmp(IntPredicate::IntSLE, tag, niche_end_const)
586 } else {
587 assert_eq!(niche_end, tag_range.end);
588 bx.icmp(IntPredicate::IntSGE, tag, niche_start_const)
589 }
590 } else {
591 bx.icmp(
592 IntPredicate::IntULE,
593 relative_discr,
594 bx.cx().const_uint(tag_llty, relative_max as u64),
595 )
596 };
597
598 (is_niche, cast_tag, niche_variants.start().as_u32() as u128)
599 };
600
601 let tagged_discr = if delta == 0 {
602 tagged_discr
603 } else {
604 bx.add(tagged_discr, bx.cx().const_uint_big(cast_to, delta))
605 };
606
607 let untagged_variant_const =
608 bx.cx().const_uint(cast_to, u64::from(untagged_variant.as_u32()));
609
610 // Thanks to parameter attributes and load metadata, LLVM already knows
611 // the general valid range of the tag. It's possible, though, for there
612 // to be an impossible value *in the middle*, which those ranges don't
613 // communicate, so it's worth an `assume` to let the optimizer know.
614 // Most importantly, this means when optimizing a variant test like
615 // `SELECT(is_niche, complex, CONST) == CONST` it's ok to simplify that
616 // to `!is_niche` because the `complex` part can't possibly match.
617 if niche_variants.contains(&untagged_variant)
618 && bx.cx().sess().opts.optimize != OptLevel::No
619 {
620 let ne = bx.icmp(IntPredicate::IntNE, tagged_discr, untagged_variant_const);
621 bx.assume(ne);
622 }
623
624 let discr = bx.select(is_niche, tagged_discr, untagged_variant_const);
625
626 // In principle we could insert assumes on the possible range of `discr`, but
627 // currently in LLVM this isn't worth it because the original `tag` will
628 // have either a `range` parameter attribute or `!range` metadata,
629 // or come from a `transmute` that already `assume`d it.
630
631 discr
632 }
633 }
634 }
635}
636
637/// Each of these variants starts out as `Either::Right` when it's uninitialized,
638/// then setting the field changes that to `Either::Left` with the backend value.
639#[derive(Debug, Copy, Clone)]
640enum OperandValueBuilder<V> {
641 ZeroSized,
642 Immediate(Either<V, abi::Scalar>),
643 Pair(Either<V, abi::Scalar>, Either<V, abi::Scalar>),
644 /// `repr(simd)` types need special handling because they each have a non-empty
645 /// array field (which uses [`OperandValue::Ref`]) despite the SIMD type itself
646 /// using [`OperandValue::Immediate`] which for any other kind of type would
647 /// mean that its one non-ZST field would also be [`OperandValue::Immediate`].
648 Vector(Either<V, ()>),
649}
650
651/// Allows building up an `OperandRef` by setting fields one at a time.
652#[derive(Debug, Copy, Clone)]
653pub(super) struct OperandRefBuilder<'tcx, V> {
654 val: OperandValueBuilder<V>,
655 layout: TyAndLayout<'tcx>,
656}
657
658impl<'a, 'tcx, V: CodegenObject> OperandRefBuilder<'tcx, V> {
659 /// Creates an uninitialized builder for an instance of the `layout`.
660 ///
661 /// ICEs for [`BackendRepr::Memory`] types (other than ZSTs), which should
662 /// be built up inside a [`PlaceRef`] instead as they need an allocated place
663 /// into which to write the values of the fields.
664 pub(super) fn new(layout: TyAndLayout<'tcx>) -> Self {
665 let val = match layout.backend_repr {
666 BackendRepr::Memory { .. } if layout.is_zst() => OperandValueBuilder::ZeroSized,
667 BackendRepr::Scalar(s) => OperandValueBuilder::Immediate(Either::Right(s)),
668 BackendRepr::ScalarPair(a, b) => {
669 OperandValueBuilder::Pair(Either::Right(a), Either::Right(b))
670 }
671 BackendRepr::SimdVector { .. } => OperandValueBuilder::Vector(Either::Right(())),
672 BackendRepr::Memory { .. } => {
673 bug!("Cannot use non-ZST Memory-ABI type in operand builder: {layout:?}");
674 }
675 };
676 OperandRefBuilder { val, layout }
677 }
678
679 pub(super) fn insert_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
680 &mut self,
681 bx: &mut Bx,
682 variant: VariantIdx,
683 field: FieldIdx,
684 field_operand: OperandRef<'tcx, V>,
685 ) {
686 if let OperandValue::ZeroSized = field_operand.val {
687 // A ZST never adds any state, so just ignore it.
688 // This special-casing is worth it because of things like
689 // `Result<!, !>` where `Ok(never)` is legal to write,
690 // but the type shows as FieldShape::Primitive so we can't
691 // actually look at the layout for the field being set.
692 return;
693 }
694
695 let is_zero_offset = if let abi::FieldsShape::Primitive = self.layout.fields {
696 // The other branch looking at field layouts ICEs for primitives,
697 // so we need to handle them separately.
698 // Because we handled ZSTs above (like the metadata in a thin pointer),
699 // the only possibility is that we're setting the one-and-only field.
700 assert!(!self.layout.is_zst());
701 assert_eq!(variant, FIRST_VARIANT);
702 assert_eq!(field, FieldIdx::ZERO);
703 true
704 } else {
705 let variant_layout = self.layout.for_variant(bx.cx(), variant);
706 let field_offset = variant_layout.fields.offset(field.as_usize());
707 field_offset == Size::ZERO
708 };
709
710 let mut update = |tgt: &mut Either<V, abi::Scalar>, src, from_scalar| {
711 let to_scalar = tgt.unwrap_right();
712 // We transmute here (rather than just `from_immediate`) because in
713 // `Result<usize, *const ()>` the field of the `Ok` is an integer,
714 // but the corresponding scalar in the enum is a pointer.
715 let imm = transmute_scalar(bx, src, from_scalar, to_scalar);
716 *tgt = Either::Left(imm);
717 };
718
719 match (field_operand.val, field_operand.layout.backend_repr) {
720 (OperandValue::ZeroSized, _) => unreachable!("Handled above"),
721 (OperandValue::Immediate(v), BackendRepr::Scalar(from_scalar)) => match &mut self.val {
722 OperandValueBuilder::Immediate(val @ Either::Right(_)) if is_zero_offset => {
723 update(val, v, from_scalar);
724 }
725 OperandValueBuilder::Pair(fst @ Either::Right(_), _) if is_zero_offset => {
726 update(fst, v, from_scalar);
727 }
728 OperandValueBuilder::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => {
729 update(snd, v, from_scalar);
730 }
731 _ => {
732 bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}")
733 }
734 },
735 (OperandValue::Immediate(v), BackendRepr::SimdVector { .. }) => match &mut self.val {
736 OperandValueBuilder::Vector(val @ Either::Right(())) if is_zero_offset => {
737 *val = Either::Left(v);
738 }
739 _ => {
740 bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}")
741 }
742 },
743 (OperandValue::Pair(a, b), BackendRepr::ScalarPair(from_sa, from_sb)) => {
744 match &mut self.val {
745 OperandValueBuilder::Pair(fst @ Either::Right(_), snd @ Either::Right(_)) => {
746 update(fst, a, from_sa);
747 update(snd, b, from_sb);
748 }
749 _ => bug!(
750 "Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}"
751 ),
752 }
753 }
754 (OperandValue::Ref(place), BackendRepr::Memory { .. }) => match &mut self.val {
755 OperandValueBuilder::Vector(val @ Either::Right(())) => {
756 let ibty = bx.cx().immediate_backend_type(self.layout);
757 let simd = bx.load_from_place(ibty, place);
758 *val = Either::Left(simd);
759 }
760 _ => {
761 bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}")
762 }
763 },
764 _ => bug!("Operand cannot be used with `insert_field`: {field_operand:?}"),
765 }
766 }
767
768 /// Insert the immediate value `imm` for field `f` in the *type itself*,
769 /// rather than into one of the variants.
770 ///
771 /// Most things want [`Self::insert_field`] instead, but this one is
772 /// necessary for writing things like enum tags that aren't in any variant.
773 pub(super) fn insert_imm(&mut self, f: FieldIdx, imm: V) {
774 let field_offset = self.layout.fields.offset(f.as_usize());
775 let is_zero_offset = field_offset == Size::ZERO;
776 match &mut self.val {
777 OperandValueBuilder::Immediate(val @ Either::Right(_)) if is_zero_offset => {
778 *val = Either::Left(imm);
779 }
780 OperandValueBuilder::Pair(fst @ Either::Right(_), _) if is_zero_offset => {
781 *fst = Either::Left(imm);
782 }
783 OperandValueBuilder::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => {
784 *snd = Either::Left(imm);
785 }
786 _ => bug!("Tried to insert {imm:?} into field {f:?} of {self:?}"),
787 }
788 }
789
790 /// After having set all necessary fields, this converts the builder back
791 /// to the normal `OperandRef`.
792 ///
793 /// ICEs if any required fields were not set.
794 pub(super) fn build(&self, cx: &impl CodegenMethods<'tcx, Value = V>) -> OperandRef<'tcx, V> {
795 let OperandRefBuilder { val, layout } = *self;
796
797 // For something like `Option::<u32>::None`, it's expected that the
798 // payload scalar will not actually have been set, so this converts
799 // unset scalars to corresponding `undef` values so long as the scalar
800 // from the layout allows uninit.
801 let unwrap = |r: Either<V, abi::Scalar>| match r {
802 Either::Left(v) => v,
803 Either::Right(s) if s.is_uninit_valid() => {
804 let bty = cx.type_from_scalar(s);
805 cx.const_undef(bty)
806 }
807 Either::Right(_) => bug!("OperandRef::build called while fields are missing {self:?}"),
808 };
809
810 let val = match val {
811 OperandValueBuilder::ZeroSized => OperandValue::ZeroSized,
812 OperandValueBuilder::Immediate(v) => OperandValue::Immediate(unwrap(v)),
813 OperandValueBuilder::Pair(a, b) => OperandValue::Pair(unwrap(a), unwrap(b)),
814 OperandValueBuilder::Vector(v) => match v {
815 Either::Left(v) => OperandValue::Immediate(v),
816 Either::Right(())
817 if let BackendRepr::SimdVector { element, .. } = layout.backend_repr
818 && element.is_uninit_valid() =>
819 {
820 let bty = cx.immediate_backend_type(layout);
821 OperandValue::Immediate(cx.const_undef(bty))
822 }
823 Either::Right(()) => {
824 bug!("OperandRef::build called while fields are missing {self:?}")
825 }
826 },
827 };
828 OperandRef { val, layout }
829 }
830}
831
832impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
833 /// Returns an `OperandValue` that's generally UB to use in any way.
834 ///
835 /// Depending on the `layout`, returns `ZeroSized` for ZSTs, an `Immediate` or
836 /// `Pair` containing poison value(s), or a `Ref` containing a poison pointer.
837 ///
838 /// Supports sized types only.
839 pub fn poison<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
840 bx: &mut Bx,
841 layout: TyAndLayout<'tcx>,
842 ) -> OperandValue<V> {
843 assert!(layout.is_sized());
844 if layout.is_zst() {
845 OperandValue::ZeroSized
846 } else if bx.cx().is_backend_immediate(layout) {
847 let ibty = bx.cx().immediate_backend_type(layout);
848 OperandValue::Immediate(bx.const_poison(ibty))
849 } else if bx.cx().is_backend_scalar_pair(layout) {
850 let ibty0 = bx.cx().scalar_pair_element_backend_type(layout, 0, true);
851 let ibty1 = bx.cx().scalar_pair_element_backend_type(layout, 1, true);
852 OperandValue::Pair(bx.const_poison(ibty0), bx.const_poison(ibty1))
853 } else {
854 let ptr = bx.cx().type_ptr();
855 OperandValue::Ref(PlaceValue::new_sized(bx.const_poison(ptr), layout.align.abi))
856 }
857 }
858
859 pub fn store<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
860 self,
861 bx: &mut Bx,
862 dest: PlaceRef<'tcx, V>,
863 ) {
864 self.store_with_flags(bx, dest, MemFlags::empty());
865 }
866
867 pub fn volatile_store<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
868 self,
869 bx: &mut Bx,
870 dest: PlaceRef<'tcx, V>,
871 ) {
872 self.store_with_flags(bx, dest, MemFlags::VOLATILE);
873 }
874
875 pub fn unaligned_volatile_store<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
876 self,
877 bx: &mut Bx,
878 dest: PlaceRef<'tcx, V>,
879 ) {
880 self.store_with_flags(bx, dest, MemFlags::VOLATILE | MemFlags::UNALIGNED);
881 }
882
883 pub fn nontemporal_store<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
884 self,
885 bx: &mut Bx,
886 dest: PlaceRef<'tcx, V>,
887 ) {
888 self.store_with_flags(bx, dest, MemFlags::NONTEMPORAL);
889 }
890
891 pub(crate) fn store_with_flags<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
892 self,
893 bx: &mut Bx,
894 dest: PlaceRef<'tcx, V>,
895 flags: MemFlags,
896 ) {
897 debug!("OperandRef::store: operand={:?}, dest={:?}", self, dest);
898 match self {
899 OperandValue::ZeroSized => {
900 // Avoid generating stores of zero-sized values, because the only way to have a
901 // zero-sized value is through `undef`/`poison`, and the store itself is useless.
902 }
903 OperandValue::Ref(val) => {
904 assert!(dest.layout.is_sized(), "cannot directly store unsized values");
905 if val.llextra.is_some() {
906 bug!("cannot directly store unsized values");
907 }
908 bx.typed_place_copy_with_flags(dest.val, val, dest.layout, flags);
909 }
910 OperandValue::Immediate(s) => {
911 let val = bx.from_immediate(s);
912 bx.store_with_flags(val, dest.val.llval, dest.val.align, flags);
913 }
914 OperandValue::Pair(a, b) => {
915 let BackendRepr::ScalarPair(a_scalar, b_scalar) = dest.layout.backend_repr else {
916 bug!("store_with_flags: invalid ScalarPair layout: {:#?}", dest.layout);
917 };
918 let b_offset = a_scalar.size(bx).align_to(b_scalar.align(bx).abi);
919
920 let val = bx.from_immediate(a);
921 let align = dest.val.align;
922 bx.store_with_flags(val, dest.val.llval, align, flags);
923
924 let llptr = bx.inbounds_ptradd(dest.val.llval, bx.const_usize(b_offset.bytes()));
925 let val = bx.from_immediate(b);
926 let align = dest.val.align.restrict_for_offset(b_offset);
927 bx.store_with_flags(val, llptr, align, flags);
928 }
929 }
930 }
931}
932
933impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
934 fn maybe_codegen_consume_direct(
935 &mut self,
936 bx: &mut Bx,
937 place_ref: mir::PlaceRef<'tcx>,
938 ) -> Option<OperandRef<'tcx, Bx::Value>> {
939 debug!("maybe_codegen_consume_direct(place_ref={:?})", place_ref);
940
941 match self.locals[place_ref.local] {
942 LocalRef::Operand(mut o) => {
943 // Moves out of scalar and scalar pair fields are trivial.
944 for elem in place_ref.projection.iter() {
945 match elem {
946 mir::ProjectionElem::Field(f, _) => {
947 assert!(
948 !o.layout.ty.is_any_ptr(),
949 "Bad PlaceRef: destructing pointers should use cast/PtrMetadata, \
950 but tried to access field {f:?} of pointer {o:?}",
951 );
952 o = o.extract_field(self, bx, f.index());
953 }
954 mir::ProjectionElem::Index(_)
955 | mir::ProjectionElem::ConstantIndex { .. } => {
956 // ZSTs don't require any actual memory access.
957 // FIXME(eddyb) deduplicate this with the identical
958 // checks in `codegen_consume` and `extract_field`.
959 let elem = o.layout.field(bx.cx(), 0);
960 if elem.is_zst() {
961 o = OperandRef::zero_sized(elem);
962 } else {
963 return None;
964 }
965 }
966 _ => return None,
967 }
968 }
969
970 Some(o)
971 }
972 LocalRef::PendingOperand => {
973 bug!("use of {:?} before def", place_ref);
974 }
975 LocalRef::Place(..) | LocalRef::UnsizedPlace(..) => {
976 // watch out for locals that do not have an
977 // alloca; they are handled somewhat differently
978 None
979 }
980 }
981 }
982
983 pub fn codegen_consume(
984 &mut self,
985 bx: &mut Bx,
986 place_ref: mir::PlaceRef<'tcx>,
987 ) -> OperandRef<'tcx, Bx::Value> {
988 debug!("codegen_consume(place_ref={:?})", place_ref);
989
990 let ty = self.monomorphized_place_ty(place_ref);
991 let layout = bx.cx().layout_of(ty);
992
993 // ZSTs don't require any actual memory access.
994 if layout.is_zst() {
995 return OperandRef::zero_sized(layout);
996 }
997
998 if let Some(o) = self.maybe_codegen_consume_direct(bx, place_ref) {
999 return o;
1000 }
1001
1002 // for most places, to consume them we just load them
1003 // out from their home
1004 let place = self.codegen_place(bx, place_ref);
1005 bx.load_operand(place)
1006 }
1007
1008 pub fn codegen_operand(
1009 &mut self,
1010 bx: &mut Bx,
1011 operand: &mir::Operand<'tcx>,
1012 ) -> OperandRef<'tcx, Bx::Value> {
1013 debug!("codegen_operand(operand={:?})", operand);
1014
1015 match *operand {
1016 mir::Operand::Copy(ref place) | mir::Operand::Move(ref place) => {
1017 self.codegen_consume(bx, place.as_ref())
1018 }
1019
1020 mir::Operand::Constant(ref constant) => {
1021 let constant_ty = self.monomorphize(constant.ty());
1022 // Most SIMD vector constants should be passed as immediates.
1023 // (In particular, some intrinsics really rely on this.)
1024 if constant_ty.is_simd() {
1025 // However, some SIMD types do not actually use the vector ABI
1026 // (in particular, packed SIMD types do not). Ensure we exclude those.
1027 let layout = bx.layout_of(constant_ty);
1028 if let BackendRepr::SimdVector { .. } = layout.backend_repr {
1029 let (llval, ty) = self.immediate_const_vector(bx, constant);
1030 return OperandRef {
1031 val: OperandValue::Immediate(llval),
1032 layout: bx.layout_of(ty),
1033 };
1034 }
1035 }
1036 self.eval_mir_constant_to_operand(bx, constant)
1037 }
1038 }
1039 }
1040}