use crate::types::{Type, TypeId, TypeKind};
use crate::value::{valref, SubclassKind, Value, ValueRef};
#[derive(Debug, Clone)]
pub struct ConstantData {
pub data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct ConstantInt {
pub value: Value,
pub bits: u32,
pub signed_val: i64,
pub unsigned_val: u64,
}
pub fn const_int(ty: Type, val: i64) -> ValueRef {
let bits = ty.integer_bit_width();
let unsigned_val = if val >= 0 {
val as u64
} else {
!((-(val as i128 + 1)) as u64)
};
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = format!("{}", val);
v.set_subclass_data(val as u32);
valref(v)
}
pub fn const_i32(val: i32) -> ValueRef {
const_int(Type::i32(), val as i64)
}
pub fn const_i64(val: i64) -> ValueRef {
const_int(Type::i64(), val)
}
pub fn const_bool(val: bool) -> ValueRef {
const_int(Type::i1(), if val { 1 } else { 0 })
}
pub fn const_i8(val: i8) -> ValueRef {
const_int(Type::i8(), val as i64)
}
#[derive(Debug, Clone)]
pub struct ConstantFP {
pub value: Value,
pub fp_val: f64,
}
pub fn const_float(val: f64) -> ValueRef {
let ty = Type::float();
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = format!("{}", (val as f32).to_bits());
valref(v)
}
pub fn const_f32(val: f32) -> ValueRef {
const_float(val as f64)
}
pub fn const_double(val: f64) -> ValueRef {
let ty = Type::double();
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = format!("{}", val.to_bits());
valref(v)
}
pub fn const_f64(val: f64) -> ValueRef {
const_double(val)
}
pub fn const_null_ptr(ty: Type) -> ValueRef {
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = "null".into();
valref(v)
}
pub fn undef_value(ty: Type) -> ValueRef {
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = "undef".into();
valref(v)
}
pub fn poison_value(ty: Type) -> ValueRef {
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = "poison".into();
valref(v)
}
pub fn const_zero(ty: Type) -> ValueRef {
match &ty.kind {
TypeKind::Integer { .. } => const_int(ty.clone(), 0),
TypeKind::Float | TypeKind::Double => const_float(0.0),
TypeKind::Pointer { .. } => const_null_ptr(ty.clone()),
TypeKind::Struct { .. } => {
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = "zeroinitializer".into();
valref(v)
}
TypeKind::Array { .. } | TypeKind::FixedVector { .. } => {
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = "zeroinitializer".into();
valref(v)
}
_ => undef_value(ty),
}
}
pub fn const_add(a: ValueRef, b: ValueRef) -> ValueRef {
let ty = a.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.push_operand(a);
v.push_operand(b);
valref(v)
}
pub fn const_gep(base: ValueRef, indices: Vec<ValueRef>) -> ValueRef {
let ty = Type::pointer(0);
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.push_operand(base);
for idx in indices {
v.push_operand(idx);
}
valref(v)
}
pub fn const_bitcast(val: ValueRef, to_type: Type) -> ValueRef {
let mut v = Value::new(to_type).with_subclass(SubclassKind::Constant);
v.push_operand(val);
valref(v)
}
pub fn const_gep_inbounds(base: ValueRef, indices: Vec<ValueRef>) -> ValueRef {
let ty = Type::pointer(0);
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.push_operand(base);
for idx in indices {
v.push_operand(idx);
}
v.subclass_data = 1; valref(v)
}
pub fn const_int_to_ptr(val: ValueRef, to_type: Type) -> ValueRef {
let mut v = Value::new(to_type).with_subclass(SubclassKind::Constant);
v.push_operand(val);
valref(v)
}
pub fn const_ptr_to_int(val: ValueRef, to_type: Type) -> ValueRef {
let mut v = Value::new(to_type).with_subclass(SubclassKind::Constant);
v.push_operand(val);
valref(v)
}
pub fn const_trunc(val: ValueRef, to_type: Type) -> ValueRef {
let mut v = Value::new(to_type).with_subclass(SubclassKind::Constant);
v.push_operand(val);
valref(v)
}
pub fn const_zext(val: ValueRef, to_type: Type) -> ValueRef {
let mut v = Value::new(to_type).with_subclass(SubclassKind::Constant);
v.push_operand(val);
valref(v)
}
pub fn const_sext(val: ValueRef, to_type: Type) -> ValueRef {
let mut v = Value::new(to_type).with_subclass(SubclassKind::Constant);
v.push_operand(val);
valref(v)
}
pub fn const_fptoui(val: ValueRef, to_type: Type) -> ValueRef {
let mut v = Value::new(to_type).with_subclass(SubclassKind::Constant);
v.push_operand(val);
valref(v)
}
pub fn const_fptosi(val: ValueRef, to_type: Type) -> ValueRef {
let mut v = Value::new(to_type).with_subclass(SubclassKind::Constant);
v.push_operand(val);
valref(v)
}
pub fn const_uitofp(val: ValueRef, to_type: Type) -> ValueRef {
let mut v = Value::new(to_type).with_subclass(SubclassKind::Constant);
v.push_operand(val);
valref(v)
}
pub fn const_sitofp(val: ValueRef, to_type: Type) -> ValueRef {
let mut v = Value::new(to_type).with_subclass(SubclassKind::Constant);
v.push_operand(val);
valref(v)
}
pub fn const_select(cond: ValueRef, true_val: ValueRef, false_val: ValueRef) -> ValueRef {
let ty = true_val.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.push_operand(cond);
v.push_operand(true_val);
v.push_operand(false_val);
valref(v)
}
pub fn const_extract_value(agg: ValueRef, indices: &[u32]) -> ValueRef {
let ty = agg.borrow().ty.clone();
let mut result_ty = ty;
for &idx in indices {
result_ty = match &result_ty.kind {
TypeKind::Struct {
element_type_ids, ..
} => {
Type::void()
}
TypeKind::Array { .. } => Type::void(),
_ => Type::void(),
};
}
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Constant);
v.push_operand(agg);
v.subclass_data = indices.iter().fold(0u32, |acc, i| acc.wrapping_add(*i));
valref(v)
}
pub fn const_insert_value(agg: ValueRef, element: ValueRef, indices: &[u32]) -> ValueRef {
let ty = agg.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.push_operand(agg);
v.push_operand(element);
v.subclass_data = indices.iter().fold(0u32, |acc, i| acc.wrapping_add(*i));
valref(v)
}
pub fn const_array(element_ty: Type, elements: &[ValueRef]) -> ValueRef {
let len = elements.len() as u32;
let ty = Type::array_with(len as u64, element_ty.id);
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
for elem in elements {
v.push_operand(elem.clone());
}
valref(v)
}
pub fn const_struct(field_types: &[Type], elements: &[ValueRef]) -> ValueRef {
let type_ids: Vec<TypeId> = field_types.iter().map(|t| t.id).collect();
let ty = Type::struct_literal_with(false, type_ids);
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
for elem in elements {
v.push_operand(elem.clone());
}
valref(v)
}
pub fn const_packed_struct(field_types: &[Type], elements: &[ValueRef]) -> ValueRef {
let type_ids: Vec<TypeId> = field_types.iter().map(|t| t.id).collect();
let ty = Type::struct_literal_with(true, type_ids);
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
for elem in elements {
v.push_operand(elem.clone());
}
valref(v)
}
pub fn const_vector(elements: &[ValueRef]) -> ValueRef {
if elements.is_empty() {
return undef_value(Type::void());
}
let elem_type_id = elements[0].borrow().ty.id;
let len = elements.len() as u32;
let ty = Type::fixed_vector_with(len, elem_type_id);
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
for elem in elements {
v.push_operand(elem.clone());
}
valref(v)
}
pub fn const_aggregate_zero(ty: Type) -> ValueRef {
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = "zeroinitializer".into();
valref(v)
}
pub fn const_block_address(func: ValueRef, block: ValueRef) -> ValueRef {
let ty = Type::pointer(0);
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.push_operand(func);
v.push_operand(block);
valref(v)
}
pub fn const_token_none() -> ValueRef {
let ty = Type::token();
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = "none".into();
valref(v)
}
pub fn const_fold_int_binop(
a_val: i64,
b_val: i64,
op: crate::opcode::Opcode,
bits: u32,
) -> Option<i64> {
use crate::opcode::Opcode;
let mask = if bits >= 64 {
u64::MAX
} else {
(1u64 << bits) - 1
};
let a = a_val as u64 & mask;
let b = b_val as u64 & mask;
let result = match op {
Opcode::Add => a.wrapping_add(b),
Opcode::Sub => a.wrapping_sub(b),
Opcode::Mul => a.wrapping_mul(b),
Opcode::SDiv | Opcode::UDiv => {
if b == 0 {
return None;
}
if op == Opcode::SDiv && a_val == i64::MIN && b_val == -1 {
return None;
}
if op == Opcode::SDiv {
a_val.wrapping_div(b_val) as u64
} else {
a / b
}
}
Opcode::SRem | Opcode::URem => {
if b == 0 {
return None;
}
if op == Opcode::SRem {
a_val.wrapping_rem(b_val) as u64
} else {
a % b
}
}
Opcode::Shl => a.wrapping_shl((b & (bits as u64 - 1)) as u32),
Opcode::LShr => a.wrapping_shr((b & (bits as u64 - 1)) as u32),
Opcode::AShr => {
let shift = (b & (bits as u64 - 1)) as u32;
((a_val >> shift) as u64) & mask
}
Opcode::And => a & b,
Opcode::Or => a | b,
Opcode::Xor => a ^ b,
_ => return None,
};
let sign_bit = 1u64 << (bits - 1);
let result_masked = result & mask;
if result_masked & sign_bit != 0 && bits < 64 {
Some(-(((result_masked ^ mask) + 1) as i64))
} else {
Some(result_masked as i64)
}
}
pub fn const_fold_icmp(a_val: i64, b_val: i64, pred: crate::instruction::ICmpPred) -> Option<bool> {
use crate::instruction::ICmpPred;
Some(match pred {
ICmpPred::Eq => a_val == b_val,
ICmpPred::Ne => a_val != b_val,
ICmpPred::Ugt => (a_val as u64) > (b_val as u64),
ICmpPred::Uge => (a_val as u64) >= (b_val as u64),
ICmpPred::Ult => (a_val as u64) < (b_val as u64),
ICmpPred::Ule => (a_val as u64) <= (b_val as u64),
ICmpPred::Sgt => a_val > b_val,
ICmpPred::Sge => a_val >= b_val,
ICmpPred::Slt => a_val < b_val,
ICmpPred::Sle => a_val <= b_val,
})
}
#[derive(Debug, Clone)]
pub struct GlobalVariable {
pub value: Value,
pub is_constant: bool,
pub initializer: Option<ValueRef>,
pub linkage: crate::function::Linkage,
pub section: Option<String>,
pub alignment: u32,
}
pub fn new_global(
ty: Type,
is_constant: bool,
linkage: crate::function::Linkage,
initializer: Option<ValueRef>,
name: &str,
) -> ValueRef {
let mut v = Value::new(ty)
.named(name)
.with_subclass(SubclassKind::GlobalVariable);
v.initializer = initializer;
v.is_constant = is_constant;
let gv = valref(v);
gv.borrow_mut().subclass_extra =
vec![if is_constant { 1u64 } else { 0u64 } | ((linkage as u64) << 1)];
gv
}
#[derive(Debug, Clone)]
pub struct MDNode {
pub operands: Vec<MetadataValue>,
pub id: u64,
}
#[derive(Debug, Clone)]
pub enum MetadataValue {
String(String),
Node(u64),
Constant(ValueRef),
Null,
}
pub fn md_node(operands: Vec<MetadataValue>) -> MDNode {
use std::sync::atomic::{AtomicU64, Ordering};
static NEXT_MD_ID: AtomicU64 = AtomicU64::new(0);
MDNode {
operands,
id: NEXT_MD_ID.fetch_add(1, Ordering::SeqCst),
}
}
pub fn md_string(s: impl Into<String>) -> MetadataValue {
MetadataValue::String(s.into())
}
#[derive(Debug, Clone)]
pub struct NamedMDNode {
pub name: String,
pub operands: Vec<u64>, }
#[derive(Debug, Clone)]
pub struct DebugLoc {
pub line: u32,
pub column: u32,
pub scope: Option<u64>, pub inlined_at: Option<u64>,
}
#[derive(Debug, Clone)]
pub enum DIKind {
CompileUnit,
File,
Subprogram,
LexicalBlock,
SubroutineType,
BasicType,
DerivedType,
CompositeType,
}
pub struct DIBuilder {
pub nodes: Vec<MDNode>,
}
impl DIBuilder {
pub fn new() -> Self {
Self { nodes: Vec::new() }
}
pub fn create_compile_unit(&mut self, file_id: u64, producer: &str) -> u64 {
let node = md_node(vec![md_string(producer), MetadataValue::Node(file_id)]);
let id = node.id;
self.nodes.push(node);
id
}
pub fn create_file(&mut self, filename: &str, directory: &str) -> u64 {
let node = md_node(vec![md_string(filename), md_string(directory)]);
let id = node.id;
self.nodes.push(node);
id
}
pub fn create_function(
&mut self,
scope_id: u64,
name: &str,
linkage_name: &str,
file_id: u64,
line: u32,
ty_id: u64,
) -> u64 {
let node = md_node(vec![
MetadataValue::Node(scope_id),
md_string(name),
md_string(linkage_name),
MetadataValue::Node(file_id),
MetadataValue::Constant(const_i32(line as i32)),
MetadataValue::Node(ty_id),
]);
let id = node.id;
self.nodes.push(node);
id
}
pub fn finalize(self) -> Vec<MDNode> {
self.nodes
}
}
impl Default for DIBuilder {
fn default() -> Self {
Self::new()
}
}
use crate::opcode::{FCmpPred, ICmpPred, Opcode};
#[derive(Debug, Clone)]
pub struct ConstExpr {
pub opcode: Opcode,
pub operands: Vec<ValueRef>,
pub ty: Type,
}
#[derive(Debug, Clone)]
pub enum Constant {
Int(i64, Type),
UInt(u64, Type),
Float(f64, Type),
Null(Type),
Undef(Type),
Poison(Type),
ZeroInitializer(Type),
Struct(Vec<Constant>, Type),
Array(Vec<Constant>, Type),
Vector(Vec<Constant>, Type),
Expr(ConstExpr),
AggregateZero(Type),
}
impl Constant {
pub fn new_expr(opcode: Opcode, operands: Vec<Constant>, ty: Type) -> Constant {
let val_operands: Vec<ValueRef> = operands.iter().map(|c| c.to_value_ref()).collect();
Constant::Expr(ConstExpr {
opcode,
operands: val_operands,
ty,
})
}
pub fn is_null_value(&self) -> bool {
matches!(self, Constant::Null(_))
}
pub fn is_all_ones_value(&self) -> bool {
match self {
Constant::Int(v, ty) => {
let bits = ty.integer_bit_width();
if bits <= 64 {
let mask = if bits == 64 {
u64::MAX
} else {
(1u64 << bits) - 1
};
*v as u64 & mask == mask
} else {
false }
}
Constant::UInt(v, ty) => {
let bits = ty.integer_bit_width();
if bits <= 64 {
let mask = if bits == 64 {
u64::MAX
} else {
(1u64 << bits) - 1
};
*v & mask == mask
} else {
false
}
}
_ => false,
}
}
pub fn get_aggregate_element(&self, idx: u32) -> Option<Constant> {
match self {
Constant::Struct(fields, _) => fields.get(idx as usize).cloned(),
Constant::Array(elems, _) => elems.get(idx as usize).cloned(),
Constant::Vector(elems, _) => elems.get(idx as usize).cloned(),
_ => None,
}
}
pub fn get_splat_value(&self) -> Option<Constant> {
match self {
Constant::Vector(elems, _) if !elems.is_empty() => {
let first = &elems[0];
if elems.iter().all(|e| e == first) {
Some(first.clone())
} else {
None
}
}
_ => None,
}
}
pub fn to_string(&self) -> String {
match self {
Constant::Int(v, ty) => format!("i{} {}", ty.integer_bit_width(), v),
Constant::UInt(v, ty) => format!("i{} {}", ty.integer_bit_width(), v),
Constant::Float(v, ty) => format!("{} {}", ty, v),
Constant::Null(ty) => format!("{} null", ty),
Constant::Undef(ty) => format!("{} undef", ty),
Constant::Poison(ty) => format!("{} poison", ty),
Constant::ZeroInitializer(ty) => format!("{} zeroinitializer", ty),
Constant::AggregateZero(ty) => format!("{} zeroinitializer", ty),
Constant::Struct(fields, _) => {
let fields_str: Vec<String> = fields.iter().map(|f| f.to_string()).collect();
format!("{{{}}}", fields_str.join(", "))
}
Constant::Array(elems, _) => {
let elems_str: Vec<String> = elems.iter().map(|e| e.to_string()).collect();
format!("[{}]", elems_str.join(", "))
}
Constant::Vector(elems, _) => {
let elems_str: Vec<String> = elems.iter().map(|e| e.to_string()).collect();
format!("<{}>", elems_str.join(", "))
}
Constant::Expr(expr) => {
format!(
"{} ({})",
expr.opcode.as_mnemonic(),
expr.operands
.iter()
.map(|v| v.borrow().name.clone())
.collect::<Vec<_>>()
.join(", ")
)
}
}
}
pub fn get_type(&self) -> Type {
match self {
Constant::Int(_, ty)
| Constant::UInt(_, ty)
| Constant::Float(_, ty)
| Constant::Null(ty)
| Constant::Undef(ty)
| Constant::Poison(ty)
| Constant::ZeroInitializer(ty)
| Constant::AggregateZero(ty)
| Constant::Struct(_, ty)
| Constant::Array(_, ty)
| Constant::Vector(_, ty) => ty.clone(),
Constant::Expr(expr) => expr.ty.clone(),
}
}
pub fn from_apint(val: &APInt, ty: &Type) -> Constant {
if let Some(sv) = val.as_signed() {
Constant::Int(sv, ty.clone())
} else {
Constant::UInt(val.as_u64(), ty.clone())
}
}
pub fn from_apfloat(val: &APFloat, ty: &Type) -> Constant {
Constant::Float(val.as_f64(), ty.clone())
}
pub fn to_value_ref(&self) -> ValueRef {
match self {
Constant::Int(v, ty) => const_int(ty.clone(), *v),
Constant::UInt(v, ty) => {
let ival = *v as i64;
const_int(ty.clone(), ival)
}
Constant::Float(v, ty) => {
if ty.is_floating_point() {
const_double(*v)
} else {
const_int(ty.clone(), *v as i64)
}
}
Constant::Null(ty) => const_null_ptr(ty.clone()),
Constant::Undef(ty) => undef_value(ty.clone()),
Constant::Poison(ty) => poison_value(ty.clone()),
Constant::ZeroInitializer(ty) | Constant::AggregateZero(ty) => const_zero(ty.clone()),
Constant::Struct(_, ty) | Constant::Array(_, ty) | Constant::Vector(_, ty) => {
const_zero(ty.clone())
}
Constant::Expr(_) => {
let ty = self.get_type();
undef_value(ty)
}
}
}
}
impl PartialEq for Constant {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Constant::Int(a, ta), Constant::Int(b, tb)) => a == b && ta.kind == tb.kind,
(Constant::UInt(a, ta), Constant::UInt(b, tb)) => a == b && ta.kind == tb.kind,
(Constant::Float(a, ta), Constant::Float(b, tb)) => {
a.to_bits() == b.to_bits() && ta.kind == tb.kind
}
(Constant::Null(_), Constant::Null(_)) => true,
(Constant::Undef(_), Constant::Undef(_)) => true,
(Constant::Poison(_), Constant::Poison(_)) => true,
(Constant::ZeroInitializer(_), Constant::ZeroInitializer(_)) => true,
(Constant::AggregateZero(_), Constant::AggregateZero(_)) => true,
(Constant::Struct(fa, _), Constant::Struct(fb, _)) => fa == fb,
(Constant::Array(ea, _), Constant::Array(eb, _)) => ea == eb,
(Constant::Vector(va, _), Constant::Vector(vb, _)) => va == vb,
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub struct APInt {
pub bits: u32,
pub val: u64,
pub limbs: Vec<u64>, }
impl APInt {
pub fn new(bits: u32, val: u64) -> Self {
Self {
bits,
val,
limbs: Vec::new(),
}
}
pub fn as_u64(&self) -> u64 {
self.val
}
pub fn as_signed(&self) -> Option<i64> {
if self.bits <= 64 {
Some(self.val as i64)
} else {
None
}
}
pub fn is_zero(&self) -> bool {
self.val == 0 && self.limbs.iter().all(|&l| l == 0)
}
pub fn is_all_ones(&self) -> bool {
if self.bits <= 64 {
let mask = if self.bits == 64 {
u64::MAX
} else {
(1u64 << self.bits) - 1
};
self.val == mask
} else {
self.limbs.iter().all(|&l| l == u64::MAX)
}
}
pub fn mask(&self) -> u64 {
if self.bits >= 64 {
self.val
} else {
self.val & ((1u64 << self.bits) - 1)
}
}
}
#[derive(Debug, Clone)]
pub struct APFloat {
pub val: f64,
pub semantics: FloatSemantics,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FloatSemantics {
Half,
BFloat,
Float,
Double,
X86FP80,
FP128,
PPCFP128,
}
impl APFloat {
pub fn new(val: f64, semantics: FloatSemantics) -> Self {
Self { val, semantics }
}
pub fn as_f64(&self) -> f64 {
self.val
}
pub fn is_zero(&self) -> bool {
self.val == 0.0
}
pub fn is_nan(&self) -> bool {
self.val.is_nan()
}
pub fn is_infinity(&self) -> bool {
self.val.is_infinite()
}
pub fn is_negative(&self) -> bool {
self.val.is_sign_negative()
}
}
pub struct ConstFolding;
impl ConstFolding {
pub fn fold_add(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Int(av, aty), Constant::Int(bv, _)) => {
let bits = aty.integer_bit_width();
let result = av.wrapping_add(*bv);
let masked = if bits < 64 {
result & ((1i64 << bits) - 1)
} else {
result
};
Some(Constant::Int(masked, aty.clone()))
}
(Constant::UInt(av, aty), Constant::UInt(bv, _)) => {
let bits = aty.integer_bit_width();
let result = av.wrapping_add(*bv);
let masked = if bits < 64 {
result & ((1u64 << bits) - 1)
} else {
result
};
Some(Constant::UInt(masked, aty.clone()))
}
_ => None,
}
}
pub fn fold_sub(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Int(av, aty), Constant::Int(bv, _)) => {
let bits = aty.integer_bit_width();
let result = av.wrapping_sub(*bv);
let masked = if bits < 64 {
result & ((1i64 << bits) - 1)
} else {
result
};
Some(Constant::Int(masked, aty.clone()))
}
(Constant::UInt(av, aty), Constant::UInt(bv, _)) => {
let bits = aty.integer_bit_width();
let result = av.wrapping_sub(*bv);
let masked = if bits < 64 {
result & ((1u64 << bits) - 1)
} else {
result
};
Some(Constant::UInt(masked, aty.clone()))
}
_ => None,
}
}
pub fn fold_mul(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Int(av, aty), Constant::Int(bv, _)) => {
let bits = aty.integer_bit_width();
let result = av.wrapping_mul(*bv);
let masked = if bits < 64 {
result & ((1i64 << bits) - 1)
} else {
result
};
Some(Constant::Int(masked, aty.clone()))
}
(Constant::UInt(av, aty), Constant::UInt(bv, _)) => {
let bits = aty.integer_bit_width();
let result = av.wrapping_mul(*bv);
let masked = if bits < 64 {
result & ((1u64 << bits) - 1)
} else {
result
};
Some(Constant::UInt(masked, aty.clone()))
}
_ => None,
}
}
pub fn fold_udiv(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::UInt(av, aty), Constant::UInt(bv, _)) => {
if *bv == 0 {
None } else {
Some(Constant::UInt(av / bv, aty.clone()))
}
}
(Constant::Int(av, aty), Constant::Int(bv, _)) => {
if *bv == 0 {
None
} else {
let ua = *av as u64;
let ub = *bv as u64;
Some(Constant::UInt(ua / ub, aty.clone()))
}
}
_ => None,
}
}
pub fn fold_sdiv(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Int(av, aty), Constant::Int(bv, _)) => {
if *bv == 0 {
None
} else if *av == i64::MIN && *bv == -1 {
Some(Constant::Int(i64::MIN, aty.clone()))
} else {
Some(Constant::Int(av / bv, aty.clone()))
}
}
_ => None,
}
}
pub fn fold_urem(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::UInt(av, aty), Constant::UInt(bv, _)) => {
if *bv == 0 {
None
} else {
Some(Constant::UInt(av % bv, aty.clone()))
}
}
(Constant::Int(av, aty), Constant::Int(bv, _)) => {
if *bv == 0 {
None
} else {
let ua = *av as u64;
let ub = *bv as u64;
Some(Constant::UInt(ua % ub, aty.clone()))
}
}
_ => None,
}
}
pub fn fold_srem(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Int(av, aty), Constant::Int(bv, _)) => {
if *bv == 0 {
None
} else {
Some(Constant::Int(av % bv, aty.clone()))
}
}
_ => None,
}
}
pub fn fold_and(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Int(av, aty), Constant::Int(bv, _)) => {
Some(Constant::Int(av & bv, aty.clone()))
}
(Constant::UInt(av, aty), Constant::UInt(bv, _)) => {
Some(Constant::UInt(av & bv, aty.clone()))
}
_ => None,
}
}
pub fn fold_or(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Int(av, aty), Constant::Int(bv, _)) => {
Some(Constant::Int(av | bv, aty.clone()))
}
(Constant::UInt(av, aty), Constant::UInt(bv, _)) => {
Some(Constant::UInt(av | bv, aty.clone()))
}
_ => None,
}
}
pub fn fold_xor(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Int(av, aty), Constant::Int(bv, _)) => {
Some(Constant::Int(av ^ bv, aty.clone()))
}
(Constant::UInt(av, aty), Constant::UInt(bv, _)) => {
Some(Constant::UInt(av ^ bv, aty.clone()))
}
_ => None,
}
}
pub fn fold_shl(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Int(av, aty), Constant::Int(bv, _)) => {
let shift = (*bv as u32) % aty.integer_bit_width();
let result = av.wrapping_shl(shift);
Some(Constant::Int(result, aty.clone()))
}
(Constant::UInt(av, aty), Constant::UInt(bv, _)) => {
let shift = (*bv as u32) % aty.integer_bit_width();
let result = av.wrapping_shl(shift);
Some(Constant::UInt(result, aty.clone()))
}
_ => None,
}
}
pub fn fold_lshr(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Int(av, aty), Constant::Int(bv, _)) => {
let shift = (*bv as u32) % aty.integer_bit_width();
let result = (*av as u64).wrapping_shr(shift) as i64;
Some(Constant::Int(result, aty.clone()))
}
(Constant::UInt(av, aty), Constant::UInt(bv, _)) => {
let shift = (*bv as u32) % aty.integer_bit_width();
let result = av.wrapping_shr(shift);
Some(Constant::UInt(result, aty.clone()))
}
_ => None,
}
}
pub fn fold_ashr(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Int(av, aty), Constant::Int(bv, _)) => {
let bits = aty.integer_bit_width();
let shift = (*bv as u32) % bits;
if shift >= bits {
let fill = if *av < 0 { u64::MAX << (bits - 1) } else { 0 };
Some(Constant::Int(fill as i64, aty.clone()))
} else {
let uval = *av as u64;
let result = if *av < 0 && shift > 0 {
let mask = u64::MAX << (bits - shift);
(uval >> shift) | mask
} else {
uval >> shift
};
let masked = if bits < 64 {
result & ((1u64 << bits) - 1)
} else {
result
};
let sign_bit = 1u64 << (bits - 1);
let sval = if masked & sign_bit != 0 {
(masked | (u64::MAX << bits)) as i64
} else {
masked as i64
};
Some(Constant::Int(sval, aty.clone()))
}
}
_ => None,
}
}
pub fn fold_fadd(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Float(av, aty), Constant::Float(bv, _)) => {
Some(Constant::Float(av + bv, aty.clone()))
}
_ => None,
}
}
pub fn fold_fsub(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Float(av, aty), Constant::Float(bv, _)) => {
Some(Constant::Float(av - bv, aty.clone()))
}
_ => None,
}
}
pub fn fold_fmul(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Float(av, aty), Constant::Float(bv, _)) => {
Some(Constant::Float(av * bv, aty.clone()))
}
_ => None,
}
}
pub fn fold_fdiv(a: &Constant, b: &Constant) -> Option<Constant> {
match (a, b) {
(Constant::Float(av, aty), Constant::Float(bv, _)) => {
if *bv == 0.0 {
None
} else {
Some(Constant::Float(av / bv, aty.clone()))
}
}
_ => None,
}
}
pub fn fold_zext(val: &Constant, to_ty: &Type) -> Option<Constant> {
match val {
Constant::Int(v, _) => {
let uv = *v as u64;
Some(Constant::UInt(uv, to_ty.clone()))
}
Constant::UInt(v, _) => Some(Constant::UInt(*v, to_ty.clone())),
_ => None,
}
}
pub fn fold_sext(val: &Constant, to_ty: &Type) -> Option<Constant> {
match val {
Constant::Int(v, from_ty) => {
let from_bits = from_ty.integer_bit_width();
let to_bits = to_ty.integer_bit_width();
if from_bits >= to_bits {
Some(Constant::Int(*v, to_ty.clone()))
} else {
let shift = 64 - from_bits;
let sv = (*v << shift) >> shift; Some(Constant::Int(sv, to_ty.clone()))
}
}
_ => None,
}
}
pub fn fold_trunc(val: &Constant, to_ty: &Type) -> Option<Constant> {
match val {
Constant::Int(v, _) => {
let to_bits = to_ty.integer_bit_width();
let uv = *v as u64;
if to_bits < 64 {
let mask = (1u64 << to_bits) - 1;
Some(Constant::UInt(uv & mask, to_ty.clone()))
} else {
Some(Constant::UInt(uv, to_ty.clone()))
}
}
Constant::UInt(v, _) => {
let to_bits = to_ty.integer_bit_width();
if to_bits < 64 {
let mask = (1u64 << to_bits) - 1;
Some(Constant::UInt(v & mask, to_ty.clone()))
} else {
Some(Constant::UInt(*v, to_ty.clone()))
}
}
_ => None,
}
}
pub fn fold_bitcast(val: &Constant, to_ty: &Type) -> Option<Constant> {
match val {
Constant::Int(v, _) => Some(Constant::Int(*v, to_ty.clone())),
Constant::UInt(v, _) => Some(Constant::UInt(*v, to_ty.clone())),
Constant::Float(v, _) => Some(Constant::Float(*v, to_ty.clone())),
_ => None,
}
}
pub fn fold_icmp(pred: &ICmpPred, a: &Constant, b: &Constant) -> Option<Constant> {
let result_ty = Type::i1();
match (a, b) {
(Constant::Int(av, _), Constant::Int(bv, _)) => {
let cmp = match pred {
ICmpPred::Eq => av == bv,
ICmpPred::Ne => av != bv,
ICmpPred::Sgt => av > bv,
ICmpPred::Sge => av >= bv,
ICmpPred::Slt => av < bv,
ICmpPred::Sle => av <= bv,
ICmpPred::Ugt => (*av as u64) > (*bv as u64),
ICmpPred::Uge => (*av as u64) >= (*bv as u64),
ICmpPred::Ult => (*av as u64) < (*bv as u64),
ICmpPred::Ule => (*av as u64) <= (*bv as u64),
};
Some(Constant::Int(if cmp { 1 } else { 0 }, result_ty))
}
(Constant::UInt(av, _), Constant::UInt(bv, _)) => {
let cmp = match pred {
ICmpPred::Eq => av == bv,
ICmpPred::Ne => av != bv,
ICmpPred::Sgt => (*av as i64) > (*bv as i64),
ICmpPred::Sge => (*av as i64) >= (*bv as i64),
ICmpPred::Slt => (*av as i64) < (*bv as i64),
ICmpPred::Sle => (*av as i64) <= (*bv as i64),
ICmpPred::Ugt => av > bv,
ICmpPred::Uge => av >= bv,
ICmpPred::Ult => av < bv,
ICmpPred::Ule => av <= bv,
};
Some(Constant::Int(if cmp { 1 } else { 0 }, result_ty))
}
_ => None,
}
}
pub fn fold_fcmp(pred: &FCmpPred, a: &Constant, b: &Constant) -> Option<Constant> {
let result_ty = Type::i1();
match (a, b) {
(Constant::Float(av, _), Constant::Float(bv, _)) => {
let ordered = !av.is_nan() && !bv.is_nan();
let cmp = match pred {
FCmpPred::False => false,
FCmpPred::True => true,
FCmpPred::Ord => ordered,
FCmpPred::Uno => !ordered,
FCmpPred::Oeq => ordered && av == bv,
FCmpPred::Ogt => ordered && av > bv,
FCmpPred::Oge => ordered && av >= bv,
FCmpPred::Olt => ordered && av < bv,
FCmpPred::Ole => ordered && av <= bv,
FCmpPred::One => ordered && av != bv,
FCmpPred::Ueq => !ordered || av == bv,
FCmpPred::Ugt => !ordered || av > bv,
FCmpPred::Uge => !ordered || av >= bv,
FCmpPred::Ult => !ordered || av < bv,
FCmpPred::Ule => !ordered || av <= bv,
FCmpPred::Une => !ordered || av != bv,
};
Some(Constant::Int(if cmp { 1 } else { 0 }, result_ty))
}
_ => None,
}
}
pub fn fold_gep(base: &Constant, indices: &[Constant]) -> Option<Constant> {
let ptr_ty = base.get_type();
if !ptr_ty.is_pointer() {
return None;
}
let mut offset: i64 = 0;
let mut cur_ty = ptr_ty.get_element_type();
for (i, idx) in indices.iter().enumerate() {
match &cur_ty {
Some(ct) => {
if i == 0 {
let size = ct.size_in_bytes() as i64;
if let Constant::Int(v, _) = idx {
offset += v * size;
} else {
return None;
}
} else {
match &ct.kind {
TypeKind::Array { .. } => {
let elem_ty = ct.get_element_type();
if let Some(et) = &elem_ty {
let elem_size = et.size_in_bytes() as i64;
if let Constant::Int(v, _) = idx {
offset += v * elem_size;
} else {
return None;
}
cur_ty = elem_ty;
} else {
return None;
}
}
TypeKind::Struct { .. } => {
if let Constant::Int(v, _) = idx {
let field_idx = *v as u32;
if let Some(field_offset) = ct.struct_field_offset(field_idx) {
offset += field_offset as i64;
cur_ty = ct.struct_field_type(field_idx);
} else {
return None;
}
} else {
return None;
}
}
TypeKind::FixedVector { .. } | TypeKind::ScalableVector { .. } => {
let elem_ty = ct.get_element_type();
if let Some(et) = &elem_ty {
let elem_size = et.size_in_bytes() as i64;
if let Constant::Int(v, _) = idx {
offset += v * elem_size;
} else {
return None;
}
cur_ty = elem_ty;
} else {
return None;
}
}
_ => return None,
}
}
}
None => {
return None;
}
}
}
Some(Constant::Int(offset, ptr_ty))
}
pub fn fold_select(cond: &Constant, tval: &Constant, fval: &Constant) -> Option<Constant> {
match cond {
Constant::Int(v, _) => {
if *v != 0 {
Some(tval.clone())
} else {
Some(fval.clone())
}
}
Constant::UInt(v, _) => {
if *v != 0 {
Some(tval.clone())
} else {
Some(fval.clone())
}
}
_ => None,
}
}
pub fn fold_extract_value(agg: &Constant, idx: u32) -> Option<Constant> {
agg.get_aggregate_element(idx)
}
pub fn fold_insert_value(agg: &Constant, val: &Constant, idx: u32) -> Option<Constant> {
match agg {
Constant::Struct(fields, ty) => {
let i = idx as usize;
let mut new_fields = fields.clone();
if i < new_fields.len() {
new_fields[i] = val.clone();
}
Some(Constant::Struct(new_fields, ty.clone()))
}
Constant::Array(elems, ty) => {
let i = idx as usize;
let mut new_elems = elems.clone();
if i < new_elems.len() {
new_elems[i] = val.clone();
}
Some(Constant::Array(new_elems, ty.clone()))
}
Constant::Vector(elems, ty) => {
let i = idx as usize;
let mut new_elems = elems.clone();
if i < new_elems.len() {
new_elems[i] = val.clone();
}
Some(Constant::Vector(new_elems, ty.clone()))
}
_ => None,
}
}
pub fn fold_extract_element(vec: &Constant, idx: &Constant) -> Option<Constant> {
match (vec, idx) {
(Constant::Vector(elems, _), Constant::Int(i, _)) => elems.get(*i as usize).cloned(),
_ => None,
}
}
pub fn fold_insert_element(vec: &Constant, elt: &Constant, idx: &Constant) -> Option<Constant> {
match (vec, idx) {
(Constant::Vector(elems, ty), Constant::Int(i, _)) => {
let iu = *i as usize;
let mut new_elems = elems.clone();
if iu < new_elems.len() {
new_elems[iu] = elt.clone();
}
Some(Constant::Vector(new_elems, ty.clone()))
}
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct GEPOperator {
pub pointer: ValueRef,
pub source_element_type: Type,
pub result_element_type: Type,
pub indices: Vec<ValueRef>,
pub is_inbounds: bool,
}
impl GEPOperator {
pub fn new(gep: &ValueRef) -> Option<Self> {
let gep_val = gep.borrow();
let ops = &gep_val.operands;
if ops.is_empty() {
return None;
}
let pointer = ops[0].clone();
let ptr_ty = pointer.borrow().ty.clone();
let source_element_type = if ptr_ty.is_pointer() {
ptr_ty.get_element_type().unwrap_or(ptr_ty.clone())
} else {
ptr_ty.clone()
};
let mut result_ty = source_element_type.clone();
for i in 1..ops.len() {
result_ty = match &result_ty.kind {
TypeKind::Array {
element_type_id, ..
} => {
crate::types::Type::new(crate::types::TypeKind::Integer { bits: 32 })
}
TypeKind::Struct {
element_type_ids, ..
} => {
result_ty.clone()
}
TypeKind::FixedVector {
element_type_id, ..
} => {
result_ty.clone()
}
_ => result_ty.clone(),
};
}
let indices: Vec<ValueRef> = ops[1..].to_vec();
Some(Self {
pointer,
source_element_type,
result_element_type: result_ty,
indices,
is_inbounds: false, })
}
pub fn num_indices(&self) -> usize {
self.indices.len()
}
pub fn has_all_zero_indices(&self) -> bool {
self.indices.iter().all(|idx| {
let i = idx.borrow();
i.name == "0" || i.name == "zeroinitializer"
})
}
pub fn has_constant_indices(&self) -> bool {
self.indices.iter().all(|idx| {
let i = idx.borrow();
i.is_constant()
})
}
}
#[derive(Debug, Clone)]
pub struct StructLayout {
pub struct_type: Type,
pub field_offsets: Vec<u64>,
pub size_in_bytes: u64,
pub alignment: u32,
}
impl StructLayout {
pub fn compute(struct_type: &Type) -> Option<Self> {
let field_types = struct_type.struct_element_type_ids();
if field_types.is_empty() && !struct_type.is_opaque_struct() {
return Some(Self {
struct_type: struct_type.clone(),
field_offsets: Vec::new(),
size_in_bytes: 0,
alignment: 1,
});
}
let is_packed = match &struct_type.kind {
TypeKind::Struct { is_packed, .. } => *is_packed,
_ => return None,
};
let mut offsets = Vec::new();
let mut current_offset: u64 = 0;
let mut max_align: u32 = 1;
for _ft in field_types {
let field_size: u64 = 4; let field_align: u32 = if is_packed { 1 } else { 4 };
if field_align > 0 && !is_packed {
let align_m1 = field_align as u64 - 1;
current_offset = (current_offset + align_m1) & !align_m1;
}
offsets.push(current_offset);
current_offset += field_size;
if field_align > max_align {
max_align = field_align;
}
}
if max_align > 0 && !is_packed {
let align_m1 = max_align as u64 - 1;
current_offset = (current_offset + align_m1) & !align_m1;
}
Some(Self {
struct_type: struct_type.clone(),
field_offsets: offsets,
size_in_bytes: current_offset,
alignment: max_align,
})
}
pub fn get_field_offset(&self, idx: u32) -> Option<u64> {
self.field_offsets.get(idx as usize).copied()
}
pub fn num_fields(&self) -> usize {
self.field_offsets.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::opcode::{FCmpPred, ICmpPred};
#[test]
fn test_const_int_identity() {
let v = const_i32(42);
assert_eq!(v.borrow().name, "42");
}
#[test]
fn test_const_i64() {
let v = const_i64(-1);
assert_eq!(v.borrow().name, "-1");
}
#[test]
fn test_const_bool() {
let t = const_bool(true);
let f = const_bool(false);
assert_eq!(t.borrow().name, "1");
assert_eq!(f.borrow().name, "0");
}
#[test]
fn test_const_float() {
let v = const_float(3.14);
assert_eq!(v.borrow().name, "3.14");
}
#[test]
fn test_const_double() {
let v = const_double(2.718);
assert_eq!(v.borrow().name, "2.718");
}
#[test]
fn test_const_null_ptr() {
let v = const_null_ptr(Type::pointer(0));
assert_eq!(v.borrow().name, "null");
}
#[test]
fn test_undef_value() {
let v = undef_value(Type::i32());
assert_eq!(v.borrow().name, "undef");
}
#[test]
fn test_poison_value() {
let v = poison_value(Type::i32());
assert_eq!(v.borrow().name, "poison");
}
#[test]
fn test_const_zero_int() {
let v = const_zero(Type::i32());
assert_eq!(v.borrow().name, "0");
}
#[test]
fn test_const_zero_ptr() {
let v = const_zero(Type::pointer(0));
assert_eq!(v.borrow().name, "null");
}
#[test]
fn test_const_zero_float() {
let v = const_zero(Type::double());
assert_eq!(v.borrow().name, "0");
}
#[test]
fn test_const_gep() {
let base = const_null_ptr(Type::pointer(0));
let idx = const_i32(0);
let v = const_gep(base, vec![idx]);
assert_eq!(v.borrow().operands.len(), 2);
}
#[test]
fn test_const_bitcast() {
let val = const_i32(42);
let v = const_bitcast(val, Type::float());
assert_eq!(v.borrow().operands.len(), 1);
}
#[test]
fn test_new_global() {
let gv = super::new_global(
Type::i32(),
true,
crate::function::Linkage::External,
None,
"my_global",
);
assert_eq!(gv.borrow().name, "my_global");
}
#[test]
fn test_constant_int_new_expr() {
let c = Constant::new_expr(
Opcode::Add,
vec![
Constant::Int(10, Type::i32()),
Constant::Int(20, Type::i32()),
],
Type::i32(),
);
match c {
Constant::Expr(expr) => {
assert_eq!(expr.opcode, Opcode::Add);
assert_eq!(expr.operands.len(), 2);
}
_ => panic!("Expected Expr"),
}
}
#[test]
fn test_constant_is_null_value() {
assert!(Constant::Null(Type::i32()).is_null_value());
assert!(!Constant::Int(0, Type::i32()).is_null_value());
assert!(!Constant::Float(0.0, Type::double()).is_null_value());
}
#[test]
fn test_constant_is_all_ones() {
assert!(Constant::Int(-1, Type::i32()).is_all_ones_value());
assert!(!Constant::Int(0, Type::i32()).is_all_ones_value());
assert!(!Constant::Int(42, Type::i32()).is_all_ones_value());
}
#[test]
fn test_constant_get_aggregate_element() {
let s = Constant::Struct(
vec![
Constant::Int(1, Type::i32()),
Constant::Int(2, Type::i32()),
Constant::Int(3, Type::i32()),
],
Type::i32(),
);
assert_eq!(
s.get_aggregate_element(1),
Some(Constant::Int(2, Type::i32()))
);
assert_eq!(s.get_aggregate_element(10), None);
}
#[test]
fn test_constant_splat_value() {
let v = Constant::Vector(
vec![
Constant::Int(5, Type::i32()),
Constant::Int(5, Type::i32()),
Constant::Int(5, Type::i32()),
],
Type::i32(),
);
assert_eq!(v.get_splat_value(), Some(Constant::Int(5, Type::i32())));
let v2 = Constant::Vector(
vec![Constant::Int(1, Type::i32()), Constant::Int(2, Type::i32())],
Type::i32(),
);
assert_eq!(v2.get_splat_value(), None);
}
#[test]
fn test_constant_to_string() {
assert_eq!(Constant::Int(42, Type::i32()).to_string(), "i32 42");
assert_eq!(Constant::Null(Type::pointer(0)).to_string(), "ptr null");
assert_eq!(Constant::Undef(Type::i32()).to_string(), "i32 undef");
}
#[test]
fn test_apint_operations() {
let a = APInt::new(32, 42);
assert!(!a.is_zero());
assert!(!a.is_all_ones());
let z = APInt::new(32, 0);
assert!(z.is_zero());
let ones = APInt::new(8, 0xFF);
assert!(ones.is_all_ones());
}
#[test]
fn test_apfloat_operations() {
let f = APFloat::new(3.14, FloatSemantics::Double);
assert!(!f.is_zero());
assert!(!f.is_nan());
assert!(!f.is_infinity());
assert!(!f.is_negative());
let nf = APFloat::new(-1.0, FloatSemantics::Double);
assert!(nf.is_negative());
let zf = APFloat::new(0.0, FloatSemantics::Float);
assert!(zf.is_zero());
}
#[test]
fn test_const_folding_add() {
let a = Constant::Int(10, Type::i32());
let b = Constant::Int(20, Type::i32());
let result = ConstFolding::fold_add(&a, &b);
assert_eq!(result, Some(Constant::Int(30, Type::i32())));
}
#[test]
fn test_const_folding_sub() {
let a = Constant::Int(50, Type::i32());
let b = Constant::Int(20, Type::i32());
let result = ConstFolding::fold_sub(&a, &b);
assert_eq!(result, Some(Constant::Int(30, Type::i32())));
}
#[test]
fn test_const_folding_mul() {
let a = Constant::Int(6, Type::i32());
let b = Constant::Int(7, Type::i32());
let result = ConstFolding::fold_mul(&a, &b);
assert_eq!(result, Some(Constant::Int(42, Type::i32())));
}
#[test]
fn test_const_folding_sdiv() {
let a = Constant::Int(100, Type::i32());
let b = Constant::Int(3, Type::i32());
let result = ConstFolding::fold_sdiv(&a, &b);
assert_eq!(result, Some(Constant::Int(33, Type::i32())));
}
#[test]
fn test_const_folding_udiv() {
let a = Constant::UInt(100, Type::i32());
let b = Constant::UInt(3, Type::i32());
let result = ConstFolding::fold_udiv(&a, &b);
assert_eq!(result, Some(Constant::UInt(33, Type::i32())));
}
#[test]
fn test_const_folding_urem() {
let a = Constant::UInt(100, Type::i32());
let b = Constant::UInt(7, Type::i32());
let result = ConstFolding::fold_urem(&a, &b);
assert_eq!(result, Some(Constant::UInt(2, Type::i32())));
}
#[test]
fn test_const_folding_and() {
let a = Constant::Int(0xFF, Type::i32());
let b = Constant::Int(0x0F, Type::i32());
let result = ConstFolding::fold_and(&a, &b);
assert_eq!(result, Some(Constant::Int(0x0F, Type::i32())));
}
#[test]
fn test_const_folding_or() {
let a = Constant::Int(0xF0, Type::i32());
let b = Constant::Int(0x0F, Type::i32());
let result = ConstFolding::fold_or(&a, &b);
assert_eq!(result, Some(Constant::Int(0xFF, Type::i32())));
}
#[test]
fn test_const_folding_xor() {
let a = Constant::Int(0xFF, Type::i32());
let b = Constant::Int(0x0F, Type::i32());
let result = ConstFolding::fold_xor(&a, &b);
assert_eq!(result, Some(Constant::Int(0xF0, Type::i32())));
}
#[test]
fn test_const_folding_shl() {
let a = Constant::Int(1, Type::i32());
let b = Constant::Int(3, Type::i32());
let result = ConstFolding::fold_shl(&a, &b);
assert_eq!(result, Some(Constant::Int(8, Type::i32())));
}
#[test]
fn test_const_folding_lshr() {
let a = Constant::UInt(16, Type::i32());
let b = Constant::UInt(2, Type::i32());
let result = ConstFolding::fold_lshr(&a, &b);
assert_eq!(result, Some(Constant::UInt(4, Type::i32())));
}
#[test]
fn test_const_folding_ashr() {
let a = Constant::Int(-16, Type::i32());
let b = Constant::Int(2, Type::i32());
let result = ConstFolding::fold_ashr(&a, &b);
assert_eq!(result, Some(Constant::Int(-4, Type::i32())));
}
#[test]
fn test_const_folding_icmp() {
let a = Constant::Int(10, Type::i32());
let b = Constant::Int(20, Type::i32());
let result = ConstFolding::fold_icmp(&ICmpPred::Slt, &a, &b);
assert_eq!(result, Some(Constant::Int(1, Type::i1())));
}
#[test]
fn test_const_folding_select() {
let cond = Constant::Int(1, Type::i1());
let tval = Constant::Int(42, Type::i32());
let fval = Constant::Int(0, Type::i32());
let result = ConstFolding::fold_select(&cond, &tval, &fval);
assert_eq!(result, Some(Constant::Int(42, Type::i32())));
}
#[test]
fn test_const_folding_fadd() {
let a = Constant::Float(1.5, Type::double());
let b = Constant::Float(2.5, Type::double());
let result = ConstFolding::fold_fadd(&a, &b);
assert_eq!(result, Some(Constant::Float(4.0, Type::double())));
}
#[test]
fn test_const_folding_zext() {
let a = Constant::Int(255, Type::i8());
let result = ConstFolding::fold_zext(&a, &Type::i32());
assert_eq!(result, Some(Constant::UInt(255, Type::i32())));
}
#[test]
fn test_const_folding_sext() {
let a = Constant::Int(-1, Type::i8());
let result = ConstFolding::fold_sext(&a, &Type::i32());
assert_eq!(result, Some(Constant::Int(-1, Type::i32())));
}
#[test]
fn test_const_folding_trunc() {
let a = Constant::UInt(0x1234, Type::i32());
let result = ConstFolding::fold_trunc(&a, &Type::i8());
assert_eq!(result, Some(Constant::UInt(0x34, Type::i8())));
}
#[test]
fn test_struct_layout_empty() {
let ty = crate::types::Type::struct_named_with("Empty".to_string(), false, vec![]);
let layout = StructLayout::compute(&ty);
assert!(layout.is_some());
let l = layout.unwrap();
assert_eq!(l.num_fields(), 0);
assert_eq!(l.size_in_bytes, 0);
}
#[test]
fn test_gep_operator() {
let base = const_null_ptr(Type::pointer(0));
let idx = const_i32(0);
let gep = const_gep(base, vec![idx]);
let op = GEPOperator::new(&gep);
assert!(op.is_some());
let g = op.unwrap();
assert_eq!(g.num_indices(), 1);
}
#[test]
fn test_constant_equality() {
let a = Constant::Int(42, Type::i32());
let b = Constant::Int(42, Type::i32());
let c = Constant::Int(100, Type::i32());
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn test_const_folding_fcmp() {
let a = Constant::Float(1.0, Type::double());
let b = Constant::Float(2.0, Type::double());
let result = ConstFolding::fold_fcmp(&FCmpPred::Olt, &a, &b);
assert_eq!(result, Some(Constant::Int(1, Type::i1())));
}
#[test]
fn test_const_folding_extract_element() {
let v = Constant::Vector(
vec![
Constant::Int(10, Type::i32()),
Constant::Int(20, Type::i32()),
Constant::Int(30, Type::i32()),
],
Type::i32(),
);
let result = ConstFolding::fold_extract_element(&v, &Constant::Int(1, Type::i32()));
assert_eq!(result, Some(Constant::Int(20, Type::i32())));
}
}