Skip to content

Commit

Permalink
Rollup merge of rust-lang#119365 - nbdd0121:asm-goto, r=Amanieu
Browse files Browse the repository at this point in the history
Add asm goto support to `asm!`

Tracking issue: rust-lang#119364

This PR implements asm-goto support, using the syntax described in "future possibilities" section of [RFC2873](https://rust-lang.github.io/rfcs/2873-inline-asm.html#asm-goto).

Currently I have only implemented the `label` part, not the `fallthrough` part (i.e. fallthrough is implicit). This doesn't reduce the expressive though, since you can use label-break to get arbitrary control flow or simply set a value and rely on jump threading optimisation to get the desired control flow. I can add that later if deemed necessary.

r? `@Amanieu`
cc `@ojeda`
  • Loading branch information
matthiaskrgr committed Mar 8, 2024
2 parents 850b1d2 + 0ee0f29 commit c0cb6ad
Show file tree
Hide file tree
Showing 76 changed files with 857 additions and 200 deletions.
5 changes: 4 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Expand Up @@ -2302,6 +2302,9 @@ pub enum InlineAsmOperand {
Sym {
sym: InlineAsmSym,
},
Label {
block: P<Block>,
},
}

impl InlineAsmOperand {
Expand All @@ -2311,7 +2314,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,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/mut_visit.rs
Expand Up @@ -1331,6 +1331,7 @@ pub fn noop_visit_inline_asm<T: MutVisitor>(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),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/visit.rs
Expand Up @@ -823,6 +823,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()
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_ast_lowering/messages.ftl
Expand Up @@ -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
Expand Down
27 changes: 23 additions & 4 deletions compiler/rustc_ast_lowering/src/asm.rs
Expand Up @@ -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;

Expand Down Expand Up @@ -237,6 +237,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))
})
Expand Down Expand Up @@ -296,6 +308,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,
});
}
}
}
}
Expand Down Expand Up @@ -335,7 +353,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");
}
};
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_ast_lowering/src/errors.rs
Expand Up @@ -261,6 +261,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 {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Expand Up @@ -1452,6 +1452,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) => {
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_borrowck/src/lib.rs
Expand Up @@ -723,7 +723,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
operands,
options: _,
line_spans: _,
destination: _,
targets: _,
unwind: _,
} => {
for op in operands {
Expand All @@ -749,7 +749,8 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
}
InlineAsmOperand::Const { value: _ }
| InlineAsmOperand::SymFn { value: _ }
| InlineAsmOperand::SymStatic { def_id: _ } => {}
| InlineAsmOperand::SymStatic { def_id: _ }
| InlineAsmOperand::Label { target_index: _ } => {}
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_borrowck/src/polonius/loan_invalidations.rs
Expand Up @@ -161,7 +161,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> {
operands,
options: _,
line_spans: _,
destination: _,
targets: _,
unwind: _,
} => {
for op in operands {
Expand All @@ -182,7 +182,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> {
}
InlineAsmOperand::Const { value: _ }
| InlineAsmOperand::SymFn { value: _ }
| InlineAsmOperand::SymStatic { def_id: _ } => {}
| InlineAsmOperand::SymStatic { def_id: _ }
| InlineAsmOperand::Label { target_index: _ } => {}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Expand Up @@ -1770,8 +1770,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.assert_iscleanup(body, block_data, real_target, is_cleanup);
self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);
}
TerminatorKind::InlineAsm { destination, unwind, .. } => {
if let Some(target) = destination {
TerminatorKind::InlineAsm { ref targets, unwind, .. } => {
for &target in targets {
self.assert_iscleanup(body, block_data, target, is_cleanup);
}
self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_builtin_macros/messages.ftl
Expand Up @@ -19,6 +19,8 @@ builtin_macros_asm_expected_other = expected operand, {$is_global_asm ->
builtin_macros_asm_explicit_register_name = explicit register arguments cannot have names
builtin_macros_asm_mayunwind = asm labels are not allowed with the `may_unwind` option
builtin_macros_asm_modifier_invalid = asm template modifier must be a single character
builtin_macros_asm_mutually_exclusive = the `{$opt1}` and `{$opt2}` options are mutually exclusive
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_builtin_macros/src/asm.rs
Expand Up @@ -164,6 +164,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
Expand Down Expand Up @@ -240,6 +243,7 @@ pub fn parse_asm_args<'a>(
let mut have_real_output = false;
let mut outputs_sp = vec![];
let mut regclass_outputs = vec![];
let mut labels_sp = vec![];
for (op, op_sp) in &args.operands {
match op {
ast::InlineAsmOperand::Out { reg, expr, .. }
Expand All @@ -257,6 +261,9 @@ pub fn parse_asm_args<'a>(
regclass_outputs.push(*op_sp);
}
}
ast::InlineAsmOperand::Label { .. } => {
labels_sp.push(*op_sp);
}
_ => {}
}
}
Expand All @@ -268,6 +275,9 @@ pub fn parse_asm_args<'a>(
// Bail out now since this is likely to confuse MIR
return Err(err);
}
if args.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() {
dcx.emit_err(errors::AsmMayUnwind { labels_sp });
}

if args.clobber_abis.len() > 0 {
if is_global_asm {
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_builtin_macros/src/errors.rs
Expand Up @@ -766,6 +766,13 @@ pub(crate) struct AsmNoReturn {
pub(crate) outputs_sp: Vec<Span>,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_asm_mayunwind)]
pub(crate) struct AsmMayUnwind {
#[primary_span]
pub(crate) labels_sp: Vec<Span>,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_global_asm_clobber_abi)]
pub(crate) struct GlobalAsmClobberAbi {
Expand Down
16 changes: 14 additions & 2 deletions compiler/rustc_codegen_cranelift/src/base.rs
Expand Up @@ -445,7 +445,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
template,
operands,
options,
destination,
targets,
line_spans: _,
unwind: _,
} => {
Expand All @@ -456,13 +456,25 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
);
}

let have_labels = if options.contains(InlineAsmOptions::NORETURN) {
!targets.is_empty()
} else {
targets.len() > 1
};
if have_labels {
fx.tcx.dcx().span_fatal(
source_info.span,
"cranelift doesn't support labels in inline assembly.",
);
}

crate::inline_asm::codegen_inline_asm_terminator(
fx,
source_info.span,
template,
operands,
*options,
*destination,
targets.get(0).copied(),
);
}
TerminatorKind::UnwindTerminate(reason) => {
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_codegen_cranelift/src/global_asm.rs
Expand Up @@ -78,7 +78,8 @@ pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String,
InlineAsmOperand::In { .. }
| InlineAsmOperand::Out { .. }
| InlineAsmOperand::InOut { .. }
| InlineAsmOperand::SplitInOut { .. } => {
| InlineAsmOperand::SplitInOut { .. }
| InlineAsmOperand::Label { .. } => {
span_bug!(op_sp, "invalid operand type for global_asm!")
}
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_cranelift/src/inline_asm.rs
Expand Up @@ -129,6 +129,9 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>(
let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx);
CInlineAsmOperand::Symbol { symbol: fx.tcx.symbol_name(instance).name.to_owned() }
}
InlineAsmOperand::Label { .. } => {
span_bug!(span, "asm! label operands are not yet supported");
}
})
.collect::<Vec<_>>();

Expand Down
31 changes: 28 additions & 3 deletions compiler/rustc_codegen_gcc/src/asm.rs
Expand Up @@ -107,7 +107,7 @@ enum ConstraintOrRegister {


impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, span: &[Span], instance: Instance<'_>, _dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>) {
fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, span: &[Span], instance: Instance<'_>, dest: Option<Self::BasicBlock>, _catch_funclet: Option<(Self::BasicBlock, Option<&Self::Funclet>)>) {
if options.contains(InlineAsmOptions::MAY_UNWIND) {
self.sess().dcx()
.create_err(UnwindingInlineAsm { span: span[0] })
Expand All @@ -126,6 +126,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
// added to `outputs.len()`
let mut inputs = vec![];

// GCC index of a label equals its position in the array added to
// `outputs.len() + inputs.len()`.
let mut labels = vec![];

// Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _`
let mut clobbers = vec![];

Expand Down Expand Up @@ -269,6 +273,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
// some targets to add a leading underscore (Mach-O).
constants_len += self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name.len();
}

InlineAsmOperandRef::Label { label } => {
labels.push(label);
}
}
}

Expand Down Expand Up @@ -368,6 +376,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
InlineAsmOperandRef::Const { .. } => {
// processed in the previous pass
}

InlineAsmOperandRef::Label { .. } => {
// processed in the previous pass
}
}
}

Expand Down Expand Up @@ -454,6 +466,14 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
InlineAsmOperandRef::Const { ref string } => {
template_str.push_str(string);
}

InlineAsmOperandRef::Label { label } => {
let label_gcc_index = labels.iter()
.position(|&l| l == label)
.expect("wrong rust index");
let gcc_index = label_gcc_index + outputs.len() + inputs.len();
push_to_template(Some('l'), gcc_index);
}
}
}
}
Expand All @@ -466,7 +486,12 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
// 4. Generate Extended Asm block

let block = self.llbb();
let extended_asm = block.add_extended_asm(None, &template_str);
let extended_asm = if let Some(dest) = dest {
assert!(!labels.is_empty());
block.end_with_extended_asm_goto(None, &template_str, &labels, Some(dest))
} else {
block.add_extended_asm(None, &template_str)
};

for op in &outputs {
extended_asm.add_output_operand(None, &op.to_constraint(), op.tmp_var);
Expand Down Expand Up @@ -494,7 +519,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
if !options.contains(InlineAsmOptions::NOSTACK) {
// TODO(@Commeownist): figure out how to align stack
}
if options.contains(InlineAsmOptions::NORETURN) {
if dest.is_none() && options.contains(InlineAsmOptions::NORETURN) {
let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable");
let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) };
self.call(self.type_void(), None, None, builtin_unreachable, &[], None);
Expand Down

0 comments on commit c0cb6ad

Please sign in to comment.