rustc_codegen_ssa/mir/
rvalue.rs

1use std::assert_matches::assert_matches;
2
3use arrayvec::ArrayVec;
4use rustc_abi::{self as abi, FIRST_VARIANT, FieldIdx};
5use rustc_middle::ty::adjustment::PointerCoercion;
6use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
7use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
8use rustc_middle::{bug, mir, span_bug};
9use rustc_session::config::OptLevel;
10use rustc_span::{DUMMY_SP, Span};
11use tracing::{debug, instrument};
12
13use super::operand::{OperandRef, OperandValue};
14use super::place::PlaceRef;
15use super::{FunctionCx, LocalRef};
16use crate::common::IntPredicate;
17use crate::traits::*;
18use crate::{MemFlags, base};
19
20impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
21    #[instrument(level = "trace", skip(self, bx))]
22    pub(crate) fn codegen_rvalue(
23        &mut self,
24        bx: &mut Bx,
25        dest: PlaceRef<'tcx, Bx::Value>,
26        rvalue: &mir::Rvalue<'tcx>,
27    ) {
28        match *rvalue {
29            mir::Rvalue::Use(ref operand) => {
30                let cg_operand = self.codegen_operand(bx, operand);
31                // FIXME: consider not copying constants through stack. (Fixable by codegen'ing
32                // constants into `OperandValue::Ref`; why don’t we do that yet if we don’t?)
33                cg_operand.val.store(bx, dest);
34            }
35
36            mir::Rvalue::Cast(
37                mir::CastKind::PointerCoercion(PointerCoercion::Unsize, _),
38                ref source,
39                _,
40            ) => {
41                // The destination necessarily contains a wide pointer, so if
42                // it's a scalar pair, it's a wide pointer or newtype thereof.
43                if bx.cx().is_backend_scalar_pair(dest.layout) {
44                    // Into-coerce of a thin pointer to a wide pointer -- just
45                    // use the operand path.
46                    let temp = self.codegen_rvalue_operand(bx, rvalue);
47                    temp.val.store(bx, dest);
48                    return;
49                }
50
51                // Unsize of a nontrivial struct. I would prefer for
52                // this to be eliminated by MIR building, but
53                // `CoerceUnsized` can be passed by a where-clause,
54                // so the (generic) MIR may not be able to expand it.
55                let operand = self.codegen_operand(bx, source);
56                match operand.val {
57                    OperandValue::Pair(..) | OperandValue::Immediate(_) => {
58                        // Unsize from an immediate structure. We don't
59                        // really need a temporary alloca here, but
60                        // avoiding it would require us to have
61                        // `coerce_unsized_into` use `extractvalue` to
62                        // index into the struct, and this case isn't
63                        // important enough for it.
64                        debug!("codegen_rvalue: creating ugly alloca");
65                        let scratch = PlaceRef::alloca(bx, operand.layout);
66                        scratch.storage_live(bx);
67                        operand.val.store(bx, scratch);
68                        base::coerce_unsized_into(bx, scratch, dest);
69                        scratch.storage_dead(bx);
70                    }
71                    OperandValue::Ref(val) => {
72                        if val.llextra.is_some() {
73                            bug!("unsized coercion on an unsized rvalue");
74                        }
75                        base::coerce_unsized_into(bx, val.with_type(operand.layout), dest);
76                    }
77                    OperandValue::ZeroSized => {
78                        bug!("unsized coercion on a ZST rvalue");
79                    }
80                }
81            }
82
83            mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, _ty) => {
84                let src = self.codegen_operand(bx, operand);
85                self.codegen_transmute(bx, src, dest);
86            }
87
88            mir::Rvalue::Repeat(ref elem, count) => {
89                let cg_elem = self.codegen_operand(bx, elem);
90
91                // Do not generate the loop for zero-sized elements or empty arrays.
92                if dest.layout.is_zst() {
93                    return;
94                }
95
96                let try_init_all_same = |bx: &mut Bx, v| {
97                    let start = dest.val.llval;
98                    let size = bx.const_usize(dest.layout.size.bytes());
99
100                    // Use llvm.memset.p0i8.* to initialize all same byte arrays
101                    if let Some(int) = bx.cx().const_to_opt_u128(v, false) {
102                        let bytes = &int.to_le_bytes()[..cg_elem.layout.size.bytes_usize()];
103                        let first = bytes[0];
104                        if bytes[1..].iter().all(|&b| b == first) {
105                            let fill = bx.cx().const_u8(first);
106                            bx.memset(start, fill, size, dest.val.align, MemFlags::empty());
107                            return true;
108                        }
109                    }
110
111                    // Use llvm.memset.p0i8.* to initialize byte arrays
112                    let v = bx.from_immediate(v);
113                    if bx.cx().val_ty(v) == bx.cx().type_i8() {
114                        bx.memset(start, v, size, dest.val.align, MemFlags::empty());
115                        return true;
116                    }
117                    false
118                };
119
120                match cg_elem.val {
121                    OperandValue::Immediate(v) => {
122                        if try_init_all_same(bx, v) {
123                            return;
124                        }
125                    }
126                    _ => (),
127                }
128
129                let count = self
130                    .monomorphize(count)
131                    .try_to_target_usize(bx.tcx())
132                    .expect("expected monomorphic const in codegen");
133
134                bx.write_operand_repeatedly(cg_elem, count, dest);
135            }
136
137            // This implementation does field projection, so never use it for `RawPtr`,
138            // which will always be fine with the `codegen_rvalue_operand` path below.
139            mir::Rvalue::Aggregate(ref kind, ref operands)
140                if !matches!(**kind, mir::AggregateKind::RawPtr(..)) =>
141            {
142                let (variant_index, variant_dest, active_field_index) = match **kind {
143                    mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
144                        let variant_dest = dest.project_downcast(bx, variant_index);
145                        (variant_index, variant_dest, active_field_index)
146                    }
147                    _ => (FIRST_VARIANT, dest, None),
148                };
149                if active_field_index.is_some() {
150                    assert_eq!(operands.len(), 1);
151                }
152                for (i, operand) in operands.iter_enumerated() {
153                    let op = self.codegen_operand(bx, operand);
154                    // Do not generate stores and GEPis for zero-sized fields.
155                    if !op.layout.is_zst() {
156                        let field_index = active_field_index.unwrap_or(i);
157                        let field = if let mir::AggregateKind::Array(_) = **kind {
158                            let llindex = bx.cx().const_usize(field_index.as_u32().into());
159                            variant_dest.project_index(bx, llindex)
160                        } else {
161                            variant_dest.project_field(bx, field_index.as_usize())
162                        };
163                        op.val.store(bx, field);
164                    }
165                }
166                dest.codegen_set_discr(bx, variant_index);
167            }
168
169            _ => {
170                assert!(self.rvalue_creates_operand(rvalue, DUMMY_SP));
171                let temp = self.codegen_rvalue_operand(bx, rvalue);
172                temp.val.store(bx, dest);
173            }
174        }
175    }
176
177    fn codegen_transmute(
178        &mut self,
179        bx: &mut Bx,
180        src: OperandRef<'tcx, Bx::Value>,
181        dst: PlaceRef<'tcx, Bx::Value>,
182    ) {
183        // The MIR validator enforces no unsized transmutes.
184        assert!(src.layout.is_sized());
185        assert!(dst.layout.is_sized());
186
187        if let Some(val) = self.codegen_transmute_operand(bx, src, dst.layout) {
188            val.store(bx, dst);
189            return;
190        }
191
192        match src.val {
193            OperandValue::Ref(..) | OperandValue::ZeroSized => {
194                span_bug!(
195                    self.mir.span,
196                    "Operand path should have handled transmute \
197                    from {src:?} to place {dst:?}"
198                );
199            }
200            OperandValue::Immediate(..) | OperandValue::Pair(..) => {
201                // When we have immediate(s), the alignment of the source is irrelevant,
202                // so we can store them using the destination's alignment.
203                src.val.store(bx, dst.val.with_type(src.layout));
204            }
205        }
206    }
207
208    /// Attempts to transmute an `OperandValue` to another `OperandValue`.
209    ///
210    /// Returns `None` for cases that can't work in that framework, such as for
211    /// `Immediate`->`Ref` that needs an `alloc` to get the location.
212    pub(crate) fn codegen_transmute_operand(
213        &mut self,
214        bx: &mut Bx,
215        operand: OperandRef<'tcx, Bx::Value>,
216        cast: TyAndLayout<'tcx>,
217    ) -> Option<OperandValue<Bx::Value>> {
218        // Check for transmutes that are always UB.
219        if operand.layout.size != cast.size
220            || operand.layout.is_uninhabited()
221            || cast.is_uninhabited()
222        {
223            if !operand.layout.is_uninhabited() {
224                // Since this is known statically and the input could have existed
225                // without already having hit UB, might as well trap for it.
226                bx.abort();
227            }
228
229            // Because this transmute is UB, return something easy to generate,
230            // since it's fine that later uses of the value are probably UB.
231            return Some(OperandValue::poison(bx, cast));
232        }
233
234        let operand_kind = self.value_kind(operand.layout);
235        let cast_kind = self.value_kind(cast);
236
237        match operand.val {
238            OperandValue::Ref(source_place_val) => {
239                assert_eq!(source_place_val.llextra, None);
240                assert_matches!(operand_kind, OperandValueKind::Ref);
241                // The existing alignment is part of `source_place_val`,
242                // so that alignment will be used, not `cast`'s.
243                Some(bx.load_operand(source_place_val.with_type(cast)).val)
244            }
245            OperandValue::ZeroSized => {
246                let OperandValueKind::ZeroSized = operand_kind else {
247                    bug!("Found {operand_kind:?} for operand {operand:?}");
248                };
249                if let OperandValueKind::ZeroSized = cast_kind {
250                    Some(OperandValue::ZeroSized)
251                } else {
252                    None
253                }
254            }
255            OperandValue::Immediate(imm) => {
256                let OperandValueKind::Immediate(from_scalar) = operand_kind else {
257                    bug!("Found {operand_kind:?} for operand {operand:?}");
258                };
259                if let OperandValueKind::Immediate(to_scalar) = cast_kind
260                    && from_scalar.size(self.cx) == to_scalar.size(self.cx)
261                {
262                    let from_backend_ty = bx.backend_type(operand.layout);
263                    let to_backend_ty = bx.backend_type(cast);
264                    Some(OperandValue::Immediate(self.transmute_immediate(
265                        bx,
266                        imm,
267                        from_scalar,
268                        from_backend_ty,
269                        to_scalar,
270                        to_backend_ty,
271                    )))
272                } else {
273                    None
274                }
275            }
276            OperandValue::Pair(imm_a, imm_b) => {
277                let OperandValueKind::Pair(in_a, in_b) = operand_kind else {
278                    bug!("Found {operand_kind:?} for operand {operand:?}");
279                };
280                if let OperandValueKind::Pair(out_a, out_b) = cast_kind
281                    && in_a.size(self.cx) == out_a.size(self.cx)
282                    && in_b.size(self.cx) == out_b.size(self.cx)
283                {
284                    let in_a_ibty = bx.scalar_pair_element_backend_type(operand.layout, 0, false);
285                    let in_b_ibty = bx.scalar_pair_element_backend_type(operand.layout, 1, false);
286                    let out_a_ibty = bx.scalar_pair_element_backend_type(cast, 0, false);
287                    let out_b_ibty = bx.scalar_pair_element_backend_type(cast, 1, false);
288                    Some(OperandValue::Pair(
289                        self.transmute_immediate(bx, imm_a, in_a, in_a_ibty, out_a, out_a_ibty),
290                        self.transmute_immediate(bx, imm_b, in_b, in_b_ibty, out_b, out_b_ibty),
291                    ))
292                } else {
293                    None
294                }
295            }
296        }
297    }
298
299    /// Cast one of the immediates from an [`OperandValue::Immediate`]
300    /// or an [`OperandValue::Pair`] to an immediate of the target type.
301    ///
302    /// Returns `None` if the cast is not possible.
303    fn cast_immediate(
304        &self,
305        bx: &mut Bx,
306        mut imm: Bx::Value,
307        from_scalar: abi::Scalar,
308        from_backend_ty: Bx::Type,
309        to_scalar: abi::Scalar,
310        to_backend_ty: Bx::Type,
311    ) -> Option<Bx::Value> {
312        use abi::Primitive::*;
313
314        // When scalars are passed by value, there's no metadata recording their
315        // valid ranges. For example, `char`s are passed as just `i32`, with no
316        // way for LLVM to know that they're 0x10FFFF at most. Thus we assume
317        // the range of the input value too, not just the output range.
318        self.assume_scalar_range(bx, imm, from_scalar, from_backend_ty);
319
320        imm = match (from_scalar.primitive(), to_scalar.primitive()) {
321            (Int(_, is_signed), Int(..)) => bx.intcast(imm, to_backend_ty, is_signed),
322            (Float(_), Float(_)) => {
323                let srcsz = bx.cx().float_width(from_backend_ty);
324                let dstsz = bx.cx().float_width(to_backend_ty);
325                if dstsz > srcsz {
326                    bx.fpext(imm, to_backend_ty)
327                } else if srcsz > dstsz {
328                    bx.fptrunc(imm, to_backend_ty)
329                } else {
330                    imm
331                }
332            }
333            (Int(_, is_signed), Float(_)) => {
334                if is_signed {
335                    bx.sitofp(imm, to_backend_ty)
336                } else {
337                    bx.uitofp(imm, to_backend_ty)
338                }
339            }
340            (Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
341            (Int(_, is_signed), Pointer(..)) => {
342                let usize_imm = bx.intcast(imm, bx.cx().type_isize(), is_signed);
343                bx.inttoptr(usize_imm, to_backend_ty)
344            }
345            (Float(_), Int(_, is_signed)) => bx.cast_float_to_int(is_signed, imm, to_backend_ty),
346            _ => return None,
347        };
348        Some(imm)
349    }
350
351    /// Transmutes one of the immediates from an [`OperandValue::Immediate`]
352    /// or an [`OperandValue::Pair`] to an immediate of the target type.
353    ///
354    /// `to_backend_ty` must be the *non*-immediate backend type (so it will be
355    /// `i8`, not `i1`, for `bool`-like types.)
356    fn transmute_immediate(
357        &self,
358        bx: &mut Bx,
359        mut imm: Bx::Value,
360        from_scalar: abi::Scalar,
361        from_backend_ty: Bx::Type,
362        to_scalar: abi::Scalar,
363        to_backend_ty: Bx::Type,
364    ) -> Bx::Value {
365        assert_eq!(from_scalar.size(self.cx), to_scalar.size(self.cx));
366
367        // While optimizations will remove no-op transmutes, they might still be
368        // there in debug or things that aren't no-op in MIR because they change
369        // the Rust type but not the underlying layout/niche.
370        if from_scalar == to_scalar && from_backend_ty == to_backend_ty {
371            return imm;
372        }
373
374        use abi::Primitive::*;
375        imm = bx.from_immediate(imm);
376
377        // If we have a scalar, we must already know its range. Either
378        //
379        // 1) It's a parameter with `range` parameter metadata,
380        // 2) It's something we `load`ed with `!range` metadata, or
381        // 3) After a transmute we `assume`d the range (see below).
382        //
383        // That said, last time we tried removing this, it didn't actually help
384        // the rustc-perf results, so might as well keep doing it
385        // <https://github.com/rust-lang/rust/pull/135610#issuecomment-2599275182>
386        self.assume_scalar_range(bx, imm, from_scalar, from_backend_ty);
387
388        imm = match (from_scalar.primitive(), to_scalar.primitive()) {
389            (Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
390            (Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
391            (Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm),
392            (Pointer(..), Int(..)) => {
393                // FIXME: this exposes the provenance, which shouldn't be necessary.
394                bx.ptrtoint(imm, to_backend_ty)
395            }
396            (Float(_), Pointer(..)) => {
397                let int_imm = bx.bitcast(imm, bx.cx().type_isize());
398                bx.ptradd(bx.const_null(bx.type_ptr()), int_imm)
399            }
400            (Pointer(..), Float(_)) => {
401                // FIXME: this exposes the provenance, which shouldn't be necessary.
402                let int_imm = bx.ptrtoint(imm, bx.cx().type_isize());
403                bx.bitcast(int_imm, to_backend_ty)
404            }
405        };
406
407        // This `assume` remains important for cases like (a conceptual)
408        //    transmute::<u32, NonZeroU32>(x) == 0
409        // since it's never passed to something with parameter metadata (especially
410        // after MIR inlining) so the only way to tell the backend about the
411        // constraint that the `transmute` introduced is to `assume` it.
412        self.assume_scalar_range(bx, imm, to_scalar, to_backend_ty);
413
414        imm = bx.to_immediate_scalar(imm, to_scalar);
415        imm
416    }
417
418    fn assume_scalar_range(
419        &self,
420        bx: &mut Bx,
421        imm: Bx::Value,
422        scalar: abi::Scalar,
423        backend_ty: Bx::Type,
424    ) {
425        if matches!(self.cx.sess().opts.optimize, OptLevel::No) || scalar.is_always_valid(self.cx) {
426            return;
427        }
428
429        match scalar.primitive() {
430            abi::Primitive::Int(..) => {
431                let range = scalar.valid_range(self.cx);
432                bx.assume_integer_range(imm, backend_ty, range);
433            }
434            abi::Primitive::Pointer(abi::AddressSpace::DATA)
435                if !scalar.valid_range(self.cx).contains(0) =>
436            {
437                bx.assume_nonnull(imm);
438            }
439            abi::Primitive::Pointer(..) | abi::Primitive::Float(..) => {}
440        }
441    }
442
443    pub(crate) fn codegen_rvalue_unsized(
444        &mut self,
445        bx: &mut Bx,
446        indirect_dest: PlaceRef<'tcx, Bx::Value>,
447        rvalue: &mir::Rvalue<'tcx>,
448    ) {
449        debug!(
450            "codegen_rvalue_unsized(indirect_dest.llval={:?}, rvalue={:?})",
451            indirect_dest.val.llval, rvalue
452        );
453
454        match *rvalue {
455            mir::Rvalue::Use(ref operand) => {
456                let cg_operand = self.codegen_operand(bx, operand);
457                cg_operand.val.store_unsized(bx, indirect_dest);
458            }
459
460            _ => bug!("unsized assignment other than `Rvalue::Use`"),
461        }
462    }
463
464    pub(crate) fn codegen_rvalue_operand(
465        &mut self,
466        bx: &mut Bx,
467        rvalue: &mir::Rvalue<'tcx>,
468    ) -> OperandRef<'tcx, Bx::Value> {
469        assert!(
470            self.rvalue_creates_operand(rvalue, DUMMY_SP),
471            "cannot codegen {rvalue:?} to operand",
472        );
473
474        match *rvalue {
475            mir::Rvalue::Cast(ref kind, ref source, mir_cast_ty) => {
476                let operand = self.codegen_operand(bx, source);
477                debug!("cast operand is {:?}", operand);
478                let cast = bx.cx().layout_of(self.monomorphize(mir_cast_ty));
479
480                let val = match *kind {
481                    mir::CastKind::PointerExposeProvenance => {
482                        assert!(bx.cx().is_backend_immediate(cast));
483                        let llptr = operand.immediate();
484                        let llcast_ty = bx.cx().immediate_backend_type(cast);
485                        let lladdr = bx.ptrtoint(llptr, llcast_ty);
486                        OperandValue::Immediate(lladdr)
487                    }
488                    mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _) => {
489                        match *operand.layout.ty.kind() {
490                            ty::FnDef(def_id, args) => {
491                                let instance = ty::Instance::resolve_for_fn_ptr(
492                                    bx.tcx(),
493                                    bx.typing_env(),
494                                    def_id,
495                                    args,
496                                )
497                                .unwrap();
498                                OperandValue::Immediate(bx.get_fn_addr(instance))
499                            }
500                            _ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty),
501                        }
502                    }
503                    mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_), _) => {
504                        match *operand.layout.ty.kind() {
505                            ty::Closure(def_id, args) => {
506                                let instance = Instance::resolve_closure(
507                                    bx.cx().tcx(),
508                                    def_id,
509                                    args,
510                                    ty::ClosureKind::FnOnce,
511                                );
512                                OperandValue::Immediate(bx.cx().get_fn_addr(instance))
513                            }
514                            _ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty),
515                        }
516                    }
517                    mir::CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer, _) => {
518                        // This is a no-op at the LLVM level.
519                        operand.val
520                    }
521                    mir::CastKind::PointerCoercion(PointerCoercion::Unsize, _) => {
522                        assert!(bx.cx().is_backend_scalar_pair(cast));
523                        let (lldata, llextra) = operand.val.pointer_parts();
524                        let (lldata, llextra) =
525                            base::unsize_ptr(bx, lldata, operand.layout.ty, cast.ty, llextra);
526                        OperandValue::Pair(lldata, llextra)
527                    }
528                    mir::CastKind::PointerCoercion(
529                        PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, _
530                    ) => {
531                        bug!("{kind:?} is for borrowck, and should never appear in codegen");
532                    }
533                    mir::CastKind::PtrToPtr
534                        if bx.cx().is_backend_scalar_pair(operand.layout) =>
535                    {
536                        if let OperandValue::Pair(data_ptr, meta) = operand.val {
537                            if bx.cx().is_backend_scalar_pair(cast) {
538                                OperandValue::Pair(data_ptr, meta)
539                            } else {
540                                // Cast of wide-ptr to thin-ptr is an extraction of data-ptr.
541                                OperandValue::Immediate(data_ptr)
542                            }
543                        } else {
544                            bug!("unexpected non-pair operand");
545                        }
546                    }
547                    mir::CastKind::PointerCoercion(PointerCoercion::DynStar, _) => {
548                        let (lldata, llextra) = operand.val.pointer_parts();
549                        let (lldata, llextra) =
550                            base::cast_to_dyn_star(bx, lldata, operand.layout, cast.ty, llextra);
551                        OperandValue::Pair(lldata, llextra)
552                    }
553                    | mir::CastKind::IntToInt
554                    | mir::CastKind::FloatToInt
555                    | mir::CastKind::FloatToFloat
556                    | mir::CastKind::IntToFloat
557                    | mir::CastKind::PtrToPtr
558                    | mir::CastKind::FnPtrToPtr
559                    // Since int2ptr can have arbitrary integer types as input (so we have to do
560                    // sign extension and all that), it is currently best handled in the same code
561                    // path as the other integer-to-X casts.
562                    | mir::CastKind::PointerWithExposedProvenance => {
563                        let imm = operand.immediate();
564                        let operand_kind = self.value_kind(operand.layout);
565                        let OperandValueKind::Immediate(from_scalar) = operand_kind else {
566                            bug!("Found {operand_kind:?} for operand {operand:?}");
567                        };
568                        let from_backend_ty = bx.cx().immediate_backend_type(operand.layout);
569
570                        assert!(bx.cx().is_backend_immediate(cast));
571                        let to_backend_ty = bx.cx().immediate_backend_type(cast);
572                        if operand.layout.is_uninhabited() {
573                            let val = OperandValue::Immediate(bx.cx().const_poison(to_backend_ty));
574                            return OperandRef { val, layout: cast };
575                        }
576                        let cast_kind = self.value_kind(cast);
577                        let OperandValueKind::Immediate(to_scalar) = cast_kind else {
578                            bug!("Found {cast_kind:?} for operand {cast:?}");
579                        };
580
581                        self.cast_immediate(bx, imm, from_scalar, from_backend_ty, to_scalar, to_backend_ty)
582                            .map(OperandValue::Immediate)
583                            .unwrap_or_else(|| {
584                                bug!("Unsupported cast of {operand:?} to {cast:?}");
585                            })
586                    }
587                    mir::CastKind::Transmute => {
588                        self.codegen_transmute_operand(bx, operand, cast).unwrap_or_else(|| {
589                            bug!("Unsupported transmute-as-operand of {operand:?} to {cast:?}");
590                        })
591                    }
592                };
593                OperandRef { val, layout: cast }
594            }
595
596            mir::Rvalue::Ref(_, bk, place) => {
597                let mk_ref = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| {
598                    Ty::new_ref(tcx, tcx.lifetimes.re_erased, ty, bk.to_mutbl_lossy())
599                };
600                self.codegen_place_to_pointer(bx, place, mk_ref)
601            }
602
603            mir::Rvalue::CopyForDeref(place) => {
604                self.codegen_operand(bx, &mir::Operand::Copy(place))
605            }
606            mir::Rvalue::RawPtr(kind, place) => {
607                let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| {
608                    Ty::new_ptr(tcx, ty, kind.to_mutbl_lossy())
609                };
610                self.codegen_place_to_pointer(bx, place, mk_ptr)
611            }
612
613            mir::Rvalue::Len(place) => {
614                let size = self.evaluate_array_len(bx, place);
615                OperandRef {
616                    val: OperandValue::Immediate(size),
617                    layout: bx.cx().layout_of(bx.tcx().types.usize),
618                }
619            }
620
621            mir::Rvalue::BinaryOp(op_with_overflow, box (ref lhs, ref rhs))
622                if let Some(op) = op_with_overflow.overflowing_to_wrapping() =>
623            {
624                let lhs = self.codegen_operand(bx, lhs);
625                let rhs = self.codegen_operand(bx, rhs);
626                let result = self.codegen_scalar_checked_binop(
627                    bx,
628                    op,
629                    lhs.immediate(),
630                    rhs.immediate(),
631                    lhs.layout.ty,
632                );
633                let val_ty = op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty);
634                let operand_ty = Ty::new_tup(bx.tcx(), &[val_ty, bx.tcx().types.bool]);
635                OperandRef { val: result, layout: bx.cx().layout_of(operand_ty) }
636            }
637            mir::Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => {
638                let lhs = self.codegen_operand(bx, lhs);
639                let rhs = self.codegen_operand(bx, rhs);
640                let llresult = match (lhs.val, rhs.val) {
641                    (
642                        OperandValue::Pair(lhs_addr, lhs_extra),
643                        OperandValue::Pair(rhs_addr, rhs_extra),
644                    ) => self.codegen_wide_ptr_binop(
645                        bx,
646                        op,
647                        lhs_addr,
648                        lhs_extra,
649                        rhs_addr,
650                        rhs_extra,
651                        lhs.layout.ty,
652                    ),
653
654                    (OperandValue::Immediate(lhs_val), OperandValue::Immediate(rhs_val)) => self
655                        .codegen_scalar_binop(
656                            bx,
657                            op,
658                            lhs_val,
659                            rhs_val,
660                            lhs.layout.ty,
661                            rhs.layout.ty,
662                        ),
663
664                    _ => bug!(),
665                };
666                OperandRef {
667                    val: OperandValue::Immediate(llresult),
668                    layout: bx.cx().layout_of(op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty)),
669                }
670            }
671
672            mir::Rvalue::UnaryOp(op, ref operand) => {
673                let operand = self.codegen_operand(bx, operand);
674                let is_float = operand.layout.ty.is_floating_point();
675                let (val, layout) = match op {
676                    mir::UnOp::Not => {
677                        let llval = bx.not(operand.immediate());
678                        (OperandValue::Immediate(llval), operand.layout)
679                    }
680                    mir::UnOp::Neg => {
681                        let llval = if is_float {
682                            bx.fneg(operand.immediate())
683                        } else {
684                            bx.neg(operand.immediate())
685                        };
686                        (OperandValue::Immediate(llval), operand.layout)
687                    }
688                    mir::UnOp::PtrMetadata => {
689                        assert!(operand.layout.ty.is_raw_ptr() || operand.layout.ty.is_ref(),);
690                        let (_, meta) = operand.val.pointer_parts();
691                        assert_eq!(operand.layout.fields.count() > 1, meta.is_some());
692                        if let Some(meta) = meta {
693                            (OperandValue::Immediate(meta), operand.layout.field(self.cx, 1))
694                        } else {
695                            (OperandValue::ZeroSized, bx.cx().layout_of(bx.tcx().types.unit))
696                        }
697                    }
698                };
699                assert!(
700                    val.is_expected_variant_for_type(self.cx, layout),
701                    "Made wrong variant {val:?} for type {layout:?}",
702                );
703                OperandRef { val, layout }
704            }
705
706            mir::Rvalue::Discriminant(ref place) => {
707                let discr_ty = rvalue.ty(self.mir, bx.tcx());
708                let discr_ty = self.monomorphize(discr_ty);
709                let operand = self.codegen_consume(bx, place.as_ref());
710                let discr = operand.codegen_get_discr(self, bx, discr_ty);
711                OperandRef {
712                    val: OperandValue::Immediate(discr),
713                    layout: self.cx.layout_of(discr_ty),
714                }
715            }
716
717            mir::Rvalue::NullaryOp(ref null_op, ty) => {
718                let ty = self.monomorphize(ty);
719                let layout = bx.cx().layout_of(ty);
720                let val = match null_op {
721                    mir::NullOp::SizeOf => {
722                        assert!(bx.cx().type_is_sized(ty));
723                        let val = layout.size.bytes();
724                        bx.cx().const_usize(val)
725                    }
726                    mir::NullOp::AlignOf => {
727                        assert!(bx.cx().type_is_sized(ty));
728                        let val = layout.align.abi.bytes();
729                        bx.cx().const_usize(val)
730                    }
731                    mir::NullOp::OffsetOf(fields) => {
732                        let val = bx
733                            .tcx()
734                            .offset_of_subfield(bx.typing_env(), layout, fields.iter())
735                            .bytes();
736                        bx.cx().const_usize(val)
737                    }
738                    mir::NullOp::UbChecks => {
739                        let val = bx.tcx().sess.ub_checks();
740                        bx.cx().const_bool(val)
741                    }
742                    mir::NullOp::ContractChecks => {
743                        let val = bx.tcx().sess.contract_checks();
744                        bx.cx().const_bool(val)
745                    }
746                };
747                let tcx = self.cx.tcx();
748                OperandRef {
749                    val: OperandValue::Immediate(val),
750                    layout: self.cx.layout_of(null_op.ty(tcx)),
751                }
752            }
753
754            mir::Rvalue::ThreadLocalRef(def_id) => {
755                assert!(bx.cx().tcx().is_static(def_id));
756                let layout = bx.layout_of(bx.cx().tcx().static_ptr_ty(def_id, bx.typing_env()));
757                let static_ = if !def_id.is_local() && bx.cx().tcx().needs_thread_local_shim(def_id)
758                {
759                    let instance = ty::Instance {
760                        def: ty::InstanceKind::ThreadLocalShim(def_id),
761                        args: ty::GenericArgs::empty(),
762                    };
763                    let fn_ptr = bx.get_fn_addr(instance);
764                    let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
765                    let fn_ty = bx.fn_decl_backend_type(fn_abi);
766                    let fn_attrs = if bx.tcx().def_kind(instance.def_id()).has_codegen_attrs() {
767                        Some(bx.tcx().codegen_fn_attrs(instance.def_id()))
768                    } else {
769                        None
770                    };
771                    bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None, Some(instance))
772                } else {
773                    bx.get_static(def_id)
774                };
775                OperandRef { val: OperandValue::Immediate(static_), layout }
776            }
777            mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
778            mir::Rvalue::Repeat(..) => bug!("{rvalue:?} in codegen_rvalue_operand"),
779            mir::Rvalue::Aggregate(_, ref fields) => {
780                let ty = rvalue.ty(self.mir, self.cx.tcx());
781                let ty = self.monomorphize(ty);
782                let layout = self.cx.layout_of(ty);
783
784                // `rvalue_creates_operand` has arranged that we only get here if
785                // we can build the aggregate immediate from the field immediates.
786                let mut inputs = ArrayVec::<Bx::Value, 2>::new();
787                let mut input_scalars = ArrayVec::<abi::Scalar, 2>::new();
788                for field_idx in layout.fields.index_by_increasing_offset() {
789                    let field_idx = FieldIdx::from_usize(field_idx);
790                    let op = self.codegen_operand(bx, &fields[field_idx]);
791                    let values = op.val.immediates_or_place().left_or_else(|p| {
792                        bug!("Field {field_idx:?} is {p:?} making {layout:?}");
793                    });
794                    let scalars = self.value_kind(op.layout).scalars().unwrap();
795                    assert_eq!(values.len(), scalars.len());
796                    inputs.extend(values);
797                    input_scalars.extend(scalars);
798                }
799
800                let output_scalars = self.value_kind(layout).scalars().unwrap();
801                itertools::izip!(&mut inputs, input_scalars, output_scalars).for_each(
802                    |(v, in_s, out_s)| {
803                        if in_s != out_s {
804                            // We have to be really careful about bool here, because
805                            // `(bool,)` stays i1 but `Cell<bool>` becomes i8.
806                            *v = bx.from_immediate(*v);
807                            *v = bx.to_immediate_scalar(*v, out_s);
808                        }
809                    },
810                );
811
812                let val = OperandValue::from_immediates(inputs);
813                assert!(
814                    val.is_expected_variant_for_type(self.cx, layout),
815                    "Made wrong variant {val:?} for type {layout:?}",
816                );
817                OperandRef { val, layout }
818            }
819            mir::Rvalue::ShallowInitBox(ref operand, content_ty) => {
820                let operand = self.codegen_operand(bx, operand);
821                let val = operand.immediate();
822
823                let content_ty = self.monomorphize(content_ty);
824                let box_layout = bx.cx().layout_of(Ty::new_box(bx.tcx(), content_ty));
825
826                OperandRef { val: OperandValue::Immediate(val), layout: box_layout }
827            }
828            mir::Rvalue::WrapUnsafeBinder(ref operand, binder_ty) => {
829                let operand = self.codegen_operand(bx, operand);
830                let binder_ty = self.monomorphize(binder_ty);
831                let layout = bx.cx().layout_of(binder_ty);
832                OperandRef { val: operand.val, layout }
833            }
834        }
835    }
836
837    fn evaluate_array_len(&mut self, bx: &mut Bx, place: mir::Place<'tcx>) -> Bx::Value {
838        // ZST are passed as operands and require special handling
839        // because codegen_place() panics if Local is operand.
840        if let Some(index) = place.as_local()
841            && let LocalRef::Operand(op) = self.locals[index]
842            && let ty::Array(_, n) = op.layout.ty.kind()
843        {
844            let n = n.try_to_target_usize(bx.tcx()).expect("expected monomorphic const in codegen");
845            return bx.cx().const_usize(n);
846        }
847        // use common size calculation for non zero-sized types
848        let cg_value = self.codegen_place(bx, place.as_ref());
849        cg_value.len(bx.cx())
850    }
851
852    /// Codegen an `Rvalue::RawPtr` or `Rvalue::Ref`
853    fn codegen_place_to_pointer(
854        &mut self,
855        bx: &mut Bx,
856        place: mir::Place<'tcx>,
857        mk_ptr_ty: impl FnOnce(TyCtxt<'tcx>, Ty<'tcx>) -> Ty<'tcx>,
858    ) -> OperandRef<'tcx, Bx::Value> {
859        let cg_place = self.codegen_place(bx, place.as_ref());
860        let val = cg_place.val.address();
861
862        let ty = cg_place.layout.ty;
863        assert!(
864            if bx.cx().tcx().type_has_metadata(ty, bx.cx().typing_env()) {
865                matches!(val, OperandValue::Pair(..))
866            } else {
867                matches!(val, OperandValue::Immediate(..))
868            },
869            "Address of place was unexpectedly {val:?} for pointee type {ty:?}",
870        );
871
872        OperandRef { val, layout: self.cx.layout_of(mk_ptr_ty(self.cx.tcx(), ty)) }
873    }
874
875    fn codegen_scalar_binop(
876        &mut self,
877        bx: &mut Bx,
878        op: mir::BinOp,
879        lhs: Bx::Value,
880        rhs: Bx::Value,
881        lhs_ty: Ty<'tcx>,
882        rhs_ty: Ty<'tcx>,
883    ) -> Bx::Value {
884        let is_float = lhs_ty.is_floating_point();
885        let is_signed = lhs_ty.is_signed();
886        match op {
887            mir::BinOp::Add => {
888                if is_float {
889                    bx.fadd(lhs, rhs)
890                } else {
891                    bx.add(lhs, rhs)
892                }
893            }
894            mir::BinOp::AddUnchecked => {
895                if is_signed {
896                    bx.unchecked_sadd(lhs, rhs)
897                } else {
898                    bx.unchecked_uadd(lhs, rhs)
899                }
900            }
901            mir::BinOp::Sub => {
902                if is_float {
903                    bx.fsub(lhs, rhs)
904                } else {
905                    bx.sub(lhs, rhs)
906                }
907            }
908            mir::BinOp::SubUnchecked => {
909                if is_signed {
910                    bx.unchecked_ssub(lhs, rhs)
911                } else {
912                    bx.unchecked_usub(lhs, rhs)
913                }
914            }
915            mir::BinOp::Mul => {
916                if is_float {
917                    bx.fmul(lhs, rhs)
918                } else {
919                    bx.mul(lhs, rhs)
920                }
921            }
922            mir::BinOp::MulUnchecked => {
923                if is_signed {
924                    bx.unchecked_smul(lhs, rhs)
925                } else {
926                    bx.unchecked_umul(lhs, rhs)
927                }
928            }
929            mir::BinOp::Div => {
930                if is_float {
931                    bx.fdiv(lhs, rhs)
932                } else if is_signed {
933                    bx.sdiv(lhs, rhs)
934                } else {
935                    bx.udiv(lhs, rhs)
936                }
937            }
938            mir::BinOp::Rem => {
939                if is_float {
940                    bx.frem(lhs, rhs)
941                } else if is_signed {
942                    bx.srem(lhs, rhs)
943                } else {
944                    bx.urem(lhs, rhs)
945                }
946            }
947            mir::BinOp::BitOr => bx.or(lhs, rhs),
948            mir::BinOp::BitAnd => bx.and(lhs, rhs),
949            mir::BinOp::BitXor => bx.xor(lhs, rhs),
950            mir::BinOp::Offset => {
951                let pointee_type = lhs_ty
952                    .builtin_deref(true)
953                    .unwrap_or_else(|| bug!("deref of non-pointer {:?}", lhs_ty));
954                let pointee_layout = bx.cx().layout_of(pointee_type);
955                if pointee_layout.is_zst() {
956                    // `Offset` works in terms of the size of pointee,
957                    // so offsetting a pointer to ZST is a noop.
958                    lhs
959                } else {
960                    let llty = bx.cx().backend_type(pointee_layout);
961                    if !rhs_ty.is_signed() {
962                        bx.inbounds_nuw_gep(llty, lhs, &[rhs])
963                    } else {
964                        bx.inbounds_gep(llty, lhs, &[rhs])
965                    }
966                }
967            }
968            mir::BinOp::Shl | mir::BinOp::ShlUnchecked => {
969                let rhs = base::build_shift_expr_rhs(bx, lhs, rhs, op == mir::BinOp::ShlUnchecked);
970                bx.shl(lhs, rhs)
971            }
972            mir::BinOp::Shr | mir::BinOp::ShrUnchecked => {
973                let rhs = base::build_shift_expr_rhs(bx, lhs, rhs, op == mir::BinOp::ShrUnchecked);
974                if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) }
975            }
976            mir::BinOp::Ne
977            | mir::BinOp::Lt
978            | mir::BinOp::Gt
979            | mir::BinOp::Eq
980            | mir::BinOp::Le
981            | mir::BinOp::Ge => {
982                if is_float {
983                    bx.fcmp(base::bin_op_to_fcmp_predicate(op), lhs, rhs)
984                } else {
985                    bx.icmp(base::bin_op_to_icmp_predicate(op, is_signed), lhs, rhs)
986                }
987            }
988            mir::BinOp::Cmp => {
989                use std::cmp::Ordering;
990                assert!(!is_float);
991                let pred = |op| base::bin_op_to_icmp_predicate(op, is_signed);
992                if bx.cx().tcx().sess.opts.optimize == OptLevel::No {
993                    // FIXME: This actually generates tighter assembly, and is a classic trick
994                    // <https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign>
995                    // However, as of 2023-11 it optimizes worse in things like derived
996                    // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it
997                    // better (see <https://github.com/llvm/llvm-project/issues/73417>), it'll
998                    // be worth trying it in optimized builds as well.
999                    let is_gt = bx.icmp(pred(mir::BinOp::Gt), lhs, rhs);
1000                    let gtext = bx.zext(is_gt, bx.type_i8());
1001                    let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs);
1002                    let ltext = bx.zext(is_lt, bx.type_i8());
1003                    bx.unchecked_ssub(gtext, ltext)
1004                } else {
1005                    // These operations are those expected by `tests/codegen/integer-cmp.rs`,
1006                    // from <https://github.com/rust-lang/rust/pull/63767>.
1007                    let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs);
1008                    let is_ne = bx.icmp(pred(mir::BinOp::Ne), lhs, rhs);
1009                    let ge = bx.select(
1010                        is_ne,
1011                        bx.cx().const_i8(Ordering::Greater as i8),
1012                        bx.cx().const_i8(Ordering::Equal as i8),
1013                    );
1014                    bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge)
1015                }
1016            }
1017            mir::BinOp::AddWithOverflow
1018            | mir::BinOp::SubWithOverflow
1019            | mir::BinOp::MulWithOverflow => {
1020                bug!("{op:?} needs to return a pair, so call codegen_scalar_checked_binop instead")
1021            }
1022        }
1023    }
1024
1025    fn codegen_wide_ptr_binop(
1026        &mut self,
1027        bx: &mut Bx,
1028        op: mir::BinOp,
1029        lhs_addr: Bx::Value,
1030        lhs_extra: Bx::Value,
1031        rhs_addr: Bx::Value,
1032        rhs_extra: Bx::Value,
1033        _input_ty: Ty<'tcx>,
1034    ) -> Bx::Value {
1035        match op {
1036            mir::BinOp::Eq => {
1037                let lhs = bx.icmp(IntPredicate::IntEQ, lhs_addr, rhs_addr);
1038                let rhs = bx.icmp(IntPredicate::IntEQ, lhs_extra, rhs_extra);
1039                bx.and(lhs, rhs)
1040            }
1041            mir::BinOp::Ne => {
1042                let lhs = bx.icmp(IntPredicate::IntNE, lhs_addr, rhs_addr);
1043                let rhs = bx.icmp(IntPredicate::IntNE, lhs_extra, rhs_extra);
1044                bx.or(lhs, rhs)
1045            }
1046            mir::BinOp::Le | mir::BinOp::Lt | mir::BinOp::Ge | mir::BinOp::Gt => {
1047                // a OP b ~ a.0 STRICT(OP) b.0 | (a.0 == b.0 && a.1 OP a.1)
1048                let (op, strict_op) = match op {
1049                    mir::BinOp::Lt => (IntPredicate::IntULT, IntPredicate::IntULT),
1050                    mir::BinOp::Le => (IntPredicate::IntULE, IntPredicate::IntULT),
1051                    mir::BinOp::Gt => (IntPredicate::IntUGT, IntPredicate::IntUGT),
1052                    mir::BinOp::Ge => (IntPredicate::IntUGE, IntPredicate::IntUGT),
1053                    _ => bug!(),
1054                };
1055                let lhs = bx.icmp(strict_op, lhs_addr, rhs_addr);
1056                let and_lhs = bx.icmp(IntPredicate::IntEQ, lhs_addr, rhs_addr);
1057                let and_rhs = bx.icmp(op, lhs_extra, rhs_extra);
1058                let rhs = bx.and(and_lhs, and_rhs);
1059                bx.or(lhs, rhs)
1060            }
1061            _ => {
1062                bug!("unexpected wide ptr binop");
1063            }
1064        }
1065    }
1066
1067    fn codegen_scalar_checked_binop(
1068        &mut self,
1069        bx: &mut Bx,
1070        op: mir::BinOp,
1071        lhs: Bx::Value,
1072        rhs: Bx::Value,
1073        input_ty: Ty<'tcx>,
1074    ) -> OperandValue<Bx::Value> {
1075        let (val, of) = match op {
1076            // These are checked using intrinsics
1077            mir::BinOp::Add | mir::BinOp::Sub | mir::BinOp::Mul => {
1078                let oop = match op {
1079                    mir::BinOp::Add => OverflowOp::Add,
1080                    mir::BinOp::Sub => OverflowOp::Sub,
1081                    mir::BinOp::Mul => OverflowOp::Mul,
1082                    _ => unreachable!(),
1083                };
1084                bx.checked_binop(oop, input_ty, lhs, rhs)
1085            }
1086            _ => bug!("Operator `{:?}` is not a checkable operator", op),
1087        };
1088
1089        OperandValue::Pair(val, of)
1090    }
1091
1092    pub(crate) fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>, span: Span) -> bool {
1093        match *rvalue {
1094            mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, cast_ty) => {
1095                let operand_ty = operand.ty(self.mir, self.cx.tcx());
1096                let cast_layout = self.cx.layout_of(self.monomorphize(cast_ty));
1097                let operand_layout = self.cx.layout_of(self.monomorphize(operand_ty));
1098
1099                match (self.value_kind(operand_layout), self.value_kind(cast_layout)) {
1100                    // Can always load from a pointer as needed
1101                    (OperandValueKind::Ref, _) => true,
1102
1103                    // ZST-to-ZST is the easiest thing ever
1104                    (OperandValueKind::ZeroSized, OperandValueKind::ZeroSized) => true,
1105
1106                    // But if only one of them is a ZST the sizes can't match
1107                    (OperandValueKind::ZeroSized, _) | (_, OperandValueKind::ZeroSized) => false,
1108
1109                    // Need to generate an `alloc` to get a pointer from an immediate
1110                    (OperandValueKind::Immediate(..) | OperandValueKind::Pair(..), OperandValueKind::Ref) => false,
1111
1112                    // When we have scalar immediates, we can only convert things
1113                    // where the sizes match, to avoid endianness questions.
1114                    (OperandValueKind::Immediate(a), OperandValueKind::Immediate(b)) =>
1115                        a.size(self.cx) == b.size(self.cx),
1116                    (OperandValueKind::Pair(a0, a1), OperandValueKind::Pair(b0, b1)) =>
1117                        a0.size(self.cx) == b0.size(self.cx) && a1.size(self.cx) == b1.size(self.cx),
1118
1119                    // Send mixings between scalars and pairs through the memory route
1120                    // FIXME: Maybe this could use insertvalue/extractvalue instead?
1121                    (OperandValueKind::Immediate(..), OperandValueKind::Pair(..)) |
1122                    (OperandValueKind::Pair(..), OperandValueKind::Immediate(..)) => false,
1123                }
1124            }
1125            mir::Rvalue::Ref(..) |
1126            mir::Rvalue::CopyForDeref(..) |
1127            mir::Rvalue::RawPtr(..) |
1128            mir::Rvalue::Len(..) |
1129            mir::Rvalue::Cast(..) | // (*)
1130            mir::Rvalue::ShallowInitBox(..) | // (*)
1131            mir::Rvalue::BinaryOp(..) |
1132            mir::Rvalue::UnaryOp(..) |
1133            mir::Rvalue::Discriminant(..) |
1134            mir::Rvalue::NullaryOp(..) |
1135            mir::Rvalue::ThreadLocalRef(_) |
1136            mir::Rvalue::Use(..) |
1137            mir::Rvalue::WrapUnsafeBinder(..) => // (*)
1138                true,
1139            // Arrays are always aggregates, so it's not worth checking anything here.
1140            // (If it's really `[(); N]` or `[T; 0]` and we use the place path, fine.)
1141            mir::Rvalue::Repeat(..) => false,
1142            mir::Rvalue::Aggregate(ref kind, _) => {
1143                let allowed_kind = match **kind {
1144                    // This always produces a `ty::RawPtr`, so will be Immediate or Pair
1145                    mir::AggregateKind::RawPtr(..) => true,
1146                    mir::AggregateKind::Array(..) => false,
1147                    mir::AggregateKind::Tuple => true,
1148                    mir::AggregateKind::Adt(def_id, ..) => {
1149                        let adt_def = self.cx.tcx().adt_def(def_id);
1150                        adt_def.is_struct() && !adt_def.repr().simd()
1151                    }
1152                    mir::AggregateKind::Closure(..) => true,
1153                    // FIXME: Can we do this for simple coroutines too?
1154                    mir::AggregateKind::Coroutine(..) | mir::AggregateKind::CoroutineClosure(..) => false,
1155                };
1156                allowed_kind && {
1157                let ty = rvalue.ty(self.mir, self.cx.tcx());
1158                let ty = self.monomorphize(ty);
1159                    let layout = self.cx.spanned_layout_of(ty, span);
1160                    !self.cx.is_backend_ref(layout)
1161                }
1162            }
1163        }
1164
1165        // (*) this is only true if the type is suitable
1166    }
1167
1168    /// Gets which variant of [`OperandValue`] is expected for a particular type.
1169    fn value_kind(&self, layout: TyAndLayout<'tcx>) -> OperandValueKind {
1170        if layout.is_zst() {
1171            OperandValueKind::ZeroSized
1172        } else if self.cx.is_backend_immediate(layout) {
1173            assert!(!self.cx.is_backend_scalar_pair(layout));
1174            OperandValueKind::Immediate(match layout.backend_repr {
1175                abi::BackendRepr::Scalar(s) => s,
1176                abi::BackendRepr::SimdVector { element, .. } => element,
1177                x => span_bug!(self.mir.span, "Couldn't translate {x:?} as backend immediate"),
1178            })
1179        } else if self.cx.is_backend_scalar_pair(layout) {
1180            let abi::BackendRepr::ScalarPair(s1, s2) = layout.backend_repr else {
1181                span_bug!(
1182                    self.mir.span,
1183                    "Couldn't translate {:?} as backend scalar pair",
1184                    layout.backend_repr,
1185                );
1186            };
1187            OperandValueKind::Pair(s1, s2)
1188        } else {
1189            OperandValueKind::Ref
1190        }
1191    }
1192}
1193
1194/// The variants of this match [`OperandValue`], giving details about the
1195/// backend values that will be held in that other type.
1196#[derive(Debug, Copy, Clone)]
1197enum OperandValueKind {
1198    Ref,
1199    Immediate(abi::Scalar),
1200    Pair(abi::Scalar, abi::Scalar),
1201    ZeroSized,
1202}
1203
1204impl OperandValueKind {
1205    fn scalars(self) -> Option<ArrayVec<abi::Scalar, 2>> {
1206        Some(match self {
1207            OperandValueKind::ZeroSized => ArrayVec::new(),
1208            OperandValueKind::Immediate(a) => ArrayVec::from_iter([a]),
1209            OperandValueKind::Pair(a, b) => [a, b].into(),
1210            OperandValueKind::Ref => return None,
1211        })
1212    }
1213}