use rustc_abi::BackendRepr; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv}; use rustc_middle::ty::{self, Ty}; use rustc_middle::{bug, mir, span_bug}; use super::FunctionCx; use crate::errors; use crate::mir::operand::OperandRef; use crate::traits::*; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub(crate) fn eval_mir_constant_to_operand( &self, bx: &mut Bx, constant: &mir::ConstOperand<'tcx>, ) -> OperandRef<'tcx, Bx::Value> { let val = self.eval_mir_constant(constant); let ty = self.monomorphize(constant.ty()); OperandRef::from_const(bx, val, ty) } pub fn eval_mir_constant(&self, constant: &mir::ConstOperand<'tcx>) -> mir::ConstValue { // `MirUsedCollector` visited all required_consts before codegen began, so if we got here // there can be no more constants that fail to evaluate. self.monomorphize(constant.const_) .eval(self.cx.tcx(), self.cx.typing_env(), constant.span) .expect("erroneous constant missed by mono item collection") } /// This is a convenience helper for `immediate_const_vector`. It has the precondition /// that the given `constant` is an `Const::Unevaluated` and must be convertible to /// a `ValTree`. If you want a more general version of this, talk to `wg-const-eval` on zulip. /// /// Note that this function is cursed, since usually MIR consts should not be evaluated to /// valtrees! fn eval_unevaluated_mir_constant_to_valtree( &self, constant: &mir::ConstOperand<'tcx>, ) -> Result, Ty<'tcx>>, ErrorHandled> { let uv = match self.monomorphize(constant.const_) { mir::Const::Unevaluated(uv, _) => uv.shrink(), mir::Const::Ty(_, c) => match c.kind() { // A constant that came from a const generic but was then used as an argument to // old-style simd_shuffle (passing as argument instead of as a generic param). ty::ConstKind::Value(cv) => return Ok(Ok(cv.valtree)), other => span_bug!(constant.span, "{other:#?}"), }, // We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate // a constant and write that value back into `Operand`s. This could happen, but is // unlikely. Also: all users of `simd_shuffle` are on unstable and already need to take // a lot of care around intrinsics. For an issue to happen here, it would require a // macro expanding to a `simd_shuffle` call without wrapping the constant argument in a // `const {}` block, but the user pass through arbitrary expressions. // FIXME(oli-obk): replace the magic const generic argument of `simd_shuffle` with a // real const generic, and get rid of this entire function. other => span_bug!(constant.span, "{other:#?}"), }; let uv = self.monomorphize(uv); self.cx.tcx().const_eval_resolve_for_typeck(self.cx.typing_env(), uv, constant.span) } /// process constant containing SIMD shuffle indices & constant vectors pub fn immediate_const_vector( &mut self, bx: &Bx, constant: &mir::ConstOperand<'tcx>, ) -> (Bx::Value, Ty<'tcx>) { let ty = self.monomorphize(constant.ty()); assert!(ty.is_simd()); let field_ty = ty.simd_size_and_type(bx.tcx()).1; let val = self .eval_unevaluated_mir_constant_to_valtree(constant) .ok() .map(|x| x.ok()) .flatten() .map(|val| { // A SIMD type has a single field, which is an array. let fields = val.unwrap_branch(); assert_eq!(fields.len(), 1); let array = fields[0].unwrap_branch(); // Iterate over the array elements to obtain the values in the vector. let values: Vec<_> = array .iter() .map(|field| { if let Some(prim) = field.try_to_scalar() { let layout = bx.layout_of(field_ty); let BackendRepr::Scalar(scalar) = layout.backend_repr else { bug!("from_const: invalid ByVal layout: {:#?}", layout); }; bx.scalar_to_backend(prim, scalar, bx.immediate_backend_type(layout)) } else { bug!("field is not a scalar {:?}", field) } }) .collect(); bx.const_vector(&values) }) .unwrap_or_else(|| { bx.tcx().dcx().emit_err(errors::ShuffleIndicesEvaluation { span: constant.span }); // We've errored, so we don't have to produce working code. let llty = bx.backend_type(bx.layout_of(ty)); bx.const_undef(llty) }); (val, ty) } }