Auto merge of #94579 - tmiasko:target-features, r=nagisa

Always include global target features in function attributes

This ensures that information about target features configured with
`-C target-feature=...` or detected with `-C target-cpu=native` is
retained for subsequent consumers of LLVM bitcode.

This is crucial for linker plugin LTO, since this information is not
conveyed to the plugin otherwise.

<details><summary>Additional test case demonstrating the issue</summary>

```rust
extern crate core;

#[inline]
#[target_feature(enable = "aes")]
unsafe fn f(a: u128, b: u128) -> u128 {
    use core::arch::x86_64::*;
    use core::mem::transmute;
    transmute(_mm_aesenc_si128(transmute(a), transmute(b)))
}

pub fn g(a: u128, b: u128) -> u128 {
    unsafe { f(a, b) }
}

fn main() {
    let mut args = std::env::args();
    let _ = args.next().unwrap();
    let a: u128 = args.next().unwrap().parse().unwrap();
    let b: u128 = args.next().unwrap().parse().unwrap();
    println!("{}", g(a, b));
}
```

```console
$ rustc --edition=2021 a.rs -Clinker-plugin-lto -Clink-arg=-fuse-ld=lld  -Ctarget-feature=+aes -O
...
  = note: LLVM ERROR: Cannot select: intrinsic %llvm.x86.aesni.aesenc
```

</details>

r? `@nagisa`
This commit is contained in:
bors 2022-03-06 18:07:11 +00:00
commit c38b8a8c62
7 changed files with 101 additions and 11 deletions

View file

@ -1,6 +1,7 @@
//! Set and unset common attributes on LLVM values.
use rustc_codegen_ssa::traits::*;
use rustc_data_structures::small_str::SmallStr;
use rustc_hir::def_id::DefId;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::ty::{self, TyCtxt};
@ -377,13 +378,12 @@ pub fn from_fn_attrs<'ll, 'tcx>(
}
}
if !function_features.is_empty() {
let global_features = cx.tcx.global_backend_features(()).iter().map(|s| &s[..]);
let val = global_features
.chain(function_features.iter().map(|s| &s[..]))
.intersperse(",")
.collect::<String>();
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "target-features", &val));
let global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str());
let function_features = function_features.iter().map(|s| s.as_str());
let target_features =
global_features.chain(function_features).intersperse(",").collect::<SmallStr<1024>>();
if !target_features.is_empty() {
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "target-features", &target_features));
}
attributes::apply_to_llfn(llfn, Function, &to_add);

View file

@ -20,7 +20,7 @@ stable_deref_trait = "1.0.0"
rayon = { version = "0.3.2", package = "rustc-rayon" }
rayon-core = { version = "0.3.2", package = "rustc-rayon-core" }
rustc-hash = "1.1.0"
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
smallvec = { version = "1.6.1", features = ["const_generics", "union", "may_dangle"] }
rustc_index = { path = "../rustc_index", package = "rustc_index" }
bitflags = "1.2.1"
measureme = "10.0.0"

View file

@ -80,6 +80,7 @@ pub mod obligation_forest;
pub mod owning_ref;
pub mod sip128;
pub mod small_c_str;
pub mod small_str;
pub mod snapshot_map;
pub mod stable_map;
pub mod svh;

View file

@ -62,6 +62,7 @@ impl SmallCStr {
impl Deref for SmallCStr {
type Target = ffi::CStr;
#[inline]
fn deref(&self) -> &ffi::CStr {
self.as_c_str()
}

View file

@ -0,0 +1,68 @@
use smallvec::SmallVec;
#[cfg(test)]
mod tests;
/// Like SmallVec but for strings.
#[derive(Default)]
pub struct SmallStr<const N: usize>(SmallVec<[u8; N]>);
impl<const N: usize> SmallStr<N> {
#[inline]
pub fn new() -> Self {
SmallStr(SmallVec::default())
}
#[inline]
pub fn push_str(&mut self, s: &str) {
self.0.extend_from_slice(s.as_bytes());
}
#[inline]
pub fn empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn spilled(&self) -> bool {
self.0.spilled()
}
#[inline]
pub fn as_str(&self) -> &str {
unsafe { std::str::from_utf8_unchecked(self.0.as_slice()) }
}
}
impl<const N: usize> std::ops::Deref for SmallStr<N> {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.as_str()
}
}
impl<const N: usize, A: AsRef<str>> FromIterator<A> for SmallStr<N> {
#[inline]
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = A>,
{
let mut s = SmallStr::default();
s.extend(iter);
s
}
}
impl<const N: usize, A: AsRef<str>> Extend<A> for SmallStr<N> {
#[inline]
fn extend<T>(&mut self, iter: T)
where
T: IntoIterator<Item = A>,
{
for a in iter.into_iter() {
self.push_str(a.as_ref());
}
}
}

View file

@ -0,0 +1,20 @@
use super::*;
#[test]
fn empty() {
let s = SmallStr::<1>::new();
assert!(s.empty());
assert_eq!("", s.as_str());
assert!(!s.spilled());
}
#[test]
fn from_iter() {
let s = ["aa", "bb", "cc"].iter().collect::<SmallStr<6>>();
assert_eq!("aabbcc", s.as_str());
assert!(!s.spilled());
let s = ["aa", "bb", "cc", "dd"].iter().collect::<SmallStr<6>>();
assert_eq!("aabbccdd", s.as_str());
assert!(s.spilled());
}

View file

@ -29,7 +29,7 @@ pub unsafe fn apple() -> u32 {
peach()
}
// target features same as global (not reflected or overriden in IR)
// target features same as global
#[no_mangle]
pub unsafe fn banana() -> u32 {
// CHECK-LABEL: @banana()
@ -43,5 +43,5 @@ pub unsafe fn banana() -> u32 {
// COMPAT-SAME: "target-features"="+avx2,+avx,+avx"
// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx"
// CHECK: attributes [[BANANAATTRS]]
// CHECK-NOT: target-features
// CHECK-SAME: }
// COMPAT-SAME: "target-features"="+avx2,+avx"
// INCOMPAT-SAME: "target-features"="-avx2,-avx"