Auto merge of #132173 - veluca93:abi_checks, r=RalfJung,compiler-errors · rust-lang/rust@7660aed (original) (raw)

``

1

`+

//! This module ensures that if a function's ABI requires a particular target feature,

`

``

2

`+

//! that target feature is enabled both on the callee and all callers.

`

``

3

`+

use rustc_hir::CRATE_HIR_ID;

`

``

4

`+

use rustc_middle::mir::visit::Visitor as MirVisitor;

`

``

5

`+

use rustc_middle::mir::{self, Location, traversal};

`

``

6

`+

use rustc_middle::query::Providers;

`

``

7

`+

use rustc_middle::ty::inherent::*;

`

``

8

`+

use rustc_middle::ty::{self, Instance, InstanceKind, ParamEnv, Ty, TyCtxt};

`

``

9

`+

use rustc_session::lint::builtin::ABI_UNSUPPORTED_VECTOR_TYPES;

`

``

10

`+

use rustc_span::def_id::DefId;

`

``

11

`+

use rustc_span::{DUMMY_SP, Span, Symbol};

`

``

12

`+

use rustc_target::abi:🤙:{FnAbi, PassMode};

`

``

13

`+

use rustc_target::abi::{BackendRepr, RegKind};

`

``

14

+

``

15

`+

use crate::errors::{AbiErrorDisabledVectorTypeCall, AbiErrorDisabledVectorTypeDef};

`

``

16

+

``

17

`+

fn uses_vector_registers(mode: &PassMode, repr: &BackendRepr) -> bool {

`

``

18

`+

match mode {

`

``

19

`+

PassMode::Ignore | PassMode::Indirect { .. } => false,

`

``

20

`+

PassMode::Cast { pad_i32: _, cast } => {

`

``

21

`+

cast.prefix.iter().any(|r| r.is_some_and(|x| x.kind == RegKind::Vector))

`

``

22

`+

|| cast.rest.unit.kind == RegKind::Vector

`

``

23

`+

}

`

``

24

`+

PassMode::Direct(..) | PassMode::Pair(..) => matches!(repr, BackendRepr::Vector { .. }),

`

``

25

`+

}

`

``

26

`+

}

`

``

27

+

``

28

`+

fn do_check_abi<'tcx>(

`

``

29

`+

tcx: TyCtxt<'tcx>,

`

``

30

`+

abi: &FnAbi<'tcx, Ty<'tcx>>,

`

``

31

`+

target_feature_def: DefId,

`

``

32

`+

mut emit_err: impl FnMut(&'static str),

`

``

33

`+

) {

`

``

34

`+

let Some(feature_def) = tcx.sess.target.features_for_correct_vector_abi() else {

`

``

35

`+

return;

`

``

36

`+

};

`

``

37

`+

let codegen_attrs = tcx.codegen_fn_attrs(target_feature_def);

`

``

38

`+

for arg_abi in abi.args.iter().chain(std::iter::once(&abi.ret)) {

`

``

39

`+

let size = arg_abi.layout.size;

`

``

40

`+

if uses_vector_registers(&arg_abi.mode, &arg_abi.layout.backend_repr) {

`

``

41

`+

// Find the first feature that provides at least this vector size.

`

``

42

`+

let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) {

`

``

43

`+

Some((_, feature)) => feature,

`

``

44

`+

None => {

`

``

45

`+

emit_err("");

`

``

46

`+

continue;

`

``

47

`+

}

`

``

48

`+

};

`

``

49

`+

let feature_sym = Symbol::intern(feature);

`

``

50

`+

if !tcx.sess.unstable_target_features.contains(&feature_sym)

`

``

51

`+

&& !codegen_attrs.target_features.iter().any(|x| x.name == feature_sym)

`

``

52

`+

{

`

``

53

`+

emit_err(feature);

`

``

54

`+

}

`

``

55

`+

}

`

``

56

`+

}

`

``

57

`+

}

`

``

58

+

``

59

`+

/// Checks that the ABI of a given instance of a function does not contain vector-passed arguments

`

``

60

`+

/// or return values for which the corresponding target feature is not enabled.

`

``

61

`+

fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {

`

``

62

`+

let param_env = ParamEnv::reveal_all();

`

``

63

`+

let Ok(abi) = tcx.fn_abi_of_instance(param_env.and((instance, ty::List::empty()))) else {

`

``

64

`+

// An error will be reported during codegen if we cannot determine the ABI of this

`

``

65

`+

// function.

`

``

66

`+

return;

`

``

67

`+

};

`

``

68

`+

do_check_abi(tcx, abi, instance.def_id(), |required_feature| {

`

``

69

`+

let span = tcx.def_span(instance.def_id());

`

``

70

`+

tcx.emit_node_span_lint(

`

``

71

`+

ABI_UNSUPPORTED_VECTOR_TYPES,

`

``

72

`+

CRATE_HIR_ID,

`

``

73

`+

span,

`

``

74

`+

AbiErrorDisabledVectorTypeDef { span, required_feature },

`

``

75

`+

);

`

``

76

`+

})

`

``

77

`+

}

`

``

78

+

``

79

`+

/// Checks that a call expression does not try to pass a vector-passed argument which requires a

`

``

80

`+

/// target feature that the caller does not have, as doing so causes UB because of ABI mismatch.

`

``

81

`+

fn check_call_site_abi<'tcx>(

`

``

82

`+

tcx: TyCtxt<'tcx>,

`

``

83

`+

callee: Ty<'tcx>,

`

``

84

`+

span: Span,

`

``

85

`+

caller: InstanceKind<'tcx>,

`

``

86

`+

) {

`

``

87

`+

if callee.fn_sig(tcx).abi().is_rust() {

`

``

88

`+

// "Rust" ABI never passes arguments in vector registers.

`

``

89

`+

return;

`

``

90

`+

}

`

``

91

`+

let param_env = ParamEnv::reveal_all();

`

``

92

`+

let callee_abi = match *callee.kind() {

`

``

93

`+

ty::FnPtr(..) => {

`

``

94

`+

tcx.fn_abi_of_fn_ptr(param_env.and((callee.fn_sig(tcx), ty::List::empty())))

`

``

95

`+

}

`

``

96

`+

ty::FnDef(def_id, args) => {

`

``

97

`+

// Intrinsics are handled separately by the compiler.

`

``

98

`+

if tcx.intrinsic(def_id).is_some() {

`

``

99

`+

return;

`

``

100

`+

}

`

``

101

`+

let instance = ty::Instance::expect_resolve(tcx, param_env, def_id, args, DUMMY_SP);

`

``

102

`+

tcx.fn_abi_of_instance(param_env.and((instance, ty::List::empty())))

`

``

103

`+

}

`

``

104

`+

_ => {

`

``

105

`+

panic!("Invalid function call");

`

``

106

`+

}

`

``

107

`+

};

`

``

108

+

``

109

`+

let Ok(callee_abi) = callee_abi else {

`

``

110

`+

// ABI failed to compute; this will not get through codegen.

`

``

111

`+

return;

`

``

112

`+

};

`

``

113

`+

do_check_abi(tcx, callee_abi, caller.def_id(), |required_feature| {

`

``

114

`+

tcx.emit_node_span_lint(

`

``

115

`+

ABI_UNSUPPORTED_VECTOR_TYPES,

`

``

116

`+

CRATE_HIR_ID,

`

``

117

`+

span,

`

``

118

`+

AbiErrorDisabledVectorTypeCall { span, required_feature },

`

``

119

`+

);

`

``

120

`+

});

`

``

121

`+

}

`

``

122

+

``

123

`+

struct MirCallesAbiCheck<'a, 'tcx> {

`

``

124

`+

tcx: TyCtxt<'tcx>,

`

``

125

`+

body: &'a mir::Body<'tcx>,

`

``

126

`+

instance: Instance<'tcx>,

`

``

127

`+

}

`

``

128

+

``

129

`+

impl<'a, 'tcx> MirVisitor<'tcx> for MirCallesAbiCheck<'a, 'tcx> {

`

``

130

`+

fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, _: Location) {

`

``

131

`+

match terminator.kind {

`

``

132

`+

mir::TerminatorKind::Call { ref func, ref fn_span, .. }

`

``

133

`+

| mir::TerminatorKind::TailCall { ref func, ref fn_span, .. } => {

`

``

134

`+

let callee_ty = func.ty(self.body, self.tcx);

`

``

135

`+

let callee_ty = self.instance.instantiate_mir_and_normalize_erasing_regions(

`

``

136

`+

self.tcx,

`

``

137

`+

ty::ParamEnv::reveal_all(),

`

``

138

`+

ty::EarlyBinder::bind(callee_ty),

`

``

139

`+

);

`

``

140

`+

check_call_site_abi(self.tcx, callee_ty, *fn_span, self.body.source.instance);

`

``

141

`+

}

`

``

142

`+

_ => {}

`

``

143

`+

}

`

``

144

`+

}

`

``

145

`+

}

`

``

146

+

``

147

`+

fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {

`

``

148

`+

let body = tcx.instance_mir(instance.def);

`

``

149

`+

let mut visitor = MirCallesAbiCheck { tcx, body, instance };

`

``

150

`+

for (bb, data) in traversal::mono_reachable(body, tcx, instance) {

`

``

151

`+

visitor.visit_basic_block_data(bb, data)

`

``

152

`+

}

`

``

153

`+

}

`

``

154

+

``

155

`+

fn check_feature_dependent_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {

`

``

156

`+

check_instance_abi(tcx, instance);

`

``

157

`+

check_callees_abi(tcx, instance);

`

``

158

`+

}

`

``

159

+

``

160

`+

pub(super) fn provide(providers: &mut Providers) {

`

``

161

`+

*providers = Providers { check_feature_dependent_abi, ..*providers }

`

``

162

`+

}

`