| 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<Result<ty::ValTree<'tcx>, 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) |
| } |
| } |