use crate::clang::lexer::Lexer;
use crate::clang::lexer::*;
use crate::clang::preprocessor::*;
use crate::clang::token::*;
use crate::clang::CLangStandard;
use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86TargetOS {
Linux,
Darwin,
FreeBSD,
OpenBSD,
NetBSD,
Windows,
Cygwin,
Android,
Emscripten,
Wasi,
Unknown,
}
impl X86TargetOS {
pub fn from_str(s: &str) -> Self {
match s {
"linux" => Self::Linux,
"darwin" | "macos" | "macosx" => Self::Darwin,
"freebsd" => Self::FreeBSD,
"openbsd" => Self::OpenBSD,
"netbsd" => Self::NetBSD,
"windows" | "win32" | "win64" | "msvc" => Self::Windows,
"cygwin" => Self::Cygwin,
"android" => Self::Android,
"emscripten" => Self::Emscripten,
"wasi" => Self::Wasi,
_ => Self::Unknown,
}
}
pub fn is_unix(&self) -> bool {
matches!(
self,
Self::Linux
| Self::Darwin
| Self::FreeBSD
| Self::OpenBSD
| Self::NetBSD
| Self::Android
)
}
pub fn is_windows(&self) -> bool {
matches!(self, Self::Windows | Self::Cygwin)
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Linux => "linux",
Self::Darwin => "darwin",
Self::FreeBSD => "freebsd",
Self::OpenBSD => "openbsd",
Self::NetBSD => "netbsd",
Self::Windows => "windows",
Self::Cygwin => "cygwin",
Self::Android => "android",
Self::Emscripten => "emscripten",
Self::Wasi => "wasi",
Self::Unknown => "unknown",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86TargetEnv {
Gnu,
Musl,
Bionic,
MSVC,
Mingw,
Unknown,
}
impl X86TargetEnv {
pub fn from_str(s: &str) -> Self {
match s {
"gnu" | "gnueabi" | "gnueabihf" => Self::Gnu,
"musl" | "musleabi" | "musleabihf" => Self::Musl,
"android" | "bionic" | "androideabi" => Self::Bionic,
"msvc" => Self::MSVC,
"mingw" | "mingw32" => Self::Mingw,
_ => Self::Unknown,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Gnu => "gnu",
Self::Musl => "musl",
Self::Bionic => "bionic",
Self::MSVC => "msvc",
Self::Mingw => "mingw",
Self::Unknown => "unknown",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86Feature {
MMX,
ThreeDNow,
ThreeDNowA,
SSE,
SSE2,
SSE3,
SSSE3,
SSE41,
SSE42,
SSE4A,
AVX,
AVX2,
AVX512F,
AVX512BW,
AVX512CD,
AVX512DQ,
AVX512ER,
AVX512PF,
AVX512VL,
AVX512IFMA,
AVX512VBMI,
AVX512VBMI2,
AVX512VNNI,
AVX512BITALG,
AVX512VPOPCNTDQ,
AVX5124FMAPS,
AVX5124VNNIW,
AVX512BF16,
AVX512FP16,
AVXVNNI,
AVXIFMA,
AVXNECONVERT,
AVXVNNIINT8,
FMA,
FMA4,
XOP,
F16C,
BMI,
BMI2,
LZCNT,
POPCNT,
TBM,
ADX,
SHA,
SGX,
CLFLUSHOPT,
CLWB,
PCLMUL,
AES,
RDRAND,
RDSEED,
FSGSBASE,
RDPID,
MOVBE,
MOVDIRI,
MOVDIR64B,
PTWRITE,
INVPCID,
SMAP,
SMEP,
CET,
SHSTK,
IBT,
WAITPKG,
CLDEMOTE,
SERIALIZE,
TSXLDTRK,
UINTR,
AMXBF16,
AMXTILE,
AMXINT8,
AMXFP16,
AMXCOMPLEX,
ENQCMD,
GFNI,
VAES,
VPCLMULQDQ,
PREFETCHI,
PREFETCHWT1,
RDPRU,
MCOMMIT,
CLZERO,
WBNOINVD,
XSAVE,
XSAVEC,
XSAVES,
XSAVEOPT,
PKU,
OSPKE,
LAHFSAHF,
CX16,
PRFCHW,
RAOINT,
CMPCCXADD,
HRESET,
KL,
WIDEKL,
AVX10_1_256,
AVX10_1_512,
APXF,
AVXNECONVERT2,
}
impl X86Feature {
pub fn macro_def(&self) -> Option<(&'static str, &'static str)> {
match self {
Self::MMX => Some(("__MMX__", "1")),
Self::ThreeDNow => Some(("__3dNOW__", "1")),
Self::ThreeDNowA => Some(("__3dNOW_A__", "1")),
Self::SSE => Some(("__SSE__", "1")),
Self::SSE2 => Some(("__SSE2__", "1")),
Self::SSE3 => Some(("__SSE3__", "1")),
Self::SSSE3 => Some(("__SSSE3__", "1")),
Self::SSE41 => Some(("__SSE4_1__", "1")),
Self::SSE42 => Some(("__SSE4_2__", "1")),
Self::SSE4A => Some(("__SSE4A__", "1")),
Self::AVX => Some(("__AVX__", "1")),
Self::AVX2 => Some(("__AVX2__", "1")),
Self::AVX512F => Some(("__AVX512F__", "1")),
Self::AVX512BW => Some(("__AVX512BW__", "1")),
Self::AVX512CD => Some(("__AVX512CD__", "1")),
Self::AVX512DQ => Some(("__AVX512DQ__", "1")),
Self::AVX512ER => Some(("__AVX512ER__", "1")),
Self::AVX512PF => Some(("__AVX512PF__", "1")),
Self::AVX512VL => Some(("__AVX512VL__", "1")),
Self::AVX512IFMA => Some(("__AVX512IFMA__", "1")),
Self::AVX512VBMI => Some(("__AVX512VBMI__", "1")),
Self::AVX512VBMI2 => Some(("__AVX512VBMI2__", "1")),
Self::AVX512VNNI => Some(("__AVX512VNNI__", "1")),
Self::AVX512BITALG => Some(("__AVX512BITALG__", "1")),
Self::AVX512VPOPCNTDQ => Some(("__AVX512VPOPCNTDQ__", "1")),
Self::AVX5124FMAPS => Some(("__AVX5124FMAPS__", "1")),
Self::AVX5124VNNIW => Some(("__AVX5124VNNIW__", "1")),
Self::AVX512BF16 => Some(("__AVX512BF16__", "1")),
Self::AVX512FP16 => Some(("__AVX512FP16__", "1")),
Self::AVXVNNI => Some(("__AVXVNNI__", "1")),
Self::AVXIFMA => Some(("__AVXIFMA__", "1")),
Self::AVXNECONVERT => Some(("__AVXNECONVERT__", "1")),
Self::AVXVNNIINT8 => Some(("__AVXVNNIINT8__", "1")),
Self::FMA => Some(("__FMA__", "1")),
Self::FMA4 => Some(("__FMA4__", "1")),
Self::XOP => Some(("__XOP__", "1")),
Self::F16C => Some(("__F16C__", "1")),
Self::BMI => Some(("__BMI__", "1")),
Self::BMI2 => Some(("__BMI2__", "1")),
Self::LZCNT => Some(("__LZCNT__", "1")),
Self::POPCNT => Some(("__POPCNT__", "1")),
Self::TBM => Some(("__TBM__", "1")),
Self::ADX => Some(("__ADX__", "1")),
Self::SHA => Some(("__SHA__", "1")),
Self::SGX => Some(("__SGX__", "1")),
Self::CLFLUSHOPT => Some(("__CLFLUSHOPT__", "1")),
Self::CLWB => Some(("__CLWB__", "1")),
Self::PCLMUL => Some(("__PCLMUL__", "1")),
Self::AES => Some(("__AES__", "1")),
Self::RDRAND => Some(("__RDRAND__", "1")),
Self::RDSEED => Some(("__RDSEED__", "1")),
Self::FSGSBASE => Some(("__FSGSBASE__", "1")),
Self::RDPID => Some(("__RDPID__", "1")),
Self::MOVBE => Some(("__MOVBE__", "1")),
Self::MOVDIRI => Some(("__MOVDIRI__", "1")),
Self::MOVDIR64B => Some(("__MOVDIR64B__", "1")),
Self::PTWRITE => Some(("__PTWRITE__", "1")),
Self::INVPCID => Some(("__INVPCID__", "1")),
Self::SMAP => Some(("__SMAP__", "1")),
Self::SMEP => Some(("__SMEP__", "1")),
Self::CET => Some(("__CET__", "1")),
Self::SHSTK => Some(("__SHSTK__", "1")),
Self::IBT => Some(("__IBT__", "1")),
Self::WAITPKG => Some(("__WAITPKG__", "1")),
Self::CLDEMOTE => Some(("__CLDEMOTE__", "1")),
Self::SERIALIZE => Some(("__SERIALIZE__", "1")),
Self::TSXLDTRK => Some(("__TSXLDTRK__", "1")),
Self::UINTR => Some(("__UINTR__", "1")),
Self::AMXBF16 => Some(("__AMXBF16__", "1")),
Self::AMXTILE => Some(("__AMXTILE__", "1")),
Self::AMXINT8 => Some(("__AMXINT8__", "1")),
Self::AMXFP16 => Some(("__AMXFP16__", "1")),
Self::AMXCOMPLEX => Some(("__AMXCOMPLEX__", "1")),
Self::ENQCMD => Some(("__ENQCMD__", "1")),
Self::GFNI => Some(("__GFNI__", "1")),
Self::VAES => Some(("__VAES__", "1")),
Self::VPCLMULQDQ => Some(("__VPCLMULQDQ__", "1")),
Self::PREFETCHI => Some(("__PREFETCHI__", "1")),
Self::PREFETCHWT1 => Some(("__PREFETCHWT1__", "1")),
Self::RDPRU => Some(("__RDPRU__", "1")),
Self::MCOMMIT => Some(("__MCOMMIT__", "1")),
Self::CLZERO => Some(("__CLZERO__", "1")),
Self::WBNOINVD => Some(("__WBNOINVD__", "1")),
Self::XSAVE => Some(("__XSAVE__", "1")),
Self::XSAVEC => Some(("__XSAVEC__", "1")),
Self::XSAVES => Some(("__XSAVES__", "1")),
Self::XSAVEOPT => Some(("__XSAVEOPT__", "1")),
Self::PKU => Some(("__PKU__", "1")),
Self::OSPKE => Some(("__OSPKE__", "1")),
Self::LAHFSAHF => Some(("__LAHF_SAHF__", "1")),
Self::CX16 => Some(("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16", "1")),
Self::PRFCHW => Some(("__PRFCHW__", "1")),
Self::RAOINT => Some(("__RAOINT__", "1")),
Self::CMPCCXADD => Some(("__CMPCCXADD__", "1")),
Self::HRESET => Some(("__HRESET__", "1")),
Self::KL => Some(("__KL__", "1")),
Self::WIDEKL => Some(("__WIDEKL__", "1")),
Self::AVX10_1_256 => Some(("__AVX10_1_256__", "1")),
Self::AVX10_1_512 => Some(("__AVX10_1_512__", "1")),
Self::APXF => Some(("__APX_F__", "1")),
Self::AVXNECONVERT2 => Some(("__AVXNECONVERT2__", "1")),
}
}
pub fn flag_name(&self) -> &'static str {
match self {
Self::MMX => "mmx",
Self::ThreeDNow => "3dnow",
Self::ThreeDNowA => "3dnowa",
Self::SSE => "sse",
Self::SSE2 => "sse2",
Self::SSE3 => "sse3",
Self::SSSE3 => "ssse3",
Self::SSE41 => "sse4.1",
Self::SSE42 => "sse4.2",
Self::SSE4A => "sse4a",
Self::AVX => "avx",
Self::AVX2 => "avx2",
Self::AVX512F => "avx512f",
Self::AVX512BW => "avx512bw",
Self::AVX512CD => "avx512cd",
Self::AVX512DQ => "avx512dq",
Self::AVX512ER => "avx512er",
Self::AVX512PF => "avx512pf",
Self::AVX512VL => "avx512vl",
Self::AVX512IFMA => "avx512ifma",
Self::AVX512VBMI => "avx512vbmi",
Self::AVX512VBMI2 => "avx512vbmi2",
Self::AVX512VNNI => "avx512vnni",
Self::AVX512BITALG => "avx512bitalg",
Self::AVX512VPOPCNTDQ => "avx512vpopcntdq",
Self::AVX5124FMAPS => "avx5124fmaps",
Self::AVX5124VNNIW => "avx5124vnniw",
Self::AVX512BF16 => "avx512bf16",
Self::AVX512FP16 => "avx512fp16",
Self::AVXVNNI => "avxvnni",
Self::AVXIFMA => "avxifma",
Self::AVXNECONVERT => "avxneconvert",
Self::AVXVNNIINT8 => "avxvnniint8",
Self::FMA => "fma",
Self::FMA4 => "fma4",
Self::XOP => "xop",
Self::F16C => "f16c",
Self::BMI => "bmi",
Self::BMI2 => "bmi2",
Self::LZCNT => "lzcnt",
Self::POPCNT => "popcnt",
Self::TBM => "tbm",
Self::ADX => "adx",
Self::SHA => "sha",
Self::SGX => "sgx",
Self::CLFLUSHOPT => "clflushopt",
Self::CLWB => "clwb",
Self::PCLMUL => "pclmul",
Self::AES => "aes",
Self::RDRAND => "rdrand",
Self::RDSEED => "rdseed",
Self::FSGSBASE => "fsgsbase",
Self::RDPID => "rdpid",
Self::MOVBE => "movbe",
Self::MOVDIRI => "movdiri",
Self::MOVDIR64B => "movdir64b",
Self::PTWRITE => "ptwrite",
Self::INVPCID => "invpcid",
Self::SMAP => "smap",
Self::SMEP => "smep",
Self::CET => "cet",
Self::SHSTK => "shstk",
Self::IBT => "ibt",
Self::WAITPKG => "waitpkg",
Self::CLDEMOTE => "cldemote",
Self::SERIALIZE => "serialize",
Self::TSXLDTRK => "tsxldtrk",
Self::UINTR => "uintr",
Self::AMXBF16 => "amx-bf16",
Self::AMXTILE => "amx-tile",
Self::AMXINT8 => "amx-int8",
Self::AMXFP16 => "amx-fp16",
Self::AMXCOMPLEX => "amx-complex",
Self::ENQCMD => "enqcmd",
Self::GFNI => "gfni",
Self::VAES => "vaes",
Self::VPCLMULQDQ => "vpclmulqdq",
Self::PREFETCHI => "prefetchi",
Self::PREFETCHWT1 => "prefetchwt1",
Self::RDPRU => "rdpru",
Self::MCOMMIT => "mcommit",
Self::CLZERO => "clzero",
Self::WBNOINVD => "wbnoinvd",
Self::XSAVE => "xsave",
Self::XSAVEC => "xsavec",
Self::XSAVES => "xsaves",
Self::XSAVEOPT => "xsaveopt",
Self::PKU => "pku",
Self::OSPKE => "ospke",
Self::LAHFSAHF => "sahf",
Self::CX16 => "cx16",
Self::PRFCHW => "prfchw",
Self::RAOINT => "raoint",
Self::CMPCCXADD => "cmpccxadd",
Self::HRESET => "hreset",
Self::KL => "kl",
Self::WIDEKL => "widekl",
Self::AVX10_1_256 => "avx10.1-256",
Self::AVX10_1_512 => "avx10.1-512",
Self::APXF => "apxf",
Self::AVXNECONVERT2 => "avxneconvert2",
}
}
pub fn implies(&self) -> Vec<X86Feature> {
match self {
Self::ThreeDNowA => vec![Self::ThreeDNow, Self::MMX],
Self::SSE => vec![Self::MMX],
Self::SSE2 => vec![Self::SSE],
Self::SSE3 => vec![Self::SSE2],
Self::SSSE3 => vec![Self::SSE3],
Self::SSE41 => vec![Self::SSSE3],
Self::SSE42 => vec![Self::SSE41],
Self::AVX => vec![Self::SSE42],
Self::AVX2 => vec![
Self::AVX,
Self::FMA,
Self::BMI,
Self::BMI2,
Self::F16C,
Self::LZCNT,
],
Self::AVX512F => vec![Self::AVX2],
Self::AVX512BW => vec![Self::AVX512F],
Self::AVX512CD => vec![Self::AVX512F],
Self::AVX512DQ => vec![Self::AVX512F],
Self::AVX512VL => vec![Self::AVX512F],
Self::AVX512IFMA => vec![Self::AVX512F, Self::AVX512VL],
Self::AVX512VBMI => vec![Self::AVX512F, Self::AVX512BW],
Self::AVX512VBMI2 => vec![Self::AVX512F, Self::AVX512BW, Self::AVX512VBMI],
Self::AVX512VNNI => vec![Self::AVX512F],
Self::AVX512BITALG => vec![Self::AVX512F, Self::AVX512BW],
Self::AVX512VPOPCNTDQ => vec![Self::AVX512F],
Self::AVX512BF16 => vec![Self::AVX512F, Self::AVX512BW, Self::AVX512VL],
Self::AVX512FP16 => vec![Self::AVX512F, Self::AVX512BW, Self::AVX512VL],
Self::FMA4 => vec![Self::AVX],
Self::XOP => vec![Self::AVX, Self::FMA4],
Self::F16C => vec![Self::AVX],
Self::AVXVNNI => vec![Self::AVX2],
Self::AVXIFMA => vec![Self::AVX2],
Self::AVXNECONVERT => vec![Self::AVX2],
Self::AVXVNNIINT8 => vec![Self::AVX2],
Self::AMXBF16 => vec![Self::AMXTILE],
Self::AMXINT8 => vec![Self::AMXTILE],
Self::AMXFP16 => vec![Self::AMXTILE],
Self::AMXCOMPLEX => vec![Self::AMXTILE],
Self::AVX10_1_256 => vec![Self::AVX512F],
Self::AVX10_1_512 => vec![Self::AVX10_1_256],
Self::APXF => vec![Self::AVX2],
Self::AVXNECONVERT2 => vec![Self::AVX2],
_ => vec![],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum X86MicroarchLevel {
Baseline, V2, V3, V4, }
impl X86MicroarchLevel {
pub fn features(&self) -> Vec<X86Feature> {
match self {
Self::Baseline => vec![
X86Feature::SSE2,
X86Feature::MMX,
X86Feature::CX16,
X86Feature::LAHFSAHF,
],
Self::V2 => {
let mut f = Self::Baseline.features();
f.extend(vec![
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::CX16,
]);
f
}
Self::V3 => {
let mut f = Self::V2.features();
f.extend(vec![
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::F16C,
X86Feature::FMA,
X86Feature::LZCNT,
X86Feature::MOVBE,
X86Feature::XSAVE,
]);
f
}
Self::V4 => {
let mut f = Self::V3.features();
f.extend(vec![
X86Feature::AVX512F,
X86Feature::AVX512BW,
X86Feature::AVX512CD,
X86Feature::AVX512DQ,
X86Feature::AVX512VL,
]);
f
}
}
}
pub fn macro_def(&self) -> Option<(&'static str, &'static str)> {
match self {
Self::Baseline => None, Self::V2 => Some(("__x86_64_v2__", "1")),
Self::V3 => Some(("__x86_64_v3__", "1")),
Self::V4 => Some(("__x86_64_v4__", "1")),
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"x86-64" | "x86-64-v1" | "baseline" => Some(Self::Baseline),
"x86-64-v2" | "v2" => Some(Self::V2),
"x86-64-v3" | "v3" => Some(Self::V3),
"x86-64-v4" | "v4" => Some(Self::V4),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Baseline => "",
Self::V2 => "v2",
Self::V3 => "v3",
Self::V4 => "v4",
}
}
}
#[derive(Debug, Clone)]
pub struct X86BuiltinMacros {
pub arch: String,
pub os: X86TargetOS,
pub env: X86TargetEnv,
pub is_64bit: bool,
pub standard: CLangStandard,
pub is_cxx: bool,
pub features: HashSet<X86Feature>,
pub microarch_level: X86MicroarchLevel,
pub is_pic: bool,
pub is_pie: bool,
pub finite_math_only: bool,
pub fast_math: bool,
pub gnuc_major: u32,
pub gnuc_minor: u32,
pub gnuc_patchlevel: u32,
pub experimental_cxx0x: bool,
pub gxx_abi_version: u32,
pub clang_major: u32,
pub clang_minor: u32,
pub clang_patchlevel: u32,
pub clang_version: String,
pub compiler_version: String,
pub is_hosted: bool,
pub msvc_compat: bool,
pub extra_defines: Vec<(String, String)>,
pub cxx_version: Option<u32>,
pub has_bool: bool,
}
impl X86BuiltinMacros {
pub fn new() -> Self {
Self {
arch: "x86_64".to_string(),
os: X86TargetOS::Linux,
env: X86TargetEnv::Gnu,
is_64bit: true,
standard: CLangStandard::C17,
is_cxx: false,
features: Self::default_features_x86_64(),
microarch_level: X86MicroarchLevel::Baseline,
is_pic: false,
is_pie: false,
finite_math_only: false,
fast_math: false,
gnuc_major: 4,
gnuc_minor: 2,
gnuc_patchlevel: 1,
experimental_cxx0x: false,
gxx_abi_version: 1017,
clang_major: 18,
clang_minor: 1,
clang_patchlevel: 0,
clang_version: "18.1.0".to_string(),
compiler_version: "llvm-native 18.1.0".to_string(),
is_hosted: true,
msvc_compat: false,
extra_defines: Vec::new(),
cxx_version: None,
has_bool: true,
}
}
pub fn from_triple(triple: &str) -> Self {
let parts: Vec<&str> = triple.split('-').collect();
let arch = parts.first().copied().unwrap_or("x86_64");
let os_str = parts.get(2).copied().unwrap_or("linux");
let env_str = parts.get(3).copied().unwrap_or("gnu");
let (is_64bit, features) = match arch {
"x86_64" | "amd64" => (true, Self::default_features_x86_64()),
"i386" | "i486" | "i586" | "i686" | "x86" => (false, Self::default_features_i386()),
_ => (true, Self::default_features_x86_64()),
};
Self {
arch: arch.to_string(),
os: X86TargetOS::from_str(os_str),
env: X86TargetEnv::from_str(env_str),
is_64bit,
features,
..Self::new()
}
}
fn default_features_x86_64() -> HashSet<X86Feature> {
let mut set = HashSet::new();
set.insert(X86Feature::MMX);
set.insert(X86Feature::SSE);
set.insert(X86Feature::SSE2);
set
}
fn default_features_i386() -> HashSet<X86Feature> {
HashSet::new()
}
pub fn enable_feature(&mut self, feature: X86Feature) {
for implied in feature.implies() {
self.enable_feature(implied);
}
self.features.insert(feature);
}
pub fn disable_feature(&mut self, feature: X86Feature) {
self.features.remove(&feature);
}
pub fn set_microarch_level(&mut self, level: X86MicroarchLevel) {
self.microarch_level = level;
for feature in level.features() {
self.enable_feature(feature);
}
}
pub fn has_feature(&self, feature: &X86Feature) -> bool {
self.features.contains(feature)
}
pub fn generate_all(&self) -> Vec<(String, String)> {
let mut macros = Vec::new();
self.add_arch_identification(&mut macros);
self.add_feature_macros(&mut macros);
self.add_os_macros(&mut macros);
self.add_language_macros(&mut macros);
self.add_compiler_macros(&mut macros);
self.add_type_size_macros(&mut macros);
self.add_float_limit_macros(&mut macros);
self.add_atomic_macros(&mut macros);
self.add_endian_macros(&mut macros);
self.add_position_independent_macros(&mut macros);
self.add_math_option_macros(&mut macros);
self.add_attribute_macros(&mut macros);
self.add_declspec_macros(&mut macros);
self.add_cxx_macros(&mut macros);
self.add_msvc_compat_macros(&mut macros);
for (name, value) in &self.extra_defines {
macros.push((name.clone(), value.clone()));
}
macros
}
pub fn generate_map(&self) -> HashMap<String, String> {
self.generate_all().into_iter().collect()
}
fn add_arch_identification(&self, macros: &mut Vec<(String, String)>) {
if self.is_64bit {
macros.push(("__x86_64__".into(), "1".into()));
macros.push(("__x86_64".into(), "1".into()));
macros.push(("__amd64__".into(), "1".into()));
macros.push(("__amd64".into(), "1".into()));
macros.push(("__LP64__".into(), "1".into()));
macros.push(("_LP64".into(), "1".into()));
macros.push(("__x86_64_processor__".into(), "1".into()));
macros.push(("__SIZEOF_POINTER__".into(), "8".into()));
macros.push(("__SIZEOF_LONG__".into(), "8".into()));
macros.push(("__SIZEOF_SIZE_T__".into(), "8".into()));
} else {
macros.push(("__i386__".into(), "1".into()));
macros.push(("__i386".into(), "1".into()));
macros.push(("i386".into(), "1".into()));
macros.push(("__X87__".into(), "1".into()));
macros.push(("__ILP32__".into(), "1".into()));
macros.push(("__SIZEOF_POINTER__".into(), "4".into()));
macros.push(("__SIZEOF_LONG__".into(), "4".into()));
macros.push(("__SIZEOF_SIZE_T__".into(), "4".into()));
}
if let Some((name, value)) = self.microarch_level.macro_def() {
macros.push((name.into(), value.into()));
}
macros.push(("__x86__".into(), "1".into()));
macros.push(("__x86".into(), "1".into()));
}
fn add_feature_macros(&self, macros: &mut Vec<(String, String)>) {
for feature in &self.features {
if let Some((name, value)) = feature.macro_def() {
macros.push((name.into(), value.into()));
}
}
if self.has_feature(&X86Feature::SSE) {
macros.push(("__SSE_MATH__".into(), "1".into()));
}
if self.has_feature(&X86Feature::SSE2) {
macros.push(("__SSE2_MATH__".into(), "1".into()));
}
if self.has_feature(&X86Feature::AVX512F) {
macros.push((
"__AVX512BW__".into(),
if self.has_feature(&X86Feature::AVX512BW) {
"1"
} else {
"0"
}
.into(),
));
macros.push((
"__AVX512CD__".into(),
if self.has_feature(&X86Feature::AVX512CD) {
"1"
} else {
"0"
}
.into(),
));
macros.push((
"__AVX512DQ__".into(),
if self.has_feature(&X86Feature::AVX512DQ) {
"1"
} else {
"0"
}
.into(),
));
macros.push((
"__AVX512VL__".into(),
if self.has_feature(&X86Feature::AVX512VL) {
"1"
} else {
"0"
}
.into(),
));
}
macros.push(("__REGISTER_PREFIX__".into(), "".into()));
macros.push(("__USER_LABEL_PREFIX__".into(), "".into()));
if self.has_feature(&X86Feature::AVX512F) {
macros.push(("__ZMM_REGISTER_WIDTH__".into(), "512".into()));
} else if self.has_feature(&X86Feature::AVX) {
macros.push(("__YMM_REGISTER_WIDTH__".into(), "256".into()));
macros.push(("__ZMM_REGISTER_WIDTH__".into(), "256".into()));
} else if self.has_feature(&X86Feature::SSE) {
macros.push(("__XMM_REGISTER_WIDTH__".into(), "128".into()));
}
}
fn add_os_macros(&self, macros: &mut Vec<(String, String)>) {
if self.os.is_unix() {
macros.push(("__unix__".into(), "1".into()));
macros.push(("__unix".into(), "1".into()));
}
match self.os {
X86TargetOS::Linux => {
macros.push(("__linux__".into(), "1".into()));
macros.push(("__linux".into(), "1".into()));
macros.push(("linux".into(), "1".into()));
macros.push(("__gnu_linux__".into(), "1".into()));
macros.push(("__ELF__".into(), "1".into()));
}
X86TargetOS::Darwin => {
macros.push(("__APPLE__".into(), "1".into()));
macros.push(("__MACH__".into(), "1".into()));
macros.push(("__APPLE_CC__".into(), "1".into()));
macros.push(("__MACH".into(), "1".into()));
macros.push(("__APPLE".into(), "1".into()));
}
X86TargetOS::FreeBSD => {
macros.push(("__FreeBSD__".into(), "1".into()));
macros.push(("__FreeBSD".into(), "1".into()));
macros.push(("__ELF__".into(), "1".into()));
}
X86TargetOS::OpenBSD => {
macros.push(("__OpenBSD__".into(), "1".into()));
macros.push(("__ELF__".into(), "1".into()));
}
X86TargetOS::NetBSD => {
macros.push(("__NetBSD__".into(), "1".into()));
macros.push(("__ELF__".into(), "1".into()));
}
X86TargetOS::Windows => {
macros.push(("_WIN32".into(), "1".into()));
if self.is_64bit {
macros.push(("_WIN64".into(), "1".into()));
}
macros.push(("__MINGW32__".into(), "1".into()));
if self.env == X86TargetEnv::MSVC {
macros.push(("_MSC_VER".into(), "1938".into()));
macros.push(("_MSC_FULL_VER".into(), "193833104".into()));
macros.push(("_MSC_BUILD".into(), "1".into()));
}
}
X86TargetOS::Cygwin => {
macros.push(("__CYGWIN__".into(), "1".into()));
macros.push(("__unix__".into(), "1".into()));
macros.push(("_WIN32".into(), "1".into()));
}
X86TargetOS::Android => {
macros.push(("__ANDROID__".into(), "1".into()));
macros.push(("__linux__".into(), "1".into()));
macros.push(("__unix__".into(), "1".into()));
macros.push(("__ELF__".into(), "1".into()));
}
X86TargetOS::Emscripten => {
macros.push(("__EMSCRIPTEN__".into(), "1".into()));
macros.push(("__wasm__".into(), "1".into()));
}
X86TargetOS::Wasi => {
macros.push(("__wasi__".into(), "1".into()));
macros.push(("__wasm__".into(), "1".into()));
}
X86TargetOS::Unknown => {}
}
match self.env {
X86TargetEnv::Gnu => {
if self.os == X86TargetOS::Linux {
macros.push(("__GLIBC__".into(), "2".into()));
macros.push(("__GLIBC_MINOR__".into(), "35".into()));
}
}
X86TargetEnv::Musl => {
macros.push(("__MUSL__".into(), "1".into()));
}
X86TargetEnv::Bionic => {
macros.push(("__BIONIC__".into(), "1".into()));
}
_ => {}
}
}
fn add_language_macros(&self, macros: &mut Vec<(String, String)>) {
if !self.is_cxx {
macros.push(("__STDC__".into(), "1".into()));
macros.push((
"__STDC_VERSION__".into(),
Self::stc_version_str(self.standard).into(),
));
macros.push(("__STDC_UTF_16__".into(), "1".into()));
macros.push(("__STDC_UTF_32__".into(), "1".into()));
if self.is_hosted {
macros.push(("__STDC_HOSTED__".into(), "1".into()));
} else {
macros.push(("__STDC_HOSTED__".into(), "0".into()));
}
if !matches!(
self.standard,
CLangStandard::Gnu89
| CLangStandard::Gnu99
| CLangStandard::Gnu11
| CLangStandard::Gnu17
) {
macros.push(("__STRICT_ANSI__".into(), "1".into()));
}
}
if self.standard.is_gnu() {
macros.push(("__GNUC_GNU_INLINE__".into(), "1".into()));
}
if self.has_bool && !self.is_cxx {
macros.push(("__bool_true_false_are_defined".into(), "1".into()));
}
macros.push(("__CHAR_BIT__".into(), "8".into()));
macros.push(("__WCHAR_MAX__".into(), "2147483647".into()));
macros.push(("__WCHAR_MIN__".into(), "(-__WCHAR_MAX__ - 1)".into()));
macros.push(("__WINT_MAX__".into(), "4294967295U".into()));
macros.push(("__WINT_MIN__".into(), "0U".into()));
macros.push(("__SCHAR_MAX__".into(), "127".into()));
macros.push(("__SHRT_MAX__".into(), "32767".into()));
macros.push(("__INT_MAX__".into(), "2147483647".into()));
macros.push((
"__LONG_MAX__".into(),
if self.is_64bit {
"9223372036854775807L"
} else {
"2147483647L"
}
.into(),
));
macros.push(("__LONG_LONG_MAX__".into(), "9223372036854775807LL".into()));
macros.push(("__INTMAX_MAX__".into(), "9223372036854775807LL".into()));
macros.push(("__BIGGEST_ALIGNMENT__".into(), "64".into()));
macros.push(("__INTMAX_WIDTH__".into(), "64".into()));
macros.push(("__SIG_ATOMIC_WIDTH__".into(), "32".into()));
macros.push((
"__SIZE_WIDTH__".into(),
if self.is_64bit { "64" } else { "32" }.into(),
));
macros.push((
"__PTRDIFF_WIDTH__".into(),
if self.is_64bit { "64" } else { "32" }.into(),
));
macros.push(("__WCHAR_WIDTH__".into(), "32".into()));
macros.push(("__WINT_WIDTH__".into(), "32".into()));
macros.push(("__STDC_NO_ATOMICS__".into(), "1".into()));
macros.push(("__STDC_NO_COMPLEX__".into(), "1".into()));
macros.push(("__STDC_NO_THREADS__".into(), "1".into()));
macros.push(("__STDC_NO_VLA__".into(), "1".into()));
}
fn add_compiler_macros(&self, macros: &mut Vec<(String, String)>) {
macros.push(("__clang__".into(), "1".into()));
macros.push(("__clang_major__".into(), self.clang_major.to_string()));
macros.push(("__clang_minor__".into(), self.clang_minor.to_string()));
macros.push((
"__clang_patchlevel__".into(),
self.clang_patchlevel.to_string(),
));
macros.push((
"__clang_version__".into(),
format!("\"{}\"", self.clang_version),
));
macros.push(("__GNUC__".into(), self.gnuc_major.to_string()));
macros.push(("__GNUC_MINOR__".into(), self.gnuc_minor.to_string()));
macros.push((
"__GNUC_PATCHLEVEL__".into(),
self.gnuc_patchlevel.to_string(),
));
let gcc_ver = self.gnuc_major * 10000 + self.gnuc_minor * 100 + self.gnuc_patchlevel;
macros.push(("__GNUC_VERSION__".into(), gcc_ver.to_string()));
macros.push((
"__VERSION__".into(),
format!("\"{}\"", self.compiler_version),
));
macros.push(("__llvm_native__".into(), "1".into()));
macros.push(("__llvm__".into(), "1".into()));
macros.push(("__OPTIMIZE__".into(), "1".into()));
macros.push(("__OPTIMIZE_SIZE__".into(), "0".into()));
macros.push(("__NO_INLINE__".into(), "1".into()));
if self.is_pic || self.is_pie {
macros.push((
"__PIC__".into(),
if self.is_pic || self.is_pie { "2" } else { "0" }.into(),
));
macros.push((
"__pic__".into(),
if self.is_pic || self.is_pie { "2" } else { "0" }.into(),
));
}
if self.is_pie {
macros.push(("__PIE__".into(), "2".into()));
macros.push(("__pie__".into(), "2".into()));
}
macros.push(("__DEPRECATED".into(), "1".into()));
macros.push(("__EXCEPTIONS".into(), "1".into()));
macros.push(("__GCC_HAVE_DWARF2_CFI_ASM".into(), "1".into()));
macros.push(("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1".into(), "1".into()));
macros.push(("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2".into(), "1".into()));
macros.push(("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4".into(), "1".into()));
macros.push(("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8".into(), "1".into()));
if self.has_feature(&X86Feature::CX16) {
macros.push(("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16".into(), "1".into()));
}
}
fn add_type_size_macros(&self, macros: &mut Vec<(String, String)>) {
let ptr_size = if self.is_64bit { "8" } else { "4" };
let long_size = if self.is_64bit { "8" } else { "4" };
macros.push(("__SIZEOF_INT__".into(), "4".into()));
macros.push(("__SIZEOF_LONG__".into(), long_size.into()));
macros.push(("__SIZEOF_POINTER__".into(), ptr_size.into()));
macros.push(("__SIZEOF_SIZE_T__".into(), ptr_size.into()));
macros.push(("__SIZEOF_WCHAR_T__".into(), "4".into()));
macros.push(("__SIZEOF_WINT_T__".into(), "4".into()));
macros.push(("__SIZEOF_FLOAT__".into(), "4".into()));
macros.push(("__SIZEOF_DOUBLE__".into(), "8".into()));
macros.push(("__SIZEOF_LONG_DOUBLE__".into(), "16".into()));
macros.push(("__SIZEOF_SHORT__".into(), "2".into()));
macros.push(("__SIZEOF_CHAR__".into(), "1".into()));
macros.push(("__SIZEOF_INT128__".into(), "16".into()));
macros.push(("__SIZEOF_PTRDIFF_T__".into(), ptr_size.into()));
macros.push(("__SIZEOF_FLOAT80__".into(), "16".into()));
macros.push(("__SIZEOF_FLOAT128__".into(), "16".into()));
macros.push(("__SIZEOF_FLOAT_COMPLEX__".into(), "8".into()));
macros.push(("__SIZEOF_DOUBLE_COMPLEX__".into(), "16".into()));
macros.push(("__SIZEOF_LONG_DOUBLE_COMPLEX__".into(), "32".into()));
}
fn add_float_limit_macros(&self, macros: &mut Vec<(String, String)>) {
macros.push(("__FLT_DIG__".into(), "6".into()));
macros.push(("__FLT_DECIMAL_DIG__".into(), "9".into()));
macros.push(("__FLT_EVAL_METHOD__".into(), "0".into()));
macros.push(("__FLT_EPSILON__".into(), "1.1920928955078125e-7F".into()));
macros.push(("__FLT_HAS_DENORM__".into(), "1".into()));
macros.push(("__FLT_HAS_INFINITY__".into(), "1".into()));
macros.push(("__FLT_HAS_QUIET_NAN__".into(), "1".into()));
macros.push(("__FLT_MANT_DIG__".into(), "24".into()));
macros.push(("__FLT_MAX_10_EXP__".into(), "38".into()));
macros.push(("__FLT_MAX_EXP__".into(), "128".into()));
macros.push((
"__FLT_MAX__".into(),
"3.40282346638528859811704183484516925e+38F".into(),
));
macros.push(("__FLT_MIN_10_EXP__".into(), "(-37)".into()));
macros.push(("__FLT_MIN_EXP__".into(), "(-125)".into()));
macros.push((
"__FLT_MIN__".into(),
"1.17549435082228750796873653722224568e-38F".into(),
));
macros.push((
"__FLT_NORM_MAX__".into(),
"3.40282346638528859811704183484516925e+38F".into(),
));
macros.push(("__FLT_RADIX__".into(), "2".into()));
macros.push((
"__FLT_TRUE_MIN__".into(),
"1.40129846432481707092372958328991613e-45F".into(),
));
macros.push(("__DBL_DIG__".into(), "15".into()));
macros.push(("__DBL_DECIMAL_DIG__".into(), "17".into()));
macros.push((
"__DBL_EPSILON__".into(),
"2.22044604925031308084726333618164062e-16".into(),
));
macros.push(("__DBL_HAS_DENORM__".into(), "1".into()));
macros.push(("__DBL_HAS_INFINITY__".into(), "1".into()));
macros.push(("__DBL_HAS_QUIET_NAN__".into(), "1".into()));
macros.push(("__DBL_MANT_DIG__".into(), "53".into()));
macros.push(("__DBL_MAX_10_EXP__".into(), "308".into()));
macros.push(("__DBL_MAX_EXP__".into(), "1024".into()));
macros.push((
"__DBL_MAX__".into(),
"1.79769313486231570814527423731704357e+308".into(),
));
macros.push(("__DBL_MIN_10_EXP__".into(), "(-307)".into()));
macros.push(("__DBL_MIN_EXP__".into(), "(-1021)".into()));
macros.push((
"__DBL_MIN__".into(),
"2.22507385850720138309023271733240406e-308".into(),
));
macros.push((
"__DBL_NORM_MAX__".into(),
"1.79769313486231570814527423731704357e+308".into(),
));
macros.push((
"__DBL_TRUE_MIN__".into(),
"4.94065645841246544176568792868221372e-324".into(),
));
macros.push(("__LDBL_DIG__".into(), "18".into()));
macros.push(("__LDBL_DECIMAL_DIG__".into(), "21".into()));
macros.push((
"__LDBL_EPSILON__".into(),
"1.08420217248550443400745280086994171e-19L".into(),
));
macros.push(("__LDBL_HAS_DENORM__".into(), "1".into()));
macros.push(("__LDBL_HAS_INFINITY__".into(), "1".into()));
macros.push(("__LDBL_HAS_QUIET_NAN__".into(), "1".into()));
macros.push(("__LDBL_MANT_DIG__".into(), "64".into()));
macros.push(("__LDBL_MAX_10_EXP__".into(), "4932".into()));
macros.push(("__LDBL_MAX_EXP__".into(), "16384".into()));
macros.push((
"__LDBL_MAX__".into(),
"1.18973149535723176502126385303097021e+4932L".into(),
));
macros.push(("__LDBL_MIN_10_EXP__".into(), "(-4931)".into()));
macros.push(("__LDBL_MIN_EXP__".into(), "(-16381)".into()));
macros.push((
"__LDBL_MIN__".into(),
"3.36210314311209350626267781732175260e-4932L".into(),
));
macros.push((
"__LDBL_NORM_MAX__".into(),
"1.18973149535723176502126385303097021e+4932L".into(),
));
macros.push((
"__LDBL_TRUE_MIN__".into(),
"3.64519953188247460252840593361941982e-4951L".into(),
));
macros.push(("__FLT_EVAL_METHOD_TS_18661_3__".into(), "0".into()));
}
fn add_atomic_macros(&self, macros: &mut Vec<(String, String)>) {
macros.push(("__ATOMIC_BOOL_LOCK_FREE".into(), "2".into()));
macros.push(("__ATOMIC_CHAR_LOCK_FREE".into(), "2".into()));
macros.push(("__ATOMIC_CHAR8_T_LOCK_FREE".into(), "2".into()));
macros.push(("__ATOMIC_CHAR16_T_LOCK_FREE".into(), "2".into()));
macros.push(("__ATOMIC_CHAR32_T_LOCK_FREE".into(), "2".into()));
macros.push(("__ATOMIC_WCHAR_T_LOCK_FREE".into(), "2".into()));
macros.push(("__ATOMIC_SHORT_LOCK_FREE".into(), "2".into()));
macros.push(("__ATOMIC_INT_LOCK_FREE".into(), "2".into()));
macros.push(("__ATOMIC_LONG_LOCK_FREE".into(), "2".into()));
macros.push(("__ATOMIC_LLONG_LOCK_FREE".into(), "2".into()));
macros.push(("__ATOMIC_POINTER_LOCK_FREE".into(), "2".into()));
macros.push(("__ATOMIC_RELAXED".into(), "0".into()));
macros.push(("__ATOMIC_CONSUME".into(), "1".into()));
macros.push(("__ATOMIC_ACQUIRE".into(), "2".into()));
macros.push(("__ATOMIC_RELEASE".into(), "3".into()));
macros.push(("__ATOMIC_ACQ_REL".into(), "4".into()));
macros.push(("__ATOMIC_SEQ_CST".into(), "5".into()));
macros.push(("__GCC_ATOMIC_BOOL_LOCK_FREE".into(), "2".into()));
macros.push(("__GCC_ATOMIC_CHAR_LOCK_FREE".into(), "2".into()));
macros.push(("__GCC_ATOMIC_CHAR16_T_LOCK_FREE".into(), "2".into()));
macros.push(("__GCC_ATOMIC_CHAR32_T_LOCK_FREE".into(), "2".into()));
macros.push(("__GCC_ATOMIC_WCHAR_T_LOCK_FREE".into(), "2".into()));
macros.push(("__GCC_ATOMIC_SHORT_LOCK_FREE".into(), "2".into()));
macros.push(("__GCC_ATOMIC_INT_LOCK_FREE".into(), "2".into()));
macros.push(("__GCC_ATOMIC_LONG_LOCK_FREE".into(), "2".into()));
macros.push(("__GCC_ATOMIC_LLONG_LOCK_FREE".into(), "2".into()));
macros.push(("__GCC_ATOMIC_POINTER_LOCK_FREE".into(), "2".into()));
macros.push(("__GCC_ATOMIC_TEST_AND_SET_TRUEVAL".into(), "1".into()));
macros.push(("__GCC_ATOMIC_INT_LOCK_FREE".into(), "2".into()));
macros.push(("__GCC_ATOMIC_LONG_LOCK_FREE".into(), "2".into()));
}
fn add_endian_macros(&self, macros: &mut Vec<(String, String)>) {
macros.push(("__BYTE_ORDER__".into(), "__ORDER_LITTLE_ENDIAN__".into()));
macros.push(("__ORDER_LITTLE_ENDIAN__".into(), "1234".into()));
macros.push(("__ORDER_BIG_ENDIAN__".into(), "4321".into()));
macros.push(("__ORDER_PDP_ENDIAN__".into(), "3412".into()));
macros.push((
"__FLOAT_WORD_ORDER__".into(),
"__ORDER_LITTLE_ENDIAN__".into(),
));
}
fn add_position_independent_macros(&self, macros: &mut Vec<(String, String)>) {
if self.is_pic {
macros.push(("__PIC__".into(), "2".into()));
macros.push(("__pic__".into(), "2".into()));
}
if self.is_pie {
macros.push(("__PIE__".into(), "2".into()));
macros.push(("__pie__".into(), "2".into()));
}
}
fn add_math_option_macros(&self, macros: &mut Vec<(String, String)>) {
if self.finite_math_only {
macros.push(("__FINITE_MATH_ONLY__".into(), "1".into()));
}
if self.fast_math {
macros.push(("__FAST_MATH__".into(), "1".into()));
macros.push(("__FINITE_MATH_ONLY__".into(), "1".into()));
macros.push(("__NO_MATH_ERRNO__".into(), "1".into()));
}
}
fn add_attribute_macros(&self, macros: &mut Vec<(String, String)>) {
macros.push(("__has_attribute".into(), "__has_attribute".into()));
macros.push(("__attribute__(x)".into(), "".into()));
macros.push(("__attribute_deprecated__".into(), "".into()));
macros.push(("__attribute_malloc__".into(), "".into()));
macros.push(("__attribute_pure__".into(), "".into()));
macros.push(("__attribute_const__".into(), "".into()));
macros.push(("__attribute_unused__".into(), "".into()));
macros.push(("__attribute_noreturn__".into(), "".into()));
macros.push(("__attribute_format__".into(), "".into()));
macros.push(("__attribute_nonnull__".into(), "".into()));
macros.push(("__attribute_warn_unused_result__".into(), "".into()));
macros.push(("__attribute_used__".into(), "".into()));
macros.push(("__attribute_aligned__".into(), "".into()));
macros.push(("__attribute_packed__".into(), "".into()));
macros.push(("__attribute_section__".into(), "".into()));
macros.push(("__attribute_visibility__".into(), "".into()));
macros.push(("__attribute_weak__".into(), "".into()));
macros.push(("__attribute_constructor__".into(), "".into()));
macros.push(("__attribute_destructor__".into(), "".into()));
macros.push(("__attribute_hot__".into(), "".into()));
macros.push(("__attribute_cold__".into(), "".into()));
macros.push(("__attribute_artificial__".into(), "".into()));
macros.push(("__attribute_flatten__".into(), "".into()));
macros.push(("__attribute_noinline__".into(), "".into()));
macros.push(("__attribute_always_inline__".into(), "".into()));
macros.push(("__attribute_no_sanitize__".into(), "".into()));
macros.push(("__attribute_no_address_safety_analysis__".into(), "".into()));
macros.push(("__attribute_no_thread_safety_analysis__".into(), "".into()));
macros.push(("__attribute_no_split_stack__".into(), "".into()));
macros.push(("__attribute_nocf_check__".into(), "".into()));
macros.push(("__attribute_target__".into(), "".into()));
macros.push(("__attribute_optimize__".into(), "".into()));
}
fn add_declspec_macros(&self, macros: &mut Vec<(String, String)>) {
macros.push(("__declspec(x)".into(), "__attribute__((x))".into()));
macros.push((
"__declspec_dllimport__".into(),
"__attribute__((dllimport))".into(),
));
macros.push((
"__declspec_dllexport__".into(),
"__attribute__((dllexport))".into(),
));
macros.push(("__declspec_naked__".into(), "__attribute__((naked))".into()));
macros.push((
"__declspec_noinline__".into(),
"__attribute__((noinline))".into(),
));
macros.push((
"__declspec_noreturn__".into(),
"__attribute__((noreturn))".into(),
));
macros.push((
"__declspec_nothrow__".into(),
"__attribute__((nothrow))".into(),
));
macros.push(("__declspec_novtable__".into(), "".into()));
macros.push((
"__declspec_align__".into(),
"__attribute__((aligned(x)))".into(),
));
macros.push((
"__declspec_deprecated__".into(),
"__attribute__((deprecated))".into(),
));
macros.push(("__declspec_restrict__".into(), "__restrict".into()));
macros.push(("__declspec_thread__".into(), "__thread".into()));
macros.push(("__declspec_property__".into(), "".into()));
macros.push((
"__declspec_selectany__".into(),
"__attribute__((weak))".into(),
));
macros.push(("__declspec_uuid__".into(), "".into()));
}
fn add_cxx_macros(&self, macros: &mut Vec<(String, String)>) {
if !self.is_cxx {
return;
}
if let Some(ver) = self.cxx_version {
macros.push(("__cplusplus".into(), ver.to_string()));
} else {
let ver = match self.standard {
CLangStandard::C89 | CLangStandard::Gnu89 => 199711,
CLangStandard::C99 | CLangStandard::Gnu99 => 199711, CLangStandard::C11 | CLangStandard::Gnu11 => 201103, CLangStandard::C17 | CLangStandard::Gnu17 => 201703, CLangStandard::C23 => 202002, };
macros.push(("__cplusplus".into(), ver.to_string()));
}
macros.push(("__GXX_ABI_VERSION".into(), self.gxx_abi_version.to_string()));
if self.experimental_cxx0x {
macros.push(("__GXX_EXPERIMENTAL_CXX0X__".into(), "1".into()));
}
macros.push(("__cpp_rtti".into(), "199711".into()));
macros.push(("__cpp_exceptions".into(), "199711".into()));
macros.push(("__cpp_rvalue_references".into(), "200610".into()));
macros.push(("__cpp_variadic_templates".into(), "200704".into()));
macros.push(("__cpp_initializer_lists".into(), "200806".into()));
macros.push(("__cpp_static_assert".into(), "200410".into()));
macros.push(("__cpp_decltype".into(), "200707".into()));
macros.push(("__cpp_attributes".into(), "200809".into()));
macros.push(("__cpp_lambdas".into(), "200907".into()));
macros.push(("__cpp_constexpr".into(), "200704".into()));
macros.push(("__cpp_decltype_auto".into(), "201304".into()));
macros.push(("__cpp_return_type_deduction".into(), "201304".into()));
macros.push(("__cpp_generic_lambdas".into(), "201304".into()));
macros.push(("__cpp_variable_templates".into(), "201304".into()));
macros.push(("__cpp_binary_literals".into(), "201304".into()));
macros.push(("__cpp_digit_separators".into(), "201309".into()));
macros.push(("__cpp_nsdmi".into(), "200809".into()));
macros.push(("__cpp_inheriting_constructors".into(), "201511".into()));
macros.push(("__cpp_aggregate_nsdmi".into(), "201304".into()));
macros.push(("__cpp_fold_expressions".into(), "201603".into()));
macros.push(("__cpp_if_constexpr".into(), "201606".into()));
macros.push(("__cpp_structured_bindings".into(), "201606".into()));
macros.push(("__cpp_deduction_guides".into(), "201703".into()));
macros.push(("__cpp_nontype_template_auto".into(), "201606".into()));
macros.push(("__cpp_template_template_args".into(), "201611".into()));
macros.push(("__cpp_alias_templates".into(), "200704".into()));
macros.push(("__cpp_delegating_constructors".into(), "200604".into()));
macros.push(("__cpp_raw_strings".into(), "200710".into()));
macros.push(("__cpp_unicode_characters".into(), "200704".into()));
macros.push(("__cpp_user_defined_literals".into(), "200809".into()));
macros.push(("__cpp_threadsafe_static_init".into(), "200806".into()));
macros.push(("__cpp_range_based_for".into(), "200907".into()));
macros.push(("__cpp_override_control".into(), "200907".into()));
macros.push(("__cpp_nullptr".into(), "200907".into()));
macros.push(("__cpp_enum_forward_declarations".into(), "200809".into()));
macros.push(("__cpp_explicit_conversion".into(), "200710".into()));
macros.push(("__cpp_defaulted_functions".into(), "200809".into()));
macros.push(("__cpp_deleted_functions".into(), "200809".into()));
macros.push(("__cpp_noexcept_function_type".into(), "201510".into()));
macros.push(("__cplusplus_201103L".into(), "201103L".into()));
macros.push(("__GNUG__".into(), self.gnuc_major.to_string()));
macros.push(("__DEPRECATED".into(), "1".into()));
}
fn add_msvc_compat_macros(&self, macros: &mut Vec<(String, String)>) {
if !self.msvc_compat && self.os != X86TargetOS::Windows {
return;
}
macros.push(("__STDCALL__".into(), "__attribute__((__stdcall__))".into()));
macros.push(("__CDECL__".into(), "__attribute__((__cdecl__))".into()));
macros.push((
"__FASTCALL__".into(),
"__attribute__((__fastcall__))".into(),
));
macros.push((
"__THISCALL__".into(),
"__attribute__((__thiscall__))".into(),
));
macros.push((
"__VECTORCALL__".into(),
"__attribute__((__vectorcall__))".into(),
));
macros.push(("__REGCALL__".into(), "__attribute__((__regcall__))".into()));
macros.push(("__INT8_TYPE__".into(), "signed char".into()));
macros.push(("__INT16_TYPE__".into(), "short".into()));
macros.push(("__INT32_TYPE__".into(), "int".into()));
macros.push(("__INT64_TYPE__".into(), "long long int".into()));
macros.push(("__UINT8_TYPE__".into(), "unsigned char".into()));
macros.push(("__UINT16_TYPE__".into(), "unsigned short".into()));
macros.push(("__UINT32_TYPE__".into(), "unsigned int".into()));
macros.push(("__UINT64_TYPE__".into(), "unsigned long long int".into()));
macros.push((
"__INTPTR_TYPE__".into(),
if self.is_64bit { "long int" } else { "int" }.into(),
));
macros.push((
"__UINTPTR_TYPE__".into(),
if self.is_64bit {
"long unsigned int"
} else {
"unsigned int"
}
.into(),
));
macros.push(("_MSC_VER".into(), "1938".into()));
macros.push(("_MSC_FULL_VER".into(), "193833104".into()));
macros.push(("_MSC_EXTENSIONS".into(), "1".into()));
macros.push(("_INTEGRAL_MAX_BITS".into(), "64".into()));
}
fn stc_version_str(standard: CLangStandard) -> &'static str {
match standard {
CLangStandard::C89 | CLangStandard::Gnu89 => "199409L",
CLangStandard::C99 | CLangStandard::Gnu99 => "199901L",
CLangStandard::C11 | CLangStandard::Gnu11 => "201112L",
CLangStandard::C17 | CLangStandard::Gnu17 => "201710L",
CLangStandard::C23 => "202311L",
}
}
}
impl Default for X86BuiltinMacros {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ExpansionResult {
pub tokens: Vec<Token>,
pub expanded: bool,
}
#[derive(Debug, Clone)]
pub struct X86MacroExpanderConfig {
pub max_expansion_depth: usize,
pub track_locations: bool,
pub enable_counter: bool,
pub counter: u64,
pub expand_in_skipped: bool,
pub warn_unused_params: bool,
}
impl Default for X86MacroExpanderConfig {
fn default() -> Self {
Self {
max_expansion_depth: 256,
track_locations: true,
enable_counter: true,
counter: 0,
expand_in_skipped: false,
warn_unused_params: false,
}
}
}
#[derive(Debug, Clone)]
pub struct X86MacroExpander {
pub defines: HashMap<String, MacroDef>,
pub builtins: X86BuiltinMacros,
pub config: X86MacroExpanderConfig,
expanding: HashSet<String>,
expansion_stack: Vec<ExpansionFrame>,
}
#[derive(Debug, Clone)]
pub struct ExpansionFrame {
pub macro_name: String,
pub file: Option<String>,
pub line: u32,
pub depth: usize,
}
impl X86MacroExpander {
pub fn new() -> Self {
Self {
defines: HashMap::new(),
builtins: X86BuiltinMacros::new(),
config: X86MacroExpanderConfig::default(),
expanding: HashSet::new(),
expansion_stack: Vec::new(),
}
}
pub fn with_builtins(builtins: X86BuiltinMacros) -> Self {
Self {
defines: HashMap::new(),
builtins,
config: X86MacroExpanderConfig::default(),
expanding: HashSet::new(),
expansion_stack: Vec::new(),
}
}
pub fn define(&mut self, name: &str, value: &str) {
let body_tokens = tokenize_definition_body(value);
let def = MacroDef::object_like(name, body_tokens);
self.defines.insert(name.to_string(), def);
}
pub fn define_function_like(
&mut self,
name: &str,
params: Vec<String>,
body: &str,
variadic: bool,
) {
let body_tokens = tokenize_definition_body(body);
let def = MacroDef::function_like(name, params, body_tokens, variadic);
self.defines.insert(name.to_string(), def);
}
pub fn undefine(&mut self, name: &str) -> bool {
self.defines.remove(name).is_some()
}
pub fn load_builtins(&mut self) {
for (name, value) in self.builtins.generate_all() {
self.define(&name, &value);
}
}
pub fn expand(
&mut self,
tokens: &[Token],
current_file: Option<&str>,
current_line: u32,
) -> Vec<Token> {
let mut result = Vec::new();
let mut i = 0;
let n = tokens.len();
while i < n {
let token = &tokens[i];
if token.kind == TokenKind::Identifier {
let name = &token.text;
if self.expanding.contains(name) {
result.push(token.clone());
i += 1;
continue;
}
if let Some(def) = self.defines.get(name).cloned() {
if def.is_function_like {
let mut j = i + 1;
while j < n && is_whitespace_token(&tokens[j]) {
j += 1;
}
if j < n && tokens[j].kind == TokenKind::LParen {
let args = self.parse_macro_arguments(tokens, j, n);
if let Some((args, end_idx)) = args {
let expanded = self.expand_function_macro(
&def,
&args,
current_file,
current_line,
);
result.extend(expanded);
for t in &mut result {
t.flags.is_expanded_from_macro = true;
}
i = end_idx + 1;
continue;
}
}
} else {
self.expanding.insert(name.clone());
if self.config.track_locations {
self.expansion_stack.push(ExpansionFrame {
macro_name: name.clone(),
file: current_file.map(|s| s.to_string()),
line: current_line,
depth: self.expansion_stack.len(),
});
}
let expanded = self.expand(&def.body, current_file, current_line);
for t in &expanded {
let mut tok = t.clone();
tok.flags.is_expanded_from_macro = true;
result.push(tok);
}
self.expanding.remove(name);
if self.config.track_locations {
self.expansion_stack.pop();
}
i += 1;
continue;
}
}
}
if token.kind == TokenKind::Identifier && is_builtin_macro_name(&token.text) {
let builtin = self.expand_builtin(&token.text, current_file, current_line);
if let Some(tok) = builtin {
result.push(tok);
i += 1;
continue;
}
}
result.push(token.clone());
i += 1;
}
result
}
fn parse_macro_arguments(
&self,
tokens: &[Token],
lparen_idx: usize,
total: usize,
) -> Option<(Vec<Vec<Token>>, usize)> {
let mut args: Vec<Vec<Token>> = Vec::new();
let mut current_arg: Vec<Token> = Vec::new();
let mut paren_depth = 0;
let mut i = lparen_idx + 1;
while i < total {
match tokens[i].kind {
TokenKind::LParen => {
paren_depth += 1;
current_arg.push(tokens[i].clone());
}
TokenKind::RParen => {
if paren_depth == 0 {
args.push(current_arg);
return Some((args, i));
}
paren_depth -= 1;
current_arg.push(tokens[i].clone());
}
TokenKind::Comma => {
if paren_depth == 0 {
args.push(current_arg);
current_arg = Vec::new();
} else {
current_arg.push(tokens[i].clone());
}
}
_ => {
current_arg.push(tokens[i].clone());
}
}
i += 1;
}
None
}
fn expand_function_macro(
&mut self,
def: &MacroDef,
args: &[Vec<Token>],
current_file: Option<&str>,
current_line: u32,
) -> Vec<Token> {
self.expanding.insert(def.name.clone());
if self.config.track_locations {
self.expansion_stack.push(ExpansionFrame {
macro_name: def.name.clone(),
file: current_file.map(|s| s.to_string()),
line: current_line,
depth: self.expansion_stack.len(),
});
}
let body_tokens = &def.body;
let mut result = Vec::new();
let mut i = 0;
while i < body_tokens.len() {
let token = &body_tokens[i];
if i + 1 < body_tokens.len() && body_tokens[i + 1].kind == TokenKind::HashHash {
let prev = if i > 0 {
body_tokens.get(i - 1).cloned()
} else {
None
};
let next = if i + 2 < body_tokens.len() {
body_tokens.get(i + 2).cloned()
} else {
None
};
let pasted = self.token_paste(&prev, &next, args, def);
if !result.is_empty() {
result.pop();
}
if let Some(t) = pasted {
result.push(t);
}
i += 3; continue;
}
if token.kind == TokenKind::Hash {
if i + 1 < body_tokens.len() {
let param_name = &body_tokens[i + 1].text;
if let Some(param_idx) = def.params.iter().position(|p| p == param_name) {
if param_idx < args.len() {
let stringified = stringify_tokens(&args[param_idx]);
result.push(Token {
kind: TokenKind::StringLiteral,
text: stringified,
location: token.location,
flags: TokenFlags::none(),
});
i += 2;
continue;
}
}
}
}
if token.kind == TokenKind::Identifier {
let name = &token.text;
if name == "__VA_ARGS__" && def.is_variadic {
for (arg_idx, arg_tokens) in args.iter().enumerate() {
if arg_idx >= def.params.len() {
result.extend(arg_tokens.clone());
if arg_idx < args.len() - 1 {
result.push(comma_token());
}
}
}
i += 1;
continue;
}
if let Some(param_idx) = def.params.iter().position(|p| p == name) {
if param_idx < args.len() {
result.extend(args[param_idx].clone());
i += 1;
continue;
} else if def.is_variadic && param_idx == def.params.len() - 1 {
let remaining: Vec<_> = args
.iter()
.skip(def.params.len() - 1)
.enumerate()
.flat_map(|(j, arg)| {
let mut v = arg.clone();
if j < args.len() - def.params.len() {
v.push(comma_token());
}
v
})
.collect();
result.extend(remaining);
i += 1;
continue;
}
}
}
result.push(token.clone());
i += 1;
}
let expanded = self.expand(&result, current_file, current_line);
self.expanding.remove(&def.name);
if self.config.track_locations {
self.expansion_stack.pop();
}
expanded
}
fn token_paste(
&self,
prev: &Option<Token>,
next: &Option<Token>,
args: &[Vec<Token>],
def: &MacroDef,
) -> Option<Token> {
let prev_text = prev
.as_ref()
.map(|t| resolved_param_text(t, args, def))
.unwrap_or_default();
let next_text = next
.as_ref()
.map(|t| resolved_param_text(t, args, def))
.unwrap_or_default();
if prev_text.is_empty() && next_text.is_empty() {
None
} else if prev_text.is_empty() {
next.clone()
} else if next_text.is_empty() {
prev.clone()
} else {
let combined = format!("{}{}", prev_text, next_text);
let kind = classify_pasted_token(&combined);
let location = prev
.as_ref()
.or(next.as_ref())
.map(|t| t.location)
.unwrap_or_else(SourceLoc::unknown);
Some(Token {
kind,
text: combined,
location,
flags: TokenFlags::none(),
})
}
}
fn expand_builtin(
&mut self,
name: &str,
current_file: Option<&str>,
current_line: u32,
) -> Option<Token> {
match name {
"__LINE__" => Some(Token {
kind: TokenKind::NumericLiteral,
text: current_line.to_string(),
location: SourceLoc::unknown(),
flags: TokenFlags::none(),
}),
"__FILE__" => {
let fname = current_file.unwrap_or("<unknown>");
Some(Token {
kind: TokenKind::StringLiteral,
text: format!("\"{}\"", fname),
location: SourceLoc::unknown(),
flags: TokenFlags::none(),
})
}
"__DATE__" => {
let date = current_date_string();
Some(Token {
kind: TokenKind::StringLiteral,
text: format!("\"{}\"", date),
location: SourceLoc::unknown(),
flags: TokenFlags::none(),
})
}
"__TIME__" => {
let time = current_time_string();
Some(Token {
kind: TokenKind::StringLiteral,
text: format!("\"{}\"", time),
location: SourceLoc::unknown(),
flags: TokenFlags::none(),
})
}
"__COUNTER__" => {
let val = self.config.counter;
self.config.counter += 1;
Some(Token {
kind: TokenKind::NumericLiteral,
text: val.to_string(),
location: SourceLoc::unknown(),
flags: TokenFlags::none(),
})
}
"__BASE_FILE__" => {
let fname = current_file.unwrap_or("<unknown>");
Some(Token {
kind: TokenKind::StringLiteral,
text: format!("\"{}\"", fname),
location: SourceLoc::unknown(),
flags: TokenFlags::none(),
})
}
"__TIMESTAMP__" => {
let ts = current_timestamp_string();
Some(Token {
kind: TokenKind::StringLiteral,
text: format!("\"{}\"", ts),
location: SourceLoc::unknown(),
flags: TokenFlags::none(),
})
}
"__INCLUDE_LEVEL__" => {
let level = self.expansion_stack.len();
Some(Token {
kind: TokenKind::NumericLiteral,
text: level.to_string(),
location: SourceLoc::unknown(),
flags: TokenFlags::none(),
})
}
_ => None,
}
}
pub fn backtrace(&self) -> &[ExpansionFrame] {
&self.expansion_stack
}
pub fn clear(&mut self) {
self.defines.clear();
self.expanding.clear();
self.expansion_stack.clear();
self.config.counter = 0;
}
}
impl Default for X86MacroExpander {
fn default() -> Self {
Self::new()
}
}
fn is_whitespace_token(token: &Token) -> bool {
token.kind == TokenKind::Newline || token.kind == TokenKind::Comment
}
fn is_builtin_macro_name(name: &str) -> bool {
matches!(
name,
"__LINE__"
| "__FILE__"
| "__DATE__"
| "__TIME__"
| "__COUNTER__"
| "__BASE_FILE__"
| "__TIMESTAMP__"
| "__INCLUDE_LEVEL__"
)
}
fn stringify_tokens(tokens: &[Token]) -> String {
let mut s = String::new();
for (i, token) in tokens.iter().enumerate() {
if i > 0 {
s.push(' ');
}
s.push_str(&token.text);
}
format!("\"{}\"", escape_string_content(&s))
}
fn escape_string_content(s: &str) -> String {
let mut result = String::new();
for ch in s.chars() {
match ch {
'"' => result.push_str("\\\""),
'\\' => result.push_str("\\\\"),
'\n' => result.push_str("\\n"),
'\t' => result.push_str("\\t"),
'\r' => result.push_str("\\r"),
_ => result.push(ch),
}
}
result
}
fn resolved_param_text(token: &Token, args: &[Vec<Token>], def: &MacroDef) -> String {
if token.kind == TokenKind::Identifier {
if let Some(param_idx) = def.params.iter().position(|p| p == &token.text) {
if param_idx < args.len() {
return args[param_idx]
.iter()
.map(|t| t.text.clone())
.collect::<Vec<_>>()
.join("");
}
}
}
token.text.clone()
}
fn classify_pasted_token(text: &str) -> TokenKind {
if text.is_empty() {
return TokenKind::Unknown;
}
if is_valid_identifier(text) {
match text {
"auto" => return TokenKind::KwAuto,
"break" => return TokenKind::KwBreak,
"case" => return TokenKind::KwCase,
"char" => return TokenKind::KwChar,
"const" => return TokenKind::KwConst,
"continue" => return TokenKind::KwContinue,
"default" => return TokenKind::KwDefault,
"do" => return TokenKind::KwDo,
"double" => return TokenKind::KwDouble,
"else" => return TokenKind::KwElse,
"enum" => return TokenKind::KwEnum,
"extern" => return TokenKind::KwExtern,
"float" => return TokenKind::KwFloat,
"for" => return TokenKind::KwFor,
"goto" => return TokenKind::KwGoto,
"if" => return TokenKind::KwIf,
"inline" => return TokenKind::KwInline,
"int" => return TokenKind::KwInt,
"long" => return TokenKind::KwLong,
"register" => return TokenKind::KwRegister,
"restrict" => return TokenKind::KwRestrict,
"return" => return TokenKind::KwReturn,
"short" => return TokenKind::KwShort,
"signed" => return TokenKind::KwSigned,
"sizeof" => return TokenKind::KwSizeof,
"static" => return TokenKind::KwStatic,
"struct" => return TokenKind::KwStruct,
"switch" => return TokenKind::KwSwitch,
"typedef" => return TokenKind::KwTypedef,
"union" => return TokenKind::KwUnion,
"unsigned" => return TokenKind::KwUnsigned,
"void" => return TokenKind::KwVoid,
"volatile" => return TokenKind::KwVolatile,
"while" => return TokenKind::KwWhile,
"bool" => return TokenKind::KwBool,
_ => return TokenKind::Identifier,
}
}
if text.chars().next().map_or(false, |c| c.is_ascii_digit()) {
return TokenKind::NumericLiteral;
}
match text {
"(" => TokenKind::LParen,
")" => TokenKind::RParen,
"{" => TokenKind::LBrace,
"}" => TokenKind::RBrace,
"[" => TokenKind::LBracket,
"]" => TokenKind::RBracket,
"." => TokenKind::Dot,
"->" => TokenKind::Arrow,
"++" => TokenKind::PlusPlus,
"--" => TokenKind::MinusMinus,
"+" => TokenKind::Plus,
"-" => TokenKind::Minus,
"*" => TokenKind::Star,
"/" => TokenKind::Slash,
"%" => TokenKind::Percent,
"&" => TokenKind::Ampersand,
"|" => TokenKind::Pipe,
"^^" => TokenKind::Caret,
"~" => TokenKind::Tilde,
"!" => TokenKind::Exclaim,
"<" => TokenKind::Less,
">" => TokenKind::Greater,
"<=" => TokenKind::LessEqual,
">=" => TokenKind::GreaterEqual,
"==" => TokenKind::EqualEqual,
"!=" => TokenKind::NotEqual,
"&&" => TokenKind::AndAnd,
"||" => TokenKind::OrOr,
"<<" => TokenKind::LessLess,
">>" => TokenKind::GreaterGreater,
"+=" => TokenKind::PlusEqual,
"-=" => TokenKind::MinusEqual,
"*=" => TokenKind::StarEqual,
"/=" => TokenKind::SlashEqual,
"%=" => TokenKind::PercentEqual,
"&=" => TokenKind::AmpersandEqual,
"|=" => TokenKind::PipeEqual,
"^^=" => TokenKind::CaretEqual,
"<<=" => TokenKind::LessLessEqual,
">>=" => TokenKind::GreaterGreaterEqual,
"=" => TokenKind::Equal,
";" => TokenKind::Semicolon,
"," => TokenKind::Comma,
":" => TokenKind::Colon,
"?" => TokenKind::Question,
"#" => TokenKind::Hash,
"##" => TokenKind::HashHash,
"..." => TokenKind::Ellipsis,
_ => TokenKind::Unknown,
}
}
fn is_valid_identifier(s: &str) -> bool {
let mut chars = s.chars();
match chars.next() {
None => return false,
Some(c) => {
if !c.is_ascii_alphabetic() && c != '_' {
return false;
}
}
}
chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
}
fn comma_token() -> Token {
Token {
kind: TokenKind::Comma,
text: ",".to_string(),
location: SourceLoc::unknown(),
flags: TokenFlags::none(),
}
}
fn tokenize_definition_body(body: &str) -> Vec<Token> {
let mut lexer = Lexer::new(body, CLangStandard::C17);
let tokens = lexer.lex_all();
tokens
.into_iter()
.filter(|t| t.kind != TokenKind::Newline && t.kind != TokenKind::Comment)
.cloned()
.collect()
}
fn current_date_string() -> String {
"Jun 30 2026".to_string()
}
fn current_time_string() -> String {
"00:00:00".to_string()
}
fn current_timestamp_string() -> String {
"Mon Jun 30 00:00:00 2026".to_string()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LibCFamily {
Glibc,
Musl,
Bionic,
MSVC,
Darwin,
Unknown,
}
#[derive(Debug, Clone)]
pub struct X86IncludePath {
pub path: PathBuf,
pub is_system: bool,
pub is_framework: bool,
pub is_user: bool,
pub priority: u32,
}
#[derive(Debug, Clone)]
pub struct X86IncludeResolver {
pub search_paths: Vec<X86IncludePath>,
pub libc_family: LibCFamily,
pub target_triple: String,
pub sysroot: Option<PathBuf>,
pub resource_dir: Option<PathBuf>,
pub max_include_depth: u32,
include_cache: HashMap<String, Option<PathBuf>>,
pragma_once_set: HashSet<String>,
}
impl X86IncludeResolver {
pub fn new() -> Self {
let mut resolver = Self {
search_paths: Vec::new(),
libc_family: LibCFamily::Glibc,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
sysroot: None,
resource_dir: None,
max_include_depth: 200,
include_cache: HashMap::new(),
pragma_once_set: HashSet::new(),
};
resolver.add_default_paths();
resolver
}
pub fn for_target(target_triple: &str) -> Self {
let parts: Vec<&str> = target_triple.split('-').collect();
let os = parts.get(2).copied().unwrap_or("linux");
let env = parts.get(3).copied().unwrap_or("gnu");
let libc_family = match (os, env) {
("linux", "gnu") => LibCFamily::Glibc,
("linux", "musl") => LibCFamily::Musl,
("linux", "android") | ("android", _) => LibCFamily::Bionic,
("darwin", _) | ("macos", _) => LibCFamily::Darwin,
("windows", "msvc") => LibCFamily::MSVC,
_ => LibCFamily::Unknown,
};
let mut resolver = Self {
search_paths: Vec::new(),
libc_family,
target_triple: target_triple.to_string(),
sysroot: None,
resource_dir: None,
max_include_depth: 200,
include_cache: HashMap::new(),
pragma_once_set: HashSet::new(),
};
resolver.add_default_paths();
resolver
}
pub fn set_sysroot(&mut self, path: &Path) {
self.sysroot = Some(path.to_path_buf());
self.include_cache.clear();
}
pub fn set_resource_dir(&mut self, path: &Path) {
self.resource_dir = Some(path.to_path_buf());
self.include_cache.clear();
}
pub fn add_user_path(&mut self, path: &Path) {
self.search_paths.push(X86IncludePath {
path: path.to_path_buf(),
is_system: false,
is_framework: false,
is_user: true,
priority: 0,
});
}
pub fn add_system_path(&mut self, path: &Path) {
self.search_paths.push(X86IncludePath {
path: path.to_path_buf(),
is_system: true,
is_framework: false,
is_user: false,
priority: 100,
});
}
fn add_default_paths(&mut self) {
let sysroot = self.sysroot.clone().unwrap_or_else(|| PathBuf::from("/"));
let triple = &self.target_triple;
if let Some(ref res_dir) = self.resource_dir {
self.search_paths.push(X86IncludePath {
path: res_dir.join("include"),
is_system: true,
is_framework: false,
is_user: false,
priority: 10,
});
}
match self.libc_family {
LibCFamily::Glibc => {
let multiarch = if self.target_triple.starts_with("x86_64") {
"x86_64-linux-gnu"
} else {
"i386-linux-gnu"
};
self.search_paths.push(X86IncludePath {
path: sysroot.join("usr/local/include"),
is_system: true,
is_framework: false,
is_user: false,
priority: 20,
});
self.search_paths.push(X86IncludePath {
path: sysroot.join(format!("usr/lib/{}/include", multiarch)),
is_system: true,
is_framework: false,
is_user: false,
priority: 30,
});
self.search_paths.push(X86IncludePath {
path: sysroot.join("usr/include"),
is_system: true,
is_framework: false,
is_user: false,
priority: 40,
});
self.search_paths.push(X86IncludePath {
path: sysroot.join(format!("usr/include/{}", multiarch)),
is_system: true,
is_framework: false,
is_user: false,
priority: 50,
});
self.search_paths.push(X86IncludePath {
path: sysroot.join(format!("usr/include/c++/13/{}", multiarch)),
is_system: true,
is_framework: false,
is_user: false,
priority: 60,
});
self.search_paths.push(X86IncludePath {
path: sysroot.join("usr/include/c++/13"),
is_system: true,
is_framework: false,
is_user: false,
priority: 70,
});
}
LibCFamily::Musl => {
self.search_paths.push(X86IncludePath {
path: sysroot.join("usr/local/include"),
is_system: true,
is_framework: false,
is_user: false,
priority: 20,
});
self.search_paths.push(X86IncludePath {
path: sysroot.join("usr/include"),
is_system: true,
is_framework: false,
is_user: false,
priority: 30,
});
}
LibCFamily::Bionic => {
self.search_paths.push(X86IncludePath {
path: sysroot.join(format!("usr/include")),
is_system: true,
is_framework: false,
is_user: false,
priority: 20,
});
self.search_paths.push(X86IncludePath {
path: sysroot.join("usr/local/include"),
is_system: true,
is_framework: false,
is_user: false,
priority: 30,
});
}
LibCFamily::MSVC => {
self.search_paths.push(X86IncludePath {
path: PathBuf::from("C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.38.33130/include"),
is_system: true, is_framework: false, is_user: false, priority: 20,
});
self.search_paths.push(X86IncludePath {
path: PathBuf::from(
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/ucrt",
),
is_system: true,
is_framework: false,
is_user: false,
priority: 30,
});
self.search_paths.push(X86IncludePath {
path: PathBuf::from(
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/shared",
),
is_system: true,
is_framework: false,
is_user: false,
priority: 40,
});
self.search_paths.push(X86IncludePath {
path: PathBuf::from(
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/um",
),
is_system: true,
is_framework: false,
is_user: false,
priority: 50,
});
}
LibCFamily::Darwin => {
self.search_paths.push(X86IncludePath {
path: sysroot.join("usr/local/include"),
is_system: true,
is_framework: false,
is_user: false,
priority: 20,
});
self.search_paths.push(X86IncludePath {
path: sysroot.join("usr/include"),
is_system: true,
is_framework: false,
is_user: false,
priority: 30,
});
self.search_paths.push(X86IncludePath {
path: PathBuf::from("/System/Library/Frameworks"),
is_system: true,
is_framework: true,
is_user: false,
priority: 40,
});
self.search_paths.push(X86IncludePath {
path: PathBuf::from("/Library/Frameworks"),
is_system: true,
is_framework: true,
is_user: false,
priority: 50,
});
}
LibCFamily::Unknown => {
self.search_paths.push(X86IncludePath {
path: sysroot.join("usr/local/include"),
is_system: true,
is_framework: false,
is_user: false,
priority: 20,
});
self.search_paths.push(X86IncludePath {
path: sysroot.join("usr/include"),
is_system: true,
is_framework: false,
is_user: false,
priority: 30,
});
}
}
self.search_paths
.sort_by(|a, b| a.priority.cmp(&b.priority));
}
pub fn resolve_quoted_include(
&mut self,
include_name: &str,
current_file: Option<&Path>,
) -> Option<PathBuf> {
if let Some(result) = self.include_cache.get(include_name) {
return result.clone();
}
if let Some(current) = current_file {
if let Some(parent) = current.parent() {
let candidate = parent.join(include_name);
if candidate.exists() {
self.include_cache
.insert(include_name.to_string(), Some(candidate.clone()));
return Some(candidate);
}
}
}
let result = self
.search_paths
.iter()
.filter(|p| !p.is_framework)
.map(|p| p.path.join(include_name))
.find(|p| p.exists())
.or_else(|| {
self.search_paths
.iter()
.filter(|p| p.is_framework)
.find_map(|p| {
let headername = format!(
"{}.framework/Headers/{}",
include_name.trim_end_matches(".h"),
include_name
);
let candidate = p.path.join(&headername);
if candidate.exists() {
Some(candidate)
} else {
None
}
})
});
let result = result.or_else(|| {
self.search_paths
.iter()
.filter(|p| p.is_system)
.map(|p| p.path.join(include_name))
.find(|p| p.exists())
});
self.include_cache
.insert(include_name.to_string(), result.clone());
result
}
pub fn resolve_system_include(&mut self, include_name: &str) -> Option<PathBuf> {
if let Some(result) = self.include_cache.get(include_name) {
return result.clone();
}
let result = self
.search_paths
.iter()
.filter(|p| !p.is_framework)
.map(|p| p.path.join(include_name))
.find(|p| p.exists())
.or_else(|| {
self.search_paths
.iter()
.filter(|p| p.is_framework)
.find_map(|p| {
let headername = format!(
"{}.framework/Headers/{}",
include_name.trim_end_matches(".h"),
include_name
);
let candidate = p.path.join(&headername);
if candidate.exists() {
Some(candidate)
} else {
None
}
})
});
self.include_cache
.insert(include_name.to_string(), result.clone());
result
}
pub fn mark_pragma_once(&mut self, path: &Path) {
self.pragma_once_set
.insert(path.to_string_lossy().to_string());
}
pub fn is_pragma_once(&self, path: &Path) -> bool {
self.pragma_once_set
.contains(&path.to_string_lossy().to_string())
}
pub fn clear_cache(&mut self) {
self.include_cache.clear();
}
pub fn clear_pragma_once(&mut self) {
self.pragma_once_set.clear();
}
pub fn dump_paths(&self) -> Vec<String> {
self.search_paths
.iter()
.map(|p| p.path.to_string_lossy().to_string())
.collect()
}
}
impl Default for X86IncludeResolver {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum X86PragmaKind {
GccOptimize(String),
GccTarget(String),
GccPushOptions,
GccPopOptions,
GccDiagnostic(GccDiagnosticAction),
GccPoison(Vec<String>),
GccSystemHeader,
GccVisibility(String),
ClangLoop(LoopHint),
ClangLoopUnroll(LoopHint),
ClangLoopInterleave(LoopHint),
ClangLoopVectorizeWidth(u32),
ClangLoopUnrollCount(u32),
StdcFpContract(FpContractState),
StdcFenvAccess(bool),
StdcCxLimitedRange(bool),
Pack(u8),
Once,
Message(String),
CommentLib(String),
Unknown(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GccDiagnosticAction {
Push,
Pop,
Ignored(String),
Warning(String),
Error(String),
Fatal(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LoopHint {
Enable,
Disable,
AssumeSafety,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FpContractState {
On,
Off,
Fast,
}
#[derive(Debug, Clone, Default)]
pub struct X86PragmaState {
pub optimize_stack: Vec<String>,
pub target_stack: Vec<String>,
pub diagnostic_stack: Vec<HashMap<String, GccDiagnosticAction>>,
pub visibility_stack: Vec<String>,
pub poisoned: HashSet<String>,
pub pack_alignment: u8,
pub in_system_header: bool,
pub ignore_remaining_pragmas: bool,
}
#[derive(Debug, Clone)]
pub struct X86PragmaHandler {
pub state: X86PragmaState,
pub messages: Vec<String>,
pub comment_libs: Vec<String>,
}
impl X86PragmaHandler {
pub fn new() -> Self {
Self {
state: X86PragmaState::default(),
messages: Vec::new(),
comment_libs: Vec::new(),
}
}
pub fn parse(&self, directive: &str) -> X86PragmaKind {
let trimmed = directive.trim();
if trimmed == "once" {
return X86PragmaKind::Once;
}
if trimmed.starts_with("pack(") {
let inner = &trimmed[5..trimmed.len().saturating_sub(1)];
if let Ok(n) = inner.trim().parse::<u8>() {
return X86PragmaKind::Pack(n);
}
return X86PragmaKind::Unknown(directive.to_string());
}
if let Some(msg) = extract_pragma_string(trimmed, "message") {
return X86PragmaKind::Message(msg);
}
if let Some(lib) = extract_pragma_comment_lib(trimmed) {
return X86PragmaKind::CommentLib(lib);
}
if let Some(state) = extract_fp_contract(trimmed) {
return X86PragmaKind::StdcFpContract(state);
}
if let Some(val) = extract_bool_pragma(trimmed, "STDC FENV_ACCESS") {
return X86PragmaKind::StdcFenvAccess(val);
}
if let Some(val) = extract_bool_pragma(trimmed, "STDC CX_LIMITED_RANGE") {
return X86PragmaKind::StdcCxLimitedRange(val);
}
if let Some(opt) = extract_pragma_string(trimmed, "GCC optimize") {
return X86PragmaKind::GccOptimize(opt);
}
if let Some(target) = extract_pragma_string(trimmed, "GCC target") {
return X86PragmaKind::GccTarget(target);
}
if trimmed == "GCC push_options" {
return X86PragmaKind::GccPushOptions;
}
if trimmed == "GCC pop_options" {
return X86PragmaKind::GccPopOptions;
}
if let Some(action) = extract_gcc_diagnostic(trimmed) {
return X86PragmaKind::GccDiagnostic(action);
}
if let Some(ids) = extract_gcc_poison(trimmed) {
return X86PragmaKind::GccPoison(ids);
}
if trimmed == "GCC system_header" {
return X86PragmaKind::GccSystemHeader;
}
if let Some(vis) = extract_gcc_visibility(trimmed) {
return X86PragmaKind::GccVisibility(vis);
}
if let Some(hint) = extract_clang_loop(trimmed) {
return hint;
}
X86PragmaKind::Unknown(directive.to_string())
}
pub fn handle(&mut self, pragma: &X86PragmaKind) {
match pragma {
X86PragmaKind::GccPushOptions => {
self.state.optimize_stack.push("default".to_string());
self.state.target_stack.push("default".to_string());
}
X86PragmaKind::GccPopOptions => {
self.state.optimize_stack.pop();
self.state.target_stack.pop();
}
X86PragmaKind::GccDiagnostic(action) => {
match action {
GccDiagnosticAction::Push => {
self.state.diagnostic_stack.push(HashMap::new());
}
GccDiagnosticAction::Pop => {
self.state.diagnostic_stack.pop();
}
_ => {
if let Some(current) = self.state.diagnostic_stack.last_mut() {
let name = match action {
GccDiagnosticAction::Ignored(n)
| GccDiagnosticAction::Warning(n)
| GccDiagnosticAction::Error(n)
| GccDiagnosticAction::Fatal(n) => n.clone(),
_ => return,
};
current.insert(name, action.clone());
}
}
}
}
X86PragmaKind::GccPoison(ids) => {
for id in ids {
self.state.poisoned.insert(id.clone());
}
}
X86PragmaKind::GccSystemHeader => {
self.state.in_system_header = true;
}
X86PragmaKind::Pack(n) => {
self.state.pack_alignment = *n;
}
X86PragmaKind::Message(msg) => {
self.messages.push(msg.clone());
}
X86PragmaKind::CommentLib(lib) => {
self.comment_libs.push(lib.clone());
}
_ => {}
}
}
pub fn is_poisoned(&self, ident: &str) -> bool {
self.state.poisoned.contains(ident)
}
pub fn get_pack_alignment(&self) -> u8 {
if self.state.pack_alignment == 0 {
8 } else {
self.state.pack_alignment
}
}
pub fn is_system_header(&self) -> bool {
self.state.in_system_header
}
}
impl Default for X86PragmaHandler {
fn default() -> Self {
Self::new()
}
}
fn extract_pragma_string(directive: &str, prefix: &str) -> Option<String> {
let prefix_with_paren = format!("{}(", prefix);
if !directive.starts_with(&prefix_with_paren) {
return None;
}
let start = prefix_with_paren.len();
let end = directive.len();
if end == 0 || directive.as_bytes().last().copied() != Some(b')') {
return None;
}
let inner = &directive[start..end.saturating_sub(1)].trim();
if inner.starts_with('"') && inner.ends_with('"') && inner.len() >= 2 {
Some(inner[1..inner.len().saturating_sub(1)].to_string())
} else {
Some(inner.to_string())
}
}
fn extract_bool_pragma(directive: &str, prefix: &str) -> Option<bool> {
if !directive.starts_with(prefix) {
return None;
}
let rest = directive[prefix.len()..].trim();
match rest.to_uppercase().as_str() {
"ON" => Some(true),
"OFF" => Some(false),
_ => None,
}
}
fn extract_fp_contract(directive: &str) -> Option<FpContractState> {
let prefix = "STDC FP_CONTRACT";
if !directive.starts_with(prefix) {
return None;
}
let rest = directive[prefix.len()..].trim().to_uppercase();
match rest.as_str() {
"ON" => Some(FpContractState::On),
"OFF" => Some(FpContractState::Off),
"FAST" => Some(FpContractState::Fast),
_ => None,
}
}
fn extract_pragma_comment_lib(directive: &str) -> Option<String> {
let prefix = "comment(lib,";
if !directive.starts_with(prefix) {
return None;
}
let inner = &directive[prefix.len()..];
let inner = inner.trim();
if inner.ends_with(')') {
let inner = &inner[..inner.len().saturating_sub(1)].trim();
if inner.starts_with('"') && inner.ends_with('"') && inner.len() >= 2 {
Some(inner[1..inner.len().saturating_sub(1)].to_string())
} else {
Some(inner.to_string())
}
} else {
None
}
}
fn extract_gcc_diagnostic(directive: &str) -> Option<GccDiagnosticAction> {
let prefix = "GCC diagnostic";
if !directive.starts_with(prefix) {
return None;
}
let rest = directive[prefix.len()..].trim();
if rest == "push" {
return Some(GccDiagnosticAction::Push);
}
if rest == "pop" {
return Some(GccDiagnosticAction::Pop);
}
let parts: Vec<&str> = rest.splitn(2, char::is_whitespace).collect();
if parts.len() >= 2 {
let warning_name = parts[1].trim().trim_matches('"').to_string();
match parts[0] {
"ignored" => return Some(GccDiagnosticAction::Ignored(warning_name)),
"warning" => return Some(GccDiagnosticAction::Warning(warning_name)),
"error" => return Some(GccDiagnosticAction::Error(warning_name)),
"fatal" => return Some(GccDiagnosticAction::Fatal(warning_name)),
_ => {}
}
}
None
}
fn extract_gcc_poison(directive: &str) -> Option<Vec<String>> {
let prefix = "GCC poison";
if !directive.starts_with(prefix) {
return None;
}
let rest = directive[prefix.len()..].trim();
if rest.is_empty() {
return Some(vec![]);
}
Some(rest.split_whitespace().map(|s| s.to_string()).collect())
}
fn extract_gcc_visibility(directive: &str) -> Option<String> {
let prefix = "GCC visibility push(";
if !directive.starts_with(prefix) {
return None;
}
let inner = &directive[prefix.len()..];
if inner.ends_with(')') {
Some(inner[..inner.len().saturating_sub(1)].trim().to_string())
} else {
None
}
}
fn extract_clang_loop(directive: &str) -> Option<X86PragmaKind> {
let prefix = "clang loop";
if !directive.starts_with(prefix) {
return None;
}
let rest = directive[prefix.len()..].trim();
if let Some(val) = extract_loop_hint(rest, "vectorize") {
return Some(X86PragmaKind::ClangLoop(val));
}
if let Some(val) = extract_loop_hint(rest, "unroll") {
return Some(X86PragmaKind::ClangLoopUnroll(val));
}
if let Some(val) = extract_loop_hint(rest, "interleave") {
return Some(X86PragmaKind::ClangLoopInterleave(val));
}
if let Some(width) = extract_loop_numeric(rest, "vectorize_width") {
return Some(X86PragmaKind::ClangLoopVectorizeWidth(width));
}
if let Some(count) = extract_loop_numeric(rest, "unroll_count") {
return Some(X86PragmaKind::ClangLoopUnrollCount(count));
}
None
}
fn extract_loop_hint(rest: &str, keyword: &str) -> Option<LoopHint> {
let full = format!("{}(", keyword);
if !rest.starts_with(&full) {
return None;
}
let inner = &rest[full.len()..];
let inner = inner.trim_end_matches(')').trim();
match inner {
"enable" => Some(LoopHint::Enable),
"disable" => Some(LoopHint::Disable),
"assume_safety" => Some(LoopHint::AssumeSafety),
_ => None,
}
}
fn extract_loop_numeric(rest: &str, keyword: &str) -> Option<u32> {
let full = format!("{}(", keyword);
if !rest.starts_with(&full) {
return None;
}
let inner = &rest[full.len()..];
let inner = inner.trim_end_matches(')').trim();
inner.parse::<u32>().ok()
}
#[derive(Debug, Clone)]
pub struct X86Predefines {
pub arch: String,
pub os: String,
pub env: String,
pub libc_family: LibCFamily,
pub cpu_features: HashSet<X86Feature>,
pub microarch_level: X86MicroarchLevel,
pub sse2_math: bool,
pub standard: CLangStandard,
pub is_cxx: bool,
pub pic: bool,
pub pie: bool,
}
impl X86Predefines {
pub fn from_triple(triple: &str) -> Self {
let parts: Vec<&str> = triple.split('-').collect();
let arch = parts.first().copied().unwrap_or("x86_64").to_string();
let os = parts.get(2).copied().unwrap_or("linux").to_string();
let env = parts.get(3).copied().unwrap_or("gnu").to_string();
let libc_family = match (os.as_str(), env.as_str()) {
("linux", "gnu") => LibCFamily::Glibc,
("linux", "musl") => LibCFamily::Musl,
("linux", "android") | ("android", _) => LibCFamily::Bionic,
("darwin", _) | ("macos", _) => LibCFamily::Darwin,
("windows", "msvc") => LibCFamily::MSVC,
_ => LibCFamily::Unknown,
};
let is_64bit = matches!(arch.as_str(), "x86_64" | "amd64");
let microarch_level = X86MicroarchLevel::Baseline;
let mut cpu_features = HashSet::new();
if is_64bit {
cpu_features.insert(X86Feature::MMX);
cpu_features.insert(X86Feature::SSE);
cpu_features.insert(X86Feature::SSE2);
}
Self {
arch,
os,
env,
libc_family,
cpu_features,
microarch_level,
sse2_math: is_64bit,
standard: CLangStandard::C17,
is_cxx: false,
pic: false,
pie: false,
}
}
pub fn set_cpu(&mut self, cpu_name: &str) {
let features = Self::cpu_to_features(cpu_name);
for feature in features {
self.enable_feature(feature);
}
self.microarch_level = Self::cpu_to_microarch_level(cpu_name);
}
pub fn enable_feature(&mut self, feature: X86Feature) {
for implied in feature.implies() {
self.enable_feature(implied);
}
self.cpu_features.insert(feature);
}
pub fn has_feature(&self, feature: &X86Feature) -> bool {
self.cpu_features.contains(feature)
}
pub fn compute(&self) -> Vec<(String, String)> {
let builtins = X86BuiltinMacros {
arch: self.arch.clone(),
os: X86TargetOS::from_str(&self.os),
env: X86TargetEnv::from_str(&self.env),
is_64bit: self.arch.contains("64"),
standard: self.standard,
is_cxx: self.is_cxx,
features: self.cpu_features.clone(),
microarch_level: self.microarch_level,
is_pic: self.pic,
is_pie: self.pie,
..X86BuiltinMacros::new()
};
builtins.generate_all()
}
pub fn cpu_to_features(cpu_name: &str) -> Vec<X86Feature> {
match cpu_name.to_lowercase().as_str() {
"pentium" | "pentium-mmx" => vec![X86Feature::MMX],
"pentiumpro" | "pentium2" | "pentiumii" => vec![X86Feature::MMX],
"pentium3" | "pentiumiii" => vec![X86Feature::MMX, X86Feature::SSE],
"pentium4" | "pentiumiv" | "pentium-m" => {
vec![X86Feature::MMX, X86Feature::SSE, X86Feature::SSE2]
}
"prescott" | "nocona" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
],
"core2" | "penryn" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
],
"nehalem" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
],
"westmere" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::PCLMUL,
X86Feature::AES,
],
"sandybridge" | "sandy" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::PCLMUL,
X86Feature::AES,
],
"ivybridge" | "ivy" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::F16C,
X86Feature::PCLMUL,
X86Feature::AES,
X86Feature::RDRAND,
X86Feature::FSGSBASE,
],
"haswell" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::F16C,
X86Feature::FMA,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::LZCNT,
X86Feature::PCLMUL,
X86Feature::AES,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::FSGSBASE,
],
"broadwell" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::F16C,
X86Feature::FMA,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::LZCNT,
X86Feature::PCLMUL,
X86Feature::AES,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::RDSEED,
X86Feature::FSGSBASE,
X86Feature::ADX,
X86Feature::PRFCHW,
],
"skylake" | "skylake" | "skl" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::F16C,
X86Feature::FMA,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::LZCNT,
X86Feature::PCLMUL,
X86Feature::AES,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::RDSEED,
X86Feature::FSGSBASE,
X86Feature::ADX,
X86Feature::SGX,
X86Feature::CLFLUSHOPT,
X86Feature::XSAVEC,
X86Feature::XSAVES,
],
"skylake-avx512" | "skx" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::F16C,
X86Feature::FMA,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::LZCNT,
X86Feature::PCLMUL,
X86Feature::AES,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::RDSEED,
X86Feature::FSGSBASE,
X86Feature::ADX,
X86Feature::SGX,
X86Feature::CLFLUSHOPT,
X86Feature::XSAVEC,
X86Feature::XSAVES,
X86Feature::AVX512F,
X86Feature::AVX512BW,
X86Feature::AVX512CD,
X86Feature::AVX512DQ,
X86Feature::AVX512VL,
],
"cannonlake" | "cnl" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::F16C,
X86Feature::FMA,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::LZCNT,
X86Feature::PCLMUL,
X86Feature::AES,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::RDSEED,
X86Feature::FSGSBASE,
X86Feature::ADX,
X86Feature::SGX,
X86Feature::CLFLUSHOPT,
X86Feature::XSAVEC,
X86Feature::XSAVES,
X86Feature::AVX512F,
X86Feature::AVX512BW,
X86Feature::AVX512CD,
X86Feature::AVX512DQ,
X86Feature::AVX512VL,
X86Feature::AVX512IFMA,
X86Feature::AVX512VBMI,
X86Feature::SHA,
],
"icelake-client" | "icl" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::F16C,
X86Feature::FMA,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::LZCNT,
X86Feature::PCLMUL,
X86Feature::AES,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::RDSEED,
X86Feature::FSGSBASE,
X86Feature::ADX,
X86Feature::SGX,
X86Feature::CLFLUSHOPT,
X86Feature::XSAVEC,
X86Feature::XSAVES,
X86Feature::AVX512F,
X86Feature::AVX512BW,
X86Feature::AVX512CD,
X86Feature::AVX512DQ,
X86Feature::AVX512VL,
X86Feature::AVX512IFMA,
X86Feature::AVX512VBMI,
X86Feature::AVX512VBMI2,
X86Feature::AVX512VNNI,
X86Feature::AVX512BITALG,
X86Feature::AVX512VPOPCNTDQ,
X86Feature::SHA,
X86Feature::GFNI,
X86Feature::VAES,
X86Feature::VPCLMULQDQ,
X86Feature::CLWB,
X86Feature::RDPID,
X86Feature::WAITPKG,
],
"icelake-server" | "icx" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::F16C,
X86Feature::FMA,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::LZCNT,
X86Feature::PCLMUL,
X86Feature::AES,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::RDSEED,
X86Feature::FSGSBASE,
X86Feature::ADX,
X86Feature::SGX,
X86Feature::CLFLUSHOPT,
X86Feature::XSAVEC,
X86Feature::XSAVES,
X86Feature::AVX512F,
X86Feature::AVX512BW,
X86Feature::AVX512CD,
X86Feature::AVX512DQ,
X86Feature::AVX512VL,
X86Feature::AVX512IFMA,
X86Feature::AVX512VBMI,
X86Feature::AVX512VBMI2,
X86Feature::AVX512VNNI,
X86Feature::AVX512BITALG,
X86Feature::AVX512VPOPCNTDQ,
X86Feature::GFNI,
X86Feature::VAES,
X86Feature::VPCLMULQDQ,
X86Feature::CLWB,
X86Feature::RDPID,
X86Feature::WBNOINVD,
],
"tigerlake" | "tgl" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::F16C,
X86Feature::FMA,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::LZCNT,
X86Feature::PCLMUL,
X86Feature::AES,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::RDSEED,
X86Feature::FSGSBASE,
X86Feature::ADX,
X86Feature::SGX,
X86Feature::CLFLUSHOPT,
X86Feature::XSAVEC,
X86Feature::XSAVES,
X86Feature::AVX512F,
X86Feature::AVX512BW,
X86Feature::AVX512CD,
X86Feature::AVX512DQ,
X86Feature::AVX512VL,
X86Feature::AVX512IFMA,
X86Feature::AVX512VBMI,
X86Feature::AVX512VBMI2,
X86Feature::AVX512VNNI,
X86Feature::AVX512BITALG,
X86Feature::AVX512VPOPCNTDQ,
X86Feature::GFNI,
X86Feature::VAES,
X86Feature::VPCLMULQDQ,
X86Feature::CLWB,
X86Feature::RDPID,
X86Feature::WAITPKG,
X86Feature::MOVDIRI,
X86Feature::MOVDIR64B,
X86Feature::CLDEMOTE,
X86Feature::SHSTK,
X86Feature::IBT,
X86Feature::AVX512BF16,
X86Feature::AVX512FP16,
],
"alderlake" | "adl" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::F16C,
X86Feature::FMA,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::LZCNT,
X86Feature::PCLMUL,
X86Feature::AES,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::RDSEED,
X86Feature::FSGSBASE,
X86Feature::ADX,
X86Feature::SGX,
X86Feature::CLFLUSHOPT,
X86Feature::XSAVEC,
X86Feature::XSAVES,
X86Feature::GFNI,
X86Feature::VAES,
X86Feature::VPCLMULQDQ,
X86Feature::CLWB,
X86Feature::RDPID,
X86Feature::WAITPKG,
X86Feature::MOVDIRI,
X86Feature::MOVDIR64B,
X86Feature::CLDEMOTE,
X86Feature::SHSTK,
X86Feature::IBT,
X86Feature::SERIALIZE,
X86Feature::HRESET,
X86Feature::AVXVNNI,
X86Feature::AVXIFMA,
],
"raptorlake" | "rpl" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::F16C,
X86Feature::FMA,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::LZCNT,
X86Feature::PCLMUL,
X86Feature::AES,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::RDSEED,
X86Feature::FSGSBASE,
X86Feature::ADX,
X86Feature::SGX,
X86Feature::CLFLUSHOPT,
X86Feature::XSAVEC,
X86Feature::XSAVES,
X86Feature::GFNI,
X86Feature::VAES,
X86Feature::VPCLMULQDQ,
X86Feature::CLWB,
X86Feature::RDPID,
X86Feature::WAITPKG,
X86Feature::MOVDIRI,
X86Feature::MOVDIR64B,
X86Feature::CLDEMOTE,
X86Feature::SHSTK,
X86Feature::IBT,
X86Feature::SERIALIZE,
X86Feature::HRESET,
X86Feature::AVXVNNI,
X86Feature::AVXIFMA,
],
"k8" | "opteron" | "athlon64" | "athlon-fx" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::ThreeDNow,
X86Feature::ThreeDNowA,
],
"k8-sse3" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::ThreeDNow,
X86Feature::ThreeDNowA,
],
"barcelona" | "amdfam10" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSE4A,
X86Feature::ThreeDNow,
X86Feature::ThreeDNowA,
],
"bdver1" | "bulldozer" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE4A,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::XOP,
X86Feature::FMA4,
X86Feature::AES,
X86Feature::PCLMUL,
X86Feature::LZCNT,
X86Feature::PRFCHW,
],
"bdver2" | "piledriver" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE4A,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::XOP,
X86Feature::FMA4,
X86Feature::AES,
X86Feature::PCLMUL,
X86Feature::LZCNT,
X86Feature::F16C,
X86Feature::BMI,
X86Feature::TBM,
X86Feature::FMA,
X86Feature::PRFCHW,
],
"bdver3" | "steamroller" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE4A,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::XOP,
X86Feature::FMA4,
X86Feature::AES,
X86Feature::PCLMUL,
X86Feature::LZCNT,
X86Feature::F16C,
X86Feature::BMI,
X86Feature::TBM,
X86Feature::FMA,
X86Feature::FSGSBASE,
X86Feature::PRFCHW,
],
"bdver4" | "excavator" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE4A,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::XOP,
X86Feature::FMA4,
X86Feature::AES,
X86Feature::PCLMUL,
X86Feature::LZCNT,
X86Feature::F16C,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::FMA,
X86Feature::FSGSBASE,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::PRFCHW,
X86Feature::TBM,
],
"znver1" | "zen" | "zen1" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE4A,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::FMA,
X86Feature::AES,
X86Feature::PCLMUL,
X86Feature::LZCNT,
X86Feature::F16C,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::FSGSBASE,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::RDSEED,
X86Feature::ADX,
X86Feature::SHA,
X86Feature::CLFLUSHOPT,
X86Feature::CLZERO,
X86Feature::XSAVEC,
X86Feature::XSAVES,
X86Feature::PRFCHW,
],
"znver2" | "zen2" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE4A,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::FMA,
X86Feature::AES,
X86Feature::PCLMUL,
X86Feature::LZCNT,
X86Feature::F16C,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::FSGSBASE,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::RDSEED,
X86Feature::ADX,
X86Feature::SHA,
X86Feature::CLFLUSHOPT,
X86Feature::CLZERO,
X86Feature::XSAVEC,
X86Feature::XSAVES,
X86Feature::CLWB,
X86Feature::RDPID,
X86Feature::PRFCHW,
X86Feature::WBNOINVD,
X86Feature::MCOMMIT,
],
"znver3" | "zen3" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE4A,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::FMA,
X86Feature::AES,
X86Feature::PCLMUL,
X86Feature::LZCNT,
X86Feature::F16C,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::FSGSBASE,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::RDSEED,
X86Feature::ADX,
X86Feature::SHA,
X86Feature::CLFLUSHOPT,
X86Feature::CLZERO,
X86Feature::XSAVEC,
X86Feature::XSAVES,
X86Feature::CLWB,
X86Feature::RDPID,
X86Feature::PRFCHW,
X86Feature::WBNOINVD,
X86Feature::MCOMMIT,
X86Feature::INVPCID,
X86Feature::VAES,
X86Feature::VPCLMULQDQ,
X86Feature::SHSTK,
X86Feature::IBT,
X86Feature::PKU,
],
"znver4" | "zen4" => vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE4A,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::FMA,
X86Feature::AES,
X86Feature::PCLMUL,
X86Feature::LZCNT,
X86Feature::F16C,
X86Feature::BMI,
X86Feature::BMI2,
X86Feature::FSGSBASE,
X86Feature::MOVBE,
X86Feature::RDRAND,
X86Feature::RDSEED,
X86Feature::ADX,
X86Feature::SHA,
X86Feature::CLFLUSHOPT,
X86Feature::CLZERO,
X86Feature::XSAVEC,
X86Feature::XSAVES,
X86Feature::CLWB,
X86Feature::RDPID,
X86Feature::PRFCHW,
X86Feature::WBNOINVD,
X86Feature::MCOMMIT,
X86Feature::INVPCID,
X86Feature::VAES,
X86Feature::VPCLMULQDQ,
X86Feature::SHSTK,
X86Feature::IBT,
X86Feature::PKU,
X86Feature::AVX512F,
X86Feature::AVX512BW,
X86Feature::AVX512CD,
X86Feature::AVX512DQ,
X86Feature::AVX512VL,
X86Feature::AVX512IFMA,
X86Feature::AVX512VBMI,
X86Feature::AVX512VBMI2,
X86Feature::AVX512VNNI,
X86Feature::AVX512BITALG,
X86Feature::AVX512VPOPCNTDQ,
X86Feature::AVX512BF16,
X86Feature::GFNI,
],
_ => {
vec![
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::SSSE3,
X86Feature::SSE41,
X86Feature::SSE42,
X86Feature::POPCNT,
]
}
}
}
pub fn cpu_to_microarch_level(cpu_name: &str) -> X86MicroarchLevel {
match cpu_name.to_lowercase().as_str() {
"nehalem" | "westmere" | "sandybridge" | "sandy" | "k8-sse3" | "barcelona"
| "amdfam10" => X86MicroarchLevel::V2,
"ivybridge" | "ivy" | "haswell" | "haswell" | "broadwell" | "bdver3"
| "steamroller" | "bdver4" | "excavator" | "znver1" | "zen" | "zen1" | "znver2"
| "zen2" | "znver3" | "zen3" => X86MicroarchLevel::V3,
"skylake-avx512" | "skx" | "cannonlake" | "cnl" | "icelake-client" | "icl"
| "icelake-server" | "icx" | "tigerlake" | "tgl" | "znver4" | "zen4" => {
X86MicroarchLevel::V4
}
_ => X86MicroarchLevel::Baseline,
}
}
}
impl Default for X86Predefines {
fn default() -> Self {
Self::from_triple("x86_64-unknown-linux-gnu")
}
}
#[derive(Debug, Clone)]
pub struct X86ConditionalInclusion {
pub defines: HashMap<String, String>,
pub features: HashSet<X86Feature>,
pub user_defines: HashMap<String, String>,
pub known_headers: HashSet<String>,
}
impl X86ConditionalInclusion {
pub fn new(builtins: &X86BuiltinMacros) -> Self {
let mut defines = HashMap::new();
for (name, value) in builtins.generate_all() {
defines.insert(name, value);
}
Self {
defines,
features: builtins.features.clone(),
user_defines: HashMap::new(),
known_headers: HashSet::new(),
}
}
pub fn add_define(&mut self, name: &str, value: &str) {
self.user_defines
.insert(name.to_string(), value.to_string());
}
pub fn remove_define(&mut self, name: &str) {
self.user_defines.remove(name);
}
pub fn is_defined(&self, name: &str) -> bool {
self.defines.contains_key(name) || self.user_defines.contains_key(name)
}
pub fn has_builtin(&self, name: &str) -> bool {
matches!(
name,
"__builtin_ia32_emms"
| "__builtin_ia32_ldmxcsr"
| "__builtin_ia32_stmxcsr"
| "__builtin_ia32_pause"
| "__builtin_ia32_rdtsc"
| "__builtin_ia32_rdtscp"
| "__builtin_ia32_cpuid"
| "__builtin_ia32_xgetbv"
| "__builtin_ia32_xsetbv"
| "__builtin_ia32_mfence"
| "__builtin_ia32_lfence"
| "__builtin_ia32_sfence"
| "__builtin_ia32_clflush"
| "__builtin_cpu_supports"
| "__builtin_cpu_init"
| "__builtin_cpu_is"
) || self.features.contains(&X86Feature::SSE) && name.contains("sse")
|| self.features.contains(&X86Feature::SSE2) && name.contains("sse2")
|| self.features.contains(&X86Feature::SSE3) && name.contains("sse3")
|| self.features.contains(&X86Feature::AVX) && name.contains("avx")
|| self.features.contains(&X86Feature::AVX2) && name.contains("avx2")
|| self.features.contains(&X86Feature::AVX512F) && name.contains("avx512")
|| self.features.contains(&X86Feature::FMA) && name.contains("fma")
|| self.features.contains(&X86Feature::BMI) && name.contains("bmi")
|| self.features.contains(&X86Feature::BMI2) && name.contains("bmi2")
}
pub fn has_feature(&self, name: &str) -> bool {
match name {
"address_sanitizer" | "thread_sanitizer" | "memory_sanitizer" => false,
"c_alignas"
| "c_alignof"
| "c_atomic"
| "c_generic_selections"
| "c_static_assert"
| "c_thread_local" => true,
"cxx_rtti" | "cxx_exceptions" | "cxx_access_control_sfinae" => true,
"modules" | "tls" => true,
_ => {
name.chars().all(|c| c.is_alphanumeric() || c == '_')
&& self
.defines
.contains_key(&format!("__{}__", name.to_uppercase()))
}
}
}
pub fn has_include(&self, header: &str) -> bool {
self.known_headers.contains(header)
}
pub fn has_cpp_attribute(&self, attr: &str) -> bool {
matches!(
attr,
"clang::fallthrough"
| "clang::no_sanitize"
| "carries_dependency"
| "deprecated"
| "fallthrough"
| "noreturn"
| "maybe_unused"
| "nodiscard"
| "no_unique_address"
)
}
pub fn has_attribute(&self, attr: &str) -> bool {
matches!(
attr,
"always_inline"
| "const"
| "constructor"
| "destructor"
| "deprecated"
| "format"
| "hot"
| "cold"
| "malloc"
| "noinline"
| "noreturn"
| "nothrow"
| "packed"
| "pure"
| "unused"
| "used"
| "visibility"
| "warn_unused_result"
| "weak"
| "aligned"
| "cleanup"
| "overloadable"
| "objc_method_family"
| "objc_ownership"
| "objc_requires_super"
| "flag_enum"
| "enum_extensibility"
| "target"
| "optimize"
| "gnu_inline"
| "artificial"
| "flatten"
| "error"
| "warning"
)
}
pub fn has_declspec_attribute(&self, attr: &str) -> bool {
matches!(
attr,
"dllexport"
| "dllimport"
| "naked"
| "noinline"
| "noreturn"
| "nothrow"
| "novtable"
| "align"
| "deprecated"
| "restrict"
| "thread"
| "property"
| "selectany"
| "uuid"
)
}
pub fn evaluate_condition(&self, expr: &str) -> Option<bool> {
let tokens = tokenize_condition(expr);
let mut evaluator = ConditionExprEvaluator {
tokens,
pos: 0,
ctx: self,
};
evaluator.eval()
}
pub fn eval_ifdef(&self, name: &str) -> bool {
self.is_defined(name)
}
pub fn eval_ifndef(&self, name: &str) -> bool {
!self.is_defined(name)
}
pub fn get_define_value(&self, name: &str) -> Option<&str> {
self.user_defines
.get(name)
.map(|s| s.as_str())
.or_else(|| self.defines.get(name).map(|s| s.as_str()))
}
}
impl Default for X86ConditionalInclusion {
fn default() -> Self {
Self::new(&X86BuiltinMacros::new())
}
}
struct ConditionExprEvaluator<'a> {
tokens: Vec<ConditionToken>,
pos: usize,
ctx: &'a X86ConditionalInclusion,
}
#[derive(Debug, Clone, PartialEq)]
enum ConditionToken {
Ident(String),
Number(i64),
LParen,
RParen,
Question,
Colon,
Plus,
Minus,
Star,
Slash,
Percent,
Less,
Greater,
LessEqual,
GreaterEqual,
EqualEqual,
NotEqual,
AndAnd,
OrOr,
Exclaim,
Tilde,
And,
Pipe,
Caret,
LessLess,
GreaterGreater,
DefinedIdent(String),
DefinedParen(String),
HasInclude(String),
HasBuiltin(String),
HasFeature(String),
HasAttribute(String),
HasCppAttribute(String),
HasDeclspec(String),
Eof,
Unknown,
}
impl<'a> ConditionExprEvaluator<'a> {
fn eval(&mut self) -> Option<bool> {
let val = self.eval_expression()?;
Some(val != 0)
}
fn eval_expression(&mut self) -> Option<i64> {
self.eval_conditional()
}
fn eval_conditional(&mut self) -> Option<i64> {
let mut left = self.eval_logical_or()?;
while self.peek() == Some(&ConditionToken::Question) {
self.advance();
let true_val = self.eval_expression()?;
if self.peek() != Some(&ConditionToken::Colon) {
return None;
}
self.advance();
let false_val = self.eval_conditional()?;
left = if left != 0 { true_val } else { false_val };
}
Some(left)
}
fn eval_logical_or(&mut self) -> Option<i64> {
let mut left = self.eval_logical_and()?;
while self.peek() == Some(&ConditionToken::OrOr) {
self.advance();
let right = self.eval_logical_and()?;
left = if left != 0 || right != 0 { 1 } else { 0 };
}
Some(left)
}
fn eval_logical_and(&mut self) -> Option<i64> {
let mut left = self.eval_or()?;
while self.peek() == Some(&ConditionToken::AndAnd) {
self.advance();
let right = self.eval_or()?;
left = if left != 0 && right != 0 { 1 } else { 0 };
}
Some(left)
}
fn eval_or(&mut self) -> Option<i64> {
let mut left = self.eval_xor()?;
while self.peek() == Some(&ConditionToken::Pipe) {
self.advance();
let right = self.eval_xor()?;
left |= right;
}
Some(left)
}
fn eval_xor(&mut self) -> Option<i64> {
let mut left = self.eval_and()?;
while self.peek() == Some(&ConditionToken::Caret) {
self.advance();
let right = self.eval_and()?;
left ^= right;
}
Some(left)
}
fn eval_and(&mut self) -> Option<i64> {
let mut left = self.eval_equality()?;
while self.peek() == Some(&ConditionToken::And) {
self.advance();
let right = self.eval_equality()?;
left &= right;
}
Some(left)
}
fn eval_equality(&mut self) -> Option<i64> {
let mut left = self.eval_relational()?;
while let Some(op) = self.peek().cloned() {
match op {
ConditionToken::EqualEqual => {
self.advance();
let right = self.eval_relational()?;
left = if left == right { 1 } else { 0 };
}
ConditionToken::NotEqual => {
self.advance();
let right = self.eval_relational()?;
left = if left != right { 1 } else { 0 };
}
_ => break,
}
}
Some(left)
}
fn eval_relational(&mut self) -> Option<i64> {
let mut left = self.eval_shift()?;
while let Some(op) = self.peek().cloned() {
match op {
ConditionToken::Less => {
self.advance();
let right = self.eval_shift()?;
left = if left < right { 1 } else { 0 };
}
ConditionToken::Greater => {
self.advance();
let right = self.eval_shift()?;
left = if left > right { 1 } else { 0 };
}
ConditionToken::LessEqual => {
self.advance();
let right = self.eval_shift()?;
left = if left <= right { 1 } else { 0 };
}
ConditionToken::GreaterEqual => {
self.advance();
let right = self.eval_shift()?;
left = if left >= right { 1 } else { 0 };
}
_ => break,
}
}
Some(left)
}
fn eval_shift(&mut self) -> Option<i64> {
let mut left = self.eval_additive()?;
while let Some(op) = self.peek().cloned() {
match op {
ConditionToken::LessLess => {
self.advance();
let right = self.eval_additive()?;
left = left.wrapping_shl(right as u32);
}
ConditionToken::GreaterGreater => {
self.advance();
let right = self.eval_additive()?;
left = left.wrapping_shr(right as u32);
}
_ => break,
}
}
Some(left)
}
fn eval_additive(&mut self) -> Option<i64> {
let mut left = self.eval_multiplicative()?;
while let Some(op) = self.peek().cloned() {
match op {
ConditionToken::Plus => {
self.advance();
let right = self.eval_multiplicative()?;
left = left.wrapping_add(right);
}
ConditionToken::Minus => {
self.advance();
let right = self.eval_multiplicative()?;
left = left.wrapping_sub(right);
}
_ => break,
}
}
Some(left)
}
fn eval_multiplicative(&mut self) -> Option<i64> {
let mut left = self.eval_unary()?;
while let Some(op) = self.peek().cloned() {
match op {
ConditionToken::Star => {
self.advance();
let right = self.eval_unary()?;
left = left.wrapping_mul(right);
}
ConditionToken::Slash => {
self.advance();
let right = self.eval_unary()?;
if right == 0 {
return None;
}
left = left.wrapping_div(right);
}
ConditionToken::Percent => {
self.advance();
let right = self.eval_unary()?;
if right == 0 {
return None;
}
left = left.wrapping_rem(right);
}
_ => break,
}
}
Some(left)
}
fn eval_unary(&mut self) -> Option<i64> {
match self.peek().cloned() {
Some(ConditionToken::Plus) => {
self.advance();
self.eval_unary()
}
Some(ConditionToken::Minus) => {
self.advance();
self.eval_unary().map(|v| -v)
}
Some(ConditionToken::Exclaim) => {
self.advance();
self.eval_unary().map(|v| if v == 0 { 1 } else { 0 })
}
Some(ConditionToken::Tilde) => {
self.advance();
self.eval_unary().map(|v| !v)
}
_ => self.eval_primary(),
}
}
fn eval_primary(&mut self) -> Option<i64> {
match self.peek().cloned() {
Some(ConditionToken::Number(n)) => {
self.advance();
Some(n)
}
Some(ConditionToken::Ident(ref name)) => {
let name = name.clone();
self.advance();
if let Some(val) = self.ctx.get_define_value(&name) {
let sub_tokens = tokenize_condition(val);
let mut sub_eval = ConditionExprEvaluator {
tokens: sub_tokens,
pos: 0,
ctx: self.ctx,
};
sub_eval.eval_expression()
} else {
Some(0)
}
}
Some(ConditionToken::DefinedIdent(ref name)) => {
let name = name.clone();
self.advance();
Some(if self.ctx.is_defined(&name) { 1 } else { 0 })
}
Some(ConditionToken::DefinedParen(ref name)) => {
let name = name.clone();
self.advance();
Some(if self.ctx.is_defined(&name) { 1 } else { 0 })
}
Some(ConditionToken::HasInclude(ref h)) => {
let h = h.clone();
self.advance();
Some(if self.ctx.has_include(&h) { 1 } else { 0 })
}
Some(ConditionToken::HasBuiltin(ref b)) => {
let b = b.clone();
self.advance();
Some(if self.ctx.has_builtin(&b) { 1 } else { 0 })
}
Some(ConditionToken::HasFeature(ref f)) => {
let f = f.clone();
self.advance();
Some(if self.ctx.has_feature(&f) { 1 } else { 0 })
}
Some(ConditionToken::HasAttribute(ref a)) => {
let a = a.clone();
self.advance();
Some(if self.ctx.has_attribute(&a) { 1 } else { 0 })
}
Some(ConditionToken::HasCppAttribute(ref a)) => {
let a = a.clone();
self.advance();
Some(if self.ctx.has_cpp_attribute(&a) { 1 } else { 0 })
}
Some(ConditionToken::HasDeclspec(ref a)) => {
let a = a.clone();
self.advance();
Some(if self.ctx.has_declspec_attribute(&a) {
1
} else {
0
})
}
Some(ConditionToken::LParen) => {
self.advance();
let val = self.eval_expression()?;
if self.peek() == Some(&ConditionToken::RParen) {
self.advance();
Some(val)
} else {
None
}
}
_ => None,
}
}
fn peek(&self) -> Option<&ConditionToken> {
self.tokens.get(self.pos)
}
fn advance(&mut self) {
self.pos += 1;
}
}
fn tokenize_condition(expr: &str) -> Vec<ConditionToken> {
let mut tokens = Vec::new();
let chars: Vec<char> = expr.chars().collect();
let mut i = 0;
let n = chars.len();
while i < n {
let c = chars[i];
if c.is_whitespace() {
i += 1;
continue;
}
if c == '#' {
if i + 1 < n && chars[i + 1] == '#' {
i += 2;
continue;
}
i += 1;
continue;
}
if c.is_ascii_digit() || (c == '.' && i + 1 < n && chars[i + 1].is_ascii_digit()) {
let start = i;
let mut num_str = String::new();
while i < n
&& (chars[i].is_ascii_alphanumeric()
|| chars[i] == '.'
|| chars[i] == 'x'
|| chars[i] == 'X'
|| chars[i] == '-')
{
num_str.push(chars[i]);
i += 1;
}
if let Some(num) = parse_pp_number(&num_str) {
tokens.push(ConditionToken::Number(num));
} else {
tokens.push(ConditionToken::Number(0));
}
continue;
}
if c == '\'' {
if i + 2 < n && chars[i + 2] == '\'' {
let val = chars[i + 1] as i64;
tokens.push(ConditionToken::Number(val));
i += 3;
continue;
}
i += 1;
continue;
}
if c.is_ascii_alphabetic() || c == '_' {
let start = i;
while i < n && (chars[i].is_ascii_alphanumeric() || chars[i] == '_') {
i += 1;
}
let ident: String = chars[start..i].iter().collect();
match ident.as_str() {
"defined" => {
while i < n && chars[i].is_whitespace() {
i += 1;
}
if i < n && chars[i] == '(' {
i += 1; let start = i;
while i < n && (chars[i].is_ascii_alphanumeric() || chars[i] == '_') {
i += 1;
}
let name: String = chars[start..i].iter().collect();
while i < n && chars[i].is_whitespace() {
i += 1;
}
if i < n && chars[i] == ')' {
i += 1; }
tokens.push(ConditionToken::DefinedParen(name));
} else {
let start = i;
while i < n && (chars[i].is_ascii_alphanumeric() || chars[i] == '_') {
i += 1;
}
let name: String = chars[start..i].iter().collect();
tokens.push(ConditionToken::DefinedIdent(name));
}
}
"__has_include" => {
tokens.push(ConditionToken::HasInclude(parse_has_argument(
&chars, &mut i, n,
)));
}
"__has_builtin" => {
tokens.push(ConditionToken::HasBuiltin(parse_has_argument(
&chars, &mut i, n,
)));
}
"__has_feature" => {
tokens.push(ConditionToken::HasFeature(parse_has_argument(
&chars, &mut i, n,
)));
}
"__has_attribute" => {
tokens.push(ConditionToken::HasAttribute(parse_has_argument(
&chars, &mut i, n,
)));
}
"__has_cpp_attribute" => {
tokens.push(ConditionToken::HasCppAttribute(parse_has_argument(
&chars, &mut i, n,
)));
}
"__has_declspec_attribute" => {
tokens.push(ConditionToken::HasDeclspec(parse_has_argument(
&chars, &mut i, n,
)));
}
_ => {
tokens.push(ConditionToken::Ident(ident));
}
}
continue;
}
match c {
'(' => tokens.push(ConditionToken::LParen),
')' => tokens.push(ConditionToken::RParen),
'+' => tokens.push(ConditionToken::Plus),
'-' => tokens.push(ConditionToken::Minus),
'*' => tokens.push(ConditionToken::Star),
'/' => tokens.push(ConditionToken::Slash),
'%' => tokens.push(ConditionToken::Percent),
'!' => {
if i + 1 < n && chars[i + 1] == '=' {
tokens.push(ConditionToken::NotEqual);
i += 1;
} else {
tokens.push(ConditionToken::Exclaim);
}
}
'=' => {
if i + 1 < n && chars[i + 1] == '=' {
tokens.push(ConditionToken::EqualEqual);
i += 1;
}
}
'<' => {
if i + 1 < n && chars[i + 1] == '=' {
tokens.push(ConditionToken::LessEqual);
i += 1;
} else if i + 1 < n && chars[i + 1] == '<' {
tokens.push(ConditionToken::LessLess);
i += 1;
} else {
tokens.push(ConditionToken::Less);
}
}
'>' => {
if i + 1 < n && chars[i + 1] == '=' {
tokens.push(ConditionToken::GreaterEqual);
i += 1;
} else if i + 1 < n && chars[i + 1] == '>' {
tokens.push(ConditionToken::GreaterGreater);
i += 1;
} else {
tokens.push(ConditionToken::Greater);
}
}
'&' => {
if i + 1 < n && chars[i + 1] == '&' {
tokens.push(ConditionToken::AndAnd);
i += 1;
} else {
tokens.push(ConditionToken::And);
}
}
'|' => {
if i + 1 < n && chars[i + 1] == '|' {
tokens.push(ConditionToken::OrOr);
i += 1;
} else {
tokens.push(ConditionToken::Pipe);
}
}
'^' => tokens.push(ConditionToken::Caret),
'~' => tokens.push(ConditionToken::Tilde),
':' => tokens.push(ConditionToken::Colon),
'?' => tokens.push(ConditionToken::Question),
_ => {}
}
i += 1;
}
tokens.push(ConditionToken::Eof);
tokens
}
fn parse_has_argument(chars: &[char], i: &mut usize, n: usize) -> String {
while *i < n && chars[*i].is_whitespace() {
*i += 1;
}
if *i < n && chars[*i] == '(' {
*i += 1; while *i < n && chars[*i].is_whitespace() {
*i += 1;
}
let start = *i;
if *i < n && chars[*i] == '"' {
*i += 1; while *i < n && chars[*i] != '"' {
*i += 1;
}
if *i < n {
*i += 1; }
} else {
while *i < n
&& (chars[*i].is_ascii_alphanumeric() || chars[*i] == '_' || chars[*i] == ':')
{
*i += 1;
}
}
let arg: String = chars[start..*i].iter().collect();
while *i < n && chars[*i].is_whitespace() {
*i += 1;
}
if *i < n && chars[*i] == ')' {
*i += 1; }
let arg = arg.trim_matches('"').to_string();
arg
} else {
String::new()
}
}
fn parse_pp_number(s: &str) -> Option<i64> {
let s = s.trim().trim_end_matches(&['l', 'L', 'u', 'U'][..]);
if s.is_empty() {
return Some(0);
}
if s.starts_with("0x") || s.starts_with("0X") {
i64::from_str_radix(&s[2..], 16).ok()
} else if s.starts_with('0') && s.len() > 1 {
i64::from_str_radix(&s[1..], 8).ok()
} else if let Some(c) = s.chars().next() {
if c.is_ascii_digit() || c == '-' {
s.parse::<i64>().ok()
} else {
None
}
} else {
None
}
}
#[derive(Debug, Clone)]
pub struct X86TokenPasting;
impl X86TokenPasting {
pub fn new() -> Self {
Self
}
pub fn paste(
&self,
left: Option<&Token>,
right: Option<&Token>,
args: &[Vec<Token>],
params: &[String],
) -> Option<Token> {
let left_text = left
.map(|t| self.resolve_param(t, args, params))
.unwrap_or_default();
let right_text = right
.map(|t| self.resolve_param(t, args, params))
.unwrap_or_default();
if left_text.is_empty() && right_text.is_empty() {
return None;
}
if left_text.is_empty() {
return right.cloned();
}
if right_text.is_empty() {
return left.cloned();
}
let combined = format!("{}{}", left_text, right_text);
self.classify_pasted(&combined, left)
}
pub fn paste_sequence(
&self,
tokens: &[Token],
args: &[Vec<Token>],
params: &[String],
) -> Vec<Token> {
let mut result: Vec<Token> = Vec::new();
let mut i = 0;
let n = tokens.len();
while i < n {
if i + 1 < n && tokens[i + 1].kind == TokenKind::HashHash {
let prev = if i > 0 {
let idx = i - 1;
if idx < tokens.len() {
Some(tokens[idx].clone())
} else {
None
}
} else {
None
};
let next = if i + 2 < n {
Some(tokens[i + 2].clone())
} else {
None
};
if !result.is_empty() {
result.pop();
}
if let Some(pasted) = self.paste(prev.as_ref(), next.as_ref(), args, params) {
result.push(pasted);
}
i += 2; } else {
result.push(tokens[i].clone());
}
i += 1;
}
result
}
fn resolve_param(&self, token: &Token, args: &[Vec<Token>], params: &[String]) -> String {
if token.kind == TokenKind::Identifier {
if let Some(param_idx) = params.iter().position(|p| p == &token.text) {
if param_idx < args.len() {
return args[param_idx]
.iter()
.map(|t| t.text.clone())
.collect::<Vec<_>>()
.join("");
}
}
}
token.text.clone()
}
fn classify_pasted(&self, text: &str, reference_token: Option<&Token>) -> Option<Token> {
let location = reference_token
.map(|t| t.location)
.unwrap_or_else(SourceLoc::unknown);
if text.is_empty() {
return None;
}
if text.chars().next().map_or(false, |c| c.is_ascii_digit()) {
if text.chars().all(|c| {
c.is_ascii_digit()
|| c == '.'
|| c == 'x'
|| c == 'X'
|| c.is_ascii_hexdigit() && text.starts_with("0x")
|| text.starts_with("0X")
}) {
return Some(Token {
kind: TokenKind::NumericLiteral,
text: text.to_string(),
location,
flags: TokenFlags::none(),
});
}
}
if text.chars().next().map_or(false, |c| c.is_ascii_digit()) {
return Some(Token {
kind: TokenKind::NumericLiteral,
text: text.to_string(),
location,
flags: TokenFlags::none(),
});
}
if is_valid_c_ident(text) {
let kind = classify_pasted_identifier(text);
return Some(Token {
kind,
text: text.to_string(),
location,
flags: TokenFlags::none(),
});
}
let kind = classify_pasted_operator(text);
Some(Token {
kind,
text: text.to_string(),
location,
flags: TokenFlags::none(),
})
}
}
impl Default for X86TokenPasting {
fn default() -> Self {
Self::new()
}
}
fn is_valid_c_ident(s: &str) -> bool {
let mut chars = s.chars();
match chars.next() {
None => false,
Some(c) => {
if !c.is_ascii_alphabetic() && c != '_' {
return false;
}
chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
}
}
}
fn classify_pasted_identifier(text: &str) -> TokenKind {
match text {
"auto" => TokenKind::KwAuto,
"break" => TokenKind::KwBreak,
"case" => TokenKind::KwCase,
"char" => TokenKind::KwChar,
"const" => TokenKind::KwConst,
"continue" => TokenKind::KwContinue,
"default" => TokenKind::KwDefault,
"do" => TokenKind::KwDo,
"double" => TokenKind::KwDouble,
"else" => TokenKind::KwElse,
"enum" => TokenKind::KwEnum,
"extern" => TokenKind::KwExtern,
"float" => TokenKind::KwFloat,
"for" => TokenKind::KwFor,
"goto" => TokenKind::KwGoto,
"if" => TokenKind::KwIf,
"inline" => TokenKind::KwInline,
"int" => TokenKind::KwInt,
"long" => TokenKind::KwLong,
"register" => TokenKind::KwRegister,
"restrict" => TokenKind::KwRestrict,
"return" => TokenKind::KwReturn,
"short" => TokenKind::KwShort,
"signed" => TokenKind::KwSigned,
"sizeof" => TokenKind::KwSizeof,
"static" => TokenKind::KwStatic,
"struct" => TokenKind::KwStruct,
"switch" => TokenKind::KwSwitch,
"typedef" => TokenKind::KwTypedef,
"union" => TokenKind::KwUnion,
"unsigned" => TokenKind::KwUnsigned,
"void" => TokenKind::KwVoid,
"volatile" => TokenKind::KwVolatile,
"while" => TokenKind::KwWhile,
"bool" => TokenKind::KwBool,
"true" => TokenKind::KwTrue,
"false" => TokenKind::KwFalse,
_ => TokenKind::Identifier,
}
}
fn classify_pasted_operator(text: &str) -> TokenKind {
match text {
"+" => TokenKind::Plus,
"-" => TokenKind::Minus,
"*" => TokenKind::Star,
"/" => TokenKind::Slash,
"%" => TokenKind::Percent,
"&" => TokenKind::Ampersand,
"|" => TokenKind::Pipe,
"^" => TokenKind::Caret,
"~" => TokenKind::Tilde,
"!" => TokenKind::Exclaim,
"<" => TokenKind::Less,
">" => TokenKind::Greater,
"=" => TokenKind::Equal,
"." => TokenKind::Dot,
"," => TokenKind::Comma,
";" => TokenKind::Semicolon,
":" => TokenKind::Colon,
"?" => TokenKind::Question,
"#" => TokenKind::Hash,
"##" => TokenKind::HashHash,
"(" => TokenKind::LParen,
")" => TokenKind::RParen,
"{" => TokenKind::LBrace,
"}" => TokenKind::RBrace,
"[" => TokenKind::LBracket,
"]" => TokenKind::RBracket,
_ => TokenKind::Unknown,
}
}
#[derive(Debug, Clone)]
pub struct X86Stringification;
impl X86Stringification {
pub fn new() -> Self {
Self
}
pub fn stringify(&self, tokens: &[Token]) -> String {
let mut result = String::new();
for (i, token) in tokens.iter().enumerate() {
if i > 0 {
result.push(' ');
}
result.push_str(&self.escape_token(token));
}
format!("\"{}\"", result)
}
fn escape_token(&self, token: &Token) -> String {
let text = &token.text;
let mut escaped = String::new();
for ch in text.chars() {
match ch {
'\\' => escaped.push_str("\\\\"),
'"' => escaped.push_str("\\\""),
'\n' => escaped.push_str("\\n"),
'\t' => escaped.push_str("\\t"),
'\r' => escaped.push_str("\\r"),
'\0' => escaped.push_str("\\0"),
_ => escaped.push(ch),
}
}
escaped
}
pub fn stringify_or_empty(&self, tokens: &[Token]) -> String {
if tokens.is_empty() {
"\"\"".to_string()
} else {
self.stringify(tokens)
}
}
pub fn is_stringify_preamble(token: &Token, next: Option<&Token>, params: &[String]) -> bool {
token.kind == TokenKind::Hash
&& next.map_or(false, |t| {
t.kind == TokenKind::Identifier && params.contains(&t.text)
})
}
pub fn apply_stringification(
&self,
body_tokens: &[Token],
args: &[Vec<Token>],
params: &[String],
) -> Vec<Token> {
let mut result = Vec::new();
let mut i = 0;
let n = body_tokens.len();
while i < n {
let token = &body_tokens[i];
if token.kind == TokenKind::Hash && i + 1 < n {
let next = &body_tokens[i + 1];
if next.kind == TokenKind::Identifier {
if let Some(param_idx) = params.iter().position(|p| p == &next.text) {
if param_idx < args.len() {
let stringified = self.stringify(&args[param_idx]);
result.push(Token {
kind: TokenKind::StringLiteral,
text: stringified,
location: token.location,
flags: TokenFlags::none(),
});
i += 2;
continue;
}
}
}
}
result.push(token.clone());
i += 1;
}
result
}
}
impl Default for X86Stringification {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Default)]
pub struct X86LineDirective {
pub line_offset: Option<u32>,
pub file_override: Option<String>,
}
impl X86LineDirective {
pub fn new() -> Self {
Self {
line_offset: None,
file_override: None,
}
}
pub fn apply_line(&mut self, directive_text: &str) -> Result<(), String> {
let text = directive_text.trim();
let (num_part, file_part) = if let Some(idx) = text.find('"') {
let num_part = text[..idx].trim().to_string();
let rest = &text[idx..];
let file_part = if let Some(end) = rest[1..].find('"') {
Some(rest[1..=end].to_string())
} else {
return Err("missing closing '\"' in #line directive".to_string());
};
(num_part, file_part)
} else {
(text.to_string(), None)
};
let line_num: u32 = num_part
.trim()
.parse::<i64>()
.map(|n| n as u32)
.map_err(|_| format!("invalid line number in #line directive: '{}'", num_part))?;
if line_num == 0 {
return Err("line number must be positive in #line directive".to_string());
}
self.line_offset = Some(line_num);
if let Some(filename) = file_part {
self.file_override = Some(filename);
}
Ok(())
}
pub fn effective_line(&self, file_line: u32) -> u32 {
if let Some(base) = self.line_offset {
base
} else {
file_line
}
}
pub fn effective_file(&self, actual_file: &str) -> String {
if let Some(ref override_name) = self.file_override {
override_name.clone()
} else {
actual_file.to_string()
}
}
pub fn reset(&mut self) {
self.line_offset = None;
self.file_override = None;
}
pub fn advance_line(&mut self) {
if let Some(ref mut offset) = self.line_offset {
*offset += 1;
}
}
}
pub struct X86Preprocessor {
pub builtins: X86BuiltinMacros,
pub expander: X86MacroExpander,
pub include_resolver: X86IncludeResolver,
pub pragma_handler: X86PragmaHandler,
pub conditional: X86ConditionalInclusion,
pub token_paster: X86TokenPasting,
pub stringifier: X86Stringification,
pub line_directive: X86LineDirective,
pub standard: CLangStandard,
pub is_cxx: bool,
pub base: Preprocessor,
pub current_file: Option<PathBuf>,
pub current_line: u32,
pub skipping: bool,
cond_stack: Vec<ConditionalBlockState>,
pub errors: Vec<String>,
pub warnings: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ConditionalBlockState {
Active,
Skipping,
SeenElse,
}
impl X86Preprocessor {
pub fn new() -> Self {
let builtins = X86BuiltinMacros::new();
let conditional = X86ConditionalInclusion::new(&builtins);
let mut expander = X86MacroExpander::with_builtins(builtins.clone());
expander.load_builtins();
let mut base = Preprocessor::new(CLangStandard::C17);
for (name, value) in builtins.generate_all() {
base.define(&name, &value);
}
Self {
builtins,
expander,
include_resolver: X86IncludeResolver::new(),
pragma_handler: X86PragmaHandler::new(),
conditional,
token_paster: X86TokenPasting::new(),
stringifier: X86Stringification::new(),
line_directive: X86LineDirective::new(),
standard: CLangStandard::C17,
is_cxx: false,
base,
current_file: None,
current_line: 1,
skipping: false,
cond_stack: Vec::new(),
errors: Vec::new(),
warnings: Vec::new(),
}
}
pub fn for_target(triple: &str) -> Self {
let builtins = X86BuiltinMacros::from_triple(triple);
let conditional = X86ConditionalInclusion::new(&builtins);
let mut expander = X86MacroExpander::with_builtins(builtins.clone());
expander.load_builtins();
let include_resolver = X86IncludeResolver::for_target(triple);
let mut base = Preprocessor::new(CLangStandard::C17);
for (name, value) in builtins.generate_all() {
base.define(&name, &value);
}
Self {
builtins,
expander,
include_resolver,
pragma_handler: X86PragmaHandler::new(),
conditional,
token_paster: X86TokenPasting::new(),
stringifier: X86Stringification::new(),
line_directive: X86LineDirective::new(),
standard: CLangStandard::C17,
is_cxx: false,
base,
current_file: None,
current_line: 1,
skipping: false,
cond_stack: Vec::new(),
errors: Vec::new(),
warnings: Vec::new(),
}
}
pub fn set_target(&mut self, triple: &str) {
self.builtins = X86BuiltinMacros::from_triple(triple);
self.conditional = X86ConditionalInclusion::new(&self.builtins);
self.include_resolver = X86IncludeResolver::for_target(triple);
self.expander = X86MacroExpander::with_builtins(self.builtins.clone());
self.expander.load_builtins();
self.base = Preprocessor::new(self.standard);
for (name, value) in self.builtins.generate_all() {
self.base.define(&name, &value);
}
}
pub fn add_define(&mut self, name: &str, value: &str) {
self.base.define(name, value);
self.expander.define(name, value);
self.conditional.add_define(name, value);
}
pub fn undefine(&mut self, name: &str) {
self.base.defines.remove(name);
self.expander.undefine(name);
self.conditional.remove_define(name);
}
pub fn add_include_path(&mut self, path: &Path) {
self.include_resolver.add_user_path(path);
self.base.add_include_path(path.to_string_lossy().as_ref());
}
pub fn add_system_include_path(&mut self, path: &Path) {
self.include_resolver.add_system_path(path);
}
pub fn set_standard(&mut self, standard: CLangStandard) {
self.standard = standard;
self.builtins.standard = standard;
self.conditional = X86ConditionalInclusion::new(&self.builtins);
}
pub fn enable_feature(&mut self, feature: X86Feature) {
self.builtins.enable_feature(feature);
self.conditional = X86ConditionalInclusion::new(&self.builtins);
}
pub fn disable_feature(&mut self, feature: X86Feature) {
self.builtins.disable_feature(feature);
self.conditional = X86ConditionalInclusion::new(&self.builtins);
}
pub fn set_cpu(&mut self, cpu_name: &str) {
let mut predefines = X86Predefines::from_triple("x86_64-unknown-linux-gnu");
predefines.set_cpu(cpu_name);
self.builtins.features = predefines.cpu_features;
self.builtins.microarch_level = predefines.microarch_level;
self.conditional = X86ConditionalInclusion::new(&self.builtins);
}
pub fn process_source(&mut self, source: &str, filename: &str) -> Vec<Token> {
self.current_file = Some(PathBuf::from(filename));
self.current_line = 1;
self.skipping = false;
self.cond_stack.clear();
self.errors.clear();
self.warnings.clear();
let mut lexer = Lexer::new(source, self.standard);
let tokens = lexer.lex_all();
self.process_tokens(&tokens)
}
fn process_tokens(&mut self, tokens: &[Token]) -> Vec<Token> {
let mut result = Vec::new();
let mut i = 0;
let n = tokens.len();
while i < n {
let token = &tokens[i];
if token.kind == TokenKind::Newline {
self.current_line += 1;
if self.skipping {
result.push(token.clone());
i += 1;
continue;
}
}
if token.kind == TokenKind::Hash && (i == 0 || is_at_start_of_logical_line(&tokens, i))
{
let directive_result = self.handle_directive(tokens, &mut i);
if let Some(directive_tokens) = directive_result {
result.extend(directive_tokens);
}
continue;
}
if self.skipping {
i += 1;
continue;
}
result.push(token.clone());
i += 1;
}
let expanded = self.expander.expand(
&result,
self.current_file.as_ref().and_then(|p| p.to_str()),
self.current_line,
);
expanded
}
fn handle_directive(&mut self, tokens: &[Token], idx: &mut usize) -> Option<Vec<Token>> {
let mut i = *idx + 1;
while i < tokens.len() && is_pp_whitespace(&tokens[i]) {
i += 1;
}
if i >= tokens.len() {
return None;
}
let dir_token = &tokens[i];
if dir_token.kind != TokenKind::Identifier {
return None;
}
let directive = dir_token.text.as_str();
let mut line_tokens = Vec::new();
i += 1;
while i < tokens.len()
&& tokens[i].kind != TokenKind::Newline
&& tokens[i].kind != TokenKind::Eof
{
line_tokens.push(tokens[i].clone());
i += 1;
}
if i < tokens.len() && tokens[i].kind == TokenKind::Newline {
i += 1;
}
*idx = i;
match directive {
"include" => self.handle_include(&line_tokens),
"define" => {
self.handle_define(&line_tokens);
None
}
"undef" => {
self.handle_undef(&line_tokens);
None
}
"if" => self.handle_if_condition(&line_tokens),
"ifdef" => self.handle_ifdef_condition(&line_tokens, false),
"ifndef" => self.handle_ifdef_condition(&line_tokens, true),
"elif" => {
self.handle_elif(&line_tokens);
None
}
"else" => {
self.handle_else();
None
}
"endif" => {
self.handle_endif();
None
}
"error" => {
let msg = line_tokens
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
self.errors.push(format!("#error: {}", msg));
None
}
"warning" => {
let msg = line_tokens
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
self.warnings.push(format!("#warning: {}", msg));
None
}
"pragma" => {
self.handle_pragma(&line_tokens);
None
}
"line" => {
self.handle_line(&line_tokens);
None
}
_ => None,
}
}
fn handle_include(&mut self, tokens: &[Token]) -> Option<Vec<Token>> {
let header = tokens
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join("")
.trim_matches('"')
.trim_matches('<')
.trim_matches('>')
.trim_matches('"')
.to_string();
let is_system = tokens
.iter()
.any(|t| t.text.starts_with('<') || t.text.ends_with('>'));
let resolved = if is_system {
self.include_resolver.resolve_system_include(&header)
} else {
self.include_resolver
.resolve_quoted_include(&header, self.current_file.as_deref())
};
if let Some(path) = resolved {
if let Ok(contents) = std::fs::read_to_string(&path) {
let mut lexer = Lexer::new(&contents, self.standard);
let included_tokens = lexer.lex_all();
let expanded =
self.expander
.expand(&included_tokens, Some(&path.to_string_lossy()), 1);
return Some(expanded);
} else {
self.errors.push(format!(
"cannot open include file '{}': file not found",
header
));
}
} else {
self.errors.push(format!("'{}' file not found", header));
}
None
}
fn handle_define(&mut self, tokens: &[Token]) {
if tokens.is_empty() {
return;
}
let name = &tokens[0].text;
let value: String = tokens
.iter()
.skip(1)
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
self.add_define(name, &value);
}
fn handle_undef(&mut self, tokens: &[Token]) {
if let Some(token) = tokens.first() {
self.undefine(&token.text);
}
}
fn handle_if_condition(&mut self, tokens: &[Token]) -> Option<Vec<Token>> {
let expr = tokens
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
let result = self.conditional.evaluate_condition(&expr);
match result {
Some(true) => {
if !self.skipping {
self.cond_stack.push(ConditionalBlockState::Active);
} else {
self.cond_stack.push(ConditionalBlockState::Skipping);
}
None
}
Some(false) => {
self.cond_stack.push(ConditionalBlockState::Skipping);
self.skipping = true;
None
}
None => {
if !self.skipping {
self.cond_stack.push(ConditionalBlockState::Active);
} else {
self.cond_stack.push(ConditionalBlockState::Skipping);
}
self.errors
.push(format!("could not evaluate #if condition: '{}'", expr));
None
}
}
}
fn handle_ifdef_condition(&mut self, tokens: &[Token], is_ifndef: bool) -> Option<Vec<Token>> {
if let Some(token) = tokens.first() {
let defined = self.conditional.is_defined(&token.text);
let should_activate = if is_ifndef { !defined } else { defined };
if !self.skipping {
if should_activate {
self.cond_stack.push(ConditionalBlockState::Active);
} else {
self.cond_stack.push(ConditionalBlockState::Skipping);
self.skipping = true;
}
} else {
self.cond_stack.push(ConditionalBlockState::Skipping);
}
}
None
}
fn handle_elif(&mut self, tokens: &[Token]) {
if let Some(state) = self.cond_stack.last().cloned() {
match state {
ConditionalBlockState::Active => {
self.skipping = true;
}
ConditionalBlockState::Skipping => {
let expr = tokens
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
let result = self.conditional.evaluate_condition(&expr);
if result == Some(true) {
self.skipping = false;
if let Some(last) = self.cond_stack.last_mut() {
*last = ConditionalBlockState::Active;
}
}
}
ConditionalBlockState::SeenElse => {
self.errors.push("#elif after #else is invalid".to_string());
}
}
}
}
fn handle_else(&mut self) {
if let Some(state) = self.cond_stack.last().cloned() {
match state {
ConditionalBlockState::Active => {
self.cond_stack.pop();
self.cond_stack.push(ConditionalBlockState::SeenElse);
self.skipping = true;
}
ConditionalBlockState::Skipping => {
self.cond_stack.pop();
self.cond_stack.push(ConditionalBlockState::SeenElse);
self.skipping = false;
}
ConditionalBlockState::SeenElse => {
self.errors.push("#else after #else is invalid".to_string());
}
}
}
}
fn handle_endif(&mut self) {
self.cond_stack.pop();
if self.cond_stack.is_empty() {
self.skipping = false;
} else {
let mut skipping = false;
for state in self.cond_stack.iter().rev() {
if *state != ConditionalBlockState::Active {
skipping = true;
break;
}
}
self.skipping = skipping;
}
}
fn handle_pragma(&mut self, tokens: &[Token]) {
let directive = tokens
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
let pragma_kind = self.pragma_handler.parse(&directive);
self.pragma_handler.handle(&pragma_kind);
if pragma_kind == X86PragmaKind::Once {
if let Some(ref current_file) = self.current_file {
self.include_resolver.mark_pragma_once(current_file);
}
}
}
fn handle_line(&mut self, tokens: &[Token]) {
let directive = tokens
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
if let Err(e) = self.line_directive.apply_line(&directive) {
self.errors.push(e);
}
}
pub fn effective_line(&self) -> u32 {
self.line_directive.effective_line(self.current_line)
}
pub fn effective_file(&self) -> String {
let actual = self
.current_file
.as_ref()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|| "<unknown>".to_string());
self.line_directive.effective_file(&actual)
}
pub fn should_skip(&self) -> bool {
self.skipping
}
pub fn cond_depth(&self) -> usize {
self.cond_stack.len()
}
pub fn take_errors(&mut self) -> Vec<String> {
std::mem::take(&mut self.errors)
}
pub fn take_warnings(&mut self) -> Vec<String> {
std::mem::take(&mut self.warnings)
}
}
impl Default for X86Preprocessor {
fn default() -> Self {
Self::new()
}
}
fn is_pp_whitespace(token: &Token) -> bool {
token.kind == TokenKind::Newline || token.kind == TokenKind::Comment
}
fn is_at_start_of_logical_line(tokens: &[Token], idx: usize) -> bool {
idx == 0 || tokens[idx - 1].kind == TokenKind::Newline
}
#[derive(Debug, Clone)]
pub struct DirectiveInput {
pub keyword: String,
pub arguments: Vec<Token>,
pub line: u32,
pub file: Option<String>,
pub in_skipped_block: bool,
}
#[derive(Debug, Clone)]
pub struct DirectiveOutput {
pub emitted_tokens: Vec<Token>,
pub state_changed: bool,
pub new_skip_state: bool,
pub diagnostics: Vec<(DiagnosticLevel, String)>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiagnosticLevel {
Note,
Warning,
Error,
Fatal,
}
#[derive(Debug)]
pub struct X86DirectiveProcessor {
pub expand_in_directives: bool,
pub max_include_depth: u32,
pub current_include_depth: u32,
pub warn_unknown_pragmas: bool,
pub warn_undef: bool,
pub warn_macro_redefined: bool,
}
impl X86DirectiveProcessor {
pub fn new() -> Self {
Self {
expand_in_directives: true,
max_include_depth: 200,
current_include_depth: 0,
warn_unknown_pragmas: false,
warn_undef: false,
warn_macro_redefined: true,
}
}
pub fn process(
&mut self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
included_files: &mut Vec<PathBuf>,
pragma_once_set: &mut HashSet<String>,
) -> DirectiveOutput {
let mut output = DirectiveOutput {
emitted_tokens: Vec::new(),
state_changed: false,
new_skip_state: pp.skipping,
diagnostics: Vec::new(),
};
if input.in_skipped_block {
match input.keyword.as_str() {
"if" | "ifdef" | "ifndef" => {
pp.cond_stack.push(ConditionalBlockState::Skipping);
output.state_changed = true;
}
"elif" => {
self.process_elif_in_skipped(input, pp, &mut output);
}
"else" => {
self.process_else_in_skipped(input, pp, &mut output);
}
"endif" => {
self.process_endif_in_skipped(input, pp, &mut output);
}
_ => {
}
}
return output;
}
match input.keyword.as_str() {
"include" => {
self.process_include(input, pp, included_files, pragma_once_set, &mut output);
}
"define" => {
self.process_define(input, pp, &mut output);
}
"undef" => {
self.process_undef(input, pp, &mut output);
}
"if" => {
self.process_if(input, pp, &mut output);
}
"ifdef" => {
self.process_ifdef(input, pp, false, &mut output);
}
"ifndef" => {
self.process_ifdef(input, pp, true, &mut output);
}
"elif" => {
self.process_elif(input, pp, &mut output);
}
"else" => {
self.process_else(input, pp, &mut output);
}
"endif" => {
self.process_endif(input, pp, &mut output);
}
"error" => {
let msg = input
.arguments
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
output
.diagnostics
.push((DiagnosticLevel::Error, format!("#error directive: {}", msg)));
}
"warning" => {
let msg = input
.arguments
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
output.diagnostics.push((
DiagnosticLevel::Warning,
format!("#warning directive: {}", msg),
));
}
"pragma" => {
self.process_pragma(input, pp, &mut output);
}
"line" => {
self.process_line(input, pp, &mut output);
}
"include_next" => {
self.process_include_next(input, pp, included_files, &mut output);
}
"ident" => {
let ident_text = input
.arguments
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
output
.diagnostics
.push((DiagnosticLevel::Note, format!("#ident: {}", ident_text)));
}
"sccs" => {
}
"assert" => {
self.process_assert(input, pp, &mut output);
}
"unassert" => {
let predicate = input
.arguments
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
output
.diagnostics
.push((DiagnosticLevel::Note, format!("#unassert: {}", predicate)));
}
_ => {
if self.warn_unknown_pragmas {
output.diagnostics.push((
DiagnosticLevel::Warning,
format!("unknown preprocessor directive: #{}", input.keyword),
));
}
}
}
output
}
fn process_include(
&mut self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
included_files: &mut Vec<PathBuf>,
pragma_once_set: &mut HashSet<String>,
output: &mut DirectiveOutput,
) {
let include_text = input
.arguments
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join("");
let (header_name, is_system) =
if include_text.starts_with('<') && include_text.ends_with('>') {
(
include_text[1..include_text.len().saturating_sub(1)].to_string(),
true,
)
} else if include_text.starts_with('"') && include_text.ends_with('"') {
(
include_text[1..include_text.len().saturating_sub(1)].to_string(),
false,
)
} else {
(
include_text
.trim_matches('"')
.trim_matches('<')
.trim_matches('>')
.trim_matches('"')
.to_string(),
false,
)
};
if self.current_include_depth >= self.max_include_depth {
output.diagnostics.push((
DiagnosticLevel::Fatal,
format!(
"#include nested too deeply (max depth: {})",
self.max_include_depth
),
));
return;
}
let resolved = if is_system {
pp.include_resolver.resolve_system_include(&header_name)
} else {
pp.include_resolver
.resolve_quoted_include(&header_name, pp.current_file.as_deref())
};
match resolved {
Some(path) => {
if pragma_once_set.contains(&path.to_string_lossy().to_string()) {
return;
}
included_files.push(path.clone());
if let Ok(contents) = std::fs::read_to_string(&path) {
self.current_include_depth += 1;
let prev_file = pp.current_file.clone();
let prev_line = pp.current_line;
pp.current_file = Some(path);
pp.current_line = 1;
let mut lexer = Lexer::new(&contents, pp.standard);
let included_tokens = lexer.lex_all();
let expanded = pp.expander.expand(
&included_tokens,
Some(&pp.current_file.as_ref().unwrap().to_string_lossy()),
1,
);
output.emitted_tokens = expanded;
pp.current_file = prev_file;
pp.current_line = prev_line;
self.current_include_depth -= 1;
} else {
output.diagnostics.push((
DiagnosticLevel::Error,
format!(
"cannot open include file '{}': {}",
header_name, "No such file or directory"
),
));
}
}
None => {
output.diagnostics.push((
DiagnosticLevel::Error,
format!("'{}' file not found", header_name),
));
}
}
}
fn process_include_next(
&mut self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
included_files: &mut Vec<PathBuf>,
output: &mut DirectiveOutput,
) {
let include_text = input
.arguments
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join("");
let header_name = include_text
.trim_matches('"')
.trim_matches('<')
.trim_matches('>')
.trim_matches('"')
.to_string();
let current_dir = pp
.current_file
.as_ref()
.and_then(|p| p.parent())
.map(|p| p.to_path_buf());
let found = pp
.include_resolver
.search_paths
.iter()
.filter(|sp| current_dir.as_ref().map_or(true, |cd| sp.path != *cd))
.map(|sp| sp.path.join(&header_name))
.find(|p| p.exists());
match found {
Some(path) => {
included_files.push(path.clone());
if let Ok(contents) = std::fs::read_to_string(&path) {
let mut lexer = Lexer::new(&contents, pp.standard);
let tokens = lexer.lex_all();
let expanded = pp
.expander
.expand(&tokens, Some(&path.to_string_lossy()), 1);
output.emitted_tokens = expanded;
}
}
None => {
output.diagnostics.push((
DiagnosticLevel::Error,
format!("'{}' file not found (via #include_next)", header_name),
));
}
}
}
fn process_define(
&self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
output: &mut DirectiveOutput,
) {
if input.arguments.is_empty() {
output.diagnostics.push((
DiagnosticLevel::Error,
"#define requires a macro name".into(),
));
return;
}
let name = &input.arguments[0].text;
let is_function_like = input.arguments.len() > 1 && input.arguments[0].text.ends_with(')');
let value = if input.arguments.len() > 1 {
input.arguments[1..]
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ")
} else {
String::new()
};
if self.warn_macro_redefined && pp.expander.defines.contains_key(name) {
let existing = &pp.expander.defines[name];
let existing_value = existing
.body
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join("");
if existing_value != value {
output.diagnostics.push((
DiagnosticLevel::Warning,
format!("macro '{}' redefined", name),
));
}
}
pp.add_define(name, &value);
}
fn process_undef(
&self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
output: &mut DirectiveOutput,
) {
if let Some(token) = input.arguments.first() {
let name = &token.text;
if !pp.expander.defines.contains_key(name) && self.warn_undef {
output.diagnostics.push((
DiagnosticLevel::Warning,
format!("macro '{}' is not defined", name),
));
}
pp.undefine(name);
} else {
output.diagnostics.push((
DiagnosticLevel::Error,
"#undef requires a macro name".into(),
));
}
}
fn process_if(
&self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
output: &mut DirectiveOutput,
) {
let expr = input
.arguments
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
let result = pp.conditional.evaluate_condition(&expr);
match result {
Some(true) => {
pp.cond_stack.push(ConditionalBlockState::Active);
output.state_changed = true;
output.new_skip_state = false;
}
Some(false) => {
pp.cond_stack.push(ConditionalBlockState::Skipping);
pp.skipping = true;
output.state_changed = true;
output.new_skip_state = true;
}
None => {
pp.cond_stack.push(ConditionalBlockState::Active);
output.diagnostics.push((
DiagnosticLevel::Warning,
format!("could not fully evaluate #if condition: '{}'", expr),
));
output.state_changed = true;
output.new_skip_state = false;
}
}
}
fn process_ifdef(
&self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
is_ifndef: bool,
output: &mut DirectiveOutput,
) {
if let Some(token) = input.arguments.first() {
let defined = pp.conditional.is_defined(&token.text);
let should_enter = if is_ifndef { !defined } else { defined };
if should_enter {
pp.cond_stack.push(ConditionalBlockState::Active);
output.state_changed = true;
output.new_skip_state = false;
} else {
pp.cond_stack.push(ConditionalBlockState::Skipping);
pp.skipping = true;
output.state_changed = true;
output.new_skip_state = true;
}
}
}
fn process_elif(
&self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
output: &mut DirectiveOutput,
) {
match pp.cond_stack.last().cloned() {
Some(ConditionalBlockState::Active) => {
pp.skipping = true;
output.new_skip_state = true;
}
Some(ConditionalBlockState::Skipping) => {
let expr = input
.arguments
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
let result = pp.conditional.evaluate_condition(&expr);
if result == Some(true) {
pp.skipping = false;
output.new_skip_state = false;
if let Some(last) = pp.cond_stack.last_mut() {
*last = ConditionalBlockState::Active;
}
}
}
Some(ConditionalBlockState::SeenElse) => {
output.diagnostics.push((
DiagnosticLevel::Error,
"#elif after #else is invalid".into(),
));
}
None => {
output
.diagnostics
.push((DiagnosticLevel::Error, "#elif without #if".into()));
}
}
}
fn process_else(
&self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
output: &mut DirectiveOutput,
) {
let _ = input; match pp.cond_stack.last().cloned() {
Some(ConditionalBlockState::Active) => {
pp.cond_stack.pop();
pp.cond_stack.push(ConditionalBlockState::SeenElse);
pp.skipping = true;
output.new_skip_state = true;
}
Some(ConditionalBlockState::Skipping) => {
pp.cond_stack.pop();
pp.cond_stack.push(ConditionalBlockState::SeenElse);
pp.skipping = false;
output.new_skip_state = false;
}
Some(ConditionalBlockState::SeenElse) => {
output.diagnostics.push((
DiagnosticLevel::Error,
"#else after #else is invalid".into(),
));
}
None => {
output
.diagnostics
.push((DiagnosticLevel::Error, "#else without #if".into()));
}
}
}
fn process_endif(
&self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
output: &mut DirectiveOutput,
) {
let _ = input;
if pp.cond_stack.pop().is_none() {
output
.diagnostics
.push((DiagnosticLevel::Error, "#endif without #if".into()));
}
if pp.cond_stack.is_empty() {
pp.skipping = false;
output.new_skip_state = false;
} else {
let mut skipping = false;
for state in pp.cond_stack.iter().rev() {
if *state != ConditionalBlockState::Active {
skipping = true;
break;
}
}
pp.skipping = skipping;
output.new_skip_state = skipping;
}
}
fn process_elif_in_skipped(
&self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
output: &mut DirectiveOutput,
) {
let _ = input;
if let Some(last) = pp.cond_stack.last_mut() {
if *last != ConditionalBlockState::SeenElse {
}
}
output.new_skip_state = true;
}
fn process_else_in_skipped(
&self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
output: &mut DirectiveOutput,
) {
let _ = input;
if let Some(last) = pp.cond_stack.last().cloned() {
if last == ConditionalBlockState::Skipping {
if pp.cond_stack.len() == 1 {
pp.cond_stack.pop();
pp.cond_stack.push(ConditionalBlockState::SeenElse);
pp.skipping = false;
output.new_skip_state = false;
output.state_changed = true;
}
}
}
}
fn process_endif_in_skipped(
&self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
output: &mut DirectiveOutput,
) {
let _ = input;
pp.cond_stack.pop();
if pp.cond_stack.is_empty() {
pp.skipping = false;
output.new_skip_state = false;
output.state_changed = true;
} else {
let mut skipping = false;
for state in pp.cond_stack.iter().rev() {
if *state != ConditionalBlockState::Active {
skipping = true;
break;
}
}
pp.skipping = skipping;
output.new_skip_state = skipping;
}
}
fn process_pragma(
&self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
output: &mut DirectiveOutput,
) {
let directive = input
.arguments
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
let pragma_kind = pp.pragma_handler.parse(&directive);
match &pragma_kind {
X86PragmaKind::Unknown(s) => {
if self.warn_unknown_pragmas {
output.diagnostics.push((
DiagnosticLevel::Warning,
format!("unknown pragma ignored: '{}'", s),
));
}
}
X86PragmaKind::Once => {
if let Some(ref current_file) = pp.current_file {
pp.include_resolver.mark_pragma_once(current_file);
}
}
X86PragmaKind::Message(_) => {
}
_ => {}
}
pp.pragma_handler.handle(&pragma_kind);
}
fn process_line(
&self,
input: &DirectiveInput,
pp: &mut X86Preprocessor,
output: &mut DirectiveOutput,
) {
let directive = input
.arguments
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
if let Err(e) = pp.line_directive.apply_line(&directive) {
output.diagnostics.push((
DiagnosticLevel::Error,
format!("invalid #line directive: {}", e),
));
}
}
fn process_assert(
&self,
input: &DirectiveInput,
_pp: &mut X86Preprocessor,
output: &mut DirectiveOutput,
) {
let predicate = input
.arguments
.iter()
.map(|t| t.text.as_str())
.collect::<Vec<_>>()
.join(" ");
output
.diagnostics
.push((DiagnosticLevel::Note, format!("#assert: {}", predicate)));
}
}
impl Default for X86DirectiveProcessor {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86PreprocessorBuilder {
target_triple: String,
cpu_name: Option<String>,
standard: CLangStandard,
is_cxx: bool,
pic: bool,
pie: bool,
fast_math: bool,
finite_math_only: bool,
defines: Vec<(String, String)>,
include_paths: Vec<PathBuf>,
system_include_paths: Vec<PathBuf>,
sysroot: Option<PathBuf>,
resource_dir: Option<PathBuf>,
features: Vec<X86Feature>,
disabled_features: Vec<X86Feature>,
microarch_level: X86MicroarchLevel,
cxx_version: Option<u32>,
}
impl X86PreprocessorBuilder {
pub fn new() -> Self {
Self {
target_triple: "x86_64-unknown-linux-gnu".into(),
cpu_name: None,
standard: CLangStandard::C17,
is_cxx: false,
pic: false,
pie: false,
fast_math: false,
finite_math_only: false,
defines: Vec::new(),
include_paths: Vec::new(),
system_include_paths: Vec::new(),
sysroot: None,
resource_dir: None,
features: Vec::new(),
disabled_features: Vec::new(),
microarch_level: X86MicroarchLevel::Baseline,
cxx_version: None,
}
}
pub fn target(mut self, triple: &str) -> Self {
self.target_triple = triple.to_string();
self
}
pub fn cpu(mut self, cpu_name: &str) -> Self {
self.cpu_name = Some(cpu_name.to_string());
self
}
pub fn standard(mut self, standard: CLangStandard) -> Self {
self.standard = standard;
self
}
pub fn cxx(mut self) -> Self {
self.is_cxx = true;
self
}
pub fn cxx_version(mut self, version: u32) -> Self {
self.cxx_version = Some(version);
self.is_cxx = true;
self
}
pub fn pic(mut self) -> Self {
self.pic = true;
self
}
pub fn pie(mut self) -> Self {
self.pie = true;
self
}
pub fn fast_math(mut self) -> Self {
self.fast_math = true;
self
}
pub fn finite_math_only(mut self) -> Self {
self.finite_math_only = true;
self
}
pub fn define(mut self, name: &str, value: &str) -> Self {
self.defines.push((name.to_string(), value.to_string()));
self
}
pub fn include_dir(mut self, path: &str) -> Self {
self.include_paths.push(PathBuf::from(path));
self
}
pub fn system_include_dir(mut self, path: &str) -> Self {
self.system_include_paths.push(PathBuf::from(path));
self
}
pub fn sysroot(mut self, path: &str) -> Self {
self.sysroot = Some(PathBuf::from(path));
self
}
pub fn resource_dir(mut self, path: &str) -> Self {
self.resource_dir = Some(PathBuf::from(path));
self
}
pub fn feature(mut self, feature: X86Feature) -> Self {
self.features.push(feature);
self
}
pub fn disable_feature(mut self, feature: X86Feature) -> Self {
self.disabled_features.push(feature);
self
}
pub fn microarch_level(mut self, level: X86MicroarchLevel) -> Self {
self.microarch_level = level;
self
}
pub fn build(self) -> X86Preprocessor {
let mut pp = X86Preprocessor::for_target(&self.target_triple);
pp.standard = self.standard;
pp.is_cxx = self.is_cxx;
pp.builtins.standard = self.standard;
pp.builtins.is_cxx = self.is_cxx;
pp.builtins.is_pic = self.pic;
pp.builtins.is_pie = self.pie;
pp.builtins.fast_math = self.fast_math;
pp.builtins.finite_math_only = self.finite_math_only;
if let Some(ver) = self.cxx_version {
pp.builtins.cxx_version = Some(ver);
}
if let Some(ref cpu) = self.cpu_name {
pp.set_cpu(cpu);
}
pp.builtins.set_microarch_level(self.microarch_level);
for feature in &self.features {
pp.enable_feature(*feature);
}
for feature in &self.disabled_features {
pp.disable_feature(*feature);
}
for (name, value) in &self.defines {
pp.add_define(name, value);
}
for path in &self.include_paths {
pp.add_include_path(path);
}
for path in &self.system_include_paths {
pp.add_system_include_path(path);
}
if let Some(ref sysroot) = self.sysroot {
pp.include_resolver.set_sysroot(sysroot);
}
if let Some(ref res_dir) = self.resource_dir {
pp.include_resolver.set_resource_dir(res_dir);
}
pp.conditional = X86ConditionalInclusion::new(&pp.builtins);
pp
}
}
impl Default for X86PreprocessorBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::Path;
fn make_token(text: &str, kind: TokenKind) -> Token {
Token {
kind,
text: text.to_string(),
location: SourceLoc::unknown(),
flags: TokenFlags::none(),
}
}
fn ident(name: &str) -> Token {
make_token(name, TokenKind::Identifier)
}
fn num(val: &str) -> Token {
make_token(val, TokenKind::NumericLiteral)
}
fn hash() -> Token {
make_token("#", TokenKind::Hash)
}
fn newline() -> Token {
make_token("\n", TokenKind::Newline)
}
#[test]
fn test_builtin_macros_default() {
let builtins = X86BuiltinMacros::new();
assert_eq!(builtins.arch, "x86_64");
assert!(builtins.is_64bit);
assert_eq!(builtins.os, X86TargetOS::Linux);
}
#[test]
fn test_builtin_macros_from_triple_x86_64() {
let builtins = X86BuiltinMacros::from_triple("x86_64-unknown-linux-gnu");
assert!(builtins.is_64bit);
assert!(builtins.has_feature(&X86Feature::SSE2));
}
#[test]
fn test_builtin_macros_from_triple_i386() {
let builtins = X86BuiltinMacros::from_triple("i386-unknown-linux-gnu");
assert!(!builtins.is_64bit);
assert!(!builtins.has_feature(&X86Feature::SSE));
}
#[test]
fn test_builtin_macros_generate_arch() {
let builtins = X86BuiltinMacros::new();
let macros = builtins.generate_map();
assert_eq!(macros.get("__x86_64__").map(|s| s.as_str()), Some("1"));
assert_eq!(macros.get("__LP64__").map(|s| s.as_str()), Some("1"));
assert_eq!(macros.get("__x86__").map(|s| s.as_str()), Some("1"));
}
#[test]
fn test_builtin_macros_generate_features() {
let mut builtins = X86BuiltinMacros::new();
let macros = builtins.generate_map();
assert_eq!(macros.get("__SSE__").map(|s| s.as_str()), Some("1"));
assert_eq!(macros.get("__SSE2__").map(|s| s.as_str()), Some("1"));
assert_eq!(macros.get("__MMX__").map(|s| s.as_str()), Some("1"));
assert_eq!(macros.get("__AVX__"), None);
builtins.enable_feature(X86Feature::AVX);
let macros2 = builtins.generate_map();
assert_eq!(macros2.get("__AVX__").map(|s| s.as_str()), Some("1"));
}
#[test]
fn test_builtin_macros_feature_implies() {
let mut builtins = X86BuiltinMacros::from_triple("i386-unknown-linux-gnu");
assert!(!builtins.has_feature(&X86Feature::SSE));
assert!(!builtins.has_feature(&X86Feature::SSE2));
builtins.enable_feature(X86Feature::SSE2);
assert!(builtins.has_feature(&X86Feature::SSE2));
assert!(builtins.has_feature(&X86Feature::SSE));
assert!(builtins.has_feature(&X86Feature::MMX));
}
#[test]
fn test_builtin_macros_os_linux() {
let builtins = X86BuiltinMacros::from_triple("x86_64-unknown-linux-gnu");
let macros = builtins.generate_map();
assert_eq!(macros.get("__linux__").map(|s| s.as_str()), Some("1"));
assert_eq!(macros.get("__unix__").map(|s| s.as_str()), Some("1"));
assert_eq!(macros.get("__gnu_linux__").map(|s| s.as_str()), Some("1"));
assert_eq!(macros.get("__ELF__").map(|s| s.as_str()), Some("1"));
}
#[test]
fn test_builtin_macros_os_darwin() {
let builtins = X86BuiltinMacros::from_triple("x86_64-apple-darwin");
let macros = builtins.generate_map();
assert_eq!(macros.get("__APPLE__").map(|s| s.as_str()), Some("1"));
assert_eq!(macros.get("__MACH__").map(|s| s.as_str()), Some("1"));
assert_eq!(macros.get("__unix__").map(|s| s.as_str()), Some("1"));
}
#[test]
fn test_builtin_macros_os_windows_msvc() {
let builtins = X86BuiltinMacros::from_triple("x86_64-pc-windows-msvc");
let macros = builtins.generate_map();
assert_eq!(macros.get("_WIN32").map(|s| s.as_str()), Some("1"));
assert_eq!(macros.get("_WIN64").map(|s| s.as_str()), Some("1"));
assert!(macros.contains_key("_MSC_VER"));
}
#[test]
fn test_builtin_macros_compiler_version() {
let builtins = X86BuiltinMacros::new();
let macros = builtins.generate_map();
assert_eq!(macros.get("__GNUC__").map(|s| s.as_str()), Some("4"));
assert_eq!(macros.get("__GNUC_MINOR__").map(|s| s.as_str()), Some("2"));
assert_eq!(
macros.get("__GNUC_PATCHLEVEL__").map(|s| s.as_str()),
Some("1")
);
assert_eq!(macros.get("__clang__").map(|s| s.as_str()), Some("1"));
}
#[test]
fn test_builtin_macros_language_c() {
let mut builtins = X86BuiltinMacros::new();
builtins.is_cxx = false;
let macros = builtins.generate_map();
assert_eq!(macros.get("__STDC__").map(|s| s.as_str()), Some("1"));
assert_eq!(
macros.get("__STDC_VERSION__").map(|s| s.as_str()),
Some("201710L")
);
}
#[test]
fn test_builtin_macros_language_cxx() {
let mut builtins = X86BuiltinMacros::new();
builtins.is_cxx = true;
builtins.cxx_version = Some(201703);
let macros = builtins.generate_map();
assert!(macros.contains_key("__cplusplus"));
assert_eq!(
macros.get("__cplusplus").map(|s| s.as_str()),
Some("201703")
);
assert!(macros.contains_key("__GXX_ABI_VERSION"));
}
#[test]
fn test_builtin_macros_sizes() {
let builtins = X86BuiltinMacros::new(); let macros = builtins.generate_map();
assert_eq!(macros.get("__SIZEOF_INT__").map(|s| s.as_str()), Some("4"));
assert_eq!(macros.get("__SIZEOF_LONG__").map(|s| s.as_str()), Some("8"));
assert_eq!(
macros.get("__SIZEOF_POINTER__").map(|s| s.as_str()),
Some("8")
);
assert_eq!(
macros.get("__SIZEOF_SHORT__").map(|s| s.as_str()),
Some("2")
);
}
#[test]
fn test_builtin_macros_sizes_32bit() {
let builtins = X86BuiltinMacros::from_triple("i386-unknown-linux-gnu");
let macros = builtins.generate_map();
assert_eq!(macros.get("__SIZEOF_LONG__").map(|s| s.as_str()), Some("4"));
assert_eq!(
macros.get("__SIZEOF_POINTER__").map(|s| s.as_str()),
Some("4")
);
}
#[test]
fn test_builtin_macros_float_limits() {
let builtins = X86BuiltinMacros::new();
let macros = builtins.generate_map();
assert!(macros.contains_key("__FLT_MAX__"));
assert!(macros.contains_key("__DBL_MAX__"));
assert!(macros.contains_key("__LDBL_MAX__"));
assert!(macros.contains_key("__FLT_MANT_DIG__"));
assert!(macros.contains_key("__DBL_MANT_DIG__"));
}
#[test]
fn test_builtin_macros_atomic() {
let builtins = X86BuiltinMacros::new();
let macros = builtins.generate_map();
assert_eq!(
macros.get("__ATOMIC_BOOL_LOCK_FREE").map(|s| s.as_str()),
Some("2")
);
assert_eq!(
macros.get("__ATOMIC_INT_LOCK_FREE").map(|s| s.as_str()),
Some("2")
);
assert!(macros.contains_key("__ATOMIC_RELAXED"));
assert!(macros.contains_key("__ATOMIC_SEQ_CST"));
}
#[test]
fn test_builtin_macros_endian() {
let builtins = X86BuiltinMacros::new();
let macros = builtins.generate_map();
assert!(macros.contains_key("__BYTE_ORDER__"));
assert_eq!(
macros.get("__ORDER_LITTLE_ENDIAN__").map(|s| s.as_str()),
Some("1234")
);
}
#[test]
fn test_builtin_macros_pic_pie() {
let mut builtins = X86BuiltinMacros::new();
builtins.is_pic = true;
let macros = builtins.generate_map();
assert_eq!(macros.get("__PIC__").map(|s| s.as_str()), Some("2"));
assert_eq!(macros.get("__pic__").map(|s| s.as_str()), Some("2"));
let mut builtins2 = X86BuiltinMacros::new();
builtins2.is_pie = true;
let macros2 = builtins2.generate_map();
assert_eq!(macros2.get("__PIE__").map(|s| s.as_str()), Some("2"));
}
#[test]
fn test_builtin_macros_fast_math() {
let mut builtins = X86BuiltinMacros::new();
builtins.fast_math = true;
let macros = builtins.generate_map();
assert_eq!(macros.get("__FAST_MATH__").map(|s| s.as_str()), Some("1"));
assert_eq!(
macros.get("__FINITE_MATH_ONLY__").map(|s| s.as_str()),
Some("1")
);
assert_eq!(
macros.get("__NO_MATH_ERRNO__").map(|s| s.as_str()),
Some("1")
);
}
#[test]
fn test_builtin_macros_attribute_declspec() {
let builtins = X86BuiltinMacros::new();
let macros = builtins.generate_map();
assert!(macros.contains_key("__attribute__(x)"));
assert!(macros.contains_key("__declspec(x)"));
}
#[test]
fn test_builtin_macros_microarch_level() {
let mut builtins = X86BuiltinMacros::new();
builtins.set_microarch_level(X86MicroarchLevel::V3);
let macros = builtins.generate_map();
assert_eq!(macros.get("__x86_64_v3__").map(|s| s.as_str()), Some("1"));
assert!(builtins.has_feature(&X86Feature::AVX2));
assert!(builtins.has_feature(&X86Feature::FMA));
}
#[test]
fn test_builtin_macros_sse_math_flags() {
let builtins = X86BuiltinMacros::new(); let macros = builtins.generate_map();
assert_eq!(macros.get("__SSE_MATH__").map(|s| s.as_str()), Some("1"));
assert_eq!(macros.get("__SSE2_MATH__").map(|s| s.as_str()), Some("1"));
}
#[test]
fn test_feature_macro_def_names() {
assert_eq!(X86Feature::SSE.macro_def(), Some(("__SSE__", "1")));
assert_eq!(X86Feature::SSE2.macro_def(), Some(("__SSE2__", "1")));
assert_eq!(X86Feature::AVX.macro_def(), Some(("__AVX__", "1")));
assert_eq!(X86Feature::AVX512F.macro_def(), Some(("__AVX512F__", "1")));
assert_eq!(X86Feature::BMI.macro_def(), Some(("__BMI__", "1")));
assert_eq!(X86Feature::BMI2.macro_def(), Some(("__BMI2__", "1")));
assert_eq!(X86Feature::FMA.macro_def(), Some(("__FMA__", "1")));
assert_eq!(X86Feature::MMX.macro_def(), Some(("__MMX__", "1")));
assert_eq!(X86Feature::ThreeDNow.macro_def(), Some(("__3dNOW__", "1")));
}
#[test]
fn test_feature_implies_avx() {
let implied = X86Feature::AVX.implies();
assert!(implied.contains(&X86Feature::SSE42));
assert!(implied.contains(&X86Feature::SSSE3));
}
#[test]
fn test_feature_implies_avx2() {
let implied = X86Feature::AVX2.implies();
assert!(implied.contains(&X86Feature::AVX));
assert!(implied.contains(&X86Feature::FMA));
assert!(implied.contains(&X86Feature::BMI));
assert!(implied.contains(&X86Feature::BMI2));
}
#[test]
fn test_feature_flag_names() {
assert_eq!(X86Feature::SSE.flag_name(), "sse");
assert_eq!(X86Feature::SSE41.flag_name(), "sse4.1");
assert_eq!(X86Feature::AVX512BW.flag_name(), "avx512bw");
assert_eq!(X86Feature::F16C.flag_name(), "f16c");
}
#[test]
fn test_microarch_level_parsing() {
assert_eq!(
X86MicroarchLevel::from_str("x86-64"),
Some(X86MicroarchLevel::Baseline)
);
assert_eq!(
X86MicroarchLevel::from_str("x86-64-v2"),
Some(X86MicroarchLevel::V2)
);
assert_eq!(
X86MicroarchLevel::from_str("v3"),
Some(X86MicroarchLevel::V3)
);
assert_eq!(
X86MicroarchLevel::from_str("x86-64-v4"),
Some(X86MicroarchLevel::V4)
);
assert_eq!(X86MicroarchLevel::from_str("invalid"), None);
}
#[test]
fn test_microarch_level_features_v2() {
let features = X86MicroarchLevel::V2.features();
assert!(features.contains(&X86Feature::SSE3));
assert!(features.contains(&X86Feature::SSE41));
assert!(features.contains(&X86Feature::POPCNT));
}
#[test]
fn test_microarch_level_features_v3() {
let features = X86MicroarchLevel::V3.features();
assert!(features.contains(&X86Feature::AVX2));
assert!(features.contains(&X86Feature::BMI));
assert!(features.contains(&X86Feature::FMA));
}
#[test]
fn test_microarch_level_features_v4() {
let features = X86MicroarchLevel::V4.features();
assert!(features.contains(&X86Feature::AVX512F));
assert!(features.contains(&X86Feature::AVX512BW));
assert!(features.contains(&X86Feature::AVX512VL));
}
#[test]
fn test_macro_expander_new() {
let expander = X86MacroExpander::new();
assert_eq!(expander.defines.len(), 0);
assert!(expander.expanding.is_empty());
}
#[test]
fn test_macro_expander_define_and_expand() {
let mut expander = X86MacroExpander::new();
expander.define("FOO", "bar");
expander.define("BAZ", "42");
let tokens = vec![ident("FOO")];
let result = expander.expand(&tokens, None, 1);
assert_eq!(result.len(), 1);
assert_eq!(result[0].text, "bar");
}
#[test]
fn test_macro_expander_no_recursion() {
let mut expander = X86MacroExpander::new();
expander.define("RECURSE", "RECURSE + 1");
let tokens = vec![ident("RECURSE")];
let result = expander.expand(&tokens, None, 1);
assert_eq!(result.len(), 1);
}
#[test]
fn test_macro_expander_builtins_line() {
let mut expander = X86MacroExpander::new();
let tokens = vec![ident("__LINE__")];
let result = expander.expand(&tokens, Some("test.c"), 42);
assert_eq!(result.len(), 1);
assert_eq!(result[0].text, "42");
}
#[test]
fn test_macro_expander_builtins_file() {
let mut expander = X86MacroExpander::new();
let tokens = vec![ident("__FILE__")];
let result = expander.expand(&tokens, Some("test.c"), 1);
assert_eq!(result.len(), 1);
assert_eq!(result[0].text, "\"test.c\"");
}
#[test]
fn test_macro_expander_builtins_counter() {
let mut expander = X86MacroExpander::new();
let tokens = vec![ident("__COUNTER__"), ident("__COUNTER__")];
let result = expander.expand(&tokens, None, 1);
assert_eq!(result.len(), 2);
assert_eq!(result[0].text, "0");
assert_eq!(result[1].text, "1");
}
#[test]
fn test_macro_expander_function_like() {
let mut expander = X86MacroExpander::new();
expander.define_function_like("ADD", vec!["a".into(), "b".into()], "a + b", false);
let tokens = vec![
ident("ADD"),
make_token("(", TokenKind::LParen),
num("1"),
make_token(",", TokenKind::Comma),
num("2"),
make_token(")", TokenKind::RParen),
];
let result = expander.expand(&tokens, None, 1);
let text: Vec<&str> = result.iter().map(|t| t.text.as_str()).collect();
assert!(text.contains(&"1"));
assert!(text.contains(&"2"));
}
#[test]
fn test_macro_expander_load_builtins() {
let mut expander = X86MacroExpander::new();
expander.load_builtins();
assert!(expander.defines.contains_key("__STDC__"));
assert!(expander.defines.contains_key("__x86_64__"));
}
#[test]
fn test_include_resolver_new() {
let resolver = X86IncludeResolver::new();
assert!(!resolver.search_paths.is_empty());
}
#[test]
fn test_include_resolver_for_target_linux() {
let resolver = X86IncludeResolver::for_target("x86_64-unknown-linux-gnu");
assert_eq!(resolver.libc_family, LibCFamily::Glibc);
}
#[test]
fn test_include_resolver_for_target_musl() {
let resolver = X86IncludeResolver::for_target("x86_64-unknown-linux-musl");
assert_eq!(resolver.libc_family, LibCFamily::Musl);
}
#[test]
fn test_include_resolver_for_target_darwin() {
let resolver = X86IncludeResolver::for_target("x86_64-apple-darwin");
assert_eq!(resolver.libc_family, LibCFamily::Darwin);
}
#[test]
fn test_include_resolver_for_target_msvc() {
let resolver = X86IncludeResolver::for_target("x86_64-pc-windows-msvc");
assert_eq!(resolver.libc_family, LibCFamily::MSVC);
}
#[test]
fn test_include_resolver_pragma_once() {
let mut resolver = X86IncludeResolver::new();
let path = Path::new("/tmp/test.h");
assert!(!resolver.is_pragma_once(path));
resolver.mark_pragma_once(path);
assert!(resolver.is_pragma_once(path));
}
#[test]
fn test_pragma_handler_parse_once() {
let handler = X86PragmaHandler::new();
assert_eq!(handler.parse("once"), X86PragmaKind::Once);
}
#[test]
fn test_pragma_handler_parse_pack() {
let handler = X86PragmaHandler::new();
assert_eq!(handler.parse("pack(4)"), X86PragmaKind::Pack(4));
assert_eq!(handler.parse("pack(1)"), X86PragmaKind::Pack(1));
}
#[test]
fn test_pragma_handler_parse_message() {
let handler = X86PragmaHandler::new();
assert_eq!(
handler.parse("message(\"hello world\")"),
X86PragmaKind::Message("hello world".to_string())
);
}
#[test]
fn test_pragma_handler_parse_gcc_optimize() {
let handler = X86PragmaHandler::new();
assert_eq!(
handler.parse("GCC optimize(\"O2\")"),
X86PragmaKind::GccOptimize("O2".to_string())
);
}
#[test]
fn test_pragma_handler_parse_gcc_target() {
let handler = X86PragmaHandler::new();
let result = handler.parse("GCC target(\"arch=haswell\")");
assert_eq!(result, X86PragmaKind::GccTarget("arch=haswell".to_string()));
}
#[test]
fn test_pragma_handler_parse_gcc_diagnostic_push() {
let handler = X86PragmaHandler::new();
assert_eq!(
handler.parse("GCC diagnostic push"),
X86PragmaKind::GccDiagnostic(GccDiagnosticAction::Push)
);
}
#[test]
fn test_pragma_handler_parse_gcc_diagnostic_pop() {
let handler = X86PragmaHandler::new();
assert_eq!(
handler.parse("GCC diagnostic pop"),
X86PragmaKind::GccDiagnostic(GccDiagnosticAction::Pop)
);
}
#[test]
fn test_pragma_handler_parse_gcc_poison() {
let handler = X86PragmaHandler::new();
let result = handler.parse("GCC poison printf sprintf");
assert_eq!(
result,
X86PragmaKind::GccPoison(vec!["printf".into(), "sprintf".into()])
);
}
#[test]
fn test_pragma_handler_parse_gcc_system_header() {
let handler = X86PragmaHandler::new();
assert_eq!(
handler.parse("GCC system_header"),
X86PragmaKind::GccSystemHeader
);
}
#[test]
fn test_pragma_handler_parse_clang_loop_vectorize() {
let handler = X86PragmaHandler::new();
assert_eq!(
handler.parse("clang loop vectorize(enable)"),
X86PragmaKind::ClangLoop(LoopHint::Enable)
);
}
#[test]
fn test_pragma_handler_parse_fp_contract() {
let handler = X86PragmaHandler::new();
assert_eq!(
handler.parse("STDC FP_CONTRACT ON"),
X86PragmaKind::StdcFpContract(FpContractState::On)
);
assert_eq!(
handler.parse("STDC FP_CONTRACT OFF"),
X86PragmaKind::StdcFpContract(FpContractState::Off)
);
}
#[test]
fn test_pragma_handler_parse_comment_lib() {
let handler = X86PragmaHandler::new();
let result = handler.parse("comment(lib, \"kernel32.lib\")");
assert_eq!(
result,
X86PragmaKind::CommentLib("kernel32.lib".to_string())
);
}
#[test]
fn test_pragma_handler_handle_push_pop() {
let mut handler = X86PragmaHandler::new();
handler.handle(&X86PragmaKind::GccPushOptions);
assert_eq!(handler.state.optimize_stack.len(), 1);
handler.handle(&X86PragmaKind::GccPopOptions);
assert_eq!(handler.state.optimize_stack.len(), 0);
}
#[test]
fn test_pragma_handler_handle_poison() {
let mut handler = X86PragmaHandler::new();
let pragma = X86PragmaKind::GccPoison(vec!["gets".into()]);
handler.handle(&pragma);
assert!(handler.is_poisoned("gets"));
assert!(!handler.is_poisoned("fgets"));
}
#[test]
fn test_pragma_handler_handle_pack() {
let mut handler = X86PragmaHandler::new();
handler.handle(&X86PragmaKind::Pack(1));
assert_eq!(handler.get_pack_alignment(), 1);
handler.handle(&X86PragmaKind::Pack(4));
assert_eq!(handler.get_pack_alignment(), 4);
}
#[test]
fn test_pragma_handler_handle_message() {
let mut handler = X86PragmaHandler::new();
handler.handle(&X86PragmaKind::Message("Hello from pragma".into()));
assert_eq!(handler.messages.len(), 1);
assert_eq!(handler.messages[0], "Hello from pragma");
}
#[test]
fn test_pragma_handler_handle_unknown() {
let handler = X86PragmaHandler::new();
let result = handler.parse("some_unknown_directive 123");
assert!(matches!(result, X86PragmaKind::Unknown(_)));
}
#[test]
fn test_predefines_from_triple() {
let predefines = X86Predefines::from_triple("x86_64-unknown-linux-gnu");
assert!(predefines.has_feature(&X86Feature::SSE2));
assert_eq!(predefines.libc_family, LibCFamily::Glibc);
}
#[test]
fn test_predefines_set_cpu_haswell() {
let mut predefines = X86Predefines::from_triple("x86_64-unknown-linux-gnu");
predefines.set_cpu("haswell");
assert!(predefines.has_feature(&X86Feature::AVX2));
assert!(predefines.has_feature(&X86Feature::FMA));
assert!(predefines.has_feature(&X86Feature::BMI));
assert!(predefines.has_feature(&X86Feature::BMI2));
}
#[test]
fn test_predefines_set_cpu_skylake_avx512() {
let mut predefines = X86Predefines::from_triple("x86_64-unknown-linux-gnu");
predefines.set_cpu("skylake-avx512");
assert!(predefines.has_feature(&X86Feature::AVX512F));
assert!(predefines.has_feature(&X86Feature::AVX512BW));
assert!(!predefines.has_feature(&X86Feature::AVX512IFMA));
}
#[test]
fn test_predefines_set_cpu_znver3() {
let mut predefines = X86Predefines::from_triple("x86_64-unknown-linux-gnu");
predefines.set_cpu("znver3");
assert!(predefines.has_feature(&X86Feature::AVX2));
assert!(predefines.has_feature(&X86Feature::CLZERO));
assert!(predefines.has_feature(&X86Feature::INVPCID));
}
#[test]
fn test_predefines_compute() {
let mut predefines = X86Predefines::from_triple("x86_64-unknown-linux-gnu");
predefines.set_cpu("haswell");
let macros = predefines.compute();
let map: HashMap<_, _> = macros.into_iter().collect();
assert_eq!(map.get("__AVX2__").map(|s| s.as_str()), Some("1"));
assert_eq!(map.get("__FMA__").map(|s| s.as_str()), Some("1"));
}
#[test]
fn test_predefines_cpu_to_features_baseline() {
let features = X86Predefines::cpu_to_features("pentium");
assert!(features.contains(&X86Feature::MMX));
assert!(!features.contains(&X86Feature::SSE));
}
#[test]
fn test_predefines_cpu_to_microarch_level() {
assert_eq!(
X86Predefines::cpu_to_microarch_level("haswell"),
X86MicroarchLevel::V3
);
assert_eq!(
X86Predefines::cpu_to_microarch_level("skx"),
X86MicroarchLevel::V4
);
assert_eq!(
X86Predefines::cpu_to_microarch_level("nehalem"),
X86MicroarchLevel::V2
);
}
#[test]
fn test_conditional_new() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
assert!(cond.is_defined("__STDC__"));
assert!(cond.is_defined("__x86_64__"));
}
#[test]
fn test_conditional_is_defined() {
let builtins = X86BuiltinMacros::new();
let mut cond = X86ConditionalInclusion::new(&builtins);
assert!(cond.is_defined("__STDC__"));
assert!(!cond.is_defined("MY_UNDEFINED_MACRO"));
cond.add_define("MY_MACRO", "123");
assert!(cond.is_defined("MY_MACRO"));
}
#[test]
fn test_conditional_has_feature() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
assert!(cond.has_feature("c_atomic"));
assert!(cond.has_feature("c_static_assert"));
assert!(!cond.has_feature("address_sanitizer"));
}
#[test]
fn test_conditional_has_builtin() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
assert!(cond.has_builtin("__builtin_ia32_rdtsc"));
assert!(!cond.has_builtin("__builtin_nonexistent"));
}
#[test]
fn test_conditional_has_attribute() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
assert!(cond.has_attribute("always_inline"));
assert!(cond.has_attribute("aligned"));
assert!(cond.has_attribute("noreturn"));
assert!(!cond.has_attribute("nonexistent_attr"));
}
#[test]
fn test_conditional_has_declspec_attribute() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
assert!(cond.has_declspec_attribute("dllexport"));
assert!(cond.has_declspec_attribute("noreturn"));
assert!(!cond.has_declspec_attribute("nonexistent"));
}
#[test]
fn test_conditional_eval_ifdef() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
assert!(cond.eval_ifdef("__STDC__"));
assert!(!cond.eval_ifdef("NONEXISTENT"));
}
#[test]
fn test_conditional_eval_ifndef() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
assert!(!cond.eval_ifndef("__STDC__"));
assert!(cond.eval_ifndef("NONEXISTENT"));
}
#[test]
fn test_conditional_evaluate_simple_defined() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("defined(__STDC__)");
assert_eq!(result, Some(true));
let result = cond.evaluate_condition("defined(NONEXISTENT)");
assert_eq!(result, Some(false));
}
#[test]
fn test_conditional_evaluate_arithmetic() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("1 + 2");
assert_eq!(result, Some(true)); let result = cond.evaluate_condition("0 + 0");
assert_eq!(result, Some(false));
}
#[test]
fn test_conditional_evaluate_comparison() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("5 > 3");
assert_eq!(result, Some(true));
let result = cond.evaluate_condition("2 == 0");
assert_eq!(result, Some(false));
}
#[test]
fn test_conditional_evaluate_logical() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("1 && 1");
assert_eq!(result, Some(true));
let result = cond.evaluate_condition("0 || 0");
assert_eq!(result, Some(false));
}
#[test]
fn test_conditional_evaluate_nested() {
let builtins = X86BuiltinMacros::new();
let mut cond = X86ConditionalInclusion::new(&builtins);
cond.add_define("VERSION", "4");
let result = cond.evaluate_condition("VERSION >= 4 && defined(__STDC__)");
assert_eq!(result, Some(true));
}
#[test]
fn test_conditional_evaluate_ternary() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("1 ? 5 : 0");
assert_eq!(result, Some(true));
let result = cond.evaluate_condition("0 ? 5 : 0");
assert_eq!(result, Some(false));
}
#[test]
fn test_conditional_evaluate_bitwise_unary() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("!0");
assert_eq!(result, Some(true));
let result = cond.evaluate_condition("!1");
assert_eq!(result, Some(false));
}
#[test]
fn test_conditional_evaluate_shift() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("1 << 4");
assert_eq!(result, Some(true)); let result = cond.evaluate_condition("4 >> 2");
assert_eq!(result, Some(true)); }
#[test]
fn test_conditional_evaluate_with_user_defines() {
let builtins = X86BuiltinMacros::new();
let mut cond = X86ConditionalInclusion::new(&builtins);
cond.add_define("FEATURE_LEVEL", "3");
let result = cond.evaluate_condition("FEATURE_LEVEL > 2");
assert_eq!(result, Some(true));
}
#[test]
fn test_token_pasting_simple() {
let paster = X86TokenPasting::new();
let result = paster.paste(Some(&ident("foo")), Some(&ident("bar")), &[], &[]);
assert!(result.is_some());
assert_eq!(result.unwrap().text, "foobar");
}
#[test]
fn test_token_pasting_empty_left() {
let paster = X86TokenPasting::new();
let result = paster.paste(None, Some(&ident("bar")), &[], &[]);
assert!(result.is_some());
assert_eq!(result.unwrap().text, "bar");
}
#[test]
fn test_token_pasting_empty_right() {
let paster = X86TokenPasting::new();
let result = paster.paste(Some(&ident("foo")), None, &[], &[]);
assert!(result.is_some());
assert_eq!(result.unwrap().text, "foo");
}
#[test]
fn test_token_pasting_empty_both() {
let paster = X86TokenPasting::new();
let result = paster.paste(None, None, &[], &[]);
assert!(result.is_none());
}
#[test]
fn test_token_pasting_sequence() {
let paster = X86TokenPasting::new();
let tokens = vec![
ident("prefix"),
make_token("##", TokenKind::HashHash),
ident("suffix"),
];
let result = paster.paste_sequence(&tokens, &[], &[]);
assert_eq!(result.len(), 1);
assert_eq!(result[0].text, "prefixsuffix");
}
#[test]
fn test_token_pasting_with_params() {
let paster = X86TokenPasting::new();
let params = vec!["a".to_string()];
let args = vec![vec![ident("hello")]];
let tokens = vec![
ident("a"),
make_token("##", TokenKind::HashHash),
ident("_world"),
];
let result = paster.paste_sequence(&tokens, &args, ¶ms);
assert_eq!(result.len(), 1);
assert_eq!(result[0].text, "hello_world");
}
#[test]
fn test_stringification_simple() {
let sf = X86Stringification::new();
let tokens = vec![num("42")];
let result = sf.stringify(&tokens);
assert_eq!(result, "\"42\"");
}
#[test]
fn test_stringification_multiple_tokens() {
let sf = X86Stringification::new();
let tokens = vec![num("1"), ident("+"), num("2")];
let result = sf.stringify(&tokens);
assert_eq!(result, "\"1 + 2\"");
}
#[test]
fn test_stringification_empty() {
let sf = X86Stringification::new();
let result = sf.stringify_or_empty(&[]);
assert_eq!(result, "\"\"");
}
#[test]
fn test_stringification_escaped() {
let sf = X86Stringification::new();
let token = make_token("hello \"world\"\n", TokenKind::StringLiteral);
let result = sf.stringify(&[token]);
assert!(result.contains("\\\""));
}
#[test]
fn test_stringification_apply() {
let sf = X86Stringification::new();
let body = vec![make_token("#", TokenKind::Hash), ident("x")];
let args = vec![vec![num("42")]];
let params = vec!["x".to_string()];
let result = sf.apply_stringification(&body, &args, ¶ms);
assert_eq!(result.len(), 1);
assert_eq!(result[0].kind, TokenKind::StringLiteral);
}
#[test]
fn test_line_directive_new() {
let ld = X86LineDirective::new();
assert!(ld.line_offset.is_none());
assert!(ld.file_override.is_none());
}
#[test]
fn test_line_directive_apply_line_number() {
let mut ld = X86LineDirective::new();
assert!(ld.apply_line("42").is_ok());
assert_eq!(ld.line_offset, Some(42));
assert_eq!(ld.effective_line(1), 42);
}
#[test]
fn test_line_directive_apply_line_with_file() {
let mut ld = X86LineDirective::new();
assert!(ld.apply_line("10 \"hello.c\"").is_ok());
assert_eq!(ld.line_offset, Some(10));
assert_eq!(ld.file_override, Some("hello.c".to_string()));
assert_eq!(ld.effective_file("actual.c"), "hello.c");
}
#[test]
fn test_line_directive_apply_line_zero() {
let mut ld = X86LineDirective::new();
assert!(ld.apply_line("0").is_err());
}
#[test]
fn test_line_directive_reset() {
let mut ld = X86LineDirective::new();
ld.apply_line("99 \"override.h\"").unwrap();
ld.reset();
assert!(ld.line_offset.is_none());
assert!(ld.file_override.is_none());
}
#[test]
fn test_preprocessor_new() {
let pp = X86Preprocessor::new();
assert!(!pp.skipping);
assert_eq!(pp.cond_stack.len(), 0);
}
#[test]
fn test_preprocessor_for_target() {
let pp = X86Preprocessor::for_target("x86_64-apple-darwin");
assert_eq!(pp.builtins.os, X86TargetOS::Darwin);
}
#[test]
fn test_preprocessor_add_define() {
let mut pp = X86Preprocessor::new();
pp.add_define("TEST_MACRO", "123");
assert!(pp.expander.defines.contains_key("TEST_MACRO"));
assert!(pp.conditional.is_defined("TEST_MACRO"));
}
#[test]
fn test_preprocessor_undefine() {
let mut pp = X86Preprocessor::new();
pp.add_define("TEST_MACRO", "123");
assert!(pp.expander.defines.contains_key("TEST_MACRO"));
pp.undefine("TEST_MACRO");
assert!(!pp.expander.defines.contains_key("TEST_MACRO"));
}
#[test]
fn test_preprocessor_has_x86_builtins() {
let pp = X86Preprocessor::new();
assert!(pp.conditional.is_defined("__STDC__"));
assert!(pp.conditional.is_defined("__x86_64__"));
}
#[test]
fn test_preprocessor_process_source_simple() {
let mut pp = X86Preprocessor::new();
let tokens = pp.process_source("int x = 42;\n", "test.c");
assert!(!tokens.is_empty());
let texts: Vec<&str> = tokens
.iter()
.filter(|t| {
t.kind == TokenKind::KwInt
|| t.kind == TokenKind::Identifier
|| t.kind == TokenKind::NumericLiteral
})
.map(|t| t.text.as_str())
.collect();
assert!(texts.contains(&"int"));
assert!(texts.contains(&"x"));
assert!(texts.contains(&"42"));
}
#[test]
fn test_preprocessor_process_ifdef() {
let mut pp = X86Preprocessor::new();
let source = "#ifdef __STDC__\nint y = 1;\n#else\nint y = 0;\n#endif\n";
let tokens = pp.process_source(source, "test.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(texts.contains(&"1"));
assert!(!texts.contains(&"0"));
assert_eq!(pp.errors.len(), 0);
}
#[test]
fn test_preprocessor_process_ifndef() {
let mut pp = X86Preprocessor::new();
let source = "#ifndef NONEXISTENT_MACRO\nint a = 10;\n#else\nint a = 20;\n#endif\n";
let tokens = pp.process_source(source, "test.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(texts.contains(&"10"));
assert!(!texts.contains(&"20"));
}
#[test]
fn test_preprocessor_process_if_else() {
let mut pp = X86Preprocessor::new();
pp.add_define("FLAG", "0");
let source = "#if FLAG\nint b = 100;\n#else\nint b = 200;\n#endif\n";
let tokens = pp.process_source(source, "test.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(!texts.contains(&"100"));
assert!(texts.contains(&"200"));
}
#[test]
fn test_preprocessor_process_nested_if() {
let mut pp = X86Preprocessor::new();
let source = "#if defined(__STDC__)\n#if 1\nint c = 42;\n#endif\n#endif\n";
let tokens = pp.process_source(source, "test.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(texts.contains(&"42"));
}
#[test]
fn test_preprocessor_process_if_elif() {
let mut pp = X86Preprocessor::new();
pp.add_define("MODE", "2");
let source =
"#if MODE == 1\nint d = 1;\n#elif MODE == 2\nint d = 2;\n#else\nint d = 3;\n#endif\n";
let tokens = pp.process_source(source, "test.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(!texts.contains(&"1"));
assert!(texts.contains(&"2"));
assert!(!texts.contains(&"3"));
}
#[test]
fn test_preprocessor_add_include_path() {
let mut pp = X86Preprocessor::new();
pp.add_include_path(Path::new("/some/custom/include"));
assert_eq!(pp.base.include_paths.len(), 1);
}
#[test]
fn test_preprocessor_enable_feature() {
let mut pp = X86Preprocessor::from_triple("i386-unknown-linux-gnu");
assert!(!pp.builtins.has_feature(&X86Feature::AVX));
pp.enable_feature(X86Feature::AVX);
assert!(pp.builtins.has_feature(&X86Feature::AVX));
assert!(pp.builtins.has_feature(&X86Feature::SSE));
}
#[test]
fn test_preprocessor_set_cpu() {
let mut pp = X86Preprocessor::new();
pp.set_cpu("haswell");
assert!(pp.builtins.has_feature(&X86Feature::AVX2));
assert!(pp.builtins.has_feature(&X86Feature::FMA));
}
#[test]
fn test_preprocessor_effective_line() {
let mut pp = X86Preprocessor::new();
pp.current_line = 5;
assert_eq!(pp.effective_line(), 5);
pp.line_directive.apply_line("100").unwrap();
assert_eq!(pp.effective_line(), 100);
}
#[test]
fn test_preprocessor_errors() {
let mut pp = X86Preprocessor::new();
let source = "#error This is an error message\n";
let _tokens = pp.process_source(source, "test.c");
assert_eq!(pp.errors.len(), 1);
assert!(pp.errors[0].contains("This is an error message"));
}
#[test]
fn test_preprocessor_warnings() {
let mut pp = X86Preprocessor::new();
let source = "#warning This is a warning message\n";
let _tokens = pp.process_source(source, "test.c");
assert_eq!(pp.warnings.len(), 1);
assert!(pp.warnings[0].contains("This is a warning message"));
}
#[test]
fn test_preprocessor_should_skip() {
let mut pp = X86Preprocessor::new();
assert!(!pp.should_skip());
pp.skipping = true;
assert!(pp.should_skip());
}
#[test]
fn test_preprocessor_cond_depth() {
let mut pp = X86Preprocessor::new();
assert_eq!(pp.cond_depth(), 0);
pp.cond_stack.push(ConditionalBlockState::Active);
assert_eq!(pp.cond_depth(), 1);
}
#[test]
fn test_preprocessor_take_errors() {
let mut pp = X86Preprocessor::new();
pp.errors.push("error1".into());
pp.errors.push("error2".into());
let errors = pp.take_errors();
assert_eq!(errors.len(), 2);
assert!(pp.errors.is_empty());
}
#[test]
fn test_target_os_from_str() {
assert_eq!(X86TargetOS::from_str("linux"), X86TargetOS::Linux);
assert_eq!(X86TargetOS::from_str("darwin"), X86TargetOS::Darwin);
assert_eq!(X86TargetOS::from_str("windows"), X86TargetOS::Windows);
assert_eq!(X86TargetOS::from_str("android"), X86TargetOS::Android);
assert_eq!(X86TargetOS::from_str("unknown_os"), X86TargetOS::Unknown);
}
#[test]
fn test_target_os_is_unix() {
assert!(X86TargetOS::Linux.is_unix());
assert!(X86TargetOS::Darwin.is_unix());
assert!(!X86TargetOS::Windows.is_unix());
}
#[test]
fn test_target_env_from_str() {
assert_eq!(X86TargetEnv::from_str("gnu"), X86TargetEnv::Gnu);
assert_eq!(X86TargetEnv::from_str("musl"), X86TargetEnv::Musl);
assert_eq!(X86TargetEnv::from_str("msvc"), X86TargetEnv::MSVC);
assert_eq!(X86TargetEnv::from_str("android"), X86TargetEnv::Bionic);
}
#[test]
fn test_x86_include_resolver_libc_detection() {
let r = X86IncludeResolver::for_target("x86_64-unknown-linux-gnu");
assert_eq!(r.libc_family, LibCFamily::Glibc);
let r = X86IncludeResolver::for_target("x86_64-unknown-linux-musl");
assert_eq!(r.libc_family, LibCFamily::Musl);
let r = X86IncludeResolver::for_target("x86_64-linux-android");
assert_eq!(r.libc_family, LibCFamily::Bionic);
let r = X86IncludeResolver::for_target("x86_64-apple-darwin");
assert_eq!(r.libc_family, LibCFamily::Darwin);
let r = X86IncludeResolver::for_target("x86_64-pc-windows-msvc");
assert_eq!(r.libc_family, LibCFamily::MSVC);
}
#[test]
fn test_include_resolver_dump_paths() {
let r = X86IncludeResolver::new();
let paths = r.dump_paths();
assert!(!paths.is_empty());
}
#[test]
fn test_builtin_macros_cxx_feature_macros() {
let mut builtins = X86BuiltinMacros::new();
builtins.is_cxx = true;
let macros = builtins.generate_map();
assert!(macros.contains_key("__cpp_rtti"));
assert!(macros.contains_key("__cpp_exceptions"));
assert!(macros.contains_key("__cpp_lambdas"));
assert!(macros.contains_key("__cpp_constexpr"));
}
#[test]
fn test_builtin_macros_cxx_gxx_abi() {
let mut builtins = X86BuiltinMacros::new();
builtins.is_cxx = true;
let macros = builtins.generate_map();
assert!(macros.contains_key("__GXX_ABI_VERSION"));
assert!(macros.contains_key("__GNUG__"));
}
#[test]
fn test_builtin_macros_cxx_experimental() {
let mut builtins = X86BuiltinMacros::new();
builtins.is_cxx = true;
builtins.experimental_cxx0x = true;
let macros = builtins.generate_map();
assert_eq!(
macros.get("__GXX_EXPERIMENTAL_CXX0X__").map(|s| s.as_str()),
Some("1")
);
}
#[test]
fn test_conditional_evaluate_undefined_macro_is_zero() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("UNDEFINED_MACRO");
assert_eq!(result, Some(false));
}
#[test]
fn test_conditional_evaluate_division_by_zero() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("1 / 0");
assert_eq!(result, None);
}
#[test]
fn test_conditional_evaluate_modulo_by_zero() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("1 % 0");
assert_eq!(result, None);
}
#[test]
fn test_macro_expander_deeply_nested() {
let mut expander = X86MacroExpander::new();
expander.define("A", "B");
expander.define("B", "C");
expander.define("C", "D");
expander.define("D", "42");
let result = expander.expand(&[ident("A")], None, 1);
assert_eq!(result.len(), 1);
}
#[test]
fn test_pragma_handler_invalid_inputs() {
let handler = X86PragmaHandler::new();
let result = handler.parse("");
assert!(matches!(result, X86PragmaKind::Unknown(_)));
}
#[test]
fn test_token_pasting_number_number() {
let paster = X86TokenPasting::new();
let result = paster.paste(Some(&num("12")), Some(&num("34")), &[], &[]);
assert!(result.is_some());
let tok = result.unwrap();
assert_eq!(tok.text, "1234");
}
#[test]
fn test_feature_implies_transitive() {
let implied = X86Feature::AVX512F.implies();
assert!(implied.contains(&X86Feature::AVX2));
}
#[test]
fn test_directive_processor_new() {
let dp = X86DirectiveProcessor::new();
assert!(dp.expand_in_directives);
assert_eq!(dp.max_include_depth, 200);
assert_eq!(dp.current_include_depth, 0);
}
#[test]
fn test_directive_processor_define() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "define".into(),
arguments: vec![ident("MYVAL"), num("42")],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert!(output.emitted_tokens.is_empty());
assert!(pp.expander.defines.contains_key("MYVAL"));
}
#[test]
fn test_directive_processor_undef() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
pp.add_define("TMP", "1");
assert!(pp.expander.defines.contains_key("TMP"));
let input = DirectiveInput {
keyword: "undef".into(),
arguments: vec![ident("TMP")],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert!(!pp.expander.defines.contains_key("TMP"));
let _ = output;
}
#[test]
fn test_directive_processor_if_true() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "if".into(),
arguments: vec![num("1")],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert_eq!(output.new_skip_state, false);
assert_eq!(pp.cond_depth(), 1);
}
#[test]
fn test_directive_processor_if_false() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "if".into(),
arguments: vec![num("0")],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert_eq!(output.new_skip_state, true);
}
#[test]
fn test_directive_processor_ifdef_x86_64() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "ifdef".into(),
arguments: vec![ident("__x86_64__")],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert_eq!(output.new_skip_state, false);
}
#[test]
fn test_directive_processor_ifndef_nonexistent() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "ifndef".into(),
arguments: vec![ident("NONEXISTENT_MACRO_12345")],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert_eq!(output.new_skip_state, false); }
#[test]
fn test_directive_processor_endif() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
pp.cond_stack.push(ConditionalBlockState::Active);
pp.skipping = false;
let input = DirectiveInput {
keyword: "endif".into(),
arguments: vec![],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert_eq!(pp.cond_depth(), 0);
assert_eq!(output.new_skip_state, false);
}
#[test]
fn test_directive_processor_error() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "error".into(),
arguments: vec![
make_token("Compilation", TokenKind::Identifier),
make_token("halted", TokenKind::Identifier),
],
line: 10,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert_eq!(output.diagnostics.len(), 1);
assert_eq!(output.diagnostics[0].0, DiagnosticLevel::Error);
}
#[test]
fn test_directive_processor_warning() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "warning".into(),
arguments: vec![
make_token("deprecated", TokenKind::Identifier),
make_token("API", TokenKind::Identifier),
],
line: 5,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert_eq!(output.diagnostics.len(), 1);
assert_eq!(output.diagnostics[0].0, DiagnosticLevel::Warning);
}
#[test]
fn test_directive_processor_pragma_once() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
pp.current_file = Some(PathBuf::from("/tmp/guard.h"));
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "pragma".into(),
arguments: vec![ident("once")],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let _ = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert!(pp
.include_resolver
.is_pragma_once(&PathBuf::from("/tmp/guard.h")));
}
#[test]
fn test_directive_processor_skipped_block() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
pp.skipping = true;
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "define".into(),
arguments: vec![ident("SKIPPED_MACRO"), num("1")],
line: 1,
file: Some("test.c".into()),
in_skipped_block: true,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert!(output.emitted_tokens.is_empty());
assert!(!pp.expander.defines.contains_key("SKIPPED_MACRO"));
}
#[test]
fn test_builder_default() {
let pp = X86PreprocessorBuilder::new().build();
assert!(pp.conditional.is_defined("__STDC__"));
assert!(pp.conditional.is_defined("__x86_64__"));
}
#[test]
fn test_builder_cxx_mode() {
let pp = X86PreprocessorBuilder::new()
.cxx()
.cxx_version(201703)
.build();
assert!(pp.is_cxx);
assert_eq!(pp.builtins.cxx_version, Some(201703));
let macros = pp.builtins.generate_map();
assert!(macros.contains_key("__cplusplus"));
}
#[test]
fn test_builder_cpu_haswell() {
let pp = X86PreprocessorBuilder::new().cpu("haswell").build();
assert!(pp.builtins.has_feature(&X86Feature::AVX2));
assert!(pp.builtins.has_feature(&X86Feature::FMA));
}
#[test]
fn test_builder_with_includes() {
let pp = X86PreprocessorBuilder::new()
.include_dir("/custom/include")
.system_include_dir("/usr/local/include")
.build();
let paths = pp.include_resolver.dump_paths();
assert!(paths.len() > 0);
}
#[test]
fn test_builder_with_defines() {
let pp = X86PreprocessorBuilder::new()
.define("VERSION", "3")
.define("DEBUG", "1")
.build();
assert!(pp.conditional.is_defined("VERSION"));
assert!(pp.conditional.is_defined("DEBUG"));
assert_eq!(pp.conditional.get_define_value("VERSION"), Some("3"));
}
#[test]
fn test_builder_pic_pie() {
let pp = X86PreprocessorBuilder::new().pic().pie().build();
assert!(pp.builtins.is_pic);
assert!(pp.builtins.is_pie);
}
#[test]
fn test_builder_microarch_v3() {
let pp = X86PreprocessorBuilder::new()
.microarch_level(X86MicroarchLevel::V3)
.build();
assert!(pp.builtins.has_feature(&X86Feature::AVX2));
let macros = pp.builtins.generate_map();
assert_eq!(macros.get("__x86_64_v3__").map(|s| s.as_str()), Some("1"));
}
#[test]
fn test_builder_feature_enable_disable() {
let pp = X86PreprocessorBuilder::new()
.feature(X86Feature::AVX512F)
.disable_feature(X86Feature::SSE3)
.build();
assert!(pp.builtins.has_feature(&X86Feature::AVX512F));
}
#[test]
fn test_builder_fast_math() {
let pp = X86PreprocessorBuilder::new().fast_math().build();
assert!(pp.builtins.fast_math);
let macros = pp.builtins.generate_map();
assert_eq!(macros.get("__FAST_MATH__").map(|s| s.as_str()), Some("1"));
}
#[test]
fn test_builder_target_triple() {
let pp = X86PreprocessorBuilder::new()
.target("i386-unknown-linux-gnu")
.build();
assert!(!pp.builtins.is_64bit);
assert!(pp.conditional.is_defined("__i386__"));
}
#[test]
fn test_process_define_then_use() {
let mut pp = X86Preprocessor::new();
let source = "#define VALUE 999\nint x = VALUE;\n";
let tokens = pp.process_source(source, "test.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(texts.contains(&"999"));
}
#[test]
fn test_process_define_function_like() {
let mut pp = X86Preprocessor::new();
let source = "#define SQUARE(x) ((x)*(x))\nint a = SQUARE(5);\n";
let tokens = pp.process_source(source, "test.c");
assert!(!tokens.is_empty());
}
#[test]
fn test_process_if_defined_with_x86_features() {
let mut pp = X86Preprocessor::new();
let source = "#if defined(__SSE2__)\nint has_sse2 = 1;\n#else\nint has_sse2 = 0;\n#endif\n";
let tokens = pp.process_source(source, "test.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(texts.contains(&"1"));
assert!(!texts.contains(&"0"));
}
#[test]
fn test_process_if_defined_without_avx512() {
let mut pp = X86Preprocessor::new();
let source =
"#if defined(__AVX512F__)\nint has_avx512 = 1;\n#else\nint has_avx512 = 0;\n#endif\n";
let tokens = pp.process_source(source, "test.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(!texts.contains(&"1"));
assert!(texts.contains(&"0"));
}
#[test]
fn test_process_if_nested_conditional() {
let mut pp = X86Preprocessor::new();
let source =
"#if defined(__STDC__)\n#if defined(__x86_64__)\nint result = 42;\n#endif\n#endif\n";
let tokens = pp.process_source(source, "test.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(texts.contains(&"42"));
}
#[test]
fn test_process_if_elif_chain() {
let mut pp = X86Preprocessor::new();
pp.add_define("LEVEL", "2");
let source = "#if LEVEL == 1\nint r = 1;\n#elif LEVEL == 2\nint r = 2;\n#elif LEVEL == 3\nint r = 3;\n#else\nint r = 0;\n#endif\n";
let tokens = pp.process_source(source, "test.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(texts.contains(&"2"));
assert!(!texts.contains(&"1"));
assert!(!texts.contains(&"3"));
assert!(!texts.contains(&"0") || texts.iter().filter(|&&t| t == "0").count() == 0);
}
#[test]
fn test_preprocessor_arch_i386_no_sse() {
let pp = X86Preprocessor::for_target("i386-unknown-linux-gnu");
assert!(!pp.builtins.has_feature(&X86Feature::SSE));
assert!(!pp.builtins.has_feature(&X86Feature::SSE2));
assert!(pp.conditional.is_defined("__i386__"));
assert!(!pp.conditional.is_defined("__x86_64__"));
}
#[test]
fn test_preprocessor_arch_x86_64_linux() {
let pp = X86Preprocessor::for_target("x86_64-unknown-linux-gnu");
assert!(pp.conditional.is_defined("__linux__"));
assert!(pp.conditional.is_defined("__unix__"));
assert!(pp.conditional.is_defined("__ELF__"));
}
#[test]
fn test_preprocessor_arch_x86_64_darwin() {
let pp = X86Preprocessor::for_target("x86_64-apple-darwin");
assert!(pp.conditional.is_defined("__APPLE__"));
assert!(pp.conditional.is_defined("__unix__"));
}
#[test]
fn test_preprocessor_arch_x86_64_windows_msvc() {
let pp = X86Preprocessor::for_target("x86_64-pc-windows-msvc");
assert!(pp.conditional.is_defined("_WIN32"));
assert!(pp.conditional.is_defined("_WIN64"));
}
#[test]
fn test_preprocessor_nested_include_guard_detection() {
let mut pp = X86Preprocessor::new();
let source = "#ifndef HEADER_H\n#define HEADER_H\nint value = 1;\n#endif\n";
let tokens = pp.process_source(source, "test.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(texts.contains(&"value"));
}
#[test]
fn test_preprocessor_skip_false_branch_with_error() {
let mut pp = X86Preprocessor::new();
let source = "#if 0\n#error Should not fire\n#endif\nint ok = 1;\n";
let tokens = pp.process_source(source, "test.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(texts.contains(&"ok"));
assert!(pp.errors.is_empty());
}
#[test]
fn test_preprocessor_undef_and_redefine() {
let mut pp = X86Preprocessor::new();
pp.add_define("X", "old");
assert_eq!(pp.conditional.get_define_value("X"), Some("old"));
pp.undefine("X");
assert_eq!(pp.conditional.get_define_value("X"), None);
pp.add_define("X", "new");
assert_eq!(pp.conditional.get_define_value("X"), Some("new"));
}
#[test]
fn test_conditional_get_value_cascading() {
let mut builtins = X86BuiltinMacros::new();
let mut cond = X86ConditionalInclusion::new(&builtins);
cond.add_define("A", "B");
assert_eq!(cond.get_define_value("A"), Some("B"));
assert_eq!(cond.get_define_value("__STDC__"), Some("1"));
}
#[test]
fn test_line_directive_advance() {
let mut ld = X86LineDirective::new();
ld.apply_line("100").unwrap();
assert_eq!(ld.effective_line(1), 100);
ld.advance_line();
assert_eq!(ld.effective_line(1), 101);
ld.advance_line();
assert_eq!(ld.effective_line(1), 102);
}
#[test]
fn test_macro_expander_undef_noop() {
let mut expander = X86MacroExpander::new();
let removed = expander.undefine("NONEXISTENT");
assert!(!removed);
}
#[test]
fn test_macro_expander_clear() {
let mut expander = X86MacroExpander::new();
expander.define("TMP", "1");
expander.load_builtins();
expander.clear();
assert!(expander.defines.is_empty());
assert_eq!(expander.config.counter, 0);
}
#[test]
fn test_cpu_features_full_list_coverage() {
for cpu in &["nehalem", "haswell", "skylake", "znver3", "znver4"] {
let features = X86Predefines::cpu_to_features(cpu);
assert!(!features.is_empty(), "CPU '{}' should have features", cpu);
}
}
#[test]
fn test_all_feature_macro_defs_are_valid() {
use X86Feature::*;
let features = [
MMX, SSE, SSE2, SSE3, SSSE3, SSE41, SSE42, AVX, AVX2, AVX512F, FMA, BMI, BMI2,
];
for f in &features {
assert!(
f.macro_def().is_some(),
"Feature {:?} should have a macro_def",
f
);
}
}
#[test]
fn test_float_limit_macros_present() {
let builtins = X86BuiltinMacros::new();
let macros = builtins.generate_map();
assert!(macros.contains_key("__FLT_DIG__"));
assert!(macros.contains_key("__FLT_MANT_DIG__"));
assert!(macros.contains_key("__DBL_DIG__"));
assert!(macros.contains_key("__DBL_MANT_DIG__"));
assert!(macros.contains_key("__LDBL_DIG__"));
assert!(macros.contains_key("__LDBL_MANT_DIG__"));
}
#[test]
fn test_char_bit_and_int_max() {
let builtins = X86BuiltinMacros::new();
let macros = builtins.generate_map();
assert_eq!(macros.get("__CHAR_BIT__").map(|s| s.as_str()), Some("8"));
assert_eq!(
macros.get("__INT_MAX__").map(|s| s.as_str()),
Some("2147483647")
);
}
#[test]
fn test_include_resolver_clear_cache() {
let mut resolver = X86IncludeResolver::new();
let _ = resolver.resolve_system_include("nonexistent.h");
resolver.clear_cache();
}
#[test]
fn test_pragma_handler_gcc_visibility() {
let handler = X86PragmaHandler::new();
let result = handler.parse("GCC visibility push(hidden)");
assert_eq!(result, X86PragmaKind::GccVisibility("hidden".into()));
let result = handler.parse("GCC visibility push(default)");
assert_eq!(result, X86PragmaKind::GccVisibility("default".into()));
}
#[test]
fn test_pragma_handler_poison_empty() {
let handler = X86PragmaHandler::new();
let result = handler.parse("GCC poison");
assert_eq!(result, X86PragmaKind::GccPoison(vec![]));
}
#[test]
fn test_pragma_handler_poison_single() {
let handler = X86PragmaHandler::new();
let result = handler.parse("GCC poison gets");
assert_eq!(result, X86PragmaKind::GccPoison(vec!["gets".into()]));
}
#[test]
fn test_x86_feature_all_have_flag_names() {
use X86Feature::*;
let features = [MMX, SSE, SSE2, AVX, AVX2, AVX512F, FMA, BMI, POPCNT, AES];
for f in &features {
assert!(!f.flag_name().is_empty(), "{:?}", f);
}
}
#[test]
fn test_target_os_all_recognized() {
let tests = [
("linux", X86TargetOS::Linux),
("darwin", X86TargetOS::Darwin),
("freebsd", X86TargetOS::FreeBSD),
("openbsd", X86TargetOS::OpenBSD),
("netbsd", X86TargetOS::NetBSD),
("windows", X86TargetOS::Windows),
("win32", X86TargetOS::Windows),
("win64", X86TargetOS::Windows),
("cygwin", X86TargetOS::Cygwin),
("android", X86TargetOS::Android),
("emscripten", X86TargetOS::Emscripten),
("wasi", X86TargetOS::Wasi),
];
for (input, expected) in &tests {
assert_eq!(X86TargetOS::from_str(input), *expected, "input: {}", input);
}
}
#[test]
fn test_target_env_all_recognized() {
assert_eq!(X86TargetEnv::from_str("gnu"), X86TargetEnv::Gnu);
assert_eq!(X86TargetEnv::from_str("musl"), X86TargetEnv::Musl);
assert_eq!(X86TargetEnv::from_str("msvc"), X86TargetEnv::MSVC);
assert_eq!(X86TargetEnv::from_str("android"), X86TargetEnv::Bionic);
assert_eq!(X86TargetEnv::from_str("mingw"), X86TargetEnv::Mingw);
assert_eq!(X86TargetEnv::from_str("unknown"), X86TargetEnv::Unknown);
}
#[test]
fn test_microarch_level_ordering() {
assert!(X86MicroarchLevel::Baseline < X86MicroarchLevel::V2);
assert!(X86MicroarchLevel::V2 < X86MicroarchLevel::V3);
assert!(X86MicroarchLevel::V3 < X86MicroarchLevel::V4);
}
#[test]
fn test_feature_macro_def_consistency() {
for feature in &[
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE2,
X86Feature::SSE3,
X86Feature::AVX,
X86Feature::AVX2,
X86Feature::AVX512F,
] {
let def = feature.macro_def();
let flag = feature.flag_name();
assert!(def.is_some());
assert!(!flag.is_empty());
}
}
#[test]
fn test_preprocessor_process_simple_c_program() {
let mut pp = X86Preprocessor::new();
let source = "\
int main(void) {\n\
return 0;\n\
}\n";
let tokens = pp.process_source(source, "main.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(texts.contains(&"main"));
assert!(texts.contains(&"return"));
}
#[test]
fn test_process_empty_file() {
let mut pp = X86Preprocessor::new();
let tokens = pp.process_source("", "empty.c");
assert!(tokens.is_empty());
assert_eq!(pp.errors.len(), 0);
}
#[test]
fn test_process_only_comments() {
let mut pp = X86Preprocessor::new();
let tokens = pp.process_source("/* comment */ // line comment\n", "comments.c");
let non_trivia: Vec<_> = tokens
.iter()
.filter(|t| t.kind != TokenKind::Comment && t.kind != TokenKind::Newline)
.collect();
assert!(non_trivia.is_empty());
}
#[test]
fn test_builtin_macros_all_iterate() {
let builtins = X86BuiltinMacros::new();
let all = builtins.generate_all();
assert!(
all.len() > 100,
"Expected >100 builtin macros, got {}",
all.len()
);
}
#[test]
fn test_builtin_macros_no_duplicate_keys() {
let builtins = X86BuiltinMacros::new();
let all = builtins.generate_all();
let mut seen = HashSet::new();
for (name, _) in &all {
assert!(seen.insert(name.clone()), "Duplicate macro: {}", name);
}
}
#[test]
fn test_preprocessor_define_object_like_empty() {
let mut pp = X86Preprocessor::new();
pp.add_define("EMPTY", "");
let source = "int x = EMPTY;\n";
let tokens = pp.process_source(source, "test.c");
assert_eq!(pp.errors.len(), 0);
let _ = tokens;
}
#[test]
fn test_conditional_complex_expression() {
let builtins = X86BuiltinMacros::new();
let mut cond = X86ConditionalInclusion::new(&builtins);
cond.add_define("A", "2");
cond.add_define("B", "3");
let result = cond.evaluate_condition("(A * B) + 4 == 10");
assert_eq!(result, Some(true));
}
#[test]
fn test_conditional_defined_with_parentheses() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("defined(__STDC__) && defined(__x86_64__)");
assert_eq!(result, Some(true));
}
#[test]
fn test_conditional_bitwise_operations() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("0x0F & 0xF0");
assert_eq!(result, Some(false)); let result = cond.evaluate_condition("0x0F | 0xF0");
assert_eq!(result, Some(true)); }
#[test]
fn test_conditional_evaluate_precedence() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("0 || 0 && 1");
assert_eq!(result, Some(false)); let result = cond.evaluate_condition("1 || 0 && 1");
assert_eq!(result, Some(true)); }
#[test]
fn test_stringify_token_with_quotes() {
let sf = X86Stringification::new();
let token = make_token("hello", TokenKind::StringLiteral);
let result = sf.stringify(&[token]);
assert_eq!(result, "\"hello\"");
}
#[test]
fn test_stringify_token_with_backslash() {
let sf = X86Stringification::new();
let token = make_token("path\\file", TokenKind::Identifier);
let result = sf.stringify(&[token]);
assert!(result.contains("\\\\"));
}
#[test]
fn test_token_pasting_produces_identifier() {
let paster = X86TokenPasting::new();
let result = paster.paste(Some(&ident("my_")), Some(&ident("var")), &[], &[]);
assert!(result.is_some());
let tok = result.unwrap();
assert_eq!(tok.text, "my_var");
assert_eq!(tok.kind, TokenKind::Identifier);
}
#[test]
fn test_token_pasting_produces_keyword() {
let paster = X86TokenPasting::new();
let result = paster.paste(
Some(&make_token("unsign", TokenKind::Identifier)),
Some(&make_token("ed", TokenKind::Identifier)),
&[],
&[],
);
assert!(result.is_some());
let tok = result.unwrap();
assert_eq!(tok.text, "unsigned");
assert_eq!(tok.kind, TokenKind::KwUnsigned);
}
#[test]
fn test_macro_expander_parameter_substitution() {
let mut expander = X86MacroExpander::new();
expander.define_function_like("CONCAT", vec!["a".into(), "b".into()], "a b", false);
let tokens = vec![
ident("CONCAT"),
make_token("(", TokenKind::LParen),
ident("x"),
make_token(",", TokenKind::Comma),
ident("y"),
make_token(")", TokenKind::RParen),
];
let result = expander.expand(&tokens, None, 1);
let texts: Vec<&str> = result.iter().map(|t| t.text.as_str()).collect();
assert!(texts.contains(&"x"));
assert!(texts.contains(&"y"));
}
#[test]
fn test_preprocessor_conditional_state_transitions() {
let mut pp = X86Preprocessor::new();
assert!(!pp.should_skip());
pp.cond_stack.push(ConditionalBlockState::Skipping);
pp.skipping = true;
assert!(pp.should_skip());
pp.cond_stack.pop();
pp.cond_stack.push(ConditionalBlockState::SeenElse);
pp.skipping = false;
assert!(!pp.should_skip());
pp.cond_stack.pop();
pp.skipping = false;
assert!(!pp.should_skip());
assert_eq!(pp.cond_depth(), 0);
}
#[test]
fn test_builtin_macros_sizes_all_present() {
let builtins = X86BuiltinMacros::new();
let macros = builtins.generate_map();
for k in &[
"__SIZEOF_INT__",
"__SIZEOF_LONG__",
"__SIZEOF_POINTER__",
"__SIZEOF_SIZE_T__",
"__SIZEOF_WCHAR_T__",
"__SIZEOF_FLOAT__",
"__SIZEOF_DOUBLE__",
"__SIZEOF_LONG_DOUBLE__",
"__SIZEOF_SHORT__",
"__SIZEOF_CHAR__",
] {
assert!(macros.contains_key(*k), "Missing macro: {}", k);
}
}
#[test]
fn test_builtin_macros_atomic_all_present() {
let builtins = X86BuiltinMacros::new();
let macros = builtins.generate_map();
for k in &[
"__ATOMIC_RELAXED",
"__ATOMIC_CONSUME",
"__ATOMIC_ACQUIRE",
"__ATOMIC_RELEASE",
"__ATOMIC_ACQ_REL",
"__ATOMIC_SEQ_CST",
] {
assert!(macros.contains_key(*k), "Missing macro: {}", k);
}
}
#[test]
fn test_preprocessor_has_cpp_attribute_all_known() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
assert!(cond.has_cpp_attribute("deprecated"));
assert!(cond.has_cpp_attribute("noreturn"));
assert!(cond.has_cpp_attribute("maybe_unused"));
}
#[test]
fn test_preprocessor_has_attribute_all_known() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
assert!(cond.has_attribute("always_inline"));
assert!(cond.has_attribute("noinline"));
assert!(cond.has_attribute("packed"));
assert!(cond.has_attribute("weak"));
}
#[test]
fn test_macro_expander_backtrace() {
let expander = X86MacroExpander::new();
assert!(expander.backtrace().is_empty());
}
#[test]
fn test_token_flags_expanded() {
let mut expander = X86MacroExpander::new();
expander.define("MACRO_VAL", "42");
let result = expander.expand(&[ident("MACRO_VAL")], None, 1);
assert_eq!(result.len(), 1);
assert!(result[0].flags.is_expanded_from_macro);
}
#[test]
fn test_preprocessor_x86_feature_conditional() {
let mut pp = X86Preprocessor::new();
let source = "#ifdef __SSE2__\nint sse2_ok = 1;\n#endif\n";
let tokens = pp.process_source(source, "test.c");
let texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
assert!(texts.contains(&"sse2_ok"));
}
#[test]
fn test_directive_processor_line() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "line".into(),
arguments: vec![num("42")],
line: 5,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert!(output.diagnostics.is_empty());
assert_eq!(pp.line_directive.line_offset, Some(42));
}
#[test]
fn test_directive_processor_assert() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "assert".into(),
arguments: vec![ident("system(unix)")],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert_eq!(output.diagnostics.len(), 1);
}
#[test]
fn test_directive_processor_unassert() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "unassert".into(),
arguments: vec![ident("system(unix)")],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert_eq!(output.diagnostics.len(), 1);
}
#[test]
fn test_directive_processor_ident() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "ident".into(),
arguments: vec![make_token("\"myversion\"", TokenKind::StringLiteral)],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert_eq!(output.diagnostics.len(), 1);
assert_eq!(output.diagnostics[0].0, DiagnosticLevel::Note);
}
#[test]
fn test_directive_processor_sccs() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "sccs".into(),
arguments: vec![ident("whatever")],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert!(output.diagnostics.is_empty());
}
#[test]
fn test_x86_preprocessor_set_standard() {
let mut pp = X86Preprocessor::new();
pp.set_standard(CLangStandard::C11);
assert_eq!(pp.standard, CLangStandard::C11);
assert!(pp.conditional.is_defined("__STDC__"));
}
#[test]
fn test_x86_preprocessor_set_target() {
let mut pp = X86Preprocessor::new();
pp.set_target("i386-unknown-linux-gnu");
assert!(!pp.builtins.is_64bit);
assert!(pp.conditional.is_defined("__i386__"));
assert!(!pp.conditional.is_defined("__x86_64__"));
}
#[test]
fn test_include_resolver_set_sysroot() {
let mut resolver = X86IncludeResolver::new();
resolver.set_sysroot(Path::new("/custom/sysroot"));
assert_eq!(resolver.sysroot, Some(PathBuf::from("/custom/sysroot")));
}
#[test]
fn test_include_resolver_set_resource_dir() {
let mut resolver = X86IncludeResolver::new();
resolver.set_resource_dir(Path::new("/custom/resource"));
assert_eq!(
resolver.resource_dir,
Some(PathBuf::from("/custom/resource"))
);
}
#[test]
fn test_pragma_handler_system_header() {
let mut handler = X86PragmaHandler::new();
assert!(!handler.is_system_header());
handler.handle(&X86PragmaKind::GccSystemHeader);
assert!(handler.is_system_header());
}
#[test]
fn test_pragma_handler_comment_lib_accumulation() {
let mut handler = X86PragmaHandler::new();
handler.handle(&X86PragmaKind::CommentLib("kernel32.lib".into()));
handler.handle(&X86PragmaKind::CommentLib("user32.lib".into()));
assert_eq!(handler.comment_libs.len(), 2);
assert_eq!(handler.comment_libs[0], "kernel32.lib");
assert_eq!(handler.comment_libs[1], "user32.lib");
}
#[test]
fn test_x86_feature_implies_max_depth() {
let mut builtins = X86BuiltinMacros::from_triple("i386-unknown-linux-gnu");
let before = builtins.features.len();
builtins.enable_feature(X86Feature::AVX);
let after = builtins.features.len();
assert!(after > before, "Enabling AVX should pull in many features");
}
#[test]
fn test_microarch_v4_includes_avx512_features() {
let features = X86MicroarchLevel::V4.features();
assert!(features.contains(&X86Feature::AVX512F));
assert!(features.contains(&X86Feature::AVX512BW));
assert!(features.contains(&X86Feature::AVX512DQ));
assert!(features.contains(&X86Feature::AVX512VL));
}
#[test]
fn test_conditional_evaluate_token_pasting_in_condition() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("1 ## 2");
let _ = result;
}
#[test]
fn test_directive_processor_endif_without_if() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "endif".into(),
arguments: vec![],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert!(!output.diagnostics.is_empty());
assert_eq!(output.diagnostics[0].0, DiagnosticLevel::Error);
}
#[test]
fn test_directive_processor_else_without_if() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "else".into(),
arguments: vec![],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert!(!output.diagnostics.is_empty());
}
#[test]
fn test_directive_processor_elif_without_if() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
let input = DirectiveInput {
keyword: "elif".into(),
arguments: vec![num("1")],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert!(!output.diagnostics.is_empty());
}
#[test]
fn test_directive_processor_else_after_else() {
let dp = X86DirectiveProcessor::new();
let mut pp = X86Preprocessor::new();
let mut included = Vec::new();
let mut once_set = HashSet::new();
pp.cond_stack.push(ConditionalBlockState::SeenElse);
let input = DirectiveInput {
keyword: "else".into(),
arguments: vec![],
line: 1,
file: Some("test.c".into()),
in_skipped_block: false,
};
let output = dp.process(&input, &mut pp, &mut included, &mut once_set);
assert!(!output.diagnostics.is_empty());
}
#[test]
fn test_conditional_eval_hex_literals() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("0xFF == 255");
assert_eq!(result, Some(true));
let result = cond.evaluate_condition("0x10");
assert_eq!(result, Some(true)); }
#[test]
fn test_conditional_eval_octal_literals() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("010 == 8");
assert_eq!(result, Some(true));
}
#[test]
fn test_conditional_eval_character_literals() {
let builtins = X86BuiltinMacros::new();
let cond = X86ConditionalInclusion::new(&builtins);
let result = cond.evaluate_condition("'A' == 65");
let _ = result;
}
#[test]
fn test_builtin_macros_include_cpp_version_gnu_mode() {
let mut builtins = X86BuiltinMacros::new();
builtins.is_cxx = true;
let macros = builtins.generate_map();
assert!(macros.contains_key("__GNUG__"));
}
#[test]
fn test_builtin_macros_deprecated_present() {
let builtins = X86BuiltinMacros::new();
let macros = builtins.generate_map();
assert!(macros.contains_key("__DEPRECATED"));
}
#[test]
fn test_builtin_macros_exceptions_present() {
let builtins = X86BuiltinMacros::new();
let macros = builtins.generate_map();
assert!(macros.contains_key("__EXCEPTIONS"));
}
#[test]
fn test_preprocessor_errors_accumulate() {
let mut pp = X86Preprocessor::new();
let source = "#error one\n#error two\n";
let _ = pp.process_source(source, "test.c");
assert_eq!(pp.errors.len(), 2);
let errors = pp.take_errors();
assert_eq!(errors.len(), 2);
assert!(pp.errors.is_empty());
}
#[test]
fn test_stringify_with_leading_whitespace_tokens() {
let sf = X86Stringification::new();
let tokens = vec![num(" 42 ")];
let result = sf.stringify(&tokens);
assert!(result.contains("42"));
}
#[test]
fn test_preprocessor_macro_expansion_marks_flags() {
let mut pp = X86Preprocessor::new();
pp.add_define("FLAG", "1");
let source = "int x = FLAG;\n";
let tokens = pp.process_source(source, "test.c");
let ones: Vec<_> = tokens.iter().filter(|t| t.text == "1").collect();
assert!(!ones.is_empty());
for one in ones {
assert!(one.flags.is_expanded_from_macro);
}
}
#[test]
fn test_x86_preprocessor_default_errors_empty() {
let pp = X86Preprocessor::new();
assert!(pp.errors.is_empty());
assert!(pp.warnings.is_empty());
}
#[test]
fn test_x86_preprocessor_effective_file_default() {
let pp = X86Preprocessor::new();
assert_eq!(pp.effective_file(), "<unknown>");
}
#[test]
fn test_x86_preprocessor_effective_file_with_current() {
let mut pp = X86Preprocessor::new();
pp.current_file = Some(PathBuf::from("/src/main.c"));
assert_eq!(pp.effective_file(), "/src/main.c");
}
#[test]
fn test_feature_flag_name_format() {
for feature in &[
X86Feature::MMX,
X86Feature::SSE,
X86Feature::SSE41,
X86Feature::AVX512IFMA,
] {
let flag = feature.flag_name();
assert!(!flag.contains(' '), "Flag '{}' contains space", flag);
assert!(
flag.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '.' || c == '-'),
"Flag '{}' has invalid chars",
flag
);
}
}
#[test]
fn test_all_x86feature_variants_exist_in_enum() {
let keys = [
("sse", X86Feature::SSE),
("avx", X86Feature::AVX),
("avx2", X86Feature::AVX2),
("avx512f", X86Feature::AVX512F),
];
for (flag, expected) in &keys {
assert_eq!(expected.flag_name(), *flag);
}
}
#[test]
fn test_x86_preprocessor_set_cpu_skylake_avx512_level() {
let mut pp = X86Preprocessor::new();
pp.set_cpu("skylake-avx512");
assert_eq!(pp.builtins.microarch_level, X86MicroarchLevel::V4);
}
#[test]
fn test_x86_preprocessor_set_cpu_haswell_level() {
let mut pp = X86Preprocessor::new();
pp.set_cpu("haswell");
assert_eq!(pp.builtins.microarch_level, X86MicroarchLevel::V3);
}
#[test]
fn test_include_resolver_clear_pragma_once() {
let mut resolver = X86IncludeResolver::new();
let path = Path::new("/tmp/x.h");
resolver.mark_pragma_once(path);
assert!(resolver.is_pragma_once(path));
resolver.clear_pragma_once();
assert!(!resolver.is_pragma_once(path));
}
#[test]
fn test_builder_default_build_twice() {
let b = X86PreprocessorBuilder::new();
let pp1 = b.clone().build();
let pp2 = b.build();
assert!(pp1.conditional.is_defined("__STDC__"));
assert!(pp2.conditional.is_defined("__STDC__"));
}
}