Rollup merge of #119365 - nbdd0121:asm-goto, r=Amanieu

Add asm goto support to `asm!`

Tracking issue: #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``
This commit is contained in:
Matthias Krüger 2024-03-08 08:19:17 +01:00 committed by GitHub
commit 568d3949c1

View File

@ -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] })
@ -126,6 +126,10 @@ fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_opera
// 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![];
@ -269,6 +273,10 @@ fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_opera
// 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);
}
}
}
@ -368,6 +376,10 @@ fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_opera
InlineAsmOperandRef::Const { .. } => {
// processed in the previous pass
}
InlineAsmOperandRef::Label { .. } => {
// processed in the previous pass
}
}
}
@ -454,6 +466,14 @@ fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_opera
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);
}
}
}
}
@ -466,7 +486,12 @@ fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_opera
// 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);
@ -494,7 +519,7 @@ fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_opera
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);