various fixes for naked_asm! implementation

- fix for divergence
- fix error message
- fix another cranelift test
- fix some cranelift things
- don't set the NORETURN option for naked asm
- fix use of naked_asm! in doc comment
- fix use of naked_asm! in run-make test
- use `span_bug` in unreachable branch
This commit is contained in:
Folkert de Vries 2024-09-05 19:45:40 +02:00
parent 10fa482906
commit 5fc60d1e52
29 changed files with 116 additions and 73 deletions

View File

@ -2435,7 +2435,7 @@ pub enum AsmMacro {
}
impl AsmMacro {
pub const fn macro_name(&self) -> &'static str {
pub const fn macro_name(self) -> &'static str {
match self {
AsmMacro::Asm => "asm",
AsmMacro::GlobalAsm => "global_asm",
@ -2443,13 +2443,21 @@ pub const fn macro_name(&self) -> &'static str {
}
}
pub const fn is_supported_option(&self, option: InlineAsmOptions) -> bool {
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.

View File

@ -742,6 +742,7 @@ fn visit_terminator_before_primary_effect(
}
TerminatorKind::InlineAsm {
asm_macro: _,
template: _,
operands,
options: _,

View File

@ -169,6 +169,7 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
}
}
TerminatorKind::InlineAsm {
asm_macro: _,
template: _,
operands,
options: _,

View File

@ -850,22 +850,13 @@ pub(super) fn expand_naked_asm<'cx>(
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)

View File

@ -726,6 +726,12 @@ fn index(&self, index: usize) -> &Self::Output {
/* compiler built-in */
}
#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro naked_asm() {
/* compiler built-in */
}
pub static A_STATIC: u8 = 42;
#[lang = "panic_location"]

View File

@ -390,7 +390,7 @@ fn stack_val_align() {
#[naked]
extern "C" fn naked_test() {
unsafe {
asm!("ret", options(noreturn));
naked_asm!("ret");
}
}

View File

@ -8,6 +8,7 @@
use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization;
use rustc_index::IndexVec;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::InlineAsmMacro;
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::layout::FnAbiOf;
@ -57,6 +58,7 @@ pub(crate) fn codegen_fn<'tcx>(
match &mir.basic_blocks[START_BLOCK].terminator().kind {
TerminatorKind::InlineAsm {
asm_macro: InlineAsmMacro::NakedAsm,
template,
operands,
options,
@ -498,6 +500,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
"tail calls are not yet supported in `rustc_codegen_cranelift` backend"
),
TerminatorKind::InlineAsm {
asm_macro: _,
template,
operands,
options,

View File

@ -3,7 +3,9 @@
use rustc_ast as ast;
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_hir::lang_items::LangItem;
use rustc_middle::mir::{self, AssertKind, BasicBlock, SwitchTargets, UnwindTerminateReason};
use rustc_middle::mir::{
self, AssertKind, BasicBlock, InlineAsmMacro, SwitchTargets, UnwindTerminateReason,
};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement};
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
use rustc_middle::ty::{self, Instance, Ty};
@ -1133,6 +1135,7 @@ fn codegen_asm_terminator(
&mut self,
helper: TerminatorCodegenHelper<'tcx>,
bx: &mut Bx,
asm_macro: InlineAsmMacro,
terminator: &mir::Terminator<'tcx>,
template: &[ast::InlineAsmTemplatePiece],
operands: &[mir::InlineAsmOperand<'tcx>],
@ -1203,11 +1206,7 @@ fn codegen_asm_terminator(
&operands,
options,
line_spans,
if options.contains(InlineAsmOptions::NORETURN) {
None
} else {
targets.get(0).copied()
},
if asm_macro.diverges(options) { None } else { targets.get(0).copied() },
unwind,
instance,
mergeable_succ,
@ -1381,6 +1380,7 @@ fn codegen_terminator(
}
mir::TerminatorKind::InlineAsm {
asm_macro,
template,
ref operands,
options,
@ -1390,6 +1390,7 @@ fn codegen_terminator(
} => self.codegen_asm_terminator(
helper,
bx,
asm_macro,
terminator,
template,
operands,

View File

@ -395,7 +395,7 @@ fn adjust_alloc_root_pointer(
///
/// This should take care of jumping to the next block (one of `targets`) when asm goto
/// is triggered, `targets[0]` when the assembly falls through, or diverge in case of
/// `InlineAsmOptions::NORETURN` being set.
/// naked_asm! or `InlineAsmOptions::NORETURN` being set.
fn eval_inline_asm(
_ecx: &mut InterpCx<'tcx, Self>,
_template: &'tcx [InlineAsmTemplatePiece],

View File

@ -3507,11 +3507,7 @@ fn check_expr_asm_operand(&self, expr: &'tcx hir::Expr<'tcx>, is_input: bool) {
}
fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> {
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,
};
let mut diverge = asm.asm_macro.diverges(asm.options);
for (op, _op_sp) in asm.operands {
match op {

View File

@ -2926,16 +2926,16 @@
/// ```rust
/// #![feature(asm_experimental_arch, naked_functions)]
///
/// use std::arch::asm;
/// use std::arch::naked_asm;
///
/// #[naked]
/// pub fn default_abi() -> u32 {
/// unsafe { asm!("", options(noreturn)); }
/// unsafe { naked_asm!(""); }
/// }
///
/// #[naked]
/// pub extern "Rust" fn rust_abi() -> u32 {
/// unsafe { asm!("", options(noreturn)); }
/// unsafe { naked_asm!(""); }
/// }
/// ```
///

View File

@ -4,7 +4,7 @@
use std::io::{self, Write as _};
use std::path::{Path, PathBuf};
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_ast::InlineAsmTemplatePiece;
use rustc_middle::mir::interpret::{
AllocBytes, AllocId, Allocation, GlobalAlloc, Pointer, Provenance, alloc_range,
read_target_uint,
@ -1024,9 +1024,9 @@ pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
vec!["real".into(), "unwind".into()]
}
FalseUnwind { unwind: _, .. } => vec!["real".into()],
InlineAsm { options, ref targets, unwind, .. } => {
InlineAsm { asm_macro, options, ref targets, unwind, .. } => {
let mut vec = Vec::with_capacity(targets.len() + 1);
if !options.contains(InlineAsmOptions::NORETURN) {
if !asm_macro.diverges(options) {
vec.push("return".into());
}
vec.resize(targets.len(), "label".into());

View File

@ -605,6 +605,25 @@ pub fn from_hir_call(self) -> bool {
}
}
#[derive(Clone, Copy, Debug, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
#[derive(TypeFoldable, TypeVisitable)]
/// The macro that an inline assembly block was created by
pub enum InlineAsmMacro {
/// The `asm!` macro
Asm,
/// The `naked_asm!` macro
NakedAsm,
}
impl InlineAsmMacro {
pub const fn diverges(self, options: InlineAsmOptions) -> bool {
match self {
InlineAsmMacro::Asm => options.contains(InlineAsmOptions::NORETURN),
InlineAsmMacro::NakedAsm => true,
}
}
}
///////////////////////////////////////////////////////////////////////////
// Terminators
@ -859,6 +878,9 @@ pub enum TerminatorKind<'tcx> {
/// Block ends with an inline assembly block. This is a terminator since
/// inline assembly is allowed to diverge.
InlineAsm {
/// Macro used to create this inline asm: one of `asm!` or `naked_asm!`
asm_macro: InlineAsmMacro,
/// The template for the inline assembly, with placeholders.
template: &'tcx [InlineAsmTemplatePiece],
@ -874,7 +896,7 @@ pub enum TerminatorKind<'tcx> {
/// Valid targets for the inline assembly.
/// The first element is the fallthrough destination, unless
/// InlineAsmOptions::NORETURN is set.
/// asm_macro == InlineAsmMacro::NakedAsm or InlineAsmOptions::NORETURN is set.
targets: Box<[BasicBlock]>,
/// Action to be taken if the inline assembly unwinds. This is present

View File

@ -666,6 +666,7 @@ pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
},
InlineAsm {
asm_macro: _,
template: _,
ref operands,
options: _,

View File

@ -576,6 +576,7 @@ fn super_terminator(&mut self,
}
TerminatorKind::InlineAsm {
asm_macro: _,
template: _,
operands,
options: _,

View File

@ -12,7 +12,7 @@
use std::fmt;
use std::ops::Index;
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_ast::{AsmMacro, InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd};
@ -173,6 +173,7 @@ pub struct ClosureExpr<'tcx> {
#[derive(Clone, Debug, HashStable)]
pub struct InlineAsmExpr<'tcx> {
pub asm_macro: AsmMacro,
pub template: &'tcx [InlineAsmTemplatePiece],
pub operands: Box<[InlineAsmOperand<'tcx>]>,
pub options: InlineAsmOptions,

View File

@ -148,7 +148,13 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
NamedConst { def_id: _, args: _, user_ty: _ } => {}
ConstParam { param: _, def_id: _ } => {}
StaticRef { alloc_id: _, ty: _, def_id: _ } => {}
InlineAsm(box InlineAsmExpr { ref operands, template: _, options: _, line_spans: _ }) => {
InlineAsm(box InlineAsmExpr {
asm_macro: _,
ref operands,
template: _,
options: _,
line_spans: _,
}) => {
for op in &**operands {
use InlineAsmOperand::*;
match op {

View File

@ -2,7 +2,7 @@
use std::iter;
use rustc_ast::InlineAsmOptions;
use rustc_ast::{AsmMacro, InlineAsmOptions};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
@ -384,6 +384,7 @@ pub(crate) fn expr_into_dest(
block.unit()
}
ExprKind::InlineAsm(box InlineAsmExpr {
asm_macro,
template,
ref operands,
options,
@ -392,11 +393,8 @@ pub(crate) fn expr_into_dest(
use rustc_middle::{mir, thir};
let destination_block = this.cfg.start_new_block();
let mut targets = if options.contains(InlineAsmOptions::NORETURN) {
vec![]
} else {
vec![destination_block]
};
let mut targets =
if asm_macro.diverges(options) { vec![] } else { vec![destination_block] };
let operands = operands
.into_iter()
@ -474,7 +472,16 @@ pub(crate) fn expr_into_dest(
this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
}
let asm_macro = match asm_macro {
AsmMacro::Asm => InlineAsmMacro::Asm,
AsmMacro::GlobalAsm => {
span_bug!(expr_span, "unexpected global_asm! in inline asm")
}
AsmMacro::NakedAsm => InlineAsmMacro::NakedAsm,
};
this.cfg.terminate(block, source_info, TerminatorKind::InlineAsm {
asm_macro,
template,
operands,
options,

View File

@ -672,6 +672,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
}
hir::ExprKind::InlineAsm(asm) => ExprKind::InlineAsm(Box::new(InlineAsmExpr {
asm_macro: asm.asm_macro,
template: asm.template,
operands: asm
.operands

View File

@ -818,10 +818,12 @@ fn print_closure_expr(&mut self, expr: &ClosureExpr<'tcx>, depth_lvl: usize) {
}
fn print_inline_asm_expr(&mut self, expr: &InlineAsmExpr<'tcx>, depth_lvl: usize) {
let InlineAsmExpr { template, operands, options, line_spans } = expr;
let InlineAsmExpr { asm_macro, template, operands, options, line_spans } = expr;
print_indented!(self, "InlineAsmExpr {", depth_lvl);
print_indented!(self, format!("asm_macro: {:?}", asm_macro), depth_lvl + 1);
print_indented!(self, "template: [", depth_lvl + 1);
for template_piece in template.iter() {
print_indented!(self, format!("{:?}", template_piece), depth_lvl + 2);

View File

@ -481,6 +481,7 @@ fn gather_terminator(&mut self, term: &Terminator<'tcx>) {
}
}
TerminatorKind::InlineAsm {
asm_macro: _,
template: _,
ref operands,
options: _,

View File

@ -499,7 +499,7 @@ passes_naked_functions_incompatible_attribute =
passes_naked_functions_must_naked_asm =
the `asm!` macro is not allowed in naked functions
.suggestion = consider using the `naked_asm!` macro instead
.label = consider using the `naked_asm!` macro instead
passes_no_link =
attribute should be applied to an `extern crate` item

View File

@ -1190,9 +1190,8 @@ fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
#[diag(passes_naked_functions_must_naked_asm, code = E0787)]
pub(crate) struct NakedFunctionsMustNakedAsm {
#[primary_span]
#[label]
pub span: Span,
#[suggestion(code = "naked_asm!", applicability = "machine-applicable")]
pub macro_span: Span,
}
#[derive(Diagnostic)]

View File

@ -7,10 +7,11 @@
use rustc_hir::{ExprKind, HirIdSet, StmtKind};
use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_middle::query::Providers;
use rustc_middle::span_bug;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI;
use rustc_span::Span;
use rustc_span::symbol::sym;
use rustc_span::{BytePos, Span};
use rustc_target::spec::abi::Abi;
use crate::errors::{
@ -137,10 +138,7 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<
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 });
tcx.dcx().emit_err(NakedFunctionsMustNakedAsm { span });
}
ItemKind::NonAsm => {
must_show_error = true;
@ -210,19 +208,17 @@ fn check_expr<'tcx>(&mut self, expr: &'tcx hir::Expr<'tcx>, span: Span) {
self.items.push((ItemKind::NonAsm, span));
}
ExprKind::InlineAsm(asm) => {
match asm.asm_macro {
rustc_ast::AsmMacro::Asm => {
self.items.push((ItemKind::InlineAsm, span));
}
rustc_ast::AsmMacro::NakedAsm => {
self.items.push((ItemKind::NakedAsm, span));
}
rustc_ast::AsmMacro::GlobalAsm => {
// not allowed in this position
}
ExprKind::InlineAsm(asm) => match asm.asm_macro {
rustc_ast::AsmMacro::Asm => {
self.items.push((ItemKind::InlineAsm, span));
}
}
rustc_ast::AsmMacro::NakedAsm => {
self.items.push((ItemKind::NakedAsm, span));
}
rustc_ast::AsmMacro::GlobalAsm => {
span_bug!(span, "`global_asm!` is not allowed in this position")
}
},
ExprKind::DropTemps(..) | ExprKind::Block(..) => {
hir::intravisit::walk_expr(self, expr);

View File

@ -655,6 +655,7 @@ fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
}
}
mir::TerminatorKind::InlineAsm {
asm_macro: _,
template,
operands,
options,

View File

@ -1,5 +1,5 @@
use rustc_ast::ast;
use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs};
use rustc_builtin_macros::asm::{AsmArgs, parse_asm_args};
use crate::rewrite::RewriteContext;

View File

@ -14,7 +14,7 @@
#[no_mangle]
pub extern "x86-interrupt" fn page_fault_handler(_: u64, _: u64) {
unsafe {
core::arch::asm!("ud2", options(noreturn));
core::arch::naked_asm!("ud2");
}
}

View File

@ -72,7 +72,7 @@ extern "C" fn vanilla_weak_linkage() -> u32 {
#[cfg(not(windows))]
#[linkage = "weak"]
extern "C" fn naked_weak_linkage() -> u32 {
unsafe { naked_asm!("mov rax, 42", "ret", options(noreturn)) }
unsafe { naked_asm!("mov rax, 42", "ret") }
}
// functions that are declared in an `extern "C"` block are currently not exported

View File

@ -74,9 +74,7 @@ error[E0787]: the `asm!` macro is not allowed in naked functions
--> $DIR/naked-functions.rs:13:5
|
LL | asm!("", options(raw));
| ----^^^^^^^^^^^^^^^^^^
| |
| help: consider using the `naked_asm!` macro instead: `naked_asm!`
| ^^^^^^^^^^^^^^^^^^^^^^ consider using the `naked_asm!` macro instead
error: patterns not allowed in naked function parameters
--> $DIR/naked-functions.rs:25:5