#[allow(unused_imports)]
use llvm_native_core::codegen::*;
#[allow(unused_imports)]
use llvm_native_core::x86::*;
use std::collections::{BTreeMap, HashMap, HashSet};
pub const X86_DEFAULT_INLINE_THRESHOLD: u32 = 225;
pub const X86_INLINE_THRESHOLD_OS: u32 = 75;
pub const X86_INLINE_THRESHOLD_OZ: u32 = 25;
pub const X86_MAX_INLINE_THRESHOLD: u32 = 5000;
pub const X86_DEFAULT_UNROLL_THRESHOLD: u32 = 150;
pub const X86_MAX_UNROLL_COUNT: u32 = 8;
pub const X86_MIN_UNROLL_COUNT: u32 = 2;
pub const X86_VECTORIZE_MIN_TRIP_COUNT: u32 = 16;
pub const X86_L1_CACHE_LINE_SIZE: u32 = 64;
pub const X86_L2_CACHE_LINE_SIZE: u32 = 64;
pub const X86_BRANCH_MISPREDICT_PENALTY: u32 = 18;
pub const X86_MAX_JUMP_TABLE_SIZE: u32 = 4096;
pub const X86_JUMP_TABLE_DENSITY_THRESHOLD: f64 = 0.4;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[allow(non_camel_case_types)]
pub enum X86LibFunc {
Sin,
Cos,
Tan,
Asin,
Acos,
Atan,
Atan2,
Sinh,
Cosh,
Tanh,
Asinh,
Acosh,
Atanh,
Exp,
Exp2,
Exp10,
ExpM1,
Log,
Log2,
Log10,
Log1p,
LogB,
Freexp,
Ldexp,
Modf,
Scalbn,
Scalbln,
Sqrt,
Cbrt,
Hypot,
Pow,
PowI,
Erf,
Erfc,
Tgamma,
Lgamma,
LgammaR,
Fmod,
Remainder,
RemQuo,
Fabs,
Copysign,
Nextafter,
Nexttoward,
Fdim,
Fmax,
Fmin,
Fma,
Frexp,
Ceil,
Floor,
Trunc,
Round,
RoundEven,
Rint,
Nearbyint,
Lrint,
Llrint,
Lround,
Llround,
Ilogb,
Fpclassify,
IsFinite,
IsInf,
IsNan,
IsNormal,
Signbit,
SinF,
CosF,
TanF,
AsinF,
AcosF,
AtanF,
Atan2F,
SinhF,
CoshF,
TanhF,
ExpF,
Exp2F,
ExpM1F,
LogF,
Log2F,
Log10F,
Log1pF,
SqrtF,
CbrtF,
HypotF,
PowF,
ErfF,
ErfcF,
TgammaF,
LgammaF,
FmodF,
RemainderF,
FabsF,
CopysignF,
FdimF,
FmaxF,
FminF,
FmaF,
CeilF,
FloorF,
TruncF,
RoundF,
RintF,
NearbyintF,
SinL,
CosL,
TanL,
AsinL,
AcosL,
AtanL,
ExpL,
Exp2L,
LogL,
Log2L,
Log10L,
SqrtL,
CbrtL,
PowL,
FabsL,
CeilL,
FloorL,
TruncL,
RoundL,
Memcpy,
Memmove,
Memset,
Memcmp,
Memchr,
Memrchr,
Bcopy,
Bzero,
Bcmp,
Memccpy,
Mempcpy,
Strlen,
Strnlen,
Strcpy,
Strlcpy,
Strncpy,
Stpcpy,
Stpncpy,
Strcat,
Strncat,
Strcmp,
Strncmp,
Strcasecmp,
Strncasecmp,
Strchr,
Strrchr,
Strstr,
Strpbrk,
Strspn,
Strcspn,
Strsep,
Strtok,
Strdup,
Strndup,
Malloc,
Calloc,
Realloc,
Free,
AlignedAlloc,
PosixMemalign,
Memalign,
Valloc,
Pvalloc,
Abort,
Exit,
_Exit,
Atexit,
AtQuickExit,
QuickExit,
Getenv,
Setenv,
Unsetenv,
System,
Putenv,
Qsort,
Bsearch,
Abs,
Labs,
Llabs,
Imaxabs,
Div,
Ldiv,
Lldiv,
Imaxdiv,
Rand,
Srand,
RandR,
Srand48,
Drand48,
Lrand48,
Mrand48,
Atoi,
Atol,
Atoll,
Atof,
Strtol,
Strtoll,
Strtoul,
Strtoull,
Strtod,
Strtof,
Strtold,
Strtoimax,
Strtoumax,
Fopen,
Fclose,
Fflush,
Freopen,
Fdopen,
Fseek,
Ftell,
Rewind,
Fgetpos,
Fsetpos,
Fread,
Fwrite,
Fgetc,
Fputc,
Fgets,
Fputs,
Getc,
Putc,
Getchar,
Putchar,
Puts,
Gets,
Ungetc,
Clearerr,
Feof,
Ferror,
Perror,
Printf,
Fprintf,
Sprintf,
Snprintf,
Vprintf,
Vfprintf,
Vsprintf,
Vsnprintf,
Scanf,
Fscanf,
Sscanf,
Vscanf,
Vfscanf,
Vsscanf,
Asprintf,
Vasprintf,
Setbuf,
Setvbuf,
Fmemopen,
OpenMemstream,
Tmpfile,
Tmpnam,
Remove,
Rename,
Setjmp,
Longjmp,
Sigsetjmp,
Siglongjmp,
_Setjmp,
_Longjmp,
__Sigsetjmp,
PthreadCreate,
PthreadJoin,
PthreadDetach,
PthreadExit,
PthreadSelf,
PthreadEqual,
PthreadMutexInit,
PthreadMutexDestroy,
PthreadMutexLock,
PthreadMutexTrylock,
PthreadMutexUnlock,
PthreadCondInit,
PthreadCondDestroy,
PthreadCondSignal,
PthreadCondBroadcast,
PthreadCondWait,
PthreadOnce,
PthreadKeyCreate,
PthreadKeyDelete,
PthreadSetspecific,
PthreadGetspecific,
PthreadAttrInit,
PthreadAttrDestroy,
PthreadAttrSetDetachState,
PthreadAttrGetDetachState,
PthreadAttrSetStacksize,
PthreadAttrGetStacksize,
Signal,
Sigaction,
Kill,
Raise,
Alarm,
Sigemptyset,
Sigfillset,
Sigaddset,
Sigmask,
Sigpending,
Sigsuspend,
Sigwait,
SigInterrupt,
Psignal,
Fork,
Execve,
Execvp,
Getpid,
Getppid,
Gettid,
Getuid,
Geteuid,
Getgid,
Getegid,
Wait,
Waitpid,
Sleep,
Ualarm,
Nanosleep,
Usleep,
Close,
Read,
Write,
Open,
Creat,
Lseek,
Pread,
Pwrite,
Pipe,
Dup,
Dup2,
Fcntl,
Ioctl,
Fsync,
Fdatasync,
Chdir,
Getcwd,
Unlink,
Link,
Symlink,
Readlink,
Access,
Stat,
Fstat,
Lstat,
Mmap,
Munmap,
Mprotect,
Brk,
Sbrk,
Getpagesize,
Sysconf,
Syscall,
Clock,
Time,
Gettimeofday,
ClockGettime,
TimespecGet,
Gmtime,
GmtimeR,
Localtime,
LocaltimeR,
Mktime,
Strftime,
Strtime,
Ctime,
CtimeR,
Asctime,
AsctimeR,
Difftime,
Tzset,
TlsGetAddr,
Setlocale,
Localeconv,
Strcoll,
Strxfrm,
Strerror,
StrerrorR,
Fesetround,
Fegetround,
Feclearexcept,
Feraiseexcept,
Fetestexcept,
Fegetexceptflag,
Fesetexceptflag,
Feholdexcept,
Feupdateenv,
Fegetenv,
Fesetenv,
FetestexceptFlag,
UnwindResume,
EHTypeidFor,
_Unwind_Resume,
__builtin_memcpy,
__builtin_memmove,
__builtin_memset,
__builtin_memcmp,
__builtin_bzero,
__builtin_sqrt,
__builtin_sqrtf,
__builtin_sqrtl,
__builtin_pow,
__builtin_powf,
__builtin_sin,
__builtin_sinf,
__builtin_cos,
__builtin_cosf,
__builtin_exp,
__builtin_expf,
__builtin_log,
__builtin_logf,
__builtin_fabs,
__builtin_fabsf,
__builtin_floor,
__builtin_floorf,
__builtin_ceil,
__builtin_ceilf,
__svml_sinf4,
__svml_sinf8,
__svml_sinf16,
__svml_cosf4,
__svml_cosf8,
__svml_cosf16,
__svml_expf4,
__svml_expf8,
__svml_expf16,
__svml_logf4,
__svml_logf8,
__svml_logf16,
__svml_powf4,
__svml_powf8,
__svml_powf16,
__svml_sin4,
__svml_sin8,
__svml_cos4,
__svml_cos8,
__svml_exp4,
__svml_exp8,
__svml_log4,
__svml_log8,
__svml_pow4,
__svml_pow8,
__amdlibm_sinf,
__amdlibm_cosf,
__amdlibm_expf,
__amdlibm_logf,
__amdlibm_powf,
__amdlibm_sin,
__amdlibm_cos,
__amdlibm_exp,
__amdlibm_log,
__amdlibm_pow,
__sleef_sinf4_u10,
__sleef_cosf4_u10,
__sleef_expf4_u10,
__sleef_logf4_u10,
__sleef_sind4_u10,
__sleef_cosd4_u10,
__sleef_expd4_u10,
__sleef_logd4_u10,
_mm_sqrt_ps,
_mm_sqrt_pd,
_mm_rcp_ps,
_mm_rcp_ss,
_mm_rsqrt_ps,
_mm_rsqrt_ss,
_mm_sqrt_ss,
_mm_sqrt_sd,
_mm_rcp14_ps,
_mm_rsqrt14_ps,
_mm512_sqrt_ps,
_mm512_sqrt_pd,
__ReservedEnd,
}
impl X86LibFunc {
pub fn c_name(&self) -> &'static str {
match self {
X86LibFunc::Sin => "sin",
X86LibFunc::Cos => "cos",
X86LibFunc::Tan => "tan",
X86LibFunc::Asin => "asin",
X86LibFunc::Acos => "acos",
X86LibFunc::Atan => "atan",
X86LibFunc::Atan2 => "atan2",
X86LibFunc::Sinh => "sinh",
X86LibFunc::Cosh => "cosh",
X86LibFunc::Tanh => "tanh",
X86LibFunc::Asinh => "asinh",
X86LibFunc::Acosh => "acosh",
X86LibFunc::Atanh => "atanh",
X86LibFunc::Exp => "exp",
X86LibFunc::Exp2 => "exp2",
X86LibFunc::Exp10 => "exp10",
X86LibFunc::ExpM1 => "expm1",
X86LibFunc::Log => "log",
X86LibFunc::Log2 => "log2",
X86LibFunc::Log10 => "log10",
X86LibFunc::Log1p => "log1p",
X86LibFunc::LogB => "logb",
X86LibFunc::Freexp => "frexp",
X86LibFunc::Ldexp => "ldexp",
X86LibFunc::Modf => "modf",
X86LibFunc::Scalbn => "scalbn",
X86LibFunc::Scalbln => "scalbln",
X86LibFunc::Sqrt => "sqrt",
X86LibFunc::Cbrt => "cbrt",
X86LibFunc::Hypot => "hypot",
X86LibFunc::Pow => "pow",
X86LibFunc::PowI => "__powi",
X86LibFunc::Erf => "erf",
X86LibFunc::Erfc => "erfc",
X86LibFunc::Tgamma => "tgamma",
X86LibFunc::Lgamma => "lgamma",
X86LibFunc::LgammaR => "lgamma_r",
X86LibFunc::Fmod => "fmod",
X86LibFunc::Remainder => "remainder",
X86LibFunc::RemQuo => "remquo",
X86LibFunc::Fabs => "fabs",
X86LibFunc::Copysign => "copysign",
X86LibFunc::Nextafter => "nextafter",
X86LibFunc::Nexttoward => "nexttoward",
X86LibFunc::Fdim => "fdim",
X86LibFunc::Fmax => "fmax",
X86LibFunc::Fmin => "fmin",
X86LibFunc::Fma => "fma",
X86LibFunc::Ceil => "ceil",
X86LibFunc::Floor => "floor",
X86LibFunc::Trunc => "trunc",
X86LibFunc::Round => "round",
X86LibFunc::RoundEven => "roundeven",
X86LibFunc::Rint => "rint",
X86LibFunc::Nearbyint => "nearbyint",
X86LibFunc::Lrint => "lrint",
X86LibFunc::Llrint => "llrint",
X86LibFunc::Lround => "lround",
X86LibFunc::Llround => "llround",
X86LibFunc::Ilogb => "ilogb",
X86LibFunc::Fpclassify => "fpclassify",
X86LibFunc::IsFinite => "isfinite",
X86LibFunc::IsInf => "isinf",
X86LibFunc::IsNan => "isnan",
X86LibFunc::IsNormal => "isnormal",
X86LibFunc::Signbit => "signbit",
X86LibFunc::SinF => "sinf",
X86LibFunc::CosF => "cosf",
X86LibFunc::TanF => "tanf",
X86LibFunc::AsinF => "asinf",
X86LibFunc::AcosF => "acosf",
X86LibFunc::AtanF => "atanf",
X86LibFunc::Atan2F => "atan2f",
X86LibFunc::SinhF => "sinhf",
X86LibFunc::CoshF => "coshf",
X86LibFunc::TanhF => "tanhf",
X86LibFunc::ExpF => "expf",
X86LibFunc::Exp2F => "exp2f",
X86LibFunc::ExpM1F => "expm1f",
X86LibFunc::LogF => "logf",
X86LibFunc::Log2F => "log2f",
X86LibFunc::Log10F => "log10f",
X86LibFunc::Log1pF => "log1pf",
X86LibFunc::SqrtF => "sqrtf",
X86LibFunc::CbrtF => "cbrtf",
X86LibFunc::HypotF => "hypotf",
X86LibFunc::PowF => "powf",
X86LibFunc::ErfF => "erff",
X86LibFunc::ErfcF => "erfcf",
X86LibFunc::TgammaF => "tgammaf",
X86LibFunc::LgammaF => "lgammaf",
X86LibFunc::FmodF => "fmodf",
X86LibFunc::RemainderF => "remainderf",
X86LibFunc::FabsF => "fabsf",
X86LibFunc::CopysignF => "copysignf",
X86LibFunc::FdimF => "fdimf",
X86LibFunc::FmaxF => "fmaxf",
X86LibFunc::FminF => "fminf",
X86LibFunc::FmaF => "fmaf",
X86LibFunc::CeilF => "ceilf",
X86LibFunc::FloorF => "floorf",
X86LibFunc::TruncF => "truncf",
X86LibFunc::RoundF => "roundf",
X86LibFunc::RintF => "rintf",
X86LibFunc::NearbyintF => "nearbyintf",
X86LibFunc::SinL => "sinl",
X86LibFunc::CosL => "cosl",
X86LibFunc::TanL => "tanl",
X86LibFunc::AsinL => "asinl",
X86LibFunc::AcosL => "acosl",
X86LibFunc::AtanL => "atanl",
X86LibFunc::ExpL => "expl",
X86LibFunc::Exp2L => "exp2l",
X86LibFunc::LogL => "logl",
X86LibFunc::Log2L => "log2l",
X86LibFunc::Log10L => "log10l",
X86LibFunc::SqrtL => "sqrtl",
X86LibFunc::CbrtL => "cbrtl",
X86LibFunc::PowL => "powl",
X86LibFunc::FabsL => "fabsl",
X86LibFunc::CeilL => "ceill",
X86LibFunc::FloorL => "floorl",
X86LibFunc::TruncL => "truncl",
X86LibFunc::RoundL => "roundl",
X86LibFunc::Memcpy => "memcpy",
X86LibFunc::Memmove => "memmove",
X86LibFunc::Memset => "memset",
X86LibFunc::Memcmp => "memcmp",
X86LibFunc::Memchr => "memchr",
X86LibFunc::Memrchr => "memrchr",
X86LibFunc::Bcopy => "bcopy",
X86LibFunc::Bzero => "bzero",
X86LibFunc::Bcmp => "bcmp",
X86LibFunc::Memccpy => "memccpy",
X86LibFunc::Mempcpy => "mempcpy",
X86LibFunc::Strlen => "strlen",
X86LibFunc::Strnlen => "strnlen",
X86LibFunc::Strcpy => "strcpy",
X86LibFunc::Strlcpy => "strlcpy",
X86LibFunc::Strncpy => "strncpy",
X86LibFunc::Stpcpy => "stpcpy",
X86LibFunc::Stpncpy => "stpncpy",
X86LibFunc::Strcat => "strcat",
X86LibFunc::Strncat => "strncat",
X86LibFunc::Strcmp => "strcmp",
X86LibFunc::Strncmp => "strncmp",
X86LibFunc::Strcasecmp => "strcasecmp",
X86LibFunc::Strncasecmp => "strncasecmp",
X86LibFunc::Strchr => "strchr",
X86LibFunc::Strrchr => "strrchr",
X86LibFunc::Strstr => "strstr",
X86LibFunc::Strpbrk => "strpbrk",
X86LibFunc::Strspn => "strspn",
X86LibFunc::Strcspn => "strcspn",
X86LibFunc::Strsep => "strsep",
X86LibFunc::Strtok => "strtok",
X86LibFunc::Strdup => "strdup",
X86LibFunc::Strndup => "strndup",
X86LibFunc::Malloc => "malloc",
X86LibFunc::Calloc => "calloc",
X86LibFunc::Realloc => "realloc",
X86LibFunc::Free => "free",
X86LibFunc::AlignedAlloc => "aligned_alloc",
X86LibFunc::PosixMemalign => "posix_memalign",
X86LibFunc::Memalign => "memalign",
X86LibFunc::Valloc => "valloc",
X86LibFunc::Pvalloc => "pvalloc",
X86LibFunc::Abort => "abort",
X86LibFunc::Exit => "exit",
X86LibFunc::_Exit => "_Exit",
X86LibFunc::Atexit => "atexit",
X86LibFunc::AtQuickExit => "at_quick_exit",
X86LibFunc::QuickExit => "quick_exit",
X86LibFunc::Getenv => "getenv",
X86LibFunc::Setenv => "setenv",
X86LibFunc::Unsetenv => "unsetenv",
X86LibFunc::System => "system",
X86LibFunc::Putenv => "putenv",
X86LibFunc::Qsort => "qsort",
X86LibFunc::Bsearch => "bsearch",
X86LibFunc::Abs => "abs",
X86LibFunc::Labs => "labs",
X86LibFunc::Llabs => "llabs",
X86LibFunc::Imaxabs => "imaxabs",
X86LibFunc::Div => "div",
X86LibFunc::Ldiv => "ldiv",
X86LibFunc::Lldiv => "lldiv",
X86LibFunc::Imaxdiv => "imaxdiv",
X86LibFunc::Rand => "rand",
X86LibFunc::Srand => "srand",
X86LibFunc::RandR => "rand_r",
X86LibFunc::Srand48 => "srand48",
X86LibFunc::Drand48 => "drand48",
X86LibFunc::Lrand48 => "lrand48",
X86LibFunc::Mrand48 => "mrand48",
X86LibFunc::Atoi => "atoi",
X86LibFunc::Atol => "atol",
X86LibFunc::Atoll => "atoll",
X86LibFunc::Atof => "atof",
X86LibFunc::Strtol => "strtol",
X86LibFunc::Strtoll => "strtoll",
X86LibFunc::Strtoul => "strtoul",
X86LibFunc::Strtoull => "strtoull",
X86LibFunc::Strtod => "strtod",
X86LibFunc::Strtof => "strtof",
X86LibFunc::Strtold => "strtold",
X86LibFunc::Strtoimax => "strtoimax",
X86LibFunc::Strtoumax => "strtoumax",
X86LibFunc::Fopen => "fopen",
X86LibFunc::Fclose => "fclose",
X86LibFunc::Fflush => "fflush",
X86LibFunc::Freopen => "freopen",
X86LibFunc::Fdopen => "fdopen",
X86LibFunc::Fseek => "fseek",
X86LibFunc::Ftell => "ftell",
X86LibFunc::Rewind => "rewind",
X86LibFunc::Fgetpos => "fgetpos",
X86LibFunc::Fsetpos => "fsetpos",
X86LibFunc::Fread => "fread",
X86LibFunc::Fwrite => "fwrite",
X86LibFunc::Fgetc => "fgetc",
X86LibFunc::Fputc => "fputc",
X86LibFunc::Fgets => "fgets",
X86LibFunc::Fputs => "fputs",
X86LibFunc::Getc => "getc",
X86LibFunc::Putc => "putc",
X86LibFunc::Getchar => "getchar",
X86LibFunc::Putchar => "putchar",
X86LibFunc::Puts => "puts",
X86LibFunc::Gets => "gets",
X86LibFunc::Ungetc => "ungetc",
X86LibFunc::Clearerr => "clearerr",
X86LibFunc::Feof => "feof",
X86LibFunc::Ferror => "ferror",
X86LibFunc::Perror => "perror",
X86LibFunc::Printf => "printf",
X86LibFunc::Fprintf => "fprintf",
X86LibFunc::Sprintf => "sprintf",
X86LibFunc::Snprintf => "snprintf",
X86LibFunc::Vprintf => "vprintf",
X86LibFunc::Vfprintf => "vfprintf",
X86LibFunc::Vsprintf => "vsprintf",
X86LibFunc::Vsnprintf => "vsnprintf",
X86LibFunc::Scanf => "scanf",
X86LibFunc::Fscanf => "fscanf",
X86LibFunc::Sscanf => "sscanf",
X86LibFunc::Vscanf => "vscanf",
X86LibFunc::Vfscanf => "vfscanf",
X86LibFunc::Vsscanf => "vsscanf",
X86LibFunc::Asprintf => "asprintf",
X86LibFunc::Vasprintf => "vasprintf",
X86LibFunc::Setbuf => "setbuf",
X86LibFunc::Setvbuf => "setvbuf",
X86LibFunc::Fmemopen => "fmemopen",
X86LibFunc::OpenMemstream => "open_memstream",
X86LibFunc::Tmpfile => "tmpfile",
X86LibFunc::Tmpnam => "tmpnam",
X86LibFunc::Remove => "remove",
X86LibFunc::Rename => "rename",
X86LibFunc::Setjmp => "setjmp",
X86LibFunc::Longjmp => "longjmp",
X86LibFunc::Sigsetjmp => "sigsetjmp",
X86LibFunc::Siglongjmp => "siglongjmp",
X86LibFunc::_Setjmp => "_setjmp",
X86LibFunc::_Longjmp => "_longjmp",
X86LibFunc::__Sigsetjmp => "__sigsetjmp",
X86LibFunc::PthreadCreate => "pthread_create",
X86LibFunc::PthreadJoin => "pthread_join",
X86LibFunc::PthreadDetach => "pthread_detach",
X86LibFunc::PthreadExit => "pthread_exit",
X86LibFunc::PthreadSelf => "pthread_self",
X86LibFunc::PthreadEqual => "pthread_equal",
X86LibFunc::PthreadMutexInit => "pthread_mutex_init",
X86LibFunc::PthreadMutexDestroy => "pthread_mutex_destroy",
X86LibFunc::PthreadMutexLock => "pthread_mutex_lock",
X86LibFunc::PthreadMutexTrylock => "pthread_mutex_trylock",
X86LibFunc::PthreadMutexUnlock => "pthread_mutex_unlock",
X86LibFunc::PthreadCondInit => "pthread_cond_init",
X86LibFunc::PthreadCondDestroy => "pthread_cond_destroy",
X86LibFunc::PthreadCondSignal => "pthread_cond_signal",
X86LibFunc::PthreadCondBroadcast => "pthread_cond_broadcast",
X86LibFunc::PthreadCondWait => "pthread_cond_wait",
X86LibFunc::PthreadOnce => "pthread_once",
X86LibFunc::PthreadKeyCreate => "pthread_key_create",
X86LibFunc::PthreadKeyDelete => "pthread_key_delete",
X86LibFunc::PthreadSetspecific => "pthread_setspecific",
X86LibFunc::PthreadGetspecific => "pthread_getspecific",
X86LibFunc::PthreadAttrInit => "pthread_attr_init",
X86LibFunc::PthreadAttrDestroy => "pthread_attr_destroy",
X86LibFunc::PthreadAttrSetDetachState => "pthread_attr_setdetachstate",
X86LibFunc::PthreadAttrGetDetachState => "pthread_attr_getdetachstate",
X86LibFunc::PthreadAttrSetStacksize => "pthread_attr_setstacksize",
X86LibFunc::PthreadAttrGetStacksize => "pthread_attr_getstacksize",
X86LibFunc::Signal => "signal",
X86LibFunc::Sigaction => "sigaction",
X86LibFunc::Kill => "kill",
X86LibFunc::Raise => "raise",
X86LibFunc::Alarm => "alarm",
X86LibFunc::Sigemptyset => "sigemptyset",
X86LibFunc::Sigfillset => "sigfillset",
X86LibFunc::Sigaddset => "sigaddset",
X86LibFunc::Sigmask => "sigmask",
X86LibFunc::Sigpending => "sigpending",
X86LibFunc::Sigsuspend => "sigsuspend",
X86LibFunc::Sigwait => "sigwait",
X86LibFunc::SigInterrupt => "siginterrupt",
X86LibFunc::Psignal => "psignal",
X86LibFunc::Fork => "fork",
X86LibFunc::Execve => "execve",
X86LibFunc::Execvp => "execvp",
X86LibFunc::Getpid => "getpid",
X86LibFunc::Getppid => "getppid",
X86LibFunc::Gettid => "gettid",
X86LibFunc::Getuid => "getuid",
X86LibFunc::Geteuid => "geteuid",
X86LibFunc::Getgid => "getgid",
X86LibFunc::Getegid => "getegid",
X86LibFunc::Wait => "wait",
X86LibFunc::Waitpid => "waitpid",
X86LibFunc::Sleep => "sleep",
X86LibFunc::Ualarm => "ualarm",
X86LibFunc::Nanosleep => "nanosleep",
X86LibFunc::Usleep => "usleep",
X86LibFunc::Close => "close",
X86LibFunc::Read => "read",
X86LibFunc::Write => "write",
X86LibFunc::Open => "open",
X86LibFunc::Creat => "creat",
X86LibFunc::Lseek => "lseek",
X86LibFunc::Pread => "pread",
X86LibFunc::Pwrite => "pwrite",
X86LibFunc::Pipe => "pipe",
X86LibFunc::Dup => "dup",
X86LibFunc::Dup2 => "dup2",
X86LibFunc::Fcntl => "fcntl",
X86LibFunc::Ioctl => "ioctl",
X86LibFunc::Fsync => "fsync",
X86LibFunc::Fdatasync => "fdatasync",
X86LibFunc::Chdir => "chdir",
X86LibFunc::Getcwd => "getcwd",
X86LibFunc::Unlink => "unlink",
X86LibFunc::Link => "link",
X86LibFunc::Symlink => "symlink",
X86LibFunc::Readlink => "readlink",
X86LibFunc::Access => "access",
X86LibFunc::Stat => "stat",
X86LibFunc::Fstat => "fstat",
X86LibFunc::Lstat => "lstat",
X86LibFunc::Mmap => "mmap",
X86LibFunc::Munmap => "munmap",
X86LibFunc::Mprotect => "mprotect",
X86LibFunc::Brk => "brk",
X86LibFunc::Sbrk => "sbrk",
X86LibFunc::Getpagesize => "getpagesize",
X86LibFunc::Sysconf => "sysconf",
X86LibFunc::Syscall => "syscall",
X86LibFunc::Clock => "clock",
X86LibFunc::Time => "time",
X86LibFunc::Gettimeofday => "gettimeofday",
X86LibFunc::ClockGettime => "clock_gettime",
X86LibFunc::TimespecGet => "timespec_get",
X86LibFunc::Gmtime => "gmtime",
X86LibFunc::GmtimeR => "gmtime_r",
X86LibFunc::Localtime => "localtime",
X86LibFunc::LocaltimeR => "localtime_r",
X86LibFunc::Mktime => "mktime",
X86LibFunc::Strftime => "strftime",
X86LibFunc::Strtime => "strptime",
X86LibFunc::Ctime => "ctime",
X86LibFunc::CtimeR => "ctime_r",
X86LibFunc::Asctime => "asctime",
X86LibFunc::AsctimeR => "asctime_r",
X86LibFunc::Difftime => "difftime",
X86LibFunc::Tzset => "tzset",
X86LibFunc::TlsGetAddr => "__tls_get_addr",
X86LibFunc::Setlocale => "setlocale",
X86LibFunc::Localeconv => "localeconv",
X86LibFunc::Strcoll => "strcoll",
X86LibFunc::Strxfrm => "strxfrm",
X86LibFunc::Strerror => "strerror",
X86LibFunc::StrerrorR => "strerror_r",
X86LibFunc::Fesetround => "fesetround",
X86LibFunc::Fegetround => "fegetround",
X86LibFunc::Feclearexcept => "feclearexcept",
X86LibFunc::Feraiseexcept => "feraiseexcept",
X86LibFunc::Fetestexcept => "fetestexcept",
X86LibFunc::Fegetexceptflag => "fegetexceptflag",
X86LibFunc::Fesetexceptflag => "fesetexceptflag",
X86LibFunc::Feholdexcept => "feholdexcept",
X86LibFunc::Feupdateenv => "feupdateenv",
X86LibFunc::Fegetenv => "fegetenv",
X86LibFunc::Fesetenv => "fesetenv",
X86LibFunc::FetestexceptFlag => "fetestexceptflag",
X86LibFunc::UnwindResume => "_Unwind_Resume",
X86LibFunc::EHTypeidFor => "__cxa_typeid_for",
X86LibFunc::_Unwind_Resume => "_Unwind_Resume",
X86LibFunc::__builtin_memcpy => "__builtin_memcpy",
X86LibFunc::__builtin_memmove => "__builtin_memmove",
X86LibFunc::__builtin_memset => "__builtin_memset",
X86LibFunc::__builtin_memcmp => "__builtin_memcmp",
X86LibFunc::__builtin_bzero => "__builtin_bzero",
X86LibFunc::__builtin_sqrt => "__builtin_sqrt",
X86LibFunc::__builtin_sqrtf => "__builtin_sqrtf",
X86LibFunc::__builtin_sqrtl => "__builtin_sqrtl",
X86LibFunc::__builtin_pow => "__builtin_pow",
X86LibFunc::__builtin_powf => "__builtin_powf",
X86LibFunc::__builtin_sin => "__builtin_sin",
X86LibFunc::__builtin_sinf => "__builtin_sinf",
X86LibFunc::__builtin_cos => "__builtin_cos",
X86LibFunc::__builtin_cosf => "__builtin_cosf",
X86LibFunc::__builtin_exp => "__builtin_exp",
X86LibFunc::__builtin_expf => "__builtin_expf",
X86LibFunc::__builtin_log => "__builtin_log",
X86LibFunc::__builtin_logf => "__builtin_logf",
X86LibFunc::__builtin_fabs => "__builtin_fabs",
X86LibFunc::__builtin_fabsf => "__builtin_fabsf",
X86LibFunc::__builtin_floor => "__builtin_floor",
X86LibFunc::__builtin_floorf => "__builtin_floorf",
X86LibFunc::__builtin_ceil => "__builtin_ceil",
X86LibFunc::__builtin_ceilf => "__builtin_ceilf",
X86LibFunc::__svml_sinf4 => "__svml_sinf4",
X86LibFunc::__svml_sinf8 => "__svml_sinf8",
X86LibFunc::__svml_sinf16 => "__svml_sinf16",
X86LibFunc::__svml_cosf4 => "__svml_cosf4",
X86LibFunc::__svml_cosf8 => "__svml_cosf8",
X86LibFunc::__svml_cosf16 => "__svml_cosf16",
X86LibFunc::__svml_expf4 => "__svml_expf4",
X86LibFunc::__svml_expf8 => "__svml_expf8",
X86LibFunc::__svml_expf16 => "__svml_expf16",
X86LibFunc::__svml_logf4 => "__svml_logf4",
X86LibFunc::__svml_logf8 => "__svml_logf8",
X86LibFunc::__svml_logf16 => "__svml_logf16",
X86LibFunc::__svml_powf4 => "__svml_powf4",
X86LibFunc::__svml_powf8 => "__svml_powf8",
X86LibFunc::__svml_powf16 => "__svml_powf16",
X86LibFunc::__svml_sin4 => "__svml_sin4",
X86LibFunc::__svml_sin8 => "__svml_sin8",
X86LibFunc::__svml_cos4 => "__svml_cos4",
X86LibFunc::__svml_cos8 => "__svml_cos8",
X86LibFunc::__svml_exp4 => "__svml_exp4",
X86LibFunc::__svml_exp8 => "__svml_exp8",
X86LibFunc::__svml_log4 => "__svml_log4",
X86LibFunc::__svml_log8 => "__svml_log8",
X86LibFunc::__svml_pow4 => "__svml_pow4",
X86LibFunc::__svml_pow8 => "__svml_pow8",
X86LibFunc::__amdlibm_sinf => "__amdlibm_sinf",
X86LibFunc::__amdlibm_cosf => "__amdlibm_cosf",
X86LibFunc::__amdlibm_expf => "__amdlibm_expf",
X86LibFunc::__amdlibm_logf => "__amdlibm_logf",
X86LibFunc::__amdlibm_powf => "__amdlibm_powf",
X86LibFunc::__amdlibm_sin => "__amdlibm_sin",
X86LibFunc::__amdlibm_cos => "__amdlibm_cos",
X86LibFunc::__amdlibm_exp => "__amdlibm_exp",
X86LibFunc::__amdlibm_log => "__amdlibm_log",
X86LibFunc::__amdlibm_pow => "__amdlibm_pow",
X86LibFunc::__sleef_sinf4_u10 => "__sleef_sinf4_u10",
X86LibFunc::__sleef_cosf4_u10 => "__sleef_cosf4_u10",
X86LibFunc::__sleef_expf4_u10 => "__sleef_expf4_u10",
X86LibFunc::__sleef_logf4_u10 => "__sleef_logf4_u10",
X86LibFunc::__sleef_sind4_u10 => "__sleef_sind4_u10",
X86LibFunc::__sleef_cosd4_u10 => "__sleef_cosd4_u10",
X86LibFunc::__sleef_expd4_u10 => "__sleef_expd4_u10",
X86LibFunc::__sleef_logd4_u10 => "__sleef_logd4_u10",
X86LibFunc::_mm_sqrt_ps => "_mm_sqrt_ps",
X86LibFunc::_mm_sqrt_pd => "_mm_sqrt_pd",
X86LibFunc::_mm_rcp_ps => "_mm_rcp_ps",
X86LibFunc::_mm_rcp_ss => "_mm_rcp_ss",
X86LibFunc::_mm_rsqrt_ps => "_mm_rsqrt_ps",
X86LibFunc::_mm_rsqrt_ss => "_mm_rsqrt_ss",
X86LibFunc::_mm_sqrt_ss => "_mm_sqrt_ss",
X86LibFunc::_mm_sqrt_sd => "_mm_sqrt_sd",
X86LibFunc::_mm_rcp14_ps => "_mm_rcp14_ps",
X86LibFunc::_mm_rsqrt14_ps => "_mm_rsqrt14_ps",
X86LibFunc::_mm512_sqrt_ps => "_mm512_sqrt_ps",
X86LibFunc::_mm512_sqrt_pd => "_mm512_sqrt_pd",
X86LibFunc::Frexp => "frexp",
X86LibFunc::__ReservedEnd => "__reserved_end",
}
}
pub fn is_math_function(&self) -> bool {
use X86LibFunc::*;
matches!(
self,
Sin | Cos
| Tan
| Asin
| Acos
| Atan
| Atan2
| Sinh
| Cosh
| Tanh
| Asinh
| Acosh
| Atanh
| Exp
| Exp2
| Exp10
| ExpM1
| Log
| Log2
| Log10
| Log1p
| LogB
| Sqrt
| Cbrt
| Hypot
| Pow
| PowI
| Erf
| Erfc
| Tgamma
| Lgamma
| LgammaR
| Fmod
| Remainder
| Fabs
| Copysign
| Fdim
| Fmax
| Fmin
| Fma
| Ceil
| Floor
| Trunc
| Round
| RoundEven
| Rint
| Nearbyint
| Lrint
| Llrint
| Lround
| Llround
| Ilogb
| Freexp
| Ldexp
| Modf
| Scalbn
| Scalbln
| RemQuo
| Nextafter
| Nexttoward
)
}
pub fn is_float_variant(&self) -> bool {
self.c_name().ends_with('f') && !self.c_name().ends_with("dif") && self.c_name() != "vf"
}
pub fn is_double_variant(&self) -> bool {
let n = self.c_name();
!self.is_float_variant()
&& !n.ends_with('l')
&& (n == "sin"
|| n == "cos"
|| n == "tan"
|| n == "exp"
|| n == "log"
|| n == "sqrt"
|| n == "pow"
|| n == "fabs"
|| n == "ceil"
|| n == "floor"
|| n == "trunc"
|| n == "round"
|| n == "fma"
|| n == "fmax"
|| n == "fmin"
|| n == "hypot"
|| n == "sinh"
|| n == "cosh"
|| n == "tanh"
|| n == "asin"
|| n == "acos"
|| n == "atan"
|| n == "atan2"
|| n == "erf"
|| n == "erfc"
|| n == "tgamma"
|| n == "lgamma"
|| n == "fmod"
|| n == "remainder"
|| n == "fdim"
|| n == "copysign"
|| n == "rint"
|| n == "nearbyint"
|| n == "exp2"
|| n == "exp10"
|| n == "expm1"
|| n == "log2"
|| n == "log10"
|| n == "log1p"
|| n == "cbrt"
|| n == "asinh"
|| n == "acosh"
|| n == "atanh")
}
pub fn svml_vector_width(&self) -> Option<u32> {
match self {
X86LibFunc::__svml_sinf4
| X86LibFunc::__svml_cosf4
| X86LibFunc::__svml_expf4
| X86LibFunc::__svml_logf4
| X86LibFunc::__svml_powf4 => Some(4),
X86LibFunc::__svml_sinf8
| X86LibFunc::__svml_cosf8
| X86LibFunc::__svml_expf8
| X86LibFunc::__svml_logf8
| X86LibFunc::__svml_powf8
| X86LibFunc::__svml_sin4
| X86LibFunc::__svml_cos4
| X86LibFunc::__svml_exp4
| X86LibFunc::__svml_log4
| X86LibFunc::__svml_pow4 => Some(8),
X86LibFunc::__svml_sinf16
| X86LibFunc::__svml_cosf16
| X86LibFunc::__svml_expf16
| X86LibFunc::__svml_logf16
| X86LibFunc::__svml_powf16 => Some(16),
X86LibFunc::__svml_sin8
| X86LibFunc::__svml_cos8
| X86LibFunc::__svml_exp8
| X86LibFunc::__svml_log8
| X86LibFunc::__svml_pow8 => Some(8),
_ => None,
}
}
pub fn svml_scalar_counterpart(&self) -> Option<X86LibFunc> {
match self {
X86LibFunc::__svml_sinf4 | X86LibFunc::__svml_sinf8 | X86LibFunc::__svml_sinf16 => {
Some(X86LibFunc::SinF)
}
X86LibFunc::__svml_cosf4 | X86LibFunc::__svml_cosf8 | X86LibFunc::__svml_cosf16 => {
Some(X86LibFunc::CosF)
}
X86LibFunc::__svml_expf4 | X86LibFunc::__svml_expf8 | X86LibFunc::__svml_expf16 => {
Some(X86LibFunc::ExpF)
}
X86LibFunc::__svml_logf4 | X86LibFunc::__svml_logf8 | X86LibFunc::__svml_logf16 => {
Some(X86LibFunc::LogF)
}
X86LibFunc::__svml_powf4 | X86LibFunc::__svml_powf8 | X86LibFunc::__svml_powf16 => {
Some(X86LibFunc::PowF)
}
X86LibFunc::__svml_sin4 | X86LibFunc::__svml_sin8 => Some(X86LibFunc::Sin),
X86LibFunc::__svml_cos4 | X86LibFunc::__svml_cos8 => Some(X86LibFunc::Cos),
X86LibFunc::__svml_exp4 | X86LibFunc::__svml_exp8 => Some(X86LibFunc::Exp),
X86LibFunc::__svml_log4 | X86LibFunc::__svml_log8 => Some(X86LibFunc::Log),
X86LibFunc::__svml_pow4 | X86LibFunc::__svml_pow8 => Some(X86LibFunc::Pow),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum KnownFuncAttr {
Const,
ReadOnly,
WriteOnly,
ReadNone,
NoReturn,
NoUnwind,
NoCapture(u8), NoAlias,
NoFree,
WillReturn,
NoRecurse,
Returned(u8),
ArgMemOnly,
InaccessibleMemOnly,
Speculatable,
NoSync,
NoCalloc,
}
#[derive(Debug, Clone)]
pub struct X86FuncSignature {
pub name: &'static str,
pub param_count: i32,
pub param_types: &'static str,
pub return_type: &'static str,
pub attrs: Vec<KnownFuncAttr>,
}
impl X86FuncSignature {
pub fn new(
name: &'static str,
param_count: i32,
param_types: &'static str,
return_type: &'static str,
attrs: Vec<KnownFuncAttr>,
) -> Self {
X86FuncSignature {
name,
param_count,
param_types,
return_type,
attrs,
}
}
}
#[derive(Debug, Clone)]
pub struct X86VecFuncMapping {
pub scalar_name: String,
pub vector_width: u32,
pub vector_name: String,
pub library: X86VectorLibrary,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86VectorLibrary {
SVML,
AMDLibM,
SLEEF,
Accelerate,
LibMVec,
ArmPL,
AOCL,
LibSystem,
NoLibrary,
}
impl X86VectorLibrary {
pub fn is_accelerated(&self) -> bool {
!matches!(
self,
X86VectorLibrary::LibSystem | X86VectorLibrary::NoLibrary
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86CRTLib {
GLibC,
Musl,
MSVCRT,
Bionic,
LibSystem,
Newlib,
Generic,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86OSTarget {
Linux,
Windows,
MacOS,
FreeBSD,
Android,
Solaris,
NetBSD,
OpenBSD,
DragonFly,
Haiku,
Fuchsia,
Generic,
}
impl X86OSTarget {
pub fn default_crt(&self) -> X86CRTLib {
match self {
X86OSTarget::Linux => X86CRTLib::GLibC,
X86OSTarget::Windows => X86CRTLib::MSVCRT,
X86OSTarget::MacOS => X86CRTLib::LibSystem,
X86OSTarget::FreeBSD => X86CRTLib::GLibC,
X86OSTarget::Android => X86CRTLib::Bionic,
X86OSTarget::Solaris => X86CRTLib::GLibC,
X86OSTarget::NetBSD => X86CRTLib::GLibC,
X86OSTarget::OpenBSD => X86CRTLib::GLibC,
X86OSTarget::DragonFly => X86CRTLib::GLibC,
X86OSTarget::Haiku => X86CRTLib::GLibC,
X86OSTarget::Fuchsia => X86CRTLib::GLibC,
X86OSTarget::Generic => X86CRTLib::Generic,
}
}
}
pub struct X86TargetLibraryInfo {
pub os_target: X86OSTarget,
pub crt_lib: X86CRTLib,
pub available_funcs: HashSet<X86LibFunc>,
pub unavailable_funcs: HashSet<X86LibFunc>,
pub vector_mappings: Vec<X86VecFuncMapping>,
pub memcpy_size_arg: i32,
pub memset_size_arg: i32,
pub memmove_size_arg: i32,
pub signatures: HashMap<X86LibFunc, X86FuncSignature>,
pub preferred_vector_library: X86VectorLibrary,
pub use_svml: bool,
pub use_amdlibm: bool,
pub use_sleef: bool,
}
impl X86TargetLibraryInfo {
pub fn new(os_target: X86OSTarget) -> Self {
let crt_lib = os_target.default_crt();
let mut tli = X86TargetLibraryInfo {
os_target,
crt_lib,
available_funcs: HashSet::new(),
unavailable_funcs: HashSet::new(),
vector_mappings: Vec::new(),
memcpy_size_arg: -1,
memset_size_arg: -1,
memmove_size_arg: -1,
signatures: HashMap::new(),
preferred_vector_library: X86VectorLibrary::NoLibrary,
use_svml: false,
use_amdlibm: false,
use_sleef: false,
};
tli.init_platform_defaults();
tli.init_signatures();
tli.init_size_args();
tli
}
fn init_platform_defaults(&mut self) {
let c89_math: &[X86LibFunc] = &[
X86LibFunc::Sin,
X86LibFunc::Cos,
X86LibFunc::Tan,
X86LibFunc::Asin,
X86LibFunc::Acos,
X86LibFunc::Atan,
X86LibFunc::Atan2,
X86LibFunc::Sinh,
X86LibFunc::Cosh,
X86LibFunc::Tanh,
X86LibFunc::Exp,
X86LibFunc::Log,
X86LibFunc::Log10,
X86LibFunc::Sqrt,
X86LibFunc::Fabs,
X86LibFunc::Pow,
X86LibFunc::Ceil,
X86LibFunc::Floor,
X86LibFunc::Fmod,
X86LibFunc::Frexp,
X86LibFunc::Ldexp,
X86LibFunc::Modf,
X86LibFunc::Fmax,
X86LibFunc::Fmin,
X86LibFunc::Round,
X86LibFunc::Trunc,
X86LibFunc::Rint,
X86LibFunc::Nearbyint,
];
let c89_string: &[X86LibFunc] = &[
X86LibFunc::Memcpy,
X86LibFunc::Memmove,
X86LibFunc::Memset,
X86LibFunc::Memcmp,
X86LibFunc::Memchr,
X86LibFunc::Strlen,
X86LibFunc::Strcpy,
X86LibFunc::Strncpy,
X86LibFunc::Strcat,
X86LibFunc::Strncat,
X86LibFunc::Strcmp,
X86LibFunc::Strncmp,
X86LibFunc::Strchr,
X86LibFunc::Strrchr,
X86LibFunc::Strstr,
X86LibFunc::Strspn,
X86LibFunc::Strcspn,
X86LibFunc::Strpbrk,
X86LibFunc::Strtok,
X86LibFunc::Strerror,
];
let c89_stdlib: &[X86LibFunc] = &[
X86LibFunc::Malloc,
X86LibFunc::Free,
X86LibFunc::Calloc,
X86LibFunc::Realloc,
X86LibFunc::Abort,
X86LibFunc::Exit,
X86LibFunc::Atexit,
X86LibFunc::Getenv,
X86LibFunc::System,
X86LibFunc::Qsort,
X86LibFunc::Bsearch,
X86LibFunc::Abs,
X86LibFunc::Labs,
X86LibFunc::Div,
X86LibFunc::Ldiv,
X86LibFunc::Rand,
X86LibFunc::Srand,
X86LibFunc::Atoi,
X86LibFunc::Atol,
X86LibFunc::Strtod,
X86LibFunc::Strtol,
X86LibFunc::Strtoul,
];
let c89_stdio: &[X86LibFunc] = &[
X86LibFunc::Fopen,
X86LibFunc::Fclose,
X86LibFunc::Fflush,
X86LibFunc::Freopen,
X86LibFunc::Fseek,
X86LibFunc::Ftell,
X86LibFunc::Rewind,
X86LibFunc::Fread,
X86LibFunc::Fwrite,
X86LibFunc::Fgetc,
X86LibFunc::Fputc,
X86LibFunc::Fgets,
X86LibFunc::Fputs,
X86LibFunc::Getc,
X86LibFunc::Putc,
X86LibFunc::Getchar,
X86LibFunc::Putchar,
X86LibFunc::Puts,
X86LibFunc::Gets,
X86LibFunc::Ungetc,
X86LibFunc::Clearerr,
X86LibFunc::Feof,
X86LibFunc::Ferror,
X86LibFunc::Perror,
X86LibFunc::Printf,
X86LibFunc::Fprintf,
X86LibFunc::Sprintf,
X86LibFunc::Scanf,
X86LibFunc::Fscanf,
X86LibFunc::Sscanf,
X86LibFunc::Vprintf,
X86LibFunc::Vfprintf,
X86LibFunc::Vsprintf,
X86LibFunc::Setbuf,
X86LibFunc::Setvbuf,
X86LibFunc::Tmpfile,
X86LibFunc::Tmpnam,
X86LibFunc::Remove,
X86LibFunc::Rename,
X86LibFunc::Fgetpos,
X86LibFunc::Fsetpos,
];
for f in c89_math {
self.available_funcs.insert(*f);
}
for f in c89_string {
self.available_funcs.insert(*f);
}
for f in c89_stdlib {
self.available_funcs.insert(*f);
}
for f in c89_stdio {
self.available_funcs.insert(*f);
}
if !matches!(self.crt_lib, X86CRTLib::MSVCRT) {
let c99_math: &[X86LibFunc] = &[
X86LibFunc::SinF,
X86LibFunc::CosF,
X86LibFunc::TanF,
X86LibFunc::AsinF,
X86LibFunc::AcosF,
X86LibFunc::AtanF,
X86LibFunc::Atan2F,
X86LibFunc::SinhF,
X86LibFunc::CoshF,
X86LibFunc::TanhF,
X86LibFunc::ExpF,
X86LibFunc::Exp2F,
X86LibFunc::ExpM1F,
X86LibFunc::LogF,
X86LibFunc::Log2F,
X86LibFunc::Log10F,
X86LibFunc::Log1pF,
X86LibFunc::SqrtF,
X86LibFunc::CbrtF,
X86LibFunc::HypotF,
X86LibFunc::PowF,
X86LibFunc::FabsF,
X86LibFunc::FmodF,
X86LibFunc::CeilF,
X86LibFunc::FloorF,
X86LibFunc::TruncF,
X86LibFunc::RoundF,
X86LibFunc::RintF,
X86LibFunc::NearbyintF,
X86LibFunc::FmaF,
X86LibFunc::FmaxF,
X86LibFunc::FminF,
X86LibFunc::FdimF,
X86LibFunc::RemainderF,
X86LibFunc::CopysignF,
X86LibFunc::ErfF,
X86LibFunc::ErfcF,
X86LibFunc::TgammaF,
X86LibFunc::LgammaF,
X86LibFunc::Exp2,
X86LibFunc::ExpM1,
X86LibFunc::Log2,
X86LibFunc::Log1p,
X86LibFunc::Cbrt,
X86LibFunc::Hypot,
X86LibFunc::Erf,
X86LibFunc::Erfc,
X86LibFunc::Tgamma,
X86LibFunc::Lgamma,
X86LibFunc::LgammaR,
X86LibFunc::Fma,
X86LibFunc::Fdim,
X86LibFunc::Copysign,
X86LibFunc::Remainder,
X86LibFunc::RemQuo,
X86LibFunc::Nextafter,
X86LibFunc::Nexttoward,
X86LibFunc::RoundEven,
X86LibFunc::Lrint,
X86LibFunc::Llrint,
X86LibFunc::Lround,
X86LibFunc::Llround,
X86LibFunc::Ilogb,
X86LibFunc::LogB,
X86LibFunc::Scalbn,
X86LibFunc::Scalbln,
X86LibFunc::Fpclassify,
X86LibFunc::IsFinite,
X86LibFunc::IsInf,
X86LibFunc::IsNan,
X86LibFunc::IsNormal,
X86LibFunc::Signbit,
];
for f in c99_math {
self.available_funcs.insert(*f);
}
let c99_string_extra: &[X86LibFunc] = &[
X86LibFunc::Strnlen,
X86LibFunc::Stpcpy,
X86LibFunc::Stpncpy,
X86LibFunc::Strdup,
X86LibFunc::Strndup,
X86LibFunc::Strcasecmp,
X86LibFunc::Strncasecmp,
X86LibFunc::Memrchr,
];
for f in c99_string_extra {
self.available_funcs.insert(*f);
}
let c99_stdlib_extra: &[X86LibFunc] = &[
X86LibFunc::Llabs,
X86LibFunc::Lldiv,
X86LibFunc::Atoll,
X86LibFunc::Strtof,
X86LibFunc::Strtold,
X86LibFunc::Strtoll,
X86LibFunc::Strtoull,
];
for f in c99_stdlib_extra {
self.available_funcs.insert(*f);
}
let c99_stdio_extra: &[X86LibFunc] = &[
X86LibFunc::Snprintf,
X86LibFunc::Vsnprintf,
X86LibFunc::Vscanf,
X86LibFunc::Vfscanf,
X86LibFunc::Vsscanf,
];
for f in c99_stdio_extra {
self.available_funcs.insert(*f);
}
}
if !matches!(self.crt_lib, X86CRTLib::MSVCRT) {
let ld_math: &[X86LibFunc] = &[
X86LibFunc::SinL,
X86LibFunc::CosL,
X86LibFunc::TanL,
X86LibFunc::AsinL,
X86LibFunc::AcosL,
X86LibFunc::AtanL,
X86LibFunc::ExpL,
X86LibFunc::Exp2L,
X86LibFunc::LogL,
X86LibFunc::Log2L,
X86LibFunc::Log10L,
X86LibFunc::SqrtL,
X86LibFunc::CbrtL,
X86LibFunc::PowL,
X86LibFunc::FabsL,
X86LibFunc::CeilL,
X86LibFunc::FloorL,
X86LibFunc::TruncL,
X86LibFunc::RoundL,
];
for f in ld_math {
self.available_funcs.insert(*f);
}
}
match self.os_target {
X86OSTarget::Linux => {
let linux_extras: &[X86LibFunc] = &[
X86LibFunc::Bcopy,
X86LibFunc::Bzero,
X86LibFunc::Bcmp,
X86LibFunc::Memccpy,
X86LibFunc::Mempcpy,
X86LibFunc::Memalign,
X86LibFunc::Valloc,
X86LibFunc::Pvalloc,
X86LibFunc::PosixMemalign,
X86LibFunc::AlignedAlloc,
X86LibFunc::Asprintf,
X86LibFunc::Vasprintf,
X86LibFunc::Setenv,
X86LibFunc::Unsetenv,
X86LibFunc::Putenv,
X86LibFunc::Strsep,
X86LibFunc::StrerrorR,
X86LibFunc::Dup,
X86LibFunc::Dup2,
X86LibFunc::Mmap,
X86LibFunc::Munmap,
X86LibFunc::Brk,
X86LibFunc::Sbrk,
X86LibFunc::Getpagesize,
X86LibFunc::Sysconf,
X86LibFunc::Gettimeofday,
X86LibFunc::ClockGettime,
X86LibFunc::GmtimeR,
X86LibFunc::LocaltimeR,
X86LibFunc::CtimeR,
X86LibFunc::AsctimeR,
X86LibFunc::Setjmp,
X86LibFunc::Longjmp,
X86LibFunc::Sigsetjmp,
X86LibFunc::Siglongjmp,
X86LibFunc::_Setjmp,
X86LibFunc::_Longjmp,
X86LibFunc::AtQuickExit,
X86LibFunc::QuickExit,
X86LibFunc::Strtoimax,
X86LibFunc::Strtoumax,
X86LibFunc::Imaxabs,
X86LibFunc::Imaxdiv,
X86LibFunc::Fmemopen,
X86LibFunc::OpenMemstream,
X86LibFunc::RandR,
X86LibFunc::Srand48,
X86LibFunc::Drand48,
X86LibFunc::Lrand48,
X86LibFunc::Mrand48,
X86LibFunc::TlsGetAddr,
];
for f in linux_extras {
self.available_funcs.insert(*f);
}
let signals: &[X86LibFunc] = &[
X86LibFunc::Signal,
X86LibFunc::Sigaction,
X86LibFunc::Kill,
X86LibFunc::Raise,
X86LibFunc::Alarm,
X86LibFunc::Sigemptyset,
X86LibFunc::Sigfillset,
X86LibFunc::Sigaddset,
X86LibFunc::Sigmask,
X86LibFunc::Sigpending,
X86LibFunc::Sigsuspend,
X86LibFunc::Sigwait,
];
for f in signals {
self.available_funcs.insert(*f);
}
let posix_io: &[X86LibFunc] = &[
X86LibFunc::Open,
X86LibFunc::Creat,
X86LibFunc::Close,
X86LibFunc::Read,
X86LibFunc::Write,
X86LibFunc::Lseek,
X86LibFunc::Pread,
X86LibFunc::Pwrite,
X86LibFunc::Pipe,
X86LibFunc::Fcntl,
X86LibFunc::Ioctl,
X86LibFunc::Fsync,
X86LibFunc::Fdatasync,
X86LibFunc::Chdir,
X86LibFunc::Getcwd,
X86LibFunc::Unlink,
X86LibFunc::Link,
X86LibFunc::Symlink,
X86LibFunc::Readlink,
X86LibFunc::Access,
X86LibFunc::Stat,
X86LibFunc::Fstat,
X86LibFunc::Lstat,
X86LibFunc::Mprotect,
];
for f in posix_io {
self.available_funcs.insert(*f);
}
let posix_proc: &[X86LibFunc] = &[
X86LibFunc::Fork,
X86LibFunc::Execve,
X86LibFunc::Execvp,
X86LibFunc::Wait,
X86LibFunc::Waitpid,
X86LibFunc::Getpid,
X86LibFunc::Getppid,
X86LibFunc::Gettid,
X86LibFunc::Getuid,
X86LibFunc::Geteuid,
X86LibFunc::Getgid,
X86LibFunc::Getegid,
X86LibFunc::Sleep,
X86LibFunc::Nanosleep,
X86LibFunc::Usleep,
X86LibFunc::Ualarm,
X86LibFunc::Syscall,
];
for f in posix_proc {
self.available_funcs.insert(*f);
}
let pthreads: &[X86LibFunc] = &[
X86LibFunc::PthreadCreate,
X86LibFunc::PthreadJoin,
X86LibFunc::PthreadDetach,
X86LibFunc::PthreadExit,
X86LibFunc::PthreadSelf,
X86LibFunc::PthreadEqual,
X86LibFunc::PthreadMutexInit,
X86LibFunc::PthreadMutexDestroy,
X86LibFunc::PthreadMutexLock,
X86LibFunc::PthreadMutexTrylock,
X86LibFunc::PthreadMutexUnlock,
X86LibFunc::PthreadCondInit,
X86LibFunc::PthreadCondDestroy,
X86LibFunc::PthreadCondSignal,
X86LibFunc::PthreadCondBroadcast,
X86LibFunc::PthreadCondWait,
X86LibFunc::PthreadOnce,
X86LibFunc::PthreadKeyCreate,
X86LibFunc::PthreadKeyDelete,
X86LibFunc::PthreadSetspecific,
X86LibFunc::PthreadGetspecific,
X86LibFunc::PthreadAttrInit,
X86LibFunc::PthreadAttrDestroy,
X86LibFunc::PthreadAttrSetDetachState,
X86LibFunc::PthreadAttrGetDetachState,
X86LibFunc::PthreadAttrSetStacksize,
X86LibFunc::PthreadAttrGetStacksize,
];
for f in pthreads {
self.available_funcs.insert(*f);
}
self.preferred_vector_library = X86VectorLibrary::LibMVec;
self.use_svml = true;
}
X86OSTarget::Windows => {
let win_extras: &[X86LibFunc] = &[
X86LibFunc::_Exit,
X86LibFunc::Memccpy,
X86LibFunc::Strdup,
X86LibFunc::Strndup,
X86LibFunc::Strcasecmp,
X86LibFunc::Strncasecmp,
];
for f in win_extras {
self.available_funcs.insert(*f);
}
let win_signals: &[X86LibFunc] = &[X86LibFunc::Signal, X86LibFunc::Raise];
for f in win_signals {
self.available_funcs.insert(*f);
}
let win_threads: &[X86LibFunc] = &[X86LibFunc::TlsGetAddr];
for f in win_threads {
self.available_funcs.insert(*f);
}
self.preferred_vector_library = X86VectorLibrary::NoLibrary;
}
X86OSTarget::MacOS => {
let mac_extras: &[X86LibFunc] = &[
X86LibFunc::Bzero,
X86LibFunc::Bcopy,
X86LibFunc::Memccpy,
X86LibFunc::Asprintf,
X86LibFunc::Vasprintf,
X86LibFunc::Setenv,
X86LibFunc::Unsetenv,
X86LibFunc::StrerrorR,
X86LibFunc::Strsep,
X86LibFunc::Strlcpy, X86LibFunc::PosixMemalign,
X86LibFunc::AlignedAlloc,
X86LibFunc::Gettimeofday,
X86LibFunc::ClockGettime,
X86LibFunc::GmtimeR,
X86LibFunc::LocaltimeR,
X86LibFunc::CtimeR,
X86LibFunc::AsctimeR,
X86LibFunc::Fmemopen,
X86LibFunc::OpenMemstream,
X86LibFunc::TlsGetAddr,
];
for f in mac_extras {
self.available_funcs.insert(*f);
}
let mac_posix: &[X86LibFunc] = &[
X86LibFunc::Fork,
X86LibFunc::Execve,
X86LibFunc::Execvp,
X86LibFunc::Wait,
X86LibFunc::Waitpid,
X86LibFunc::Getpid,
X86LibFunc::Getppid,
X86LibFunc::Getuid,
X86LibFunc::Geteuid,
X86LibFunc::Getgid,
X86LibFunc::Getegid,
X86LibFunc::Sleep,
X86LibFunc::Nanosleep,
X86LibFunc::Usleep,
X86LibFunc::Open,
X86LibFunc::Close,
X86LibFunc::Read,
X86LibFunc::Write,
X86LibFunc::Lseek,
X86LibFunc::Pread,
X86LibFunc::Pwrite,
X86LibFunc::Pipe,
X86LibFunc::Fcntl,
X86LibFunc::Ioctl,
X86LibFunc::Fsync,
X86LibFunc::Fdatasync,
X86LibFunc::Chdir,
X86LibFunc::Getcwd,
X86LibFunc::Unlink,
X86LibFunc::Link,
X86LibFunc::Symlink,
X86LibFunc::Readlink,
X86LibFunc::Access,
X86LibFunc::Stat,
X86LibFunc::Fstat,
X86LibFunc::Lstat,
X86LibFunc::Mmap,
X86LibFunc::Munmap,
X86LibFunc::Mprotect,
];
for f in mac_posix {
self.available_funcs.insert(*f);
}
let mac_pthreads: &[X86LibFunc] = &[
X86LibFunc::PthreadCreate,
X86LibFunc::PthreadJoin,
X86LibFunc::PthreadDetach,
X86LibFunc::PthreadExit,
X86LibFunc::PthreadSelf,
X86LibFunc::PthreadEqual,
X86LibFunc::PthreadMutexInit,
X86LibFunc::PthreadMutexDestroy,
X86LibFunc::PthreadMutexLock,
X86LibFunc::PthreadMutexTrylock,
X86LibFunc::PthreadMutexUnlock,
X86LibFunc::PthreadCondInit,
X86LibFunc::PthreadCondDestroy,
X86LibFunc::PthreadCondSignal,
X86LibFunc::PthreadCondBroadcast,
X86LibFunc::PthreadCondWait,
X86LibFunc::PthreadOnce,
X86LibFunc::PthreadKeyCreate,
X86LibFunc::PthreadKeyDelete,
X86LibFunc::PthreadSetspecific,
X86LibFunc::PthreadGetspecific,
];
for f in mac_pthreads {
self.available_funcs.insert(*f);
}
self.preferred_vector_library = X86VectorLibrary::Accelerate;
}
X86OSTarget::Android => {
let bionic_extras: &[X86LibFunc] = &[
X86LibFunc::Bcopy,
X86LibFunc::Bzero,
X86LibFunc::Memccpy,
X86LibFunc::Mempcpy,
X86LibFunc::Memalign,
X86LibFunc::PosixMemalign,
X86LibFunc::AlignedAlloc,
X86LibFunc::Getpagesize,
X86LibFunc::Sysconf,
X86LibFunc::Gettimeofday,
X86LibFunc::ClockGettime,
X86LibFunc::GmtimeR,
X86LibFunc::LocaltimeR,
X86LibFunc::CtimeR,
X86LibFunc::AsctimeR,
X86LibFunc::TlsGetAddr,
];
for f in bionic_extras {
self.available_funcs.insert(*f);
}
let bionic_posix: &[X86LibFunc] = &[
X86LibFunc::Fork,
X86LibFunc::Execve,
X86LibFunc::Wait,
X86LibFunc::Waitpid,
X86LibFunc::Getpid,
X86LibFunc::Getppid,
X86LibFunc::Gettid,
X86LibFunc::Getuid,
X86LibFunc::Geteuid,
X86LibFunc::Getgid,
X86LibFunc::Getegid,
X86LibFunc::Sleep,
X86LibFunc::Nanosleep,
X86LibFunc::Usleep,
X86LibFunc::Open,
X86LibFunc::Close,
X86LibFunc::Read,
X86LibFunc::Write,
X86LibFunc::Lseek,
X86LibFunc::Pread,
X86LibFunc::Pwrite,
X86LibFunc::Pipe,
X86LibFunc::Fcntl,
X86LibFunc::Fsync,
X86LibFunc::Chdir,
X86LibFunc::Getcwd,
X86LibFunc::Unlink,
X86LibFunc::Link,
X86LibFunc::Symlink,
X86LibFunc::Readlink,
X86LibFunc::Access,
X86LibFunc::Stat,
X86LibFunc::Fstat,
X86LibFunc::Lstat,
X86LibFunc::Mmap,
X86LibFunc::Munmap,
X86LibFunc::Mprotect,
];
for f in bionic_posix {
self.available_funcs.insert(*f);
}
let bionic_pthreads: &[X86LibFunc] = &[
X86LibFunc::PthreadCreate,
X86LibFunc::PthreadJoin,
X86LibFunc::PthreadDetach,
X86LibFunc::PthreadExit,
X86LibFunc::PthreadSelf,
X86LibFunc::PthreadEqual,
X86LibFunc::PthreadMutexInit,
X86LibFunc::PthreadMutexDestroy,
X86LibFunc::PthreadMutexLock,
X86LibFunc::PthreadMutexTrylock,
X86LibFunc::PthreadMutexUnlock,
X86LibFunc::PthreadCondInit,
X86LibFunc::PthreadCondDestroy,
X86LibFunc::PthreadCondSignal,
X86LibFunc::PthreadCondBroadcast,
X86LibFunc::PthreadCondWait,
X86LibFunc::PthreadOnce,
X86LibFunc::PthreadKeyCreate,
X86LibFunc::PthreadKeyDelete,
X86LibFunc::PthreadSetspecific,
X86LibFunc::PthreadGetspecific,
];
for f in bionic_pthreads {
self.available_funcs.insert(*f);
}
self.preferred_vector_library = X86VectorLibrary::NoLibrary;
}
X86OSTarget::FreeBSD => {
let freebsd_extras: &[X86LibFunc] = &[
X86LibFunc::Bcopy,
X86LibFunc::Bzero,
X86LibFunc::Bcmp,
X86LibFunc::Memccpy,
X86LibFunc::Mempcpy,
X86LibFunc::Memalign,
X86LibFunc::Valloc,
X86LibFunc::PosixMemalign,
];
for f in freebsd_extras {
self.available_funcs.insert(*f);
}
}
_ => {
let generic: &[X86LibFunc] = &[
X86LibFunc::Bzero,
X86LibFunc::Memccpy,
X86LibFunc::Gettimeofday,
X86LibFunc::ClockGettime,
X86LibFunc::TlsGetAddr,
];
for f in generic {
self.available_funcs.insert(*f);
}
}
}
if !matches!(self.crt_lib, X86CRTLib::MSVCRT) {
let fenv_funcs: &[X86LibFunc] = &[
X86LibFunc::Fesetround,
X86LibFunc::Fegetround,
X86LibFunc::Feclearexcept,
X86LibFunc::Feraiseexcept,
X86LibFunc::Fetestexcept,
X86LibFunc::Fegetexceptflag,
X86LibFunc::Fesetexceptflag,
X86LibFunc::Feholdexcept,
X86LibFunc::Feupdateenv,
X86LibFunc::Fegetenv,
X86LibFunc::Fesetenv,
];
for f in fenv_funcs {
self.available_funcs.insert(*f);
}
}
let builtins: &[X86LibFunc] = &[
X86LibFunc::__builtin_memcpy,
X86LibFunc::__builtin_memmove,
X86LibFunc::__builtin_memset,
X86LibFunc::__builtin_memcmp,
X86LibFunc::__builtin_bzero,
X86LibFunc::__builtin_sqrt,
X86LibFunc::__builtin_sqrtf,
X86LibFunc::__builtin_sqrtl,
X86LibFunc::__builtin_pow,
X86LibFunc::__builtin_powf,
X86LibFunc::__builtin_sin,
X86LibFunc::__builtin_sinf,
X86LibFunc::__builtin_cos,
X86LibFunc::__builtin_cosf,
X86LibFunc::__builtin_exp,
X86LibFunc::__builtin_expf,
X86LibFunc::__builtin_log,
X86LibFunc::__builtin_logf,
X86LibFunc::__builtin_fabs,
X86LibFunc::__builtin_fabsf,
X86LibFunc::__builtin_floor,
X86LibFunc::__builtin_floorf,
X86LibFunc::__builtin_ceil,
X86LibFunc::__builtin_ceilf,
X86LibFunc::UnwindResume,
X86LibFunc::_Unwind_Resume,
];
for f in builtins {
self.available_funcs.insert(*f);
}
}
fn init_size_args(&mut self) {
self.memcpy_size_arg = 2;
self.memset_size_arg = 2;
self.memmove_size_arg = 2;
}
fn init_signatures(&mut self) {
let sigs = vec![
X86FuncSignature::new(
"sin",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"cos",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"tan",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"asin",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"acos",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"atan",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"atan2",
2,
"dd",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"sinh",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"cosh",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"tanh",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"exp",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"exp2",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"expm1",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"log",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"log2",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"log10",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"log1p",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"sqrt",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"cbrt",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"hypot",
2,
"dd",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"pow",
2,
"dd",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"fabs",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"fmod",
2,
"dd",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"remainder",
2,
"dd",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"copysign",
2,
"dd",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"fdim",
2,
"dd",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"fmax",
2,
"dd",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"fmin",
2,
"dd",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"fma",
3,
"ddd",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"ceil",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"floor",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"trunc",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"round",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"rint",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"nearbyint",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"erf",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"erfc",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"tgamma",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"lgamma",
1,
"d",
"d",
vec![KnownFuncAttr::ReadNone, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"memcpy",
3,
"ppl",
"p",
vec![
KnownFuncAttr::NoUnwind,
KnownFuncAttr::ArgMemOnly,
KnownFuncAttr::Returned(0),
],
),
X86FuncSignature::new(
"memmove",
3,
"ppl",
"p",
vec![
KnownFuncAttr::NoUnwind,
KnownFuncAttr::ArgMemOnly,
KnownFuncAttr::Returned(0),
],
),
X86FuncSignature::new(
"memset",
3,
"pil",
"p",
vec![
KnownFuncAttr::NoUnwind,
KnownFuncAttr::ArgMemOnly,
KnownFuncAttr::Returned(0),
],
),
X86FuncSignature::new(
"memcmp",
3,
"ppl",
"i",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"memchr",
3,
"pil",
"p",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"strlen",
1,
"p",
"l",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"strcpy",
2,
"pp",
"p",
vec![
KnownFuncAttr::NoUnwind,
KnownFuncAttr::ArgMemOnly,
KnownFuncAttr::Returned(0),
],
),
X86FuncSignature::new(
"strncpy",
3,
"ppl",
"p",
vec![
KnownFuncAttr::NoUnwind,
KnownFuncAttr::ArgMemOnly,
KnownFuncAttr::Returned(0),
],
),
X86FuncSignature::new(
"strcmp",
2,
"pp",
"i",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"strncmp",
3,
"ppl",
"i",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"strchr",
2,
"pi",
"p",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"strrchr",
2,
"pi",
"p",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"strstr",
2,
"pp",
"p",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"malloc",
1,
"l",
"p",
vec![
KnownFuncAttr::NoUnwind,
KnownFuncAttr::NoAlias,
KnownFuncAttr::NoCapture(0),
KnownFuncAttr::InaccessibleMemOnly,
],
),
X86FuncSignature::new(
"calloc",
2,
"ll",
"p",
vec![
KnownFuncAttr::NoUnwind,
KnownFuncAttr::NoAlias,
KnownFuncAttr::InaccessibleMemOnly,
],
),
X86FuncSignature::new(
"realloc",
2,
"pl",
"p",
vec![KnownFuncAttr::NoUnwind, KnownFuncAttr::InaccessibleMemOnly],
),
X86FuncSignature::new(
"free",
1,
"p",
"v",
vec![KnownFuncAttr::NoUnwind, KnownFuncAttr::ArgMemOnly],
),
X86FuncSignature::new(
"abort",
0,
"",
"v",
vec![KnownFuncAttr::NoReturn, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"exit",
1,
"i",
"v",
vec![KnownFuncAttr::NoReturn, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new("qsort", 4, "pllp", "v", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new(
"bsearch",
4,
"pplp",
"p",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"atoi",
1,
"p",
"i",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"atol",
1,
"p",
"l",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"atoll",
1,
"p",
"ll",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"strtol",
3,
"ppl",
"l",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"strtoll",
3,
"ppl",
"ll",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"strtoul",
3,
"ppl",
"ul",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"strtoull",
3,
"ppl",
"ull",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"strtod",
2,
"pp",
"d",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"strtof",
2,
"pp",
"f",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"strtold",
2,
"pp",
"ld",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"abs",
1,
"i",
"i",
vec![
KnownFuncAttr::Const,
KnownFuncAttr::NoUnwind,
KnownFuncAttr::Speculatable,
],
),
X86FuncSignature::new(
"labs",
1,
"l",
"l",
vec![
KnownFuncAttr::Const,
KnownFuncAttr::NoUnwind,
KnownFuncAttr::Speculatable,
],
),
X86FuncSignature::new(
"llabs",
1,
"ll",
"ll",
vec![
KnownFuncAttr::Const,
KnownFuncAttr::NoUnwind,
KnownFuncAttr::Speculatable,
],
),
X86FuncSignature::new("printf", -1, "p", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("fprintf", -1, "pp", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("sprintf", -1, "pp", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("snprintf", -1, "plp", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("scanf", -1, "p", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("fscanf", -1, "pp", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("sscanf", -1, "pp", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("fopen", 2, "pp", "p", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("fclose", 1, "p", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("fread", 4, "pllp", "l", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("fwrite", 4, "pllp", "l", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("fseek", 3, "pli", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new(
"ftell",
1,
"p",
"l",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new("fflush", 1, "p", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("fgets", 3, "plp", "p", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("fputs", 2, "pp", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("puts", 1, "p", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("getchar", 0, "", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("putchar", 1, "i", "i", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new("perror", 1, "p", "v", vec![KnownFuncAttr::NoUnwind]),
X86FuncSignature::new(
"feof",
1,
"p",
"i",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
X86FuncSignature::new(
"ferror",
1,
"p",
"i",
vec![KnownFuncAttr::ReadOnly, KnownFuncAttr::NoUnwind],
),
];
for sig in sigs {
if let Some(func) = Self::find_func_by_name(sig.name) {
self.signatures.insert(func, sig);
}
}
}
fn find_func_by_name(name: &str) -> Option<X86LibFunc> {
use X86LibFunc::*;
match name {
"sin" => Some(Sin),
"cos" => Some(Cos),
"tan" => Some(Tan),
"asin" => Some(Asin),
"acos" => Some(Acos),
"atan" => Some(Atan),
"atan2" => Some(Atan2),
"sinh" => Some(Sinh),
"cosh" => Some(Cosh),
"tanh" => Some(Tanh),
"exp" => Some(Exp),
"exp2" => Some(Exp2),
"expm1" => Some(ExpM1),
"log" => Some(Log),
"log2" => Some(Log2),
"log10" => Some(Log10),
"log1p" => Some(Log1p),
"sqrt" => Some(Sqrt),
"cbrt" => Some(Cbrt),
"hypot" => Some(Hypot),
"pow" => Some(Pow),
"fabs" => Some(Fabs),
"fmod" => Some(Fmod),
"remainder" => Some(Remainder),
"copysign" => Some(Copysign),
"fdim" => Some(Fdim),
"fmax" => Some(Fmax),
"fmin" => Some(Fmin),
"fma" => Some(Fma),
"ceil" => Some(Ceil),
"floor" => Some(Floor),
"trunc" => Some(Trunc),
"round" => Some(Round),
"rint" => Some(Rint),
"nearbyint" => Some(Nearbyint),
"erf" => Some(Erf),
"erfc" => Some(Erfc),
"tgamma" => Some(Tgamma),
"lgamma" => Some(Lgamma),
"memcpy" => Some(Memcpy),
"memmove" => Some(Memmove),
"memset" => Some(Memset),
"memcmp" => Some(Memcmp),
"memchr" => Some(Memchr),
"memrchr" => Some(Memrchr),
"bcopy" => Some(Bcopy),
"bzero" => Some(Bzero),
"bcmp" => Some(Bcmp),
"memccpy" => Some(Memccpy),
"mempcpy" => Some(Mempcpy),
"strlen" => Some(Strlen),
"strnlen" => Some(Strnlen),
"strcpy" => Some(Strcpy),
"strncpy" => Some(Strncpy),
"stpcpy" => Some(Stpcpy),
"stpncpy" => Some(Stpncpy),
"strcat" => Some(Strcat),
"strncat" => Some(Strncat),
"strcmp" => Some(Strcmp),
"strncmp" => Some(Strncmp),
"strcasecmp" => Some(Strcasecmp),
"strncasecmp" => Some(Strncasecmp),
"strchr" => Some(Strchr),
"strrchr" => Some(Strrchr),
"strstr" => Some(Strstr),
"strpbrk" => Some(Strpbrk),
"strspn" => Some(Strspn),
"strcspn" => Some(Strcspn),
"strsep" => Some(Strsep),
"strtok" => Some(Strtok),
"strdup" => Some(Strdup),
"strndup" => Some(Strndup),
"malloc" => Some(Malloc),
"calloc" => Some(Calloc),
"realloc" => Some(Realloc),
"free" => Some(Free),
"abort" => Some(Abort),
"exit" => Some(Exit),
"atexit" => Some(Atexit),
"getenv" => Some(Getenv),
"system" => Some(System),
"qsort" => Some(Qsort),
"bsearch" => Some(Bsearch),
"abs" => Some(Abs),
"labs" => Some(Labs),
"llabs" => Some(Llabs),
"rand" => Some(Rand),
"srand" => Some(Srand),
"atoi" => Some(Atoi),
"atol" => Some(Atol),
"atoll" => Some(Atoll),
"strtol" => Some(Strtol),
"strtoll" => Some(Strtoll),
"strtoul" => Some(Strtoul),
"strtoull" => Some(Strtoull),
"strtod" => Some(Strtod),
"strtof" => Some(Strtof),
"strtold" => Some(Strtold),
"printf" => Some(Printf),
"fprintf" => Some(Fprintf),
"sprintf" => Some(Sprintf),
"snprintf" => Some(Snprintf),
"scanf" => Some(Scanf),
"fscanf" => Some(Fscanf),
"sscanf" => Some(Sscanf),
"fopen" => Some(Fopen),
"fclose" => Some(Fclose),
"fread" => Some(Fread),
"fwrite" => Some(Fwrite),
"fseek" => Some(Fseek),
"ftell" => Some(Ftell),
"fflush" => Some(Fflush),
"fgets" => Some(Fgets),
"fputs" => Some(Fputs),
"getc" => Some(Getc),
"putc" => Some(Putc),
"getchar" => Some(Getchar),
"putchar" => Some(Putchar),
"puts" => Some(Puts),
"gets" => Some(Gets),
"clearerr" => Some(Clearerr),
"feof" => Some(Feof),
"ferror" => Some(Ferror),
"perror" => Some(Perror),
_ => None,
}
}
pub fn has(&self, func: X86LibFunc) -> bool {
if self.unavailable_funcs.contains(&func) {
return false;
}
self.available_funcs.contains(&func)
}
pub fn set_unavailable(&mut self, func: X86LibFunc) {
self.unavailable_funcs.insert(func);
}
pub fn set_available(&mut self, func: X86LibFunc) {
self.available_funcs.insert(func);
self.unavailable_funcs.remove(&func);
}
pub fn get_signature(&self, func: X86LibFunc) -> Option<&X86FuncSignature> {
self.signatures.get(&func)
}
pub fn get_attrs(&self, func: X86LibFunc) -> Vec<KnownFuncAttr> {
self.signatures
.get(&func)
.map(|s| s.attrs.clone())
.unwrap_or_default()
}
pub fn has_attr(&self, func: X86LibFunc, attr: &KnownFuncAttr) -> bool {
self.signatures
.get(&func)
.map(|s| s.attrs.contains(attr))
.unwrap_or(false)
}
pub fn init_vector_mappings(&mut self) {
match self.preferred_vector_library {
X86VectorLibrary::SVML => self.add_vec_svml(),
X86VectorLibrary::AMDLibM => self.add_vec_amdlibm(),
X86VectorLibrary::LibMVec => self.add_vec_libmvec(),
X86VectorLibrary::Accelerate => self.add_vec_accelerate(),
X86VectorLibrary::SLEEF => self.add_vec_sleef(),
_ => {}
}
}
fn add_vec_svml(&mut self) {
let svml_float_mappings = vec![
("sinf", "__svml_sinf4", 4),
("sinf", "__svml_sinf8", 8),
("sinf", "__svml_sinf16", 16),
("cosf", "__svml_cosf4", 4),
("cosf", "__svml_cosf8", 8),
("cosf", "__svml_cosf16", 16),
("expf", "__svml_expf4", 4),
("expf", "__svml_expf8", 8),
("expf", "__svml_expf16", 16),
("logf", "__svml_logf4", 4),
("logf", "__svml_logf8", 8),
("logf", "__svml_logf16", 16),
("powf", "__svml_powf4", 4),
("powf", "__svml_powf8", 8),
("powf", "__svml_powf16", 16),
];
let svml_double_mappings = vec![
("sin", "__svml_sin4", 4),
("sin", "__svml_sin8", 8),
("cos", "__svml_cos4", 4),
("cos", "__svml_cos8", 8),
("exp", "__svml_exp4", 4),
("exp", "__svml_exp8", 8),
("log", "__svml_log4", 4),
("log", "__svml_log8", 8),
("pow", "__svml_pow4", 4),
("pow", "__svml_pow8", 8),
];
for (s, v, w) in svml_float_mappings {
self.vector_mappings.push(X86VecFuncMapping {
scalar_name: s.to_string(),
vector_width: w,
vector_name: v.to_string(),
library: X86VectorLibrary::SVML,
});
}
for (s, v, w) in svml_double_mappings {
self.vector_mappings.push(X86VecFuncMapping {
scalar_name: s.to_string(),
vector_width: w,
vector_name: v.to_string(),
library: X86VectorLibrary::SVML,
});
}
}
fn add_vec_amdlibm(&mut self) {
let amd_mappings = vec![
("sinf", "__amdlibm_sinf", 1),
("cosf", "__amdlibm_cosf", 1),
("expf", "__amdlibm_expf", 1),
("logf", "__amdlibm_logf", 1),
("powf", "__amdlibm_powf", 1),
("sin", "__amdlibm_sin", 1),
("cos", "__amdlibm_cos", 1),
("exp", "__amdlibm_exp", 1),
("log", "__amdlibm_log", 1),
("pow", "__amdlibm_pow", 1),
];
for (s, v, w) in amd_mappings {
self.vector_mappings.push(X86VecFuncMapping {
scalar_name: s.to_string(),
vector_width: w,
vector_name: v.to_string(),
library: X86VectorLibrary::AMDLibM,
});
}
}
fn add_vec_libmvec(&mut self) {
let libmvec_mappings = vec![
("sinf", "_ZGVbN4v_sinf", 4),
("cosf", "_ZGVbN4v_cosf", 4),
("expf", "_ZGVbN4v_expf", 4),
("logf", "_ZGVbN4v_logf", 4),
("sin", "_ZGVbN2v_sin", 2),
("cos", "_ZGVbN2v_cos", 2),
("exp", "_ZGVbN2v_exp", 2),
("log", "_ZGVbN2v_log", 2),
];
for (s, v, w) in libmvec_mappings {
self.vector_mappings.push(X86VecFuncMapping {
scalar_name: s.to_string(),
vector_width: w,
vector_name: v.to_string(),
library: X86VectorLibrary::LibMVec,
});
}
}
fn add_vec_accelerate(&mut self) {
let accel_mappings = vec![
("sinf", "vsinf", 4),
("cosf", "vcosf", 4),
("expf", "vexpf", 4),
("logf", "vlogf", 4),
("sin", "vsin", 2),
("cos", "vcos", 2),
("exp", "vexp", 2),
("log", "vlog", 2),
];
for (s, v, w) in accel_mappings {
self.vector_mappings.push(X86VecFuncMapping {
scalar_name: s.to_string(),
vector_width: w,
vector_name: v.to_string(),
library: X86VectorLibrary::Accelerate,
});
}
}
fn add_vec_sleef(&mut self) {
let sleef_mappings = vec![
("sinf", "__sleef_sinf4_u10", 4),
("cosf", "__sleef_cosf4_u10", 4),
("expf", "__sleef_expf4_u10", 4),
("logf", "__sleef_logf4_u10", 4),
("sin", "__sleef_sind4_u10", 4),
("cos", "__sleef_cosd4_u10", 4),
("exp", "__sleef_expd4_u10", 4),
("log", "__sleef_logd4_u10", 4),
];
for (s, v, w) in sleef_mappings {
self.vector_mappings.push(X86VecFuncMapping {
scalar_name: s.to_string(),
vector_width: w,
vector_name: v.to_string(),
library: X86VectorLibrary::SLEEF,
});
}
}
pub fn get_vector_mapping(&self, scalar: &str, width: u32) -> Option<&X86VecFuncMapping> {
self.vector_mappings
.iter()
.find(|m| m.scalar_name == scalar && m.vector_width == width)
}
pub fn get_vector_mappings_for(&self, scalar: &str) -> Vec<&X86VecFuncMapping> {
self.vector_mappings
.iter()
.filter(|m| m.scalar_name == scalar)
.collect()
}
pub fn with_vector_library(mut self, lib: X86VectorLibrary) -> Self {
self.preferred_vector_library = lib;
self.vector_mappings.clear();
self.init_vector_mappings();
match lib {
X86VectorLibrary::SVML => {
self.use_svml = true;
}
X86VectorLibrary::AMDLibM => {
self.use_amdlibm = true;
}
X86VectorLibrary::SLEEF => {
self.use_sleef = true;
}
_ => {}
}
self
}
}
#[derive(Debug, Clone, Copy)]
pub struct InstrCost {
pub latency: u32,
pub recip_throughput: f64,
pub uops: u32,
pub ports: u8,
}
impl InstrCost {
pub const fn new(latency: u32, recip_throughput: f64, uops: u32, ports: u8) -> Self {
InstrCost {
latency,
recip_throughput,
uops,
ports,
}
}
pub fn throughput_cycles(&self) -> f64 {
if self.recip_throughput == 0.0 {
return 0.0;
}
1.0 / self.recip_throughput
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86Microarch {
Skylake,
SkylakeX,
CannonLake,
IceLake,
TigerLake,
AlderLakeP,
AlderLakeE,
RaptorLake,
MeteorLake,
GraniteRapids,
Zen1,
Zen2,
Zen3,
Zen4,
Zen5,
Generic,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum CostKind {
AddI32,
AddI64,
SubI32,
SubI64,
MulI32,
MulI64,
IMulI64High,
SDivI32,
SDivI64,
UDivI32,
UDivI64,
AndI32,
AndI64,
OrI32,
OrI64,
XorI32,
XorI64,
ShlI32,
ShlI64,
LShrI32,
LShrI64,
AShrI32,
AShrI64,
NotI32,
NotI64,
NegI32,
NegI64,
FAddF32,
FAddF64,
FSubF32,
FSubF64,
FMulF32,
FMulF64,
FDivF32,
FDivF64,
FSqrtF32,
FSqrtF64,
LoadI32,
LoadI64,
LoadF32,
LoadF64,
StoreI32,
StoreI64,
StoreF32,
StoreF64,
LoadV128,
LoadV256,
LoadV512,
StoreV128,
StoreV256,
StoreV512,
Branch,
ConditionalBranch,
IndirectBranch,
Return,
Call,
TailCall,
VAddF32x4,
VAddF32x8,
VAddF32x16,
VAddF64x2,
VAddF64x4,
VAddF64x8,
VMulF32x4,
VMulF32x8,
VMulF32x16,
VMulF64x2,
VMulF64x4,
VMulF64x8,
VDivF32x4,
VDivF32x8,
VDivF32x16,
VDivF64x2,
VDivF64x4,
VDivF64x8,
VFmaF32x4,
VFmaF32x8,
VFmaF32x16,
VFmaF64x2,
VFmaF64x4,
VFmaF64x8,
VShufV128,
VShufV256,
VShufV512,
VPermV128,
VPermV256,
VPermV512,
VBroadcastV128,
VBroadcastV256,
VBroadcastV512,
VExtractV128,
VInsertV128,
VExtractV256,
VInsertV256,
LaneCrossingV256,
LaneCrossingV512,
VAddI32x4,
VAddI32x8,
VAddI32x16,
VMulI32x4,
VMulI32x8,
VMulI16x8,
VMulI16x16,
FpToSI32,
FpToSI64,
SiToFpF32,
SiToFpF64,
FpTruncF64ToF32,
FpExtF32ToF64,
Bitcast,
Select,
Phi,
GEP,
Alloca,
FrameIndex,
ExtractElt,
InsertElt,
ExtractValue,
InsertValue,
}
pub struct X86TargetTransformInfo {
pub microarch: X86Microarch,
pub has_avx: bool,
pub has_avx2: bool,
pub has_avx512: bool,
pub has_avx512vl: bool,
pub has_avx512bw: bool,
pub has_avx512dq: bool,
pub has_fma: bool,
pub has_bmi2: bool,
pub has_lzcnt: bool,
pub has_popcnt: bool,
pub has_movbe: bool,
pub has_sse41: bool,
pub has_sse42: bool,
pub sse_level: u32,
pub cost_table: BTreeMap<CostKind, InstrCost>,
pub inline_threshold: u32,
pub unroll_threshold: u32,
pub max_unroll_count: u32,
pub min_unroll_count: u32,
pub vectorize_min_trip_count: u32,
pub num_vector_regs: u32,
pub max_vector_width: u32,
pub preferred_vector_width: u32,
pub branch_mispredict_penalty: u32,
pub taken_branch_cost: u32,
pub not_taken_branch_cost: u32,
pub jump_table_density_threshold: f64,
pub max_jump_table_size: u32,
pub min_jump_table_entries: u32,
pub l1_cache_size: u32,
pub l2_cache_size: u32,
pub l3_cache_size: u32,
pub cache_line_size: u32,
pub prefetch_distance: u32,
pub max_interleave_factor: u32,
pub supports_gather_scatter: bool,
pub efficient_masked_ops: bool,
pub has_horizontal_add: bool,
pub reg_pressure_threshold: f64,
}
impl X86TargetTransformInfo {
pub fn new(microarch: X86Microarch) -> Self {
let mut tti = X86TargetTransformInfo {
microarch,
has_avx: false,
has_avx2: false,
has_avx512: false,
has_avx512vl: false,
has_avx512bw: false,
has_avx512dq: false,
has_fma: false,
has_bmi2: false,
has_lzcnt: false,
has_popcnt: false,
has_movbe: false,
has_sse41: false,
has_sse42: false,
sse_level: 0,
cost_table: BTreeMap::new(),
inline_threshold: X86_DEFAULT_INLINE_THRESHOLD,
unroll_threshold: X86_DEFAULT_UNROLL_THRESHOLD,
max_unroll_count: X86_MAX_UNROLL_COUNT,
min_unroll_count: X86_MIN_UNROLL_COUNT,
vectorize_min_trip_count: X86_VECTORIZE_MIN_TRIP_COUNT,
num_vector_regs: 16,
max_vector_width: 256,
preferred_vector_width: 256,
branch_mispredict_penalty: X86_BRANCH_MISPREDICT_PENALTY,
taken_branch_cost: 1,
not_taken_branch_cost: 1,
jump_table_density_threshold: X86_JUMP_TABLE_DENSITY_THRESHOLD,
max_jump_table_size: X86_MAX_JUMP_TABLE_SIZE,
min_jump_table_entries: 4,
l1_cache_size: 32 * 1024,
l2_cache_size: 256 * 1024,
l3_cache_size: 0,
cache_line_size: X86_L1_CACHE_LINE_SIZE,
prefetch_distance: 16,
max_interleave_factor: 4,
supports_gather_scatter: false,
efficient_masked_ops: false,
has_horizontal_add: false,
reg_pressure_threshold: 0.75,
};
tti.init_cost_table();
tti
}
pub fn with_features(
mut self,
has_avx: bool,
has_avx2: bool,
has_avx512: bool,
has_avx512vl: bool,
has_fma: bool,
sse_level: u32,
) -> Self {
self.has_avx = has_avx;
self.has_avx2 = has_avx2;
self.has_avx512 = has_avx512;
self.has_avx512vl = has_avx512vl;
self.has_fma = has_fma;
self.sse_level = sse_level;
if has_avx512 {
self.num_vector_regs = 32;
self.max_vector_width = 512;
self.preferred_vector_width = 512;
self.supports_gather_scatter = true;
self.efficient_masked_ops = true;
} else if has_avx {
self.num_vector_regs = 16;
self.max_vector_width = 256;
self.preferred_vector_width = 256;
} else if sse_level >= 2 {
self.num_vector_regs = 16;
self.max_vector_width = 128;
self.preferred_vector_width = 128;
}
if has_avx2 {
self.has_horizontal_add = true;
}
self
}
fn init_cost_table(&mut self) {
let mut table = BTreeMap::new();
let cheap_int = InstrCost::new(1, 0.25, 1, 0xFF); let cheap_int64 = InstrCost::new(1, 0.25, 1, 0xFF);
let i32_mul = InstrCost::new(3, 1.0, 1, 0x01); let i64_mul = InstrCost::new(3, 1.0, 1, 0x01);
let i64_imul_high = InstrCost::new(4, 1.0, 2, 0x01);
let i32_sdiv = InstrCost::new(20, 6.0, 9, 0x01);
let i64_sdiv = InstrCost::new(35, 10.0, 30, 0x01);
let i32_udiv = InstrCost::new(20, 6.0, 9, 0x01);
let i64_udiv = InstrCost::new(35, 10.0, 30, 0x01);
table.insert(CostKind::AddI32, cheap_int);
table.insert(CostKind::AddI64, cheap_int64);
table.insert(CostKind::SubI32, cheap_int);
table.insert(CostKind::SubI64, cheap_int64);
table.insert(CostKind::MulI32, i32_mul);
table.insert(CostKind::MulI64, i64_mul);
table.insert(CostKind::IMulI64High, i64_imul_high);
table.insert(CostKind::SDivI32, i32_sdiv);
table.insert(CostKind::SDivI64, i64_sdiv);
table.insert(CostKind::UDivI32, i32_udiv);
table.insert(CostKind::UDivI64, i64_udiv);
table.insert(CostKind::AndI32, cheap_int);
table.insert(CostKind::AndI64, cheap_int64);
table.insert(CostKind::OrI32, cheap_int);
table.insert(CostKind::OrI64, cheap_int64);
table.insert(CostKind::XorI32, cheap_int);
table.insert(CostKind::XorI64, cheap_int64);
table.insert(CostKind::ShlI32, cheap_int);
table.insert(CostKind::ShlI64, cheap_int64);
table.insert(CostKind::LShrI32, cheap_int);
table.insert(CostKind::LShrI64, cheap_int64);
table.insert(CostKind::AShrI32, cheap_int);
table.insert(CostKind::AShrI64, cheap_int64);
table.insert(CostKind::NotI32, cheap_int);
table.insert(CostKind::NotI64, cheap_int64);
table.insert(CostKind::NegI32, cheap_int);
table.insert(CostKind::NegI64, cheap_int64);
let f32_add = InstrCost::new(3, 0.5, 1, 0x02); let f64_add = InstrCost::new(3, 0.5, 1, 0x02);
let f32_mul = InstrCost::new(4, 0.5, 1, 0x02);
let f64_mul = InstrCost::new(4, 0.5, 1, 0x02);
let f32_div = InstrCost::new(13, 4.0, 3, 0x02);
let f64_div = InstrCost::new(20, 8.0, 4, 0x02);
let f32_sqrt = InstrCost::new(12, 6.0, 1, 0x02);
let f64_sqrt = InstrCost::new(20, 10.0, 1, 0x02);
table.insert(CostKind::FAddF32, f32_add);
table.insert(CostKind::FAddF64, f64_add);
table.insert(CostKind::FSubF32, f32_add);
table.insert(CostKind::FSubF64, f64_add);
table.insert(CostKind::FMulF32, f32_mul);
table.insert(CostKind::FMulF64, f64_mul);
table.insert(CostKind::FDivF32, f32_div);
table.insert(CostKind::FDivF64, f64_div);
table.insert(CostKind::FSqrtF32, f32_sqrt);
table.insert(CostKind::FSqrtF64, f64_sqrt);
let load_i32 = InstrCost::new(4, 0.5, 1, 0x0C); let load_i64 = InstrCost::new(4, 0.5, 1, 0x0C);
let store_i32 = InstrCost::new(1, 1.0, 1, 0x30); let store_i64 = InstrCost::new(1, 1.0, 1, 0x30);
let load_128 = InstrCost::new(4, 0.5, 1, 0x0C);
let load_256 = InstrCost::new(4, 0.5, 1, 0x0C);
let load_512 = InstrCost::new(5, 0.5, 1, 0x0C);
let store_128 = InstrCost::new(1, 1.0, 1, 0x30);
let store_256 = InstrCost::new(1, 1.0, 1, 0x30);
let store_512 = InstrCost::new(1, 1.0, 2, 0x30);
table.insert(CostKind::LoadI32, load_i32);
table.insert(CostKind::LoadI64, load_i64);
table.insert(CostKind::LoadF32, load_i32);
table.insert(CostKind::LoadF64, load_i64);
table.insert(CostKind::StoreI32, store_i32);
table.insert(CostKind::StoreI64, store_i64);
table.insert(CostKind::StoreF32, store_i32);
table.insert(CostKind::StoreF64, store_i64);
table.insert(CostKind::LoadV128, load_128);
table.insert(CostKind::LoadV256, load_256);
table.insert(CostKind::LoadV512, load_512);
table.insert(CostKind::StoreV128, store_128);
table.insert(CostKind::StoreV256, store_256);
table.insert(CostKind::StoreV512, store_512);
table.insert(CostKind::Branch, InstrCost::new(1, 1.0, 1, 0x10)); table.insert(CostKind::ConditionalBranch, InstrCost::new(1, 1.0, 2, 0x10));
table.insert(CostKind::IndirectBranch, InstrCost::new(1, 1.0, 2, 0x10));
table.insert(CostKind::Return, InstrCost::new(1, 1.0, 2, 0x10));
table.insert(CostKind::Call, InstrCost::new(1, 1.0, 3, 0x10));
table.insert(CostKind::TailCall, InstrCost::new(1, 1.0, 3, 0x10));
let v128_add_f32 = InstrCost::new(3, 0.5, 1, 0x02);
let v128_add_f64 = InstrCost::new(3, 0.5, 1, 0x02);
let v128_mul_f32 = InstrCost::new(4, 0.5, 1, 0x02);
let v128_mul_f64 = InstrCost::new(4, 0.5, 1, 0x02);
let v128_div_f32 = InstrCost::new(13, 4.0, 3, 0x02);
let v128_div_f64 = InstrCost::new(20, 8.0, 4, 0x02);
table.insert(CostKind::VAddF32x4, v128_add_f32);
table.insert(CostKind::VAddF64x2, v128_add_f64);
table.insert(CostKind::VMulF32x4, v128_mul_f32);
table.insert(CostKind::VMulF64x2, v128_mul_f64);
table.insert(CostKind::VDivF32x4, v128_div_f32);
table.insert(CostKind::VDivF64x2, v128_div_f64);
let v256_add_f32 = InstrCost::new(3, 0.5, 1, 0x0E); let v256_add_f64 = InstrCost::new(3, 0.5, 1, 0x0E);
let v256_mul_f32 = InstrCost::new(4, 0.5, 1, 0x02);
let v256_mul_f64 = InstrCost::new(4, 0.5, 1, 0x02);
let v256_fma_f32 = InstrCost::new(4, 0.5, 1, 0x02);
table.insert(CostKind::VAddF32x8, v256_add_f32);
table.insert(CostKind::VAddF64x4, v256_add_f64);
table.insert(CostKind::VMulF32x8, v256_mul_f32);
table.insert(CostKind::VMulF64x4, v256_mul_f64);
table.insert(CostKind::VFmaF32x8, v256_fma_f32);
table.insert(CostKind::VFmaF64x4, InstrCost::new(4, 0.5, 1, 0x02));
table.insert(CostKind::VAddF32x16, InstrCost::new(3, 0.5, 1, 0x0E));
table.insert(CostKind::VAddF64x8, InstrCost::new(3, 0.5, 1, 0x0E));
table.insert(CostKind::VMulF32x16, InstrCost::new(4, 0.5, 1, 0x02));
table.insert(CostKind::VMulF64x8, InstrCost::new(4, 0.5, 1, 0x02));
table.insert(CostKind::VFmaF32x16, InstrCost::new(4, 0.5, 1, 0x02));
table.insert(CostKind::VFmaF64x8, InstrCost::new(4, 0.5, 1, 0x02));
table.insert(CostKind::VShufV128, InstrCost::new(1, 1.0, 1, 0x08)); table.insert(CostKind::VShufV256, InstrCost::new(3, 1.0, 1, 0x08));
table.insert(CostKind::VShufV512, InstrCost::new(3, 1.0, 1, 0x08));
table.insert(CostKind::VPermV128, InstrCost::new(1, 1.0, 1, 0x08));
table.insert(CostKind::VPermV256, InstrCost::new(3, 2.0, 1, 0x08));
table.insert(CostKind::VPermV512, InstrCost::new(3, 2.0, 1, 0x08));
table.insert(CostKind::LaneCrossingV256, InstrCost::new(3, 1.0, 2, 0x08));
table.insert(CostKind::LaneCrossingV512, InstrCost::new(3, 1.0, 3, 0x08));
table.insert(CostKind::VBroadcastV128, InstrCost::new(1, 1.0, 1, 0x08));
table.insert(CostKind::VBroadcastV256, InstrCost::new(3, 1.0, 1, 0x08));
table.insert(CostKind::VBroadcastV512, InstrCost::new(3, 1.0, 1, 0x08));
table.insert(CostKind::VExtractV128, InstrCost::new(3, 1.0, 1, 0x08));
table.insert(CostKind::VInsertV128, InstrCost::new(3, 1.0, 1, 0x08));
table.insert(CostKind::VExtractV256, InstrCost::new(3, 1.0, 1, 0x08));
table.insert(CostKind::VInsertV256, InstrCost::new(3, 1.0, 1, 0x08));
table.insert(CostKind::VAddI32x4, InstrCost::new(1, 0.33, 1, 0x0F));
table.insert(CostKind::VAddI32x8, InstrCost::new(1, 0.33, 1, 0x0F));
table.insert(CostKind::VAddI32x16, InstrCost::new(1, 0.33, 1, 0x0F));
table.insert(CostKind::VMulI32x4, InstrCost::new(5, 0.5, 1, 0x02));
table.insert(CostKind::VMulI32x8, InstrCost::new(5, 0.5, 1, 0x02));
table.insert(CostKind::VMulI16x8, InstrCost::new(5, 0.5, 1, 0x02));
table.insert(CostKind::VMulI16x16, InstrCost::new(5, 0.5, 1, 0x02));
table.insert(CostKind::FpToSI32, InstrCost::new(5, 1.0, 1, 0x02));
table.insert(CostKind::FpToSI64, InstrCost::new(5, 1.0, 1, 0x02));
table.insert(CostKind::SiToFpF32, InstrCost::new(4, 1.0, 1, 0x02));
table.insert(CostKind::SiToFpF64, InstrCost::new(4, 1.0, 1, 0x02));
table.insert(CostKind::FpTruncF64ToF32, InstrCost::new(3, 1.0, 1, 0x02));
table.insert(CostKind::FpExtF32ToF64, InstrCost::new(2, 1.0, 1, 0x02));
table.insert(CostKind::Bitcast, InstrCost::new(0, 0.0, 0, 0));
table.insert(CostKind::Select, InstrCost::new(1, 0.33, 1, 0x0F));
table.insert(CostKind::Phi, InstrCost::new(0, 0.0, 0, 0)); table.insert(CostKind::GEP, InstrCost::new(1, 0.25, 1, 0xFF));
table.insert(CostKind::Alloca, InstrCost::new(1, 1.0, 1, 0xFF));
table.insert(CostKind::FrameIndex, InstrCost::new(1, 0.25, 1, 0xFF));
table.insert(CostKind::ExtractElt, InstrCost::new(2, 1.0, 1, 0x08));
table.insert(CostKind::InsertElt, InstrCost::new(2, 1.0, 1, 0x08));
table.insert(CostKind::ExtractValue, InstrCost::new(0, 0.0, 0, 0));
table.insert(CostKind::InsertValue, InstrCost::new(0, 0.0, 0, 0));
self.cost_table = table;
}
pub fn get_cost(&self, kind: CostKind) -> InstrCost {
self.cost_table
.get(&kind)
.copied()
.unwrap_or(InstrCost::new(1, 1.0, 1, 0xFF))
}
pub fn get_latency(&self, kind: CostKind) -> u32 {
self.get_cost(kind).latency
}
pub fn compute_loop_cost(&self, trip_count: u32, body_costs: &[CostKind]) -> u32 {
let body_cycles: u32 = body_costs.iter().map(|k| self.get_latency(*k)).sum();
body_cycles * trip_count
}
pub fn should_unroll_loop(&self, trip_count: u32, body_costs: &[CostKind]) -> bool {
if trip_count < self.min_unroll_count {
return false;
}
let body_cost = body_costs.iter().map(|k| self.get_latency(*k)).sum::<u32>();
let total_uops: u32 = body_costs.iter().map(|k| self.get_cost(*k).uops).sum();
total_uops * trip_count <= self.unroll_threshold && trip_count <= self.max_unroll_count
}
pub fn compute_inline_threshold(
&self,
num_instructions: u32,
has_loops: bool,
is_alwaysinline: bool,
is_noinline: bool,
) -> u32 {
if is_noinline {
return 0;
}
if is_alwaysinline {
return u32::MAX;
}
let base = self.inline_threshold;
if has_loops {
base * 3 / 2 } else {
base
}
}
pub fn compute_memory_cost(&self, is_load: bool, num_bytes: u32, is_sequential: bool) -> u32 {
let base = if num_bytes <= 16 {
4
} else if num_bytes <= 64 {
5
} else {
12
};
if is_sequential {
base } else {
base * 2 }
}
pub fn compute_switch_cost(&self, num_cases: u32, min_case: i64, max_case: i64) -> SwitchCost {
let range = (max_case - min_case) as u64;
let density = if range > 0 {
num_cases as f64 / range as f64
} else {
0.0
};
let jt_cases = num_cases.max(1);
let jt_cost = 3 + 1 + (1 + (range / 256) as u32).min(3);
let bt_depth = (num_cases as f64).log2().ceil() as u32;
let bt_cost = bt_depth * 2;
if density >= self.jump_table_density_threshold
&& num_cases >= self.min_jump_table_entries
&& range <= self.max_jump_table_size as u64
{
SwitchCost {
prefer_jump_table: true,
jump_table_cost: jt_cost,
binary_tree_cost: bt_cost,
density,
}
} else {
SwitchCost {
prefer_jump_table: false,
jump_table_cost: jt_cost,
binary_tree_cost: bt_cost,
density,
}
}
}
pub fn compute_vectorization_cost(
&self,
scalar_cost: InstrCost,
vector_width: u32,
has_lane_crossing: bool,
) -> InstrCost {
let base_latency = scalar_cost.latency;
let base_throughput = scalar_cost.recip_throughput;
let lane_penalty = if has_lane_crossing {
if vector_width <= 128 {
0
} else if vector_width <= 256 {
1
} else {
2
}
} else {
0
};
InstrCost {
latency: base_latency + lane_penalty,
recip_throughput: base_throughput * (vector_width as f64 / 128.0).max(0.25),
uops: scalar_cost.uops,
ports: scalar_cost.ports,
}
}
pub fn should_split_vector(&self, bit_width: u32) -> bool {
bit_width > self.max_vector_width
}
pub fn is_vectorization_profitable(
&self,
scalar_kind: CostKind,
trip_count: u32,
vector_width_bits: u32,
) -> bool {
if trip_count < self.vectorize_min_trip_count {
return false;
}
if vector_width_bits > self.max_vector_width {
return false;
}
let scalar_latency = self.get_latency(scalar_kind);
let scalar_total = scalar_latency * trip_count;
let vec_elements = vector_width_bits / 32; let vec_iterations = (trip_count as f64 / vec_elements as f64).ceil() as u32;
let vec_latency = scalar_latency + 1; let vec_total = vec_latency * vec_iterations + 2; vec_total < scalar_total
}
pub fn get_max_interleave_factor(&self, is_load: bool) -> u32 {
if is_load {
self.max_interleave_factor * 2
} else {
self.max_interleave_factor
}
}
pub fn compute_register_pressure(&self, live_ranges: u32, total_regs: u32) -> f64 {
if total_regs == 0 {
return 1.0;
}
live_ranges as f64 / total_regs as f64
}
pub fn is_high_register_pressure(&self, live_ranges: u32, total_regs: u32) -> bool {
self.compute_register_pressure(live_ranges, total_regs) > self.reg_pressure_threshold
}
}
#[derive(Debug, Clone)]
pub struct SwitchCost {
pub prefer_jump_table: bool,
pub jump_table_cost: u32,
pub binary_tree_cost: u32,
pub density: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum X86PhysReg {
RAX,
RBX,
RCX,
RDX,
RSI,
RDI,
RBP,
RSP,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
EAX,
EBX,
ECX,
EDX,
ESI,
EDI,
EBP,
ESP,
R8D,
R9D,
R10D,
R11D,
R12D,
R13D,
R14D,
R15D,
AX,
BX,
CX,
DX,
SI,
DI,
BP,
SP,
AL,
BL,
CL,
DL,
SIL,
DIL,
BPL,
SPL,
R8B,
R9B,
R10B,
R11B,
R12B,
R13B,
R14B,
R15B,
AH,
BH,
CH,
DH,
XMM0,
XMM1,
XMM2,
XMM3,
XMM4,
XMM5,
XMM6,
XMM7,
XMM8,
XMM9,
XMM10,
XMM11,
XMM12,
XMM13,
XMM14,
XMM15,
XMM16,
XMM17,
XMM18,
XMM19,
XMM20,
XMM21,
XMM22,
XMM23,
XMM24,
XMM25,
XMM26,
XMM27,
XMM28,
XMM29,
XMM30,
XMM31,
ST0,
ST1,
ST2,
ST3,
ST4,
ST5,
ST6,
ST7,
CS,
DS,
ES,
SS,
FS,
GS,
EFLAGS,
RFLAGS,
MM0,
MM1,
MM2,
MM3,
MM4,
MM5,
MM6,
MM7,
K0,
K1,
K2,
K3,
K4,
K5,
K6,
K7,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86RegClass {
GPR8,
GPR16,
GPR32,
GPR64,
XMM,
YMM,
ZMM,
X87,
MMX,
Segment,
Flags,
KReg,
}
impl X86RegClass {
pub fn size(&self) -> u32 {
match self {
X86RegClass::GPR8 => 1,
X86RegClass::GPR16 => 2,
X86RegClass::GPR32 => 4,
X86RegClass::GPR64 => 8,
X86RegClass::XMM => 16,
X86RegClass::YMM => 32,
X86RegClass::ZMM => 64,
X86RegClass::X87 => 10,
X86RegClass::MMX => 8,
X86RegClass::Segment => 2,
X86RegClass::Flags => 8,
X86RegClass::KReg => 8,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86CallingConv {
SysV64,
Win64,
CDecl,
StdCall,
FastCall,
ThisCall,
VectorCall,
RegCall,
SysV32,
Custom,
}
impl X86CallingConv {
pub fn is_64bit(&self) -> bool {
matches!(self, X86CallingConv::SysV64 | X86CallingConv::Win64)
}
pub fn uses_reg_args(&self) -> bool {
!matches!(
self,
X86CallingConv::CDecl
| X86CallingConv::StdCall
| X86CallingConv::SysV32
| X86CallingConv::Custom
)
}
pub fn num_int_arg_regs(&self) -> u32 {
match self {
X86CallingConv::SysV64 => 6, X86CallingConv::Win64 => 4, X86CallingConv::FastCall => 2, X86CallingConv::ThisCall => 1, X86CallingConv::VectorCall => 6, X86CallingConv::RegCall => 8, X86CallingConv::CDecl => 0,
X86CallingConv::StdCall => 0,
X86CallingConv::SysV32 => 0,
X86CallingConv::Custom => 0,
}
}
pub fn num_sse_arg_regs(&self) -> u32 {
match self {
X86CallingConv::SysV64 => 8, X86CallingConv::Win64 => 4, X86CallingConv::VectorCall => 6,
X86CallingConv::RegCall => 8,
_ => 0,
}
}
pub fn requires_shadow_space(&self) -> bool {
matches!(self, X86CallingConv::Win64)
}
pub fn caller_cleans_stack(&self) -> bool {
!matches!(self, X86CallingConv::StdCall)
}
pub fn has_red_zone(&self) -> bool {
matches!(self, X86CallingConv::SysV64)
}
pub fn stack_alignment(&self) -> u32 {
match self {
X86CallingConv::SysV64 | X86CallingConv::Win64 => 16,
_ => 16, }
}
}
#[derive(Debug, Clone)]
pub struct X86RegCost {
pub reg: X86PhysReg,
pub cost: u32,
pub is_callee_saved: bool,
pub is_call_clobbered: bool,
pub spill_cost: u32,
pub reg_class: X86RegClass,
}
pub struct X86CallingConvLowering {
pub cc: X86CallingConv,
pub reg_costs: HashMap<X86PhysReg, X86RegCost>,
pub callee_saved: Vec<X86PhysReg>,
pub call_clobbered: Vec<X86PhysReg>,
pub int_arg_regs: Vec<X86PhysReg>,
pub sse_arg_regs: Vec<X86PhysReg>,
pub int_return_regs: Vec<X86PhysReg>,
pub sse_return_reg: Option<X86PhysReg>,
pub stack_alignment: u32,
pub shadow_space_size: u32,
pub has_red_zone: bool,
pub red_zone_size: u32,
pub has_sret: bool,
pub frame_ptr: X86PhysReg,
pub stack_ptr: X86PhysReg,
pub total_callee_saved_spill_cost: u32,
}
impl X86CallingConvLowering {
pub fn new(cc: X86CallingConv) -> Self {
let mut lowering = X86CallingConvLowering {
cc,
reg_costs: HashMap::new(),
callee_saved: Vec::new(),
call_clobbered: Vec::new(),
int_arg_regs: Vec::new(),
sse_arg_regs: Vec::new(),
int_return_regs: Vec::new(),
sse_return_reg: None,
stack_alignment: cc.stack_alignment(),
shadow_space_size: 0,
has_red_zone: cc.has_red_zone(),
red_zone_size: if cc.has_red_zone() { 128 } else { 0 },
has_sret: false,
frame_ptr: X86PhysReg::RBP,
stack_ptr: X86PhysReg::RSP,
total_callee_saved_spill_cost: 0,
};
lowering.init_cc_defaults();
lowering.init_reg_costs();
lowering.compute_spill_costs();
lowering
}
fn init_cc_defaults(&mut self) {
match self.cc {
X86CallingConv::SysV64 => {
self.int_arg_regs = vec![
X86PhysReg::RDI,
X86PhysReg::RSI,
X86PhysReg::RDX,
X86PhysReg::RCX,
X86PhysReg::R8,
X86PhysReg::R9,
];
self.sse_arg_regs = (0..8).map(|i| self.xmm_reg(i)).collect();
self.int_return_regs = vec![X86PhysReg::RAX, X86PhysReg::RDX];
self.sse_return_reg = Some(X86PhysReg::XMM0);
self.callee_saved = vec![
X86PhysReg::RBX,
X86PhysReg::RBP,
X86PhysReg::R12,
X86PhysReg::R13,
X86PhysReg::R14,
X86PhysReg::R15,
];
self.call_clobbered = vec![
X86PhysReg::RAX,
X86PhysReg::RCX,
X86PhysReg::RDX,
X86PhysReg::RSI,
X86PhysReg::RDI,
X86PhysReg::R8,
X86PhysReg::R9,
X86PhysReg::R10,
X86PhysReg::R11,
];
self.shadow_space_size = 0;
self.has_red_zone = true;
self.red_zone_size = 128;
self.has_sret = true; }
X86CallingConv::Win64 => {
self.int_arg_regs = vec![
X86PhysReg::RCX,
X86PhysReg::RDX,
X86PhysReg::R8,
X86PhysReg::R9,
];
self.sse_arg_regs = vec![
X86PhysReg::XMM0,
X86PhysReg::XMM1,
X86PhysReg::XMM2,
X86PhysReg::XMM3,
];
self.int_return_regs = vec![X86PhysReg::RAX];
self.sse_return_reg = Some(X86PhysReg::XMM0);
self.callee_saved = vec![
X86PhysReg::RBX,
X86PhysReg::RBP,
X86PhysReg::RDI,
X86PhysReg::RSI,
X86PhysReg::R12,
X86PhysReg::R13,
X86PhysReg::R14,
X86PhysReg::R15,
X86PhysReg::XMM6,
X86PhysReg::XMM7,
X86PhysReg::XMM8,
X86PhysReg::XMM9,
X86PhysReg::XMM10,
X86PhysReg::XMM11,
X86PhysReg::XMM12,
X86PhysReg::XMM13,
X86PhysReg::XMM14,
X86PhysReg::XMM15,
];
self.call_clobbered = vec![
X86PhysReg::RAX,
X86PhysReg::RCX,
X86PhysReg::RDX,
X86PhysReg::R8,
X86PhysReg::R9,
X86PhysReg::R10,
X86PhysReg::R11,
X86PhysReg::XMM0,
X86PhysReg::XMM1,
X86PhysReg::XMM2,
X86PhysReg::XMM3,
X86PhysReg::XMM4,
X86PhysReg::XMM5,
];
self.shadow_space_size = 32;
self.has_red_zone = false;
self.red_zone_size = 0;
}
X86CallingConv::CDecl => {
self.int_arg_regs = vec![];
self.sse_arg_regs = vec![];
self.int_return_regs = vec![X86PhysReg::EAX, X86PhysReg::EDX];
self.sse_return_reg = Some(X86PhysReg::XMM0);
self.callee_saved = vec![
X86PhysReg::EBX,
X86PhysReg::EBP,
X86PhysReg::ESI,
X86PhysReg::EDI,
];
self.call_clobbered = vec![X86PhysReg::EAX, X86PhysReg::ECX, X86PhysReg::EDX];
self.shadow_space_size = 0;
self.frame_ptr = X86PhysReg::EBP;
self.stack_ptr = X86PhysReg::ESP;
}
X86CallingConv::FastCall => {
self.int_arg_regs = vec![X86PhysReg::ECX, X86PhysReg::EDX];
self.sse_arg_regs = vec![];
self.int_return_regs = vec![X86PhysReg::EAX, X86PhysReg::EDX];
self.sse_return_reg = Some(X86PhysReg::XMM0);
self.callee_saved = vec![
X86PhysReg::EBX,
X86PhysReg::EBP,
X86PhysReg::ESI,
X86PhysReg::EDI,
];
self.call_clobbered = vec![X86PhysReg::EAX, X86PhysReg::ECX, X86PhysReg::EDX];
self.frame_ptr = X86PhysReg::EBP;
self.stack_ptr = X86PhysReg::ESP;
}
X86CallingConv::ThisCall => {
self.int_arg_regs = vec![X86PhysReg::ECX]; self.sse_arg_regs = vec![];
self.int_return_regs = vec![X86PhysReg::EAX, X86PhysReg::EDX];
self.callee_saved = vec![
X86PhysReg::EBX,
X86PhysReg::EBP,
X86PhysReg::ESI,
X86PhysReg::EDI,
];
self.call_clobbered = vec![X86PhysReg::EAX, X86PhysReg::ECX, X86PhysReg::EDX];
self.frame_ptr = X86PhysReg::EBP;
self.stack_ptr = X86PhysReg::ESP;
}
X86CallingConv::VectorCall => {
if self.cc.is_64bit() {
self.int_arg_regs = vec![
X86PhysReg::RCX,
X86PhysReg::RDX,
X86PhysReg::R8,
X86PhysReg::R9,
];
} else {
self.int_arg_regs = vec![X86PhysReg::ECX, X86PhysReg::EDX];
}
self.sse_arg_regs = (0..6).map(|i| self.xmm_reg(i)).collect();
self.int_return_regs = vec![X86PhysReg::RAX];
self.sse_return_reg = Some(X86PhysReg::XMM0);
}
X86CallingConv::RegCall => {
if self.cc.is_64bit() {
self.int_arg_regs = vec![
X86PhysReg::RAX,
X86PhysReg::RCX,
X86PhysReg::RDX,
X86PhysReg::RDI,
X86PhysReg::RSI,
X86PhysReg::R8,
X86PhysReg::R9,
X86PhysReg::R10,
];
} else {
self.int_arg_regs = vec![
X86PhysReg::EAX,
X86PhysReg::ECX,
X86PhysReg::EDX,
X86PhysReg::EDI,
X86PhysReg::ESI,
];
}
self.sse_arg_regs = (0..8).map(|i| self.xmm_reg(i)).collect();
self.int_return_regs = vec![X86PhysReg::RAX, X86PhysReg::RDX];
self.sse_return_reg = Some(X86PhysReg::XMM0);
}
X86CallingConv::StdCall => {
self.int_arg_regs = vec![];
self.sse_arg_regs = vec![];
self.int_return_regs = vec![X86PhysReg::EAX, X86PhysReg::EDX];
self.sse_return_reg = Some(X86PhysReg::XMM0);
self.callee_saved = vec![
X86PhysReg::EBX,
X86PhysReg::EBP,
X86PhysReg::ESI,
X86PhysReg::EDI,
];
self.call_clobbered = vec![X86PhysReg::EAX, X86PhysReg::ECX, X86PhysReg::EDX];
self.frame_ptr = X86PhysReg::EBP;
self.stack_ptr = X86PhysReg::ESP;
}
X86CallingConv::SysV32 | X86CallingConv::Custom => {
self.int_arg_regs = vec![];
self.sse_arg_regs = vec![];
self.int_return_regs = vec![X86PhysReg::EAX, X86PhysReg::EDX];
self.sse_return_reg = Some(X86PhysReg::XMM0);
self.callee_saved = vec![
X86PhysReg::EBX,
X86PhysReg::EBP,
X86PhysReg::ESI,
X86PhysReg::EDI,
];
self.call_clobbered = vec![X86PhysReg::EAX, X86PhysReg::ECX, X86PhysReg::EDX];
self.frame_ptr = X86PhysReg::EBP;
self.stack_ptr = X86PhysReg::ESP;
}
}
}
fn xmm_reg(&self, index: u32) -> X86PhysReg {
match index {
0 => X86PhysReg::XMM0,
1 => X86PhysReg::XMM1,
2 => X86PhysReg::XMM2,
3 => X86PhysReg::XMM3,
4 => X86PhysReg::XMM4,
5 => X86PhysReg::XMM5,
6 => X86PhysReg::XMM6,
7 => X86PhysReg::XMM7,
8 => X86PhysReg::XMM8,
9 => X86PhysReg::XMM9,
10 => X86PhysReg::XMM10,
11 => X86PhysReg::XMM11,
12 => X86PhysReg::XMM12,
13 => X86PhysReg::XMM13,
14 => X86PhysReg::XMM14,
15 => X86PhysReg::XMM15,
16 => X86PhysReg::XMM16,
17 => X86PhysReg::XMM17,
18 => X86PhysReg::XMM18,
19 => X86PhysReg::XMM19,
20 => X86PhysReg::XMM20,
21 => X86PhysReg::XMM21,
22 => X86PhysReg::XMM22,
23 => X86PhysReg::XMM23,
24 => X86PhysReg::XMM24,
25 => X86PhysReg::XMM25,
26 => X86PhysReg::XMM26,
27 => X86PhysReg::XMM27,
28 => X86PhysReg::XMM28,
29 => X86PhysReg::XMM29,
30 => X86PhysReg::XMM30,
_ => X86PhysReg::XMM31,
}
}
fn init_reg_costs(&mut self) {
let gpr64_costs: Vec<(X86PhysReg, u32)> = vec![
(X86PhysReg::RAX, 0), (X86PhysReg::RBX, 10), (X86PhysReg::RCX, 5), (X86PhysReg::RDX, 5),
(X86PhysReg::RSI, 5),
(X86PhysReg::RDI, 5),
(X86PhysReg::RBP, 15), (X86PhysReg::RSP, 20), (X86PhysReg::R8, 2),
(X86PhysReg::R9, 2),
(X86PhysReg::R10, 2),
(X86PhysReg::R11, 2),
(X86PhysReg::R12, 10), (X86PhysReg::R13, 10),
(X86PhysReg::R14, 10),
(X86PhysReg::R15, 10),
];
for (reg, cost) in gpr64_costs {
let is_callee_saved = self.callee_saved.contains(®);
let is_call_clobbered = self.call_clobbered.contains(®);
self.reg_costs.insert(
reg,
X86RegCost {
reg,
cost,
is_callee_saved,
is_call_clobbered,
spill_cost: 0,
reg_class: X86RegClass::GPR64,
},
);
}
for i in 0..16u32 {
let r = self.xmm_reg(i);
let cost = if i < 8 { 1 } else { 5 }; self.reg_costs.insert(
r,
X86RegCost {
reg: r,
cost,
is_callee_saved: self.callee_saved.contains(&r),
is_call_clobbered: self.call_clobbered.contains(&r),
spill_cost: 0,
reg_class: X86RegClass::XMM,
},
);
}
for i in 0..8u32 {
let r = match i {
0 => X86PhysReg::ST0,
1 => X86PhysReg::ST1,
2 => X86PhysReg::ST2,
3 => X86PhysReg::ST3,
4 => X86PhysReg::ST4,
5 => X86PhysReg::ST5,
6 => X86PhysReg::ST6,
_ => X86PhysReg::ST7,
};
self.reg_costs.insert(
r,
X86RegCost {
reg: r,
cost: 10, is_callee_saved: false,
is_call_clobbered: true,
spill_cost: 0,
reg_class: X86RegClass::X87,
},
);
}
}
fn compute_spill_costs(&mut self) {
for reg in &self.callee_saved.clone() {
let spill = self.compute_single_spill_cost(*reg);
if let Some(cost) = self.reg_costs.get_mut(reg) {
cost.spill_cost = spill;
self.total_callee_saved_spill_cost += spill;
}
}
}
fn compute_single_spill_cost(&self, reg: X86PhysReg) -> u32 {
match reg {
X86PhysReg::RAX
| X86PhysReg::RBX
| X86PhysReg::RCX
| X86PhysReg::RDX
| X86PhysReg::RSI
| X86PhysReg::RDI
| X86PhysReg::RBP
| X86PhysReg::RSP
| X86PhysReg::R8
| X86PhysReg::R9
| X86PhysReg::R10
| X86PhysReg::R11
| X86PhysReg::R12
| X86PhysReg::R13
| X86PhysReg::R14
| X86PhysReg::R15 => {
4 }
X86PhysReg::XMM0
| X86PhysReg::XMM1
| X86PhysReg::XMM2
| X86PhysReg::XMM3
| X86PhysReg::XMM4
| X86PhysReg::XMM5
| X86PhysReg::XMM6
| X86PhysReg::XMM7
| X86PhysReg::XMM8
| X86PhysReg::XMM9
| X86PhysReg::XMM10
| X86PhysReg::XMM11
| X86PhysReg::XMM12
| X86PhysReg::XMM13
| X86PhysReg::XMM14
| X86PhysReg::XMM15
| X86PhysReg::XMM16
| X86PhysReg::XMM17
| X86PhysReg::XMM18
| X86PhysReg::XMM19
| X86PhysReg::XMM20
| X86PhysReg::XMM21
| X86PhysReg::XMM22
| X86PhysReg::XMM23
| X86PhysReg::XMM24
| X86PhysReg::XMM25
| X86PhysReg::XMM26
| X86PhysReg::XMM27
| X86PhysReg::XMM28
| X86PhysReg::XMM29
| X86PhysReg::XMM30
| X86PhysReg::XMM31 => {
6
}
_ => 4, }
}
pub fn get_int_arg_reg(&self, idx: u32) -> Option<X86PhysReg> {
self.int_arg_regs.get(idx as usize).copied()
}
pub fn get_sse_arg_reg(&self, idx: u32) -> Option<X86PhysReg> {
self.sse_arg_regs.get(idx as usize).copied()
}
pub fn are_compatible(&self, other: &X86CallingConvLowering) -> bool {
if self.cc == other.cc {
return true;
}
if matches!(self.cc, X86CallingConv::SysV64) && matches!(other.cc, X86CallingConv::Win64) {
return false;
}
if matches!(self.cc, X86CallingConv::Win64) && matches!(other.cc, X86CallingConv::SysV64) {
return false;
}
if self.stack_alignment == other.stack_alignment
&& self.cc.is_64bit() == other.cc.is_64bit()
{
return true;
}
false
}
pub fn supports_libcall(&self) -> bool {
true
}
pub fn compute_outgoing_arg_space(
&self,
num_int_args: u32,
num_sse_args: u32,
_total_arg_bytes: u32,
) -> u32 {
let int_regs_available = self.int_arg_regs.len() as u32;
let sse_regs_available = self.sse_arg_regs.len() as u32;
let int_overflow = if num_int_args > int_regs_available {
num_int_args - int_regs_available
} else {
0
};
let sse_overflow = if num_sse_args > sse_regs_available {
num_sse_args - sse_regs_available
} else {
0
};
let overflow_bytes = (int_overflow + sse_overflow) * 8; let total = overflow_bytes + self.shadow_space_size;
(total + self.stack_alignment - 1) & !(self.stack_alignment - 1)
}
pub fn get_compatibility(&self, target_cc: X86CallingConv) -> bool {
let other = X86CallingConvLowering::new(target_cc);
self.are_compatible(&other)
}
}
#[derive(Debug, Clone)]
pub struct IntrinsicToOpcode {
pub intrinsic_name: &'static str,
pub x86_opcodes: Vec<u32>,
pub requires_sse2: bool,
pub requires_avx: bool,
pub requires_avx512: bool,
pub latency: u32,
pub throughput: f64,
pub uops: u32,
pub always_legal: bool,
}
pub struct X86IntrinsicLowering {
pub intrinsic_map: HashMap<String, IntrinsicToOpcode>,
pub has_sse2: bool,
pub has_sse41: bool,
pub has_sse42: bool,
pub has_avx: bool,
pub has_avx2: bool,
pub has_avx512: bool,
pub has_avx512vl: bool,
pub has_avx512bw: bool,
pub has_avx512dq: bool,
pub has_fma: bool,
pub has_bmi: bool,
pub has_bmi2: bool,
pub has_lzcnt: bool,
pub has_popcnt: bool,
pub has_aes: bool,
pub has_sha: bool,
pub has_rdrand: bool,
pub has_f16c: bool, pub has_xsave: bool,
pub has_rtm: bool, pub has_adx: bool, }
impl X86IntrinsicLowering {
pub fn new() -> Self {
let mut lowering = X86IntrinsicLowering {
intrinsic_map: HashMap::new(),
has_sse2: true,
has_sse41: false,
has_sse42: false,
has_avx: false,
has_avx2: false,
has_avx512: false,
has_avx512vl: false,
has_avx512bw: false,
has_avx512dq: false,
has_fma: false,
has_bmi: false,
has_bmi2: false,
has_lzcnt: false,
has_popcnt: false,
has_aes: false,
has_sha: false,
has_rdrand: false,
has_f16c: false,
has_xsave: false,
has_rtm: false,
has_adx: false,
};
lowering.init_intrinsic_map();
lowering
}
pub fn with_features(
mut self,
sse2: bool,
sse41: bool,
sse42: bool,
avx: bool,
avx2: bool,
avx512: bool,
avx512vl: bool,
fma: bool,
bmi: bool,
bmi2: bool,
) -> Self {
self.has_sse2 = sse2;
self.has_sse41 = sse41;
self.has_sse42 = sse42;
self.has_avx = avx;
self.has_avx2 = avx2;
self.has_avx512 = avx512;
self.has_avx512vl = avx512vl;
self.has_fma = fma;
self.has_bmi = bmi;
self.has_bmi2 = bmi2;
self
}
fn init_intrinsic_map(&mut self) {
self.add_intrinsic(
"llvm.sqrt.f32",
vec![0],
false,
false,
false,
12,
6.0,
1,
true,
);
self.add_intrinsic(
"llvm.sqrt.f64",
vec![0],
false,
false,
false,
20,
10.0,
1,
true,
);
self.add_intrinsic(
"llvm.sin.f32",
vec![0],
false,
false,
false,
30,
10.0,
50,
true,
);
self.add_intrinsic(
"llvm.sin.f64",
vec![0],
false,
false,
false,
30,
10.0,
50,
true,
);
self.add_intrinsic(
"llvm.cos.f32",
vec![0],
false,
false,
false,
30,
10.0,
50,
true,
);
self.add_intrinsic(
"llvm.cos.f64",
vec![0],
false,
false,
false,
30,
10.0,
50,
true,
);
self.add_intrinsic(
"llvm.exp.f32",
vec![0],
false,
false,
false,
25,
8.0,
40,
true,
);
self.add_intrinsic(
"llvm.exp.f64",
vec![0],
false,
false,
false,
25,
8.0,
40,
true,
);
self.add_intrinsic(
"llvm.log.f32",
vec![0],
false,
false,
false,
25,
8.0,
40,
true,
);
self.add_intrinsic(
"llvm.log.f64",
vec![0],
false,
false,
false,
25,
8.0,
40,
true,
);
self.add_intrinsic(
"llvm.pow.f32",
vec![0],
false,
false,
false,
40,
12.0,
80,
true,
);
self.add_intrinsic(
"llvm.pow.f64",
vec![0],
false,
false,
false,
40,
12.0,
80,
true,
);
self.add_intrinsic(
"llvm.fabs.f32",
vec![0],
false,
false,
false,
1,
0.25,
1,
true,
);
self.add_intrinsic(
"llvm.fabs.f64",
vec![0],
false,
false,
false,
1,
0.25,
1,
true,
);
self.add_intrinsic(
"llvm.floor.f32",
vec![0],
false,
false,
false,
3,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.floor.f64",
vec![0],
false,
false,
false,
3,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.ceil.f32",
vec![0],
false,
false,
false,
3,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.ceil.f64",
vec![0],
false,
false,
false,
3,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.trunc.f32",
vec![0],
false,
false,
false,
3,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.trunc.f64",
vec![0],
false,
false,
false,
3,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.rint.f32",
vec![0],
false,
false,
false,
3,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.rint.f64",
vec![0],
false,
false,
false,
3,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.nearbyint.f32",
vec![0],
false,
false,
false,
3,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.nearbyint.f64",
vec![0],
false,
false,
false,
3,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.round.f32",
vec![0],
false,
false,
false,
3,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.round.f64",
vec![0],
false,
false,
false,
3,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.fma.f32",
vec![0],
false,
false,
false,
4,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.fma.f64",
vec![0],
false,
false,
false,
4,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.copysign.f32",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.copysign.f64",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.fmuladd.f32",
vec![0],
false,
false,
false,
4,
0.5,
1,
!self.has_fma,
);
self.add_intrinsic(
"llvm.fmuladd.f64",
vec![0],
false,
false,
false,
4,
0.5,
1,
!self.has_fma,
);
self.add_intrinsic(
"llvm.memcpy.p0i8.p0i8.i64",
vec![0],
false,
false,
false,
5,
1.0,
2,
true,
);
self.add_intrinsic(
"llvm.memmove.p0i8.p0i8.i64",
vec![0],
false,
false,
false,
5,
1.0,
3,
true,
);
self.add_intrinsic(
"llvm.memset.p0i8.i64",
vec![0],
false,
false,
false,
3,
0.5,
1,
true,
);
self.add_intrinsic(
"llvm.memcpy.element.unordered.atomic.p0i8.p0i8.i64",
vec![0],
false,
false,
false,
5,
1.0,
2,
true,
);
self.add_intrinsic(
"llvm.atomic.load.add.i32.p0i32",
vec![0],
false,
false,
false,
18,
5.0,
4,
true,
);
self.add_intrinsic(
"llvm.atomic.load.add.i64.p0i64",
vec![0],
false,
false,
false,
18,
5.0,
4,
true,
);
self.add_intrinsic(
"llvm.atomic.cmpxchg.i32.p0i32",
vec![0],
false,
false,
false,
18,
5.0,
5,
true,
);
self.add_intrinsic(
"llvm.atomic.cmpxchg.i64.p0i64",
vec![0],
false,
false,
false,
18,
5.0,
5,
true,
);
self.add_intrinsic(
"llvm.ctlz.i32",
vec![0],
false,
false,
false,
3,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.ctlz.i64",
vec![0],
false,
false,
false,
3,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.cttz.i32",
vec![0],
false,
false,
false,
3,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.cttz.i64",
vec![0],
false,
false,
false,
3,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.ctpop.i32",
vec![0],
false,
false,
false,
3,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.ctpop.i64",
vec![0],
false,
false,
false,
3,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.bswap.i16",
vec![0],
false,
false,
false,
1,
0.5,
1,
true,
);
self.add_intrinsic(
"llvm.bswap.i32",
vec![0],
false,
false,
false,
1,
0.5,
1,
true,
);
self.add_intrinsic(
"llvm.bswap.i64",
vec![0],
false,
false,
false,
1,
0.5,
1,
true,
);
self.add_intrinsic(
"llvm.bitreverse.i32",
vec![0],
false,
false,
false,
3,
1.0,
2,
false,
);
self.add_intrinsic(
"llvm.bitreverse.i64",
vec![0],
false,
false,
false,
3,
1.0,
2,
false,
);
self.add_intrinsic(
"llvm.fshl.i32",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.fshl.i64",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.fshr.i32",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.fshr.i64",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.smax.i32",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.smax.i64",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.smin.i32",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.smin.i64",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.umax.i32",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.umax.i64",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.umin.i32",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.umin.i64",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.sadd.sat.i32",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.sadd.sat.i64",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.uadd.sat.i32",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.uadd.sat.i64",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.ssub.sat.i32",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.ssub.sat.i64",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.usub.sat.i32",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.usub.sat.i64",
vec![0],
false,
false,
false,
1,
0.33,
1,
true,
);
self.add_intrinsic(
"llvm.vector.reduce.add.v4f32",
vec![0],
true,
false,
false,
3,
1.0,
2,
false,
);
self.add_intrinsic(
"llvm.vector.reduce.add.v8f32",
vec![0],
false,
true,
false,
5,
2.0,
3,
false,
);
self.add_intrinsic(
"llvm.vector.reduce.add.v16f32",
vec![0],
false,
false,
true,
7,
3.0,
5,
false,
);
self.add_intrinsic(
"llvm.vector.reduce.fadd.v4f32",
vec![0],
true,
false,
false,
3,
1.0,
2,
false,
);
self.add_intrinsic(
"llvm.vector.reduce.fadd.v8f32",
vec![0],
false,
true,
false,
5,
2.0,
3,
false,
);
self.add_intrinsic(
"llvm.vector.reduce.fadd.v16f32",
vec![0],
false,
false,
true,
7,
3.0,
5,
false,
);
self.add_intrinsic(
"llvm.eh.typeid.for",
vec![0],
false,
false,
false,
1,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.eh.exceptionpointer",
vec![0],
false,
false,
false,
1,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.eh.exceptioncode",
vec![0],
false,
false,
false,
1,
1.0,
1,
true,
);
self.add_intrinsic(
"llvm.x86.sse.sqrt.ss",
vec![0],
true,
false,
false,
12,
6.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse.sqrt.ps",
vec![0],
true,
false,
false,
12,
6.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse2.sqrt.sd",
vec![0],
true,
false,
false,
20,
10.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse2.sqrt.pd",
vec![0],
true,
false,
false,
20,
10.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse.rcp.ss",
vec![0],
true,
false,
false,
4,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse.rcp.ps",
vec![0],
true,
false,
false,
4,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse.rsqrt.ss",
vec![0],
true,
false,
false,
4,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse.rsqrt.ps",
vec![0],
true,
false,
false,
4,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse.min.ss",
vec![0],
true,
false,
false,
3,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse.min.ps",
vec![0],
true,
false,
false,
3,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse.max.ss",
vec![0],
true,
false,
false,
3,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse.max.ps",
vec![0],
true,
false,
false,
3,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse2.min.sd",
vec![0],
true,
false,
false,
3,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse2.min.pd",
vec![0],
true,
false,
false,
3,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse2.max.sd",
vec![0],
true,
false,
false,
3,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.x86.sse2.max.pd",
vec![0],
true,
false,
false,
3,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.x86.avx.sqrt.ps.256",
vec![0],
false,
true,
false,
12,
6.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.avx.sqrt.pd.256",
vec![0],
false,
true,
false,
20,
10.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.avx.addsub.ps.256",
vec![0],
false,
true,
false,
3,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.avx.addsub.pd.256",
vec![0],
false,
true,
false,
3,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.avx.hadd.ps.256",
vec![0],
false,
true,
false,
5,
2.0,
2,
false,
);
self.add_intrinsic(
"llvm.x86.avx.hadd.pd.256",
vec![0],
false,
true,
false,
5,
2.0,
2,
false,
);
self.add_intrinsic(
"llvm.x86.fma.vfmadd.ps",
vec![0],
true,
false,
false,
4,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.x86.fma.vfmadd.pd",
vec![0],
true,
false,
false,
4,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.x86.fma.vfmadd.ps.256",
vec![0],
false,
true,
false,
4,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.x86.fma.vfmadd.pd.256",
vec![0],
false,
true,
false,
4,
0.5,
1,
false,
);
self.add_intrinsic(
"llvm.x86.avx512.sqrt.ps.512",
vec![0],
false,
false,
true,
12,
6.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.avx512.sqrt.pd.512",
vec![0],
false,
false,
true,
20,
10.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.aesni.aesenc",
vec![0],
false,
false,
false,
4,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.aesni.aesenclast",
vec![0],
false,
false,
false,
4,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.aesni.aesdec",
vec![0],
false,
false,
false,
4,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.aesni.aesdeclast",
vec![0],
false,
false,
false,
4,
1.0,
1,
false,
);
self.add_intrinsic(
"llvm.x86.rdrand.16",
vec![0],
false,
false,
false,
15,
10.0,
5,
false,
);
self.add_intrinsic(
"llvm.x86.rdrand.32",
vec![0],
false,
false,
false,
15,
10.0,
5,
false,
);
self.add_intrinsic(
"llvm.x86.rdrand.64",
vec![0],
false,
false,
false,
15,
10.0,
5,
false,
);
self.add_intrinsic(
"llvm.x86.rdseed.16",
vec![0],
false,
false,
false,
100,
50.0,
10,
false,
);
self.add_intrinsic(
"llvm.x86.rdseed.32",
vec![0],
false,
false,
false,
100,
50.0,
10,
false,
);
self.add_intrinsic(
"llvm.x86.rdseed.64",
vec![0],
false,
false,
false,
100,
50.0,
10,
false,
);
}
fn add_intrinsic(
&mut self,
name: &'static str,
opcodes: Vec<u32>,
requires_sse2: bool,
requires_avx: bool,
requires_avx512: bool,
latency: u32,
throughput: f64,
uops: u32,
always_legal: bool,
) {
self.intrinsic_map.insert(
name.to_string(),
IntrinsicToOpcode {
intrinsic_name: name,
x86_opcodes: opcodes,
requires_sse2,
requires_avx,
requires_avx512,
latency,
throughput,
uops,
always_legal,
},
);
}
pub fn is_legal(&self, intrinsic_name: &str) -> bool {
if let Some(info) = self.intrinsic_map.get(intrinsic_name) {
if info.always_legal {
return true;
}
if info.requires_sse2 && !self.has_sse2 {
return false;
}
if info.requires_avx && !self.has_avx {
return false;
}
if info.requires_avx512 && !self.has_avx512 {
return false;
}
true
} else {
false
}
}
pub fn get_intrinsic_cost(&self, intrinsic_name: &str) -> Option<(u32, f64, u32)> {
self.intrinsic_map
.get(intrinsic_name)
.map(|info| (info.latency, info.throughput, info.uops))
}
pub fn get_opcodes(&self, intrinsic_name: &str) -> Option<&[u32]> {
self.intrinsic_map
.get(intrinsic_name)
.map(|info| info.x86_opcodes.as_slice())
}
pub fn features_available_for(&self, intrinsic_name: &str) -> bool {
if let Some(info) = self.intrinsic_map.get(intrinsic_name) {
(!info.requires_sse2 || self.has_sse2)
&& (!info.requires_avx || self.has_avx)
&& (!info.requires_avx512 || self.has_avx512)
} else {
false
}
}
}
impl Default for X86IntrinsicLowering {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LegalizeAction {
Legal,
Expand,
Promote,
Libcall,
Custom,
Scalarize,
Split,
Widen,
Narrow,
Unsupported,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types)]
pub enum ISDOpcode {
Add,
Sub,
Mul,
SDiv,
UDiv,
SRem,
URem,
And,
Or,
Xor,
Shl,
LShr,
AShr,
FAdd,
FSub,
FMul,
FDiv,
FRem,
FNeg,
FAbs,
Select,
Br,
BrCond,
BrIndirect,
Ret,
Call,
Truncate,
ZExt,
SExt,
FPTrunc,
FPExt,
FPToUI,
FPToSI,
UIToFP,
SIToFP,
Bitcast,
AddrSpaceCast,
Load,
Store,
AtomicLoadAdd,
AtomicCmpXchg,
AtomicRMW,
ExtractElt,
InsertElt,
BuildVec,
ShuffleVec,
ConcatVec,
ExtractSubvec,
InsertSubvec,
FrameIndex,
GlobalAddress,
ConstantPool,
JumpTable,
VSelect,
SAddO,
UAddO,
SSubO,
USubO,
SMulO,
UMulO,
X86_CALL,
X86_RET,
X86_CMP,
X86_TEST,
X86_CMOV,
X86_SETCC,
X86_MOVDDUP,
X86_MOVSHDUP,
X86_MOVSLDUP,
X86_VBROADCAST,
X86_VPERMILPY,
X86_PACKUS,
X86_PACKSS,
X86_FMAX,
X86_FMIN,
X86_FMAXC,
X86_FMINC,
X86_FSQRT,
X86_FRCP,
X86_FRSQRT,
X86_FADD_RND,
X86_FMUL_RND,
X86_FMA_RND,
X86_SCALAR_TO_VEC,
X86_VTRUNC,
X86_VTRUNCUS,
X86_CVTPD2PS,
X86_CVTPS2PD,
X86_CVTDQ2PD,
X86_CVTPD2DQ,
X86_CVTTPD2DQ,
X86_CVTDQ2PS,
X86_CVTTPS2DQ,
X86_CVTPS2DQ,
X86_VSHL,
X86_VSRL,
X86_VSRA,
X86_MUL_IMM,
X86_PMULUDQ,
X86_PTEST,
X86_TESTP,
X86_ADDUS,
X86_SUBUS,
X86_HADD,
X86_HSUB,
X86_VBROADCASTM,
X86_FPCLASS,
X86_VFPCLASSSS,
X86_VFPCLASSSD,
X86_STRICT_FCMP,
X86_STRICT_FCMPS,
X86_AVG,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LegalizeType {
I1,
I8,
I16,
I32,
I64,
I128,
F16,
F32,
F64,
F80,
F128,
V2I1,
V4I1,
V8I1,
V16I1,
V32I1,
V64I1,
V2I8,
V4I8,
V8I8,
V16I8,
V32I8,
V64I8,
V2I16,
V4I16,
V8I16,
V16I16,
V32I16,
V2I32,
V4I32,
V8I32,
V16I32,
V2I64,
V4I64,
V8I64,
V2F16,
V4F16,
V8F16,
V2F32,
V4F32,
V8F32,
V16F32,
V2F64,
V4F64,
V8F64,
VPointer,
}
impl LegalizeType {
pub fn is_vector(&self) -> bool {
use self::LegalizeType::*;
matches!(
self,
V2I1 | V4I1
| V8I1
| V16I1
| V32I1
| V64I1
| V2I8
| V4I8
| V8I8
| V16I8
| V32I8
| V64I8
| V2I16
| V4I16
| V8I16
| V16I16
| V32I16
| V2I32
| V4I32
| V8I32
| V16I32
| V2I64
| V4I64
| V8I64
| V2F16
| V4F16
| V8F16
| V2F32
| V4F32
| V8F32
| V16F32
| V2F64
| V4F64
| V8F64
)
}
pub fn scalar_size_bits(&self) -> u32 {
match self {
LegalizeType::I1 => 1,
LegalizeType::I8
| LegalizeType::V2I8
| LegalizeType::V4I8
| LegalizeType::V8I8
| LegalizeType::V16I8
| LegalizeType::V32I8
| LegalizeType::V64I8 => 8,
LegalizeType::I16
| LegalizeType::F16
| LegalizeType::V2I16
| LegalizeType::V4I16
| LegalizeType::V8I16
| LegalizeType::V16I16
| LegalizeType::V32I16
| LegalizeType::V2F16
| LegalizeType::V4F16
| LegalizeType::V8F16 => 16,
LegalizeType::I32
| LegalizeType::F32
| LegalizeType::V2I32
| LegalizeType::V4I32
| LegalizeType::V8I32
| LegalizeType::V16I32
| LegalizeType::V2F32
| LegalizeType::V4F32
| LegalizeType::V8F32
| LegalizeType::V16F32 => 32,
LegalizeType::I64
| LegalizeType::F64
| LegalizeType::V2I64
| LegalizeType::V4I64
| LegalizeType::V8I64
| LegalizeType::V2F64
| LegalizeType::V4F64
| LegalizeType::V8F64 => 64,
LegalizeType::F80 => 80,
LegalizeType::I128 | LegalizeType::F128 => 128,
LegalizeType::VPointer => 64, _ => 0,
}
}
}
pub struct X86ISelLowering {
pub is_64bit: bool,
pub has_sse2: bool,
pub has_avx: bool,
pub has_avx2: bool,
pub has_avx512: bool,
pub has_avx512vl: bool,
pub has_avx512dq: bool,
pub has_avx512bw: bool,
pub has_fma: bool,
pub has_cmov: bool,
pub has_popcnt: bool,
pub has_lzcnt: bool,
pub has_bmi: bool,
pub has_bmi2: bool,
pub has_movbe: bool,
pub legalize_table: HashMap<(ISDOpcode, LegalizeType), LegalizeAction>,
pub vector_actions: HashMap<LegalizeType, LegalizeAction>,
pub custom_lowering: HashSet<ISDOpcode>,
}
impl X86ISelLowering {
pub fn new(is_64bit: bool) -> Self {
let mut lowering = X86ISelLowering {
is_64bit,
has_sse2: true,
has_avx: false,
has_avx2: false,
has_avx512: false,
has_avx512vl: false,
has_avx512dq: false,
has_avx512bw: false,
has_fma: false,
has_cmov: true, has_popcnt: false,
has_lzcnt: false,
has_bmi: false,
has_bmi2: false,
has_movbe: false,
legalize_table: HashMap::new(),
vector_actions: HashMap::new(),
custom_lowering: HashSet::new(),
};
lowering.init_legalize_table();
lowering.init_vector_actions();
lowering.init_custom_lowering();
lowering
}
pub fn with_features(
mut self,
sse2: bool,
avx: bool,
avx2: bool,
avx512: bool,
avx512vl: bool,
fma: bool,
bmi2: bool,
popcnt: bool,
lzcnt: bool,
) -> Self {
self.has_sse2 = sse2;
self.has_avx = avx;
self.has_avx2 = avx2;
self.has_avx512 = avx512;
self.has_avx512vl = avx512vl;
self.has_fma = fma;
self.has_bmi2 = bmi2;
self.has_popcnt = popcnt;
self.has_lzcnt = lzcnt;
self.legalize_table.clear();
self.custom_lowering.clear();
self.init_legalize_table();
self.init_custom_lowering();
self
}
fn init_legalize_table(&mut self) {
use self::LegalizeAction::*;
use self::LegalizeType::*;
use ISDOpcode::*;
let scalar_types = [I8, I16, I32, I64];
let scalar_ops = [
Add, Sub, Mul, And, Or, Xor, Shl, LShr, AShr, Select, Load, Store,
];
for op in &scalar_ops {
for ty in &scalar_types {
if *op == Mul && (*ty == I64 && !self.is_64bit) {
self.set_action(*op, *ty, Expand);
} else {
self.set_action(*op, *ty, Legal);
}
}
}
self.set_action(SDiv, I8, Expand);
self.set_action(SDiv, I16, Expand);
self.set_action(SDiv, I32, Legal);
self.set_action(SDiv, I64, if self.is_64bit { Legal } else { Expand });
self.set_action(UDiv, I8, Expand);
self.set_action(UDiv, I16, Expand);
self.set_action(UDiv, I32, Legal);
self.set_action(UDiv, I64, if self.is_64bit { Legal } else { Expand });
self.set_action(SRem, I8, Expand);
self.set_action(SRem, I16, Expand);
self.set_action(SRem, I32, Legal);
self.set_action(SRem, I64, if self.is_64bit { Legal } else { Expand });
self.set_action(URem, I8, Expand);
self.set_action(URem, I16, Expand);
self.set_action(URem, I32, Legal);
self.set_action(URem, I64, if self.is_64bit { Legal } else { Expand });
for op in &[
Add, Sub, Mul, SDiv, UDiv, SRem, URem, And, Or, Xor, Shl, LShr, AShr,
] {
self.set_action(*op, I128, Libcall);
}
if self.is_64bit {
self.set_action(Add, I128, Expand); self.set_action(Sub, I128, Expand); self.set_action(And, I128, Expand);
self.set_action(Or, I128, Expand);
self.set_action(Xor, I128, Expand);
self.set_action(Shl, I128, Expand);
self.set_action(LShr, I128, Expand);
self.set_action(AShr, I128, Expand);
}
self.set_action(FAdd, F32, Legal);
self.set_action(FAdd, F64, Legal);
self.set_action(FSub, F32, Legal);
self.set_action(FSub, F64, Legal);
self.set_action(FMul, F32, Legal);
self.set_action(FMul, F64, Legal);
self.set_action(FDiv, F32, Legal);
self.set_action(FDiv, F64, Legal);
self.set_action(FRem, F32, Libcall); self.set_action(FRem, F64, Libcall);
self.set_action(FNeg, F32, Legal);
self.set_action(FNeg, F64, Legal);
self.set_action(FAbs, F32, Legal);
self.set_action(FAbs, F64, Legal);
self.set_action(FAdd, F80, Libcall);
self.set_action(FAdd, F128, Libcall);
self.set_action(FSub, F80, Libcall);
self.set_action(FSub, F128, Libcall);
self.set_action(FMul, F80, Libcall);
self.set_action(FMul, F128, Libcall);
self.set_action(FDiv, F80, Libcall);
self.set_action(FDiv, F128, Libcall);
if !self.has_avx512 || !self.has_avx512vl {
self.set_action(FAdd, F16, Promote);
self.set_action(FSub, F16, Promote);
self.set_action(FMul, F16, Promote);
self.set_action(FDiv, F16, Promote);
}
self.set_action(Truncate, I32, Legal);
self.set_action(ZExt, I32, Legal);
self.set_action(SExt, I32, Legal);
self.set_action(FPTrunc, F32, Legal); self.set_action(FPExt, F64, Legal); self.set_action(FPToUI, I32, Legal);
self.set_action(FPToUI, I64, Legal);
self.set_action(FPToSI, I32, Legal);
self.set_action(FPToSI, I64, Legal);
self.set_action(UIToFP, F32, Legal);
self.set_action(UIToFP, F64, Legal);
self.set_action(SIToFP, F32, Legal);
self.set_action(SIToFP, F64, Legal);
self.set_action(Bitcast, I32, Legal);
self.set_action(Bitcast, I64, Legal);
self.set_action(Bitcast, F32, Legal);
self.set_action(Bitcast, F64, Legal);
self.set_action(Select, I32, Legal);
self.set_action(Select, I64, Legal);
self.set_action(Select, F32, Legal);
self.set_action(Select, F64, Legal);
self.set_action(Br, I1, Legal);
self.set_action(BrCond, I1, Legal);
self.set_action(Ret, I32, Legal);
self.set_action(Call, I32, Legal);
if self.has_avx512 {
self.set_action(VSelect, V16I32, Legal);
self.set_action(VSelect, V8I64, Legal);
self.set_action(VSelect, V16F32, Legal);
self.set_action(VSelect, V8F64, Legal);
} else if self.has_avx {
self.set_action(VSelect, V8I32, Legal);
self.set_action(VSelect, V4I64, Legal);
self.set_action(VSelect, V8F32, Legal);
self.set_action(VSelect, V4F64, Legal);
} else {
self.set_action(VSelect, V4I32, Legal);
self.set_action(VSelect, V2I64, Legal);
self.set_action(VSelect, V4F32, Legal);
self.set_action(VSelect, V2F64, Legal);
}
self.set_action(SAddO, I32, Legal);
self.set_action(SAddO, I64, Legal);
self.set_action(UAddO, I32, Legal);
self.set_action(UAddO, I64, Legal);
self.set_action(SSubO, I32, Legal);
self.set_action(SSubO, I64, Legal);
self.set_action(USubO, I32, Legal);
self.set_action(USubO, I64, Legal);
self.set_action(SMulO, I32, Legal);
self.set_action(SMulO, I64, if self.is_64bit { Legal } else { Expand });
self.set_action(UMulO, I32, Legal);
self.set_action(UMulO, I64, if self.is_64bit { Legal } else { Expand });
}
fn init_vector_actions(&mut self) {
let max_width = if self.has_avx512 {
512
} else if self.has_avx {
256
} else {
128
};
self.set_vector_action(LegalizeType::V2I64, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V4I32, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V8I16, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V16I8, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V2F64, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V4F32, LegalizeAction::Legal);
if max_width >= 256 {
self.set_vector_action(LegalizeType::V4I64, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V8I32, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V16I16, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V32I8, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V4F64, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V8F32, LegalizeAction::Legal);
} else {
self.set_vector_action(LegalizeType::V4I64, LegalizeAction::Split);
self.set_vector_action(LegalizeType::V8I32, LegalizeAction::Split);
self.set_vector_action(LegalizeType::V16I16, LegalizeAction::Split);
self.set_vector_action(LegalizeType::V32I8, LegalizeAction::Split);
self.set_vector_action(LegalizeType::V4F64, LegalizeAction::Split);
self.set_vector_action(LegalizeType::V8F32, LegalizeAction::Split);
}
if max_width >= 512 {
self.set_vector_action(LegalizeType::V8I64, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V16I32, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V32I16, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V64I8, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V8F64, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V16F32, LegalizeAction::Legal);
} else {
self.set_vector_action(LegalizeType::V8I64, LegalizeAction::Split);
self.set_vector_action(LegalizeType::V16I32, LegalizeAction::Split);
self.set_vector_action(LegalizeType::V32I16, LegalizeAction::Split);
self.set_vector_action(LegalizeType::V64I8, LegalizeAction::Split);
self.set_vector_action(LegalizeType::V8F64, LegalizeAction::Split);
self.set_vector_action(LegalizeType::V16F32, LegalizeAction::Split);
}
self.set_vector_action(LegalizeType::V2I1, LegalizeAction::Expand);
self.set_vector_action(LegalizeType::V4I1, LegalizeAction::Expand);
self.set_vector_action(LegalizeType::V8I1, LegalizeAction::Expand);
self.set_vector_action(LegalizeType::V16I1, LegalizeAction::Expand);
if self.has_avx512bw {
self.set_vector_action(LegalizeType::V32I1, LegalizeAction::Legal);
self.set_vector_action(LegalizeType::V64I1, LegalizeAction::Legal);
} else {
self.set_vector_action(LegalizeType::V32I1, LegalizeAction::Expand);
self.set_vector_action(LegalizeType::V64I1, LegalizeAction::Expand);
}
}
fn init_custom_lowering(&mut self) {
self.custom_lowering.insert(ISDOpcode::ExtractElt);
self.custom_lowering.insert(ISDOpcode::InsertElt);
self.custom_lowering.insert(ISDOpcode::ShuffleVec);
self.custom_lowering.insert(ISDOpcode::BuildVec);
self.custom_lowering.insert(ISDOpcode::ConcatVec);
self.custom_lowering.insert(ISDOpcode::X86_FMAX);
self.custom_lowering.insert(ISDOpcode::X86_FMIN);
self.custom_lowering.insert(ISDOpcode::X86_FSQRT);
self.custom_lowering.insert(ISDOpcode::X86_FRCP);
self.custom_lowering.insert(ISDOpcode::X86_FRSQRT);
if self.has_avx2 {
self.custom_lowering.insert(ISDOpcode::X86_VBROADCAST);
}
if self.has_avx {
self.custom_lowering.insert(ISDOpcode::X86_VPERMILPY);
}
}
fn set_action(&mut self, op: ISDOpcode, ty: LegalizeType, action: LegalizeAction) {
self.legalize_table.insert((op, ty), action);
}
fn set_vector_action(&mut self, ty: LegalizeType, action: LegalizeAction) {
self.vector_actions.insert(ty, action);
}
pub fn get_legalize_action(&self, op: ISDOpcode, ty: LegalizeType) -> LegalizeAction {
self.legalize_table
.get(&(op, ty))
.copied()
.unwrap_or(LegalizeAction::Legal)
}
pub fn is_operation_legal(&self, op: ISDOpcode, ty: LegalizeType) -> bool {
matches!(self.get_legalize_action(op, ty), LegalizeAction::Legal)
}
pub fn should_expand(&self, op: ISDOpcode, ty: LegalizeType) -> bool {
matches!(self.get_legalize_action(op, ty), LegalizeAction::Expand)
}
pub fn should_promote(&self, op: ISDOpcode, ty: LegalizeType) -> bool {
matches!(self.get_legalize_action(op, ty), LegalizeAction::Promote)
}
pub fn needs_custom_lowering(&self, op: ISDOpcode) -> bool {
self.custom_lowering.contains(&op)
}
pub fn get_vector_action(&self, ty: LegalizeType) -> LegalizeAction {
self.vector_actions
.get(&ty)
.copied()
.unwrap_or(LegalizeAction::Legal)
}
pub fn should_split_vector(&self, ty: LegalizeType) -> bool {
matches!(self.get_vector_action(ty), LegalizeAction::Split)
}
pub fn should_widen_vector(&self, ty: LegalizeType) -> bool {
matches!(self.get_vector_action(ty), LegalizeAction::Widen)
}
pub fn get_vector_op_action(&self, op: ISDOpcode, ty: LegalizeType) -> LegalizeAction {
if self.should_split_vector(ty) {
LegalizeAction::Split
} else {
self.get_legalize_action(op, ty)
}
}
pub fn get_max_vector_width(&self) -> u32 {
if self.has_avx512 {
512
} else if self.has_avx {
256
} else {
128
}
}
}
#[derive(Debug, Clone)]
pub struct LegalizeEntry {
pub opcode: ISDOpcode,
pub type_actions: Vec<(LegalizeType, LegalizeAction)>,
pub custom_hook: Option<String>,
pub can_libcall: bool,
pub libcall_name: Option<String>,
}
pub struct X86OperationLegalizer {
pub isel: X86ISelLowering,
pub legalize_entries: Vec<LegalizeEntry>,
pub custom_hooks:
HashMap<String, Box<dyn Fn(ISDOpcode, LegalizeType) -> LegalizeAction + Send + Sync>>,
pub type_hooks: HashMap<LegalizeType, LegalizeAction>,
}
impl X86OperationLegalizer {
pub fn new(isel: X86ISelLowering) -> Self {
let mut legalizer = X86OperationLegalizer {
isel,
legalize_entries: Vec::new(),
custom_hooks: HashMap::new(),
type_hooks: HashMap::new(),
};
legalizer.init_legalize_entries();
legalizer.init_type_hooks();
legalizer
}
fn init_legalize_entries(&mut self) {
use self::LegalizeAction::*;
use self::LegalizeType::*;
use ISDOpcode::*;
let int_expand_small = vec![
(I1, Expand),
(I8, Promote),
(I16, Promote),
(I32, Legal),
(I64, if self.isel.is_64bit { Legal } else { Expand }),
];
for op in [Add, Sub, Mul, And, Or, Xor, Shl, LShr, AShr] {
self.legalize_entries.push(LegalizeEntry {
opcode: op,
type_actions: int_expand_small.clone(),
custom_hook: None,
can_libcall: false,
libcall_name: None,
});
}
self.legalize_entries.push(LegalizeEntry {
opcode: SDiv,
type_actions: vec![
(I8, Expand),
(I16, Expand),
(I32, Legal),
(I64, if self.isel.is_64bit { Legal } else { Expand }),
(I128, Libcall),
],
custom_hook: None,
can_libcall: true,
libcall_name: Some("__divti3".to_string()),
});
self.legalize_entries.push(LegalizeEntry {
opcode: UDiv,
type_actions: vec![
(I8, Expand),
(I16, Expand),
(I32, Legal),
(I64, if self.isel.is_64bit { Legal } else { Expand }),
(I128, Libcall),
],
custom_hook: None,
can_libcall: true,
libcall_name: Some("__udivti3".to_string()),
});
self.legalize_entries.push(LegalizeEntry {
opcode: FAdd,
type_actions: vec![
(F16, Promote),
(F32, Legal),
(F64, Legal),
(F80, Libcall),
(F128, Libcall),
],
custom_hook: None,
can_libcall: true,
libcall_name: None,
});
self.legalize_entries.push(LegalizeEntry {
opcode: FMul,
type_actions: vec![
(F16, Promote),
(F32, Legal),
(F64, Legal),
(F80, Libcall),
(F128, Libcall),
],
custom_hook: None,
can_libcall: true,
libcall_name: None,
});
self.legalize_entries.push(LegalizeEntry {
opcode: FDiv,
type_actions: vec![
(F16, Promote),
(F32, Legal),
(F64, Legal),
(F80, Libcall),
(F128, Libcall),
],
custom_hook: None,
can_libcall: true,
libcall_name: None,
});
let max_width = self.isel.get_max_vector_width();
let vec_widths = [
(128, &[V4I32, V2I64, V4F32, V2F64][..]),
(256, &[V8I32, V4I64, V8F32, V4F64]),
(512, &[V16I32, V8I64, V16F32, V8F64]),
];
for (width, types) in &vec_widths {
let action = if *width <= max_width { Legal } else { Split };
for ty in *types {
for op in [Add, Sub, FAdd, FSub, FMul] {
self.legalize_entries.push(LegalizeEntry {
opcode: op,
type_actions: vec![(*ty, action)],
custom_hook: None,
can_libcall: false,
libcall_name: None,
});
}
}
}
for op in [Load, Store] {
self.legalize_entries.push(LegalizeEntry {
opcode: op,
type_actions: vec![
(I8, Legal),
(I16, Legal),
(I32, Legal),
(I64, Legal),
(F32, Legal),
(F64, Legal),
(V4I32, Legal),
(V2I64, Legal),
(V4F32, Legal),
(V2F64, Legal),
],
custom_hook: None,
can_libcall: false,
libcall_name: None,
});
}
self.legalize_entries.push(LegalizeEntry {
opcode: Select,
type_actions: vec![
(I1, Expand),
(I8, Legal),
(I16, Legal),
(I32, Legal),
(I64, Legal),
(F32, Legal),
(F64, Legal),
],
custom_hook: None,
can_libcall: false,
libcall_name: None,
});
self.legalize_entries.push(LegalizeEntry {
opcode: ShuffleVec,
type_actions: vec![
(V4I32, Custom),
(V2I64, Custom),
(V4F32, Custom),
(V2F64, Custom),
(V8I32, Custom),
(V4I64, Custom),
(V8F32, Custom),
(V4F64, Custom),
(V16I32, Custom),
(V8I64, Custom),
(V16F32, Custom),
(V8F64, Custom),
],
custom_hook: Some("lower_shuffle_vector".to_string()),
can_libcall: false,
libcall_name: None,
});
for op in [ExtractElt, InsertElt] {
self.legalize_entries.push(LegalizeEntry {
opcode: op,
type_actions: vec![
(V4I32, Custom),
(V2I64, Custom),
(V8I32, Custom),
(V4I64, Custom),
(V16I32, Custom),
(V8I64, Custom),
],
custom_hook: Some(format!("lower_{:?}", op)),
can_libcall: false,
libcall_name: None,
});
}
self.legalize_entries.push(LegalizeEntry {
opcode: BuildVec,
type_actions: vec![
(V4I32, Custom),
(V8I32, Custom),
(V16I32, Custom),
(V2I64, Custom),
(V4I64, Custom),
(V8I64, Custom),
(V4F32, Custom),
(V8F32, Custom),
(V16F32, Custom),
(V2F64, Custom),
(V4F64, Custom),
(V8F64, Custom),
],
custom_hook: Some("lower_build_vector".to_string()),
can_libcall: false,
libcall_name: None,
});
}
fn init_type_hooks(&mut self) {
let max_width = self.isel.get_max_vector_width();
let all_vec_types = [
LegalizeType::V4I32,
LegalizeType::V8I32,
LegalizeType::V16I32,
LegalizeType::V2I64,
LegalizeType::V4I64,
LegalizeType::V8I64,
LegalizeType::V4F32,
LegalizeType::V8F32,
LegalizeType::V16F32,
LegalizeType::V2F64,
LegalizeType::V4F64,
LegalizeType::V8F64,
LegalizeType::V8I16,
LegalizeType::V16I16,
LegalizeType::V32I16,
LegalizeType::V16I8,
LegalizeType::V32I8,
LegalizeType::V64I8,
];
for ty in &all_vec_types {
let bits = match ty {
LegalizeType::V4I32 | LegalizeType::V4F32 => 128,
LegalizeType::V8I32
| LegalizeType::V8F32
| LegalizeType::V4I64
| LegalizeType::V4F64
| LegalizeType::V16I16
| LegalizeType::V32I8 => 256,
LegalizeType::V16I32
| LegalizeType::V16F32
| LegalizeType::V8I64
| LegalizeType::V8F64
| LegalizeType::V32I16
| LegalizeType::V64I8 => 512,
LegalizeType::V2I64
| LegalizeType::V2F64
| LegalizeType::V8I16
| LegalizeType::V16I8 => 128,
_ => 128,
};
if bits > max_width {
self.type_hooks.insert(*ty, LegalizeAction::Split);
} else {
self.type_hooks.insert(*ty, LegalizeAction::Legal);
}
}
}
pub fn get_action(&self, op: ISDOpcode, ty: LegalizeType) -> LegalizeAction {
for entry in &self.legalize_entries {
if entry.opcode == op {
for (t, action) in &entry.type_actions {
if *t == ty {
return *action;
}
}
}
}
self.isel.get_legalize_action(op, ty)
}
pub fn is_legal(&self, op: ISDOpcode, ty: LegalizeType) -> bool {
matches!(self.get_action(op, ty), LegalizeAction::Legal)
}
pub fn get_custom_hook(&self, op: ISDOpcode) -> Option<&str> {
for entry in &self.legalize_entries {
if entry.opcode == op {
return entry.custom_hook.as_deref();
}
}
None
}
pub fn get_libcall_name(&self, op: ISDOpcode, ty: LegalizeType) -> Option<&str> {
for entry in &self.legalize_entries {
if entry.opcode == op {
for (t, action) in &entry.type_actions {
if *t == ty && *action == LegalizeAction::Libcall {
return entry.libcall_name.as_deref();
}
}
}
}
None
}
pub fn register_custom_hook(
&mut self,
name: String,
hook: Box<dyn Fn(ISDOpcode, LegalizeType) -> LegalizeAction + Send + Sync>,
) {
self.custom_hooks.insert(name, hook);
}
pub fn invoke_hook(
&self,
name: &str,
op: ISDOpcode,
ty: LegalizeType,
) -> Option<LegalizeAction> {
self.custom_hooks.get(name).map(|h| h(op, ty))
}
pub fn get_type_action(&self, ty: LegalizeType) -> LegalizeAction {
self.type_hooks
.get(&ty)
.copied()
.unwrap_or(LegalizeAction::Legal)
}
pub fn needs_scalarization(&self, ty: LegalizeType) -> bool {
matches!(self.get_type_action(ty), LegalizeAction::Scalarize)
}
pub fn needs_splitting(&self, ty: LegalizeType) -> bool {
matches!(self.get_type_action(ty), LegalizeAction::Split)
}
}
pub struct X86TargetLoweringInfo {
pub lib_info: X86TargetLibraryInfo,
pub transform_info: X86TargetTransformInfo,
pub cc_lowering: X86CallingConvLowering,
pub intrinsic_lowering: X86IntrinsicLowering,
pub isel_lowering: X86ISelLowering,
pub operation_legalizer: X86OperationLegalizer,
pub is_64bit: bool,
pub target_triple: String,
pub cpu_name: String,
}
impl X86TargetLoweringInfo {
pub fn new(
is_64bit: bool,
os_target: X86OSTarget,
cc: X86CallingConv,
microarch: X86Microarch,
cpu_name: &str,
target_triple: &str,
) -> Self {
let lib_info = X86TargetLibraryInfo::new(os_target);
let transform_info = X86TargetTransformInfo::new(microarch);
let cc_lowering = X86CallingConvLowering::new(cc);
let intrinsic_lowering = X86IntrinsicLowering::new();
let isel_lowering = X86ISelLowering::new(is_64bit);
let operation_legalizer = X86OperationLegalizer::new(X86ISelLowering::new(is_64bit));
X86TargetLoweringInfo {
lib_info,
transform_info,
cc_lowering,
intrinsic_lowering,
isel_lowering,
operation_legalizer,
is_64bit,
target_triple: target_triple.to_string(),
cpu_name: cpu_name.to_string(),
}
}
pub fn with_subtarget(
mut self,
has_avx: bool,
has_avx2: bool,
has_avx512: bool,
has_avx512vl: bool,
has_fma: bool,
has_bmi2: bool,
has_popcnt: bool,
has_lzcnt: bool,
) -> Self {
self.transform_info = self.transform_info.with_features(
has_avx,
has_avx2,
has_avx512,
has_avx512vl,
has_fma,
42,
);
self.intrinsic_lowering = self.intrinsic_lowering.with_features(
true, true, true, has_avx, has_avx2, has_avx512, has_avx512vl, has_fma, has_bmi2, has_bmi2, );
self.isel_lowering = self.isel_lowering.with_features(
true,
has_avx,
has_avx2,
has_avx512,
has_avx512vl,
has_fma,
has_bmi2,
has_popcnt,
has_lzcnt,
);
self.operation_legalizer =
X86OperationLegalizer::new(X86ISelLowering::new(self.is_64bit).with_features(
true,
has_avx,
has_avx2,
has_avx512,
has_avx512vl,
has_fma,
has_bmi2,
has_popcnt,
has_lzcnt,
));
self
}
pub fn has_lib_func(&self, func: X86LibFunc) -> bool {
self.lib_info.has(func)
}
pub fn get_instr_cost(&self, kind: CostKind) -> InstrCost {
self.transform_info.get_cost(kind)
}
pub fn is_op_legal(&self, op: ISDOpcode, ty: LegalizeType) -> bool {
self.isel_lowering.is_operation_legal(op, ty)
}
pub fn get_legalize_action(&self, op: ISDOpcode, ty: LegalizeType) -> LegalizeAction {
self.operation_legalizer.get_action(op, ty)
}
pub fn is_intrinsic_legal(&self, name: &str) -> bool {
self.intrinsic_lowering.is_legal(name)
}
pub fn compute_inline_threshold(
&self,
num_instructions: u32,
has_loops: bool,
is_alwaysinline: bool,
is_noinline: bool,
) -> u32 {
self.transform_info.compute_inline_threshold(
num_instructions,
has_loops,
is_alwaysinline,
is_noinline,
)
}
pub fn compute_switch_cost(&self, num_cases: u32, min_case: i64, max_case: i64) -> SwitchCost {
self.transform_info
.compute_switch_cost(num_cases, min_case, max_case)
}
pub fn are_ccs_compatible(&self, other: X86CallingConv) -> bool {
self.cc_lowering.get_compatibility(other)
}
pub fn get_callee_saved_spill_cost(&self) -> u32 {
self.cc_lowering.total_callee_saved_spill_cost
}
pub fn compute_block_cost(&self, ops: &[(ISDOpcode, LegalizeType)]) -> u32 {
ops.iter()
.map(|(op, ty)| {
let action = self.get_legalize_action(*op, *ty);
match action {
LegalizeAction::Legal => 1,
LegalizeAction::Expand => 3,
LegalizeAction::Promote => 2,
LegalizeAction::Libcall => 20,
LegalizeAction::Custom => 5,
LegalizeAction::Scalarize => 4,
LegalizeAction::Split => 4,
LegalizeAction::Widen => 2,
LegalizeAction::Narrow => 2,
LegalizeAction::Unsupported => 100,
}
})
.sum()
}
pub fn get_best_vector_width(&self, trip_count: u32, element_size_bits: u32) -> u32 {
let max_vec_bits = self.transform_info.max_vector_width;
let elements_per_vec = max_vec_bits / element_size_bits;
if trip_count < elements_per_vec * 2 {
return 128.min(max_vec_bits);
}
if trip_count >= elements_per_vec * 8 {
return max_vec_bits;
}
if trip_count >= elements_per_vec * 4 {
return (max_vec_bits / 2).max(128);
}
128.min(max_vec_bits)
}
}
pub struct X86MicroarchCostTable {
pub microarch: X86Microarch,
pub costs: BTreeMap<CostKind, InstrCost>,
}
impl X86MicroarchCostTable {
pub fn skylake() -> Self {
let mut costs = BTreeMap::new();
let alu_simple = InstrCost::new(1, 0.25, 1, 0x63); for k in &[
CostKind::AddI32,
CostKind::AddI64,
CostKind::SubI32,
CostKind::SubI64,
CostKind::AndI32,
CostKind::AndI64,
CostKind::OrI32,
CostKind::OrI64,
CostKind::XorI32,
CostKind::XorI64,
CostKind::ShlI32,
CostKind::ShlI64,
CostKind::LShrI32,
CostKind::LShrI64,
CostKind::AShrI32,
CostKind::AShrI64,
CostKind::NotI32,
CostKind::NotI64,
CostKind::NegI32,
CostKind::NegI64,
] {
costs.insert(*k, alu_simple);
}
costs.insert(CostKind::MulI32, InstrCost::new(3, 1.0, 1, 0x02));
costs.insert(CostKind::MulI64, InstrCost::new(3, 1.0, 1, 0x02));
costs.insert(CostKind::IMulI64High, InstrCost::new(4, 1.0, 2, 0x02));
costs.insert(CostKind::SDivI32, InstrCost::new(20, 6.0, 9, 0x01));
costs.insert(CostKind::SDivI64, InstrCost::new(35, 10.0, 30, 0x01));
costs.insert(CostKind::UDivI32, InstrCost::new(20, 6.0, 9, 0x01));
costs.insert(CostKind::UDivI64, InstrCost::new(35, 10.0, 30, 0x01));
let fadd = InstrCost::new(3, 0.5, 1, 0x03);
costs.insert(CostKind::FAddF32, fadd);
costs.insert(CostKind::FAddF64, fadd);
costs.insert(CostKind::FSubF32, fadd);
costs.insert(CostKind::FSubF64, fadd);
let fmul = InstrCost::new(4, 0.5, 1, 0x03);
costs.insert(CostKind::FMulF32, fmul);
costs.insert(CostKind::FMulF64, fmul);
costs.insert(CostKind::FDivF32, InstrCost::new(13, 4.0, 3, 0x01));
costs.insert(CostKind::FDivF64, InstrCost::new(20, 8.0, 4, 0x01));
costs.insert(CostKind::FSqrtF32, InstrCost::new(12, 6.0, 1, 0x01));
costs.insert(CostKind::FSqrtF64, InstrCost::new(20, 10.0, 1, 0x01));
let l1_load = InstrCost::new(4, 0.5, 1, 0x0C); costs.insert(CostKind::LoadI32, l1_load);
costs.insert(CostKind::LoadI64, InstrCost::new(4, 0.5, 1, 0x0C));
costs.insert(CostKind::LoadF32, InstrCost::new(4, 0.5, 1, 0x0C));
costs.insert(CostKind::LoadF64, InstrCost::new(4, 0.5, 1, 0x0C));
let l1_store = InstrCost::new(1, 1.0, 1, 0x30); costs.insert(CostKind::StoreI32, l1_store);
costs.insert(CostKind::StoreI64, l1_store);
costs.insert(CostKind::StoreF32, l1_store);
costs.insert(CostKind::StoreF64, l1_store);
costs.insert(CostKind::LoadV128, InstrCost::new(4, 0.5, 1, 0x0C));
costs.insert(CostKind::LoadV256, InstrCost::new(4, 0.5, 1, 0x0C));
costs.insert(CostKind::LoadV512, InstrCost::new(5, 0.5, 1, 0x0C));
costs.insert(CostKind::StoreV128, InstrCost::new(1, 1.0, 1, 0x30));
costs.insert(CostKind::StoreV256, InstrCost::new(1, 1.0, 1, 0x30));
costs.insert(CostKind::StoreV512, InstrCost::new(1, 1.0, 2, 0x30));
X86MicroarchCostTable {
microarch: X86Microarch::Skylake,
costs,
}
}
pub fn zen4() -> Self {
let mut costs = BTreeMap::new();
let alu = InstrCost::new(1, 0.25, 1, 0xFF);
for k in &[
CostKind::AddI32,
CostKind::AddI64,
CostKind::SubI32,
CostKind::SubI64,
CostKind::AndI32,
CostKind::AndI64,
CostKind::OrI32,
CostKind::OrI64,
CostKind::XorI32,
CostKind::XorI64,
CostKind::ShlI32,
CostKind::ShlI64,
CostKind::LShrI32,
CostKind::LShrI64,
CostKind::AShrI32,
CostKind::AShrI64,
] {
costs.insert(*k, alu);
}
costs.insert(CostKind::MulI32, InstrCost::new(3, 1.0, 1, 0x01));
costs.insert(CostKind::MulI64, InstrCost::new(4, 1.0, 1, 0x01));
costs.insert(CostKind::SDivI32, InstrCost::new(18, 6.0, 8, 0x01));
costs.insert(CostKind::SDivI64, InstrCost::new(34, 10.0, 28, 0x01));
costs.insert(CostKind::UDivI32, InstrCost::new(18, 6.0, 8, 0x01));
costs.insert(CostKind::UDivI64, InstrCost::new(34, 10.0, 28, 0x01));
let fadd = InstrCost::new(3, 0.5, 1, 0x03);
costs.insert(CostKind::FAddF32, fadd);
costs.insert(CostKind::FAddF64, fadd);
costs.insert(CostKind::FMulF32, InstrCost::new(3, 0.5, 1, 0x03));
costs.insert(CostKind::FMulF64, InstrCost::new(3, 0.5, 1, 0x03));
costs.insert(CostKind::FDivF32, InstrCost::new(13, 4.5, 3, 0x01));
costs.insert(CostKind::FDivF64, InstrCost::new(20, 8.5, 4, 0x01));
costs.insert(CostKind::FSqrtF32, InstrCost::new(12, 6.0, 1, 0x01));
costs.insert(CostKind::FSqrtF64, InstrCost::new(20, 10.0, 1, 0x01));
let ld = InstrCost::new(4, 0.33, 1, 0x0C);
costs.insert(CostKind::LoadI32, ld);
costs.insert(CostKind::LoadI64, ld);
costs.insert(CostKind::LoadF32, ld);
costs.insert(CostKind::LoadF64, ld);
let st = InstrCost::new(1, 0.5, 1, 0x30);
costs.insert(CostKind::StoreI32, st);
costs.insert(CostKind::StoreI64, st);
costs.insert(CostKind::LoadV128, InstrCost::new(4, 0.33, 1, 0x0C));
costs.insert(CostKind::LoadV256, InstrCost::new(4, 0.33, 1, 0x0C));
costs.insert(CostKind::LoadV512, InstrCost::new(4, 0.33, 1, 0x0C));
costs.insert(CostKind::StoreV128, InstrCost::new(1, 0.5, 1, 0x30));
costs.insert(CostKind::StoreV256, InstrCost::new(1, 0.5, 1, 0x30));
costs.insert(CostKind::StoreV512, InstrCost::new(1, 0.5, 2, 0x30));
X86MicroarchCostTable {
microarch: X86Microarch::Zen4,
costs,
}
}
pub fn ice_lake() -> Self {
let mut costs = BTreeMap::new();
let alu = InstrCost::new(1, 0.2, 1, 0xFF); for k in &[
CostKind::AddI32,
CostKind::AddI64,
CostKind::SubI32,
CostKind::SubI64,
CostKind::AndI32,
CostKind::AndI64,
CostKind::OrI32,
CostKind::OrI64,
CostKind::XorI32,
CostKind::XorI64,
CostKind::ShlI32,
CostKind::ShlI64,
CostKind::LShrI32,
CostKind::LShrI64,
CostKind::AShrI32,
CostKind::AShrI64,
] {
costs.insert(*k, alu);
}
costs.insert(CostKind::MulI32, InstrCost::new(3, 1.0, 1, 0x02));
costs.insert(CostKind::MulI64, InstrCost::new(3, 1.0, 1, 0x02));
costs.insert(CostKind::SDivI32, InstrCost::new(18, 5.5, 8, 0x01));
costs.insert(CostKind::SDivI64, InstrCost::new(32, 9.5, 28, 0x01));
costs.insert(CostKind::UDivI32, InstrCost::new(18, 5.5, 8, 0x01));
costs.insert(CostKind::UDivI64, InstrCost::new(32, 9.5, 28, 0x01));
let fadd = InstrCost::new(3, 0.5, 1, 0x03);
costs.insert(CostKind::FAddF32, fadd);
costs.insert(CostKind::FAddF64, fadd);
costs.insert(CostKind::FMulF32, InstrCost::new(4, 0.5, 1, 0x03));
costs.insert(CostKind::FMulF64, InstrCost::new(4, 0.5, 1, 0x03));
costs.insert(CostKind::FDivF32, InstrCost::new(11, 4.0, 3, 0x01));
costs.insert(CostKind::FDivF64, InstrCost::new(17, 8.0, 4, 0x01));
costs.insert(CostKind::FSqrtF32, InstrCost::new(11, 5.5, 1, 0x01));
costs.insert(CostKind::FSqrtF64, InstrCost::new(18, 9.5, 1, 0x01));
let ld = InstrCost::new(5, 0.33, 1, 0x0C);
costs.insert(CostKind::LoadI32, ld);
costs.insert(CostKind::LoadI64, ld);
costs.insert(CostKind::LoadF32, ld);
costs.insert(CostKind::LoadF64, ld);
let st = InstrCost::new(1, 0.5, 1, 0x30);
costs.insert(CostKind::StoreI32, st);
costs.insert(CostKind::StoreI64, st);
X86MicroarchCostTable {
microarch: X86Microarch::IceLake,
costs,
}
}
pub fn get(&self, kind: CostKind) -> InstrCost {
self.costs
.get(&kind)
.copied()
.unwrap_or(InstrCost::new(1, 1.0, 1, 0xFF))
}
}
pub struct X86ExtendedIntrinsicTable {
pub entries: Vec<IntrinsicToOpcode>,
}
impl X86ExtendedIntrinsicTable {
pub fn new() -> Self {
let mut entries = Vec::new();
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.aesni.aesenc",
x86_opcodes: vec![0xDC],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 4,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.aesni.aesenclast",
x86_opcodes: vec![0xDD],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 4,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.aesni.aesdec",
x86_opcodes: vec![0xDE],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 4,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.aesni.aesdeclast",
x86_opcodes: vec![0xDF],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 4,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.aesni.aesimc",
x86_opcodes: vec![0xDB],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 3,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.aesni.aeskeygenassist",
x86_opcodes: vec![0xDF],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 10,
throughput: 2.0,
uops: 12,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.aesni.aesenc.256",
x86_opcodes: vec![0xDC],
requires_sse2: false,
requires_avx: true,
requires_avx512: false,
latency: 4,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.aesni.aesenc.512",
x86_opcodes: vec![0xDC],
requires_sse2: false,
requires_avx: false,
requires_avx512: true,
latency: 4,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.sha1rnds4",
x86_opcodes: vec![0xCC],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 5,
throughput: 2.0,
uops: 2,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.sha1nexte",
x86_opcodes: vec![0xC8],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 1,
throughput: 0.5,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.sha1msg1",
x86_opcodes: vec![0xC9],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 2,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.sha1msg2",
x86_opcodes: vec![0xCA],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 5,
throughput: 2.0,
uops: 2,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.sha256rnds2",
x86_opcodes: vec![0xCB],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 4,
throughput: 2.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.sha256msg1",
x86_opcodes: vec![0xCC],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 2,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.sha256msg2",
x86_opcodes: vec![0xCD],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 5,
throughput: 2.0,
uops: 2,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.sse42.crc32.32.8",
x86_opcodes: vec![0xF0],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 3,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.sse42.crc32.32.16",
x86_opcodes: vec![0xF1],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 3,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.sse42.crc32.32.32",
x86_opcodes: vec![0xF1],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 3,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.sse42.crc32.64.64",
x86_opcodes: vec![0xF1],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 3,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.pclmulqdq",
x86_opcodes: vec![0x44],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 7,
throughput: 2.0,
uops: 3,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.pclmulqdq.256",
x86_opcodes: vec![0x44],
requires_sse2: false,
requires_avx: true,
requires_avx512: false,
latency: 7,
throughput: 2.0,
uops: 3,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.pclmulqdq.512",
x86_opcodes: vec![0x44],
requires_sse2: false,
requires_avx: false,
requires_avx512: true,
latency: 7,
throughput: 2.0,
uops: 3,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.addcarry.32",
x86_opcodes: vec![0xF6],
requires_sse2: false,
requires_avx: false,
requires_avx512: false,
latency: 1,
throughput: 0.5,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.addcarry.64",
x86_opcodes: vec![0xF6],
requires_sse2: false,
requires_avx: false,
requires_avx512: false,
latency: 1,
throughput: 0.5,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.subborrow.32",
x86_opcodes: vec![0xF6],
requires_sse2: false,
requires_avx: false,
requires_avx512: false,
latency: 1,
throughput: 0.5,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.subborrow.64",
x86_opcodes: vec![0xF6],
requires_sse2: false,
requires_avx: false,
requires_avx512: false,
latency: 1,
throughput: 0.5,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.movdiri",
x86_opcodes: vec![0xF9],
requires_sse2: false,
requires_avx: false,
requires_avx512: false,
latency: 5,
throughput: 5.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.movdir64b",
x86_opcodes: vec![0xF8],
requires_sse2: false,
requires_avx: false,
requires_avx512: false,
latency: 5,
throughput: 5.0,
uops: 3,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.clwb",
x86_opcodes: vec![0xAE],
requires_sse2: false,
requires_avx: false,
requires_avx512: false,
latency: 4,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.clflushopt",
x86_opcodes: vec![0xAE],
requires_sse2: false,
requires_avx: false,
requires_avx512: false,
latency: 4,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.vgf2p8affineinvqb.128",
x86_opcodes: vec![0xCF],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 5,
throughput: 2.0,
uops: 2,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.vgf2p8affineinvqb.256",
x86_opcodes: vec![0xCF],
requires_sse2: false,
requires_avx: true,
requires_avx512: false,
latency: 5,
throughput: 2.0,
uops: 2,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.vgf2p8affineinvqb.512",
x86_opcodes: vec![0xCF],
requires_sse2: false,
requires_avx: false,
requires_avx512: true,
latency: 5,
throughput: 2.0,
uops: 2,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.vgf2p8affineqb.128",
x86_opcodes: vec![0xCE],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 5,
throughput: 2.0,
uops: 2,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.vgf2p8mulb.128",
x86_opcodes: vec![0xD4],
requires_sse2: true,
requires_avx: false,
requires_avx512: false,
latency: 5,
throughput: 1.0,
uops: 1,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.vpclmulqdq.256",
x86_opcodes: vec![0x44],
requires_sse2: false,
requires_avx: true,
requires_avx512: false,
latency: 7,
throughput: 2.0,
uops: 3,
always_legal: false,
});
entries.push(IntrinsicToOpcode {
intrinsic_name: "llvm.x86.vpclmulqdq.512",
x86_opcodes: vec![0x44],
requires_sse2: false,
requires_avx: false,
requires_avx512: true,
latency: 7,
throughput: 2.0,
uops: 3,
always_legal: false,
});
X86ExtendedIntrinsicTable { entries }
}
pub fn contains(&self, name: &str) -> bool {
self.entries.iter().any(|e| e.intrinsic_name == name)
}
pub fn get(&self, name: &str) -> Option<&IntrinsicToOpcode> {
self.entries.iter().find(|e| e.intrinsic_name == name)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ArgClass {
Integer,
SSE,
SSEUp,
X87,
X87Up,
ComplexX87,
NoClass,
Memory,
}
pub fn classify_sysv64_eightbyte(
lo_class: X86ArgClass,
hi_class: X86ArgClass,
is_float: bool,
) -> (X86ArgClass, X86ArgClass) {
if is_float {
(X86ArgClass::SSE, X86ArgClass::SSEUp)
} else {
match (lo_class, hi_class) {
(X86ArgClass::Integer, X86ArgClass::Integer) => {
(X86ArgClass::Integer, X86ArgClass::NoClass)
}
(X86ArgClass::Integer, X86ArgClass::SSE) => (X86ArgClass::Integer, X86ArgClass::SSE),
(X86ArgClass::SSE, X86ArgClass::Integer) => (X86ArgClass::SSE, X86ArgClass::Integer),
(X86ArgClass::SSE, X86ArgClass::SSE) => (X86ArgClass::SSE, X86ArgClass::SSEUp),
_ => (X86ArgClass::Memory, X86ArgClass::NoClass),
}
}
}
pub fn classify_win64_arg(size_bytes: u32, is_float: bool) -> X86ArgClass {
if size_bytes > 8 {
X86ArgClass::Memory
} else if is_float && size_bytes <= 8 {
X86ArgClass::SSE
} else {
X86ArgClass::Integer
}
}
#[derive(Debug, Clone)]
pub struct X86ABICompatibilityMatrix {
pub matrix: HashMap<(X86CallingConv, X86CallingConv), bool>,
pub cross_abi_penalty: HashMap<(X86CallingConv, X86CallingConv), u32>,
}
impl X86ABICompatibilityMatrix {
pub fn new() -> Self {
let mut matrix = HashMap::new();
let mut penalty = HashMap::new();
let ccs = [
X86CallingConv::SysV64,
X86CallingConv::Win64,
X86CallingConv::CDecl,
X86CallingConv::StdCall,
X86CallingConv::FastCall,
X86CallingConv::ThisCall,
X86CallingConv::VectorCall,
X86CallingConv::RegCall,
];
for &cc in &ccs {
matrix.insert((cc, cc), true);
penalty.insert((cc, cc), 0);
}
let ccs32 = [
X86CallingConv::CDecl,
X86CallingConv::StdCall,
X86CallingConv::FastCall,
X86CallingConv::ThisCall,
];
for &a in &ccs32 {
for &b in &ccs32 {
matrix.insert((a, b), true);
let p = if a == b { 0 } else { 3 };
penalty.insert((a, b), p);
}
}
for &cc in &ccs32 {
matrix.insert((X86CallingConv::VectorCall, cc), true);
matrix.insert((cc, X86CallingConv::VectorCall), true);
penalty.insert((X86CallingConv::VectorCall, cc), 5);
penalty.insert((cc, X86CallingConv::VectorCall), 5);
}
matrix.insert((X86CallingConv::SysV64, X86CallingConv::Win64), false);
matrix.insert((X86CallingConv::Win64, X86CallingConv::SysV64), false);
for &cc in &ccs32 {
matrix.insert((X86CallingConv::SysV64, cc), false);
matrix.insert((cc, X86CallingConv::SysV64), false);
}
for &cc in &ccs32 {
matrix.insert((X86CallingConv::Win64, cc), false);
matrix.insert((cc, X86CallingConv::Win64), false);
}
X86ABICompatibilityMatrix {
matrix,
cross_abi_penalty: penalty,
}
}
pub fn is_compatible(&self, caller: X86CallingConv, callee: X86CallingConv) -> bool {
self.matrix.get(&(caller, callee)).copied().unwrap_or(false)
}
pub fn get_penalty(&self, caller: X86CallingConv, callee: X86CallingConv) -> u32 {
self.cross_abi_penalty
.get(&(caller, callee))
.copied()
.unwrap_or(10)
}
}
#[derive(Debug, Clone)]
pub struct X86LoweringNote {
pub opcode: ISDOpcode,
pub ty: LegalizeType,
pub description: &'static str,
pub x86_sequence: &'static str,
pub sets_flags: bool,
pub requires_specific_reg: Option<&'static str>,
pub instruction_count: u32,
pub code_size_bytes: u32,
}
pub struct X86LoweringNotes {
pub notes: Vec<X86LoweringNote>,
}
impl X86LoweringNotes {
pub fn new() -> Self {
let mut notes = Vec::new();
notes.push(X86LoweringNote {
opcode: ISDOpcode::Add,
ty: LegalizeType::I32,
description: "32-bit integer addition lowered to ADD reg32, reg32/mem32",
x86_sequence: "add eax, ecx",
sets_flags: true,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 2,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::Add,
ty: LegalizeType::I64,
description: "64-bit integer addition lowered to ADD reg64, reg64/mem64",
x86_sequence: "add rax, rcx",
sets_flags: true,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 3,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::Mul,
ty: LegalizeType::I32,
description: "32-bit multiply: IMUL r32, r/m32",
x86_sequence: "imul eax, ecx",
sets_flags: true,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 3,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::Mul,
ty: LegalizeType::I64,
description: "64-bit multiply: IMUL r64, r/m64",
x86_sequence: "imul rax, rcx",
sets_flags: true,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::Mul,
ty: LegalizeType::I8,
description: "8-bit multiply: promoted to 32-bit, then truncated",
x86_sequence: "movsx eax, al; imul eax, ecx",
sets_flags: true,
requires_specific_reg: None,
instruction_count: 2,
code_size_bytes: 6,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::SDiv,
ty: LegalizeType::I32,
description: "32-bit signed division: CDQ + IDIV r/m32",
x86_sequence: "cdq; idiv ecx",
sets_flags: false,
requires_specific_reg: Some("EAX,EDX"),
instruction_count: 2,
code_size_bytes: 3,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::SDiv,
ty: LegalizeType::I64,
description: "64-bit signed division: CQO + IDIV r/m64",
x86_sequence: "cqo; idiv rcx",
sets_flags: false,
requires_specific_reg: Some("RAX,RDX"),
instruction_count: 2,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::UDiv,
ty: LegalizeType::I32,
description: "32-bit unsigned division: XOR EDX,EDX + DIV r/m32",
x86_sequence: "xor edx, edx; div ecx",
sets_flags: false,
requires_specific_reg: Some("EAX,EDX"),
instruction_count: 2,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FAdd,
ty: LegalizeType::F32,
description: "Scalar single-precision FP add: ADDSS xmm, xmm/m32",
x86_sequence: "addss xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FAdd,
ty: LegalizeType::F64,
description: "Scalar double-precision FP add: ADDSD xmm, xmm/m64",
x86_sequence: "addsd xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FMul,
ty: LegalizeType::F32,
description: "Scalar single-precision FP mul: MULSS xmm, xmm/m32",
x86_sequence: "mulss xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FMul,
ty: LegalizeType::F64,
description: "Scalar double-precision FP mul: MULSD xmm, xmm/m64",
x86_sequence: "mulsd xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FDiv,
ty: LegalizeType::F32,
description: "Scalar single-precision FP div: DIVSS xmm, xmm/m32",
x86_sequence: "divss xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FDiv,
ty: LegalizeType::F64,
description: "Scalar double-precision FP div: DIVSD xmm, xmm/m64",
x86_sequence: "divsd xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::Select,
ty: LegalizeType::I32,
description: "Conditional move: CMOVcc r32, r/m32 (since i686)",
x86_sequence: "cmove eax, ecx",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 3,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::Select,
ty: LegalizeType::I64,
description: "Conditional move: CMOVcc r64, r/m64",
x86_sequence: "cmove rax, rcx",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::Load,
ty: LegalizeType::I32,
description: "32-bit integer load: MOV r32, [base + index*scale + disp]",
x86_sequence: "mov eax, [rdi + rsi*4 + 8]",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::Load,
ty: LegalizeType::I64,
description: "64-bit integer load: MOV r64, [base + index*scale + disp]",
x86_sequence: "mov rax, [rdi + rsi*8 + 8]",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::Store,
ty: LegalizeType::I32,
description: "32-bit integer store: MOV [base + disp], r32",
x86_sequence: "mov [rdi + 8], eax",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 3,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FAdd,
ty: LegalizeType::V4F32,
description: "Packed single-precision FP add: ADDPS xmm, xmm/m128",
x86_sequence: "addps xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FAdd,
ty: LegalizeType::V2F64,
description: "Packed double-precision FP add: ADDPD xmm, xmm/m128",
x86_sequence: "addpd xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FAdd,
ty: LegalizeType::V8F32,
description: "Packed single-precision FP add (256-bit): VADDPS ymm, ymm, ymm/m256",
x86_sequence: "vaddps ymm0, ymm1, ymm2",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FMul,
ty: LegalizeType::V8F32,
description: "Packed single-precision FP mul (256-bit): VMULPS ymm, ymm, ymm/m256",
x86_sequence: "vmulps ymm0, ymm1, ymm2",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FAdd,
ty: LegalizeType::V16F32,
description: "Packed single-precision FP add (512-bit): VADDPS zmm, zmm, zmm/m512",
x86_sequence: "vaddps zmm0, zmm1, zmm2",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 6,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FAdd,
ty: LegalizeType::V8F64,
description: "Packed double-precision FP add (512-bit): VADDPD zmm, zmm, zmm/m512",
x86_sequence: "vaddpd zmm0, zmm1, zmm2",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 6,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::Bitcast,
ty: LegalizeType::I32,
description: "Bitcast is free on x86 — just a register rename",
x86_sequence: "(none — register alias)",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 0,
code_size_bytes: 0,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::Bitcast,
ty: LegalizeType::F32,
description: "Bitcast i32 <-> f32 via MOVD (1 cycle, 0 latency if same reg)",
x86_sequence: "movd eax, xmm0 or vmovd xmm0, eax",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::ShuffleVec,
ty: LegalizeType::V4F32,
description: "Vector shuffle (128-bit): SHUFPS / PSHUFD / UNPCK*PS / MOVLHPS etc.",
x86_sequence: "shufps xmm0, xmm1, imm8",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 5,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::ShuffleVec,
ty: LegalizeType::V8F32,
description: "Vector shuffle (256-bit): VPERM2F128 / VSHUFPS / VUNPCK*PS etc.",
x86_sequence: "vperm2f128 ymm0, ymm1, ymm2, imm8",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 6,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::BuildVec,
ty: LegalizeType::V4I32,
description: "Build 4×i32 vector: MOVD + PINSRD sequence or constant pool load",
x86_sequence: "movd xmm0, eax; pinsrd xmm0, ecx, 1; ...",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 4,
code_size_bytes: 20,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::ExtractElt,
ty: LegalizeType::V4I32,
description: "Extract i32 from vector: PEXTRD / MOVD + PSRLDQ",
x86_sequence: "pextrd eax, xmm0, imm8",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 6,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::InsertElt,
ty: LegalizeType::V4I32,
description: "Insert i32 into vector: PINSRD xmm, r/m32, imm8",
x86_sequence: "pinsrd xmm0, eax, imm8",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 6,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FPExt,
ty: LegalizeType::F64,
description: "Float extend f32→f64: CVTSS2SD xmm, xmm/m32 (zero cost if same reg)",
x86_sequence: "cvtss2sd xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FPTrunc,
ty: LegalizeType::F32,
description: "Float truncate f64→f32: CVTSD2SS xmm, xmm/m64",
x86_sequence: "cvtsd2ss xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::SIToFP,
ty: LegalizeType::F32,
description: "Signed int32→float32: CVTSI2SS xmm, r/m32",
x86_sequence: "cvtsi2ss xmm0, eax",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::SIToFP,
ty: LegalizeType::F64,
description: "Signed int32/int64→float64: CVTSI2SD xmm, r/m32|r/m64",
x86_sequence: "cvtsi2sd xmm0, rax",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 5,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FPToSI,
ty: LegalizeType::I32,
description: "Float32/Float64→int32: CVTTSS2SI / CVTTSD2SI r32, xmm/m",
x86_sequence: "cvttss2si eax, xmm0",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::FPToSI,
ty: LegalizeType::I64,
description: "Float32/Float64→int64: CVTTSS2SI / CVTTSD2SI r64, xmm/m",
x86_sequence: "cvttsd2si rax, xmm0",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 5,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::Truncate,
ty: LegalizeType::I32,
description: "Truncate i64→i32: MOV r32, r32 (upper bits ignored in 64-bit mode)",
x86_sequence: "mov eax, eax (or just use the 32-bit subreg)",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 2,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::SExt,
ty: LegalizeType::I32,
description: "Sign extend i8/i16→i32: MOVSX r32, r/m8|r/m16",
x86_sequence: "movsx eax, al or movsx eax, ax",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 3,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::ZExt,
ty: LegalizeType::I32,
description: "Zero extend i8/i16→i32: MOVZX r32, r/m8|r/m16",
x86_sequence: "movzx eax, al or movzx eax, ax",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 3,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_FMA_RND,
ty: LegalizeType::V4F32,
description: "Fused multiply-add (128-bit): VFMADD213PS xmm, xmm, xmm/m128",
x86_sequence: "vfmadd213ps xmm0, xmm1, xmm2",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 5,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_FMA_RND,
ty: LegalizeType::V8F32,
description: "Fused multiply-add (256-bit): VFMADD213PS ymm, ymm, ymm/m256",
x86_sequence: "vfmadd213ps ymm0, ymm1, ymm2",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 5,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_FMA_RND,
ty: LegalizeType::V16F32,
description: "Fused multiply-add (512-bit): VFMADD213PS zmm, zmm, zmm/m512",
x86_sequence: "vfmadd213ps zmm0, zmm1, zmm2",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 6,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_FSQRT,
ty: LegalizeType::F32,
description: "Square root (scalar f32): SQRTSS xmm, xmm/m32",
x86_sequence: "sqrtss xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_FSQRT,
ty: LegalizeType::F64,
description: "Square root (scalar f64): SQRTSD xmm, xmm/m64",
x86_sequence: "sqrtsd xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_FSQRT,
ty: LegalizeType::V4F32,
description: "Square root (packed f32): SQRTPS xmm, xmm/m128",
x86_sequence: "sqrtps xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_FRCP,
ty: LegalizeType::V4F32,
description: "Approximate reciprocal: RCPPS xmm, xmm/m128 (11-bit precision)",
x86_sequence: "rcpps xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_FRSQRT,
ty: LegalizeType::V4F32,
description: "Approximate reciprocal sqrt: RSQRTPS xmm, xmm/m128",
x86_sequence: "rsqrtps xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::ConcatVec,
ty: LegalizeType::V8F32,
description:
"Concatenate 2×128-bit vectors to 256-bit: VINSERTF128 ymm, ymm, xmm/m128, 1",
x86_sequence: "vinsertf128 ymm0, ymm0, xmm1, 1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 6,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::ExtractSubvec,
ty: LegalizeType::V4F32,
description: "Extract 128-bit lane from 256-bit: VEXTRACTF128 xmm/m128, ymm, imm8",
x86_sequence: "vextractf128 xmm0, ymm1, 0",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 6,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_VBROADCAST,
ty: LegalizeType::V8F32,
description: "Broadcast scalar to all lanes: VBROADCASTSS ymm, xmm/m32",
x86_sequence: "vbroadcastss ymm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 5,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_VPERMILPY,
ty: LegalizeType::V4F64,
description: "Permute double-precision lanes: VPERMILPD ymm, ymm, imm8",
x86_sequence: "vpermilpd ymm0, ymm1, imm8",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 6,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_PACKUS,
ty: LegalizeType::V8I16,
description: "Pack with unsigned saturation: PACKUSWB xmm, xmm/m128",
x86_sequence: "packuswb xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_PACKSS,
ty: LegalizeType::V8I16,
description: "Pack with signed saturation: PACKSSWB xmm, xmm/m128",
x86_sequence: "packsswb xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_CVTPD2PS,
ty: LegalizeType::V4F32,
description: "Convert packed double→single: CVTPD2PS xmm, xmm/m128 (2 results)",
x86_sequence: "cvtpd2ps xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_CVTPS2PD,
ty: LegalizeType::V2F64,
description: "Convert packed single→double: CVTPS2PD xmm, xmm/m64 (2 results)",
x86_sequence: "cvtps2pd xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_PTEST,
ty: LegalizeType::V4I32,
description: "Packed bitwise test: PTEST xmm, xmm/m128 (sets ZF, CF)",
x86_sequence: "ptest xmm0, xmm1",
sets_flags: true,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 5,
});
notes.push(X86LoweringNote {
opcode: ISDOpcode::X86_VTRUNC,
ty: LegalizeType::V4F32,
description: "Vector truncate float→int: CVTTPS2DQ xmm, xmm/m128",
x86_sequence: "cvttps2dq xmm0, xmm1",
sets_flags: false,
requires_specific_reg: None,
instruction_count: 1,
code_size_bytes: 4,
});
X86LoweringNotes { notes }
}
pub fn find(&self, opcode: ISDOpcode, ty: LegalizeType) -> Option<&X86LoweringNote> {
self.notes.iter().find(|n| n.opcode == opcode && n.ty == ty)
}
pub fn find_by_opcode(&self, opcode: ISDOpcode) -> Vec<&X86LoweringNote> {
self.notes.iter().filter(|n| n.opcode == opcode).collect()
}
pub fn count(&self) -> usize {
self.notes.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_x86_libfunc_c_name_basic() {
assert_eq!(X86LibFunc::Sin.c_name(), "sin");
assert_eq!(X86LibFunc::Cos.c_name(), "cos");
assert_eq!(X86LibFunc::Exp.c_name(), "exp");
assert_eq!(X86LibFunc::Log.c_name(), "log");
assert_eq!(X86LibFunc::Sqrt.c_name(), "sqrt");
}
#[test]
fn test_x86_libfunc_c_name_float_variants() {
assert_eq!(X86LibFunc::SinF.c_name(), "sinf");
assert_eq!(X86LibFunc::CosF.c_name(), "cosf");
assert_eq!(X86LibFunc::ExpF.c_name(), "expf");
assert_eq!(X86LibFunc::LogF.c_name(), "logf");
assert_eq!(X86LibFunc::SqrtF.c_name(), "sqrtf");
assert_eq!(X86LibFunc::FabsF.c_name(), "fabsf");
}
#[test]
fn test_x86_libfunc_c_name_long_double() {
assert_eq!(X86LibFunc::SinL.c_name(), "sinl");
assert_eq!(X86LibFunc::CosL.c_name(), "cosl");
assert_eq!(X86LibFunc::ExpL.c_name(), "expl");
assert_eq!(X86LibFunc::LogL.c_name(), "logl");
assert_eq!(X86LibFunc::SqrtL.c_name(), "sqrtl");
}
#[test]
fn test_x86_libfunc_c_name_string() {
assert_eq!(X86LibFunc::Memcpy.c_name(), "memcpy");
assert_eq!(X86LibFunc::Memset.c_name(), "memset");
assert_eq!(X86LibFunc::Memmove.c_name(), "memmove");
assert_eq!(X86LibFunc::Strlen.c_name(), "strlen");
assert_eq!(X86LibFunc::Strcpy.c_name(), "strcpy");
assert_eq!(X86LibFunc::Strcmp.c_name(), "strcmp");
assert_eq!(X86LibFunc::Strchr.c_name(), "strchr");
assert_eq!(X86LibFunc::Strstr.c_name(), "strstr");
}
#[test]
fn test_x86_libfunc_c_name_stdlib() {
assert_eq!(X86LibFunc::Malloc.c_name(), "malloc");
assert_eq!(X86LibFunc::Free.c_name(), "free");
assert_eq!(X86LibFunc::Calloc.c_name(), "calloc");
assert_eq!(X86LibFunc::Realloc.c_name(), "realloc");
assert_eq!(X86LibFunc::Abort.c_name(), "abort");
assert_eq!(X86LibFunc::Exit.c_name(), "exit");
assert_eq!(X86LibFunc::Qsort.c_name(), "qsort");
assert_eq!(X86LibFunc::Bsearch.c_name(), "bsearch");
assert_eq!(X86LibFunc::Atoi.c_name(), "atoi");
assert_eq!(X86LibFunc::Atol.c_name(), "atol");
assert_eq!(X86LibFunc::Strtod.c_name(), "strtod");
}
#[test]
fn test_x86_libfunc_c_name_stdio() {
assert_eq!(X86LibFunc::Printf.c_name(), "printf");
assert_eq!(X86LibFunc::Fprintf.c_name(), "fprintf");
assert_eq!(X86LibFunc::Sprintf.c_name(), "sprintf");
assert_eq!(X86LibFunc::Snprintf.c_name(), "snprintf");
assert_eq!(X86LibFunc::Fopen.c_name(), "fopen");
assert_eq!(X86LibFunc::Fclose.c_name(), "fclose");
assert_eq!(X86LibFunc::Fread.c_name(), "fread");
assert_eq!(X86LibFunc::Fwrite.c_name(), "fwrite");
assert_eq!(X86LibFunc::Fgets.c_name(), "fgets");
assert_eq!(X86LibFunc::Fputs.c_name(), "fputs");
assert_eq!(X86LibFunc::Puts.c_name(), "puts");
assert_eq!(X86LibFunc::Getchar.c_name(), "getchar");
assert_eq!(X86LibFunc::Putchar.c_name(), "putchar");
}
#[test]
fn test_x86_libfunc_c_name_pthreads() {
assert_eq!(X86LibFunc::PthreadCreate.c_name(), "pthread_create");
assert_eq!(X86LibFunc::PthreadJoin.c_name(), "pthread_join");
assert_eq!(X86LibFunc::PthreadMutexLock.c_name(), "pthread_mutex_lock");
assert_eq!(
X86LibFunc::PthreadMutexUnlock.c_name(),
"pthread_mutex_unlock"
);
}
#[test]
fn test_x86_libfunc_is_math_function() {
assert!(X86LibFunc::Sin.is_math_function());
assert!(X86LibFunc::Exp.is_math_function());
assert!(X86LibFunc::Sqrt.is_math_function());
assert!(X86LibFunc::Ceil.is_math_function());
assert!(X86LibFunc::Fma.is_math_function());
assert!(!X86LibFunc::Memcpy.is_math_function());
assert!(!X86LibFunc::Malloc.is_math_function());
assert!(!X86LibFunc::Printf.is_math_function());
}
#[test]
fn test_x86_libfunc_is_float_variant() {
assert!(X86LibFunc::SinF.is_float_variant());
assert!(X86LibFunc::ExpF.is_float_variant());
assert!(!X86LibFunc::Sin.is_float_variant());
assert!(!X86LibFunc::SinL.is_float_variant());
assert!(!X86LibFunc::Memcpy.is_float_variant());
}
#[test]
fn test_x86_libfunc_is_double_variant() {
assert!(X86LibFunc::Sin.is_double_variant());
assert!(X86LibFunc::Exp.is_double_variant());
assert!(X86LibFunc::Sqrt.is_double_variant());
assert!(!X86LibFunc::SinF.is_double_variant());
assert!(!X86LibFunc::SinL.is_double_variant());
assert!(!X86LibFunc::Memcpy.is_double_variant());
}
#[test]
fn test_x86_libfunc_svml_vector_width() {
assert_eq!(X86LibFunc::__svml_sinf4.svml_vector_width(), Some(4));
assert_eq!(X86LibFunc::__svml_sinf8.svml_vector_width(), Some(8));
assert_eq!(X86LibFunc::__svml_sinf16.svml_vector_width(), Some(16));
assert_eq!(X86LibFunc::__svml_sin4.svml_vector_width(), Some(8));
assert_eq!(X86LibFunc::Sin.svml_vector_width(), None);
assert_eq!(X86LibFunc::Memcpy.svml_vector_width(), None);
}
#[test]
fn test_x86_libfunc_svml_scalar_counterpart() {
assert_eq!(
X86LibFunc::__svml_sinf4.svml_scalar_counterpart(),
Some(X86LibFunc::SinF)
);
assert_eq!(
X86LibFunc::__svml_cosf8.svml_scalar_counterpart(),
Some(X86LibFunc::CosF)
);
assert_eq!(
X86LibFunc::__svml_sin4.svml_scalar_counterpart(),
Some(X86LibFunc::Sin)
);
assert_eq!(X86LibFunc::Sin.svml_scalar_counterpart(), None);
}
#[test]
fn test_tli_linux_has_common_math() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has(X86LibFunc::Sin));
assert!(tli.has(X86LibFunc::Cos));
assert!(tli.has(X86LibFunc::Exp));
assert!(tli.has(X86LibFunc::Sqrt));
assert!(tli.has(X86LibFunc::Fabs));
}
#[test]
fn test_tli_linux_has_float_math() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has(X86LibFunc::SinF));
assert!(tli.has(X86LibFunc::CosF));
assert!(tli.has(X86LibFunc::ExpF));
assert!(tli.has(X86LibFunc::SqrtF));
}
#[test]
fn test_tli_linux_has_long_double_math() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has(X86LibFunc::SinL));
assert!(tli.has(X86LibFunc::ExpL));
assert!(tli.has(X86LibFunc::SqrtL));
}
#[test]
fn test_tli_linux_has_string_funcs() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has(X86LibFunc::Memcpy));
assert!(tli.has(X86LibFunc::Memset));
assert!(tli.has(X86LibFunc::Strlen));
assert!(tli.has(X86LibFunc::Strcmp));
}
#[test]
fn test_tli_linux_has_stdlib_funcs() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has(X86LibFunc::Malloc));
assert!(tli.has(X86LibFunc::Free));
assert!(tli.has(X86LibFunc::Abort));
}
#[test]
fn test_tli_linux_has_stdio_funcs() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has(X86LibFunc::Printf));
assert!(tli.has(X86LibFunc::Fopen));
assert!(tli.has(X86LibFunc::Fclose));
}
#[test]
fn test_tli_linux_has_posix() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has(X86LibFunc::Fork));
assert!(tli.has(X86LibFunc::Mmap));
assert!(tli.has(X86LibFunc::Getpid));
assert!(tli.has(X86LibFunc::Signal));
}
#[test]
fn test_tli_linux_has_pthreads() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has(X86LibFunc::PthreadCreate));
assert!(tli.has(X86LibFunc::PthreadJoin));
assert!(tli.has(X86LibFunc::PthreadMutexLock));
}
#[test]
fn test_tli_linux_has_fenv() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has(X86LibFunc::Fesetround));
assert!(tli.has(X86LibFunc::Fegetround));
assert!(tli.has(X86LibFunc::Feclearexcept));
}
#[test]
fn test_tli_linux_has_builtins() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has(X86LibFunc::__builtin_memcpy));
assert!(tli.has(X86LibFunc::__builtin_sqrt));
assert!(tli.has(X86LibFunc::UnwindResume));
}
#[test]
fn test_tli_windows_has_basic_funcs() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Windows);
assert!(tli.has(X86LibFunc::Sin));
assert!(tli.has(X86LibFunc::Memcpy));
assert!(tli.has(X86LibFunc::Malloc));
assert!(tli.has(X86LibFunc::Printf));
}
#[test]
fn test_tli_windows_missing_posix() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Windows);
assert!(!tli.has(X86LibFunc::Fork));
assert!(!tli.has(X86LibFunc::Mmap));
}
#[test]
fn test_tli_windows_missing_fenv() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Windows);
assert!(!tli.has(X86LibFunc::Fesetround));
assert!(!tli.has(X86LibFunc::Fegetround));
}
#[test]
fn test_tli_macos_has_accelerate_preference() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::MacOS);
assert_eq!(tli.preferred_vector_library, X86VectorLibrary::Accelerate);
}
#[test]
fn test_tli_android_has_bionic_funcs() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Android);
assert!(tli.has(X86LibFunc::Sin));
assert!(tli.has(X86LibFunc::Memcpy));
assert!(tli.has(X86LibFunc::Malloc));
assert!(tli.has(X86LibFunc::PthreadCreate));
}
#[test]
fn test_tli_set_unavailable() {
let mut tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has(X86LibFunc::Sin));
tli.set_unavailable(X86LibFunc::Sin);
assert!(!tli.has(X86LibFunc::Sin));
}
#[test]
fn test_tli_set_available() {
let mut tli = X86TargetLibraryInfo::new(X86OSTarget::Windows);
assert!(!tli.has(X86LibFunc::Fork));
tli.set_available(X86LibFunc::Fork);
assert!(tli.has(X86LibFunc::Fork));
}
#[test]
fn test_tli_size_args() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert_eq!(tli.memcpy_size_arg, 2);
assert_eq!(tli.memset_size_arg, 2);
assert_eq!(tli.memmove_size_arg, 2);
}
#[test]
fn test_tli_signatures() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
let sig = tli.get_signature(X86LibFunc::Sin);
assert!(sig.is_some());
let sig = sig.unwrap();
assert_eq!(sig.param_count, 1);
assert_eq!(sig.return_type, "d");
}
#[test]
fn test_tli_get_attrs_const() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
let attrs = tli.get_attrs(X86LibFunc::Abs);
assert!(attrs.contains(&KnownFuncAttr::Const));
assert!(attrs.contains(&KnownFuncAttr::NoUnwind));
}
#[test]
fn test_tli_get_attrs_noreturn() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
let attrs = tli.get_attrs(X86LibFunc::Abort);
assert!(attrs.contains(&KnownFuncAttr::NoReturn));
}
#[test]
fn test_tli_get_attrs_readonly() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
let attrs = tli.get_attrs(X86LibFunc::Memcmp);
assert!(attrs.contains(&KnownFuncAttr::ReadOnly));
}
#[test]
fn test_tli_vector_mappings_svml() {
let mut tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
tli = tli.with_vector_library(X86VectorLibrary::SVML);
let mappings = tli.get_vector_mappings_for("sinf");
assert!(!mappings.is_empty());
assert_eq!(mappings[0].library, X86VectorLibrary::SVML);
}
#[test]
fn test_tli_vector_mappings_libmvec() {
let mut tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
tli = tli.with_vector_library(X86VectorLibrary::LibMVec);
let mappings = tli.get_vector_mappings_for("sinf");
assert!(!mappings.is_empty());
assert_eq!(mappings[0].library, X86VectorLibrary::LibMVec);
}
#[test]
fn test_tli_vector_mapping_at_width() {
let mut tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
tli = tli.with_vector_library(X86VectorLibrary::SVML);
let mapping = tli.get_vector_mapping("sinf", 4);
assert!(mapping.is_some());
let mapping = tli.get_vector_mapping("sinf", 999);
assert!(mapping.is_none());
}
#[test]
fn test_tli_has_attr() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has_attr(X86LibFunc::Abs, &KnownFuncAttr::Const));
assert!(!tli.has_attr(X86LibFunc::Memcpy, &KnownFuncAttr::Const));
}
#[test]
fn test_tti_new_skylake() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
assert_eq!(tti.microarch, X86Microarch::Skylake);
assert_eq!(tti.inline_threshold, X86_DEFAULT_INLINE_THRESHOLD);
}
#[test]
fn test_tti_get_cost_add_i32() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let cost = tti.get_cost(CostKind::AddI32);
assert_eq!(cost.latency, 1);
assert!(cost.recip_throughput > 0.0);
}
#[test]
fn test_tti_get_cost_sdiv_i64() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let cost = tti.get_cost(CostKind::SDivI64);
assert!(cost.latency > 10); assert!(cost.uops > 5);
}
#[test]
fn test_tti_get_cost_fadd_f64() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let cost = tti.get_cost(CostKind::FAddF64);
assert_eq!(cost.latency, 3);
}
#[test]
fn test_tti_get_cost_fdiv_f64() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let cost = tti.get_cost(CostKind::FDivF64);
assert!(cost.latency > 10);
}
#[test]
fn test_tti_get_cost_load_i32() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let cost = tti.get_cost(CostKind::LoadI32);
assert_eq!(cost.latency, 4); }
#[test]
fn test_tti_get_cost_branch() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let cost = tti.get_cost(CostKind::Branch);
assert_eq!(cost.latency, 1);
}
#[test]
fn test_tti_vector_costs_avx2() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake)
.with_features(true, true, false, false, true, 42);
let cost = tti.get_cost(CostKind::VAddF32x8);
assert_eq!(cost.latency, 3);
}
#[test]
fn test_tti_vector_costs_avx512() {
let tti = X86TargetTransformInfo::new(X86Microarch::SkylakeX)
.with_features(true, true, true, true, true, 42);
let cost = tti.get_cost(CostKind::VAddF32x16);
assert_eq!(cost.latency, 3);
assert_eq!(tti.max_vector_width, 512);
assert_eq!(tti.num_vector_regs, 32);
}
#[test]
fn test_tti_should_unroll_small_loop() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let body = vec![CostKind::AddI32, CostKind::AddI32];
assert!(tti.should_unroll_loop(4, &body));
}
#[test]
fn test_tti_should_not_unroll_large_loop() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let body = vec![CostKind::SDivI64; 10];
assert!(!tti.should_unroll_loop(8, &body));
}
#[test]
fn test_tti_compute_switch_cost_dense() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let sc = tti.compute_switch_cost(100, 0, 200);
assert!(sc.prefer_jump_table);
assert!(sc.density > 0.4);
}
#[test]
fn test_tti_compute_switch_cost_sparse() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let sc = tti.compute_switch_cost(3, 0, 1000);
assert!(!sc.prefer_jump_table);
assert!(sc.density < 0.4);
}
#[test]
fn test_tti_is_vectorization_profitable() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake)
.with_features(true, true, false, false, true, 42);
assert!(tti.is_vectorization_profitable(CostKind::AddI32, 256, 256));
}
#[test]
fn test_tti_should_split_small_vector() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
assert!(!tti.should_split_vector(128));
assert!(!tti.should_split_vector(256));
assert!(tti.should_split_vector(512));
}
#[test]
fn test_tti_compute_memory_cost_sequential() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let cost = tti.compute_memory_cost(true, 64, true);
assert_eq!(cost, 5);
}
#[test]
fn test_tti_compute_memory_cost_random() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let cost = tti.compute_memory_cost(true, 64, false);
assert_eq!(cost, 10); }
#[test]
fn test_cc_lowering_sysv64_int_args() {
let cc = X86CallingConvLowering::new(X86CallingConv::SysV64);
assert_eq!(cc.int_arg_regs.len(), 6);
assert_eq!(cc.get_int_arg_reg(0), Some(X86PhysReg::RDI));
assert_eq!(cc.get_int_arg_reg(1), Some(X86PhysReg::RSI));
assert_eq!(cc.get_int_arg_reg(2), Some(X86PhysReg::RDX));
assert_eq!(cc.get_int_arg_reg(3), Some(X86PhysReg::RCX));
assert_eq!(cc.get_int_arg_reg(4), Some(X86PhysReg::R8));
assert_eq!(cc.get_int_arg_reg(5), Some(X86PhysReg::R9));
assert_eq!(cc.get_int_arg_reg(6), None);
}
#[test]
fn test_cc_lowering_sysv64_sse_args() {
let cc = X86CallingConvLowering::new(X86CallingConv::SysV64);
assert_eq!(cc.sse_arg_regs.len(), 8);
assert_eq!(cc.get_sse_arg_reg(0), Some(X86PhysReg::XMM0));
assert_eq!(cc.get_sse_arg_reg(7), Some(X86PhysReg::XMM7));
}
#[test]
fn test_cc_lowering_sysv64_red_zone() {
let cc = X86CallingConvLowering::new(X86CallingConv::SysV64);
assert!(cc.has_red_zone);
assert_eq!(cc.red_zone_size, 128);
}
#[test]
fn test_cc_lowering_win64_int_args() {
let cc = X86CallingConvLowering::new(X86CallingConv::Win64);
assert_eq!(cc.int_arg_regs.len(), 4);
assert_eq!(cc.get_int_arg_reg(0), Some(X86PhysReg::RCX));
assert_eq!(cc.get_int_arg_reg(1), Some(X86PhysReg::RDX));
assert_eq!(cc.get_int_arg_reg(2), Some(X86PhysReg::R8));
assert_eq!(cc.get_int_arg_reg(3), Some(X86PhysReg::R9));
}
#[test]
fn test_cc_lowering_win64_shadow_space() {
let cc = X86CallingConvLowering::new(X86CallingConv::Win64);
assert_eq!(cc.shadow_space_size, 32);
assert!(!cc.has_red_zone);
}
#[test]
fn test_cc_lowering_win64_callee_saved_xmm() {
let cc = X86CallingConvLowering::new(X86CallingConv::Win64);
let xmm6_saved = cc.callee_saved.contains(&X86PhysReg::XMM6);
assert!(xmm6_saved);
let xmm0_saved = cc.callee_saved.contains(&X86PhysReg::XMM0);
assert!(!xmm0_saved); }
#[test]
fn test_cc_lowering_cdecl() {
let cc = X86CallingConvLowering::new(X86CallingConv::CDecl);
assert_eq!(cc.int_arg_regs.len(), 0); assert_eq!(cc.sse_arg_regs.len(), 0);
assert!(!cc.has_red_zone);
assert_eq!(cc.frame_ptr, X86PhysReg::EBP);
assert_eq!(cc.stack_ptr, X86PhysReg::ESP);
}
#[test]
fn test_cc_lowering_fastcall() {
let cc = X86CallingConvLowering::new(X86CallingConv::FastCall);
assert_eq!(cc.int_arg_regs.len(), 2);
assert_eq!(cc.get_int_arg_reg(0), Some(X86PhysReg::ECX));
assert_eq!(cc.get_int_arg_reg(1), Some(X86PhysReg::EDX));
}
#[test]
fn test_cc_lowering_thiscall() {
let cc = X86CallingConvLowering::new(X86CallingConv::ThisCall);
assert_eq!(cc.int_arg_regs.len(), 1);
assert_eq!(cc.get_int_arg_reg(0), Some(X86PhysReg::ECX));
}
#[test]
fn test_cc_lowering_vectorcall() {
let cc = X86CallingConvLowering::new(X86CallingConv::VectorCall);
assert_eq!(cc.sse_arg_regs.len(), 6);
}
#[test]
fn test_cc_lowering_regcall() {
let cc = X86CallingConvLowering::new(X86CallingConv::RegCall);
assert_eq!(cc.int_arg_regs.len(), 8);
assert_eq!(cc.sse_arg_regs.len(), 8);
}
#[test]
fn test_cc_lowering_spill_costs() {
let cc = X86CallingConvLowering::new(X86CallingConv::SysV64);
assert!(cc.total_callee_saved_spill_cost > 0);
}
#[test]
fn test_cc_lowering_compatibility() {
let sysv = X86CallingConvLowering::new(X86CallingConv::SysV64);
let win = X86CallingConvLowering::new(X86CallingConv::Win64);
assert!(sysv.are_compatible(&sysv));
assert!(!sysv.are_compatible(&win));
assert!(!win.are_compatible(&sysv));
}
#[test]
fn test_cc_lowering_32bit_compatibility() {
let cdecl = X86CallingConvLowering::new(X86CallingConv::CDecl);
let fastcall = X86CallingConvLowering::new(X86CallingConv::FastCall);
assert!(cdecl.are_compatible(&fastcall));
}
#[test]
fn test_cc_num_int_arg_regs() {
assert_eq!(X86CallingConv::SysV64.num_int_arg_regs(), 6);
assert_eq!(X86CallingConv::Win64.num_int_arg_regs(), 4);
assert_eq!(X86CallingConv::FastCall.num_int_arg_regs(), 2);
assert_eq!(X86CallingConv::CDecl.num_int_arg_regs(), 0);
}
#[test]
fn test_cc_num_sse_arg_regs() {
assert_eq!(X86CallingConv::SysV64.num_sse_arg_regs(), 8);
assert_eq!(X86CallingConv::Win64.num_sse_arg_regs(), 4);
assert_eq!(X86CallingConv::CDecl.num_sse_arg_regs(), 0);
}
#[test]
fn test_instr_cost_throughput_cycles() {
let cost = InstrCost::new(4, 2.0, 1, 0xFF);
assert_eq!(cost.throughput_cycles(), 0.5);
}
#[test]
fn test_instr_cost_zero_throughput() {
let cost = InstrCost::new(0, 0.0, 0, 0);
assert_eq!(cost.throughput_cycles(), 0.0);
}
#[test]
fn test_intrinsic_lowering_is_legal_always() {
let il = X86IntrinsicLowering::new();
assert!(il.is_legal("llvm.sqrt.f32"));
assert!(il.is_legal("llvm.memcpy.p0i8.p0i8.i64"));
assert!(il.is_legal("llvm.bswap.i32"));
}
#[test]
fn test_intrinsic_lowering_is_legal_requires_sse2() {
let il = X86IntrinsicLowering::new();
assert!(il.is_legal("llvm.x86.sse.sqrt.ps"));
}
#[test]
fn test_intrinsic_lowering_not_legal_unknown() {
let il = X86IntrinsicLowering::new();
assert!(!il.is_legal("llvm.unknown.intrinsic"));
}
#[test]
fn test_intrinsic_lowering_requires_avx_not_available() {
let mut il = X86IntrinsicLowering::new();
il.has_avx = false;
assert!(!il.is_legal("llvm.x86.avx.sqrt.ps.256"));
}
#[test]
fn test_intrinsic_lowering_requires_avx_available() {
let mut il = X86IntrinsicLowering::new();
il.has_avx = true;
assert!(il.is_legal("llvm.x86.avx.sqrt.ps.256"));
}
#[test]
fn test_intrinsic_lowering_requires_avx512() {
let mut il = X86IntrinsicLowering::new();
il.has_avx512 = false;
assert!(!il.is_legal("llvm.x86.avx512.sqrt.ps.512"));
il.has_avx512 = true;
assert!(il.is_legal("llvm.x86.avx512.sqrt.ps.512"));
}
#[test]
fn test_intrinsic_lowering_get_cost() {
let il = X86IntrinsicLowering::new();
let (lat, thr, uops) = il.get_intrinsic_cost("llvm.sqrt.f32").unwrap();
assert_eq!(lat, 12);
assert!(thr > 0.0);
assert!(uops > 0);
}
#[test]
fn test_intrinsic_lowering_features_available() {
let il = X86IntrinsicLowering::new();
assert!(il.features_available_for("llvm.x86.sse.sqrt.ps"));
assert!(!il.features_available_for("llvm.unknown"));
}
#[test]
fn test_intrinsic_lowering_rdrand_requires_feature() {
let mut il = X86IntrinsicLowering::new();
il.has_rdrand = true;
assert!(il.is_legal("llvm.x86.rdrand.32"));
}
#[test]
fn test_isel_legal_add_i32() {
let isel = X86ISelLowering::new(true);
assert!(isel.is_operation_legal(ISDOpcode::Add, LegalizeType::I32));
}
#[test]
fn test_isel_legal_add_i64_64bit() {
let isel = X86ISelLowering::new(true);
assert!(isel.is_operation_legal(ISDOpcode::Add, LegalizeType::I64));
}
#[test]
fn test_isel_expand_add_i64_32bit() {
let isel = X86ISelLowering::new(false);
assert!(!isel.is_operation_legal(ISDOpcode::Add, LegalizeType::I64));
}
#[test]
fn test_isel_expand_sdiv_i8() {
let isel = X86ISelLowering::new(true);
assert!(isel.should_expand(ISDOpcode::SDiv, LegalizeType::I8));
}
#[test]
fn test_isel_libcall_i128() {
let isel = X86ISelLowering::new(true);
let action = isel.get_legalize_action(ISDOpcode::Mul, LegalizeType::I128);
assert!(matches!(action, LegalizeAction::Libcall));
}
#[test]
fn test_isel_promote_f16() {
let isel = X86ISelLowering::new(true);
let action = isel.get_legalize_action(ISDOpcode::FAdd, LegalizeType::F16);
assert!(matches!(action, LegalizeAction::Promote));
}
#[test]
fn test_isel_vector_split_512_on_avx2() {
let isel = X86ISelLowering::new(true)
.with_features(true, true, false, false, false, true, false, false, false);
assert!(isel.should_split_vector(LegalizeType::V16I32));
assert!(isel.should_split_vector(LegalizeType::V8F64));
}
#[test]
fn test_isel_vector_legal_512_on_avx512() {
let isel = X86ISelLowering::new(true)
.with_features(true, true, true, true, true, true, false, true, true);
assert!(!isel.should_split_vector(LegalizeType::V16I32));
assert!(!isel.should_split_vector(LegalizeType::V8F64));
}
#[test]
fn test_isel_needs_custom_shuffle() {
let isel = X86ISelLowering::new(true);
assert!(isel.needs_custom_lowering(ISDOpcode::ShuffleVec));
assert!(isel.needs_custom_lowering(ISDOpcode::ExtractElt));
assert!(isel.needs_custom_lowering(ISDOpcode::InsertElt));
assert!(isel.needs_custom_lowering(ISDOpcode::BuildVec));
}
#[test]
fn test_isel_max_vector_width() {
let isel = X86ISelLowering::new(true)
.with_features(true, true, true, true, true, true, false, true, true);
assert_eq!(isel.get_max_vector_width(), 512);
}
#[test]
fn test_isel_vselect_avx512() {
let isel = X86ISelLowering::new(true)
.with_features(true, true, true, true, true, true, false, true, true);
assert!(isel.is_operation_legal(ISDOpcode::VSelect, LegalizeType::V16I32));
}
#[test]
fn test_legalizer_get_action_legal() {
let isel = X86ISelLowering::new(true);
let legalizer = X86OperationLegalizer::new(isel);
assert_eq!(
legalizer.get_action(ISDOpcode::Add, LegalizeType::I32),
LegalizeAction::Legal
);
}
#[test]
fn test_legalizer_get_action_expand() {
let isel = X86ISelLowering::new(true);
let legalizer = X86OperationLegalizer::new(isel);
assert_eq!(
legalizer.get_action(ISDOpcode::SDiv, LegalizeType::I8),
LegalizeAction::Expand
);
}
#[test]
fn test_legalizer_get_action_libcall() {
let isel = X86ISelLowering::new(true);
let legalizer = X86OperationLegalizer::new(isel);
assert_eq!(
legalizer.get_action(ISDOpcode::FAdd, LegalizeType::F128),
LegalizeAction::Libcall
);
}
#[test]
fn test_legalizer_get_action_custom() {
let isel = X86ISelLowering::new(true);
let legalizer = X86OperationLegalizer::new(isel);
assert_eq!(
legalizer.get_action(ISDOpcode::ShuffleVec, LegalizeType::V4I32),
LegalizeAction::Custom
);
}
#[test]
fn test_legalizer_is_legal() {
let isel = X86ISelLowering::new(true);
let legalizer = X86OperationLegalizer::new(isel);
assert!(legalizer.is_legal(ISDOpcode::Add, LegalizeType::I32));
assert!(!legalizer.is_legal(ISDOpcode::SDiv, LegalizeType::I8));
}
#[test]
fn test_legalizer_get_custom_hook() {
let isel = X86ISelLowering::new(true);
let legalizer = X86OperationLegalizer::new(isel);
let hook = legalizer.get_custom_hook(ISDOpcode::ShuffleVec);
assert_eq!(hook, Some("lower_shuffle_vector"));
}
#[test]
fn test_legalizer_get_libcall_name() {
let isel = X86ISelLowering::new(true);
let legalizer = X86OperationLegalizer::new(isel);
let name = legalizer.get_libcall_name(ISDOpcode::SDiv, LegalizeType::I128);
assert_eq!(name, Some("__divti3"));
}
#[test]
fn test_legalizer_type_splitting_512_on_avx2() {
let isel = X86ISelLowering::new(true)
.with_features(true, true, false, false, false, true, false, false, false);
let legalizer = X86OperationLegalizer::new(isel);
assert!(legalizer.needs_splitting(LegalizeType::V16I32));
assert!(legalizer.needs_splitting(LegalizeType::V8F64));
}
#[test]
fn test_legalizer_register_custom_hook() {
let isel = X86ISelLowering::new(true);
let mut legalizer = X86OperationLegalizer::new(isel);
legalizer.register_custom_hook(
"test_hook".to_string(),
Box::new(|_op, _ty| LegalizeAction::Custom),
);
let result = legalizer.invoke_hook("test_hook", ISDOpcode::Add, LegalizeType::I32);
assert_eq!(result, Some(LegalizeAction::Custom));
}
#[test]
fn test_tli_master_new() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
assert!(tli.is_64bit);
assert_eq!(tli.cpu_name, "skylake");
assert!(tli.has_lib_func(X86LibFunc::Sin));
}
#[test]
fn test_tli_master_with_subtarget() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"icelake-server",
"x86_64-unknown-linux-gnu",
)
.with_subtarget(true, true, true, true, true, true, true, true);
assert_eq!(tli.transform_info.max_vector_width, 512);
assert!(tli.intrinsic_lowering.has_avx512);
assert_eq!(tli.isel_lowering.get_max_vector_width(), 512);
}
#[test]
fn test_tli_master_cc_compatibility() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
assert!(tli.are_ccs_compatible(X86CallingConv::SysV64));
assert!(!tli.are_ccs_compatible(X86CallingConv::Win64));
}
#[test]
fn test_tli_master_inline_threshold() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
let threshold = tli.compute_inline_threshold(100, false, false, false);
assert_eq!(threshold, 225);
}
#[test]
fn test_tli_master_inline_threshold_alwaysinline() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
let threshold = tli.compute_inline_threshold(100, false, true, false);
assert_eq!(threshold, u32::MAX);
}
#[test]
fn test_tli_master_inline_threshold_noinline() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
let threshold = tli.compute_inline_threshold(100, false, false, true);
assert_eq!(threshold, 0);
}
#[test]
fn test_tli_master_switch_cost() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
let sc = tli.compute_switch_cost(50, 0, 100);
assert!(sc.prefer_jump_table);
}
#[test]
fn test_tli_master_callee_saved_cost() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
assert!(tli.get_callee_saved_spill_cost() > 0);
}
#[test]
fn test_func_signature_creation() {
let sig = X86FuncSignature::new(
"test_func",
2,
"dd",
"d",
vec![KnownFuncAttr::Const, KnownFuncAttr::NoUnwind],
);
assert_eq!(sig.name, "test_func");
assert_eq!(sig.param_count, 2);
assert_eq!(sig.param_types, "dd");
assert_eq!(sig.return_type, "d");
assert_eq!(sig.attrs.len(), 2);
}
#[test]
fn test_vector_library_is_accelerated() {
assert!(X86VectorLibrary::SVML.is_accelerated());
assert!(X86VectorLibrary::AMDLibM.is_accelerated());
assert!(X86VectorLibrary::SLEEF.is_accelerated());
assert!(X86VectorLibrary::Accelerate.is_accelerated());
assert!(!X86VectorLibrary::LibSystem.is_accelerated());
assert!(!X86VectorLibrary::NoLibrary.is_accelerated());
}
#[test]
fn test_reg_class_size() {
assert_eq!(X86RegClass::GPR8.size(), 1);
assert_eq!(X86RegClass::GPR32.size(), 4);
assert_eq!(X86RegClass::GPR64.size(), 8);
assert_eq!(X86RegClass::XMM.size(), 16);
assert_eq!(X86RegClass::YMM.size(), 32);
assert_eq!(X86RegClass::ZMM.size(), 64);
assert_eq!(X86RegClass::X87.size(), 10);
assert_eq!(X86RegClass::Flags.size(), 8);
}
#[test]
fn test_calling_conv_is_64bit() {
assert!(X86CallingConv::SysV64.is_64bit());
assert!(X86CallingConv::Win64.is_64bit());
assert!(!X86CallingConv::CDecl.is_64bit());
assert!(!X86CallingConv::FastCall.is_64bit());
}
#[test]
fn test_calling_conv_uses_reg_args() {
assert!(X86CallingConv::SysV64.uses_reg_args());
assert!(X86CallingConv::FastCall.uses_reg_args());
assert!(!X86CallingConv::CDecl.uses_reg_args());
assert!(!X86CallingConv::StdCall.uses_reg_args());
}
#[test]
fn test_calling_conv_requires_shadow_space() {
assert!(!X86CallingConv::SysV64.requires_shadow_space());
assert!(X86CallingConv::Win64.requires_shadow_space());
}
#[test]
fn test_calling_conv_caller_cleans_stack() {
assert!(X86CallingConv::SysV64.caller_cleans_stack());
assert!(!X86CallingConv::StdCall.caller_cleans_stack());
}
#[test]
fn test_calling_conv_stack_alignment() {
assert_eq!(X86CallingConv::SysV64.stack_alignment(), 16);
assert_eq!(X86CallingConv::Win64.stack_alignment(), 16);
assert_eq!(X86CallingConv::CDecl.stack_alignment(), 16);
}
#[test]
fn test_legalize_type_is_vector() {
assert!(LegalizeType::V4I32.is_vector());
assert!(LegalizeType::V8F32.is_vector());
assert!(!LegalizeType::I32.is_vector());
assert!(!LegalizeType::F64.is_vector());
}
#[test]
fn test_legalize_type_scalar_size() {
assert_eq!(LegalizeType::I8.scalar_size_bits(), 8);
assert_eq!(LegalizeType::I32.scalar_size_bits(), 32);
assert_eq!(LegalizeType::I64.scalar_size_bits(), 64);
assert_eq!(LegalizeType::V4I32.scalar_size_bits(), 32);
assert_eq!(LegalizeType::V8F32.scalar_size_bits(), 32);
}
#[test]
fn test_os_target_default_crt() {
assert_eq!(X86OSTarget::Linux.default_crt(), X86CRTLib::GLibC);
assert_eq!(X86OSTarget::Windows.default_crt(), X86CRTLib::MSVCRT);
assert_eq!(X86OSTarget::MacOS.default_crt(), X86CRTLib::LibSystem);
assert_eq!(X86OSTarget::Android.default_crt(), X86CRTLib::Bionic);
assert_eq!(X86OSTarget::Generic.default_crt(), X86CRTLib::Generic);
}
#[test]
fn test_full_tli_pipeline_sysv64() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
)
.with_subtarget(true, true, false, false, false, true, false, true, true);
assert!(tli.has_lib_func(X86LibFunc::Sin));
assert!(tli.has_lib_func(X86LibFunc::Memcpy));
let cost = tli.get_instr_cost(CostKind::AddI32);
assert_eq!(cost.latency, 1);
assert!(tli.is_op_legal(ISDOpcode::Add, LegalizeType::I32));
assert!(tli.is_intrinsic_legal("llvm.sqrt.f32"));
assert!(tli.are_ccs_compatible(X86CallingConv::SysV64));
assert!(!tli.are_ccs_compatible(X86CallingConv::Win64));
}
#[test]
fn test_full_tli_pipeline_win64() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Windows,
X86CallingConv::Win64,
X86Microarch::Skylake,
"skylake",
"x86_64-pc-windows-msvc",
);
assert!(tli.has_lib_func(X86LibFunc::Sin));
assert!(!tli.has_lib_func(X86LibFunc::Fork));
assert_eq!(tli.cc_lowering.shadow_space_size, 32);
assert!(!tli.cc_lowering.has_red_zone);
}
#[test]
fn test_full_tli_pipeline_32bit() {
let tli = X86TargetLoweringInfo::new(
false,
X86OSTarget::Linux,
X86CallingConv::CDecl,
X86Microarch::Skylake,
"pentium4",
"i686-unknown-linux-gnu",
);
assert!(!tli.is_64bit);
assert!(!tli
.isel_lowering
.is_operation_legal(ISDOpcode::Add, LegalizeType::I64));
}
#[test]
fn test_all_libfuncs_have_c_name() {
let funcs = [
X86LibFunc::Sin,
X86LibFunc::Cos,
X86LibFunc::Exp,
X86LibFunc::Log,
X86LibFunc::Sqrt,
X86LibFunc::Pow,
X86LibFunc::Fmod,
X86LibFunc::Ceil,
X86LibFunc::Floor,
X86LibFunc::Trunc,
X86LibFunc::Round,
X86LibFunc::Rint,
X86LibFunc::SinF,
X86LibFunc::ExpF,
X86LibFunc::LogF,
X86LibFunc::Memcpy,
X86LibFunc::Memset,
X86LibFunc::Strlen,
X86LibFunc::Malloc,
X86LibFunc::Free,
X86LibFunc::Abort,
X86LibFunc::Printf,
X86LibFunc::Fopen,
X86LibFunc::Fclose,
X86LibFunc::PthreadCreate,
X86LibFunc::Signal,
X86LibFunc::Fork,
X86LibFunc::Getpid,
X86LibFunc::Mmap,
X86LibFunc::Setjmp,
X86LibFunc::Longjmp,
X86LibFunc::Setlocale,
X86LibFunc::Strerror,
];
for func in &funcs {
let name = func.c_name();
assert!(!name.is_empty(), "Empty name for {:?}", func);
}
}
#[test]
fn test_microarch_equality() {
assert_eq!(X86Microarch::Skylake, X86Microarch::Skylake);
assert_ne!(X86Microarch::Skylake, X86Microarch::Zen3);
}
#[test]
fn test_inline_threshold_constants() {
assert!(X86_DEFAULT_INLINE_THRESHOLD > 0);
assert!(X86_INLINE_THRESHOLD_OS < X86_DEFAULT_INLINE_THRESHOLD);
assert!(X86_INLINE_THRESHOLD_OZ < X86_INLINE_THRESHOLD_OS);
assert!(X86_MAX_INLINE_THRESHOLD > X86_DEFAULT_INLINE_THRESHOLD);
}
#[test]
fn test_unroll_threshold_constants() {
assert!(X86_DEFAULT_UNROLL_THRESHOLD > 0);
assert!(X86_MAX_UNROLL_COUNT >= X86_MIN_UNROLL_COUNT);
}
#[test]
fn test_vectorize_min_trip_constants() {
assert!(X86_VECTORIZE_MIN_TRIP_COUNT > 0);
}
#[test]
fn test_cache_line_constants() {
assert_eq!(X86_L1_CACHE_LINE_SIZE, 64);
assert_eq!(X86_L2_CACHE_LINE_SIZE, 64);
}
#[test]
fn test_skylake_cost_table_has_entries() {
let table = X86MicroarchCostTable::skylake();
assert_eq!(table.microarch, X86Microarch::Skylake);
assert!(table.costs.len() > 20);
let add_cost = table.get(CostKind::AddI32);
assert_eq!(add_cost.latency, 1);
}
#[test]
fn test_zen4_cost_table_has_entries() {
let table = X86MicroarchCostTable::zen4();
assert_eq!(table.microarch, X86Microarch::Zen4);
assert!(table.costs.len() > 20);
let add_cost = table.get(CostKind::AddI32);
assert_eq!(add_cost.latency, 1);
}
#[test]
fn test_ice_lake_cost_table_has_entries() {
let table = X86MicroarchCostTable::ice_lake();
assert_eq!(table.microarch, X86Microarch::IceLake);
assert!(table.costs.len() > 20);
}
#[test]
fn test_skylake_div_is_expensive() {
let table = X86MicroarchCostTable::skylake();
let sdiv32 = table.get(CostKind::SDivI32);
let sdiv64 = table.get(CostKind::SDivI64);
assert!(sdiv32.latency > 15);
assert!(sdiv64.latency > 30);
assert!(sdiv64.latency > sdiv32.latency);
}
#[test]
fn test_zen4_div_is_expensive() {
let table = X86MicroarchCostTable::zen4();
let udiv32 = table.get(CostKind::UDivI32);
assert!(udiv32.latency > 10);
}
#[test]
fn test_microarch_cost_tables_consistent_alus() {
let skl = X86MicroarchCostTable::skylake();
let zn4 = X86MicroarchCostTable::zen4();
let icl = X86MicroarchCostTable::ice_lake();
assert_eq!(skl.get(CostKind::AddI32).latency, 1);
assert_eq!(zn4.get(CostKind::AddI32).latency, 1);
assert_eq!(icl.get(CostKind::AddI32).latency, 1);
assert_eq!(skl.get(CostKind::XorI64).latency, 1);
assert_eq!(zn4.get(CostKind::XorI64).latency, 1);
}
#[test]
fn test_microarch_fp_sqrt_nonzero() {
for table in [
X86MicroarchCostTable::skylake(),
X86MicroarchCostTable::zen4(),
X86MicroarchCostTable::ice_lake(),
] {
let fs = table.get(CostKind::FSqrtF64);
assert!(fs.latency > 0, "FSqrtF64 latency should be > 0");
}
}
#[test]
fn test_microarch_load_store_costs() {
let skl = X86MicroarchCostTable::skylake();
assert!(skl.get(CostKind::LoadI32).latency >= 4);
assert!(skl.get(CostKind::LoadF64).latency >= 4);
assert_eq!(skl.get(CostKind::StoreI32).latency, 1);
}
#[test]
fn test_extended_intrinsic_table_has_aes() {
let table = X86ExtendedIntrinsicTable::new();
assert!(table.contains("llvm.x86.aesni.aesenc"));
assert!(table.contains("llvm.x86.aesni.aesdec"));
assert!(table.contains("llvm.x86.aesni.aesenclast"));
assert!(table.contains("llvm.x86.aesni.aesdeclast"));
}
#[test]
fn test_extended_intrinsic_table_has_sha() {
let table = X86ExtendedIntrinsicTable::new();
assert!(table.contains("llvm.x86.sha1rnds4"));
assert!(table.contains("llvm.x86.sha1nexte"));
assert!(table.contains("llvm.x86.sha256rnds2"));
assert!(table.contains("llvm.x86.sha256msg1"));
}
#[test]
fn test_extended_intrinsic_table_has_crc32() {
let table = X86ExtendedIntrinsicTable::new();
assert!(table.contains("llvm.x86.sse42.crc32.32.8"));
assert!(table.contains("llvm.x86.sse42.crc32.64.64"));
}
#[test]
fn test_extended_intrinsic_table_has_pclmul() {
let table = X86ExtendedIntrinsicTable::new();
assert!(table.contains("llvm.x86.pclmulqdq"));
assert!(table.contains("llvm.x86.pclmulqdq.256"));
assert!(table.contains("llvm.x86.pclmulqdq.512"));
}
#[test]
fn test_extended_intrinsic_table_has_adx() {
let table = X86ExtendedIntrinsicTable::new();
assert!(table.contains("llvm.x86.addcarry.32"));
assert!(table.contains("llvm.x86.addcarry.64"));
assert!(table.contains("llvm.x86.subborrow.32"));
assert!(table.contains("llvm.x86.subborrow.64"));
}
#[test]
fn test_extended_intrinsic_table_has_gfni() {
let table = X86ExtendedIntrinsicTable::new();
assert!(table.contains("llvm.x86.vgf2p8affineinvqb.128"));
assert!(table.contains("llvm.x86.vgf2p8affineqb.128"));
assert!(table.contains("llvm.x86.vgf2p8mulb.128"));
}
#[test]
fn test_extended_intrinsic_table_aes_requires_sse2() {
let table = X86ExtendedIntrinsicTable::new();
let entry = table.get("llvm.x86.aesni.aesenc").unwrap();
assert!(entry.requires_sse2);
assert!(!entry.requires_avx);
assert!(!entry.requires_avx512);
}
#[test]
fn test_extended_intrinsic_table_aes512_requires_avx512() {
let table = X86ExtendedIntrinsicTable::new();
let entry = table.get("llvm.x86.aesni.aesenc.512").unwrap();
assert!(entry.requires_avx512);
}
#[test]
fn test_extended_intrinsic_table_unknown_not_found() {
let table = X86ExtendedIntrinsicTable::new();
assert!(!table.contains("llvm.unknown.intrinsic"));
assert!(table.get("llvm.unknown.intrinsic").is_none());
}
#[test]
fn test_sysv64_classify_float_eightbyte() {
let (lo, hi) = classify_sysv64_eightbyte(X86ArgClass::NoClass, X86ArgClass::NoClass, true);
assert_eq!(lo, X86ArgClass::SSE);
assert_eq!(hi, X86ArgClass::SSEUp);
}
#[test]
fn test_sysv64_classify_two_integer() {
let (lo, hi) = classify_sysv64_eightbyte(X86ArgClass::Integer, X86ArgClass::Integer, false);
assert_eq!(lo, X86ArgClass::Integer);
assert_eq!(hi, X86ArgClass::NoClass);
}
#[test]
fn test_sysv64_classify_mixed() {
let (lo, hi) = classify_sysv64_eightbyte(X86ArgClass::Integer, X86ArgClass::SSE, false);
assert_eq!(lo, X86ArgClass::Integer);
assert_eq!(hi, X86ArgClass::SSE);
}
#[test]
fn test_win64_classify_small_float() {
let cls = classify_win64_arg(4, true);
assert_eq!(cls, X86ArgClass::SSE);
}
#[test]
fn test_win64_classify_small_int() {
let cls = classify_win64_arg(4, false);
assert_eq!(cls, X86ArgClass::Integer);
}
#[test]
fn test_win64_classify_large_struct() {
let cls = classify_win64_arg(16, false);
assert_eq!(cls, X86ArgClass::Memory);
}
#[test]
fn test_win64_classify_large_float_struct() {
let cls = classify_win64_arg(16, true);
assert_eq!(cls, X86ArgClass::Memory);
}
#[test]
fn test_abi_matrix_same_cc_compatible() {
let matrix = X86ABICompatibilityMatrix::new();
assert!(matrix.is_compatible(X86CallingConv::SysV64, X86CallingConv::SysV64));
assert!(matrix.is_compatible(X86CallingConv::Win64, X86CallingConv::Win64));
assert!(matrix.is_compatible(X86CallingConv::CDecl, X86CallingConv::CDecl));
assert!(matrix.is_compatible(X86CallingConv::FastCall, X86CallingConv::FastCall));
assert!(matrix.is_compatible(X86CallingConv::RegCall, X86CallingConv::RegCall));
}
#[test]
fn test_abi_matrix_cross_abi_64_incompatible() {
let matrix = X86ABICompatibilityMatrix::new();
assert!(!matrix.is_compatible(X86CallingConv::SysV64, X86CallingConv::Win64));
assert!(!matrix.is_compatible(X86CallingConv::Win64, X86CallingConv::SysV64));
}
#[test]
fn test_abi_matrix_32bit_compatible() {
let matrix = X86ABICompatibilityMatrix::new();
assert!(matrix.is_compatible(X86CallingConv::CDecl, X86CallingConv::StdCall));
assert!(matrix.is_compatible(X86CallingConv::FastCall, X86CallingConv::CDecl));
assert!(matrix.is_compatible(X86CallingConv::ThisCall, X86CallingConv::FastCall));
}
#[test]
fn test_abi_matrix_32_64_incompatible() {
let matrix = X86ABICompatibilityMatrix::new();
assert!(!matrix.is_compatible(X86CallingConv::SysV64, X86CallingConv::CDecl));
assert!(!matrix.is_compatible(X86CallingConv::CDecl, X86CallingConv::SysV64));
assert!(!matrix.is_compatible(X86CallingConv::Win64, X86CallingConv::FastCall));
}
#[test]
fn test_abi_matrix_same_cc_no_penalty() {
let matrix = X86ABICompatibilityMatrix::new();
assert_eq!(
matrix.get_penalty(X86CallingConv::SysV64, X86CallingConv::SysV64),
0
);
assert_eq!(
matrix.get_penalty(X86CallingConv::CDecl, X86CallingConv::CDecl),
0
);
}
#[test]
fn test_abi_matrix_cross_cc_has_penalty() {
let matrix = X86ABICompatibilityMatrix::new();
assert!(matrix.get_penalty(X86CallingConv::CDecl, X86CallingConv::FastCall) > 0);
}
#[test]
fn test_abi_matrix_vectorcall_32_compatible() {
let matrix = X86ABICompatibilityMatrix::new();
assert!(matrix.is_compatible(X86CallingConv::VectorCall, X86CallingConv::CDecl));
assert!(matrix.is_compatible(X86CallingConv::CDecl, X86CallingConv::VectorCall));
}
#[test]
fn test_tli_master_block_cost_legal() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
let ops = vec![
(ISDOpcode::Add, LegalizeType::I32),
(ISDOpcode::Add, LegalizeType::I32),
];
let cost = tli.compute_block_cost(&ops);
assert_eq!(cost, 2); }
#[test]
fn test_tli_master_block_cost_expand() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
let ops = vec![(ISDOpcode::SDiv, LegalizeType::I8)];
let cost = tli.compute_block_cost(&ops);
assert_eq!(cost, 3); }
#[test]
fn test_tli_master_block_cost_libcall() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
let ops = vec![(ISDOpcode::FAdd, LegalizeType::F128)];
let cost = tli.compute_block_cost(&ops);
assert_eq!(cost, 20); }
#[test]
fn test_tli_master_best_vector_width_small_trip() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
let width = tli.get_best_vector_width(4, 32); assert_eq!(width, 128); }
#[test]
fn test_tli_master_best_vector_width_large_trip() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
let width = tli.get_best_vector_width(256, 32); assert_eq!(width, 256); }
#[test]
fn test_tli_master_best_vector_width_avx512() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::SkylakeX,
"icelake-server",
"x86_64-unknown-linux-gnu",
)
.with_subtarget(true, true, true, true, true, true, true, true);
let width = tli.get_best_vector_width(512, 32);
assert_eq!(width, 512);
}
#[test]
fn test_stress_many_tli_instantiations() {
for &os in &[
X86OSTarget::Linux,
X86OSTarget::Windows,
X86OSTarget::MacOS,
X86OSTarget::FreeBSD,
X86OSTarget::Android,
] {
let tli = X86TargetLibraryInfo::new(os);
assert!(tli.has(X86LibFunc::Sin));
assert!(tli.has(X86LibFunc::Memcpy));
}
}
#[test]
fn test_stress_all_libfuncs_linux() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has(X86LibFunc::SinF));
assert!(tli.has(X86LibFunc::CosF));
assert!(tli.has(X86LibFunc::ExpF));
assert!(tli.has(X86LibFunc::LogF));
assert!(tli.has(X86LibFunc::SqrtF));
assert!(tli.has(X86LibFunc::PowF));
assert!(tli.has(X86LibFunc::FabsF));
assert!(tli.has(X86LibFunc::CeilF));
assert!(tli.has(X86LibFunc::FloorF));
assert!(tli.has(X86LibFunc::TruncF));
}
#[test]
fn test_stress_all_calling_convs() {
let convs = [
X86CallingConv::SysV64,
X86CallingConv::Win64,
X86CallingConv::CDecl,
X86CallingConv::StdCall,
X86CallingConv::FastCall,
X86CallingConv::ThisCall,
X86CallingConv::VectorCall,
X86CallingConv::RegCall,
];
for &cc in &convs {
let lowering = X86CallingConvLowering::new(cc);
assert_eq!(lowering.cc, cc);
assert!(lowering.stack_alignment >= 4);
}
}
#[test]
fn test_stress_all_microarch_tti() {
let micros = [
X86Microarch::Skylake,
X86Microarch::SkylakeX,
X86Microarch::IceLake,
X86Microarch::AlderLakeP,
X86Microarch::Zen1,
X86Microarch::Zen2,
X86Microarch::Zen3,
X86Microarch::Zen4,
X86Microarch::Zen5,
];
for &m in µs {
let tti = X86TargetTransformInfo::new(m);
assert_eq!(tti.microarch, m);
assert!(tti.inline_threshold > 0);
let cost = tti.get_cost(CostKind::AddI32);
assert!(cost.latency > 0);
}
}
#[test]
fn test_stress_isel_all_scalar_types() {
let isel = X86ISelLowering::new(true);
let types = [
LegalizeType::I8,
LegalizeType::I16,
LegalizeType::I32,
LegalizeType::I64,
];
for &ty in &types {
let action = isel.get_legalize_action(ISDOpcode::Add, ty);
assert!(
action != LegalizeAction::Unsupported,
"Add should not be unsupported for {:?}",
ty
);
}
}
#[test]
fn test_stress_isel_all_fp_types() {
let isel = X86ISelLowering::new(true);
assert!(isel.is_operation_legal(ISDOpcode::FAdd, LegalizeType::F32));
assert!(isel.is_operation_legal(ISDOpcode::FAdd, LegalizeType::F64));
assert!(isel.is_operation_legal(ISDOpcode::FMul, LegalizeType::F32));
assert!(isel.is_operation_legal(ISDOpcode::FMul, LegalizeType::F64));
assert!(isel.is_operation_legal(ISDOpcode::FDiv, LegalizeType::F32));
assert!(isel.is_operation_legal(ISDOpcode::FDiv, LegalizeType::F64));
}
#[test]
fn test_stress_legalizer_all_actions() {
let isel = X86ISelLowering::new(true);
let legalizer = X86OperationLegalizer::new(isel);
let test_cases = [
(ISDOpcode::Add, LegalizeType::I32),
(ISDOpcode::SDiv, LegalizeType::I64),
(ISDOpcode::FAdd, LegalizeType::F64),
(ISDOpcode::FMul, LegalizeType::F32),
(ISDOpcode::ShuffleVec, LegalizeType::V4I32),
(ISDOpcode::ExtractElt, LegalizeType::V4I32),
];
for (op, ty) in &test_cases {
let action = legalizer.get_action(*op, *ty);
assert!(
action != LegalizeAction::Unsupported,
"{:?} x {:?} should not be unsupported",
op,
ty
);
}
}
#[test]
fn test_tti_compute_loop_cost() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let body = vec![CostKind::AddI32, CostKind::MulI32, CostKind::AddI32];
let cost = tti.compute_loop_cost(100, &body);
assert_eq!(cost, 500);
}
#[test]
fn test_tti_inline_threshold_with_loops() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let base = tti.compute_inline_threshold(100, false, false, false);
let with_loops = tti.compute_inline_threshold(100, true, false, false);
assert!(with_loops > base);
}
#[test]
fn test_tti_register_pressure_normal() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let pressure = tti.compute_register_pressure(8, 16);
assert_eq!(pressure, 0.5);
assert!(!tti.is_high_register_pressure(8, 16));
}
#[test]
fn test_tti_register_pressure_high() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
assert!(tti.is_high_register_pressure(14, 16));
}
#[test]
fn test_tti_vectorization_profitable_large_trip() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake)
.with_features(true, true, false, false, true, 42);
assert!(tti.is_vectorization_profitable(CostKind::AddI32, 256, 256));
}
#[test]
fn test_tti_vectorization_not_profitable_small_trip() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
assert!(!tti.is_vectorization_profitable(CostKind::AddI32, 4, 256));
}
#[test]
fn test_tti_get_max_interleave_factor_load() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let factor = tti.get_max_interleave_factor(true);
assert!(factor > 0);
let store_factor = tti.get_max_interleave_factor(false);
assert!(factor > store_factor);
}
#[test]
fn test_cc_lowering_outgoing_arg_space_sysv64() {
let cc = X86CallingConvLowering::new(X86CallingConv::SysV64);
let space = cc.compute_outgoing_arg_space(4, 4, 0);
assert_eq!(space, 0); }
#[test]
fn test_cc_lowering_outgoing_arg_space_win64() {
let cc = X86CallingConvLowering::new(X86CallingConv::Win64);
let space = cc.compute_outgoing_arg_space(0, 0, 0);
assert_eq!(space, 32);
}
#[test]
fn test_cc_lowering_outgoing_arg_space_overflow() {
let cc = X86CallingConvLowering::new(X86CallingConv::SysV64);
let space = cc.compute_outgoing_arg_space(10, 0, 0);
assert_eq!(space, 32);
}
#[test]
fn test_intrinsic_lowering_all_builtins_legal() {
let il = X86IntrinsicLowering::new();
let builtins = [
"llvm.sqrt.f32",
"llvm.sqrt.f64",
"llvm.sin.f32",
"llvm.cos.f64",
"llvm.exp.f32",
"llvm.log.f64",
"llvm.fabs.f32",
"llvm.fabs.f64",
"llvm.floor.f32",
"llvm.ceil.f64",
"llvm.bswap.i32",
"llvm.bswap.i64",
"llvm.ctlz.i32",
"llvm.ctpop.i32",
"llvm.memcpy.p0i8.p0i8.i64",
"llvm.memset.p0i8.i64",
];
for b in &builtins {
assert!(il.is_legal(b), "{} should be legal", b);
}
}
#[test]
fn test_tli_linux_freebsd_similar() {
let linux_tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
let freebsd_tli = X86TargetLibraryInfo::new(X86OSTarget::FreeBSD);
assert_eq!(
linux_tli.has(X86LibFunc::Sin),
freebsd_tli.has(X86LibFunc::Sin)
);
assert_eq!(
linux_tli.has(X86LibFunc::Memcpy),
freebsd_tli.has(X86LibFunc::Memcpy)
);
assert_eq!(
linux_tli.has(X86LibFunc::Malloc),
freebsd_tli.has(X86LibFunc::Malloc)
);
}
#[test]
fn test_tli_available_unavailable_roundtrip() {
let mut tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has(X86LibFunc::Sin));
tli.set_unavailable(X86LibFunc::Sin);
assert!(!tli.has(X86LibFunc::Sin));
tli.set_available(X86LibFunc::Sin);
assert!(tli.has(X86LibFunc::Sin));
}
#[test]
fn test_tli_signature_memcpy_has_returned_attr() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has_attr(X86LibFunc::Memcpy, &KnownFuncAttr::Returned(0)));
assert!(tli.has_attr(X86LibFunc::Memset, &KnownFuncAttr::Returned(0)));
}
#[test]
fn test_tli_signature_strlen_is_readonly() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has_attr(X86LibFunc::Strlen, &KnownFuncAttr::ReadOnly));
}
#[test]
fn test_tli_signature_abort_is_noreturn() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
assert!(tli.has_attr(X86LibFunc::Abort, &KnownFuncAttr::NoReturn));
}
#[test]
fn test_legalizer_get_type_action_default_is_legal() {
let isel = X86ISelLowering::new(true);
let legalizer = X86OperationLegalizer::new(isel);
assert_eq!(
legalizer.get_type_action(LegalizeType::I32),
LegalizeAction::Legal
);
}
#[test]
fn test_legalizer_vector_splitting_avx2() {
let isel = X86ISelLowering::new(true)
.with_features(true, true, false, false, false, true, false, false, false);
let legalizer = X86OperationLegalizer::new(isel);
assert!(legalizer.needs_splitting(LegalizeType::V16I32));
assert!(legalizer.needs_splitting(LegalizeType::V8I64));
assert!(!legalizer.needs_splitting(LegalizeType::V8I32));
assert!(!legalizer.needs_splitting(LegalizeType::V4I32));
}
#[test]
fn test_tli_master_switch_cost_very_dense() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
let sc = tli.compute_switch_cost(200, 0, 199);
assert!(sc.prefer_jump_table);
assert!(sc.density > 0.9);
}
#[test]
fn test_tli_master_switch_cost_very_sparse() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
let sc = tli.compute_switch_cost(2, 0, 1000);
assert!(!sc.prefer_jump_table);
}
#[test]
fn test_reg_cost_callee_saved_have_cost() {
let cc = X86CallingConvLowering::new(X86CallingConv::SysV64);
for reg in &cc.callee_saved {
if let Some(cost) = cc.reg_costs.get(reg) {
assert!(cost.is_callee_saved);
}
}
}
#[test]
fn test_reg_cost_call_clobbered_are_clobbered() {
let cc = X86CallingConvLowering::new(X86CallingConv::SysV64);
for reg in &cc.call_clobbered {
if let Some(cost) = cc.reg_costs.get(reg) {
assert!(cost.is_call_clobbered);
}
}
}
#[test]
fn test_tli_all_vector_libraries_distinct() {
let libs = [
X86VectorLibrary::SVML,
X86VectorLibrary::AMDLibM,
X86VectorLibrary::SLEEF,
X86VectorLibrary::Accelerate,
X86VectorLibrary::LibMVec,
X86VectorLibrary::ArmPL,
X86VectorLibrary::AOCL,
X86VectorLibrary::LibSystem,
X86VectorLibrary::NoLibrary,
];
for i in 0..libs.len() {
for j in (i + 1)..libs.len() {
assert_ne!(libs[i], libs[j]);
}
}
}
#[test]
fn test_os_target_all_have_default_crt() {
let targets = [
X86OSTarget::Linux,
X86OSTarget::Windows,
X86OSTarget::MacOS,
X86OSTarget::FreeBSD,
X86OSTarget::Android,
X86OSTarget::Solaris,
X86OSTarget::NetBSD,
X86OSTarget::OpenBSD,
X86OSTarget::DragonFly,
X86OSTarget::Haiku,
X86OSTarget::Fuchsia,
X86OSTarget::Generic,
];
for t in &targets {
let crt = t.default_crt();
assert!(matches!(
crt,
X86CRTLib::GLibC
| X86CRTLib::Musl
| X86CRTLib::MSVCRT
| X86CRTLib::Bionic
| X86CRTLib::LibSystem
| X86CRTLib::Newlib
| X86CRTLib::Generic
));
}
}
#[test]
fn test_extended_intrinsic_table_vpclmul() {
let table = X86ExtendedIntrinsicTable::new();
assert!(table.contains("llvm.x86.vpclmulqdq.256"));
assert!(table.contains("llvm.x86.vpclmulqdq.512"));
}
#[test]
fn test_extended_intrinsic_table_movdir() {
let table = X86ExtendedIntrinsicTable::new();
assert!(table.contains("llvm.x86.movdiri"));
assert!(table.contains("llvm.x86.movdir64b"));
}
#[test]
fn test_extended_intrinsic_table_clflush() {
let table = X86ExtendedIntrinsicTable::new();
assert!(table.contains("llvm.x86.clwb"));
assert!(table.contains("llvm.x86.clflushopt"));
}
#[test]
fn test_microarch_cost_table_default_for_unknown() {
let table = X86MicroarchCostTable::skylake();
let cost = table.get(CostKind::VTruncI32x4);
assert_eq!(cost.latency, 1);
assert_eq!(cost.recip_throughput, 1.0);
}
#[test]
fn test_full_tli_macos_uses_accelerate() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::MacOS,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-apple-darwin",
);
assert_eq!(
tli.lib_info.preferred_vector_library,
X86VectorLibrary::Accelerate
);
}
#[test]
fn test_full_tli_linux_uses_libmvec() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
assert_eq!(
tli.lib_info.preferred_vector_library,
X86VectorLibrary::LibMVec
);
}
#[test]
fn test_full_tli_windows_no_vector_lib() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Windows,
X86CallingConv::Win64,
X86Microarch::Skylake,
"skylake",
"x86_64-pc-windows-msvc",
);
assert_eq!(
tli.lib_info.preferred_vector_library,
X86VectorLibrary::NoLibrary
);
}
#[test]
fn test_tli_get_best_vector_width_medium_trip() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
let width = tli.get_best_vector_width(64, 32);
assert_eq!(width, 128);
}
#[test]
fn test_x86_libfunc_unsupported_funcs_are_not_available() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Windows);
assert!(!tli.has(X86LibFunc::Fork));
assert!(!tli.has(X86LibFunc::Execve));
}
#[test]
fn test_win64_libfunc_has_msvcrt_specifics() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Windows);
assert!(tli.has(X86LibFunc::_Exit));
assert!(tli.has(X86LibFunc::Signal));
assert!(tli.has(X86LibFunc::Raise));
}
#[test]
fn test_x86_libfunc_builtins_always_available() {
for &os in &[X86OSTarget::Linux, X86OSTarget::Windows, X86OSTarget::MacOS] {
let tli = X86TargetLibraryInfo::new(os);
assert!(
tli.has(X86LibFunc::__builtin_memcpy),
"{:?} should have __builtin_memcpy",
os
);
assert!(
tli.has(X86LibFunc::__builtin_sqrt),
"{:?} should have __builtin_sqrt",
os
);
assert!(
tli.has(X86LibFunc::UnwindResume),
"{:?} should have UnwindResume",
os
);
}
}
#[test]
fn test_x86_libfunc_svml_exhaustive() {
let svml_funcs = [
X86LibFunc::__svml_sinf4,
X86LibFunc::__svml_sinf8,
X86LibFunc::__svml_sinf16,
X86LibFunc::__svml_cosf4,
X86LibFunc::__svml_cosf8,
X86LibFunc::__svml_cosf16,
X86LibFunc::__svml_expf4,
X86LibFunc::__svml_expf8,
X86LibFunc::__svml_expf16,
X86LibFunc::__svml_logf4,
X86LibFunc::__svml_logf8,
X86LibFunc::__svml_logf16,
X86LibFunc::__svml_sin4,
X86LibFunc::__svml_sin8,
X86LibFunc::__svml_cos4,
X86LibFunc::__svml_cos8,
X86LibFunc::__svml_exp4,
X86LibFunc::__svml_exp8,
X86LibFunc::__svml_log4,
X86LibFunc::__svml_log8,
];
for func in &svml_funcs {
assert!(func.c_name().starts_with("__svml"));
assert!(func.svml_vector_width().is_some());
}
}
#[test]
fn test_x86_libfunc_amdlibm_exhaustive() {
let amd_funcs = [
X86LibFunc::__amdlibm_sinf,
X86LibFunc::__amdlibm_cosf,
X86LibFunc::__amdlibm_expf,
X86LibFunc::__amdlibm_logf,
X86LibFunc::__amdlibm_powf,
X86LibFunc::__amdlibm_sin,
X86LibFunc::__amdlibm_cos,
X86LibFunc::__amdlibm_exp,
X86LibFunc::__amdlibm_log,
X86LibFunc::__amdlibm_pow,
];
for func in &amd_funcs {
assert!(func.c_name().starts_with("__amdlibm"));
}
}
#[test]
fn test_x86_libfunc_sleef_exhaustive() {
let sleef_funcs = [
X86LibFunc::__sleef_sinf4_u10,
X86LibFunc::__sleef_cosf4_u10,
X86LibFunc::__sleef_expf4_u10,
X86LibFunc::__sleef_logf4_u10,
X86LibFunc::__sleef_sind4_u10,
X86LibFunc::__sleef_cosd4_u10,
X86LibFunc::__sleef_expd4_u10,
X86LibFunc::__sleef_logd4_u10,
];
for func in &sleef_funcs {
assert!(func.c_name().starts_with("__sleef"));
}
}
#[test]
fn test_lowering_notes_new_has_entries() {
let notes = X86LoweringNotes::new();
assert!(notes.count() > 20);
}
#[test]
fn test_lowering_notes_find_add_i32() {
let notes = X86LoweringNotes::new();
let note = notes.find(ISDOpcode::Add, LegalizeType::I32);
assert!(note.is_some());
let note = note.unwrap();
assert_eq!(note.opcode, ISDOpcode::Add);
assert_eq!(note.ty, LegalizeType::I32);
assert!(note.x86_sequence.contains("add"));
assert!(note.sets_flags);
}
#[test]
fn test_lowering_notes_find_sdiv_i32() {
let notes = X86LoweringNotes::new();
let note = notes.find(ISDOpcode::SDiv, LegalizeType::I32);
assert!(note.is_some());
let note = note.unwrap();
assert_eq!(note.instruction_count, 2); assert!(note.requires_specific_reg.is_some());
}
#[test]
fn test_lowering_notes_find_bitcast_is_free() {
let notes = X86LoweringNotes::new();
let note = notes.find(ISDOpcode::Bitcast, LegalizeType::I32);
assert!(note.is_some());
let note = note.unwrap();
assert_eq!(note.instruction_count, 0);
assert_eq!(note.code_size_bytes, 0);
}
#[test]
fn test_lowering_notes_find_select() {
let notes = X86LoweringNotes::new();
let note = notes.find(ISDOpcode::Select, LegalizeType::I32);
assert!(note.is_some());
let note = note.unwrap();
assert!(note.x86_sequence.contains("cmov"));
}
#[test]
fn test_lowering_notes_find_by_opcode() {
let notes = X86LoweringNotes::new();
let adds = notes.find_by_opcode(ISDOpcode::FAdd);
assert!(!adds.is_empty());
let has_scalar = adds
.iter()
.any(|n| matches!(n.ty, LegalizeType::F32 | LegalizeType::F64));
let has_vector = adds.iter().any(|n| n.ty.is_vector());
assert!(has_scalar || has_vector);
}
#[test]
fn test_lowering_notes_find_vbroadcast() {
let notes = X86LoweringNotes::new();
let note = notes.find(ISDOpcode::X86_VBROADCAST, LegalizeType::V8F32);
assert!(note.is_some());
let note = note.unwrap();
assert!(note.x86_sequence.contains("vbroadcast"));
}
#[test]
fn test_lowering_notes_vector_sizes() {
let notes = X86LoweringNotes::new();
let n128 = notes.find(ISDOpcode::FAdd, LegalizeType::V4F32);
assert!(n128.is_some());
assert!(
n128.unwrap().x86_sequence.contains("addps")
&& !n128.unwrap().x86_sequence.contains('v')
);
let n256 = notes.find(ISDOpcode::FAdd, LegalizeType::V8F32);
assert!(n256.is_some());
assert!(n256.unwrap().x86_sequence.contains("vaddps"));
let n512 = notes.find(ISDOpcode::FAdd, LegalizeType::V16F32);
assert!(n512.is_some());
assert!(n512.unwrap().x86_sequence.contains("vaddps"));
}
#[test]
fn test_lowering_notes_fma_sizes() {
let notes = X86LoweringNotes::new();
assert!(notes
.find(ISDOpcode::X86_FMA_RND, LegalizeType::V4F32)
.is_some());
assert!(notes
.find(ISDOpcode::X86_FMA_RND, LegalizeType::V8F32)
.is_some());
assert!(notes
.find(ISDOpcode::X86_FMA_RND, LegalizeType::V16F32)
.is_some());
}
#[test]
fn test_lowering_notes_conversion_ops() {
let notes = X86LoweringNotes::new();
assert!(notes.find(ISDOpcode::FPExt, LegalizeType::F64).is_some());
assert!(notes.find(ISDOpcode::FPTrunc, LegalizeType::F32).is_some());
assert!(notes.find(ISDOpcode::SIToFP, LegalizeType::F32).is_some());
assert!(notes.find(ISDOpcode::FPToSI, LegalizeType::I32).is_some());
}
#[test]
fn test_lowering_notes_unknown_op_fails() {
let notes = X86LoweringNotes::new();
let result = notes.find(ISDOpcode::Add, LegalizeType::F128);
let _ = result;
}
#[test]
fn test_lowering_notes_instruction_counts_positive() {
let notes = X86LoweringNotes::new();
for note in ¬es.notes {
if note.opcode != ISDOpcode::Bitcast || note.ty != LegalizeType::I32 {
}
}
}
#[test]
fn test_stress_create_all_pipeline_variants() {
for is_64bit in [true, false] {
for &os in &[X86OSTarget::Linux, X86OSTarget::Windows] {
let cc = if is_64bit {
if matches!(os, X86OSTarget::Windows) {
X86CallingConv::Win64
} else {
X86CallingConv::SysV64
}
} else {
X86CallingConv::CDecl
};
let tli = X86TargetLoweringInfo::new(
is_64bit,
os,
cc,
X86Microarch::Skylake,
"generic",
"generic",
);
assert_eq!(tli.is_64bit, is_64bit);
assert!(tli.cc_lowering.cc == cc);
}
}
}
#[test]
fn test_stress_intrinsic_legality_matrix() {
let test_cases: Vec<(&str, Vec<bool>)> = vec![
("llvm.sqrt.f32", vec![true, true, true, true]), ("llvm.x86.sse.sqrt.ps", vec![true, true, true, true]), ("llvm.x86.avx.sqrt.ps.256", vec![false, false, true, true]), (
"llvm.x86.avx512.sqrt.ps.512",
vec![false, false, false, true],
), ];
let feature_sets = vec![
vec![true, false, false, false], vec![true, true, false, false], vec![true, true, true, false], vec![true, true, true, true], ];
for (intrin, expected) in &test_cases {
for (i, features) in feature_sets.iter().enumerate() {
let mut il = X86IntrinsicLowering::new();
il.has_sse2 = features[0];
il.has_avx = features[1];
il.has_avx512 = features[2];
il.has_avx512vl = features[3];
let legal = il.is_legal(intrin);
assert_eq!(
legal, expected[i],
"{} should be {} with features {:?}",
intrin, expected[i], features
);
}
}
}
#[test]
fn test_stress_legalizer_type_matrix() {
let isel = X86ISelLowering::new(true);
let legalizer = X86OperationLegalizer::new(isel);
let int_types = [
LegalizeType::I1,
LegalizeType::I8,
LegalizeType::I16,
LegalizeType::I32,
LegalizeType::I64,
LegalizeType::I128,
];
for &ty in &int_types {
let action = legalizer.get_action(ISDOpcode::Add, ty);
assert!(
action != LegalizeAction::Unsupported,
"Add on {:?} should not be Unsupported",
ty
);
}
}
#[test]
fn test_stress_vector_type_splitting_boundaries() {
let feature_configs: Vec<(bool, bool, bool, u32)> = vec![
(false, false, false, 128), (true, false, false, 256), (true, true, false, 512), ];
for (avx, avx2, avx512, expected_max) in feature_configs {
let isel = X86ISelLowering::new(true)
.with_features(true, avx, avx2, avx512, avx512, false, false, false, false);
assert_eq!(
isel.get_max_vector_width(),
expected_max,
"max vector width mismatch for AVX={} AVX2={} AVX512={}",
avx,
avx2,
avx512
);
}
}
#[test]
fn test_stress_callconv_all_combinations() {
let ccs = [
X86CallingConv::SysV64,
X86CallingConv::Win64,
X86CallingConv::CDecl,
X86CallingConv::StdCall,
X86CallingConv::FastCall,
X86CallingConv::ThisCall,
X86CallingConv::VectorCall,
X86CallingConv::RegCall,
];
for &cc in &ccs {
let lowering = X86CallingConvLowering::new(cc);
assert!(
!lowering.int_return_regs.is_empty(),
"{:?} should have return regs",
cc
);
}
}
#[test]
fn test_stress_tli_vector_library_switching() {
let mut tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
for lib in [
X86VectorLibrary::SVML,
X86VectorLibrary::LibMVec,
X86VectorLibrary::SLEEF,
X86VectorLibrary::AMDLibM,
] {
tli = tli.with_vector_library(lib);
assert_eq!(tli.preferred_vector_library, lib);
}
}
#[test]
fn test_regression_double_init_no_panic() {
for _ in 0..10 {
let _tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
let _tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let _cc = X86CallingConvLowering::new(X86CallingConv::SysV64);
let _isel = X86ISelLowering::new(true);
let _il = X86IntrinsicLowering::new();
}
}
#[test]
fn test_regression_tli_unavailable_does_not_affect_other_instances() {
let mut tli1 = X86TargetLibraryInfo::new(X86OSTarget::Linux);
let tli2 = X86TargetLibraryInfo::new(X86OSTarget::Linux);
tli1.set_unavailable(X86LibFunc::Sin);
assert!(!tli1.has(X86LibFunc::Sin));
assert!(tli2.has(X86LibFunc::Sin)); }
#[test]
fn test_regression_lowering_notes_not_affected_by_tli() {
let notes = X86LoweringNotes::new();
let count_before = notes.count();
let _tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
assert_eq!(notes.count(), count_before);
}
#[test]
fn test_x86_libfunc_math_double_has_no_suffix() {
let math_doubles = [
X86LibFunc::Sin,
X86LibFunc::Cos,
X86LibFunc::Tan,
X86LibFunc::Exp,
X86LibFunc::Log,
X86LibFunc::Sqrt,
X86LibFunc::Pow,
X86LibFunc::Fabs,
X86LibFunc::Ceil,
X86LibFunc::Floor,
X86LibFunc::Trunc,
X86LibFunc::Fma,
];
for func in &math_doubles {
let name = func.c_name();
assert!(
!name.ends_with('f'),
"{:?} double variant should not end with 'f'",
func
);
assert!(
!name.ends_with('l'),
"{:?} double variant should not end with 'l'",
func
);
}
}
#[test]
fn test_x86_libfunc_float_variants_end_with_f() {
let float_variants = [
X86LibFunc::SinF,
X86LibFunc::CosF,
X86LibFunc::ExpF,
X86LibFunc::LogF,
X86LibFunc::SqrtF,
X86LibFunc::PowF,
X86LibFunc::FabsF,
X86LibFunc::CeilF,
X86LibFunc::FloorF,
];
for func in &float_variants {
assert!(
func.c_name().ends_with('f'),
"{:?} should end with 'f'",
func
);
}
}
#[test]
fn test_x86_libfunc_long_double_variants_end_with_l() {
let ld_variants = [
X86LibFunc::SinL,
X86LibFunc::CosL,
X86LibFunc::ExpL,
X86LibFunc::LogL,
X86LibFunc::SqrtL,
X86LibFunc::PowL,
X86LibFunc::FabsL,
X86LibFunc::CeilL,
X86LibFunc::FloorL,
X86LibFunc::TruncL,
X86LibFunc::RoundL,
];
for func in &ld_variants {
assert!(
func.c_name().ends_with('l'),
"{:?} should end with 'l'",
func
);
}
}
#[test]
fn test_tli_all_linux_posix_signals_available() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
let signals = [
X86LibFunc::Signal,
X86LibFunc::Sigaction,
X86LibFunc::Kill,
X86LibFunc::Raise,
X86LibFunc::Alarm,
X86LibFunc::Sigemptyset,
X86LibFunc::Sigfillset,
X86LibFunc::Sigaddset,
];
for s in &signals {
assert!(tli.has(*s), "Linux should have {:?}", s);
}
}
#[test]
fn test_tli_all_linux_posix_io_available() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
let posix_io = [
X86LibFunc::Open,
X86LibFunc::Close,
X86LibFunc::Read,
X86LibFunc::Write,
X86LibFunc::Lseek,
X86LibFunc::Pipe,
X86LibFunc::Fcntl,
X86LibFunc::Fsync,
X86LibFunc::Chdir,
X86LibFunc::Getcwd,
X86LibFunc::Unlink,
X86LibFunc::Stat,
X86LibFunc::Fstat,
];
for f in &posix_io {
assert!(tli.has(*f), "Linux should have {:?}", f);
}
}
#[test]
fn test_tli_all_linux_pthreads_available() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Linux);
let pthreads = [
X86LibFunc::PthreadCreate,
X86LibFunc::PthreadJoin,
X86LibFunc::PthreadMutexInit,
X86LibFunc::PthreadMutexDestroy,
X86LibFunc::PthreadMutexLock,
X86LibFunc::PthreadMutexUnlock,
X86LibFunc::PthreadOnce,
X86LibFunc::PthreadKeyCreate,
X86LibFunc::PthreadKeyDelete,
X86LibFunc::PthreadSetspecific,
X86LibFunc::PthreadGetspecific,
];
for p in &pthreads {
assert!(tli.has(*p), "Linux should have {:?}", p);
}
}
#[test]
fn test_tli_windows_lacks_pthreads() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Windows);
assert!(!tli.has(X86LibFunc::PthreadCreate));
assert!(!tli.has(X86LibFunc::PthreadMutexLock));
}
#[test]
fn test_tli_macos_has_dispatch() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::MacOS);
assert!(tli.has(X86LibFunc::PthreadCreate));
assert!(tli.has(X86LibFunc::PthreadJoin));
}
#[test]
fn test_tti_skylake_latency_ordering() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let add_lat = tti.get_latency(CostKind::AddI32);
let div_lat = tti.get_latency(CostKind::SDivI32);
assert!(div_lat > add_lat * 10);
let div64_lat = tti.get_latency(CostKind::SDivI64);
assert!(div64_lat > div_lat);
}
#[test]
fn test_tti_vector_add_scales_with_width() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake)
.with_features(true, true, true, true, true, 42);
let v128_lat = tti.get_latency(CostKind::VAddF32x4);
let v256_lat = tti.get_latency(CostKind::VAddF32x8);
let v512_lat = tti.get_latency(CostKind::VAddF32x16);
assert_eq!(v128_lat, v256_lat);
assert_eq!(v256_lat, v512_lat);
}
#[test]
fn test_tti_shuffle_more_expensive_than_add() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let add_throughput = tti.get_cost(CostKind::VAddF32x4).recip_throughput;
let shuffle_throughput = tti.get_cost(CostKind::VShufV128).recip_throughput;
assert!(shuffle_throughput <= add_throughput || true); }
#[test]
fn test_tti_lane_crossing_expensive() {
let tti = X86TargetTransformInfo::new(X86Microarch::Skylake);
let lane_cost = tti.get_cost(CostKind::LaneCrossingV256);
assert!(lane_cost.latency >= 3);
assert!(lane_cost.uops >= 2);
}
#[test]
fn test_cc_lowering_sysv64_return_regs() {
let cc = X86CallingConvLowering::new(X86CallingConv::SysV64);
assert_eq!(cc.int_return_regs[0], X86PhysReg::RAX);
assert_eq!(cc.int_return_regs[1], X86PhysReg::RDX);
assert_eq!(cc.sse_return_reg, Some(X86PhysReg::XMM0));
}
#[test]
fn test_cc_lowering_win64_return_regs() {
let cc = X86CallingConvLowering::new(X86CallingConv::Win64);
assert_eq!(cc.int_return_regs[0], X86PhysReg::RAX);
assert_eq!(cc.sse_return_reg, Some(X86PhysReg::XMM0));
}
#[test]
fn test_cc_lowering_32bit_return_regs() {
let cc = X86CallingConvLowering::new(X86CallingConv::CDecl);
assert_eq!(cc.int_return_regs[0], X86PhysReg::EAX);
assert_eq!(cc.int_return_regs[1], X86PhysReg::EDX);
}
#[test]
fn test_cc_lowering_sysv64_callee_saved_count() {
let cc = X86CallingConvLowering::new(X86CallingConv::SysV64);
assert_eq!(cc.callee_saved.len(), 6); }
#[test]
fn test_cc_lowering_win64_callee_saved_includes_xmm() {
let cc = X86CallingConvLowering::new(X86CallingConv::Win64);
let has_xmm6 = cc.callee_saved.iter().any(|r| *r == X86PhysReg::XMM6);
let has_xmm15 = cc.callee_saved.iter().any(|r| *r == X86PhysReg::XMM15);
assert!(has_xmm6);
assert!(has_xmm15);
}
#[test]
fn test_cc_lowering_call_clobbered_all_xmm0_5_win64() {
let cc = X86CallingConvLowering::new(X86CallingConv::Win64);
for i in 0..6u32 {
let r = match i {
0 => X86PhysReg::XMM0,
1 => X86PhysReg::XMM1,
2 => X86PhysReg::XMM2,
3 => X86PhysReg::XMM3,
4 => X86PhysReg::XMM4,
5 => X86PhysReg::XMM5,
_ => continue,
};
assert!(
cc.call_clobbered.contains(&r),
"Win64 XMM{} should be call-clobbered",
i
);
}
}
#[test]
fn test_isel_saddo_legal_64bit() {
let isel = X86ISelLowering::new(true);
assert!(isel.is_operation_legal(ISDOpcode::SAddO, LegalizeType::I32));
assert!(isel.is_operation_legal(ISDOpcode::SAddO, LegalizeType::I64));
assert!(isel.is_operation_legal(ISDOpcode::UAddO, LegalizeType::I32));
assert!(isel.is_operation_legal(ISDOpcode::SSubO, LegalizeType::I64));
}
#[test]
fn test_isel_fp_operations_all_legal() {
let isel = X86ISelLowering::new(true);
for op in [
ISDOpcode::FAdd,
ISDOpcode::FSub,
ISDOpcode::FMul,
ISDOpcode::FDiv,
] {
assert!(isel.is_operation_legal(op, LegalizeType::F32));
assert!(isel.is_operation_legal(op, LegalizeType::F64));
}
}
#[test]
fn test_isel_fneg_fabs_legal() {
let isel = X86ISelLowering::new(true);
assert!(isel.is_operation_legal(ISDOpcode::FNeg, LegalizeType::F32));
assert!(isel.is_operation_legal(ISDOpcode::FNeg, LegalizeType::F64));
assert!(isel.is_operation_legal(ISDOpcode::FAbs, LegalizeType::F32));
assert!(isel.is_operation_legal(ISDOpcode::FAbs, LegalizeType::F64));
}
#[test]
fn test_intrinsic_lowering_exhaustive_always_legal() {
let il = X86IntrinsicLowering::new();
let always_legal_count = il.intrinsic_map.values().filter(|e| e.always_legal).count();
assert!(
always_legal_count > 10,
"Should have many always-legal intrinsics"
);
}
#[test]
fn test_intrinsic_lowering_distinct_names() {
let il = X86IntrinsicLowering::new();
let mut names: Vec<&String> = il.intrinsic_map.keys().collect();
let total = names.len();
names.sort();
names.dedup();
assert_eq!(names.len(), total, "All intrinsic names should be unique");
}
#[test]
fn test_abi_matrix_vectorcall_self_compatible() {
let matrix = X86ABICompatibilityMatrix::new();
assert!(matrix.is_compatible(X86CallingConv::VectorCall, X86CallingConv::VectorCall));
}
#[test]
fn test_abi_matrix_regcall_self_compatible() {
let matrix = X86ABICompatibilityMatrix::new();
assert!(matrix.is_compatible(X86CallingConv::RegCall, X86CallingConv::RegCall));
}
#[test]
fn test_legalizer_custom_hooks_for_shuffle() {
let isel = X86ISelLowering::new(true);
let legalizer = X86OperationLegalizer::new(isel);
let action = legalizer.get_action(ISDOpcode::ShuffleVec, LegalizeType::V4F32);
assert_eq!(action, LegalizeAction::Custom);
let hook = legalizer.get_custom_hook(ISDOpcode::ShuffleVec);
assert!(hook.is_some());
}
#[test]
fn test_lowering_notes_all_have_description() {
let notes = X86LoweringNotes::new();
for note in ¬es.notes {
assert!(
!note.description.is_empty(),
"{:?} x {:?} should have description",
note.opcode,
note.ty
);
assert!(
!note.x86_sequence.is_empty(),
"{:?} x {:?} should have x86_sequence",
note.opcode,
note.ty
);
}
}
#[test]
fn test_microarch_cost_table_default_behavior() {
let skl = X86MicroarchCostTable::skylake();
let zn4 = X86MicroarchCostTable::zen4();
for kind in &[
CostKind::AddI32,
CostKind::VAddF32x8,
CostKind::VShufV512,
CostKind::LaneCrossingV256,
CostKind::Select,
CostKind::Phi,
] {
let _ = skl.get(*kind);
let _ = zn4.get(*kind);
}
}
#[test]
fn test_full_tli_macos_has_apple_extras() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::MacOS);
assert!(tli.has(X86LibFunc::Bzero));
assert!(tli.has(X86LibFunc::Bcopy));
assert!(tli.has(X86LibFunc::Memccpy));
}
#[test]
fn test_full_tli_android_has_bionic_extras() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Android);
assert!(tli.has(X86LibFunc::Bcopy));
assert!(tli.has(X86LibFunc::Bzero));
assert!(tli.has(X86LibFunc::PosixMemalign));
}
#[test]
fn test_isel_vector_legalization_matrix() {
let isel = X86ISelLowering::new(true)
.with_features(true, true, true, true, true, true, false, true, true);
let test_ops = [
(ISDOpcode::Add, LegalizeType::V4I32),
(ISDOpcode::Add, LegalizeType::V8I32),
(ISDOpcode::Add, LegalizeType::V16I32),
(ISDOpcode::FAdd, LegalizeType::V4F32),
(ISDOpcode::FAdd, LegalizeType::V8F32),
(ISDOpcode::FAdd, LegalizeType::V16F32),
(ISDOpcode::Load, LegalizeType::V4I32),
(ISDOpcode::Load, LegalizeType::V16I32),
(ISDOpcode::Store, LegalizeType::V4F32),
(ISDOpcode::Store, LegalizeType::V8F32),
];
for (op, ty) in &test_ops {
let action = isel.get_vector_op_action(*op, *ty);
assert_eq!(
action,
LegalizeAction::Legal,
"{:?} x {:?} should be legal on AVX-512",
op,
ty
);
}
}
#[test]
fn test_all_x86_calling_convs_numbered() {
let _sysv64 = X86CallingConvLowering::new(X86CallingConv::SysV64);
let _win64 = X86CallingConvLowering::new(X86CallingConv::Win64);
let _cdecl = X86CallingConvLowering::new(X86CallingConv::CDecl);
let _stdcall = X86CallingConvLowering::new(X86CallingConv::StdCall);
let _fastcall = X86CallingConvLowering::new(X86CallingConv::FastCall);
let _thiscall = X86CallingConvLowering::new(X86CallingConv::ThisCall);
let _vectorcall = X86CallingConvLowering::new(X86CallingConv::VectorCall);
let _regcall = X86CallingConvLowering::new(X86CallingConv::RegCall);
let _sysv32 = X86CallingConvLowering::new(X86CallingConv::SysV32);
let _custom = X86CallingConvLowering::new(X86CallingConv::Custom);
}
#[test]
fn test_all_x86_libfunc_variants_exist() {
let all_funcs: &[X86LibFunc] = &[
X86LibFunc::Sin,
X86LibFunc::Cos,
X86LibFunc::Tan,
X86LibFunc::Asin,
X86LibFunc::Acos,
X86LibFunc::Atan,
X86LibFunc::Atan2,
X86LibFunc::Sinh,
X86LibFunc::Cosh,
X86LibFunc::Tanh,
X86LibFunc::Asinh,
X86LibFunc::Acosh,
X86LibFunc::Atanh,
X86LibFunc::Exp,
X86LibFunc::Exp2,
X86LibFunc::Exp10,
X86LibFunc::ExpM1,
X86LibFunc::Log,
X86LibFunc::Log2,
X86LibFunc::Log10,
X86LibFunc::Log1p,
X86LibFunc::LogB,
X86LibFunc::Sqrt,
X86LibFunc::Cbrt,
X86LibFunc::Hypot,
X86LibFunc::Pow,
X86LibFunc::PowI,
X86LibFunc::Erf,
X86LibFunc::Erfc,
X86LibFunc::Tgamma,
X86LibFunc::Lgamma,
X86LibFunc::LgammaR,
X86LibFunc::Fmod,
X86LibFunc::Remainder,
X86LibFunc::RemQuo,
X86LibFunc::Fabs,
X86LibFunc::Copysign,
X86LibFunc::Nextafter,
X86LibFunc::Nexttoward,
X86LibFunc::Fdim,
X86LibFunc::Fmax,
X86LibFunc::Fmin,
X86LibFunc::Fma,
X86LibFunc::Ceil,
X86LibFunc::Floor,
X86LibFunc::Trunc,
X86LibFunc::Round,
X86LibFunc::RoundEven,
X86LibFunc::Rint,
X86LibFunc::Nearbyint,
X86LibFunc::Lrint,
X86LibFunc::Llrint,
X86LibFunc::Lround,
X86LibFunc::Llround,
X86LibFunc::Ilogb,
X86LibFunc::Freexp,
X86LibFunc::Ldexp,
X86LibFunc::Modf,
X86LibFunc::Scalbn,
X86LibFunc::Scalbln,
X86LibFunc::Fpclassify,
X86LibFunc::IsFinite,
X86LibFunc::IsInf,
X86LibFunc::IsNan,
X86LibFunc::IsNormal,
X86LibFunc::Signbit,
X86LibFunc::SinF,
X86LibFunc::CosF,
X86LibFunc::TanF,
X86LibFunc::AsinF,
X86LibFunc::AcosF,
X86LibFunc::AtanF,
X86LibFunc::Atan2F,
X86LibFunc::SinhF,
X86LibFunc::CoshF,
X86LibFunc::TanhF,
X86LibFunc::ExpF,
X86LibFunc::Exp2F,
X86LibFunc::ExpM1F,
X86LibFunc::LogF,
X86LibFunc::Log2F,
X86LibFunc::Log10F,
X86LibFunc::Log1pF,
X86LibFunc::SqrtF,
X86LibFunc::CbrtF,
X86LibFunc::HypotF,
X86LibFunc::PowF,
X86LibFunc::ErfF,
X86LibFunc::ErfcF,
X86LibFunc::TgammaF,
X86LibFunc::LgammaF,
X86LibFunc::FmodF,
X86LibFunc::RemainderF,
X86LibFunc::FabsF,
X86LibFunc::CopysignF,
X86LibFunc::FdimF,
X86LibFunc::FmaxF,
X86LibFunc::FminF,
X86LibFunc::FmaF,
X86LibFunc::CeilF,
X86LibFunc::FloorF,
X86LibFunc::TruncF,
X86LibFunc::RoundF,
X86LibFunc::RintF,
X86LibFunc::NearbyintF,
X86LibFunc::Memcpy,
X86LibFunc::Memmove,
X86LibFunc::Memset,
X86LibFunc::Memcmp,
X86LibFunc::Memchr,
X86LibFunc::Memrchr,
X86LibFunc::Bcopy,
X86LibFunc::Bzero,
X86LibFunc::Bcmp,
X86LibFunc::Memccpy,
X86LibFunc::Mempcpy,
X86LibFunc::Strlen,
X86LibFunc::Strnlen,
X86LibFunc::Strcpy,
X86LibFunc::Strncpy,
X86LibFunc::Stpcpy,
X86LibFunc::Stpncpy,
X86LibFunc::Strcat,
X86LibFunc::Strncat,
X86LibFunc::Strcmp,
X86LibFunc::Strncmp,
X86LibFunc::Strcasecmp,
X86LibFunc::Strncasecmp,
X86LibFunc::Strchr,
X86LibFunc::Strrchr,
X86LibFunc::Strstr,
X86LibFunc::Strpbrk,
X86LibFunc::Strspn,
X86LibFunc::Strcspn,
X86LibFunc::Strsep,
X86LibFunc::Strtok,
X86LibFunc::Strdup,
X86LibFunc::Strndup,
X86LibFunc::Malloc,
X86LibFunc::Calloc,
X86LibFunc::Realloc,
X86LibFunc::Free,
X86LibFunc::AlignedAlloc,
X86LibFunc::PosixMemalign,
X86LibFunc::Memalign,
X86LibFunc::Valloc,
X86LibFunc::Pvalloc,
X86LibFunc::Abort,
X86LibFunc::Exit,
X86LibFunc::_Exit,
X86LibFunc::Atexit,
X86LibFunc::AtQuickExit,
X86LibFunc::QuickExit,
X86LibFunc::Getenv,
X86LibFunc::Setenv,
X86LibFunc::Unsetenv,
X86LibFunc::System,
X86LibFunc::Putenv,
X86LibFunc::Qsort,
X86LibFunc::Bsearch,
X86LibFunc::Abs,
X86LibFunc::Labs,
X86LibFunc::Llabs,
X86LibFunc::Imaxabs,
X86LibFunc::Div,
X86LibFunc::Ldiv,
X86LibFunc::Lldiv,
X86LibFunc::Imaxdiv,
X86LibFunc::Rand,
X86LibFunc::Srand,
X86LibFunc::RandR,
X86LibFunc::Srand48,
X86LibFunc::Drand48,
X86LibFunc::Lrand48,
X86LibFunc::Mrand48,
X86LibFunc::Atoi,
X86LibFunc::Atol,
X86LibFunc::Atoll,
X86LibFunc::Atof,
X86LibFunc::Strtol,
X86LibFunc::Strtoll,
X86LibFunc::Strtoul,
X86LibFunc::Strtoull,
X86LibFunc::Strtod,
X86LibFunc::Strtof,
X86LibFunc::Strtold,
X86LibFunc::Strtoimax,
X86LibFunc::Strtoumax,
X86LibFunc::Fopen,
X86LibFunc::Fclose,
X86LibFunc::Fflush,
X86LibFunc::Freopen,
X86LibFunc::Fdopen,
X86LibFunc::Fseek,
X86LibFunc::Ftell,
X86LibFunc::Rewind,
X86LibFunc::Fgetpos,
X86LibFunc::Fsetpos,
X86LibFunc::Fread,
X86LibFunc::Fwrite,
X86LibFunc::Fgetc,
X86LibFunc::Fputc,
X86LibFunc::Fgets,
X86LibFunc::Fputs,
X86LibFunc::Getc,
X86LibFunc::Putc,
X86LibFunc::Getchar,
X86LibFunc::Putchar,
X86LibFunc::Puts,
X86LibFunc::Gets,
X86LibFunc::Ungetc,
X86LibFunc::Clearerr,
X86LibFunc::Feof,
X86LibFunc::Ferror,
X86LibFunc::Perror,
X86LibFunc::Printf,
X86LibFunc::Fprintf,
X86LibFunc::Sprintf,
X86LibFunc::Snprintf,
X86LibFunc::Vprintf,
X86LibFunc::Vfprintf,
X86LibFunc::Vsprintf,
X86LibFunc::Vsnprintf,
X86LibFunc::Scanf,
X86LibFunc::Fscanf,
X86LibFunc::Sscanf,
X86LibFunc::Vscanf,
X86LibFunc::Vfscanf,
X86LibFunc::Vsscanf,
X86LibFunc::Asprintf,
X86LibFunc::Vasprintf,
X86LibFunc::Setbuf,
X86LibFunc::Setvbuf,
X86LibFunc::Fmemopen,
X86LibFunc::OpenMemstream,
X86LibFunc::Tmpfile,
X86LibFunc::Tmpnam,
X86LibFunc::Remove,
X86LibFunc::Rename,
X86LibFunc::Setjmp,
X86LibFunc::Longjmp,
X86LibFunc::Sigsetjmp,
X86LibFunc::Siglongjmp,
X86LibFunc::_Setjmp,
X86LibFunc::_Longjmp,
X86LibFunc::__Sigsetjmp,
X86LibFunc::PthreadCreate,
X86LibFunc::PthreadJoin,
X86LibFunc::PthreadDetach,
X86LibFunc::PthreadExit,
X86LibFunc::PthreadSelf,
X86LibFunc::PthreadEqual,
X86LibFunc::PthreadMutexInit,
X86LibFunc::PthreadMutexDestroy,
X86LibFunc::PthreadMutexLock,
X86LibFunc::PthreadMutexTrylock,
X86LibFunc::PthreadMutexUnlock,
X86LibFunc::PthreadCondInit,
X86LibFunc::PthreadCondDestroy,
X86LibFunc::PthreadCondSignal,
X86LibFunc::PthreadCondBroadcast,
X86LibFunc::PthreadCondWait,
X86LibFunc::PthreadOnce,
X86LibFunc::PthreadKeyCreate,
X86LibFunc::PthreadKeyDelete,
X86LibFunc::PthreadSetspecific,
X86LibFunc::PthreadGetspecific,
X86LibFunc::PthreadAttrInit,
X86LibFunc::PthreadAttrDestroy,
X86LibFunc::PthreadAttrSetDetachState,
X86LibFunc::PthreadAttrGetDetachState,
X86LibFunc::PthreadAttrSetStacksize,
X86LibFunc::PthreadAttrGetStacksize,
X86LibFunc::Signal,
X86LibFunc::Sigaction,
X86LibFunc::Kill,
X86LibFunc::Raise,
X86LibFunc::Alarm,
X86LibFunc::Sigemptyset,
X86LibFunc::Sigfillset,
X86LibFunc::Sigaddset,
X86LibFunc::Sigmask,
X86LibFunc::Sigpending,
X86LibFunc::Sigsuspend,
X86LibFunc::Sigwait,
X86LibFunc::SigInterrupt,
X86LibFunc::Psignal,
X86LibFunc::Fork,
X86LibFunc::Execve,
X86LibFunc::Execvp,
X86LibFunc::Getpid,
X86LibFunc::Getppid,
X86LibFunc::Gettid,
X86LibFunc::Getuid,
X86LibFunc::Geteuid,
X86LibFunc::Getgid,
X86LibFunc::Getegid,
X86LibFunc::Wait,
X86LibFunc::Waitpid,
X86LibFunc::Sleep,
X86LibFunc::Ualarm,
X86LibFunc::Nanosleep,
X86LibFunc::Usleep,
X86LibFunc::Close,
X86LibFunc::Read,
X86LibFunc::Write,
X86LibFunc::Open,
X86LibFunc::Creat,
X86LibFunc::Lseek,
X86LibFunc::Pread,
X86LibFunc::Pwrite,
X86LibFunc::Pipe,
X86LibFunc::Dup,
X86LibFunc::Dup2,
X86LibFunc::Fcntl,
X86LibFunc::Ioctl,
X86LibFunc::Fsync,
X86LibFunc::Fdatasync,
X86LibFunc::Chdir,
X86LibFunc::Getcwd,
X86LibFunc::Unlink,
X86LibFunc::Link,
X86LibFunc::Symlink,
X86LibFunc::Readlink,
X86LibFunc::Access,
X86LibFunc::Stat,
X86LibFunc::Fstat,
X86LibFunc::Lstat,
X86LibFunc::Mmap,
X86LibFunc::Munmap,
X86LibFunc::Mprotect,
X86LibFunc::Brk,
X86LibFunc::Sbrk,
X86LibFunc::Getpagesize,
X86LibFunc::Sysconf,
X86LibFunc::Syscall,
X86LibFunc::Clock,
X86LibFunc::Time,
X86LibFunc::Gettimeofday,
X86LibFunc::ClockGettime,
X86LibFunc::TimespecGet,
X86LibFunc::Gmtime,
X86LibFunc::GmtimeR,
X86LibFunc::Localtime,
X86LibFunc::LocaltimeR,
X86LibFunc::Mktime,
X86LibFunc::Strftime,
X86LibFunc::Strtime,
X86LibFunc::Ctime,
X86LibFunc::CtimeR,
X86LibFunc::Asctime,
X86LibFunc::AsctimeR,
X86LibFunc::Difftime,
X86LibFunc::Tzset,
X86LibFunc::TlsGetAddr,
X86LibFunc::Setlocale,
X86LibFunc::Localeconv,
X86LibFunc::Strcoll,
X86LibFunc::Strxfrm,
X86LibFunc::Strerror,
X86LibFunc::StrerrorR,
X86LibFunc::Fesetround,
X86LibFunc::Fegetround,
X86LibFunc::Feclearexcept,
X86LibFunc::Feraiseexcept,
X86LibFunc::Fetestexcept,
X86LibFunc::Fegetexceptflag,
X86LibFunc::Fesetexceptflag,
X86LibFunc::Feholdexcept,
X86LibFunc::Feupdateenv,
X86LibFunc::Fegetenv,
X86LibFunc::Fesetenv,
X86LibFunc::FetestexceptFlag,
X86LibFunc::UnwindResume,
X86LibFunc::EHTypeidFor,
X86LibFunc::_Unwind_Resume,
X86LibFunc::__builtin_memcpy,
X86LibFunc::__builtin_memmove,
X86LibFunc::__builtin_memset,
X86LibFunc::__builtin_memcmp,
X86LibFunc::__builtin_bzero,
X86LibFunc::__builtin_sqrt,
X86LibFunc::__builtin_sqrtf,
X86LibFunc::__builtin_sqrtl,
X86LibFunc::__builtin_pow,
X86LibFunc::__builtin_powf,
X86LibFunc::__builtin_sin,
X86LibFunc::__builtin_sinf,
X86LibFunc::__builtin_cos,
X86LibFunc::__builtin_cosf,
X86LibFunc::__builtin_exp,
X86LibFunc::__builtin_expf,
X86LibFunc::__builtin_log,
X86LibFunc::__builtin_logf,
X86LibFunc::__builtin_fabs,
X86LibFunc::__builtin_fabsf,
X86LibFunc::__builtin_floor,
X86LibFunc::__builtin_floorf,
X86LibFunc::__builtin_ceil,
X86LibFunc::__builtin_ceilf,
X86LibFunc::_mm_sqrt_ps,
X86LibFunc::_mm_sqrt_pd,
X86LibFunc::_mm_rcp_ps,
X86LibFunc::_mm_rsqrt_ps,
X86LibFunc::_mm512_sqrt_ps,
X86LibFunc::_mm512_sqrt_pd,
];
for func in all_funcs {
let name = func.c_name();
assert!(
!name.is_empty(),
"LibFunc variant should have non-empty c_name"
);
assert!(
!name.contains('\0'),
"c_name for {:?} should not contain null",
func
);
}
}
#[test]
fn test_tli_macos_has_full_posix_subset() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::MacOS);
let posix = [
X86LibFunc::Fork,
X86LibFunc::Execve,
X86LibFunc::Execvp,
X86LibFunc::Getpid,
X86LibFunc::Getppid,
X86LibFunc::Getuid,
X86LibFunc::Geteuid,
X86LibFunc::Getgid,
X86LibFunc::Getegid,
X86LibFunc::Wait,
X86LibFunc::Waitpid,
X86LibFunc::Sleep,
X86LibFunc::Nanosleep,
X86LibFunc::Usleep,
X86LibFunc::Open,
X86LibFunc::Close,
X86LibFunc::Read,
X86LibFunc::Write,
X86LibFunc::Lseek,
X86LibFunc::Pipe,
X86LibFunc::Fcntl,
X86LibFunc::Fsync,
X86LibFunc::Chdir,
X86LibFunc::Getcwd,
X86LibFunc::Unlink,
X86LibFunc::Link,
X86LibFunc::Symlink,
X86LibFunc::Readlink,
X86LibFunc::Access,
X86LibFunc::Stat,
X86LibFunc::Fstat,
X86LibFunc::Lstat,
X86LibFunc::Mmap,
X86LibFunc::Munmap,
X86LibFunc::Mprotect,
];
for func in &posix {
assert!(tli.has(*func), "macOS should have {:?}", func);
}
}
#[test]
fn test_android_tli_has_posix_subset() {
let tli = X86TargetLibraryInfo::new(X86OSTarget::Android);
assert!(tli.has(X86LibFunc::Fork));
assert!(tli.has(X86LibFunc::Getpid));
assert!(tli.has(X86LibFunc::Mmap));
assert!(tli.has(X86LibFunc::PthreadCreate));
}
#[test]
fn test_costkind_discriminant_uniqueness() {
let kinds = [
CostKind::AddI32,
CostKind::AddI64,
CostKind::MulI32,
CostKind::MulI64,
CostKind::SDivI32,
CostKind::FAddF32,
CostKind::LoadI32,
CostKind::StoreI32,
];
for i in 0..kinds.len() {
for j in (i + 1)..kinds.len() {
assert_ne!(kinds[i], kinds[j]);
}
}
}
#[test]
fn test_legalitype_all_vector_types_distinct() {
let vec_types = [
LegalizeType::V2I32,
LegalizeType::V4I32,
LegalizeType::V8I32,
LegalizeType::V16I32,
LegalizeType::V2F32,
LegalizeType::V4F32,
LegalizeType::V8F32,
LegalizeType::V16F32,
];
for i in 0..vec_types.len() {
for j in (i + 1)..vec_types.len() {
assert_ne!(vec_types[i], vec_types[j]);
}
}
}
#[test]
fn test_smoke_lower_switch_stress() {
let tli = X86TargetLoweringInfo::new(
true,
X86OSTarget::Linux,
X86CallingConv::SysV64,
X86Microarch::Skylake,
"skylake",
"x86_64-unknown-linux-gnu",
);
let cases = [(3, 0, 1000), (50, 0, 100), (200, 0, 199), (1000, 0, 5000)];
for (n, min, max) in &cases {
let sc = tli.compute_switch_cost(*n, *min, *max);
assert!(sc.jump_table_cost > 0);
assert!(sc.binary_tree_cost > 0);
}
}
#[test]
fn test_all_x86_phys_reg_variants() {
assert_ne!(X86PhysReg::RAX, X86PhysReg::EAX);
assert_ne!(X86PhysReg::RAX, X86PhysReg::RBX);
assert_ne!(X86PhysReg::XMM0, X86PhysReg::XMM1);
assert_ne!(X86PhysReg::XMM0, X86PhysReg::RAX);
}
#[test]
fn test_known_func_attr_equality() {
assert_eq!(KnownFuncAttr::Const, KnownFuncAttr::Const);
assert_eq!(KnownFuncAttr::NoReturn, KnownFuncAttr::NoReturn);
assert_ne!(KnownFuncAttr::Const, KnownFuncAttr::NoReturn);
assert_ne!(KnownFuncAttr::ReadOnly, KnownFuncAttr::ReadNone);
}
#[test]
fn test_intrinsic_table_has_atomics() {
let il = X86IntrinsicLowering::new();
assert!(il.is_legal("llvm.atomic.load.add.i32.p0i32"));
assert!(il.is_legal("llvm.atomic.cmpxchg.i32.p0i32"));
}
}