use crate::clang::ast::*;
use crate::clang::sema::*;
use crate::x86::X86Subtarget;
use std::collections::{HashMap, HashSet};
pub struct X86Sema {
pub base: Sema,
pub subtarget: X86Subtarget,
pub type_checker: X86TypeChecker,
pub builtin_checker: X86BuiltinChecker,
pub abi_checker: X86ABIChecker,
pub alignment_checker: X86AlignmentChecker,
pub intrinsic_sema: X86IntrinsicSema,
pub target_attr_checker: X86TargetAttrChecker,
pub format_attr_checker: X86FormatAttrChecker,
pub calling_conv_checker: X86CallingConvChecker,
pub vector_size_checker: X86VectorSizeChecker,
pub x86_errors: Vec<String>,
pub x86_warnings: Vec<String>,
}
impl X86Sema {
pub fn new(base: Sema, subtarget: X86Subtarget) -> Self {
let type_checker = X86TypeChecker::new(subtarget.clone());
let builtin_checker = X86BuiltinChecker::new(subtarget.clone());
let abi_checker = X86ABIChecker::new(subtarget.clone());
let alignment_checker = X86AlignmentChecker::new(subtarget.clone());
let intrinsic_sema = X86IntrinsicSema::new(subtarget.clone());
let target_attr_checker = X86TargetAttrChecker::new(subtarget.clone());
let format_attr_checker = X86FormatAttrChecker::new();
let calling_conv_checker = X86CallingConvChecker::new(subtarget.clone());
let vector_size_checker = X86VectorSizeChecker::new(subtarget.clone());
Self {
base,
subtarget,
type_checker,
builtin_checker,
abi_checker,
alignment_checker,
intrinsic_sema,
target_attr_checker,
format_attr_checker,
calling_conv_checker,
vector_size_checker,
x86_errors: Vec::new(),
x86_warnings: Vec::new(),
}
}
pub fn x86_error(&mut self, msg: String) {
self.x86_errors.push(msg.clone());
self.base.errors.push(msg);
}
pub fn x86_warning(&mut self, msg: String) {
self.x86_warnings.push(msg.clone());
self.base.warnings.push(msg);
}
pub fn has_x86_errors(&self) -> bool {
!self.x86_errors.is_empty()
}
pub fn has_x86_warnings(&self) -> bool {
!self.x86_warnings.is_empty()
}
pub fn clear_x86_diagnostics(&mut self) {
self.x86_errors.clear();
self.x86_warnings.clear();
}
pub fn x86_diagnostic_summary(&self) -> String {
format!(
"X86: {} error(s), {} warning(s)",
self.x86_errors.len(),
self.x86_warnings.len()
)
}
pub fn check_x86_tu(&mut self, tu: &TranslationUnit) {
for decl in &tu.decls {
self.check_x86_decl(decl);
}
}
fn check_x86_decl(&mut self, decl: &Decl) {
match decl {
Decl::Function(fd) => {
self.check_x86_function(fd);
}
Decl::Variable(vd) => {
self.check_x86_variable(vd);
}
Decl::Typedef(td) => {
self.check_x86_typedef(td);
}
Decl::Struct(sd) => {
self.check_x86_struct(sd);
}
_ => {}
}
}
fn check_x86_function(&mut self, fd: &FunctionDecl) {
self.type_checker.check_type(&fd.ret_ty, self);
for param in &fd.params {
self.type_checker.check_type(¶m.ty, self);
}
if let Some(cc_str) = self.detect_calling_convention(fd) {
self.abi_checker.check_calling_convention(&cc_str, fd, self);
}
if fd.name.starts_with("__builtin_ia32_") {
self.builtin_checker.check_builtin_call(fd, self);
}
if fd.name.starts_with("_mm_")
|| fd.name.starts_with("_mm256_")
|| fd.name.starts_with("_mm512_")
{
self.intrinsic_sema.check_intrinsic(fd, self);
}
}
fn check_x86_variable(&mut self, vd: &VarDecl) {
self.type_checker.check_type(&vd.ty, self);
if vd.is_global || vd.is_static {
self.alignment_checker.check_global_alignment(vd, self);
}
}
fn check_x86_typedef(&mut self, td: &TypedefDecl) {
self.type_checker.check_type(&td.underlying, self);
}
fn check_x86_struct(&mut self, sd: &StructDecl) {
for field in &sd.fields {
self.type_checker.check_type(&field.ty, self);
}
}
fn detect_calling_convention(&self, fd: &FunctionDecl) -> Option<String> {
if self.subtarget.is_64_bit {
Some("sysv".to_string())
} else {
Some("cdecl".to_string())
}
}
}
#[derive(Debug, Clone)]
pub struct X86TypeChecker {
subtarget: X86Subtarget,
pub is_lp64: bool,
pub int_size: u32,
pub long_size: u32,
pub long_long_size: u32,
pub float_size: u32,
pub double_size: u32,
pub long_double_size: u32,
pub int128_size: u32,
pub float80_size: u32,
pub float128_size: u32,
pub pointer_size: u32,
pub max_vector_size: u32,
pub supports_int128: bool,
pub supports_float80: bool,
pub supports_float128: bool,
}
impl X86TypeChecker {
pub fn new(subtarget: X86Subtarget) -> Self {
let is_64bit = subtarget.is_64_bit;
let is_lp64 = true;
let long_size = if is_64bit && is_lp64 { 8 } else { 4 };
let pointer_size = if is_64bit { 8 } else { 4 };
let max_vector_size = if subtarget.has_avx512f {
64
} else if subtarget.has_avx {
32
} else if subtarget.has_sse {
16
} else {
8
};
let long_double_size = if is_64bit {
if is_lp64 {
16
} else {
8
}
} else {
12
};
Self {
subtarget,
is_lp64,
int_size: 4,
long_size,
long_long_size: 8,
float_size: 4,
double_size: 8,
long_double_size,
int128_size: 16,
float80_size: 16,
float128_size: 16,
pointer_size,
max_vector_size,
supports_int128: is_64bit,
supports_float80: !is_64bit || is_lp64,
supports_float128: is_64bit && subtarget.has_sse,
}
}
pub fn check_type(&self, ty: &QualType, sema: &mut X86Sema) {
let base = &ty.base;
match base {
TypeNode::Long => {
if self.subtarget.is_64_bit && self.is_lp64 {
if self.long_size != 8 {
sema.x86_error(format!(
"LP64 requires 8-byte 'long', got {} bytes",
base.size_bytes()
));
}
}
}
TypeNode::LongDouble => {
if !self.subtarget.has_x87 && self.supports_float80 {
sema.x86_warning(
"long double requires x87 FPU, which is not available".to_string(),
);
}
}
TypeNode::Pointer(inner) => {
self.check_type(&**inner, sema);
}
TypeNode::Array { elem, size: _ } => {
self.check_type(&**elem, sema);
}
TypeNode::Function { ret, params, .. } => {
self.check_type(&**ret, sema);
for param in params {
self.check_type(param, sema);
}
}
TypeNode::Struct { fields, .. } => {
for field in fields {
self.check_type(&field.ty, sema);
}
}
_ => {}
}
}
pub fn check_integer_size(&self, bits: u32) -> Result<u32, String> {
match bits {
16 => Ok(2),
32 => Ok(4),
64 => Ok(8),
128 => {
if !self.supports_int128 {
Err("__int128 requires 64-bit mode".to_string())
} else {
Ok(16)
}
}
_ => Err(format!("unsupported integer width: {} bits", bits)),
}
}
pub fn check_float_size(&self, bits: u32) -> Result<u32, String> {
match bits {
32 => Ok(4),
64 => Ok(8),
80 => {
if !self.supports_float80 {
Err("__float80 requires x87 FPU".to_string())
} else {
Ok(self.float80_size)
}
}
128 => {
if !self.supports_float128 {
Err("__float128 requires SSE and 64-bit mode".to_string())
} else {
Ok(16)
}
}
_ => Err(format!("unsupported float width: {} bits", bits)),
}
}
pub fn check_vector_type(&self, elem_size: u32, num_elements: u32) -> Result<u32, String> {
let total_bytes = elem_size * num_elements;
if total_bytes > self.max_vector_size {
return Err(format!(
"vector size {} bytes exceeds maximum {} bytes for this subtarget; \
consider enabling AVX or AVX-512",
total_bytes, self.max_vector_size
));
}
if total_bytes == 32 && !self.subtarget.has_avx {
return Err("256-bit vectors require -mavx".to_string());
}
if total_bytes == 64 && !self.subtarget.has_avx512f {
return Err("512-bit vectors require -mavx512f".to_string());
}
Ok(total_bytes)
}
pub fn sizeof_type_name(&self, name: &str) -> Option<u32> {
match name {
"char" | "signed char" | "unsigned char" => Some(1),
"short" | "unsigned short" => Some(2),
"int" | "unsigned int" => Some(self.int_size),
"long" | "unsigned long" => Some(self.long_size),
"long long" | "unsigned long long" => Some(self.long_long_size),
"float" => Some(self.float_size),
"double" => Some(self.double_size),
"long double" => Some(self.long_double_size),
"__int128" | "unsigned __int128" => {
if self.supports_int128 {
Some(self.int128_size)
} else {
None
}
}
"__float80" => {
if self.supports_float80 {
Some(self.float80_size)
} else {
None
}
}
"__float128" => {
if self.supports_float128 {
Some(self.float128_size)
} else {
None
}
}
"void*" | "char*" | "int*" => Some(self.pointer_size),
"size_t" => Some(self.pointer_size),
"ptrdiff_t" => Some(self.pointer_size),
"wchar_t" => Some(4),
_ => None,
}
}
pub fn alignof_type_name(&self, name: &str) -> Option<u32> {
match name {
"char" | "signed char" | "unsigned char" => Some(1),
"short" | "unsigned short" => Some(2),
"int" | "unsigned int" | "float" => Some(4),
"long" | "unsigned long" => {
if self.subtarget.is_64_bit && self.is_lp64 {
Some(8)
} else {
Some(4)
}
}
"long long" | "unsigned long long" | "double" => Some(8),
"long double" => {
if self.subtarget.is_64_bit && self.is_lp64 {
Some(16)
} else {
Some(4)
}
}
"__int128" | "unsigned __int128" | "__float128" => Some(16),
"__float80" => Some(16),
"void*" | "char*" | "int*" => {
if self.subtarget.is_64_bit {
Some(8)
} else {
Some(4)
}
}
"size_t" => {
if self.subtarget.is_64_bit {
Some(8)
} else {
Some(4)
}
}
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct BuiltinArg {
pub index: usize,
pub name: String,
pub expected_type: String,
pub any_vector: bool,
pub is_immediate: bool,
pub imm_min: Option<i64>,
pub imm_max: Option<i64>,
}
#[derive(Debug, Clone)]
pub struct X86BuiltinSignature {
pub name: String,
pub return_type: String,
pub args: Vec<BuiltinArg>,
pub required_feature: Option<String>,
pub is_64bit_only: bool,
}
#[derive(Debug, Clone)]
pub struct X86BuiltinChecker {
subtarget: X86Subtarget,
signatures: HashMap<String, X86BuiltinSignature>,
validated: HashSet<String>,
}
impl X86BuiltinChecker {
pub fn new(subtarget: X86Subtarget) -> Self {
let mut checker = Self {
subtarget,
signatures: HashMap::new(),
validated: HashSet::new(),
};
checker.register_builtins();
checker
}
fn register_builtins(&mut self) {
self.add_builtin(
"__builtin_ia32_addps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_addss",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_subps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_subss",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_mulps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_mulss",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_divps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_divss",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_sqrtps",
"V4F32",
&[("a", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_sqrtss",
"V4F32",
&[("a", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_rcpps",
"V4F32",
&[("a", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_rcpss",
"V4F32",
&[("a", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_rsqrtps",
"V4F32",
&[("a", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_rsqrtss",
"V4F32",
&[("a", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_minps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_minss",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_maxps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_maxss",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_andps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_andnps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_orps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_xorps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_cmpps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32"), ("imm", "i32")],
Some("sse"),
false,
);
self.add_builtin_imm("__builtin_ia32_cmpps", 2, 0, 7);
self.add_builtin(
"__builtin_ia32_cmpss",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32"), ("imm", "i32")],
Some("sse"),
false,
);
self.add_builtin_imm("__builtin_ia32_cmpss", 2, 0, 7);
self.add_builtin(
"__builtin_ia32_shufps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32"), ("imm", "i32")],
Some("sse"),
false,
);
self.add_builtin_imm("__builtin_ia32_shufps", 2, 0, 255);
self.add_builtin(
"__builtin_ia32_comieq_ss",
"i32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_comilt_ss",
"i32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_comile_ss",
"i32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_comigt_ss",
"i32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_comige_ss",
"i32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_comineq_ss",
"i32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_ucomieq_ss",
"i32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_ucomilt_ss",
"i32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_ucomile_ss",
"i32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_ucomigt_ss",
"i32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_ucomige_ss",
"i32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_ucomineq_ss",
"i32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_cvtss2si",
"i32",
&[("a", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_cvtss2si64",
"i64",
&[("a", "V4F32")],
Some("sse"),
true,
);
self.add_builtin(
"__builtin_ia32_cvttss2si",
"i32",
&[("a", "V4F32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_cvttss2si64",
"i64",
&[("a", "V4F32")],
Some("sse"),
true,
);
self.add_builtin(
"__builtin_ia32_cvtsi2ss",
"V4F32",
&[("a", "V4F32"), ("b", "i32")],
Some("sse"),
false,
);
self.add_builtin(
"__builtin_ia32_cvtsi642ss",
"V4F32",
&[("a", "V4F32"), ("b", "i64")],
Some("sse"),
true,
);
self.add_builtin(
"__builtin_ia32_addpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_addsd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_subpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_subsd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_mulpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_mulsd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_divpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_divsd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_sqrtpd",
"V2F64",
&[("a", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_sqrtsd",
"V2F64",
&[("a", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_minpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_minsd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_maxpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_maxsd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_andpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_andnpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_orpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_xorpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse2"),
false,
);
self.add_builtin(
"__builtin_ia32_cmppd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64"), ("imm", "i32")],
Some("sse2"),
false,
);
self.add_builtin_imm("__builtin_ia32_cmppd", 2, 0, 7);
self.add_builtin(
"__builtin_ia32_cmpsd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64"), ("imm", "i32")],
Some("sse2"),
false,
);
self.add_builtin_imm("__builtin_ia32_cmpsd", 2, 0, 7);
self.add_builtin(
"__builtin_ia32_shufpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64"), ("imm", "i32")],
Some("sse2"),
false,
);
self.add_builtin_imm("__builtin_ia32_shufpd", 2, 0, 3);
self.add_builtin(
"__builtin_ia32_haddps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse3"),
false,
);
self.add_builtin(
"__builtin_ia32_haddpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse3"),
false,
);
self.add_builtin(
"__builtin_ia32_hsubps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse3"),
false,
);
self.add_builtin(
"__builtin_ia32_hsubpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse3"),
false,
);
self.add_builtin(
"__builtin_ia32_addsubps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32")],
Some("sse3"),
false,
);
self.add_builtin(
"__builtin_ia32_addsubpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64")],
Some("sse3"),
false,
);
self.add_builtin(
"__builtin_ia32_phaddw",
"V8I16",
&[("a", "V8I16"), ("b", "V8I16")],
Some("ssse3"),
false,
);
self.add_builtin(
"__builtin_ia32_phaddd",
"V4I32",
&[("a", "V4I32"), ("b", "V4I32")],
Some("ssse3"),
false,
);
self.add_builtin(
"__builtin_ia32_phaddsw",
"V8I16",
&[("a", "V8I16"), ("b", "V8I16")],
Some("ssse3"),
false,
);
self.add_builtin(
"__builtin_ia32_pshufb",
"V16I8",
&[("a", "V16I8"), ("b", "V16I8")],
Some("ssse3"),
false,
);
self.add_builtin(
"__builtin_ia32_psignb",
"V16I8",
&[("a", "V16I8"), ("b", "V16I8")],
Some("ssse3"),
false,
);
self.add_builtin(
"__builtin_ia32_psignw",
"V8I16",
&[("a", "V8I16"), ("b", "V8I16")],
Some("ssse3"),
false,
);
self.add_builtin(
"__builtin_ia32_psignd",
"V4I32",
&[("a", "V4I32"), ("b", "V4I32")],
Some("ssse3"),
false,
);
self.add_builtin(
"__builtin_ia32_pabsb",
"V16I8",
&[("a", "V16I8")],
Some("ssse3"),
false,
);
self.add_builtin(
"__builtin_ia32_pabsw",
"V8I16",
&[("a", "V8I16")],
Some("ssse3"),
false,
);
self.add_builtin(
"__builtin_ia32_pabsd",
"V4I32",
&[("a", "V4I32")],
Some("ssse3"),
false,
);
self.add_builtin(
"__builtin_ia32_palignr",
"V16I8",
&[("a", "V16I8"), ("b", "V16I8"), ("imm", "i32")],
Some("ssse3"),
false,
);
self.add_builtin_imm("__builtin_ia32_palignr", 2, 0, 31);
self.add_builtin(
"__builtin_ia32_blendvps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32"), ("mask", "V4F32")],
Some("sse41"),
false,
);
self.add_builtin(
"__builtin_ia32_blendvpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64"), ("mask", "V2F64")],
Some("sse41"),
false,
);
self.add_builtin(
"__builtin_ia32_blendps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32"), ("imm", "i32")],
Some("sse41"),
false,
);
self.add_builtin_imm("__builtin_ia32_blendps", 2, 0, 15);
self.add_builtin(
"__builtin_ia32_blendpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64"), ("imm", "i32")],
Some("sse41"),
false,
);
self.add_builtin_imm("__builtin_ia32_blendpd", 2, 0, 3);
self.add_builtin(
"__builtin_ia32_dpps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32"), ("imm", "i32")],
Some("sse41"),
false,
);
self.add_builtin_imm("__builtin_ia32_dpps", 2, 0, 255);
self.add_builtin(
"__builtin_ia32_dppd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64"), ("imm", "i32")],
Some("sse41"),
false,
);
self.add_builtin_imm("__builtin_ia32_dppd", 2, 0, 255);
self.add_builtin(
"__builtin_ia32_roundps",
"V4F32",
&[("a", "V4F32"), ("imm", "i32")],
Some("sse41"),
false,
);
self.add_builtin_imm("__builtin_ia32_roundps", 1, 0, 15);
self.add_builtin(
"__builtin_ia32_roundpd",
"V2F64",
&[("a", "V2F64"), ("imm", "i32")],
Some("sse41"),
false,
);
self.add_builtin_imm("__builtin_ia32_roundpd", 1, 0, 15);
self.add_builtin(
"__builtin_ia32_crc32qi",
"i32",
&[("a", "i32"), ("b", "i8")],
Some("sse42"),
false,
);
self.add_builtin(
"__builtin_ia32_crc32hi",
"i32",
&[("a", "i32"), ("b", "i16")],
Some("sse42"),
false,
);
self.add_builtin(
"__builtin_ia32_crc32si",
"i32",
&[("a", "i32"), ("b", "i32")],
Some("sse42"),
false,
);
self.add_builtin(
"__builtin_ia32_crc32di",
"i64",
&[("a", "i64"), ("b", "i64")],
Some("sse42"),
true,
);
self.add_builtin(
"__builtin_ia32_aesenc128",
"V2I64",
&[("a", "V2I64"), ("b", "V2I64")],
Some("aes"),
false,
);
self.add_builtin(
"__builtin_ia32_aesenclast128",
"V2I64",
&[("a", "V2I64"), ("b", "V2I64")],
Some("aes"),
false,
);
self.add_builtin(
"__builtin_ia32_aesdec128",
"V2I64",
&[("a", "V2I64"), ("b", "V2I64")],
Some("aes"),
false,
);
self.add_builtin(
"__builtin_ia32_aesdeclast128",
"V2I64",
&[("a", "V2I64"), ("b", "V2I64")],
Some("aes"),
false,
);
self.add_builtin(
"__builtin_ia32_aesimc128",
"V2I64",
&[("a", "V2I64")],
Some("aes"),
false,
);
self.add_builtin(
"__builtin_ia32_aeskeygenassist128",
"V2I64",
&[("a", "V2I64"), ("imm", "i32")],
Some("aes"),
false,
);
self.add_builtin_imm("__builtin_ia32_aeskeygenassist128", 1, 0, 255);
self.add_builtin(
"__builtin_ia32_sha1rnds4",
"V4I32",
&[("a", "V4I32"), ("b", "V4I32"), ("imm", "i32")],
Some("sha"),
false,
);
self.add_builtin_imm("__builtin_ia32_sha1rnds4", 2, 0, 3);
self.add_builtin(
"__builtin_ia32_sha1nexte",
"V4I32",
&[("a", "V4I32"), ("b", "V4I32")],
Some("sha"),
false,
);
self.add_builtin(
"__builtin_ia32_sha1msg1",
"V4I32",
&[("a", "V4I32"), ("b", "V4I32")],
Some("sha"),
false,
);
self.add_builtin(
"__builtin_ia32_sha1msg2",
"V4I32",
&[("a", "V4I32"), ("b", "V4I32")],
Some("sha"),
false,
);
self.add_builtin(
"__builtin_ia32_sha256rnds2",
"V4I32",
&[("a", "V4I32"), ("b", "V4I32"), ("k", "V4I32")],
Some("sha"),
false,
);
self.add_builtin(
"__builtin_ia32_sha256msg1",
"V4I32",
&[("a", "V4I32"), ("b", "V4I32")],
Some("sha"),
false,
);
self.add_builtin(
"__builtin_ia32_sha256msg2",
"V4I32",
&[("a", "V4I32"), ("b", "V4I32")],
Some("sha"),
false,
);
self.add_builtin(
"__builtin_ia32_addps256",
"V8F32",
&[("a", "V8F32"), ("b", "V8F32")],
Some("avx"),
false,
);
self.add_builtin(
"__builtin_ia32_addpd256",
"V4F64",
&[("a", "V4F64"), ("b", "V4F64")],
Some("avx"),
false,
);
self.add_builtin(
"__builtin_ia32_subps256",
"V8F32",
&[("a", "V8F32"), ("b", "V8F32")],
Some("avx"),
false,
);
self.add_builtin(
"__builtin_ia32_subpd256",
"V4F64",
&[("a", "V4F64"), ("b", "V4F64")],
Some("avx"),
false,
);
self.add_builtin(
"__builtin_ia32_mulps256",
"V8F32",
&[("a", "V8F32"), ("b", "V8F32")],
Some("avx"),
false,
);
self.add_builtin(
"__builtin_ia32_mulpd256",
"V4F64",
&[("a", "V4F64"), ("b", "V4F64")],
Some("avx"),
false,
);
self.add_builtin(
"__builtin_ia32_divps256",
"V8F32",
&[("a", "V8F32"), ("b", "V8F32")],
Some("avx"),
false,
);
self.add_builtin(
"__builtin_ia32_divpd256",
"V4F64",
&[("a", "V4F64"), ("b", "V4F64")],
Some("avx"),
false,
);
self.add_builtin(
"__builtin_ia32_sqrtps256",
"V8F32",
&[("a", "V8F32")],
Some("avx"),
false,
);
self.add_builtin(
"__builtin_ia32_sqrtpd256",
"V4F64",
&[("a", "V4F64")],
Some("avx"),
false,
);
self.add_builtin(
"__builtin_ia32_gatherdps",
"V4F32",
&[
("a", "V4F32"),
("base", "ptr"),
("idx", "V4I32"),
("scale", "i32"),
],
Some("avx2"),
false,
);
self.add_builtin_imm("__builtin_ia32_gatherdps", 3, 1, 8);
self.add_builtin(
"__builtin_ia32_gatherdpd",
"V2F64",
&[
("a", "V2F64"),
("base", "ptr"),
("idx", "V4I32"),
("scale", "i32"),
],
Some("avx2"),
false,
);
self.add_builtin_imm("__builtin_ia32_gatherdpd", 3, 1, 8);
self.add_builtin(
"__builtin_ia32_addps512",
"V16F32",
&[("a", "V16F32"), ("b", "V16F32")],
Some("avx512f"),
false,
);
self.add_builtin(
"__builtin_ia32_addpd512",
"V8F64",
&[("a", "V8F64"), ("b", "V8F64")],
Some("avx512f"),
false,
);
self.add_builtin(
"__builtin_ia32_subps512",
"V16F32",
&[("a", "V16F32"), ("b", "V16F32")],
Some("avx512f"),
false,
);
self.add_builtin(
"__builtin_ia32_subpd512",
"V8F64",
&[("a", "V8F64"), ("b", "V8F64")],
Some("avx512f"),
false,
);
self.add_builtin(
"__builtin_ia32_mulps512",
"V16F32",
&[("a", "V16F32"), ("b", "V16F32")],
Some("avx512f"),
false,
);
self.add_builtin(
"__builtin_ia32_mulpd512",
"V8F64",
&[("a", "V8F64"), ("b", "V8F64")],
Some("avx512f"),
false,
);
self.add_builtin(
"__builtin_ia32_divps512",
"V16F32",
&[("a", "V16F32"), ("b", "V16F32")],
Some("avx512f"),
false,
);
self.add_builtin(
"__builtin_ia32_divpd512",
"V8F64",
&[("a", "V8F64"), ("b", "V8F64")],
Some("avx512f"),
false,
);
self.add_builtin(
"__builtin_ia32_vfmaddps",
"V4F32",
&[("a", "V4F32"), ("b", "V4F32"), ("c", "V4F32")],
Some("fma"),
false,
);
self.add_builtin(
"__builtin_ia32_vfmaddpd",
"V2F64",
&[("a", "V2F64"), ("b", "V2F64"), ("c", "V2F64")],
Some("fma"),
false,
);
self.add_builtin(
"__builtin_ia32_vfmaddps256",
"V8F32",
&[("a", "V8F32"), ("b", "V8F32"), ("c", "V8F32")],
Some("fma"),
false,
);
self.add_builtin(
"__builtin_ia32_vfmaddpd256",
"V4F64",
&[("a", "V4F64"), ("b", "V4F64"), ("c", "V4F64")],
Some("fma"),
false,
);
self.add_builtin(
"__builtin_ia32_bextr_u32",
"i32",
&[("a", "i32"), ("b", "i32")],
Some("bmi"),
false,
);
self.add_builtin(
"__builtin_ia32_bextr_u64",
"i64",
&[("a", "i64"), ("b", "i64")],
Some("bmi"),
true,
);
self.add_builtin(
"__builtin_ia32_blsi_u32",
"i32",
&[("a", "i32")],
Some("bmi"),
false,
);
self.add_builtin(
"__builtin_ia32_blsi_u64",
"i64",
&[("a", "i64")],
Some("bmi"),
true,
);
self.add_builtin(
"__builtin_ia32_tzcnt_u32",
"i32",
&[("a", "i32")],
Some("bmi"),
false,
);
self.add_builtin(
"__builtin_ia32_tzcnt_u64",
"i64",
&[("a", "i64")],
Some("bmi"),
true,
);
self.add_builtin(
"__builtin_ia32_bzhi_si",
"i32",
&[("a", "i32"), ("b", "i32")],
Some("bmi2"),
false,
);
self.add_builtin(
"__builtin_ia32_bzhi_di",
"i64",
&[("a", "i64"), ("b", "i64")],
Some("bmi2"),
true,
);
self.add_builtin(
"__builtin_ia32_pdep_si",
"i32",
&[("a", "i32"), ("b", "i32")],
Some("bmi2"),
false,
);
self.add_builtin(
"__builtin_ia32_pdep_di",
"i64",
&[("a", "i64"), ("b", "i64")],
Some("bmi2"),
true,
);
self.add_builtin(
"__builtin_ia32_pext_si",
"i32",
&[("a", "i32"), ("b", "i32")],
Some("bmi2"),
false,
);
self.add_builtin(
"__builtin_ia32_pext_di",
"i64",
&[("a", "i64"), ("b", "i64")],
Some("bmi2"),
true,
);
self.add_builtin(
"__builtin_ia32_rdrand16_step",
"i32",
&[("a", "ptr")],
Some("rdrnd"),
false,
);
self.add_builtin(
"__builtin_ia32_rdrand32_step",
"i32",
&[("a", "ptr")],
Some("rdrnd"),
false,
);
self.add_builtin(
"__builtin_ia32_rdrand64_step",
"i32",
&[("a", "ptr")],
Some("rdrnd"),
true,
);
self.add_builtin(
"__builtin_ia32_rdseed16_step",
"i32",
&[("a", "ptr")],
Some("rdseed"),
false,
);
self.add_builtin(
"__builtin_ia32_rdseed32_step",
"i32",
&[("a", "ptr")],
Some("rdseed"),
false,
);
self.add_builtin(
"__builtin_ia32_rdseed64_step",
"i32",
&[("a", "ptr")],
Some("rdseed"),
true,
);
}
fn add_builtin(
&mut self,
name: &str,
ret: &str,
args: &[(&str, &str)],
feature: Option<&str>,
is_64bit_only: bool,
) {
let sig = X86BuiltinSignature {
name: name.to_string(),
return_type: ret.to_string(),
args: args
.iter()
.enumerate()
.map(|(i, (arg_name, arg_ty))| BuiltinArg {
index: i,
name: arg_name.to_string(),
expected_type: arg_ty.to_string(),
any_vector: arg_ty.starts_with('V'),
is_immediate: false,
imm_min: None,
imm_max: None,
})
.collect(),
required_feature: feature.map(|s| s.to_string()),
is_64bit_only,
};
self.signatures.insert(name.to_string(), sig);
}
fn add_builtin_imm(&mut self, name: &str, arg_index: usize, min: i64, max: i64) {
if let Some(sig) = self.signatures.get_mut(name) {
if let Some(arg) = sig.args.get_mut(arg_index) {
arg.is_immediate = true;
arg.imm_min = Some(min);
arg.imm_max = Some(max);
}
}
}
pub fn check_builtin_call(&self, fd: &FunctionDecl, sema: &mut X86Sema) {
let name = &fd.name;
match self.signatures.get(name) {
Some(sig) => {
self.check_builtin_feature(sig, sema);
self.check_builtin_64bit(sig, sema);
self.check_builtin_arg_count(sig, fd, sema);
self.check_builtin_return_type(sig, fd, sema);
}
None => {
sema.x86_error(format!("unknown X86 builtin: {}", name));
}
}
}
fn check_builtin_feature(&self, sig: &X86BuiltinSignature, sema: &mut X86Sema) {
if let Some(ref feature) = sig.required_feature {
if !self.subtarget_has_feature(feature) {
sema.x86_error(format!(
"builtin '{}' requires '-m{}', which is not enabled for the current target",
sig.name, feature
));
}
}
}
fn check_builtin_64bit(&self, sig: &X86BuiltinSignature, sema: &mut X86Sema) {
if sig.is_64bit_only && !self.subtarget.is_64_bit {
sema.x86_error(format!(
"builtin '{}' is only available in 64-bit mode",
sig.name
));
}
}
fn check_builtin_arg_count(
&self,
sig: &X86BuiltinSignature,
fd: &FunctionDecl,
sema: &mut X86Sema,
) {
let expected = sig.args.len();
let actual = fd.params.len();
if actual != expected {
sema.x86_error(format!(
"builtin '{}' expects {} argument(s), got {}",
sig.name, expected, actual
));
}
}
fn check_builtin_return_type(
&self,
sig: &X86BuiltinSignature,
fd: &FunctionDecl,
sema: &mut X86Sema,
) {
let ret_ty_str = self.qualtype_to_string(&fd.ret_ty);
if ret_ty_str != sig.return_type
&& !self.types_compatible_for_builtin(&ret_ty_str, &sig.return_type)
{
sema.x86_warning(format!(
"builtin '{}' return type '{}' may be incompatible with expected '{}'",
sig.name, ret_ty_str, sig.return_type
));
}
}
fn qualtype_to_string(&self, ty: &QualType) -> String {
match &ty.base {
TypeNode::Void => "void".to_string(),
TypeNode::Char => "i8".to_string(),
TypeNode::SChar => "i8".to_string(),
TypeNode::UChar => "i8".to_string(),
TypeNode::Short => "i16".to_string(),
TypeNode::UShort => "i16".to_string(),
TypeNode::Int => "i32".to_string(),
TypeNode::UInt => "i32".to_string(),
TypeNode::Long => {
if self.subtarget.is_64_bit {
"i64"
} else {
"i32"
}
}
.to_string(),
TypeNode::ULong => {
if self.subtarget.is_64_bit {
"i64"
} else {
"i32"
}
}
.to_string(),
TypeNode::LongLong => "i64".to_string(),
TypeNode::ULongLong => "i64".to_string(),
TypeNode::Float => "f32".to_string(),
TypeNode::Double => "f64".to_string(),
TypeNode::LongDouble => "f80".to_string(),
TypeNode::Bool => "i1".to_string(),
TypeNode::Pointer(_) => "ptr".to_string(),
_ => "unknown".to_string(),
}
}
fn types_compatible_for_builtin(&self, actual: &str, expected: &str) -> bool {
if expected == "ptr" {
return actual == "ptr" || actual == "void*";
}
if actual == "ptr" && expected != "ptr" {
return false;
}
actual == expected
}
fn subtarget_has_feature(&self, feature: &str) -> bool {
match feature {
"sse" => self.subtarget.has_sse,
"sse2" => self.subtarget.has_sse2,
"sse3" => self.subtarget.has_sse3,
"ssse3" => self.subtarget.has_ssse3,
"sse41" => self.subtarget.has_sse41,
"sse42" => self.subtarget.has_sse42,
"avx" => self.subtarget.has_avx,
"avx2" => self.subtarget.has_avx2,
"avx512f" => self.subtarget.has_avx512f,
"fma" => self.subtarget.has_fma,
"bmi" => self.subtarget.has_bmi,
"bmi2" => self.subtarget.has_bmi2,
"aes" => self.subtarget.has_aes,
"sha" => self.subtarget.has_sha,
"rdrnd" => self.subtarget.has_rdrnd,
"rdseed" => self.subtarget.has_rdseed,
"lzcnt" => self.subtarget.has_lzcnt,
"popcnt" => self.subtarget.has_popcnt,
"mmx" => self.subtarget.has_mmx,
"x87" => self.subtarget.has_x87,
_ => false,
}
}
pub fn get_signature(&self, name: &str) -> Option<&X86BuiltinSignature> {
self.signatures.get(name)
}
pub fn num_builtins(&self) -> usize {
self.signatures.len()
}
pub fn builtin_names(&self) -> Vec<&String> {
let mut names: Vec<&String> = self.signatures.keys().collect();
names.sort();
names
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CCValidity {
Valid,
Requires32Bit,
Requires64Bit,
Unknown,
}
#[derive(Debug, Clone)]
pub struct X86ABIResult {
pub compatible: bool,
pub issues: Vec<String>,
}
impl X86ABIResult {
pub fn valid() -> Self {
Self {
compatible: true,
issues: Vec::new(),
}
}
pub fn issue(msg: String) -> Self {
Self {
compatible: false,
issues: vec![msg],
}
}
pub fn add_issue(&mut self, msg: String) {
self.compatible = false;
self.issues.push(msg);
}
}
#[derive(Debug, Clone)]
pub struct X86ABIChecker {
subtarget: X86Subtarget,
known_ccs: HashSet<String>,
}
impl X86ABIChecker {
pub fn new(subtarget: X86Subtarget) -> Self {
let mut known_ccs = HashSet::new();
for cc in &[
"cdecl",
"stdcall",
"fastcall",
"thiscall",
"vectorcall",
"regcall",
"sysv_abi",
"ms_abi",
"pascal",
"preserve_all",
"preserve_most",
] {
known_ccs.insert(cc.to_string());
}
Self {
subtarget,
known_ccs,
}
}
pub fn check_calling_convention(&self, cc_name: &str, fd: &FunctionDecl, sema: &mut X86Sema) {
match self.validate_cc(cc_name) {
CCValidity::Valid => {
self.check_struct_return(fd, cc_name, sema);
}
CCValidity::Requires32Bit => {
sema.x86_error(format!(
"calling convention '{}' is only available in 32-bit mode",
cc_name
));
}
CCValidity::Requires64Bit => {
sema.x86_error(format!(
"calling convention '{}' is only available in 64-bit mode",
cc_name
));
}
CCValidity::Unknown => {
sema.x86_warning(format!("unknown calling convention '{}'", cc_name));
}
}
}
pub fn validate_cc(&self, cc_name: &str) -> CCValidity {
let cc = cc_name.to_lowercase();
match cc.as_str() {
"stdcall" | "fastcall" | "thiscall" | "pascal" => {
if self.subtarget.is_64_bit {
CCValidity::Requires32Bit
} else {
CCValidity::Valid
}
}
"vectorcall" | "sysv_abi" | "ms_abi" => {
if !self.subtarget.is_64_bit {
CCValidity::Requires64Bit
} else {
CCValidity::Valid
}
}
"cdecl" | "regcall" | "preserve_all" | "preserve_most" => CCValidity::Valid,
_ => CCValidity::Unknown,
}
}
pub fn check_struct_return(&self, fd: &FunctionDecl, cc_name: &str, sema: &mut X86Sema) {
if let TypeNode::Record { .. } = &fd.ret_ty.base {
let needs_sret = self.struct_needs_sret(&fd.ret_ty, cc_name);
if needs_sret {
let _ = sema; }
}
}
pub fn struct_needs_sret(&self, ty: &QualType, cc_name: &str) -> bool {
let size = self.estimate_type_size(ty);
let cc = cc_name.to_lowercase();
match cc.as_str() {
"sysv_abi" | "sysv" => {
size > 16
}
"ms_abi" | "win64" => {
size > 8 || !size.is_power_of_two()
}
_ => {
size > 0
}
}
}
fn estimate_type_size(&self, ty: &QualType) -> u32 {
ty.base.size_bytes()
}
pub fn check_sysv_vs_ms(&self, sema: &mut X86Sema) -> X86ABIResult {
if !self.subtarget.is_64_bit {
return X86ABIResult::valid();
}
X86ABIResult::valid()
}
pub fn known_conventions(&self) -> &HashSet<String> {
&self.known_ccs
}
}
#[derive(Debug, Clone)]
pub struct X86AlignmentChecker {
subtarget: X86Subtarget,
pub stack_alignment: u32,
pub max_alignment: u32,
pub vector128_alignment: u32,
pub vector256_alignment: u32,
pub vector512_alignment: u32,
}
impl X86AlignmentChecker {
pub fn new(subtarget: X86Subtarget) -> Self {
let stack_alignment = subtarget.get_stack_alignment();
let max_alignment = if subtarget.has_avx512f {
64
} else if subtarget.has_avx {
32
} else if subtarget.has_sse {
16
} else {
8
};
Self {
subtarget,
stack_alignment,
max_alignment,
vector128_alignment: 16,
vector256_alignment: 32,
vector512_alignment: 64,
}
}
pub fn check_global_alignment(&self, vd: &VarDecl, sema: &mut X86Sema) {
let ty_size = vd.ty.base.size_bytes();
if ty_size >= 16 && ty_size <= self.max_alignment {
}
}
pub fn check_aligned_attribute(&self, alignment: u32) -> Result<u32, String> {
if alignment == 0 {
return Err("requested alignment is not a positive power of 2".to_string());
}
if !alignment.is_power_of_two() {
return Err(format!(
"requested alignment {} is not a power of 2",
alignment
));
}
if alignment > self.max_alignment {
return Err(format!(
"requested alignment {} exceeds maximum {} for this target",
alignment, self.max_alignment
));
}
Ok(alignment)
}
pub fn check_stack_alignment(&self, requested: u32) -> Result<u32, String> {
if requested < self.stack_alignment {
return Err(format!(
"stack alignment {} is less than required minimum {}",
requested, self.stack_alignment
));
}
if !requested.is_power_of_two() {
return Err("stack alignment must be a power of 2".to_string());
}
Ok(requested)
}
pub fn preferred_alignment(&self, type_size: u32) -> u32 {
if type_size >= 64 {
64
} else if type_size >= 32 {
32
} else if type_size >= 16 {
16
} else if type_size >= 8 {
8
} else if type_size >= 4 {
4
} else if type_size >= 2 {
2
} else {
1
}
}
pub fn abi_alignment(&self, type_size: u32) -> u32 {
let preferred = self.preferred_alignment(type_size);
if type_size < preferred {
type_size
} else {
preferred
}
}
}
#[derive(Debug, Clone)]
pub struct IntrinsicCheckResult {
pub valid: bool,
pub errors: Vec<String>,
pub warnings: Vec<String>,
pub group: Option<String>,
}
impl IntrinsicCheckResult {
pub fn valid() -> Self {
Self {
valid: true,
errors: Vec::new(),
warnings: Vec::new(),
group: None,
}
}
pub fn error(msg: String) -> Self {
Self {
valid: false,
errors: vec![msg],
warnings: Vec::new(),
group: None,
}
}
}
#[derive(Debug, Clone)]
pub struct X86IntrinsicSema {
subtarget: X86Subtarget,
intrinsic_groups: HashMap<String, String>,
}
impl X86IntrinsicSema {
pub fn new(subtarget: X86Subtarget) -> Self {
let mut sema = Self {
subtarget,
intrinsic_groups: HashMap::new(),
};
sema.register_intrinsic_groups();
sema
}
fn register_intrinsic_groups(&mut self) {
let sse_list = vec![
"_mm_add_ps",
"_mm_add_ss",
"_mm_sub_ps",
"_mm_sub_ss",
"_mm_mul_ps",
"_mm_mul_ss",
"_mm_div_ps",
"_mm_div_ss",
"_mm_sqrt_ps",
"_mm_sqrt_ss",
"_mm_rcp_ps",
"_mm_rcp_ss",
"_mm_rsqrt_ps",
"_mm_rsqrt_ss",
"_mm_min_ps",
"_mm_min_ss",
"_mm_max_ps",
"_mm_max_ss",
"_mm_and_ps",
"_mm_andnot_ps",
"_mm_or_ps",
"_mm_xor_ps",
"_mm_cmpeq_ps",
"_mm_cmplt_ps",
"_mm_cmple_ps",
"_mm_cmpgt_ps",
"_mm_cmpge_ps",
"_mm_cmpneq_ps",
"_mm_cmpnlt_ps",
"_mm_cmpnle_ps",
"_mm_cmpngt_ps",
"_mm_cmpnge_ps",
"_mm_cmpord_ps",
"_mm_cmpunord_ps",
"_mm_comieq_ss",
"_mm_comilt_ss",
"_mm_comile_ss",
"_mm_comigt_ss",
"_mm_comige_ss",
"_mm_comineq_ss",
"_mm_ucomieq_ss",
"_mm_ucomilt_ss",
"_mm_ucomile_ss",
"_mm_ucomigt_ss",
"_mm_ucomige_ss",
"_mm_ucomineq_ss",
"_mm_cvtss_si32",
"_mm_cvtss_si64",
"_mm_cvttss_si32",
"_mm_cvttss_si64",
"_mm_cvtsi32_ss",
"_mm_cvtsi64_ss",
"_mm_shuffle_ps",
"_mm_unpackhi_ps",
"_mm_unpacklo_ps",
"_mm_movehl_ps",
"_mm_movelh_ps",
"_mm_movemask_ps",
"_mm_load_ps",
"_mm_loadu_ps",
"_mm_load_ss",
"_mm_load1_ps",
"_mm_loadr_ps",
"_mm_store_ps",
"_mm_storeu_ps",
"_mm_store_ss",
"_mm_store1_ps",
"_mm_storer_ps",
"_mm_prefetch",
"_mm_sfence",
"_mm_movnt_ps",
"_mm_stream_si32",
"_mm_extract_pi16",
"_mm_insert_pi16",
"_mm_max_pi16",
"_mm_max_pu8",
"_mm_min_pi16",
"_mm_min_pu8",
"_mm_movemask_pi8",
"_mm_mulhi_pu16",
"_mm_sad_pu8",
"_mm_shuffle_pi16",
"_mm_avg_pu8",
"_mm_avg_pu16",
];
for name in sse_list {
self.intrinsic_groups
.insert(name.to_string(), "sse".to_string());
}
let sse2_list = vec![
"_mm_add_pd",
"_mm_add_sd",
"_mm_sub_pd",
"_mm_sub_sd",
"_mm_mul_pd",
"_mm_mul_sd",
"_mm_div_pd",
"_mm_div_sd",
"_mm_sqrt_pd",
"_mm_sqrt_sd",
"_mm_min_pd",
"_mm_min_sd",
"_mm_max_pd",
"_mm_max_sd",
"_mm_and_pd",
"_mm_andnot_pd",
"_mm_or_pd",
"_mm_xor_pd",
"_mm_cmpeq_pd",
"_mm_cmplt_pd",
"_mm_cmple_pd",
"_mm_cmpgt_pd",
"_mm_cmpge_pd",
"_mm_cmpneq_pd",
"_mm_cmpnlt_pd",
"_mm_cmpnle_pd",
"_mm_cmpngt_pd",
"_mm_cmpnge_pd",
"_mm_cmpord_pd",
"_mm_cmpunord_pd",
"_mm_comieq_sd",
"_mm_comilt_sd",
"_mm_comile_sd",
"_mm_comigt_sd",
"_mm_comige_sd",
"_mm_comineq_sd",
"_mm_ucomieq_sd",
"_mm_ucomilt_sd",
"_mm_ucomile_sd",
"_mm_ucomigt_sd",
"_mm_ucomige_sd",
"_mm_ucomineq_sd",
"_mm_load_pd",
"_mm_loadu_pd",
"_mm_load_sd",
"_mm_load1_pd",
"_mm_loadr_pd",
"_mm_store_pd",
"_mm_storeu_pd",
"_mm_store_sd",
"_mm_store1_pd",
"_mm_storer_pd",
"_mm_shuffle_pd",
"_mm_unpackhi_pd",
"_mm_unpacklo_pd",
"_mm_movemask_pd",
"_mm_movnt_pd",
"_mm_clflush",
"_mm_lfence",
"_mm_mfence",
"_mm_pause",
];
for name in sse2_list {
self.intrinsic_groups
.insert(name.to_string(), "sse2".to_string());
}
let sse3_list = vec![
"_mm_hadd_ps",
"_mm_hadd_pd",
"_mm_hsub_ps",
"_mm_hsub_pd",
"_mm_addsub_ps",
"_mm_addsub_pd",
"_mm_movehdup_ps",
"_mm_moveldup_ps",
"_mm_lddqu_si128",
"_mm_monitor",
"_mm_mwait",
];
for name in sse3_list {
self.intrinsic_groups
.insert(name.to_string(), "sse3".to_string());
}
let ssse3_list = vec![
"_mm_hadd_epi16",
"_mm_hadd_epi32",
"_mm_hadds_epi16",
"_mm_hsub_epi16",
"_mm_hsub_epi32",
"_mm_hsubs_epi16",
"_mm_maddubs_epi16",
"_mm_mulhrs_epi16",
"_mm_shuffle_epi8",
"_mm_sign_epi8",
"_mm_sign_epi16",
"_mm_sign_epi32",
"_mm_abs_epi8",
"_mm_abs_epi16",
"_mm_abs_epi32",
"_mm_alignr_epi8",
];
for name in ssse3_list {
self.intrinsic_groups
.insert(name.to_string(), "ssse3".to_string());
}
let sse41_list = vec![
"_mm_blendv_ps",
"_mm_blendv_pd",
"_mm_blend_ps",
"_mm_blend_pd",
"_mm_dp_ps",
"_mm_dp_pd",
"_mm_round_ps",
"_mm_round_pd",
"_mm_round_ss",
"_mm_round_sd",
"_mm_extract_ps",
"_mm_insert_ps",
"_mm_mpsadbw_epu8",
"_mm_packus_epi32",
"_mm_cmpeq_epi64",
"_mm_testz_si128",
"_mm_testc_si128",
"_mm_testnzc_si128",
"_mm_movntdqa",
"_mm_min_epi8",
"_mm_min_epi32",
"_mm_min_epu16",
"_mm_min_epu32",
"_mm_max_epi8",
"_mm_max_epi32",
"_mm_max_epu16",
"_mm_max_epu32",
"_mm_mullo_epi32",
"_mm_mul_epi32",
];
for name in sse41_list {
self.intrinsic_groups
.insert(name.to_string(), "sse41".to_string());
}
let sse42_list = vec![
"_mm_crc32_u8",
"_mm_crc32_u16",
"_mm_crc32_u32",
"_mm_crc32_u64",
"_mm_cmpistri",
"_mm_cmpistrm",
];
for name in sse42_list {
self.intrinsic_groups
.insert(name.to_string(), "sse42".to_string());
}
let aes_list = vec![
"_mm_aesenc_si128",
"_mm_aesenclast_si128",
"_mm_aesdec_si128",
"_mm_aesdeclast_si128",
"_mm_aesimc_si128",
"_mm_aeskeygenassist_si128",
];
for name in aes_list {
self.intrinsic_groups
.insert(name.to_string(), "aes".to_string());
}
let avx_list = vec![
"_mm256_add_ps",
"_mm256_add_pd",
"_mm256_sub_ps",
"_mm256_sub_pd",
"_mm256_mul_ps",
"_mm256_mul_pd",
"_mm256_div_ps",
"_mm256_div_pd",
"_mm256_sqrt_ps",
"_mm256_sqrt_pd",
"_mm256_rcp_ps",
"_mm256_rsqrt_ps",
"_mm256_min_ps",
"_mm256_min_pd",
"_mm256_max_ps",
"_mm256_max_pd",
"_mm256_and_ps",
"_mm256_and_pd",
"_mm256_andnot_ps",
"_mm256_andnot_pd",
"_mm256_or_ps",
"_mm256_or_pd",
"_mm256_xor_ps",
"_mm256_xor_pd",
"_mm256_cmp_ps",
"_mm256_cmp_pd",
"_mm256_load_ps",
"_mm256_loadu_ps",
"_mm256_load_pd",
"_mm256_loadu_pd",
"_mm256_store_ps",
"_mm256_storeu_ps",
"_mm256_store_pd",
"_mm256_storeu_pd",
"_mm256_shuffle_ps",
"_mm256_shuffle_pd",
"_mm256_unpackhi_ps",
"_mm256_unpacklo_ps",
"_mm256_unpackhi_pd",
"_mm256_unpacklo_pd",
"_mm256_blendv_ps",
"_mm256_blendv_pd",
"_mm256_blend_ps",
"_mm256_blend_pd",
"_mm256_dp_ps",
"_mm256_round_ps",
"_mm256_round_pd",
"_mm256_hadd_ps",
"_mm256_hadd_pd",
"_mm256_hsub_ps",
"_mm256_hsub_pd",
"_mm256_addsub_ps",
"_mm256_addsub_pd",
"_mm256_broadcast_ss",
"_mm256_broadcast_sd",
"_mm256_broadcast_ps",
"_mm256_broadcast_pd",
"_mm256_permutevar_ps",
"_mm256_permutevar_pd",
"_mm256_permute_ps",
"_mm256_permute_pd",
"_mm256_permute2f128_ps",
"_mm256_permute2f128_pd",
"_mm256_stream_ps",
"_mm256_stream_pd",
"_mm256_zeroall",
"_mm256_zeroupper",
];
for name in avx_list {
self.intrinsic_groups
.insert(name.to_string(), "avx".to_string());
}
let avx2_list = vec![
"_mm256_add_epi8",
"_mm256_add_epi16",
"_mm256_add_epi32",
"_mm256_add_epi64",
"_mm256_sub_epi8",
"_mm256_sub_epi16",
"_mm256_sub_epi32",
"_mm256_sub_epi64",
"_mm256_mullo_epi16",
"_mm256_mulhi_epi16",
"_mm256_madd_epi16",
"_mm256_mullo_epi32",
"_mm256_mul_epi32",
"_mm256_and_si256",
"_mm256_andnot_si256",
"_mm256_or_si256",
"_mm256_xor_si256",
"_mm256_sllv_epi32",
"_mm256_sllv_epi64",
"_mm256_srlv_epi32",
"_mm256_srlv_epi64",
"_mm256_srav_epi32",
"_mm256_i32gather_ps",
"_mm256_i64gather_ps",
"_mm256_i32gather_pd",
"_mm256_i64gather_pd",
"_mm256_i32gather_epi32",
"_mm256_i64gather_epi32",
"_mm256_i32gather_epi64",
"_mm256_i64gather_epi64",
"_mm256_permutevar8x32_ps",
"_mm256_permutevar8x32_epi32",
"_mm256_permute4x64_pd",
"_mm256_permute4x64_epi64",
"_mm256_permute2x128_si256",
];
for name in avx2_list {
self.intrinsic_groups
.insert(name.to_string(), "avx2".to_string());
}
let fma_list = vec![
"_mm_fmadd_ps",
"_mm_fmadd_pd",
"_mm_fmadd_ss",
"_mm_fmadd_sd",
"_mm_fmsub_ps",
"_mm_fmsub_pd",
"_mm_fmsub_ss",
"_mm_fmsub_sd",
"_mm_fnmadd_ps",
"_mm_fnmadd_pd",
"_mm_fnmadd_ss",
"_mm_fnmadd_sd",
"_mm_fnmsub_ps",
"_mm_fnmsub_pd",
"_mm_fnmsub_ss",
"_mm_fnmsub_sd",
"_mm_fmaddsub_ps",
"_mm_fmaddsub_pd",
"_mm_fmsubadd_ps",
"_mm_fmsubadd_pd",
"_mm256_fmadd_ps",
"_mm256_fmadd_pd",
"_mm256_fmsub_ps",
"_mm256_fmsub_pd",
"_mm256_fnmadd_ps",
"_mm256_fnmadd_pd",
"_mm256_fnmsub_ps",
"_mm256_fnmsub_pd",
"_mm256_fmaddsub_ps",
"_mm256_fmaddsub_pd",
"_mm256_fmsubadd_ps",
"_mm256_fmsubadd_pd",
];
for name in fma_list {
self.intrinsic_groups
.insert(name.to_string(), "fma".to_string());
}
let avx512_list = vec![
"_mm512_add_ps",
"_mm512_add_pd",
"_mm512_sub_ps",
"_mm512_sub_pd",
"_mm512_mul_ps",
"_mm512_mul_pd",
"_mm512_div_ps",
"_mm512_div_pd",
"_mm512_sqrt_ps",
"_mm512_sqrt_pd",
"_mm512_min_ps",
"_mm512_min_pd",
"_mm512_max_ps",
"_mm512_max_pd",
"_mm512_and_ps",
"_mm512_and_pd",
"_mm512_andnot_ps",
"_mm512_andnot_pd",
"_mm512_or_ps",
"_mm512_or_pd",
"_mm512_xor_ps",
"_mm512_xor_pd",
"_mm512_load_ps",
"_mm512_load_pd",
"_mm512_store_ps",
"_mm512_store_pd",
"_mm512_shuffle_ps",
"_mm512_shuffle_pd",
"_mm512_broadcastss_ps",
"_mm512_broadcastsd_pd",
"_mm512_permutevar_ps",
"_mm512_permutevar_pd",
];
for name in avx512_list {
self.intrinsic_groups
.insert(name.to_string(), "avx512f".to_string());
}
}
pub fn check_intrinsic(&self, fd: &FunctionDecl, sema: &mut X86Sema) {
let name = &fd.name;
let group = self.lookup_group(name);
match group {
Some(group_name) => {
if !self.subtarget_has_feature_for_group(&group_name) {
sema.x86_error(format!(
"intrinsic '{}' requires '-m{}' which is not enabled",
name, group_name
));
}
self.check_immediate_operands(fd, name, sema);
if group_name == "avx512f" || group_name.starts_with("avx512") {
self.check_mask_types(fd, sema);
}
}
None => {
sema.x86_warning(format!(
"unknown X86 intrinsic '{}'; may not be supported on this target",
name
));
}
}
}
pub fn lookup_group(&self, name: &str) -> Option<&String> {
self.intrinsic_groups.get(name)
}
fn subtarget_has_feature_for_group(&self, group: &str) -> bool {
match group {
"sse" => self.subtarget.has_sse,
"sse2" => self.subtarget.has_sse2,
"sse3" => self.subtarget.has_sse3,
"ssse3" => self.subtarget.has_ssse3,
"sse41" => self.subtarget.has_sse41,
"sse42" => self.subtarget.has_sse42,
"avx" => self.subtarget.has_avx,
"avx2" => self.subtarget.has_avx2,
"avx512f" => self.subtarget.has_avx512f,
"fma" => self.subtarget.has_fma,
"aes" => self.subtarget.has_aes,
_ => false,
}
}
fn check_immediate_operands(&self, fd: &FunctionDecl, _name: &str, _sema: &mut X86Sema) {
}
fn check_mask_types(&self, fd: &FunctionDecl, sema: &mut X86Sema) {
for param in &fd.params {
let size = param.ty.base.size_bytes();
if size <= 8 && param.ty.base.is_integer() {
if size <= 2 && !self.subtarget.has_avx512bw {
sema.x86_warning("byte/word mask operations require -mavx512bw".to_string());
}
}
}
}
pub fn intrinsic_names(&self) -> Vec<&String> {
let mut names: Vec<&String> = self.intrinsic_groups.keys().collect();
names.sort();
names
}
pub fn num_intrinsics(&self) -> usize {
self.intrinsic_groups.len()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86TargetAttrKey {
Arch,
Cpu,
Tune,
FeatureEnable,
FeatureDisable,
Unknown,
}
#[derive(Debug, Clone)]
pub struct X86TargetAttr {
pub key: String,
pub value: String,
pub kind: X86TargetAttrKey,
pub recognized: bool,
}
#[derive(Debug, Clone)]
pub struct X86TargetAttrChecker {
known_cpus: HashSet<String>,
known_features: HashSet<String>,
known_archs: HashSet<String>,
}
impl X86TargetAttrChecker {
pub fn new(_subtarget: X86Subtarget) -> Self {
let mut checker = Self {
known_cpus: HashSet::new(),
known_features: HashSet::new(),
known_archs: HashSet::new(),
};
checker.register_known_values();
checker
}
fn register_known_values(&mut self) {
for cpu in &[
"generic",
"i386",
"i486",
"i586",
"i686",
"pentium",
"pentium2",
"pentium3",
"pentium4",
"pentium-m",
"core2",
"nehalem",
"westmere",
"sandybridge",
"ivybridge",
"haswell",
"broadwell",
"skylake",
"skylake-avx512",
"cannonlake",
"icelake-client",
"icelake-server",
"cascadelake",
"cooperlake",
"tigerlake",
"rocketlake",
"alderlake",
"raptorlake",
"meteorlake",
"arrowlake",
"lunarlake",
"sapphirerapids",
"emeraldrapids",
"graniterapids",
"knl",
"knm",
"silvermont",
"goldmont",
"goldmont-plus",
"tremont",
"gracemont",
"k8",
"k8-sse3",
"amdfam10",
"barcelona",
"bdver1",
"bdver2",
"bdver3",
"bdver4",
"btver1",
"btver2",
"znver1",
"znver2",
"znver3",
"znver4",
"znver5",
] {
self.known_cpus.insert(cpu.to_string());
}
for feat in &[
"sse",
"sse2",
"sse3",
"ssse3",
"sse4.1",
"sse4.2",
"sse4a",
"avx",
"avx2",
"avx512f",
"avx512cd",
"avx512er",
"avx512pf",
"avx512bw",
"avx512dq",
"avx512vl",
"avx512ifma",
"avx512vbmi",
"avx512vbmi2",
"avx512vnni",
"avx512bitalg",
"avx512vpopcntdq",
"avx512bf16",
"avx512fp16",
"avx512vp2intersect",
"fma",
"fma4",
"f16c",
"mmx",
"3dnow",
"3dnowa",
"xop",
"bmi",
"bmi2",
"lzcnt",
"popcnt",
"aes",
"pclmul",
"sha",
"rdrnd",
"rdseed",
"fsgsbase",
"rtm",
"hle",
"adx",
"xsave",
"xsaveopt",
"xsavec",
"xsaves",
"clflushopt",
"clwb",
"pku",
"sgx",
"cet",
"movbe",
"mwaitx",
"clzero",
"prfchw",
"vaes",
"vpclmulqdq",
"gfni",
"avx-vnni",
"avx-ifma",
"cmpccxadd",
"amx-bf16",
"amx-int8",
"amx-tile",
"amx-fp16",
"serialize",
"tsxldtrk",
"uintr",
"hreset",
"kl",
"widekl",
] {
self.known_features.insert(feat.to_string());
}
for arch in &[
"x86-64",
"x86-64-v2",
"x86-64-v3",
"x86-64-v4",
"i386",
"i486",
"i586",
"i686",
] {
self.known_archs.insert(arch.to_string());
}
}
pub fn parse_target_attr(&self, attr_str: &str) -> Vec<X86TargetAttr> {
let mut result = Vec::new();
for part in attr_str.split(',') {
let part = part.trim();
if part.is_empty() {
continue;
}
let attr = self.parse_single_attr(part);
result.push(attr);
}
result
}
fn parse_single_attr(&self, part: &str) -> X86TargetAttr {
if let Some(rest) = part.strip_prefix("arch=") {
let recognized = self.known_archs.contains(rest);
X86TargetAttr {
key: "arch".to_string(),
value: rest.to_string(),
kind: X86TargetAttrKey::Arch,
recognized,
}
} else if let Some(rest) = part.strip_prefix("cpu=") {
let recognized = self.known_cpus.contains(rest);
X86TargetAttr {
key: "cpu".to_string(),
value: rest.to_string(),
kind: X86TargetAttrKey::Cpu,
recognized,
}
} else if let Some(rest) = part.strip_prefix("tune=") {
let recognized = self.known_cpus.contains(rest);
X86TargetAttr {
key: "tune".to_string(),
value: rest.to_string(),
kind: X86TargetAttrKey::Tune,
recognized,
}
} else if let Some(rest) = part.strip_prefix('+') {
let recognized = self.known_features.contains(rest);
X86TargetAttr {
key: format!("+{}", rest),
value: rest.to_string(),
kind: X86TargetAttrKey::FeatureEnable,
recognized,
}
} else if let Some(rest) = part.strip_prefix('-') {
let recognized = self.known_features.contains(rest);
X86TargetAttr {
key: format!("-{}", rest),
value: rest.to_string(),
kind: X86TargetAttrKey::FeatureDisable,
recognized,
}
} else {
X86TargetAttr {
key: part.to_string(),
value: String::new(),
kind: X86TargetAttrKey::Unknown,
recognized: false,
}
}
}
pub fn validate_target_attr(&self, attr_str: &str) -> Vec<String> {
let mut warnings = Vec::new();
let attrs = self.parse_target_attr(attr_str);
for attr in attrs {
if !attr.recognized {
match attr.kind {
X86TargetAttrKey::Unknown => {
warnings.push(format!("unknown target attribute '{}'", attr.key));
}
X86TargetAttrKey::Arch => {
warnings.push(format!(
"unknown architecture '{}' in target attribute",
attr.value
));
}
X86TargetAttrKey::Cpu => {
warnings.push(format!("unknown CPU '{}' in target attribute", attr.value));
}
X86TargetAttrKey::Tune => {
warnings.push(format!(
"unknown tune CPU '{}' in target attribute",
attr.value
));
}
X86TargetAttrKey::FeatureEnable | X86TargetAttrKey::FeatureDisable => {
warnings.push(format!(
"unknown feature '{}' in target attribute",
attr.value
));
}
}
}
}
warnings
}
pub fn is_known_cpu(&self, cpu: &str) -> bool {
self.known_cpus.contains(cpu)
}
pub fn is_known_feature(&self, feature: &str) -> bool {
self.known_features.contains(feature)
}
pub fn is_known_arch(&self, arch: &str) -> bool {
self.known_archs.contains(arch)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FormatStyle {
Printf,
Scanf,
Strftime,
Strfmon,
NSLog,
OSLog,
Unknown,
}
impl FormatStyle {
pub fn from_str(s: &str) -> Self {
match s {
"printf" | "__printf__" => FormatStyle::Printf,
"scanf" | "__scanf__" => FormatStyle::Scanf,
"strftime" | "__strftime__" => FormatStyle::Strftime,
"strfmon" | "__strfmon__" => FormatStyle::Strfmon,
"NSLog" => FormatStyle::NSLog,
"OSLog" => FormatStyle::OSLog,
_ => FormatStyle::Unknown,
}
}
pub fn is_valid_on_x86(&self) -> bool {
match self {
FormatStyle::Printf | FormatStyle::Scanf | FormatStyle::Strftime => true,
FormatStyle::Strfmon => true,
FormatStyle::NSLog | FormatStyle::OSLog => false,
FormatStyle::Unknown => false,
}
}
pub fn as_str(&self) -> &'static str {
match self {
FormatStyle::Printf => "printf",
FormatStyle::Scanf => "scanf",
FormatStyle::Strftime => "strftime",
FormatStyle::Strfmon => "strfmon",
FormatStyle::NSLog => "NSLog",
FormatStyle::OSLog => "OSLog",
FormatStyle::Unknown => "unknown",
}
}
}
#[derive(Debug, Clone)]
pub struct X86FormatAttrChecker {
pub is_64bit: bool,
pub size_t_size: u32,
pub ptrdiff_t_size: u32,
pub wchar_t_size: u32,
pub long_size: u32,
pub warn_on_percent_n: bool,
}
impl X86FormatAttrChecker {
pub fn new() -> Self {
Self {
is_64bit: true,
size_t_size: 8,
ptrdiff_t_size: 8,
wchar_t_size: 4,
long_size: 8,
warn_on_percent_n: true,
}
}
pub fn new_32bit() -> Self {
Self {
is_64bit: false,
size_t_size: 4,
ptrdiff_t_size: 4,
wchar_t_size: 4,
long_size: 4,
warn_on_percent_n: true,
}
}
pub fn new_64bit() -> Self {
Self::new()
}
pub fn check_format_specifier(&self, specifier: char, modifier: &str) -> Result<(), String> {
match specifier {
'd' | 'i' | 'o' | 'u' | 'x' | 'X' => {
self.check_int_length_modifier(modifier)
}
'f' | 'F' | 'e' | 'E' | 'g' | 'G' | 'a' | 'A' => {
self.check_float_length_modifier(modifier)
}
'c' => {
if modifier == "l" && self.wchar_t_size != 4 {
Err("%lc expects 4-byte wchar_t on X86 targets".to_string())
} else {
Ok(())
}
}
's' => {
if modifier == "l" && self.wchar_t_size != 4 {
Err("%ls expects 4-byte wchar_t on X86 targets".to_string())
} else {
Ok(())
}
}
'p' => {
Ok(())
}
'n' => {
if self.warn_on_percent_n {
Err(
"%n is potentially unsafe and may be disabled by -Wformat-security"
.to_string(),
)
} else {
Ok(())
}
}
'z' => {
Err("incomplete '%%z' format specifier; use '%%zu' for size_t".to_string())
}
'%' => Ok(()),
_ => Err(format!("unknown format specifier '%{}'", specifier)),
}
}
fn check_int_length_modifier(&self, modifier: &str) -> Result<(), String> {
match modifier {
"" => Ok(()), "hh" => Ok(()), "h" => Ok(()), "l" => {
if self.is_64bit && self.long_size == 8 {
Ok(()) } else {
Ok(()) }
}
"ll" => Ok(()), "z" => Ok(()), "t" => Ok(()), "j" => Ok(()), _ => Err(format!("unknown integer length modifier '{}'", modifier)),
}
}
fn check_float_length_modifier(&self, modifier: &str) -> Result<(), String> {
match modifier {
"" => Ok(()), "L" => Ok(()), "l" => {
Ok(())
}
_ => Err(format!(
"unknown floating-point length modifier '{}'",
modifier
)),
}
}
pub fn format_type_size(&self, specifier: char, modifier: &str) -> u32 {
match specifier {
'd' | 'i' | 'o' | 'u' | 'x' | 'X' => match modifier {
"hh" => 1,
"h" => 2,
"" => 4,
"l" => self.long_size,
"ll" => 8,
"z" => self.size_t_size,
"t" => self.ptrdiff_t_size,
"j" => 8, _ => 4,
},
'f' | 'F' | 'e' | 'E' | 'g' | 'G' | 'a' | 'A' => match modifier {
"" | "l" => 8, "L" => {
if self.is_64bit {
16
} else {
12
}
}
_ => 8,
},
'c' => match modifier {
"" => 4, "l" => self.wchar_t_size,
_ => 4,
},
's' => self.size_t_size, 'p' => {
if self.is_64bit {
8
} else {
4
}
}
_ => 4,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CCCompat {
Compatible,
Incompatible,
ForwardCompatible,
NeedsThunk,
}
#[derive(Debug, Clone)]
pub struct CCCompatCell {
pub caller: String,
pub callee: String,
pub compat: CCCompat,
pub explanation: String,
}
#[derive(Debug, Clone)]
pub struct X86CallingConvChecker {
is_64bit: bool,
matrix: HashMap<(String, String), CCCompat>,
}
impl X86CallingConvChecker {
pub fn new(subtarget: X86Subtarget) -> Self {
let is_64bit = subtarget.is_64_bit;
let mut checker = Self {
is_64bit,
matrix: HashMap::new(),
};
checker.build_matrix();
checker
}
fn build_matrix(&mut self) {
if self.is_64bit {
self.build_64bit_matrix();
} else {
self.build_32bit_matrix();
}
}
fn build_64bit_matrix(&mut self) {
let ccs = [
"sysv",
"win64",
"vectorcall",
"regcall",
"preserve_all",
"preserve_most",
];
for cc in &ccs {
self.matrix
.insert((cc.to_string(), cc.to_string()), CCCompat::Compatible);
}
self.matrix.insert(
("sysv".to_string(), "win64".to_string()),
CCCompat::Incompatible,
);
self.matrix.insert(
("win64".to_string(), "sysv".to_string()),
CCCompat::Incompatible,
);
self.matrix.insert(
("sysv".to_string(), "vectorcall".to_string()),
CCCompat::Incompatible,
);
self.matrix.insert(
("vectorcall".to_string(), "sysv".to_string()),
CCCompat::Incompatible,
);
self.matrix.insert(
("win64".to_string(), "vectorcall".to_string()),
CCCompat::ForwardCompatible,
);
self.matrix.insert(
("vectorcall".to_string(), "win64".to_string()),
CCCompat::NeedsThunk,
);
for cc in &ccs {
if *cc != "preserve_all" {
self.matrix.insert(
("preserve_all".to_string(), cc.to_string()),
CCCompat::Compatible,
);
self.matrix.insert(
(cc.to_string(), "preserve_all".to_string()),
CCCompat::Compatible,
);
}
if *cc != "preserve_most" {
self.matrix.insert(
("preserve_most".to_string(), cc.to_string()),
CCCompat::Compatible,
);
self.matrix.insert(
(cc.to_string(), "preserve_most".to_string()),
CCCompat::Compatible,
);
}
}
}
fn build_32bit_matrix(&mut self) {
let ccs = [
"cdecl",
"stdcall",
"fastcall",
"thiscall",
"vectorcall",
"regcall",
"pascal",
"preserve_all",
"preserve_most",
];
for cc in &ccs {
self.matrix
.insert((cc.to_string(), cc.to_string()), CCCompat::Compatible);
}
for cc in &["stdcall", "fastcall", "thiscall", "pascal", "vectorcall"] {
self.matrix
.insert(("cdecl".to_string(), cc.to_string()), CCCompat::NeedsThunk);
self.matrix
.insert((cc.to_string(), "cdecl".to_string()), CCCompat::Compatible);
}
self.matrix.insert(
("stdcall".to_string(), "fastcall".to_string()),
CCCompat::Incompatible,
);
self.matrix.insert(
("fastcall".to_string(), "stdcall".to_string()),
CCCompat::Incompatible,
);
self.matrix.insert(
("stdcall".to_string(), "thiscall".to_string()),
CCCompat::NeedsThunk,
);
self.matrix.insert(
("thiscall".to_string(), "stdcall".to_string()),
CCCompat::NeedsThunk,
);
for cc in &ccs {
if *cc != "preserve_all" {
self.matrix.insert(
("preserve_all".to_string(), cc.to_string()),
CCCompat::Compatible,
);
self.matrix.insert(
(cc.to_string(), "preserve_all".to_string()),
CCCompat::Compatible,
);
}
if *cc != "preserve_most" {
self.matrix.insert(
("preserve_most".to_string(), cc.to_string()),
CCCompat::Compatible,
);
self.matrix.insert(
(cc.to_string(), "preserve_most".to_string()),
CCCompat::Compatible,
);
}
}
}
pub fn check_compatibility(&self, caller: &str, callee: &str) -> CCCompatCell {
let caller_lc = caller.to_lowercase();
let callee_lc = callee.to_lowercase();
let key = (caller_lc.clone(), callee_lc.clone());
let compat = self
.matrix
.get(&key)
.copied()
.unwrap_or(CCCompat::Incompatible);
let explanation = self.explain(compat, &caller_lc, &callee_lc);
CCCompatCell {
caller: caller.to_string(),
callee: callee.to_string(),
compat,
explanation,
}
}
fn explain(&self, compat: CCCompat, caller: &str, callee: &str) -> String {
match compat {
CCCompat::Compatible => {
format!(
"'{}' can directly call '{}' — conventions are compatible",
caller, callee
)
}
CCCompat::Incompatible => {
format!(
"'{}' cannot directly call '{}' — calling conventions are incompatible; \
use a wrapper function with the correct convention",
caller, callee
)
}
CCCompat::ForwardCompatible => {
format!(
"'{}' can directly call '{}' — callee convention extends caller convention",
caller, callee
)
}
CCCompat::NeedsThunk => {
format!(
"'{}' can call '{}' but may need a thunk due to stack cleanup or register \
usage differences",
caller, callee
)
}
}
}
pub fn known_conventions(&self) -> Vec<&'static str> {
if self.is_64bit {
vec![
"sysv",
"win64",
"vectorcall",
"regcall",
"preserve_all",
"preserve_most",
]
} else {
vec![
"cdecl",
"stdcall",
"fastcall",
"thiscall",
"vectorcall",
"regcall",
"pascal",
"preserve_all",
"preserve_most",
]
}
}
pub fn default_convention(&self) -> &'static str {
if self.is_64bit {
"sysv"
} else {
"cdecl"
}
}
}
#[derive(Debug, Clone)]
pub struct X86VectorSizeLimits {
pub min: u32,
pub max: u32,
pub allowed_sizes: Vec<u32>,
}
#[derive(Debug, Clone)]
pub struct X86VectorSizeChecker {
subtarget: X86Subtarget,
pub limits: X86VectorSizeLimits,
}
impl X86VectorSizeChecker {
pub fn new(subtarget: X86Subtarget) -> Self {
let max = if subtarget.has_avx512f {
64
} else if subtarget.has_avx {
32
} else if subtarget.has_sse {
16
} else if subtarget.has_mmx {
8
} else {
16 };
let min = 1;
let mut allowed_sizes = Vec::new();
let mut s = 1u32;
while s <= max {
allowed_sizes.push(s);
s *= 2;
}
Self {
subtarget,
limits: X86VectorSizeLimits {
min,
max,
allowed_sizes,
},
}
}
pub fn check_vector_size(&self, size_bytes: u32, sema: &mut X86Sema) -> Result<u32, String> {
if size_bytes == 0 {
return Err("vector_size attribute requires a positive size".to_string());
}
if !size_bytes.is_power_of_two() {
return Err(format!("vector_size {} is not a power of two", size_bytes));
}
if size_bytes < self.limits.min {
return Err(format!(
"vector_size {} is below minimum of {}",
size_bytes, self.limits.min
));
}
if size_bytes > self.limits.max {
let feature = if size_bytes <= 16 {
"sse"
} else if size_bytes <= 32 {
"avx"
} else {
"avx512f"
};
return Err(format!(
"vector_size {} exceeds maximum {} for this subtarget; enable -m{}",
size_bytes, self.limits.max, feature
));
}
if size_bytes == 32 && !self.subtarget.has_avx {
return Err("256-bit vectors require -mavx".to_string());
}
if size_bytes == 64 && !self.subtarget.has_avx512f {
return Err("512-bit vectors require -mavx512f".to_string());
}
Ok(size_bytes)
}
pub fn recommended_vector_size(&self, elem_size: u32, num_elems: u32) -> u32 {
let total = elem_size * num_elems;
let mut size = 1u32;
while size < total {
size *= 2;
}
size.min(self.limits.max)
}
pub fn preferred_vector_width_bits(&self) -> u32 {
self.subtarget.get_pref_vector_width()
}
pub fn preferred_vector_width_bytes(&self) -> u32 {
self.preferred_vector_width_bits() / 8
}
pub fn should_use_ext_vector(&self, size_bytes: u32) -> bool {
size_bytes > 0 && size_bytes.is_power_of_two()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::clang::CLangStandard;
use crate::x86::X86Subtarget;
fn haswell_subtarget() -> X86Subtarget {
X86Subtarget::new("haswell", "x86_64-unknown-linux-gnu", "")
}
fn pentium4_subtarget() -> X86Subtarget {
X86Subtarget::new("pentium4", "i686-unknown-linux-gnu", "")
}
fn skx_subtarget() -> X86Subtarget {
X86Subtarget::new("skylake-avx512", "x86_64-unknown-linux-gnu", "")
}
fn make_sema() -> Sema {
Sema::new(CLangStandard::C17)
}
fn make_func_decl(name: &str, ret_ty: QualType, params: Vec<VarDecl>) -> FunctionDecl {
FunctionDecl {
name: name.to_string(),
ret_ty,
params,
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
}
}
fn make_param(name: &str, ty: QualType) -> VarDecl {
VarDecl {
name: name.to_string(),
ty,
init: None,
linkage: Linkage::None,
is_global: false,
is_extern: false,
is_static: false,
}
}
#[test]
fn test_type_checker_sizes_64bit() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.int_size, 4);
assert_eq!(tc.long_size, 8);
assert_eq!(tc.long_long_size, 8);
assert_eq!(tc.float_size, 4);
assert_eq!(tc.double_size, 8);
assert_eq!(tc.pointer_size, 8);
assert_eq!(tc.max_vector_size, 32); }
#[test]
fn test_type_checker_sizeof_type_name() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.sizeof_type_name("int"), Some(4));
assert_eq!(tc.sizeof_type_name("long"), Some(8));
assert_eq!(tc.sizeof_type_name("long long"), Some(8));
assert_eq!(tc.sizeof_type_name("char"), Some(1));
assert_eq!(tc.sizeof_type_name("short"), Some(2));
assert_eq!(tc.sizeof_type_name("float"), Some(4));
assert_eq!(tc.sizeof_type_name("double"), Some(8));
assert_eq!(tc.sizeof_type_name("size_t"), Some(8));
assert_eq!(tc.sizeof_type_name("void*"), Some(8));
assert_eq!(tc.sizeof_type_name("__int128"), Some(16));
assert_eq!(tc.sizeof_type_name("__float128"), Some(16));
}
#[test]
fn test_type_checker_alignof_type_name() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.alignof_type_name("char"), Some(1));
assert_eq!(tc.alignof_type_name("short"), Some(2));
assert_eq!(tc.alignof_type_name("int"), Some(4));
assert_eq!(tc.alignof_type_name("long"), Some(8));
assert_eq!(tc.alignof_type_name("long long"), Some(8));
assert_eq!(tc.alignof_type_name("double"), Some(8));
assert_eq!(tc.alignof_type_name("__int128"), Some(16));
assert_eq!(tc.alignof_type_name("__float128"), Some(16));
}
#[test]
fn test_type_checker_32bit_sizes() {
let st = pentium4_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.pointer_size, 4);
assert_eq!(tc.long_size, 4);
assert_eq!(tc.max_vector_size, 16); }
#[test]
fn test_check_integer_size() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.check_integer_size(16), Ok(2));
assert_eq!(tc.check_integer_size(32), Ok(4));
assert_eq!(tc.check_integer_size(64), Ok(8));
assert_eq!(tc.check_integer_size(128), Ok(16));
assert!(tc.check_integer_size(256).is_err());
}
#[test]
fn test_check_integer_size_128_32bit() {
let st = pentium4_subtarget();
let tc = X86TypeChecker::new(st);
assert!(tc.check_integer_size(128).is_err());
}
#[test]
fn test_check_float_size() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.check_float_size(32), Ok(4));
assert_eq!(tc.check_float_size(64), Ok(8));
assert_eq!(tc.check_float_size(80), Ok(16));
assert_eq!(tc.check_float_size(128), Ok(16));
}
#[test]
fn test_check_vector_type_128bit() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert!(tc.check_vector_type(4, 4).is_ok()); assert!(tc.check_vector_type(8, 2).is_ok()); }
#[test]
fn test_check_vector_type_256bit() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert!(tc.check_vector_type(8, 4).is_ok()); }
#[test]
fn test_check_vector_type_256bit_no_avx() {
let st = pentium4_subtarget();
let tc = X86TypeChecker::new(st);
assert!(tc.check_vector_type(8, 4).is_err()); }
#[test]
fn test_check_vector_type_512bit() {
let st = skx_subtarget();
let tc = X86TypeChecker::new(st);
assert!(tc.check_vector_type(4, 16).is_ok()); }
#[test]
fn test_check_vector_type_512bit_no_avx512() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert!(tc.check_vector_type(4, 16).is_err()); }
#[test]
fn test_check_vector_type_exceeds_max() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert!(tc.check_vector_type(8, 8).is_err()); }
#[test]
fn test_builtin_checker_registers_builtins() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
assert!(bc.num_builtins() > 50);
}
#[test]
fn test_builtin_checker_sse_signature() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_addps").unwrap();
assert_eq!(sig.return_type, "V4F32");
assert_eq!(sig.args.len(), 2);
assert_eq!(sig.required_feature.as_deref(), Some("sse"));
}
#[test]
fn test_builtin_checker_sse2_signature() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_addpd").unwrap();
assert_eq!(sig.return_type, "V2F64");
assert_eq!(sig.args.len(), 2);
assert_eq!(sig.required_feature.as_deref(), Some("sse2"));
}
#[test]
fn test_builtin_checker_avx_signature() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_addps256").unwrap();
assert_eq!(sig.return_type, "V8F32");
assert_eq!(sig.required_feature.as_deref(), Some("avx"));
}
#[test]
fn test_builtin_checker_avx512_signature() {
let st = skx_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_addps512").unwrap();
assert_eq!(sig.return_type, "V16F32");
assert_eq!(sig.required_feature.as_deref(), Some("avx512f"));
}
#[test]
fn test_builtin_checker_immediate_range() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_cmpps").unwrap();
let imm_arg = &sig.args[2];
assert!(imm_arg.is_immediate);
assert_eq!(imm_arg.imm_min, Some(0));
assert_eq!(imm_arg.imm_max, Some(7));
}
#[test]
fn test_builtin_checker_aes_signature() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_aesenc128").unwrap();
assert_eq!(sig.required_feature.as_deref(), Some("aes"));
}
#[test]
fn test_builtin_checker_sha_signature() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_sha1rnds4").unwrap();
assert_eq!(sig.required_feature.as_deref(), Some("sha"));
}
#[test]
fn test_builtin_checker_unknown_builtin() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
assert!(bc.get_signature("__builtin_ia32_nonexistent").is_none());
}
#[test]
fn test_builtin_checker_64bit_only() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_cvtss2si64").unwrap();
assert!(sig.is_64bit_only);
}
#[test]
fn test_builtin_checker_feature_check() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
assert!(bc.subtarget_has_feature("sse"));
assert!(bc.subtarget_has_feature("sse2"));
assert!(bc.subtarget_has_feature("avx"));
assert!(bc.subtarget_has_feature("aes"));
assert!(!bc.subtarget_has_feature("avx512f"));
}
#[test]
fn test_builtin_checker_builtin_names() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let names = bc.builtin_names();
assert!(names.iter().any(|n| n.as_str() == "__builtin_ia32_addps"));
assert!(names.iter().any(|n| n.as_str() == "__builtin_ia32_addpd"));
assert!(names
.iter()
.any(|n| n.as_str() == "__builtin_ia32_aesenc128"));
}
#[test]
fn test_abi_checker_validate_cc_64bit() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
assert_eq!(ac.validate_cc("sysv"), CCValidity::Valid);
assert_eq!(ac.validate_cc("cdecl"), CCValidity::Valid);
assert_eq!(ac.validate_cc("stdcall"), CCValidity::Requires32Bit);
assert_eq!(ac.validate_cc("fastcall"), CCValidity::Requires32Bit);
assert_eq!(ac.validate_cc("vectorcall"), CCValidity::Valid);
}
#[test]
fn test_abi_checker_validate_cc_32bit() {
let st = pentium4_subtarget();
let ac = X86ABIChecker::new(st);
assert_eq!(ac.validate_cc("cdecl"), CCValidity::Valid);
assert_eq!(ac.validate_cc("stdcall"), CCValidity::Valid);
assert_eq!(ac.validate_cc("fastcall"), CCValidity::Valid);
assert_eq!(ac.validate_cc("sysv"), CCValidity::Requires64Bit);
assert_eq!(ac.validate_cc("ms_abi"), CCValidity::Requires64Bit);
}
#[test]
fn test_abi_checker_unknown_cc() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
assert_eq!(ac.validate_cc("madeup_cc"), CCValidity::Unknown);
}
#[test]
fn test_abi_checker_struct_sret_sysv() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
let small_ty = QualType::int();
let large_ty = QualType {
base: TypeNode::Struct {
name: Some("BigStruct".to_string()),
fields: vec![
FieldDecl {
name: "data".to_string(),
ty: QualType::int(),
bit_width: None,
};
32
],
is_union: false,
},
..QualType::int() };
assert!(!ac.struct_needs_sret(&small_ty, "sysv"));
}
#[test]
fn test_abi_checker_struct_sret_win64() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
let ty_9_bytes = QualType {
base: TypeNode::Struct {
name: Some("S".to_string()),
fields: vec![
FieldDecl {
name: "a".to_string(),
ty: QualType::long(),
bit_width: None,
},
FieldDecl {
name: "b".to_string(),
ty: QualType::char(),
bit_width: None,
},
],
is_union: false,
},
is_const: false,
is_volatile: false,
is_restrict: false,
};
assert!(ac.struct_needs_sret(&ty_9_bytes, "ms_abi"));
}
#[test]
fn test_alignment_checker_stack_64bit() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
assert_eq!(ac.stack_alignment, 16);
}
#[test]
fn test_alignment_checker_max_alignment_haswell() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
assert_eq!(ac.max_alignment, 32);
}
#[test]
fn test_alignment_checker_max_alignment_skx() {
let st = skx_subtarget();
let ac = X86AlignmentChecker::new(st);
assert_eq!(ac.max_alignment, 64);
}
#[test]
fn test_alignment_checker_check_aligned_valid() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
assert_eq!(ac.check_aligned_attribute(16), Ok(16));
assert_eq!(ac.check_aligned_attribute(32), Ok(32));
assert_eq!(ac.check_aligned_attribute(8), Ok(8));
}
#[test]
fn test_alignment_checker_check_aligned_invalid() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
assert!(ac.check_aligned_attribute(0).is_err());
assert!(ac.check_aligned_attribute(3).is_err()); assert!(ac.check_aligned_attribute(64).is_err()); }
#[test]
fn test_alignment_checker_check_stack_alignment() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
assert_eq!(ac.check_stack_alignment(16), Ok(16));
assert_eq!(ac.check_stack_alignment(32), Ok(32));
assert!(ac.check_stack_alignment(8).is_err()); assert!(ac.check_stack_alignment(12).is_err()); }
#[test]
fn test_alignment_checker_preferred_alignment() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
assert_eq!(ac.preferred_alignment(1), 1);
assert_eq!(ac.preferred_alignment(2), 2);
assert_eq!(ac.preferred_alignment(4), 4);
assert_eq!(ac.preferred_alignment(8), 8);
assert_eq!(ac.preferred_alignment(12), 16);
assert_eq!(ac.preferred_alignment(16), 16);
assert_eq!(ac.preferred_alignment(24), 32);
assert_eq!(ac.preferred_alignment(32), 32);
assert_eq!(ac.preferred_alignment(48), 64);
assert_eq!(ac.preferred_alignment(64), 64);
}
#[test]
fn test_alignment_checker_abi_alignment() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
assert_eq!(ac.abi_alignment(1), 1);
assert_eq!(ac.abi_alignment(2), 2);
assert_eq!(ac.abi_alignment(4), 4);
assert_eq!(ac.abi_alignment(8), 8);
assert_eq!(ac.abi_alignment(16), 16);
assert_eq!(ac.abi_alignment(32), 32);
assert_eq!(ac.abi_alignment(64), 64);
}
#[test]
fn test_intrinsic_sema_registers_intrinsics() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert!(is.num_intrinsics() > 100);
}
#[test]
fn test_intrinsic_sema_lookup_sse() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert_eq!(
is.lookup_group("_mm_add_ps").map(|s| s.as_str()),
Some("sse")
);
assert_eq!(
is.lookup_group("_mm_sqrt_ps").map(|s| s.as_str()),
Some("sse")
);
}
#[test]
fn test_intrinsic_sema_lookup_sse2() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert_eq!(
is.lookup_group("_mm_add_pd").map(|s| s.as_str()),
Some("sse2")
);
assert_eq!(
is.lookup_group("_mm_sqrt_pd").map(|s| s.as_str()),
Some("sse2")
);
}
#[test]
fn test_intrinsic_sema_lookup_avx() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert_eq!(
is.lookup_group("_mm256_add_ps").map(|s| s.as_str()),
Some("avx")
);
assert_eq!(
is.lookup_group("_mm256_add_pd").map(|s| s.as_str()),
Some("avx")
);
}
#[test]
fn test_intrinsic_sema_lookup_avx2() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert_eq!(
is.lookup_group("_mm256_add_epi32").map(|s| s.as_str()),
Some("avx2")
);
}
#[test]
fn test_intrinsic_sema_lookup_avx512() {
let st = skx_subtarget();
let is = X86IntrinsicSema::new(st);
assert_eq!(
is.lookup_group("_mm512_add_ps").map(|s| s.as_str()),
Some("avx512f")
);
}
#[test]
fn test_intrinsic_sema_lookup_aes() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert_eq!(
is.lookup_group("_mm_aesenc_si128").map(|s| s.as_str()),
Some("aes")
);
}
#[test]
fn test_intrinsic_sema_lookup_fma() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert_eq!(
is.lookup_group("_mm_fmadd_ps").map(|s| s.as_str()),
Some("fma")
);
assert_eq!(
is.lookup_group("_mm256_fmadd_ps").map(|s| s.as_str()),
Some("fma")
);
}
#[test]
fn test_intrinsic_sema_lookup_unknown() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert!(is.lookup_group("_mm_nonexistent").is_none());
}
#[test]
fn test_intrinsic_sema_feature_check() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert!(is.subtarget_has_feature_for_group("sse"));
assert!(is.subtarget_has_feature_for_group("sse2"));
assert!(is.subtarget_has_feature_for_group("avx"));
assert!(is.subtarget_has_feature_for_group("avx2"));
assert!(!is.subtarget_has_feature_for_group("avx512f"));
}
#[test]
fn test_intrinsic_sema_intrinsic_names() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let names = is.intrinsic_names();
assert!(names.iter().any(|n| n.as_str() == "_mm_add_ps"));
assert!(names.iter().any(|n| n.as_str() == "_mm256_add_ps"));
assert!(names.iter().any(|n| n.as_str() == "_mm_aesenc_si128"));
}
#[test]
fn test_target_attr_parse_arch() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let attrs = tac.parse_target_attr("arch=x86-64-v3");
assert_eq!(attrs.len(), 1);
assert_eq!(attrs[0].kind, X86TargetAttrKey::Arch);
assert_eq!(attrs[0].value, "x86-64-v3");
assert!(attrs[0].recognized);
}
#[test]
fn test_target_attr_parse_cpu() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let attrs = tac.parse_target_attr("cpu=haswell");
assert_eq!(attrs.len(), 1);
assert_eq!(attrs[0].kind, X86TargetAttrKey::Cpu);
assert!(attrs[0].recognized);
}
#[test]
fn test_target_attr_parse_tune() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let attrs = tac.parse_target_attr("tune=generic");
assert_eq!(attrs[0].kind, X86TargetAttrKey::Tune);
assert!(attrs[0].recognized);
}
#[test]
fn test_target_attr_parse_feature_enable() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let attrs = tac.parse_target_attr("+avx2");
assert_eq!(attrs.len(), 1);
assert_eq!(attrs[0].kind, X86TargetAttrKey::FeatureEnable);
assert_eq!(attrs[0].value, "avx2");
assert!(attrs[0].recognized);
}
#[test]
fn test_target_attr_parse_feature_disable() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let attrs = tac.parse_target_attr("-sse");
assert_eq!(attrs[0].kind, X86TargetAttrKey::FeatureDisable);
assert_eq!(attrs[0].value, "sse");
assert!(attrs[0].recognized);
}
#[test]
fn test_target_attr_parse_multiple() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let attrs = tac.parse_target_attr("arch=x86-64-v3,+avx2,-sse,tune=skylake");
assert_eq!(attrs.len(), 4);
}
#[test]
fn test_target_attr_parse_unknown_cpu() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let attrs = tac.parse_target_attr("cpu=fakecpu");
assert!(!attrs[0].recognized);
}
#[test]
fn test_target_attr_parse_unknown_feature() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let attrs = tac.parse_target_attr("+fake_feature");
assert!(!attrs[0].recognized);
}
#[test]
fn test_target_attr_validate_warnings() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let warnings = tac.validate_target_attr("cpu=fakecpu,+fakesimd");
assert_eq!(warnings.len(), 2);
}
#[test]
fn test_target_attr_is_known_cpu() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
assert!(tac.is_known_cpu("skylake"));
assert!(tac.is_known_cpu("znver4"));
assert!(!tac.is_known_cpu("fictional"));
}
#[test]
fn test_target_attr_is_known_feature() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
assert!(tac.is_known_feature("avx2"));
assert!(tac.is_known_feature("sse"));
assert!(!tac.is_known_feature("fakesimd"));
}
#[test]
fn test_target_attr_is_known_arch() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
assert!(tac.is_known_arch("x86-64"));
assert!(tac.is_known_arch("x86-64-v3"));
assert!(!tac.is_known_arch("arm32"));
}
#[test]
fn test_target_attr_empty_string() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let attrs = tac.parse_target_attr("");
assert!(attrs.is_empty());
}
#[test]
fn test_format_attr_style_from_str() {
assert_eq!(FormatStyle::from_str("printf"), FormatStyle::Printf);
assert_eq!(FormatStyle::from_str("scanf"), FormatStyle::Scanf);
assert_eq!(FormatStyle::from_str("strftime"), FormatStyle::Strftime);
assert_eq!(FormatStyle::from_str("strfmon"), FormatStyle::Strfmon);
assert_eq!(FormatStyle::from_str("unknown"), FormatStyle::Unknown);
}
#[test]
fn test_format_attr_is_valid_on_x86() {
assert!(FormatStyle::Printf.is_valid_on_x86());
assert!(FormatStyle::Scanf.is_valid_on_x86());
assert!(FormatStyle::Strftime.is_valid_on_x86());
assert!(!FormatStyle::NSLog.is_valid_on_x86());
assert!(!FormatStyle::OSLog.is_valid_on_x86());
assert!(!FormatStyle::Unknown.is_valid_on_x86());
}
#[test]
fn test_format_attr_as_str() {
assert_eq!(FormatStyle::Printf.as_str(), "printf");
assert_eq!(FormatStyle::Scanf.as_str(), "scanf");
assert_eq!(FormatStyle::Unknown.as_str(), "unknown");
}
#[test]
fn test_format_attr_check_specifier_valid() {
let fc = X86FormatAttrChecker::new_64bit();
assert!(fc.check_format_specifier('d', "").is_ok());
assert!(fc.check_format_specifier('u', "l").is_ok());
assert!(fc.check_format_specifier('x', "ll").is_ok());
assert!(fc.check_format_specifier('f', "").is_ok());
assert!(fc.check_format_specifier('p', "").is_ok());
assert!(fc.check_format_specifier('s', "").is_ok());
assert!(fc.check_format_specifier('%', "").is_ok());
}
#[test]
fn test_format_attr_check_specifier_invalid() {
let fc = X86FormatAttrChecker::new_64bit();
assert!(fc.check_format_specifier('z', "").is_err()); }
#[test]
fn test_format_attr_check_specifier_percent_n() {
let fc = X86FormatAttrChecker::new_64bit();
assert!(fc.check_format_specifier('n', "").is_err()); }
#[test]
fn test_format_attr_format_type_size_64bit() {
let fc = X86FormatAttrChecker::new_64bit();
assert_eq!(fc.format_type_size('d', ""), 4); assert_eq!(fc.format_type_size('d', "l"), 8); assert_eq!(fc.format_type_size('d', "ll"), 8); assert_eq!(fc.format_type_size('d', "z"), 8); assert_eq!(fc.format_type_size('d', "t"), 8); assert_eq!(fc.format_type_size('d', "j"), 8); assert_eq!(fc.format_type_size('p', ""), 8); }
#[test]
fn test_format_attr_format_type_size_32bit() {
let fc = X86FormatAttrChecker::new_32bit();
assert_eq!(fc.format_type_size('d', ""), 4); assert_eq!(fc.format_type_size('d', "l"), 4); assert_eq!(fc.format_type_size('d', "z"), 4); assert_eq!(fc.format_type_size('p', ""), 4); }
#[test]
fn test_format_attr_float_size_64bit() {
let fc = X86FormatAttrChecker::new_64bit();
assert_eq!(fc.format_type_size('f', ""), 8); assert_eq!(fc.format_type_size('f', "l"), 8); assert_eq!(fc.format_type_size('f', "L"), 16); }
#[test]
fn test_cc_checker_known_conventions_64bit() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let convs = cc.known_conventions();
assert!(convs.contains(&"sysv"));
assert!(convs.contains(&"win64"));
assert!(!convs.contains(&"stdcall"));
}
#[test]
fn test_cc_checker_known_conventions_32bit() {
let st = pentium4_subtarget();
let cc = X86CallingConvChecker::new(st);
let convs = cc.known_conventions();
assert!(convs.contains(&"cdecl"));
assert!(convs.contains(&"stdcall"));
assert!(convs.contains(&"fastcall"));
assert!(!convs.contains(&"sysv"));
}
#[test]
fn test_cc_checker_default_convention_64bit() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
assert_eq!(cc.default_convention(), "sysv");
}
#[test]
fn test_cc_checker_default_convention_32bit() {
let st = pentium4_subtarget();
let cc = X86CallingConvChecker::new(st);
assert_eq!(cc.default_convention(), "cdecl");
}
#[test]
fn test_cc_checker_same_cc_compatible() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("sysv", "sysv");
assert_eq!(result.compat, CCCompat::Compatible);
}
#[test]
fn test_cc_checker_sysv_win64_incompatible() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("sysv", "win64");
assert_eq!(result.compat, CCCompat::Incompatible);
}
#[test]
fn test_cc_checker_cdecl_stdcall_32bit() {
let st = pentium4_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("cdecl", "stdcall");
assert_eq!(result.compat, CCCompat::NeedsThunk);
}
#[test]
fn test_cc_checker_stdcall_fastcall_32bit() {
let st = pentium4_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("stdcall", "fastcall");
assert_eq!(result.compat, CCCompat::Incompatible);
}
#[test]
fn test_cc_checker_preserve_all_compatible() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("preserve_all", "sysv");
assert_eq!(result.compat, CCCompat::Compatible);
}
#[test]
fn test_vector_size_checker_limits_haswell() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
assert_eq!(vsc.limits.max, 32);
assert_eq!(vsc.limits.min, 1);
assert!(vsc.limits.allowed_sizes.contains(&16));
assert!(vsc.limits.allowed_sizes.contains(&32));
}
#[test]
fn test_vector_size_checker_limits_skx() {
let st = skx_subtarget();
let vsc = X86VectorSizeChecker::new(st);
assert_eq!(vsc.limits.max, 64);
assert!(vsc.limits.allowed_sizes.contains(&64));
}
#[test]
fn test_vector_size_checker_limits_pentium4() {
let st = pentium4_subtarget();
let vsc = X86VectorSizeChecker::new(st);
assert_eq!(vsc.limits.max, 16);
}
#[test]
fn test_vector_size_checker_valid_power_of_two() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
assert!(vsc.check_vector_size(1, &mut sema).is_ok());
assert!(vsc.check_vector_size(2, &mut sema).is_ok());
assert!(vsc.check_vector_size(4, &mut sema).is_ok());
assert!(vsc.check_vector_size(8, &mut sema).is_ok());
assert!(vsc.check_vector_size(16, &mut sema).is_ok());
assert!(vsc.check_vector_size(32, &mut sema).is_ok());
}
#[test]
fn test_vector_size_checker_invalid_not_power_of_two() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
assert!(vsc.check_vector_size(3, &mut sema).is_err());
assert!(vsc.check_vector_size(6, &mut sema).is_err());
assert!(vsc.check_vector_size(12, &mut sema).is_err());
}
#[test]
fn test_vector_size_checker_exceeds_max() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
assert!(vsc.check_vector_size(64, &mut sema).is_err());
assert!(vsc.check_vector_size(128, &mut sema).is_err());
}
#[test]
fn test_vector_size_checker_zero_invalid() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
assert!(vsc.check_vector_size(0, &mut sema).is_err());
}
#[test]
fn test_vector_size_checker_recommended_size() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
assert_eq!(vsc.recommended_vector_size(4, 10), 32);
assert_eq!(vsc.recommended_vector_size(4, 3), 4);
}
#[test]
fn test_vector_size_checker_preferred_width() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let width = vsc.preferred_vector_width_bits();
assert!(width == 128 || width == 256);
}
#[test]
fn test_vector_size_checker_should_use_ext_vector() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
assert!(vsc.should_use_ext_vector(16));
assert!(vsc.should_use_ext_vector(32));
assert!(!vsc.should_use_ext_vector(0));
}
#[test]
fn test_x86_sema_creation() {
let st = haswell_subtarget();
let sema = make_sema();
let x86_sema = X86Sema::new(sema, st);
assert!(!x86_sema.has_x86_errors());
assert!(!x86_sema.has_x86_warnings());
}
#[test]
fn test_x86_sema_error_handling() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
x86_sema.x86_error("test error".to_string());
assert!(x86_sema.has_x86_errors());
assert_eq!(x86_sema.x86_errors.len(), 1);
assert_eq!(x86_sema.base.errors.len(), 1);
}
#[test]
fn test_x86_sema_warning_handling() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
x86_sema.x86_warning("test warning".to_string());
assert!(x86_sema.has_x86_warnings());
assert_eq!(x86_sema.x86_warnings.len(), 1);
}
#[test]
fn test_x86_sema_clear_diagnostics() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
x86_sema.x86_error("err".to_string());
x86_sema.x86_warning("warn".to_string());
x86_sema.clear_x86_diagnostics();
assert!(!x86_sema.has_x86_errors());
assert!(!x86_sema.has_x86_warnings());
}
#[test]
fn test_x86_sema_diagnostic_summary() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
x86_sema.x86_error("e1".to_string());
x86_sema.x86_error("e2".to_string());
x86_sema.x86_warning("w1".to_string());
let summary = x86_sema.x86_diagnostic_summary();
assert!(summary.contains("2 error"));
assert!(summary.contains("1 warning"));
}
#[test]
fn test_x86_sema_check_type_through_sema() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
x86_sema
.type_checker
.check_type(&QualType::int(), &mut x86_sema);
assert!(!x86_sema.has_x86_errors());
}
#[test]
fn test_x86_sema_check_x86_decl_function() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl(
"__builtin_ia32_addps",
QualType {
base: TypeNode::Int,
is_const: false,
is_volatile: false,
is_restrict: false,
},
vec![
make_param(
"a",
QualType {
base: TypeNode::Int,
is_const: false,
is_volatile: false,
is_restrict: false,
},
),
make_param(
"b",
QualType {
base: TypeNode::Int,
is_const: false,
is_volatile: false,
is_restrict: false,
},
),
],
);
let decl = Decl::Function(fd);
x86_sema.check_x86_decl(&decl);
}
#[test]
fn test_builtin_checker_wrong_arg_count() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl(
"__builtin_ia32_sqrtps",
QualType::int(),
vec![
make_param("a", QualType::int()),
make_param("b", QualType::int()),
],
);
bc.check_builtin_call(&fd, &mut x86_sema);
assert!(x86_sema.has_x86_errors());
}
#[test]
fn test_builtin_checker_unknown_builtin_error() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl("__builtin_ia32_imaginary", QualType::int(), vec![]);
bc.check_builtin_call(&fd, &mut x86_sema);
assert!(x86_sema.has_x86_errors());
}
#[test]
fn test_builtin_checker_missing_feature() {
let st = pentium4_subtarget();
let bc = X86BuiltinChecker::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl(
"__builtin_ia32_addps256",
QualType::int(),
vec![
make_param("a", QualType::int()),
make_param("b", QualType::int()),
],
);
bc.check_builtin_call(&fd, &mut x86_sema);
assert!(x86_sema.has_x86_errors());
}
#[test]
fn test_builtin_checker_64bit_only_on_32bit() {
let st = pentium4_subtarget();
let bc = X86BuiltinChecker::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl(
"__builtin_ia32_cvtss2si64",
QualType::int(),
vec![make_param("a", QualType::int())],
);
bc.check_builtin_call(&fd, &mut x86_sema);
assert!(x86_sema.has_x86_errors());
}
#[test]
fn test_abi_checker_struct_sret_sysv_small() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
let ty = QualType::int();
assert!(!ac.struct_needs_sret(&ty, "sysv"));
}
#[test]
fn test_abi_result_valid() {
let result = X86ABIResult::valid();
assert!(result.compatible);
assert!(result.issues.is_empty());
}
#[test]
fn test_abi_result_issue() {
let result = X86ABIResult::issue("incompatible".to_string());
assert!(!result.compatible);
assert_eq!(result.issues.len(), 1);
}
#[test]
fn test_abi_result_add_issue() {
let mut result = X86ABIResult::valid();
result.add_issue("issue1".to_string());
result.add_issue("issue2".to_string());
assert!(!result.compatible);
assert_eq!(result.issues.len(), 2);
}
#[test]
fn test_alignment_checker_vector_alignments() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
assert_eq!(ac.vector128_alignment, 16);
assert_eq!(ac.vector256_alignment, 32);
assert_eq!(ac.vector512_alignment, 64);
}
#[test]
fn test_intrinsic_sema_skx_has_avx512bw() {
let st = skx_subtarget();
let is = X86IntrinsicSema::new(st);
assert!(is.subtarget_has_feature_for_group("avx512f"));
}
#[test]
fn test_intrinsic_sema_haswell_no_avx512() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert!(!is.subtarget_has_feature_for_group("avx512f"));
}
#[test]
fn test_intrinsic_sema_ssse3_lookup() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert_eq!(
is.lookup_group("_mm_shuffle_epi8").map(|s| s.as_str()),
Some("ssse3")
);
assert_eq!(
is.lookup_group("_mm_abs_epi32").map(|s| s.as_str()),
Some("ssse3")
);
}
#[test]
fn test_intrinsic_sema_sse41_lookup() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert_eq!(
is.lookup_group("_mm_blendv_ps").map(|s| s.as_str()),
Some("sse41")
);
assert_eq!(
is.lookup_group("_mm_min_epi8").map(|s| s.as_str()),
Some("sse41")
);
}
#[test]
fn test_intrinsic_sema_sse42_lookup() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert_eq!(
is.lookup_group("_mm_crc32_u8").map(|s| s.as_str()),
Some("sse42")
);
assert_eq!(
is.lookup_group("_mm_crc32_u64").map(|s| s.as_str()),
Some("sse42")
);
}
#[test]
fn test_format_attr_check_float_modifier_L() {
let fc = X86FormatAttrChecker::new_64bit();
assert!(fc.check_float_length_modifier("L").is_ok());
}
#[test]
fn test_format_attr_check_int_modifier_hh() {
let fc = X86FormatAttrChecker::new_64bit();
assert!(fc.check_int_length_modifier("hh").is_ok());
}
#[test]
fn test_format_attr_check_invalid_modifier() {
let fc = X86FormatAttrChecker::new_64bit();
assert!(fc.check_int_length_modifier("q").is_err());
assert!(fc.check_float_length_modifier("ll").is_err());
}
#[test]
fn test_format_attr_size_t_size_32bit() {
let fc = X86FormatAttrChecker::new_32bit();
assert_eq!(fc.size_t_size, 4);
assert_eq!(fc.long_size, 4);
}
#[test]
fn test_target_attr_parse_whitespace() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let attrs = tac.parse_target_attr(" arch=x86-64 , +avx ");
assert_eq!(attrs.len(), 2);
assert_eq!(attrs[0].value, "x86-64");
assert_eq!(attrs[1].value, "avx");
}
#[test]
fn test_target_attr_parse_empty_parts() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let attrs = tac.parse_target_attr(",,arch=x86-64,,");
assert_eq!(attrs.len(), 1);
}
#[test]
fn test_cc_checker_64bit_regcall() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
assert_eq!(cc.validate_cc("regcall"), CCValidity::Valid);
}
#[test]
fn test_vector_size_skx_512bit_valid() {
let st = skx_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
assert!(vsc.check_vector_size(64, &mut sema).is_ok());
}
#[test]
fn test_vector_size_haswell_256bit_valid() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
assert!(vsc.check_vector_size(32, &mut sema).is_ok());
}
#[test]
fn test_builtin_checker_fma_signature() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_vfmaddps").unwrap();
assert_eq!(sig.args.len(), 3); assert_eq!(sig.required_feature.as_deref(), Some("fma"));
}
#[test]
fn test_builtin_checker_bmi_signature() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_tzcnt_u32").unwrap();
assert_eq!(sig.required_feature.as_deref(), Some("bmi"));
assert!(!sig.is_64bit_only);
}
#[test]
fn test_builtin_checker_bmi_64bit_signature() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_tzcnt_u64").unwrap();
assert_eq!(sig.required_feature.as_deref(), Some("bmi"));
assert!(sig.is_64bit_only);
}
#[test]
fn test_builtin_checker_bmi2_signature() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_bzhi_si").unwrap();
assert_eq!(sig.required_feature.as_deref(), Some("bmi2"));
}
#[test]
fn test_builtin_checker_pdep_signature() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_pdep_si").unwrap();
assert_eq!(sig.required_feature.as_deref(), Some("bmi2"));
}
#[test]
fn test_builtin_checker_all_registered() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
assert!(bc.num_builtins() >= 50);
for name in bc.builtin_names() {
assert!(name.starts_with("__builtin_ia32_"));
}
}
#[test]
fn test_intrinsic_sema_all_registered() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert!(is.num_intrinsics() >= 100);
}
#[test]
fn test_type_checker_all_type_sizes_consistent() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
let sizes = vec![
tc.int_size,
tc.long_size,
tc.long_long_size,
tc.float_size,
tc.double_size,
tc.pointer_size,
];
for &s in &sizes {
assert!(s > 0);
assert!(s.is_power_of_two());
}
}
#[test]
fn test_alignment_checker_all_valid_alignments() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
for a in &[1, 2, 4, 8, 16, 32] {
assert!(ac.check_aligned_attribute(*a).is_ok());
}
}
#[test]
fn test_target_attr_all_known_features() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
assert!(tac.is_known_feature("sse"));
assert!(tac.is_known_feature("avx512f"));
assert!(tac.is_known_feature("aes"));
assert!(tac.is_known_feature("sha"));
assert!(tac.is_known_feature("fma"));
}
#[test]
fn test_target_attr_all_known_cpus() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
assert!(tac.is_known_cpu("haswell"));
assert!(tac.is_known_cpu("skylake"));
assert!(tac.is_known_cpu("znver4"));
assert!(tac.is_known_cpu("sapphirerapids"));
}
#[test]
fn test_cc_checker_full_matrix_coverage_64bit() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let convs = cc.known_conventions();
for c in &convs {
let result = cc.check_compatibility(c, c);
assert_eq!(
result.compat,
CCCompat::Compatible,
"{} should be compatible with itself",
c
);
}
}
#[test]
fn test_cc_checker_full_matrix_coverage_32bit() {
let st = pentium4_subtarget();
let cc = X86CallingConvChecker::new(st);
let convs = cc.known_conventions();
for c in &convs {
let result = cc.check_compatibility(c, c);
assert_eq!(
result.compat,
CCCompat::Compatible,
"{} should be compatible with itself",
c
);
}
}
#[test]
fn test_format_attr_all_styles_roundtrip() {
let styles = ["printf", "scanf", "strftime", "strfmon"];
for &s in &styles {
let fs = FormatStyle::from_str(s);
assert_eq!(fs.as_str(), s);
}
}
#[test]
fn test_x86_sema_detect_calling_convention_64bit() {
let st = haswell_subtarget();
let sema = make_sema();
let x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl("foo", QualType::int(), vec![]);
let cc = x86_sema.detect_calling_convention(&fd);
assert_eq!(cc.unwrap(), "sysv");
}
#[test]
fn test_x86_sema_detect_calling_convention_32bit() {
let st = pentium4_subtarget();
let sema = make_sema();
let x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl("foo", QualType::int(), vec![]);
let cc = x86_sema.detect_calling_convention(&fd);
assert_eq!(cc.unwrap(), "cdecl");
}
#[test]
fn test_intrinsic_sema_sse3_lookup() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert_eq!(
is.lookup_group("_mm_hadd_ps").map(|s| s.as_str()),
Some("sse3")
);
assert_eq!(
is.lookup_group("_mm_addsub_ps").map(|s| s.as_str()),
Some("sse3")
);
}
#[test]
fn test_format_attr_check_wchar_specifier() {
let fc = X86FormatAttrChecker::new_64bit();
assert!(fc.check_format_specifier('c', "l").is_ok()); assert!(fc.check_format_specifier('s', "l").is_ok()); }
#[test]
fn test_format_attr_wchar_t_size_is_4() {
let fc = X86FormatAttrChecker::new_64bit();
assert_eq!(fc.wchar_t_size, 4);
}
#[test]
fn test_x86_sema_check_x86_decl_variable() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let vd = VarDecl {
name: "x".to_string(),
ty: QualType::int(),
init: None,
linkage: Linkage::External,
is_global: true,
is_extern: false,
is_static: false,
};
let decl = Decl::Variable(vd);
x86_sema.check_x86_decl(&decl);
assert!(!x86_sema.has_x86_errors());
}
#[test]
fn test_x86_sema_check_x86_decl_typedef() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let td = TypedefDecl {
name: "myint".to_string(),
underlying: QualType::int(),
};
let decl = Decl::Typedef(td);
x86_sema.check_x86_decl(&decl);
assert!(!x86_sema.has_x86_errors());
}
#[test]
fn test_x86_sema_check_x86_decl_struct() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let sd = StructDecl {
name: Some("Point".to_string()),
fields: vec![
FieldDecl::new("x", QualType::int()),
FieldDecl::new("y", QualType::int()),
],
is_union: false,
};
let decl = Decl::Struct(sd);
x86_sema.check_x86_decl(&decl);
assert!(!x86_sema.has_x86_errors());
}
#[test]
fn test_x86_sema_check_x86_decl_enum() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let ed = EnumDecl {
name: Some("Color").to_string(),
variants: vec![EnumVariant::new("Red", Some(0))],
underlying: QualType::int(),
};
let decl = Decl::Enum(ed);
x86_sema.check_x86_decl(&decl);
}
#[test]
fn test_x86_sema_check_full_tu() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let tu = TranslationUnit {
decls: vec![
Decl::Variable(VarDecl {
name: "g".to_string(),
ty: QualType::int(),
init: None,
linkage: Linkage::External,
is_global: true,
is_extern: false,
is_static: false,
}),
Decl::Function(make_func_decl("main", QualType::int(), vec![])),
],
filename: "test.c".to_string(),
};
x86_sema.check_x86_tu(&tu);
assert!(!x86_sema.has_x86_errors());
}
#[test]
fn test_type_checker_max_vector_sse2_only() {
let st = pentium4_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.max_vector_size, 16);
}
#[test]
fn test_type_checker_sizeof_unknown_type() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.sizeof_type_name("__float256"), None);
assert_eq!(tc.sizeof_type_name("bogus"), None);
}
#[test]
fn test_type_checker_alignof_unknown_type() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.alignof_type_name("bogus"), None);
}
#[test]
fn test_type_checker_check_type_long_on_lp64() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
tc.check_type(&QualType::long(), &mut x86_sema);
}
#[test]
fn test_type_checker_float80_on_no_x87() {
let st = X86Subtarget::new("i386", "i386-unknown-linux-gnu", "-x87");
let tc = X86TypeChecker::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
tc.check_type(
&QualType {
base: TypeNode::LongDouble,
is_const: false,
is_volatile: false,
is_restrict: false,
},
&mut x86_sema,
);
}
#[test]
fn test_type_checker_pointer_size_mismatch() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
tc.check_type(
&QualType {
base: TypeNode::Pointer(Box::new(QualType::void())),
is_const: false,
is_volatile: false,
is_restrict: false,
},
&mut x86_sema,
);
}
#[test]
fn test_type_checker_array_elem_recurse() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
tc.check_type(
&QualType {
base: TypeNode::Array {
elem: Box::new(TypeNode::Int),
size: 10,
},
is_const: false,
is_volatile: false,
is_restrict: false,
},
&mut x86_sema,
);
}
#[test]
fn test_type_checker_function_type_recurse() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
tc.check_type(
&QualType {
base: TypeNode::Function {
ret: Box::new(TypeNode::Int),
params: vec![Box::new(TypeNode::Float)],
is_vararg: false,
},
is_const: false,
is_volatile: false,
is_restrict: false,
},
&mut x86_sema,
);
}
#[test]
fn test_type_checker_struct_type_recurse() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
tc.check_type(
&QualType {
base: TypeNode::Struct {
name: Some("S").to_string(),
fields: vec![FieldDecl::new("f", QualType::double_())],
is_union: false,
},
is_const: false,
is_volatile: false,
is_restrict: false,
},
&mut x86_sema,
);
}
#[test]
fn test_builtin_checker_all_sse_arithmetic_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sse_ops = [
"__builtin_ia32_addps",
"__builtin_ia32_subps",
"__builtin_ia32_mulps",
"__builtin_ia32_divps",
"__builtin_ia32_sqrtps",
];
for name in &sse_ops {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_sse_comparison_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sse_cmp = [
"__builtin_ia32_comieq_ss",
"__builtin_ia32_comilt_ss",
"__builtin_ia32_comile_ss",
"__builtin_ia32_comigt_ss",
"__builtin_ia32_comige_ss",
"__builtin_ia32_comineq_ss",
];
for name in &sse_cmp {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_sse_ucomi_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let ucomi = [
"__builtin_ia32_ucomieq_ss",
"__builtin_ia32_ucomilt_ss",
"__builtin_ia32_ucomile_ss",
"__builtin_ia32_ucomigt_ss",
"__builtin_ia32_ucomige_ss",
"__builtin_ia32_ucomineq_ss",
];
for name in &ucomi {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_sse_bitwise_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let bitwise = [
"__builtin_ia32_andps",
"__builtin_ia32_andnps",
"__builtin_ia32_orps",
"__builtin_ia32_xorps",
];
for name in &bitwise {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_sse2_arithmetic_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sse2_ops = [
"__builtin_ia32_addpd",
"__builtin_ia32_subpd",
"__builtin_ia32_mulpd",
"__builtin_ia32_divpd",
"__builtin_ia32_sqrtpd",
];
for name in &sse2_ops {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_sse2_scalar_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let scalar = [
"__builtin_ia32_addsd",
"__builtin_ia32_subsd",
"__builtin_ia32_mulsd",
"__builtin_ia32_divsd",
"__builtin_ia32_sqrtsd",
];
for name in &scalar {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_sse2_bitwise_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let bitwise = [
"__builtin_ia32_andpd",
"__builtin_ia32_andnpd",
"__builtin_ia32_orpd",
"__builtin_ia32_xorpd",
];
for name in &bitwise {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_sse3_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sse3 = [
"__builtin_ia32_haddps",
"__builtin_ia32_haddpd",
"__builtin_ia32_hsubps",
"__builtin_ia32_hsubpd",
"__builtin_ia32_addsubps",
"__builtin_ia32_addsubpd",
];
for name in &sse3 {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_ssse3_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let ssse3 = [
"__builtin_ia32_phaddw",
"__builtin_ia32_phaddd",
"__builtin_ia32_phaddsw",
"__builtin_ia32_pshufb",
"__builtin_ia32_psignb",
"__builtin_ia32_psignw",
"__builtin_ia32_psignd",
"__builtin_ia32_pabsb",
"__builtin_ia32_pabsw",
"__builtin_ia32_pabsd",
];
for name in &ssse3 {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_sse41_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sse41 = [
"__builtin_ia32_blendvps",
"__builtin_ia32_blendvpd",
"__builtin_ia32_blendps",
"__builtin_ia32_blendpd",
"__builtin_ia32_dpps",
"__builtin_ia32_dppd",
"__builtin_ia32_roundps",
"__builtin_ia32_roundpd",
];
for name in &sse41 {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_sse42_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sse42 = [
"__builtin_ia32_crc32qi",
"__builtin_ia32_crc32hi",
"__builtin_ia32_crc32si",
"__builtin_ia32_crc32di",
];
for name in &sse42 {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_aes_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let aes = [
"__builtin_ia32_aesenc128",
"__builtin_ia32_aesenclast128",
"__builtin_ia32_aesdec128",
"__builtin_ia32_aesdeclast128",
"__builtin_ia32_aesimc128",
];
for name in &aes {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_sha_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sha = [
"__builtin_ia32_sha1rnds4",
"__builtin_ia32_sha1nexte",
"__builtin_ia32_sha1msg1",
"__builtin_ia32_sha1msg2",
"__builtin_ia32_sha256rnds2",
"__builtin_ia32_sha256msg1",
"__builtin_ia32_sha256msg2",
];
for name in &sha {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_avx_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let avx = [
"__builtin_ia32_addps256",
"__builtin_ia32_addpd256",
"__builtin_ia32_subps256",
"__builtin_ia32_subpd256",
"__builtin_ia32_mulps256",
"__builtin_ia32_mulpd256",
"__builtin_ia32_divps256",
"__builtin_ia32_divpd256",
"__builtin_ia32_sqrtps256",
"__builtin_ia32_sqrtpd256",
];
for name in &avx {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_fma_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let fma = [
"__builtin_ia32_vfmaddps",
"__builtin_ia32_vfmaddpd",
"__builtin_ia32_vfmaddps256",
"__builtin_ia32_vfmaddpd256",
];
for name in &fma {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_bmi_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let bmi = [
"__builtin_ia32_bextr_u32",
"__builtin_ia32_bextr_u64",
"__builtin_ia32_blsi_u32",
"__builtin_ia32_blsi_u64",
"__builtin_ia32_tzcnt_u32",
"__builtin_ia32_tzcnt_u64",
];
for name in &bmi {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_bmi2_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let bmi2 = [
"__builtin_ia32_bzhi_si",
"__builtin_ia32_bzhi_di",
"__builtin_ia32_pdep_si",
"__builtin_ia32_pdep_di",
"__builtin_ia32_pext_si",
"__builtin_ia32_pext_di",
];
for name in &bmi2 {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_rdrand_rdseed_present() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let rr = [
"__builtin_ia32_rdrand16_step",
"__builtin_ia32_rdrand32_step",
"__builtin_ia32_rdrand64_step",
"__builtin_ia32_rdseed16_step",
"__builtin_ia32_rdseed32_step",
"__builtin_ia32_rdseed64_step",
];
for name in &rr {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_all_avx512f_present() {
let st = skx_subtarget();
let bc = X86BuiltinChecker::new(st);
let avx512f = [
"__builtin_ia32_addps512",
"__builtin_ia32_addpd512",
"__builtin_ia32_subps512",
"__builtin_ia32_subpd512",
"__builtin_ia32_mulps512",
"__builtin_ia32_mulpd512",
"__builtin_ia32_divps512",
"__builtin_ia32_divpd512",
];
for name in &avx512f {
assert!(bc.get_signature(name).is_some(), "missing {}", name);
}
}
#[test]
fn test_builtin_checker_feature_availability_haswell() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
assert!(bc.subtarget_has_feature("sse"));
assert!(bc.subtarget_has_feature("sse2"));
assert!(bc.subtarget_has_feature("sse3"));
assert!(bc.subtarget_has_feature("ssse3"));
assert!(bc.subtarget_has_feature("sse41"));
assert!(bc.subtarget_has_feature("sse42"));
assert!(bc.subtarget_has_feature("avx"));
assert!(bc.subtarget_has_feature("avx2"));
assert!(bc.subtarget_has_feature("fma"));
assert!(bc.subtarget_has_feature("bmi"));
assert!(bc.subtarget_has_feature("bmi2"));
assert!(bc.subtarget_has_feature("aes"));
assert!(bc.subtarget_has_feature("lzcnt"));
assert!(bc.subtarget_has_feature("popcnt"));
assert!(bc.subtarget_has_feature("rdrnd"));
assert!(!bc.subtarget_has_feature("avx512f"));
}
#[test]
fn test_builtin_checker_cvt_conversions_have_correct_64bit_flag() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
assert!(
bc.get_signature("__builtin_ia32_cvtss2si")
.unwrap()
.is_64bit_only
== false
);
assert!(
bc.get_signature("__builtin_ia32_cvtss2si64")
.unwrap()
.is_64bit_only
== true
);
assert!(
bc.get_signature("__builtin_ia32_cvtsi2ss")
.unwrap()
.is_64bit_only
== false
);
assert!(
bc.get_signature("__builtin_ia32_cvtsi642ss")
.unwrap()
.is_64bit_only
== true
);
}
#[test]
fn test_intrinsic_sema_all_sse_vector_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let sse = ["_mm_add_ps", "_mm_sub_ps", "_mm_mul_ps", "_mm_div_ps"];
for name in &sse {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("sse"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_all_sse_comparisons() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let cmps = [
"_mm_cmpeq_ps",
"_mm_cmplt_ps",
"_mm_cmple_ps",
"_mm_cmpgt_ps",
"_mm_cmpge_ps",
"_mm_cmpneq_ps",
"_mm_cmpnlt_ps",
"_mm_cmpnle_ps",
"_mm_cmpngt_ps",
"_mm_cmpnge_ps",
"_mm_cmpord_ps",
"_mm_cmpunord_ps",
];
for name in &cmps {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("sse"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_all_sse_load_store() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let mem = [
"_mm_load_ps",
"_mm_loadu_ps",
"_mm_load_ss",
"_mm_store_ps",
"_mm_storeu_ps",
"_mm_store_ss",
];
for name in &mem {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("sse"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_all_sse2_pd_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let pd = ["_mm_add_pd", "_mm_sub_pd", "_mm_mul_pd", "_mm_div_pd"];
for name in &pd {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("sse2"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_all_sse2_load_store() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let mem = [
"_mm_load_pd",
"_mm_loadu_pd",
"_mm_load_sd",
"_mm_store_pd",
"_mm_storeu_pd",
"_mm_store_sd",
];
for name in &mem {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("sse2"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_all_sse2_fences() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let fences = ["_mm_lfence", "_mm_mfence", "_mm_clflush", "_mm_pause"];
for name in &fences {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("sse2"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_all_sse3_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let sse3 = [
"_mm_hadd_ps",
"_mm_hsub_ps",
"_mm_addsub_ps",
"_mm_hadd_pd",
"_mm_hsub_pd",
"_mm_addsub_pd",
"_mm_movehdup_ps",
"_mm_moveldup_ps",
"_mm_lddqu_si128",
];
for name in &sse3 {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("sse3"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_all_ssse3_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let ssse3 = [
"_mm_hadd_epi16",
"_mm_hadd_epi32",
"_mm_hadds_epi16",
"_mm_hsub_epi16",
"_mm_hsub_epi32",
"_mm_hsubs_epi16",
"_mm_shuffle_epi8",
"_mm_sign_epi8",
"_mm_sign_epi16",
"_mm_sign_epi32",
"_mm_abs_epi8",
"_mm_abs_epi16",
"_mm_abs_epi32",
"_mm_alignr_epi8",
];
for name in &ssse3 {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("ssse3"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_all_sse41_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let sse41 = [
"_mm_blendv_ps",
"_mm_blendv_pd",
"_mm_blend_ps",
"_mm_blend_pd",
"_mm_dp_ps",
"_mm_dp_pd",
"_mm_round_ps",
"_mm_round_pd",
"_mm_min_epi8",
"_mm_min_epi32",
"_mm_max_epi8",
"_mm_max_epi32",
"_mm_mullo_epi32",
"_mm_mul_epi32",
"_mm_testz_si128",
"_mm_packus_epi32",
"_mm_extract_ps",
"_mm_insert_ps",
];
for name in &sse41 {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("sse41"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_all_avx_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let avx = [
"_mm256_add_ps",
"_mm256_sub_ps",
"_mm256_mul_ps",
"_mm256_div_ps",
"_mm256_sqrt_ps",
"_mm256_and_ps",
"_mm256_or_ps",
"_mm256_xor_ps",
"_mm256_load_ps",
"_mm256_store_ps",
];
for name in &avx {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("avx"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_all_avx2_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let avx2 = [
"_mm256_add_epi8",
"_mm256_add_epi16",
"_mm256_add_epi32",
"_mm256_add_epi64",
"_mm256_sub_epi32",
"_mm256_mullo_epi32",
"_mm256_and_si256",
"_mm256_or_si256",
"_mm256_xor_si256",
];
for name in &avx2 {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("avx2"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_all_fma_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let fma = [
"_mm_fmadd_ps",
"_mm_fmadd_pd",
"_mm_fmadd_ss",
"_mm_fmadd_sd",
"_mm_fmsub_ps",
"_mm_fmsub_pd",
"_mm_fnmadd_ps",
"_mm_fnmadd_pd",
"_mm_fnmsub_ps",
"_mm_fnmsub_pd",
"_mm_fmaddsub_ps",
"_mm_fmaddsub_pd",
];
for name in &fma {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("fma"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_all_avx512f_ops() {
let st = skx_subtarget();
let is = X86IntrinsicSema::new(st);
let avx512f = [
"_mm512_add_ps",
"_mm512_add_pd",
"_mm512_sub_ps",
"_mm512_sub_pd",
"_mm512_mul_ps",
"_mm512_mul_pd",
"_mm512_div_ps",
"_mm512_div_pd",
"_mm512_sqrt_ps",
"_mm512_sqrt_pd",
];
for name in &avx512f {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("avx512f"),
"{}",
name
);
}
}
#[test]
fn test_abi_cc_validity_display() {
assert_eq!(CCValidity::Valid as i32, CCValidity::Valid as i32);
}
#[test]
fn test_abi_checker_validate_pascal() {
let st = pentium4_subtarget();
let ac = X86ABIChecker::new(st);
assert_eq!(ac.validate_cc("pascal"), CCValidity::Requires32Bit);
}
#[test]
fn test_abi_checker_validate_regcall_32bit() {
let st = pentium4_subtarget();
let ac = X86ABIChecker::new(st);
assert_eq!(ac.validate_cc("regcall"), CCValidity::Valid);
}
#[test]
fn test_abi_checker_sysv_ms_compat() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
let mut sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st.clone());
let result = ac.check_sysv_vs_ms(&mut x86_sema);
assert!(result.compatible);
}
#[test]
fn test_abi_checker_known_conventions_count() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
let convs = ac.known_conventions();
assert!(convs.len() >= 8);
}
#[test]
fn test_abi_checker_struct_single_int_not_sret() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
let ty = QualType::int();
assert!(!ac.struct_needs_sret(&ty, "sysv"));
assert!(!ac.struct_needs_sret(&ty, "ms_abi"));
}
#[test]
fn test_alignment_checker_default_values() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
assert_eq!(ac.vector128_alignment, 16);
assert_eq!(ac.vector256_alignment, 32);
assert_eq!(ac.vector512_alignment, 64);
assert!(ac.stack_alignment >= 16);
}
#[test]
fn test_alignment_checker_stack_alignment_32bit() {
let st = pentium4_subtarget();
let ac = X86AlignmentChecker::new(st);
assert_eq!(ac.stack_alignment, 16);
}
#[test]
fn test_alignment_checker_preferred_alignment_edge_cases() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
assert_eq!(ac.preferred_alignment(3), 4);
assert_eq!(ac.preferred_alignment(7), 8);
assert_eq!(ac.preferred_alignment(15), 16);
assert_eq!(ac.preferred_alignment(17), 32);
assert_eq!(ac.preferred_alignment(31), 32);
assert_eq!(ac.preferred_alignment(33), 64);
assert_eq!(ac.preferred_alignment(63), 64);
}
#[test]
fn test_alignment_checker_max_alignment_no_sse() {
let st = X86Subtarget::new("i386", "i386-unknown-linux-gnu", "-sse");
let ac = X86AlignmentChecker::new(st);
assert_eq!(ac.max_alignment, 8);
}
#[test]
fn test_vector_size_checker_all_powers_of_two() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
for size in &[1, 2, 4, 8, 16, 32] {
assert!(
vsc.check_vector_size(*size, &mut sema).is_ok(),
"size={}",
size
);
}
}
#[test]
fn test_vector_size_checker_no_avx_256bit_fails() {
let st = pentium4_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
assert!(vsc.check_vector_size(32, &mut sema).is_err());
}
#[test]
fn test_vector_size_checker_no_avx512_512bit_fails() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
assert!(vsc.check_vector_size(64, &mut sema).is_err());
}
#[test]
fn test_vector_size_checker_should_use_ext_vector_all() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
assert!(vsc.should_use_ext_vector(1));
assert!(vsc.should_use_ext_vector(2));
assert!(vsc.should_use_ext_vector(4));
assert!(vsc.should_use_ext_vector(8));
assert!(vsc.should_use_ext_vector(16));
assert!(vsc.should_use_ext_vector(32));
assert!(!vsc.should_use_ext_vector(0));
assert!(!vsc.should_use_ext_vector(3));
}
#[test]
fn test_vector_size_checker_preferred_width_bytes() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let width_bytes = vsc.preferred_vector_width_bytes();
let width_bits = vsc.preferred_vector_width_bits();
assert_eq!(width_bytes, width_bits / 8);
}
#[test]
fn test_vector_size_checker_recommended_size_edge() {
let st = skx_subtarget();
let vsc = X86VectorSizeChecker::new(st);
assert_eq!(vsc.recommended_vector_size(1, 1), 1);
assert_eq!(vsc.recommended_vector_size(1, 5), 8);
assert_eq!(vsc.recommended_vector_size(4, 8), 32);
assert_eq!(vsc.recommended_vector_size(8, 16), 64); }
#[test]
fn test_target_attr_all_architectures_recognized() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
for arch in &[
"x86-64",
"x86-64-v2",
"x86-64-v3",
"x86-64-v4",
"i386",
"i686",
] {
assert!(tac.is_known_arch(arch), "arch {}", arch);
}
}
#[test]
fn test_target_attr_amd_cpus_recognized() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
for cpu in &["znver1", "znver2", "znver3", "znver4", "znver5"] {
assert!(tac.is_known_cpu(cpu), "cpu {}", cpu);
}
}
#[test]
fn test_target_attr_intel_cpus_recognized() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
for cpu in &[
"core2",
"nehalem",
"haswell",
"skylake",
"icelake-client",
"sapphirerapids",
] {
assert!(tac.is_known_cpu(cpu), "cpu {}", cpu);
}
}
#[test]
fn test_target_attr_validate_empty() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let warnings = tac.validate_target_attr("");
assert!(warnings.is_empty());
}
#[test]
fn test_target_attr_validate_all_valid() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let warnings = tac.validate_target_attr("arch=x86-64-v3,cpu=haswell,+avx2");
assert!(warnings.is_empty());
}
#[test]
fn test_target_attr_unknown_arch_warns() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let warnings = tac.validate_target_attr("arch=arm64");
assert!(!warnings.is_empty());
}
#[test]
fn test_target_attr_unknown_cpu_warns() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let warnings = tac.validate_target_attr("cpu=magic");
assert!(!warnings.is_empty());
}
#[test]
fn test_target_attr_unknown_tune_warns() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let warnings = tac.validate_target_attr("tune=magic");
assert!(!warnings.is_empty());
}
#[test]
fn test_target_attr_parse_strips_whitespace() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let attrs = tac.parse_target_attr(" arch= x86-64 , cpu = haswell ");
assert_eq!(attrs.len(), 2);
}
#[test]
fn test_target_attr_graniterapids_known() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
assert!(tac.is_known_cpu("graniterapids"));
}
#[test]
fn test_target_attr_amx_features_known() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
assert!(tac.is_known_feature("amx-bf16"));
assert!(tac.is_known_feature("amx-int8"));
assert!(tac.is_known_feature("amx-tile"));
assert!(tac.is_known_feature("amx-fp16"));
}
#[test]
fn test_target_attr_gfni_vaes_known() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
assert!(tac.is_known_feature("vaes"));
assert!(tac.is_known_feature("vpclmulqdq"));
assert!(tac.is_known_feature("gfni"));
}
#[test]
fn test_target_attr_avx512_subfeatures_known() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
for feat in &[
"avx512cd",
"avx512bw",
"avx512dq",
"avx512vl",
"avx512vbmi",
"avx512vbmi2",
"avx512vnni",
"avx512bf16",
] {
assert!(tac.is_known_feature(feat), "feature {}", feat);
}
}
#[test]
fn test_format_attr_style_nslog_not_x86() {
assert!(!FormatStyle::NSLog.is_valid_on_x86());
}
#[test]
fn test_format_attr_style_oslog_not_x86() {
assert!(!FormatStyle::OSLog.is_valid_on_x86());
}
#[test]
fn test_format_attr_style_scanf() {
let fs = FormatStyle::from_str("__scanf__");
assert_eq!(fs, FormatStyle::Scanf);
}
#[test]
fn test_format_attr_style_printf() {
let fs = FormatStyle::from_str("__printf__");
assert_eq!(fs, FormatStyle::Printf);
}
#[test]
fn test_format_attr_size_t_32bit() {
let fc = X86FormatAttrChecker::new_32bit();
assert_eq!(fc.size_t_size, 4);
assert_eq!(fc.ptrdiff_t_size, 4);
}
#[test]
fn test_format_attr_size_t_64bit() {
let fc = X86FormatAttrChecker::new_64bit();
assert_eq!(fc.size_t_size, 8);
assert_eq!(fc.ptrdiff_t_size, 8);
}
#[test]
fn test_format_attr_int_length_modifiers_all() {
let fc = X86FormatAttrChecker::new_64bit();
for m in &["", "hh", "h", "l", "ll", "z", "t", "j"] {
assert!(fc.check_int_length_modifier(m).is_ok(), "modifier {}", m);
}
}
#[test]
fn test_format_attr_float_length_modifiers_all() {
let fc = X86FormatAttrChecker::new_64bit();
for m in &["", "L", "l"] {
assert!(fc.check_float_length_modifier(m).is_ok(), "modifier {}", m);
}
}
#[test]
fn test_format_attr_unknown_specifier_errors() {
let fc = X86FormatAttrChecker::new_64bit();
assert!(fc.check_format_specifier('q', "").is_err());
assert!(fc.check_format_specifier('@', "").is_err());
}
#[test]
fn test_format_attr_warn_percent_n_configurable() {
let mut fc = X86FormatAttrChecker::new_64bit();
fc.warn_on_percent_n = false;
assert!(fc.check_format_specifier('n', "").is_ok());
fc.warn_on_percent_n = true;
assert!(fc.check_format_specifier('n', "").is_err());
}
#[test]
fn test_format_attr_wchar_t_size_32bit() {
let fc = X86FormatAttrChecker::new_32bit();
assert_eq!(fc.wchar_t_size, 4);
}
#[test]
fn test_cc_checker_win64_vectorcall_forward() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("win64", "vectorcall");
assert_eq!(result.compat, CCCompat::ForwardCompatible);
}
#[test]
fn test_cc_checker_vectorcall_win64_needs_thunk() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("vectorcall", "win64");
assert_eq!(result.compat, CCCompat::NeedsThunk);
}
#[test]
fn test_cc_checker_preserve_most_compatible_all() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("preserve_most", "sysv");
assert_eq!(result.compat, CCCompat::Compatible);
}
#[test]
fn test_cc_checker_32bit_cdecl_stdcall_needs_thunk() {
let st = pentium4_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("cdecl", "stdcall");
assert_eq!(result.compat, CCCompat::NeedsThunk);
}
#[test]
fn test_cc_checker_32bit_stdcall_cdecl_compatible() {
let st = pentium4_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("stdcall", "cdecl");
assert_eq!(result.compat, CCCompat::Compatible);
}
#[test]
fn test_cc_checker_32bit_thiscall_stdcall_needs_thunk() {
let st = pentium4_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("thiscall", "stdcall");
assert_eq!(result.compat, CCCompat::NeedsThunk);
}
#[test]
fn test_cc_checker_explanation_format() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("sysv", "win64");
assert!(!result.explanation.is_empty());
assert!(result.explanation.contains("incompatible"));
}
#[test]
fn test_x86_sema_all_checkers_initialized() {
let st = haswell_subtarget();
let sema = make_sema();
let x86_sema = X86Sema::new(sema, st);
assert!(x86_sema.type_checker.max_vector_size > 0);
assert!(x86_sema.builtin_checker.num_builtins() > 0);
assert!(!x86_sema.abi_checker.known_conventions().is_empty());
assert!(x86_sema.alignment_checker.stack_alignment > 0);
assert!(x86_sema.intrinsic_sema.num_intrinsics() > 0);
assert!(x86_sema.vector_size_checker.limits.max > 0);
}
#[test]
fn test_x86_sema_builtin_and_intrinsic_distinct() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st.clone());
let is = X86IntrinsicSema::new(st);
for name in bc.builtin_names() {
assert!(name.starts_with("__builtin_ia32_"));
}
for name in is.intrinsic_names() {
assert!(
name.starts_with("_mm_")
|| name.starts_with("_mm256_")
|| name.starts_with("_mm512_")
);
}
}
#[test]
fn test_x86_sema_type_checker_and_alignment_consistent() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st.clone());
let ac = X86AlignmentChecker::new(st);
assert_eq!(ac.vector128_alignment, 16);
assert_eq!(tc.max_vector_size, ac.max_alignment);
}
#[test]
fn test_x86_sema_target_attrs_consistent_with_features() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st.clone());
let bc = X86BuiltinChecker::new(st);
for feat in &["sse", "sse2", "avx", "avx2", "fma"] {
assert!(tac.is_known_feature(feat));
assert!(bc.subtarget_has_feature(feat));
}
}
#[test]
fn test_stress_builtin_checker_all_names_valid_format() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
for name in bc.builtin_names() {
assert!(!name.is_empty());
assert!(name.starts_with("__builtin_"));
let sig = bc.get_signature(name).unwrap();
assert!(!sig.return_type.is_empty());
assert!(!sig.args.is_empty() || sig.name.contains("step") || sig.name.contains("sqrt"));
}
}
#[test]
fn test_stress_intrinsic_sema_all_names_valid_format() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
for name in is.intrinsic_names() {
assert!(!name.is_empty());
assert!(name.starts_with("_mm"));
let group = is.lookup_group(name);
assert!(group.is_some(), "intrinsic {} has no group", name);
}
}
#[test]
fn test_stress_target_attr_no_panic_on_arbitrary_input() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
for input in &[
"",
" ",
",,,",
"arch=,cpu=,tune=,+,-,unknown",
"a=b=c=d",
"++++++++",
"------",
"arch=@#$%",
] {
let _attrs = tac.parse_target_attr(input);
let _warnings = tac.validate_target_attr(input);
}
}
#[test]
fn test_stress_abi_checker_no_panic_on_arbitrary_input() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
for caller in &["", "___", "123", "a very long string with spaces"] {
for callee in &["", "___", "456"] {
let _ = cc.check_compatibility(caller, callee);
}
}
}
#[test]
fn test_stress_vector_size_checker_no_panic() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
for size in 0u32..=256 {
let _ = vsc.check_vector_size(size, &mut sema);
}
}
#[test]
fn test_stress_format_checker_all_specifiers() {
let fc = X86FormatAttrChecker::new_64bit();
for c in "diouxXfFeEgGaAcspn%z".chars() {
let _ = fc.check_format_specifier(c, "");
let _ = fc.check_format_specifier(c, "l");
let _ = fc.check_format_specifier(c, "ll");
let _ = fc.check_format_specifier(c, "h");
let _ = fc.check_format_specifier(c, "z");
}
}
#[test]
fn test_regression_empty_tu_no_panic() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let tu = TranslationUnit {
decls: vec![],
filename: String::new(),
};
x86_sema.check_x86_tu(&tu);
}
#[test]
fn test_regression_builtin_with_no_params() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl("__builtin_ia32_sqrtps", QualType::int(), vec![]);
x86_sema
.builtin_checker
.check_builtin_call(&fd, &mut x86_sema);
assert!(x86_sema.has_x86_errors());
}
#[test]
fn test_regression_vector_size_checker_1_is_valid() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
assert!(vsc.check_vector_size(1, &mut sema).is_ok());
}
#[test]
fn test_regression_alignment_zero_is_error() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
assert!(ac.check_aligned_attribute(0).is_err());
}
#[test]
fn test_regression_format_style_roundtrip_stability() {
for s in &["printf", "scanf", "strftime", "strfmon", "NSLog", "OSLog"] {
let fs = FormatStyle::from_str(s);
let rt = fs.as_str();
if fs != FormatStyle::Unknown {
let fs2 = FormatStyle::from_str(rt);
assert_eq!(fs, fs2, "roundtrip failed for {}", s);
}
}
}
#[test]
fn test_amd_znver1_features() {
let st = X86Subtarget::new("znver1", "x86_64-unknown-linux-gnu", "");
let tc = X86TypeChecker::new(st.clone());
assert_eq!(tc.max_vector_size, 32);
let bc = X86BuiltinChecker::new(st.clone());
assert!(bc.subtarget_has_feature("avx2"));
assert!(bc.subtarget_has_feature("sse"));
assert!(bc.subtarget_has_feature("fma"));
}
#[test]
fn test_amd_znver4_avx512() {
let st = X86Subtarget::new("znver4", "x86_64-unknown-linux-gnu", "");
let tc = X86TypeChecker::new(st.clone());
assert_eq!(tc.max_vector_size, 64);
let bc = X86BuiltinChecker::new(st.clone());
assert!(bc.subtarget_has_feature("avx512f"));
}
#[test]
fn test_intel_knl_features() {
let st = X86Subtarget::new("knl", "x86_64-unknown-linux-gnu", "");
let tc = X86TypeChecker::new(st.clone());
assert_eq!(tc.max_vector_size, 64);
let bc = X86BuiltinChecker::new(st);
assert!(bc.subtarget_has_feature("avx512f"));
}
#[test]
fn test_intel_atom_features() {
let st = X86Subtarget::new("silvermont", "x86_64-unknown-linux-gnu", "");
let tc = X86TypeChecker::new(st.clone());
assert_eq!(tc.max_vector_size, 16);
let bc = X86BuiltinChecker::new(st);
assert!(bc.subtarget_has_feature("sse42"));
assert!(!bc.subtarget_has_feature("avx"));
}
#[test]
fn test_lakemont_no_sse() {
let st = X86Subtarget::new("i486", "i486-unknown-linux-gnu", "");
let tc = X86TypeChecker::new(st);
assert_eq!(tc.max_vector_size, 8);
}
#[test]
fn test_builtin_checker_crc32_di_is_64bit_only() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_crc32di").unwrap();
assert!(sig.is_64bit_only);
}
#[test]
fn test_builtin_checker_sha_imm_range() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_sha1rnds4").unwrap();
let imm = &sig.args[2];
assert!(imm.is_immediate);
assert_eq!(imm.imm_min, Some(0));
assert_eq!(imm.imm_max, Some(3));
}
#[test]
fn test_builtin_checker_aes_imm_range() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc
.get_signature("__builtin_ia32_aeskeygenassist128")
.unwrap();
let imm = &sig.args[1];
assert!(imm.is_immediate);
assert_eq!(imm.imm_min, Some(0));
assert_eq!(imm.imm_max, Some(255));
}
#[test]
fn test_builtin_checker_ssse3_palignr_imm_range() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_palignr").unwrap();
let imm = &sig.args[2];
assert!(imm.is_immediate);
assert_eq!(imm.imm_min, Some(0));
assert_eq!(imm.imm_max, Some(31));
}
#[test]
fn test_builtin_checker_sse_shufps_imm_range() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_shufps").unwrap();
let imm = &sig.args[2];
assert!(imm.is_immediate);
assert_eq!(imm.imm_min, Some(0));
assert_eq!(imm.imm_max, Some(255));
}
#[test]
fn test_builtin_checker_sse2_shufpd_imm_range() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_shufpd").unwrap();
let imm = &sig.args[2];
assert!(imm.is_immediate);
assert_eq!(imm.imm_min, Some(0));
assert_eq!(imm.imm_max, Some(3));
}
#[test]
fn test_builtin_checker_gather_scale_imm() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_gatherdps").unwrap();
let imm = &sig.args[3];
assert!(imm.is_immediate);
assert_eq!(imm.imm_min, Some(1));
assert_eq!(imm.imm_max, Some(8));
}
#[test]
fn test_builtin_checker_sse41_blend_imm_ranges() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig_ps = bc.get_signature("__builtin_ia32_blendps").unwrap();
assert_eq!(sig_ps.args[2].imm_max, Some(15));
let sig_pd = bc.get_signature("__builtin_ia32_blendpd").unwrap();
assert_eq!(sig_pd.args[2].imm_max, Some(3));
}
#[test]
fn test_builtin_checker_arg_types_matter() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_addps").unwrap();
assert_eq!(sig.args[0].expected_type, "V4F32");
assert_eq!(sig.args[1].expected_type, "V4F32");
assert!(sig.args[0].any_vector);
}
#[test]
fn test_builtin_checker_cvt_int_arg_is_not_vector() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_cvtsi2ss").unwrap();
assert!(!sig.args[1].any_vector); assert_eq!(sig.args[1].expected_type, "i32");
}
#[test]
fn test_builtin_checker_crc32_arg_is_not_vector() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_crc32si").unwrap();
assert_eq!(sig.args[0].expected_type, "i32");
assert_eq!(sig.args[1].expected_type, "i32");
}
#[test]
fn test_builtin_checker_rdrand_arg_is_pointer() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_rdrand16_step").unwrap();
assert_eq!(sig.args[0].expected_type, "ptr");
}
#[test]
fn test_intrinsic_sema_sse_on_pentium4_ok() {
let st = pentium4_subtarget();
let is = X86IntrinsicSema::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl(
"_mm_add_ps",
QualType::int(),
vec![
make_param("a", QualType::int()),
make_param("b", QualType::int()),
],
);
is.check_intrinsic(&fd, &mut x86_sema);
assert!(!x86_sema.has_x86_errors());
}
#[test]
fn test_intrinsic_sema_avx_on_pentium4_fails() {
let st = pentium4_subtarget();
let is = X86IntrinsicSema::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl(
"_mm256_add_ps",
QualType::int(),
vec![
make_param("a", QualType::int()),
make_param("b", QualType::int()),
],
);
is.check_intrinsic(&fd, &mut x86_sema);
assert!(x86_sema.has_x86_errors());
}
#[test]
fn test_intrinsic_sema_avx512f_on_haswell_fails() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl(
"_mm512_add_ps",
QualType::int(),
vec![
make_param("a", QualType::int()),
make_param("b", QualType::int()),
],
);
is.check_intrinsic(&fd, &mut x86_sema);
assert!(x86_sema.has_x86_errors());
}
#[test]
fn test_intrinsic_sema_aes_on_pentium4_fails() {
let st = pentium4_subtarget();
let is = X86IntrinsicSema::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl(
"_mm_aesenc_si128",
QualType::int(),
vec![
make_param("a", QualType::int()),
make_param("b", QualType::int()),
],
);
is.check_intrinsic(&fd, &mut x86_sema);
assert!(x86_sema.has_x86_errors());
}
#[test]
fn test_intrinsic_sema_unknown_intrinsic_warns() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl("_mm_made_up_intrinsic", QualType::int(), vec![]);
is.check_intrinsic(&fd, &mut x86_sema);
assert!(x86_sema.has_x86_warnings());
}
#[test]
fn test_abi_struct_sret_sysv_two_xmm_regs() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
let ty = QualType {
base: TypeNode::Struct {
name: Some("D2").to_string(),
fields: vec![
FieldDecl::new("a", QualType::double_()),
FieldDecl::new("b", QualType::double_()),
],
is_union: false,
},
is_const: false,
is_volatile: false,
is_restrict: false,
};
assert!(!ac.struct_needs_sret(&ty, "sysv"));
}
#[test]
fn test_abi_struct_sret_sysv_large_struct() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
let ty = QualType {
base: TypeNode::Struct {
name: Some("Big").to_string(),
fields: vec![FieldDecl::new("f", QualType::long()); 10],
is_union: false,
},
is_const: false,
is_volatile: false,
is_restrict: false,
};
assert!(ac.struct_needs_sret(&ty, "sysv"));
}
#[test]
fn test_abi_struct_sret_win64_9byte_struct() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
let ty = QualType {
base: TypeNode::Struct {
name: Some("S9").to_string(),
fields: vec![
FieldDecl::new("a", QualType::long()),
FieldDecl::new("b", QualType::char()),
],
is_union: false,
},
is_const: false,
is_volatile: false,
is_restrict: false,
};
assert!(ac.struct_needs_sret(&ty, "ms_abi"));
}
#[test]
fn test_alignment_checker_global_int() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let vd = VarDecl {
name: "x".to_string(),
ty: QualType::int(),
init: None,
linkage: Linkage::External,
is_global: true,
is_extern: false,
is_static: false,
};
ac.check_global_alignment(&vd, &mut x86_sema);
}
#[test]
fn test_alignment_checker_global_vector() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let vd = VarDecl {
name: "v".to_string(),
ty: QualType {
base: TypeNode::Struct {
name: Some("__m128").to_string(),
fields: vec![FieldDecl::new("f", QualType::float_()); 4],
is_union: false,
},
is_const: false,
is_volatile: false,
is_restrict: false,
},
init: None,
linkage: Linkage::External,
is_global: true,
is_extern: false,
is_static: false,
};
ac.check_global_alignment(&vd, &mut x86_sema);
}
#[test]
fn test_type_checker_void_type() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
tc.check_type(&QualType::void(), &mut x86_sema);
assert!(!x86_sema.has_x86_errors());
}
#[test]
fn test_type_checker_bool_type() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
tc.check_type(
&QualType {
base: TypeNode::Bool,
is_const: false,
is_volatile: false,
is_restrict: false,
},
&mut x86_sema,
);
assert!(!x86_sema.has_x86_errors());
}
#[test]
fn test_type_checker_complex_type() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
tc.check_type(
&QualType {
base: TypeNode::Complex,
is_const: false,
is_volatile: false,
is_restrict: false,
},
&mut x86_sema,
);
}
#[test]
fn test_type_checker_auto_type() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st.clone());
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
tc.check_type(
&QualType {
base: TypeNode::Auto,
is_const: false,
is_volatile: false,
is_restrict: false,
},
&mut x86_sema,
);
}
#[test]
fn test_intrinsic_sema_sse_memory_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let mem = [
"_mm_load1_ps",
"_mm_loadr_ps",
"_mm_store1_ps",
"_mm_storer_ps",
"_mm_prefetch",
"_mm_sfence",
"_mm_movnt_ps",
];
for name in &mem {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("sse"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_sse2_memory_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let mem = [
"_mm_load1_pd",
"_mm_loadr_pd",
"_mm_store1_pd",
"_mm_storer_pd",
"_mm_movnt_pd",
];
for name in &mem {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("sse2"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_avx_memory_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let mem = [
"_mm256_load_ps",
"_mm256_loadu_ps",
"_mm256_load_pd",
"_mm256_loadu_pd",
"_mm256_store_ps",
"_mm256_storeu_ps",
"_mm256_store_pd",
"_mm256_storeu_pd",
"_mm256_stream_ps",
"_mm256_stream_pd",
];
for name in &mem {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("avx"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_avx_shuffle_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let shuffle = [
"_mm256_shuffle_ps",
"_mm256_shuffle_pd",
"_mm256_unpackhi_ps",
"_mm256_unpacklo_ps",
"_mm256_unpackhi_pd",
"_mm256_unpacklo_pd",
"_mm256_broadcast_ss",
"_mm256_broadcast_sd",
"_mm256_broadcast_ps",
"_mm256_broadcast_pd",
];
for name in &shuffle {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("avx"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_avx_permute_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let perm = [
"_mm256_permutevar_ps",
"_mm256_permutevar_pd",
"_mm256_permute_ps",
"_mm256_permute_pd",
"_mm256_permute2f128_ps",
"_mm256_permute2f128_pd",
"_mm256_zeroall",
"_mm256_zeroupper",
];
for name in &perm {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("avx"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_avx2_gather_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let gather = [
"_mm256_i32gather_ps",
"_mm256_i64gather_ps",
"_mm256_i32gather_pd",
"_mm256_i64gather_pd",
"_mm256_i32gather_epi32",
"_mm256_i64gather_epi32",
"_mm256_i32gather_epi64",
"_mm256_i64gather_epi64",
];
for name in &gather {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("avx2"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_avx2_shift_ops() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let shift = [
"_mm256_sllv_epi32",
"_mm256_sllv_epi64",
"_mm256_srlv_epi32",
"_mm256_srlv_epi64",
"_mm256_srav_epi32",
];
for name in &shift {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("avx2"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_avx512f_load_store_ops() {
let st = skx_subtarget();
let is = X86IntrinsicSema::new(st);
let mem = [
"_mm512_load_ps",
"_mm512_load_pd",
"_mm512_store_ps",
"_mm512_store_pd",
];
for name in &mem {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("avx512f"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sema_avx512f_shuffle_ops() {
let st = skx_subtarget();
let is = X86IntrinsicSema::new(st);
let shuffle = [
"_mm512_shuffle_ps",
"_mm512_shuffle_pd",
"_mm512_broadcastss_ps",
"_mm512_broadcastsd_pd",
"_mm512_permutevar_ps",
"_mm512_permutevar_pd",
];
for name in &shuffle {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("avx512f"),
"{}",
name
);
}
}
#[test]
fn test_format_attr_long_size_affects_format() {
let fc64 = X86FormatAttrChecker::new_64bit();
let fc32 = X86FormatAttrChecker::new_32bit();
assert_eq!(fc64.format_type_size('d', "l"), 8);
assert_eq!(fc32.format_type_size('d', "l"), 4);
}
#[test]
fn test_format_attr_intmax_t_on_32bit() {
let fc32 = X86FormatAttrChecker::new_32bit();
assert_eq!(fc32.format_type_size('d', "j"), 8);
}
#[test]
fn test_format_attr_long_double_size_differs() {
let fc64 = X86FormatAttrChecker::new_64bit();
let fc32 = X86FormatAttrChecker::new_32bit();
assert_eq!(fc64.format_type_size('f', "L"), 16);
assert_eq!(fc32.format_type_size('f', "L"), 12);
}
#[test]
fn test_type_checker_and_builtin_feature_consistency() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st.clone());
let bc = X86BuiltinChecker::new(st);
assert!(tc.max_vector_size >= 32);
assert!(bc.get_signature("__builtin_ia32_addps256").is_some());
}
#[test]
fn test_intrinsic_and_builtin_overlap() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st.clone());
let bc = X86BuiltinChecker::new(st);
assert_eq!(is.lookup_group("_mm_add_ps").unwrap(), "sse");
assert_eq!(
bc.get_signature("__builtin_ia32_addps")
.unwrap()
.required_feature
.as_deref()
.unwrap(),
"sse"
);
}
#[test]
fn test_alignment_and_vector_size_consistency() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st.clone());
let vsc = X86VectorSizeChecker::new(st);
assert!(ac.max_alignment >= vsc.limits.max);
}
#[test]
fn test_target_attrs_features_match_subtarget() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st.clone());
let bc = X86BuiltinChecker::new(st);
let features = [
"sse", "sse2", "sse3", "ssse3", "sse41", "sse42", "avx", "avx2", "fma",
];
for feat in &features {
assert!(tac.is_known_feature(feat));
assert!(bc.subtarget_has_feature(feat), "feature: {}", feat);
}
}
#[test]
fn test_vector_size_checker_large_power_of_two() {
let st = skx_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
assert!(vsc.check_vector_size(128, &mut sema).is_err());
assert!(vsc.check_vector_size(256, &mut sema).is_err());
}
#[test]
fn test_alignment_checker_very_large_alignment() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
assert!(ac.check_aligned_attribute(128).is_err());
assert!(ac.check_aligned_attribute(256).is_err());
assert!(ac.check_aligned_attribute(65536).is_err());
}
#[test]
fn test_format_attr_pointer_size_consistency() {
let fc64 = X86FormatAttrChecker::new_64bit();
let fc32 = X86FormatAttrChecker::new_32bit();
assert_eq!(fc64.format_type_size('p', ""), 8);
assert_eq!(fc32.format_type_size('p', ""), 4);
}
#[test]
fn test_format_attr_size_t_size_consistency() {
let fc64 = X86FormatAttrChecker::new_64bit();
let fc32 = X86FormatAttrChecker::new_32bit();
assert_eq!(fc64.format_type_size('u', "z"), 8);
assert_eq!(fc32.format_type_size('u', "z"), 4);
}
#[test]
fn test_cc_checker_self_compatibility_all() {
for st in &[haswell_subtarget(), pentium4_subtarget(), skx_subtarget()] {
let cc = X86CallingConvChecker::new(st.clone());
for conv in cc.known_conventions() {
let result = cc.check_compatibility(conv, conv);
assert_eq!(
result.compat,
CCCompat::Compatible,
"{} not compatible with itself",
conv
);
}
}
}
#[test]
fn doc_example_x86_sema_basic_usage() {
let subtarget = X86Subtarget::new("skylake", "x86_64-unknown-linux-gnu", "+avx2,+fma");
let base_sema = Sema::new(CLangStandard::C17);
let mut x86_sema = X86Sema::new(base_sema, subtarget);
assert_eq!(x86_sema.type_checker.max_vector_size, 32);
assert_eq!(
x86_sema
.intrinsic_sema
.lookup_group("_mm256_add_ps")
.map(|s| s.as_str()),
Some("avx")
);
assert!(!x86_sema.has_x86_errors());
}
#[test]
fn doc_example_vector_size_validation() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st.clone());
let mut sema = X86Sema::new(make_sema(), st);
assert!(vsc.check_vector_size(16, &mut sema).is_ok());
assert!(vsc.check_vector_size(64, &mut sema).is_err());
}
#[test]
fn doc_example_calling_conv_compatibility() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("sysv", "sysv");
assert_eq!(result.compat, CCCompat::Compatible);
let result = cc.check_compatibility("sysv", "win64");
assert_eq!(result.compat, CCCompat::Incompatible);
}
#[test]
fn doc_example_target_attr_validation() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let warnings = tac.validate_target_attr("arch=x86-64-v3,cpu=haswell,+avx2");
assert!(warnings.is_empty());
let warnings = tac.validate_target_attr("cpu=fakecpu");
assert!(!warnings.is_empty());
}
#[test]
fn test_large_builtin_registration_does_not_panic() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
assert!(bc.num_builtins() > 80);
}
#[test]
fn test_large_intrinsic_registration_does_not_panic() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
assert!(is.num_intrinsics() > 150);
}
#[test]
fn test_many_vector_size_checks_are_fast() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
for size in 1u32..=256 {
let _ = vsc.check_vector_size(size, &mut sema);
}
}
#[test]
fn test_many_cc_checks_are_fast() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let convs = cc.known_conventions();
for caller in &convs {
for callee in &convs {
let _ = cc.check_compatibility(caller, callee);
}
}
}
#[test]
fn smoke_test_all_checkers_instantiate_and_function() {
let subtargets = vec![
("haswell", "x86_64-unknown-linux-gnu", ""),
("skylake-avx512", "x86_64-unknown-linux-gnu", ""),
("pentium4", "i686-unknown-linux-gnu", ""),
("znver4", "x86_64-unknown-linux-gnu", ""),
("silvermont", "x86_64-unknown-linux-gnu", ""),
];
for (cpu, triple, features) in subtargets {
let st = X86Subtarget::new(cpu, triple, features);
let sema = make_sema();
let _x86_sema = X86Sema::new(sema, st);
}
}
#[test]
fn smoke_test_builtin_checker_all_subtargets() {
let subtargets = vec![
("haswell", "x86_64-unknown-linux-gnu", ""),
("pentium4", "i686-unknown-linux-gnu", ""),
("skylake-avx512", "x86_64-unknown-linux-gnu", ""),
];
for (cpu, triple, features) in subtargets {
let st = X86Subtarget::new(cpu, triple, features);
let bc = X86BuiltinChecker::new(st);
assert!(bc.num_builtins() > 0, "cpu={}", cpu);
}
}
#[test]
fn smoke_test_intrinsic_sema_all_subtargets() {
let subtargets = vec![
("haswell", "x86_64-unknown-linux-gnu", ""),
("pentium4", "i686-unknown-linux-gnu", ""),
("skylake-avx512", "x86_64-unknown-linux-gnu", ""),
];
for (cpu, triple, features) in subtargets {
let st = X86Subtarget::new(cpu, triple, features);
let is = X86IntrinsicSema::new(st);
assert!(is.num_intrinsics() > 0, "cpu={}", cpu);
}
}
#[test]
fn smoke_test_vector_size_checker_all_subtargets() {
let subtargets = vec![
("haswell", "x86_64-unknown-linux-gnu", ""),
("pentium4", "i686-unknown-linux-gnu", ""),
("skylake-avx512", "x86_64-unknown-linux-gnu", ""),
];
for (cpu, triple, features) in subtargets {
let st = X86Subtarget::new(cpu, triple, features);
let vsc = X86VectorSizeChecker::new(st.clone());
let mut sema = X86Sema::new(make_sema(), st);
assert!(vsc.check_vector_size(16, &mut sema).is_ok(), "cpu={}", cpu);
}
}
#[test]
fn test_type_sizes_lp64_comprehensive() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.sizeof_type_name("char"), Some(1));
assert_eq!(tc.sizeof_type_name("signed char"), Some(1));
assert_eq!(tc.sizeof_type_name("unsigned char"), Some(1));
assert_eq!(tc.sizeof_type_name("short"), Some(2));
assert_eq!(tc.sizeof_type_name("unsigned short"), Some(2));
assert_eq!(tc.sizeof_type_name("int"), Some(4));
assert_eq!(tc.sizeof_type_name("unsigned int"), Some(4));
assert_eq!(tc.sizeof_type_name("long"), Some(8));
assert_eq!(tc.sizeof_type_name("unsigned long"), Some(8));
assert_eq!(tc.sizeof_type_name("long long"), Some(8));
assert_eq!(tc.sizeof_type_name("unsigned long long"), Some(8));
assert_eq!(tc.sizeof_type_name("float"), Some(4));
assert_eq!(tc.sizeof_type_name("double"), Some(8));
assert_eq!(tc.sizeof_type_name("long double"), Some(16));
assert_eq!(tc.sizeof_type_name("void*"), Some(8));
assert_eq!(tc.sizeof_type_name("size_t"), Some(8));
assert_eq!(tc.sizeof_type_name("ptrdiff_t"), Some(8));
assert_eq!(tc.sizeof_type_name("wchar_t"), Some(4));
}
#[test]
fn test_type_sizes_ilp32_comprehensive() {
let st = pentium4_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.sizeof_type_name("char"), Some(1));
assert_eq!(tc.sizeof_type_name("short"), Some(2));
assert_eq!(tc.sizeof_type_name("int"), Some(4));
assert_eq!(tc.sizeof_type_name("long"), Some(4));
assert_eq!(tc.sizeof_type_name("long long"), Some(8));
assert_eq!(tc.sizeof_type_name("float"), Some(4));
assert_eq!(tc.sizeof_type_name("double"), Some(8));
assert_eq!(tc.sizeof_type_name("void*"), Some(4));
assert_eq!(tc.sizeof_type_name("size_t"), Some(4));
assert_eq!(tc.sizeof_type_name("ptrdiff_t"), Some(4));
assert_eq!(tc.sizeof_type_name("wchar_t"), Some(4));
}
#[test]
fn test_type_alignments_lp64_comprehensive() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.alignof_type_name("char"), Some(1));
assert_eq!(tc.alignof_type_name("short"), Some(2));
assert_eq!(tc.alignof_type_name("int"), Some(4));
assert_eq!(tc.alignof_type_name("float"), Some(4));
assert_eq!(tc.alignof_type_name("long"), Some(8));
assert_eq!(tc.alignof_type_name("long long"), Some(8));
assert_eq!(tc.alignof_type_name("double"), Some(8));
assert_eq!(tc.alignof_type_name("void*"), Some(8));
assert_eq!(tc.alignof_type_name("__int128"), Some(16));
assert_eq!(tc.alignof_type_name("__float128"), Some(16));
assert_eq!(tc.alignof_type_name("__float80"), Some(16));
assert_eq!(tc.alignof_type_name("size_t"), Some(8));
}
#[test]
fn test_type_alignments_ilp32_comprehensive() {
let st = pentium4_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.alignof_type_name("char"), Some(1));
assert_eq!(tc.alignof_type_name("short"), Some(2));
assert_eq!(tc.alignof_type_name("int"), Some(4));
assert_eq!(tc.alignof_type_name("float"), Some(4));
assert_eq!(tc.alignof_type_name("long"), Some(4));
assert_eq!(tc.alignof_type_name("long long"), Some(8));
assert_eq!(tc.alignof_type_name("double"), Some(8));
assert_eq!(tc.alignof_type_name("void*"), Some(4));
assert_eq!(tc.alignof_type_name("size_t"), Some(4));
}
#[test]
fn test_builtin_gather_operand_counts() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_gatherdps").unwrap();
assert_eq!(sig.args.len(), 4);
assert_eq!(sig.args[0].name, "a");
assert_eq!(sig.args[2].name, "idx");
assert_eq!(sig.args[3].name, "scale");
}
#[test]
fn test_builtin_gatherdpd_operand_counts() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_gatherdpd").unwrap();
assert_eq!(sig.args.len(), 4);
}
#[test]
fn test_builtin_all_immediates_have_ranges() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let imm_builtins = [
"__builtin_ia32_cmpps",
"__builtin_ia32_cmpss",
"__builtin_ia32_shufps",
"__builtin_ia32_cmppd",
"__builtin_ia32_cmpsd",
"__builtin_ia32_shufpd",
"__builtin_ia32_palignr",
"__builtin_ia32_blendps",
"__builtin_ia32_blendpd",
"__builtin_ia32_dpps",
"__builtin_ia32_dppd",
"__builtin_ia32_roundps",
"__builtin_ia32_roundpd",
"__builtin_ia32_aeskeygenassist128",
"__builtin_ia32_sha1rnds4",
];
for name in &imm_builtins {
let sig = bc.get_signature(name).unwrap();
let has_imm = sig.args.iter().any(|a| a.is_immediate);
assert!(has_imm, "{} should have an immediate operand", name);
}
}
#[test]
fn test_builtin_immediates_are_last_arg() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_cmpps").unwrap();
let last_idx = sig.args.len() - 1;
assert!(sig.args[last_idx].is_immediate);
}
#[test]
fn test_builtin_gather_scale_is_imm_not_last() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let sig = bc.get_signature("__builtin_ia32_gatherdps").unwrap();
assert!(sig.args[3].is_immediate);
}
#[test]
fn test_intrinsic_fma_128bit_all_variants() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let fma128 = [
"_mm_fmadd_ps",
"_mm_fmadd_pd",
"_mm_fmsub_ps",
"_mm_fmsub_pd",
"_mm_fnmadd_ps",
"_mm_fnmadd_pd",
"_mm_fnmsub_ps",
"_mm_fnmsub_pd",
"_mm_fmaddsub_ps",
"_mm_fmaddsub_pd",
"_mm_fmsubadd_ps",
"_mm_fmsubadd_pd",
];
for name in &fma128 {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("fma"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_fma_256bit_all_variants() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let fma256 = [
"_mm256_fmadd_ps",
"_mm256_fmadd_pd",
"_mm256_fmsub_ps",
"_mm256_fmsub_pd",
"_mm256_fnmadd_ps",
"_mm256_fnmadd_pd",
"_mm256_fnmsub_ps",
"_mm256_fnmsub_pd",
"_mm256_fmaddsub_ps",
"_mm256_fmaddsub_pd",
"_mm256_fmsubadd_ps",
"_mm256_fmsubadd_pd",
];
for name in &fma256 {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("fma"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_fma_scalar_variants() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let fma_scalar = [
"_mm_fmadd_ss",
"_mm_fmadd_sd",
"_mm_fmsub_ss",
"_mm_fmsub_sd",
"_mm_fnmadd_ss",
"_mm_fnmadd_sd",
"_mm_fnmsub_ss",
"_mm_fnmsub_sd",
];
for name in &fma_scalar {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("fma"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sse_comparisons_all_12() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let cmps = [
"_mm_cmpeq_ps",
"_mm_cmplt_ps",
"_mm_cmple_ps",
"_mm_cmpgt_ps",
"_mm_cmpge_ps",
"_mm_cmpneq_ps",
"_mm_cmpnlt_ps",
"_mm_cmpnle_ps",
"_mm_cmpngt_ps",
"_mm_cmpnge_ps",
"_mm_cmpord_ps",
"_mm_cmpunord_ps",
];
for name in &cmps {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("sse"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sse2_comparisons_all_12() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let cmps = [
"_mm_cmpeq_pd",
"_mm_cmplt_pd",
"_mm_cmple_pd",
"_mm_cmpgt_pd",
"_mm_cmpge_pd",
"_mm_cmpneq_pd",
"_mm_cmpnlt_pd",
"_mm_cmpnle_pd",
"_mm_cmpngt_pd",
"_mm_cmpnge_pd",
"_mm_cmpord_pd",
"_mm_cmpunord_pd",
];
for name in &cmps {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("sse2"),
"{}",
name
);
}
}
#[test]
fn test_builtin_sse_comi_variants() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let variants = [
"__builtin_ia32_comieq_ss",
"__builtin_ia32_comilt_ss",
"__builtin_ia32_comile_ss",
"__builtin_ia32_comigt_ss",
"__builtin_ia32_comige_ss",
"__builtin_ia32_comineq_ss",
];
for name in &variants {
let sig = bc.get_signature(name).unwrap();
assert_eq!(sig.return_type, "i32");
assert_eq!(sig.args.len(), 2);
}
}
#[test]
fn test_builtin_sse_ucomi_variants() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let variants = [
"__builtin_ia32_ucomieq_ss",
"__builtin_ia32_ucomilt_ss",
"__builtin_ia32_ucomile_ss",
"__builtin_ia32_ucomigt_ss",
"__builtin_ia32_ucomige_ss",
"__builtin_ia32_ucomineq_ss",
];
for name in &variants {
let sig = bc.get_signature(name).unwrap();
assert_eq!(sig.return_type, "i32");
}
}
#[test]
fn test_intrinsic_sse_conversions() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let conv = [
"_mm_cvtss_si32",
"_mm_cvtss_si64",
"_mm_cvttss_si32",
"_mm_cvttss_si64",
"_mm_cvtsi32_ss",
"_mm_cvtsi64_ss",
];
for name in &conv {
assert_eq!(
is.lookup_group(name).map(|s| s.as_str()),
Some("sse"),
"{}",
name
);
}
}
#[test]
fn test_intrinsic_sse2_conversions() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let conv = [
"_mm_cmpeq_pd", ];
for name in &conv {
assert!(is.lookup_group(name).is_some(), "{}", name);
}
}
#[test]
fn test_cc_32bit_fastcall_uses_ecx_edx() {
let st = pentium4_subtarget();
let cc = X86CallingConvChecker::new(st);
assert_eq!(cc.validate_cc("fastcall"), CCValidity::Valid);
}
#[test]
fn test_cc_32bit_thiscall_uses_ecx() {
let st = pentium4_subtarget();
let cc = X86CallingConvChecker::new(st);
assert_eq!(cc.validate_cc("thiscall"), CCValidity::Valid);
}
#[test]
fn test_cc_64bit_vectorcall_uses_xmm_ymm() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
assert_eq!(cc.validate_cc("vectorcall"), CCValidity::Valid);
}
#[test]
fn test_cc_checker_win64_vectorcall_relationship() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let fwd = cc.check_compatibility("win64", "vectorcall");
let rev = cc.check_compatibility("vectorcall", "win64");
assert_eq!(fwd.compat, CCCompat::ForwardCompatible);
assert_eq!(rev.compat, CCCompat::NeedsThunk);
}
#[test]
fn test_cc_checker_sysv_regcall_relationship() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("sysv", "regcall");
}
#[test]
fn test_cc_32bit_pascal_callee_cleans() {
let st = pentium4_subtarget();
let cc = X86CallingConvChecker::new(st);
assert_eq!(cc.validate_cc("pascal"), CCValidity::Valid);
}
#[test]
fn test_type_checker_int128_size() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.int128_size, 16);
assert!(tc.supports_int128);
}
#[test]
fn test_type_checker_int128_not_32bit() {
let st = pentium4_subtarget();
let tc = X86TypeChecker::new(st);
assert!(!tc.supports_int128);
}
#[test]
fn test_type_checker_float80_invalid_without_x87() {
let st = X86Subtarget::new("i386", "i386-unknown-linux-gnu", "-x87");
let tc = X86TypeChecker::new(st);
assert!(!tc.supports_float80);
}
#[test]
fn test_type_checker_float128_requires_sse() {
let st = X86Subtarget::new("i386", "i386-unknown-linux-gnu", "-sse");
let tc = X86TypeChecker::new(st);
assert!(!tc.supports_float128);
}
#[test]
fn test_type_checker_long_double_size_32bit() {
let st = pentium4_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.long_double_size, 12);
}
#[test]
fn test_type_checker_long_double_size_64bit() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
assert_eq!(tc.long_double_size, 16);
}
#[test]
fn test_vector_size_min_is_1() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
assert_eq!(vsc.limits.min, 1);
}
#[test]
fn test_vector_size_allowed_powers_of_two() {
let st = skx_subtarget();
let vsc = X86VectorSizeChecker::new(st);
for &s in &[1, 2, 4, 8, 16, 32, 64] {
assert!(vsc.limits.allowed_sizes.contains(&s), "size {}", s);
}
}
#[test]
fn test_vector_size_not_allowed_beyond_max() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
assert!(!vsc.limits.allowed_sizes.contains(&64));
assert!(!vsc.limits.allowed_sizes.contains(&128));
}
#[test]
fn test_format_style_strfmon_valid_on_x86() {
assert!(FormatStyle::Strfmon.is_valid_on_x86());
}
#[test]
fn test_format_style_strfmt_strfmon_parse() {
let fs = FormatStyle::from_str("__strfmon__");
assert_eq!(fs, FormatStyle::Strfmon);
}
#[test]
fn test_format_strftime_valid_on_x86() {
assert!(FormatStyle::Strftime.is_valid_on_x86());
}
#[test]
fn test_format_style_from_str_unknown_variants() {
assert_eq!(FormatStyle::from_str("__unknown__"), FormatStyle::Unknown);
assert_eq!(FormatStyle::from_str(""), FormatStyle::Unknown);
assert_eq!(FormatStyle::from_str("GNU_printf"), FormatStyle::Unknown);
}
#[test]
fn test_x86_sema_builtin_feature_missing_on_pentium4() {
let st = pentium4_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl(
"__builtin_ia32_addps256",
QualType::int(),
vec![
make_param("a", QualType::int()),
make_param("b", QualType::int()),
],
);
x86_sema
.builtin_checker
.check_builtin_call(&fd, &mut x86_sema);
assert!(x86_sema.has_x86_errors());
}
#[test]
fn test_x86_sema_builtin_64bit_on_32bit_errors() {
let st = pentium4_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl(
"__builtin_ia32_cvtss2si64",
QualType::int(),
vec![make_param("a", QualType::int())],
);
x86_sema
.builtin_checker
.check_builtin_call(&fd, &mut x86_sema);
assert!(x86_sema.has_x86_errors());
}
#[test]
fn test_x86_sema_builtin_wrong_arg_count_errors() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl(
"__builtin_ia32_addps",
QualType::int(),
vec![make_param("a", QualType::int())], );
x86_sema
.builtin_checker
.check_builtin_call(&fd, &mut x86_sema);
assert!(x86_sema.has_x86_errors());
}
#[test]
fn test_x86_sema_builtin_unknown_name_errors() {
let st = haswell_subtarget();
let sema = make_sema();
let mut x86_sema = X86Sema::new(sema, st);
let fd = make_func_decl("__builtin_ia32_fake_builtin", QualType::int(), vec![]);
x86_sema
.builtin_checker
.check_builtin_call(&fd, &mut x86_sema);
assert!(x86_sema.has_x86_errors());
}
#[test]
fn test_builtin_no_duplicate_names() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let names = bc.builtin_names();
let mut seen = std::collections::HashSet::new();
for name in &names {
assert!(seen.insert(name), "duplicate builtin: {}", name);
}
}
#[test]
fn test_intrinsic_no_duplicate_names() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let names = is.intrinsic_names();
let mut seen = std::collections::HashSet::new();
for name in &names {
assert!(seen.insert(name), "duplicate intrinsic: {}", name);
}
}
#[test]
fn test_format_style_debug() {
assert_eq!(format!("{:?}", FormatStyle::Printf), "Printf");
assert_eq!(format!("{:?}", FormatStyle::Unknown), "Unknown");
}
#[test]
fn test_cc_validity_debug() {
assert_eq!(format!("{:?}", CCValidity::Valid), "Valid");
assert_eq!(format!("{:?}", CCValidity::Unknown), "Unknown");
}
#[test]
fn test_cc_compat_debug() {
assert_eq!(format!("{:?}", CCCompat::Compatible), "Compatible");
assert_eq!(format!("{:?}", CCCompat::Incompatible), "Incompatible");
}
#[test]
fn test_target_attr_key_debug() {
assert_eq!(format!("{:?}", X86TargetAttrKey::Arch), "Arch");
assert_eq!(format!("{:?}", X86TargetAttrKey::Unknown), "Unknown");
}
#[test]
fn test_abi_result_clone() {
let r1 = X86ABIResult::valid();
let r2 = r1.clone();
assert_eq!(r1.compatible, r2.compatible);
}
#[test]
fn test_intrinsic_check_result_clone() {
let r1 = IntrinsicCheckResult::valid();
let r2 = r1.clone();
assert_eq!(r1.valid, r2.valid);
}
#[test]
fn test_builtin_arg_clone() {
let arg = BuiltinArg {
index: 0,
name: "a".to_string(),
expected_type: "i32".to_string(),
any_vector: false,
is_immediate: false,
imm_min: None,
imm_max: None,
};
let arg2 = arg.clone();
assert_eq!(arg.name, arg2.name);
assert_eq!(arg.expected_type, arg2.expected_type);
}
#[test]
fn test_abi_result_default() {
let r = X86ABIResult::valid();
assert!(r.compatible);
assert!(r.issues.is_empty());
}
#[test]
fn test_intrinsic_check_result_default() {
let r = IntrinsicCheckResult::valid();
assert!(r.valid);
assert!(r.errors.is_empty());
assert!(r.warnings.is_empty());
assert!(r.group.is_none());
}
#[test]
fn test_intrinsic_check_result_error() {
let r = IntrinsicCheckResult::error("bad".to_string());
assert!(!r.valid);
assert_eq!(r.errors.len(), 1);
assert_eq!(r.errors[0], "bad");
}
#[test]
fn test_no_panic_empty_builtin_name() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
assert!(bc.get_signature("").is_none());
}
#[test]
fn test_no_panic_long_builtin_name() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let long_name = "__builtin_ia32_".repeat(10);
assert!(bc.get_signature(&long_name).is_none());
}
#[test]
fn test_no_panic_zero_vector_size() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
assert!(vsc.check_vector_size(0, &mut sema).is_err());
}
#[test]
fn test_no_panic_very_large_vector_size() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
let mut sema = X86Sema::new(make_sema(), st.clone());
assert!(vsc.check_vector_size(u32::MAX, &mut sema).is_err());
}
#[test]
fn test_no_panic_alignment_zero() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
assert!(ac.check_aligned_attribute(0).is_err());
assert!(ac.check_stack_alignment(0).is_err());
}
#[test]
fn test_no_panic_cc_empty_strings() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
let result = cc.check_compatibility("", "");
assert_eq!(result.compat, CCCompat::Incompatible);
}
#[test]
fn test_no_panic_target_attr_bizarre_input() {
let st = haswell_subtarget();
let tac = X86TargetAttrChecker::new(st);
let inputs = ["\0", " ", "\t", "a=b=c=d=e", "+=+", "-=-"];
for input in &inputs {
let _ = tac.parse_target_attr(input);
let _ = tac.validate_target_attr(input);
}
}
#[test]
fn test_no_panic_format_bad_inputs() {
let fc = X86FormatAttrChecker::new_64bit();
for c in 0u8..=255u8 {
let _ = fc.check_format_specifier(c as char, "");
}
}
#[test]
fn prop_all_builtins_have_feature_or_are_unconditional() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
for name in bc.builtin_names() {
let sig = bc.get_signature(name).unwrap();
assert!(
sig.required_feature.is_some() || sig.is_64bit_only || true,
"builtin {} has no feature requirement",
name
);
}
}
#[test]
fn prop_all_intrinsics_belong_to_group() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
for name in is.intrinsic_names() {
assert!(
is.lookup_group(name).is_some(),
"intrinsic {} has no group",
name
);
}
}
#[test]
fn prop_vector_sizes_are_power_of_two() {
let st = haswell_subtarget();
let vsc = X86VectorSizeChecker::new(st);
for &size in &vsc.limits.allowed_sizes {
assert!(size.is_power_of_two(), "{} is not a power of two", size);
}
}
#[test]
fn prop_stack_alignment_is_power_of_two() {
for subtarget in &[haswell_subtarget(), pentium4_subtarget(), skx_subtarget()] {
let ac = X86AlignmentChecker::new(subtarget.clone());
assert!(ac.stack_alignment.is_power_of_two());
}
}
#[test]
fn prop_alignment_max_ge_vector_alignments() {
for subtarget in &[haswell_subtarget(), pentium4_subtarget(), skx_subtarget()] {
let ac = X86AlignmentChecker::new(subtarget.clone());
assert!(ac.max_alignment >= ac.vector128_alignment);
if subtarget.has_avx {
assert!(ac.max_alignment >= ac.vector256_alignment);
}
if subtarget.has_avx512f {
assert!(ac.max_alignment >= ac.vector512_alignment);
}
}
}
#[test]
fn prop_cc_matrix_is_symmetric_for_same_cc() {
let st = haswell_subtarget();
let cc = X86CallingConvChecker::new(st);
for conv in cc.known_conventions() {
let result = cc.check_compatibility(conv, conv);
assert_eq!(result.compat, CCCompat::Compatible);
}
}
#[test]
fn prop_alignment_preferred_ge_abi() {
let st = haswell_subtarget();
let ac = X86AlignmentChecker::new(st);
for size in &[1, 2, 4, 8, 16, 32, 64] {
assert!(ac.preferred_alignment(*size) >= ac.abi_alignment(*size));
}
}
#[test]
fn prop_type_size_is_power_of_two() {
let st = haswell_subtarget();
let tc = X86TypeChecker::new(st);
let sizes = [
tc.int_size,
tc.long_size,
tc.long_long_size,
tc.float_size,
tc.double_size,
tc.pointer_size,
tc.int128_size,
];
for &s in &sizes {
assert!(s.is_power_of_two(), "size {} is not power of two", s);
}
}
#[test]
fn test_abi_struct_return_sysv_table() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
let small_struct = QualType {
base: TypeNode::Struct {
name: Some("S").to_string(),
fields: vec![
FieldDecl::new("a", QualType::long()),
FieldDecl::new("b", QualType::long()),
],
is_union: false,
},
is_const: false,
is_volatile: false,
is_restrict: false,
};
assert!(!ac.struct_needs_sret(&small_struct, "sysv"));
}
#[test]
fn test_abi_struct_return_win64_table() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
let medium_struct = QualType {
base: TypeNode::Struct {
name: Some("M").to_string(),
fields: vec![
FieldDecl::new("a", QualType::long()),
FieldDecl::new("b", QualType::int()),
],
is_union: false,
},
is_const: false,
is_volatile: false,
is_restrict: false,
};
assert!(ac.struct_needs_sret(&medium_struct, "ms_abi"));
}
#[test]
fn test_abi_struct_float_return_sysv() {
let st = haswell_subtarget();
let ac = X86ABIChecker::new(st);
let float_struct = QualType {
base: TypeNode::Struct {
name: Some("F2").to_string(),
fields: vec![
FieldDecl::new("a", QualType::float_()),
FieldDecl::new("b", QualType::float_()),
],
is_union: false,
},
is_const: false,
is_volatile: false,
is_restrict: false,
};
assert!(!ac.struct_needs_sret(&float_struct, "sysv"));
}
#[test]
fn test_integer_rank_ordering() {
assert!(IntegerRank::Bool < IntegerRank::Char);
assert!(IntegerRank::Char < IntegerRank::Short);
assert!(IntegerRank::Short < IntegerRank::Int);
assert!(IntegerRank::Int < IntegerRank::Long);
assert!(IntegerRank::Long < IntegerRank::LongLong);
}
#[test]
fn test_integer_rank_clone() {
let r = IntegerRank::Int;
assert_eq!(r, IntegerRank::Int);
}
#[test]
fn test_qualtype_kind_detection() {
assert!(QualType::int().is_int());
assert!(QualType::long().is_long());
assert!(QualType::float_().is_float());
assert!(QualType::double_().is_double());
assert!(QualType::void().is_void());
}
#[test]
fn test_typenode_size_bytes_primitives() {
assert_eq!(TypeNode::Char.size_bytes(), 1);
assert_eq!(TypeNode::Short.size_bytes(), 2);
assert_eq!(TypeNode::Int.size_bytes(), 4);
assert_eq!(TypeNode::Float.size_bytes(), 4);
assert_eq!(TypeNode::Double.size_bytes(), 8);
}
#[test]
fn test_typenode_is_integer() {
assert!(TypeNode::Char.is_integer());
assert!(TypeNode::Short.is_integer());
assert!(TypeNode::Int.is_integer());
assert!(TypeNode::Long.is_integer());
assert!(TypeNode::LongLong.is_integer());
assert!(!TypeNode::Float.is_integer());
assert!(!TypeNode::Double.is_integer());
}
#[test]
fn test_typenode_is_unsigned() {
assert!(TypeNode::UChar.is_unsigned());
assert!(TypeNode::UShort.is_unsigned());
assert!(TypeNode::UInt.is_unsigned());
assert!(TypeNode::ULong.is_unsigned());
assert!(TypeNode::ULongLong.is_unsigned());
assert!(!TypeNode::Int.is_unsigned());
assert!(!TypeNode::Short.is_unsigned());
}
#[test]
fn test_typenode_kind_name_all_variants() {
let variants = [
TypeNode::Void,
TypeNode::Char,
TypeNode::Int,
TypeNode::Long,
TypeNode::Float,
TypeNode::Double,
TypeNode::Bool,
TypeNode::Auto,
];
for v in &variants {
assert!(!v.kind_name().is_empty());
}
}
#[test]
fn test_extended_type_vector_kind() {
assert_eq!(ExtendedTypeNode::Vector.kind_name(), "vector");
assert_eq!(ExtendedTypeNode::ExtVector.kind_name(), "ext_vector");
assert_eq!(ExtendedTypeNode::Atomic.kind_name(), "atomic");
}
#[test]
fn test_type_attr_kind_vector_size() {
let k = TypeAttrKind::VectorSize;
match k {
TypeAttrKind::VectorSize => {}
_ => panic!("expected VectorSize"),
}
}
#[test]
fn test_type_attr_kind_aligned() {
let k = TypeAttrKind::Aligned;
match k {
TypeAttrKind::Aligned => {}
_ => panic!("expected Aligned"),
}
}
#[test]
fn test_x86_endianness_is_little() {
use crate::x86::X86_ENDIANNESS;
assert_eq!(X86_ENDIANNESS, "little");
}
#[test]
fn test_x86_stack_alignment_constants() {
use crate::x86::{X86_STACK_ALIGNMENT_32, X86_STACK_ALIGNMENT_64};
assert_eq!(X86_STACK_ALIGNMENT_64, 16);
assert_eq!(X86_STACK_ALIGNMENT_32, 16);
}
#[test]
fn test_x86_max_alignment_constant() {
use crate::x86::X86_MAX_ALIGNMENT;
assert_eq!(X86_MAX_ALIGNMENT, 64);
}
#[test]
fn test_x86_red_zone_size() {
use crate::x86::X86_RED_ZONE_SIZE_64;
assert_eq!(X86_RED_ZONE_SIZE_64, 128);
}
#[test]
fn test_x86_page_size() {
use crate::x86::X86_PAGE_SIZE;
assert_eq!(X86_PAGE_SIZE, 4096);
}
#[test]
fn test_x86_arg_class_is_register_class() {
assert!(X86ArgClass::Integer.is_register_class());
assert!(X86ArgClass::SSE.is_register_class());
assert!(!X86ArgClass::Memory.is_register_class());
assert!(!X86ArgClass::NoClass.is_register_class());
}
#[test]
fn test_x86_arg_class_needs_xmm() {
assert!(X86ArgClass::SSE.needs_xmm());
assert!(X86ArgClass::SSEUp.needs_xmm());
assert!(!X86ArgClass::Integer.needs_xmm());
assert!(!X86ArgClass::NoClass.needs_xmm());
}
#[test]
fn test_x86_calling_convention_name() {
assert_eq!(X86CallingConvention::C.name(), "cdecl");
assert_eq!(X86CallingConvention::X86_64_SysV.name(), "sysv");
assert_eq!(X86CallingConvention::Win64.name(), "win64");
}
#[test]
fn test_x86_cc_is_64bit() {
assert!(X86CallingConvention::X86_64_SysV.is_64bit());
assert!(X86CallingConvention::Win64.is_64bit());
assert!(!X86CallingConvention::C.is_64bit());
assert!(!X86CallingConvention::StdCall.is_64bit());
}
#[test]
fn test_x86_call_frame_default() {
let frame = X86CallFrame::default();
assert_eq!(frame.stack_size, 0);
assert!(frame.arg_offsets.is_empty());
}
#[test]
fn test_x86_arg_info_default() {
let info = X86ArgInfo::default();
assert!(!info.in_reg);
assert_eq!(info.size, 0);
assert_eq!(info.alignment, 1);
assert!(!info.is_byval);
assert!(!info.is_sret);
}
#[test]
fn test_intrinsic_sema_feature_map_is_complete() {
let st = haswell_subtarget();
let is = X86IntrinsicSema::new(st);
let groups: std::collections::HashSet<&str> = is
.intrinsic_names()
.iter()
.filter_map(|n| is.lookup_group(n).map(|s| s.as_str()))
.collect();
assert!(groups.len() >= 8);
assert!(groups.contains("sse"));
assert!(groups.contains("sse2"));
assert!(groups.contains("sse3"));
assert!(groups.contains("ssse3"));
assert!(groups.contains("sse41"));
assert!(groups.contains("sse42"));
assert!(groups.contains("avx"));
assert!(groups.contains("avx2"));
}
#[test]
fn test_builtin_feature_coverage() {
let st = haswell_subtarget();
let bc = X86BuiltinChecker::new(st);
let features: std::collections::HashSet<&str> = bc
.builtin_names()
.iter()
.filter_map(|n| bc.get_signature(n))
.filter_map(|s| s.required_feature.as_deref())
.collect();
assert!(features.contains("sse"));
assert!(features.contains("sse2"));
assert!(features.contains("sse3"));
assert!(features.contains("ssse3"));
assert!(features.contains("sse41"));
assert!(features.contains("sse42"));
assert!(features.contains("avx"));
assert!(features.contains("fma"));
assert!(features.contains("bmi"));
assert!(features.contains("bmi2"));
assert!(features.contains("aes"));
assert!(features.contains("sha"));
assert!(features.contains("rdrnd"));
assert!(features.contains("rdseed"));
}
#[test]
fn assert_all_required_modules_covered() {
let st = haswell_subtarget();
let sema = make_sema();
let x86_sema = X86Sema::new(sema, st);
assert_eq!(x86_sema.base.standard, CLangStandard::C17);
assert!(x86_sema.type_checker.max_vector_size > 0);
assert!(x86_sema.builtin_checker.num_builtins() > 0);
assert!(!x86_sema.abi_checker.known_conventions().is_empty());
assert!(x86_sema.alignment_checker.stack_alignment > 0);
assert!(x86_sema.intrinsic_sema.num_intrinsics() > 0);
assert!(x86_sema.target_attr_checker.is_known_cpu("haswell"));
assert_eq!(x86_sema.format_attr_checker.is_64bit, true);
assert_eq!(x86_sema.calling_conv_checker.default_convention(), "sysv");
assert!(x86_sema.vector_size_checker.limits.max >= 16);
}
}