Skip to content

Commit

Permalink
Auto merge of #128651 - folkertdev:naked-asm-macro-v2, r=Amanieu
Browse files Browse the repository at this point in the history
add `naked_asm!` macro for use in `#[naked]` functions

tracking issue: #90957

Adds the `core::arch::naked_asm` macro, to be used in `#[naked]` functions, but providing better error messages and a place to explain the restrictions on assembly in naked functions.

This PR does not yet require that the `naked_asm!` macro is used inside of `#[naked]` functions:

- the `asm!` macro can still be used in `#[naked]` functions currently, with the same restrictions and error messages as before.
- the `naked_asm!` macro can be used outside of `#[naked]` functions. It has not yet been decided whether that should be allowed long-term.

In this PR, the parsing code of `naked_asm!` now enforces the restrictions on assembly in naked functions, with the exception of checking that the `noreturn` option is specified. It also has not currently been decided if `noreturn` should be implicit or not.

This PR looks large because it touches a bunch of tests. The code changes are mostly straightforward I think: we now have 3 flavors of assembly macro, and that information must be propagated through the parsing code and error messages.

cc `@Lokathor`

r? `@Amanieu`
  • Loading branch information
bors committed Oct 6, 2024
2 parents 55a22d2 + 5fc60d1 commit 1b3b8e7
Show file tree
Hide file tree
Showing 56 changed files with 551 additions and 519 deletions.
28 changes: 27 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2278,7 +2278,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 @@ -2434,6 +2434,32 @@ pub enum AsmMacro {
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),
}
}

pub const fn diverges(self, options: InlineAsmOptions) -> bool {
match self {
AsmMacro::Asm => options.contains(InlineAsmOptions::NORETURN),
AsmMacro::GlobalAsm => true,
AsmMacro::NakedAsm => true,
}
}
}

/// Inline assembly.
///
/// E.g., `asm!("NOP");`.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,7 @@ impl<'a, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'a, 'tcx, R>
}

TerminatorKind::InlineAsm {
asm_macro: _,
template: _,
operands,
options: _,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/polonius/loan_invalidations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
}
}
TerminatorKind::InlineAsm {
asm_macro: _,
template: _,
operands,
options: _,
Expand Down
24 changes: 12 additions & 12 deletions compiler/rustc_builtin_macros/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ builtin_macros_asm_duplicate_arg = duplicate argument named `{$name}`
builtin_macros_asm_expected_comma = expected token: `,`
.label = expected `,`
builtin_macros_asm_expected_other = expected operand, {$is_global_asm ->
[true] options
*[false] clobber_abi, options
builtin_macros_asm_expected_other = expected operand, {$is_inline_asm ->
[false] options
*[true] clobber_abi, options
}, or additional template string
builtin_macros_asm_expected_string_literal = expected string literal
Expand Down Expand Up @@ -51,6 +51,15 @@ builtin_macros_asm_sym_no_path = expected a path for argument to `sym`
builtin_macros_asm_underscore_input = _ cannot be used for input operands
builtin_macros_asm_unsupported_clobber_abi = `clobber_abi` cannot be used with `{$macro_name}!`
builtin_macros_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `{$macro_name}!`
.label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it
builtin_macros_asm_unsupported_option = the `{$symbol}` option cannot be used with `{$macro_name}!`
.label = the `{$symbol}` option is not meaningful for global-scoped inline assembly
.suggestion = remove this option
builtin_macros_assert_missing_comma = unexpected string literal
.suggestion = try adding a comma
Expand Down Expand Up @@ -194,15 +203,6 @@ builtin_macros_format_unused_args = multiple unused formatting arguments
builtin_macros_format_use_positional = consider using a positional formatting argument instead
builtin_macros_global_asm_clobber_abi = `clobber_abi` cannot be used with `global_asm!`
builtin_macros_global_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `global_asm!`
.label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it
builtin_macros_global_asm_unsupported_option = the `{$symbol}` option cannot be used with `global_asm!`
.label = the `{$symbol}` option is not meaningful for global-scoped inline assembly
.suggestion = remove this option
builtin_macros_invalid_crate_attribute = invalid crate attribute
builtin_macros_multiple_default_attrs = multiple `#[default]` attributes
Expand Down
128 changes: 68 additions & 60 deletions compiler/rustc_builtin_macros/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,23 @@ pub struct AsmArgs {
/// - `Ok(true)` if the current token matches the keyword, and was expected
/// - `Ok(false)` if the current token does not match the keyword
/// - `Err(_)` if the current token matches the keyword, but was not expected
fn eat_operand_keyword<'a>(p: &mut Parser<'a>, symbol: Symbol, expect: bool) -> PResult<'a, bool> {
if expect {
fn eat_operand_keyword<'a>(
p: &mut Parser<'a>,
symbol: Symbol,
asm_macro: AsmMacro,
) -> PResult<'a, bool> {
if matches!(asm_macro, AsmMacro::Asm) {
Ok(p.eat_keyword(symbol))
} else {
let span = p.token.span;
if p.eat_keyword_noexpect(symbol) {
// in gets printed as `r#in` otherwise
let symbol = if symbol == kw::In { "in" } else { symbol.as_str() };
Err(p.dcx().create_err(errors::GlobalAsmUnsupportedOperand { span, symbol }))
Err(p.dcx().create_err(errors::AsmUnsupportedOperand {
span,
symbol,
macro_name: asm_macro.macro_name(),
}))
} else {
Ok(false)
}
Expand All @@ -56,18 +64,18 @@ fn parse_args<'a>(
ecx: &ExtCtxt<'a>,
sp: Span,
tts: TokenStream,
is_global_asm: bool,
asm_macro: AsmMacro,
) -> PResult<'a, AsmArgs> {
let mut p = ecx.new_parser_from_tts(tts);
parse_asm_args(&mut p, sp, is_global_asm)
parse_asm_args(&mut p, sp, asm_macro)
}

// Primarily public for rustfmt consumption.
// Internal consumers should continue to leverage `expand_asm`/`expand__global_asm`
pub fn parse_asm_args<'a>(
p: &mut Parser<'a>,
sp: Span,
is_global_asm: bool,
asm_macro: AsmMacro,
) -> PResult<'a, AsmArgs> {
let dcx = p.dcx();

Expand Down Expand Up @@ -110,7 +118,7 @@ pub fn parse_asm_args<'a>(

// Parse options
if p.eat_keyword(sym::options) {
parse_options(p, &mut args, is_global_asm)?;
parse_options(p, &mut args, asm_macro)?;
allow_templates = false;
continue;
}
Expand All @@ -129,23 +137,23 @@ pub fn parse_asm_args<'a>(
};

let mut explicit_reg = false;
let op = if eat_operand_keyword(p, kw::In, !is_global_asm)? {
let op = if eat_operand_keyword(p, kw::In, asm_macro)? {
let reg = parse_reg(p, &mut explicit_reg)?;
if p.eat_keyword(kw::Underscore) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
ast::InlineAsmOperand::In { reg, expr }
} else if eat_operand_keyword(p, sym::out, !is_global_asm)? {
} else if eat_operand_keyword(p, sym::out, asm_macro)? {
let reg = parse_reg(p, &mut explicit_reg)?;
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: false }
} else if eat_operand_keyword(p, sym::lateout, !is_global_asm)? {
} else if eat_operand_keyword(p, sym::lateout, asm_macro)? {
let reg = parse_reg(p, &mut explicit_reg)?;
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: true }
} else if eat_operand_keyword(p, sym::inout, !is_global_asm)? {
} else if eat_operand_keyword(p, sym::inout, asm_macro)? {
let reg = parse_reg(p, &mut explicit_reg)?;
if p.eat_keyword(kw::Underscore) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
Expand All @@ -159,7 +167,7 @@ pub fn parse_asm_args<'a>(
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: false }
}
} else if eat_operand_keyword(p, sym::inlateout, !is_global_asm)? {
} else if eat_operand_keyword(p, sym::inlateout, asm_macro)? {
let reg = parse_reg(p, &mut explicit_reg)?;
if p.eat_keyword(kw::Underscore) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
Expand All @@ -173,7 +181,7 @@ pub fn parse_asm_args<'a>(
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: true }
}
} else if eat_operand_keyword(p, sym::label, !is_global_asm)? {
} else if eat_operand_keyword(p, sym::label, asm_macro)? {
let block = p.parse_block()?;
ast::InlineAsmOperand::Label { block }
} else if p.eat_keyword(kw::Const) {
Expand Down Expand Up @@ -205,7 +213,7 @@ pub fn parse_asm_args<'a>(
_ => {
let err = dcx.create_err(errors::AsmExpectedOther {
span: template.span,
is_global_asm,
is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
});
return Err(err);
}
Expand Down Expand Up @@ -301,20 +309,25 @@ pub fn parse_asm_args<'a>(
dcx.emit_err(errors::AsmMayUnwind { labels_sp });
}

if args.clobber_abis.len() > 0 {
if is_global_asm {
let err = dcx.create_err(errors::GlobalAsmClobberAbi {
spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
});
if !args.clobber_abis.is_empty() {
match asm_macro {
AsmMacro::GlobalAsm | AsmMacro::NakedAsm => {
let err = dcx.create_err(errors::AsmUnsupportedClobberAbi {
spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
macro_name: asm_macro.macro_name(),
});

// Bail out now since this is likely to confuse later stages
return Err(err);
}
if !regclass_outputs.is_empty() {
dcx.emit_err(errors::AsmClobberNoReg {
spans: regclass_outputs,
clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
});
// Bail out now since this is likely to confuse later stages
return Err(err);
}
AsmMacro::Asm => {
if !regclass_outputs.is_empty() {
dcx.emit_err(errors::AsmClobberNoReg {
spans: regclass_outputs,
clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
});
}
}
}
}

Expand All @@ -335,10 +348,15 @@ fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
///
/// This function must be called immediately after the option token is parsed.
/// Otherwise, the suggestion will be incorrect.
fn err_unsupported_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
fn err_unsupported_option(p: &Parser<'_>, asm_macro: AsmMacro, symbol: Symbol, span: Span) {
// Tool-only output
let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span };
p.dcx().emit_err(errors::GlobalAsmUnsupportedOption { span, symbol, full_span });
p.dcx().emit_err(errors::AsmUnsupportedOption {
span,
symbol,
full_span,
macro_name: asm_macro.macro_name(),
});
}

/// Try to set the provided option in the provided `AsmArgs`.
Expand All @@ -349,12 +367,12 @@ fn err_unsupported_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
fn try_set_option<'a>(
p: &Parser<'a>,
args: &mut AsmArgs,
is_global_asm: bool,
asm_macro: AsmMacro,
symbol: Symbol,
option: ast::InlineAsmOptions,
) {
if is_global_asm && !ast::InlineAsmOptions::GLOBAL_OPTIONS.contains(option) {
err_unsupported_option(p, symbol, p.prev_token.span);
if !asm_macro.is_supported_option(option) {
err_unsupported_option(p, asm_macro, symbol, p.prev_token.span);
} else if args.options.contains(option) {
err_duplicate_option(p, symbol, p.prev_token.span);
} else {
Expand All @@ -365,7 +383,7 @@ fn try_set_option<'a>(
fn parse_options<'a>(
p: &mut Parser<'a>,
args: &mut AsmArgs,
is_global_asm: bool,
asm_macro: AsmMacro,
) -> PResult<'a, ()> {
let span_start = p.prev_token.span;

Expand All @@ -386,15 +404,14 @@ fn parse_options<'a>(

'blk: {
for (symbol, option) in OPTIONS {
let kw_matched =
if !is_global_asm || ast::InlineAsmOptions::GLOBAL_OPTIONS.contains(option) {
p.eat_keyword(symbol)
} else {
p.eat_keyword_noexpect(symbol)
};
let kw_matched = if asm_macro.is_supported_option(option) {
p.eat_keyword(symbol)
} else {
p.eat_keyword_noexpect(symbol)
};

if kw_matched {
try_set_option(p, args, is_global_asm, symbol, option);
try_set_option(p, args, asm_macro, symbol, option);
break 'blk;
}
}
Expand Down Expand Up @@ -483,7 +500,7 @@ fn parse_reg<'a>(

fn expand_preparsed_asm(
ecx: &mut ExtCtxt<'_>,
asm_macro: ast::AsmMacro,
asm_macro: AsmMacro,
args: AsmArgs,
) -> ExpandResult<Result<ast::InlineAsm, ErrorGuaranteed>, ()> {
let mut template = vec![];
Expand Down Expand Up @@ -797,7 +814,7 @@ pub(super) fn expand_asm<'cx>(
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
ExpandResult::Ready(match parse_args(ecx, sp, tts, false) {
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::Asm) {
Ok(args) => {
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::Asm, args) else {
return ExpandResult::Retry(());
Expand Down Expand Up @@ -826,29 +843,20 @@ pub(super) fn expand_naked_asm<'cx>(
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
ExpandResult::Ready(match parse_args(ecx, sp, tts, false) {
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::NakedAsm) {
Ok(args) => {
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::NakedAsm, args)
else {
return ExpandResult::Retry(());
};
let expr = match mac {
Ok(mut inline_asm) => {
// for future compatibility, we always set the NORETURN option.
//
// When we turn `asm!` into `naked_asm!` with this implementation, we can drop
// the `options(noreturn)`, which makes the upgrade smooth when `naked_asm!`
// starts disallowing the `noreturn` option in the future
inline_asm.options |= ast::InlineAsmOptions::NORETURN;

P(ast::Expr {
id: ast::DUMMY_NODE_ID,
kind: ast::ExprKind::InlineAsm(P(inline_asm)),
span: sp,
attrs: ast::AttrVec::new(),
tokens: None,
})
}
Ok(inline_asm) => P(ast::Expr {
id: ast::DUMMY_NODE_ID,
kind: ast::ExprKind::InlineAsm(P(inline_asm)),
span: sp,
attrs: ast::AttrVec::new(),
tokens: None,
}),
Err(guar) => DummyResult::raw_expr(sp, Some(guar)),
};
MacEager::expr(expr)
Expand All @@ -865,7 +873,7 @@ pub(super) fn expand_global_asm<'cx>(
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
ExpandResult::Ready(match parse_args(ecx, sp, tts, true) {
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::GlobalAsm) {
Ok(args) => {
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::GlobalAsm, args)
else {
Expand Down
Loading

0 comments on commit 1b3b8e7

Please sign in to comment.