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