diff --git a/Cargo.lock b/Cargo.lock index a4b4e49f82c2e..af81e7d788794 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,11 +205,11 @@ dependencies = [ [[package]] name = "ar_archive_writer" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8412a2d690663356cba5a2532f3ed55d1e8090743bc6695b88403b27df67733" +checksum = "9c39fa49fe538a6966c7b05c2ef464b92138e717d73812053be1c643ae6f9d1f" dependencies = [ - "object 0.35.0", + "object 0.36.2", ] [[package]] @@ -2454,15 +2454,6 @@ dependencies = [ "ruzstd 0.5.0", ] -[[package]] -name = "object" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" -dependencies = [ - "memchr", -] - [[package]] name = "object" version = "0.36.2" diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs index 1935005a08c54..084654af09dd7 100644 --- a/compiler/rustc_codegen_cranelift/src/archive.rs +++ b/compiler/rustc_codegen_cranelift/src/archive.rs @@ -1,4 +1,4 @@ -use std::path::{Path, PathBuf}; +use std::path::Path; use rustc_codegen_ssa::back::archive::{ ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, @@ -16,10 +16,9 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { &self, sess: &Session, _lib_name: &str, - _dll_imports: &[rustc_session::cstore::DllImport], - _tmpdir: &Path, - _is_direct_dependency: bool, - ) -> PathBuf { + _import_name_and_ordinal_vector: Vec<(String, Option)>, + _output_path: &Path, + ) { sess.dcx().fatal("raw-dylib is not yet supported by rustc_codegen_cranelift"); } } diff --git a/compiler/rustc_codegen_gcc/src/archive.rs b/compiler/rustc_codegen_gcc/src/archive.rs index 36f5e0f8094e8..0cee05f1cea38 100644 --- a/compiler/rustc_codegen_gcc/src/archive.rs +++ b/compiler/rustc_codegen_gcc/src/archive.rs @@ -1,9 +1,8 @@ -use std::path::{Path, PathBuf}; +use std::path::Path; use rustc_codegen_ssa::back::archive::{ ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, }; -use rustc_session::cstore::DllImport; use rustc_session::Session; pub(crate) struct ArArchiveBuilderBuilder; @@ -17,10 +16,9 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { &self, _sess: &Session, _lib_name: &str, - _dll_imports: &[DllImport], - _tmpdir: &Path, - _is_direct_dependency: bool, - ) -> PathBuf { + _import_name_and_ordinal_vector: Vec<(String, Option)>, + _output_path: &Path, + ) { unimplemented!("creating dll imports is not yet supported"); } } diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index e521551304ef8..5fdf2680aac88 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -75,7 +75,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>( let function_features = codegen_fn_attrs .target_features .iter() - .map(|features| features.as_str()) + .map(|features| features.name.as_str()) .collect::>(); if let Some(features) = check_tied_features( diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 8bb90efe6fb7c..5308ccdb61469 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -65,8 +65,8 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec( to_add.extend(tune_cpu_attr(cx)); let function_features = - codegen_fn_attrs.target_features.iter().map(|f| f.as_str()).collect::>(); + codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::>(); if let Some(f) = llvm_util::check_tied_features( cx.tcx.sess, diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 2f5a864d70eaf..a2ab19ac800b7 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -1,21 +1,19 @@ //! A helper class for dealing with static archives -use std::ffi::{c_char, c_void, CStr, CString, OsString}; +use std::ffi::{c_char, c_void, CStr, CString}; use std::path::{Path, PathBuf}; -use std::{env, io, mem, ptr, str}; +use std::{io, mem, ptr, str}; use rustc_codegen_ssa::back::archive::{ - try_extract_macho_fat_archive, ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder, - ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind, DEFAULT_OBJECT_READER, + create_mingw_dll_import_lib, try_extract_macho_fat_archive, ArArchiveBuilder, + ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind, + DEFAULT_OBJECT_READER, }; -use rustc_session::cstore::DllImport; +use rustc_codegen_ssa::common; use rustc_session::Session; use tracing::trace; -use crate::common; -use crate::errors::{ - DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary, ErrorWritingDEFFile, -}; +use crate::errors::ErrorCreatingImportLibrary; use crate::llvm::archive_ro::{ArchiveRO, Child}; use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport}; @@ -121,116 +119,21 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { &self, sess: &Session, lib_name: &str, - dll_imports: &[DllImport], - tmpdir: &Path, - is_direct_dependency: bool, - ) -> PathBuf { - let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; - let output_path = tmpdir.join(format!("{lib_name}{name_suffix}.lib")); - - let target = &sess.target; - let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(target); - - let import_name_and_ordinal_vector: Vec<(String, Option)> = dll_imports - .iter() - .map(|import: &DllImport| { - if sess.target.arch == "x86" { - ( - common::i686_decorated_name(import, mingw_gnu_toolchain, false), - import.ordinal(), - ) - } else { - (import.name.to_string(), import.ordinal()) - } - }) - .collect(); - - if mingw_gnu_toolchain { + import_name_and_ordinal_vector: Vec<(String, Option)>, + output_path: &Path, + ) { + if common::is_mingw_gnu_toolchain(&sess.target) { // The binutils linker used on -windows-gnu targets cannot read the import // libraries generated by LLVM: in our attempts, the linker produced an .EXE // that loaded but crashed with an AV upon calling one of the imported // functions. Therefore, use binutils to create the import library instead, // by writing a .DEF file to the temp dir and calling binutils's dlltool. - let def_file_path = tmpdir.join(format!("{lib_name}{name_suffix}.def")); - - let def_file_content = format!( - "EXPORTS\n{}", - import_name_and_ordinal_vector - .into_iter() - .map(|(name, ordinal)| { - match ordinal { - Some(n) => format!("{name} @{n} NONAME"), - None => name, - } - }) - .collect::>() - .join("\n") + create_mingw_dll_import_lib( + sess, + lib_name, + import_name_and_ordinal_vector, + output_path, ); - - match std::fs::write(&def_file_path, def_file_content) { - Ok(_) => {} - Err(e) => { - sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e }); - } - }; - - // --no-leading-underscore: For the `import_name_type` feature to work, we need to be - // able to control the *exact* spelling of each of the symbols that are being imported: - // hence we don't want `dlltool` adding leading underscores automatically. - let dlltool = find_binutils_dlltool(sess); - let temp_prefix = { - let mut path = PathBuf::from(&output_path); - path.pop(); - path.push(lib_name); - path - }; - // dlltool target architecture args from: - // https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69 - let (dlltool_target_arch, dlltool_target_bitness) = match sess.target.arch.as_ref() { - "x86_64" => ("i386:x86-64", "--64"), - "x86" => ("i386", "--32"), - "aarch64" => ("arm64", "--64"), - "arm" => ("arm", "--32"), - _ => panic!("unsupported arch {}", sess.target.arch), - }; - let mut dlltool_cmd = std::process::Command::new(&dlltool); - dlltool_cmd - .arg("-d") - .arg(def_file_path) - .arg("-D") - .arg(lib_name) - .arg("-l") - .arg(&output_path) - .arg("-m") - .arg(dlltool_target_arch) - .arg("-f") - .arg(dlltool_target_bitness) - .arg("--no-leading-underscore") - .arg("--temp-prefix") - .arg(temp_prefix); - - match dlltool_cmd.output() { - Err(e) => { - sess.dcx().emit_fatal(ErrorCallingDllTool { - dlltool_path: dlltool.to_string_lossy(), - error: e, - }); - } - // dlltool returns '0' on failure, so check for error output instead. - Ok(output) if !output.stderr.is_empty() => { - sess.dcx().emit_fatal(DlltoolFailImportLibrary { - dlltool_path: dlltool.to_string_lossy(), - dlltool_args: dlltool_cmd - .get_args() - .map(|arg| arg.to_string_lossy()) - .collect::>() - .join(" "), - stdout: String::from_utf8_lossy(&output.stdout), - stderr: String::from_utf8_lossy(&output.stderr), - }) - } - _ => {} - } } else { // we've checked for \0 characters in the library name already let dll_name_z = CString::new(lib_name).unwrap(); @@ -242,9 +145,9 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { trace!(" output_path {}", output_path.display()); trace!( " import names: {}", - dll_imports + import_name_and_ordinal_vector .iter() - .map(|import| import.name.to_string()) + .map(|(name, _ordinal)| name.clone()) .collect::>() .join(", "), ); @@ -281,9 +184,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { error: llvm::last_error().unwrap_or("unknown LLVM error".to_string()), }); } - }; - - output_path + } } } @@ -457,39 +358,3 @@ impl<'a> LlvmArchiveBuilder<'a> { fn string_to_io_error(s: String) -> io::Error { io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}")) } - -fn find_binutils_dlltool(sess: &Session) -> OsString { - assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc); - if let Some(dlltool_path) = &sess.opts.cg.dlltool { - return dlltool_path.clone().into_os_string(); - } - - let tool_name: OsString = if sess.host.options.is_like_windows { - // If we're compiling on Windows, always use "dlltool.exe". - "dlltool.exe" - } else { - // On other platforms, use the architecture-specific name. - match sess.target.arch.as_ref() { - "x86_64" => "x86_64-w64-mingw32-dlltool", - "x86" => "i686-w64-mingw32-dlltool", - "aarch64" => "aarch64-w64-mingw32-dlltool", - - // For non-standard architectures (e.g., aarch32) fallback to "dlltool". - _ => "dlltool", - } - } - .into(); - - // NOTE: it's not clear how useful it is to explicitly search PATH. - for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { - let full_path = dir.join(&tool_name); - if full_path.is_file() { - return full_path.into_os_string(); - } - } - - // The user didn't specify the location of the dlltool binary, and we weren't able - // to find the appropriate one on the PATH. Just return the name of the tool - // and let the invocation fail with a hopefully useful error message. - tool_name -} diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 5a7909d151139..a1f2433ab6f3b 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -95,11 +95,14 @@ pub fn write_output_file<'ll>( } } -pub fn create_informational_target_machine(sess: &Session) -> OwnedTargetMachine { +pub fn create_informational_target_machine( + sess: &Session, + only_base_features: bool, +) -> OwnedTargetMachine { let config = TargetMachineFactoryConfig { split_dwarf_file: None, output_obj_file: None }; // Can't use query system here quite yet because this function is invoked before the query // system/tcx is set up. - let features = llvm_util::global_llvm_features(sess, false); + let features = llvm_util::global_llvm_features(sess, false, only_base_features); target_machine_factory(sess, config::OptLevel::No, &features)(config) .unwrap_or_else(|err| llvm_err(sess.dcx(), err).raise()) } diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index c913bdebaaa7c..9030b3d848f9a 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -4,13 +4,14 @@ //! and methods are represented as just a fn ptr and not a full //! closure. +use rustc_codegen_ssa::common; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; use tracing::debug; use crate::context::CodegenCx; use crate::value::Value; -use crate::{attributes, common, llvm}; +use crate::{attributes, llvm}; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. @@ -46,7 +47,7 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> } else { let instance_def_id = instance.def_id(); let llfn = if tcx.sess.target.arch == "x86" - && let Some(dllimport) = common::get_dllimport(tcx, instance_def_id, sym) + && let Some(dllimport) = crate::common::get_dllimport(tcx, instance_def_id, sym) { // Fix for https://github.com/rust-lang/rust/issues/104453 // On x86 Windows, LLVM uses 'L' as the prefix for any private diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 197bbb9ddbf9e..65f974c568936 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -1,7 +1,5 @@ //! Code that is useful in various codegen modules. -use std::fmt::Write; - use libc::{c_char, c_uint}; use rustc_ast::Mutability; use rustc_codegen_ssa::traits::*; @@ -10,9 +8,8 @@ use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; use rustc_middle::ty::TyCtxt; -use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType}; +use rustc_session::cstore::DllImport; use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer}; -use rustc_target::spec::Target; use tracing::debug; use crate::consts::const_alloc_to_llvm; @@ -379,64 +376,3 @@ pub(crate) fn get_dllimport<'tcx>( tcx.native_library(id) .and_then(|lib| lib.dll_imports.iter().find(|di| di.name.as_str() == name)) } - -pub(crate) fn is_mingw_gnu_toolchain(target: &Target) -> bool { - target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty() -} - -pub(crate) fn i686_decorated_name( - dll_import: &DllImport, - mingw: bool, - disable_name_mangling: bool, -) -> String { - let name = dll_import.name.as_str(); - - let (add_prefix, add_suffix) = match dll_import.import_name_type { - Some(PeImportNameType::NoPrefix) => (false, true), - Some(PeImportNameType::Undecorated) => (false, false), - _ => (true, true), - }; - - // Worst case: +1 for disable name mangling, +1 for prefix, +4 for suffix (@@__). - let mut decorated_name = String::with_capacity(name.len() + 6); - - if disable_name_mangling { - // LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be disabled. - decorated_name.push('\x01'); - } - - let prefix = if add_prefix && dll_import.is_fn { - match dll_import.calling_convention { - DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None, - DllCallingConvention::Stdcall(_) => (!mingw - || dll_import.import_name_type == Some(PeImportNameType::Decorated)) - .then_some('_'), - DllCallingConvention::Fastcall(_) => Some('@'), - } - } else if !dll_import.is_fn && !mingw { - // For static variables, prefix with '_' on MSVC. - Some('_') - } else { - None - }; - if let Some(prefix) = prefix { - decorated_name.push(prefix); - } - - decorated_name.push_str(name); - - if add_suffix && dll_import.is_fn { - match dll_import.calling_convention { - DllCallingConvention::C => {} - DllCallingConvention::Stdcall(arg_list_size) - | DllCallingConvention::Fastcall(arg_list_size) => { - write!(&mut decorated_name, "@{arg_list_size}").unwrap(); - } - DllCallingConvention::Vectorcall(arg_list_size) => { - write!(&mut decorated_name, "@@{arg_list_size}").unwrap(); - } - } - } - - decorated_name -} diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index c3ea4a18a71bd..75b298f14ca1e 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -1,5 +1,6 @@ use std::ops::Range; +use rustc_codegen_ssa::common; use rustc_codegen_ssa::traits::*; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -18,7 +19,7 @@ use rustc_target::abi::{ }; use tracing::{debug, instrument, trace}; -use crate::common::{self, CodegenCx}; +use crate::common::CodegenCx; use crate::errors::{ InvalidMinimumAlignmentNotPowerOfTwo, InvalidMinimumAlignmentTooLarge, SymbolAlreadyDefined, }; @@ -195,7 +196,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( g2 } } else if cx.tcx.sess.target.arch == "x86" - && let Some(dllimport) = common::get_dllimport(cx.tcx, def_id, sym) + && let Some(dllimport) = crate::common::get_dllimport(cx.tcx, def_id, sym) { cx.declare_global( &common::i686_decorated_name( diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 14540d41e7c62..dd3f39eceadb6 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -149,7 +149,7 @@ pub unsafe fn create_module<'ll>( // Ensure the data-layout values hardcoded remain the defaults. { - let tm = crate::back::write::create_informational_target_machine(tcx.sess); + let tm = crate::back::write::create_informational_target_machine(tcx.sess, false); unsafe { llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, &tm); } diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index a3957bc52a508..7e53d32ce8cd3 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::ffi::CString; use std::path::Path; @@ -71,28 +70,6 @@ pub(crate) struct InvalidMinimumAlignmentTooLarge { #[diag(codegen_llvm_sanitizer_memtag_requires_mte)] pub(crate) struct SanitizerMemtagRequiresMte; -#[derive(Diagnostic)] -#[diag(codegen_llvm_error_writing_def_file)] -pub(crate) struct ErrorWritingDEFFile { - pub error: std::io::Error, -} - -#[derive(Diagnostic)] -#[diag(codegen_llvm_error_calling_dlltool)] -pub(crate) struct ErrorCallingDllTool<'a> { - pub dlltool_path: Cow<'a, str>, - pub error: std::io::Error, -} - -#[derive(Diagnostic)] -#[diag(codegen_llvm_dlltool_fail_import_library)] -pub(crate) struct DlltoolFailImportLibrary<'a> { - pub dlltool_path: Cow<'a, str>, - pub dlltool_args: String, - pub stdout: Cow<'a, str>, - pub stderr: Cow<'a, str>, -} - #[derive(Diagnostic)] #[diag(codegen_llvm_dynamic_linking_with_lto)] #[note] diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 41e9cfd1066b9..518a86e0cb06d 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -269,7 +269,7 @@ impl CodegenBackend for LlvmCodegenBackend { fn provide(&self, providers: &mut Providers) { providers.global_backend_features = - |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true) + |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true, false) } fn print(&self, req: &PrintRequest, out: &mut String, sess: &Session) { @@ -434,7 +434,7 @@ impl ModuleLlvm { ModuleLlvm { llmod_raw, llcx, - tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)), + tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess, false)), } } } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index af8a9be1ccbfd..9fd8ca43789dd 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -8,6 +8,7 @@ use libc::c_int; use rustc_codegen_ssa::base::wants_wasm_eh; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::small_c_str::SmallCStr; +use rustc_data_structures::unord::UnordSet; use rustc_fs_util::path_to_c_string; use rustc_middle::bug; use rustc_session::config::{PrintKind, PrintRequest}; @@ -239,40 +240,8 @@ pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> LLVMFeature<'a> { } // In LLVM neon implicitly enables fp, but we manually enable // neon when a feature only implicitly enables fp - ("aarch64", "f32mm") => { - LLVMFeature::with_dependency("f32mm", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "f64mm") => { - LLVMFeature::with_dependency("f64mm", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "fhm") => { - LLVMFeature::with_dependency("fp16fml", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "fp16") => { - LLVMFeature::with_dependency("fullfp16", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "jsconv") => { - LLVMFeature::with_dependency("jsconv", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve") => { - LLVMFeature::with_dependency("sve", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2") => { - LLVMFeature::with_dependency("sve2", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-aes") => { - LLVMFeature::with_dependency("sve2-aes", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-sm4") => { - LLVMFeature::with_dependency("sve2-sm4", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-sha3") => { - LLVMFeature::with_dependency("sve2-sha3", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-bitperm") => LLVMFeature::with_dependency( - "sve2-bitperm", - TargetFeatureFoldStrength::EnableOnly("neon"), - ), + ("aarch64", "fhm") => LLVMFeature::new("fp16fml"), + ("aarch64", "fp16") => LLVMFeature::new("fullfp16"), // In LLVM 18, `unaligned-scalar-mem` was merged with `unaligned-vector-mem` into a single feature called // `fast-unaligned-access`. In LLVM 19, it was split back out. ("riscv32" | "riscv64", "unaligned-scalar-mem") if get_version().0 == 18 => { @@ -308,11 +277,53 @@ pub fn check_tied_features( /// Used to generate cfg variables and apply features /// Must express features in the way Rust understands them pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { - let target_machine = create_informational_target_machine(sess); + let mut features = vec![]; + + // Add base features for the target + let target_machine = create_informational_target_machine(sess, true); + features.extend( + sess.target + .supported_target_features() + .iter() + .filter(|(feature, _, _)| { + // skip checking special features, as LLVM may not understands them + if RUSTC_SPECIAL_FEATURES.contains(feature) { + return true; + } + // check that all features in a given smallvec are enabled + for llvm_feature in to_llvm_features(sess, feature) { + let cstr = SmallCStr::new(llvm_feature); + if !unsafe { llvm::LLVMRustHasFeature(&target_machine, cstr.as_ptr()) } { + return false; + } + } + true + }) + .map(|(feature, _, _)| Symbol::intern(feature)), + ); + + // Add enabled features + for (enabled, feature) in + sess.opts.cg.target_feature.split(',').filter_map(|s| match s.chars().next() { + Some('+') => Some((true, Symbol::intern(&s[1..]))), + Some('-') => Some((false, Symbol::intern(&s[1..]))), + _ => None, + }) + { + if enabled { + features.extend(sess.target.implied_target_features(std::iter::once(feature))); + } else { + features.retain(|f| { + !sess.target.implied_target_features(std::iter::once(*f)).contains(&feature) + }); + } + } + + // Filter enabled features based on feature gates sess.target .supported_target_features() .iter() - .filter_map(|&(feature, gate)| { + .filter_map(|&(feature, gate, _)| { if sess.is_nightly_build() || allow_unstable || gate.is_stable() { Some(feature) } else { @@ -320,18 +331,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { } }) .filter(|feature| { - // skip checking special features, as LLVM may not understands them - if RUSTC_SPECIAL_FEATURES.contains(feature) { - return true; - } - // check that all features in a given smallvec are enabled - for llvm_feature in to_llvm_features(sess, feature) { - let cstr = SmallCStr::new(llvm_feature); - if !unsafe { llvm::LLVMRustHasFeature(&target_machine, cstr.as_ptr()) } { - return false; - } - } - true + RUSTC_SPECIAL_FEATURES.contains(feature) || features.contains(&Symbol::intern(feature)) }) .map(|feature| Symbol::intern(feature)) .collect() @@ -386,7 +386,7 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach .target .supported_target_features() .iter() - .map(|(feature, _gate)| { + .map(|(feature, _gate, _implied)| { // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings. let llvm_feature = to_llvm_features(sess, *feature).llvm_feature_name; let desc = @@ -440,7 +440,7 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach pub(crate) fn print(req: &PrintRequest, mut out: &mut String, sess: &Session) { require_inited(); - let tm = create_informational_target_machine(sess); + let tm = create_informational_target_machine(sess, false); match req.kind { PrintKind::TargetCPUs => { // SAFETY generate a C compatible string from a byte slice to pass @@ -488,7 +488,11 @@ pub fn target_cpu(sess: &Session) -> &str { /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, /// `--target` and similar). -pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec { +pub(crate) fn global_llvm_features( + sess: &Session, + diagnostics: bool, + only_base_features: bool, +) -> Vec { // Features that come earlier are overridden by conflicting features later in the string. // Typically we'll want more explicit settings to override the implicit ones, so: // @@ -548,94 +552,124 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec return None, - Some(c @ ('+' | '-')) => c, - Some(_) => { - if diagnostics { - sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); + if !only_base_features { + let supported_features = sess.target.supported_target_features(); + let (llvm_major, _, _) = get_version(); + let mut featsmap = FxHashMap::default(); + + // insert implied features + let mut all_rust_features = vec![]; + for feature in sess.opts.cg.target_feature.split(',') { + match feature.strip_prefix('+') { + Some(feature) => all_rust_features.extend( + UnordSet::from( + sess.target + .implied_target_features(std::iter::once(Symbol::intern(feature))), + ) + .to_sorted_stable_ord() + .iter() + .map(|s| format!("+{}", s.as_str())), + ), + _ => all_rust_features.push(feature.to_string()), + } + } + + let feats = all_rust_features + .iter() + .filter_map(|s| { + let enable_disable = match s.chars().next() { + None => return None, + Some(c @ ('+' | '-')) => c, + Some(_) => { + if diagnostics { + sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); + } + return None; } - return None; - } - }; + }; - let feature = backend_feature_name(sess, s)?; - // Warn against use of LLVM specific feature names and unstable features on the CLI. - if diagnostics { - let feature_state = supported_features.iter().find(|&&(v, _)| v == feature); - if feature_state.is_none() { - let rust_feature = supported_features.iter().find_map(|&(rust_feature, _)| { - let llvm_features = to_llvm_features(sess, rust_feature); - if llvm_features.contains(feature) && !llvm_features.contains(rust_feature) - { - Some(rust_feature) + let feature = backend_feature_name(sess, s)?; + // Warn against use of LLVM specific feature names and unstable features on the CLI. + if diagnostics { + let feature_state = supported_features.iter().find(|&&(v, _, _)| v == feature); + if feature_state.is_none() { + let rust_feature = + supported_features.iter().find_map(|&(rust_feature, _, _)| { + let llvm_features = to_llvm_features(sess, rust_feature); + if llvm_features.contains(feature) + && !llvm_features.contains(rust_feature) + { + Some(rust_feature) + } else { + None + } + }); + let unknown_feature = if let Some(rust_feature) = rust_feature { + UnknownCTargetFeature { + feature, + rust_feature: PossibleFeature::Some { rust_feature }, + } } else { - None - } - }); - let unknown_feature = if let Some(rust_feature) = rust_feature { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::Some { rust_feature }, - } - } else { - UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } - }; - sess.dcx().emit_warn(unknown_feature); - } else if feature_state - .is_some_and(|(_name, feature_gate)| !feature_gate.is_stable()) - { - // An unstable feature. Warn about using it. - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } + }; + sess.dcx().emit_warn(unknown_feature); + } else if feature_state + .is_some_and(|(_name, feature_gate, _implied)| !feature_gate.is_stable()) + { + // An unstable feature. Warn about using it. + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + } } - } - if diagnostics { - // FIXME(nagisa): figure out how to not allocate a full hashset here. - featsmap.insert(feature, enable_disable == '+'); - } + if diagnostics { + // FIXME(nagisa): figure out how to not allocate a full hashset here. + featsmap.insert(feature, enable_disable == '+'); + } - // rustc-specific features do not get passed down to LLVM… - if RUSTC_SPECIFIC_FEATURES.contains(&feature) { - return None; - } + // rustc-specific features do not get passed down to LLVM… + if RUSTC_SPECIFIC_FEATURES.contains(&feature) { + return None; + } - // if the target-feature is "backchain" and LLVM version is greater than 18 - // then we also need to add "+backchain" to the target-features attribute. - // otherwise, we will only add the naked `backchain` attribute to the attribute-group. - if feature == "backchain" && llvm_major < 18 { - return None; - } - // ... otherwise though we run through `to_llvm_features` when - // passing requests down to LLVM. This means that all in-language - // features also work on the command line instead of having two - // different names when the LLVM name and the Rust name differ. - let llvm_feature = to_llvm_features(sess, feature); - - Some( - std::iter::once(format!("{}{}", enable_disable, llvm_feature.llvm_feature_name)) - .chain(llvm_feature.dependency.into_iter().filter_map(move |feat| { - match (enable_disable, feat) { + // if the target-feature is "backchain" and LLVM version is greater than 18 + // then we also need to add "+backchain" to the target-features attribute. + // otherwise, we will only add the naked `backchain` attribute to the attribute-group. + if feature == "backchain" && llvm_major < 18 { + return None; + } + // ... otherwise though we run through `to_llvm_features` when + // passing requests down to LLVM. This means that all in-language + // features also work on the command line instead of having two + // different names when the LLVM name and the Rust name differ. + let llvm_feature = to_llvm_features(sess, feature); + + Some( + std::iter::once(format!( + "{}{}", + enable_disable, llvm_feature.llvm_feature_name + )) + .chain(llvm_feature.dependency.into_iter().filter_map( + move |feat| match (enable_disable, feat) { ('-' | '+', TargetFeatureFoldStrength::Both(f)) | ('+', TargetFeatureFoldStrength::EnableOnly(f)) => { Some(format!("{enable_disable}{f}")) } _ => None, - } - })), - ) - }) - .flatten(); - features.extend(feats); + }, + )), + ) + }) + .flatten(); + features.extend(feats); + + if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { + sess.dcx().emit_err(TargetFeatureDisableOrEnable { + features: f, + span: None, + missing_features: None, + }); + } + } // -Zfixed-x18 if sess.opts.unstable_opts.fixed_x18 { @@ -646,30 +680,6 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec, which didn't make - // it into a released version of LLVM yet. - // - // This doesn't use the "implicit target feature" system because it is only - // used for function attributes in other targets, which fixes this bug as - // well on the function attribute level. - if sess.target.families.contains(&"wasm".into()) { - if features.iter().any(|f| f == "+relaxed-simd") - && !features.iter().any(|f| f == "+simd128") - { - features.push("+simd128".into()); - } - } - - if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { - sess.dcx().emit_err(TargetFeatureDisableOrEnable { - features: f, - span: None, - missing_features: None, - }); - } - features } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 2767ad5ec9ce6..a7b943f250c26 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -ar_archive_writer = "0.3.0" +ar_archive_writer = "0.3.2" arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" cc = "1.0.90" diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 57d789aef80ca..80f25d42a085e 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -24,8 +24,19 @@ codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$e codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error} +codegen_ssa_dlltool_fail_import_library = + Dlltool could not create import library with {$dlltool_path} {$dlltool_args}: + {$stdout} + {$stderr} + +codegen_ssa_error_calling_dlltool = + Error calling dlltool '{$dlltool_path}': {$error} + codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error} +codegen_ssa_error_writing_def_file = + Error writing .DEF file: {$error} + codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 4f93b7b23c6e6..ce55d99f506d5 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -1,4 +1,6 @@ +use std::env; use std::error::Error; +use std::ffi::OsString; use std::fs::{self, File}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; @@ -9,7 +11,6 @@ use object::read::archive::ArchiveFile; use object::read::macho::FatArch; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; -use rustc_session::cstore::DllImport; use rustc_session::Session; use rustc_span::symbol::Symbol; use tempfile::Builder as TempFileBuilder; @@ -17,6 +18,7 @@ use tempfile::Builder as TempFileBuilder; use super::metadata::search_for_section; // Re-exporting for rustc_codegen_llvm::back::archive pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind}; +use crate::errors::{DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorWritingDEFFile}; pub trait ArchiveBuilderBuilder { fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box; @@ -30,10 +32,9 @@ pub trait ArchiveBuilderBuilder { &self, sess: &Session, lib_name: &str, - dll_imports: &[DllImport], - tmpdir: &Path, - is_direct_dependency: bool, - ) -> PathBuf; + import_name_and_ordinal_vector: Vec<(String, Option)>, + output_path: &Path, + ); fn extract_bundled_libs<'a>( &'a self, @@ -72,6 +73,130 @@ pub trait ArchiveBuilderBuilder { } } +pub fn create_mingw_dll_import_lib( + sess: &Session, + lib_name: &str, + import_name_and_ordinal_vector: Vec<(String, Option)>, + output_path: &Path, +) { + let def_file_path = output_path.with_extension("def"); + + let def_file_content = format!( + "EXPORTS\n{}", + import_name_and_ordinal_vector + .into_iter() + .map(|(name, ordinal)| { + match ordinal { + Some(n) => format!("{name} @{n} NONAME"), + None => name, + } + }) + .collect::>() + .join("\n") + ); + + match std::fs::write(&def_file_path, def_file_content) { + Ok(_) => {} + Err(e) => { + sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e }); + } + }; + + // --no-leading-underscore: For the `import_name_type` feature to work, we need to be + // able to control the *exact* spelling of each of the symbols that are being imported: + // hence we don't want `dlltool` adding leading underscores automatically. + let dlltool = find_binutils_dlltool(sess); + let temp_prefix = { + let mut path = PathBuf::from(&output_path); + path.pop(); + path.push(lib_name); + path + }; + // dlltool target architecture args from: + // https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69 + let (dlltool_target_arch, dlltool_target_bitness) = match sess.target.arch.as_ref() { + "x86_64" => ("i386:x86-64", "--64"), + "x86" => ("i386", "--32"), + "aarch64" => ("arm64", "--64"), + "arm" => ("arm", "--32"), + _ => panic!("unsupported arch {}", sess.target.arch), + }; + let mut dlltool_cmd = std::process::Command::new(&dlltool); + dlltool_cmd + .arg("-d") + .arg(def_file_path) + .arg("-D") + .arg(lib_name) + .arg("-l") + .arg(&output_path) + .arg("-m") + .arg(dlltool_target_arch) + .arg("-f") + .arg(dlltool_target_bitness) + .arg("--no-leading-underscore") + .arg("--temp-prefix") + .arg(temp_prefix); + + match dlltool_cmd.output() { + Err(e) => { + sess.dcx().emit_fatal(ErrorCallingDllTool { + dlltool_path: dlltool.to_string_lossy(), + error: e, + }); + } + // dlltool returns '0' on failure, so check for error output instead. + Ok(output) if !output.stderr.is_empty() => { + sess.dcx().emit_fatal(DlltoolFailImportLibrary { + dlltool_path: dlltool.to_string_lossy(), + dlltool_args: dlltool_cmd + .get_args() + .map(|arg| arg.to_string_lossy()) + .collect::>() + .join(" "), + stdout: String::from_utf8_lossy(&output.stdout), + stderr: String::from_utf8_lossy(&output.stderr), + }) + } + _ => {} + } +} + +fn find_binutils_dlltool(sess: &Session) -> OsString { + assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc); + if let Some(dlltool_path) = &sess.opts.cg.dlltool { + return dlltool_path.clone().into_os_string(); + } + + let tool_name: OsString = if sess.host.options.is_like_windows { + // If we're compiling on Windows, always use "dlltool.exe". + "dlltool.exe" + } else { + // On other platforms, use the architecture-specific name. + match sess.target.arch.as_ref() { + "x86_64" => "x86_64-w64-mingw32-dlltool", + "x86" => "i686-w64-mingw32-dlltool", + "aarch64" => "aarch64-w64-mingw32-dlltool", + + // For non-standard architectures (e.g., aarch32) fallback to "dlltool". + _ => "dlltool", + } + } + .into(); + + // NOTE: it's not clear how useful it is to explicitly search PATH. + for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { + let full_path = dir.join(&tool_name); + if full_path.is_file() { + return full_path.into_os_string(); + } + } + + // The user didn't specify the location of the dlltool binary, and we weren't able + // to find the appropriate one on the PATH. Just return the name of the tool + // and let the invocation fail with a hopefully useful error message. + tool_name +} + pub trait ArchiveBuilder { fn add_file(&mut self, path: &Path); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 653895380bebc..45a92fd03d547 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -51,7 +51,8 @@ use super::linker::{self, Linker}; use super::metadata::{create_wrapper_file, MetadataPosition}; use super::rpath::{self, RPathConfig}; use crate::{ - errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib, + common, errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, + NativeLib, }; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { @@ -390,17 +391,13 @@ fn link_rlib<'a>( } } - for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())? - { - let output_path = archive_builder_builder.create_dll_import_lib( - sess, - &raw_dylib_name, - &raw_dylib_imports, - tmpdir.as_ref(), - true, - ); - + for output_path in create_dll_import_libs( + sess, + archive_builder_builder, + codegen_results.crate_info.used_libraries.iter(), + tmpdir.as_ref(), + true, + )? { ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| { sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: output_path, error }); }); @@ -488,6 +485,47 @@ fn collate_raw_dylibs<'a>( .collect()) } +fn create_dll_import_libs<'a>( + sess: &Session, + archive_builder_builder: &dyn ArchiveBuilderBuilder, + used_libraries: impl IntoIterator, + tmpdir: &Path, + is_direct_dependency: bool, +) -> Result, ErrorGuaranteed> { + Ok(collate_raw_dylibs(sess, used_libraries)? + .into_iter() + .map(|(raw_dylib_name, raw_dylib_imports)| { + let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; + let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib")); + + let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(&sess.target); + + let import_name_and_ordinal_vector: Vec<(String, Option)> = raw_dylib_imports + .iter() + .map(|import: &DllImport| { + if sess.target.arch == "x86" { + ( + common::i686_decorated_name(import, mingw_gnu_toolchain, false), + import.ordinal(), + ) + } else { + (import.name.to_string(), import.ordinal()) + } + }) + .collect(); + + archive_builder_builder.create_dll_import_lib( + sess, + &raw_dylib_name, + import_name_and_ordinal_vector, + &output_path, + ); + + output_path + }) + .collect()) +} + /// Create a static archive. /// /// This is essentially the same thing as an rlib, but it also involves adding all of the upstream @@ -2305,16 +2343,14 @@ fn linker_with_args( ); // Link with the import library generated for any raw-dylib functions. - for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())? - { - cmd.add_object(&archive_builder_builder.create_dll_import_lib( - sess, - &raw_dylib_name, - &raw_dylib_imports, - tmpdir, - true, - )); + for output_path in create_dll_import_libs( + sess, + archive_builder_builder, + codegen_results.crate_info.used_libraries.iter(), + tmpdir, + true, + )? { + cmd.add_object(&output_path); } // As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case // they are used within inlined functions or instantiated generic functions. We do this *after* @@ -2339,16 +2375,14 @@ fn linker_with_args( .flatten() .collect::>(); native_libraries_from_nonstatics.sort_unstable_by(|a, b| a.name.as_str().cmp(b.name.as_str())); - for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, native_libraries_from_nonstatics)? - { - cmd.add_object(&archive_builder_builder.create_dll_import_lib( - sess, - &raw_dylib_name, - &raw_dylib_imports, - tmpdir, - false, - )); + for output_path in create_dll_import_libs( + sess, + archive_builder_builder, + native_libraries_from_nonstatics, + tmpdir, + false, + )? { + cmd.add_object(&output_path); } // Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index a972c0cd99d10..bfb1d217eae8a 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -4,7 +4,9 @@ use rustc_hir::LangItem; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_middle::{bug, mir, span_bug}; +use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType}; use rustc_span::Span; +use rustc_target::spec::Target; use crate::traits::*; @@ -176,3 +178,66 @@ pub fn asm_const_to_str<'tcx>( _ => span_bug!(sp, "asm const has bad type {}", ty_and_layout.ty), } } + +pub fn is_mingw_gnu_toolchain(target: &Target) -> bool { + target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty() +} + +pub fn i686_decorated_name( + dll_import: &DllImport, + mingw: bool, + disable_name_mangling: bool, +) -> String { + let name = dll_import.name.as_str(); + + let (add_prefix, add_suffix) = match dll_import.import_name_type { + Some(PeImportNameType::NoPrefix) => (false, true), + Some(PeImportNameType::Undecorated) => (false, false), + _ => (true, true), + }; + + // Worst case: +1 for disable name mangling, +1 for prefix, +4 for suffix (@@__). + let mut decorated_name = String::with_capacity(name.len() + 6); + + if disable_name_mangling { + // LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be disabled. + decorated_name.push('\x01'); + } + + let prefix = if add_prefix && dll_import.is_fn { + match dll_import.calling_convention { + DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None, + DllCallingConvention::Stdcall(_) => (!mingw + || dll_import.import_name_type == Some(PeImportNameType::Decorated)) + .then_some('_'), + DllCallingConvention::Fastcall(_) => Some('@'), + } + } else if !dll_import.is_fn && !mingw { + // For static variables, prefix with '_' on MSVC. + Some('_') + } else { + None + }; + if let Some(prefix) = prefix { + decorated_name.push(prefix); + } + + decorated_name.push_str(name); + + if add_suffix && dll_import.is_fn { + use std::fmt::Write; + + match dll_import.calling_convention { + DllCallingConvention::C => {} + DllCallingConvention::Stdcall(arg_list_size) + | DllCallingConvention::Fastcall(arg_list_size) => { + write!(&mut decorated_name, "@{arg_list_size}").unwrap(); + } + DllCallingConvention::Vectorcall(arg_list_size) => { + write!(&mut decorated_name, "@@{arg_list_size}").unwrap(); + } + } + } + + decorated_name +} diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 2d1eae630cf16..94bf0ab34e21b 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1026,6 +1026,28 @@ pub struct FailedToGetLayout<'tcx> { pub err: LayoutError<'tcx>, } +#[derive(Diagnostic)] +#[diag(codegen_ssa_dlltool_fail_import_library)] +pub(crate) struct DlltoolFailImportLibrary<'a> { + pub dlltool_path: Cow<'a, str>, + pub dlltool_args: String, + pub stdout: Cow<'a, str>, + pub stderr: Cow<'a, str>, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_error_writing_def_file)] +pub(crate) struct ErrorWritingDEFFile { + pub error: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_error_calling_dlltool)] +pub(crate) struct ErrorCallingDllTool<'a> { + pub dlltool_path: Cow<'a, str>, + pub error: std::io::Error, +} + #[derive(Diagnostic)] #[diag(codegen_ssa_error_creating_remark_dir)] pub struct ErrorCreatingRemarkDir { diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 127244a34f8f0..cf8f7fa25d856 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,11 +1,12 @@ use rustc_ast::ast; use rustc_attr::InstructionSetAttr; use rustc_data_structures::fx::FxIndexSet; -use rustc_data_structures::unord::UnordMap; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_middle::bug; +use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; @@ -18,7 +19,7 @@ pub fn from_target_feature( tcx: TyCtxt<'_>, attr: &ast::Attribute, supported_target_features: &UnordMap>, - target_features: &mut Vec, + target_features: &mut Vec, ) { let Some(list) = attr.meta_item_list() else { return }; let bad_item = |span| { @@ -30,6 +31,7 @@ pub fn from_target_feature( .emit(); }; let rust_features = tcx.features(); + let mut added_target_features = Vec::new(); for item in list { // Only `enable = ...` is accepted in the meta-item list. if !item.has_name(sym::enable) { @@ -44,7 +46,7 @@ pub fn from_target_feature( }; // We allow comma separation to enable multiple features. - target_features.extend(value.as_str().split(',').filter_map(|feature| { + added_target_features.extend(value.as_str().split(',').filter_map(|feature| { let Some(feature_gate) = supported_target_features.get(feature) else { let msg = format!("the feature named `{feature}` is not valid for this target"); let mut err = tcx.dcx().struct_span_err(item.span(), msg); @@ -98,13 +100,26 @@ pub fn from_target_feature( })); } - for (feature, requires) in tcx.sess.target.implicit_target_features() { - if target_features.iter().any(|f| f.as_str() == *feature) - && !target_features.iter().any(|f| f.as_str() == *requires) - { - target_features.push(Symbol::intern(requires)); - } + // Add explicit features + target_features.extend( + added_target_features.iter().copied().map(|name| TargetFeature { name, implied: false }), + ); + + // Add implied features + let mut implied_target_features = UnordSet::new(); + for feature in added_target_features.iter() { + implied_target_features.extend(tcx.implied_target_features(*feature).clone()); } + for feature in added_target_features.iter() { + implied_target_features.remove(feature); + } + target_features.extend( + implied_target_features + .into_sorted_stable_ord() + .iter() + .copied() + .map(|name| TargetFeature { name, implied: true }), + ) } /// Computes the set of target features used in a function for the purposes of @@ -113,7 +128,7 @@ fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet { let mut target_features = tcx.sess.unstable_target_features.clone(); if tcx.def_kind(did).has_codegen_attrs() { let attrs = tcx.codegen_fn_attrs(did); - target_features.extend(&attrs.target_features); + target_features.extend(attrs.target_features.iter().map(|feature| feature.name)); match attrs.instruction_set { None => {} Some(InstructionSetAttr::ArmA32) => { @@ -158,10 +173,14 @@ pub(crate) fn provide(providers: &mut Providers) { .target .supported_target_features() .iter() - .map(|&(a, b)| (a.to_string(), b.as_feature_name())) + .map(|&(a, b, _)| (a.to_string(), b.as_feature_name())) .collect() } }, + implied_target_features: |tcx, feature| { + UnordSet::from(tcx.sess.target.implied_target_features(std::iter::once(feature))) + .into_sorted_stable_ord() + }, asm_target_features, ..*providers } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 2c5147678e8cb..b5f3d07d90b35 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -317,19 +317,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { && attrs .target_features .iter() - .any(|feature| !self.tcx.sess.target_features.contains(feature)) + .any(|feature| !self.tcx.sess.target_features.contains(&feature.name)) { throw_ub_custom!( fluent::const_eval_unavailable_target_features_for_fn, unavailable_feats = attrs .target_features .iter() - .filter(|&feature| !self.tcx.sess.target_features.contains(feature)) + .filter(|&feature| !feature.implied + && !self.tcx.sess.target_features.contains(&feature.name)) .fold(String::new(), |mut s, feature| { if !s.is_empty() { s.push_str(", "); } - s.push_str(feature.as_str()); + s.push_str(feature.name.as_str()); s }), ); diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index ff6a3a9c12d36..b7d290e58d22b 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -28,7 +28,7 @@ pub struct CodegenFnAttrs { pub link_ordinal: Option, /// The `#[target_feature(enable = "...")]` attribute and the enabled /// features (only enabled features are supported right now). - pub target_features: Vec, + pub target_features: Vec, /// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found. pub linkage: Option, /// The `#[linkage = "..."]` attribute on foreign items and the value we found. @@ -51,6 +51,15 @@ pub struct CodegenFnAttrs { pub patchable_function_entry: Option, } +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct TargetFeature { + /// The name of the target feature (e.g. "avx") + pub name: Symbol, + /// The feature is implied by another feature, rather than explicitly added by the + /// `#[target_feature]` attribute + pub implied: bool, +} + #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub struct PatchableFunctionEntry { /// Nops to prepend to the function diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index c22c2e985abba..5b114c9515c19 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -2183,6 +2183,12 @@ rustc_queries! { desc { "looking up supported target features" } } + query implied_target_features(feature: Symbol) -> &'tcx Vec { + arena_cache + eval_always + desc { "looking up implied target features" } + } + query features_query(_: ()) -> &'tcx rustc_feature::Features { feedable desc { "looking up enabled feature gates" } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 48018fcaa36df..54a4204da71e8 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -5,6 +5,7 @@ use std::ops::Bound; use rustc_errors::DiagArgValue; use rustc_hir::def::DefKind; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; +use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::mir::BorrowKind; use rustc_middle::span_bug; use rustc_middle::thir::visit::Visitor; @@ -31,7 +32,7 @@ struct UnsafetyVisitor<'a, 'tcx> { safety_context: SafetyContext, /// The `#[target_feature]` attributes of the body. Used for checking /// calls to functions with `#[target_feature]` (RFC 2396). - body_target_features: &'tcx [Symbol], + body_target_features: &'tcx [TargetFeature], /// When inside the LHS of an assignment to a field, this is the type /// of the LHS and the span of the assignment expression. assignment_info: Option>, @@ -442,14 +443,21 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { // is_like_wasm check in hir_analysis/src/collect.rs let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features; if !self.tcx.sess.target.options.is_like_wasm - && !callee_features - .iter() - .all(|feature| self.body_target_features.contains(feature)) + && !callee_features.iter().all(|feature| { + self.body_target_features.iter().any(|f| f.name == feature.name) + }) { let missing: Vec<_> = callee_features .iter() .copied() - .filter(|feature| !self.body_target_features.contains(feature)) + .filter(|feature| { + !feature.implied + && !self + .body_target_features + .iter() + .any(|body_feature| body_feature.name == feature.name) + }) + .map(|feature| feature.name) .collect(); let build_enabled = self .tcx diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index f30732e6aaf3b..0f012242c3738 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -479,7 +479,9 @@ impl<'tcx> Inliner<'tcx> { return Err("incompatible instruction set"); } - if callee_attrs.target_features != self.codegen_fn_attrs.target_features { + let callee_feature_names = callee_attrs.target_features.iter().map(|f| f.name); + let this_feature_names = self.codegen_fn_attrs.target_features.iter().map(|f| f.name); + if callee_feature_names.ne(this_feature_names) { // In general it is not correct to inline a callee with target features that are a // subset of the caller. This is because the callee might contain calls, and the ABI of // those calls depends on the target features of the surrounding function. By moving a diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 4e2617c467949..da66ba270b33c 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_span::symbol::{sym, Symbol}; /// Features that control behaviour of rustc, rather than the codegen. @@ -53,136 +54,154 @@ impl Stability { // // Stabilizing a target feature requires t-lang approval. -const ARM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +type ImpliedFeatures = &'static [&'static str]; + +const ARM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("aclass", Unstable(sym::arm_target_feature)), - ("aes", Unstable(sym::arm_target_feature)), - ("crc", Unstable(sym::arm_target_feature)), - ("d32", Unstable(sym::arm_target_feature)), - ("dotprod", Unstable(sym::arm_target_feature)), - ("dsp", Unstable(sym::arm_target_feature)), - ("fp-armv8", Unstable(sym::arm_target_feature)), - ("i8mm", Unstable(sym::arm_target_feature)), - ("mclass", Unstable(sym::arm_target_feature)), - ("neon", Unstable(sym::arm_target_feature)), - ("rclass", Unstable(sym::arm_target_feature)), - ("sha2", Unstable(sym::arm_target_feature)), + ("aclass", Unstable(sym::arm_target_feature), &[]), + ("aes", Unstable(sym::arm_target_feature), &["neon"]), + ("crc", Unstable(sym::arm_target_feature), &[]), + ("d32", Unstable(sym::arm_target_feature), &[]), + ("dotprod", Unstable(sym::arm_target_feature), &["neon"]), + ("dsp", Unstable(sym::arm_target_feature), &[]), + ("fp-armv8", Unstable(sym::arm_target_feature), &["vfp4"]), + ("i8mm", Unstable(sym::arm_target_feature), &["neon"]), + ("mclass", Unstable(sym::arm_target_feature), &[]), + ("neon", Unstable(sym::arm_target_feature), &["vfp3"]), + ("rclass", Unstable(sym::arm_target_feature), &[]), + ("sha2", Unstable(sym::arm_target_feature), &["neon"]), // This is needed for inline assembly, but shouldn't be stabilized as-is // since it should be enabled per-function using #[instruction_set], not // #[target_feature]. - ("thumb-mode", Unstable(sym::arm_target_feature)), - ("thumb2", Unstable(sym::arm_target_feature)), - ("trustzone", Unstable(sym::arm_target_feature)), - ("v5te", Unstable(sym::arm_target_feature)), - ("v6", Unstable(sym::arm_target_feature)), - ("v6k", Unstable(sym::arm_target_feature)), - ("v6t2", Unstable(sym::arm_target_feature)), - ("v7", Unstable(sym::arm_target_feature)), - ("v8", Unstable(sym::arm_target_feature)), - ("vfp2", Unstable(sym::arm_target_feature)), - ("vfp3", Unstable(sym::arm_target_feature)), - ("vfp4", Unstable(sym::arm_target_feature)), - ("virtualization", Unstable(sym::arm_target_feature)), + ("thumb-mode", Unstable(sym::arm_target_feature), &[]), + ("thumb2", Unstable(sym::arm_target_feature), &[]), + ("trustzone", Unstable(sym::arm_target_feature), &[]), + ("v5te", Unstable(sym::arm_target_feature), &[]), + ("v6", Unstable(sym::arm_target_feature), &["v5te"]), + ("v6k", Unstable(sym::arm_target_feature), &["v6"]), + ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "thumb2"]), + ("v7", Unstable(sym::arm_target_feature), &["v6t2"]), + ("v8", Unstable(sym::arm_target_feature), &["v7"]), + ("vfp2", Unstable(sym::arm_target_feature), &[]), + ("vfp3", Unstable(sym::arm_target_feature), &["vfp2", "d32"]), + ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), + ("virtualization", Unstable(sym::arm_target_feature), &[]), // tidy-alphabetical-end ]; -const AARCH64_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const AARCH64_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start // FEAT_AES & FEAT_PMULL - ("aes", Stable), + ("aes", Stable, &["neon"]), // FEAT_BF16 - ("bf16", Stable), + ("bf16", Stable, &[]), // FEAT_BTI - ("bti", Stable), + ("bti", Stable, &[]), // FEAT_CRC - ("crc", Stable), + ("crc", Stable, &[]), // FEAT_DIT - ("dit", Stable), + ("dit", Stable, &[]), // FEAT_DotProd - ("dotprod", Stable), + ("dotprod", Stable, &["neon"]), // FEAT_DPB - ("dpb", Stable), + ("dpb", Stable, &[]), // FEAT_DPB2 - ("dpb2", Stable), + ("dpb2", Stable, &["dpb"]), // FEAT_F32MM - ("f32mm", Stable), + ("f32mm", Stable, &["sve"]), // FEAT_F64MM - ("f64mm", Stable), + ("f64mm", Stable, &["sve"]), // FEAT_FCMA - ("fcma", Stable), + ("fcma", Stable, &["neon"]), // FEAT_FHM - ("fhm", Stable), + ("fhm", Stable, &["fp16"]), // FEAT_FLAGM - ("flagm", Stable), + ("flagm", Stable, &[]), // FEAT_FP16 - ("fp16", Stable), + // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 + ("fp16", Stable, &["neon"]), // FEAT_FRINTTS - ("frintts", Stable), + ("frintts", Stable, &[]), // FEAT_I8MM - ("i8mm", Stable), + ("i8mm", Stable, &[]), // FEAT_JSCVT - ("jsconv", Stable), + // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 + ("jsconv", Stable, &["neon"]), // FEAT_LOR - ("lor", Stable), + ("lor", Stable, &[]), // FEAT_LSE - ("lse", Stable), + ("lse", Stable, &[]), // FEAT_MTE & FEAT_MTE2 - ("mte", Stable), + ("mte", Stable, &[]), // FEAT_AdvSimd & FEAT_FP - ("neon", Stable), + ("neon", Stable, &[]), // FEAT_PAUTH (address authentication) - ("paca", Stable), + ("paca", Stable, &[]), // FEAT_PAUTH (generic authentication) - ("pacg", Stable), + ("pacg", Stable, &[]), // FEAT_PAN - ("pan", Stable), + ("pan", Stable, &[]), // FEAT_PMUv3 - ("pmuv3", Stable), + ("pmuv3", Stable, &[]), // FEAT_RAND - ("rand", Stable), + ("rand", Stable, &[]), // FEAT_RAS & FEAT_RASv1p1 - ("ras", Stable), + ("ras", Stable, &[]), // FEAT_RCPC - ("rcpc", Stable), + ("rcpc", Stable, &[]), // FEAT_RCPC2 - ("rcpc2", Stable), + ("rcpc2", Stable, &["rcpc"]), // FEAT_RDM - ("rdm", Stable), + ("rdm", Stable, &["neon"]), // FEAT_SB - ("sb", Stable), + ("sb", Stable, &[]), // FEAT_SHA1 & FEAT_SHA256 - ("sha2", Stable), + ("sha2", Stable, &["neon"]), // FEAT_SHA512 & FEAT_SHA3 - ("sha3", Stable), + ("sha3", Stable, &["sha2"]), // FEAT_SM3 & FEAT_SM4 - ("sm4", Stable), + ("sm4", Stable, &["neon"]), // FEAT_SPE - ("spe", Stable), + ("spe", Stable, &[]), // FEAT_SSBS & FEAT_SSBS2 - ("ssbs", Stable), + ("ssbs", Stable, &[]), // FEAT_SVE - ("sve", Stable), + // It was decided that SVE requires Neon: https://github.com/rust-lang/rust/pull/91608 + // + // LLVM doesn't enable Neon for SVE. ARM indicates that they're separate, but probably always + // exist together: https://developer.arm.com/documentation/102340/0100/New-features-in-SVE2 + // + // "For backwards compatibility, Neon and VFP are required in the latest architectures." + ("sve", Stable, &["neon"]), // FEAT_SVE2 - ("sve2", Stable), + ("sve2", Stable, &["sve"]), // FEAT_SVE2_AES - ("sve2-aes", Stable), + ("sve2-aes", Stable, &["sve2", "aes"]), // FEAT_SVE2_BitPerm - ("sve2-bitperm", Stable), + ("sve2-bitperm", Stable, &["sve2"]), // FEAT_SVE2_SHA3 - ("sve2-sha3", Stable), + ("sve2-sha3", Stable, &["sve2", "sha3"]), // FEAT_SVE2_SM4 - ("sve2-sm4", Stable), + ("sve2-sm4", Stable, &["sve2", "sm4"]), // FEAT_TME - ("tme", Stable), - ("v8.1a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.2a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.3a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.4a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.5a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.6a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.7a", Unstable(sym::aarch64_ver_target_feature)), + ("tme", Stable, &[]), + ( + "v8.1a", + Unstable(sym::aarch64_ver_target_feature), + &["crc", "lse", "rdm", "pan", "lor", "vh"], + ), + ("v8.2a", Unstable(sym::aarch64_ver_target_feature), &["v8.1a", "ras", "dpb"]), + ( + "v8.3a", + Unstable(sym::aarch64_ver_target_feature), + &["v8.2a", "rcpc", "paca", "pacg", "jsconv"], + ), + ("v8.4a", Unstable(sym::aarch64_ver_target_feature), &["v8.3a", "dotprod", "dit", "flagm"]), + ("v8.5a", Unstable(sym::aarch64_ver_target_feature), &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), + ("v8.6a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "bf16", "i8mm"]), + ("v8.7a", Unstable(sym::aarch64_ver_target_feature), &[]), // FEAT_VHE - ("vh", Stable), + ("vh", Stable, &[]), // tidy-alphabetical-end ]; @@ -190,224 +209,223 @@ const AARCH64_TIED_FEATURES: &[&[&str]] = &[ &["paca", "pacg"], // Together these represent `pauth` in LLVM ]; -const X86_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const X86_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("adx", Stable), - ("aes", Stable), - ("amx-bf16", Unstable(sym::x86_amx_intrinsics)), - ("amx-complex", Unstable(sym::x86_amx_intrinsics)), - ("amx-fp16", Unstable(sym::x86_amx_intrinsics)), - ("amx-int8", Unstable(sym::x86_amx_intrinsics)), - ("amx-tile", Unstable(sym::x86_amx_intrinsics)), - ("avx", Stable), - ("avx2", Stable), - ("avx512bf16", Unstable(sym::avx512_target_feature)), - ("avx512bitalg", Unstable(sym::avx512_target_feature)), - ("avx512bw", Unstable(sym::avx512_target_feature)), - ("avx512cd", Unstable(sym::avx512_target_feature)), - ("avx512dq", Unstable(sym::avx512_target_feature)), - ("avx512f", Unstable(sym::avx512_target_feature)), - ("avx512fp16", Unstable(sym::avx512_target_feature)), - ("avx512ifma", Unstable(sym::avx512_target_feature)), - ("avx512vbmi", Unstable(sym::avx512_target_feature)), - ("avx512vbmi2", Unstable(sym::avx512_target_feature)), - ("avx512vl", Unstable(sym::avx512_target_feature)), - ("avx512vnni", Unstable(sym::avx512_target_feature)), - ("avx512vp2intersect", Unstable(sym::avx512_target_feature)), - ("avx512vpopcntdq", Unstable(sym::avx512_target_feature)), - ("avxifma", Unstable(sym::avx512_target_feature)), - ("avxneconvert", Unstable(sym::avx512_target_feature)), - ("avxvnni", Unstable(sym::avx512_target_feature)), - ("avxvnniint16", Unstable(sym::avx512_target_feature)), - ("avxvnniint8", Unstable(sym::avx512_target_feature)), - ("bmi1", Stable), - ("bmi2", Stable), - ("cmpxchg16b", Stable), - ("ermsb", Unstable(sym::ermsb_target_feature)), - ("f16c", Stable), - ("fma", Stable), - ("fxsr", Stable), - ("gfni", Unstable(sym::avx512_target_feature)), - ("lahfsahf", Unstable(sym::lahfsahf_target_feature)), - ("lzcnt", Stable), - ("movbe", Stable), - ("pclmulqdq", Stable), - ("popcnt", Stable), - ("prfchw", Unstable(sym::prfchw_target_feature)), - ("rdrand", Stable), - ("rdseed", Stable), - ("rtm", Unstable(sym::rtm_target_feature)), - ("sha", Stable), - ("sha512", Unstable(sym::sha512_sm_x86)), - ("sm3", Unstable(sym::sha512_sm_x86)), - ("sm4", Unstable(sym::sha512_sm_x86)), - ("sse", Stable), - ("sse2", Stable), - ("sse3", Stable), - ("sse4.1", Stable), - ("sse4.2", Stable), - ("sse4a", Unstable(sym::sse4a_target_feature)), - ("ssse3", Stable), - ("tbm", Unstable(sym::tbm_target_feature)), - ("vaes", Unstable(sym::avx512_target_feature)), - ("vpclmulqdq", Unstable(sym::avx512_target_feature)), - ("xop", Unstable(sym::xop_target_feature)), - ("xsave", Stable), - ("xsavec", Stable), - ("xsaveopt", Stable), - ("xsaves", Stable), + ("adx", Stable, &[]), + ("aes", Stable, &["sse2"]), + ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-complex", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), + ("avx", Stable, &["sse4.2"]), + ("avx2", Stable, &["avx"]), + ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), + ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), + ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]), + ("bmi1", Stable, &[]), + ("bmi2", Stable, &[]), + ("cmpxchg16b", Stable, &[]), + ("ermsb", Unstable(sym::ermsb_target_feature), &[]), + ("f16c", Stable, &["avx"]), + ("fma", Stable, &["avx"]), + ("fxsr", Stable, &[]), + ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]), + ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), + ("lzcnt", Stable, &[]), + ("movbe", Stable, &[]), + ("pclmulqdq", Stable, &[]), + ("popcnt", Stable, &[]), + ("prfchw", Unstable(sym::prfchw_target_feature), &[]), + ("rdrand", Stable, &[]), + ("rdseed", Stable, &[]), + ("rtm", Unstable(sym::rtm_target_feature), &[]), + ("sha", Stable, &["sse2"]), + ("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]), + ("sm3", Unstable(sym::sha512_sm_x86), &["avx"]), + ("sm4", Unstable(sym::sha512_sm_x86), &["avx2"]), + ("sse", Stable, &[]), + ("sse2", Stable, &["sse"]), + ("sse3", Stable, &["sse2"]), + ("sse4.1", Stable, &["ssse3"]), + ("sse4.2", Stable, &["sse4.1"]), + ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]), + ("ssse3", Stable, &["sse3"]), + ("tbm", Unstable(sym::tbm_target_feature), &[]), + ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]), + ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), + ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), + ("xsave", Stable, &[]), + ("xsavec", Stable, &["xsave"]), + ("xsaveopt", Stable, &["xsave"]), + ("xsaves", Stable, &["xsave"]), // tidy-alphabetical-end ]; -const HEXAGON_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const HEXAGON_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("hvx", Unstable(sym::hexagon_target_feature)), - ("hvx-length128b", Unstable(sym::hexagon_target_feature)), + ("hvx", Unstable(sym::hexagon_target_feature), &[]), + ("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]), // tidy-alphabetical-end ]; -const POWERPC_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const POWERPC_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("altivec", Unstable(sym::powerpc_target_feature)), - ("power10-vector", Unstable(sym::powerpc_target_feature)), - ("power8-altivec", Unstable(sym::powerpc_target_feature)), - ("power8-vector", Unstable(sym::powerpc_target_feature)), - ("power9-altivec", Unstable(sym::powerpc_target_feature)), - ("power9-vector", Unstable(sym::powerpc_target_feature)), - ("vsx", Unstable(sym::powerpc_target_feature)), + ("altivec", Unstable(sym::powerpc_target_feature), &[]), + ("power10-vector", Unstable(sym::powerpc_target_feature), &["power9-vector"]), + ("power8-altivec", Unstable(sym::powerpc_target_feature), &["altivec"]), + ("power8-vector", Unstable(sym::powerpc_target_feature), &["vsx", "power8-altivec"]), + ("power9-altivec", Unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power9-vector", Unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), + ("vsx", Unstable(sym::powerpc_target_feature), &["altivec"]), // tidy-alphabetical-end ]; -const MIPS_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const MIPS_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("fp64", Unstable(sym::mips_target_feature)), - ("msa", Unstable(sym::mips_target_feature)), - ("virt", Unstable(sym::mips_target_feature)), + ("fp64", Unstable(sym::mips_target_feature), &[]), + ("msa", Unstable(sym::mips_target_feature), &[]), + ("virt", Unstable(sym::mips_target_feature), &[]), // tidy-alphabetical-end ]; -const RISCV_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const RISCV_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("a", Stable), - ("c", Stable), - ("d", Unstable(sym::riscv_target_feature)), - ("e", Unstable(sym::riscv_target_feature)), - ("f", Unstable(sym::riscv_target_feature)), - ("m", Stable), - ("relax", Unstable(sym::riscv_target_feature)), - ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature)), - ("v", Unstable(sym::riscv_target_feature)), - ("zba", Stable), - ("zbb", Stable), - ("zbc", Stable), - ("zbkb", Stable), - ("zbkc", Stable), - ("zbkx", Stable), - ("zbs", Stable), - ("zdinx", Unstable(sym::riscv_target_feature)), - ("zfh", Unstable(sym::riscv_target_feature)), - ("zfhmin", Unstable(sym::riscv_target_feature)), - ("zfinx", Unstable(sym::riscv_target_feature)), - ("zhinx", Unstable(sym::riscv_target_feature)), - ("zhinxmin", Unstable(sym::riscv_target_feature)), - ("zk", Stable), - ("zkn", Stable), - ("zknd", Stable), - ("zkne", Stable), - ("zknh", Stable), - ("zkr", Stable), - ("zks", Stable), - ("zksed", Stable), - ("zksh", Stable), - ("zkt", Stable), + ("a", Stable, &[]), + ("c", Stable, &[]), + ("d", Unstable(sym::riscv_target_feature), &["f"]), + ("e", Unstable(sym::riscv_target_feature), &[]), + ("f", Unstable(sym::riscv_target_feature), &[]), + ("m", Stable, &[]), + ("relax", Unstable(sym::riscv_target_feature), &[]), + ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature), &[]), + ("v", Unstable(sym::riscv_target_feature), &[]), + ("zba", Stable, &[]), + ("zbb", Stable, &[]), + ("zbc", Stable, &[]), + ("zbkb", Stable, &[]), + ("zbkc", Stable, &[]), + ("zbkx", Stable, &[]), + ("zbs", Stable, &[]), + ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]), + ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]), + ("zfinx", Unstable(sym::riscv_target_feature), &[]), + ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]), + ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zk", Stable, &["zkn", "zkr", "zkt"]), + ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), + ("zknd", Stable, &[]), + ("zkne", Stable, &[]), + ("zknh", Stable, &[]), + ("zkr", Stable, &[]), + ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), + ("zksed", Stable, &[]), + ("zksh", Stable, &[]), + ("zkt", Stable, &[]), // tidy-alphabetical-end ]; -const WASM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const WASM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("atomics", Unstable(sym::wasm_target_feature)), - ("bulk-memory", Stable), - ("exception-handling", Unstable(sym::wasm_target_feature)), - ("extended-const", Stable), - ("multivalue", Unstable(sym::wasm_target_feature)), - ("mutable-globals", Stable), - ("nontrapping-fptoint", Stable), - ("reference-types", Unstable(sym::wasm_target_feature)), - ("relaxed-simd", Stable), - ("sign-ext", Stable), - ("simd128", Stable), + ("atomics", Unstable(sym::wasm_target_feature), &[]), + ("bulk-memory", Stable, &[]), + ("exception-handling", Unstable(sym::wasm_target_feature), &[]), + ("extended-const", Stable, &[]), + ("multivalue", Unstable(sym::wasm_target_feature), &[]), + ("mutable-globals", Stable, &[]), + ("nontrapping-fptoint", Stable, &[]), + ("reference-types", Unstable(sym::wasm_target_feature), &[]), + ("relaxed-simd", Stable, &["simd128"]), + ("sign-ext", Stable, &[]), + ("simd128", Stable, &[]), // tidy-alphabetical-end ]; -const WASM_IMPLICIT_FEATURES: &[(&str, &str)] = &[("relaxed-simd", "simd128")]; - -const BPF_ALLOWED_FEATURES: &[(&str, Stability)] = &[("alu32", Unstable(sym::bpf_target_feature))]; +const BPF_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = + &[("alu32", Unstable(sym::bpf_target_feature), &[])]; -const CSKY_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const CSKY_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("10e60", Unstable(sym::csky_target_feature)), - ("2e3", Unstable(sym::csky_target_feature)), - ("3e3r1", Unstable(sym::csky_target_feature)), - ("3e3r2", Unstable(sym::csky_target_feature)), - ("3e3r3", Unstable(sym::csky_target_feature)), - ("3e7", Unstable(sym::csky_target_feature)), - ("7e10", Unstable(sym::csky_target_feature)), - ("cache", Unstable(sym::csky_target_feature)), - ("doloop", Unstable(sym::csky_target_feature)), - ("dsp1e2", Unstable(sym::csky_target_feature)), - ("dspe60", Unstable(sym::csky_target_feature)), - ("e1", Unstable(sym::csky_target_feature)), - ("e2", Unstable(sym::csky_target_feature)), - ("edsp", Unstable(sym::csky_target_feature)), - ("elrw", Unstable(sym::csky_target_feature)), - ("float1e2", Unstable(sym::csky_target_feature)), - ("float1e3", Unstable(sym::csky_target_feature)), - ("float3e4", Unstable(sym::csky_target_feature)), - ("float7e60", Unstable(sym::csky_target_feature)), - ("floate1", Unstable(sym::csky_target_feature)), - ("hard-tp", Unstable(sym::csky_target_feature)), - ("high-registers", Unstable(sym::csky_target_feature)), - ("hwdiv", Unstable(sym::csky_target_feature)), - ("mp", Unstable(sym::csky_target_feature)), - ("mp1e2", Unstable(sym::csky_target_feature)), - ("nvic", Unstable(sym::csky_target_feature)), - ("trust", Unstable(sym::csky_target_feature)), - ("vdsp2e60f", Unstable(sym::csky_target_feature)), - ("vdspv1", Unstable(sym::csky_target_feature)), - ("vdspv2", Unstable(sym::csky_target_feature)), + ("10e60", Unstable(sym::csky_target_feature), &["7e10"]), + ("2e3", Unstable(sym::csky_target_feature), &["e2"]), + ("3e3r1", Unstable(sym::csky_target_feature), &[]), + ("3e3r2", Unstable(sym::csky_target_feature), &["3e3r1", "doloop"]), + ("3e3r3", Unstable(sym::csky_target_feature), &["doloop"]), + ("3e7", Unstable(sym::csky_target_feature), &["2e3"]), + ("7e10", Unstable(sym::csky_target_feature), &["3e7"]), + ("cache", Unstable(sym::csky_target_feature), &[]), + ("doloop", Unstable(sym::csky_target_feature), &[]), + ("dsp1e2", Unstable(sym::csky_target_feature), &[]), + ("dspe60", Unstable(sym::csky_target_feature), &[]), + ("e1", Unstable(sym::csky_target_feature), &["elrw"]), + ("e2", Unstable(sym::csky_target_feature), &["e2"]), + ("edsp", Unstable(sym::csky_target_feature), &[]), + ("elrw", Unstable(sym::csky_target_feature), &[]), + ("float1e2", Unstable(sym::csky_target_feature), &[]), + ("float1e3", Unstable(sym::csky_target_feature), &[]), + ("float3e4", Unstable(sym::csky_target_feature), &[]), + ("float7e60", Unstable(sym::csky_target_feature), &[]), + ("floate1", Unstable(sym::csky_target_feature), &[]), + ("hard-tp", Unstable(sym::csky_target_feature), &[]), + ("high-registers", Unstable(sym::csky_target_feature), &[]), + ("hwdiv", Unstable(sym::csky_target_feature), &[]), + ("mp", Unstable(sym::csky_target_feature), &["2e3"]), + ("mp1e2", Unstable(sym::csky_target_feature), &["3e7"]), + ("nvic", Unstable(sym::csky_target_feature), &[]), + ("trust", Unstable(sym::csky_target_feature), &[]), + ("vdsp2e60f", Unstable(sym::csky_target_feature), &[]), + ("vdspv1", Unstable(sym::csky_target_feature), &[]), + ("vdspv2", Unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end //fpu // tidy-alphabetical-start - ("fdivdu", Unstable(sym::csky_target_feature)), - ("fpuv2_df", Unstable(sym::csky_target_feature)), - ("fpuv2_sf", Unstable(sym::csky_target_feature)), - ("fpuv3_df", Unstable(sym::csky_target_feature)), - ("fpuv3_hf", Unstable(sym::csky_target_feature)), - ("fpuv3_hi", Unstable(sym::csky_target_feature)), - ("fpuv3_sf", Unstable(sym::csky_target_feature)), - ("hard-float", Unstable(sym::csky_target_feature)), - ("hard-float-abi", Unstable(sym::csky_target_feature)), + ("fdivdu", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_sf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hi", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_sf", Unstable(sym::csky_target_feature), &[]), + ("hard-float", Unstable(sym::csky_target_feature), &[]), + ("hard-float-abi", Unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end ]; -const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("d", Unstable(sym::loongarch_target_feature)), - ("f", Unstable(sym::loongarch_target_feature)), - ("frecipe", Unstable(sym::loongarch_target_feature)), - ("lasx", Unstable(sym::loongarch_target_feature)), - ("lbt", Unstable(sym::loongarch_target_feature)), - ("lsx", Unstable(sym::loongarch_target_feature)), - ("lvz", Unstable(sym::loongarch_target_feature)), - ("relax", Unstable(sym::loongarch_target_feature)), - ("ual", Unstable(sym::loongarch_target_feature)), + ("d", Unstable(sym::loongarch_target_feature), &["f"]), + ("f", Unstable(sym::loongarch_target_feature), &[]), + ("frecipe", Unstable(sym::loongarch_target_feature), &[]), + ("lasx", Unstable(sym::loongarch_target_feature), &["lsx"]), + ("lbt", Unstable(sym::loongarch_target_feature), &[]), + ("lsx", Unstable(sym::loongarch_target_feature), &["d"]), + ("lvz", Unstable(sym::loongarch_target_feature), &[]), + ("relax", Unstable(sym::loongarch_target_feature), &[]), + ("ual", Unstable(sym::loongarch_target_feature), &[]), // tidy-alphabetical-end ]; -const IBMZ_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const IBMZ_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("backchain", Unstable(sym::s390x_target_feature)), - ("vector", Unstable(sym::s390x_target_feature)), + ("backchain", Unstable(sym::s390x_target_feature), &[]), + ("vector", Unstable(sym::s390x_target_feature), &[]), // tidy-alphabetical-end ]; @@ -430,10 +448,13 @@ pub fn all_known_features() -> impl Iterator { .chain(LOONGARCH_ALLOWED_FEATURES) .chain(IBMZ_ALLOWED_FEATURES) .cloned() + .map(|(f, s, _)| (f, s)) } impl super::spec::Target { - pub fn supported_target_features(&self) -> &'static [(&'static str, Stability)] { + pub fn supported_target_features( + &self, + ) -> &'static [(&'static str, Stability, ImpliedFeatures)] { match &*self.arch { "arm" => ARM_ALLOWED_FEATURES, "aarch64" | "arm64ec" => AARCH64_ALLOWED_FEATURES, @@ -458,12 +479,27 @@ impl super::spec::Target { } } - /// Returns a list of target features. Each items first target feature - /// implicitly enables the second one. - pub fn implicit_target_features(&self) -> &'static [(&'static str, &'static str)] { - match &*self.arch { - "wasm32" | "wasm64" => WASM_IMPLICIT_FEATURES, - _ => &[], + pub fn implied_target_features( + &self, + base_features: impl Iterator, + ) -> FxHashSet { + let implied_features = self + .supported_target_features() + .iter() + .map(|(f, _, i)| (Symbol::intern(f), i)) + .collect::>(); + + // implied target features have their own implied target features, so we traverse the + // map until there are no more features to add + let mut features = FxHashSet::default(); + let mut new_features = base_features.collect::>(); + while let Some(new_feature) = new_features.pop() { + if features.insert(new_feature) { + if let Some(implied_features) = implied_features.get(&new_feature) { + new_features.extend(implied_features.iter().copied().map(Symbol::intern)) + } + } } + features } } diff --git a/library/Cargo.lock b/library/Cargo.lock index 223b61456c267..c5182516f7d14 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -58,9 +58,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.114" +version = "0.1.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb58b199190fcfe0846f55a3b545cd6b07a34bdd5930a476ff856f3ebcc5558a" +checksum = "a91dae36d82fe12621dfb5b596d7db766187747749b22e33ac068e1bfc356f4a" dependencies = [ "cc", "rustc-std-workspace-core", diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 479eb0a2ba743..82d5893dc5e55 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -10,10 +10,7 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "0.1.114", features = ['rustc-dep-of-std'] } - -[target.'cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")))'.dependencies] -compiler_builtins = { version = "0.1.114", features = ["no-f16-f128"] } +compiler_builtins = { version = "0.1.117", features = ['rustc-dep-of-std'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index fe601855cc1e7..06e818fb7c09d 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "0.1.114" } +compiler_builtins = { version = "0.1.117" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.14", default-features = false, features = [ diff --git a/src/doc/rustc/src/platform-support/vxworks.md b/src/doc/rustc/src/platform-support/vxworks.md index c0a818558e0bf..7c73ee8cfa890 100644 --- a/src/doc/rustc/src/platform-support/vxworks.md +++ b/src/doc/rustc/src/platform-support/vxworks.md @@ -20,6 +20,12 @@ Target triplets available: ## Requirements +### OS version + +The minimum supported version is VxWorks 7. + +## Building + Rust for each target can be cross-compiled with its specific target vsb configuration. Std support is added but not yet fully tested. ## Building the target diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs index 0706f3bee05c1..ba28c75e87c9e 100644 --- a/src/tools/compiletest/src/command-list.rs +++ b/src/tools/compiletest/src/command-list.rs @@ -170,6 +170,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-32bit", "only-64bit", "only-aarch64", + "only-aarch64-unknown-linux-gnu", "only-apple", "only-arm", "only-avr", diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 014bc5954949d..0bddb0c7b4735 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -25,7 +25,6 @@ run-make/pgo-indirect-call-promotion/Makefile run-make/remap-path-prefix-dwarf/Makefile run-make/reproducible-build/Makefile run-make/rlib-format-packed-bundled-libs/Makefile -run-make/simd-ffi/Makefile run-make/split-debuginfo/Makefile run-make/staticlib-dylib-linkage/Makefile run-make/symbol-mangling-hashed/Makefile diff --git a/tests/codegen/sse42-implies-crc32.rs b/tests/codegen/sse42-implies-crc32.rs index 94fcd77bc8842..8a9c496a3a541 100644 --- a/tests/codegen/sse42-implies-crc32.rs +++ b/tests/codegen/sse42-implies-crc32.rs @@ -12,4 +12,4 @@ pub unsafe fn crc32sse(v: u8) -> u32 { _mm_crc32_u8(out, v) } -// CHECK: attributes #0 {{.*"target-features"=".*\+sse4.2,\+crc32"}} +// CHECK: attributes #0 {{.*"target-features"=".*\+sse4.2,\+crc32.*"}} diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs index 1e2c364dbbc9a..f38a1ae72de5d 100644 --- a/tests/codegen/target-feature-overrides.rs +++ b/tests/codegen/target-feature-overrides.rs @@ -1,7 +1,7 @@ //@ revisions: COMPAT INCOMPAT //@ needs-llvm-components: x86 //@ compile-flags: --target=x86_64-unknown-linux-gnu -Copt-level=3 -//@ [COMPAT] compile-flags: -Ctarget-feature=+avx2,+avx +//@ [COMPAT] compile-flags: -Ctarget-feature=+avx2 //@ [INCOMPAT] compile-flags: -Ctarget-feature=-avx2,-avx // See also tests/assembly/target-feature-multiple.rs @@ -39,8 +39,8 @@ pub unsafe fn banana() -> u32 { } // CHECK: attributes [[APPLEATTRS]] -// COMPAT-SAME: "target-features"="+avx2,+avx,+avx" -// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx" +// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" +// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx,{{.*}}" // CHECK: attributes [[BANANAATTRS]] -// COMPAT-SAME: "target-features"="+avx2,+avx" +// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" // INCOMPAT-SAME: "target-features"="-avx2,-avx" diff --git a/tests/codegen/tied-features-strength.rs b/tests/codegen/tied-features-strength.rs index 7f0805bc1b435..1b4596ae2cb57 100644 --- a/tests/codegen/tied-features-strength.rs +++ b/tests/codegen/tied-features-strength.rs @@ -8,7 +8,7 @@ // is LLVM-14 we can remove the optional regex matching for this feature. //@ [ENABLE_SVE] compile-flags: -C target-feature=+sve -Copt-level=0 -// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+sve,?)|(\+neon,?))*}}" } +// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" } //@ [DISABLE_SVE] compile-flags: -C target-feature=-sve -Copt-level=0 // DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(-sve,?)|(\+neon,?))*}}" } diff --git a/tests/run-make/mte-ffi/bar.h b/tests/run-make/mte-ffi/bar.h new file mode 100644 index 0000000000000..a2292ae02a308 --- /dev/null +++ b/tests/run-make/mte-ffi/bar.h @@ -0,0 +1,43 @@ +#ifndef __BAR_H +#define __BAR_H + +#include +#include +#include +#include +#include + +// Set the allocation tag on the destination address using the STG instruction. +#define set_tag(tagged_addr) do { \ + asm volatile("stg %0, [%0]" : : "r" (tagged_addr) : "memory"); \ +} while (0) + +int mte_enabled() { + return (getauxval(AT_HWCAP2)) & HWCAP2_MTE; +} + +void *alloc_page() { + // Enable MTE with synchronous checking + if (prctl(PR_SET_TAGGED_ADDR_CTRL, + PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | (0xfffe << PR_MTE_TAG_SHIFT), + 0, 0, 0)) + { + perror("prctl() failed"); + } + + // Using `mmap` allows us to ensure that, on systems which support MTE, the allocated + // memory is 16-byte aligned for MTE. + // This also allows us to explicitly specify whether the region should be protected by + // MTE or not. + if (mte_enabled()) { + void *ptr = mmap(NULL, sysconf(_SC_PAGESIZE), + PROT_READ | PROT_WRITE | PROT_MTE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + } else { + void *ptr = mmap(NULL, sysconf(_SC_PAGESIZE), + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + } +} + +#endif // __BAR_H diff --git a/tests/run-make/mte-ffi/bar_float.c b/tests/run-make/mte-ffi/bar_float.c new file mode 100644 index 0000000000000..a1590f62765a6 --- /dev/null +++ b/tests/run-make/mte-ffi/bar_float.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include "bar.h" + +extern void foo(char*); + +void bar(char *ptr) { + if (((uintptr_t)ptr >> 56) != 0x1f) { + fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); + exit(1); + } +} + +int main(void) +{ + float *ptr = alloc_page(); + if (ptr == MAP_FAILED) + { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), + // and a different value in the ignored top 4 bits. + ptr = (float *)((uintptr_t)ptr | 0x1fl << 56); + + if (mte_enabled()) { + set_tag(ptr); + } + + ptr[0] = 2.0f; + ptr[1] = 1.5f; + + foo(ptr); // should change the contents of the page and call `bar` + + if (ptr[0] != 0.5f || ptr[1] != 0.2f) { + fprintf(stderr, "invalid data in memory; expected '0.5 0.2', got '%f %f'\n", + ptr[0], ptr[1]); + return EXIT_FAILURE; + } + + return 0; +} diff --git a/tests/run-make/mte-ffi/bar_function.c b/tests/run-make/mte-ffi/bar_function.c new file mode 100644 index 0000000000000..1fa48d32a0c88 --- /dev/null +++ b/tests/run-make/mte-ffi/bar_function.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include "bar.h" + +typedef void (*fp)(int (*)()); + +extern void foo(fp); + +void bar(int (*ptr)()) { + if (((uintptr_t)ptr >> 56) != 0x2f) { + fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); + exit(1); + } + + int r = (*ptr)(); + if (r != 32) { + fprintf(stderr, "invalid return value; expected 32, got '%d'\n", r); + exit(1); + } +} + +int main(void) +{ + fp ptr = alloc_page(); + if (ptr == MAP_FAILED) + { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), + // and a different value in the ignored top 4 bits. + ptr = (fp)((uintptr_t)&bar | 0x1fl << 56); + + foo(ptr); + + return 0; +} diff --git a/tests/run-make/mte-ffi/bar_int.c b/tests/run-make/mte-ffi/bar_int.c new file mode 100644 index 0000000000000..d1c79e95dc9cb --- /dev/null +++ b/tests/run-make/mte-ffi/bar_int.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include "bar.h" + +extern void foo(unsigned int *); + +void bar(char *ptr) { + if (((uintptr_t)ptr >> 56) != 0x1f) { + fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); + exit(1); + } +} + +int main(void) +{ + // Construct a pointer with an arbitrary tag in bits 56-59, simulating an MTE tag. + // It's only necessary that the tag is preserved across FFI bounds for this test. + unsigned int *ptr; + + ptr = alloc_page(); + if (ptr == MAP_FAILED) + { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), + // and a different value in the ignored top 4 bits. + ptr = (unsigned int *)((uintptr_t)ptr | 0x1fl << 56); + + if (mte_enabled()) { + set_tag(ptr); + } + + ptr[0] = 61; + ptr[1] = 62; + + foo(ptr); // should change the contents of the page to start with 0x63 0x64 and call `bar` + + if (ptr[0] != 0x63 || ptr[1] != 0x64) { + fprintf(stderr, "invalid data in memory; expected '63 64', got '%d %d'\n", ptr[0], ptr[1]); + return EXIT_FAILURE; + } + + return 0; +} diff --git a/tests/run-make/mte-ffi/bar_string.c b/tests/run-make/mte-ffi/bar_string.c new file mode 100644 index 0000000000000..5669ffd6695e7 --- /dev/null +++ b/tests/run-make/mte-ffi/bar_string.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include "bar.h" + +extern void foo(char*); + +void bar(char *ptr) { + if (((uintptr_t)ptr >> 56) != 0x2f) { + fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); + exit(1); + } + + if (strcmp(ptr, "cd")) { + fprintf(stderr, "invalid data in memory; expected 'cd', got '%s'\n", ptr); + exit(1); + } +} + +int main(void) +{ + // Construct a pointer with an arbitrary tag in bits 56-59, simulating an MTE tag. + // It's only necessary that the tag is preserved across FFI bounds for this test. + char *ptr; + + ptr = alloc_page(); + if (ptr == MAP_FAILED) + { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), + // and a different value in the ignored top 4 bits. + ptr = (unsigned int *)((uintptr_t)ptr | 0x1fl << 56); + + if (mte_enabled()) { + set_tag(ptr); + } + + ptr[0] = 'a'; + ptr[1] = 'b'; + ptr[2] = '\0'; + + foo(ptr); + + return 0; +} diff --git a/tests/run-make/mte-ffi/foo_float.rs b/tests/run-make/mte-ffi/foo_float.rs new file mode 100644 index 0000000000000..c1bedd5249459 --- /dev/null +++ b/tests/run-make/mte-ffi/foo_float.rs @@ -0,0 +1,19 @@ +#![crate_type = "cdylib"] +#![crate_name = "foo"] + +use std::os::raw::c_float; + +extern "C" { + fn bar(ptr: *const c_float); +} + +#[no_mangle] +pub extern "C" fn foo(ptr: *mut c_float) { + assert_eq!((ptr as usize) >> 56, 0x1f); + + unsafe { + *ptr = 0.5; + *ptr.wrapping_add(1) = 0.2; + bar(ptr); + } +} diff --git a/tests/run-make/mte-ffi/foo_function.rs b/tests/run-make/mte-ffi/foo_function.rs new file mode 100644 index 0000000000000..2c8e0b2623851 --- /dev/null +++ b/tests/run-make/mte-ffi/foo_function.rs @@ -0,0 +1,17 @@ +#![crate_type = "cdylib"] +#![crate_name = "foo"] + +extern "C" fn ret32() -> i32 { + 32 +} + +#[no_mangle] +pub extern "C" fn foo(ptr: extern "C" fn(extern "C" fn() -> i32)) { + assert_eq!((ptr as usize) >> 56, 0x1f); + + // Store an arbitrary tag in the tag bits, and convert back to the correct pointer type. + let p = ((ret32 as usize) | (0x2f << 56)) as *const (); + let p: extern "C" fn() -> i32 = unsafe { std::mem::transmute(p) }; + + unsafe { ptr(p) } +} diff --git a/tests/run-make/mte-ffi/foo_int.rs b/tests/run-make/mte-ffi/foo_int.rs new file mode 100644 index 0000000000000..106d863cb8127 --- /dev/null +++ b/tests/run-make/mte-ffi/foo_int.rs @@ -0,0 +1,19 @@ +#![crate_type = "cdylib"] +#![crate_name = "foo"] + +use std::os::raw::c_uint; + +extern "C" { + fn bar(ptr: *const c_uint); +} + +#[no_mangle] +pub extern "C" fn foo(ptr: *mut c_uint) { + assert_eq!((ptr as usize) >> 56, 0x1f); + + unsafe { + *ptr = 0x63; + *ptr.wrapping_add(1) = 0x64; + bar(ptr); + } +} diff --git a/tests/run-make/mte-ffi/foo_string.rs b/tests/run-make/mte-ffi/foo_string.rs new file mode 100644 index 0000000000000..5474480244892 --- /dev/null +++ b/tests/run-make/mte-ffi/foo_string.rs @@ -0,0 +1,27 @@ +#![crate_type = "cdylib"] +#![crate_name = "foo"] + +use std::arch::asm; +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +extern "C" { + fn bar(ptr: *const c_char); +} + +#[no_mangle] +pub extern "C" fn foo(ptr: *const c_char) { + assert_eq!((ptr as usize) >> 56, 0x1f); + + let s = unsafe { CStr::from_ptr(ptr) }; + assert_eq!(s.to_str().unwrap(), "ab"); + + let s = CString::from_vec_with_nul("cd\0".into()).unwrap(); + let mut p = ((s.as_ptr() as usize) | (0x2f << 56)) as *const c_char; + unsafe { + #[cfg(target_feature = "mte")] + asm!("stg {p}, [{p}]", p = inout(reg) p); + + bar(p); + } +} diff --git a/tests/run-make/mte-ffi/rmake.rs b/tests/run-make/mte-ffi/rmake.rs new file mode 100644 index 0000000000000..f4fafb796e3c5 --- /dev/null +++ b/tests/run-make/mte-ffi/rmake.rs @@ -0,0 +1,38 @@ +// Tests that MTE tags and values stored in the top byte of a pointer (TBI) are +// preserved across FFI boundaries (C <-> Rust). +// This test does not require MTE: whilst the test will use MTE if available, if it is not, +// arbitrary tag bits are set using TBI. + +// This test is only valid for AArch64. +// The linker must be explicitly specified when cross-compiling, so it is limited to +// `aarch64-unknown-linux-gnu`. +//@ only-aarch64-unknown-linux-gnu + +use run_make_support::{cc, dynamic_lib_name, extra_c_flags, run, rustc, target}; + +fn main() { + run_test("int"); + run_test("float"); + run_test("string"); + run_test("function"); +} + +fn run_test(variant: &str) { + let flags = { + let mut flags = extra_c_flags(); + flags.push("-march=armv8.5-a+memtag"); + flags + }; + println!("{variant} test..."); + rustc() + .input(format!("foo_{variant}.rs")) + .target(target()) + .linker("aarch64-linux-gnu-gcc") + .run(); + cc().input(format!("bar_{variant}.c")) + .input(dynamic_lib_name("foo")) + .out_exe("test") + .args(&flags) + .run(); + run("test"); +} diff --git a/tests/run-make/rust-lld/rmake.rs b/tests/run-make/rust-lld/rmake.rs index 87477c122301d..d0bc19130d7cb 100644 --- a/tests/run-make/rust-lld/rmake.rs +++ b/tests/run-make/rust-lld/rmake.rs @@ -2,15 +2,17 @@ // see https://github.com/rust-lang/compiler-team/issues/510 for more info //@ needs-rust-lld -//@ ignore-msvc //@ ignore-s390x lld does not yet support s390x as target use std::process::Output; use run_make_support::regex::Regex; -use run_make_support::rustc; +use run_make_support::{is_msvc, rustc}; fn main() { + // lld-link is used if msvc, otherwise a gnu-compatible lld is used. + let linker_version_flag = if is_msvc() { "--version" } else { "-Wl,-v" }; + // Opt-in to lld and the self-contained linker, to link with rust-lld. We'll check that by // asking the linker to display its version number with a link-arg. let output = rustc() @@ -18,7 +20,7 @@ fn main() { .arg("-Zlinker-features=+lld") .arg("-Clink-self-contained=+linker") .arg("-Zunstable-options") - .link_arg("-Wl,-v") + .link_arg(linker_version_flag) .input("main.rs") .run(); assert!( @@ -27,10 +29,10 @@ fn main() { output.stderr_utf8() ); - // It should not be used when we explictly opt-out of lld. + // It should not be used when we explicitly opt-out of lld. let output = rustc() .env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info") - .link_arg("-Wl,-v") + .link_arg(linker_version_flag) .arg("-Zlinker-features=-lld") .input("main.rs") .run(); @@ -44,7 +46,7 @@ fn main() { // times to rustc. let output = rustc() .env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info") - .link_arg("-Wl,-v") + .link_arg(linker_version_flag) .arg("-Clink-self-contained=+linker") .arg("-Zunstable-options") .arg("-Zlinker-features=-lld") diff --git a/tests/run-make/simd-ffi/Makefile b/tests/run-make/simd-ffi/Makefile deleted file mode 100644 index 297353470411f..0000000000000 --- a/tests/run-make/simd-ffi/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -include ../tools.mk - -TARGETS = -ifeq ($(filter arm,$(LLVM_COMPONENTS)),arm) -# construct a fairly exhaustive list of platforms that we -# support. These ones don't follow a pattern -TARGETS += arm-linux-androideabi arm-unknown-linux-gnueabihf arm-unknown-linux-gnueabi -endif - -ifeq ($(filter x86,$(LLVM_COMPONENTS)),x86) -X86_ARCHS = i686 x86_64 -else -X86_ARCHS = -endif - -# these ones do, each OS lists the architectures it supports -LINUX=$(filter aarch64 mips,$(LLVM_COMPONENTS)) $(X86_ARCHS) -ifeq ($(filter mips,$(LLVM_COMPONENTS)),mips) -LINUX += mipsel -endif - -WINDOWS=$(X86_ARCHS) -# fails with: failed to get iphonesimulator SDK path: no such file or directory -#IOS=i386 aarch64 armv7 -DARWIN=$(X86_ARCHS) - -$(foreach arch,$(LINUX),$(eval TARGETS += $(arch)-unknown-linux-gnu)) -$(foreach arch,$(WINDOWS),$(eval TARGETS += $(arch)-pc-windows-gnu)) -#$(foreach arch,$(IOS),$(eval TARGETS += $(arch)-apple-ios)) -$(foreach arch,$(DARWIN),$(eval TARGETS += $(arch)-apple-darwin)) - -all: $(TARGETS) - -define MK_TARGETS -# compile the rust file to the given target, but only to asm and IR -# form, to avoid having to have an appropriate linker. -# -# we need some features because the integer SIMD instructions are not -# enabled by-default for i686 and ARM; these features will be invalid -# on some platforms, but LLVM just prints a warning so that's fine for -# now. -$(1): simd.rs - $$(RUSTC) --target=$(1) --emit=llvm-ir,asm simd.rs \ - -C target-feature='+neon,+sse2' -C extra-filename=-$(1) -endef - -$(foreach targetxxx,$(TARGETS),$(eval $(call MK_TARGETS,$(targetxxx)))) diff --git a/tests/run-make/simd-ffi/rmake.rs b/tests/run-make/simd-ffi/rmake.rs new file mode 100644 index 0000000000000..04990c8bdf437 --- /dev/null +++ b/tests/run-make/simd-ffi/rmake.rs @@ -0,0 +1,63 @@ +// Using SIMD types in a program with foreign-function interfaces used to result in an ICE +// (internal compiler error). Since this was fixed in #21233, it should be checked that +// compilation of SIMD and FFI together should be successful on all the most common +// architectures. +// Note that this test does not check linking or binary execution. +// See https://github.com/rust-lang/rust/pull/21233 + +use run_make_support::{llvm_components_contain, rustc}; + +fn main() { + let mut targets = Vec::new(); + // arm-specific targets. + if llvm_components_contain("arm") { + targets.append(&mut vec![ + "arm-linux-androideabi".to_owned(), + "arm-unknown-linux-gnueabihf".to_owned(), + "arm-unknown-linux-gnueabi".to_owned(), + ]); + } + let mut x86_archs = Vec::new(); + if llvm_components_contain("x86") { + x86_archs.append(&mut vec!["i686", "x86_64"]); + } + // Linux has all x86 targets, plus aarch64 and mips. + let mut extra_targets = x86_archs.clone(); + if llvm_components_contain("aarch64") { + extra_targets.push("aarch64"); + } + if llvm_components_contain("mips") { + extra_targets.append(&mut vec!["mips", "mipsel"]); + } + + for target in extra_targets { + let linux = format!("{target}-unknown-linux-gnu"); + targets.push(linux); + } + + // Windows and Darwin (OSX) only receive x86 targets. + let extra_targets = x86_archs.clone(); + for target in extra_targets { + let windows = format!("{target}-pc-windows-gnu"); + let darwin = format!("{target}-apple-darwin"); + targets.push(windows); + targets.push(darwin); + } + + for target in targets { + // compile the rust file to the given target, but only to asm and IR + // form, to avoid having to have an appropriate linker. + // + // we need some features because the integer SIMD instructions are not + // enabled by-default for i686 and ARM; these features will be invalid + // on some platforms, but LLVM just prints a warning so that's fine for + // now. + rustc() + .target(&target) + .emit("llvm-ir,asm") + .input("simd.rs") + .arg("-Ctarget-feature=+neon,+sse") + .arg(&format!("-Cextra-filename=-{target}")) + .run(); + } +} diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs index de002ef71d7d3..fec4e75290fc8 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs @@ -34,6 +34,7 @@ fn foo() { #[target_feature(enable = "sse2")] fn bar() { + sse2(); avx_bmi2(); //~^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe Quux.avx_bmi2(); @@ -43,7 +44,6 @@ fn bar() { #[target_feature(enable = "avx")] fn baz() { sse2(); - //~^ ERROR call to function `sse2` with `#[target_feature]` is unsafe avx_bmi2(); //~^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe Quux.avx_bmi2(); @@ -54,7 +54,8 @@ fn baz() { #[target_feature(enable = "bmi2")] fn qux() { sse2(); - //~^ ERROR call to function `sse2` with `#[target_feature]` is unsafe + avx_bmi2(); + Quux.avx_bmi2(); } const _: () = sse2(); @@ -64,8 +65,6 @@ const _: () = sse2_and_fxsr(); //~^ ERROR call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe #[deny(unsafe_op_in_unsafe_fn)] -#[target_feature(enable = "avx")] -#[target_feature(enable = "bmi2")] unsafe fn needs_unsafe_block() { sse2(); //~^ ERROR call to function `sse2` with `#[target_feature]` is unsafe diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr index 537819ab8595c..1ddf05b40a606 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr @@ -24,7 +24,7 @@ LL | Quux.avx_bmi2(); = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:37:5 + --> $DIR/safe-calls.rs:38:5 | LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` @@ -32,22 +32,13 @@ LL | avx_bmi2(); = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:39:5 + --> $DIR/safe-calls.rs:40:5 | LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` | = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:45:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:47:5 | @@ -65,16 +56,7 @@ LL | Quux.avx_bmi2(); = help: in order for the call to be safe, the context requires the following additional target feature: bmi2 error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:56:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:60:15 + --> $DIR/safe-calls.rs:61:15 | LL | const _: () = sse2(); | ^^^^^^ call to function with `#[target_feature]` @@ -83,7 +65,7 @@ LL | const _: () = sse2(); = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` error[E0133]: call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:63:15 + --> $DIR/safe-calls.rs:64:15 | LL | const _: () = sse2_and_fxsr(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` @@ -92,7 +74,7 @@ LL | const _: () = sse2_and_fxsr(); = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block - --> $DIR/safe-calls.rs:70:5 + --> $DIR/safe-calls.rs:69:5 | LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` @@ -101,16 +83,16 @@ LL | sse2(); = help: in order for the call to be safe, the context requires the following additional target feature: sse2 = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` note: an unsafe function restricts its caller, but its body is safe by default - --> $DIR/safe-calls.rs:69:1 + --> $DIR/safe-calls.rs:68:1 | LL | unsafe fn needs_unsafe_block() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/safe-calls.rs:66:8 + --> $DIR/safe-calls.rs:67:8 | LL | #[deny(unsafe_op_in_unsafe_fn)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/target-feature/asm-implied-features-issue-128125.rs b/tests/ui/target-feature/asm-implied-features-issue-128125.rs new file mode 100644 index 0000000000000..2b4f1d7df8563 --- /dev/null +++ b/tests/ui/target-feature/asm-implied-features-issue-128125.rs @@ -0,0 +1,10 @@ +//@ only-x86_64 +//@ build-pass +#![allow(dead_code)] + +#[target_feature(enable = "avx2")] +unsafe fn demo(v: std::arch::x86_64::__m256i) { + std::arch::asm!("/* {v} */", v = in(ymm_reg) v); +} + +fn main() {} diff --git a/tests/ui/target-feature/implied-features.rs b/tests/ui/target-feature/implied-features.rs new file mode 100644 index 0000000000000..4fdd843e6c289 --- /dev/null +++ b/tests/ui/target-feature/implied-features.rs @@ -0,0 +1,24 @@ +//@ only-x86_64 +//@ build-pass +#![feature(target_feature_11)] +#![allow(dead_code)] + +#[target_feature(enable = "ssse3")] +fn call_ssse3() {} + +#[target_feature(enable = "avx")] +fn call_avx() {} + +#[target_feature(enable = "avx2")] +fn test_avx2() { + call_ssse3(); + call_avx(); +} + +#[target_feature(enable = "fma")] +fn test_fma() { + call_ssse3(); + call_avx(); +} + +fn main() {}