Skip to content
This repository was archived by the owner on Apr 28, 2025. It is now read-only.

Add copysignf16, copysignf128, fabsf16, and fabsf128 #320

Closed
wants to merge 4 commits into from
Closed
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
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,17 @@ arch = []

# This tells the compiler to assume that a Nightly toolchain is being used and
# that it should activate any useful Nightly things accordingly.
unstable = ["unstable-intrinsics"]
unstable = ["unstable-intrinsics", "unstable-float"]

# Enable calls to functions in `core::intrinsics`
unstable-intrinsics = []

# Make some internal things public for testing.
unstable-test-support = []

# Enable the nightly-only `f16` and `f128`.
unstable-float = []

# Used to prevent using any intrinsics or arch-specific code.
#
# HACK: this is a negative feature which is generally a bad idea in Cargo, but
Expand Down
30 changes: 5 additions & 25 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use std::env;

mod configure;

fn main() {
let cfg = configure::Config::from_env();

println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rustc-check-cfg=cfg(assert_no_panic)");

Expand All @@ -14,29 +18,5 @@ fn main() {
}
}

configure_intrinsics();
configure_arch();
}

/// Simplify the feature logic for enabling intrinsics so code only needs to use
/// `cfg(intrinsics_enabled)`.
fn configure_intrinsics() {
println!("cargo:rustc-check-cfg=cfg(intrinsics_enabled)");

// Disabled by default; `unstable-intrinsics` enables again; `force-soft-floats` overrides
// to disable.
if cfg!(feature = "unstable-intrinsics") && !cfg!(feature = "force-soft-floats") {
println!("cargo:rustc-cfg=intrinsics_enabled");
}
}

/// Simplify the feature logic for enabling arch-specific features so code only needs to use
/// `cfg(arch_enabled)`.
fn configure_arch() {
println!("cargo:rustc-check-cfg=cfg(arch_enabled)");

// Enabled by default via the "arch" feature, `force-soft-floats` overrides to disable.
if cfg!(feature = "arch") && !cfg!(feature = "force-soft-floats") {
println!("cargo:rustc-cfg=arch_enabled");
}
configure::emit_libm_config(&cfg);
}
10 changes: 9 additions & 1 deletion ci/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export RUST_BACKTRACE="${RUST_BACKTRACE:-full}"
# Needed for no-panic to correct detect a lack of panics
export RUSTFLAGS="${RUSTFLAGS:-} -Ccodegen-units=1"

# MPFR tests consume a lot of stack and often overflow. Default to 20 MiB (10x
# the default)
export RUST_MIN_STACK="${RUST_MIN_STACK:-20971520}"

target="${1:-}"

if [ -z "$target" ]; then
Expand Down Expand Up @@ -57,7 +61,7 @@ case "$target" in
*windows-gnu) extra_flags="$extra_flags --exclude libm-macros" ;;
esac

# Make sure we can build with overriding features. We test the indibidual
# Make sure we can build with overriding features. We test the individual
# features it controls separately.
cargo check --no-default-features
cargo check --features "force-soft-floats"
Expand All @@ -69,6 +73,10 @@ if [ "${BUILD_ONLY:-}" = "1" ]; then

echo "can't run tests on $target; skipping"
else
# Check `force-soft-floats` again, this time including test crates.
cargo check --all --target "$target" $extra_flags \
--features "force-soft-floats" --all-targets

cmd="cargo test --all --target $target $extra_flags"

# stable by default
Expand Down
168 changes: 168 additions & 0 deletions configure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Configuration shared with both libm and libm-test

use std::env;
use std::path::PathBuf;

#[allow(dead_code)]
pub struct Config {
pub manifest_dir: PathBuf,
pub out_dir: PathBuf,
pub opt_level: u8,
pub target_arch: String,
pub target_env: String,
pub target_family: Option<String>,
pub target_os: String,
pub target_string: String,
pub target_vendor: String,
pub target_features: Vec<String>,
}

impl Config {
pub fn from_env() -> Self {
let target_features = env::var("CARGO_CFG_TARGET_FEATURE")
.map(|feats| feats.split(',').map(ToOwned::to_owned).collect())
.unwrap_or_default();

Self {
manifest_dir: PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()),
out_dir: PathBuf::from(env::var("OUT_DIR").unwrap()),
opt_level: env::var("OPT_LEVEL").unwrap().parse().unwrap(),
target_arch: env::var("CARGO_CFG_TARGET_ARCH").unwrap(),
target_env: env::var("CARGO_CFG_TARGET_ENV").unwrap(),
target_family: env::var("CARGO_CFG_TARGET_FAMILY").ok(),
target_os: env::var("CARGO_CFG_TARGET_OS").unwrap(),
target_string: env::var("TARGET").unwrap(),
target_vendor: env::var("CARGO_CFG_TARGET_VENDOR").unwrap(),
target_features,
}
}
}

/// Libm gets most config options made available.
#[allow(dead_code)]
pub fn emit_libm_config(cfg: &Config) {
emit_intrinsics_cfg();
emit_arch_cfg();
emit_optimization_cfg(cfg);
emit_cfg_shorthands(cfg);
emit_f16_f128_cfg(cfg);
}

/// Tests don't need most feature-related config.
#[allow(dead_code)]
pub fn emit_test_config(cfg: &Config) {
emit_optimization_cfg(cfg);
emit_cfg_shorthands(cfg);
emit_f16_f128_cfg(cfg);
}

/// Simplify the feature logic for enabling intrinsics so code only needs to use
/// `cfg(intrinsics_enabled)`.
fn emit_intrinsics_cfg() {
println!("cargo:rustc-check-cfg=cfg(intrinsics_enabled)");

// Disabled by default; `unstable-intrinsics` enables again; `force-soft-floats` overrides
// to disable.
if cfg!(feature = "unstable-intrinsics") && !cfg!(feature = "force-soft-floats") {
println!("cargo:rustc-cfg=intrinsics_enabled");
}
}

/// Simplify the feature logic for enabling arch-specific features so code only needs to use
/// `cfg(arch_enabled)`.
fn emit_arch_cfg() {
println!("cargo:rustc-check-cfg=cfg(arch_enabled)");

// Enabled by default via the "arch" feature, `force-soft-floats` overrides to disable.
if cfg!(feature = "arch") && !cfg!(feature = "force-soft-floats") {
println!("cargo:rustc-cfg=arch_enabled");
}
}

/// Some tests are extremely slow. Emit a config option based on optimization level.
fn emit_optimization_cfg(cfg: &Config) {
println!("cargo:rustc-check-cfg=cfg(optimizations_enabled)");

if cfg.opt_level >= 2 {
println!("cargo:rustc-cfg=optimizations_enabled");
}
}

/// Provide an alias for common longer config combinations.
fn emit_cfg_shorthands(cfg: &Config) {
println!("cargo:rustc-check-cfg=cfg(x86_no_sse)");
if cfg.target_arch == "x86" && !cfg.target_features.iter().any(|f| f == "sse") {
// Shorthand to detect i586 targets
println!("cargo:rustc-cfg=x86_no_sse");
}
}

/// Configure whether or not `f16` and `f128` support should be enabled.
fn emit_f16_f128_cfg(cfg: &Config) {
println!("cargo:rustc-check-cfg=cfg(f16_enabled)");
println!("cargo:rustc-check-cfg=cfg(f128_enabled)");

// `unstable-float` enables these features.
if !cfg!(feature = "unstable-float") {
return;
}

// Set whether or not `f16` and `f128` are supported at a basic level by LLVM. This only means
// that the backend will not crash when using these types and generates code that can be called
// without crashing (no infinite recursion). This does not mean that the platform doesn't have
// ABI or other bugs.
//
// We do this here rather than in `rust-lang/rust` because configuring via cargo features is
// not straightforward.
//
// Original source of this list:
// <https://github.com/rust-lang/compiler-builtins/pull/652#issuecomment-2266151350>
let f16_enabled = match cfg.target_arch.as_str() {
// Unsupported <https://github.com/llvm/llvm-project/issues/94434>
"arm64ec" => false,
// Selection failure <https://github.com/llvm/llvm-project/issues/50374>
"s390x" => false,
// Infinite recursion <https://github.com/llvm/llvm-project/issues/97981>
// FIXME(llvm): loongarch fixed by <https://github.com/llvm/llvm-project/pull/107791>
"csky" => false,
"hexagon" => false,
"loongarch64" => false,
"mips" | "mips64" | "mips32r6" | "mips64r6" => false,
"powerpc" | "powerpc64" => false,
"sparc" | "sparc64" => false,
"wasm32" | "wasm64" => false,
// Most everything else works as of LLVM 19
_ => true,
};

let f128_enabled = match cfg.target_arch.as_str() {
// Unsupported (libcall is not supported) <https://github.com/llvm/llvm-project/issues/121122>
"amdgpu" => false,
// Unsupported <https://github.com/llvm/llvm-project/issues/94434>
"arm64ec" => false,
// Selection failure <https://github.com/llvm/llvm-project/issues/96432>
"mips64" | "mips64r6" => false,
// Selection failure <https://github.com/llvm/llvm-project/issues/95471>
"nvptx64" => false,
// Selection failure <https://github.com/llvm/llvm-project/issues/101545>
"powerpc64" if &cfg.target_os == "aix" => false,
// Selection failure <https://github.com/llvm/llvm-project/issues/41838>
"sparc" => false,
// Most everything else works as of LLVM 19
_ => true,
};

// If the feature is set, disable these types.
let disable_both = env::var_os("CARGO_FEATURE_NO_F16_F128").is_some();

println!("cargo:rustc-check-cfg=cfg(f16_enabled)");
println!("cargo:rustc-check-cfg=cfg(f128_enabled)");

if f16_enabled && !disable_both {
println!("cargo:rustc-cfg=f16_enabled");
}

if f128_enabled && !disable_both {
println!("cargo:rustc-cfg=f128_enabled");
}
}
2 changes: 2 additions & 0 deletions crates/compiler-builtins-smoke-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ force-soft-floats = []
unexpected_cfgs = { level = "warn", check-cfg = [
"cfg(arch_enabled)",
"cfg(assert_no_panic)",
"cfg(f128_enabled)",
"cfg(f16_enabled)",
"cfg(intrinsics_enabled)",
] }
7 changes: 7 additions & 0 deletions crates/libm-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,10 @@ heck = "0.5.0"
proc-macro2 = "1.0.88"
quote = "1.0.37"
syn = { version = "2.0.79", features = ["full", "extra-traits", "visit-mut"] }

[lints.rust]
# Values used during testing
unexpected_cfgs = { level = "warn", check-cfg = [
'cfg(f16_enabled)',
'cfg(f128_enabled)',
] }
66 changes: 54 additions & 12 deletions crates/libm-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ use syn::visit_mut::VisitMut;
use syn::{Ident, ItemEnum};

const ALL_FUNCTIONS: &[(Ty, Signature, Option<Signature>, &[&str])] = &[
(
// `fn(f16) -> f16`
Ty::F16,
Signature { args: &[Ty::F16], returns: &[Ty::F16] },
None,
&["fabsf16"],
),
(
// `fn(f32) -> f32`
Ty::F32,
Expand All @@ -36,6 +43,20 @@ const ALL_FUNCTIONS: &[(Ty, Signature, Option<Signature>, &[&str])] = &[
"tgamma", "trunc",
],
),
(
// `fn(f128) -> f128`
Ty::F128,
Signature { args: &[Ty::F128], returns: &[Ty::F128] },
None,
&["fabsf128"],
),
(
// `(f16, f16) -> f16`
Ty::F16,
Signature { args: &[Ty::F16, Ty::F16], returns: &[Ty::F16] },
None,
&["copysignf16"],
),
(
// `(f32, f32) -> f32`
Ty::F32,
Expand Down Expand Up @@ -72,6 +93,13 @@ const ALL_FUNCTIONS: &[(Ty, Signature, Option<Signature>, &[&str])] = &[
"remainder",
],
),
(
// `(f128, f128) -> f128`
Ty::F128,
Signature { args: &[Ty::F128, Ty::F128], returns: &[Ty::F128] },
None,
&["copysignf128"],
),
(
// `(f32, f32, f32) -> f32`
Ty::F32,
Expand Down Expand Up @@ -190,7 +218,7 @@ const KNOWN_TYPES: &[&str] = &["FTy", "CFn", "CArgs", "CRet", "RustFn", "RustArg

/// A type used in a function signature.
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Ty {
F16,
F32,
Expand Down Expand Up @@ -331,7 +359,7 @@ pub fn base_name_enum(attributes: pm::TokenStream, tokens: pm::TokenStream) -> p
/// // The Rust version's return type (e.g. `(f32, f32)`)
/// RustRet: $RustRet:ty,
/// // Attributes for the current function, if any
/// attrs: [$($meta:meta)*]
/// attrs: [$($meta:meta),*],
/// // Extra tokens passed directly (if any)
/// extra: [$extra:ident],
/// // Extra function-tokens passed directly (if any)
Expand All @@ -349,6 +377,8 @@ pub fn base_name_enum(attributes: pm::TokenStream, tokens: pm::TokenStream) -> p
/// skip: [sin, cos],
/// // Attributes passed as `attrs` for specific functions. For example, here the invocation
/// // with `sinf` and that with `cosf` will both get `meta1` and `meta2`, but no others will.
/// // Note that `f16_enabled` and `f128_enabled` will always get emitted regardless of whether
/// // or not this is specified.
/// attributes: [
/// #[meta1]
/// #[meta2]
Expand Down Expand Up @@ -507,16 +537,28 @@ fn expand(input: StructuredInput, fn_list: &[&FunctionInfo]) -> syn::Result<pm2:
let fn_name = Ident::new(func.name, Span::call_site());

// Prepare attributes in an `attrs: ...` field
let meta_field = match &input.attributes {
Some(attrs) => {
let meta = attrs
.iter()
.filter(|map| map.names.contains(&fn_name))
.flat_map(|map| &map.meta);
quote! { attrs: [ #( #meta )* ] }
}
None => pm2::TokenStream::new(),
};
let mut meta_fields = Vec::new();
if let Some(attrs) = &input.attributes {
let meta_iter = attrs
.iter()
.filter(|map| map.names.contains(&fn_name))
.flat_map(|map| &map.meta)
.map(|v| v.into_token_stream());

meta_fields.extend(meta_iter);
}

// Always emit f16 and f128 meta so this doesn't need to be repeated everywhere
if func.rust_sig.args.contains(&Ty::F16) || func.rust_sig.returns.contains(&Ty::F16) {
let ts = quote! { cfg(f16_enabled) };
meta_fields.push(ts);
}
if func.rust_sig.args.contains(&Ty::F128) || func.rust_sig.returns.contains(&Ty::F128) {
let ts = quote! { cfg(f128_enabled) };
meta_fields.push(ts);
}

let meta_field = quote! { attrs: [ #( #meta_fields ),* ], };

// Prepare extra in an `extra: ...` field, running the replacer
let extra_field = match input.extra.clone() {
Expand Down
Loading
Loading