Auto merge of #138601 - RalfJung:wasm-abi-fcw, r=alexcrichton · rust-lang/rust@068609c (original) (raw)

1

1

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

`

2

2

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

`

3

3

`use rustc_abi::{BackendRepr, RegKind};

`

4

``

`-

use rustc_hir::CRATE_HIR_ID;

`

5

``

`-

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

`

6

``

`-

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

`

7

``

`-

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

`

``

4

`+

use rustc_hir::{CRATE_HIR_ID, HirId};

`

``

5

`+

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

`

``

6

`+

use rustc_middle::ty::layout::LayoutCx;

`

``

7

`+

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

`

``

8

`+

use rustc_session::lint::builtin::{ABI_UNSUPPORTED_VECTOR_TYPES, WASM_C_ABI};

`

8

9

`use rustc_span::def_id::DefId;

`

9

10

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

`

10

``

`-

use rustc_target::callconv::{Conv, FnAbi, PassMode};

`

``

11

`+

use rustc_target::callconv::{ArgAbi, Conv, FnAbi, PassMode};

`

``

12

`+

use rustc_target::spec::{HasWasmCAbiOpt, WasmCAbi};

`

11

13

``

12

14

`use crate::errors;

`

13

15

``

`@@ -26,13 +28,15 @@ fn uses_vector_registers(mode: &PassMode, repr: &BackendRepr) -> bool {

`

26

28

`/// for a certain function.

`

27

29

`` /// is_call indicates whether this is a call-site check or a definition-site check;

``

28

30

`/// this is only relevant for the wording in the emitted error.

`

29

``

`-

fn do_check_abi<'tcx>(

`

``

31

`+

fn do_check_simd_vector_abi<'tcx>(

`

30

32

`tcx: TyCtxt<'tcx>,

`

31

33

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

`

32

34

`def_id: DefId,

`

33

35

`is_call: bool,

`

34

``

`-

span: impl Fn() -> Span,

`

``

36

`+

loc: impl Fn() -> (Span, HirId),

`

35

37

`) {

`

``

38

`+

// We check this on all functions, including those using the "Rust" ABI.

`

``

39

`+

// For the "Rust" ABI it would be a bug if the lint ever triggered, but better safe than sorry.

`

36

40

`let feature_def = tcx.sess.target.features_for_correct_vector_abi();

`

37

41

`let codegen_attrs = tcx.codegen_fn_attrs(def_id);

`

38

42

`let have_feature = |feat: Symbol| {

`

`@@ -46,10 +50,10 @@ fn do_check_abi<'tcx>(

`

46

50

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

`

47

51

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

`

48

52

`None => {

`

49

``

`-

let span = span();

`

``

53

`+

let (span, hir_id) = loc();

`

50

54

` tcx.emit_node_span_lint(

`

51

55

`ABI_UNSUPPORTED_VECTOR_TYPES,

`

52

``

`-

CRATE_HIR_ID,

`

``

56

`+

hir_id,

`

53

57

` span,

`

54

58

` errors::AbiErrorUnsupportedVectorType {

`

55

59

` span,

`

`@@ -62,10 +66,10 @@ fn do_check_abi<'tcx>(

`

62

66

`};

`

63

67

`if !have_feature(Symbol::intern(feature)) {

`

64

68

`// Emit error.

`

65

``

`-

let span = span();

`

``

69

`+

let (span, hir_id) = loc();

`

66

70

` tcx.emit_node_span_lint(

`

67

71

`ABI_UNSUPPORTED_VECTOR_TYPES,

`

68

``

`-

CRATE_HIR_ID,

`

``

72

`+

hir_id,

`

69

73

` span,

`

70

74

` errors::AbiErrorDisabledVectorType {

`

71

75

` span,

`

`@@ -79,15 +83,71 @@ fn do_check_abi<'tcx>(

`

79

83

`}

`

80

84

`` // The vectorcall ABI is special in that it requires SSE2 no matter which types are being passed.

``

81

85

`if abi.conv == Conv::X86VectorCall && !have_feature(sym::sse2) {

`

``

86

`+

let (span, _hir_id) = loc();

`

82

87

` tcx.dcx().emit_err(errors::AbiRequiredTargetFeature {

`

83

``

`-

span: span(),

`

``

88

`+

span,

`

84

89

`required_feature: "sse2",

`

85

90

`abi: "vectorcall",

`

86

91

` is_call,

`

87

92

`});

`

88

93

`}

`

89

94

`}

`

90

95

``

``

96

`+

/// Determines whether the given argument is passed the same way on the old and new wasm ABIs.

`

``

97

`+

fn wasm_abi_safe<'tcx>(tcx: TyCtxt<'tcx>, arg: &ArgAbi<'tcx, Ty<'tcx>>) -> bool {

`

``

98

`+

if matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) {

`

``

99

`+

return true;

`

``

100

`+

}

`

``

101

+

``

102

`` +

// This matches unwrap_trivial_aggregate in the wasm ABI logic.

``

``

103

`+

if arg.layout.is_aggregate() {

`

``

104

`+

let cx = LayoutCx::new(tcx, TypingEnv::fully_monomorphized());

`

``

105

`+

if let Some(unit) = arg.layout.homogeneous_aggregate(&cx).ok().and_then(|ha| ha.unit()) {

`

``

106

`+

let size = arg.layout.size;

`

``

107

`` +

// Ensure there's just a single unit element in arg.

``

``

108

`+

if unit.size == size {

`

``

109

`+

return true;

`

``

110

`+

}

`

``

111

`+

}

`

``

112

`+

}

`

``

113

+

``

114

`+

false

`

``

115

`+

}

`

``

116

+

``

117

`` +

/// Warns against usage of extern "C" on wasm32-unknown-unknown that is affected by the

``

``

118

`+

/// ABI transition.

`

``

119

`+

fn do_check_wasm_abi<'tcx>(

`

``

120

`+

tcx: TyCtxt<'tcx>,

`

``

121

`+

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

`

``

122

`+

is_call: bool,

`

``

123

`+

loc: impl Fn() -> (Span, HirId),

`

``

124

`+

) {

`

``

125

`` +

// Only proceed for extern "C" fn on wasm32-unknown-unknown (same check as what adjust_for_foreign_abi uses to call compute_wasm_abi_info),

``

``

126

`` +

// and only proceed if wasm_c_abi_opt indicates we should emit the lint.

``

``

127

`+

if !(tcx.sess.target.arch == "wasm32"

`

``

128

`+

&& tcx.sess.target.os == "unknown"

`

``

129

`+

&& tcx.wasm_c_abi_opt() == WasmCAbi::Legacy { with_lint: true }

`

``

130

`+

&& abi.conv == Conv::C)

`

``

131

`+

{

`

``

132

`+

return;

`

``

133

`+

}

`

``

134

`+

// Warn against all types whose ABI will change. Return values are not affected by this change.

`

``

135

`+

for arg_abi in abi.args.iter() {

`

``

136

`+

if wasm_abi_safe(tcx, arg_abi) {

`

``

137

`+

continue;

`

``

138

`+

}

`

``

139

`+

let (span, hir_id) = loc();

`

``

140

`+

tcx.emit_node_span_lint(

`

``

141

`+

WASM_C_ABI,

`

``

142

`+

hir_id,

`

``

143

`+

span,

`

``

144

`+

errors::WasmCAbiTransition { ty: arg_abi.layout.ty, is_call },

`

``

145

`+

);

`

``

146

`+

// Let's only warn once per function.

`

``

147

`+

break;

`

``

148

`+

}

`

``

149

`+

}

`

``

150

+

91

151

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

`

92

152

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

`

93

153

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

`

`@@ -98,22 +158,24 @@ fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {

`

98

158

`// function.

`

99

159

`return;

`

100

160

`};

`

101

``

`-

do_check_abi(

`

102

``

`-

tcx,

`

103

``

`-

abi,

`

104

``

`-

instance.def_id(),

`

105

``

`-

/is_call/ false,

`

106

``

`-

|| tcx.def_span(instance.def_id()),

`

107

``

`-

)

`

``

161

`+

let loc = || {

`

``

162

`+

let def_id = instance.def_id();

`

``

163

`+

(

`

``

164

`+

tcx.def_span(def_id),

`

``

165

`+

def_id.as_local().map(|did| tcx.local_def_id_to_hir_id(did)).unwrap_or(CRATE_HIR_ID),

`

``

166

`+

)

`

``

167

`+

};

`

``

168

`+

do_check_simd_vector_abi(tcx, abi, instance.def_id(), /is_call/ false, loc);

`

``

169

`+

do_check_wasm_abi(tcx, abi, /is_call/ false, loc);

`

108

170

`}

`

109

171

``

110

172

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

`

111

173

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

`

112

174

`fn check_call_site_abi<'tcx>(

`

113

175

`tcx: TyCtxt<'tcx>,

`

114

176

`callee: Ty<'tcx>,

`

115

``

`-

span: Span,

`

116

177

`caller: InstanceKind<'tcx>,

`

``

178

`+

loc: impl Fn() -> (Span, HirId) + Copy,

`

117

179

`) {

`

118

180

`if callee.fn_sig(tcx).abi().is_rustic_abi() {

`

119

181

`// we directly handle the soundness of Rust ABIs

`

`@@ -141,7 +203,8 @@ fn check_call_site_abi<'tcx>(

`

141

203

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

`

142

204

`return;

`

143

205

`};

`

144

``

`-

do_check_abi(tcx, callee_abi, caller.def_id(), /is_call/ true, || span);

`

``

206

`+

do_check_simd_vector_abi(tcx, callee_abi, caller.def_id(), /is_call/ true, loc);

`

``

207

`+

do_check_wasm_abi(tcx, callee_abi, /is_call/ true, loc);

`

145

208

`}

`

146

209

``

147

210

`fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) {

`

`@@ -157,7 +220,19 @@ fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &m

`

157

220

` ty::TypingEnv::fully_monomorphized(),

`

158

221

` ty::EarlyBinder::bind(callee_ty),

`

159

222

`);

`

160

``

`-

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

`

``

223

`+

check_call_site_abi(tcx, callee_ty, body.source.instance, || {

`

``

224

`+

let loc = Location {

`

``

225

`+

block: bb,

`

``

226

`+

statement_index: body.basic_blocks[bb].statements.len(),

`

``

227

`+

};

`

``

228

`+

(

`

``

229

`+

*fn_span,

`

``

230

`+

body.source_info(loc)

`

``

231

`+

.scope

`

``

232

`+

.lint_root(&body.source_scopes)

`

``

233

`+

.unwrap_or(CRATE_HIR_ID),

`

``

234

`+

)

`

``

235

`+

});

`

161

236

`}

`

162

237

` _ => {}

`

163

238

`}

`