]> git.proxmox.com Git - rustc.git/blame_incremental - compiler/rustc_hir_analysis/src/check/intrinsicck.rs
bump version to 1.80.1+dfsg1-1~bpo12+pve1
[rustc.git] / compiler / rustc_hir_analysis / src / check / intrinsicck.rs
... / ...
CommitLineData
1use rustc_ast::InlineAsmTemplatePiece;
2use rustc_data_structures::fx::FxIndexSet;
3use rustc_hir as hir;
4use rustc_middle::bug;
5use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy};
6use rustc_session::lint;
7use rustc_span::def_id::LocalDefId;
8use rustc_span::Symbol;
9use rustc_target::abi::FieldIdx;
10use rustc_target::asm::{
11 InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo,
12};
13
14pub struct InlineAsmCtxt<'a, 'tcx> {
15 tcx: TyCtxt<'tcx>,
16 param_env: ty::ParamEnv<'tcx>,
17 get_operand_ty: Box<dyn Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a>,
18}
19
20impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
21 pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self {
22 InlineAsmCtxt {
23 tcx,
24 param_env: ty::ParamEnv::empty(),
25 get_operand_ty: Box::new(|e| bug!("asm operand in global asm: {e:?}")),
26 }
27 }
28
29 pub fn new_in_fn(
30 tcx: TyCtxt<'tcx>,
31 param_env: ty::ParamEnv<'tcx>,
32 get_operand_ty: impl Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a,
33 ) -> Self {
34 InlineAsmCtxt { tcx, param_env, get_operand_ty: Box::new(get_operand_ty) }
35 }
36
37 // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
38 fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
39 // Type still may have region variables, but `Sized` does not depend
40 // on those, so just erase them before querying.
41 if ty.is_sized(self.tcx, self.param_env) {
42 return true;
43 }
44 if let ty::Foreign(..) = ty.kind() {
45 return true;
46 }
47 false
48 }
49
50 fn get_asm_ty(&self, ty: Ty<'tcx>) -> Option<InlineAsmType> {
51 let asm_ty_isize = match self.tcx.sess.target.pointer_width {
52 16 => InlineAsmType::I16,
53 32 => InlineAsmType::I32,
54 64 => InlineAsmType::I64,
55 width => bug!("unsupported pointer width: {width}"),
56 };
57
58 match *ty.kind() {
59 ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8),
60 ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16),
61 ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32),
62 ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64),
63 ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128),
64 ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize),
65 ty::Float(FloatTy::F32) => Some(InlineAsmType::F32),
66 ty::Float(FloatTy::F64) => Some(InlineAsmType::F64),
67 ty::FnPtr(_) => Some(asm_ty_isize),
68 ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize),
69 ty::Adt(adt, args) if adt.repr().simd() => {
70 let fields = &adt.non_enum_variant().fields;
71 let elem_ty = fields[FieldIdx::ZERO].ty(self.tcx, args);
72
73 let (size, ty) = match elem_ty.kind() {
74 ty::Array(ty, len) => {
75 if let Some(len) =
76 len.try_eval_target_usize(self.tcx, self.tcx.param_env(adt.did()))
77 {
78 (len, *ty)
79 } else {
80 return None;
81 }
82 }
83 _ => (fields.len() as u64, elem_ty),
84 };
85
86 match ty.kind() {
87 ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::VecI8(size)),
88 ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => {
89 Some(InlineAsmType::VecI16(size))
90 }
91 ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => {
92 Some(InlineAsmType::VecI32(size))
93 }
94 ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => {
95 Some(InlineAsmType::VecI64(size))
96 }
97 ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => {
98 Some(InlineAsmType::VecI128(size))
99 }
100 ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => {
101 Some(match self.tcx.sess.target.pointer_width {
102 16 => InlineAsmType::VecI16(size),
103 32 => InlineAsmType::VecI32(size),
104 64 => InlineAsmType::VecI64(size),
105 width => bug!("unsupported pointer width: {width}"),
106 })
107 }
108 ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(size)),
109 ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(size)),
110 _ => None,
111 }
112 }
113 ty::Infer(_) => bug!("unexpected infer ty in asm operand"),
114 _ => None,
115 }
116 }
117
118 fn check_asm_operand_type(
119 &self,
120 idx: usize,
121 reg: InlineAsmRegOrRegClass,
122 expr: &'tcx hir::Expr<'tcx>,
123 template: &[InlineAsmTemplatePiece],
124 is_input: bool,
125 tied_input: Option<(&'tcx hir::Expr<'tcx>, Option<InlineAsmType>)>,
126 target_features: &FxIndexSet<Symbol>,
127 ) -> Option<InlineAsmType> {
128 let ty = (self.get_operand_ty)(expr);
129 if ty.has_non_region_infer() {
130 bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty);
131 }
132
133 let asm_ty = match *ty.kind() {
134 // `!` is allowed for input but not for output (issue #87802)
135 ty::Never if is_input => return None,
136 _ if ty.references_error() => return None,
137 ty::Adt(adt, args) if Some(adt.did()) == self.tcx.lang_items().maybe_uninit() => {
138 let fields = &adt.non_enum_variant().fields;
139 let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx, args);
140 // FIXME: Are we just trying to map to the `T` in `MaybeUninit<T>`?
141 // If so, just get it from the args.
142 let ty::Adt(ty, args) = ty.kind() else {
143 unreachable!("expected first field of `MaybeUninit` to be an ADT")
144 };
145 assert!(
146 ty.is_manually_drop(),
147 "expected first field of `MaybeUninit` to be `ManuallyDrop`"
148 );
149 let fields = &ty.non_enum_variant().fields;
150 let ty = fields[FieldIdx::ZERO].ty(self.tcx, args);
151 self.get_asm_ty(ty)
152 }
153 _ => self.get_asm_ty(ty),
154 };
155 let Some(asm_ty) = asm_ty else {
156 let msg = format!("cannot use value of type `{ty}` for inline assembly");
157 self.tcx
158 .dcx()
159 .struct_span_err(expr.span, msg)
160 .with_note(
161 "only integers, floats, SIMD vectors, pointers and function pointers \
162 can be used as arguments for inline assembly",
163 )
164 .emit();
165 return None;
166 };
167
168 // Check that the type implements Copy. The only case where this can
169 // possibly fail is for SIMD types which don't #[derive(Copy)].
170 if !ty.is_copy_modulo_regions(self.tcx, self.param_env) {
171 let msg = "arguments for inline assembly must be copyable";
172 self.tcx
173 .dcx()
174 .struct_span_err(expr.span, msg)
175 .with_note(format!("`{ty}` does not implement the Copy trait"))
176 .emit();
177 }
178
179 // Ideally we wouldn't need to do this, but LLVM's register allocator
180 // really doesn't like it when tied operands have different types.
181 //
182 // This is purely an LLVM limitation, but we have to live with it since
183 // there is no way to hide this with implicit conversions.
184 //
185 // For the purposes of this check we only look at the `InlineAsmType`,
186 // which means that pointers and integers are treated as identical (modulo
187 // size).
188 if let Some((in_expr, Some(in_asm_ty))) = tied_input {
189 if in_asm_ty != asm_ty {
190 let msg = "incompatible types for asm inout argument";
191 let in_expr_ty = (self.get_operand_ty)(in_expr);
192 self.tcx
193 .dcx()
194 .struct_span_err(vec![in_expr.span, expr.span], msg)
195 .with_span_label(in_expr.span, format!("type `{in_expr_ty}`"))
196 .with_span_label(expr.span, format!("type `{ty}`"))
197 .with_note(
198 "asm inout arguments must have the same type, \
199 unless they are both pointers or integers of the same size",
200 )
201 .emit();
202 }
203
204 // All of the later checks have already been done on the input, so
205 // let's not emit errors and warnings twice.
206 return Some(asm_ty);
207 }
208
209 // Check the type against the list of types supported by the selected
210 // register class.
211 let asm_arch = self.tcx.sess.asm_arch.unwrap();
212 let reg_class = reg.reg_class();
213 let supported_tys = reg_class.supported_types(asm_arch);
214 let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else {
215 let msg = format!("type `{ty}` cannot be used with this register class");
216 let mut err = self.tcx.dcx().struct_span_err(expr.span, msg);
217 let supported_tys: Vec<_> = supported_tys.iter().map(|(t, _)| t.to_string()).collect();
218 err.note(format!(
219 "register class `{}` supports these types: {}",
220 reg_class.name(),
221 supported_tys.join(", "),
222 ));
223 if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) {
224 err.help(format!("consider using the `{}` register class instead", suggest.name()));
225 }
226 err.emit();
227 return Some(asm_ty);
228 };
229
230 // Check whether the selected type requires a target feature. Note that
231 // this is different from the feature check we did earlier. While the
232 // previous check checked that this register class is usable at all
233 // with the currently enabled features, some types may only be usable
234 // with a register class when a certain feature is enabled. We check
235 // this here since it depends on the results of typeck.
236 //
237 // Also note that this check isn't run when the operand type is never
238 // (!). In that case we still need the earlier check to verify that the
239 // register class is usable at all.
240 if let Some(feature) = feature {
241 if !target_features.contains(feature) {
242 let msg = format!("`{feature}` target feature is not enabled");
243 self.tcx
244 .dcx()
245 .struct_span_err(expr.span, msg)
246 .with_note(format!(
247 "this is required to use type `{}` with register class `{}`",
248 ty,
249 reg_class.name(),
250 ))
251 .emit();
252 return Some(asm_ty);
253 }
254 }
255
256 // Check whether a modifier is suggested for using this type.
257 if let Some(ModifierInfo {
258 modifier: suggested_modifier,
259 result: suggested_result,
260 size: suggested_size,
261 }) = reg_class.suggest_modifier(asm_arch, asm_ty)
262 {
263 // Search for any use of this operand without a modifier and emit
264 // the suggestion for them.
265 let mut spans = vec![];
266 for piece in template {
267 if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece
268 {
269 if operand_idx == idx && modifier.is_none() {
270 spans.push(span);
271 }
272 }
273 }
274 if !spans.is_empty() {
275 let ModifierInfo {
276 modifier: default_modifier,
277 result: default_result,
278 size: default_size,
279 } = reg_class.default_modifier(asm_arch).unwrap();
280 self.tcx.node_span_lint(
281 lint::builtin::ASM_SUB_REGISTER,
282 expr.hir_id,
283 spans,
284 |lint| {
285 lint.primary_message("formatting may not be suitable for sub-register argument");
286 lint.span_label(expr.span, "for this argument");
287 lint.help(format!(
288 "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)",
289 ));
290 lint.help(format!(
291 "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)",
292 ));
293 },
294 );
295 }
296 }
297
298 Some(asm_ty)
299 }
300
301 pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>, enclosing_id: LocalDefId) {
302 let target_features = self.tcx.asm_target_features(enclosing_id.to_def_id());
303 let Some(asm_arch) = self.tcx.sess.asm_arch else {
304 self.tcx.dcx().delayed_bug("target architecture does not support asm");
305 return;
306 };
307 for (idx, (op, op_sp)) in asm.operands.iter().enumerate() {
308 // Validate register classes against currently enabled target
309 // features. We check that at least one type is available for
310 // the enabled features.
311 //
312 // We ignore target feature requirements for clobbers: if the
313 // feature is disabled then the compiler doesn't care what we
314 // do with the registers.
315 //
316 // Note that this is only possible for explicit register
317 // operands, which cannot be used in the asm string.
318 if let Some(reg) = op.reg() {
319 // Some explicit registers cannot be used depending on the
320 // target. Reject those here.
321 if let InlineAsmRegOrRegClass::Reg(reg) = reg {
322 if let InlineAsmReg::Err = reg {
323 // `validate` will panic on `Err`, as an error must
324 // already have been reported.
325 continue;
326 }
327 if let Err(msg) = reg.validate(
328 asm_arch,
329 self.tcx.sess.relocation_model(),
330 target_features,
331 &self.tcx.sess.target,
332 op.is_clobber(),
333 ) {
334 let msg = format!("cannot use register `{}`: {}", reg.name(), msg);
335 self.tcx.dcx().span_err(*op_sp, msg);
336 continue;
337 }
338 }
339
340 if !op.is_clobber() {
341 let mut missing_required_features = vec![];
342 let reg_class = reg.reg_class();
343 if let InlineAsmRegClass::Err = reg_class {
344 continue;
345 }
346 for &(_, feature) in reg_class.supported_types(asm_arch) {
347 match feature {
348 Some(feature) => {
349 if target_features.contains(&feature) {
350 missing_required_features.clear();
351 break;
352 } else {
353 missing_required_features.push(feature);
354 }
355 }
356 None => {
357 missing_required_features.clear();
358 break;
359 }
360 }
361 }
362
363 // We are sorting primitive strs here and can use unstable sort here
364 missing_required_features.sort_unstable();
365 missing_required_features.dedup();
366 match &missing_required_features[..] {
367 [] => {}
368 [feature] => {
369 let msg = format!(
370 "register class `{}` requires the `{}` target feature",
371 reg_class.name(),
372 feature
373 );
374 self.tcx.dcx().span_err(*op_sp, msg);
375 // register isn't enabled, don't do more checks
376 continue;
377 }
378 features => {
379 let msg = format!(
380 "register class `{}` requires at least one of the following target features: {}",
381 reg_class.name(),
382 features
383 .iter()
384 .map(|f| f.as_str())
385 .intersperse(", ")
386 .collect::<String>(),
387 );
388 self.tcx.dcx().span_err(*op_sp, msg);
389 // register isn't enabled, don't do more checks
390 continue;
391 }
392 }
393 }
394 }
395
396 match *op {
397 hir::InlineAsmOperand::In { reg, expr } => {
398 self.check_asm_operand_type(
399 idx,
400 reg,
401 expr,
402 asm.template,
403 true,
404 None,
405 target_features,
406 );
407 }
408 hir::InlineAsmOperand::Out { reg, late: _, expr } => {
409 if let Some(expr) = expr {
410 self.check_asm_operand_type(
411 idx,
412 reg,
413 expr,
414 asm.template,
415 false,
416 None,
417 target_features,
418 );
419 }
420 }
421 hir::InlineAsmOperand::InOut { reg, late: _, expr } => {
422 self.check_asm_operand_type(
423 idx,
424 reg,
425 expr,
426 asm.template,
427 false,
428 None,
429 target_features,
430 );
431 }
432 hir::InlineAsmOperand::SplitInOut { reg, late: _, in_expr, out_expr } => {
433 let in_ty = self.check_asm_operand_type(
434 idx,
435 reg,
436 in_expr,
437 asm.template,
438 true,
439 None,
440 target_features,
441 );
442 if let Some(out_expr) = out_expr {
443 self.check_asm_operand_type(
444 idx,
445 reg,
446 out_expr,
447 asm.template,
448 false,
449 Some((in_expr, in_ty)),
450 target_features,
451 );
452 }
453 }
454 // No special checking is needed for these:
455 // - Typeck has checked that Const operands are integers.
456 // - AST lowering guarantees that SymStatic points to a static.
457 hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymStatic { .. } => {}
458 // Check that sym actually points to a function. Later passes
459 // depend on this.
460 hir::InlineAsmOperand::SymFn { anon_const } => {
461 let ty = self.tcx.type_of(anon_const.def_id).instantiate_identity();
462 match ty.kind() {
463 ty::Never | ty::Error(_) => {}
464 ty::FnDef(..) => {}
465 _ => {
466 self.tcx
467 .dcx()
468 .struct_span_err(*op_sp, "invalid `sym` operand")
469 .with_span_label(
470 self.tcx.def_span(anon_const.def_id),
471 format!("is {} `{}`", ty.kind().article(), ty),
472 )
473 .with_help(
474 "`sym` operands must refer to either a function or a static",
475 )
476 .emit();
477 }
478 };
479 }
480 // No special checking is needed for labels.
481 hir::InlineAsmOperand::Label { .. } => {}
482 }
483 }
484 }
485}