diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 525d1dbe9d0d3..bed9242a3dbcf 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2343,6 +2343,7 @@ unsafe extern "C" { 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; diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 180559d28d848..e9983d11c127f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -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(); +} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index c15c3c229398c..b05f5bc4a8e20 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -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}"); diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index b4f6bb4583c10..bad99f34567d9 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -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, diff --git a/compiler/rustc_session/src/config/print_request.rs b/compiler/rustc_session/src/config/print_request.rs index dc53fcc6955f7..7c8ca4c0d3df3 100644 --- a/compiler/rustc_session/src/config/print_request.rs +++ b/compiler/rustc_session/src/config/print_request.rs @@ -15,6 +15,7 @@ use crate::macros::AllVariants; pub struct PrintRequest { pub kind: PrintKind, pub out: OutFileName, + pub arg: Option, } #[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 @@ impl PrintKind { 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 @@ impl PrintKind { // 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 { let mut prints = Vec::::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 diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 4c534e9ae0eda..53a6c0cb5737d 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -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: ` 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 diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 76bf2cdbea667..9498f7953ad04 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -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: ` — 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 diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 34c6c1374b632..949c5ebfb29a7 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -156,6 +156,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "min-llvm-version", "min-system-llvm-version", "minicore-compile-flags", + "needs-asm-mnemonic", "needs-asm-support", "needs-backends", "needs-crate-type", diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index e9f3d6c28d6ef..88ca347edd0a5 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -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 @@ impl CachedNeedsConditions { 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:?}"), + } +} diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 4cd75fcfa511a..56d52982a8211 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -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")); +} diff --git a/tests/run-make/naked-dead-code-elimination/rmake.rs b/tests/run-make/naked-dead-code-elimination/rmake.rs index 1be22de367c99..8e4c26fc34508 100644 --- a/tests/run-make/naked-dead-code-elimination/rmake.rs +++ b/tests/run-make/naked-dead-code-elimination/rmake.rs @@ -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}; diff --git a/tests/run-make/print-request-help-stable-unstable/help-diff.diff b/tests/run-make/print-request-help-stable-unstable/help-diff.diff index e382a24782711..828a98d96b563 100644 --- a/tests/run-make/print-request-help-stable-unstable/help-diff.diff +++ b/tests/run-make/print-request-help-stable-unstable/help-diff.diff @@ -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 diff --git a/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err b/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err index 70764ea13aa87..d0e4c81f1de9d 100644 --- a/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err +++ b/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err @@ -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 diff --git a/tests/run-make/rustc-help/help-v.stdout b/tests/run-make/rustc-help/help-v.stdout index 0acbb766c1556..1531f61089e9a 100644 --- a/tests/run-make/rustc-help/help-v.stdout +++ b/tests/run-make/rustc-help/help-v.stdout @@ -43,7 +43,7 @@ Options: --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - . + . -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 -o Write output to FILENAME diff --git a/tests/run-make/rustc-help/help.stdout b/tests/run-make/rustc-help/help.stdout index 4075dd0282999..f96feccf35980 100644 --- a/tests/run-make/rustc-help/help.stdout +++ b/tests/run-make/rustc-help/help.stdout @@ -43,7 +43,7 @@ Options: --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - . + . -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 -o Write output to FILENAME diff --git a/tests/ui/compile-flags/invalid/print-without-arg.stderr b/tests/ui/compile-flags/invalid/print-without-arg.stderr index ff9669614360a..98788b4b87228 100644 --- a/tests/ui/compile-flags/invalid/print-without-arg.stderr +++ b/tests/ui/compile-flags/invalid/print-without-arg.stderr @@ -3,5 +3,5 @@ error: Argument to option 'print' missing --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - . + . diff --git a/tests/ui/compile-flags/invalid/print.stderr b/tests/ui/compile-flags/invalid/print.stderr index e2521ebf26a41..3eb7634d915db 100644 --- a/tests/ui/compile-flags/invalid/print.stderr +++ b/tests/ui/compile-flags/invalid/print.stderr @@ -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 diff --git a/tests/ui/print-request/print-lints-help.stderr b/tests/ui/print-request/print-lints-help.stderr index d39c6326e318b..c6e74e7dce7b6 100644 --- a/tests/ui/print-request/print-lints-help.stderr +++ b/tests/ui/print-request/print-lints-help.stderr @@ -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