use crate::types::Type;
use crate::value::ValueRef;
use std::fmt;
use std::rc::Rc;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AtomicOrdering {
NotAtomic = 0,
Unordered = 1,
Monotonic = 2,
Acquire = 3,
Release = 4,
AcquireRelease = 5,
SequentiallyConsistent = 6,
}
impl AtomicOrdering {
pub fn is_at_least(&self, other: AtomicOrdering) -> bool {
(*self as u32) >= (other as u32)
}
pub fn is_stronger_than(&self, other: AtomicOrdering) -> bool {
(*self as u32) > (other as u32)
}
pub fn is_at_least_acquire(&self) -> bool {
self.is_at_least(AtomicOrdering::Acquire)
}
pub fn is_at_least_release(&self) -> bool {
self.is_at_least(AtomicOrdering::Release)
}
}
impl fmt::Display for AtomicOrdering {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AtomicOrdering::NotAtomic => write!(f, "not_atomic"),
AtomicOrdering::Unordered => write!(f, "unordered"),
AtomicOrdering::Monotonic => write!(f, "monotonic"),
AtomicOrdering::Acquire => write!(f, "acquire"),
AtomicOrdering::Release => write!(f, "release"),
AtomicOrdering::AcquireRelease => write!(f, "acq_rel"),
AtomicOrdering::SequentiallyConsistent => write!(f, "seq_cst"),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct FastMathFlags {
pub no_nans: bool,
pub no_infs: bool,
pub no_signed_zeros: bool,
pub allow_reciprocal: bool,
pub allow_contract: bool,
pub allow_reassociation: bool,
pub approx_func: bool,
}
impl FastMathFlags {
pub fn all() -> Self {
FastMathFlags {
no_nans: true,
no_infs: true,
no_signed_zeros: true,
allow_reciprocal: true,
allow_contract: true,
allow_reassociation: true,
approx_func: true,
}
}
pub fn is_any_set(&self) -> bool {
self.no_nans
|| self.no_infs
|| self.no_signed_zeros
|| self.allow_reciprocal
|| self.allow_contract
|| self.allow_reassociation
|| self.approx_func
}
}
#[derive(Debug, Clone)]
pub struct OperandBundle {
pub tag: String,
pub inputs: Vec<ValueRef>,
}
impl OperandBundle {
pub fn new(tag: String, inputs: Vec<ValueRef>) -> Self {
OperandBundle { tag, inputs }
}
}
#[derive(Debug, Clone, Default)]
pub struct OperandBundleSet {
pub bundles: Vec<OperandBundle>,
}
impl OperandBundleSet {
pub fn empty() -> Self {
OperandBundleSet {
bundles: Vec::new(),
}
}
pub fn has_inputs(&self) -> bool {
!self.bundles.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct AllocaInfo {
pub allocated_type: Type,
pub num_elements: u64,
pub alignment: u32,
pub addr_space: u32,
pub is_array_allocation: bool,
}
#[derive(Debug, Clone)]
pub struct LoadInfo {
pub pointer_operand: ValueRef,
pub is_volatile: bool,
pub alignment: u32,
pub ordering: AtomicOrdering,
pub is_atomic: bool,
}
#[derive(Debug, Clone)]
pub struct StoreInfo {
pub value_operand: ValueRef,
pub pointer_operand: ValueRef,
pub is_volatile: bool,
pub alignment: u32,
pub ordering: AtomicOrdering,
pub is_atomic: bool,
}
#[derive(Debug, Clone)]
pub struct GEPInfo {
pub pointer_operand: ValueRef,
pub indices: Vec<ValueRef>,
pub source_element_type: Type,
pub is_inbounds: bool,
pub in_range_index: Option<u32>,
}
#[derive(Debug, Clone)]
pub struct PHIInfo {
pub incoming_values: Vec<(ValueRef, String)>, pub num_incoming: usize,
}
impl PHIInfo {
pub fn new() -> Self {
PHIInfo {
incoming_values: Vec::new(),
num_incoming: 0,
}
}
pub fn add_incoming(&mut self, value: ValueRef, block: String) {
self.incoming_values.push((value, block));
self.num_incoming = self.incoming_values.len();
}
pub fn remove_incoming_value(&mut self, block: &str) -> bool {
if let Some(pos) = self.incoming_values.iter().position(|(_, b)| b == block) {
self.incoming_values.remove(pos);
self.num_incoming = self.incoming_values.len();
return true;
}
false
}
pub fn get_incoming_value_for_block(&self, block: &str) -> Option<&ValueRef> {
self.incoming_values
.iter()
.find(|(_, b)| b == block)
.map(|(v, _)| v)
}
pub fn has_constant_or_undef_value(&self) -> bool {
false }
}
#[derive(Debug, Clone)]
pub struct CallInfo {
pub callee: ValueRef,
pub arguments: Vec<ValueRef>,
pub is_tail_call: bool,
pub is_musttail: bool,
pub is_notail: bool,
pub calling_conv: u32,
pub operand_bundles: OperandBundleSet,
pub is_convergent: bool,
}
#[derive(Debug, Clone)]
pub struct SwitchInfo {
pub condition: ValueRef,
pub default_dest: String,
pub cases: Vec<(ValueRef, String)>, }
impl SwitchInfo {
pub fn num_cases(&self) -> usize {
self.cases.len()
}
pub fn find_case_value(&self, value: &ValueRef) -> Option<&String> {
self.cases
.iter()
.find(|(v, _)| std::ptr::eq(Rc::as_ptr(v), Rc::as_ptr(value)))
.map(|(_, d)| d)
}
pub fn add_case(&mut self, value: ValueRef, dest: String) {
self.cases.push((value, dest));
}
pub fn remove_case(&mut self, value: &ValueRef) -> bool {
if let Some(pos) = self
.cases
.iter()
.position(|(v, _)| std::ptr::eq(Rc::as_ptr(v), Rc::as_ptr(value)))
{
self.cases.remove(pos);
return true;
}
false
}
}
#[derive(Debug, Clone)]
pub struct InvokeInfo {
pub callee: ValueRef,
pub arguments: Vec<ValueRef>,
pub normal_dest: String,
pub unwind_dest: String,
pub operand_bundles: OperandBundleSet,
pub calling_conv: u32,
}
#[derive(Debug, Clone)]
pub struct LandingPadInfo {
pub result_type: Type,
pub personality_fn: Option<ValueRef>,
pub is_cleanup: bool,
pub clauses: Vec<LandingPadClause>,
}
#[derive(Debug, Clone)]
pub enum LandingPadClause {
Catch(ValueRef),
Filter(Vec<ValueRef>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AtomicRMWBinOp {
Xchg,
Add,
Sub,
And,
Nand,
Or,
Xor,
Max,
Min,
UMax,
UMin,
FAdd,
FSub,
FMax,
FMin,
UIncWrap,
UDecWrap,
}
#[derive(Debug, Clone)]
pub struct AtomicRMWInfo {
pub pointer: ValueRef,
pub value: ValueRef,
pub operation: AtomicRMWBinOp,
pub ordering: AtomicOrdering,
pub is_volatile: bool,
}
#[derive(Debug, Clone)]
pub struct AtomicCmpXchgInfo {
pub pointer: ValueRef,
pub compare_value: ValueRef,
pub new_value: ValueRef,
pub success_ordering: AtomicOrdering,
pub failure_ordering: AtomicOrdering,
pub is_weak: bool,
pub is_volatile: bool,
}
#[derive(Debug, Clone)]
pub struct FenceInfo {
pub ordering: AtomicOrdering,
pub scope: SyncScope,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SyncScope {
SingleThread,
System,
}
#[derive(Debug, Clone)]
pub struct CatchSwitchInfo {
pub parent_pad: Option<ValueRef>,
pub unwind_dest: Option<String>,
pub handlers: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct CatchRetInfo {
pub catch_pad: ValueRef,
pub successor: String,
}
#[derive(Debug, Clone)]
pub struct CleanupRetInfo {
pub cleanup_pad: ValueRef,
pub unwind_dest: Option<String>,
}
#[derive(Debug, Clone)]
pub struct CleanupPadInfo {
pub parent_pad: Option<ValueRef>,
pub args: Vec<ValueRef>,
}
#[derive(Debug, Clone)]
pub struct CatchPadInfo {
pub catch_switch: ValueRef,
pub args: Vec<ValueRef>,
}
#[derive(Debug, Clone)]
pub struct FreezeInfo {
pub operand: ValueRef,
}
#[derive(Debug, Clone)]
pub struct VAArgInfo {
pub va_list: ValueRef,
pub result_type: Type,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(non_camel_case_types)]
pub enum CmpPredicate {
ICMP_EQ = 32,
ICMP_NE = 33,
ICMP_UGT = 34,
ICMP_UGE = 35,
ICMP_ULT = 36,
ICMP_ULE = 37,
ICMP_SGT = 38,
ICMP_SGE = 39,
ICMP_SLT = 40,
ICMP_SLE = 41,
FCMP_FALSE = 0,
FCMP_OEQ = 1,
FCMP_OGT = 2,
FCMP_OGE = 3,
FCMP_OLT = 4,
FCMP_OLE = 5,
FCMP_ONE = 6,
FCMP_ORD = 7,
FCMP_UNO = 8,
FCMP_UEQ = 9,
FCMP_UGT = 10,
FCMP_UGE = 11,
FCMP_ULT = 12,
FCMP_ULE = 13,
FCMP_UNE = 14,
FCMP_TRUE = 15,
}
impl CmpPredicate {
pub fn is_int_predicate(&self) -> bool {
(*self as u32) >= 32 && (*self as u32) <= 41
}
pub fn is_fp_predicate(&self) -> bool {
(*self as u32) <= 15
}
pub fn get_swapped_predicate(&self) -> CmpPredicate {
match self {
CmpPredicate::ICMP_UGT => CmpPredicate::ICMP_ULT,
CmpPredicate::ICMP_UGE => CmpPredicate::ICMP_ULE,
CmpPredicate::ICMP_ULT => CmpPredicate::ICMP_UGT,
CmpPredicate::ICMP_ULE => CmpPredicate::ICMP_UGE,
CmpPredicate::ICMP_SGT => CmpPredicate::ICMP_SLT,
CmpPredicate::ICMP_SGE => CmpPredicate::ICMP_SLE,
CmpPredicate::ICMP_SLT => CmpPredicate::ICMP_SGT,
CmpPredicate::ICMP_SLE => CmpPredicate::ICMP_SGE,
CmpPredicate::FCMP_OGT => CmpPredicate::FCMP_OLT,
CmpPredicate::FCMP_OGE => CmpPredicate::FCMP_OLE,
CmpPredicate::FCMP_OLT => CmpPredicate::FCMP_OGT,
CmpPredicate::FCMP_OLE => CmpPredicate::FCMP_OGE,
CmpPredicate::FCMP_UGT => CmpPredicate::FCMP_ULT,
CmpPredicate::FCMP_UGE => CmpPredicate::FCMP_ULE,
CmpPredicate::FCMP_ULT => CmpPredicate::FCMP_UGT,
CmpPredicate::FCMP_ULE => CmpPredicate::FCMP_UGE,
other => *other,
}
}
pub fn is_equality(&self) -> bool {
matches!(
self,
CmpPredicate::ICMP_EQ
| CmpPredicate::ICMP_NE
| CmpPredicate::FCMP_OEQ
| CmpPredicate::FCMP_UEQ
| CmpPredicate::FCMP_ONE
| CmpPredicate::FCMP_UNE
)
}
}
impl fmt::Display for CmpPredicate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CmpPredicate::ICMP_EQ => write!(f, "eq"),
CmpPredicate::ICMP_NE => write!(f, "ne"),
CmpPredicate::ICMP_UGT => write!(f, "ugt"),
CmpPredicate::ICMP_UGE => write!(f, "uge"),
CmpPredicate::ICMP_ULT => write!(f, "ult"),
CmpPredicate::ICMP_ULE => write!(f, "ule"),
CmpPredicate::ICMP_SGT => write!(f, "sgt"),
CmpPredicate::ICMP_SGE => write!(f, "sge"),
CmpPredicate::ICMP_SLT => write!(f, "slt"),
CmpPredicate::ICMP_SLE => write!(f, "sle"),
CmpPredicate::FCMP_OEQ => write!(f, "oeq"),
CmpPredicate::FCMP_ONE => write!(f, "one"),
CmpPredicate::FCMP_OGT => write!(f, "ogt"),
CmpPredicate::FCMP_OGE => write!(f, "oge"),
CmpPredicate::FCMP_OLT => write!(f, "olt"),
CmpPredicate::FCMP_OLE => write!(f, "ole"),
CmpPredicate::FCMP_ORD => write!(f, "ord"),
CmpPredicate::FCMP_UNO => write!(f, "uno"),
_ => write!(f, "unknown_pred"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InstructionFlag {
NoUnsignedWrap,
NoSignedWrap,
Exact,
Disjoint,
NonNeg,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_atomic_ordering_compare() {
assert!(AtomicOrdering::SequentiallyConsistent.is_stronger_than(AtomicOrdering::Acquire));
assert!(AtomicOrdering::Acquire.is_at_least_acquire());
assert!(!AtomicOrdering::Release.is_at_least_acquire());
}
#[test]
fn test_phi_node() {
let mut phi = PHIInfo::new();
phi.add_incoming(ValueRef::default(), "entry".to_string());
phi.add_incoming(ValueRef::default(), "loop".to_string());
assert_eq!(phi.num_incoming, 2);
phi.remove_incoming_value("entry");
assert_eq!(phi.num_incoming, 1);
}
#[test]
fn test_switch_info() {
let mut sw = SwitchInfo {
condition: ValueRef::default(),
default_dest: "default".to_string(),
cases: Vec::new(),
};
sw.add_case(ValueRef::default(), "case1".to_string());
assert_eq!(sw.num_cases(), 1);
}
#[test]
fn test_cmp_predicate_swap() {
assert_eq!(
CmpPredicate::ICMP_UGT.get_swapped_predicate(),
CmpPredicate::ICMP_ULT
);
assert_eq!(
CmpPredicate::FCMP_OGT.get_swapped_predicate(),
CmpPredicate::FCMP_OLT
);
assert_eq!(
CmpPredicate::ICMP_EQ.get_swapped_predicate(),
CmpPredicate::ICMP_EQ
);
}
#[test]
fn test_cmp_is_equality() {
assert!(CmpPredicate::ICMP_EQ.is_equality());
assert!(CmpPredicate::ICMP_NE.is_equality());
assert!(!CmpPredicate::ICMP_UGT.is_equality());
}
#[test]
fn test_fast_math_flags() {
let flags = FastMathFlags::all();
assert!(flags.is_any_set());
assert!(flags.no_nans);
}
}