diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index d3468499b4b3a..83de7d3892314 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2875,6 +2875,71 @@ enum AsmLabelKind { Binary, } +/// Checks if a potential label is actually a Hexagon register span notation. +/// +/// Hexagon assembly uses register span notation like `r1:0`, `V5:4.w`, `p1:0` etc. +/// These follow the pattern: `[letter][digit(s)]:[digit(s)][optional_suffix]` +/// +/// Returns `true` if the string matches a valid Hexagon register span pattern. +pub fn is_hexagon_register_span(possible_label: &str) -> bool { + // Extract the full register span from the context + if let Some(colon_idx) = possible_label.find(':') { + let after_colon = &possible_label[colon_idx + 1..]; + is_hexagon_register_span_impl(&possible_label[..colon_idx], after_colon) + } else { + false + } +} + +/// Helper function for use within the lint when we have statement context. +fn is_hexagon_register_span_context( + possible_label: &str, + statement: &str, + colon_idx: usize, +) -> bool { + // Extract what comes after the colon in the statement + let after_colon_start = colon_idx + 1; + if after_colon_start >= statement.len() { + return false; + } + + // Get the part after the colon, up to the next whitespace or special character + let after_colon_full = &statement[after_colon_start..]; + let after_colon = after_colon_full + .chars() + .take_while(|&c| c.is_ascii_alphanumeric() || c == '.') + .collect::(); + + is_hexagon_register_span_impl(possible_label, &after_colon) +} + +/// Core implementation for checking hexagon register spans. +fn is_hexagon_register_span_impl(before_colon: &str, after_colon: &str) -> bool { + if before_colon.len() < 1 || after_colon.is_empty() { + return false; + } + + let mut chars = before_colon.chars(); + let start = chars.next().unwrap(); + + // Must start with a letter (r, V, p, etc.) + if !start.is_ascii_alphabetic() { + return false; + } + + let rest = &before_colon[1..]; + + // Check if the part after the first letter is all digits and non-empty + if rest.is_empty() || !rest.chars().all(|c| c.is_ascii_digit()) { + return false; + } + + // Check if after colon starts with digits (may have suffix like .w, .h) + let digits_after = after_colon.chars().take_while(|c| c.is_ascii_digit()).collect::(); + + !digits_after.is_empty() +} + impl<'tcx> LateLintPass<'tcx> for AsmLabels { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { if let hir::Expr { @@ -2957,6 +3022,14 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels { break 'label_loop; } + // Check for Hexagon register span notation (e.g., "r1:0", "V5:4", "V3:2.w") + // This is valid Hexagon assembly syntax, not a label + if matches!(cx.tcx.sess.asm_arch, Some(InlineAsmArch::Hexagon)) + && is_hexagon_register_span_context(possible_label, statement, idx) + { + break 'label_loop; + } + for c in chars { // Inside a template format arg, any character is permitted for the // purposes of label detection because we assume that it can be diff --git a/compiler/rustc_lint/src/tests.rs b/compiler/rustc_lint/src/tests.rs index f49301b0215db..b3c91583914a1 100644 --- a/compiler/rustc_lint/src/tests.rs +++ b/compiler/rustc_lint/src/tests.rs @@ -2,6 +2,7 @@ use rustc_span::{Symbol, create_default_session_globals_then}; +use crate::builtin::is_hexagon_register_span; use crate::levels::parse_lint_and_tool_name; #[test] @@ -27,3 +28,29 @@ fn parse_lint_multiple_path() { ) }); } + +#[test] +fn test_hexagon_register_span_patterns() { + // Valid Hexagon register span patterns + assert!(is_hexagon_register_span("r1:0")); + assert!(is_hexagon_register_span("r15:14")); + assert!(is_hexagon_register_span("V5:4")); + assert!(is_hexagon_register_span("V3:2")); + assert!(is_hexagon_register_span("V5:4.w")); + assert!(is_hexagon_register_span("V3:2.h")); + assert!(is_hexagon_register_span("r99:98")); + assert!(is_hexagon_register_span("V123:122.whatever")); + + // Invalid patterns - these should be treated as potential labels + assert!(!is_hexagon_register_span("label1")); + assert!(!is_hexagon_register_span("foo:")); + assert!(!is_hexagon_register_span(":0")); + assert!(!is_hexagon_register_span("r:0")); // missing digits before colon + assert!(!is_hexagon_register_span("r1:")); // missing digits after colon + assert!(!is_hexagon_register_span("r1:a")); // non-digit after colon + assert!(!is_hexagon_register_span("1:0")); // starts with digit, not letter + assert!(!is_hexagon_register_span("r1")); // no colon + assert!(!is_hexagon_register_span("r")); // too short + assert!(!is_hexagon_register_span("")); // empty + assert!(!is_hexagon_register_span("ra:0")); // letter in first digit group +} diff --git a/tests/ui/asm/hexagon-register-pairs.rs b/tests/ui/asm/hexagon-register-pairs.rs new file mode 100644 index 0000000000000..75f2960778455 --- /dev/null +++ b/tests/ui/asm/hexagon-register-pairs.rs @@ -0,0 +1,37 @@ +//@ add-minicore +//@ compile-flags: --target hexagon-unknown-linux-musl -C target-feature=+hvx-length128b +//@ needs-llvm-components: hexagon +//@ ignore-backends: gcc + +#![feature(no_core, asm_experimental_arch)] +#![crate_type = "lib"] +#![no_core] + +//~? WARN unstable feature specified for `-Ctarget-feature`: `hvx-length128b` + +extern crate minicore; +use minicore::*; + +fn test_register_spans() { + unsafe { + // These are valid Hexagon register span notations, not labels + // Should NOT trigger the named labels lint + + // General register pairs + asm!("r1:0 = memd(r29+#0)", lateout("r0") _, lateout("r1") _); + asm!("r3:2 = combine(#1, #0)", lateout("r2") _, lateout("r3") _); + asm!("r15:14 = memd(r30+#8)", lateout("r14") _, lateout("r15") _); + asm!("memd(r29+#0) = r5:4", in("r4") 0u32, in("r5") 0u32); + + // These patterns look like register spans but test different edge cases + // All should NOT trigger the lint as they match valid hexagon register syntax patterns + asm!("V5:4 = vaddw(v1:0, v1:0)", options(nostack)); // Uppercase V register pair + asm!("v1:0.w = vsub(v1:0.w,v1:0.w):sat", options(nostack)); // Lowercase v with suffix + + // Mixed with actual labels should still trigger for the labels + asm!("label1: r7:6 = combine(#2, #3)"); //~ ERROR avoid using named labels + + // Regular labels should still trigger + asm!("hexagon_label: nop"); //~ ERROR avoid using named labels + } +} diff --git a/tests/ui/asm/hexagon-register-pairs.stderr b/tests/ui/asm/hexagon-register-pairs.stderr new file mode 100644 index 0000000000000..c5974ba01f176 --- /dev/null +++ b/tests/ui/asm/hexagon-register-pairs.stderr @@ -0,0 +1,25 @@ +warning: unstable feature specified for `-Ctarget-feature`: `hvx-length128b` + | + = note: this feature is not stably supported; its behavior can change in the future + +error: avoid using named labels in inline assembly + --> $DIR/hexagon-register-pairs.rs:32:15 + | +LL | asm!("label1: r7:6 = combine(#2, #3)"); + | ^^^^^^ + | + = help: only local labels of the form `:` should be used in inline asm + = note: see the asm section of Rust By Example for more information + = note: `#[deny(named_asm_labels)]` on by default + +error: avoid using named labels in inline assembly + --> $DIR/hexagon-register-pairs.rs:35:15 + | +LL | asm!("hexagon_label: nop"); + | ^^^^^^^^^^^^^ + | + = help: only local labels of the form `:` should be used in inline asm + = note: see the asm section of Rust By Example for more information + +error: aborting due to 2 previous errors; 1 warning emitted +