-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Rewrite local labels in inline assembly #81570
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
70f627c
4bb2d5d
bce67de
e78a1bf
b72835f
be00fa4
1e27711
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,12 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { | |
} | ||
} | ||
} else { | ||
// Labels should be made local to prevent issues with inlined `asm!` and duplicate labels | ||
// Fixes https://github.com/rust-lang/rust/issues/74262 | ||
if let Some(label) = get_llvm_label_from_str(s) { | ||
labels.push(label.to_owned()); | ||
} | ||
|
||
template_str.push_str(s) | ||
} | ||
} | ||
|
@@ -244,6 +251,38 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { | |
} | ||
} | ||
|
||
fn valid_ident_continuation(c: char) -> bool { | ||
('a'..='z').contains(&c) | ||
|| ('A'..='Z').contains(&c) | ||
|| ('0'..='9').contains(&c) | ||
|| '_' == c | ||
|| '$' == c | ||
|| '.' == c | ||
|| '@' == c | ||
} | ||
|
||
for label in labels.iter() { | ||
let ranges: Vec<_> = template_str | ||
.match_indices(label) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this checks if the character before the match is a non-identifier character. This means that if you have a 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() || !valid_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 +913,31 @@ fn llvm_fixup_output_type( | |
_ => layout.llvm_type(cx), | ||
} | ||
} | ||
|
||
/// Gets a LLVM label from a &str if it exists | ||
/// Uses this as reference: https://llvm.org/docs/AMDGPUOperandSyntax.html#symbols | ||
fn get_llvm_label_from_str(s: &str) -> Option<&str> { | ||
if let Some(colon_idx) = s.find(':') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
let substr = &s[..colon_idx]; | ||
let mut chars = substr.chars(); | ||
if let Some(c) = chars.next() { | ||
// First char is [a-zA-Z_.] | ||
if ('a'..='z').contains(&c) || ('A'..='Z').contains(&c) || '_' == c || '.' == c { | ||
// All subsequent chars are [a-zA-Z0-9_$.@] | ||
if chars.all(|c| { | ||
('a'..='z').contains(&c) | ||
|| ('A'..='Z').contains(&c) | ||
|| ('0'..='9').contains(&c) | ||
|| '_' == c | ||
|| '$' == c | ||
|| '.' == c | ||
|| '@' == c | ||
}) { | ||
asquared31415 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return Some(substr); | ||
} | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One way to get more arbitrary stuff into a asm label is to quote it as a string, like this: void test(void) {
__asm__(".intel_syntax noprefix\n"
"\"01☺☺@banana.?@!\": jmp \"01☺☺@banana.?@!\"");
} or in Rust #![feature(asm)]
pub fn test() {
unsafe {
asm!(r#"
"☺helloworld?@!$%^&": jmp "☺helloworld?@!$%^&"
"#);
}
} So that's something we should handle or forbid too. |
||
|
||
None | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// compile-flags: -g | ||
// build-pass | ||
|
||
#![feature(asm)] | ||
|
||
#[inline(always)] | ||
fn asm() { | ||
unsafe { | ||
asm!("duplabel: nop",); | ||
} | ||
} | ||
|
||
fn main() { | ||
asm(); | ||
asm(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// compile-flags: -g | ||
// build-pass | ||
|
||
#![feature(asm)] | ||
|
||
fn main() { | ||
unsafe { | ||
asm!("jmp j", "nop", "j: nop"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// compile-flags: -g | ||
// build-pass | ||
|
||
#![feature(asm)] | ||
|
||
fn main() { | ||
unsafe { | ||
asm!("jmp l", "l: nop", "jmp l2", "l2: nop"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// compile-flags: -g | ||
// build-pass | ||
|
||
#![feature(asm)] | ||
|
||
fn main() { | ||
unsafe { | ||
asm!("jmp l", "nop", "l: nop"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A template string piece may consist of multiple lines (e.g. raw string literals). These strings could define more than one label.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah thanks, I didn't think of this case