Skip to content

Commit 1b3b8e7

Browse files
committed
Auto merge of #128651 - folkertdev:naked-asm-macro-v2, r=Amanieu
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`
2 parents 55a22d2 + 5fc60d1 commit 1b3b8e7

File tree

56 files changed

+551
-519
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+551
-519
lines changed

Diff for: compiler/rustc_ast/src/ast.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -2278,7 +2278,7 @@ impl InlineAsmOptions {
22782278
pub const COUNT: usize = Self::all().bits().count_ones() as usize;
22792279

22802280
pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);
2281-
pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN);
2281+
pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);
22822282

22832283
pub fn human_readable_names(&self) -> Vec<&'static str> {
22842284
let mut options = vec![];
@@ -2434,6 +2434,32 @@ pub enum AsmMacro {
24342434
NakedAsm,
24352435
}
24362436

2437+
impl AsmMacro {
2438+
pub const fn macro_name(self) -> &'static str {
2439+
match self {
2440+
AsmMacro::Asm => "asm",
2441+
AsmMacro::GlobalAsm => "global_asm",
2442+
AsmMacro::NakedAsm => "naked_asm",
2443+
}
2444+
}
2445+
2446+
pub const fn is_supported_option(self, option: InlineAsmOptions) -> bool {
2447+
match self {
2448+
AsmMacro::Asm => true,
2449+
AsmMacro::GlobalAsm => InlineAsmOptions::GLOBAL_OPTIONS.contains(option),
2450+
AsmMacro::NakedAsm => InlineAsmOptions::NAKED_OPTIONS.contains(option),
2451+
}
2452+
}
2453+
2454+
pub const fn diverges(self, options: InlineAsmOptions) -> bool {
2455+
match self {
2456+
AsmMacro::Asm => options.contains(InlineAsmOptions::NORETURN),
2457+
AsmMacro::GlobalAsm => true,
2458+
AsmMacro::NakedAsm => true,
2459+
}
2460+
}
2461+
}
2462+
24372463
/// Inline assembly.
24382464
///
24392465
/// E.g., `asm!("NOP");`.

Diff for: compiler/rustc_borrowck/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,7 @@ impl<'a, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'a, 'tcx, R>
742742
}
743743

744744
TerminatorKind::InlineAsm {
745+
asm_macro: _,
745746
template: _,
746747
operands,
747748
options: _,

Diff for: compiler/rustc_borrowck/src/polonius/loan_invalidations.rs

+1
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
169169
}
170170
}
171171
TerminatorKind::InlineAsm {
172+
asm_macro: _,
172173
template: _,
173174
operands,
174175
options: _,

Diff for: compiler/rustc_builtin_macros/messages.ftl

+12-12
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ builtin_macros_asm_duplicate_arg = duplicate argument named `{$name}`
1212
builtin_macros_asm_expected_comma = expected token: `,`
1313
.label = expected `,`
1414
15-
builtin_macros_asm_expected_other = expected operand, {$is_global_asm ->
16-
[true] options
17-
*[false] clobber_abi, options
15+
builtin_macros_asm_expected_other = expected operand, {$is_inline_asm ->
16+
[false] options
17+
*[true] clobber_abi, options
1818
}, or additional template string
1919
2020
builtin_macros_asm_expected_string_literal = expected string literal
@@ -51,6 +51,15 @@ builtin_macros_asm_sym_no_path = expected a path for argument to `sym`
5151
5252
builtin_macros_asm_underscore_input = _ cannot be used for input operands
5353
54+
builtin_macros_asm_unsupported_clobber_abi = `clobber_abi` cannot be used with `{$macro_name}!`
55+
56+
builtin_macros_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `{$macro_name}!`
57+
.label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it
58+
59+
builtin_macros_asm_unsupported_option = the `{$symbol}` option cannot be used with `{$macro_name}!`
60+
.label = the `{$symbol}` option is not meaningful for global-scoped inline assembly
61+
.suggestion = remove this option
62+
5463
builtin_macros_assert_missing_comma = unexpected string literal
5564
.suggestion = try adding a comma
5665
@@ -194,15 +203,6 @@ builtin_macros_format_unused_args = multiple unused formatting arguments
194203
195204
builtin_macros_format_use_positional = consider using a positional formatting argument instead
196205
197-
builtin_macros_global_asm_clobber_abi = `clobber_abi` cannot be used with `global_asm!`
198-
199-
builtin_macros_global_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `global_asm!`
200-
.label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it
201-
202-
builtin_macros_global_asm_unsupported_option = the `{$symbol}` option cannot be used with `global_asm!`
203-
.label = the `{$symbol}` option is not meaningful for global-scoped inline assembly
204-
.suggestion = remove this option
205-
206206
builtin_macros_invalid_crate_attribute = invalid crate attribute
207207
208208
builtin_macros_multiple_default_attrs = multiple `#[default]` attributes

Diff for: compiler/rustc_builtin_macros/src/asm.rs

+68-60
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,23 @@ pub struct AsmArgs {
3737
/// - `Ok(true)` if the current token matches the keyword, and was expected
3838
/// - `Ok(false)` if the current token does not match the keyword
3939
/// - `Err(_)` if the current token matches the keyword, but was not expected
40-
fn eat_operand_keyword<'a>(p: &mut Parser<'a>, symbol: Symbol, expect: bool) -> PResult<'a, bool> {
41-
if expect {
40+
fn eat_operand_keyword<'a>(
41+
p: &mut Parser<'a>,
42+
symbol: Symbol,
43+
asm_macro: AsmMacro,
44+
) -> PResult<'a, bool> {
45+
if matches!(asm_macro, AsmMacro::Asm) {
4246
Ok(p.eat_keyword(symbol))
4347
} else {
4448
let span = p.token.span;
4549
if p.eat_keyword_noexpect(symbol) {
4650
// in gets printed as `r#in` otherwise
4751
let symbol = if symbol == kw::In { "in" } else { symbol.as_str() };
48-
Err(p.dcx().create_err(errors::GlobalAsmUnsupportedOperand { span, symbol }))
52+
Err(p.dcx().create_err(errors::AsmUnsupportedOperand {
53+
span,
54+
symbol,
55+
macro_name: asm_macro.macro_name(),
56+
}))
4957
} else {
5058
Ok(false)
5159
}
@@ -56,18 +64,18 @@ fn parse_args<'a>(
5664
ecx: &ExtCtxt<'a>,
5765
sp: Span,
5866
tts: TokenStream,
59-
is_global_asm: bool,
67+
asm_macro: AsmMacro,
6068
) -> PResult<'a, AsmArgs> {
6169
let mut p = ecx.new_parser_from_tts(tts);
62-
parse_asm_args(&mut p, sp, is_global_asm)
70+
parse_asm_args(&mut p, sp, asm_macro)
6371
}
6472

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

@@ -110,7 +118,7 @@ pub fn parse_asm_args<'a>(
110118

111119
// Parse options
112120
if p.eat_keyword(sym::options) {
113-
parse_options(p, &mut args, is_global_asm)?;
121+
parse_options(p, &mut args, asm_macro)?;
114122
allow_templates = false;
115123
continue;
116124
}
@@ -129,23 +137,23 @@ pub fn parse_asm_args<'a>(
129137
};
130138

131139
let mut explicit_reg = false;
132-
let op = if eat_operand_keyword(p, kw::In, !is_global_asm)? {
140+
let op = if eat_operand_keyword(p, kw::In, asm_macro)? {
133141
let reg = parse_reg(p, &mut explicit_reg)?;
134142
if p.eat_keyword(kw::Underscore) {
135143
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
136144
return Err(err);
137145
}
138146
let expr = p.parse_expr()?;
139147
ast::InlineAsmOperand::In { reg, expr }
140-
} else if eat_operand_keyword(p, sym::out, !is_global_asm)? {
148+
} else if eat_operand_keyword(p, sym::out, asm_macro)? {
141149
let reg = parse_reg(p, &mut explicit_reg)?;
142150
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
143151
ast::InlineAsmOperand::Out { reg, expr, late: false }
144-
} else if eat_operand_keyword(p, sym::lateout, !is_global_asm)? {
152+
} else if eat_operand_keyword(p, sym::lateout, asm_macro)? {
145153
let reg = parse_reg(p, &mut explicit_reg)?;
146154
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
147155
ast::InlineAsmOperand::Out { reg, expr, late: true }
148-
} else if eat_operand_keyword(p, sym::inout, !is_global_asm)? {
156+
} else if eat_operand_keyword(p, sym::inout, asm_macro)? {
149157
let reg = parse_reg(p, &mut explicit_reg)?;
150158
if p.eat_keyword(kw::Underscore) {
151159
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
@@ -159,7 +167,7 @@ pub fn parse_asm_args<'a>(
159167
} else {
160168
ast::InlineAsmOperand::InOut { reg, expr, late: false }
161169
}
162-
} else if eat_operand_keyword(p, sym::inlateout, !is_global_asm)? {
170+
} else if eat_operand_keyword(p, sym::inlateout, asm_macro)? {
163171
let reg = parse_reg(p, &mut explicit_reg)?;
164172
if p.eat_keyword(kw::Underscore) {
165173
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
@@ -173,7 +181,7 @@ pub fn parse_asm_args<'a>(
173181
} else {
174182
ast::InlineAsmOperand::InOut { reg, expr, late: true }
175183
}
176-
} else if eat_operand_keyword(p, sym::label, !is_global_asm)? {
184+
} else if eat_operand_keyword(p, sym::label, asm_macro)? {
177185
let block = p.parse_block()?;
178186
ast::InlineAsmOperand::Label { block }
179187
} else if p.eat_keyword(kw::Const) {
@@ -205,7 +213,7 @@ pub fn parse_asm_args<'a>(
205213
_ => {
206214
let err = dcx.create_err(errors::AsmExpectedOther {
207215
span: template.span,
208-
is_global_asm,
216+
is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
209217
});
210218
return Err(err);
211219
}
@@ -301,20 +309,25 @@ pub fn parse_asm_args<'a>(
301309
dcx.emit_err(errors::AsmMayUnwind { labels_sp });
302310
}
303311

304-
if args.clobber_abis.len() > 0 {
305-
if is_global_asm {
306-
let err = dcx.create_err(errors::GlobalAsmClobberAbi {
307-
spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
308-
});
312+
if !args.clobber_abis.is_empty() {
313+
match asm_macro {
314+
AsmMacro::GlobalAsm | AsmMacro::NakedAsm => {
315+
let err = dcx.create_err(errors::AsmUnsupportedClobberAbi {
316+
spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
317+
macro_name: asm_macro.macro_name(),
318+
});
309319

310-
// Bail out now since this is likely to confuse later stages
311-
return Err(err);
312-
}
313-
if !regclass_outputs.is_empty() {
314-
dcx.emit_err(errors::AsmClobberNoReg {
315-
spans: regclass_outputs,
316-
clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
317-
});
320+
// Bail out now since this is likely to confuse later stages
321+
return Err(err);
322+
}
323+
AsmMacro::Asm => {
324+
if !regclass_outputs.is_empty() {
325+
dcx.emit_err(errors::AsmClobberNoReg {
326+
spans: regclass_outputs,
327+
clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
328+
});
329+
}
330+
}
318331
}
319332
}
320333

@@ -335,10 +348,15 @@ fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
335348
///
336349
/// This function must be called immediately after the option token is parsed.
337350
/// Otherwise, the suggestion will be incorrect.
338-
fn err_unsupported_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
351+
fn err_unsupported_option(p: &Parser<'_>, asm_macro: AsmMacro, symbol: Symbol, span: Span) {
339352
// Tool-only output
340353
let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span };
341-
p.dcx().emit_err(errors::GlobalAsmUnsupportedOption { span, symbol, full_span });
354+
p.dcx().emit_err(errors::AsmUnsupportedOption {
355+
span,
356+
symbol,
357+
full_span,
358+
macro_name: asm_macro.macro_name(),
359+
});
342360
}
343361

344362
/// Try to set the provided option in the provided `AsmArgs`.
@@ -349,12 +367,12 @@ fn err_unsupported_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
349367
fn try_set_option<'a>(
350368
p: &Parser<'a>,
351369
args: &mut AsmArgs,
352-
is_global_asm: bool,
370+
asm_macro: AsmMacro,
353371
symbol: Symbol,
354372
option: ast::InlineAsmOptions,
355373
) {
356-
if is_global_asm && !ast::InlineAsmOptions::GLOBAL_OPTIONS.contains(option) {
357-
err_unsupported_option(p, symbol, p.prev_token.span);
374+
if !asm_macro.is_supported_option(option) {
375+
err_unsupported_option(p, asm_macro, symbol, p.prev_token.span);
358376
} else if args.options.contains(option) {
359377
err_duplicate_option(p, symbol, p.prev_token.span);
360378
} else {
@@ -365,7 +383,7 @@ fn try_set_option<'a>(
365383
fn parse_options<'a>(
366384
p: &mut Parser<'a>,
367385
args: &mut AsmArgs,
368-
is_global_asm: bool,
386+
asm_macro: AsmMacro,
369387
) -> PResult<'a, ()> {
370388
let span_start = p.prev_token.span;
371389

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

387405
'blk: {
388406
for (symbol, option) in OPTIONS {
389-
let kw_matched =
390-
if !is_global_asm || ast::InlineAsmOptions::GLOBAL_OPTIONS.contains(option) {
391-
p.eat_keyword(symbol)
392-
} else {
393-
p.eat_keyword_noexpect(symbol)
394-
};
407+
let kw_matched = if asm_macro.is_supported_option(option) {
408+
p.eat_keyword(symbol)
409+
} else {
410+
p.eat_keyword_noexpect(symbol)
411+
};
395412

396413
if kw_matched {
397-
try_set_option(p, args, is_global_asm, symbol, option);
414+
try_set_option(p, args, asm_macro, symbol, option);
398415
break 'blk;
399416
}
400417
}
@@ -483,7 +500,7 @@ fn parse_reg<'a>(
483500

484501
fn expand_preparsed_asm(
485502
ecx: &mut ExtCtxt<'_>,
486-
asm_macro: ast::AsmMacro,
503+
asm_macro: AsmMacro,
487504
args: AsmArgs,
488505
) -> ExpandResult<Result<ast::InlineAsm, ErrorGuaranteed>, ()> {
489506
let mut template = vec![];
@@ -797,7 +814,7 @@ pub(super) fn expand_asm<'cx>(
797814
sp: Span,
798815
tts: TokenStream,
799816
) -> MacroExpanderResult<'cx> {
800-
ExpandResult::Ready(match parse_args(ecx, sp, tts, false) {
817+
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::Asm) {
801818
Ok(args) => {
802819
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::Asm, args) else {
803820
return ExpandResult::Retry(());
@@ -826,29 +843,20 @@ pub(super) fn expand_naked_asm<'cx>(
826843
sp: Span,
827844
tts: TokenStream,
828845
) -> MacroExpanderResult<'cx> {
829-
ExpandResult::Ready(match parse_args(ecx, sp, tts, false) {
846+
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::NakedAsm) {
830847
Ok(args) => {
831848
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::NakedAsm, args)
832849
else {
833850
return ExpandResult::Retry(());
834851
};
835852
let expr = match mac {
836-
Ok(mut inline_asm) => {
837-
// for future compatibility, we always set the NORETURN option.
838-
//
839-
// When we turn `asm!` into `naked_asm!` with this implementation, we can drop
840-
// the `options(noreturn)`, which makes the upgrade smooth when `naked_asm!`
841-
// starts disallowing the `noreturn` option in the future
842-
inline_asm.options |= ast::InlineAsmOptions::NORETURN;
843-
844-
P(ast::Expr {
845-
id: ast::DUMMY_NODE_ID,
846-
kind: ast::ExprKind::InlineAsm(P(inline_asm)),
847-
span: sp,
848-
attrs: ast::AttrVec::new(),
849-
tokens: None,
850-
})
851-
}
853+
Ok(inline_asm) => P(ast::Expr {
854+
id: ast::DUMMY_NODE_ID,
855+
kind: ast::ExprKind::InlineAsm(P(inline_asm)),
856+
span: sp,
857+
attrs: ast::AttrVec::new(),
858+
tokens: None,
859+
}),
852860
Err(guar) => DummyResult::raw_expr(sp, Some(guar)),
853861
};
854862
MacEager::expr(expr)
@@ -865,7 +873,7 @@ pub(super) fn expand_global_asm<'cx>(
865873
sp: Span,
866874
tts: TokenStream,
867875
) -> MacroExpanderResult<'cx> {
868-
ExpandResult::Ready(match parse_args(ecx, sp, tts, true) {
876+
ExpandResult::Ready(match parse_args(ecx, sp, tts, AsmMacro::GlobalAsm) {
869877
Ok(args) => {
870878
let ExpandResult::Ready(mac) = expand_preparsed_asm(ecx, AsmMacro::GlobalAsm, args)
871879
else {

0 commit comments

Comments
 (0)