diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3fdb2a2225a..d0287af9cd3 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2294,6 +2294,9 @@ pub enum InlineAsmOperand { Sym { sym: InlineAsmSym, }, + Label { + block: P, + }, } impl InlineAsmOperand { @@ -2303,7 +2306,7 @@ impl InlineAsmOperand { | Self::Out { reg, .. } | Self::InOut { reg, .. } | Self::SplitInOut { reg, .. } => Some(reg), - Self::Const { .. } | Self::Sym { .. } => None, + Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None, } } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index c42c4199973..c2eddef9fb7 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1330,6 +1330,7 @@ pub fn noop_visit_inline_asm(asm: &mut InlineAsm, vis: &mut T) { } InlineAsmOperand::Const { anon_const } => vis.visit_anon_const(anon_const), InlineAsmOperand::Sym { sym } => vis.visit_inline_asm_sym(sym), + InlineAsmOperand::Label { block } => vis.visit_block(block), } } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index ecf379cc240..7fbfe4d97c0 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -885,6 +885,7 @@ pub fn walk_inline_asm<'a, V: Visitor<'a>>(visitor: &mut V, asm: &'a InlineAsm) try_visit!(visitor.visit_anon_const(anon_const)) } InlineAsmOperand::Sym { sym } => try_visit!(visitor.visit_inline_asm_sym(sym)), + InlineAsmOperand::Label { block } => try_visit!(visitor.visit_block(block)), } } V::Result::output() diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index e87cf05713c..d91d65497e1 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -88,6 +88,9 @@ ast_lowering_invalid_abi_suggestion = did you mean ast_lowering_invalid_asm_template_modifier_const = asm template modifiers are not allowed for `const` arguments +ast_lowering_invalid_asm_template_modifier_label = + asm template modifiers are not allowed for `label` arguments + ast_lowering_invalid_asm_template_modifier_reg_class = invalid asm template modifier for this register class diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index fd717e82d26..f91183e2cbf 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -3,9 +3,9 @@ use crate::{ImplTraitContext, ImplTraitPosition, ParamMode, ResolverAstLoweringE use super::errors::{ AbiSpecifiedMultipleTimes, AttSyntaxOnlyX86, ClobberAbiNotSupported, InlineAsmUnsupportedTarget, InvalidAbiClobberAbi, InvalidAsmTemplateModifierConst, - InvalidAsmTemplateModifierRegClass, InvalidAsmTemplateModifierRegClassSub, - InvalidAsmTemplateModifierSym, InvalidRegister, InvalidRegisterClass, RegisterClassOnlyClobber, - RegisterConflict, + InvalidAsmTemplateModifierLabel, InvalidAsmTemplateModifierRegClass, + InvalidAsmTemplateModifierRegClassSub, InvalidAsmTemplateModifierSym, InvalidRegister, + InvalidRegisterClass, RegisterClassOnlyClobber, RegisterConflict, }; use super::LoweringContext; @@ -236,6 +236,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } } + InlineAsmOperand::Label { block } => { + if !self.tcx.features().asm_goto { + feature_err( + sess, + sym::asm_goto, + *op_sp, + "label operands for inline assembly are unstable", + ) + .emit(); + } + hir::InlineAsmOperand::Label { block: self.lower_block(block, false) } + } }; (op, self.lower_span(*op_sp)) }) @@ -295,6 +307,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { op_span: op_sp, }); } + hir::InlineAsmOperand::Label { .. } => { + self.dcx().emit_err(InvalidAsmTemplateModifierLabel { + placeholder_span, + op_span: op_sp, + }); + } } } } @@ -334,7 +352,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } - | hir::InlineAsmOperand::SymStatic { .. } => { + | hir::InlineAsmOperand::SymStatic { .. } + | hir::InlineAsmOperand::Label { .. } => { unreachable!("{op:?} is not a register operand"); } }; diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 834409da675..76659216d87 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -262,6 +262,16 @@ pub struct InvalidAsmTemplateModifierSym { pub op_span: Span, } +#[derive(Diagnostic, Clone, Copy)] +#[diag(ast_lowering_invalid_asm_template_modifier_label)] +pub struct InvalidAsmTemplateModifierLabel { + #[primary_span] + #[label(ast_lowering_template_modifier)] + pub placeholder_span: Span, + #[label(ast_lowering_argument)] + pub op_span: Span, +} + #[derive(Diagnostic, Clone, Copy)] #[diag(ast_lowering_register_class_only_clobber)] pub struct RegisterClassOnlyClobber { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 7ea0078ea3b..8d64ecb2430 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1331,6 +1331,10 @@ impl<'a> State<'a> { s.print_path(&sym.path, true, 0); } } + InlineAsmOperand::Label { block } => { + s.head("label"); + s.print_block(block); + } } } AsmArg::ClobberAbi(abi) => { diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index c5a73c31995..93eb3a9a43e 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -166,6 +166,9 @@ pub fn parse_asm_args<'a>( path: path.clone(), }; ast::InlineAsmOperand::Sym { sym } + } else if !is_global_asm && p.eat_keyword(sym::label) { + let block = p.parse_block()?; + ast::InlineAsmOperand::Label { block } } else if allow_templates { let template = p.parse_expr()?; // If it can't possibly expand to a string, provide diagnostics here to include other diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 295e2769109..1a4795c0213 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -76,7 +76,8 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { hir::InlineAsmOperand::In { .. } | hir::InlineAsmOperand::Out { .. } | hir::InlineAsmOperand::InOut { .. } - | hir::InlineAsmOperand::SplitInOut { .. } => { + | hir::InlineAsmOperand::SplitInOut { .. } + | hir::InlineAsmOperand::Label { .. } => { span_bug!(*op_sp, "invalid operand type for global_asm!") } }) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 93c183a65ef..74ec6a9713f 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -345,6 +345,8 @@ declare_features! ( (unstable, asm_const, "1.58.0", Some(93332)), /// Enables experimental inline assembly support for additional architectures. (unstable, asm_experimental_arch, "1.58.0", Some(93335)), + /// Allows using `label` operands in inline assembly. + (unstable, asm_goto, "CURRENT_RUSTC_VERSION", Some(119364)), /// Allows the `may_unwind` option in inline assembly. (unstable, asm_unwind, "1.58.0", Some(93334)), /// Allows users to enforce equality of associated constants `TraitImpl`. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 78e7c636a3e..2b8cd47bd1f 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2645,6 +2645,9 @@ pub enum InlineAsmOperand<'hir> { path: QPath<'hir>, def_id: DefId, }, + Label { + block: &'hir Block<'hir>, + }, } impl<'hir> InlineAsmOperand<'hir> { @@ -2654,7 +2657,10 @@ impl<'hir> InlineAsmOperand<'hir> { | Self::Out { reg, .. } | Self::InOut { reg, .. } | Self::SplitInOut { reg, .. } => Some(reg), - Self::Const { .. } | Self::SymFn { .. } | Self::SymStatic { .. } => None, + Self::Const { .. } + | Self::SymFn { .. } + | Self::SymStatic { .. } + | Self::Label { .. } => None, } } @@ -2675,6 +2681,12 @@ pub struct InlineAsm<'hir> { pub line_spans: &'hir [Span], } +impl InlineAsm<'_> { + pub fn contains_label(&self) -> bool { + self.operands.iter().any(|x| matches!(x.0, InlineAsmOperand::Label { .. })) + } +} + /// Represents a parameter in a function header. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Param<'hir> { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 1c38a45d3a3..278a0a6e6b9 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1290,6 +1290,7 @@ pub fn walk_inline_asm<'v, V: Visitor<'v>>( InlineAsmOperand::SymStatic { path, .. } => { try_visit!(visitor.visit_qpath(path, id, *op_sp)); } + InlineAsmOperand::Label { block } => try_visit!(visitor.visit_block(block)), } } V::Result::output() diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index d03b02f028d..9de660407d7 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -470,6 +470,8 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } }; } + // No special checking is needed for labels. + hir::InlineAsmOperand::Label { .. } => {} } } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 8f8f747339b..b5bb063c5ed 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1265,6 +1265,10 @@ impl<'a> State<'a> { s.space(); s.print_qpath(path, true); } + hir::InlineAsmOperand::Label { block } => { + s.head("label"); + s.print_block(block); + } }, AsmArg::Options(opts) => { s.word("options"); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 81440b0562e..614761c03bd 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3264,6 +3264,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // be well-formed. hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } => {} hir::InlineAsmOperand::SymStatic { .. } => {} + hir::InlineAsmOperand::Label { block } => { + self.check_block_no_value(block); + } } } if asm.options.contains(ast::InlineAsmOptions::NORETURN) { diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 04fb7bcf4f3..ba0383d19b9 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -293,6 +293,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { | hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } | hir::InlineAsmOperand::SymStatic { .. } => {} + hir::InlineAsmOperand::Label { block } => { + self.walk_block(block); + } } } } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 62762168cf4..0e64ed89753 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -656,6 +656,9 @@ impl<'tcx> Cx<'tcx> { hir::InlineAsmOperand::SymStatic { path: _, def_id } => { InlineAsmOperand::SymStatic { def_id } } + hir::InlineAsmOperand::Label { .. } => { + todo!() + } }) .collect(), options: asm.options, diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 5593de60784..3ab6c4b9b6a 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -446,7 +446,8 @@ fn collect_items_rec<'tcx>( hir::InlineAsmOperand::In { .. } | hir::InlineAsmOperand::Out { .. } | hir::InlineAsmOperand::InOut { .. } - | hir::InlineAsmOperand::SplitInOut { .. } => { + | hir::InlineAsmOperand::SplitInOut { .. } + | hir::InlineAsmOperand::Label { .. } => { span_bug!(*op_sp, "invalid operand type for global_asm!") } } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 1bae5b32240..9c0020f02d9 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -434,7 +434,7 @@ impl<'a> Parser<'a> { } /// Parses a block. No inner attributes are allowed. - pub(super) fn parse_block(&mut self) -> PResult<'a, P> { + pub fn parse_block(&mut self) -> PResult<'a, P> { let (attrs, block) = self.parse_inner_attrs_and_block()?; if let [.., last] = &*attrs { self.error_on_forbidden_inner_attr( diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 487407014d1..e5033e1f51f 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -86,7 +86,6 @@ use crate::errors; use self::LiveNodeKind::*; use self::VarKind::*; -use rustc_ast::InlineAsmOptions; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_hir::def::*; @@ -416,6 +415,12 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id)); } + // Inline assembly may contain labels. + hir::ExprKind::InlineAsm(asm) if asm.contains_label() => { + self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id)); + intravisit::walk_expr(self, expr); + } + // otherwise, live nodes are not required: hir::ExprKind::Index(..) | hir::ExprKind::Field(..) @@ -1045,20 +1050,53 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { | hir::ExprKind::Repeat(ref e, _) => self.propagate_through_expr(e, succ), hir::ExprKind::InlineAsm(asm) => { - // Handle non-returning asm - let mut succ = if asm.options.contains(InlineAsmOptions::NORETURN) { - self.exit_ln - } else { - succ - }; + // + // (inputs) + // | + // v + // (outputs) + // / \ + // | | + // v v + // (labels)(fallthrough) + // | | + // v v + // ( succ / exit_ln ) - // Do a first pass for writing outputs only + // Handle non-returning asm + let mut succ = + if self.typeck_results.expr_ty(expr).is_never() { self.exit_ln } else { succ }; + + // Do a first pass for labels only + if asm.contains_label() { + let ln = self.live_node(expr.hir_id, expr.span); + self.init_from_succ(ln, succ); + for (op, _op_sp) in asm.operands.iter().rev() { + match op { + hir::InlineAsmOperand::Label { block } => { + let label_ln = self.propagate_through_block(block, succ); + self.merge_from_succ(ln, label_ln); + } + hir::InlineAsmOperand::In { .. } + | hir::InlineAsmOperand::Out { .. } + | hir::InlineAsmOperand::InOut { .. } + | hir::InlineAsmOperand::SplitInOut { .. } + | hir::InlineAsmOperand::Const { .. } + | hir::InlineAsmOperand::SymFn { .. } + | hir::InlineAsmOperand::SymStatic { .. } => {} + } + } + succ = ln; + } + + // Do a second pass for writing outputs only for (op, _op_sp) in asm.operands.iter().rev() { match op { hir::InlineAsmOperand::In { .. } | hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } - | hir::InlineAsmOperand::SymStatic { .. } => {} + | hir::InlineAsmOperand::SymStatic { .. } + | hir::InlineAsmOperand::Label { .. } => {} hir::InlineAsmOperand::Out { expr, .. } => { if let Some(expr) = expr { succ = self.write_place(expr, succ, ACC_WRITE); @@ -1075,7 +1113,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } } - // Then do a second pass for inputs + // Then do a third pass for inputs for (op, _op_sp) in asm.operands.iter().rev() { match op { hir::InlineAsmOperand::In { expr, .. } => { @@ -1097,7 +1135,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } - | hir::InlineAsmOperand::SymStatic { .. } => {} + | hir::InlineAsmOperand::SymStatic { .. } + | hir::InlineAsmOperand::Label { .. } => {} } } succ diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 0455d6d4acb..27c9c1306e6 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -237,7 +237,8 @@ impl<'tcx> CheckInlineAssembly<'tcx> { InlineAsmOperand::In { .. } | InlineAsmOperand::Out { .. } | InlineAsmOperand::InOut { .. } - | InlineAsmOperand::SplitInOut { .. } => Some(op_sp), + | InlineAsmOperand::SplitInOut { .. } + | InlineAsmOperand::Label { .. } => Some(op_sp), }) .collect(); if !unsupported_operands.is_empty() { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2f4da29133f..8b8d99f8816 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1246,6 +1246,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, self.resolve_anon_const(anon_const, AnonConstKind::InlineConst); } InlineAsmOperand::Sym { sym } => self.visit_inline_asm_sym(sym), + InlineAsmOperand::Label { block } => self.visit_block(block), } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 46472a131ff..0cfff1623db 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -399,6 +399,7 @@ symbols! { asm, asm_const, asm_experimental_arch, + asm_goto, asm_sym, asm_unwind, assert, diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index 245a903f998..65d922f03df 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -255,6 +255,9 @@ fn never_loop_expr<'tcx>( InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => { NeverLoopResult::Normal }, + InlineAsmOperand::Label { block } => { + never_loop_block(cx, block, local_labels, main_loop_id) + } })), ExprKind::OffsetOf(_, _) | ExprKind::Yield(_, _) diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index d50332e82da..643852c1c54 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -835,6 +835,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_body(anon_const.body); }, InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path), + InlineAsmOperand::Label { block } => self.hash_block(block), } } },