Add --print=backend-has-mnemonic and needs-asm-mnemonic directive

Add infrastructure to query LLVM backend for specific assembly mnemonics
and use it in compiletest to conditionally run tests based on instruction
availability.

This fixes test failures with naked-dead-code-elimination which requires
the `RET` mnemonic.

Co-authored-by: Folkert de Vries <flokkievids@gmail.com>
This commit is contained in:
Eddy (Eduard) Stefes
2026-04-23 15:42:44 +02:00
parent 92c7010294
commit 2a8e588c90
18 changed files with 171 additions and 13 deletions
@@ -2343,6 +2343,7 @@ pub(crate) fn LLVMRustDILocationCloneWithBaseDiscriminator<'a>(
pub(crate) fn LLVMRustWriteValueToString(value_ref: &Value, s: &RustString);
pub(crate) fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool;
pub(crate) fn LLVMRustTargetHasMnemonic(T: &TargetMachine, s: *const c_char) -> bool;
pub(crate) fn LLVMRustPrintTargetCPUs(TM: &TargetMachine, OutStr: &RustString);
pub(crate) fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t;
@@ -480,6 +480,10 @@ pub(crate) fn print(req: &PrintRequest, out: &mut String, sess: &Session) {
match req.kind {
PrintKind::TargetCPUs => print_target_cpus(sess, tm.raw(), out),
PrintKind::TargetFeatures => print_target_features(sess, tm.raw(), out),
PrintKind::BackendHasMnemonic => {
let mnemonic = req.arg.as_deref().expect("BackendHasMnemonic requires arg");
print_target_has_mnemonic(tm.raw(), mnemonic, out)
}
_ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
}
}
@@ -738,3 +742,10 @@ pub(crate) fn tune_cpu(sess: &Session) -> Option<&str> {
let name = sess.opts.unstable_opts.tune_cpu.as_ref()?;
Some(handle_native(name))
}
fn print_target_has_mnemonic(tm: &llvm::TargetMachine, mnemonic: &str, out: &mut String) {
use std::fmt::Write;
let cstr = SmallCStr::new(mnemonic);
let has_mnemonic = unsafe { llvm::LLVMRustTargetHasMnemonic(tm, cstr.as_ptr()) };
writeln!(out, "{}", has_mnemonic).unwrap();
}
+3
View File
@@ -804,6 +804,9 @@ fn print_crate_info(
let calling_conventions = rustc_abi::all_names();
println_info!("{}", calling_conventions.join("\n"));
}
BackendHasMnemonic => {
codegen_backend.print(req, &mut crate_info, sess);
}
BackendHasZstd => {
let has_zstd: bool = codegen_backend.has_zstd();
println_info!("{has_zstd}");
@@ -19,6 +19,7 @@
#include "llvm/IR/Verifier.h"
#include "llvm/IRPrinter/IRPrintingPasses.h"
#include "llvm/LTO/LTO.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Object/ObjectFile.h"
@@ -94,6 +95,25 @@ extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM,
return MCInfo->checkFeatures(std::string("+") + Feature);
}
/// Check whether the target has a specific assembly mnemonic like `ret` or
/// `nop`.
/// This should be fast enough but if its not we have to look into another
/// method of checking.
extern "C" bool LLVMRustTargetHasMnemonic(LLVMTargetMachineRef TM,
const char *Mnemonic) {
TargetMachine *Target = unwrap(TM);
const MCInstrInfo *MII = Target->getMCInstrInfo();
StringRef MnemonicRef(Mnemonic);
for (unsigned i = 0; i < MII->getNumOpcodes(); i++) {
StringRef Name = MII->getName(i);
if (Name.equals_insensitive(MnemonicRef)) {
return true;
}
}
return false;
}
enum class LLVMRustCodeModel {
Tiny,
Small,
@@ -15,6 +15,7 @@
pub struct PrintRequest {
pub kind: PrintKind,
pub out: OutFileName,
pub arg: Option<String>,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -22,6 +23,7 @@ pub struct PrintRequest {
pub enum PrintKind {
// tidy-alphabetical-start
AllTargetSpecsJson,
BackendHasMnemonic,
BackendHasZstd,
CallingConventions,
Cfg,
@@ -60,6 +62,7 @@ fn name(self) -> &'static str {
match self {
// tidy-alphabetical-start
AllTargetSpecsJson => "all-target-specs-json",
BackendHasMnemonic => "backend-has-mnemonic",
BackendHasZstd => "backend-has-zstd",
CallingConventions => "calling-conventions",
Cfg => "cfg",
@@ -113,7 +116,8 @@ fn is_stable(self) -> bool {
// Unstable values:
AllTargetSpecsJson => false,
BackendHasZstd => false, // (perma-unstable, for use by compiletest)
BackendHasMnemonic => false, // (perma-unstable, for use by compiletest)
BackendHasZstd => false, // (perma-unstable, for use by compiletest)
CheckCfg => false,
CrateRootLintLevels => false,
SupportedCrateTypes => false,
@@ -150,11 +154,19 @@ pub(crate) fn collect_print_requests(
) -> Vec<PrintRequest> {
let mut prints = Vec::<PrintRequest>::new();
if cg.target_cpu.as_deref() == Some("help") {
prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout });
prints.push(PrintRequest {
kind: PrintKind::TargetCPUs,
out: OutFileName::Stdout,
arg: None,
});
cg.target_cpu = None;
};
if cg.target_feature == "help" {
prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout });
prints.push(PrintRequest {
kind: PrintKind::TargetFeatures,
out: OutFileName::Stdout,
arg: None,
});
cg.target_feature = String::new();
}
@@ -167,9 +179,22 @@ pub(crate) fn collect_print_requests(
prints.extend(matches.opt_strs("print").into_iter().map(|req| {
let (req, out) = split_out_file_name(&req);
let kind = if let Some(print_kind) = PrintKind::from_str(req) {
let (kind, arg) = if let Some(mnemonic) = req.strip_prefix("backend-has-mnemonic") {
check_print_request_stability(early_dcx, unstable_opts, PrintKind::BackendHasMnemonic);
// BackendHasMnemonic requires a mnemonic argument
if let Some(mnemonic) = mnemonic.strip_prefix(':')
&& !mnemonic.is_empty()
{
(PrintKind::BackendHasMnemonic, Some(mnemonic.to_string()))
} else {
early_dcx.early_fatal(
"expected mnemonic name after `--print=backend-has-mnemonic:`, \
for example: `--print=backend-has-mnemonic:RET`",
);
}
} else if let Some(print_kind) = PrintKind::from_str(req) {
check_print_request_stability(early_dcx, unstable_opts, print_kind);
print_kind
(print_kind, None)
} else {
let is_nightly = nightly_options::match_is_nightly_build(matches);
emit_unknown_print_request_help(early_dcx, req, is_nightly)
@@ -185,7 +210,7 @@ pub(crate) fn collect_print_requests(
}
}
PrintRequest { kind, out }
PrintRequest { kind, out, arg }
}));
prints
@@ -346,6 +346,17 @@ See also the [codegen tests](#codegen-tests) for a similar set of tests.
If you need to work with `#![no_std]` cross-compiling tests, consult the
[`minicore` test auxiliary](./minicore.md) chapter.
#### Conditional assembly tests based on instruction support
Tests that depend on specific assembly instructions being available can use the
`//@ needs-asm-mnemonic: <MNEMONIC>` directive. This will skip the test if the
target backend does not support the specified instruction mnemonic.
For example, a test that requires the `RET` instruction:
```rust,ignore
//@ needs-asm-mnemonic: RET
```
[`tests/assembly-llvm`]: https://github.com/rust-lang/rust/tree/HEAD/tests/assembly-llvm
@@ -163,6 +163,9 @@ The following directives will check rustc build settings and target settings:
For tests that cross-compile to explicit targets
via `--target`, use `needs-llvm-components` instead to ensure the appropriate
backend is available.
- `needs-asm-mnemonic: <MNEMONIC>` — ignores if the target backend does not
support the specified assembly mnemonic (e.g., `RET`, `NOP`).
Only supported with the LLVM backend.
- `needs-profiler-runtime` — ignores the test if the profiler runtime was not
enabled for the target (`build.profiler = true` in `bootstrap.toml`)
- `needs-sanitizer-support` — ignores if the sanitizer support was not enabled
@@ -156,6 +156,7 @@
"min-llvm-version",
"min-system-llvm-version",
"minicore-compile-flags",
"needs-asm-mnemonic",
"needs-asm-support",
"needs-backends",
"needs-crate-type",
@@ -290,6 +290,37 @@ pub(super) fn handle_needs(
}
}
if name == "needs-asm-mnemonic" {
let Some(rest) = ln.value_after_colon() else {
return IgnoreDecision::Error {
message: "expected `needs-asm-mnemonic` to have a mnemonic name after colon"
.to_string(),
};
};
if !config.default_codegen_backend.is_llvm() {
return IgnoreDecision::Ignore {
reason: "skipping test as non-LLVM backend does not support mnemonic queries"
.to_string(),
};
}
let mnemonic = rest.trim();
let has_mnemonic = match mnemonic {
"ret" => cache.has_ret_mnemonic,
"nop" => cache.has_nop_mnemonic,
_ => has_mnemonic(config, mnemonic),
};
if has_mnemonic {
return IgnoreDecision::Continue;
} else {
return IgnoreDecision::Ignore {
reason: format!("skipping test as target does not have `{mnemonic}` mnemonic"),
};
}
}
if !name.starts_with("needs-") {
return IgnoreDecision::Continue;
}
@@ -352,6 +383,10 @@ pub(super) struct CachedNeedsConditions {
symlinks: bool,
/// Whether LLVM built with zstd, for the `needs-llvm-zstd` directive.
llvm_zstd: bool,
/// Might add particular other mnemonics heavily needed by tests here.
/// Otherwise call into llvm for every check
has_ret_mnemonic: bool,
has_nop_mnemonic: bool,
}
impl CachedNeedsConditions {
@@ -399,6 +434,8 @@ pub(super) fn load(config: &Config) -> Self {
llvm_zstd: llvm_has_zstd(&config),
dlltool: find_dlltool(&config),
symlinks: has_symlinks(),
has_ret_mnemonic: has_mnemonic(config, "ret"),
has_nop_mnemonic: has_mnemonic(config, "nop"),
}
}
}
@@ -466,3 +503,26 @@ fn llvm_has_zstd(config: &Config) -> bool {
_ => panic!("unexpected output from `--print=backend-has-zstd`: {output:?}"),
}
}
fn has_mnemonic(config: &Config, mnemonic: &str) -> bool {
if !config.default_codegen_backend.is_llvm() {
return false;
}
let target_flag = format!("--target={}", config.target);
let output = query_rustc_output(
config,
&[
&target_flag,
"-Zunstable-options",
&format!("--print=backend-has-mnemonic:{}", mnemonic),
],
Default::default(),
);
match output.trim() {
"true" => true,
"false" => false,
_ => panic!("unexpected output from `--print=backend-has-mnemonic:{mnemonic}`: {output:?}"),
}
}
@@ -1254,3 +1254,25 @@ fn test_edition_range_edition_to_test() {
assert_edition_to_test(2018, range, Some(e2024));
assert_edition_to_test(2018, range, Some(efuture));
}
#[test]
fn needs_asm_mnemonic() {
let config_x86_64 = cfg().target("x86_64-unknown-linux-gnu").build();
let config_aarch64 = cfg().target("aarch64-unknown-linux-gnu").build();
// invalid mnemonic
assert!(check_ignore(&config_x86_64, "//@ needs-asm-mnemonic:GRUGGY"));
assert!(check_ignore(&config_aarch64, "//@ needs-asm-mnemonic:gruggy"));
// valid x86 and aarch64
assert!(!check_ignore(&config_x86_64, "//@ needs-asm-mnemonic:RET"));
assert!(!check_ignore(&config_aarch64, "//@ needs-asm-mnemonic:ret"));
// this is aarch64 specific
assert!(check_ignore(&config_x86_64, "//@ needs-asm-mnemonic:ldrsbwui"));
assert!(!check_ignore(&config_aarch64, "//@ needs-asm-mnemonic:LDRSBWui"));
// this is x86 specific
assert!(check_ignore(&config_aarch64, "//@ needs-asm-mnemonic:CMPxCHG16B"));
assert!(!check_ignore(&config_x86_64, "//@ needs-asm-mnemonic:CMPXchg16B"));
}
@@ -1,5 +1,6 @@
//@ ignore-cross-compile
//@ needs-asm-support
//@ needs-asm-mnemonic: RET
use run_make_support::symbols::object_contains_any_symbol;
use run_make_support::{bin_name, rustc};
@@ -2,6 +2,6 @@
error: unknown print request: `xxx`
|
- = help: valid print requests are: `calling-conventions`, `cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `tls-models`
+ = help: valid print requests are: `all-target-specs-json`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`
+ = help: valid print requests are: `all-target-specs-json`, `backend-has-mnemonic`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information
@@ -1,5 +1,5 @@
error: unknown print request: `xxx`
|
= help: valid print requests are: `all-target-specs-json`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`
= help: valid print requests are: `all-target-specs-json`, `backend-has-mnemonic`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information
+1 -1
View File
@@ -43,7 +43,7 @@ Options:
--print <INFO>[=<FILE>]
Compiler information to print on stdout (or to a file)
INFO may be one of
<all-target-specs-json|backend-has-zstd|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|target-spec-json-schema|tls-models>.
<all-target-specs-json|backend-has-mnemonic|backend-has-zstd|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|target-spec-json-schema|tls-models>.
-g Equivalent to -C debuginfo=2
-O Equivalent to -C opt-level=3
-o <FILENAME> Write output to FILENAME
+1 -1
View File
@@ -43,7 +43,7 @@ Options:
--print <INFO>[=<FILE>]
Compiler information to print on stdout (or to a file)
INFO may be one of
<all-target-specs-json|backend-has-zstd|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|target-spec-json-schema|tls-models>.
<all-target-specs-json|backend-has-mnemonic|backend-has-zstd|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|target-spec-json-schema|tls-models>.
-g Equivalent to -C debuginfo=2
-O Equivalent to -C opt-level=3
-o <FILENAME> Write output to FILENAME
@@ -3,5 +3,5 @@ error: Argument to option 'print' missing
--print <INFO>[=<FILE>]
Compiler information to print on stdout (or to a file)
INFO may be one of
<all-target-specs-json|backend-has-zstd|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|target-spec-json-schema|tls-models>.
<all-target-specs-json|backend-has-mnemonic|backend-has-zstd|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|target-spec-json-schema|tls-models>.
+1 -1
View File
@@ -1,5 +1,5 @@
error: unknown print request: `yyyy`
|
= help: valid print requests are: `all-target-specs-json`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`
= help: valid print requests are: `all-target-specs-json`, `backend-has-mnemonic`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information
@@ -1,6 +1,6 @@
error: unknown print request: `lints`
|
= help: valid print requests are: `all-target-specs-json`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`
= help: valid print requests are: `all-target-specs-json`, `backend-has-mnemonic`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`
= help: use `-Whelp` to print a list of lints
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information