diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index 2e965c59ebb53..d099f8f79cfef 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -1,4 +1,4 @@ -use rustc_ast::InlineAsmTemplatePiece; +use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_data_structures::fx::FxIndexSet; use rustc_hir::{self as hir, LangItem}; use rustc_middle::bug; @@ -124,7 +124,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { idx: usize, reg: InlineAsmRegOrRegClass, expr: &'tcx hir::Expr<'tcx>, - template: &[InlineAsmTemplatePiece], + asm: &hir::InlineAsm<'tcx>, is_input: bool, tied_input: Option<(&'tcx hir::Expr<'tcx>, Option)>, target_features: &FxIndexSet, @@ -267,7 +267,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { // Search for any use of this operand without a modifier and emit // the suggestion for them. let mut spans = vec![]; - for piece in template { + for piece in asm.template { if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece { if operand_idx == idx && modifier.is_none() { @@ -299,6 +299,28 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } } + match *ty.kind() { + ty::RawPtr(_, hir::Mutability::Mut) if asm.options.contains(InlineAsmOptions::READONLY) => + self + .tcx + .dcx() + .struct_span_warn(expr.span, "passing a mutable pointer to asm! block with 'readonly' option.") + .with_note("`readonly` means that no memory write happens inside the asm! block.") + .with_note("This is not limited to global variables, it also includes passed pointers.") + .with_note("If passing this mutable pointer is intentional, remove the `readonly` attribute.") + .emit(), + ty::RawPtr(_, _) if asm.options.contains(InlineAsmOptions::NOMEM) => + self + .tcx + .dcx() + .struct_span_warn(expr.span, "passing a pointer to asm! block with 'nomem' option.") + .with_note("`nomem` means that no memory write or read happens inside the asm! block.") + .with_note("This is not limited to global variables, it also includes passed pointers.") + .with_note("If passing this pointer is intentional, replace the `nomem` attribute with `readonly` or remove it completely.") + .emit(), + _ => {} // we're only interested in pointers when asm! has `nomem` or `readonly` + } + Some(asm_ty) } @@ -399,15 +421,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { match *op { hir::InlineAsmOperand::In { reg, expr } => { - self.check_asm_operand_type( - idx, - reg, - expr, - asm.template, - true, - None, - target_features, - ); + self.check_asm_operand_type(idx, reg, expr, asm, true, None, target_features); } hir::InlineAsmOperand::Out { reg, late: _, expr } => { if let Some(expr) = expr { @@ -415,7 +429,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { idx, reg, expr, - asm.template, + asm, false, None, target_features, @@ -423,22 +437,14 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } } hir::InlineAsmOperand::InOut { reg, late: _, expr } => { - self.check_asm_operand_type( - idx, - reg, - expr, - asm.template, - false, - None, - target_features, - ); + self.check_asm_operand_type(idx, reg, expr, asm, false, None, target_features); } hir::InlineAsmOperand::SplitInOut { reg, late: _, in_expr, out_expr } => { let in_ty = self.check_asm_operand_type( idx, reg, in_expr, - asm.template, + asm, true, None, target_features, @@ -448,7 +454,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { idx, reg, out_expr, - asm.template, + asm, false, Some((in_expr, in_ty)), target_features, diff --git a/tests/ui/asm/x86_64/passing-pointer-nomem-readonly.rs b/tests/ui/asm/x86_64/passing-pointer-nomem-readonly.rs new file mode 100644 index 0000000000000..f0da0cc1531fa --- /dev/null +++ b/tests/ui/asm/x86_64/passing-pointer-nomem-readonly.rs @@ -0,0 +1,34 @@ +//@ only-x86_64 +//@ needs-asm-support +//@ build-pass + +#![crate_type = "lib"] +#![no_std] + +unsafe fn nomem_bad(p: &i32) { + core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + //~^ WARNING passing a pointer to asm! block with 'nomem' option. +} + +unsafe fn readonly_bad(p: &mut i32) { + core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); + //~^ WARNING passing a mutable pointer to asm! block with 'readonly' option. +} + +unsafe fn nomem_good(p: &i32) { + core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); + let p = p as *const i32 as usize; + core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); +} + +unsafe fn readonly_good(p: &mut i32) { + core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(nostack, preserves_flags)); + core::arch::asm!("mov {p}, {p}", p = in(reg) &*p, options(readonly, nostack, preserves_flags)); + let p = p as *const i32; + core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); +} + +unsafe fn nomem_bad2(p: &mut i32) { + core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + //~^ WARNING passing a pointer to asm! block with 'nomem' option. +} diff --git a/tests/ui/asm/x86_64/passing-pointer-nomem-readonly.stderr b/tests/ui/asm/x86_64/passing-pointer-nomem-readonly.stderr new file mode 100644 index 0000000000000..644d14967ecdf --- /dev/null +++ b/tests/ui/asm/x86_64/passing-pointer-nomem-readonly.stderr @@ -0,0 +1,32 @@ +warning: passing a pointer to asm! block with 'nomem' option. + --> $DIR/passing-pointer-nomem-readonly.rs:9:50 + | +LL | core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + | ^ + | + = note: `nomem` means that no memory write or read happens inside the asm! block. + = note: This is not limited to global variables, it also includes passed pointers. + = note: If passing this pointer is intentional, replace the `nomem` attribute with `readonly` or remove it completely. + +warning: passing a mutable pointer to asm! block with 'readonly' option. + --> $DIR/passing-pointer-nomem-readonly.rs:14:50 + | +LL | core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); + | ^ + | + = note: `readonly` means that no memory write happens inside the asm! block. + = note: This is not limited to global variables, it also includes passed pointers. + = note: If passing this mutable pointer is intentional, remove the `readonly` attribute. + +warning: passing a pointer to asm! block with 'nomem' option. + --> $DIR/passing-pointer-nomem-readonly.rs:32:50 + | +LL | core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + | ^ + | + = note: `nomem` means that no memory write or read happens inside the asm! block. + = note: This is not limited to global variables, it also includes passed pointers. + = note: If passing this pointer is intentional, replace the `nomem` attribute with `readonly` or remove it completely. + +warning: 3 warnings emitted +