| 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::diagnostics; | |
| 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 tcx = self.cx.tcx(); | |
| let uv = match self.monomorphize(constant.const_) { | |
| mir::Const::Unevaluated(uv, _) => uv.shrink(tcx), | |
| 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); | |
| 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.to_branch(); | |
| assert_eq!(fields.len(), 1); | |
| let array = fields[0].to_branch(); | |
| // Iterate over the array elements to obtain the values in the vector. | |
| let values: Vec<_> = array | |
| .iter() | |
| .map(|field| { | |
| let Some(prim) = field.try_to_scalar() else { | |
| bug!("field is not a scalar {:?}", field) | |
| }; | |
| 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)) | |
| }) | |
| .collect(); | |
| bx.const_vector(&values) | |
| }) | |
| .unwrap_or_else(|| { | |
| bx.tcx() | |
| .dcx() | |
| .emit_err(diagnostics::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) | |
| } | |
| } |