use bitflags::bitflags;
bitflags! {
pub struct Arch: u8 {
const AARCH64 = 1 << 0;
const X86_64 = 1 << 1;
const RISCV64 = 1 << 2;
const ALL = Self::AARCH64.bits() | Self::X86_64.bits() | Self::RISCV64.bits();
}
pub struct CompilerFamily: u8 {
const GNU = 1 << 0;
const CLANG = 1 << 1;
const ALL = Self::GNU.bits() | Self::CLANG.bits();
}
pub struct CompilerLanguage: u8 {
const C = 1 << 0;
const CXX = 1 << 1;
const ALL = Self::C.bits() | Self::CXX.bits();
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct CompilerVersion {
major: u16,
minor: u16,
}
impl CompilerVersion {
pub const fn new(major: u16, minor: u16) -> Self {
Self { major, minor }
}
#[inline]
pub fn parse_str(str: &str) -> Option<Self> {
let mut parts = str.split('.');
let major = parts.next()?.trim().parse::<u16>().ok()?;
let minor = parts.next().unwrap_or("0").trim().parse::<u16>().ok()?;
Some(Self::new(major, minor))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Compiler {
arch: Arch,
family: CompilerFamily,
language: CompilerLanguage,
version: CompilerVersion,
}
impl Compiler {
pub const fn new(
arch: Arch,
family: CompilerFamily,
language: CompilerLanguage,
version: CompilerVersion,
) -> Self {
Self {
arch,
family,
language,
version,
}
}
}
struct CompileFlag {
name: &'static str,
archs: Arch,
families: CompilerFamily,
languages: CompilerLanguage,
min_version: Option<CompilerVersion>,
}
impl CompileFlag {
#[inline]
fn is_active(&self, compiler: &Compiler) -> bool {
self.archs.intersects(compiler.arch)
&& self.families.intersects(compiler.family)
&& self.languages.intersects(compiler.language)
&& self.min_version.map_or(true, |min| compiler.version >= min)
}
}
pub fn get_compile_flags(compiler: &Compiler) -> impl Iterator<Item = &'static str> + '_ {
const COMPILE_FLAGS: &[CompileFlag] = &[
CompileFlag {
name: "-gdwarf",
archs: Arch::ALL,
families: CompilerFamily::ALL,
languages: CompilerLanguage::ALL,
min_version: None,
},
CompileFlag {
name: "-ffunction-sections",
archs: Arch::ALL,
families: CompilerFamily::ALL,
languages: CompilerLanguage::ALL,
min_version: None,
},
CompileFlag {
name: "-fdata-sections",
archs: Arch::ALL,
families: CompilerFamily::ALL,
languages: CompilerLanguage::ALL,
min_version: None,
},
CompileFlag {
name: "-fno-common",
archs: Arch::ALL,
families: CompilerFamily::ALL,
languages: CompilerLanguage::ALL,
min_version: None,
},
CompileFlag {
name: "-fmerge-constants",
archs: Arch::ALL,
families: CompilerFamily::GNU,
languages: CompilerLanguage::ALL,
min_version: Some(CompilerVersion::new(4, 8)),
},
CompileFlag {
name: "-fno-tree-slp-vectorize",
archs: Arch::ALL,
families: CompilerFamily::GNU,
languages: CompilerLanguage::ALL,
min_version: Some(CompilerVersion::new(4, 9)),
},
CompileFlag {
name: "-fno-slp-vectorize",
archs: Arch::ALL,
families: CompilerFamily::CLANG,
languages: CompilerLanguage::ALL,
min_version: None,
},
CompileFlag {
name: "-fno-integrated-as",
archs: Arch::ALL,
families: CompilerFamily::CLANG,
languages: CompilerLanguage::ALL,
min_version: None,
},
CompileFlag {
name: "-Werror=uninitialized",
archs: Arch::ALL,
families: CompilerFamily::CLANG,
languages: CompilerLanguage::ALL,
min_version: None,
},
CompileFlag {
name: "-mno-outline-atomics",
archs: Arch::AARCH64,
families: CompilerFamily::GNU,
languages: CompilerLanguage::ALL,
min_version: Some(CompilerVersion::new(9, 4)),
},
CompileFlag {
name: "-mno-outline-atomics",
archs: Arch::AARCH64,
families: CompilerFamily::CLANG,
languages: CompilerLanguage::ALL,
min_version: Some(CompilerVersion::new(10, 0)),
},
];
COMPILE_FLAGS
.iter()
.filter(move |f| f.is_active(compiler))
.map(|f| f.name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_version_parse() {
assert_eq!(
CompilerVersion::parse_str("11"),
Some(CompilerVersion::new(11, 0))
);
assert_eq!(
CompilerVersion::parse_str("11.2"),
Some(CompilerVersion::new(11, 2))
);
assert_eq!(
CompilerVersion::parse_str("9.4.1"),
Some(CompilerVersion::new(9, 4))
);
assert_eq!(
CompilerVersion::parse_str(" 11 . 2 "),
Some(CompilerVersion::new(11, 2))
);
assert_eq!(CompilerVersion::parse_str("11."), None);
assert_eq!(CompilerVersion::parse_str("11.invalid"), None);
assert_eq!(CompilerVersion::parse_str("invalid"), None);
assert_eq!(CompilerVersion::parse_str(""), None);
}
#[test]
fn test_gcc_modern_aarch64() {
let compiler = Compiler::new(
Arch::AARCH64,
CompilerFamily::GNU,
CompilerLanguage::C,
CompilerVersion::new(9, 4),
);
let flags: Vec<_> = get_compile_flags(&compiler).collect();
assert!(flags.contains(&"-gdwarf"));
assert!(flags.contains(&"-ffunction-sections"));
assert!(flags.contains(&"-fdata-sections"));
assert!(flags.contains(&"-fmerge-constants"));
assert!(flags.contains(&"-fno-common"));
assert!(flags.contains(&"-fno-tree-slp-vectorize"));
assert!(flags.contains(&"-mno-outline-atomics"));
assert!(!flags.contains(&"-fno-slp-vectorize"));
assert!(!flags.contains(&"-fno-integrated-as"));
assert!(!flags.contains(&"-Werror=uninitialized"));
}
#[test]
fn test_clang_x86_64() {
let compiler = Compiler::new(
Arch::X86_64,
CompilerFamily::CLANG,
CompilerLanguage::CXX,
CompilerVersion::new(12, 0),
);
let flags: Vec<_> = get_compile_flags(&compiler).collect();
assert!(flags.contains(&"-fno-slp-vectorize"));
assert!(flags.contains(&"-Werror=uninitialized"));
assert!(flags.contains(&"-fno-integrated-as"));
assert!(!flags.contains(&"-mno-outline-atomics"));
}
#[test]
fn test_old_gcc_riscv() {
let compiler = Compiler::new(
Arch::RISCV64,
CompilerFamily::GNU,
CompilerLanguage::C,
CompilerVersion::new(4, 7),
);
let flags: Vec<_> = get_compile_flags(&compiler).collect();
assert!(flags.contains(&"-gdwarf"));
assert!(!flags.contains(&"-fmerge-constants"));
assert!(!flags.contains(&"-fno-tree-slp-vectorize"));
}
#[test]
fn test_version_thresholds() {
let arch = Arch::AARCH64;
let fam = CompilerFamily::CLANG;
let lang = CompilerLanguage::C;
let v9 = Compiler::new(arch, fam, lang, CompilerVersion::new(9, 0));
assert!(!get_compile_flags(&v9).any(|f| f == "-mno-outline-atomics"));
let v10 = Compiler::new(arch, fam, lang, CompilerVersion::new(10, 0));
assert!(get_compile_flags(&v10).any(|f| f == "-mno-outline-atomics"));
}
}