|  | 
|  | 1 | +use rustc_abi::Abi; | 
|  | 2 | +use rustc_middle::ty::{self, Instance, InstanceKind, ParamEnv, Ty, TyCtxt}; | 
|  | 3 | +use rustc_span::def_id::DefId; | 
|  | 4 | +use rustc_span::{Span, Symbol}; | 
|  | 5 | +use rustc_target::abi::call::{FnAbi, PassMode}; | 
|  | 6 | + | 
|  | 7 | +use crate::errors::{AbiErrorDisabledVectorTypeCall, AbiErrorDisabledVectorTypeDef}; | 
|  | 8 | + | 
|  | 9 | +// Represents the least-constraining feature that is required for vector types up to a certain size | 
|  | 10 | +// to have their "proper" ABI. | 
|  | 11 | +const X86_VECTOR_FEATURES: &'static [(u64, &'static str)] = | 
|  | 12 | +    &[(128, "sse"), (256, "avx"), (512, "avx512f")]; | 
|  | 13 | + | 
|  | 14 | +fn do_check_abi<'tcx>( | 
|  | 15 | +    tcx: TyCtxt<'tcx>, | 
|  | 16 | +    abi: &FnAbi<'tcx, Ty<'tcx>>, | 
|  | 17 | +    target_feature_def: DefId, | 
|  | 18 | +    emit_err: impl Fn(&'static str), | 
|  | 19 | +) { | 
|  | 20 | +    let feature_def = if tcx.sess.target.arch == "x86" || tcx.sess.target.arch == "x86_64" { | 
|  | 21 | +        X86_VECTOR_FEATURES | 
|  | 22 | +    } else if tcx.sess.target.arch == "aarch64" { | 
|  | 23 | +        // ABI on aarch64 does not depend on target features. | 
|  | 24 | +        return; | 
|  | 25 | +    } else { | 
|  | 26 | +        // FIXME: add support for non-tier1 architectures | 
|  | 27 | +        return; | 
|  | 28 | +    }; | 
|  | 29 | +    let codegen_attrs = tcx.codegen_fn_attrs(target_feature_def); | 
|  | 30 | +    for arg_abi in abi.args.iter().chain(std::iter::once(&abi.ret)) { | 
|  | 31 | +        let size = arg_abi.layout.size; | 
|  | 32 | +        if matches!(arg_abi.layout.abi, Abi::Vector { .. }) | 
|  | 33 | +            && !matches!(arg_abi.mode, PassMode::Indirect { .. }) | 
|  | 34 | +        { | 
|  | 35 | +            let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) { | 
|  | 36 | +                Some((_, feature)) => feature, | 
|  | 37 | +                None => panic!("Unknown vector size: {}; arg = {:?}", size.bits(), arg_abi), | 
|  | 38 | +            }; | 
|  | 39 | +            let feature_sym = Symbol::intern(feature); | 
|  | 40 | +            if !tcx.sess.unstable_target_features.contains(&feature_sym) | 
|  | 41 | +                && !codegen_attrs.target_features.iter().any(|x| x.name == feature_sym) | 
|  | 42 | +            { | 
|  | 43 | +                emit_err(feature); | 
|  | 44 | +            } | 
|  | 45 | +        } | 
|  | 46 | +    } | 
|  | 47 | +} | 
|  | 48 | + | 
|  | 49 | +/// Checks that the ABI of a given instance of a function does not contain vector-passed arguments | 
|  | 50 | +/// or return values for which the corresponding target feature is not enabled. | 
|  | 51 | +pub fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { | 
|  | 52 | +    let param_env = ParamEnv::reveal_all(); | 
|  | 53 | +    let Ok(abi) = tcx.fn_abi_of_instance(param_env.and((instance, ty::List::empty()))) else { | 
|  | 54 | +        // An error will be reported during codegen if we cannot determine the ABI of this | 
|  | 55 | +        // function. | 
|  | 56 | +        return; | 
|  | 57 | +    }; | 
|  | 58 | +    do_check_abi(tcx, abi, instance.def_id(), |required_feature| { | 
|  | 59 | +        tcx.dcx().emit_err(AbiErrorDisabledVectorTypeDef { | 
|  | 60 | +            span: tcx.def_span(instance.def_id()), | 
|  | 61 | +            required_feature, | 
|  | 62 | +        }); | 
|  | 63 | +    }) | 
|  | 64 | +} | 
|  | 65 | + | 
|  | 66 | +/// Checks that a call expression does not try to pass a vector-passed argument which requires a | 
|  | 67 | +/// target feature that the caller does not have, as doing so causes UB because of ABI mismatch. | 
|  | 68 | +pub fn check_call_site_abi<'tcx>( | 
|  | 69 | +    tcx: TyCtxt<'tcx>, | 
|  | 70 | +    ty: Ty<'tcx>, | 
|  | 71 | +    span: Span, | 
|  | 72 | +    caller: InstanceKind<'tcx>, | 
|  | 73 | +) { | 
|  | 74 | +    let param_env = ParamEnv::reveal_all(); | 
|  | 75 | +    let callee_abi = match *ty.kind() { | 
|  | 76 | +        ty::FnPtr(..) => tcx.fn_abi_of_fn_ptr(param_env.and((ty.fn_sig(tcx), ty::List::empty()))), | 
|  | 77 | +        ty::FnDef(def_id, args) => { | 
|  | 78 | +            // Intrinsics are handled separately by the compiler. | 
|  | 79 | +            if tcx.intrinsic(def_id).is_some() { | 
|  | 80 | +                return; | 
|  | 81 | +            } | 
|  | 82 | +            let instance = ty::Instance::expect_resolve(tcx, param_env, def_id, args, span); | 
|  | 83 | +            tcx.fn_abi_of_instance(param_env.and((instance, ty::List::empty()))) | 
|  | 84 | +        } | 
|  | 85 | +        _ => { | 
|  | 86 | +            panic!("Invalid function call"); | 
|  | 87 | +        } | 
|  | 88 | +    }; | 
|  | 89 | + | 
|  | 90 | +    let Ok(callee_abi) = callee_abi else { | 
|  | 91 | +        // ABI failed to compute; this will not get through codegen. | 
|  | 92 | +        return; | 
|  | 93 | +    }; | 
|  | 94 | +    do_check_abi(tcx, callee_abi, caller.def_id(), |required_feature| { | 
|  | 95 | +        tcx.dcx().emit_err(AbiErrorDisabledVectorTypeCall { span, required_feature }); | 
|  | 96 | +    }) | 
|  | 97 | +} | 
0 commit comments