diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 8801211d51bc3..b62d4a2c2ca9a 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -200,6 +200,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { // Build the template string let mut template_str = String::new(); + let mut labels = vec![]; for piece in template { match *piece { InlineAsmTemplatePiece::String(ref s) => { @@ -212,6 +213,10 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { } } } else { + if let Some(label) = get_llvm_label_from_str(s) { + labels.push(label.to_owned()); + } + template_str.push_str(s) } } @@ -244,6 +249,30 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { } } + // Labels should be made local to prevent issues with inlined `asm!` and duplicate labels + // Fixes https://github.com/rust-lang/rust/issues/74262 + for label in labels.iter() { + let ranges: Vec<_> = template_str + .match_indices(label) + .filter_map(|(idx, _)| { + let label_end_idx = idx + label.len(); + let next_char = template_str[label_end_idx..].chars().nth(0); + if next_char.is_none() || !llvm_ident_continuation(next_char.unwrap()) { + Some(idx..label_end_idx) + } else { + None + } + }) + .collect(); + + // We add a constant length of 18 to the string every time + // from ${:private} and ${:uid} + for (i, range) in ranges.iter().enumerate() { + let real_range = range.start + (i * 18)..range.end + (i * 18); + template_str.replace_range(real_range, &format!("${{:private}}{}${{:uid}}", label)) + } + } + if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) { match asm_arch { InlineAsmArch::AArch64 | InlineAsmArch::Arm => { @@ -874,3 +903,35 @@ fn llvm_fixup_output_type( _ => layout.llvm_type(cx), } } + +// Valid llvm symbols: https://llvm.org/docs/AMDGPUOperandSyntax.html#symbols +// First char is [a-zA-Z_.] +fn llvm_ident_start(c: char) -> bool { + ('a'..='z').contains(&c) || ('A'..='Z').contains(&c) || '_' == c || '.' == c +} + +// All subsequent chars are [a-zA-Z0-9_$.@] +fn llvm_ident_continuation(c: char) -> bool { + ('a'..='z').contains(&c) + || ('A'..='Z').contains(&c) + || ('0'..='9').contains(&c) + || '_' == c + || '$' == c + || '.' == c + || '@' == c +} + +/// Gets a LLVM label from a &str if it exists +fn get_llvm_label_from_str(s: &str) -> Option<&str> { + if let Some(colon_idx) = s.find(':') { + let substr = &s[..colon_idx]; + let mut chars = substr.chars(); + if let Some(c) = chars.next() { + if llvm_ident_start(c) && chars.all(llvm_ident_continuation) { + return Some(substr); + } + } + } + + None +} diff --git a/src/test/ui/asm/duplicate-labels-inline.rs b/src/test/ui/asm/duplicate-labels-inline.rs new file mode 100644 index 0000000000000..85f156b873a81 --- /dev/null +++ b/src/test/ui/asm/duplicate-labels-inline.rs @@ -0,0 +1,16 @@ +// compile-flags: -g +// build-pass + +#![feature(asm)] + +#[inline(always)] +fn asm() { + unsafe { + asm!("duplabel: nop",); + } +} + +fn main() { + asm(); + asm(); +} diff --git a/src/test/ui/asm/duplicate-labels.rs b/src/test/ui/asm/duplicate-labels.rs new file mode 100644 index 0000000000000..67a6664afa6c6 --- /dev/null +++ b/src/test/ui/asm/duplicate-labels.rs @@ -0,0 +1,21 @@ +// compile-flags: -g +// build-pass + +#![feature(asm)] + +fn asm1() { + unsafe { + asm!("duplabel: nop",); + } +} + +fn asm2() { + unsafe { + asm!("nop", "duplabel: nop",); + } +} + +fn main() { + asm1(); + asm2(); +} diff --git a/src/test/ui/asm/label-substr-instruction.rs b/src/test/ui/asm/label-substr-instruction.rs new file mode 100644 index 0000000000000..82efd6fe7cd4a --- /dev/null +++ b/src/test/ui/asm/label-substr-instruction.rs @@ -0,0 +1,10 @@ +// compile-flags: -g +// build-pass + +#![feature(asm)] + +fn main() { + unsafe { + asm!("jmp j", "nop", "j: nop"); + } +} diff --git a/src/test/ui/asm/label-substr-label.rs b/src/test/ui/asm/label-substr-label.rs new file mode 100644 index 0000000000000..cd20ccbefa845 --- /dev/null +++ b/src/test/ui/asm/label-substr-label.rs @@ -0,0 +1,10 @@ +// compile-flags: -g +// build-pass + +#![feature(asm)] + +fn main() { + unsafe { + asm!("jmp l", "l: nop", "jmp l2", "l2: nop"); + } +} diff --git a/src/test/ui/asm/label-use-before-declaration.rs b/src/test/ui/asm/label-use-before-declaration.rs new file mode 100644 index 0000000000000..2205acec63af0 --- /dev/null +++ b/src/test/ui/asm/label-use-before-declaration.rs @@ -0,0 +1,10 @@ +// compile-flags: -g +// build-pass + +#![feature(asm)] + +fn main() { + unsafe { + asm!("jmp l", "nop", "l: nop"); + } +}