Skip to content

Commit

Permalink
disallow asm! in #[naked] functions
Browse files Browse the repository at this point in the history
also disallow the `noreturn` option, and infer `naked_asm!` as `!`
  • Loading branch information
folkertdev committed Sep 5, 2024
1 parent 6c40f8f commit c1e29cb
Show file tree
Hide file tree
Showing 30 changed files with 255 additions and 263 deletions.
31 changes: 30 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2272,7 +2272,7 @@ impl InlineAsmOptions {
pub const COUNT: usize = Self::all().bits().count_ones() as usize;

pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);
pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN);
pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);

pub fn human_readable_names(&self) -> Vec<&'static str> {
let mut options = vec![];
Expand Down Expand Up @@ -2418,11 +2418,40 @@ impl InlineAsmOperand {
}
}

#[derive(Clone, Copy, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum AsmMacro {
/// The `asm!` macro
Asm,
/// The `global_asm!` macro
GlobalAsm,
/// The `naked_asm!` macro
NakedAsm,
}

impl AsmMacro {
pub const fn macro_name(&self) -> &'static str {
match self {
AsmMacro::Asm => "asm",
AsmMacro::GlobalAsm => "global_asm",
AsmMacro::NakedAsm => "naked_asm",
}
}

pub const fn is_supported_option(&self, option: InlineAsmOptions) -> bool {
match self {
AsmMacro::Asm => true,
AsmMacro::GlobalAsm => InlineAsmOptions::GLOBAL_OPTIONS.contains(option),
AsmMacro::NakedAsm => InlineAsmOptions::NAKED_OPTIONS.contains(option),
}
}
}

/// Inline assembly.
///
/// E.g., `asm!("NOP");`.
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct InlineAsm {
pub asm_macro: AsmMacro,
pub template: Vec<InlineAsmTemplatePiece>,
pub template_strs: Box<[(Symbol, Option<Symbol>, Span)]>,
pub operands: Vec<(InlineAsmOperand, Span)>,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1388,6 +1388,7 @@ fn walk_anon_const<T: MutVisitor>(vis: &mut T, AnonConst { id, value }: &mut Ano
fn walk_inline_asm<T: MutVisitor>(vis: &mut T, asm: &mut InlineAsm) {
// FIXME: Visit spans inside all this currently ignored stuff.
let InlineAsm {
asm_macro: _,
template: _,
template_strs: _,
operands,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,7 @@ pub fn walk_anon_const<'a, V: Visitor<'a>>(visitor: &mut V, constant: &'a AnonCo

pub fn walk_inline_asm<'a, V: Visitor<'a>>(visitor: &mut V, asm: &'a InlineAsm) -> V::Result {
let InlineAsm {
asm_macro: _,
template: _,
template_strs: _,
operands,
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_ast_lowering/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
);
let line_spans =
self.arena.alloc_from_iter(asm.line_spans.iter().map(|span| self.lower_span(*span)));
let hir_asm =
hir::InlineAsm { template, template_strs, operands, options: asm.options, line_spans };
let hir_asm = hir::InlineAsm {
asm_macro: asm.asm_macro,
template,
template_strs,
operands,
options: asm.options,
line_spans,
};
self.arena.alloc(hir_asm)
}
}
40 changes: 8 additions & 32 deletions compiler/rustc_builtin_macros/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use lint::BuiltinLintDiag;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::AsmMacro;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_errors::PResult;
use rustc_expand::base::*;
Expand Down Expand Up @@ -59,35 +60,6 @@ fn eat_operand_keyword<'a>(
}
}

// Public for rustfmt consumption.
#[derive(Copy, Clone)]
pub enum AsmMacro {
/// The `asm!` macro
Asm,
/// The `global_asm!` macro
GlobalAsm,
/// The `naked_asm!` macro
NakedAsm,
}

impl AsmMacro {
const fn macro_name(&self) -> &'static str {
match self {
AsmMacro::Asm => "asm",
AsmMacro::GlobalAsm => "global_asm",
AsmMacro::NakedAsm => "naked_asm",
}
}

const fn is_supported_option(&self, option: ast::InlineAsmOptions) -> bool {
match self {
AsmMacro::Asm => true,
AsmMacro::GlobalAsm => ast::InlineAsmOptions::GLOBAL_OPTIONS.contains(option),
AsmMacro::NakedAsm => ast::InlineAsmOptions::NAKED_OPTIONS.contains(option),
}
}
}

fn parse_args<'a>(
ecx: &ExtCtxt<'a>,
sp: Span,
Expand Down Expand Up @@ -530,6 +502,7 @@ fn parse_reg<'a>(

fn expand_preparsed_asm(
ecx: &mut ExtCtxt<'_>,
asm_macro: AsmMacro,
args: AsmArgs,
) -> ExpandResult<Result<ast::InlineAsm, ErrorGuaranteed>, ()> {
let mut template = vec![];
Expand Down Expand Up @@ -820,6 +793,7 @@ fn expand_preparsed_asm(
}

ExpandResult::Ready(Ok(ast::InlineAsm {
asm_macro,
template,
template_strs: template_strs.into_boxed_slice(),
operands: args.operands,
Expand All @@ -836,7 +810,7 @@ pub(super) fn expand_asm<'cx>(
) -> MacroExpanderResult<'cx> {
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::Asm) {
Ok(args) => {
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, args) else {
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::Asm, args) else {
return ExpandResult::Retry(());
};
let expr = match mac {
Expand Down Expand Up @@ -865,7 +839,8 @@ pub(super) fn expand_global_asm<'cx>(
) -> MacroExpanderResult<'cx> {
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::GlobalAsm) {
Ok(args) => {
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, args) else {
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::GlobalAsm, args)
else {
return ExpandResult::Retry(());
};
match mac {
Expand Down Expand Up @@ -899,7 +874,8 @@ pub(super) fn expand_naked_asm<'cx>(
) -> MacroExpanderResult<'cx> {
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::NakedAsm) {
Ok(args) => {
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, args) else {
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::NakedAsm, args)
else {
return ExpandResult::Retry(());
};
let expr = match mac {
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_error_codes/src/error_codes/E0787.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ pub extern "C" fn f() -> u32 {
}
```

The naked functions must be defined using a single inline assembly
block.
The naked function must be defined using a single `naked_asm!` assembly block.

The execution must never fall through past the end of the assembly
code so the block must use `noreturn` option. The asm block can also
code, so it must either return or diverge. The asm block can also
use `att_syntax` and `raw` options, but others options are not allowed.

The asm block must not contain any operands other than `const` and
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2914,6 +2914,7 @@ impl<'hir> InlineAsmOperand<'hir> {

#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct InlineAsm<'hir> {
pub asm_macro: ast::AsmMacro,
pub template: &'hir [InlineAsmTemplatePiece],
pub template_strs: &'hir [(Symbol, Option<Symbol>, Span)],
pub operands: &'hir [(InlineAsmOperand<'hir>, Span)],
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3307,7 +3307,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> {
let mut diverge = asm.options.contains(ast::InlineAsmOptions::NORETURN);
let mut diverge = match asm.asm_macro {
rustc_ast::AsmMacro::Asm => asm.options.contains(ast::InlineAsmOptions::NORETURN),
rustc_ast::AsmMacro::GlobalAsm => true,
rustc_ast::AsmMacro::NakedAsm => true,
};

for (op, _op_sp) in asm.operands {
match op {
Expand Down
12 changes: 6 additions & 6 deletions compiler/rustc_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -485,9 +485,9 @@ passes_must_use_no_effect =
`#[must_use]` has no effect when applied to {$article} {$target}
passes_naked_functions_asm_block =
naked functions must contain a single asm block
.label_multiple_asm = multiple asm blocks are unsupported in naked functions
.label_non_asm = non-asm is unsupported in naked functions
naked functions must contain a single `naked_asm!` invocation
.label_multiple_asm = multiple `naked_asm!` invocations are not allowed in naked functions
.label_non_asm = not allowed in naked functions
passes_naked_functions_asm_options =
asm options unsupported in naked functions: {$unsupported_options}
Expand All @@ -497,9 +497,9 @@ passes_naked_functions_incompatible_attribute =
.label = the `{$attr}` attribute is incompatible with `#[naked]`
.naked_attribute = function marked with `#[naked]` here
passes_naked_functions_must_use_noreturn =
asm in naked functions must use `noreturn` option
.suggestion = consider specifying that the asm block is responsible for returning from the function
passes_naked_functions_must_naked_asm =
the `asm!` macro is not allowed in naked functions
.suggestion = consider using the `naked_asm!` macro instead
passes_naked_functions_operands =
only `const` and `sym` operands are supported in naked functions
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1202,12 +1202,12 @@ pub(crate) struct NakedFunctionsAsmOptions {
}

#[derive(Diagnostic)]
#[diag(passes_naked_functions_must_use_noreturn, code = E0787)]
pub(crate) struct NakedFunctionsMustUseNoreturn {
#[diag(passes_naked_functions_must_naked_asm, code = E0787)]
pub(crate) struct NakedFunctionsMustNakedAsm {
#[primary_span]
pub span: Span,
#[suggestion(code = ", options(noreturn)", applicability = "machine-applicable")]
pub last_span: Span,
#[suggestion(code = "naked_asm!", applicability = "machine-applicable")]
pub macro_span: Span,
}

#[derive(Diagnostic)]
Expand Down
50 changes: 29 additions & 21 deletions compiler/rustc_passes/src/naked_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI;
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_span::{BytePos, Span};
use rustc_target::spec::abi::Abi;

use crate::errors::{
NakedFunctionsAsmBlock, NakedFunctionsAsmOptions, NakedFunctionsMustUseNoreturn,
NakedFunctionsAsmBlock, NakedFunctionsAsmOptions, NakedFunctionsMustNakedAsm,
NakedFunctionsOperands, NoPatterns, ParamsNotAllowed, UndefinedNakedFunctionAbi,
};

Expand All @@ -29,8 +29,7 @@ fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
continue;
}

let naked = tcx.has_attr(def_id, sym::naked);
if !naked {
if !tcx.has_attr(def_id, sym::naked) {
continue;
}

Expand Down Expand Up @@ -117,21 +116,29 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) {
let mut this = CheckInlineAssembly { tcx, items: Vec::new() };
this.visit_body(body);
if let [(ItemKind::Asm | ItemKind::Err, _)] = this.items[..] {
if let [(ItemKind::NakedAsm | ItemKind::Err, _)] = this.items[..] {
// Ok.
} else {
let mut must_show_error = false;
let mut has_asm = false;
let mut has_naked_asm = false;
let mut has_err = false;
let mut multiple_asms = vec![];
let mut non_asms = vec![];
for &(kind, span) in &this.items {
match kind {
ItemKind::Asm if has_asm => {
ItemKind::NakedAsm if has_naked_asm => {
must_show_error = true;
multiple_asms.push(span);
}
ItemKind::Asm => has_asm = true,
ItemKind::NakedAsm => has_naked_asm = true,
ItemKind::InlineAsm => {
has_err = true;

// the span that contains the `asm!` call,
// so tooling can replace it with `naked_asm!`
let macro_span = span.with_hi(span.lo() + BytePos("asm!".len() as u32));
tcx.dcx().emit_err(NakedFunctionsMustNakedAsm { span, macro_span });
}
ItemKind::NonAsm => {
must_show_error = true;
non_asms.push(span);
Expand Down Expand Up @@ -160,7 +167,8 @@ struct CheckInlineAssembly<'tcx> {

#[derive(Copy, Clone)]
enum ItemKind {
Asm,
NakedAsm,
InlineAsm,
NonAsm,
Err,
}
Expand Down Expand Up @@ -201,8 +209,18 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
}

ExprKind::InlineAsm(asm) => {
self.items.push((ItemKind::Asm, span));
self.check_inline_asm(asm, span);
match asm.asm_macro {
rustc_ast::AsmMacro::Asm => {
self.items.push((ItemKind::InlineAsm, span));
}
rustc_ast::AsmMacro::NakedAsm => {
self.items.push((ItemKind::NakedAsm, span));
self.check_inline_asm(asm, span);
}
rustc_ast::AsmMacro::GlobalAsm => {
// not allowed in this position
}
}
}

ExprKind::DropTemps(..) | ExprKind::Block(..) => {
Expand Down Expand Up @@ -246,16 +264,6 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
.join(", "),
});
}

if !asm.options.contains(InlineAsmOptions::NORETURN) {
let last_span = asm
.operands
.last()
.map_or_else(|| asm.template_strs.last().unwrap().2, |op| op.1)
.shrink_to_hi();

self.tcx.dcx().emit_err(NakedFunctionsMustUseNoreturn { span, last_span });
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/tools/rustfmt/src/parse/macros/asm.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use rustc_ast::ast;
use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs, AsmMacro};
use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs};

use crate::rewrite::RewriteContext;

#[allow(dead_code)]
pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option<AsmArgs> {
let ts = mac.args.tokens.clone();
let mut parser = super::build_parser(context, ts);
parse_asm_args(&mut parser, mac.span(), AsmMacro::Asm).ok()
parse_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok()
}
4 changes: 2 additions & 2 deletions tests/codegen/naked-fn/aligned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#![crate_type = "lib"]
#![feature(naked_functions, fn_align)]
use std::arch::asm;
use std::arch::naked_asm;

// CHECK: Function Attrs: naked
// CHECK-NEXT: define{{.*}}void @naked_empty()
Expand All @@ -16,5 +16,5 @@ pub unsafe extern "C" fn naked_empty() {
// CHECK-NEXT: start:
// CHECK-NEXT: call void asm
// CHECK-NEXT: unreachable
asm!("ret", options(noreturn));
naked_asm!("ret");
}
Loading

0 comments on commit c1e29cb

Please sign in to comment.