use crate::name::Name;
use crate::predicates::*;
use crate::types::{FPType, NamedStructDef, Type, TypeRef, Typed, Types};
use std::convert::TryFrom;
use std::fmt::{self, Display};
use std::ops::Deref;
use std::sync::Arc;
#[derive(PartialEq, Clone, Debug)]
pub enum Constant {
    Int {
        
        bits: u32,
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        value: u64,
    },
    Float(Float),
    
    Null(TypeRef),
    
    AggregateZero(TypeRef),
    Struct {
        name: Option<String>, 
        values: Vec<ConstantRef>,
        is_packed: bool,
    },
    Array {
        element_type: TypeRef,
        elements: Vec<ConstantRef>,
    },
    Vector(Vec<ConstantRef>),
    
    Undef(TypeRef),
    
    #[cfg(feature="llvm-12-or-greater")]
    Poison(TypeRef),
    
    
    BlockAddress, 
    GlobalReference {
        name: Name,
        ty: TypeRef,
    },
    TokenNone,
    
    
    Add(Add),
    Sub(Sub),
    Mul(Mul),
    UDiv(UDiv),
    SDiv(SDiv),
    URem(URem),
    SRem(SRem),
    
    And(And),
    Or(Or),
    Xor(Xor),
    Shl(Shl),
    LShr(LShr),
    AShr(AShr),
    
    FAdd(FAdd),
    FSub(FSub),
    FMul(FMul),
    FDiv(FDiv),
    FRem(FRem),
    
    ExtractElement(ExtractElement),
    InsertElement(InsertElement),
    ShuffleVector(ShuffleVector),
    
    ExtractValue(ExtractValue),
    InsertValue(InsertValue),
    
    GetElementPtr(GetElementPtr),
    
    Trunc(Trunc),
    ZExt(ZExt),
    SExt(SExt),
    FPTrunc(FPTrunc),
    FPExt(FPExt),
    FPToUI(FPToUI),
    FPToSI(FPToSI),
    UIToFP(UIToFP),
    SIToFP(SIToFP),
    PtrToInt(PtrToInt),
    IntToPtr(IntToPtr),
    BitCast(BitCast),
    AddrSpaceCast(AddrSpaceCast),
    
    ICmp(ICmp),
    FCmp(FCmp),
    Select(Select),
}
#[derive(PartialEq, Clone, Debug)]
#[allow(non_camel_case_types)]
pub enum Float {
    Half, 
    #[cfg(feature="llvm-11-or-greater")]
    BFloat, 
    Single(f32),
    Double(f64),
    Quadruple, 
    X86_FP80,  
    PPC_FP128, 
}
impl Typed for Float {
    fn get_type(&self, types: &Types) -> TypeRef {
        types.fp(match self {
            Float::Half => FPType::Half,
            #[cfg(feature="llvm-11-or-greater")]
            Float::BFloat => FPType::BFloat,
            Float::Single(_) => FPType::Single,
            Float::Double(_) => FPType::Double,
            Float::Quadruple => FPType::FP128,
            Float::X86_FP80 => FPType::X86_FP80,
            Float::PPC_FP128 => FPType::PPC_FP128,
        })
    }
}
impl Display for Float {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Float::Half => write!(f, "half"),
            #[cfg(feature="llvm-11-or-greater")]
            Float::BFloat => write!(f, "bfloat"),
            Float::Single(s) => write!(f, "float {}", s),
            Float::Double(d) => write!(f, "double {}", d),
            Float::Quadruple => write!(f, "quadruple"),
            Float::X86_FP80 => write!(f, "x86_fp80"),
            Float::PPC_FP128 => write!(f, "ppc_fp128"),
        }
    }
}
impl Typed for Constant {
    #[rustfmt::skip] 
    fn get_type(&self, types: &Types) -> TypeRef {
        match self {
            Constant::Int { bits, .. } => types.int(*bits),
            Constant::Float(f) => types.type_of(f),
            Constant::Null(t) => t.clone(),
            Constant::AggregateZero(t) => t.clone(),
            Constant::Struct { values, is_packed, .. } => types.struct_of(
                values.iter().map(|v| types.type_of(v)).collect(),
                *is_packed,
            ),
            Constant::Array { element_type, elements } => types.array_of(
                element_type.clone(),
                elements.len(),
            ),
            #[cfg(feature="llvm-11-or-greater")]
            Constant::Vector(v) => types.vector_of(
                types.type_of(&v[0]),
                v.len(),
                false, 
            ),
            #[cfg(feature="llvm-10-or-lower")]
            Constant::Vector(v) => types.vector_of(
                types.type_of(&v[0]),
                v.len(),
            ),
            Constant::Undef(t) => t.clone(),
            #[cfg(feature="llvm-12-or-greater")]
            Constant::Poison(t) => t.clone(),
            Constant::BlockAddress { .. } => types.label_type(),
            Constant::GlobalReference { ty, .. } => types.pointer_to(ty.clone()),
            Constant::TokenNone => types.token_type(),
            Constant::Add(a) => types.type_of(a),
            Constant::Sub(s) => types.type_of(s),
            Constant::Mul(m) => types.type_of(m),
            Constant::UDiv(d) => types.type_of(d),
            Constant::SDiv(d) => types.type_of(d),
            Constant::URem(r) => types.type_of(r),
            Constant::SRem(r) => types.type_of(r),
            Constant::And(a) => types.type_of(a),
            Constant::Or(o) => types.type_of(o),
            Constant::Xor(x) => types.type_of(x),
            Constant::Shl(s) => types.type_of(s),
            Constant::LShr(l) => types.type_of(l),
            Constant::AShr(a) => types.type_of(a),
            Constant::FAdd(f) => types.type_of(f),
            Constant::FSub(f) => types.type_of(f),
            Constant::FMul(f) => types.type_of(f),
            Constant::FDiv(f) => types.type_of(f),
            Constant::FRem(f) => types.type_of(f),
            Constant::ExtractElement(e) => types.type_of(e),
            Constant::InsertElement(i) => types.type_of(i),
            Constant::ShuffleVector(s) => types.type_of(s),
            Constant::ExtractValue(e) => types.type_of(e),
            Constant::InsertValue(i) => types.type_of(i),
            Constant::GetElementPtr(g) => types.type_of(g),
            Constant::Trunc(t) => types.type_of(t),
            Constant::ZExt(z) => types.type_of(z),
            Constant::SExt(s) => types.type_of(s),
            Constant::FPTrunc(f) => types.type_of(f),
            Constant::FPExt(f) => types.type_of(f),
            Constant::FPToUI(f) => types.type_of(f),
            Constant::FPToSI(f) => types.type_of(f),
            Constant::UIToFP(u) => types.type_of(u),
            Constant::SIToFP(s) => types.type_of(s),
            Constant::PtrToInt(p) => types.type_of(p),
            Constant::IntToPtr(i) => types.type_of(i),
            Constant::BitCast(b) => types.type_of(b),
            Constant::AddrSpaceCast(a) => types.type_of(a),
            Constant::ICmp(i) => types.type_of(i),
            Constant::FCmp(f) => types.type_of(f),
            Constant::Select(s) => types.type_of(s),
        }
    }
}
impl Display for Constant {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Constant::Int { bits, value } => {
                if *bits == 1 {
                    if *value == 0 {
                        write!(f, "i1 false")
                    } else {
                        write!(f, "i1 true")
                    }
                } else {
                    
                    match *bits {
                        16 => {
                            let signed_val = (*value & 0xFFFF) as i16;
                            if signed_val > -1000 {
                                write!(f, "i{} {}", bits, signed_val)
                            } else {
                                write!(f, "i{} {}", bits, *value)
                            }
                        },
                        32 => {
                            let signed_val = (*value & 0xFFFF_FFFF) as i32;
                            if signed_val > -1000 {
                                write!(f, "i{} {}", bits, signed_val)
                            } else {
                                write!(f, "i{} {}", bits, *value)
                            }
                        },
                        64 => {
                            let signed_val = *value as i64;
                            if signed_val > -1000 {
                                write!(f, "i{} {}", bits, signed_val)
                            } else {
                                write!(f, "i{} {}", bits, *value)
                            }
                        },
                        _ => write!(f, "i{} {}", bits, value),
                    }
                }
            },
            Constant::Float(float) => write!(f, "{}", float),
            Constant::Null(ty) => write!(f, "{} null", ty),
            Constant::AggregateZero(ty) => write!(f, "{} zeroinitializer", ty),
            Constant::Struct { values, is_packed, .. } => {
                if *is_packed {
                    write!(f, "<")?;
                }
                write!(f, "{{ ")?;
                for (i, val) in values.iter().enumerate() {
                    if i == values.len() - 1 {
                        write!(f, "{}", val)?;
                    } else {
                        write!(f, "{}, ", val)?;
                    }
                }
                write!(f, " }}")?;
                if *is_packed {
                    write!(f, ">")?;
                }
                Ok(())
            },
            Constant::Array { elements, .. } => {
                write!(f, "[ ")?;
                for (i, elt) in elements.iter().enumerate() {
                    if i == elements.len() - 1 {
                        write!(f, "{}", elt)?;
                    } else {
                        write!(f, "{}, ", elt)?;
                    }
                }
                write!(f, " ]")?;
                Ok(())
            },
            Constant::Vector(v) => {
                write!(f, "< ")?;
                for (i, elt) in v.iter().enumerate() {
                    if i == v.len() - 1 {
                        write!(f, "{}", elt)?;
                    } else {
                        write!(f, "{}, ", elt)?;
                    }
                }
                write!(f, " >")?;
                Ok(())
            },
            Constant::Undef(ty) => write!(f, "{} undef", ty),
            #[cfg(feature="llvm-12-or-greater")]
            Constant::Poison(ty) => write!(f, "{} poison", ty),
            Constant::BlockAddress => write!(f, "blockaddr"),
            Constant::GlobalReference { name, ty } => {
                let name = match name {
                    Name::Name(n) => n,
                    _ => panic!("Expected global to be named, not numbered"),
                };
                match ty.as_ref() {
                    Type::FuncType { .. } => {
                        
                        write!(f, "@{}", name)
                    },
                    _ => {
                        
                        write!(f, "{}* @{}", ty, name)
                    },
                }
            },
            Constant::TokenNone => write!(f, "none"),
            Constant::Add(a) => write!(f, "{}", a),
            Constant::Sub(s) => write!(f, "{}", s),
            Constant::Mul(m) => write!(f, "{}", m),
            Constant::UDiv(d) => write!(f, "{}", d),
            Constant::SDiv(d) => write!(f, "{}", d),
            Constant::URem(r) => write!(f, "{}", r),
            Constant::SRem(r) => write!(f, "{}", r),
            Constant::And(a) => write!(f, "{}", a),
            Constant::Or(o) => write!(f, "{}", o),
            Constant::Xor(x) => write!(f, "{}", x),
            Constant::Shl(s) => write!(f, "{}", s),
            Constant::LShr(l) => write!(f, "{}", l),
            Constant::AShr(a) => write!(f, "{}", a),
            Constant::FAdd(a) => write!(f, "{}", a),
            Constant::FSub(s) => write!(f, "{}", s),
            Constant::FMul(m) => write!(f, "{}", m),
            Constant::FDiv(d) => write!(f, "{}", d),
            Constant::FRem(r) => write!(f, "{}", r),
            Constant::ExtractElement(e) => write!(f, "{}", e),
            Constant::InsertElement(i) => write!(f, "{}", i),
            Constant::ShuffleVector(s) => write!(f, "{}", s),
            Constant::ExtractValue(e) => write!(f, "{}", e),
            Constant::InsertValue(i) => write!(f, "{}", i),
            Constant::GetElementPtr(g) => write!(f, "{}", g),
            Constant::Trunc(t) => write!(f, "{}", t),
            Constant::ZExt(z) => write!(f, "{}", z),
            Constant::SExt(s) => write!(f, "{}", s),
            Constant::FPTrunc(t) => write!(f, "{}", t),
            Constant::FPExt(e) => write!(f, "{}", e),
            Constant::FPToUI(t) => write!(f, "{}", t),
            Constant::FPToSI(t) => write!(f, "{}", t),
            Constant::UIToFP(t) => write!(f, "{}", t),
            Constant::SIToFP(t) => write!(f, "{}", t),
            Constant::PtrToInt(p) => write!(f, "{}", p),
            Constant::IntToPtr(i) => write!(f, "{}", i),
            Constant::BitCast(b) => write!(f, "{}", b),
            Constant::AddrSpaceCast(a) => write!(f, "{}", a),
            Constant::ICmp(i) => write!(f, "{}", i),
            Constant::FCmp(c) => write!(f, "{}", c),
            Constant::Select(s) => write!(f, "{}", s),
        }
    }
}
#[derive(PartialEq, Clone, Debug)]
pub struct ConstantRef(Arc<Constant>);
impl AsRef<Constant> for ConstantRef {
    fn as_ref(&self) -> &Constant {
        self.0.as_ref()
    }
}
impl Deref for ConstantRef {
    type Target = Constant;
    fn deref(&self) -> &Constant {
        self.0.deref()
    }
}
impl Typed for ConstantRef {
    fn get_type(&self, types: &Types) -> TypeRef {
        self.as_ref().get_type(types)
    }
}
impl Display for ConstantRef {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", &self.0)
    }
}
impl ConstantRef {
    
    
    
    
    
    pub fn new(c: Constant) -> Self {
        Self(Arc::new(c))
    }
}
pub trait ConstUnaryOp {
    fn get_operand(&self) -> ConstantRef;
}
pub trait ConstBinaryOp {
    fn get_operand0(&self) -> ConstantRef;
    fn get_operand1(&self) -> ConstantRef;
}
macro_rules! impl_constexpr {
    ($expr:ty, $id:ident) => {
        impl From<$expr> for Constant {
            fn from(expr: $expr) -> Constant {
                Constant::$id(expr)
            }
        }
        impl TryFrom<Constant> for $expr {
            type Error = &'static str;
            fn try_from(constant: Constant) -> Result<Self, Self::Error> {
                match constant {
                    Constant::$id(expr) => Ok(expr),
                    _ => Err("Constant is not of requested kind"),
                }
            }
        }
    };
}
macro_rules! impl_unop {
    ($expr:ty) => {
        impl ConstUnaryOp for $expr {
            fn get_operand(&self) -> ConstantRef {
                self.operand.clone()
            }
        }
    };
}
macro_rules! impl_binop {
    ($expr:ty, $dispname:expr) => {
        impl ConstBinaryOp for $expr {
            fn get_operand0(&self) -> ConstantRef {
                self.operand0.clone()
            }
            fn get_operand1(&self) -> ConstantRef {
                self.operand1.clone()
            }
        }
    };
}
macro_rules! binop_same_type {
    ($expr:ty, $dispname:expr) => {
        impl_binop!($expr, $dispname);
        impl Typed for $expr {
            fn get_type(&self, types: &Types) -> TypeRef {
                let t = types.type_of(&self.get_operand0());
                debug_assert_eq!(t, types.type_of(&self.get_operand1()));
                t
            }
        }
        impl Display for $expr {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{} ({}, {})", $dispname, &self.operand0, &self.operand1)
            }
        }
    };
}
macro_rules! binop_left_type {
    ($expr:ty, $dispname:expr) => {
        impl_binop!($expr, $dispname);
        impl Typed for $expr {
            fn get_type(&self, types: &Types) -> TypeRef {
                types.type_of(&self.get_operand0())
            }
        }
        impl Display for $expr {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{} ({}, {})", $dispname, &self.operand0, &self.operand1)
            }
        }
    };
}
macro_rules! unop_explicitly_typed {
    ($expr:ty, $dispname:expr) => {
        impl_unop!($expr);
        impl Typed for $expr {
            fn get_type(&self, _types: &Types) -> TypeRef {
                self.to_type.clone()
            }
        }
        impl Display for $expr {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{} ({} to {})",
                    $dispname, &self.get_operand(), &self.to_type,
                )
            }
        }
    };
}
#[derive(PartialEq, Clone, Debug)]
pub struct Add {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
    
    
}
impl_constexpr!(Add, Add);
binop_same_type!(Add, "add");
#[derive(PartialEq, Clone, Debug)]
pub struct Sub {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
    
    
}
impl_constexpr!(Sub, Sub);
binop_same_type!(Sub, "sub");
#[derive(PartialEq, Clone, Debug)]
pub struct Mul {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
    
    
}
impl_constexpr!(Mul, Mul);
binop_same_type!(Mul, "mul");
#[derive(PartialEq, Clone, Debug)]
pub struct UDiv {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
    
}
impl_constexpr!(UDiv, UDiv);
binop_same_type!(UDiv, "udiv");
#[derive(PartialEq, Clone, Debug)]
pub struct SDiv {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
    
}
impl_constexpr!(SDiv, SDiv);
binop_same_type!(SDiv, "sdiv");
#[derive(PartialEq, Clone, Debug)]
pub struct URem {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
}
impl_constexpr!(URem, URem);
binop_same_type!(URem, "urem");
#[derive(PartialEq, Clone, Debug)]
pub struct SRem {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
}
impl_constexpr!(SRem, SRem);
binop_same_type!(SRem, "srem");
#[derive(PartialEq, Clone, Debug)]
pub struct And {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
}
impl_constexpr!(And, And);
binop_same_type!(And, "and");
#[derive(PartialEq, Clone, Debug)]
pub struct Or {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
}
impl_constexpr!(Or, Or);
binop_same_type!(Or, "or");
#[derive(PartialEq, Clone, Debug)]
pub struct Xor {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
}
impl_constexpr!(Xor, Xor);
binop_same_type!(Xor, "xor");
#[derive(PartialEq, Clone, Debug)]
pub struct Shl {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
    
    
}
impl_constexpr!(Shl, Shl);
binop_left_type!(Shl, "shl");
#[derive(PartialEq, Clone, Debug)]
pub struct LShr {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
    
}
impl_constexpr!(LShr, LShr);
binop_left_type!(LShr, "lshr");
#[derive(PartialEq, Clone, Debug)]
pub struct AShr {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
    
}
impl_constexpr!(AShr, AShr);
binop_left_type!(AShr, "ashr");
#[derive(PartialEq, Clone, Debug)]
pub struct FAdd {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
}
impl_constexpr!(FAdd, FAdd);
binop_same_type!(FAdd, "fadd");
#[derive(PartialEq, Clone, Debug)]
pub struct FSub {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
}
impl_constexpr!(FSub, FSub);
binop_same_type!(FSub, "fsub");
#[derive(PartialEq, Clone, Debug)]
pub struct FMul {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
}
impl_constexpr!(FMul, FMul);
binop_same_type!(FMul, "fmul");
#[derive(PartialEq, Clone, Debug)]
pub struct FDiv {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
}
impl_constexpr!(FDiv, FDiv);
binop_same_type!(FDiv, "fdiv");
#[derive(PartialEq, Clone, Debug)]
pub struct FRem {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
}
impl_constexpr!(FRem, FRem);
binop_same_type!(FRem, "frem");
#[derive(PartialEq, Clone, Debug)]
pub struct ExtractElement {
    pub vector: ConstantRef,
    pub index: ConstantRef,
}
impl_constexpr!(ExtractElement, ExtractElement);
impl Typed for ExtractElement {
    fn get_type(&self, types: &Types) -> TypeRef {
        match types.type_of(&self.vector).as_ref() {
            Type::VectorType { element_type, .. } => element_type.clone(),
            ty => panic!(
                "Expected an ExtractElement vector to be VectorType, got {:?}",
                ty
            ),
        }
    }
}
impl Display for ExtractElement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "extractelement ({}, {})", &self.vector, &self.index)
    }
}
#[derive(PartialEq, Clone, Debug)]
pub struct InsertElement {
    pub vector: ConstantRef,
    pub element: ConstantRef,
    pub index: ConstantRef,
}
impl_constexpr!(InsertElement, InsertElement);
impl Typed for InsertElement {
    fn get_type(&self, types: &Types) -> TypeRef {
        types.type_of(&self.vector)
    }
}
impl Display for InsertElement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "insertelement ({}, {}, {})",
            &self.vector, &self.element, &self.index,
        )
    }
}
#[derive(PartialEq, Clone, Debug)]
pub struct ShuffleVector {
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
    pub mask: ConstantRef,
}
impl_constexpr!(ShuffleVector, ShuffleVector);
impl_binop!(ShuffleVector, "shufflevector");
impl Typed for ShuffleVector {
    fn get_type(&self, types: &Types) -> TypeRef {
        let ty = types.type_of(&self.operand0);
        debug_assert_eq!(ty, types.type_of(&self.operand1));
        match ty.as_ref() {
            Type::VectorType { element_type, .. } => match types.type_of(&self.mask).as_ref() {
                #[cfg(feature="llvm-11-or-greater")]
                Type::VectorType { num_elements, scalable, .. } => {
                    types.vector_of(element_type.clone(), *num_elements, *scalable)
                },
                #[cfg(feature="llvm-10-or-lower")]
                Type::VectorType { num_elements, .. } => {
                    types.vector_of(element_type.clone(), *num_elements)
                },
                ty => panic!(
                    "Expected a ShuffleVector mask to be VectorType, got {:?}",
                    ty
                ),
            },
            _ => panic!(
                "Expected a ShuffleVector operand to be VectorType, got {:?}",
                ty
            ),
        }
    }
}
impl Display for ShuffleVector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "shufflevector ({}, {}, {})",
            &self.operand0, &self.operand1, &self.mask,
        )
    }
}
#[derive(PartialEq, Clone, Debug)]
pub struct ExtractValue {
    pub aggregate: ConstantRef,
    pub indices: Vec<u32>,
}
impl_constexpr!(ExtractValue, ExtractValue);
impl Typed for ExtractValue {
    fn get_type(&self, types: &Types) -> TypeRef {
        ev_type(types.type_of(&self.aggregate), self.indices.iter().copied())
    }
}
fn ev_type(cur_type: TypeRef, mut indices: impl Iterator<Item = u32>) -> TypeRef {
    match indices.next() {
        None => cur_type,
        Some(index) => match cur_type.as_ref() {
            Type::ArrayType { element_type, .. } => ev_type(element_type.clone(), indices),
            Type::StructType { element_types, .. } => ev_type(
                element_types
                    .get(index as usize)
                    .expect("ExtractValue index out of range")
                    .clone(),
                indices,
            ),
            _ => panic!(
                "ExtractValue from something that's not ArrayType or StructType; its type is {:?}",
                cur_type
            ),
        },
    }
}
impl Display for ExtractValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "extractvalue ({}", &self.aggregate)?;
        for idx in &self.indices {
            write!(f, ", {}", idx)?;
        }
        write!(f, ")")?;
        Ok(())
    }
}
#[derive(PartialEq, Clone, Debug)]
pub struct InsertValue {
    pub aggregate: ConstantRef,
    pub element: ConstantRef,
    pub indices: Vec<u32>,
}
impl_constexpr!(InsertValue, InsertValue);
impl Typed for InsertValue {
    fn get_type(&self, types: &Types) -> TypeRef {
        types.type_of(&self.aggregate)
    }
}
impl Display for InsertValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "insertvalue ({}, {}", &self.aggregate, &self.element)?;
        for idx in &self.indices {
            write!(f, ", {}", idx)?;
        }
        write!(f, ")")?;
        Ok(())
    }
}
#[derive(PartialEq, Clone, Debug)]
pub struct GetElementPtr {
    pub address: ConstantRef,
    pub indices: Vec<ConstantRef>,
    pub in_bounds: bool,
}
impl_constexpr!(GetElementPtr, GetElementPtr);
impl Typed for GetElementPtr {
    fn get_type(&self, types: &Types) -> TypeRef {
        gep_type(types.type_of(&self.address), self.indices.iter(), types)
    }
}
fn gep_type<'c>(
    cur_type: TypeRef,
    mut indices: impl Iterator<Item = &'c ConstantRef>,
    types: &Types,
) -> TypeRef {
    match indices.next() {
        None => types.pointer_to(cur_type), 
        Some(index) => match cur_type.as_ref() {
            Type::PointerType { pointee_type, .. } => gep_type(pointee_type.clone(), indices, types),
            Type::VectorType { element_type, .. } => gep_type(element_type.clone(), indices, types),
            Type::ArrayType { element_type, .. } => gep_type(element_type.clone(), indices, types),
            Type::StructType { element_types, .. } => {
                if let Constant::Int { value, .. } = index.as_ref() {
                    gep_type(
                        element_types.get(*value as usize).cloned().expect("GEP index out of range"),
                        indices,
                        types,
                    )
                } else {
                    panic!("Expected GEP index on a constant struct to be a Constant::Int; got {:?}", index)
                }
            },
            Type::NamedStructType { name } => match types.named_struct_def(name) {
                None => panic!("Named struct with no definition (struct name {:?})", name),
                Some(NamedStructDef::Opaque) => panic!("GEP on an opaque struct type"),
                Some(NamedStructDef::Defined(ty)) => match ty.as_ref() {
                    Type::StructType { element_types, .. } => {
                        if let Constant::Int { value, .. } = index.as_ref() {
                            gep_type(element_types.get(*value as usize).cloned().expect("GEP index out of range"), indices, types)
                        } else {
                            panic!("Expected GEP index on a struct to be a Constant::Int; got {:?}", index)
                        }
                    },
                    ty => panic!("Expected NamedStructDef inner type to be a StructType; got {:?}", ty),
                },
            }
            _ => panic!("Expected GEP base type to be a PointerType, VectorType, ArrayType, StructType, or NamedStructType; got {:?}", cur_type),
        },
    }
}
impl Display for GetElementPtr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "getelementptr{} ({}",
            if self.in_bounds { " inbounds" } else { "" },
            &self.address
        )?;
        for idx in &self.indices {
            write!(f, ", {}", idx)?;
        }
        write!(f, ")")?;
        Ok(())
    }
}
#[derive(PartialEq, Clone, Debug)]
pub struct Trunc {
    pub operand: ConstantRef,
    pub to_type: TypeRef,
}
impl_constexpr!(Trunc, Trunc);
unop_explicitly_typed!(Trunc, "trunc");
#[derive(PartialEq, Clone, Debug)]
pub struct ZExt {
    pub operand: ConstantRef,
    pub to_type: TypeRef,
}
impl_constexpr!(ZExt, ZExt);
unop_explicitly_typed!(ZExt, "zext");
#[derive(PartialEq, Clone, Debug)]
pub struct SExt {
    pub operand: ConstantRef,
    pub to_type: TypeRef,
}
impl_constexpr!(SExt, SExt);
unop_explicitly_typed!(SExt, "sext");
#[derive(PartialEq, Clone, Debug)]
pub struct FPTrunc {
    pub operand: ConstantRef,
    pub to_type: TypeRef,
}
impl_constexpr!(FPTrunc, FPTrunc);
unop_explicitly_typed!(FPTrunc, "fptrunc");
#[derive(PartialEq, Clone, Debug)]
pub struct FPExt {
    pub operand: ConstantRef,
    pub to_type: TypeRef,
}
impl_constexpr!(FPExt, FPExt);
unop_explicitly_typed!(FPExt, "fpext");
#[derive(PartialEq, Clone, Debug)]
pub struct FPToUI {
    pub operand: ConstantRef,
    pub to_type: TypeRef,
}
impl_constexpr!(FPToUI, FPToUI);
unop_explicitly_typed!(FPToUI, "fptoui");
#[derive(PartialEq, Clone, Debug)]
pub struct FPToSI {
    pub operand: ConstantRef,
    pub to_type: TypeRef,
}
impl_constexpr!(FPToSI, FPToSI);
unop_explicitly_typed!(FPToSI, "fptosi");
#[derive(PartialEq, Clone, Debug)]
pub struct UIToFP {
    pub operand: ConstantRef,
    pub to_type: TypeRef,
}
impl_constexpr!(UIToFP, UIToFP);
unop_explicitly_typed!(UIToFP, "uitofp");
#[derive(PartialEq, Clone, Debug)]
pub struct SIToFP {
    pub operand: ConstantRef,
    pub to_type: TypeRef,
}
impl_constexpr!(SIToFP, SIToFP);
unop_explicitly_typed!(SIToFP, "sitofp");
#[derive(PartialEq, Clone, Debug)]
pub struct PtrToInt {
    pub operand: ConstantRef,
    pub to_type: TypeRef,
}
impl_constexpr!(PtrToInt, PtrToInt);
unop_explicitly_typed!(PtrToInt, "ptrtoint");
#[derive(PartialEq, Clone, Debug)]
pub struct IntToPtr {
    pub operand: ConstantRef,
    pub to_type: TypeRef,
}
impl_constexpr!(IntToPtr, IntToPtr);
unop_explicitly_typed!(IntToPtr, "inttoptr");
#[derive(PartialEq, Clone, Debug)]
pub struct BitCast {
    pub operand: ConstantRef,
    pub to_type: TypeRef,
}
impl_constexpr!(BitCast, BitCast);
unop_explicitly_typed!(BitCast, "bitcast");
#[derive(PartialEq, Clone, Debug)]
pub struct AddrSpaceCast {
    pub operand: ConstantRef,
    pub to_type: TypeRef,
}
impl_constexpr!(AddrSpaceCast, AddrSpaceCast);
unop_explicitly_typed!(AddrSpaceCast, "addrspacecast");
#[derive(PartialEq, Clone, Debug)]
pub struct ICmp {
    pub predicate: IntPredicate,
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
}
impl_constexpr!(ICmp, ICmp);
impl_binop!(ICmp, "icmp");
impl Typed for ICmp {
    fn get_type(&self, types: &Types) -> TypeRef {
        let ty = types.type_of(&self.operand0);
        debug_assert_eq!(ty, types.type_of(&self.operand1));
        match ty.as_ref() {
            #[cfg(feature="llvm-11-or-greater")]
            Type::VectorType { num_elements, scalable, .. } => {
                types.vector_of(types.bool(), *num_elements, *scalable)
            },
            #[cfg(feature="llvm-10-or-lower")]
            Type::VectorType { num_elements, .. } => {
                types.vector_of(types.bool(), *num_elements)
            },
            _ => types.bool(),
        }
    }
}
impl Display for ICmp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "icmp {} ({}, {})",
            &self.predicate, &self.operand0, &self.operand1,
        )
    }
}
#[derive(PartialEq, Clone, Debug)]
pub struct FCmp {
    pub predicate: FPPredicate,
    pub operand0: ConstantRef,
    pub operand1: ConstantRef,
}
impl_constexpr!(FCmp, FCmp);
impl_binop!(FCmp, "fcmp");
impl Typed for FCmp {
    fn get_type(&self, types: &Types) -> TypeRef {
        let ty = types.type_of(&self.operand0);
        debug_assert_eq!(ty, types.type_of(&self.operand1));
        match ty.as_ref() {
            #[cfg(feature="llvm-11-or-greater")]
            Type::VectorType { num_elements, scalable, .. } => {
                types.vector_of(types.bool(), *num_elements, *scalable)
            },
            #[cfg(feature="llvm-10-or-lower")]
            Type::VectorType { num_elements, .. } => {
                types.vector_of(types.bool(), *num_elements)
            },
            _ => types.bool(),
        }
    }
}
impl Display for FCmp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "fcmp {} ({}, {})",
            &self.predicate, &self.operand0, &self.operand1,
        )
    }
}
#[derive(PartialEq, Clone, Debug)]
pub struct Select {
    pub condition: ConstantRef,
    pub true_value: ConstantRef,
    pub false_value: ConstantRef,
}
impl_constexpr!(Select, Select);
impl Typed for Select {
    fn get_type(&self, types: &Types) -> TypeRef {
        let t = types.type_of(&self.true_value);
        debug_assert_eq!(t, types.type_of(&self.false_value));
        t
    }
}
impl Display for Select {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "select ({}, {}, {})",
            &self.condition, &self.true_value, &self.false_value,
        )
    }
}
use crate::llvm_sys::*;
use crate::module::ModuleContext;
use std::collections::hash_map::Entry;
impl Constant {
    pub(crate) fn from_llvm_ref(constant: LLVMValueRef, ctx: &mut ModuleContext) -> ConstantRef {
        if let Some(constantref) = ctx.constants.get(&constant) {
            return constantref.clone();
        }
        let parsed = Self::parse_from_llvm_ref(constant, ctx);
        match ctx.constants.entry(constant) {
            Entry::Occupied(_) => panic!("This case should have been handled above"),
            Entry::Vacant(ventry) => ventry.insert(ConstantRef::new(parsed)).clone(),
        }
    }
    fn parse_from_llvm_ref(constant: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
        use llvm_sys::LLVMValueKind;
        if unsafe { LLVMIsAConstant(constant).is_null() } {
            panic!(
                "Constant::from_llvm_ref: argument wasn't a constant; ValueKind {:?}",
                unsafe { LLVMGetValueKind(constant) }
            )
        }
        match unsafe { LLVMGetValueKind(constant) } {
            LLVMValueKind::LLVMConstantIntValueKind => {
                match ctx.types.type_from_llvm_ref( unsafe { LLVMTypeOf(constant) } ).as_ref() {
                    Type::IntegerType { bits } => Constant::Int {
                        bits: *bits,
                        value: unsafe { LLVMConstIntGetZExtValue(constant) } as u64,
                    },
                    ty => panic!("Expected Constant::Int to have type Type::IntegerType; got {:?}", ty),
                }
            },
            LLVMValueKind::LLVMConstantFPValueKind => {
                match ctx.types.type_from_llvm_ref( unsafe { LLVMTypeOf(constant) } ).as_ref() {
                    Type::FPType(fptype) => Constant::Float(match fptype {
                        FPType::Half => Float::Half,
                        #[cfg(feature="llvm-11-or-greater")]
                        FPType::BFloat => Float::BFloat,
                        FPType::Single => Float::Single( unsafe {
                            let mut b = 0;
                            let b_ptr: *mut std::os::raw::c_int = &mut b;
                            LLVMConstRealGetDouble(constant, b_ptr)
                        } as f32),
                        FPType::Double => Float::Double( unsafe {
                            let mut b = 0;
                            let b_ptr: *mut std::os::raw::c_int = &mut b;
                            LLVMConstRealGetDouble(constant, b_ptr)
                        } ),
                        FPType::FP128 => Float::Quadruple,
                        FPType::X86_FP80 => Float::X86_FP80,
                        FPType::PPC_FP128 => Float::PPC_FP128,
                    }),
                    ty => panic!("Expected Constant::Float to have type Type::FPType; got {:?}", ty),
                }
            },
            LLVMValueKind::LLVMConstantStructValueKind => {
                let (num_elements, is_packed) = match ctx.types.type_from_llvm_ref( unsafe { LLVMTypeOf(constant) } ).as_ref() {
                    Type::StructType { element_types, is_packed } => (element_types.len(), *is_packed),
                    Type::NamedStructType { name } => match ctx.types.named_struct_def(name) {
                        NamedStructDef::Opaque => panic!("Constant of opaque struct type (struct name {:?})", name),
                        NamedStructDef::Defined(ty) => match ty.as_ref() {
                            Type::StructType { element_types, is_packed } => {
                                (element_types.len(), *is_packed)
                            },
                            ty => panic!("Expected NamedStructDef inner type to be a StructType, but it actually is a {:?}", ty),
                        },
                    },
                    ty => panic!("Expected Constant::Struct to have type StructType or NamedStructType; got {:?}", ty),
                };
                Constant::Struct {
                    name: None,  
                    values: {
                        (0 .. num_elements).map(|i| {
                            Constant::from_llvm_ref( unsafe { LLVMGetOperand(constant, i as u32) }, ctx)
                        }).collect()
                    },
                    is_packed,
                }
            },
            LLVMValueKind::LLVMConstantArrayValueKind => {
                match ctx.types.type_from_llvm_ref( unsafe { LLVMTypeOf(constant) } ).as_ref() {
                    Type::ArrayType { element_type, num_elements } => Constant::Array {
                        element_type: element_type.clone(),
                        elements: {
                            (0 .. *num_elements).map(|i| Constant::from_llvm_ref( unsafe { LLVMGetOperand(constant, i as u32) }, ctx)).collect()
                        },
                    },
                    ty => panic!("Expected Constant::Array to have type Type::ArrayType; got {:?}", ty),
                }
            },
            LLVMValueKind::LLVMConstantVectorValueKind => {
                let num_elements = unsafe { LLVMGetNumOperands(constant) };
                Constant::Vector(
                    (0 .. num_elements).map(|i| Constant::from_llvm_ref( unsafe { LLVMGetOperand(constant, i as u32) }, ctx)).collect()
                )
            },
            LLVMValueKind::LLVMConstantDataArrayValueKind => {
                match ctx.types.type_from_llvm_ref( unsafe { LLVMTypeOf(constant) } ).as_ref() {
                    Type::ArrayType { element_type, num_elements } => Constant::Array {
                        element_type: element_type.clone(),
                        elements: {
                            (0 .. *num_elements).map(|i| Constant::from_llvm_ref( unsafe { LLVMGetElementAsConstant(constant, i as u32) }, ctx)).collect()
                        },
                    },
                    ty => panic!("Expected ConstantDataArray to have type Type::ArrayType; got {:?}", ty),
                }
            },
            LLVMValueKind::LLVMConstantDataVectorValueKind => {
                match ctx.types.type_from_llvm_ref( unsafe { LLVMTypeOf(constant) } ).as_ref() {
                    Type::VectorType { num_elements, .. } => Constant::Vector(
                        (0 .. *num_elements).map(|i| Constant::from_llvm_ref( unsafe { LLVMGetElementAsConstant(constant, i as u32) }, ctx)).collect()
                    ),
                    ty => panic!("Expected ConstantDataVector to have type Type::VectorType; got {:?}", ty),
                }
            },
            LLVMValueKind::LLVMConstantPointerNullValueKind => {
                Constant::Null(ctx.types.type_from_llvm_ref( unsafe { LLVMTypeOf(constant) } ))
            },
            LLVMValueKind::LLVMConstantAggregateZeroValueKind => {
                Constant::AggregateZero(ctx.types.type_from_llvm_ref( unsafe { LLVMTypeOf(constant) } ))
            },
            LLVMValueKind::LLVMUndefValueValueKind => {
                Constant::Undef(ctx.types.type_from_llvm_ref( unsafe { LLVMTypeOf(constant) } ))
            },
            #[cfg(feature = "llvm-12-or-greater")]
            LLVMValueKind::LLVMPoisonValueKind => {
                Constant::Poison(ctx.types.type_from_llvm_ref( unsafe { LLVMTypeOf(constant) } ))
            },
            LLVMValueKind::LLVMConstantTokenNoneValueKind => {
                Constant::TokenNone
            },
            LLVMValueKind::LLVMBlockAddressValueKind => {
                Constant::BlockAddress
            },
            LLVMValueKind::LLVMConstantExprValueKind => {
                use llvm_sys::LLVMOpcode;
                match unsafe { LLVMGetConstOpcode(constant) } {
                    LLVMOpcode::LLVMAdd => Constant::Add(Add::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMSub => Constant::Sub(Sub::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMMul => Constant::Mul(Mul::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMUDiv => Constant::UDiv(UDiv::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMSDiv => Constant::SDiv(SDiv::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMURem => Constant::URem(URem::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMSRem => Constant::SRem(SRem::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMAnd => Constant::And(And::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMOr => Constant::Or(Or::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMXor => Constant::Xor(Xor::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMShl => Constant::Shl(Shl::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMLShr => Constant::LShr(LShr::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMAShr => Constant::AShr(AShr::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMFAdd => Constant::FAdd(FAdd::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMFSub => Constant::FSub(FSub::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMFMul => Constant::FMul(FMul::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMFDiv => Constant::FDiv(FDiv::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMFRem => Constant::FRem(FRem::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMExtractElement => Constant::ExtractElement(ExtractElement::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMInsertElement => Constant::InsertElement(InsertElement::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMShuffleVector => Constant::ShuffleVector(ShuffleVector::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMExtractValue => Constant::ExtractValue(ExtractValue::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMInsertValue => Constant::InsertValue(InsertValue::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMGetElementPtr => Constant::GetElementPtr(GetElementPtr::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMTrunc => Constant::Trunc(Trunc::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMZExt => Constant::ZExt(ZExt::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMSExt => Constant::SExt(SExt::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMFPTrunc => Constant::FPTrunc(FPTrunc::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMFPExt => Constant::FPExt(FPExt::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMFPToUI => Constant::FPToUI(FPToUI::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMFPToSI => Constant::FPToSI(FPToSI::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMUIToFP => Constant::UIToFP(UIToFP::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMSIToFP => Constant::SIToFP(SIToFP::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMPtrToInt => Constant::PtrToInt(PtrToInt::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMIntToPtr => Constant::IntToPtr(IntToPtr::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMBitCast => Constant::BitCast(BitCast::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMAddrSpaceCast => Constant::AddrSpaceCast(AddrSpaceCast::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMICmp => Constant::ICmp(ICmp::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMFCmp => Constant::FCmp(FCmp::from_llvm_ref(constant, ctx)),
                    LLVMOpcode::LLVMSelect => Constant::Select(Select::from_llvm_ref(constant, ctx)),
                    opcode => panic!("ConstantExpr has unexpected opcode {:?}", opcode),
                }
            },
            _ if unsafe { !LLVMIsAGlobalValue(constant).is_null() } => {
                Constant::GlobalReference {
                    name: ctx.global_names.get(&constant)
                        .unwrap_or_else(|| { let names: Vec<_> = ctx.global_names.values().collect(); panic!("Global not found in ctx.global_names; have names {:?}", names) })
                        .clone(),
                    ty: ctx.types.type_from_llvm_ref( unsafe { LLVMGlobalGetValueType(constant) } ),
                }
            },
            k => panic!("Constant::from_llvm_ref: don't know how to handle this Constant with ValueKind {:?}", k),
        }
    }
}
macro_rules! binop_from_llvm {
    ($expr:ident) => {
        impl $expr {
            pub(crate) fn from_llvm_ref(expr: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
                assert_eq!(unsafe { LLVMGetNumOperands(expr) }, 2);
                Self {
                    operand0: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 0) }, ctx),
                    operand1: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 1) }, ctx),
                }
            }
        }
    };
}
binop_from_llvm!(Add);
binop_from_llvm!(Sub);
binop_from_llvm!(Mul);
binop_from_llvm!(UDiv);
binop_from_llvm!(SDiv);
binop_from_llvm!(URem);
binop_from_llvm!(SRem);
binop_from_llvm!(And);
binop_from_llvm!(Or);
binop_from_llvm!(Xor);
binop_from_llvm!(Shl);
binop_from_llvm!(LShr);
binop_from_llvm!(AShr);
binop_from_llvm!(FAdd);
binop_from_llvm!(FSub);
binop_from_llvm!(FMul);
binop_from_llvm!(FDiv);
binop_from_llvm!(FRem);
impl ExtractElement {
    pub(crate) fn from_llvm_ref(expr: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
        assert_eq!(unsafe { LLVMGetNumOperands(expr) }, 2);
        Self {
            vector: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 0) }, ctx),
            index: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 1) }, ctx),
        }
    }
}
impl InsertElement {
    pub(crate) fn from_llvm_ref(expr: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
        assert_eq!(unsafe { LLVMGetNumOperands(expr) }, 3);
        Self {
            vector: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 0) }, ctx),
            element: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 1) }, ctx),
            index: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 2) }, ctx),
        }
    }
}
impl ShuffleVector {
    #[cfg(feature="llvm-10-or-lower")]
    pub(crate) fn from_llvm_ref(expr: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
        assert_eq!(unsafe { LLVMGetNumOperands(expr) }, 3);
        Self {
            operand0: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 0) }, ctx),
            operand1: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 1) }, ctx),
            mask: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 2) }, ctx),
        }
    }
    #[cfg(feature="llvm-11-or-greater")]
    pub(crate) fn from_llvm_ref(expr: LLVMValueRef, _ctx: &mut ModuleContext) -> Self {
        assert_eq!(unsafe { LLVMGetNumOperands(expr) }, 2);
        
        
        
        panic!("Encountered a Constant::ShuffleVector, which is not supported for LLVM 11+")
    }
}
impl ExtractValue {
    pub(crate) fn from_llvm_ref(expr: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
        assert_eq!(unsafe { LLVMGetNumOperands(expr) }, 2);
        Self {
            aggregate: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 0) }, ctx),
            indices: unsafe {
                let num_indices = LLVMGetNumIndices(expr);
                let ptr = LLVMGetIndices(expr);
                std::slice::from_raw_parts(ptr, num_indices as usize).to_vec()
            },
        }
    }
}
impl InsertValue {
    pub(crate) fn from_llvm_ref(expr: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
        assert_eq!(unsafe { LLVMGetNumOperands(expr) }, 3);
        Self {
            aggregate: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 0) }, ctx),
            element: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 1) }, ctx),
            indices: unsafe {
                let num_indices = LLVMGetNumIndices(expr);
                let ptr = LLVMGetIndices(expr);
                std::slice::from_raw_parts(ptr, num_indices as usize).to_vec()
            },
        }
    }
}
impl GetElementPtr {
    pub(crate) fn from_llvm_ref(expr: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
        Self {
            address: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 0) }, ctx),
            indices: {
                let num_indices = unsafe { LLVMGetNumOperands(expr) as u32 } - 1; 
                (1 ..= num_indices)
                    .map(|i| Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, i) }, ctx))
                    .collect()
            },
            in_bounds: unsafe { LLVMIsInBounds(expr) } != 0,
        }
    }
}
macro_rules! typed_unop_from_llvm {
    ($expr:ident) => {
        impl $expr {
            pub(crate) fn from_llvm_ref(expr: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
                assert_eq!(unsafe { LLVMGetNumOperands(expr) }, 1);
                Self {
                    operand: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 0) }, ctx),
                    to_type: ctx.types.type_from_llvm_ref(unsafe { LLVMTypeOf(expr) }),
                }
            }
        }
    };
}
typed_unop_from_llvm!(Trunc);
typed_unop_from_llvm!(ZExt);
typed_unop_from_llvm!(SExt);
typed_unop_from_llvm!(FPTrunc);
typed_unop_from_llvm!(FPExt);
typed_unop_from_llvm!(FPToUI);
typed_unop_from_llvm!(FPToSI);
typed_unop_from_llvm!(UIToFP);
typed_unop_from_llvm!(SIToFP);
typed_unop_from_llvm!(PtrToInt);
typed_unop_from_llvm!(IntToPtr);
typed_unop_from_llvm!(BitCast);
typed_unop_from_llvm!(AddrSpaceCast);
impl ICmp {
    pub(crate) fn from_llvm_ref(expr: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
        assert_eq!(unsafe { LLVMGetNumOperands(expr) }, 2);
        Self {
            predicate: IntPredicate::from_llvm(unsafe { LLVMGetICmpPredicate(expr) }),
            operand0: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 0) }, ctx),
            operand1: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 1) }, ctx),
        }
    }
}
impl FCmp {
    pub(crate) fn from_llvm_ref(expr: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
        assert_eq!(unsafe { LLVMGetNumOperands(expr) }, 2);
        Self {
            predicate: FPPredicate::from_llvm(unsafe { LLVMGetFCmpPredicate(expr) }),
            operand0: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 0) }, ctx),
            operand1: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 1) }, ctx),
        }
    }
}
impl Select {
    pub(crate) fn from_llvm_ref(expr: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
        assert_eq!(unsafe { LLVMGetNumOperands(expr) }, 3);
        Self {
            condition: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 0) }, ctx),
            true_value: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 1) }, ctx),
            false_value: Constant::from_llvm_ref(unsafe { LLVMGetOperand(expr, 2) }, ctx),
        }
    }
}