use crate::clang::ast::*;
use crate::clang::clang_target::{
ClangABI, ClangTargetInfo, DebugInfoKind, FunctionAttrMapper, StackProtectorLevel,
TargetCodeGenInfo, TargetFlags,
};
use crate::clang::codegen::ClangCodeGen;
use crate::clang::{CLangStandard, ClangOptions};
use crate::context::LLVMContext;
use crate::ir_builder::IRBuilder;
use crate::module::Module;
use crate::types::{
ArrayType, DataLayout, FixedVectorType, FunctionType, PointerType, ScalableVectorType,
StructType, Type, TypeId, TypeKind,
};
use crate::value::{valref, SubclassKind, Value, ValueRef};
use crate::x86::x86_calling_convention::{
assign_sysv_args, classify_arg_type, EightByteClass, EightByteClassification, HFADetector,
HfaBaseType, MicrosoftX64CC, RegCallConvention, ReturnRegister, ReturnValueLowering,
StackFrameLayout, VarArgsInfo, X86_32CallingConvention, X32ABI,
};
use crate::x86::x86_register_info::*;
use crate::x86::{
CallConv, X86ArgClass, X86ArgInfo, X86CallFrame, X86CallingConvention, X86FrameInfo,
X86FrameLowering, X86InstrDesc, X86InstrInfo, X86MemOperand, X86Opcode, X86Operand,
X86RegisterInfo, X86SchedInfo, X86Subtarget, X86TargetMachine, X86_32_REG_COUNT,
X86_64_REG_COUNT,
};
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt;
#[derive(Debug, Clone)]
pub struct AggregateRegisterAssignment {
gpr_fields: Vec<(u32, u32, u32)>,
sse_fields: Vec<(u32, u32, u32)>,
}
impl AggregateRegisterAssignment {
pub fn new() -> Self {
Self {
gpr_fields: Vec::new(),
sse_fields: Vec::new(),
}
}
pub fn add_gpr_field(&mut self, reg: u32, offset: u32, size: u32) {
self.gpr_fields.push((reg, offset, size));
}
pub fn add_sse_field(&mut self, reg: u32, offset: u32, size: u32) {
self.sse_fields.push((reg, offset, size));
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TargetVariant {
X86_64,
X86_32,
X86_16,
X32,
}
impl X86TargetVariant {
pub fn from_triple(triple: &str) -> Self {
if triple.contains("x32") || triple.contains("gnux32") {
X86TargetVariant::X32
} else if triple.starts_with("x86_64") || triple.starts_with("amd64") {
X86TargetVariant::X86_64
} else if triple.starts_with("i386")
|| triple.starts_with("i486")
|| triple.starts_with("i586")
|| triple.starts_with("i686")
{
X86TargetVariant::X86_32
} else if triple.starts_with("i8086") || triple.starts_with("i286") {
X86TargetVariant::X86_16
} else {
if triple.starts_with("x86") || triple.starts_with("amd") {
X86TargetVariant::X86_64
} else {
X86TargetVariant::X86_64
}
}
}
pub fn is_64bit(&self) -> bool {
matches!(self, X86TargetVariant::X86_64 | X86TargetVariant::X32)
}
pub fn is_32bit(&self) -> bool {
matches!(self, X86TargetVariant::X86_32)
}
pub fn pointer_size_bytes(&self) -> u32 {
match self {
X86TargetVariant::X86_64 => 8,
X86TargetVariant::X86_32 => 4,
X86TargetVariant::X86_16 => 2,
X86TargetVariant::X32 => 4,
}
}
pub fn stack_alignment(&self) -> u32 {
match self {
X86TargetVariant::X86_64 => 16,
X86TargetVariant::X86_32 => 16,
X86TargetVariant::X86_16 => 2,
X86TargetVariant::X32 => 16,
}
}
pub fn red_zone_size(&self) -> u32 {
match self {
X86TargetVariant::X86_64 | X86TargetVariant::X32 => 128,
_ => 0,
}
}
}
impl fmt::Display for X86TargetVariant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86TargetVariant::X86_64 => write!(f, "x86_64"),
X86TargetVariant::X86_32 => write!(f, "i386"),
X86TargetVariant::X86_16 => write!(f, "i8086"),
X86TargetVariant::X32 => write!(f, "x86_64-x32"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86OSABI {
SystemV,
Windows,
Darwin,
}
impl X86OSABI {
pub fn from_triple(triple: &str) -> Self {
if triple.contains("windows") || triple.contains("mingw") || triple.contains("cygwin") {
X86OSABI::Windows
} else if triple.contains("darwin") || triple.contains("macos") {
X86OSABI::Darwin
} else {
X86OSABI::SystemV
}
}
pub fn default_calling_convention(&self, variant: X86TargetVariant) -> X86CallingConvention {
match (self, variant) {
(X86OSABI::Windows, X86TargetVariant::X86_64 | X86TargetVariant::X32) => {
X86CallingConvention::Win64
}
(X86OSABI::Windows, _) => X86CallingConvention::C,
(_, X86TargetVariant::X86_64 | X86TargetVariant::X32) => {
X86CallingConvention::X86_64_SysV
}
_ => X86CallingConvention::C,
}
}
}
#[derive(Debug, Clone)]
pub struct X86CodeGenFlags {
pub use_avx: bool,
pub use_avx2: bool,
pub use_avx512: bool,
pub use_sse41: bool,
pub use_sse42: bool,
pub force_frame_pointer: bool,
pub use_red_zone: bool,
pub cx16: bool,
pub use_x87: bool,
pub optimize_for_size: bool,
pub position_independent: bool,
pub omit_frame_pointer: bool,
pub stack_alignment_override: u32,
pub relaxed_atomics: bool,
pub address_sanitizer: bool,
pub thread_sanitizer: bool,
pub memory_sanitizer: bool,
pub lvi_mitigation: bool,
pub cet_ibt: bool,
pub cet_ss: bool,
}
impl Default for X86CodeGenFlags {
fn default() -> Self {
Self {
use_avx: false,
use_avx2: false,
use_avx512: false,
use_sse41: false,
use_sse42: false,
force_frame_pointer: false,
use_red_zone: true,
cx16: true,
use_x87: false,
optimize_for_size: false,
position_independent: false,
omit_frame_pointer: true,
stack_alignment_override: 0,
relaxed_atomics: false,
address_sanitizer: false,
thread_sanitizer: false,
memory_sanitizer: false,
lvi_mitigation: false,
cet_ibt: false,
cet_ss: false,
}
}
}
pub struct ClangX86CodeGen<'a> {
pub base: ClangCodeGen<'a>,
pub target_variant: X86TargetVariant,
pub os_abi: X86OSABI,
pub calling_convention: X86CallingConvention,
pub flags: X86CodeGenFlags,
pub lowering_info: X86LoweringInfo,
pub intrinsic_mapper: X86IntrinsicMapper,
pub vector_abi: X86VectorABI,
pub aggregate_lowering: X86AggregateLowering,
pub varargs: Option<X86VarArgs>,
pub tls_model: X86TLSModel,
pub stack_protector: X86StackProtector,
pub exception_handling: X86ExceptionHandling,
pub debug_info: X86DebugInfo,
pub address_space_map: X86AddressSpaceMap,
pub target_info: Option<ClangTargetInfo>,
_phantom: std::marker::PhantomData<&'a ()>,
}
impl<'a> ClangX86CodeGen<'a> {
pub fn new(module_name: &str, target_triple: &str) -> Self {
let base = ClangCodeGen::new(module_name, target_triple);
let target_variant = X86TargetVariant::from_triple(target_triple);
let os_abi = X86OSABI::from_triple(target_triple);
let cc = os_abi.default_calling_convention(target_variant);
let lowering_info = X86LoweringInfo::new(target_variant, os_abi);
let intrinsic_mapper = X86IntrinsicMapper::new(target_variant);
let vector_abi = X86VectorABI::new(target_variant);
let aggregate_lowering = X86AggregateLowering::new(target_variant, os_abi);
let tls_model = X86TLSModel::default();
let stack_protector = X86StackProtector::default();
let exception_handling = X86ExceptionHandling::new(os_abi);
let debug_info = X86DebugInfo::new(target_variant, os_abi);
let address_space_map = X86AddressSpaceMap::default();
Self {
base,
target_variant,
os_abi,
calling_convention: cc,
flags: X86CodeGenFlags::default(),
lowering_info,
intrinsic_mapper,
vector_abi,
aggregate_lowering,
varargs: None,
tls_model,
stack_protector,
exception_handling,
debug_info,
address_space_map,
target_info: None,
_phantom: std::marker::PhantomData,
}
}
pub fn from_target_info(module_name: &str, target_info: ClangTargetInfo) -> Self {
let triple = target_info.triple.clone();
let mut cg = Self::new(module_name, &triple);
cg.target_info = Some(target_info);
cg.sync_from_target_info();
cg
}
fn sync_from_target_info(&mut self) {
if let Some(ref ti) = self.target_info {
self.flags.position_independent = ti.position_independent;
self.flags.omit_frame_pointer = !ti.no_frame_pointer_elim;
self.flags.optimize_for_size = matches!(
ti.opt_level,
crate::clang::clang_target::ClangOptLevel::DefaultSize
| crate::clang::clang_target::ClangOptLevel::AggressiveSize
);
if ti.use_soft_float {
self.flags.use_x87 = false;
}
self.stack_protector.set_level(ti.stack_protector);
self.debug_info.set_kind(ti.debug_info);
self.tls_model.set_local_exec(ti.local_exec_tls);
}
}
pub fn module(&self) -> &Module {
&self.base.module
}
pub fn module_mut(&mut self) -> &mut Module {
&mut self.base.module
}
pub fn context(&self) -> &LLVMContext {
&self.base.context
}
pub fn builder(&self) -> &IRBuilder {
&self.base.builder
}
pub fn builder_mut(&mut self) -> &mut IRBuilder {
&mut self.base.builder
}
pub fn set_target_triple(&mut self, triple: &str) {
self.base.module.set_target_triple(triple);
self.target_variant = X86TargetVariant::from_triple(triple);
self.os_abi = X86OSABI::from_triple(triple);
self.calling_convention = self.os_abi.default_calling_convention(self.target_variant);
self.lowering_info = X86LoweringInfo::new(self.target_variant, self.os_abi);
self.intrinsic_mapper = X86IntrinsicMapper::new(self.target_variant);
self.vector_abi = X86VectorABI::new(self.target_variant);
self.aggregate_lowering = X86AggregateLowering::new(self.target_variant, self.os_abi);
self.exception_handling = X86ExceptionHandling::new(self.os_abi);
self.debug_info = X86DebugInfo::new(self.target_variant, self.os_abi);
}
pub fn set_calling_convention(&mut self, cc: X86CallingConvention) {
self.calling_convention = cc;
}
pub fn enable_features(&mut self, features: &[String]) {
for feat in features {
match feat.as_str() {
"avx" => self.flags.use_avx = true,
"avx2" => {
self.flags.use_avx = true;
self.flags.use_avx2 = true;
}
"avx512f" => {
self.flags.use_avx = true;
self.flags.use_avx2 = true;
self.flags.use_avx512 = true;
}
"sse4.1" => self.flags.use_sse41 = true,
"sse4.2" => self.flags.use_sse42 = true,
"cx16" => self.flags.cx16 = true,
_ => {}
}
}
}
pub fn disable_features(&mut self, features: &[String]) {
for feat in features {
match feat.as_str() {
"avx" => {
self.flags.use_avx = false;
self.flags.use_avx2 = false;
self.flags.use_avx512 = false;
}
"avx2" => {
self.flags.use_avx2 = false;
self.flags.use_avx512 = false;
}
"avx512f" => self.flags.use_avx512 = false,
"sse4.1" => self.flags.use_sse41 = false,
"sse4.2" => self.flags.use_sse42 = false,
"cx16" => self.flags.cx16 = false,
_ => {}
}
}
}
pub fn data_layout_string(&self) -> String {
match self.target_variant {
X86TargetVariant::X86_64 => "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-\
f80:128-n8:16:32:64-S128"
.to_string(),
X86TargetVariant::X86_32 => "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
i64:64-f80:128-n8:16:32-S128"
.to_string(),
X86TargetVariant::X86_16 => "e-m:e-p:16:16-i16:16-n8:16".to_string(),
X86TargetVariant::X32 => "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
i64:64-i128:128-f80:128-n8:16:32:64-S128"
.to_string(),
}
}
pub fn apply_data_layout(&mut self) {
let dl = self.data_layout_string();
self.base.module.set_data_layout(&dl);
}
pub fn lower_type(&self, ty: &Type) -> Type {
self.lowering_info.lower_type(ty)
}
pub fn lower_parameter_type(&self, ty: &Type) -> Type {
self.lowering_info.lower_parameter_type(ty)
}
pub fn lower_return_type(&self, ty: &Type) -> Type {
self.lowering_info.lower_return_type(ty)
}
pub fn classify_argument_type(&self, ty: &Type) -> Vec<X86ArgClass> {
self.lowering_info.classify_arg_type(ty)
}
pub fn uses_sret(&self, ty: &Type) -> bool {
self.lowering_info.uses_sret(ty)
}
pub fn get_int_param_regs(&self) -> Vec<u16> {
self.calling_convention.get_int_param_regs()
}
pub fn get_sse_param_regs(&self) -> Vec<u16> {
self.calling_convention.get_sse_param_regs()
}
pub fn map_builtin_to_intrinsic(&self, name: &str) -> Option<String> {
self.intrinsic_mapper.map_builtin(name)
}
pub fn map_vector_builtin(&self, name: &str, bit_width: u32) -> Option<String> {
self.intrinsic_mapper.map_vector_builtin(name, bit_width)
}
pub fn lower_aggregate_for_arg(&mut self, ty: &Type) -> AggregatePassingInfo {
self.aggregate_lowering.lower_for_argument(ty)
}
pub fn lower_aggregate_for_return(&mut self, ty: &Type) -> AggregateReturnInfo {
self.aggregate_lowering.lower_for_return(ty)
}
pub fn init_varargs(&mut self, num_fixed_params: usize) {
self.varargs = Some(X86VarArgs::new(
self.target_variant,
self.calling_convention,
num_fixed_params,
));
}
pub fn varargs_info(&self) -> Option<&X86VarArgs> {
self.varargs.as_ref()
}
pub fn emit_stack_protector_setup(&mut self) {
if self.stack_protector.has_protector {
}
}
pub fn emit_stack_protector_check(&mut self) {
if self.stack_protector.has_protector {
}
}
pub fn emit_landing_pad(
&mut self,
personality_fn: &str,
catch_types: &[Type],
) -> Result<(), String> {
let _ = personality_fn;
let _ = catch_types;
Ok(())
}
pub fn emit_function_debug_info(
&mut self,
name: &str,
file: &str,
line: u32,
linkage_name: Option<&str>,
) {
let _ = name;
let _ = file;
let _ = line;
let _ = linkage_name;
}
pub fn get_address_space(&self, qual: AddressSpaceQual) -> u32 {
self.address_space_map.map_address_space(qual)
}
}
#[derive(Debug, Clone)]
pub struct X86LoweringInfo {
target_variant: X86TargetVariant,
os_abi: X86OSABI,
pointer_size: u32,
long_double_size: u32,
has_int128: bool,
type_cache: HashMap<TypeId, Type>,
}
impl X86LoweringInfo {
pub fn new(target_variant: X86TargetVariant, os_abi: X86OSABI) -> Self {
let pointer_size = target_variant.pointer_size_bytes();
let long_double_size = match (&target_variant, &os_abi) {
(X86TargetVariant::X86_64 | X86TargetVariant::X32, X86OSABI::Windows) => 8,
(X86TargetVariant::X86_32, X86OSABI::Windows) => 8,
_ => 16, };
let has_int128 = matches!(
target_variant,
X86TargetVariant::X86_64 | X86TargetVariant::X32
);
Self {
target_variant,
os_abi,
pointer_size,
long_double_size,
has_int128,
type_cache: HashMap::new(),
}
}
pub fn lower_type(&self, ty: &Type) -> Type {
match &ty.kind {
TypeKind::Void => Type::void(),
TypeKind::Half => Type::half(),
TypeKind::BFloat => Type::bfloat(),
TypeKind::Float => Type::float(),
TypeKind::Double => Type::double(),
TypeKind::FP128 => Type::fp128(),
TypeKind::X86FP80 => Type::x86_fp80(),
TypeKind::PPCFP128 => Type::ppc_fp128(),
TypeKind::Label => Type::label(),
TypeKind::Metadata => Type::metadata(),
TypeKind::Token => Type::token(),
TypeKind::X86AMX => Type::x86_amx(),
TypeKind::X86MMX => Type::x86_mmx(),
TypeKind::Integer { bits } => Type::int(*bits),
TypeKind::Pointer { addr_space } => Type::pointer(*addr_space),
TypeKind::Array {
len,
element_type_id,
} => {
Type::array_with(*len, *element_type_id)
}
TypeKind::Struct {
name,
is_packed,
element_type_ids,
..
} => Type::struct_literal_with(*is_packed, element_type_ids.clone()),
TypeKind::FixedVector {
len,
element_type_id,
} => Type::fixed_vector_with(*len, *element_type_id),
TypeKind::ScalableVector {
min_elems,
element_type_id,
} => Type::scalable_vector_with(*min_elems, *element_type_id),
TypeKind::Function {
return_type_id,
param_type_ids,
is_vararg,
} => Type::function_type_with(*return_type_id, param_type_ids.clone(), *is_vararg),
}
}
pub fn lower_parameter_type(&self, ty: &Type) -> Type {
match &ty.kind {
TypeKind::Array { .. } => {
Type::pointer(0)
}
TypeKind::Function { .. } => {
Type::pointer(0)
}
_ => self.lower_type(ty),
}
}
pub fn lower_return_type(&self, ty: &Type) -> Type {
if self.uses_sret(ty) {
Type::void()
} else {
self.lower_type(ty)
}
}
pub fn uses_sret(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::Struct {
element_type_ids,
..
} => {
let size = self.compute_struct_size(element_type_ids);
match self.os_abi {
X86OSABI::Windows => size > 8,
_ => size > 16,
}
}
TypeKind::Array { len, .. } => {
let size = *len as u64 * 8;
match self.os_abi {
X86OSABI::Windows => size > 8,
_ => size > 16,
}
}
TypeKind::X86FP80 => true, _ => false,
}
}
fn compute_struct_size(&self, element_type_ids: &[TypeId]) -> u64 {
element_type_ids.len() as u64 * 8
}
pub fn classify_arg_type(&self, ty: &Type) -> Vec<X86ArgClass> {
let mut classes = Vec::new();
match self.os_abi {
X86OSABI::Windows => {
self.classify_win64(ty, &mut classes);
}
_ => {
self.classify_sysv(ty, 0, &mut classes);
}
}
classes
}
fn classify_sysv(&self, ty: &Type, offset: usize, classes: &mut Vec<X86ArgClass>) {
match &ty.kind {
TypeKind::Void => {
classes.push(X86ArgClass::NoClass);
}
TypeKind::Integer { bits } => {
let eightbyte_idx = offset / 8;
while classes.len() <= eightbyte_idx {
classes.push(X86ArgClass::NoClass);
}
let current = classes[eightbyte_idx];
if current == X86ArgClass::NoClass || current == X86ArgClass::Integer {
classes[eightbyte_idx] = X86ArgClass::Integer;
} else if current == X86ArgClass::SSE {
} else {
classes[eightbyte_idx] = X86ArgClass::Memory;
}
}
TypeKind::Float | TypeKind::Double => {
let eightbyte_idx = offset / 8;
while classes.len() <= eightbyte_idx {
classes.push(X86ArgClass::NoClass);
}
if classes[eightbyte_idx] == X86ArgClass::NoClass
|| classes[eightbyte_idx] == X86ArgClass::SSE
{
classes[eightbyte_idx] = X86ArgClass::SSE;
} else if classes[eightbyte_idx] == X86ArgClass::SSEUp {
classes[eightbyte_idx] = X86ArgClass::SSE;
} else {
classes[eightbyte_idx] = X86ArgClass::Memory;
}
}
TypeKind::Pointer { .. } => {
let eightbyte_idx = offset / 8;
while classes.len() <= eightbyte_idx {
classes.push(X86ArgClass::NoClass);
}
if classes[eightbyte_idx] == X86ArgClass::NoClass
|| classes[eightbyte_idx] == X86ArgClass::Integer
{
classes[eightbyte_idx] = X86ArgClass::Integer;
}
}
TypeKind::X86FP80 => {
classes.push(X86ArgClass::X87);
classes.push(X86ArgClass::X87Up);
}
TypeKind::Struct {
element_type_ids,
..
} => {
let mut field_offset = offset;
for _ in element_type_ids {
self.classify_sysv(&Type::i64(), field_offset, classes);
field_offset += 8;
}
}
_ => {
let eightbyte_idx = offset / 8;
while classes.len() <= eightbyte_idx {
classes.push(X86ArgClass::NoClass);
}
if classes[eightbyte_idx] == X86ArgClass::NoClass
|| classes[eightbyte_idx] == X86ArgClass::Integer
{
classes[eightbyte_idx] = X86ArgClass::Integer;
}
}
}
}
fn classify_win64(&self, _ty: &Type, classes: &mut Vec<X86ArgClass>) {
classes.push(X86ArgClass::Integer);
}
pub fn type_size_in_bits(&self, ty: &Type) -> u64 {
match &ty.kind {
TypeKind::Void => 0,
TypeKind::Half | TypeKind::BFloat => 16,
TypeKind::Float => 32,
TypeKind::Double => 64,
TypeKind::X86FP80 => 80,
TypeKind::FP128 | TypeKind::PPCFP128 => 128,
TypeKind::Integer { bits } => *bits as u64,
TypeKind::Pointer { .. } => (self.pointer_size * 8) as u64,
TypeKind::Array { len, .. } => *len as u64 * 64,
TypeKind::Struct {
element_type_ids,
..
} => element_type_ids.len() as u64 * 64,
TypeKind::FixedVector {
len,
element_type_id: _,
} => *len as u64 * 64,
_ => 64,
}
}
pub fn type_alignment_in_bits(&self, ty: &Type) -> u64 {
let size = self.type_size_in_bits(ty);
std::cmp::min(size, 512)
}
pub fn is_hfa(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::Struct {
element_type_ids,
..
} => {
if element_type_ids.is_empty() {
return false;
}
let mut seen_fp_type: Option<TypeKind> = None;
for _ in element_type_ids {
let field = Type::float();
match field.kind {
TypeKind::Float | TypeKind::Double => {
if let Some(ref seen) = seen_fp_type {
if std::mem::discriminant(seen)
!= std::mem::discriminant(&field.kind)
{
return false;
}
} else {
seen_fp_type = Some(field.kind);
}
}
_ => return false,
}
}
seen_fp_type.is_some()
}
TypeKind::Array {
len: _,
element_type_id: _,
} => {
false }
_ => false,
}
}
pub fn hfa_element_count(&self, ty: &Type) -> usize {
match &ty.kind {
TypeKind::Struct {
element_type_ids,
..
} => element_type_ids.len(),
_ => 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86MemoryOrder {
Relaxed,
Acquire,
Release,
AcqRel,
SeqCst,
}
impl X86MemoryOrder {
pub fn llvm_name(&self) -> &'static str {
match self {
X86MemoryOrder::Relaxed => "monotonic",
X86MemoryOrder::Acquire => "acquire",
X86MemoryOrder::Release => "release",
X86MemoryOrder::AcqRel => "acq_rel",
X86MemoryOrder::SeqCst => "seq_cst",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86RoundMode {
ToNearest,
Down,
Up,
ToZero,
Current,
}
impl X86RoundMode {
pub fn llvm_imm(&self) -> u32 {
match self {
X86RoundMode::ToNearest => 0,
X86RoundMode::Down => 1,
X86RoundMode::Up => 2,
X86RoundMode::ToZero => 3,
X86RoundMode::Current => 4,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86VCmpPred {
EQ,
LT,
LE,
UNORD,
NEQ,
NLT,
NLE,
ORD,
EQ_UQ,
NGE,
NGT,
FALSE,
NEQ_OQ,
GE,
GT,
TRUE,
EQ_OS,
LT_OQ,
LE_OQ,
UNORD_S,
NEQ_US,
NLT_UQ,
NLE_UQ,
ORD_S,
EQ_US,
NGE_UQ,
NGT_UQ,
FALSE_OS,
NEQ_OS,
GE_OQ,
GT_OQ,
TRUE_US,
}
impl X86VCmpPred {
pub fn as_imm(&self) -> u32 {
match self {
X86VCmpPred::EQ => 0,
X86VCmpPred::LT => 1,
X86VCmpPred::LE => 2,
X86VCmpPred::UNORD => 3,
X86VCmpPred::NEQ => 4,
X86VCmpPred::NLT => 5,
X86VCmpPred::NLE => 6,
X86VCmpPred::ORD => 7,
X86VCmpPred::EQ_UQ => 8,
X86VCmpPred::NGE => 9,
X86VCmpPred::NGT => 10,
X86VCmpPred::FALSE => 11,
X86VCmpPred::NEQ_OQ => 12,
X86VCmpPred::GE => 13,
X86VCmpPred::GT => 14,
X86VCmpPred::TRUE => 15,
X86VCmpPred::EQ_OS => 16,
X86VCmpPred::LT_OQ => 17,
X86VCmpPred::LE_OQ => 18,
X86VCmpPred::UNORD_S => 19,
X86VCmpPred::NEQ_US => 20,
X86VCmpPred::NLT_UQ => 21,
X86VCmpPred::NLE_UQ => 22,
X86VCmpPred::ORD_S => 23,
X86VCmpPred::EQ_US => 24,
X86VCmpPred::NGE_UQ => 25,
X86VCmpPred::NGT_UQ => 26,
X86VCmpPred::FALSE_OS => 27,
X86VCmpPred::NEQ_OS => 28,
X86VCmpPred::GE_OQ => 29,
X86VCmpPred::GT_OQ => 30,
X86VCmpPred::TRUE_US => 31,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86IntrinsicCategory {
SSE,
SSE2,
SSE3,
SSSE3,
SSE41,
SSE42,
AVX,
AVX2,
AVX512,
FMA,
BMI,
BMI2,
AES,
SHA,
MMX,
ADX,
RDRAND,
CRC32,
XSAVE,
General,
}
impl X86IntrinsicCategory {
pub fn feature_flag(&self) -> &'static str {
match self {
X86IntrinsicCategory::SSE => "sse",
X86IntrinsicCategory::SSE2 => "sse2",
X86IntrinsicCategory::SSE3 => "sse3",
X86IntrinsicCategory::SSSE3 => "ssse3",
X86IntrinsicCategory::SSE41 => "sse4.1",
X86IntrinsicCategory::SSE42 => "sse4.2",
X86IntrinsicCategory::AVX => "avx",
X86IntrinsicCategory::AVX2 => "avx2",
X86IntrinsicCategory::AVX512 => "avx512f",
X86IntrinsicCategory::FMA => "fma",
X86IntrinsicCategory::BMI => "bmi",
X86IntrinsicCategory::BMI2 => "bmi2",
X86IntrinsicCategory::AES => "aes",
X86IntrinsicCategory::SHA => "sha",
X86IntrinsicCategory::MMX => "mmx",
X86IntrinsicCategory::ADX => "adx",
X86IntrinsicCategory::RDRAND => "rdrnd",
X86IntrinsicCategory::CRC32 => "sse4.2",
X86IntrinsicCategory::XSAVE => "xsave",
X86IntrinsicCategory::General => "",
}
}
}
#[derive(Debug, Clone)]
pub struct X86IntrinsicEntry {
pub builtin_name: String,
pub llvm_intrinsic: String,
pub category: X86IntrinsicCategory,
pub min_bit_width: u32,
pub num_operands: usize,
}
pub struct X86IntrinsicMapper {
target_variant: X86TargetVariant,
builtin_map: HashMap<String, X86IntrinsicEntry>,
reverse_map: HashMap<String, String>,
}
impl X86IntrinsicMapper {
pub fn new(target_variant: X86TargetVariant) -> Self {
let mut mapper = Self {
target_variant,
builtin_map: HashMap::new(),
reverse_map: HashMap::new(),
};
mapper.populate_intrinsics();
mapper
}
pub fn map_builtin(&self, name: &str) -> Option<String> {
self.builtin_map
.get(name)
.map(|entry| entry.llvm_intrinsic.clone())
}
pub fn map_vector_builtin(&self, name: &str, bit_width: u32) -> Option<String> {
self.builtin_map.get(name).and_then(|entry| {
if entry.min_bit_width > 0 && bit_width >= entry.min_bit_width {
Some(entry.llvm_intrinsic.clone())
} else if entry.min_bit_width == 0 {
Some(entry.llvm_intrinsic.clone())
} else {
None
}
})
}
pub fn get_entry(&self, name: &str) -> Option<&X86IntrinsicEntry> {
self.builtin_map.get(name)
}
pub fn category_for_builtin(&self, name: &str) -> Option<X86IntrinsicCategory> {
self.builtin_map.get(name).map(|e| e.category)
}
pub fn is_available(&self, name: &str, features: &HashSet<String>) -> bool {
if let Some(entry) = self.builtin_map.get(name) {
let flag = entry.category.feature_flag();
flag.is_empty() || features.contains(flag)
} else {
false
}
}
pub fn builtins_for_category(&self, category: X86IntrinsicCategory) -> Vec<&X86IntrinsicEntry> {
self.builtin_map
.values()
.filter(|e| e.category == category)
.collect()
}
pub fn all_builtin_names(&self) -> Vec<&str> {
self.builtin_map.keys().map(|s| s.as_str()).collect()
}
fn populate_intrinsics(&mut self) {
self.add_entry(
"__builtin_ia32_addps",
"llvm.x86.sse.add.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_addss",
"llvm.x86.sse.add.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_subps",
"llvm.x86.sse.sub.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_subss",
"llvm.x86.sse.sub.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_mulps",
"llvm.x86.sse.mul.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_mulss",
"llvm.x86.sse.mul.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_divps",
"llvm.x86.sse.div.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_divss",
"llvm.x86.sse.div.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_rcpps",
"llvm.x86.sse.rcp.ps",
X86IntrinsicCategory::SSE,
128,
1,
);
self.add_entry(
"__builtin_ia32_rcpss",
"llvm.x86.sse.rcp.ss",
X86IntrinsicCategory::SSE,
128,
1,
);
self.add_entry(
"__builtin_ia32_rsqrtps",
"llvm.x86.sse.rsqrt.ps",
X86IntrinsicCategory::SSE,
128,
1,
);
self.add_entry(
"__builtin_ia32_rsqrtss",
"llvm.x86.sse.rsqrt.ss",
X86IntrinsicCategory::SSE,
128,
1,
);
self.add_entry(
"__builtin_ia32_sqrtps",
"llvm.x86.sse.sqrt.ps",
X86IntrinsicCategory::SSE,
128,
1,
);
self.add_entry(
"__builtin_ia32_sqrtss",
"llvm.x86.sse.sqrt.ss",
X86IntrinsicCategory::SSE,
128,
1,
);
self.add_entry(
"__builtin_ia32_minps",
"llvm.x86.sse.min.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_minss",
"llvm.x86.sse.min.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_maxps",
"llvm.x86.sse.max.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_maxss",
"llvm.x86.sse.max.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_andps",
"llvm.x86.sse.and.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_andnps",
"llvm.x86.sse.andn.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_orps",
"llvm.x86.sse.or.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_xorps",
"llvm.x86.sse.xor.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_cmpps",
"llvm.x86.sse.cmp.ps",
X86IntrinsicCategory::SSE,
128,
3,
);
self.add_entry(
"__builtin_ia32_cmpss",
"llvm.x86.sse.cmp.ss",
X86IntrinsicCategory::SSE,
128,
3,
);
self.add_entry(
"__builtin_ia32_comieq",
"llvm.x86.sse.comieq.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_comilt",
"llvm.x86.sse.comilt.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_comile",
"llvm.x86.sse.comile.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_comigt",
"llvm.x86.sse.comigt.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_comige",
"llvm.x86.sse.comige.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_comineq",
"llvm.x86.sse.comineq.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_ucomieq",
"llvm.x86.sse.ucomieq.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_ucomilt",
"llvm.x86.sse.ucomilt.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_ucomile",
"llvm.x86.sse.ucomile.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_ucomigt",
"llvm.x86.sse.ucomigt.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_ucomige",
"llvm.x86.sse.ucomige.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_ucomineq",
"llvm.x86.sse.ucomineq.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_shufps",
"llvm.x86.sse.shuf.ps",
X86IntrinsicCategory::SSE,
128,
3,
);
self.add_entry(
"__builtin_ia32_unpckhps",
"llvm.x86.sse.unpckh.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_unpcklps",
"llvm.x86.sse.unpckl.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_movlhps",
"llvm.x86.sse.movlh.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_movhlps",
"llvm.x86.sse.movhl.ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_movss",
"llvm.x86.sse.mov.ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_cvtss2si",
"llvm.x86.sse.cvtss2si",
X86IntrinsicCategory::SSE,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvtss2si64",
"llvm.x86.sse.cvtss2si64",
X86IntrinsicCategory::SSE,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvtsi2ss",
"llvm.x86.sse.cvtsi2ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_cvtsi642ss",
"llvm.x86.sse.cvtsi642ss",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_cvtps2pi",
"llvm.x86.sse.cvtps2pi",
X86IntrinsicCategory::SSE,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvtpi2ps",
"llvm.x86.sse.cvtpi2ps",
X86IntrinsicCategory::SSE,
128,
2,
);
self.add_entry(
"__builtin_ia32_ldmxcsr",
"llvm.x86.sse.ldmxcsr",
X86IntrinsicCategory::SSE,
0,
1,
);
self.add_entry(
"__builtin_ia32_stmxcsr",
"llvm.x86.sse.stmxcsr",
X86IntrinsicCategory::SSE,
0,
0,
);
self.add_entry(
"__builtin_ia32_sfence",
"llvm.x86.sse.sfence",
X86IntrinsicCategory::SSE,
0,
0,
);
self.add_entry(
"__builtin_ia32_addpd",
"llvm.x86.sse2.add.pd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_addsd",
"llvm.x86.sse2.add.sd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_subpd",
"llvm.x86.sse2.sub.pd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_subsd",
"llvm.x86.sse2.sub.sd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_mulpd",
"llvm.x86.sse2.mul.pd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_mulsd",
"llvm.x86.sse2.mul.sd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_divpd",
"llvm.x86.sse2.div.pd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_divsd",
"llvm.x86.sse2.div.sd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_sqrtpd",
"llvm.x86.sse2.sqrt.pd",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_sqrtsd",
"llvm.x86.sse2.sqrt.sd",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_minpd",
"llvm.x86.sse2.min.pd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_minsd",
"llvm.x86.sse2.min.sd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_maxpd",
"llvm.x86.sse2.max.pd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_maxsd",
"llvm.x86.sse2.max.sd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_andpd",
"llvm.x86.sse2.and.pd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_andnpd",
"llvm.x86.sse2.andn.pd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_orpd",
"llvm.x86.sse2.or.pd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_xorpd",
"llvm.x86.sse2.xor.pd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_cmppd",
"llvm.x86.sse2.cmp.pd",
X86IntrinsicCategory::SSE2,
128,
3,
);
self.add_entry(
"__builtin_ia32_cmpsd",
"llvm.x86.sse2.cmp.sd",
X86IntrinsicCategory::SSE2,
128,
3,
);
self.add_entry(
"__builtin_ia32_shufpd",
"llvm.x86.sse2.shuf.pd",
X86IntrinsicCategory::SSE2,
128,
3,
);
self.add_entry(
"__builtin_ia32_paddb",
"llvm.x86.sse2.padd.b",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_paddw",
"llvm.x86.sse2.padd.w",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_paddd",
"llvm.x86.sse2.padd.d",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_paddq",
"llvm.x86.sse2.padd.q",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_psubb",
"llvm.x86.sse2.psub.b",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_psubw",
"llvm.x86.sse2.psub.w",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_psubd",
"llvm.x86.sse2.psub.d",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_psubq",
"llvm.x86.sse2.psub.q",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_pmullw",
"llvm.x86.sse2.pmull.w",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_pmulhw",
"llvm.x86.sse2.pmulh.w",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_pmulhuw",
"llvm.x86.sse2.pmulhu.w",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_pmuludq",
"llvm.x86.sse2.pmulu.dq",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_pavgb",
"llvm.x86.sse2.pavg.b",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_pavgw",
"llvm.x86.sse2.pavg.w",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_pmaxub",
"llvm.x86.sse2.pmaxu.b",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_pmaxsw",
"llvm.x86.sse2.pmaxs.w",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_pminub",
"llvm.x86.sse2.pminu.b",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_pminsw",
"llvm.x86.sse2.pmins.w",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_psadbw",
"llvm.x86.sse2.psad.bw",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_packsswb",
"llvm.x86.sse2.packsswb.128",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_packssdw",
"llvm.x86.sse2.packssdw.128",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_packuswb",
"llvm.x86.sse2.packuswb.128",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_punpckhbw",
"llvm.x86.sse2.punpckhb.w",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_punpckhwd",
"llvm.x86.sse2.punpckhw.d",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_punpckhdq",
"llvm.x86.sse2.punpckhd.q",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_punpcklbw",
"llvm.x86.sse2.punpcklb.w",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_punpcklwd",
"llvm.x86.sse2.punpcklw.d",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_punpckldq",
"llvm.x86.sse2.punpckld.q",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_psllw",
"llvm.x86.sse2.psll.w",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_pslld",
"llvm.x86.sse2.psll.d",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_psllq",
"llvm.x86.sse2.psll.q",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_psrlw",
"llvm.x86.sse2.psrl.w",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_psrld",
"llvm.x86.sse2.psrl.d",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_psrlq",
"llvm.x86.sse2.psrl.q",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_psraw",
"llvm.x86.sse2.psra.w",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_psrad",
"llvm.x86.sse2.psra.d",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_pmovmskb",
"llvm.x86.sse2.pmovmskb.128",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvtdq2ps",
"llvm.x86.sse2.cvtdq2ps",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvttps2dq",
"llvm.x86.sse2.cvttps2dq",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvtps2dq",
"llvm.x86.sse2.cvtps2dq",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvttpd2dq",
"llvm.x86.sse2.cvttpd2dq",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvtpd2dq",
"llvm.x86.sse2.cvtpd2dq",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvtdq2pd",
"llvm.x86.sse2.cvtdq2pd",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvtpd2ps",
"llvm.x86.sse2.cvtpd2ps",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvtps2pd",
"llvm.x86.sse2.cvtps2pd",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvtsd2si",
"llvm.x86.sse2.cvtsd2si",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvtsd2si64",
"llvm.x86.sse2.cvtsd2si64",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvttsd2si",
"llvm.x86.sse2.cvttsd2si",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvttsd2si64",
"llvm.x86.sse2.cvttsd2si64",
X86IntrinsicCategory::SSE2,
128,
1,
);
self.add_entry(
"__builtin_ia32_cvtsi2sd",
"llvm.x86.sse2.cvtsi2sd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_cvtsi642sd",
"llvm.x86.sse2.cvtsi642sd",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_mfence",
"llvm.x86.sse2.mfence",
X86IntrinsicCategory::SSE2,
0,
0,
);
self.add_entry(
"__builtin_ia32_lfence",
"llvm.x86.sse2.lfence",
X86IntrinsicCategory::SSE2,
0,
0,
);
self.add_entry(
"__builtin_ia32_clflush",
"llvm.x86.sse2.clflush",
X86IntrinsicCategory::SSE2,
0,
1,
);
self.add_entry(
"__builtin_ia32_maskmovdqu",
"llvm.x86.sse2.maskmov.dqu",
X86IntrinsicCategory::SSE2,
128,
2,
);
self.add_entry(
"__builtin_ia32_addsubps",
"llvm.x86.sse3.addsub.ps",
X86IntrinsicCategory::SSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_addsubpd",
"llvm.x86.sse3.addsub.pd",
X86IntrinsicCategory::SSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_haddps",
"llvm.x86.sse3.hadd.ps",
X86IntrinsicCategory::SSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_haddpd",
"llvm.x86.sse3.hadd.pd",
X86IntrinsicCategory::SSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_hsubps",
"llvm.x86.sse3.hsub.ps",
X86IntrinsicCategory::SSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_hsubpd",
"llvm.x86.sse3.hsub.pd",
X86IntrinsicCategory::SSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_movshdup",
"llvm.x86.sse3.movshdup",
X86IntrinsicCategory::SSE3,
128,
1,
);
self.add_entry(
"__builtin_ia32_movsldup",
"llvm.x86.sse3.movsldup",
X86IntrinsicCategory::SSE3,
128,
1,
);
self.add_entry(
"__builtin_ia32_movddup",
"llvm.x86.sse3.movddup",
X86IntrinsicCategory::SSE3,
128,
1,
);
self.add_entry(
"__builtin_ia32_lddqu",
"llvm.x86.sse3.ldu.dq",
X86IntrinsicCategory::SSE3,
128,
1,
);
self.add_entry(
"__builtin_ia32_monitor",
"llvm.x86.sse3.monitor",
X86IntrinsicCategory::SSE3,
0,
3,
);
self.add_entry(
"__builtin_ia32_mwait",
"llvm.x86.sse3.mwait",
X86IntrinsicCategory::SSE3,
0,
2,
);
self.add_entry(
"__builtin_ia32_pshufb",
"llvm.x86.ssse3.pshuf.b.128",
X86IntrinsicCategory::SSSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_phaddw",
"llvm.x86.ssse3.phadd.w.128",
X86IntrinsicCategory::SSSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_phaddd",
"llvm.x86.ssse3.phadd.d.128",
X86IntrinsicCategory::SSSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_phaddsw",
"llvm.x86.ssse3.phadd.sw.128",
X86IntrinsicCategory::SSSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_phsubw",
"llvm.x86.ssse3.phsub.w.128",
X86IntrinsicCategory::SSSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_phsubd",
"llvm.x86.ssse3.phsub.d.128",
X86IntrinsicCategory::SSSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_phsubsw",
"llvm.x86.ssse3.phsub.sw.128",
X86IntrinsicCategory::SSSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_pmaddubsw",
"llvm.x86.ssse3.pmadd.ub.sw.128",
X86IntrinsicCategory::SSSE3,
128,
2,
);
self.add_entry(
"__builtin_ia32_palignr",
"llvm.x86.ssse3.palign.r.128",
X86IntrinsicCategory::SSSE3,
128,
3,
);
self.add_entry(
"__builtin_ia32_pabsb",
"llvm.x86.ssse3.pabs.b.128",
X86IntrinsicCategory::SSSE3,
128,
1,
);
self.add_entry(
"__builtin_ia32_pabsw",
"llvm.x86.ssse3.pabs.w.128",
X86IntrinsicCategory::SSSE3,
128,
1,
);
self.add_entry(
"__builtin_ia32_pabsd",
"llvm.x86.ssse3.pabs.d.128",
X86IntrinsicCategory::SSSE3,
128,
1,
);
self.add_entry(
"__builtin_ia32_blendvps",
"llvm.x86.sse41.blendvps",
X86IntrinsicCategory::SSE41,
128,
3,
);
self.add_entry(
"__builtin_ia32_blendvpd",
"llvm.x86.sse41.blendvpd",
X86IntrinsicCategory::SSE41,
128,
3,
);
self.add_entry(
"__builtin_ia32_dpps",
"llvm.x86.sse41.dpps",
X86IntrinsicCategory::SSE41,
128,
3,
);
self.add_entry(
"__builtin_ia32_dppd",
"llvm.x86.sse41.dppd",
X86IntrinsicCategory::SSE41,
128,
3,
);
self.add_entry(
"__builtin_ia32_roundps",
"llvm.x86.sse41.round.ps",
X86IntrinsicCategory::SSE41,
128,
2,
);
self.add_entry(
"__builtin_ia32_roundpd",
"llvm.x86.sse41.round.pd",
X86IntrinsicCategory::SSE41,
128,
2,
);
self.add_entry(
"__builtin_ia32_roundss",
"llvm.x86.sse41.round.ss",
X86IntrinsicCategory::SSE41,
128,
3,
);
self.add_entry(
"__builtin_ia32_roundsd",
"llvm.x86.sse41.round.sd",
X86IntrinsicCategory::SSE41,
128,
3,
);
self.add_entry(
"__builtin_ia32_pminsb",
"llvm.x86.sse41.pminsb",
X86IntrinsicCategory::SSE41,
128,
2,
);
self.add_entry(
"__builtin_ia32_pminud",
"llvm.x86.sse41.pminud",
X86IntrinsicCategory::SSE41,
128,
2,
);
self.add_entry(
"__builtin_ia32_pmaxsb",
"llvm.x86.sse41.pmaxsb",
X86IntrinsicCategory::SSE41,
128,
2,
);
self.add_entry(
"__builtin_ia32_pmaxud",
"llvm.x86.sse41.pmaxud",
X86IntrinsicCategory::SSE41,
128,
2,
);
self.add_entry(
"__builtin_ia32_pmovsxbw",
"llvm.x86.sse41.pmovsxbw",
X86IntrinsicCategory::SSE41,
128,
1,
);
self.add_entry(
"__builtin_ia32_pmovsxbd",
"llvm.x86.sse41.pmovsxbd",
X86IntrinsicCategory::SSE41,
128,
1,
);
self.add_entry(
"__builtin_ia32_pmovsxbq",
"llvm.x86.sse41.pmovsxbq",
X86IntrinsicCategory::SSE41,
128,
1,
);
self.add_entry(
"__builtin_ia32_pmovsxwd",
"llvm.x86.sse41.pmovsxwd",
X86IntrinsicCategory::SSE41,
128,
1,
);
self.add_entry(
"__builtin_ia32_pmovsxwq",
"llvm.x86.sse41.pmovsxwq",
X86IntrinsicCategory::SSE41,
128,
1,
);
self.add_entry(
"__builtin_ia32_pmovsxdq",
"llvm.x86.sse41.pmovsxdq",
X86IntrinsicCategory::SSE41,
128,
1,
);
self.add_entry(
"__builtin_ia32_pmovzxbw",
"llvm.x86.sse41.pmovzxbw",
X86IntrinsicCategory::SSE41,
128,
1,
);
self.add_entry(
"__builtin_ia32_pmovzxbd",
"llvm.x86.sse41.pmovzxbd",
X86IntrinsicCategory::SSE41,
128,
1,
);
self.add_entry(
"__builtin_ia32_pmovzxbq",
"llvm.x86.sse41.pmovzxbq",
X86IntrinsicCategory::SSE41,
128,
1,
);
self.add_entry(
"__builtin_ia32_pmovzxwd",
"llvm.x86.sse41.pmovzxwd",
X86IntrinsicCategory::SSE41,
128,
1,
);
self.add_entry(
"__builtin_ia32_pmovzxwq",
"llvm.x86.sse41.pmovzxwq",
X86IntrinsicCategory::SSE41,
128,
1,
);
self.add_entry(
"__builtin_ia32_pmovzxdq",
"llvm.x86.sse41.pmovzxdq",
X86IntrinsicCategory::SSE41,
128,
1,
);
self.add_entry(
"__builtin_ia32_pmuldq",
"llvm.x86.sse41.pmuldq",
X86IntrinsicCategory::SSE41,
128,
2,
);
self.add_entry(
"__builtin_ia32_packusdw",
"llvm.x86.sse41.packusdw",
X86IntrinsicCategory::SSE41,
128,
2,
);
self.add_entry(
"__builtin_ia32_ptestc",
"llvm.x86.sse41.ptestc",
X86IntrinsicCategory::SSE41,
128,
2,
);
self.add_entry(
"__builtin_ia32_ptestz",
"llvm.x86.sse41.ptestz",
X86IntrinsicCategory::SSE41,
128,
2,
);
self.add_entry(
"__builtin_ia32_ptestnzc",
"llvm.x86.sse41.ptestnzc",
X86IntrinsicCategory::SSE41,
128,
2,
);
self.add_entry(
"__builtin_ia32_insertps",
"llvm.x86.sse41.insertps",
X86IntrinsicCategory::SSE41,
128,
3,
);
self.add_entry(
"__builtin_ia32_extractps",
"llvm.x86.sse41.extractps",
X86IntrinsicCategory::SSE41,
128,
2,
);
self.add_entry(
"__builtin_ia32_crc32qi",
"llvm.x86.sse42.crc32.8",
X86IntrinsicCategory::SSE42,
0,
2,
);
self.add_entry(
"__builtin_ia32_crc32hi",
"llvm.x86.sse42.crc32.16",
X86IntrinsicCategory::SSE42,
0,
2,
);
self.add_entry(
"__builtin_ia32_crc32si",
"llvm.x86.sse42.crc32.32",
X86IntrinsicCategory::SSE42,
0,
2,
);
self.add_entry(
"__builtin_ia32_crc32di",
"llvm.x86.sse42.crc32.64",
X86IntrinsicCategory::SSE42,
0,
2,
);
self.add_entry(
"__builtin_ia32_pcmpestrm128",
"llvm.x86.sse42.pcmpestrm128",
X86IntrinsicCategory::SSE42,
128,
5,
);
self.add_entry(
"__builtin_ia32_pcmpestri128",
"llvm.x86.sse42.pcmpestri128",
X86IntrinsicCategory::SSE42,
128,
5,
);
self.add_entry(
"__builtin_ia32_pcmpistrm128",
"llvm.x86.sse42.pcmpistrm128",
X86IntrinsicCategory::SSE42,
128,
3,
);
self.add_entry(
"__builtin_ia32_pcmpistri128",
"llvm.x86.sse42.pcmpistri128",
X86IntrinsicCategory::SSE42,
128,
3,
);
self.add_entry(
"__builtin_ia32_addps256",
"llvm.x86.avx.add.ps.256",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_addpd256",
"llvm.x86.avx.add.pd.256",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_subps256",
"llvm.x86.avx.sub.ps.256",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_subpd256",
"llvm.x86.avx.sub.pd.256",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_mulps256",
"llvm.x86.avx.mul.ps.256",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_mulpd256",
"llvm.x86.avx.mul.pd.256",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_divps256",
"llvm.x86.avx.div.ps.256",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_divpd256",
"llvm.x86.avx.div.pd.256",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_sqrtps256",
"llvm.x86.avx.sqrt.ps.256",
X86IntrinsicCategory::AVX,
256,
1,
);
self.add_entry(
"__builtin_ia32_sqrtpd256",
"llvm.x86.avx.sqrt.pd.256",
X86IntrinsicCategory::AVX,
256,
1,
);
self.add_entry(
"__builtin_ia32_vpermilvarps",
"llvm.x86.avx.vpermilvar.ps",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_vpermilvarpd",
"llvm.x86.avx.vpermilvar.pd",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_vzeroall",
"llvm.x86.avx.vzeroall",
X86IntrinsicCategory::AVX,
0,
0,
);
self.add_entry(
"__builtin_ia32_vzeroupper",
"llvm.x86.avx.vzeroupper",
X86IntrinsicCategory::AVX,
0,
0,
);
self.add_entry(
"__builtin_ia32_lddqu256",
"llvm.x86.avx.ldu.dq.256",
X86IntrinsicCategory::AVX,
256,
1,
);
self.add_entry(
"__builtin_ia32_movntdq256",
"llvm.x86.avx.movnt.dq.256",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_maskloadps",
"llvm.x86.avx.maskload.ps",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_maskloadpd",
"llvm.x86.avx.maskload.pd",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_maskstoreps",
"llvm.x86.avx.maskstore.ps",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_maskstorepd",
"llvm.x86.avx.maskstore.pd",
X86IntrinsicCategory::AVX,
256,
2,
);
self.add_entry(
"__builtin_ia32_pabsb256",
"llvm.x86.avx2.pabs.b",
X86IntrinsicCategory::AVX2,
256,
1,
);
self.add_entry(
"__builtin_ia32_pabsw256",
"llvm.x86.avx2.pabs.w",
X86IntrinsicCategory::AVX2,
256,
1,
);
self.add_entry(
"__builtin_ia32_pabsd256",
"llvm.x86.avx2.pabs.d",
X86IntrinsicCategory::AVX2,
256,
1,
);
self.add_entry(
"__builtin_ia32_paddb256",
"llvm.x86.avx2.padd.b",
X86IntrinsicCategory::AVX2,
256,
2,
);
self.add_entry(
"__builtin_ia32_paddw256",
"llvm.x86.avx2.padd.w",
X86IntrinsicCategory::AVX2,
256,
2,
);
self.add_entry(
"__builtin_ia32_paddd256",
"llvm.x86.avx2.padd.d",
X86IntrinsicCategory::AVX2,
256,
2,
);
self.add_entry(
"__builtin_ia32_paddq256",
"llvm.x86.avx2.padd.q",
X86IntrinsicCategory::AVX2,
256,
2,
);
self.add_entry(
"__builtin_ia32_psubb256",
"llvm.x86.avx2.psub.b",
X86IntrinsicCategory::AVX2,
256,
2,
);
self.add_entry(
"__builtin_ia32_psubw256",
"llvm.x86.avx2.psub.w",
X86IntrinsicCategory::AVX2,
256,
2,
);
self.add_entry(
"__builtin_ia32_psubd256",
"llvm.x86.avx2.psub.d",
X86IntrinsicCategory::AVX2,
256,
2,
);
self.add_entry(
"__builtin_ia32_psubq256",
"llvm.x86.avx2.psub.q",
X86IntrinsicCategory::AVX2,
256,
2,
);
self.add_entry(
"__builtin_ia32_pmullw256",
"llvm.x86.avx2.pmull.w",
X86IntrinsicCategory::AVX2,
256,
2,
);
self.add_entry(
"__builtin_ia32_pmulld256",
"llvm.x86.avx2.pmull.d",
X86IntrinsicCategory::AVX2,
256,
2,
);
self.add_entry(
"__builtin_ia32_pmulhw256",
"llvm.x86.avx2.pmulh.w",
X86IntrinsicCategory::AVX2,
256,
2,
);
self.add_entry(
"__builtin_ia32_pmulhuw256",
"llvm.x86.avx2.pmulhu.w",
X86IntrinsicCategory::AVX2,
256,
2,
);
self.add_entry(
"__builtin_ia32_pmuludq256",
"llvm.x86.avx2.pmulu.dq",
X86IntrinsicCategory::AVX2,
256,
2,
);
self.add_entry(
"__builtin_ia32_gather_dpd",
"llvm.x86.avx2.gather.d.pd",
X86IntrinsicCategory::AVX2,
256,
4,
);
self.add_entry(
"__builtin_ia32_gather_dps",
"llvm.x86.avx2.gather.d.ps",
X86IntrinsicCategory::AVX2,
256,
4,
);
self.add_entry(
"__builtin_ia32_gather_qpd",
"llvm.x86.avx2.gather.q.pd",
X86IntrinsicCategory::AVX2,
256,
4,
);
self.add_entry(
"__builtin_ia32_gather_qps",
"llvm.x86.avx2.gather.q.ps",
X86IntrinsicCategory::AVX2,
256,
4,
);
self.add_entry(
"__builtin_ia32_paddb512",
"llvm.x86.avx512.padd.b.512",
X86IntrinsicCategory::AVX512,
512,
2,
);
self.add_entry(
"__builtin_ia32_paddw512",
"llvm.x86.avx512.padd.w.512",
X86IntrinsicCategory::AVX512,
512,
2,
);
self.add_entry(
"__builtin_ia32_paddd512",
"llvm.x86.avx512.padd.d.512",
X86IntrinsicCategory::AVX512,
512,
2,
);
self.add_entry(
"__builtin_ia32_paddq512",
"llvm.x86.avx512.padd.q.512",
X86IntrinsicCategory::AVX512,
512,
2,
);
self.add_entry(
"__builtin_ia32_psubb512",
"llvm.x86.avx512.psub.b.512",
X86IntrinsicCategory::AVX512,
512,
2,
);
self.add_entry(
"__builtin_ia32_addps512",
"llvm.x86.avx512.add.ps.512",
X86IntrinsicCategory::AVX512,
512,
2,
);
self.add_entry(
"__builtin_ia32_addpd512",
"llvm.x86.avx512.add.pd.512",
X86IntrinsicCategory::AVX512,
512,
2,
);
self.add_entry(
"__builtin_ia32_mulps512",
"llvm.x86.avx512.mul.ps.512",
X86IntrinsicCategory::AVX512,
512,
2,
);
self.add_entry(
"__builtin_ia32_mulpd512",
"llvm.x86.avx512.mul.pd.512",
X86IntrinsicCategory::AVX512,
512,
2,
);
self.add_entry(
"__builtin_ia32_compressstoreps512",
"llvm.x86.avx512.compressstore.ps.512",
X86IntrinsicCategory::AVX512,
512,
3,
);
self.add_entry(
"__builtin_ia32_expandloadps512",
"llvm.x86.avx512.expandload.ps.512",
X86IntrinsicCategory::AVX512,
512,
3,
);
self.add_entry(
"__builtin_ia32_kandb",
"llvm.x86.avx512.kand.b",
X86IntrinsicCategory::AVX512,
0,
2,
);
self.add_entry(
"__builtin_ia32_kandnb",
"llvm.x86.avx512.kandn.b",
X86IntrinsicCategory::AVX512,
0,
2,
);
self.add_entry(
"__builtin_ia32_korb",
"llvm.x86.avx512.kor.b",
X86IntrinsicCategory::AVX512,
0,
2,
);
self.add_entry(
"__builtin_ia32_kxnorb",
"llvm.x86.avx512.kxnor.b",
X86IntrinsicCategory::AVX512,
0,
2,
);
self.add_entry(
"__builtin_ia32_kxorb",
"llvm.x86.avx512.kxor.b",
X86IntrinsicCategory::AVX512,
0,
2,
);
self.add_entry(
"__builtin_ia32_knotb",
"llvm.x86.avx512.knot.b",
X86IntrinsicCategory::AVX512,
0,
1,
);
self.add_entry(
"__builtin_ia32_vfmaddps",
"llvm.x86.fma.vfmadd.ps",
X86IntrinsicCategory::FMA,
256,
3,
);
self.add_entry(
"__builtin_ia32_vfmaddpd",
"llvm.x86.fma.vfmadd.pd",
X86IntrinsicCategory::FMA,
256,
3,
);
self.add_entry(
"__builtin_ia32_vfmsubps",
"llvm.x86.fma.vfmsub.ps",
X86IntrinsicCategory::FMA,
256,
3,
);
self.add_entry(
"__builtin_ia32_vfmsubpd",
"llvm.x86.fma.vfmsub.pd",
X86IntrinsicCategory::FMA,
256,
3,
);
self.add_entry(
"__builtin_ia32_vfnmaddps",
"llvm.x86.fma.vfnmadd.ps",
X86IntrinsicCategory::FMA,
256,
3,
);
self.add_entry(
"__builtin_ia32_vfnmaddpd",
"llvm.x86.fma.vfnmadd.pd",
X86IntrinsicCategory::FMA,
256,
3,
);
self.add_entry(
"__builtin_ia32_bextr_u32",
"llvm.x86.bmi.bextr.32",
X86IntrinsicCategory::BMI,
0,
2,
);
self.add_entry(
"__builtin_ia32_bextr_u64",
"llvm.x86.bmi.bextr.64",
X86IntrinsicCategory::BMI,
0,
2,
);
self.add_entry(
"__builtin_ia32_blsi_u32",
"llvm.x86.bmi.blsi.32",
X86IntrinsicCategory::BMI,
0,
1,
);
self.add_entry(
"__builtin_ia32_blsi_u64",
"llvm.x86.bmi.blsi.64",
X86IntrinsicCategory::BMI,
0,
1,
);
self.add_entry(
"__builtin_ia32_blsmsk_u32",
"llvm.x86.bmi.blsmsk.32",
X86IntrinsicCategory::BMI,
0,
1,
);
self.add_entry(
"__builtin_ia32_blsmsk_u64",
"llvm.x86.bmi.blsmsk.64",
X86IntrinsicCategory::BMI,
0,
1,
);
self.add_entry(
"__builtin_ia32_blsr_u32",
"llvm.x86.bmi.blsr.32",
X86IntrinsicCategory::BMI,
0,
1,
);
self.add_entry(
"__builtin_ia32_blsr_u64",
"llvm.x86.bmi.blsr.64",
X86IntrinsicCategory::BMI,
0,
1,
);
self.add_entry(
"__builtin_ia32_tzcnt_u32",
"llvm.x86.bmi.tzcnt.32",
X86IntrinsicCategory::BMI,
0,
1,
);
self.add_entry(
"__builtin_ia32_tzcnt_u64",
"llvm.x86.bmi.tzcnt.64",
X86IntrinsicCategory::BMI,
0,
1,
);
self.add_entry(
"__builtin_ia32_bzhi_si",
"llvm.x86.bmi2.bzhi.32",
X86IntrinsicCategory::BMI2,
0,
2,
);
self.add_entry(
"__builtin_ia32_bzhi_di",
"llvm.x86.bmi2.bzhi.64",
X86IntrinsicCategory::BMI2,
0,
2,
);
self.add_entry(
"__builtin_ia32_pdep_si",
"llvm.x86.bmi2.pdep.32",
X86IntrinsicCategory::BMI2,
0,
2,
);
self.add_entry(
"__builtin_ia32_pdep_di",
"llvm.x86.bmi2.pdep.64",
X86IntrinsicCategory::BMI2,
0,
2,
);
self.add_entry(
"__builtin_ia32_pext_si",
"llvm.x86.bmi2.pext.32",
X86IntrinsicCategory::BMI2,
0,
2,
);
self.add_entry(
"__builtin_ia32_pext_di",
"llvm.x86.bmi2.pext.64",
X86IntrinsicCategory::BMI2,
0,
2,
);
self.add_entry(
"__builtin_ia32_aesenc128",
"llvm.x86.aesni.aesenc",
X86IntrinsicCategory::AES,
128,
2,
);
self.add_entry(
"__builtin_ia32_aesenclast128",
"llvm.x86.aesni.aesenclast",
X86IntrinsicCategory::AES,
128,
2,
);
self.add_entry(
"__builtin_ia32_aesdec128",
"llvm.x86.aesni.aesdec",
X86IntrinsicCategory::AES,
128,
2,
);
self.add_entry(
"__builtin_ia32_aesdeclast128",
"llvm.x86.aesni.aesdeclast",
X86IntrinsicCategory::AES,
128,
2,
);
self.add_entry(
"__builtin_ia32_aeskeygenassist128",
"llvm.x86.aesni.aeskeygenassist",
X86IntrinsicCategory::AES,
128,
2,
);
self.add_entry(
"__builtin_ia32_sha1rnds4",
"llvm.x86.sha1rnds4",
X86IntrinsicCategory::SHA,
128,
3,
);
self.add_entry(
"__builtin_ia32_sha1nexte",
"llvm.x86.sha1nexte",
X86IntrinsicCategory::SHA,
128,
2,
);
self.add_entry(
"__builtin_ia32_sha1msg1",
"llvm.x86.sha1msg1",
X86IntrinsicCategory::SHA,
128,
2,
);
self.add_entry(
"__builtin_ia32_sha1msg2",
"llvm.x86.sha1msg2",
X86IntrinsicCategory::SHA,
128,
2,
);
self.add_entry(
"__builtin_ia32_sha256rnds2",
"llvm.x86.sha256rnds2",
X86IntrinsicCategory::SHA,
128,
3,
);
self.add_entry(
"__builtin_ia32_sha256msg1",
"llvm.x86.sha256msg1",
X86IntrinsicCategory::SHA,
128,
2,
);
self.add_entry(
"__builtin_ia32_sha256msg2",
"llvm.x86.sha256msg2",
X86IntrinsicCategory::SHA,
128,
2,
);
self.add_entry(
"__builtin_ia32_rdrand16_step",
"llvm.x86.rdrand.16",
X86IntrinsicCategory::RDRAND,
0,
1,
);
self.add_entry(
"__builtin_ia32_rdrand32_step",
"llvm.x86.rdrand.32",
X86IntrinsicCategory::RDRAND,
0,
1,
);
self.add_entry(
"__builtin_ia32_rdrand64_step",
"llvm.x86.rdrand.64",
X86IntrinsicCategory::RDRAND,
0,
1,
);
self.add_entry(
"__builtin_ia32_rdseed16_step",
"llvm.x86.rdseed.16",
X86IntrinsicCategory::RDRAND,
0,
1,
);
self.add_entry(
"__builtin_ia32_rdseed32_step",
"llvm.x86.rdseed.32",
X86IntrinsicCategory::RDRAND,
0,
1,
);
self.add_entry(
"__builtin_ia32_rdseed64_step",
"llvm.x86.rdseed.64",
X86IntrinsicCategory::RDRAND,
0,
1,
);
self.add_entry(
"__builtin_ia32_pause",
"llvm.x86.sse2.pause",
X86IntrinsicCategory::General,
0,
0,
);
self.add_entry(
"__builtin_ia32_rdtsc",
"llvm.x86.rdtsc",
X86IntrinsicCategory::General,
0,
0,
);
self.add_entry(
"__builtin_ia32_rdtscp",
"llvm.x86.rdtscp",
X86IntrinsicCategory::General,
0,
1,
);
self.add_entry(
"__builtin_ia32_cpuid",
"llvm.x86.cpuid",
X86IntrinsicCategory::General,
0,
2,
);
self.add_entry(
"__builtin_ia32_xgetbv",
"llvm.x86.xgetbv",
X86IntrinsicCategory::General,
0,
1,
);
self.add_entry(
"__builtin_ia32_xsetbv",
"llvm.x86.xsetbv",
X86IntrinsicCategory::General,
0,
2,
);
self.add_entry(
"__builtin_ia32_rdpmc",
"llvm.x86.rdpmc",
X86IntrinsicCategory::General,
0,
1,
);
self.add_entry(
"__builtin_ia32_wbinvd",
"llvm.x86.wbinvd",
X86IntrinsicCategory::General,
0,
0,
);
self.add_entry(
"__builtin_ia32_invlpg",
"llvm.x86.invlpg",
X86IntrinsicCategory::General,
0,
1,
);
self.add_entry(
"__builtin_ia32_wrmsr",
"llvm.x86.wrmsr",
X86IntrinsicCategory::General,
0,
2,
);
self.add_entry(
"__builtin_ia32_rdmsr",
"llvm.x86.rdmsr",
X86IntrinsicCategory::General,
0,
1,
);
}
fn add_entry(
&mut self,
builtin: &str,
intrinsic: &str,
category: X86IntrinsicCategory,
min_bit_width: u32,
num_operands: usize,
) {
let entry = X86IntrinsicEntry {
builtin_name: builtin.to_string(),
llvm_intrinsic: intrinsic.to_string(),
category,
min_bit_width,
num_operands,
};
self.builtin_map.insert(builtin.to_string(), entry);
self.reverse_map
.insert(intrinsic.to_string(), builtin.to_string());
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86VectorWidth {
V64,
V128,
V256,
V512,
}
impl X86VectorWidth {
pub fn from_bits(bits: u32) -> Self {
match bits {
0..=64 => X86VectorWidth::V64,
65..=128 => X86VectorWidth::V128,
129..=256 => X86VectorWidth::V256,
_ => X86VectorWidth::V512,
}
}
pub fn bytes(&self) -> u32 {
match self {
X86VectorWidth::V64 => 8,
X86VectorWidth::V128 => 16,
X86VectorWidth::V256 => 32,
X86VectorWidth::V512 => 64,
}
}
pub fn alignment(&self) -> u32 {
self.bytes()
}
}
#[derive(Debug, Clone)]
pub struct X86VectorArg {
pub width: X86VectorWidth,
pub num_elements: u32,
pub element_type: Type,
pub reg_assignment: Option<u32>,
pub in_memory: bool,
pub stack_offset: Option<u32>,
}
#[derive(Debug, Clone)]
pub struct X86VectorLoweringResult {
pub value: Option<ValueRef>,
pub is_libcall: bool,
pub libcall_name: Option<String>,
pub is_constant: bool,
pub error: Option<String>,
}
impl X86VectorLoweringResult {
pub fn success(value: ValueRef) -> Self {
Self {
value: Some(value),
is_libcall: false,
libcall_name: None,
is_constant: false,
error: None,
}
}
pub fn libcall(name: &str) -> Self {
Self {
value: None,
is_libcall: true,
libcall_name: Some(name.to_string()),
is_constant: false,
error: None,
}
}
pub fn constant(value: ValueRef) -> Self {
Self {
value: Some(value),
is_libcall: false,
libcall_name: None,
is_constant: true,
error: None,
}
}
pub fn error(msg: &str) -> Self {
Self {
value: None,
is_libcall: false,
libcall_name: None,
is_constant: false,
error: Some(msg.to_string()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86VectorLane {
Lo,
Hi,
Cross,
Within,
}
#[derive(Debug, Clone)]
pub struct X86ShuffleMask {
pub indices: Vec<i32>,
pub is_zero_extend: bool,
pub is_sign_extend: bool,
pub is_single_instruction: bool,
pub canonical_instruction: Option<String>,
}
impl X86ShuffleMask {
pub fn new(indices: Vec<i32>) -> Self {
let is_zx = indices.iter().all(|&i| i >= 0 || i == -1);
Self {
indices,
is_zero_extend: is_zx,
is_sign_extend: false,
is_single_instruction: false,
canonical_instruction: None,
}
}
pub fn is_identity(&self) -> bool {
self.indices
.iter()
.enumerate()
.all(|(i, &idx)| idx == i as i32)
}
pub fn is_splat(&self) -> bool {
if self.indices.is_empty() {
return false;
}
let first = self.indices[0];
first >= 0 && self.indices.iter().all(|&i| i == first || i == -1)
}
pub fn is_zeroing(&self) -> bool {
self.indices.iter().any(|&i| i == -1)
}
pub fn num_elements(&self) -> usize {
self.indices.len()
}
pub fn is_unpckl(&self, num_elements_total: usize) -> bool {
let n = num_elements_total / 2;
if self.indices.len() != num_elements_total {
return false;
}
for i in 0..n / 2 {
let expected_lo = i as i32;
let expected_hi = (i + num_elements_total / 2) as i32;
if self.indices[i * 2] != expected_lo || self.indices[i * 2 + 1] != expected_hi {
return false;
}
}
true
}
pub fn is_unpckh(&self, num_elements_total: usize) -> bool {
let n = num_elements_total / 2;
if self.indices.len() != num_elements_total {
return false;
}
let half = n / 2;
for i in 0..half {
let expected_lo = (i + half) as i32;
let expected_hi = (i + half + num_elements_total) as i32;
if self.indices[i * 2] != expected_lo || self.indices[i * 2 + 1] != expected_hi {
return false;
}
}
true
}
}
pub struct X86VectorABI {
target_variant: X86TargetVariant,
sse_param_regs: Vec<u32>,
sse_reg_count: usize,
used_sse_regs: HashSet<u32>,
current_stack_offset: u32,
is_vectorcall: bool,
}
impl X86VectorABI {
pub fn new(target_variant: X86TargetVariant) -> Self {
let (sse_param_regs, sse_reg_count) = match target_variant {
X86TargetVariant::X86_64 | X86TargetVariant::X32 => {
(vec![0, 1, 2, 3, 4, 5, 6, 7], 8)
}
X86TargetVariant::X86_32 => {
(vec![0, 1, 2], 3)
}
X86TargetVariant::X86_16 => (vec![], 0),
};
Self {
target_variant,
sse_param_regs,
sse_reg_count,
used_sse_regs: HashSet::new(),
current_stack_offset: 0,
is_vectorcall: false,
}
}
pub fn enable_vectorcall(&mut self) {
self.is_vectorcall = true;
if matches!(
self.target_variant,
X86TargetVariant::X86_64 | X86TargetVariant::X32
) {
self.sse_param_regs = vec![0, 1, 2, 3, 4, 5];
self.sse_reg_count = 6;
}
}
pub fn reset(&mut self) {
self.used_sse_regs.clear();
self.current_stack_offset = 0;
}
pub fn classify_vector_arg(&mut self, ty: &Type) -> X86VectorArg {
match &ty.kind {
TypeKind::FixedVector {
len,
element_type_id,
} => {
let elem_type = Type::float();
let elem_size = elem_type.size_in_bytes();
let total_bits = *len as u32 * elem_size as u32 * 8;
let width = X86VectorWidth::from_bits(total_bits);
let reg = self.assign_register(width);
let (in_memory, stack_offset) = if reg.is_none() && !self.is_vectorcall {
if self.current_stack_offset == 0 {
self.current_stack_offset = 8;
}
let off = self.current_stack_offset;
self.current_stack_offset += width.bytes();
(true, Some(off))
} else {
(false, None)
};
X86VectorArg {
width,
num_elements: *len as u32,
element_type: elem_type,
reg_assignment: reg,
in_memory,
stack_offset,
}
}
TypeKind::ScalableVector {
min_elems,
element_type_id: _,
} => {
let width = X86VectorWidth::V512;
let reg = self.assign_register(width);
X86VectorArg {
width,
num_elements: *min_elems as u32,
element_type: Type::float(),
reg_assignment: reg,
in_memory: reg.is_none(),
stack_offset: if reg.is_none() {
let off = self.current_stack_offset;
self.current_stack_offset += 64;
Some(off)
} else {
None
},
}
}
_ => {
X86VectorArg {
width: X86VectorWidth::V64,
num_elements: 1,
element_type: Type::i64(),
reg_assignment: None,
in_memory: false,
stack_offset: None,
}
}
}
}
fn assign_register(&mut self, width: X86VectorWidth) -> Option<u32> {
for ® in &self.sse_param_regs {
if !self.used_sse_regs.contains(®) {
self.used_sse_regs.insert(reg);
return Some(reg);
}
}
None
}
pub fn is_hva(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::Struct {
element_type_ids,
..
} => {
if element_type_ids.is_empty() {
return false;
}
let mut first_width: Option<u64> = None;
for _ in element_type_ids {
let field = Type::i64();
let width = field.size_in_bytes();
if let Some(fw) = first_width {
if width != fw {
return false;
}
} else {
first_width = Some(width);
}
}
first_width.is_some()
}
_ => false,
}
}
pub fn available_sse_regs(&self) -> usize {
self.sse_reg_count - self.used_sse_regs.len()
}
pub fn current_stack_offset(&self) -> u32 {
self.current_stack_offset
}
pub fn get_vector_return_reg(&self, ty: &Type) -> Option<u32> {
match &ty.kind {
TypeKind::FixedVector {
len,
element_type_id: _,
} => {
let size = *len as u32 * 8;
if size <= 512 {
Some(0) } else {
None
}
}
_ => None,
}
}
pub fn requires_alignment(&self, width: X86VectorWidth) -> bool {
match width {
X86VectorWidth::V64 => false,
X86VectorWidth::V128 => self.target_variant.is_64bit(),
X86VectorWidth::V256 => {
self.target_variant.is_64bit()
}
X86VectorWidth::V512 => true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AggregatePassingStrategy {
Indirect,
SplitIntegers,
SplitSSE,
SplitMixed,
SingleRegister,
Stack,
}
#[derive(Debug, Clone)]
pub struct AggregatePassingInfo {
pub strategy: AggregatePassingStrategy,
pub class0: X86ArgClass,
pub class1: X86ArgClass,
pub total_size: u64,
pub gpr_count: usize,
pub sse_count: usize,
pub needs_stack_temp: bool,
pub stack_alignment: u32,
}
#[derive(Debug, Clone)]
pub struct AggregateReturnInfo {
pub uses_sret: bool,
pub return_regs: Vec<ReturnRegister>,
pub classes: Vec<X86ArgClass>,
pub total_size: u64,
pub is_multi_reg: bool,
}
pub struct X86AggregateLowering {
target_variant: X86TargetVariant,
os_abi: X86OSABI,
pointer_size: u32,
lower_cache: HashMap<TypeId, AggregatePassingInfo>,
return_cache: HashMap<TypeId, AggregateReturnInfo>,
}
impl X86AggregateLowering {
pub fn new(target_variant: X86TargetVariant, os_abi: X86OSABI) -> Self {
let pointer_size = target_variant.pointer_size_bytes();
Self {
target_variant,
os_abi,
pointer_size,
lower_cache: HashMap::new(),
return_cache: HashMap::new(),
}
}
pub fn lower_for_argument(&mut self, ty: &Type) -> AggregatePassingInfo {
if let Some(cached) = self.lower_cache.get(&ty.id) {
return cached.clone();
}
let info = self.compute_argument_lowering(ty);
self.lower_cache.insert(ty.id, info.clone());
info
}
fn compute_argument_lowering(&self, ty: &Type) -> AggregatePassingInfo {
let size = self.type_size(ty);
match self.os_abi {
X86OSABI::Windows => self.compute_win64_arg_info(ty, size),
_ => self.compute_sysv_arg_info(ty, size),
}
}
fn compute_sysv_arg_info(&self, ty: &Type, size: u64) -> AggregatePassingInfo {
if size > 16 {
return AggregatePassingInfo {
strategy: AggregatePassingStrategy::Indirect,
class0: X86ArgClass::Memory,
class1: X86ArgClass::Memory,
total_size: size,
gpr_count: 0,
sse_count: 0,
needs_stack_temp: true,
stack_alignment: 8,
};
}
let classes = self.classify_sysv_aggregate(ty);
let (class0, class1) = self.extract_classes(&classes);
let is_mem = class0 == X86ArgClass::Memory;
if is_mem {
AggregatePassingInfo {
strategy: AggregatePassingStrategy::Indirect,
class0,
class1,
total_size: size,
gpr_count: 0,
sse_count: 0,
needs_stack_temp: true,
stack_alignment: 8,
}
} else {
let gpr_count = if class0 == X86ArgClass::Integer { 1 } else { 0 }
+ if class1 == X86ArgClass::Integer { 1 } else { 0 };
let sse_count = if class0 == X86ArgClass::SSE { 1 } else { 0 }
+ if class1 == X86ArgClass::SSE { 1 } else { 0 };
let strategy = match (class0, class1) {
(X86ArgClass::Integer, X86ArgClass::NoClass) => {
AggregatePassingStrategy::SingleRegister
}
(X86ArgClass::Integer, X86ArgClass::Integer) => {
AggregatePassingStrategy::SplitIntegers
}
(X86ArgClass::SSE, X86ArgClass::NoClass) => {
AggregatePassingStrategy::SingleRegister
}
(X86ArgClass::SSE, X86ArgClass::SSE) | (X86ArgClass::SSE, X86ArgClass::SSEUp) => {
AggregatePassingStrategy::SplitSSE
}
(X86ArgClass::Integer, X86ArgClass::SSE)
| (X86ArgClass::SSE, X86ArgClass::Integer) => AggregatePassingStrategy::SplitMixed,
_ => AggregatePassingStrategy::SingleRegister,
};
AggregatePassingInfo {
strategy,
class0,
class1,
total_size: size,
gpr_count,
sse_count,
needs_stack_temp: false,
stack_alignment: 8,
}
}
}
fn compute_win64_arg_info(&self, ty: &Type, size: u64) -> AggregatePassingInfo {
if size > 8 {
AggregatePassingInfo {
strategy: AggregatePassingStrategy::Indirect,
class0: X86ArgClass::Memory,
class1: X86ArgClass::Memory,
total_size: size,
gpr_count: 0,
sse_count: 0,
needs_stack_temp: true,
stack_alignment: 8,
}
} else {
AggregatePassingInfo {
strategy: AggregatePassingStrategy::SingleRegister,
class0: X86ArgClass::Integer,
class1: X86ArgClass::NoClass,
total_size: size,
gpr_count: 1,
sse_count: 0,
needs_stack_temp: false,
stack_alignment: 8,
}
}
}
fn classify_sysv_aggregate(&self, ty: &Type) -> Vec<X86ArgClass> {
let mut classes = Vec::new();
match &ty.kind {
TypeKind::Struct {
element_type_ids,
..
} => {
let mut offset = 0usize;
for _ in element_type_ids {
let field_size = 8usize;
let eightbyte = offset / 8;
while classes.len() <= eightbyte {
classes.push(X86ArgClass::NoClass);
}
if classes[eightbyte] == X86ArgClass::NoClass
|| classes[eightbyte] == X86ArgClass::Integer
{
classes[eightbyte] = X86ArgClass::Integer;
}
offset += field_size;
}
}
TypeKind::Array { len, .. } => {
let num_eightbytes = ((*len as usize * 8) + 7) / 8;
for _ in 0..num_eightbytes {
classes.push(X86ArgClass::Integer);
}
}
_ => {
classes.push(X86ArgClass::Integer);
}
}
classes
}
fn extract_classes(&self, classes: &[X86ArgClass]) -> (X86ArgClass, X86ArgClass) {
let c0 = classes.first().copied().unwrap_or(X86ArgClass::NoClass);
let c1 = classes.get(1).copied().unwrap_or(X86ArgClass::NoClass);
(c0, c1)
}
pub fn lower_for_return(&mut self, ty: &Type) -> AggregateReturnInfo {
if let Some(cached) = self.return_cache.get(&ty.id) {
return cached.clone();
}
let info = self.compute_return_lowering(ty);
self.return_cache.insert(ty.id, info.clone());
info
}
fn compute_return_lowering(&self, ty: &Type) -> AggregateReturnInfo {
let size = self.type_size(ty);
match self.os_abi {
X86OSABI::Windows => {
if size > 8 {
AggregateReturnInfo {
uses_sret: true,
return_regs: vec![],
classes: vec![X86ArgClass::Memory],
total_size: size,
is_multi_reg: false,
}
} else {
AggregateReturnInfo {
uses_sret: false,
return_regs: vec![ReturnRegister {
reg: 0, offset: 0,
size: size as u8,
is_xmm: false,
}],
classes: vec![X86ArgClass::Integer],
total_size: size,
is_multi_reg: false,
}
}
}
_ => {
if size > 16 {
AggregateReturnInfo {
uses_sret: true,
return_regs: vec![],
classes: vec![X86ArgClass::Memory],
total_size: size,
is_multi_reg: false,
}
} else if size <= 8 {
AggregateReturnInfo {
uses_sret: false,
return_regs: vec![ReturnRegister {
reg: 0,
offset: 0,
size: size as u8,
is_xmm: false,
}],
classes: vec![X86ArgClass::Integer],
total_size: size,
is_multi_reg: false,
}
} else {
AggregateReturnInfo {
uses_sret: false,
return_regs: vec![
ReturnRegister {
reg: 0,
offset: 0,
size: 8,
is_xmm: false,
},
ReturnRegister {
reg: 1, offset: 8,
size: (size - 8) as u8,
is_xmm: false,
},
],
classes: vec![X86ArgClass::Integer, X86ArgClass::Integer],
total_size: size,
is_multi_reg: true,
}
}
}
}
}
fn type_size(&self, ty: &Type) -> u64 {
match &ty.kind {
TypeKind::Integer { bits } => (*bits as u64 + 7) / 8,
TypeKind::Float => 4,
TypeKind::Double => 8,
TypeKind::X86FP80 => 16,
TypeKind::FP128 | TypeKind::PPCFP128 => 16,
TypeKind::Pointer { .. } => self.pointer_size as u64,
TypeKind::Array { len, .. } => *len as u64 * 8,
TypeKind::Struct {
element_type_ids,
..
} => element_type_ids.len() as u64 * 8,
TypeKind::FixedVector { len, .. } => *len as u64 * 8,
_ => 8,
}
}
pub fn is_trivial_for_abi(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::Struct {
element_type_ids,
..
} => self.type_size(ty) <= 16 && element_type_ids.len() <= 2,
_ => false,
}
}
pub fn stack_alignment_for_aggregate(&self, ty: &Type) -> u32 {
let size = self.type_size(ty);
if size >= 32 {
32
} else if size >= 16 {
16
} else {
8
}
}
pub fn clear_caches(&mut self) {
self.lower_cache.clear();
self.return_cache.clear();
}
pub fn compute_register_assignment(
&self,
ty: &Type,
available_gprs: &[u32],
available_sse: &[u32],
) -> AggregateRegisterAssignment {
let info = self.compute_argument_lowering(ty);
let mut assignment = AggregateRegisterAssignment::new();
let mut gpr_idx = 0usize;
let mut sse_idx = 0usize;
if info.gpr_count > 0 && gpr_idx < available_gprs.len() {
assignment.add_gpr_field(available_gprs[gpr_idx], 0, 8);
gpr_idx += 1;
if info.gpr_count > 1 && gpr_idx < available_gprs.len() {
assignment.add_gpr_field(available_gprs[gpr_idx], 8, 8);
}
}
if info.sse_count > 0 && sse_idx < available_sse.len() {
assignment.add_sse_field(available_sse[sse_idx], 0, 8);
sse_idx += 1;
if info.sse_count > 1 && sse_idx < available_sse.len() {
assignment.add_sse_field(available_sse[sse_idx], 8, 8);
}
}
assignment
}
pub fn is_homogeneous_aggregate(&self, ty: &Type) -> Option<Type> {
match &ty.kind {
TypeKind::Struct {
element_type_ids,
..
} => {
if element_type_ids.is_empty() {
return None;
}
let mut base_type: Option<Type> = None;
for _ in element_type_ids {
let field = Type::float(); if let Some(ref bt) = base_type {
if bt.kind != field.kind {
return None;
}
} else {
base_type = Some(field);
}
}
base_type
}
_ => None,
}
}
pub fn unpack_struct_from_regs(
&self,
reg_values: &[(u32, ValueRef)],
alloca_ptr: ValueRef,
field_offsets: &[u32],
field_sizes: &[u32],
) -> Vec<(u32, u32, u32)> {
let mut stored_fields = Vec::new();
for (i, ((reg, val), (&offset, &size))) in reg_values
.iter()
.zip(field_offsets.iter().zip(field_sizes.iter()))
.enumerate()
{
stored_fields.push((i as u32, offset, size));
let _ = reg;
let _ = val;
let _ = alloca_ptr;
}
stored_fields
}
pub fn pack_struct_to_regs(
&self,
alloca_ptr: ValueRef,
field_offsets: &[u32],
field_sizes: &[u32],
ret_regs: &[u32],
) -> Vec<(u32, u32, u32)> {
let mut packed = Vec::new();
for (i, ((&offset, &size), ®)) in field_offsets
.iter()
.zip(field_sizes.iter())
.zip(ret_regs.iter())
.enumerate()
{
packed.push((reg, offset, size));
let _ = alloca_ptr;
let _ = i;
}
packed
}
pub fn needs_sret(&self, ty: &Type) -> bool {
let info = self.compute_return_lowering(ty);
info.uses_sret
}
pub fn is_hfa_or_hva(&self, ty: &Type) -> bool {
self.is_homogeneous_aggregate(ty).is_some()
}
}
#[derive(Debug, Clone)]
pub struct X86VarArgs {
target_variant: X86TargetVariant,
calling_convention: X86CallingConvention,
num_fixed_params: usize,
gp_offset_offset: u32,
fp_offset_offset: u32,
overflow_arg_area_offset: u32,
reg_save_area_offset: u32,
va_list_size: u32,
num_gpr_save: usize,
num_xmm_save: usize,
reg_save_area_size: u32,
initial_gp_offset: u32,
initial_fp_offset: u32,
}
impl X86VarArgs {
pub fn new(
target_variant: X86TargetVariant,
calling_convention: X86CallingConvention,
num_fixed_params: usize,
) -> Self {
match (target_variant, calling_convention) {
(X86TargetVariant::X86_64, X86CallingConvention::Win64) => {
Self::win64_varargs(num_fixed_params)
}
(X86TargetVariant::X86_64 | X86TargetVariant::X32, _) => {
Self::sysv64_varargs(target_variant, num_fixed_params)
}
(X86TargetVariant::X86_32, _) => Self::x86_32_varargs(num_fixed_params),
_ => Self::sysv64_varargs(target_variant, num_fixed_params),
}
}
fn sysv64_varargs(target_variant: X86TargetVariant, num_fixed_params: usize) -> Self {
let gp_offset_offset = 0u32;
let fp_offset_offset = 4u32;
let overflow_arg_area_offset = 8u32;
let reg_save_area_offset = 16u32;
let va_list_size = 24u32;
let num_gpr_save = 6usize; let num_xmm_save = 8usize; let reg_save_area_size = (num_gpr_save * 8 + num_xmm_save * 16) as u32;
let initial_gp_offset = (num_fixed_params * 8) as u32;
let initial_fp_offset = if num_fixed_params > 0 {
(num_fixed_params.min(num_xmm_save) * 16) as u32
} else {
0
};
Self {
target_variant,
calling_convention: X86CallingConvention::X86_64_SysV,
num_fixed_params,
gp_offset_offset,
fp_offset_offset,
overflow_arg_area_offset,
reg_save_area_offset,
va_list_size,
num_gpr_save,
num_xmm_save,
reg_save_area_size,
initial_gp_offset,
initial_fp_offset,
}
}
fn win64_varargs(num_fixed_params: usize) -> Self {
let gp_offset_offset = 0u32;
let fp_offset_offset = 4u32;
let overflow_arg_area_offset = 8u32;
let reg_save_area_offset = 16u32;
let va_list_size = 32u32;
let num_gpr_save = 4usize; let num_xmm_save = 4usize; let reg_save_area_size = (num_gpr_save * 8 + num_xmm_save * 16) as u32;
let initial_gp_offset = 0;
let initial_fp_offset = 0;
Self {
target_variant: X86TargetVariant::X86_64,
calling_convention: X86CallingConvention::Win64,
num_fixed_params,
gp_offset_offset,
fp_offset_offset,
overflow_arg_area_offset,
reg_save_area_offset,
va_list_size,
num_gpr_save,
num_xmm_save,
reg_save_area_size,
initial_gp_offset,
initial_fp_offset,
}
}
fn x86_32_varargs(num_fixed_params: usize) -> Self {
let gp_offset_offset = 0u32;
let fp_offset_offset = 0u32;
let overflow_arg_area_offset = 0u32;
let reg_save_area_offset = 0u32;
let va_list_size = 4u32; let num_gpr_save = 0;
let num_xmm_save = 0;
let reg_save_area_size = 0;
let initial_gp_offset = (num_fixed_params * 4) as u32;
let initial_fp_offset = 0;
Self {
target_variant: X86TargetVariant::X86_32,
calling_convention: X86CallingConvention::C,
num_fixed_params,
gp_offset_offset,
fp_offset_offset,
overflow_arg_area_offset,
reg_save_area_offset,
va_list_size,
num_gpr_save,
num_xmm_save,
reg_save_area_size,
initial_gp_offset,
initial_fp_offset,
}
}
pub fn gp_offset_offset(&self) -> u32 {
self.gp_offset_offset
}
pub fn fp_offset_offset(&self) -> u32 {
self.fp_offset_offset
}
pub fn overflow_arg_area_offset(&self) -> u32 {
self.overflow_arg_area_offset
}
pub fn reg_save_area_offset(&self) -> u32 {
self.reg_save_area_offset
}
pub fn va_list_size(&self) -> u32 {
self.va_list_size
}
pub fn num_gpr_save(&self) -> usize {
self.num_gpr_save
}
pub fn num_xmm_save(&self) -> usize {
self.num_xmm_save
}
pub fn initial_gp_offset(&self) -> u32 {
self.initial_gp_offset
}
pub fn initial_fp_offset(&self) -> u32 {
self.initial_fp_offset
}
pub fn is_sysv(&self) -> bool {
!matches!(self.calling_convention, X86CallingConvention::Win64)
}
pub fn is_win64(&self) -> bool {
matches!(self.calling_convention, X86CallingConvention::Win64)
}
pub fn is_32bit_stack(&self) -> bool {
matches!(self.target_variant, X86TargetVariant::X86_32)
}
pub fn reg_save_area_size(&self) -> u32 {
self.reg_save_area_size
}
pub fn align_arg_size(&self, size: u32) -> u32 {
if self.is_32bit_stack() {
(size + 3) & !3
} else {
(size + 7) & !7
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TLSModelKind {
GeneralDynamic,
LocalDynamic,
InitialExec,
LocalExec,
}
impl X86TLSModelKind {
pub fn llvm_name(&self) -> &'static str {
match self {
X86TLSModelKind::GeneralDynamic => "general-dynamic",
X86TLSModelKind::LocalDynamic => "local-dynamic",
X86TLSModelKind::InitialExec => "initial-exec",
X86TLSModelKind::LocalExec => "local-exec",
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"general-dynamic" | "global-dynamic" => Some(X86TLSModelKind::GeneralDynamic),
"local-dynamic" => Some(X86TLSModelKind::LocalDynamic),
"initial-exec" => Some(X86TLSModelKind::InitialExec),
"local-exec" => Some(X86TLSModelKind::LocalExec),
_ => None,
}
}
}
impl Default for X86TLSModelKind {
fn default() -> Self {
X86TLSModelKind::GeneralDynamic
}
}
#[derive(Debug, Clone)]
pub struct X86TLSModel {
pub model: X86TLSModelKind,
pub local_exec_tls: bool,
pub tls_address_space: u32,
pub use_tlsdesc: bool,
pub segment_register: X86TLSSegmentReg,
pub gnu2_dialect: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TLSSegmentReg {
FS,
GS,
}
impl Default for X86TLSSegmentReg {
fn default() -> Self {
X86TLSSegmentReg::FS
}
}
impl X86TLSModel {
pub fn new() -> Self {
Self::default()
}
pub fn sysv_amd64() -> Self {
Self {
model: X86TLSModelKind::GeneralDynamic,
local_exec_tls: false,
tls_address_space: 0,
use_tlsdesc: true,
segment_register: X86TLSSegmentReg::FS,
gnu2_dialect: true,
}
}
pub fn windows_x64() -> Self {
Self {
model: X86TLSModelKind::GeneralDynamic,
local_exec_tls: false,
tls_address_space: 0,
use_tlsdesc: false,
segment_register: X86TLSSegmentReg::GS,
gnu2_dialect: false,
}
}
pub fn set_local_exec(&mut self, le: bool) {
self.local_exec_tls = le;
if le {
self.model = X86TLSModelKind::LocalExec;
}
}
pub fn set_model(&mut self, model: X86TLSModelKind) {
self.model = model;
}
pub fn llvm_model_string(&self) -> &'static str {
self.model.llvm_name()
}
pub fn address_space(&self) -> u32 {
self.tls_address_space
}
pub fn segment_prefix(&self) -> &'static str {
match self.segment_register {
X86TLSSegmentReg::FS => "%fs:",
X86TLSSegmentReg::GS => "%gs:",
}
}
pub fn access_sequence(&self, is_pic: bool) -> X86TLSAccessSeq {
match self.model {
X86TLSModelKind::GeneralDynamic => {
if self.use_tlsdesc {
X86TLSAccessSeq::TLSDESC
} else if is_pic {
X86TLSAccessSeq::GeneralDynamicPIC
} else {
X86TLSAccessSeq::GeneralDynamicNonPIC
}
}
X86TLSModelKind::LocalDynamic => {
if is_pic {
X86TLSAccessSeq::LocalDynamicPIC
} else {
X86TLSAccessSeq::LocalDynamicNonPIC
}
}
X86TLSModelKind::InitialExec => X86TLSAccessSeq::InitialExec,
X86TLSModelKind::LocalExec => X86TLSAccessSeq::LocalExec,
}
}
pub fn requires_tls_get_addr(&self) -> bool {
matches!(
self.model,
X86TLSModelKind::GeneralDynamic | X86TLSModelKind::LocalDynamic
)
}
}
impl Default for X86TLSModel {
fn default() -> Self {
Self {
model: X86TLSModelKind::GeneralDynamic,
local_exec_tls: false,
tls_address_space: 0,
use_tlsdesc: false,
segment_register: X86TLSSegmentReg::FS,
gnu2_dialect: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TLSAccessSeq {
GeneralDynamicPIC,
GeneralDynamicNonPIC,
TLSDESC,
LocalDynamicPIC,
LocalDynamicNonPIC,
InitialExec,
LocalExec,
}
impl X86TLSAccessSeq {
pub fn requires_call(&self) -> bool {
matches!(
self,
X86TLSAccessSeq::GeneralDynamicPIC
| X86TLSAccessSeq::GeneralDynamicNonPIC
| X86TLSAccessSeq::TLSDESC
| X86TLSAccessSeq::LocalDynamicPIC
| X86TLSAccessSeq::LocalDynamicNonPIC
)
}
pub fn relocation_type(&self) -> &'static str {
match self {
X86TLSAccessSeq::GeneralDynamicPIC => "R_X86_64_TLSGD",
X86TLSAccessSeq::GeneralDynamicNonPIC => "R_X86_64_TLSGD",
X86TLSAccessSeq::TLSDESC => "R_X86_64_TLSDESC",
X86TLSAccessSeq::LocalDynamicPIC => "R_X86_64_TLSLD",
X86TLSAccessSeq::LocalDynamicNonPIC => "R_X86_64_TLSLD",
X86TLSAccessSeq::InitialExec => "R_X86_64_GOTTPOFF",
X86TLSAccessSeq::LocalExec => "R_X86_64_TPOFF32",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum X86StackProtectorLevel {
None,
Basic,
Strong,
All,
}
impl X86StackProtectorLevel {
pub fn from_flag(flag: &str) -> Self {
match flag {
"none" | "no" | "off" => X86StackProtectorLevel::None,
"basic" | "default" => X86StackProtectorLevel::Basic,
"strong" => X86StackProtectorLevel::Strong,
"all" => X86StackProtectorLevel::All,
_ => X86StackProtectorLevel::Basic,
}
}
}
#[derive(Debug, Clone)]
pub struct X86StackProtector {
pub level: X86StackProtectorLevel,
pub has_protector: bool,
pub canary_global_name: String,
pub use_fs_canary: bool,
pub use_security_cookie: bool,
pub random_canary: Option<u64>,
pub canary_stack_offset: Option<i64>,
}
impl X86StackProtector {
pub fn new() -> Self {
Self::default()
}
pub fn set_level(&mut self, level: StackProtectorLevel) {
self.level = match level {
StackProtectorLevel::None => X86StackProtectorLevel::None,
StackProtectorLevel::Basic => X86StackProtectorLevel::Basic,
StackProtectorLevel::Strong => X86StackProtectorLevel::Strong,
StackProtectorLevel::All => X86StackProtectorLevel::All,
};
}
pub fn is_enabled(&self) -> bool {
self.level > X86StackProtectorLevel::None
}
pub fn should_protect(&self, has_local_array: bool, has_addr_taken: bool) -> bool {
match self.level {
X86StackProtectorLevel::None => false,
X86StackProtectorLevel::Basic => has_local_array,
X86StackProtectorLevel::Strong => has_local_array || has_addr_taken,
X86StackProtectorLevel::All => true,
}
}
pub fn canary_global_name(&self) -> &str {
if self.use_security_cookie {
"__security_cookie"
} else if self.use_fs_canary {
"__stack_chk_guard"
} else {
"__stack_chk_guard"
}
}
pub fn emit_setup(&mut self, cg: &ClangX86CodeGen) {
self.has_protector = true;
self.canary_stack_offset = Some(-8); let _ = cg; }
pub fn emit_check(&mut self, cg: &ClangX86CodeGen) {
if !self.has_protector {
return;
}
self.has_protector = false;
let _ = cg;
}
pub fn failure_function(&self) -> &'static str {
if self.use_security_cookie {
"__security_check_cookie"
} else {
"__stack_chk_fail"
}
}
pub fn uses_fs_segment(&self, os_abi: X86OSABI) -> bool {
match os_abi {
X86OSABI::SystemV => true,
X86OSABI::Darwin => true,
X86OSABI::Windows => false,
}
}
}
impl Default for X86StackProtector {
fn default() -> Self {
Self {
level: X86StackProtectorLevel::Basic,
has_protector: false,
canary_global_name: "__stack_chk_guard".to_string(),
use_fs_canary: true,
use_security_cookie: false,
random_canary: None,
canary_stack_offset: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PersonalityKind {
GXX,
GCC,
MSVC_X64,
MSVC_C,
SEH,
Wasm,
}
impl X86PersonalityKind {
pub fn symbol_name(&self) -> &'static str {
match self {
X86PersonalityKind::GXX => "__gxx_personality_v0",
X86PersonalityKind::GCC => "__gcc_personality_v0",
X86PersonalityKind::MSVC_X64 => "__CxxFrameHandler3",
X86PersonalityKind::MSVC_C => "__C_specific_handler",
X86PersonalityKind::SEH => "_except_handler3",
X86PersonalityKind::Wasm => "__gxx_wasm_personality_v0",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86EHTablesFormat {
Dwarf,
ARM,
Win64,
CompactUnwind,
}
#[derive(Debug, Clone)]
pub struct X86LandingPad {
pub catch_types: Vec<Type>,
pub is_cleanup: bool,
pub filter_fn: Option<String>,
pub type_ids: Vec<u32>,
}
#[derive(Debug, Clone)]
pub struct X86ExceptionHandling {
os_abi: X86OSABI,
personality: X86PersonalityKind,
table_format: X86EHTablesFormat,
emit_unwind_tables: bool,
nounwind_info: bool,
landing_pad_counter: u64,
type_info_counter: u64,
}
impl X86ExceptionHandling {
pub fn new(os_abi: X86OSABI) -> Self {
let (personality, table_format) = match os_abi {
X86OSABI::Windows => (X86PersonalityKind::MSVC_X64, X86EHTablesFormat::Win64),
X86OSABI::Darwin => (X86PersonalityKind::GXX, X86EHTablesFormat::CompactUnwind),
X86OSABI::SystemV => (X86PersonalityKind::GXX, X86EHTablesFormat::Dwarf),
};
Self {
os_abi,
personality,
table_format,
emit_unwind_tables: true,
nounwind_info: false,
landing_pad_counter: 0,
type_info_counter: 0,
}
}
pub fn personality_symbol(&self) -> &'static str {
self.personality.symbol_name()
}
pub fn table_format(&self) -> X86EHTablesFormat {
self.table_format
}
pub fn set_emit_unwind_tables(&mut self, emit: bool) {
self.emit_unwind_tables = emit;
}
pub fn set_nounwind_info(&mut self, info: bool) {
self.nounwind_info = info;
}
pub fn next_landing_pad_id(&mut self) -> u64 {
let id = self.landing_pad_counter;
self.landing_pad_counter += 1;
id
}
pub fn next_type_info_id(&mut self) -> u64 {
let id = self.type_info_counter;
self.type_info_counter += 1;
id
}
pub fn emit_landing_pad(
&mut self,
cg: &ClangX86CodeGen,
personality_fn: &str,
catch_types: &[Type],
) -> Result<(), String> {
let lp_id = self.next_landing_pad_id();
let _ = cg;
let _ = personality_fn;
let _ = catch_types;
let _ = lp_id;
Ok(())
}
pub fn emit_lsda(&self, cg: &ClangX86CodeGen, func_name: &str) {
let _ = cg;
let _ = func_name;
}
pub fn is_gnu_personality(&self) -> bool {
matches!(
self.personality,
X86PersonalityKind::GXX | X86PersonalityKind::GCC
)
}
pub fn is_windows_personality(&self) -> bool {
matches!(
self.personality,
X86PersonalityKind::MSVC_X64 | X86PersonalityKind::MSVC_C | X86PersonalityKind::SEH
)
}
pub fn type_info_encoding(&self) -> &'static str {
if self.is_gnu_personality() {
"DW_EH_PE_absptr"
} else {
"IMAGE_REL_BASED_DIR64"
}
}
pub fn generate_call_site_table(&self) -> Vec<X86CallSiteEntry> {
Vec::new()
}
pub fn generate_action_table(&self) -> Vec<X86ActionEntry> {
Vec::new()
}
}
#[derive(Debug, Clone)]
pub struct X86CallSiteEntry {
pub start_offset: u32,
pub length: u32,
pub landing_pad_offset: u32,
pub action_index: u32,
}
#[derive(Debug, Clone)]
pub struct X86ActionEntry {
pub type_filter: i32,
pub next_action: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86DebugFormat {
DwarfV4,
DwarfV5,
CodeView,
DwarfInCOFF,
None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DwarfBaseType {
Address,
Boolean,
ComplexFloat,
Float,
Signed,
SignedChar,
Unsigned,
UnsignedChar,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DwarfRegisterMapEntry {
pub llvm_reg: u32,
pub dwarf_reg: i32,
pub name: &'static str,
pub size: u32,
}
pub struct X86DebugInfo {
target_variant: X86TargetVariant,
os_abi: X86OSABI,
debug_format: X86DebugFormat,
debug_kind: DebugInfoKind,
dwarf_reg_map: HashMap<u32, DwarfRegisterMapEntry>,
compilation_dir: String,
file_cache: HashMap<String, u64>,
id_counter: u64,
emit_column_info: bool,
emit_macros: bool,
}
impl X86DebugInfo {
pub fn new(target_variant: X86TargetVariant, os_abi: X86OSABI) -> Self {
let debug_format = match os_abi {
X86OSABI::Windows => X86DebugFormat::CodeView,
_ => X86DebugFormat::DwarfV4,
};
let mut di = Self {
target_variant,
os_abi,
debug_format,
debug_kind: DebugInfoKind::NoDebug,
dwarf_reg_map: HashMap::new(),
compilation_dir: ".".to_string(),
file_cache: HashMap::new(),
id_counter: 0,
emit_column_info: true,
emit_macros: false,
};
di.populate_dwarf_reg_map();
di
}
fn populate_dwarf_reg_map(&mut self) {
if matches!(
self.target_variant,
X86TargetVariant::X86_64 | X86TargetVariant::X32
) {
self.add_dwarf_reg(0, 0, "rax", 8);
self.add_dwarf_reg(1, 1, "rdx", 8);
self.add_dwarf_reg(2, 2, "rcx", 8);
self.add_dwarf_reg(3, 3, "rbx", 8);
self.add_dwarf_reg(4, 4, "rsi", 8);
self.add_dwarf_reg(5, 5, "rdi", 8);
self.add_dwarf_reg(6, 6, "rbp", 8);
self.add_dwarf_reg(7, 7, "rsp", 8);
for i in 8u32..16 {
self.add_dwarf_reg(i, i as i32, "", 8);
}
self.add_dwarf_reg(16, 16, "rip", 8);
for i in 0u32..16 {
self.add_dwarf_reg(17 + i, 17 + i as i32, "", 16);
}
} else {
self.add_dwarf_reg(0, 0, "eax", 4);
self.add_dwarf_reg(1, 1, "ecx", 4);
self.add_dwarf_reg(2, 2, "edx", 4);
self.add_dwarf_reg(3, 3, "ebx", 4);
self.add_dwarf_reg(4, 4, "esp", 4);
self.add_dwarf_reg(5, 5, "ebp", 4);
self.add_dwarf_reg(6, 6, "esi", 4);
self.add_dwarf_reg(7, 7, "edi", 4);
self.add_dwarf_reg(8, 8, "eip", 4);
for i in 0u32..8 {
self.add_dwarf_reg(21 + i, 21 + i as i32, "", 16);
}
}
}
fn add_dwarf_reg(&mut self, llvm_reg: u32, dwarf_reg: i32, name: &'static str, size: u32) {
self.dwarf_reg_map.insert(
llvm_reg,
DwarfRegisterMapEntry {
llvm_reg,
dwarf_reg,
name,
size,
},
);
}
pub fn set_kind(&mut self, kind: DebugInfoKind) {
self.debug_kind = kind;
}
pub fn has_debug(&self) -> bool {
self.debug_kind.has_debug()
}
pub fn set_compilation_dir(&mut self, dir: &str) {
self.compilation_dir = dir.to_string();
}
pub fn emit_function_debug_info(
&mut self,
cg: &ClangX86CodeGen,
name: &str,
file: &str,
line: u32,
linkage_name: Option<&str>,
) {
if !self.has_debug() {
return;
}
let _ = cg;
let _ = name;
let _ = file;
let _ = line;
let _ = linkage_name;
self.id_counter += 1;
}
pub fn dwarf_reg_for_llvm(&self, llvm_reg: u32) -> Option<i32> {
self.dwarf_reg_map.get(&llvm_reg).map(|e| e.dwarf_reg)
}
pub fn dwarf_base_type_for_type(&self, ty: &Type) -> Option<DwarfBaseType> {
match &ty.kind {
TypeKind::Integer { bits } => {
if *bits == 1 {
Some(DwarfBaseType::Boolean)
} else if *bits <= 8 {
Some(DwarfBaseType::UnsignedChar)
} else {
Some(DwarfBaseType::Unsigned)
}
}
TypeKind::Float | TypeKind::Double | TypeKind::Half | TypeKind::BFloat => {
Some(DwarfBaseType::Float)
}
TypeKind::Pointer { .. } => Some(DwarfBaseType::Address),
_ => None,
}
}
pub fn dwarf_address_size(&self) -> u8 {
match self.target_variant {
X86TargetVariant::X86_64 => 8,
X86TargetVariant::X86_32 | X86TargetVariant::X32 => 4,
X86TargetVariant::X86_16 => 2,
}
}
pub fn dwarf_pointer_encoding(&self) -> &'static str {
match self.dwarf_address_size() {
8 => "DW_EH_PE_udata8",
4 => "DW_EH_PE_udata4",
_ => "DW_EH_PE_udata2",
}
}
pub fn emit_column_info(&self) -> bool {
self.emit_column_info
}
pub fn set_emit_column_info(&mut self, emit: bool) {
self.emit_column_info = emit;
}
pub fn debug_format(&self) -> X86DebugFormat {
self.debug_format
}
pub fn next_id(&mut self) -> u64 {
let id = self.id_counter;
self.id_counter += 1;
id
}
pub fn map_type_to_dwarf_encoding(&self, ty: &Type) -> u8 {
match &ty.kind {
TypeKind::Integer { bits } => {
if *bits == 1 {
0x02 } else if *bits <= 8 {
if *bits == 8 {
0x06 } else {
0x05 }
} else {
0x05 }
}
TypeKind::Float | TypeKind::Double | TypeKind::Half => {
0x04 }
TypeKind::Pointer { .. } => {
0x01 }
_ => 0x05, }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AddressSpaceQual {
Default,
Global,
Constant,
ThreadLocal,
Generic,
OpenCLGlobal,
OpenCLLocal,
OpenCLConstant,
OpenCLPrivate,
CudaGlobal,
CudaShared,
CudaConstant,
CudaLocal,
SegFS,
SegGS,
SegSS,
}
impl AddressSpaceQual {
pub fn default_space(&self) -> u32 {
0
}
pub fn name(&self) -> &'static str {
match self {
AddressSpaceQual::Default => "default",
AddressSpaceQual::Global => "global",
AddressSpaceQual::Constant => "constant",
AddressSpaceQual::ThreadLocal => "thread_local",
AddressSpaceQual::Generic => "generic",
AddressSpaceQual::OpenCLGlobal => "opencl_global",
AddressSpaceQual::OpenCLLocal => "opencl_local",
AddressSpaceQual::OpenCLConstant => "opencl_constant",
AddressSpaceQual::OpenCLPrivate => "opencl_private",
AddressSpaceQual::CudaGlobal => "cuda_global",
AddressSpaceQual::CudaShared => "cuda_shared",
AddressSpaceQual::CudaConstant => "cuda_constant",
AddressSpaceQual::CudaLocal => "cuda_local",
AddressSpaceQual::SegFS => "seg_fs",
AddressSpaceQual::SegGS => "seg_gs",
AddressSpaceQual::SegSS => "seg_ss",
}
}
}
pub struct X86AddressSpaceMap {
space_map: HashMap<AddressSpaceQual, u32>,
reverse_map: HashMap<u32, AddressSpaceQual>,
supports_flat: bool,
supports_segments: bool,
supports_opencl: bool,
supports_cuda: bool,
}
impl X86AddressSpaceMap {
pub fn new() -> Self {
let mut map = Self {
space_map: HashMap::new(),
reverse_map: HashMap::new(),
supports_flat: true,
supports_segments: false,
supports_opencl: false,
supports_cuda: false,
};
map.populate_defaults();
map
}
pub fn for_opencl() -> Self {
let mut map = Self {
space_map: HashMap::new(),
reverse_map: HashMap::new(),
supports_flat: true,
supports_segments: true,
supports_opencl: true,
supports_cuda: false,
};
map.populate_defaults();
map.space_map.insert(AddressSpaceQual::OpenCLGlobal, 1);
map.space_map.insert(AddressSpaceQual::OpenCLLocal, 3);
map.space_map.insert(AddressSpaceQual::OpenCLConstant, 2);
map.space_map.insert(AddressSpaceQual::OpenCLPrivate, 0);
map.space_map.insert(AddressSpaceQual::Generic, 4);
for (qual, space) in &map.space_map.clone() {
map.reverse_map.insert(*space, *qual);
}
map
}
pub fn for_cuda() -> Self {
let mut map = Self {
space_map: HashMap::new(),
reverse_map: HashMap::new(),
supports_flat: true,
supports_segments: false,
supports_opencl: false,
supports_cuda: true,
};
map.populate_defaults();
map.space_map.insert(AddressSpaceQual::CudaGlobal, 1);
map.space_map.insert(AddressSpaceQual::CudaConstant, 2);
map.space_map.insert(AddressSpaceQual::CudaShared, 3);
map.space_map.insert(AddressSpaceQual::CudaLocal, 5);
for (qual, space) in &map.space_map.clone() {
map.reverse_map.insert(*space, *qual);
}
map
}
fn populate_defaults(&mut self) {
self.space_map.insert(AddressSpaceQual::Default, 0);
self.space_map.insert(AddressSpaceQual::Global, 0);
self.space_map.insert(AddressSpaceQual::Constant, 0);
self.space_map.insert(AddressSpaceQual::ThreadLocal, 0);
self.space_map.insert(AddressSpaceQual::OpenCLPrivate, 0);
self.space_map.insert(AddressSpaceQual::CudaLocal, 0);
self.reverse_map.insert(0, AddressSpaceQual::Default);
}
pub fn map_address_space(&self, qual: AddressSpaceQual) -> u32 {
*self.space_map.get(&qual).unwrap_or(&0)
}
pub fn qualifier_for_space(&self, space: u32) -> Option<AddressSpaceQual> {
self.reverse_map.get(&space).copied()
}
pub fn is_supported(&self, space: u32) -> bool {
self.reverse_map.contains_key(&space)
}
pub fn default_address_space(&self) -> u32 {
0
}
pub fn is_default_address_space(&self, space: u32) -> bool {
space == 0
}
pub fn tls_address_space(&self) -> u32 {
self.map_address_space(AddressSpaceQual::ThreadLocal)
}
pub fn global_address_space(&self) -> u32 {
self.map_address_space(AddressSpaceQual::Global)
}
pub fn constant_address_space(&self) -> u32 {
self.map_address_space(AddressSpaceQual::Constant)
}
pub fn requires_addrspacecast(&self, from: u32, to: u32) -> bool {
from != to && (!self.is_default_address_space(from) || !self.is_default_address_space(to))
}
pub fn all_address_spaces(&self) -> Vec<(AddressSpaceQual, u32)> {
self.space_map.iter().map(|(k, v)| (*k, *v)).collect()
}
pub fn register_address_space(&mut self, qual: AddressSpaceQual, space: u32) {
self.space_map.insert(qual, space);
self.reverse_map.insert(space, qual);
}
pub fn segment_register_for_space(&self, space: u32) -> Option<&'static str> {
let qual = self.qualifier_for_space(space)?;
match qual {
AddressSpaceQual::SegFS => Some("fs"),
AddressSpaceQual::SegGS => Some("gs"),
AddressSpaceQual::SegSS => Some("ss"),
_ => None,
}
}
}
impl Default for X86AddressSpaceMap {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_codegen(name: &str) -> ClangX86CodeGen<'static> {
ClangX86CodeGen::new(name, "x86_64-unknown-linux-gnu")
}
fn make_codegen_win64(name: &str) -> ClangX86CodeGen<'static> {
ClangX86CodeGen::new(name, "x86_64-pc-windows-msvc")
}
fn make_codegen_32(name: &str) -> ClangX86CodeGen<'static> {
ClangX86CodeGen::new(name, "i386-unknown-linux-gnu")
}
fn make_codegen_x32(name: &str) -> ClangX86CodeGen<'static> {
ClangX86CodeGen::new(name, "x86_64-unknown-linux-gnux32")
}
#[test]
fn test_target_variant_from_triple_x86_64() {
assert_eq!(
X86TargetVariant::from_triple("x86_64-unknown-linux-gnu"),
X86TargetVariant::X86_64
);
assert_eq!(
X86TargetVariant::from_triple("amd64-pc-freebsd"),
X86TargetVariant::X86_64
);
}
#[test]
fn test_target_variant_from_triple_i386() {
assert_eq!(
X86TargetVariant::from_triple("i386-unknown-linux-gnu"),
X86TargetVariant::X86_32
);
assert_eq!(
X86TargetVariant::from_triple("i686-pc-linux-gnu"),
X86TargetVariant::X86_32
);
}
#[test]
fn test_target_variant_from_triple_x32() {
assert_eq!(
X86TargetVariant::from_triple("x86_64-unknown-linux-gnux32"),
X86TargetVariant::X32
);
}
#[test]
fn test_target_variant_pointer_size() {
assert_eq!(X86TargetVariant::X86_64.pointer_size_bytes(), 8);
assert_eq!(X86TargetVariant::X86_32.pointer_size_bytes(), 4);
assert_eq!(X86TargetVariant::X86_16.pointer_size_bytes(), 2);
assert_eq!(X86TargetVariant::X32.pointer_size_bytes(), 4);
}
#[test]
fn test_target_variant_is_64bit() {
assert!(X86TargetVariant::X86_64.is_64bit());
assert!(!X86TargetVariant::X86_32.is_64bit());
assert!(!X86TargetVariant::X86_16.is_64bit());
assert!(X86TargetVariant::X32.is_64bit());
}
#[test]
fn test_target_variant_stack_alignment() {
assert_eq!(X86TargetVariant::X86_64.stack_alignment(), 16);
assert_eq!(X86TargetVariant::X86_32.stack_alignment(), 16);
assert_eq!(X86TargetVariant::X86_16.stack_alignment(), 2);
}
#[test]
fn test_target_variant_red_zone() {
assert_eq!(X86TargetVariant::X86_64.red_zone_size(), 128);
assert_eq!(X86TargetVariant::X86_32.red_zone_size(), 0);
assert_eq!(X86TargetVariant::X32.red_zone_size(), 128);
}
#[test]
fn test_target_variant_display() {
assert_eq!(format!("{}", X86TargetVariant::X86_64), "x86_64");
assert_eq!(format!("{}", X86TargetVariant::X86_32), "i386");
}
#[test]
fn test_os_abi_from_triple_linux() {
assert_eq!(
X86OSABI::from_triple("x86_64-unknown-linux-gnu"),
X86OSABI::SystemV
);
}
#[test]
fn test_os_abi_from_triple_windows() {
assert_eq!(
X86OSABI::from_triple("x86_64-pc-windows-msvc"),
X86OSABI::Windows
);
assert_eq!(
X86OSABI::from_triple("x86_64-w64-mingw32"),
X86OSABI::Windows
);
}
#[test]
fn test_os_abi_from_triple_darwin() {
assert_eq!(
X86OSABI::from_triple("x86_64-apple-darwin"),
X86OSABI::Darwin
);
assert_eq!(
X86OSABI::from_triple("x86_64-apple-macos"),
X86OSABI::Darwin
);
}
#[test]
fn test_os_abi_default_cc() {
assert_eq!(
X86OSABI::SystemV.default_calling_convention(X86TargetVariant::X86_64),
X86CallingConvention::X86_64_SysV
);
assert_eq!(
X86OSABI::Windows.default_calling_convention(X86TargetVariant::X86_64),
X86CallingConvention::Win64
);
}
#[test]
fn test_codegen_new() {
let cg = make_codegen("test");
assert_eq!(cg.target_variant, X86TargetVariant::X86_64);
assert_eq!(cg.os_abi, X86OSABI::SystemV);
assert_eq!(cg.calling_convention, X86CallingConvention::X86_64_SysV);
}
#[test]
fn test_codegen_new_win64() {
let cg = make_codegen_win64("test");
assert_eq!(cg.target_variant, X86TargetVariant::X86_64);
assert_eq!(cg.os_abi, X86OSABI::Windows);
assert_eq!(cg.calling_convention, X86CallingConvention::Win64);
}
#[test]
fn test_codegen_new_32() {
let cg = make_codegen_32("test");
assert_eq!(cg.target_variant, X86TargetVariant::X86_32);
assert_eq!(cg.calling_convention, X86CallingConvention::C);
}
#[test]
fn test_codegen_new_x32() {
let cg = make_codegen_x32("test");
assert_eq!(cg.target_variant, X86TargetVariant::X32);
assert!(cg.target_variant.is_64bit());
}
#[test]
fn test_codegen_data_layout_64() {
let cg = make_codegen("test");
let dl = cg.data_layout_string();
assert!(dl.contains("p270:32:32"));
assert!(dl.contains("i64:64"));
assert!(dl.contains("S128"));
}
#[test]
fn test_codegen_data_layout_32() {
let cg = make_codegen_32("test");
let dl = cg.data_layout_string();
assert!(dl.contains("p:32:32"));
}
#[test]
fn test_codegen_enable_features() {
let mut cg = make_codegen("test");
assert!(!cg.flags.use_avx);
cg.enable_features(&["avx".to_string(), "sse4.1".to_string()]);
assert!(cg.flags.use_avx);
assert!(cg.flags.use_sse41);
assert!(!cg.flags.use_avx2);
}
#[test]
fn test_codegen_enable_avx2_implies_avx() {
let mut cg = make_codegen("test");
cg.enable_features(&["avx2".to_string()]);
assert!(cg.flags.use_avx);
assert!(cg.flags.use_avx2);
}
#[test]
fn test_codegen_enable_avx512_implies_all() {
let mut cg = make_codegen("test");
cg.enable_features(&["avx512f".to_string()]);
assert!(cg.flags.use_avx);
assert!(cg.flags.use_avx2);
assert!(cg.flags.use_avx512);
}
#[test]
fn test_codegen_disable_features() {
let mut cg = make_codegen("test");
cg.enable_features(&["avx".to_string()]);
assert!(cg.flags.use_avx);
cg.disable_features(&["avx".to_string()]);
assert!(!cg.flags.use_avx);
assert!(!cg.flags.use_avx2);
assert!(!cg.flags.use_avx512);
}
#[test]
fn test_codegen_set_target_triple() {
let mut cg = make_codegen("test");
cg.set_target_triple("i386-pc-windows-msvc");
assert_eq!(cg.target_variant, X86TargetVariant::X86_32);
assert_eq!(cg.os_abi, X86OSABI::Windows);
}
#[test]
fn test_codegen_set_calling_convention() {
let mut cg = make_codegen("test");
cg.set_calling_convention(X86CallingConvention::Win64);
assert_eq!(cg.calling_convention, X86CallingConvention::Win64);
}
#[test]
fn test_codegen_get_int_param_regs_sysv() {
let cg = make_codegen("test");
let regs = cg.get_int_param_regs();
assert_eq!(regs.len(), 6); }
#[test]
fn test_codegen_get_sse_param_regs_sysv() {
let cg = make_codegen("test");
let regs = cg.get_sse_param_regs();
assert_eq!(regs.len(), 8); }
#[test]
fn test_lowering_info_new_64() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(li.pointer_size, 8);
assert!(li.has_int128);
}
#[test]
fn test_lowering_info_new_32() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_32, X86OSABI::SystemV);
assert_eq!(li.pointer_size, 4);
assert!(!li.has_int128);
}
#[test]
fn test_lowering_info_win64_long_double() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::Windows);
assert_eq!(li.long_double_size, 8);
}
#[test]
fn test_lowering_info_sysv_long_double() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(li.long_double_size, 16);
}
#[test]
fn test_lowering_info_uses_sret_large_struct() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let large_struct =
Type::struct_literal_with(&[&Type::i64(), &Type::i64(), &Type::i64()], false);
assert!(li.uses_sret(&large_struct));
}
#[test]
fn test_lowering_info_uses_sret_small_struct() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let small_struct = Type::struct_literal_with(&[&Type::i32()], false);
assert!(!li.uses_sret(&small_struct));
}
#[test]
fn test_lowering_info_uses_sret_win64() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::Windows);
let medium_struct = Type::struct_literal_with(&[&Type::i64(), &Type::i64()], false);
assert!(li.uses_sret(&medium_struct));
}
#[test]
fn test_lowering_info_classify_int() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let classes = li.classify_arg_type(&Type::i32());
assert_eq!(classes.len(), 1);
assert_eq!(classes[0], X86ArgClass::Integer);
}
#[test]
fn test_lowering_info_classify_float() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let classes = li.classify_arg_type(&Type::float());
assert_eq!(classes[0], X86ArgClass::SSE);
}
#[test]
fn test_lowering_info_classify_double() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let classes = li.classify_arg_type(&Type::double());
assert_eq!(classes[0], X86ArgClass::SSE);
}
#[test]
fn test_lowering_info_classify_pointer() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let classes = li.classify_arg_type(&Type::pointer(&Type::i8()));
assert_eq!(classes[0], X86ArgClass::Integer);
}
#[test]
fn test_lowering_info_is_hfa_true() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let hfa_struct = Type::struct_literal_with(&[&Type::float(), &Type::float()], false);
assert!(li.is_hfa(&hfa_struct));
}
#[test]
fn test_lowering_info_is_hfa_false_mixed() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let mixed_struct = Type::struct_literal_with(&[&Type::float(), &Type::i32()], false);
assert!(!li.is_hfa(&mixed_struct));
}
#[test]
fn test_lowering_info_type_size_in_bits() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(li.type_size_in_bits(&Type::i32()), 32);
assert_eq!(li.type_size_in_bits(&Type::i64()), 64);
assert_eq!(li.type_size_in_bits(&Type::float()), 32);
assert_eq!(li.type_size_in_bits(&Type::double()), 64);
}
#[test]
fn test_intrinsic_mapper_new() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
assert!(mapper.all_builtin_names().len() > 100);
}
#[test]
fn test_intrinsic_mapper_sse_addps() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
let result = mapper.map_builtin("__builtin_ia32_addps");
assert_eq!(result, Some("llvm.x86.sse.add.ps".to_string()));
}
#[test]
fn test_intrinsic_mapper_sse2_cvtsd2si() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
let result = mapper.map_builtin("__builtin_ia32_cvtsd2si");
assert_eq!(result, Some("llvm.x86.sse2.cvtsd2si".to_string()));
}
#[test]
fn test_intrinsic_mapper_avx_addps256() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
let result = mapper.map_builtin("__builtin_ia32_addps256");
assert_eq!(result, Some("llvm.x86.avx.add.ps.256".to_string()));
}
#[test]
fn test_intrinsic_mapper_avx512_addps512() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
let result = mapper.map_builtin("__builtin_ia32_addps512");
assert_eq!(result, Some("llvm.x86.avx512.add.ps.512".to_string()));
}
#[test]
fn test_intrinsic_mapper_bmi_bextr() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
let result = mapper.map_builtin("__builtin_ia32_bextr_u32");
assert_eq!(result, Some("llvm.x86.bmi.bextr.32".to_string()));
}
#[test]
fn test_intrinsic_mapper_unknown() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
let result = mapper.map_builtin("__builtin_nonexistent");
assert_eq!(result, None);
}
#[test]
fn test_intrinsic_mapper_category() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
let cat = mapper.category_for_builtin("__builtin_ia32_addps");
assert_eq!(cat, Some(X86IntrinsicCategory::SSE));
}
#[test]
fn test_intrinsic_mapper_vector_builtin_sufficient_width() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
let result = mapper.map_vector_builtin("__builtin_ia32_addps256", 256);
assert_eq!(result, Some("llvm.x86.avx.add.ps.256".to_string()));
}
#[test]
fn test_intrinsic_mapper_vector_builtin_insufficient_width() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
let result = mapper.map_vector_builtin("__builtin_ia32_addps256", 128);
assert_eq!(result, None);
}
#[test]
fn test_intrinsic_mapper_all_categories() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
let sse = mapper.builtins_for_category(X86IntrinsicCategory::SSE);
let sse2 = mapper.builtins_for_category(X86IntrinsicCategory::SSE2);
let avx = mapper.builtins_for_category(X86IntrinsicCategory::AVX);
let avx2 = mapper.builtins_for_category(X86IntrinsicCategory::AVX2);
let avx512 = mapper.builtins_for_category(X86IntrinsicCategory::AVX512);
let aes = mapper.builtins_for_category(X86IntrinsicCategory::AES);
let bmi = mapper.builtins_for_category(X86IntrinsicCategory::BMI);
assert!(sse.len() > 10);
assert!(sse2.len() > 20);
assert!(avx.len() > 5);
assert!(avx2.len() > 5);
assert!(avx512.len() > 5);
assert!(aes.len() > 3);
assert!(bmi.len() > 3);
}
#[test]
fn test_intrinsic_mapper_feature_check() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
let mut features = HashSet::new();
assert!(!mapper.is_available("__builtin_ia32_addps", &features));
features.insert("sse".to_string());
assert!(mapper.is_available("__builtin_ia32_addps", &features));
}
#[test]
fn test_vector_abi_new_64() {
let vabi = X86VectorABI::new(X86TargetVariant::X86_64);
assert_eq!(vabi.sse_reg_count, 8);
assert_eq!(vabi.available_sse_regs(), 8);
}
#[test]
fn test_vector_abi_new_32() {
let vabi = X86VectorABI::new(X86TargetVariant::X86_32);
assert_eq!(vabi.sse_reg_count, 3);
}
#[test]
fn test_vector_abi_classify_128() {
let mut vabi = X86VectorABI::new(X86TargetVariant::X86_64);
let ty = Type::fixed_vector_with(&Type::float(), 4); let arg = vabi.classify_vector_arg(&ty);
assert_eq!(arg.width, X86VectorWidth::V128);
assert!(!arg.in_memory);
assert_eq!(arg.reg_assignment, Some(0));
}
#[test]
fn test_vector_abi_classify_256() {
let mut vabi = X86VectorABI::new(X86TargetVariant::X86_64);
let ty = Type::fixed_vector_with(&Type::double(), 4); let arg = vabi.classify_vector_arg(&ty);
assert_eq!(arg.width, X86VectorWidth::V256);
assert!(!arg.in_memory);
}
#[test]
fn test_vector_abi_reg_exhaustion() {
let mut vabi = X86VectorABI::new(X86TargetVariant::X86_64);
for i in 0..8 {
let ty = Type::fixed_vector_with(&Type::float(), 4);
let arg = vabi.classify_vector_arg(&ty);
assert_eq!(arg.reg_assignment, Some(i));
}
let ty = Type::fixed_vector_with(&Type::float(), 4);
let arg = vabi.classify_vector_arg(&ty);
assert!(arg.in_memory);
assert_eq!(arg.reg_assignment, None);
}
#[test]
fn test_vector_abi_vectorcall() {
let mut vabi = X86VectorABI::new(X86TargetVariant::X86_64);
vabi.enable_vectorcall();
assert_eq!(vabi.sse_reg_count, 6); }
#[test]
fn test_vector_abi_is_hva() {
let vabi = X86VectorABI::new(X86TargetVariant::X86_64);
let hva_struct = Type::struct_literal_with(
&[
&Type::fixed_vector_with(&Type::float(), 4),
&Type::fixed_vector_with(&Type::float(), 4),
],
false,
);
assert!(vabi.is_hva(&hva_struct));
}
#[test]
fn test_vector_abi_get_vector_return_reg() {
let vabi = X86VectorABI::new(X86TargetVariant::X86_64);
let ty = Type::fixed_vector_with(&Type::double(), 2); assert_eq!(vabi.get_vector_return_reg(&ty), Some(0));
}
#[test]
fn test_vector_abi_reset() {
let mut vabi = X86VectorABI::new(X86TargetVariant::X86_64);
let ty = Type::fixed_vector_with(&Type::float(), 4);
vabi.classify_vector_arg(&ty);
assert_eq!(vabi.available_sse_regs(), 7);
vabi.reset();
assert_eq!(vabi.available_sse_regs(), 8);
}
#[test]
fn test_aggregate_lowering_new() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(al.pointer_size, 8);
}
#[test]
fn test_aggregate_lowering_small_struct_sysv() {
let mut al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let small = Type::struct_literal_with(&[&Type::i32()], false);
let info = al.lower_for_argument(&small);
assert!(!info.needs_stack_temp);
assert!(matches!(
info.strategy,
AggregatePassingStrategy::SingleRegister
));
}
#[test]
fn test_aggregate_lowering_large_struct_sysv() {
let mut al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let large = Type::struct_literal_with(
&[&Type::i64(), &Type::i64(), &Type::i64(), &Type::i64()],
false,
);
let info = al.lower_for_argument(&large);
assert!(info.needs_stack_temp);
assert!(matches!(info.strategy, AggregatePassingStrategy::Indirect));
}
#[test]
fn test_aggregate_lowering_caching() {
let mut al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let ty = Type::struct_literal_with(&[&Type::i32()], false);
let info1 = al.lower_for_argument(&ty);
let info2 = al.lower_for_argument(&ty);
assert_eq!(info1.strategy, info2.strategy);
}
#[test]
fn test_aggregate_lowering_return_small_sysv() {
let mut al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let small = Type::struct_literal_with(&[&Type::i32()], false);
let info = al.lower_for_return(&small);
assert!(!info.uses_sret);
assert_eq!(info.return_regs.len(), 1);
}
#[test]
fn test_aggregate_lowering_return_large_sysv() {
let mut al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let large = Type::struct_literal_with(&[&Type::i64(), &Type::i64(), &Type::i64()], false);
let info = al.lower_for_return(&large);
assert!(info.uses_sret);
}
#[test]
fn test_aggregate_lowering_win64_large() {
let mut al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::Windows);
let medium = Type::struct_literal_with(&[&Type::i64(), &Type::i64()], false);
let info = al.lower_for_argument(&medium);
assert!(info.needs_stack_temp);
}
#[test]
fn test_aggregate_lowering_is_trivial() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let trivial = Type::struct_literal_with(&[&Type::i32(), &Type::i32()], false);
assert!(al.is_trivial_for_abi(&trivial));
let nontrivial =
Type::struct_literal_with(&[&Type::i64(), &Type::i64(), &Type::i64()], false);
assert!(!al.is_trivial_for_abi(&nontrivial));
}
#[test]
fn test_aggregate_lowering_clear_caches() {
let mut al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let ty = Type::struct_literal_with(&[&Type::i32()], false);
al.lower_for_argument(&ty);
al.lower_for_return(&ty);
al.clear_caches();
al.lower_for_argument(&ty);
al.lower_for_return(&ty);
}
#[test]
fn test_varargs_sysv_64() {
let va = X86VarArgs::new(
X86TargetVariant::X86_64,
X86CallingConvention::X86_64_SysV,
2,
);
assert_eq!(va.va_list_size(), 24);
assert_eq!(va.gp_offset_offset(), 0);
assert_eq!(va.fp_offset_offset(), 4);
assert_eq!(va.overflow_arg_area_offset(), 8);
assert_eq!(va.reg_save_area_offset(), 16);
assert_eq!(va.num_gpr_save(), 6);
assert_eq!(va.num_xmm_save(), 8);
assert!(va.is_sysv());
assert!(!va.is_win64());
}
#[test]
fn test_varargs_win64() {
let va = X86VarArgs::new(X86TargetVariant::X86_64, X86CallingConvention::Win64, 3);
assert_eq!(va.va_list_size(), 32);
assert_eq!(va.num_gpr_save(), 4);
assert_eq!(va.num_xmm_save(), 4);
assert!(va.is_win64());
assert!(!va.is_sysv());
}
#[test]
fn test_varargs_32() {
let va = X86VarArgs::new(X86TargetVariant::X86_32, X86CallingConvention::C, 1);
assert_eq!(va.va_list_size(), 4);
assert!(va.is_32bit_stack());
}
#[test]
fn test_varargs_initial_offsets() {
let va = X86VarArgs::new(
X86TargetVariant::X86_64,
X86CallingConvention::X86_64_SysV,
3,
);
assert_eq!(va.initial_gp_offset(), 24); }
#[test]
fn test_varargs_align_arg_size_64() {
let va = X86VarArgs::new(
X86TargetVariant::X86_64,
X86CallingConvention::X86_64_SysV,
0,
);
assert_eq!(va.align_arg_size(1), 8);
assert_eq!(va.align_arg_size(8), 8);
assert_eq!(va.align_arg_size(9), 16);
}
#[test]
fn test_varargs_align_arg_size_32() {
let va = X86VarArgs::new(X86TargetVariant::X86_32, X86CallingConvention::C, 0);
assert_eq!(va.align_arg_size(1), 4);
assert_eq!(va.align_arg_size(4), 4);
assert_eq!(va.align_arg_size(5), 8);
}
#[test]
fn test_tls_model_default() {
let tls = X86TLSModel::default();
assert_eq!(tls.model, X86TLSModelKind::GeneralDynamic);
assert!(!tls.local_exec_tls);
assert!(!tls.use_tlsdesc);
}
#[test]
fn test_tls_model_sysv_amd64() {
let tls = X86TLSModel::sysv_amd64();
assert!(tls.use_tlsdesc);
assert!(tls.gnu2_dialect);
assert_eq!(tls.segment_register, X86TLSSegmentReg::FS);
}
#[test]
fn test_tls_model_windows() {
let tls = X86TLSModel::windows_x64();
assert!(!tls.use_tlsdesc);
assert!(!tls.gnu2_dialect);
assert_eq!(tls.segment_register, X86TLSSegmentReg::GS);
}
#[test]
fn test_tls_model_set_local_exec() {
let mut tls = X86TLSModel::default();
tls.set_local_exec(true);
assert!(tls.local_exec_tls);
assert_eq!(tls.model, X86TLSModelKind::LocalExec);
}
#[test]
fn test_tls_model_access_sequence_gd_pic() {
let tls = X86TLSModel::sysv_amd64();
let seq = tls.access_sequence(true);
assert_eq!(seq, X86TLSAccessSeq::TLSDESC);
}
#[test]
fn test_tls_model_access_sequence_le() {
let mut tls = X86TLSModel::default();
tls.set_local_exec(true);
let seq = tls.access_sequence(false);
assert_eq!(seq, X86TLSAccessSeq::LocalExec);
}
#[test]
fn test_tls_model_requires_tls_get_addr() {
let tls = X86TLSModel::default();
assert!(tls.requires_tls_get_addr());
let mut le = X86TLSModel::default();
le.set_local_exec(true);
assert!(!le.requires_tls_get_addr());
}
#[test]
fn test_tls_model_kind_from_str() {
assert_eq!(
X86TLSModelKind::from_str("general-dynamic"),
Some(X86TLSModelKind::GeneralDynamic)
);
assert_eq!(
X86TLSModelKind::from_str("local-exec"),
Some(X86TLSModelKind::LocalExec)
);
assert_eq!(X86TLSModelKind::from_str("invalid"), None);
}
#[test]
fn test_tls_access_seq_relocation() {
assert_eq!(
X86TLSAccessSeq::GeneralDynamicPIC.relocation_type(),
"R_X86_64_TLSGD"
);
assert_eq!(
X86TLSAccessSeq::LocalExec.relocation_type(),
"R_X86_64_TPOFF32"
);
}
#[test]
fn test_stack_protector_default() {
let sp = X86StackProtector::default();
assert_eq!(sp.level, X86StackProtectorLevel::Basic);
assert!(!sp.has_protector);
}
#[test]
fn test_stack_protector_levels() {
assert_eq!(
X86StackProtectorLevel::from_flag("none"),
X86StackProtectorLevel::None
);
assert_eq!(
X86StackProtectorLevel::from_flag("strong"),
X86StackProtectorLevel::Strong
);
assert_eq!(
X86StackProtectorLevel::from_flag("all"),
X86StackProtectorLevel::All
);
}
#[test]
fn test_stack_protector_should_protect() {
let sp = X86StackProtector::default();
assert!(sp.should_protect(true, false)); assert!(!sp.should_protect(false, false)); }
#[test]
fn test_stack_protector_strong() {
let mut sp = X86StackProtector::default();
sp.level = X86StackProtectorLevel::Strong;
assert!(sp.should_protect(false, true)); assert!(!sp.should_protect(false, false)); }
#[test]
fn test_stack_protector_all() {
let mut sp = X86StackProtector::default();
sp.level = X86StackProtectorLevel::All;
assert!(sp.should_protect(false, false)); }
#[test]
fn test_stack_protector_canary_name() {
let sp = X86StackProtector::default();
assert_eq!(sp.canary_global_name(), "__stack_chk_guard");
assert_eq!(sp.failure_function(), "__stack_chk_fail");
}
#[test]
fn test_stack_protector_windows() {
let mut sp = X86StackProtector::default();
sp.use_security_cookie = true;
sp.use_fs_canary = false;
assert_eq!(sp.canary_global_name(), "__security_cookie");
assert_eq!(sp.failure_function(), "__security_check_cookie");
}
#[test]
fn test_stack_protector_fs_segment() {
let sp = X86StackProtector::default();
assert!(sp.uses_fs_segment(X86OSABI::SystemV));
assert!(!sp.uses_fs_segment(X86OSABI::Windows));
}
#[test]
fn test_eh_new_sysv() {
let eh = X86ExceptionHandling::new(X86OSABI::SystemV);
assert!(eh.is_gnu_personality());
assert!(!eh.is_windows_personality());
assert_eq!(eh.personality_symbol(), "__gxx_personality_v0");
}
#[test]
fn test_eh_new_windows() {
let eh = X86ExceptionHandling::new(X86OSABI::Windows);
assert!(eh.is_windows_personality());
assert!(!eh.is_gnu_personality());
assert_eq!(eh.personality_symbol(), "__CxxFrameHandler3");
}
#[test]
fn test_eh_new_darwin() {
let eh = X86ExceptionHandling::new(X86OSABI::Darwin);
assert!(eh.is_gnu_personality());
assert_eq!(eh.table_format(), X86EHTablesFormat::CompactUnwind);
}
#[test]
fn test_eh_table_format() {
let eh_sysv = X86ExceptionHandling::new(X86OSABI::SystemV);
assert_eq!(eh_sysv.table_format(), X86EHTablesFormat::Dwarf);
let eh_win = X86ExceptionHandling::new(X86OSABI::Windows);
assert_eq!(eh_win.table_format(), X86EHTablesFormat::Win64);
}
#[test]
fn test_eh_landing_pad_id() {
let mut eh = X86ExceptionHandling::new(X86OSABI::SystemV);
assert_eq!(eh.next_landing_pad_id(), 0);
assert_eq!(eh.next_landing_pad_id(), 1);
assert_eq!(eh.next_landing_pad_id(), 2);
}
#[test]
fn test_eh_type_info_id() {
let mut eh = X86ExceptionHandling::new(X86OSABI::SystemV);
assert_eq!(eh.next_type_info_id(), 0);
assert_eq!(eh.next_type_info_id(), 1);
}
#[test]
fn test_eh_type_info_encoding() {
let eh_gnu = X86ExceptionHandling::new(X86OSABI::SystemV);
assert_eq!(eh_gnu.type_info_encoding(), "DW_EH_PE_absptr");
let eh_win = X86ExceptionHandling::new(X86OSABI::Windows);
assert_eq!(eh_win.type_info_encoding(), "IMAGE_REL_BASED_DIR64");
}
#[test]
fn test_eh_emit_landing_pad() {
let mut eh = X86ExceptionHandling::new(X86OSABI::SystemV);
let cg = make_codegen("test");
let result = eh.emit_landing_pad(&cg, "__gxx_personality_v0", &[]);
assert!(result.is_ok());
}
#[test]
fn test_debug_info_new_64() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(di.debug_format(), X86DebugFormat::DwarfV4);
}
#[test]
fn test_debug_info_new_windows() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::Windows);
assert_eq!(di.debug_format(), X86DebugFormat::CodeView);
}
#[test]
fn test_debug_info_dwarf_reg_map_64() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(di.dwarf_reg_for_llvm(0), Some(0)); assert_eq!(di.dwarf_reg_for_llvm(6), Some(6)); assert_eq!(di.dwarf_reg_for_llvm(17), Some(17)); }
#[test]
fn test_debug_info_dwarf_reg_map_32() {
let di = X86DebugInfo::new(X86TargetVariant::X86_32, X86OSABI::SystemV);
assert_eq!(di.dwarf_reg_for_llvm(0), Some(0)); assert_eq!(di.dwarf_reg_for_llvm(5), Some(5)); assert_eq!(di.dwarf_reg_for_llvm(8), Some(8)); }
#[test]
fn test_debug_info_address_size() {
let di64 = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(di64.dwarf_address_size(), 8);
let di32 = X86DebugInfo::new(X86TargetVariant::X86_32, X86OSABI::SystemV);
assert_eq!(di32.dwarf_address_size(), 4);
}
#[test]
fn test_debug_info_dwarf_base_type_for_int() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let bt = di.dwarf_base_type_for_type(&Type::i32());
assert_eq!(bt, Some(DwarfBaseType::Unsigned));
}
#[test]
fn test_debug_info_dwarf_base_type_for_float() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let bt = di.dwarf_base_type_for_type(&Type::float());
assert_eq!(bt, Some(DwarfBaseType::Float));
}
#[test]
fn test_debug_info_dwarf_base_type_for_ptr() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let bt = di.dwarf_base_type_for_type(&Type::pointer(&Type::i8()));
assert_eq!(bt, Some(DwarfBaseType::Address));
}
#[test]
fn test_debug_info_has_debug() {
let mut di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(!di.has_debug());
di.set_kind(DebugInfoKind::FullDebug);
assert!(di.has_debug());
}
#[test]
fn test_debug_info_next_id() {
let mut di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(di.next_id(), 0);
assert_eq!(di.next_id(), 1);
assert_eq!(di.next_id(), 2);
}
#[test]
fn test_debug_info_map_type_to_dwarf() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(di.map_type_to_dwarf_encoding(&Type::i32()), 0x05);
assert_eq!(di.map_type_to_dwarf_encoding(&Type::float()), 0x04);
assert_eq!(
di.map_type_to_dwarf_encoding(&Type::pointer(&Type::i8())),
0x01
);
assert_eq!(di.map_type_to_dwarf_encoding(&Type::i1()), 0x02);
}
#[test]
fn test_address_space_map_default() {
let asm = X86AddressSpaceMap::new();
assert_eq!(asm.map_address_space(AddressSpaceQual::Default), 0);
assert_eq!(asm.default_address_space(), 0);
assert!(asm.is_default_address_space(0));
assert!(!asm.is_default_address_space(1));
}
#[test]
fn test_address_space_map_opencl() {
let asm = X86AddressSpaceMap::for_opencl();
assert_eq!(asm.map_address_space(AddressSpaceQual::OpenCLGlobal), 1);
assert_eq!(asm.map_address_space(AddressSpaceQual::OpenCLLocal), 3);
assert_eq!(asm.map_address_space(AddressSpaceQual::OpenCLConstant), 2);
}
#[test]
fn test_address_space_map_cuda() {
let asm = X86AddressSpaceMap::for_cuda();
assert_eq!(asm.map_address_space(AddressSpaceQual::CudaGlobal), 1);
assert_eq!(asm.map_address_space(AddressSpaceQual::CudaShared), 3);
assert_eq!(asm.map_address_space(AddressSpaceQual::CudaConstant), 2);
}
#[test]
fn test_address_space_map_qualifier_for_space() {
let asm = X86AddressSpaceMap::new();
assert_eq!(asm.qualifier_for_space(0), Some(AddressSpaceQual::Default));
assert_eq!(asm.qualifier_for_space(99), None);
}
#[test]
fn test_address_space_map_requires_addrspacecast() {
let asm = X86AddressSpaceMap::for_opencl();
assert!(asm.requires_addrspacecast(1, 3)); assert!(!asm.requires_addrspacecast(0, 0)); }
#[test]
fn test_address_space_map_register_custom() {
let mut asm = X86AddressSpaceMap::new();
asm.register_address_space(AddressSpaceQual::SegFS, 257);
assert_eq!(asm.map_address_space(AddressSpaceQual::SegFS), 257);
assert_eq!(asm.qualifier_for_space(257), Some(AddressSpaceQual::SegFS));
}
#[test]
fn test_address_space_map_segment_register() {
let mut asm = X86AddressSpaceMap::new();
asm.register_address_space(AddressSpaceQual::SegFS, 257);
asm.register_address_space(AddressSpaceQual::SegGS, 258);
assert_eq!(asm.segment_register_for_space(257), Some("fs"));
assert_eq!(asm.segment_register_for_space(258), Some("gs"));
assert_eq!(asm.segment_register_for_space(0), None);
}
#[test]
fn test_address_space_map_all_spaces() {
let asm = X86AddressSpaceMap::new();
let spaces = asm.all_address_spaces();
assert!(!spaces.is_empty());
assert!(spaces.contains(&(AddressSpaceQual::Default, 0)));
}
#[test]
fn test_codegen_flags_default() {
let flags = X86CodeGenFlags::default();
assert!(!flags.use_avx);
assert!(flags.use_red_zone);
assert!(flags.omit_frame_pointer);
assert!(!flags.address_sanitizer);
}
#[test]
fn test_codegen_flags_can_override() {
let flags = X86CodeGenFlags {
use_avx: true,
use_red_zone: false,
..Default::default()
};
assert!(flags.use_avx);
assert!(!flags.use_red_zone);
}
#[test]
fn test_vector_width_from_bits() {
assert_eq!(X86VectorWidth::from_bits(64), X86VectorWidth::V64);
assert_eq!(X86VectorWidth::from_bits(128), X86VectorWidth::V128);
assert_eq!(X86VectorWidth::from_bits(256), X86VectorWidth::V256);
assert_eq!(X86VectorWidth::from_bits(512), X86VectorWidth::V512);
}
#[test]
fn test_vector_width_bytes() {
assert_eq!(X86VectorWidth::V64.bytes(), 8);
assert_eq!(X86VectorWidth::V128.bytes(), 16);
assert_eq!(X86VectorWidth::V256.bytes(), 32);
assert_eq!(X86VectorWidth::V512.bytes(), 64);
}
#[test]
fn test_aggregate_strategy_equality() {
assert_eq!(
AggregatePassingStrategy::SingleRegister,
AggregatePassingStrategy::SingleRegister
);
assert_ne!(
AggregatePassingStrategy::SingleRegister,
AggregatePassingStrategy::Indirect
);
}
#[test]
fn test_tls_kind_llvm_name() {
assert_eq!(
X86TLSModelKind::GeneralDynamic.llvm_name(),
"general-dynamic"
);
assert_eq!(X86TLSModelKind::LocalExec.llvm_name(), "local-exec");
}
#[test]
fn test_personality_kind_symbols() {
assert_eq!(
X86PersonalityKind::GXX.symbol_name(),
"__gxx_personality_v0"
);
assert_eq!(
X86PersonalityKind::MSVC_X64.symbol_name(),
"__CxxFrameHandler3"
);
assert_eq!(X86PersonalityKind::SEH.symbol_name(), "_except_handler3");
}
#[test]
fn test_codegen_from_target_info() {
let ti = ClangTargetInfo::new("x86_64-unknown-linux-gnu");
let cg = ClangX86CodeGen::from_target_info("test", ti);
assert_eq!(cg.target_variant, X86TargetVariant::X86_64);
assert_eq!(cg.os_abi, X86OSABI::SystemV);
}
#[test]
fn test_address_space_qual_names() {
assert_eq!(AddressSpaceQual::Default.name(), "default");
assert_eq!(AddressSpaceQual::OpenCLGlobal.name(), "opencl_global");
assert_eq!(AddressSpaceQual::CudaGlobal.name(), "cuda_global");
assert_eq!(AddressSpaceQual::SegFS.name(), "seg_fs");
}
#[test]
fn test_os_abi_darwin_cc() {
assert_eq!(
X86OSABI::Darwin.default_calling_convention(X86TargetVariant::X86_64),
X86CallingConvention::X86_64_SysV
);
}
#[test]
fn test_varargs_reg_save_area_size_sysv() {
let va = X86VarArgs::new(
X86TargetVariant::X86_64,
X86CallingConvention::X86_64_SysV,
0,
);
assert_eq!(va.reg_save_area_size(), 176);
}
#[test]
fn test_varargs_reg_save_area_size_win64() {
let va = X86VarArgs::new(X86TargetVariant::X86_64, X86CallingConvention::Win64, 0);
assert_eq!(va.reg_save_area_size(), 96);
}
#[test]
fn test_eh_set_emit_unwind_tables() {
let mut eh = X86ExceptionHandling::new(X86OSABI::SystemV);
assert!(eh.emit_unwind_tables);
eh.set_emit_unwind_tables(false);
assert!(!eh.emit_unwind_tables);
}
#[test]
fn test_eh_set_nounwind_info() {
let mut eh = X86ExceptionHandling::new(X86OSABI::SystemV);
assert!(!eh.nounwind_info);
eh.set_nounwind_info(true);
assert!(eh.nounwind_info);
}
#[test]
fn test_debug_info_set_compilation_dir() {
let mut di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
di.set_compilation_dir("/home/user/project");
assert_eq!(di.compilation_dir, "/home/user/project");
}
#[test]
fn test_debug_info_column_info() {
let mut di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(di.emit_column_info());
di.set_emit_column_info(false);
assert!(!di.emit_column_info());
}
#[test]
fn test_stack_protector_level_ordering() {
assert!(X86StackProtectorLevel::All > X86StackProtectorLevel::Strong);
assert!(X86StackProtectorLevel::Strong > X86StackProtectorLevel::Basic);
assert!(X86StackProtectorLevel::Basic > X86StackProtectorLevel::None);
}
#[test]
fn test_intrinsic_entry_fields() {
let entry = X86IntrinsicEntry {
builtin_name: "__builtin_ia32_addps".to_string(),
llvm_intrinsic: "llvm.x86.sse.add.ps".to_string(),
category: X86IntrinsicCategory::SSE,
min_bit_width: 128,
num_operands: 2,
};
assert_eq!(entry.min_bit_width, 128);
assert_eq!(entry.num_operands, 2);
assert_eq!(entry.category.feature_flag(), "sse");
}
#[test]
fn test_eh_call_site_entry() {
let cs = X86CallSiteEntry {
start_offset: 0x1000,
length: 0x50,
landing_pad_offset: 0x1100,
action_index: 1,
};
assert_eq!(cs.start_offset, 0x1000);
assert_eq!(cs.length, 0x50);
}
#[test]
fn test_eh_action_entry() {
let action = X86ActionEntry {
type_filter: 1,
next_action: 0,
};
assert_eq!(action.type_filter, 1);
assert_eq!(action.next_action, 0);
}
#[test]
fn test_aggregate_lowering_32() {
let mut al = X86AggregateLowering::new(X86TargetVariant::X86_32, X86OSABI::SystemV);
let small = Type::struct_literal_with(&[&Type::i32()], false);
let info = al.lower_for_argument(&small);
assert_eq!(info.total_size, 8);
}
#[test]
fn test_varargs_32_no_saves() {
let va = X86VarArgs::new(X86TargetVariant::X86_32, X86CallingConvention::C, 0);
assert_eq!(va.num_gpr_save(), 0);
assert_eq!(va.num_xmm_save(), 0);
assert_eq!(va.reg_save_area_size(), 0);
}
#[test]
fn test_eh_generate_call_site_table_empty() {
let eh = X86ExceptionHandling::new(X86OSABI::SystemV);
let table = eh.generate_call_site_table();
assert!(table.is_empty());
}
#[test]
fn test_eh_generate_action_table_empty() {
let eh = X86ExceptionHandling::new(X86OSABI::SystemV);
let table = eh.generate_action_table();
assert!(table.is_empty());
}
#[test]
fn test_dwarf_register_map_entry() {
let entry = DwarfRegisterMapEntry {
llvm_reg: 0,
dwarf_reg: 0,
name: "rax",
size: 8,
};
assert_eq!(entry.name, "rax");
assert_eq!(entry.size, 8);
}
#[test]
fn test_tls_access_seq_requires_call() {
assert!(X86TLSAccessSeq::GeneralDynamicPIC.requires_call());
assert!(X86TLSAccessSeq::TLSDESC.requires_call());
assert!(X86TLSAccessSeq::LocalDynamicPIC.requires_call());
assert!(!X86TLSAccessSeq::InitialExec.requires_call());
assert!(!X86TLSAccessSeq::LocalExec.requires_call());
}
#[test]
fn test_intrinsic_category_feature_flags() {
assert_eq!(X86IntrinsicCategory::SSE.feature_flag(), "sse");
assert_eq!(X86IntrinsicCategory::AVX.feature_flag(), "avx");
assert_eq!(X86IntrinsicCategory::AVX512.feature_flag(), "avx512f");
assert_eq!(X86IntrinsicCategory::General.feature_flag(), "");
}
#[test]
fn test_lowering_info_param_type_array_decay() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let arr_type = Type::array_with(&Type::i32(), 10);
let lowered = li.lower_parameter_type(&arr_type);
assert!(lowered.is_pointer());
}
#[test]
fn test_aggregate_lowering_stack_alignment() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let large = Type::struct_literal_with(
&[&Type::i64(), &Type::i64(), &Type::i64(), &Type::i64()],
false,
);
let alignment = al.stack_alignment_for_aggregate(&large);
assert_eq!(alignment, 32);
}
#[test]
fn test_debug_info_darwin_format() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::Darwin);
assert_eq!(di.debug_format(), X86DebugFormat::DwarfV4);
}
#[test]
fn test_lowering_info_type_alignment() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(li.type_alignment_in_bits(&Type::i32()), 32);
assert_eq!(li.type_alignment_in_bits(&Type::i64()), 64);
assert_eq!(li.type_alignment_in_bits(&Type::double()), 64);
}
#[test]
fn test_stack_protector_set_level_from_clang() {
let mut sp = X86StackProtector::default();
sp.set_level(StackProtectorLevel::Strong);
assert_eq!(sp.level, X86StackProtectorLevel::Strong);
sp.set_level(StackProtectorLevel::None);
assert_eq!(sp.level, X86StackProtectorLevel::None);
sp.set_level(StackProtectorLevel::All);
assert_eq!(sp.level, X86StackProtectorLevel::All);
}
#[test]
fn test_intrinsic_mapper_xsave() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
assert_eq!(
mapper.map_builtin("__builtin_ia32_xsave"),
Some("llvm.x86.xsave".to_string())
);
assert_eq!(
mapper.map_builtin("__builtin_ia32_xrstor"),
Some("llvm.x86.xrstor".to_string())
);
}
#[test]
fn test_intrinsic_mapper_adx() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
assert_eq!(
mapper.map_builtin("__builtin_ia32_addcarry_u32"),
Some("llvm.x86.addcarry.32".to_string())
);
assert_eq!(
mapper.map_builtin("__builtin_ia32_subborrow_u64"),
Some("llvm.x86.subborrow.64".to_string())
);
}
#[test]
fn test_intrinsic_mapper_clflushopt() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
assert_eq!(
mapper.map_builtin("__builtin_ia32_clflush"),
Some("llvm.x86.sse2.clflush".to_string())
);
}
#[test]
fn test_intrinsic_mapper_fma() {
let mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
assert_eq!(
mapper.map_builtin("__builtin_ia32_vfmaddps"),
Some("llvm.x86.fma.vfmadd.ps".to_string())
);
}
#[test]
fn test_aggregate_lowering_is_homogeneous() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let hfa = Type::struct_literal_with(&[&Type::float(), &Type::float()], false);
assert!(al.is_homogeneous_aggregate(&hfa).is_some());
}
#[test]
fn test_aggregate_lowering_register_assignment_sysv() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let ty = Type::struct_literal_with(&[&Type::i64(), &Type::i64()], false);
let gprs = vec![5, 4, 3, 2, 8, 9]; let sses = vec![0, 1, 2, 3, 4, 5, 6, 7];
let assignment = al.compute_register_assignment(&ty, &gprs, &sses);
assert_eq!(assignment.total_gpr_count(), 2);
assert_eq!(assignment.total_sse_count(), 0);
}
#[test]
fn test_aggregate_lowering_needs_sret() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let large = Type::struct_literal_with(&[&Type::i64(), &Type::i64(), &Type::i64()], false);
assert!(al.needs_sret(&large));
let small = Type::struct_literal_with(&[&Type::i32()], false);
assert!(!al.needs_sret(&small));
}
#[test]
fn test_aggregate_lowering_is_hfa_or_hva() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let hfa = Type::struct_literal_with(&[&Type::float(), &Type::float()], false);
assert!(al.is_hfa_or_hva(&hfa));
let mixed = Type::struct_literal_with(&[&Type::float(), &Type::i32()], false);
assert!(!al.is_hfa_or_hva(&mixed));
}
#[test]
fn test_aggregate_lowering_unpack_pack() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let stored = al.unpack_struct_from_regs(&[], valref(0), &[], &[]);
assert!(stored.is_empty());
let packed = al.pack_struct_to_regs(valref(0), &[], &[], &[]);
assert!(packed.is_empty());
}
#[test]
fn test_vector_lowering_result_success() {
let result = X86VectorLoweringResult::success(valref(42));
assert!(result.value.is_some());
assert!(!result.is_libcall);
assert!(!result.is_constant);
assert!(result.error.is_none());
}
#[test]
fn test_vector_lowering_result_libcall() {
let result = X86VectorLoweringResult::libcall("__svml_sin4");
assert!(result.is_libcall);
assert_eq!(result.libcall_name, Some("__svml_sin4".to_string()));
}
#[test]
fn test_vector_lowering_result_error() {
let result = X86VectorLoweringResult::error("unsupported vector width");
assert!(result.error.is_some());
}
#[test]
fn test_shuffle_mask_identity() {
let mask = X86ShuffleMask::new(vec![0, 1, 2, 3]);
assert!(mask.is_identity());
assert!(!mask.is_splat());
assert_eq!(mask.num_elements(), 4);
}
#[test]
fn test_shuffle_mask_splat() {
let mask = X86ShuffleMask::new(vec![2, 2, 2, 2]);
assert!(!mask.is_identity());
assert!(mask.is_splat());
}
#[test]
fn test_shuffle_mask_zeroing() {
let mask = X86ShuffleMask::new(vec![0, -1, 2, -1]);
assert!(mask.is_zeroing());
assert!(!mask.is_splat());
}
#[test]
fn test_shuffle_mask_unpckl() {
let mask = X86ShuffleMask::new(vec![0, 4, 1, 5]);
assert!(!mask.is_identity());
}
#[test]
fn test_tls_model_gnu2_dialect() {
let tls = X86TLSModel::sysv_amd64();
assert!(tls.gnu2_dialect);
assert!(tls.use_tlsdesc);
let win_tls = X86TLSModel::windows_x64();
assert!(!win_tls.gnu2_dialect);
assert!(!win_tls.use_tlsdesc);
}
#[test]
fn test_tls_model_segment_register() {
let sysv = X86TLSModel::sysv_amd64();
assert_eq!(sysv.segment_register, X86TLSSegmentReg::FS);
assert_eq!(sysv.segment_prefix(), "%fs:");
let win = X86TLSModel::windows_x64();
assert_eq!(win.segment_register, X86TLSSegmentReg::GS);
assert_eq!(win.segment_prefix(), "%gs:");
}
#[test]
fn test_tls_model_access_sequence_ie() {
let mut tls = X86TLSModel::sysv_amd64();
tls.set_model(X86TLSModelKind::InitialExec);
let seq = tls.access_sequence(false);
assert_eq!(seq, X86TLSAccessSeq::InitialExec);
assert!(!seq.requires_call());
}
#[test]
fn test_stack_protector_is_enabled() {
let sp = X86StackProtector::default();
assert!(sp.is_enabled());
let mut none = X86StackProtector::default();
none.level = X86StackProtectorLevel::None;
assert!(!none.is_enabled());
}
#[test]
fn test_stack_protector_emit_setup() {
let mut sp = X86StackProtector::default();
let cg = make_codegen("protector_test");
sp.emit_setup(&cg);
assert!(sp.has_protector);
assert_eq!(sp.canary_stack_offset, Some(-8));
}
#[test]
fn test_stack_protector_emit_check_noop() {
let mut sp = X86StackProtector::default();
let cg = make_codegen("protector_test");
sp.emit_check(&cg);
assert!(!sp.has_protector);
}
#[test]
fn test_eh_personality_gcc() {
assert_eq!(
X86PersonalityKind::GCC.symbol_name(),
"__gcc_personality_v0"
);
}
#[test]
fn test_eh_personality_msvc_c() {
assert_eq!(
X86PersonalityKind::MSVC_C.symbol_name(),
"__C_specific_handler"
);
}
#[test]
fn test_eh_call_site_table_generation() {
let mut eh = X86ExceptionHandling::new(X86OSABI::SystemV);
assert!(eh.generate_call_site_table().is_empty());
assert!(eh.generate_action_table().is_empty());
}
#[test]
fn test_eh_emit_lsda_noop() {
let eh = X86ExceptionHandling::new(X86OSABI::SystemV);
let cg = make_codegen("lsda_test");
eh.emit_lsda(&cg, "test_func");
}
#[test]
fn test_debug_info_unknown_reg() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(di.dwarf_reg_for_llvm(999), None);
}
#[test]
fn test_debug_info_emit_function_no_debug() {
let mut di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
di.set_kind(DebugInfoKind::NoDebug);
let cg = make_codegen("debug_test");
di.emit_function_debug_info(&cg, "test", "test.c", 10, None);
}
#[test]
fn test_debug_info_pointer_encoding() {
let di64 = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(di64.dwarf_pointer_encoding(), "DW_EH_PE_udata8");
let di32 = X86DebugInfo::new(X86TargetVariant::X86_32, X86OSABI::SystemV);
assert_eq!(di32.dwarf_pointer_encoding(), "DW_EH_PE_udata4");
}
#[test]
fn test_debug_info_map_bool_to_dwarf() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(di.map_type_to_dwarf_encoding(&Type::i1()), 0x02);
}
#[test]
fn test_debug_info_map_signed_char() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(di.map_type_to_dwarf_encoding(&Type::i8()), 0x06);
}
#[test]
fn test_address_space_map_tls_space() {
let asm = X86AddressSpaceMap::new();
assert_eq!(asm.tls_address_space(), 0);
assert_eq!(asm.global_address_space(), 0);
assert_eq!(asm.constant_address_space(), 0);
}
#[test]
fn test_address_space_map_supported() {
let asm = X86AddressSpaceMap::new();
assert!(asm.is_supported(0));
assert!(!asm.is_supported(99));
let opencl = X86AddressSpaceMap::for_opencl();
assert!(opencl.is_supported(1));
assert!(opencl.is_supported(3));
}
#[test]
fn test_address_space_map_addrspacecast() {
let asm = X86AddressSpaceMap::for_opencl();
assert!(!asm.requires_addrspacecast(0, 0));
assert!(asm.requires_addrspacecast(1, 3));
assert!(asm.requires_addrspacecast(0, 1));
}
#[test]
fn test_codegen_from_target_info_with_flags() {
let mut ti = ClangTargetInfo::new("x86_64-unknown-linux-gnu");
ti.position_independent = true;
let cg = ClangX86CodeGen::from_target_info("pic_test", ti);
assert!(cg.flags.position_independent);
}
#[test]
fn test_codegen_module_access() {
let cg = make_codegen("module_test");
assert!(!cg.module().get_name().is_empty());
}
#[test]
fn test_codegen_apply_data_layout() {
let mut cg = make_codegen("dl_test");
cg.apply_data_layout();
}
#[test]
fn test_codegen_varargs_init() {
let mut cg = make_codegen("va_test");
assert!(cg.varargs_info().is_none());
cg.init_varargs(3);
assert!(cg.varargs_info().is_some());
let va = cg.varargs_info().unwrap();
assert_eq!(va.num_fixed_params, 3);
}
#[test]
fn test_codegen_builder_access() {
let cg = make_codegen("builder_test");
let _builder = cg.builder();
}
#[test]
fn test_codegen_flags_sanitizers() {
let mut flags = X86CodeGenFlags::default();
assert!(!flags.address_sanitizer);
assert!(!flags.thread_sanitizer);
assert!(!flags.memory_sanitizer);
flags.address_sanitizer = true;
assert!(flags.address_sanitizer);
}
#[test]
fn test_codegen_flags_security() {
let mut flags = X86CodeGenFlags::default();
assert!(!flags.cet_ibt);
assert!(!flags.cet_ss);
assert!(!flags.lvi_mitigation);
flags.cet_ibt = true;
flags.cet_ss = true;
assert!(flags.cet_ibt);
assert!(flags.cet_ss);
}
#[test]
fn test_codegen_flags_fp_behavior() {
let mut flags = X86CodeGenFlags::default();
assert!(!flags.use_x87);
assert!(flags.use_red_zone);
flags.use_x87 = true;
flags.use_red_zone = false;
assert!(flags.use_x87);
assert!(!flags.use_red_zone);
}
#[test]
fn test_vector_width_from_bits_edge_cases() {
assert_eq!(X86VectorWidth::from_bits(0), X86VectorWidth::V64);
assert_eq!(X86VectorWidth::from_bits(65), X86VectorWidth::V128);
assert_eq!(X86VectorWidth::from_bits(129), X86VectorWidth::V256);
assert_eq!(X86VectorWidth::from_bits(300), X86VectorWidth::V512);
}
#[test]
fn test_vector_width_alignment() {
assert_eq!(X86VectorWidth::V64.alignment(), 8);
assert_eq!(X86VectorWidth::V128.alignment(), 16);
assert_eq!(X86VectorWidth::V256.alignment(), 32);
assert_eq!(X86VectorWidth::V512.alignment(), 64);
}
#[test]
fn test_aggregate_register_assignment_default() {
let assignment = AggregateRegisterAssignment::default();
assert_eq!(assignment.total_gpr_count(), 0);
assert_eq!(assignment.total_sse_count(), 0);
}
#[test]
fn test_aggregate_register_assignment_add_gpr() {
let mut assignment = AggregateRegisterAssignment::new();
assignment.add_gpr_field(5, 0, 8);
assignment.add_gpr_field(4, 8, 8);
assert_eq!(assignment.total_gpr_count(), 2);
assert_eq!(assignment.total_sse_count(), 0);
}
#[test]
fn test_aggregate_register_assignment_add_sse() {
let mut assignment = AggregateRegisterAssignment::new();
assignment.add_sse_field(0, 0, 16);
assert_eq!(assignment.total_gpr_count(), 0);
assert_eq!(assignment.total_sse_count(), 1);
}
#[test]
fn test_lowering_info_has_int128() {
let li64 = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(li64.has_int128);
let li32 = X86LoweringInfo::new(X86TargetVariant::X86_32, X86OSABI::SystemV);
assert!(!li32.has_int128);
}
#[test]
fn test_lowering_info_large_classify() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let large = Type::struct_literal_with(
&[&Type::i64(), &Type::i64(), &Type::i64(), &Type::i64()],
false,
);
let classes = li.classify_arg_type(&large);
assert!(!classes.is_empty());
}
#[test]
fn test_lowering_info_hfa_count() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let hfa = Type::struct_literal_with(&[&Type::float(), &Type::float()], false);
assert_eq!(li.hfa_element_count(&hfa), 2);
}
#[test]
fn test_aggregate_strategy_all_variants() {
let strategies = [
AggregatePassingStrategy::Indirect,
AggregatePassingStrategy::SplitIntegers,
AggregatePassingStrategy::SplitSSE,
AggregatePassingStrategy::SplitMixed,
AggregatePassingStrategy::SingleRegister,
AggregatePassingStrategy::Stack,
];
for s in &strategies {
assert_eq!(*s, *s);
}
}
#[test]
fn test_varargs_sysv_is_not_win64() {
let va = X86VarArgs::new(
X86TargetVariant::X86_64,
X86CallingConvention::X86_64_SysV,
0,
);
assert!(!va.is_win64());
assert!(va.is_sysv());
assert!(!va.is_32bit_stack());
}
#[test]
fn test_varargs_initial_fp_offset() {
let va = X86VarArgs::new(
X86TargetVariant::X86_64,
X86CallingConvention::X86_64_SysV,
1,
);
assert_eq!(va.initial_fp_offset(), 16);
}
#[test]
fn test_tls_model_address_space() {
let tls = X86TLSModel::default();
assert_eq!(tls.address_space(), 0);
}
#[test]
fn test_tls_model_set_model() {
let mut tls = X86TLSModel::default();
tls.set_model(X86TLSModelKind::InitialExec);
assert_eq!(tls.model, X86TLSModelKind::InitialExec);
}
#[test]
fn test_tls_access_seq_all_relocations() {
assert_eq!(
X86TLSAccessSeq::TLSDESC.relocation_type(),
"R_X86_64_TLSDESC"
);
assert_eq!(
X86TLSAccessSeq::LocalDynamicPIC.relocation_type(),
"R_X86_64_TLSLD"
);
assert_eq!(
X86TLSAccessSeq::InitialExec.relocation_type(),
"R_X86_64_GOTTPOFF"
);
}
#[test]
fn test_debug_info_darwin_address_size() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::Darwin);
assert_eq!(di.dwarf_address_size(), 8);
}
#[test]
fn test_debug_info_kind_transitions() {
let mut di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(!di.has_debug());
di.set_kind(DebugInfoKind::LineTablesOnly);
assert!(di.has_debug());
di.set_kind(DebugInfoKind::FullWithMacros);
assert!(di.has_debug());
di.set_kind(DebugInfoKind::NoDebug);
assert!(!di.has_debug());
}
#[test]
fn test_eh_windows_seh_personality() {
let eh = X86ExceptionHandling::new(X86OSABI::Windows);
assert!(eh.is_windows_personality());
}
#[test]
fn test_eh_setters_chain() {
let mut eh = X86ExceptionHandling::new(X86OSABI::SystemV);
eh.set_emit_unwind_tables(true);
eh.set_nounwind_info(true);
assert!(eh.emit_unwind_tables);
assert!(eh.nounwind_info);
}
#[test]
fn test_x86_memory_order_llvm_names() {
assert_eq!(X86MemoryOrder::Relaxed.llvm_name(), "monotonic");
assert_eq!(X86MemoryOrder::SeqCst.llvm_name(), "seq_cst");
}
#[test]
fn test_x86_round_mode_llvm_imm() {
assert_eq!(X86RoundMode::ToNearest.llvm_imm(), 0);
assert_eq!(X86RoundMode::ToZero.llvm_imm(), 3);
}
#[test]
fn test_x86_vcmp_pred_as_imm() {
assert_eq!(X86VCmpPred::EQ.as_imm(), 0);
assert_eq!(X86VCmpPred::TRUE_US.as_imm(), 31);
}
#[test]
fn test_x86_target_variant_is_32bit() {
assert!(X86TargetVariant::X86_32.is_32bit());
assert!(!X86TargetVariant::X86_64.is_32bit());
}
#[test]
fn test_x86_os_abi_windows_32_cc() {
assert_eq!(
X86OSABI::Windows.default_calling_convention(X86TargetVariant::X86_32),
X86CallingConvention::C
);
}
#[test]
fn test_x86_vector_abi_requires_alignment() {
let vabi = X86VectorABI::new(X86TargetVariant::X86_64);
assert!(!vabi.requires_alignment(X86VectorWidth::V64));
assert!(vabi.requires_alignment(X86VectorWidth::V128));
assert!(vabi.requires_alignment(X86VectorWidth::V512));
}
#[test]
fn test_x86_aggregate_strategy_stack() {
let mut al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let huge = Type::struct_literal_with(&[&Type::i64(); 10], false);
let info = al.lower_for_argument(&huge);
assert!(info.needs_stack_temp);
}
#[test]
fn test_x86_varargs_x32() {
let va = X86VarArgs::new(X86TargetVariant::X32, X86CallingConvention::X86_64_SysV, 2);
assert_eq!(va.num_gpr_save(), 6);
assert_eq!(va.num_xmm_save(), 8);
}
#[test]
fn test_x86_tls_model_llvm_string() {
assert_eq!(X86TLSModelKind::InitialExec.llvm_name(), "initial-exec");
assert_eq!(X86TLSModelKind::LocalDynamic.llvm_name(), "local-dynamic");
}
#[test]
fn test_x86_tls_access_seq_non_pic() {
let tls = X86TLSModel::sysv_amd64();
let seq = tls.access_sequence(false);
assert_eq!(seq, X86TLSAccessSeq::TLSDESC);
}
#[test]
fn test_x86_eh_personality_wasm() {
assert_eq!(
X86PersonalityKind::Wasm.symbol_name(),
"__gxx_wasm_personality_v0"
);
}
#[test]
fn test_x86_debug_format_dwarf() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(di.debug_format(), X86DebugFormat::DwarfV4);
}
#[test]
fn test_x86_address_space_qual_default_space() {
assert_eq!(AddressSpaceQual::Default.default_space(), 0);
}
#[test]
fn test_x86_aggregate_info_two_reg() {
let mut al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let two = Type::struct_literal_with(&[&Type::i64(), &Type::i64()], false);
let info = al.lower_for_return(&two);
assert!(info.is_multi_reg);
assert!(!info.uses_sret);
}
}
impl X86LoweringInfo {
pub fn hfa_element_type(&self, ty: &Type) -> Option<Type> {
if self.is_hfa(ty) {
Some(Type::float())
} else {
None
}
}
pub fn should_pass_in_integer_regs(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::Integer { .. } | TypeKind::Pointer { .. } => true,
TypeKind::Struct {
element_type_ids,
..
} => self.type_size_in_bits(ty) <= 128 && element_type_ids.len() <= 2,
_ => false,
}
}
pub fn should_pass_in_sse_regs(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::Float | TypeKind::Double => true,
TypeKind::Struct { .. } => self.is_hfa(ty),
TypeKind::FixedVector { len, .. } => *len as u64 * 8 <= 256,
_ => false,
}
}
pub fn num_eightbytes(&self, ty: &Type) -> usize {
let size = self.type_size_in_bits(ty);
((size + 63) / 64) as usize
}
pub fn is_valid_sse_type(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::FixedVector {
len,
element_type_id: _,
} => {
let size = *len as u64 * 8;
size == 64 || size == 128
}
_ => false,
}
}
pub fn is_valid_avx_type(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::FixedVector {
len,
element_type_id: _,
} => {
let size = *len as u64 * 8;
size == 256
}
_ => false,
}
}
pub fn is_valid_avx512_type(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::FixedVector {
len,
element_type_id: _,
} => {
let size = *len as u64 * 8;
size == 512
}
TypeKind::ScalableVector { .. } => true,
_ => false,
}
}
pub fn preferred_vector_width(&self, elem_type: &Type) -> u32 {
match elem_type.kind {
TypeKind::Integer { bits } => match bits {
8 => 128,
16 => 128,
32 => 256,
64 => 512,
_ => 128,
},
TypeKind::Float => 256,
TypeKind::Double => 256,
_ => 128,
}
}
pub fn needs_zero_extension(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::Integer { bits } => *bits < 32,
_ => false,
}
}
pub fn long_size_bits(&self) -> u32 {
match self.target_variant {
X86TargetVariant::X86_64 => 64,
X86TargetVariant::X86_32 => 32,
X86TargetVariant::X86_16 => 32,
X86TargetVariant::X32 => 32,
}
}
pub fn wchar_size_bits(&self) -> u32 {
match self.os_abi {
X86OSABI::Windows => 16,
_ => 32,
}
}
}
impl X86IntrinsicMapper {
#[allow(dead_code)]
fn populate_extended_intrinsics(&mut self) {
self.add_entry(
"__builtin_ia32_xsave",
"llvm.x86.xsave",
X86IntrinsicCategory::XSAVE,
0,
2,
);
self.add_entry(
"__builtin_ia32_xsave64",
"llvm.x86.xsave64",
X86IntrinsicCategory::XSAVE,
0,
2,
);
self.add_entry(
"__builtin_ia32_xrstor",
"llvm.x86.xrstor",
X86IntrinsicCategory::XSAVE,
0,
2,
);
self.add_entry(
"__builtin_ia32_xrstor64",
"llvm.x86.xrstor64",
X86IntrinsicCategory::XSAVE,
0,
2,
);
self.add_entry(
"__builtin_ia32_xsaveopt",
"llvm.x86.xsaveopt",
X86IntrinsicCategory::XSAVE,
0,
2,
);
self.add_entry(
"__builtin_ia32_xsaves",
"llvm.x86.xsaves",
X86IntrinsicCategory::XSAVE,
0,
2,
);
self.add_entry(
"__builtin_ia32_xrstors",
"llvm.x86.xrstors",
X86IntrinsicCategory::XSAVE,
0,
2,
);
self.add_entry(
"__builtin_ia32_addcarry_u32",
"llvm.x86.addcarry.32",
X86IntrinsicCategory::ADX,
0,
3,
);
self.add_entry(
"__builtin_ia32_addcarry_u64",
"llvm.x86.addcarry.64",
X86IntrinsicCategory::ADX,
0,
3,
);
self.add_entry(
"__builtin_ia32_addcarryx_u32",
"llvm.x86.addcarryx.32",
X86IntrinsicCategory::ADX,
0,
3,
);
self.add_entry(
"__builtin_ia32_subborrow_u32",
"llvm.x86.subborrow.32",
X86IntrinsicCategory::ADX,
0,
3,
);
self.add_entry(
"__builtin_ia32_subborrow_u64",
"llvm.x86.subborrow.64",
X86IntrinsicCategory::ADX,
0,
3,
);
self.add_entry(
"__builtin_ia32_vcvtps2ph",
"llvm.x86.vcvtps2ph.128",
X86IntrinsicCategory::FMA,
128,
2,
);
self.add_entry(
"__builtin_ia32_vcvtps2ph256",
"llvm.x86.vcvtps2ph.256",
X86IntrinsicCategory::FMA,
256,
2,
);
self.add_entry(
"__builtin_ia32_vcvtph2ps",
"llvm.x86.vcvtph2ps.128",
X86IntrinsicCategory::FMA,
128,
1,
);
self.add_entry(
"__builtin_ia32_vcvtph2ps256",
"llvm.x86.vcvtph2ps.256",
X86IntrinsicCategory::FMA,
256,
1,
);
self.add_entry(
"__builtin_ia32_clwb",
"llvm.x86.clwb",
X86IntrinsicCategory::General,
0,
1,
);
self.add_entry(
"__builtin_ia32_clzero",
"llvm.x86.clzero",
X86IntrinsicCategory::General,
0,
1,
);
self.add_entry(
"__builtin_ia32_encls",
"llvm.x86.encls",
X86IntrinsicCategory::General,
0,
2,
);
self.add_entry(
"__builtin_ia32_enclu",
"llvm.x86.enclu",
X86IntrinsicCategory::General,
0,
2,
);
self.add_entry(
"__builtin_ia32_enclv",
"llvm.x86.enclv",
X86IntrinsicCategory::General,
0,
2,
);
self.add_entry(
"__builtin_ia32_umonitor",
"llvm.x86.umonitor",
X86IntrinsicCategory::General,
0,
1,
);
self.add_entry(
"__builtin_ia32_umwait",
"llvm.x86.umwait",
X86IntrinsicCategory::General,
0,
2,
);
self.add_entry(
"__builtin_ia32_tpause",
"llvm.x86.tpause",
X86IntrinsicCategory::General,
0,
2,
);
self.add_entry(
"__builtin_ia32_serialize",
"llvm.x86.serialize",
X86IntrinsicCategory::General,
0,
0,
);
self.add_entry(
"__builtin_ia32_rdpid",
"llvm.x86.rdpid",
X86IntrinsicCategory::General,
0,
0,
);
self.add_entry(
"__builtin_ia32_ptwrite32",
"llvm.x86.ptwrite32",
X86IntrinsicCategory::General,
0,
1,
);
self.add_entry(
"__builtin_ia32_cldemote",
"llvm.x86.cldemote",
X86IntrinsicCategory::General,
0,
1,
);
self.add_entry(
"__builtin_ia32_wbnoinvd",
"llvm.x86.wbnoinvd",
X86IntrinsicCategory::General,
0,
0,
);
self.add_entry(
"__builtin_ia32_directstore_u32",
"llvm.x86.directstore32",
X86IntrinsicCategory::General,
0,
2,
);
self.add_entry(
"__builtin_ia32_movdir64b",
"llvm.x86.movdir64b",
X86IntrinsicCategory::General,
0,
2,
);
self.add_entry(
"__builtin_ia32_pcmpestrm128",
"llvm.x86.sse42.pcmpestrm128",
X86IntrinsicCategory::SSE42,
128,
5,
);
self.add_entry(
"__builtin_ia32_pcmpestri128",
"llvm.x86.sse42.pcmpestri128",
X86IntrinsicCategory::SSE42,
128,
5,
);
self.add_entry(
"__builtin_ia32_pcmpistrm128",
"llvm.x86.sse42.pcmpistrm128",
X86IntrinsicCategory::SSE42,
128,
3,
);
self.add_entry(
"__builtin_ia32_pcmpistri128",
"llvm.x86.sse42.pcmpistri128",
X86IntrinsicCategory::SSE42,
128,
3,
);
}
}
#[cfg(test)]
mod more_tests {
use super::*;
#[test]
fn test_lowering_hfa_element_type() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let hfa = Type::struct_literal_with(&[&Type::float(), &Type::float()], false);
assert!(li.hfa_element_type(&hfa).is_some());
let non_hfa = Type::struct_literal_with(&[&Type::float(), &Type::i32()], false);
assert!(li.hfa_element_type(&non_hfa).is_none());
}
#[test]
fn test_lowering_should_pass_in_integer_regs() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(li.should_pass_in_integer_regs(&Type::i32()));
assert!(li.should_pass_in_integer_regs(&Type::pointer(&Type::i8())));
assert!(!li.should_pass_in_integer_regs(&Type::float()));
}
#[test]
fn test_lowering_should_pass_in_sse_regs() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(li.should_pass_in_sse_regs(&Type::float()));
assert!(li.should_pass_in_sse_regs(&Type::double()));
assert!(!li.should_pass_in_sse_regs(&Type::i64()));
}
#[test]
fn test_lowering_num_eightbytes() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(li.num_eightbytes(&Type::i32()), 1);
assert_eq!(li.num_eightbytes(&Type::i64()), 1);
let large = Type::struct_literal_with(&[&Type::i64(), &Type::i64(), &Type::i64()], false);
assert_eq!(li.num_eightbytes(&large), 3);
}
#[test]
fn test_lowering_is_valid_sse_type() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(li.is_valid_sse_type(&Type::fixed_vector_with(&Type::float(), 4)));
assert!(!li.is_valid_sse_type(&Type::fixed_vector_with(&Type::float(), 8)));
assert!(!li.is_valid_sse_type(&Type::i32()));
}
#[test]
fn test_lowering_is_valid_avx_type() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(li.is_valid_avx_type(&Type::fixed_vector_with(&Type::float(), 8)));
assert!(!li.is_valid_avx_type(&Type::fixed_vector_with(&Type::float(), 4)));
}
#[test]
fn test_lowering_is_valid_avx512_type() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(li.is_valid_avx512_type(&Type::fixed_vector_with(&Type::float(), 16)));
assert!(!li.is_valid_avx512_type(&Type::fixed_vector_with(&Type::float(), 8)));
}
#[test]
fn test_lowering_preferred_vector_width() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(li.preferred_vector_width(&Type::i8()), 128);
assert_eq!(li.preferred_vector_width(&Type::i32()), 256);
assert_eq!(li.preferred_vector_width(&Type::float()), 256);
assert_eq!(li.preferred_vector_width(&Type::double()), 256);
}
#[test]
fn test_lowering_needs_zero_extension() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(li.needs_zero_extension(&Type::i8()));
assert!(li.needs_zero_extension(&Type::i16()));
assert!(!li.needs_zero_extension(&Type::i32()));
assert!(!li.needs_zero_extension(&Type::i64()));
assert!(!li.needs_zero_extension(&Type::float()));
}
#[test]
fn test_lowering_long_size() {
let li64 = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(li64.long_size_bits(), 64);
let li32 = X86LoweringInfo::new(X86TargetVariant::X86_32, X86OSABI::SystemV);
assert_eq!(li32.long_size_bits(), 32);
let lix32 = X86LoweringInfo::new(X86TargetVariant::X32, X86OSABI::SystemV);
assert_eq!(lix32.long_size_bits(), 32);
}
#[test]
fn test_lowering_wchar_size() {
let li_linux = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(li_linux.wchar_size_bits(), 32);
let li_win = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::Windows);
assert_eq!(li_win.wchar_size_bits(), 16);
}
#[test]
fn test_lowering_lower_return_type_sret() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let large = Type::struct_literal_with(&[&Type::i64(); 4], false);
let lowered = li.lower_return_type(&large);
assert!(lowered.is_void());
}
#[test]
fn test_lowering_lower_return_type_normal() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let lowered = li.lower_return_type(&Type::i32());
assert!(lowered.is_integer());
}
#[test]
fn test_lowering_parameter_type_function_decay() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let fn_type = Type::function_type_with(&Type::void(), &[], false);
let lowered = li.lower_parameter_type(&fn_type);
assert!(lowered.is_pointer());
}
#[test]
fn test_intrinsic_mapper_extended_coverage() {
let mut mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
mapper.populate_extended_intrinsics();
assert_eq!(
mapper.map_builtin("__builtin_ia32_xsave"),
Some("llvm.x86.xsave".to_string())
);
assert_eq!(
mapper.map_builtin("__builtin_ia32_addcarry_u32"),
Some("llvm.x86.addcarry.32".to_string())
);
assert_eq!(
mapper.map_builtin("__builtin_ia32_clwb"),
Some("llvm.x86.clwb".to_string())
);
assert_eq!(
mapper.map_builtin("__builtin_ia32_serialize"),
Some("llvm.x86.serialize".to_string())
);
assert_eq!(
mapper.map_builtin("__builtin_ia32_movdir64b"),
Some("llvm.x86.movdir64b".to_string())
);
}
#[test]
fn test_intrinsic_mapper_f16c() {
let mut mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
mapper.populate_extended_intrinsics();
assert_eq!(
mapper.map_builtin("__builtin_ia32_vcvtps2ph"),
Some("llvm.x86.vcvtps2ph.128".to_string())
);
assert_eq!(
mapper.map_builtin("__builtin_ia32_vcvtph2ps256"),
Some("llvm.x86.vcvtph2ps.256".to_string())
);
}
#[test]
fn test_intrinsic_mapper_sgx() {
let mut mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
mapper.populate_extended_intrinsics();
assert_eq!(
mapper.map_builtin("__builtin_ia32_encls"),
Some("llvm.x86.encls".to_string())
);
assert_eq!(
mapper.map_builtin("__builtin_ia32_enclu"),
Some("llvm.x86.enclu".to_string())
);
}
#[test]
fn test_intrinsic_mapper_waitpkg() {
let mut mapper = X86IntrinsicMapper::new(X86TargetVariant::X86_64);
mapper.populate_extended_intrinsics();
assert_eq!(
mapper.map_builtin("__builtin_ia32_umonitor"),
Some("llvm.x86.umonitor".to_string())
);
assert_eq!(
mapper.map_builtin("__builtin_ia32_tpause"),
Some("llvm.x86.tpause".to_string())
);
}
#[test]
fn test_aggregate_lowering_win64_return_medium() {
let mut al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::Windows);
let medium = Type::struct_literal_with(&[&Type::i64(), &Type::i64()], false);
let info = al.lower_for_return(&medium);
assert!(info.uses_sret);
}
#[test]
fn test_aggregate_lowering_sysv_mixed_class() {
let mut al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let mixed = Type::struct_literal_with(&[&Type::float(), &Type::i64()], false);
let info = al.lower_for_argument(&mixed);
assert!(!info.needs_stack_temp);
}
#[test]
fn test_vector_abi_scalable() {
let mut vabi = X86VectorABI::new(X86TargetVariant::X86_64);
let scalable = Type::scalable_vector_with(&Type::float(), 4);
let arg = vabi.classify_vector_arg(&scalable);
assert_eq!(arg.width, X86VectorWidth::V512);
}
#[test]
fn test_codegen_init_varargs_32() {
let mut cg = make_codegen_32("va32_test");
cg.init_varargs(2);
let va = cg.varargs_info().unwrap();
assert!(va.is_32bit_stack());
}
#[test]
fn test_codegen_classify_argument_type() {
let cg = make_codegen("classify_test");
let classes = cg.classify_argument_type(&Type::i64());
assert!(!classes.is_empty());
}
#[test]
fn test_codegen_uses_sret_check() {
let cg = make_codegen("sret_test");
let large = Type::struct_literal_with(&[&Type::i64(); 4], false);
assert!(cg.uses_sret(&large));
let small = Type::struct_literal_with(&[&Type::i32()], false);
assert!(!cg.uses_sret(&small));
}
#[test]
fn test_codegen_map_builtin() {
let cg = make_codegen("builtin_test");
assert_eq!(
cg.map_builtin_to_intrinsic("__builtin_ia32_addps"),
Some("llvm.x86.sse.add.ps".to_string())
);
assert_eq!(cg.map_builtin_to_intrinsic("nonexistent"), None);
}
#[test]
fn test_codegen_map_vector_builtin() {
let cg = make_codegen("vec_test");
assert_eq!(
cg.map_vector_builtin("__builtin_ia32_addps256", 256),
Some("llvm.x86.avx.add.ps.256".to_string())
);
assert_eq!(cg.map_vector_builtin("__builtin_ia32_addps256", 128), None);
}
#[test]
fn test_codegen_lower_aggregate_for_arg() {
let cg = make_codegen("agg_test");
let ty = Type::struct_literal_with(&[&Type::i32()], false);
let info = cg.lower_aggregate_for_arg(&ty);
assert!(!info.needs_stack_temp);
}
#[test]
fn test_codegen_lower_aggregate_for_return() {
let cg = make_codegen("agg_ret_test");
let large = Type::struct_literal_with(&[&Type::i64(); 4], false);
let info = cg.lower_aggregate_for_return(&large);
assert!(info.uses_sret);
}
#[test]
fn test_codegen_emit_stack_protector() {
let mut cg = make_codegen("sp_test");
cg.emit_stack_protector_setup();
cg.emit_stack_protector_check();
}
#[test]
fn test_codegen_emit_function_debug_info() {
let mut cg = make_codegen("di_test");
cg.emit_function_debug_info("test_func", "test.c", 42, None);
}
#[test]
fn test_codegen_get_address_space() {
let cg = make_codegen("as_test");
assert_eq!(cg.get_address_space(AddressSpaceQual::Default), 0);
}
#[test]
fn test_codegen_emit_landing_pad() {
let mut cg = make_codegen("eh_test");
let result = cg.emit_landing_pad("__gxx_personality_v0", &[]);
assert!(result.is_ok());
}
#[test]
fn test_codegen_lower_type() {
let cg = make_codegen("type_test");
let lowered = cg.lower_type(&Type::i32());
assert!(lowered.is_integer());
}
#[test]
fn test_codegen_lower_parameter_type() {
let cg = make_codegen("param_test");
let arr = Type::array_with(&Type::i32(), 10);
let lowered = cg.lower_parameter_type(&arr);
assert!(lowered.is_pointer());
}
#[test]
fn test_codegen_lower_return_type() {
let cg = make_codegen("ret_test");
let large = Type::struct_literal_with(&[&Type::i64(); 4], false);
let lowered = cg.lower_return_type(&large);
assert!(lowered.is_void());
}
}
impl<'a> ClangX86CodeGen<'a> {
pub fn has_cpu_feature(&self, feature: &str) -> bool {
match feature {
"sse" => true, "sse2" => true, "sse3" => self.target_variant.is_64bit(),
"ssse3" => self.target_variant.is_64bit(),
"sse4.1" => self.flags.use_sse41 || self.flags.use_avx,
"sse4.2" => self.flags.use_sse42 || self.flags.use_avx,
"avx" => self.flags.use_avx,
"avx2" => self.flags.use_avx2,
"avx512f" => self.flags.use_avx512,
"fma" => self.flags.use_avx,
"bmi" => self.target_variant.is_64bit(),
"bmi2" => self.target_variant.is_64bit(),
"adx" => self.target_variant.is_64bit(),
"aes" => self.target_variant.is_64bit(),
"cx16" => self.flags.cx16,
"xsave" => self.target_variant.is_64bit(),
_ => false,
}
}
pub fn optimal_data_layout(&self) -> DataLayout {
match self.target_variant {
X86TargetVariant::X86_64 => DataLayout::x86_64_linux(),
X86TargetVariant::X86_32 => {
DataLayout::parse("e-m:e-p:32:32-i64:64-f80:128-n8:16:32:64-S128").unwrap()
}
X86TargetVariant::X86_16 => {
DataLayout::parse("e-m:e-p:16:16-i64:64-f80:128-n8:16:32:64-S128").unwrap()
}
X86TargetVariant::X32 => {
DataLayout::parse("e-m:e-p:32:32-i64:64-f80:128-n8:16:32:64-S128").unwrap()
}
}
}
pub fn estimate_frame_size(&self, num_locals: usize, has_varargs: bool) -> u64 {
let mut size = 8u64; if !self.flags.omit_frame_pointer {
size += 8; }
size += 16 * 3;
size += (num_locals as u64) * 8;
if has_varargs {
if self.target_variant.is_64bit()
&& !matches!(self.calling_convention, X86CallingConvention::Win64)
{
size += 176; }
}
let align = self.target_variant.stack_alignment() as u64;
((size + align - 1) / align) * align
}
pub fn is_attribute_applicable(&self, attr: &str) -> bool {
match attr {
"no-red-zone" => self.target_variant.is_64bit(),
"interrupt" => true,
"signal" => true,
"nodplicate" => true,
"target" => true,
"naked" => true,
_ => true,
}
}
pub fn default_code_model(&self) -> &'static str {
match self.target_variant {
X86TargetVariant::X86_64 => "small",
X86TargetVariant::X86_32 => "small",
X86TargetVariant::X86_16 => "tiny",
X86TargetVariant::X32 => "medium",
}
}
pub fn default_reloc_model(&self) -> &'static str {
if self.flags.position_independent {
"pic"
} else {
"static"
}
}
pub fn supports_zero_cost_eh(&self) -> bool {
self.target_variant.is_64bit()
}
pub fn uses_sjlj_eh(&self) -> bool {
matches!(self.target_variant, X86TargetVariant::X86_32)
&& matches!(self.os_abi, X86OSABI::Windows)
}
pub fn stack_probe_function(&self) -> Option<&'static str> {
match self.os_abi {
X86OSABI::Windows => Some("__chkstk"),
_ => None,
}
}
pub fn supports_regparm(&self) -> bool {
matches!(self.target_variant, X86TargetVariant::X86_32)
}
pub fn natural_stack_alignment(&self) -> u32 {
if self.flags.stack_alignment_override > 0 {
self.flags.stack_alignment_override
} else {
self.target_variant.stack_alignment()
}
}
}
impl X86ExceptionHandling {
pub fn uses_seh(&self) -> bool {
matches!(self.os_abi, X86OSABI::Windows)
&& matches!(self.table_format, X86EHTablesFormat::Win64)
}
pub fn register_function_name(&self) -> &'static str {
if self.uses_seh() {
"__C_specific_handler"
} else {
"__gxx_personality_v0"
}
}
pub fn needs_cleanup_landing_pad(&self) -> bool {
self.is_gnu_personality()
}
pub fn lsda_encoding_byte(&self) -> u8 {
match self.table_format {
X86EHTablesFormat::Dwarf => 0x1b, X86EHTablesFormat::Win64 => 0x03, X86EHTablesFormat::CompactUnwind => 0x9b, X86EHTablesFormat::ARM => 0x00,
}
}
pub fn type_info_encoding_byte(&self) -> u8 {
match self.os_abi {
X86OSABI::Windows => 0x02, _ => 0x1b, }
}
pub fn needs_exception_pointer(&self) -> bool {
match self.personality {
X86PersonalityKind::GXX | X86PersonalityKind::GCC => true,
X86PersonalityKind::MSVC_X64 => false,
_ => true,
}
}
pub fn lsda_alignment(&self) -> u32 {
match self.table_format {
X86EHTablesFormat::Dwarf => 4,
X86EHTablesFormat::Win64 => 4,
X86EHTablesFormat::CompactUnwind => 8,
_ => 4,
}
}
pub fn catch_all_filter(&self) -> i32 {
match self.personality {
X86PersonalityKind::MSVC_X64 | X86PersonalityKind::MSVC_C => 1,
_ => 0, }
}
pub fn selector_type(&self) -> Type {
Type::i32()
}
}
impl X86DebugInfo {
pub fn default_dwarf_version(&self) -> u16 {
match self.os_abi {
X86OSABI::Darwin => 4, _ => 5, }
}
pub fn use_split_dwarf(&self) -> bool {
matches!(
self.debug_kind,
DebugInfoKind::FullDebug | DebugInfoKind::FullWithMacros
) && matches!(self.os_abi, X86OSABI::SystemV)
}
pub fn debug_section_prefix(&self) -> &'static str {
match self.debug_format {
X86DebugFormat::DwarfV4 | X86DebugFormat::DwarfV5 => ".debug",
X86DebugFormat::CodeView => ".debug$",
_ => ".debug",
}
}
pub fn debug_abbrev_section(&self) -> String {
format!("{}_abbrev", self.debug_section_prefix())
}
pub fn debug_info_section(&self) -> String {
format!("{}_info", self.debug_section_prefix())
}
pub fn debug_line_section(&self) -> String {
format!("{}_line", self.debug_section_prefix())
}
pub fn debug_str_section(&self) -> String {
format!("{}_str", self.debug_section_prefix())
}
pub fn debug_ranges_section(&self) -> String {
format!("{}_ranges", self.debug_section_prefix())
}
pub fn uses_linker_debug_info(&self) -> bool {
matches!(self.debug_format, X86DebugFormat::CodeView)
}
}
#[cfg(test)]
mod final_tests {
use super::*;
#[test]
fn test_codegen_has_cpu_feature() {
let mut cg = make_codegen("feature_test");
assert!(cg.has_cpu_feature("sse"));
assert!(cg.has_cpu_feature("sse2"));
assert!(!cg.has_cpu_feature("avx"));
cg.enable_features(&["avx".to_string()]);
assert!(cg.has_cpu_feature("avx"));
}
#[test]
fn test_codegen_estimate_frame_size() {
let cg = make_codegen("frame_test");
let size = cg.estimate_frame_size(10, false);
assert!(size >= 80); assert_eq!(size % 16, 0); }
#[test]
fn test_codegen_estimate_frame_size_varargs() {
let cg = make_codegen("frame_va_test");
let size_no_va = cg.estimate_frame_size(5, false);
let size_va = cg.estimate_frame_size(5, true);
assert!(size_va > size_no_va);
}
#[test]
fn test_codegen_is_attribute_applicable() {
let cg = make_codegen("attr_test");
assert!(cg.is_attribute_applicable("no-red-zone"));
assert!(cg.is_attribute_applicable("interrupt"));
}
#[test]
fn test_codegen_default_code_model() {
let cg64 = make_codegen("cm64_test");
assert_eq!(cg64.default_code_model(), "small");
let cg32 = make_codegen_32("cm32_test");
assert_eq!(cg32.default_code_model(), "small");
}
#[test]
fn test_codegen_default_reloc_model() {
let cg = make_codegen("reloc_test");
assert_eq!(cg.default_reloc_model(), "static");
}
#[test]
fn test_codegen_supports_zero_cost_eh() {
let cg64 = make_codegen("eh64_test");
assert!(cg64.supports_zero_cost_eh());
let cg32 = make_codegen_32("eh32_test");
assert!(!cg32.supports_zero_cost_eh());
}
#[test]
fn test_codegen_uses_sjlj_eh() {
let cg_linux32 = make_codegen_32("sjlj_linux");
assert!(!cg_linux32.uses_sjlj_eh());
}
#[test]
fn test_codegen_stack_probe_function() {
let cg_win = make_codegen_win64("probe_win");
assert_eq!(cg_win.stack_probe_function(), Some("__chkstk"));
let cg_linux = make_codegen("probe_linux");
assert_eq!(cg_linux.stack_probe_function(), None);
}
#[test]
fn test_codegen_supports_regparm() {
let cg32 = make_codegen_32("regparm_test");
assert!(cg32.supports_regparm());
let cg64 = make_codegen("regparm64_test");
assert!(!cg64.supports_regparm());
}
#[test]
fn test_codegen_natural_stack_alignment() {
let cg = make_codegen("align_test");
assert_eq!(cg.natural_stack_alignment(), 16);
}
#[test]
fn test_eh_uses_seh() {
let eh_sysv = X86ExceptionHandling::new(X86OSABI::SystemV);
assert!(!eh_sysv.uses_seh());
let eh_win = X86ExceptionHandling::new(X86OSABI::Windows);
assert!(eh_win.uses_seh());
}
#[test]
fn test_eh_register_function_name() {
let eh_sysv = X86ExceptionHandling::new(X86OSABI::SystemV);
assert_eq!(eh_sysv.register_function_name(), "__gxx_personality_v0");
let eh_win = X86ExceptionHandling::new(X86OSABI::Windows);
assert_eq!(eh_win.register_function_name(), "__C_specific_handler");
}
#[test]
fn test_eh_needs_cleanup_landing_pad() {
let eh = X86ExceptionHandling::new(X86OSABI::SystemV);
assert!(eh.needs_cleanup_landing_pad());
}
#[test]
fn test_eh_lsda_encoding_byte() {
let eh = X86ExceptionHandling::new(X86OSABI::SystemV);
assert_eq!(eh.lsda_encoding_byte(), 0x1b);
}
#[test]
fn test_eh_type_info_encoding_byte() {
let eh_sysv = X86ExceptionHandling::new(X86OSABI::SystemV);
assert_eq!(eh_sysv.type_info_encoding_byte(), 0x1b);
let eh_win = X86ExceptionHandling::new(X86OSABI::Windows);
assert_eq!(eh_win.type_info_encoding_byte(), 0x02);
}
#[test]
fn test_eh_needs_exception_pointer() {
let eh = X86ExceptionHandling::new(X86OSABI::SystemV);
assert!(eh.needs_exception_pointer());
}
#[test]
fn test_eh_lsda_alignment() {
let eh = X86ExceptionHandling::new(X86OSABI::SystemV);
assert_eq!(eh.lsda_alignment(), 4);
}
#[test]
fn test_eh_catch_all_filter() {
let eh_gnu = X86ExceptionHandling::new(X86OSABI::SystemV);
assert_eq!(eh_gnu.catch_all_filter(), 0);
let eh_win = X86ExceptionHandling::new(X86OSABI::Windows);
assert_eq!(eh_win.catch_all_filter(), 1);
}
#[test]
fn test_eh_selector_type() {
let eh = X86ExceptionHandling::new(X86OSABI::SystemV);
let ty = eh.selector_type();
assert!(ty.is_integer());
}
#[test]
fn test_debug_info_default_dwarf_version() {
let di_linux = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(di_linux.default_dwarf_version(), 5);
let di_mac = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::Darwin);
assert_eq!(di_mac.default_dwarf_version(), 4);
}
#[test]
fn test_debug_info_use_split_dwarf() {
let mut di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(!di.use_split_dwarf());
di.set_kind(DebugInfoKind::FullDebug);
assert!(di.use_split_dwarf());
}
#[test]
fn test_debug_info_section_names() {
let di = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(di.debug_abbrev_section(), ".debug_abbrev");
assert_eq!(di.debug_info_section(), ".debug_info");
assert_eq!(di.debug_line_section(), ".debug_line");
assert_eq!(di.debug_str_section(), ".debug_str");
}
#[test]
fn test_debug_info_uses_linker_debug_info() {
let di_codeview = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::Windows);
assert!(di_codeview.uses_linker_debug_info());
let di_dwarf = X86DebugInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(!di_dwarf.uses_linker_debug_info());
}
#[test]
fn test_shuffle_mask_is_unpckl_true() {
let mask = X86ShuffleMask::new(vec![0, 4, 1, 5]);
assert!(mask.is_unpckl(8));
}
#[test]
fn test_shuffle_mask_is_unpckh_true() {
let mask = X86ShuffleMask::new(vec![2, 6, 3, 7]);
assert!(mask.is_unpckh(8));
}
#[test]
fn test_vector_lowering_result_constant() {
let result = X86VectorLoweringResult::constant(valref(99));
assert!(result.is_constant);
assert!(!result.is_libcall);
}
#[test]
fn test_aggregate_register_assignment_mixed() {
let mut assignment = AggregateRegisterAssignment::new();
assignment.add_gpr_field(5, 0, 8);
assignment.add_sse_field(0, 8, 8);
assert_eq!(assignment.total_gpr_count(), 1);
assert_eq!(assignment.total_sse_count(), 1);
}
#[test]
fn test_target_variant_x32_distinct() {
assert!(X86TargetVariant::X32.is_64bit());
assert_eq!(X86TargetVariant::X32.pointer_size_bytes(), 4);
assert_ne!(X86TargetVariant::X86_64, X86TargetVariant::X32);
}
#[test]
fn test_os_abi_darwin_is_not_windows() {
let cc = X86OSABI::Darwin.default_calling_convention(X86TargetVariant::X86_64);
assert_ne!(cc, X86CallingConvention::Win64);
}
#[test]
fn test_vector_abi_get_stack_offset() {
let mut vabi = X86VectorABI::new(X86TargetVariant::X86_64);
for _ in 0..8 {
let ty = Type::fixed_vector_with(&Type::float(), 4);
vabi.classify_vector_arg(&ty);
}
assert!(vabi.current_stack_offset() > 0);
}
#[test]
fn test_address_space_map_opencl_supported() {
let asm = X86AddressSpaceMap::for_opencl();
assert!(asm.is_supported(1));
assert!(asm.is_supported(2));
assert!(asm.is_supported(3));
assert!(asm.is_supported(4));
}
#[test]
fn test_codegen_flags_stack_alignment_override() {
let mut flags = X86CodeGenFlags::default();
assert_eq!(flags.stack_alignment_override, 0);
flags.stack_alignment_override = 32;
assert_eq!(flags.stack_alignment_override, 32);
}
#[test]
fn test_tls_kind_parse_all() {
assert_eq!(
X86TLSModelKind::from_str("global-dynamic"),
Some(X86TLSModelKind::GeneralDynamic)
);
assert_eq!(
X86TLSModelKind::from_str("local-dynamic"),
Some(X86TLSModelKind::LocalDynamic)
);
assert_eq!(
X86TLSModelKind::from_str("initial-exec"),
Some(X86TLSModelKind::InitialExec)
);
assert_eq!(
X86TLSModelKind::from_str("local-exec"),
Some(X86TLSModelKind::LocalExec)
);
}
}
impl X86VectorABI {
pub fn fits_in_one_register(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::FixedVector {
len,
element_type_id: _,
} => {
let size = *len as u32 * 8;
size <= 128 || (self.flags_allow_avx() && size <= 256)
}
_ => false,
}
}
#[allow(dead_code)]
fn flags_allow_avx(&self) -> bool {
self.sse_reg_count >= 8
}
pub fn register_class_for_width(&self, width: X86VectorWidth) -> &'static str {
match width {
X86VectorWidth::V64 => "VR64",
X86VectorWidth::V128 => "VR128",
X86VectorWidth::V256 => "VR256",
X86VectorWidth::V512 => "VR512",
}
}
pub fn needs_stack_realignment(&self, args: &[X86VectorArg]) -> bool {
args.iter().any(|a| a.in_memory && a.width.alignment() > 16)
}
pub fn spilled_vector_stack_space(&self, args: &[X86VectorArg]) -> u32 {
args.iter()
.filter(|a| a.in_memory)
.map(|a| a.width.bytes())
.sum()
}
}
impl X86AggregateLowering {
pub fn requires_copy(&self, ty: &Type) -> bool {
let info = self.compute_argument_lowering(ty);
info.needs_stack_temp || info.strategy == AggregatePassingStrategy::Indirect
}
pub fn effective_passing_size(&self, ty: &Type) -> u64 {
let size = self.type_size(ty);
if size <= 8 {
8
} else if size <= 16 {
16
} else {
size
}
}
pub fn eightbyte_classes(&self, ty: &Type) -> Vec<EightByteClass> {
let classes = self.classify_sysv_aggregate(ty);
classes
.iter()
.map(|c| match c {
X86ArgClass::Integer => EightByteClass::INTEGER,
X86ArgClass::SSE => EightByteClass::SSE,
X86ArgClass::SSEUp => EightByteClass::SSEUP,
X86ArgClass::X87 => EightByteClass::X87,
X86ArgClass::X87Up => EightByteClass::X87UP,
X86ArgClass::ComplexX87 => EightByteClass::COMPLEX_X87,
X86ArgClass::Memory => EightByteClass::MEMORY,
X86ArgClass::NoClass => EightByteClass::NO_CLASS,
})
.collect()
}
pub fn is_all_sse(&self, ty: &Type) -> bool {
let classes = self.eightbyte_classes(ty);
!classes.is_empty()
&& classes.iter().all(|c| {
matches!(
c,
EightByteClass::SSE | EightByteClass::SSEUP | EightByteClass::NO_CLASS
)
})
}
pub fn gpr_count_for_type(&self, ty: &Type) -> usize {
let classes = self.eightbyte_classes(ty);
classes
.iter()
.filter(|c| matches!(c, EightByteClass::INTEGER))
.count()
}
pub fn sse_count_for_type(&self, ty: &Type) -> usize {
let classes = self.eightbyte_classes(ty);
classes
.iter()
.filter(|c| matches!(c, EightByteClass::SSE | EightByteClass::SSEUP))
.count()
}
}
impl X86VarArgs {
pub fn va_arg_size(&self, ty: &Type) -> u32 {
let raw_size = match &ty.kind {
TypeKind::Integer { bits } => (*bits as u32 + 7) / 8,
TypeKind::Float => 4,
TypeKind::Double => 8,
TypeKind::Pointer { .. } => self.target_variant.pointer_size_bytes(),
_ => 8,
};
self.align_arg_size(raw_size)
}
pub fn register_save_area_end(&self) -> u32 {
self.reg_save_area_offset + self.reg_save_area_size
}
pub fn needs_integer_promotion(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::Integer { bits } => *bits < 32,
_ => false,
}
}
pub fn promoted_type(&self, ty: &Type) -> Type {
match &ty.kind {
TypeKind::Integer { bits } if *bits < 32 => Type::i32(),
TypeKind::Float => Type::double(),
_ => ty.clone(),
}
}
}
impl X86TLSModel {
pub fn elf_relocation(&self, is_local: bool) -> &'static str {
match (self.model, is_local) {
(X86TLSModelKind::GeneralDynamic, _) => "R_X86_64_TLSGD",
(X86TLSModelKind::LocalDynamic, _) => "R_X86_64_TLSLD",
(X86TLSModelKind::InitialExec, _) => "R_X86_64_GOTTPOFF",
(X86TLSModelKind::LocalExec, _) => "R_X86_64_TPOFF32",
}
}
pub fn requires_got_entry(&self) -> bool {
matches!(
self.model,
X86TLSModelKind::GeneralDynamic
| X86TLSModelKind::LocalDynamic
| X86TLSModelKind::InitialExec
)
}
pub fn supports_lazy_binding(&self) -> bool {
matches!(self.model, X86TLSModelKind::GeneralDynamic)
}
pub fn access_sequence_length(&self) -> usize {
match self.model {
X86TLSModelKind::GeneralDynamic => 5,
X86TLSModelKind::LocalDynamic => 5,
X86TLSModelKind::InitialExec => 3,
X86TLSModelKind::LocalExec => 2,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum X86ArchLevel {
V1,
V2,
V3,
V4,
}
impl X86ArchLevel {
pub fn from_string(s: &str) -> Option<Self> {
match s {
"x86-64" | "x86-64-v1" => Some(X86ArchLevel::V1),
"x86-64-v2" => Some(X86ArchLevel::V2),
"x86-64-v3" => Some(X86ArchLevel::V3),
"x86-64-v4" => Some(X86ArchLevel::V4),
_ => None,
}
}
pub fn required_features(&self) -> Vec<&'static str> {
match self {
X86ArchLevel::V1 => vec![
"cmov", "cx8", "fpu", "fxsr", "mmx", "syscall", "sse", "sse2",
],
X86ArchLevel::V2 => vec![
"cmov", "cx8", "cx16", "fpu", "fxsr", "mmx", "syscall", "sse", "sse2", "sse3",
"ssse3", "sse4.1", "sse4.2", "popcnt",
],
X86ArchLevel::V3 => vec![
"avx", "avx2", "bmi1", "bmi2", "f16c", "fma", "lzcnt", "movbe", "xsave",
],
X86ArchLevel::V4 => vec!["avx512f", "avx512bw", "avx512cd", "avx512dq", "avx512vl"],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SchedModel {
Generic,
SandyBridge,
Haswell,
SkylakeClient,
SkylakeServer,
IceLake,
AlderLakeP,
Zen2,
Zen3,
Zen4,
Zen5,
}
impl X86SchedModel {
pub fn from_cpu_name(cpu: &str) -> Self {
match cpu {
"sandybridge" | "corei7-avx" => X86SchedModel::SandyBridge,
"haswell" | "core-avx2" => X86SchedModel::Haswell,
"skylake" | "skylake-avx512" => X86SchedModel::SkylakeClient,
"skylake-server" | "skx" => X86SchedModel::SkylakeServer,
"icelake-client" | "icelake-server" => X86SchedModel::IceLake,
"alderlake" | "raptorlake" => X86SchedModel::AlderLakeP,
"znver2" => X86SchedModel::Zen2,
"znver3" => X86SchedModel::Zen3,
"znver4" => X86SchedModel::Zen4,
"znver5" => X86SchedModel::Zen5,
_ => X86SchedModel::Generic,
}
}
pub fn name(&self) -> &'static str {
match self {
X86SchedModel::Generic => "generic",
X86SchedModel::SandyBridge => "sandybridge",
X86SchedModel::Haswell => "haswell",
X86SchedModel::SkylakeClient => "skylake",
X86SchedModel::SkylakeServer => "skylake-server",
X86SchedModel::IceLake => "icelake",
X86SchedModel::AlderLakeP => "alderlake",
X86SchedModel::Zen2 => "znver2",
X86SchedModel::Zen3 => "znver3",
X86SchedModel::Zen4 => "znver4",
X86SchedModel::Zen5 => "znver5",
}
}
}
#[cfg(test)]
mod last_tests {
use super::*;
#[test]
fn test_vector_abi_fits_in_one_register() {
let vabi = X86VectorABI::new(X86TargetVariant::X86_64);
let v4sf = Type::fixed_vector_with(&Type::float(), 4);
assert!(vabi.fits_in_one_register(&v4sf));
let v2df = Type::fixed_vector_with(&Type::double(), 2);
assert!(vabi.fits_in_one_register(&v2df));
}
#[test]
fn test_vector_abi_register_class_for_width() {
let vabi = X86VectorABI::new(X86TargetVariant::X86_64);
assert_eq!(vabi.register_class_for_width(X86VectorWidth::V128), "VR128");
assert_eq!(vabi.register_class_for_width(X86VectorWidth::V256), "VR256");
assert_eq!(vabi.register_class_for_width(X86VectorWidth::V512), "VR512");
}
#[test]
fn test_vector_abi_needs_stack_realignment() {
let vabi = X86VectorABI::new(X86TargetVariant::X86_64);
let args = vec![X86VectorArg {
width: X86VectorWidth::V256,
num_elements: 8,
element_type: Type::float(),
reg_assignment: None,
in_memory: true,
stack_offset: Some(0),
}];
assert!(vabi.needs_stack_realignment(&args));
}
#[test]
fn test_vector_abi_spilled_vector_stack_space() {
let vabi = X86VectorABI::new(X86TargetVariant::X86_64);
let args = vec![
X86VectorArg {
width: X86VectorWidth::V128,
num_elements: 4,
element_type: Type::float(),
reg_assignment: None,
in_memory: true,
stack_offset: Some(0),
},
X86VectorArg {
width: X86VectorWidth::V256,
num_elements: 8,
element_type: Type::float(),
reg_assignment: None,
in_memory: true,
stack_offset: Some(16),
},
];
assert_eq!(vabi.spilled_vector_stack_space(&args), 48);
}
#[test]
fn test_aggregate_lowering_requires_copy() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let large = Type::struct_literal_with(&[&Type::i64(); 4], false);
assert!(al.requires_copy(&large));
let small = Type::struct_literal_with(&[&Type::i32()], false);
assert!(!al.requires_copy(&small));
}
#[test]
fn test_aggregate_lowering_effective_passing_size() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert_eq!(al.effective_passing_size(&Type::i32()), 8);
assert_eq!(al.effective_passing_size(&Type::i64()), 8);
}
#[test]
fn test_aggregate_lowering_eightbyte_classes() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let ty = Type::struct_literal_with(&[&Type::i64(), &Type::i64()], false);
let classes = al.eightbyte_classes(&ty);
assert_eq!(classes.len(), 2);
}
#[test]
fn test_aggregate_lowering_is_all_sse() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let sse_struct = Type::struct_literal_with(&[&Type::float(), &Type::float()], false);
assert!(al.is_all_sse(&sse_struct));
let int_struct = Type::struct_literal_with(&[&Type::i32(), &Type::i32()], false);
assert!(!al.is_all_sse(&int_struct));
}
#[test]
fn test_aggregate_lowering_gpr_count_for_type() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let int_struct = Type::struct_literal_with(&[&Type::i64(), &Type::i64()], false);
let count = al.gpr_count_for_type(&int_struct);
assert!(count > 0);
}
#[test]
fn test_aggregate_lowering_sse_count_for_type() {
let al = X86AggregateLowering::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let sse_struct = Type::struct_literal_with(&[&Type::float(), &Type::float()], false);
let count = al.sse_count_for_type(&sse_struct);
assert!(count > 0);
}
#[test]
fn test_varargs_va_arg_size() {
let va = X86VarArgs::new(
X86TargetVariant::X86_64,
X86CallingConvention::X86_64_SysV,
0,
);
assert_eq!(va.va_arg_size(&Type::i32()), 8);
assert_eq!(va.va_arg_size(&Type::i64()), 8);
assert_eq!(va.va_arg_size(&Type::float()), 8);
assert_eq!(va.va_arg_size(&Type::double()), 8);
}
#[test]
fn test_varargs_register_save_area_end() {
let va = X86VarArgs::new(
X86TargetVariant::X86_64,
X86CallingConvention::X86_64_SysV,
0,
);
assert!(va.register_save_area_end() > 0);
}
#[test]
fn test_varargs_needs_integer_promotion() {
let va = X86VarArgs::new(
X86TargetVariant::X86_64,
X86CallingConvention::X86_64_SysV,
0,
);
assert!(va.needs_integer_promotion(&Type::i8()));
assert!(va.needs_integer_promotion(&Type::i16()));
assert!(!va.needs_integer_promotion(&Type::i32()));
assert!(!va.needs_integer_promotion(&Type::i64()));
}
#[test]
fn test_varargs_promoted_type() {
let va = X86VarArgs::new(
X86TargetVariant::X86_64,
X86CallingConvention::X86_64_SysV,
0,
);
assert_eq!(
va.promoted_type(&Type::i8()).kind,
TypeKind::Integer { bits: 32 }
);
assert_eq!(va.promoted_type(&Type::float()).kind, TypeKind::Double);
}
#[test]
fn test_tls_model_elf_relocation() {
let tls = X86TLSModel::sysv_amd64();
assert_eq!(tls.elf_relocation(false), "R_X86_64_TLSGD");
}
#[test]
fn test_tls_model_requires_got_entry() {
assert!(X86TLSModel::sysv_amd64().requires_got_entry());
let mut le = X86TLSModel::sysv_amd64();
le.set_local_exec(true);
assert!(!le.requires_got_entry());
}
#[test]
fn test_tls_model_supports_lazy_binding() {
assert!(X86TLSModel::sysv_amd64().supports_lazy_binding());
}
#[test]
fn test_tls_model_access_sequence_length() {
assert_eq!(X86TLSModel::sysv_amd64().access_sequence_length(), 5);
let mut le = X86TLSModel::sysv_amd64();
le.set_local_exec(true);
assert_eq!(le.access_sequence_length(), 2);
}
#[test]
fn test_x86_arch_level_from_string() {
assert_eq!(
X86ArchLevel::from_string("x86-64-v2"),
Some(X86ArchLevel::V2)
);
assert_eq!(
X86ArchLevel::from_string("x86-64-v4"),
Some(X86ArchLevel::V4)
);
assert_eq!(X86ArchLevel::from_string("invalid"), None);
}
#[test]
fn test_x86_arch_level_features() {
let v1 = X86ArchLevel::V1.required_features();
assert!(v1.contains(&"sse"));
assert!(v1.contains(&"sse2"));
let v3 = X86ArchLevel::V3.required_features();
assert!(v3.contains(&"avx"));
assert!(v3.contains(&"avx2"));
assert!(v3.contains(&"bmi1"));
assert!(v3.contains(&"fma"));
}
#[test]
fn test_x86_arch_level_ordering() {
assert!(X86ArchLevel::V4 > X86ArchLevel::V3);
assert!(X86ArchLevel::V3 > X86ArchLevel::V2);
assert!(X86ArchLevel::V2 > X86ArchLevel::V1);
}
#[test]
fn test_x86_sched_model_from_cpu() {
assert_eq!(
X86SchedModel::from_cpu_name("haswell"),
X86SchedModel::Haswell
);
assert_eq!(X86SchedModel::from_cpu_name("znver3"), X86SchedModel::Zen3);
assert_eq!(
X86SchedModel::from_cpu_name("unknown"),
X86SchedModel::Generic
);
}
#[test]
fn test_x86_sched_model_name() {
assert_eq!(X86SchedModel::Haswell.name(), "haswell");
assert_eq!(X86SchedModel::Zen4.name(), "znver4");
assert_eq!(X86SchedModel::Generic.name(), "generic");
}
#[test]
fn test_codegen_optimal_data_layout() {
let cg64 = make_codegen("dl64");
let dl = cg64.optimal_data_layout();
assert_eq!(dl.pointer_size_bits(), 64);
let cg32 = make_codegen_32("dl32");
let dl = cg32.optimal_data_layout();
assert_eq!(dl.pointer_size_bits(), 32);
}
#[test]
fn test_codegen_x86_varargs_32_promotion() {
let va = X86VarArgs::new(X86TargetVariant::X86_32, X86CallingConvention::C, 0);
assert!(va.needs_integer_promotion(&Type::i8()));
assert!(va.needs_integer_promotion(&Type::i16()));
assert!(!va.needs_integer_promotion(&Type::i32()));
}
}
impl X86StackProtector {
pub fn has_protected_locals(&self) -> bool {
self.has_protector && self.canary_stack_offset.is_some()
}
pub fn canary_rbp_offset(&self) -> Option<i64> {
self.canary_stack_offset
}
pub fn canary_size(&self, target: X86TargetVariant) -> u32 {
match target {
X86TargetVariant::X86_64 | X86TargetVariant::X32 => 8,
X86TargetVariant::X86_32 => 4,
X86TargetVariant::X86_16 => 2,
}
}
pub fn supports_random_canary(&self) -> bool {
matches!(
self.level,
X86StackProtectorLevel::Strong | X86StackProtectorLevel::All
)
}
pub fn fs_canary_load_asm(&self) -> &'static str {
"movq %fs:0x28, %rax"
}
pub fn canary_check_asm(&self) -> &'static str {
"cmpq %fs:0x28, %rax"
}
pub fn failure_label(&self) -> String {
"__stack_chk_fail".to_string()
}
}
impl X86TLSAccessSeq {
pub fn instruction_count(&self) -> usize {
match self {
X86TLSAccessSeq::GeneralDynamicPIC => 6,
X86TLSAccessSeq::GeneralDynamicNonPIC => 5,
X86TLSAccessSeq::TLSDESC => 5,
X86TLSAccessSeq::LocalDynamicPIC => 5,
X86TLSAccessSeq::LocalDynamicNonPIC => 4,
X86TLSAccessSeq::InitialExec => 3,
X86TLSAccessSeq::LocalExec => 2,
}
}
pub fn clobbered_regs(&self) -> &'static [&'static str] {
match self {
X86TLSAccessSeq::GeneralDynamicPIC
| X86TLSAccessSeq::GeneralDynamicNonPIC
| X86TLSAccessSeq::LocalDynamicPIC
| X86TLSAccessSeq::LocalDynamicNonPIC => &["rax", "rdx", "rcx"],
X86TLSAccessSeq::TLSDESC => &["rax"],
X86TLSAccessSeq::InitialExec => &["rax"],
X86TLSAccessSeq::LocalExec => &[],
}
}
pub fn key_mnemonic(&self) -> &'static str {
match self {
X86TLSAccessSeq::GeneralDynamicPIC => "leaq",
X86TLSAccessSeq::GeneralDynamicNonPIC => "movabsq",
X86TLSAccessSeq::TLSDESC => "leaq",
X86TLSAccessSeq::LocalDynamicPIC => "leaq",
X86TLSAccessSeq::LocalDynamicNonPIC => "movabsq",
X86TLSAccessSeq::InitialExec => "movq",
X86TLSAccessSeq::LocalExec => "movq",
}
}
}
impl X86CallSiteEntry {
pub fn default() -> Self {
Self {
start_offset: 0,
length: 0,
landing_pad_offset: 0,
action_index: 0,
}
}
pub fn has_landing_pad(&self) -> bool {
self.landing_pad_offset != 0
}
pub fn has_actions(&self) -> bool {
self.action_index != 0
}
}
impl X86ActionEntry {
pub fn default() -> Self {
Self {
type_filter: 0,
next_action: 0,
}
}
pub fn is_cleanup(&self) -> bool {
self.type_filter == 0
}
pub fn is_last(&self) -> bool {
self.next_action == 0
}
pub fn is_catch_all(&self) -> bool {
self.type_filter == 1
}
}
impl X86DebugFormat {
pub fn supports_type_units(&self) -> bool {
matches!(self, X86DebugFormat::DwarfV4 | X86DebugFormat::DwarfV5)
}
pub fn uses_v5_line_header(&self) -> bool {
matches!(self, X86DebugFormat::DwarfV5)
}
pub fn file_extension(&self) -> &'static str {
match self {
X86DebugFormat::DwarfV4 | X86DebugFormat::DwarfV5 => ".dwo",
X86DebugFormat::CodeView => ".pdb",
X86DebugFormat::DwarfInCOFF => ".dwo",
X86DebugFormat::None => "",
}
}
}
impl DwarfRegisterMapEntry {
pub fn is_gpr(&self) -> bool {
self.llvm_reg < 16
}
pub fn is_vector(&self) -> bool {
self.llvm_reg >= 17 && self.llvm_reg < 49
}
pub fn is_x87(&self) -> bool {
self.llvm_reg >= 33 && self.llvm_reg < 41
}
pub fn display_name(&self) -> &'static str {
if self.name.is_empty() {
"unknown"
} else {
self.name
}
}
}
impl X86LoweringInfo {
pub fn is_legal_integer(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::Integer { bits } => {
let valid: &[u32] = if self.target_variant.is_64bit() {
&[1, 8, 16, 32, 64]
} else {
&[1, 8, 16, 32]
};
valid.contains(bits)
}
_ => false,
}
}
pub fn is_extended_fp(&self, ty: &Type) -> bool {
match &ty.kind {
TypeKind::X86FP80 => self.os_abi != X86OSABI::Windows,
TypeKind::FP128 => true,
_ => false,
}
}
pub fn min_stack_alignment(&self, ty: &Type) -> u32 {
let align = self.type_alignment_in_bits(ty);
std::cmp::max(align as u32, self.target_variant.stack_alignment() * 8) as u32
}
}
#[cfg(test)]
mod truly_final_tests {
use super::*;
#[test]
fn test_stack_protector_has_protected_locals() {
let mut sp = X86StackProtector::default();
assert!(!sp.has_protected_locals());
sp.emit_setup(&make_codegen("sp_protect"));
assert!(sp.has_protected_locals());
}
#[test]
fn test_stack_protector_canary_size() {
let sp = X86StackProtector::default();
assert_eq!(sp.canary_size(X86TargetVariant::X86_64), 8);
assert_eq!(sp.canary_size(X86TargetVariant::X86_32), 4);
assert_eq!(sp.canary_size(X86TargetVariant::X86_16), 2);
}
#[test]
fn test_stack_protector_supports_random_canary() {
let mut sp = X86StackProtector::default();
sp.level = X86StackProtectorLevel::Basic;
assert!(!sp.supports_random_canary());
sp.level = X86StackProtectorLevel::Strong;
assert!(sp.supports_random_canary());
sp.level = X86StackProtectorLevel::All;
assert!(sp.supports_random_canary());
}
#[test]
fn test_stack_protector_canary_check_asm() {
let sp = X86StackProtector::default();
assert!(sp.canary_check_asm().contains("cmp"));
}
#[test]
fn test_tls_access_seq_instruction_count() {
assert_eq!(X86TLSAccessSeq::GeneralDynamicPIC.instruction_count(), 6);
assert_eq!(X86TLSAccessSeq::LocalExec.instruction_count(), 2);
}
#[test]
fn test_tls_access_seq_clobbered_regs() {
let regs = X86TLSAccessSeq::GeneralDynamicPIC.clobbered_regs();
assert!(regs.contains(&"rax"));
assert!(regs.contains(&"rcx"));
}
#[test]
fn test_tls_access_seq_key_mnemonic() {
assert_eq!(X86TLSAccessSeq::GeneralDynamicPIC.key_mnemonic(), "leaq");
assert_eq!(X86TLSAccessSeq::LocalExec.key_mnemonic(), "movq");
}
#[test]
fn test_eh_call_site_default() {
let cs = X86CallSiteEntry::default();
assert_eq!(cs.start_offset, 0);
assert!(!cs.has_landing_pad());
assert!(!cs.has_actions());
}
#[test]
fn test_eh_call_site_with_lp() {
let cs = X86CallSiteEntry {
start_offset: 0x100,
length: 0x20,
landing_pad_offset: 0x200,
action_index: 1,
};
assert!(cs.has_landing_pad());
assert!(cs.has_actions());
}
#[test]
fn test_eh_action_default() {
let action = X86ActionEntry::default();
assert!(action.is_cleanup());
assert!(action.is_last());
}
#[test]
fn test_eh_action_catch_all() {
let action = X86ActionEntry {
type_filter: 1,
next_action: 0,
};
assert!(action.is_catch_all());
assert!(action.is_last());
}
#[test]
fn test_debug_format_supports_type_units() {
assert!(X86DebugFormat::DwarfV4.supports_type_units());
assert!(X86DebugFormat::DwarfV5.supports_type_units());
assert!(!X86DebugFormat::CodeView.supports_type_units());
}
#[test]
fn test_debug_format_uses_v5_line_header() {
assert!(!X86DebugFormat::DwarfV4.uses_v5_line_header());
assert!(X86DebugFormat::DwarfV5.uses_v5_line_header());
}
#[test]
fn test_debug_format_file_extension() {
assert_eq!(X86DebugFormat::DwarfV5.file_extension(), ".dwo");
assert_eq!(X86DebugFormat::CodeView.file_extension(), ".pdb");
}
#[test]
fn test_dwarf_register_entry_is_gpr() {
let entry = DwarfRegisterMapEntry {
llvm_reg: 0,
dwarf_reg: 0,
name: "rax",
size: 8,
};
assert!(entry.is_gpr());
assert!(!entry.is_vector());
}
#[test]
fn test_dwarf_register_entry_is_vector() {
let entry = DwarfRegisterMapEntry {
llvm_reg: 17,
dwarf_reg: 17,
name: "xmm0",
size: 16,
};
assert!(!entry.is_gpr());
assert!(entry.is_vector());
}
#[test]
fn test_dwarf_register_entry_display_name() {
let named = DwarfRegisterMapEntry {
llvm_reg: 5,
dwarf_reg: 5,
name: "rdi",
size: 8,
};
assert_eq!(named.display_name(), "rdi");
let unnamed = DwarfRegisterMapEntry {
llvm_reg: 8,
dwarf_reg: 8,
name: "",
size: 8,
};
assert_eq!(unnamed.display_name(), "unknown");
}
#[test]
fn test_lowering_is_legal_integer() {
let li64 = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(li64.is_legal_integer(&Type::i64()));
assert!(li64.is_legal_integer(&Type::i32()));
assert!(li64.is_legal_integer(&Type::i1()));
assert!(!li64.is_legal_integer(&Type::float()));
let li32 = X86LoweringInfo::new(X86TargetVariant::X86_32, X86OSABI::SystemV);
assert!(li32.is_legal_integer(&Type::i32()));
}
#[test]
fn test_lowering_is_extended_fp() {
let li_sysv = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
assert!(li_sysv.is_extended_fp(&Type::x86_fp80()));
assert!(li_sysv.is_extended_fp(&Type::fp128()));
let li_win = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::Windows);
assert!(!li_win.is_extended_fp(&Type::x86_fp80()));
}
#[test]
fn test_lowering_min_stack_alignment() {
let li = X86LoweringInfo::new(X86TargetVariant::X86_64, X86OSABI::SystemV);
let alignment = li.min_stack_alignment(&Type::i64());
assert!(alignment >= 128); }
}
impl<'a> ClangX86CodeGen<'a> {
pub fn alloca_intrinsic(&self) -> &'static str {
match self.os_abi {
X86OSABI::Windows => "llvm.stacksave",
_ => "llvm.stacksave",
}
}
pub fn internal_global_prefix(&self) -> &'static str {
match self.os_abi {
X86OSABI::Windows => "\x01?",
_ => ".L",
}
}
pub fn resolves_label_differences(&self) -> bool {
match self.os_abi {
X86OSABI::Windows => false, _ => true,
}
}
pub fn max_common_alignment(&self) -> u32 {
match self.target_variant {
X86TargetVariant::X86_64 => 32,
X86TargetVariant::X86_32 => 32,
X86TargetVariant::X86_16 => 2,
X86TargetVariant::X32 => 32,
}
}
pub fn supports_ifunc(&self) -> bool {
matches!(self.os_abi, X86OSABI::SystemV | X86OSABI::Darwin)
&& self.target_variant.is_64bit()
}
}
#[cfg(test)]
mod very_last_tests {
use super::*;
#[test]
fn test_codegen_alloca_intrinsic() {
let cg = make_codegen("alloca_test");
assert!(cg.alloca_intrinsic().contains("stacksave"));
}
#[test]
fn test_codegen_internal_global_prefix() {
let cg_sysv = make_codegen("prefix_sysv");
assert!(cg_sysv.internal_global_prefix().contains(".L"));
let cg_win = make_codegen_win64("prefix_win");
assert!(cg_win.internal_global_prefix().contains("?"));
}
#[test]
fn test_codegen_resolves_label_differences() {
let cg_sysv = make_codegen("label_sysv");
assert!(cg_sysv.resolves_label_differences());
let cg_win = make_codegen_win64("label_win");
assert!(!cg_win.resolves_label_differences());
}
#[test]
fn test_codegen_max_common_alignment() {
let cg64 = make_codegen("common64");
assert_eq!(cg64.max_common_alignment(), 32);
let cg16 = ClangX86CodeGen::new("common16", "i8086-unknown-none");
assert_eq!(cg16.max_common_alignment(), 2);
}
#[test]
fn test_codegen_supports_ifunc() {
let cg = make_codegen("ifunc_test");
assert!(cg.supports_ifunc());
let cg_win = make_codegen_win64("ifunc_win");
assert!(!cg_win.supports_ifunc());
}
#[test]
fn test_full_pipeline_codegen_creation() {
let cg = ClangX86CodeGen::new("pipeline_test", "x86_64-unknown-linux-gnu");
assert_eq!(cg.target_variant, X86TargetVariant::X86_64);
assert_eq!(cg.os_abi, X86OSABI::SystemV);
let _li = &cg.lowering_info;
let _im = &cg.intrinsic_mapper;
let _va = &cg.vector_abi;
let _al = &cg.aggregate_lowering;
let _tl = &cg.tls_model;
let _sp = &cg.stack_protector;
let _eh = &cg.exception_handling;
let _di = &cg.debug_info;
let _as = &cg.address_space_map;
}
#[test]
fn test_full_windows_pipeline() {
let cg = ClangX86CodeGen::new("win_pipe", "x86_64-pc-windows-msvc");
assert_eq!(cg.target_variant, X86TargetVariant::X86_64);
assert_eq!(cg.os_abi, X86OSABI::Windows);
assert_eq!(cg.calling_convention, X86CallingConvention::Win64);
assert_eq!(
cg.exception_handling.personality_symbol(),
"__CxxFrameHandler3"
);
}
#[test]
fn test_full_32bit_pipeline() {
let cg = ClangX86CodeGen::new("pipe32", "i686-unknown-linux-gnu");
assert_eq!(cg.target_variant, X86TargetVariant::X86_32);
assert_eq!(cg.os_abi, X86OSABI::SystemV);
assert_eq!(cg.calling_convention, X86CallingConvention::C);
}
#[test]
fn test_full_x32_pipeline() {
let cg = ClangX86CodeGen::new("pipex32", "x86_64-unknown-linux-gnux32");
assert_eq!(cg.target_variant, X86TargetVariant::X32);
assert_eq!(cg.calling_convention, X86CallingConvention::X86_64_SysV);
assert_eq!(cg.target_variant.pointer_size_bytes(), 4);
}
}
#[cfg(test)]
mod rounding_tests {
use super::*;
#[test]
fn test_all_x86_target_variants_exist() {
for v in &[
X86TargetVariant::X86_64,
X86TargetVariant::X86_32,
X86TargetVariant::X86_16,
X86TargetVariant::X32,
] {
assert!(!format!("{}", v).is_empty());
}
}
#[test]
fn test_all_x86_os_abi_exist() {
for abi in &[X86OSABI::SystemV, X86OSABI::Windows, X86OSABI::Darwin] {
let _cc = abi.default_calling_convention(X86TargetVariant::X86_64);
}
}
#[test]
fn test_all_calling_conventions_have_names() {
for cc in &[
X86CallingConvention::C,
X86CallingConvention::Fast,
X86CallingConvention::StdCall,
X86CallingConvention::ThisCall,
X86CallingConvention::VectorCall,
X86CallingConvention::X86_64_SysV,
X86CallingConvention::Win64,
X86CallingConvention::PreserveAll,
X86CallingConvention::PreserveMost,
X86CallingConvention::GHC,
X86CallingConvention::AnyReg,
] {
assert!(!cc.name().is_empty());
}
}
#[test]
fn test_all_intrinsic_categories_have_flags() {
for cat in &[
X86IntrinsicCategory::SSE,
X86IntrinsicCategory::SSE2,
X86IntrinsicCategory::SSE3,
X86IntrinsicCategory::SSSE3,
X86IntrinsicCategory::SSE41,
X86IntrinsicCategory::SSE42,
X86IntrinsicCategory::AVX,
X86IntrinsicCategory::AVX2,
X86IntrinsicCategory::AVX512,
X86IntrinsicCategory::FMA,
X86IntrinsicCategory::BMI,
X86IntrinsicCategory::BMI2,
X86IntrinsicCategory::AES,
X86IntrinsicCategory::SHA,
X86IntrinsicCategory::MMX,
X86IntrinsicCategory::ADX,
X86IntrinsicCategory::RDRAND,
X86IntrinsicCategory::CRC32,
X86IntrinsicCategory::XSAVE,
X86IntrinsicCategory::General,
] {
let flag = cat.feature_flag();
if *cat != X86IntrinsicCategory::General {
assert!(!flag.is_empty(), "Category {:?} has empty flag", cat);
}
}
}
#[test]
fn test_all_personality_kinds_have_symbols() {
for pk in &[
X86PersonalityKind::GXX,
X86PersonalityKind::GCC,
X86PersonalityKind::MSVC_X64,
X86PersonalityKind::MSVC_C,
X86PersonalityKind::SEH,
X86PersonalityKind::Wasm,
] {
assert!(!pk.symbol_name().is_empty());
}
}
#[test]
fn test_all_tls_models_work() {
for model in &[
X86TLSModelKind::GeneralDynamic,
X86TLSModelKind::LocalDynamic,
X86TLSModelKind::InitialExec,
X86TLSModelKind::LocalExec,
] {
assert!(!model.llvm_name().is_empty());
}
}
#[test]
fn test_all_eh_table_formats() {
for fmt in &[
X86EHTablesFormat::Dwarf,
X86EHTablesFormat::ARM,
X86EHTablesFormat::Win64,
X86EHTablesFormat::CompactUnwind,
] {
let _ = fmt;
}
}
#[test]
fn test_all_debug_formats() {
for fmt in &[
X86DebugFormat::DwarfV4,
X86DebugFormat::DwarfV5,
X86DebugFormat::CodeView,
X86DebugFormat::DwarfInCOFF,
X86DebugFormat::None,
] {
let _ = fmt.file_extension();
}
}
#[test]
fn test_all_address_space_qualifiers() {
for qual in &[
AddressSpaceQual::Default,
AddressSpaceQual::Global,
AddressSpaceQual::Constant,
AddressSpaceQual::ThreadLocal,
AddressSpaceQual::Generic,
AddressSpaceQual::OpenCLGlobal,
AddressSpaceQual::OpenCLLocal,
AddressSpaceQual::OpenCLConstant,
AddressSpaceQual::OpenCLPrivate,
AddressSpaceQual::CudaGlobal,
AddressSpaceQual::CudaShared,
AddressSpaceQual::CudaConstant,
AddressSpaceQual::CudaLocal,
AddressSpaceQual::SegFS,
AddressSpaceQual::SegGS,
AddressSpaceQual::SegSS,
] {
assert!(!qual.name().is_empty());
}
}
#[test]
fn test_all_sched_models() {
for model in &[
X86SchedModel::Generic,
X86SchedModel::SandyBridge,
X86SchedModel::Haswell,
X86SchedModel::SkylakeClient,
X86SchedModel::SkylakeServer,
X86SchedModel::IceLake,
X86SchedModel::AlderLakeP,
X86SchedModel::Zen2,
X86SchedModel::Zen3,
X86SchedModel::Zen4,
X86SchedModel::Zen5,
] {
assert!(!model.name().is_empty());
}
}
#[test]
fn test_all_arch_levels_ordered() {
let levels = vec![
X86ArchLevel::V1,
X86ArchLevel::V2,
X86ArchLevel::V3,
X86ArchLevel::V4,
];
for w in levels.windows(2) {
assert!(w[1] > w[0]);
assert!(w[1] >= w[0]);
assert!(w[0] < w[1]);
assert!(w[0] <= w[1]);
}
}
#[test]
fn test_all_vector_widths() {
for w in &[
X86VectorWidth::V64,
X86VectorWidth::V128,
X86VectorWidth::V256,
X86VectorWidth::V512,
] {
assert!(w.bytes() > 0);
assert!(w.alignment() > 0);
}
}
#[test]
fn test_all_tls_access_sequences() {
for seq in &[
X86TLSAccessSeq::GeneralDynamicPIC,
X86TLSAccessSeq::GeneralDynamicNonPIC,
X86TLSAccessSeq::TLSDESC,
X86TLSAccessSeq::LocalDynamicPIC,
X86TLSAccessSeq::LocalDynamicNonPIC,
X86TLSAccessSeq::InitialExec,
X86TLSAccessSeq::LocalExec,
] {
assert!(!seq.relocation_type().is_empty());
assert!(seq.instruction_count() > 0);
}
}
#[test]
fn test_final_smoke_test() {
let mut cg = ClangX86CodeGen::new("smoke", "x86_64-unknown-linux-gnu");
cg.enable_features(&[
"avx".into(),
"avx2".into(),
"avx512f".into(),
"sse4.1".into(),
"sse4.2".into(),
]);
cg.apply_data_layout();
assert!(cg.flags.use_avx);
assert!(cg.flags.use_avx2);
assert!(cg.flags.use_avx512);
assert!(cg.flags.use_sse41);
assert!(cg.flags.use_sse42);
}
}