mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Auto merge of #132514 - Zalathar:print-target-cpus, r=jieyouxu
Port most of `--print=target-cpus` to Rust The logic and formatting needed by `--print=target-cpus` has historically been carried out in C++ code. Originally it used `printf` to write directly to the console, but later it switched over to writing to a `std::ostringstream` and then passing its buffer to a callback function pointer. This PR replaces that C++ code with a very simple function that writes a list of CPU names to a `&RustString`, with the rest of the logic and formatting being handled by ordinary safe Rust code.
This commit is contained in:
commit
59ae5eba7e
@ -2190,12 +2190,8 @@ unsafe extern "C" {
|
|||||||
|
|
||||||
pub fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool;
|
pub fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool;
|
||||||
|
|
||||||
pub fn LLVMRustPrintTargetCPUs(
|
#[allow(improper_ctypes)]
|
||||||
T: &TargetMachine,
|
pub(crate) fn LLVMRustPrintTargetCPUs(TM: &TargetMachine, OutStr: &RustString);
|
||||||
cpu: *const c_char,
|
|
||||||
print: unsafe extern "C" fn(out: *mut c_void, string: *const c_char, len: usize),
|
|
||||||
out: *mut c_void,
|
|
||||||
);
|
|
||||||
pub fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t;
|
pub fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t;
|
||||||
pub fn LLVMRustGetTargetFeature(
|
pub fn LLVMRustGetTargetFeature(
|
||||||
T: &TargetMachine,
|
T: &TargetMachine,
|
||||||
@ -2204,7 +2200,7 @@ unsafe extern "C" {
|
|||||||
Desc: &mut *const c_char,
|
Desc: &mut *const c_char,
|
||||||
);
|
);
|
||||||
|
|
||||||
pub fn LLVMRustGetHostCPUName(len: *mut usize) -> *const c_char;
|
pub fn LLVMRustGetHostCPUName(LenOut: &mut size_t) -> *const u8;
|
||||||
|
|
||||||
// This function makes copies of pointed to data, so the data's lifetime may end after this
|
// This function makes copies of pointed to data, so the data's lifetime may end after this
|
||||||
// function returns.
|
// function returns.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use std::ffi::{CStr, CString, c_char, c_void};
|
use std::collections::VecDeque;
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
@ -387,7 +388,65 @@ fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> {
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMachine) {
|
pub(crate) fn print(req: &PrintRequest, out: &mut String, sess: &Session) {
|
||||||
|
require_inited();
|
||||||
|
let tm = create_informational_target_machine(sess, false);
|
||||||
|
match req.kind {
|
||||||
|
PrintKind::TargetCPUs => print_target_cpus(sess, &tm, out),
|
||||||
|
PrintKind::TargetFeatures => print_target_features(sess, &tm, out),
|
||||||
|
_ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_target_cpus(sess: &Session, tm: &llvm::TargetMachine, out: &mut String) {
|
||||||
|
let cpu_names = llvm::build_string(|s| unsafe {
|
||||||
|
llvm::LLVMRustPrintTargetCPUs(&tm, s);
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
struct Cpu<'a> {
|
||||||
|
cpu_name: &'a str,
|
||||||
|
remark: String,
|
||||||
|
}
|
||||||
|
// Compare CPU against current target to label the default.
|
||||||
|
let target_cpu = handle_native(&sess.target.cpu);
|
||||||
|
let make_remark = |cpu_name| {
|
||||||
|
if cpu_name == target_cpu {
|
||||||
|
// FIXME(#132514): This prints the LLVM target string, which can be
|
||||||
|
// different from the Rust target string. Is that intended?
|
||||||
|
let target = &sess.target.llvm_target;
|
||||||
|
format!(
|
||||||
|
" - This is the default target CPU for the current build target (currently {target})."
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
"".to_owned()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut cpus = cpu_names
|
||||||
|
.lines()
|
||||||
|
.map(|cpu_name| Cpu { cpu_name, remark: make_remark(cpu_name) })
|
||||||
|
.collect::<VecDeque<_>>();
|
||||||
|
|
||||||
|
// Only print the "native" entry when host and target are the same arch,
|
||||||
|
// since otherwise it could be wrong or misleading.
|
||||||
|
if sess.host.arch == sess.target.arch {
|
||||||
|
let host = get_host_cpu_name();
|
||||||
|
cpus.push_front(Cpu {
|
||||||
|
cpu_name: "native",
|
||||||
|
remark: format!(" - Select the CPU of the current host (currently {host})."),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let max_name_width = cpus.iter().map(|cpu| cpu.cpu_name.len()).max().unwrap_or(0);
|
||||||
|
writeln!(out, "Available CPUs for this target:").unwrap();
|
||||||
|
for Cpu { cpu_name, remark } in cpus {
|
||||||
|
// Only pad the CPU name if there's a remark to print after it.
|
||||||
|
let width = if remark.is_empty() { 0 } else { max_name_width };
|
||||||
|
writeln!(out, " {cpu_name:<width$}{remark}").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_target_features(sess: &Session, tm: &llvm::TargetMachine, out: &mut String) {
|
||||||
let mut llvm_target_features = llvm_target_features(tm);
|
let mut llvm_target_features = llvm_target_features(tm);
|
||||||
let mut known_llvm_target_features = FxHashSet::<&'static str>::default();
|
let mut known_llvm_target_features = FxHashSet::<&'static str>::default();
|
||||||
let mut rustc_target_features = sess
|
let mut rustc_target_features = sess
|
||||||
@ -447,52 +506,31 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach
|
|||||||
writeln!(out, "and may be renamed or removed in a future version of LLVM or rustc.\n").unwrap();
|
writeln!(out, "and may be renamed or removed in a future version of LLVM or rustc.\n").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn print(req: &PrintRequest, mut out: &mut String, sess: &Session) {
|
/// Returns the host CPU name, according to LLVM.
|
||||||
require_inited();
|
fn get_host_cpu_name() -> &'static str {
|
||||||
let tm = create_informational_target_machine(sess, false);
|
let mut len = 0;
|
||||||
match req.kind {
|
// SAFETY: The underlying C++ global function returns a `StringRef` that
|
||||||
PrintKind::TargetCPUs => {
|
// isn't tied to any particular backing buffer, so it must be 'static.
|
||||||
// SAFETY generate a C compatible string from a byte slice to pass
|
let slice: &'static [u8] = unsafe {
|
||||||
// the target CPU name into LLVM, the lifetime of the reference is
|
let ptr = llvm::LLVMRustGetHostCPUName(&mut len);
|
||||||
// at least as long as the C function
|
assert!(!ptr.is_null());
|
||||||
let cpu_cstring = CString::new(handle_native(sess.target.cpu.as_ref()))
|
slice::from_raw_parts(ptr, len)
|
||||||
.unwrap_or_else(|e| bug!("failed to convert to cstring: {}", e));
|
};
|
||||||
unsafe extern "C" fn callback(out: *mut c_void, string: *const c_char, len: usize) {
|
str::from_utf8(slice).expect("host CPU name should be UTF-8")
|
||||||
let out = unsafe { &mut *(out as *mut &mut String) };
|
|
||||||
let bytes = unsafe { slice::from_raw_parts(string as *const u8, len) };
|
|
||||||
write!(out, "{}", String::from_utf8_lossy(bytes)).unwrap();
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
llvm::LLVMRustPrintTargetCPUs(
|
|
||||||
&tm,
|
|
||||||
cpu_cstring.as_ptr(),
|
|
||||||
callback,
|
|
||||||
(&raw mut out) as *mut c_void,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PrintKind::TargetFeatures => print_target_features(out, sess, &tm),
|
|
||||||
_ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_native(name: &str) -> &str {
|
/// If the given string is `"native"`, returns the host CPU name according to
|
||||||
if name != "native" {
|
/// LLVM. Otherwise, the string is returned as-is.
|
||||||
return name;
|
fn handle_native(cpu_name: &str) -> &str {
|
||||||
}
|
match cpu_name {
|
||||||
|
"native" => get_host_cpu_name(),
|
||||||
unsafe {
|
_ => cpu_name,
|
||||||
let mut len = 0;
|
|
||||||
let ptr = llvm::LLVMRustGetHostCPUName(&mut len);
|
|
||||||
str::from_utf8(slice::from_raw_parts(ptr as *const u8, len)).unwrap()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn target_cpu(sess: &Session) -> &str {
|
pub(crate) fn target_cpu(sess: &Session) -> &str {
|
||||||
match sess.opts.cg.target_cpu {
|
let cpu_name = sess.opts.cg.target_cpu.as_deref().unwrap_or_else(|| &sess.target.cpu);
|
||||||
Some(ref name) => handle_native(name),
|
handle_native(cpu_name)
|
||||||
None => handle_native(sess.target.cpu.as_ref()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
|
/// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
|
||||||
|
@ -317,49 +317,17 @@ template <typename KV> static size_t getLongestEntryLength(ArrayRef<KV> Table) {
|
|||||||
return MaxLen;
|
return MaxLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
using PrintBackendInfo = void(void *, const char *Data, size_t Len);
|
|
||||||
|
|
||||||
extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM,
|
extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM,
|
||||||
const char *TargetCPU,
|
RustStringRef OutStr) {
|
||||||
PrintBackendInfo Print, void *Out) {
|
ArrayRef<SubtargetSubTypeKV> CPUTable =
|
||||||
const TargetMachine *Target = unwrap(TM);
|
unwrap(TM)->getMCSubtargetInfo()->getAllProcessorDescriptions();
|
||||||
const Triple::ArchType HostArch =
|
auto OS = RawRustStringOstream(OutStr);
|
||||||
Triple(sys::getDefaultTargetTriple()).getArch();
|
|
||||||
const Triple::ArchType TargetArch = Target->getTargetTriple().getArch();
|
|
||||||
|
|
||||||
std::ostringstream Buf;
|
// Just print a bare list of target CPU names, and let Rust-side code handle
|
||||||
|
// the full formatting of `--print=target-cpus`.
|
||||||
const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
|
|
||||||
const ArrayRef<SubtargetSubTypeKV> CPUTable =
|
|
||||||
MCInfo->getAllProcessorDescriptions();
|
|
||||||
unsigned MaxCPULen = getLongestEntryLength(CPUTable);
|
|
||||||
|
|
||||||
Buf << "Available CPUs for this target:\n";
|
|
||||||
// Don't print the "native" entry when the user specifies --target with a
|
|
||||||
// different arch since that could be wrong or misleading.
|
|
||||||
if (HostArch == TargetArch) {
|
|
||||||
MaxCPULen = std::max(MaxCPULen, (unsigned)std::strlen("native"));
|
|
||||||
const StringRef HostCPU = sys::getHostCPUName();
|
|
||||||
Buf << " " << std::left << std::setw(MaxCPULen) << "native"
|
|
||||||
<< " - Select the CPU of the current host "
|
|
||||||
"(currently "
|
|
||||||
<< HostCPU.str() << ").\n";
|
|
||||||
}
|
|
||||||
for (auto &CPU : CPUTable) {
|
for (auto &CPU : CPUTable) {
|
||||||
// Compare cpu against current target to label the default
|
OS << CPU.Key << "\n";
|
||||||
if (strcmp(CPU.Key, TargetCPU) == 0) {
|
|
||||||
Buf << " " << std::left << std::setw(MaxCPULen) << CPU.Key
|
|
||||||
<< " - This is the default target CPU for the current build target "
|
|
||||||
"(currently "
|
|
||||||
<< Target->getTargetTriple().str() << ").";
|
|
||||||
} else {
|
|
||||||
Buf << " " << CPU.Key;
|
|
||||||
}
|
|
||||||
Buf << "\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &BufString = Buf.str();
|
|
||||||
Print(Out, BufString.data(), BufString.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) {
|
extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) {
|
||||||
@ -382,9 +350,9 @@ extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index,
|
|||||||
*Desc = Feat.Desc;
|
*Desc = Feat.Desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" const char *LLVMRustGetHostCPUName(size_t *len) {
|
extern "C" const char *LLVMRustGetHostCPUName(size_t *OutLen) {
|
||||||
StringRef Name = sys::getHostCPUName();
|
StringRef Name = sys::getHostCPUName();
|
||||||
*len = Name.size();
|
*OutLen = Name.size();
|
||||||
return Name.data();
|
return Name.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
tests/run-make/print-target-cpus-native/rmake.rs
Normal file
39
tests/run-make/print-target-cpus-native/rmake.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//@ ignore-cross-compile
|
||||||
|
//@ needs-llvm-components: aarch64 x86
|
||||||
|
// FIXME(#132514): Is needs-llvm-components actually necessary for this test?
|
||||||
|
|
||||||
|
use run_make_support::{assert_contains_regex, rfs, rustc, target};
|
||||||
|
|
||||||
|
// Test that when querying `--print=target-cpus` for a target with the same
|
||||||
|
// architecture as the host, the first CPU is "native" with a suitable remark.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let expected = r"^Available CPUs for this target:
|
||||||
|
native +- Select the CPU of the current host \(currently [^ )]+\)\.
|
||||||
|
";
|
||||||
|
|
||||||
|
// Without an explicit target.
|
||||||
|
rustc().print("target-cpus").run().assert_stdout_contains_regex(expected);
|
||||||
|
|
||||||
|
// With an explicit target that happens to be the host.
|
||||||
|
let host = target(); // Because of ignore-cross-compile, assume host == target.
|
||||||
|
rustc().print("target-cpus").target(host).run().assert_stdout_contains_regex(expected);
|
||||||
|
|
||||||
|
// With an explicit output path.
|
||||||
|
rustc().print("target-cpus=./xyzzy.txt").run().assert_stdout_equals("");
|
||||||
|
assert_contains_regex(rfs::read_to_string("./xyzzy.txt"), expected);
|
||||||
|
|
||||||
|
// Now try some cross-target queries with the same arch as the host.
|
||||||
|
// (Specify multiple targets so that at least one of them is not the host.)
|
||||||
|
let cross_targets: &[&str] = if cfg!(target_arch = "aarch64") {
|
||||||
|
&["aarch64-unknown-linux-gnu", "aarch64-apple-darwin"]
|
||||||
|
} else if cfg!(target_arch = "x86_64") {
|
||||||
|
&["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"]
|
||||||
|
} else {
|
||||||
|
&[]
|
||||||
|
};
|
||||||
|
for target in cross_targets {
|
||||||
|
println!("Trying target: {target}");
|
||||||
|
rustc().print("target-cpus").target(target).run().assert_stdout_contains_regex(expected);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user