Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/back/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub(crate) mod archive;
pub(crate) mod lto;
pub(crate) mod owned_mc_subtarget_info;
pub(crate) mod owned_target_machine;
mod profiling;
pub(crate) mod write;
49 changes: 49 additions & 0 deletions compiler/rustc_codegen_llvm/src/back/owned_mc_subtarget_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use std::ffi::CStr;
use std::ptr::NonNull;

use rustc_data_structures::small_c_str::SmallCStr;

use crate::diagnostics::LlvmError;
use crate::llvm;

/// Responsible for safely creating and disposing llvm::MCSubtargetInfo via ffi functions.
/// Not cloneable as there is no clone function for llvm::MCSubtargetInfo.
pub(crate) struct OwnedMCSubtargetInfo {
info_unique: NonNull<llvm::MCSubtargetInfo>,
}

impl OwnedMCSubtargetInfo {
pub(crate) fn new(
triple: &CStr,
cpu: &CStr,
features: &CStr,
) -> Result<Self, LlvmError<'static>> {
// SAFETY: llvm::LLVMRustCreateMCSubtargetInfo copies pointed-to data.
let info_ptr = unsafe {
llvm::LLVMRustCreateMCSubtargetInfo(triple.as_ptr(), cpu.as_ptr(), features.as_ptr())
};

NonNull::new(info_ptr)
.map(|info_unique| Self { info_unique })
.ok_or_else(|| LlvmError::CreateMCSubtargetInfo { triple: SmallCStr::from(triple) })
}

pub(crate) fn has_feature(&self, feature: &CStr) -> bool {
// SAFETY: `new` ensures we have a valid pointer created by
// `llvm::LLVMRustCreateMCSubtargetInfo`.
unsafe {
llvm::LLVMRustMCSubtargetInfoHasFeature(self.info_unique.as_ref(), feature.as_ptr())
}
}
}

impl Drop for OwnedMCSubtargetInfo {
fn drop(&mut self) {
// SAFETY: `new` ensures we have a valid pointer created by
// `llvm::LLVMRustCreateMCSubtargetInfo` and `OwnedMCSubtargetInfo` is not copyable so
// there is no double free or use after free.
unsafe {
llvm::LLVMRustDisposeMCSubtargetInfo(self.info_unique);
}
}
}
7 changes: 2 additions & 5 deletions compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::ffi::CStr;
use std::marker::PhantomData;
use std::ptr::NonNull;

use rustc_data_structures::small_c_str::SmallCStr;
Expand All @@ -9,10 +8,8 @@ use crate::llvm;

/// Responsible for safely creating and disposing llvm::TargetMachine via ffi functions.
/// Not cloneable as there is no clone function for llvm::TargetMachine.
#[repr(transparent)]
pub struct OwnedTargetMachine {
tm_unique: NonNull<llvm::TargetMachine>,
phantom: PhantomData<llvm::TargetMachine>,
}

impl OwnedTargetMachine {
Expand Down Expand Up @@ -41,7 +38,7 @@ impl OwnedTargetMachine {
use_wasm_eh: bool,
large_data_threshold: u64,
) -> Result<Self, LlvmError<'static>> {
// SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data
// SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed-to data.
let tm_ptr = unsafe {
llvm::LLVMRustCreateTargetMachine(
triple.as_ptr(),
Expand Down Expand Up @@ -71,7 +68,7 @@ impl OwnedTargetMachine {
};

NonNull::new(tm_ptr)
.map(|tm_unique| Self { tm_unique, phantom: PhantomData })
.map(|tm_unique| Self { tm_unique })
.ok_or_else(|| LlvmError::CreateTargetMachine { triple: SmallCStr::from(triple) })
}

Expand Down
12 changes: 3 additions & 9 deletions compiler/rustc_codegen_llvm/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,12 @@ fn write_output_file<'ll>(
result.into_result().unwrap_or_else(|()| llvm_err(dcx, LlvmError::WriteOutput { path: output }))
}

/// If `for_cfg` is `true` then we are creating this machine for the purpose of populating
/// [`rustc_codegen_ssa::TargetConfig`] based on what LLVM actually enables in this configuration.
/// `-Ctarget-feature` should be ignored in that case since it is already processed separately.
pub(crate) fn create_informational_target_machine(
sess: &Session,
for_cfg: bool,
) -> OwnedTargetMachine {
pub(crate) fn create_informational_target_machine(sess: &Session) -> 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, for_cfg);
let features = llvm_util::global_llvm_features(sess, /* for_cfg */ false);

target_machine_factory(sess, config::OptLevel::No, &features)(sess.dcx(), config)
}

Expand Down Expand Up @@ -212,7 +207,6 @@ pub(crate) fn target_machine_factory(

let code_model = to_llvm_code_model(sess.code_model());

// This is used to set cfg_has_threads, so all logic must be in this method.
let singlethread = sess.target.singlethread(&sess.internal_target_features);

let triple = SmallCStr::new(&versioned_llvm_target(sess));
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ pub(crate) unsafe fn create_module<'ll>(

// Ensure the data-layout values hardcoded remain the defaults.
{
let tm = crate::back::write::create_informational_target_machine(sess, false);
let tm = crate::back::write::create_informational_target_machine(sess);
unsafe {
llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm.raw());
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_codegen_llvm/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ pub(crate) enum LlvmError<'a> {
WriteOutput { path: &'a Path },
#[diag("could not create LLVM TargetMachine for triple: {$triple}")]
CreateTargetMachine { triple: SmallCStr },
#[diag("could not create LLVM MCSubtargetInfo for triple: {$triple}")]
CreateMCSubtargetInfo { triple: SmallCStr },
#[diag("failed to run LLVM passes")]
RunLlvmPasses,
#[diag("failed to write LLVM IR to {$path}")]
Expand All @@ -145,6 +147,9 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for WithLlvmError<'_> {
CreateTargetMachine { .. } => {
msg!("could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}")
}
CreateMCSubtargetInfo { .. } => {
msg!("could not create LLVM MCSubtargetInfo for triple: {$triple}: {$llvm_err}")
}
RunLlvmPasses => msg!("failed to run LLVM passes: {$llvm_err}"),
WriteIr { .. } => msg!("failed to write LLVM IR to {$path}: {$llvm_err}"),
PrepareThinLtoContext => {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_codegen_llvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ impl CodegenBackend for LlvmCodegenBackend {

fn provide(&self, providers: &mut Providers) {
providers.queries.global_backend_features =
|tcx, ()| llvm_util::global_llvm_features(tcx.sess, false)
|tcx, ()| llvm_util::global_llvm_features(tcx.sess, /* for_cfg */ false)
}

fn print(&self, req: &PrintRequest, out: &mut String, sess: &Session) {
Expand Down Expand Up @@ -493,7 +493,7 @@ impl ModuleLlvm {
ModuleLlvm {
llmod_raw,
llcx,
tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess, false)),
tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)),
}
}
}
Expand Down
15 changes: 14 additions & 1 deletion compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ unsafe extern "C" {
pub type TargetMachine;
}
unsafe extern "C" {
pub(crate) type MCSubtargetInfo;
pub(crate) type Twine;
pub(crate) type DiagnosticInfo;
pub(crate) type SMDiagnostic;
Expand Down Expand Up @@ -2362,7 +2363,6 @@ unsafe extern "C" {
pub(crate) fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString);
pub(crate) fn LLVMRustWriteValueToString(value_ref: &Value, s: &RustString);

pub(crate) fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool;
pub(crate) fn LLVMRustTargetHasMnemonic(T: &TargetMachine, s: *const c_char) -> bool;

pub(crate) fn LLVMRustPrintTargetCPUs(TM: &TargetMachine, OutStr: &RustString);
Expand Down Expand Up @@ -2404,6 +2404,19 @@ unsafe extern "C" {
LargeDataThreshold: u64,
) -> *mut TargetMachine;

pub(crate) fn LLVMRustCreateMCSubtargetInfo(
TripleStr: *const c_char,
CPU: *const c_char,
Features: *const c_char,
) -> *mut MCSubtargetInfo;

pub(crate) fn LLVMRustMCSubtargetInfoHasFeature(
MCInfo: &MCSubtargetInfo,
Feature: *const c_char,
) -> bool;

pub(crate) fn LLVMRustDisposeMCSubtargetInfo(MCInfo: ptr::NonNull<MCSubtargetInfo>);

pub(crate) fn LLVMRustAddLibraryInfo<'a>(
T: &TargetMachine,
PM: &PassManager<'a>,
Expand Down
29 changes: 20 additions & 9 deletions compiler/rustc_codegen_llvm/src/llvm_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::sync::Once;
use std::{ptr, slice, str};

use libc::c_int;
use rustc_codegen_ssa::back::versioned_llvm_target;
use rustc_codegen_ssa::base::wants_wasm_eh;
use rustc_codegen_ssa::target_features::internal_target_features;
use rustc_codegen_ssa::{TargetConfig, target_features};
Expand All @@ -20,7 +21,8 @@ use rustc_target::spec::{
};
use smallvec::{SmallVec, smallvec};

use crate::back::write::create_informational_target_machine;
use crate::back::owned_mc_subtarget_info::OwnedMCSubtargetInfo;
use crate::back::write::{create_informational_target_machine, llvm_err};
use crate::{diagnostics, llvm};

static INIT: Once = Once::new();
Expand Down Expand Up @@ -318,7 +320,14 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
///
/// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled outside codegen.
pub(crate) fn target_config(sess: &Session) -> TargetConfig {
let target_machine = create_informational_target_machine(sess, true);
require_inited();
let target_features = global_llvm_features(sess, /* for_cfg */ true);

let triple = SmallCStr::new(&versioned_llvm_target(sess));
let cpu = SmallCStr::new(target_cpu(sess));
let features = CString::new(target_features.join(",")).unwrap();
let mc_subtarget_info = OwnedMCSubtargetInfo::new(&triple, &cpu, &features)
.unwrap_or_else(|err| llvm_err(sess.dcx(), err));

let internal_target_features = internal_target_features(
sess,
Expand All @@ -329,16 +338,17 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig {
},
|feature| {
// This closure determines whether the target CPU has the feature according to LLVM. We
// do *not* consider the `-Ctarget-feature`s here, as that will be handled later in
// do *not* consider the `-Ctarget-feature`s here (that's why we passed `for_cfg: true`
// to `global_llvm_features` above) because that will be handled later in
// `internal_target_features`.
if let Some(feat) = to_llvm_features(sess, feature) {
// All the LLVM features this expands to must be enabled.
for llvm_feature in feat {
let cstr = SmallCStr::new(llvm_feature);
// `LLVMRustHasFeature` is moderately expensive. On targets with many
// `has_feature` is moderately expensive. On targets with many
// features (e.g. x86) these calls take a non-trivial fraction of runtime
// when compiling very small programs.
if !unsafe { llvm::LLVMRustHasFeature(target_machine.raw(), cstr.as_ptr()) } {
if !mc_subtarget_info.has_feature(&cstr) {
return false;
}
}
Expand Down Expand Up @@ -479,7 +489,7 @@ fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> {

pub(crate) fn print(req: &PrintRequest, out: &mut String, sess: &Session) {
require_inited();
let tm = create_informational_target_machine(sess, false);
let tm = create_informational_target_machine(sess);
match req.kind {
PrintKind::TargetCPUs => print_target_cpus(sess, tm.raw(), out),
PrintKind::TargetFeatures => print_target_features(sess, tm.raw(), out),
Expand All @@ -497,10 +507,11 @@ fn print_target_cpus(sess: &Session, tm: &llvm::TargetMachine, out: &mut String)
cpu_name: &'a str,
remark: String,
}
// Compare CPU against current target to label the default.
// Compare CPU against current target to label the default. Do not print it if
// `need_explicit_cpu` is set, because in that case the concept of default makes less sense.
let target_cpu = handle_native(&sess.target.cpu);
let make_remark = |cpu_name| {
if cpu_name == target_cpu {
if cpu_name == target_cpu && !sess.target.need_explicit_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;
Expand Down Expand Up @@ -776,7 +787,7 @@ pub(crate) fn tune_cpu(sess: &Session) -> Option<&str> {

pub(crate) fn target_has_mnemonic(sess: &Session, mnemonic: &str) -> bool {
require_inited();
let tm = create_informational_target_machine(sess, false);
let tm = create_informational_target_machine(sess);
let cstr = SmallCStr::new(mnemonic);
unsafe { llvm::LLVMRustTargetHasMnemonic(tm.raw(), cstr.as_ptr()) }
}
30 changes: 23 additions & 7 deletions compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,31 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char *FileName) {
timeTraceProfilerCleanup();
}

extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM,
const char *Feature) {
TargetMachine *Target = unwrap(TM);
#if LLVM_VERSION_GE(23, 0)
const MCSubtargetInfo &MCInfo = Target->getMCSubtargetInfo();
extern "C" MCSubtargetInfo *
LLVMRustCreateMCSubtargetInfo(const char *TripleStr, const char *CPU,
const char *Features) {
std::string Error;
auto Trip = Triple(Triple::normalize(TripleStr));
const llvm::Target *TheTarget = TargetRegistry::lookupTarget(Trip, Error);
if (TheTarget == nullptr) {
LLVMRustSetLastError(Error.c_str());
return nullptr;
}

#if LLVM_VERSION_GE(22, 0)
return TheTarget->createMCSubtargetInfo(Trip, CPU, Features);
#else
const MCSubtargetInfo &MCInfo = *Target->getMCSubtargetInfo();
return TheTarget->createMCSubtargetInfo(Trip.str(), CPU, Features);
#endif
return MCInfo.checkFeatures(std::string("+") + Feature);
}

extern "C" bool LLVMRustMCSubtargetInfoHasFeature(MCSubtargetInfo *MCInfo,
const char *Feature) {
return MCInfo->checkFeatures(std::string("+") + Feature);
}

extern "C" void LLVMRustDisposeMCSubtargetInfo(MCSubtargetInfo *MCInfo) {
delete MCInfo;
}

/// Check whether the target has a specific assembly mnemonic like `ret` or
Expand Down
9 changes: 5 additions & 4 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2260,11 +2260,12 @@ pub struct TargetOptions {
/// Extra arguments to pass to the external assembler (when used)
pub asm_args: StaticCow<[StaticCow<str>]>,

/// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults
/// to "generic".
/// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Must be a name the backend
/// accepts. Defaults to "generic" (which some backends won't accept).
pub cpu: StaticCow<str>,
/// Whether a cpu needs to be explicitly set.
/// Set to true if there is no default cpu. Defaults to false.
/// Whether a cpu needs to be explicitly set via `-Ctarget-cpu` for codegen to run. (Even if
/// true, `cpu` is still consulted on non-codegen paths such as cfg/feature computation.)
/// Defaults to false.
pub need_explicit_cpu: bool,
/// Whether `-Ctarget-cpu` is treated as a target modifier. If this is set
/// all crates that are linked together must have been compiled with the
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/spec/targets/avr_none.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub(crate) fn target() -> Target {
pointer_width: 16,
options: TargetOptions {
c_int_width: 16,
cpu: "avr2".into(),
exe_suffix: ".elf".into(),
linker: Some("avr-gcc".into()),
eh_frame_header: false,
Expand Down
15 changes: 14 additions & 1 deletion tests/run-make/print-cfg/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::collections::HashSet;
use std::iter::FromIterator;
use std::path::PathBuf;

use run_make_support::{rfs, rustc};
use run_make_support::{llvm_components_contain, rfs, rustc};

struct PrintCfg {
target: &'static str,
Expand Down Expand Up @@ -73,6 +73,19 @@ fn main() {
includes: &["target_has_threads"],
disallow: &[],
});
// AVR is experimental, so don't assume it's supported.
if llvm_components_contain("avr") {
check(PrintCfg {
target: "avr-none",
args: &[],
includes: &[
"target_feature=\"addsubiw\"",
"target_feature=\"ijmpcall\"",
"target_feature=\"lpm\"",
],
disallow: &[],
});
}
}

fn check(PrintCfg { target, args, includes, disallow }: PrintCfg) {
Expand Down
7 changes: 6 additions & 1 deletion tests/run-make/target-specs/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,10 @@ fn main() {
.crate_type("lib")
.arg("-Ctarget-cpu=generic")
.run();
rustc().arg("-Zunstable-options").target("require-explicit-cpu").print("target-cpus").run();
rustc()
.arg("-Zunstable-options")
.target("require-explicit-cpu")
.print("target-cpus")
.run()
.assert_stdout_not_contains("default target CPU");
}
Loading
Loading