1use std::assert_matches::debug_assert_matches;
2
3use rustc_abi::FieldIdx;
4use rustc_ast::InlineAsmTemplatePiece;
5use rustc_data_structures::fx::FxIndexSet;
6use rustc_hir::def_id::DefId;
7use rustc_hir::{self as hir, LangItem};
8use rustc_middle::bug;
9use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy};
10use rustc_session::lint;
11use rustc_span::def_id::LocalDefId;
12use rustc_span::{Symbol, sym};
13use rustc_target::asm::{
14 InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo,
15};
16
17use crate::errors::RegisterTypeUnstable;
18
19pub struct InlineAsmCtxt<'a, 'tcx> {
20 tcx: TyCtxt<'tcx>,
21 typing_env: ty::TypingEnv<'tcx>,
22 get_operand_ty: Box<dyn Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a>,
23}
24
25enum NonAsmTypeReason<'tcx> {
26 UnevaluatedSIMDArrayLength(DefId, ty::Const<'tcx>),
27 Invalid(Ty<'tcx>),
28 InvalidElement(DefId, Ty<'tcx>),
29}
30
31impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
32 pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self {
33 InlineAsmCtxt {
34 tcx,
35 typing_env: ty::TypingEnv {
36 typing_mode: ty::TypingMode::non_body_analysis(),
37 param_env: ty::ParamEnv::empty(),
38 },
39 get_operand_ty: Box::new(|e| bug!("asm operand in global asm: {e:?}")),
40 }
41 }
42
43 pub fn new_in_fn(
46 tcx: TyCtxt<'tcx>,
47 typing_env: ty::TypingEnv<'tcx>,
48 get_operand_ty: impl Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a,
49 ) -> Self {
50 InlineAsmCtxt { tcx, typing_env, get_operand_ty: Box::new(get_operand_ty) }
51 }
52
53 fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
55 if ty.is_sized(self.tcx, self.typing_env) {
58 return true;
59 }
60 if let ty::Foreign(..) = ty.kind() {
61 return true;
62 }
63 false
64 }
65
66 fn get_asm_ty(&self, ty: Ty<'tcx>) -> Result<InlineAsmType, NonAsmTypeReason<'tcx>> {
67 let asm_ty_isize = match self.tcx.sess.target.pointer_width {
68 16 => InlineAsmType::I16,
69 32 => InlineAsmType::I32,
70 64 => InlineAsmType::I64,
71 width => bug!("unsupported pointer width: {width}"),
72 };
73
74 match *ty.kind() {
75 ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::I8),
76 ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::I16),
77 ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::I32),
78 ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::I64),
79 ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Ok(InlineAsmType::I128),
80 ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Ok(asm_ty_isize),
81 ty::Float(FloatTy::F16) => Ok(InlineAsmType::F16),
82 ty::Float(FloatTy::F32) => Ok(InlineAsmType::F32),
83 ty::Float(FloatTy::F64) => Ok(InlineAsmType::F64),
84 ty::Float(FloatTy::F128) => Ok(InlineAsmType::F128),
85 ty::FnPtr(..) => Ok(asm_ty_isize),
86 ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Ok(asm_ty_isize),
87 ty::Adt(adt, args) if adt.repr().simd() => {
88 let fields = &adt.non_enum_variant().fields;
89 let field = &fields[FieldIdx::ZERO];
90 let elem_ty = field.ty(self.tcx, args);
91
92 let (size, ty) = match elem_ty.kind() {
93 ty::Array(ty, len) => {
94 let len = self.tcx.normalize_erasing_regions(self.typing_env, *len);
95 if let Some(len) = len.try_to_target_usize(self.tcx) {
96 (len, *ty)
97 } else {
98 return Err(NonAsmTypeReason::UnevaluatedSIMDArrayLength(
99 field.did, len,
100 ));
101 }
102 }
103 _ => (fields.len() as u64, elem_ty),
104 };
105
106 match ty.kind() {
107 ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::VecI8(size)),
108 ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::VecI16(size)),
109 ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::VecI32(size)),
110 ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::VecI64(size)),
111 ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => {
112 Ok(InlineAsmType::VecI128(size))
113 }
114 ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => {
115 Ok(match self.tcx.sess.target.pointer_width {
116 16 => InlineAsmType::VecI16(size),
117 32 => InlineAsmType::VecI32(size),
118 64 => InlineAsmType::VecI64(size),
119 width => bug!("unsupported pointer width: {width}"),
120 })
121 }
122 ty::Float(FloatTy::F16) => Ok(InlineAsmType::VecF16(size)),
123 ty::Float(FloatTy::F32) => Ok(InlineAsmType::VecF32(size)),
124 ty::Float(FloatTy::F64) => Ok(InlineAsmType::VecF64(size)),
125 ty::Float(FloatTy::F128) => Ok(InlineAsmType::VecF128(size)),
126 _ => Err(NonAsmTypeReason::InvalidElement(field.did, ty)),
127 }
128 }
129 ty::Infer(_) => bug!("unexpected infer ty in asm operand"),
130 _ => Err(NonAsmTypeReason::Invalid(ty)),
131 }
132 }
133
134 fn check_asm_operand_type(
135 &self,
136 idx: usize,
137 reg: InlineAsmRegOrRegClass,
138 expr: &'tcx hir::Expr<'tcx>,
139 template: &[InlineAsmTemplatePiece],
140 is_input: bool,
141 tied_input: Option<(&'tcx hir::Expr<'tcx>, Option<InlineAsmType>)>,
142 target_features: &FxIndexSet<Symbol>,
143 ) -> Option<InlineAsmType> {
144 let ty = (self.get_operand_ty)(expr);
145 if ty.has_non_region_infer() {
146 bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty);
147 }
148
149 let asm_ty = match *ty.kind() {
150 ty::Never if is_input => return None,
152 _ if ty.references_error() => return None,
153 ty::Adt(adt, args) if self.tcx.is_lang_item(adt.did(), LangItem::MaybeUninit) => {
154 let fields = &adt.non_enum_variant().fields;
155 let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx, args);
156 let ty::Adt(ty, args) = ty.kind() else {
159 unreachable!("expected first field of `MaybeUninit` to be an ADT")
160 };
161 assert!(
162 ty.is_manually_drop(),
163 "expected first field of `MaybeUninit` to be `ManuallyDrop`"
164 );
165 let fields = &ty.non_enum_variant().fields;
166 let ty = fields[FieldIdx::ZERO].ty(self.tcx, args);
167 self.get_asm_ty(ty)
168 }
169 _ => self.get_asm_ty(ty),
170 };
171 let asm_ty = match asm_ty {
172 Ok(asm_ty) => asm_ty,
173 Err(reason) => {
174 match reason {
175 NonAsmTypeReason::UnevaluatedSIMDArrayLength(did, len) => {
176 let msg = format!("cannot evaluate SIMD vector length `{len}`");
177 self.tcx
178 .dcx()
179 .struct_span_err(self.tcx.def_span(did), msg)
180 .with_span_note(
181 expr.span,
182 "SIMD vector length needs to be known statically for use in `asm!`",
183 )
184 .emit();
185 }
186 NonAsmTypeReason::Invalid(ty) => {
187 let msg = format!("cannot use value of type `{ty}` for inline assembly");
188 self.tcx.dcx().struct_span_err(expr.span, msg).with_note(
189 "only integers, floats, SIMD vectors, pointers and function pointers \
190 can be used as arguments for inline assembly",
191 ).emit();
192 }
193 NonAsmTypeReason::InvalidElement(did, ty) => {
194 let msg = format!(
195 "cannot use SIMD vector with element type `{ty}` for inline assembly"
196 );
197 self.tcx.dcx()
198 .struct_span_err(self.tcx.def_span(did), msg).with_span_note(
199 expr.span,
200 "only integers, floats, SIMD vectors, pointers and function pointers \
201 can be used as arguments for inline assembly",
202 ).emit();
203 }
204 }
205 return None;
206 }
207 };
208
209 if !self.tcx.type_is_copy_modulo_regions(self.typing_env, ty) {
212 let msg = "arguments for inline assembly must be copyable";
213 self.tcx
214 .dcx()
215 .struct_span_err(expr.span, msg)
216 .with_note(format!("`{ty}` does not implement the Copy trait"))
217 .emit();
218 }
219
220 if let Some((in_expr, Some(in_asm_ty))) = tied_input {
230 if in_asm_ty != asm_ty {
231 let msg = "incompatible types for asm inout argument";
232 let in_expr_ty = (self.get_operand_ty)(in_expr);
233 self.tcx
234 .dcx()
235 .struct_span_err(vec![in_expr.span, expr.span], msg)
236 .with_span_label(in_expr.span, format!("type `{in_expr_ty}`"))
237 .with_span_label(expr.span, format!("type `{ty}`"))
238 .with_note(
239 "asm inout arguments must have the same type, \
240 unless they are both pointers or integers of the same size",
241 )
242 .emit();
243 }
244
245 return Some(asm_ty);
248 }
249
250 let asm_arch = self.tcx.sess.asm_arch.unwrap();
253 let allow_experimental_reg = self.tcx.features().asm_experimental_reg();
254 let reg_class = reg.reg_class();
255 let supported_tys = reg_class.supported_types(asm_arch, allow_experimental_reg);
256 let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else {
257 let mut err = if !allow_experimental_reg
258 && reg_class.supported_types(asm_arch, true).iter().any(|&(t, _)| t == asm_ty)
259 {
260 self.tcx.sess.create_feature_err(
261 RegisterTypeUnstable { span: expr.span, ty },
262 sym::asm_experimental_reg,
263 )
264 } else {
265 let msg = format!("type `{ty}` cannot be used with this register class");
266 let mut err = self.tcx.dcx().struct_span_err(expr.span, msg);
267 let supported_tys: Vec<_> =
268 supported_tys.iter().map(|(t, _)| t.to_string()).collect();
269 err.note(format!(
270 "register class `{}` supports these types: {}",
271 reg_class.name(),
272 supported_tys.join(", "),
273 ));
274 err
275 };
276 if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) {
277 err.help(format!("consider using the `{}` register class instead", suggest.name()));
278 }
279 err.emit();
280 return Some(asm_ty);
281 };
282
283 if let Some(feature) = feature {
294 if !target_features.contains(feature) {
295 let msg = format!("`{feature}` target feature is not enabled");
296 self.tcx
297 .dcx()
298 .struct_span_err(expr.span, msg)
299 .with_note(format!(
300 "this is required to use type `{}` with register class `{}`",
301 ty,
302 reg_class.name(),
303 ))
304 .emit();
305 return Some(asm_ty);
306 }
307 }
308
309 if let Some(ModifierInfo {
311 modifier: suggested_modifier,
312 result: suggested_result,
313 size: suggested_size,
314 }) = reg_class.suggest_modifier(asm_arch, asm_ty)
315 {
316 let mut spans = vec![];
319 for piece in template {
320 if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece
321 {
322 if operand_idx == idx && modifier.is_none() {
323 spans.push(span);
324 }
325 }
326 }
327 if !spans.is_empty() {
328 let ModifierInfo {
329 modifier: default_modifier,
330 result: default_result,
331 size: default_size,
332 } = reg_class.default_modifier(asm_arch).unwrap();
333 self.tcx.node_span_lint(
334 lint::builtin::ASM_SUB_REGISTER,
335 expr.hir_id,
336 spans,
337 |lint| {
338 lint.primary_message("formatting may not be suitable for sub-register argument");
339 lint.span_label(expr.span, "for this argument");
340 lint.help(format!(
341 "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)",
342 ));
343 lint.help(format!(
344 "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)",
345 ));
346 },
347 );
348 }
349 }
350
351 Some(asm_ty)
352 }
353
354 pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>, enclosing_id: LocalDefId) {
355 let target_features = self.tcx.asm_target_features(enclosing_id.to_def_id());
356 let Some(asm_arch) = self.tcx.sess.asm_arch else {
357 self.tcx.dcx().delayed_bug("target architecture does not support asm");
358 return;
359 };
360 let allow_experimental_reg = self.tcx.features().asm_experimental_reg();
361 for (idx, (op, op_sp)) in asm.operands.iter().enumerate() {
362 if let Some(reg) = op.reg() {
373 if let InlineAsmRegOrRegClass::Reg(reg) = reg {
376 if let InlineAsmReg::Err = reg {
377 continue;
380 }
381 if let Err(msg) = reg.validate(
382 asm_arch,
383 self.tcx.sess.relocation_model(),
384 target_features,
385 &self.tcx.sess.target,
386 op.is_clobber(),
387 ) {
388 let msg = format!("cannot use register `{}`: {}", reg.name(), msg);
389 self.tcx.dcx().span_err(*op_sp, msg);
390 continue;
391 }
392 }
393
394 if !op.is_clobber() {
395 let mut missing_required_features = vec![];
396 let reg_class = reg.reg_class();
397 if let InlineAsmRegClass::Err = reg_class {
398 continue;
399 }
400 for &(_, feature) in reg_class.supported_types(asm_arch, allow_experimental_reg)
401 {
402 match feature {
403 Some(feature) => {
404 if target_features.contains(&feature) {
405 missing_required_features.clear();
406 break;
407 } else {
408 missing_required_features.push(feature);
409 }
410 }
411 None => {
412 missing_required_features.clear();
413 break;
414 }
415 }
416 }
417
418 missing_required_features.sort_unstable();
420 missing_required_features.dedup();
421 match &missing_required_features[..] {
422 [] => {}
423 [feature] => {
424 let msg = format!(
425 "register class `{}` requires the `{}` target feature",
426 reg_class.name(),
427 feature
428 );
429 self.tcx.dcx().span_err(*op_sp, msg);
430 continue;
432 }
433 features => {
434 let msg = format!(
435 "register class `{}` requires at least one of the following target features: {}",
436 reg_class.name(),
437 features
438 .iter()
439 .map(|f| f.as_str())
440 .intersperse(", ")
441 .collect::<String>(),
442 );
443 self.tcx.dcx().span_err(*op_sp, msg);
444 continue;
446 }
447 }
448 }
449 }
450
451 match *op {
452 hir::InlineAsmOperand::In { reg, expr } => {
453 self.check_asm_operand_type(
454 idx,
455 reg,
456 expr,
457 asm.template,
458 true,
459 None,
460 target_features,
461 );
462 }
463 hir::InlineAsmOperand::Out { reg, late: _, expr } => {
464 if let Some(expr) = expr {
465 self.check_asm_operand_type(
466 idx,
467 reg,
468 expr,
469 asm.template,
470 false,
471 None,
472 target_features,
473 );
474 }
475 }
476 hir::InlineAsmOperand::InOut { reg, late: _, expr } => {
477 self.check_asm_operand_type(
478 idx,
479 reg,
480 expr,
481 asm.template,
482 false,
483 None,
484 target_features,
485 );
486 }
487 hir::InlineAsmOperand::SplitInOut { reg, late: _, in_expr, out_expr } => {
488 let in_ty = self.check_asm_operand_type(
489 idx,
490 reg,
491 in_expr,
492 asm.template,
493 true,
494 None,
495 target_features,
496 );
497 if let Some(out_expr) = out_expr {
498 self.check_asm_operand_type(
499 idx,
500 reg,
501 out_expr,
502 asm.template,
503 false,
504 Some((in_expr, in_ty)),
505 target_features,
506 );
507 }
508 }
509 hir::InlineAsmOperand::Const { anon_const } => {
511 debug_assert_matches!(
512 self.tcx.type_of(anon_const.def_id).instantiate_identity().kind(),
513 ty::Error(_) | ty::Int(_) | ty::Uint(_)
514 );
515 }
516 hir::InlineAsmOperand::SymFn { anon_const } => {
518 debug_assert_matches!(
519 self.tcx.type_of(anon_const.def_id).instantiate_identity().kind(),
520 ty::Error(_) | ty::FnDef(..)
521 );
522 }
523 hir::InlineAsmOperand::SymStatic { .. } => {}
525 hir::InlineAsmOperand::Label { .. } => {}
527 }
528 }
529 }
530}