Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add naked_asm! macro for use in #[naked] functions #128651

Merged
merged 8 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
folkertdev marked this conversation as resolved.
Show resolved Hide resolved

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
Loading