#![warn(rust_2018_idioms, single_use_lifetimes)]
use std::{
env,
io::Write,
process::{Command, Stdio},
str,
sync::atomic::{AtomicUsize, Ordering},
};
include!("no_atomic.rs");
const LATEST_STABLE: Version =
Version { minor: 59, nightly: false, commit_date: Date::new(2022, 2, 23) };
const PROBE_ATOMIC_128: &str = r#"
#![no_std]
#![feature(integer_atomics)]
fn _probe() {
let v = core::sync::atomic::AtomicU128::new(0_u128);
let _: u128 = v.swap(1_u128, core::sync::atomic::Ordering::Relaxed);
}
"#;
const PROBE_CMPXCHG16B: &str = r#"
#![no_std]
#![feature(stdsimd, cmpxchg16b_target_feature)]
#[allow(unused_unsafe)]
#[target_feature(enable = "cmpxchg16b")]
unsafe fn _probe(dst: *mut u128) {
unsafe {
let _: u128 = core::arch::x86_64::cmpxchg16b(
dst,
0_u128,
0_u128,
core::sync::atomic::Ordering::Relaxed,
core::sync::atomic::Ordering::Relaxed,
);
}
}
"#;
const PROBE_ATOMIC_INTRINSICS: &str = r#"
#![no_std]
#![feature(core_intrinsics)]
#[allow(unused_unsafe)]
unsafe fn _probe(dst: *mut u128) {
unsafe {
let _: u128 = core::intrinsics::atomic_load_acq(dst);
let _: u128 = core::intrinsics::atomic_load_relaxed(dst);
let _: u128 = core::intrinsics::atomic_load(dst);
let _: () = core::intrinsics::atomic_store_rel(dst, 0_u128);
let _: () = core::intrinsics::atomic_store_relaxed(dst, 0_u128);
let _: () = core::intrinsics::atomic_store(dst, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchg_acq(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchg_rel(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchg_acqrel(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchg_relaxed(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchg(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchg_acq_failrelaxed(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchg_acqrel_failrelaxed(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchg_failrelaxed(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchg_failacq(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchgweak_acq(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchgweak_rel(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchgweak_acqrel(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchgweak_relaxed(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchgweak(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchgweak_acq_failrelaxed(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchgweak_acqrel_failrelaxed(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchgweak_failrelaxed(dst, 0_u128, 0_u128);
let _: (u128, bool) = core::intrinsics::atomic_cxchgweak_failacq(dst, 0_u128, 0_u128);
}
}
"#;
fn main() {
println!("cargo:rerun-if-changed=no_atomic.rs");
let target = match env::var("TARGET") {
Ok(target) => target,
Err(e) => {
println!(
"cargo:warning={}: unable to get TARGET environment variable: {}",
env!("CARGO_PKG_NAME"),
e
);
return;
}
};
let aarch64 = target.starts_with("aarch64");
let x86_64 = target.starts_with("x86_64");
if aarch64 || x86_64 {
println!("cargo:rerun-if-env-changed=RUSTFLAGS");
println!("cargo:rerun-if-env-changed=CARGO_BUILD_RUSTFLAGS");
println!(
"cargo:rerun-if-env-changed=CARGO_TARGET_{}_RUSTFLAGS",
target.to_uppercase().replace('-', "_").replace('.', "_")
);
}
let version = match rustc_version() {
Some(version) => version,
None => {
println!("cargo:warning={}: unable to determine rustc version", env!("CARGO_PKG_NAME"));
LATEST_STABLE
}
};
if version.minor < 37 {
println!("cargo:rustc-cfg=portable_atomic_no_underscore_consts");
}
if version.minor < 45 {
println!("cargo:rustc-cfg=portable_atomic_no_atomic_min_max");
}
if version.minor < 52 {
println!("cargo:rustc-cfg=portable_atomic_no_unsafe_op_in_unsafe_fn");
}
if version.minor < 56 {
println!("cargo:rustc-cfg=portable_atomic_no_core_unwind_safe");
}
if version.minor < 59 {
println!("cargo:rustc-cfg=portable_atomic_no_asm");
}
if version.minor >= 61 && (!version.nightly || version.commit_date >= Date::new(2022, 3, 15)) {
println!("cargo:rustc-cfg=portable_atomic_aarch64_target_feature");
}
if version.minor >= 60 || version.nightly {
println!("cargo:rustc-cfg=portable_atomic_cfg_target_has_atomic");
if version.nightly && version.minor <= 60 && version.commit_date < Date::new(2022, 2, 10) {
println!("cargo:rustc-cfg=portable_atomic_unstable_cfg_target_has_atomic");
}
} else {
println!("cargo:rustc-cfg=portable_atomic_no_cfg_target_has_atomic");
if NO_ATOMIC_CAS.contains(&&*target) {
println!("cargo:rustc-cfg=portable_atomic_no_atomic_cas");
}
if NO_ATOMIC_64.contains(&&*target) {
println!("cargo:rustc-cfg=portable_atomic_no_atomic_64");
} else {
}
}
if NO_ATOMIC.contains(&&*target) {
println!("cargo:rustc-cfg=portable_atomic_no_atomic_load_store");
}
if target.starts_with("thumbv6m-") && target.contains("-none") {
println!("cargo:rustc-cfg=portable_atomic_armv6m");
}
if target.starts_with("armv5te-") {
println!("cargo:rustc-cfg=portable_atomic_armv5te");
}
if aarch64
&& (version.minor >= 59 || version.nightly)
&& has_target_feature("lse", target == "aarch64-apple-darwin", &version, Some(61))
{
println!("cargo:rustc-cfg=portable_atomic_target_feature_lse");
}
let may_use_cmpxchg16b = x86_64 && (version.minor >= 59 || version.nightly);
let mut has_cmpxchg16b = false;
if may_use_cmpxchg16b {
has_cmpxchg16b =
has_target_feature("cmpxchg16b", target == "x86_64-apple-darwin", &version, None);
}
if has_cmpxchg16b {
println!("cargo:rustc-cfg=portable_atomic_target_feature_cmpxchg16b");
}
if version.nightly {
println!("cargo:rustc-cfg=portable_atomic_nightly");
let sanitize = std::env::var("CARGO_CFG_SANITIZE").unwrap_or_default();
if sanitize.contains("thread") {
println!("cargo:rustc-cfg=sanitize_thread");
}
if aarch64 || x86_64 {
if HAS_ATOMIC_128.contains(&&*target)
&& probe(PROBE_ATOMIC_128, &target).unwrap_or(false)
{
println!("cargo:rustc-cfg=portable_atomic_core_atomic_128");
} else if may_use_cmpxchg16b
&& (has_cmpxchg16b
|| cfg!(feature = "fallback") && cfg!(feature = "outline-atomics"))
&& probe(PROBE_CMPXCHG16B, &target).unwrap_or(false)
{
println!("cargo:rustc-cfg=portable_atomic_cmpxchg16b_stdsimd");
if cfg!(feature = "fallback") && cfg!(feature = "outline-atomics") {
println!("cargo:rustc-cfg=portable_atomic_cmpxchg16b_dynamic");
}
}
} else if target.starts_with("s390x")
&& probe(PROBE_ATOMIC_INTRINSICS, &target).unwrap_or(false)
{
println!("cargo:rustc-cfg=portable_atomic_s390x_atomic_128");
}
}
}
struct Version {
minor: u32,
nightly: bool,
commit_date: Date,
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Date {
year: u16,
month: u8,
day: u8,
}
impl Date {
const fn new(year: u16, month: u8, day: u8) -> Self {
Self { year, month, day }
}
}
fn rustc_version() -> Option<Version> {
let rustc = env::var_os("RUSTC")?;
let output = Command::new(rustc).args(&["--version", "--verbose"]).output().ok()?;
let output = str::from_utf8(&output.stdout).ok()?;
let mut release = output
.lines()
.find(|line| line.starts_with("release: "))
.map(|line| &line["release: ".len()..])?
.splitn(2, '-');
let version = release.next().unwrap();
let channel = release.next().unwrap_or_default();
let mut digits = version.splitn(3, '.');
let major = digits.next()?.parse::<u32>().ok()?;
if major != 1 {
return None;
}
let minor = digits.next()?.parse::<u32>().ok()?;
let _patch = digits.next().unwrap_or("0").parse::<u32>().ok()?;
let nightly = channel == "nightly" || channel == "dev";
let mut commit_date = output
.lines()
.find(|line| line.starts_with("commit-date: "))
.map(|line| &line["commit-date: ".len()..])?
.splitn(3, '-');
let year = commit_date.next()?.parse::<u16>().ok()?;
let month = commit_date.next()?.parse::<u8>().ok()?;
let day = commit_date.next()?.parse::<u8>().ok()?;
if month > 12 || day > 31 {
return None;
}
Some(Version { minor, nightly, commit_date: Date::new(year, month, day) })
}
fn probe(code: &str, target: &str) -> Option<bool> {
static ID: AtomicUsize = AtomicUsize::new(0);
let rustc = env::var_os("RUSTC")?;
let out_dir = env::var_os("OUT_DIR")?;
let id = ID.fetch_add(1, Ordering::Relaxed);
let mut cmd = if let Some(wrapper) = env::var_os("CARGO_RUSTC_WRAPPER") {
let mut cmd = Command::new(wrapper);
cmd.arg(rustc);
cmd
} else {
Command::new(rustc)
};
cmd.stderr(Stdio::null())
.arg("--edition=2018")
.arg("--crate-name")
.arg(format!("portable_atomic_build{}", id))
.arg("--crate-type=lib")
.arg("--out-dir")
.arg(out_dir)
.arg("--emit=llvm-ir")
.arg("--cap-lints=warn");
cmd.arg("--target").arg(target);
if let Some(rustflags) = env::var_os("CARGO_ENCODED_RUSTFLAGS") {
if !rustflags.is_empty() {
for flag in rustflags.to_string_lossy().split('\x1f') {
cmd.arg(flag);
}
}
}
cmd.arg("-").stdin(Stdio::piped());
let mut child = cmd.spawn().ok()?;
let mut stdin = child.stdin.take().expect("rustc stdin");
stdin.write_all(code.as_bytes()).ok()?;
drop(stdin);
let status = child.wait().ok()?;
Some(status.success())
}
fn has_target_feature(
name: &str,
mut has_target_feature: bool,
version: &Version,
stabilized: Option<u32>,
) -> bool {
if version.nightly || stabilized.map_or(false, |stabilized| version.minor >= stabilized) {
has_target_feature = env::var("CARGO_CFG_TARGET_FEATURE")
.ok()
.map_or(false, |s| s.split(',').any(|s| s == name));
} else if let Some(rustflags) = env::var_os("CARGO_ENCODED_RUSTFLAGS") {
for mut flag in rustflags.to_string_lossy().split('\x1f') {
if flag.starts_with("-C") {
flag = &flag["-C".len()..];
}
if flag.starts_with("target-feature=") {
flag = &flag["target-feature=".len()..];
for s in flag.split(',').filter(|s| !s.is_empty()) {
match (s.as_bytes()[0] as char, &s.as_bytes()[1..]) {
('+', f) if f == name.as_bytes() => has_target_feature = true,
('-', f) if f == name.as_bytes() => has_target_feature = false,
_ => {}
}
}
}
}
}
has_target_feature
}