]>
Commit | Line | Data |
---|---|---|
5869c6ff | 1 | use rustc_ast::InlineAsmTemplatePiece; |
353b0b11 | 2 | use rustc_data_structures::fx::FxIndexSet; |
dfeec247 | 3 | use rustc_hir as hir; |
31ef2f64 | 4 | use rustc_middle::bug; |
9ffffee4 | 5 | use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy}; |
f9f354fc | 6 | use rustc_session::lint; |
9ffffee4 | 7 | use rustc_span::def_id::LocalDefId; |
c0240ec0 | 8 | use rustc_span::Symbol; |
353b0b11 | 9 | use rustc_target::abi::FieldIdx; |
e8be2606 FG |
10 | use rustc_target::asm::{ |
11 | InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo, | |
12 | }; | |
60c5eb7d | 13 | |
f2b60f7d FG |
14 | pub 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 | ||
20 | impl<'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 | } | |
f9f354fc | 36 | |
923072b8 | 37 | // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()` |
f9f354fc | 38 | fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool { |
923072b8 FG |
39 | // Type still may have region variables, but `Sized` does not depend |
40 | // on those, so just erase them before querying. | |
2b03887a | 41 | if ty.is_sized(self.tcx, self.param_env) { |
f9f354fc XL |
42 | return true; |
43 | } | |
1b1a35ee | 44 | if let ty::Foreign(..) = ty.kind() { |
f9f354fc XL |
45 | return true; |
46 | } | |
47 | false | |
48 | } | |
49 | ||
781aab86 | 50 | fn get_asm_ty(&self, ty: Ty<'tcx>) -> Option<InlineAsmType> { |
29967ef6 | 51 | let asm_ty_isize = match self.tcx.sess.target.pointer_width { |
f9f354fc XL |
52 | 16 => InlineAsmType::I16, |
53 | 32 => InlineAsmType::I32, | |
54 | 64 => InlineAsmType::I64, | |
4b012472 | 55 | width => bug!("unsupported pointer width: {width}"), |
f9f354fc | 56 | }; |
923072b8 | 57 | |
781aab86 | 58 | match *ty.kind() { |
f9f354fc XL |
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), | |
e8be2606 | 68 | ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize), |
add651ee | 69 | ty::Adt(adt, args) if adt.repr().simd() => { |
f9f354fc | 70 | let fields = &adt.non_enum_variant().fields; |
e8be2606 | 71 | let elem_ty = fields[FieldIdx::ZERO].ty(self.tcx, args); |
49aad941 FG |
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 | } | |
f9f354fc | 82 | } |
49aad941 FG |
83 | _ => (fields.len() as u64, elem_ty), |
84 | }; | |
85 | ||
86 | match ty.kind() { | |
49aad941 | 87 | ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::VecI8(size)), |
f9f354fc | 88 | ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => { |
49aad941 | 89 | Some(InlineAsmType::VecI16(size)) |
f9f354fc XL |
90 | } |
91 | ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => { | |
49aad941 | 92 | Some(InlineAsmType::VecI32(size)) |
f9f354fc XL |
93 | } |
94 | ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => { | |
49aad941 | 95 | Some(InlineAsmType::VecI64(size)) |
f9f354fc XL |
96 | } |
97 | ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => { | |
49aad941 | 98 | Some(InlineAsmType::VecI128(size)) |
f9f354fc XL |
99 | } |
100 | ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => { | |
29967ef6 | 101 | Some(match self.tcx.sess.target.pointer_width { |
49aad941 FG |
102 | 16 => InlineAsmType::VecI16(size), |
103 | 32 => InlineAsmType::VecI32(size), | |
104 | 64 => InlineAsmType::VecI64(size), | |
4b012472 | 105 | width => bug!("unsupported pointer width: {width}"), |
f9f354fc XL |
106 | }) |
107 | } | |
49aad941 FG |
108 | ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(size)), |
109 | ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(size)), | |
f9f354fc XL |
110 | _ => None, |
111 | } | |
112 | } | |
4b012472 | 113 | ty::Infer(_) => bug!("unexpected infer ty in asm operand"), |
f9f354fc | 114 | _ => None, |
781aab86 FG |
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); | |
4b012472 FG |
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(), | |
e8be2606 | 147 | "expected first field of `MaybeUninit` to be `ManuallyDrop`" |
4b012472 | 148 | ); |
781aab86 | 149 | let fields = &ty.non_enum_variant().fields; |
e8be2606 | 150 | let ty = fields[FieldIdx::ZERO].ty(self.tcx, args); |
781aab86 FG |
151 | self.get_asm_ty(ty) |
152 | } | |
153 | _ => self.get_asm_ty(ty), | |
f9f354fc | 154 | }; |
5e7ed085 | 155 | let Some(asm_ty) = asm_ty else { |
49aad941 | 156 | let msg = format!("cannot use value of type `{ty}` for inline assembly"); |
c0240ec0 FG |
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(); | |
5e7ed085 | 165 | return None; |
f9f354fc XL |
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)]. | |
2b03887a | 170 | if !ty.is_copy_modulo_regions(self.tcx, self.param_env) { |
f9f354fc | 171 | let msg = "arguments for inline assembly must be copyable"; |
c0240ec0 FG |
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(); | |
f9f354fc XL |
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 { | |
f035d41b | 190 | let msg = "incompatible types for asm inout argument"; |
f2b60f7d | 191 | let in_expr_ty = (self.get_operand_ty)(in_expr); |
c0240ec0 FG |
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(); | |
f9f354fc XL |
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); | |
5e7ed085 | 214 | let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else { |
49aad941 | 215 | let msg = format!("type `{ty}` cannot be used with this register class"); |
c0240ec0 | 216 | let mut err = self.tcx.dcx().struct_span_err(expr.span, msg); |
add651ee | 217 | let supported_tys: Vec<_> = supported_tys.iter().map(|(t, _)| t.to_string()).collect(); |
49aad941 | 218 | err.note(format!( |
5e7ed085 FG |
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) { | |
add651ee | 224 | err.help(format!("consider using the `{}` register class instead", suggest.name())); |
f9f354fc | 225 | } |
5e7ed085 FG |
226 | err.emit(); |
227 | return Some(asm_ty); | |
f9f354fc XL |
228 | }; |
229 | ||
230 | // Check whether the selected type requires a target feature. Note that | |
c295e0f8 XL |
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. | |
f9f354fc XL |
236 | // |
237 | // Also note that this check isn't run when the operand type is never | |
c295e0f8 XL |
238 | // (!). In that case we still need the earlier check to verify that the |
239 | // register class is usable at all. | |
f9f354fc | 240 | if let Some(feature) = feature { |
353b0b11 | 241 | if !target_features.contains(feature) { |
add651ee | 242 | let msg = format!("`{feature}` target feature is not enabled"); |
c0240ec0 FG |
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(); | |
f9f354fc XL |
252 | return Some(asm_ty); |
253 | } | |
254 | } | |
255 | ||
256 | // Check whether a modifier is suggested for using this type. | |
e8be2606 FG |
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) | |
f9f354fc XL |
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() { | |
e8be2606 FG |
275 | let ModifierInfo { |
276 | modifier: default_modifier, | |
277 | result: default_result, | |
278 | size: default_size, | |
279 | } = reg_class.default_modifier(asm_arch).unwrap(); | |
c0240ec0 | 280 | self.tcx.node_span_lint( |
f9f354fc XL |
281 | lint::builtin::ASM_SUB_REGISTER, |
282 | expr.hir_id, | |
283 | spans, | |
284 | |lint| { | |
31ef2f64 | 285 | lint.primary_message("formatting may not be suitable for sub-register argument"); |
2b03887a | 286 | lint.span_label(expr.span, "for this argument"); |
49aad941 | 287 | lint.help(format!( |
e8be2606 | 288 | "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)", |
f9f354fc | 289 | )); |
49aad941 | 290 | lint.help(format!( |
e8be2606 | 291 | "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)", |
f9f354fc | 292 | )); |
f9f354fc XL |
293 | }, |
294 | ); | |
295 | } | |
296 | } | |
297 | ||
298 | Some(asm_ty) | |
299 | } | |
300 | ||
9ffffee4 FG |
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()); | |
923072b8 | 303 | let Some(asm_arch) = self.tcx.sess.asm_arch else { |
c0240ec0 | 304 | self.tcx.dcx().delayed_bug("target architecture does not support asm"); |
923072b8 FG |
305 | return; |
306 | }; | |
c295e0f8 XL |
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() { | |
5e7ed085 FG |
319 | // Some explicit registers cannot be used depending on the |
320 | // target. Reject those here. | |
321 | if let InlineAsmRegOrRegClass::Reg(reg) = reg { | |
923072b8 FG |
322 | if let InlineAsmReg::Err = reg { |
323 | // `validate` will panic on `Err`, as an error must | |
324 | // already have been reported. | |
325 | continue; | |
326 | } | |
5e7ed085 FG |
327 | if let Err(msg) = reg.validate( |
328 | asm_arch, | |
329 | self.tcx.sess.relocation_model(), | |
4b012472 | 330 | target_features, |
5e7ed085 FG |
331 | &self.tcx.sess.target, |
332 | op.is_clobber(), | |
333 | ) { | |
334 | let msg = format!("cannot use register `{}`: {}", reg.name(), msg); | |
c0240ec0 | 335 | self.tcx.dcx().span_err(*op_sp, msg); |
5e7ed085 FG |
336 | continue; |
337 | } | |
338 | } | |
339 | ||
c295e0f8 XL |
340 | if !op.is_clobber() { |
341 | let mut missing_required_features = vec![]; | |
342 | let reg_class = reg.reg_class(); | |
923072b8 FG |
343 | if let InlineAsmRegClass::Err = reg_class { |
344 | continue; | |
345 | } | |
5e7ed085 | 346 | for &(_, feature) in reg_class.supported_types(asm_arch) { |
c295e0f8 XL |
347 | match feature { |
348 | Some(feature) => { | |
5e7ed085 | 349 | if target_features.contains(&feature) { |
c295e0f8 XL |
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 | ); | |
c0240ec0 | 374 | self.tcx.dcx().span_err(*op_sp, msg); |
c295e0f8 XL |
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(), | |
5099ac24 FG |
382 | features |
383 | .iter() | |
384 | .map(|f| f.as_str()) | |
385 | .intersperse(", ") | |
386 | .collect::<String>(), | |
c295e0f8 | 387 | ); |
c0240ec0 | 388 | self.tcx.dcx().span_err(*op_sp, msg); |
c295e0f8 XL |
389 | // register isn't enabled, don't do more checks |
390 | continue; | |
391 | } | |
392 | } | |
393 | } | |
394 | } | |
395 | ||
f9f354fc | 396 | match *op { |
9c376795 | 397 | hir::InlineAsmOperand::In { reg, expr } => { |
c295e0f8 XL |
398 | self.check_asm_operand_type( |
399 | idx, | |
400 | reg, | |
401 | expr, | |
402 | asm.template, | |
403 | true, | |
404 | None, | |
4b012472 | 405 | target_features, |
c295e0f8 | 406 | ); |
f9f354fc | 407 | } |
9c376795 | 408 | hir::InlineAsmOperand::Out { reg, late: _, expr } => { |
f9f354fc | 409 | if let Some(expr) = expr { |
c295e0f8 XL |
410 | self.check_asm_operand_type( |
411 | idx, | |
412 | reg, | |
413 | expr, | |
414 | asm.template, | |
415 | false, | |
416 | None, | |
4b012472 | 417 | target_features, |
c295e0f8 | 418 | ); |
f9f354fc XL |
419 | } |
420 | } | |
9c376795 | 421 | hir::InlineAsmOperand::InOut { reg, late: _, expr } => { |
c295e0f8 XL |
422 | self.check_asm_operand_type( |
423 | idx, | |
424 | reg, | |
425 | expr, | |
426 | asm.template, | |
427 | false, | |
428 | None, | |
4b012472 | 429 | target_features, |
c295e0f8 | 430 | ); |
f9f354fc | 431 | } |
9c376795 | 432 | hir::InlineAsmOperand::SplitInOut { reg, late: _, in_expr, out_expr } => { |
c295e0f8 XL |
433 | let in_ty = self.check_asm_operand_type( |
434 | idx, | |
435 | reg, | |
436 | in_expr, | |
437 | asm.template, | |
438 | true, | |
439 | None, | |
4b012472 | 440 | target_features, |
c295e0f8 | 441 | ); |
f9f354fc XL |
442 | if let Some(out_expr) = out_expr { |
443 | self.check_asm_operand_type( | |
444 | idx, | |
445 | reg, | |
446 | out_expr, | |
447 | asm.template, | |
94222f64 | 448 | false, |
f9f354fc | 449 | Some((in_expr, in_ty)), |
4b012472 | 450 | target_features, |
f9f354fc XL |
451 | ); |
452 | } | |
453 | } | |
04454e1e FG |
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 } => { | |
add651ee | 461 | let ty = self.tcx.type_of(anon_const.def_id).instantiate_identity(); |
04454e1e FG |
462 | match ty.kind() { |
463 | ty::Never | ty::Error(_) => {} | |
464 | ty::FnDef(..) => {} | |
465 | _ => { | |
c0240ec0 FG |
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(); | |
04454e1e FG |
477 | } |
478 | }; | |
479 | } | |
c620b35d FG |
480 | // No special checking is needed for labels. |
481 | hir::InlineAsmOperand::Label { .. } => {} | |
04454e1e FG |
482 | } |
483 | } | |
1a4d82fc JJ |
484 | } |
485 | } |