use crate::basic_block;
use crate::constants;
use crate::instruction::{self, FCmpPred, ICmpPred};
use crate::opcode::Opcode;
use crate::types::Type;
use crate::value::{valref, SubclassKind, Value, ValueRef};
#[derive(Debug, Clone)]
pub struct IRBuilder {
pub context: crate::context::LLVMContext,
pub current_block: Option<ValueRef>,
pub current_function: Option<ValueRef>,
pub insert_point: Option<(ValueRef, usize)>,
name_counter: u64,
debug_loc: Option<crate::constants::DebugLoc>,
fast_math_flags: FastMathFlags,
}
impl IRBuilder {
pub fn new(context: &crate::context::LLVMContext) -> Self {
Self {
context: context.clone(),
current_block: None,
current_function: None,
insert_point: None,
name_counter: 0,
debug_loc: None,
fast_math_flags: FastMathFlags::default(),
}
}
pub fn new_empty() -> Self {
Self {
context: crate::context::LLVMContext::new(),
current_block: None,
current_function: None,
insert_point: None,
name_counter: 0,
debug_loc: None,
fast_math_flags: FastMathFlags::default(),
}
}
pub fn set_insert_point(&mut self, block: &ValueRef, position: usize) {
self.current_block = Some(block.clone());
self.insert_point = Some((block.clone(), position));
}
pub fn set_insert_point_to_end(&mut self, block: &ValueRef) {
self.current_block = Some(block.clone());
self.insert_point = Some((block.clone(), usize::MAX));
}
pub fn set_insert_point_to_start(&mut self, block: &ValueRef) {
self.current_block = Some(block.clone());
self.insert_point = Some((block.clone(), 0));
}
pub fn get_insert_block(&self) -> Option<&ValueRef> {
self.insert_point.as_ref().map(|(block, _)| block)
}
pub fn get_insert_position(&self) -> Option<usize> {
self.insert_point.as_ref().map(|(_, pos)| *pos)
}
fn finish_inst(&mut self, inst: ValueRef) -> ValueRef {
if let Some(ref block) = self.current_block {
let mut b = block.borrow_mut();
b.operands.push(inst.clone());
b.instructions.push(inst.clone());
}
inst
}
pub fn clear_insert_point(&mut self) {
self.insert_point = None;
self.current_block = None;
}
pub fn clear_insertion_point(&mut self) {
self.clear_insert_point();
}
pub fn set_current_function(&mut self, func: ValueRef) {
self.current_function = Some(func);
}
pub fn get_current_function(&self) -> Option<&ValueRef> {
self.current_function.as_ref()
}
fn next_name(&mut self, prefix: &str) -> String {
let n = self.name_counter;
self.name_counter += 1;
if prefix.is_empty() {
format!("tmp{}", n)
} else {
format!("{}{}", prefix, n)
}
}
fn next_tmp_name(&mut self) -> String {
self.next_name("tmp")
}
pub fn create_ret_void(&mut self) -> ValueRef {
self.finish_inst(instruction::ret_void())
}
pub fn create_ret(&mut self, value: &ValueRef) -> ValueRef {
self.finish_inst(instruction::ret_val(value.clone()))
}
pub fn create_br(&mut self, dest: &ValueRef) -> ValueRef {
self.finish_inst(instruction::br(dest.clone()))
}
pub fn create_cond_br(
&mut self,
cond: &ValueRef,
then_bb: &ValueRef,
else_bb: &ValueRef,
) -> ValueRef {
self.finish_inst(instruction::br_cond(
cond.clone(),
then_bb.clone(),
else_bb.clone(),
))
}
pub fn create_switch(
&mut self,
value: &ValueRef,
default: &ValueRef,
cases: &[(ValueRef, ValueRef)],
) -> ValueRef {
self.finish_inst(instruction::switch(
value.clone(),
default.clone(),
cases.to_vec(),
))
}
pub fn create_unreachable(&mut self) -> ValueRef {
self.finish_inst(valref(
Value::new(Type::void()).with_subclass(SubclassKind::Instruction),
))
}
pub fn create_add(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::add(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_sub(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::sub(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_mul(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::mul(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_udiv(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::udiv(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_sdiv(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::sdiv(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_urem(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::urem(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_srem(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::srem(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_fadd(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::fadd(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_fsub(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::fsub(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_fmul(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::fmul(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_fdiv(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::fdiv(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_frem(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::frem(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_shl(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::shl(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_lshr(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::lshr(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_ashr(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::ashr(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_and(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::and(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_or(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::or(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_xor(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let inst = instruction::xor(lhs, rhs);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_alloca(&mut self, ty: Type, name: &str) -> ValueRef {
let inst = instruction::alloca(ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_load(&mut self, ty: Type, ptr: ValueRef, name: &str) -> ValueRef {
let inst = instruction::load(ty, ptr);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_store(&mut self, val: ValueRef, ptr: ValueRef) -> ValueRef {
self.finish_inst(instruction::store(val, ptr))
}
pub fn create_gep(&mut self, ptr: ValueRef, indices: Vec<ValueRef>, name: &str) -> ValueRef {
let _pointee_ty = ptr.borrow().ty.clone();
let inst = instruction::getelementptr(_pointee_ty, ptr, indices);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_inbounds_gep(
&mut self,
ptr: ValueRef,
indices: Vec<ValueRef>,
name: &str,
) -> ValueRef {
let inst = self.create_gep(ptr, indices, name);
let nm = inst.borrow().name.clone();
inst.borrow_mut().name = format!("inbounds.{}", nm);
inst
}
pub fn create_trunc(&mut self, val: ValueRef, ty: Type, name: &str) -> ValueRef {
let inst = instruction::trunc(val, ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_zext(&mut self, val: ValueRef, ty: Type, name: &str) -> ValueRef {
let inst = instruction::zext(val, ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_sext(&mut self, val: ValueRef, ty: Type, name: &str) -> ValueRef {
let inst = instruction::sext(val, ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_fptrunc(&mut self, val: ValueRef, ty: Type, name: &str) -> ValueRef {
let inst = instruction::fptrunc(val, ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_fpext(&mut self, val: ValueRef, ty: Type, name: &str) -> ValueRef {
let inst = instruction::fpext(val, ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_fptoui(&mut self, val: ValueRef, ty: Type, name: &str) -> ValueRef {
let inst = instruction::fptoui(val, ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_fptosi(&mut self, val: ValueRef, ty: Type, name: &str) -> ValueRef {
let inst = instruction::fptosi(val, ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_uitofp(&mut self, val: ValueRef, ty: Type, name: &str) -> ValueRef {
let inst = instruction::uitofp(val, ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_sitofp(&mut self, val: ValueRef, ty: Type, name: &str) -> ValueRef {
let inst = instruction::sitofp(val, ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_bitcast(&mut self, val: ValueRef, ty: Type, name: &str) -> ValueRef {
let inst = instruction::bitcast(val, ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_addrspacecast(&mut self, val: ValueRef, ty: Type, name: &str) -> ValueRef {
let vty = val.borrow().ty.clone();
let int_ty = Type::i64();
let int_val = instruction::ptrtoint(val, int_ty.clone());
let inst = instruction::inttoptr(int_val, ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_ptrtoint(&mut self, val: ValueRef, ty: Type, name: &str) -> ValueRef {
let inst = instruction::ptrtoint(val, ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_inttoptr(&mut self, val: ValueRef, ty: Type, name: &str) -> ValueRef {
let inst = instruction::inttoptr(val, ty);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_icmp(
&mut self,
pred: ICmpPred,
lhs: ValueRef,
rhs: ValueRef,
name: &str,
) -> ValueRef {
let inst = instruction::icmp(pred, lhs, rhs);
if !name.is_empty() && !name.starts_with("icmp.") {
let current = inst.borrow().name.clone(); drop(inst.borrow()); inst.borrow_mut().name = format!("{}.{}", current, name);
}
self.finish_inst(inst)
}
pub fn create_fcmp(
&mut self,
pred: FCmpPred,
lhs: ValueRef,
rhs: ValueRef,
name: &str,
) -> ValueRef {
let inst = instruction::fcmp(pred, lhs, rhs);
if !name.is_empty() && !name.starts_with("fcmp.") {
let current = inst.borrow().name.clone(); drop(inst.borrow()); inst.borrow_mut().name = format!("{}.{}", current, name);
}
self.finish_inst(inst)
}
pub fn create_call(
&mut self,
return_ty: Type,
func: ValueRef,
args: Vec<ValueRef>,
name: &str,
) -> ValueRef {
let inst = instruction::call(return_ty, func, args);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_phi(
&mut self,
ty: Type,
incoming: Vec<(ValueRef, ValueRef)>,
name: &str,
) -> ValueRef {
let inst = instruction::phi(ty, incoming);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_select(
&mut self,
cond: ValueRef,
tval: ValueRef,
fval: ValueRef,
name: &str,
) -> ValueRef {
let inst = instruction::select(cond, tval, fval);
if !name.is_empty() {
inst.borrow_mut().name = name.to_string();
}
self.finish_inst(inst)
}
pub fn create_extract_value(&mut self, agg: ValueRef, idx: u32, name: &str) -> ValueRef {
let ty = agg.borrow().ty.clone();
let idx_val = constants::const_i32(idx as i32);
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::ExtractValue);
v.operands = vec![agg, idx_val];
v.num_operands = 2;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_insert_value(
&mut self,
agg: ValueRef,
val: ValueRef,
idx: u32,
name: &str,
) -> ValueRef {
let ty = agg.borrow().ty.clone();
let idx_val = constants::const_i32(idx as i32);
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::InsertValue);
v.operands = vec![agg, val, idx_val];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_extract_element(&mut self, vec: ValueRef, idx: ValueRef, name: &str) -> ValueRef {
let ty = vec.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::ExtractElement);
v.operands = vec![vec, idx];
v.num_operands = 2;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_insert_element(
&mut self,
vec: ValueRef,
elt: ValueRef,
idx: ValueRef,
name: &str,
) -> ValueRef {
let ty = vec.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::InsertElement);
v.operands = vec![vec, elt, idx];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn get_int32(&self, val: i32) -> ValueRef {
constants::const_i32(val)
}
pub fn get_int64(&self, val: i64) -> ValueRef {
constants::const_i64(val)
}
pub fn get_bool(&self, val: bool) -> ValueRef {
constants::const_bool(val)
}
pub fn get_float(&self, val: f64) -> ValueRef {
constants::const_float(val)
}
pub fn get_int8(&self, val: i8) -> ValueRef {
constants::const_i8(val)
}
pub fn get_f32(&self, val: f32) -> ValueRef {
constants::const_float(val as f64)
}
pub fn get_f64(&self, val: f64) -> ValueRef {
constants::const_double(val)
}
pub fn get_null(&self, ty: Type) -> ValueRef {
constants::const_null_ptr(ty)
}
pub fn get_undef(&self, ty: Type) -> ValueRef {
constants::undef_value(ty)
}
pub fn get_zero(&self, ty: Type) -> ValueRef {
constants::const_zero(ty)
}
pub fn set_current_debug_location(&mut self, loc: crate::constants::DebugLoc) {
self.debug_loc = Some(loc);
}
pub fn clear_debug_loc(&mut self) {
self.debug_loc = None;
}
pub fn get_current_debug_location(&self) -> Option<&crate::constants::DebugLoc> {
self.debug_loc.as_ref()
}
pub fn create_basic_block(&self, name: &str) -> ValueRef {
basic_block::new_basic_block(name)
}
pub fn create_and_set_block(&mut self, name: &str) -> ValueRef {
let bb = self.create_basic_block(name);
self.set_insert_point_to_end(&bb);
bb
}
pub fn create_function(
&mut self,
name: &str,
return_type: Type,
param_types: &[Type],
) -> ValueRef {
let func = crate::function::new_function(name, return_type, param_types);
self.current_function = Some(func.clone());
func
}
pub fn begin_function(
&mut self,
name: &str,
return_type: Type,
param_types: &[Type],
) -> (ValueRef, ValueRef) {
let func = self.create_function(name, return_type, param_types);
let entry = self.create_basic_block("entry");
self.set_insert_point_to_end(&entry);
(func, entry)
}
pub fn finish_function(&mut self) {
self.clear_insert_point();
}
pub fn has_insert_point(&self) -> bool {
self.insert_point.is_some()
}
pub fn is_in_function(&self) -> bool {
self.current_function.is_some()
}
}
impl Default for IRBuilder {
fn default() -> Self {
Self::new_empty()
}
}
#[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,
}
impl AtomicRMWBinOp {
pub fn as_str(&self) -> &'static str {
match self {
AtomicRMWBinOp::Xchg => "xchg",
AtomicRMWBinOp::Add => "add",
AtomicRMWBinOp::Sub => "sub",
AtomicRMWBinOp::And => "and",
AtomicRMWBinOp::Nand => "nand",
AtomicRMWBinOp::Or => "or",
AtomicRMWBinOp::Xor => "xor",
AtomicRMWBinOp::Max => "max",
AtomicRMWBinOp::Min => "min",
AtomicRMWBinOp::UMax => "umax",
AtomicRMWBinOp::UMin => "umin",
AtomicRMWBinOp::FAdd => "fadd",
AtomicRMWBinOp::FSub => "fsub",
AtomicRMWBinOp::FMax => "fmax",
AtomicRMWBinOp::FMin => "fmin",
AtomicRMWBinOp::UIncWrap => "uinc_wrap",
AtomicRMWBinOp::UDecWrap => "udec_wrap",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AtomicOrdering {
NotAtomic,
Unordered,
Monotonic,
Acquire,
Release,
AcqRel,
SeqCst,
}
impl AtomicOrdering {
pub fn as_str(&self) -> &'static str {
match self {
AtomicOrdering::NotAtomic => "not_atomic",
AtomicOrdering::Unordered => "unordered",
AtomicOrdering::Monotonic => "monotonic",
AtomicOrdering::Acquire => "acquire",
AtomicOrdering::Release => "release",
AtomicOrdering::AcqRel => "acq_rel",
AtomicOrdering::SeqCst => "seq_cst",
}
}
pub fn is_at_least_acquire(&self) -> bool {
matches!(
self,
AtomicOrdering::Acquire | AtomicOrdering::AcqRel | AtomicOrdering::SeqCst
)
}
pub fn is_at_least_release(&self) -> bool {
matches!(
self,
AtomicOrdering::Release | AtomicOrdering::AcqRel | AtomicOrdering::SeqCst
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct FastMathFlags {
pub allow_reassoc: bool,
pub no_nans: bool,
pub no_infs: bool,
pub no_signed_zeros: bool,
pub allow_reciprocal: bool,
pub allow_contract: bool,
pub approx_func: bool,
}
impl FastMathFlags {
pub fn new() -> Self {
Self::default()
}
pub fn set_fast(&mut self) {
self.allow_reassoc = true;
self.no_nans = true;
self.no_infs = true;
self.no_signed_zeros = true;
self.allow_reciprocal = true;
self.allow_contract = true;
self.approx_func = true;
}
pub fn clear(&mut self) {
*self = Self::default();
}
pub fn any(&self) -> bool {
self.allow_reassoc
|| self.no_nans
|| self.no_infs
|| self.no_signed_zeros
|| self.allow_reciprocal
|| self.allow_contract
|| self.approx_func
}
pub fn is_fast(&self) -> bool {
self.allow_reassoc
&& self.no_nans
&& self.no_infs
&& self.no_signed_zeros
&& self.allow_reciprocal
&& self.allow_contract
&& self.approx_func
}
pub fn to_bits(&self) -> u32 {
let mut bits: u32 = 0;
if self.allow_reassoc {
bits |= 1 << 0;
}
if self.no_nans {
bits |= 1 << 1;
}
if self.no_infs {
bits |= 1 << 2;
}
if self.no_signed_zeros {
bits |= 1 << 3;
}
if self.allow_reciprocal {
bits |= 1 << 4;
}
if self.allow_contract {
bits |= 1 << 5;
}
if self.approx_func {
bits |= 1 << 6;
}
bits
}
pub fn from_bits(bits: u32) -> Self {
Self {
allow_reassoc: (bits & (1 << 0)) != 0,
no_nans: (bits & (1 << 1)) != 0,
no_infs: (bits & (1 << 2)) != 0,
no_signed_zeros: (bits & (1 << 3)) != 0,
allow_reciprocal: (bits & (1 << 4)) != 0,
allow_contract: (bits & (1 << 5)) != 0,
approx_func: (bits & (1 << 6)) != 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnaryIntrinsic {
Sqrt,
Sin,
Cos,
Pow,
Exp,
Exp2,
Log,
Log10,
Log2,
FAbs,
Floor,
Ceil,
Trunc,
RInt,
NearbyInt,
Round,
RoundEven,
CopySign,
FMA,
FMin,
FMax,
Minimum,
Maximum,
LdExp,
}
impl UnaryIntrinsic {
pub fn intrinsic_name(&self) -> &'static str {
match self {
UnaryIntrinsic::Sqrt => "llvm.sqrt.f32",
UnaryIntrinsic::Sin => "llvm.sin.f32",
UnaryIntrinsic::Cos => "llvm.cos.f32",
UnaryIntrinsic::Pow => "llvm.pow.f32",
UnaryIntrinsic::Exp => "llvm.exp.f32",
UnaryIntrinsic::Exp2 => "llvm.exp2.f32",
UnaryIntrinsic::Log => "llvm.log.f32",
UnaryIntrinsic::Log10 => "llvm.log10.f32",
UnaryIntrinsic::Log2 => "llvm.log2.f32",
UnaryIntrinsic::FAbs => "llvm.fabs.f32",
UnaryIntrinsic::Floor => "llvm.floor.f32",
UnaryIntrinsic::Ceil => "llvm.ceil.f32",
UnaryIntrinsic::Trunc => "llvm.trunc.f32",
UnaryIntrinsic::RInt => "llvm.rint.f32",
UnaryIntrinsic::NearbyInt => "llvm.nearbyint.f32",
UnaryIntrinsic::Round => "llvm.round.f32",
UnaryIntrinsic::RoundEven => "llvm.roundeven.f32",
UnaryIntrinsic::CopySign => "llvm.copysign.f32",
UnaryIntrinsic::FMA => "llvm.fma.f32",
UnaryIntrinsic::FMin => "llvm.minnum.f32",
UnaryIntrinsic::FMax => "llvm.maxnum.f32",
UnaryIntrinsic::Minimum => "llvm.minimum.f32",
UnaryIntrinsic::Maximum => "llvm.maximum.f32",
UnaryIntrinsic::LdExp => "llvm.ldexp.f32",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinaryIntrinsic {
Pow,
CopySign,
FMA,
FMin,
FMax,
Minimum,
Maximum,
LdExp,
USubSat,
UAddSat,
SMulO,
}
impl BinaryIntrinsic {
pub fn intrinsic_name(&self) -> &'static str {
match self {
BinaryIntrinsic::Pow => "llvm.pow.f32",
BinaryIntrinsic::CopySign => "llvm.copysign.f32",
BinaryIntrinsic::FMA => "llvm.fma.f32",
BinaryIntrinsic::FMin => "llvm.minnum.f32",
BinaryIntrinsic::FMax => "llvm.maxnum.f32",
BinaryIntrinsic::Minimum => "llvm.minimum.f32",
BinaryIntrinsic::Maximum => "llvm.maximum.f32",
BinaryIntrinsic::LdExp => "llvm.ldexp.f32",
BinaryIntrinsic::USubSat => "llvm.usub.sat.i32",
BinaryIntrinsic::UAddSat => "llvm.uadd.sat.i32",
BinaryIntrinsic::SMulO => "llvm.smul.with.overflow.i32",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VectorReduceOp {
Add,
Mul,
And,
Or,
Xor,
SMax,
SMin,
UMax,
UMin,
FAdd,
FMul,
FMax,
FMin,
}
impl VectorReduceOp {
pub fn as_str(&self) -> &'static str {
match self {
VectorReduceOp::Add => "add",
VectorReduceOp::Mul => "mul",
VectorReduceOp::And => "and",
VectorReduceOp::Or => "or",
VectorReduceOp::Xor => "xor",
VectorReduceOp::SMax => "smax",
VectorReduceOp::SMin => "smin",
VectorReduceOp::UMax => "umax",
VectorReduceOp::UMin => "umin",
VectorReduceOp::FAdd => "fadd",
VectorReduceOp::FMul => "fmul",
VectorReduceOp::FMax => "fmax",
VectorReduceOp::FMin => "fmin",
}
}
}
impl IRBuilder {
pub fn create_shuffle_vector(
&mut self,
v1: ValueRef,
v2: ValueRef,
mask: ValueRef,
name: &str,
) -> ValueRef {
let ty = v1.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::ShuffleVector);
v.operands = vec![v1, v2, mask];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_atomic_rmw(
&mut self,
bin_op: AtomicRMWBinOp,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
let ty = val.borrow().ty.clone();
let order_val = constants::const_i32(ordering as i32);
let op_val = constants::const_i32(bin_op as i32);
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::AtomicRMW);
v.operands = vec![ptr, val, order_val, op_val];
v.num_operands = 4;
v.subclass_data = bin_op as u32;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_atomic_cmp_xchg(
&mut self,
ptr: ValueRef,
cmp: ValueRef,
new_val: ValueRef,
success_ordering: AtomicOrdering,
failure_ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
let ty = cmp.borrow().ty.clone();
let succ_val = constants::const_i32(success_ordering as i32);
let fail_val = constants::const_i32(failure_ordering as i32);
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::CmpXchg);
v.operands = vec![ptr, cmp, new_val, succ_val, fail_val];
v.num_operands = 5;
v.subclass_data = (success_ordering as u32) << 16 | (failure_ordering as u32);
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_fence(&mut self, ordering: AtomicOrdering) -> ValueRef {
let order_val = constants::const_i32(ordering as i32);
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Fence);
v.operands = vec![order_val];
v.num_operands = 1;
v.subclass_data = ordering as u32;
valref(v)
}
pub fn create_vector_splat(&mut self, num_elems: u32, value: ValueRef, name: &str) -> ValueRef {
let elem_ty = value.borrow().ty.clone();
let vec_ty = Type::fixed_vector_with(num_elems, elem_ty.id);
let mut v = Value::new(vec_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::ShuffleVector);
v.operands = vec![value.clone(), value.clone(), value];
v.num_operands = 3;
v.subclass_data = num_elems; if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_vector_reverse(&mut self, vec: ValueRef, name: &str) -> ValueRef {
let ty = vec.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::ShuffleVector);
v.operands = vec![vec.clone(), vec, constants::const_i32(-1)];
v.num_operands = 3;
v.subclass_data = 1; if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_vector_reduce(
&mut self,
op: VectorReduceOp,
vec: ValueRef,
name: &str,
) -> ValueRef {
let vec_ty = vec.borrow().ty.clone();
let scalar_ty = match &vec_ty.kind {
crate::types::TypeKind::FixedVector {
element_type_id: _, ..
} => {
Type::int(32) }
_ => Type::int(32),
};
let mut v = Value::new(scalar_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let intrinsic_name = format!("llvm.vector.reduce.{}", op.as_str());
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = intrinsic_name;
valref(fv)
};
v.operands = vec![func_val, vec];
v.num_operands = 2;
v.subclass_data = op as u32;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_step_vector(&mut self, ty: Type, name: &str) -> ValueRef {
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let intrinsic_name = "llvm.experimental.stepvector";
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = intrinsic_name.to_string();
valref(fv)
};
v.operands = vec![func_val];
v.num_operands = 1;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_masked_load(
&mut self,
ptr: ValueRef,
align: u32,
mask: ValueRef,
passthru: ValueRef,
name: &str,
) -> ValueRef {
let ty = passthru.borrow().ty.clone();
let align_val = constants::const_i32(align as i32);
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let intrinsic_name = "llvm.masked.load";
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = intrinsic_name.to_string();
valref(fv)
};
v.operands = vec![func_val, ptr, align_val, mask, passthru];
v.num_operands = 5;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_masked_store(
&mut self,
val: ValueRef,
ptr: ValueRef,
align: u32,
mask: ValueRef,
) -> ValueRef {
let align_val = constants::const_i32(align as i32);
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let intrinsic_name = "llvm.masked.store";
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = intrinsic_name.to_string();
valref(fv)
};
v.operands = vec![func_val, val, ptr, align_val, mask];
v.num_operands = 5;
valref(v)
}
pub fn create_masked_gather(
&mut self,
ptrs: ValueRef,
align: u32,
mask: ValueRef,
passthru: ValueRef,
name: &str,
) -> ValueRef {
let ty = passthru.borrow().ty.clone();
let align_val = constants::const_i32(align as i32);
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let intrinsic_name = "llvm.masked.gather";
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = intrinsic_name.to_string();
valref(fv)
};
v.operands = vec![func_val, ptrs, align_val, mask, passthru];
v.num_operands = 5;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_masked_scatter(
&mut self,
vals: ValueRef,
ptrs: ValueRef,
align: u32,
mask: ValueRef,
) -> ValueRef {
let align_val = constants::const_i32(align as i32);
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let intrinsic_name = "llvm.masked.scatter";
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = intrinsic_name.to_string();
valref(fv)
};
v.operands = vec![func_val, vals, ptrs, align_val, mask];
v.num_operands = 5;
valref(v)
}
pub fn create_vector_insert(
&mut self,
vec: ValueRef,
subvec: ValueRef,
idx: u64,
name: &str,
) -> ValueRef {
let ty = vec.borrow().ty.clone();
let idx_val = constants::const_i64(idx as i64);
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::ShuffleVector);
v.operands = vec![vec, subvec, idx_val];
v.num_operands = 3;
v.subclass_data = idx as u32;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_vector_extract(
&mut self,
vec: ValueRef,
num_elems: u32,
idx: u64,
name: &str,
) -> ValueRef {
let vec_ty = vec.borrow().ty.clone();
let result_ty = match &vec_ty.kind {
crate::types::TypeKind::FixedVector {
element_type_id, ..
} => Type::fixed_vector_with(num_elems, *element_type_id),
_ => Type::fixed_vector_with(num_elems, Type::i32().id),
};
let idx_val = constants::const_i64(idx as i64);
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::ShuffleVector);
v.operands = vec![vec, idx_val];
v.num_operands = 2;
v.subclass_data = (num_elems << 16) | (idx as u32 & 0xFFFF);
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_mem_set(
&mut self,
ptr: ValueRef,
val: ValueRef,
size: ValueRef,
align: u32,
is_volatile: bool,
) -> ValueRef {
let align_val = constants::const_i32(align as i32);
let volatile_val = constants::const_bool(is_volatile);
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.memset.p0.i64".to_string();
valref(fv)
};
v.operands = vec![func_val, ptr, val, size, volatile_val, align_val];
v.num_operands = 6;
valref(v)
}
pub fn create_mem_cpy(
&mut self,
dst: ValueRef,
src: ValueRef,
size: ValueRef,
align: u32,
is_volatile: bool,
) -> ValueRef {
let align_val = constants::const_i32(align as i32);
let volatile_val = constants::const_bool(is_volatile);
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.memcpy.p0.p0.i64".to_string();
valref(fv)
};
v.operands = vec![func_val, dst, src, size, volatile_val, align_val];
v.num_operands = 6;
valref(v)
}
pub fn create_mem_move(
&mut self,
dst: ValueRef,
src: ValueRef,
size: ValueRef,
align: u32,
is_volatile: bool,
) -> ValueRef {
let align_val = constants::const_i32(align as i32);
let volatile_val = constants::const_bool(is_volatile);
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.memmove.p0.p0.i64".to_string();
valref(fv)
};
v.operands = vec![func_val, dst, src, size, volatile_val, align_val];
v.num_operands = 6;
valref(v)
}
pub fn create_mem_set_inline(
&mut self,
ptr: ValueRef,
val: ValueRef,
size: ValueRef,
align: u32,
is_volatile: bool,
) -> ValueRef {
let align_val = constants::const_i32(align as i32);
let volatile_val = constants::const_bool(is_volatile);
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.memset.inline.p0.i64".to_string();
valref(fv)
};
v.operands = vec![func_val, ptr, val, size, volatile_val, align_val];
v.num_operands = 6;
valref(v)
}
pub fn create_lifetime_start(&mut self, ptr: ValueRef, size: ValueRef) -> ValueRef {
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.lifetime.start.p0".to_string();
valref(fv)
};
v.operands = vec![func_val, size, ptr];
v.num_operands = 3;
valref(v)
}
pub fn create_lifetime_end(&mut self, ptr: ValueRef, size: ValueRef) -> ValueRef {
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.lifetime.end.p0".to_string();
valref(fv)
};
v.operands = vec![func_val, size, ptr];
v.num_operands = 3;
valref(v)
}
pub fn create_invariant_start(&mut self, ptr: ValueRef, size: ValueRef) -> ValueRef {
let ty = Type::pointer(0);
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.invariant.start.p0".to_string();
valref(fv)
};
v.operands = vec![func_val, size, ptr];
v.num_operands = 3;
valref(v)
}
pub fn create_invariant_end(
&mut self,
token: ValueRef,
ptr: ValueRef,
size: ValueRef,
) -> ValueRef {
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.invariant.end.p0".to_string();
valref(fv)
};
v.operands = vec![func_val, token, size, ptr];
v.num_operands = 4;
valref(v)
}
pub fn create_launder_invariant_group(&mut self, ptr: ValueRef) -> ValueRef {
let ty = ptr.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.launder.invariant.group.p0".to_string();
valref(fv)
};
v.operands = vec![func_val, ptr];
v.num_operands = 2;
valref(v)
}
pub fn create_strip_invariant_group(&mut self, ptr: ValueRef) -> ValueRef {
let ty = ptr.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.strip.invariant.group.p0".to_string();
valref(fv)
};
v.operands = vec![func_val, ptr];
v.num_operands = 2;
valref(v)
}
pub fn create_unary_intrinsic(
&mut self,
intrinsic: crate::ir_builder::UnaryIntrinsic,
operand: ValueRef,
name: &str,
) -> ValueRef {
let ty = operand.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = intrinsic.intrinsic_name().to_string();
valref(fv)
};
v.operands = vec![func_val, operand];
v.num_operands = 2;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_binary_intrinsic(
&mut self,
intrinsic: crate::ir_builder::BinaryIntrinsic,
op1: ValueRef,
op2: ValueRef,
name: &str,
) -> ValueRef {
let ty = op1.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = intrinsic.intrinsic_name().to_string();
valref(fv)
};
v.operands = vec![func_val, op1, op2];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_coro_id(
&mut self,
align: u32,
promise: ValueRef,
coroaddr: ValueRef,
fnaddrs: ValueRef,
) -> ValueRef {
let align_val = constants::const_i32(align as i32);
let mut v = Value::new(Type::token()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.coro.id".to_string();
valref(fv)
};
v.operands = vec![func_val, align_val, promise, coroaddr, fnaddrs];
v.num_operands = 5;
valref(v)
}
pub fn create_coro_alloc(&mut self, coro_id: ValueRef) -> ValueRef {
let mut v = Value::new(Type::i1()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.coro.alloc".to_string();
valref(fv)
};
v.operands = vec![func_val, coro_id];
v.num_operands = 2;
valref(v)
}
pub fn create_coro_begin(&mut self, coro_id: ValueRef, mem: ValueRef) -> ValueRef {
let mut v = Value::new(Type::pointer(0)).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.coro.begin".to_string();
valref(fv)
};
v.operands = vec![func_val, coro_id, mem];
v.num_operands = 3;
valref(v)
}
pub fn create_coro_free(&mut self, coro_id: ValueRef, frame: ValueRef) -> ValueRef {
let mut v = Value::new(Type::pointer(0)).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.coro.free".to_string();
valref(fv)
};
v.operands = vec![func_val, coro_id, frame];
v.num_operands = 3;
valref(v)
}
pub fn create_coro_end(&mut self, frame: ValueRef, unwind: bool) -> ValueRef {
let unwind_val = constants::const_bool(unwind);
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.coro.end".to_string();
valref(fv)
};
v.operands = vec![func_val, frame, unwind_val];
v.num_operands = 3;
valref(v)
}
pub fn create_coro_frame(&mut self) -> ValueRef {
let mut v = Value::new(Type::pointer(0)).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.coro.frame".to_string();
valref(fv)
};
v.operands = vec![func_val];
v.num_operands = 1;
valref(v)
}
pub fn create_coro_save(&mut self, frame: ValueRef) -> ValueRef {
let mut v = Value::new(Type::token()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.coro.save".to_string();
valref(fv)
};
v.operands = vec![func_val, frame];
v.num_operands = 2;
valref(v)
}
pub fn create_coro_suspend(&mut self, save_token: ValueRef, is_final: bool) -> ValueRef {
let final_val = constants::const_bool(is_final);
let mut v = Value::new(Type::i8()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.coro.suspend".to_string();
valref(fv)
};
v.operands = vec![func_val, save_token, final_val];
v.num_operands = 3;
valref(v)
}
pub fn create_coro_promise(&mut self, frame: ValueRef, align: u32, from: bool) -> ValueRef {
let align_val = constants::const_i32(align as i32);
let from_val = constants::const_bool(from);
let mut v = Value::new(Type::pointer(0)).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.coro.promise".to_string();
valref(fv)
};
v.operands = vec![func_val, frame, align_val, from_val];
v.num_operands = 4;
valref(v)
}
pub fn create_coro_size(&mut self) -> ValueRef {
let mut v = Value::new(Type::i64()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.coro.size.i64".to_string();
valref(fv)
};
v.operands = vec![func_val];
v.num_operands = 1;
valref(v)
}
pub fn create_nsw_add(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Add);
v.operands = vec![lhs, rhs];
v.num_operands = 2;
v.subclass_data = 1; if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_nsw_sub(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Sub);
v.operands = vec![lhs, rhs];
v.num_operands = 2;
v.subclass_data = 1; if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_nsw_mul(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Mul);
v.operands = vec![lhs, rhs];
v.num_operands = 2;
v.subclass_data = 1; if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_nuw_add(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Add);
v.operands = vec![lhs, rhs];
v.num_operands = 2;
v.subclass_data = 2; if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_nuw_sub(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Sub);
v.operands = vec![lhs, rhs];
v.num_operands = 2;
v.subclass_data = 2; if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_nuw_mul(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Mul);
v.operands = vec![lhs, rhs];
v.num_operands = 2;
v.subclass_data = 2; if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_nsw_neg(&mut self, val: ValueRef, name: &str) -> ValueRef {
let ty = val.borrow().ty.clone();
let zero = constants::const_zero(ty.clone());
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Sub);
v.operands = vec![zero, val];
v.num_operands = 2;
v.subclass_data = 1; if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_add_with_overflow(
&mut self,
lhs: ValueRef,
rhs: ValueRef,
name: &str,
) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let result_ty = Type::struct_literal_with(false, vec![ty.id, Type::i1().id]);
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.sadd.with.overflow.i32".to_string();
valref(fv)
};
v.operands = vec![func_val, lhs, rhs];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_sub_with_overflow(
&mut self,
lhs: ValueRef,
rhs: ValueRef,
name: &str,
) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let result_ty = Type::struct_literal_with(false, vec![ty.id, Type::i1().id]);
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.ssub.with.overflow.i32".to_string();
valref(fv)
};
v.operands = vec![func_val, lhs, rhs];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_mul_with_overflow(
&mut self,
lhs: ValueRef,
rhs: ValueRef,
name: &str,
) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let result_ty = Type::struct_literal_with(false, vec![ty.id, Type::i1().id]);
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.smul.with.overflow.i32".to_string();
valref(fv)
};
v.operands = vec![func_val, lhs, rhs];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_sadd_sat(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.sadd.sat.i32".to_string();
valref(fv)
};
v.operands = vec![func_val, lhs, rhs];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_uadd_sat(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.uadd.sat.i32".to_string();
valref(fv)
};
v.operands = vec![func_val, lhs, rhs];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_ssub_sat(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.ssub.sat.i32".to_string();
valref(fv)
};
v.operands = vec![func_val, lhs, rhs];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_usub_sat(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.usub.sat.i32".to_string();
valref(fv)
};
v.operands = vec![func_val, lhs, rhs];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_sshl_sat(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.sshl.sat.i32".to_string();
valref(fv)
};
v.operands = vec![func_val, lhs, rhs];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_ushl_sat(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.ushl.sat.i32".to_string();
valref(fv)
};
v.operands = vec![func_val, lhs, rhs];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_sadd_o(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.create_add_with_overflow(lhs, rhs, name)
}
pub fn create_uadd_o(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let result_ty = Type::struct_literal_with(false, vec![ty.id, Type::i1().id]);
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.uadd.with.overflow.i32".to_string();
valref(fv)
};
v.operands = vec![func_val, lhs, rhs];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_ssub_o(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.create_sub_with_overflow(lhs, rhs, name)
}
pub fn create_usub_o(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let result_ty = Type::struct_literal_with(false, vec![ty.id, Type::i1().id]);
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.usub.with.overflow.i32".to_string();
valref(fv)
};
v.operands = vec![func_val, lhs, rhs];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn create_smul_o(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.create_mul_with_overflow(lhs, rhs, name)
}
pub fn create_umul_o(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let result_ty = Type::struct_literal_with(false, vec![ty.id, Type::i1().id]);
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.umul.with.overflow.i32".to_string();
valref(fv)
};
v.operands = vec![func_val, lhs, rhs];
v.num_operands = 3;
if !name.is_empty() {
v.name = name.to_string();
}
valref(v)
}
pub fn get_int1_ty(&self) -> Type {
Type::i1()
}
pub fn get_int8_ty(&self) -> Type {
Type::i8()
}
pub fn get_int16_ty(&self) -> Type {
Type::i16()
}
pub fn get_int32_ty(&self) -> Type {
Type::i32()
}
pub fn get_int64_ty(&self) -> Type {
Type::i64()
}
pub fn get_int128_ty(&self) -> Type {
Type::i128()
}
pub fn get_float_ty(&self) -> Type {
Type::float()
}
pub fn get_double_ty(&self) -> Type {
Type::double()
}
pub fn get_void_ty(&self) -> Type {
Type::void()
}
pub fn get_int_ty(&self, num_bits: u32) -> Type {
Type::int(num_bits)
}
pub fn get_ptr_ty(&self) -> Type {
Type::pointer(0)
}
pub fn get_int_ptr_ty(&self) -> Type {
Type::i64()
}
pub fn set_fast_math_flags(&mut self, flags: FastMathFlags) {
self.fast_math_flags = flags;
}
pub fn get_fast_math_flags(&self) -> FastMathFlags {
self.fast_math_flags
}
pub fn clear_fast_math_flags(&mut self) {
self.fast_math_flags = FastMathFlags::default();
}
pub fn set_current_debug_location_2(&mut self, loc: crate::constants::DebugLoc) {
self.debug_loc = Some(loc);
}
pub fn get_current_debug_location_2(&self) -> Option<&crate::constants::DebugLoc> {
self.debug_loc.as_ref()
}
pub fn set_default_fp_math_tag(&mut self, _tag: Option<ValueRef>) {
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::opcode::{FCmpPred, ICmpPred};
#[test]
fn test_builder_new() {
let builder = IRBuilder::default();
assert!(!builder.has_insert_point());
assert!(!builder.is_in_function());
assert!(builder.get_insert_block().is_none());
}
#[test]
fn test_builder_set_insert_point() {
let mut builder = IRBuilder::default();
let bb = builder.create_basic_block("test");
builder.set_insert_point_to_end(&bb);
assert!(builder.has_insert_point());
assert_eq!(builder.get_insert_position(), Some(usize::MAX));
}
#[test]
fn test_builder_clear_insertion_point() {
let mut builder = IRBuilder::default();
let bb = builder.create_basic_block("test");
builder.set_insert_point_to_end(&bb);
builder.clear_insertion_point();
assert!(!builder.has_insert_point());
}
#[test]
fn test_create_ret_void() {
let mut builder = IRBuilder::default();
let inst = builder.create_ret_void();
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_create_ret_val() {
let mut builder = IRBuilder::default();
let val = builder.get_int32(42);
let inst = builder.create_ret(&val);
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_create_br() {
let mut builder = IRBuilder::default();
let dest = builder.create_basic_block("dest");
let inst = builder.create_br(&dest);
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_create_cond_br() {
let mut builder = IRBuilder::default();
let cond = builder.get_bool(true);
let then_bb = builder.create_basic_block("then");
let else_bb = builder.create_basic_block("else");
let inst = builder.create_cond_br(&cond, &then_bb, &else_bb);
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_create_unreachable() {
let mut builder = IRBuilder::default();
let inst = builder.create_unreachable();
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_create_add() {
let mut builder = IRBuilder::default();
let a = builder.get_int32(10);
let b = builder.get_int32(20);
let inst = builder.create_add(a, b, "sum");
assert!(inst.borrow().is_instruction());
assert_eq!(inst.borrow().name, "sum");
}
#[test]
fn test_create_sub() {
let mut builder = IRBuilder::default();
let a = builder.get_int32(50);
let b = builder.get_int32(20);
let inst = builder.create_sub(a, b, "diff");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_create_mul() {
let mut builder = IRBuilder::default();
let a = builder.get_int32(6);
let b = builder.get_int32(7);
let inst = builder.create_mul(a, b, "prod");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_create_alloca() {
let mut builder = IRBuilder::default();
let inst = builder.create_alloca(Type::i32(), "ptr");
assert!(inst.borrow().is_instruction());
assert_eq!(inst.borrow().name, "ptr");
}
#[test]
fn test_create_load() {
let mut builder = IRBuilder::default();
let alloca = builder.create_alloca(Type::i32(), "ptr");
let inst = builder.create_load(Type::i32(), alloca, "val");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_create_store() {
let mut builder = IRBuilder::default();
let alloca = builder.create_alloca(Type::i32(), "ptr");
let val = builder.get_int32(42);
let inst = builder.create_store(val, alloca);
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_create_icmp() {
let mut builder = IRBuilder::default();
let a = builder.get_int32(10);
let b = builder.get_int32(20);
let inst = builder.create_icmp(ICmpPred::Slt, a, b, "cmp");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_create_call() {
let mut builder = IRBuilder::default();
let func = builder.create_function("callee", Type::void(), &[]);
let inst = builder.create_call(Type::void(), func, vec![], "call");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_create_phi() {
let mut builder = IRBuilder::default();
let bb1 = builder.create_basic_block("bb1");
let bb2 = builder.create_basic_block("bb2");
let v1 = builder.get_int32(1);
let v2 = builder.get_int32(2);
let inst = builder.create_phi(Type::i32(), vec![(v1, bb1), (v2, bb2)], "phi");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_get_int_constants() {
let builder = IRBuilder::default();
let v32 = builder.get_int32(42);
assert_eq!(v32.borrow().name, "42");
let v64 = builder.get_int64(-1);
assert_eq!(v64.borrow().name, "-1");
let vbool = builder.get_bool(true);
assert_eq!(vbool.borrow().name, "1");
let vfloat = builder.get_float(3.14);
assert_eq!(vfloat.borrow().name, "3.14");
}
#[test]
fn test_builder_new_with_context() {
let ctx = crate::context::LLVMContext::new();
let builder = IRBuilder::new(&ctx);
assert!(!builder.has_insert_point());
}
#[test]
fn test_builder_set_insert_point_start() {
let mut builder = IRBuilder::default();
let bb = builder.create_basic_block("test");
builder.set_insert_point_to_start(&bb);
assert_eq!(builder.get_insert_position(), Some(0));
}
#[test]
fn test_builder_set_current_function() {
let mut builder = IRBuilder::default();
let func = builder.create_function("my_func", Type::void(), &[]);
builder.set_current_function(func.clone());
assert!(builder.is_in_function());
}
#[test]
fn test_builder_create_switch() {
let mut builder = IRBuilder::default();
let val = builder.get_int32(1);
let default_bb = builder.create_basic_block("default");
let case_bb = builder.create_basic_block("case1");
let inst = builder.create_switch(&val, &default_bb, &[(builder.get_int32(1), case_bb)]);
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_udiv() {
let mut builder = IRBuilder::default();
let a = builder.get_int32(100);
let b = builder.get_int32(3);
let inst = builder.create_udiv(a, b, "quot");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_sdiv() {
let mut builder = IRBuilder::default();
let a = builder.get_int32(100);
let b = builder.get_int32(3);
let inst = builder.create_sdiv(a, b, "quot");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_urem() {
let mut builder = IRBuilder::default();
let a = builder.get_int32(100);
let b = builder.get_int32(7);
let inst = builder.create_urem(a, b, "rem");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_srem() {
let mut builder = IRBuilder::default();
let a = builder.get_int32(100);
let b = builder.get_int32(7);
let inst = builder.create_srem(a, b, "rem");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_fadd() {
let mut builder = IRBuilder::default();
let a = builder.get_f64(1.5);
let b = builder.get_f64(2.5);
let inst = builder.create_fadd(a, b, "fsum");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_fsub() {
let mut builder = IRBuilder::default();
let a = builder.get_f64(10.0);
let b = builder.get_f64(3.0);
let inst = builder.create_fsub(a, b, "fdiff");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_fmul() {
let mut builder = IRBuilder::default();
let a = builder.get_f64(2.0);
let b = builder.get_f64(3.5);
let inst = builder.create_fmul(a, b, "fprod");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_fdiv() {
let mut builder = IRBuilder::default();
let a = builder.get_f64(10.0);
let b = builder.get_f64(2.0);
let inst = builder.create_fdiv(a, b, "fquot");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_frem() {
let mut builder = IRBuilder::default();
let a = builder.get_f64(10.5);
let b = builder.get_f64(3.0);
let inst = builder.create_frem(a, b, "frem");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_shl() {
let mut builder = IRBuilder::default();
let a = builder.get_int32(1);
let b = builder.get_int32(3);
let inst = builder.create_shl(a, b, "shifted");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_lshr() {
let mut builder = IRBuilder::default();
let a = builder.get_int32(16);
let b = builder.get_int32(2);
let inst = builder.create_lshr(a, b, "shr");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_ashr() {
let mut builder = IRBuilder::default();
let a = builder.get_int32(-16);
let b = builder.get_int32(2);
let inst = builder.create_ashr(a, b, "sar");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_and_or_xor() {
let mut builder = IRBuilder::default();
let a = builder.get_int32(0xFF);
let b = builder.get_int32(0x0F);
let and = builder.create_and(a.clone(), b.clone(), "and");
assert!(and.borrow().is_instruction());
let or = builder.create_or(a.clone(), b.clone(), "or");
assert!(or.borrow().is_instruction());
let xor = builder.create_xor(a, b, "xor");
assert!(xor.borrow().is_instruction());
}
#[test]
fn test_builder_create_gep() {
let mut builder = IRBuilder::default();
let ptr = builder.get_null(Type::pointer(0));
let idx = builder.get_int32(0);
let inst = builder.create_gep(ptr, vec![idx], "gep");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_inbounds_gep() {
let mut builder = IRBuilder::default();
let ptr = builder.get_null(Type::pointer(0));
let idx = builder.get_int32(0);
let inst = builder.create_inbounds_gep(ptr, vec![idx], "gep");
assert!(inst.borrow().is_instruction());
assert!(inst.borrow().name.contains("inbounds"));
}
#[test]
fn test_builder_create_cast_ops() {
let mut builder = IRBuilder::default();
let val = builder.get_int32(42);
let trunc = builder.create_trunc(val.clone(), Type::i8(), "tr");
assert!(trunc.borrow().is_instruction());
let zext = builder.create_zext(val.clone(), Type::i64(), "zx");
assert!(zext.borrow().is_instruction());
let sext = builder.create_sext(val.clone(), Type::i64(), "sx");
assert!(sext.borrow().is_instruction());
let bitcast = builder.create_bitcast(val, Type::float(), "bc");
assert!(bitcast.borrow().is_instruction());
}
#[test]
fn test_builder_create_fp_cast_ops() {
let mut builder = IRBuilder::default();
let fval = builder.get_f64(3.14);
let fptrunc = builder.create_fptrunc(fval.clone(), Type::float(), "fp");
assert!(fptrunc.borrow().is_instruction());
let fpext = builder.create_fpext(fval.clone(), Type::double(), "fe");
assert!(fpext.borrow().is_instruction());
let fptoui = builder.create_fptoui(fval.clone(), Type::i32(), "fu");
assert!(fptoui.borrow().is_instruction());
let fptosi = builder.create_fptosi(fval.clone(), Type::i32(), "fs");
assert!(fptosi.borrow().is_instruction());
}
#[test]
fn test_builder_create_int_to_fp_cast_ops() {
let mut builder = IRBuilder::default();
let ival = builder.get_int32(42);
let uitofp = builder.create_uitofp(ival.clone(), Type::double(), "uf");
assert!(uitofp.borrow().is_instruction());
let sitofp = builder.create_sitofp(ival, Type::double(), "sf");
assert!(sitofp.borrow().is_instruction());
}
#[test]
fn test_builder_create_addrspacecast() {
let mut builder = IRBuilder::default();
let ptr = builder.get_null(Type::pointer(0));
let inst = builder.create_addrspacecast(ptr, Type::pointer(1), "asc");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_fcmp() {
let mut builder = IRBuilder::default();
let a = builder.get_f64(1.0);
let b = builder.get_f64(2.0);
let inst = builder.create_fcmp(FCmpPred::Olt, a, b, "fcmp");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_select() {
let mut builder = IRBuilder::default();
let cond = builder.get_bool(true);
let tval = builder.get_int32(42);
let fval = builder.get_int32(0);
let inst = builder.create_select(cond, tval, fval, "sel");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_extract_value() {
let mut builder = IRBuilder::default();
let agg = builder.get_undef(Type::struct_named_with("S".to_string(), false, vec![]));
let inst = builder.create_extract_value(agg, 0, "ev");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_insert_value() {
let mut builder = IRBuilder::default();
let agg = builder.get_undef(Type::struct_named_with("S".to_string(), false, vec![]));
let val = builder.get_int32(10);
let inst = builder.create_insert_value(agg, val, 0, "iv");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_extract_element() {
let mut builder = IRBuilder::default();
let vec = builder.get_undef(Type::fixed_vector_with(4, crate::types::Type::i32().id));
let idx = builder.get_int32(0);
let inst = builder.create_extract_element(vec, idx, "ee");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_insert_element() {
let mut builder = IRBuilder::default();
let vec = builder.get_undef(Type::fixed_vector_with(4, crate::types::Type::i32().id));
let elt = builder.get_int32(42);
let idx = builder.get_int32(0);
let inst = builder.create_insert_element(vec, elt, idx, "ie");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_get_helpers() {
let builder = IRBuilder::default();
let i8 = builder.get_int8(5);
assert_eq!(i8.borrow().name, "5");
let f32 = builder.get_f32(1.5);
assert!(f32.borrow().name.contains("1.5"));
let null = builder.get_null(Type::pointer(0));
assert_eq!(null.borrow().name, "null");
let undef = builder.get_undef(Type::i32());
assert_eq!(undef.borrow().name, "undef");
let zero = builder.get_zero(Type::i32());
assert_eq!(zero.borrow().name, "0");
}
#[test]
fn test_builder_debug_location() {
let mut builder = IRBuilder::default();
assert!(builder.get_current_debug_location().is_none());
let loc = crate::constants::DebugLoc {
line: 42,
column: 10,
scope: None,
inlined_at: None,
};
builder.set_current_debug_location(loc);
assert!(builder.get_current_debug_location().is_some());
builder.clear_debug_loc();
assert!(builder.get_current_debug_location().is_none());
}
#[test]
fn test_builder_begin_finish_function() {
let mut builder = IRBuilder::default();
let (func, entry) = builder.begin_function("test_fn", Type::void(), &[]);
assert!(builder.is_in_function());
assert!(builder.has_insert_point());
builder.finish_function();
assert!(!builder.has_insert_point());
assert!(builder.is_in_function());
}
#[test]
fn test_builder_name_generation() {
let mut builder = IRBuilder::default();
let a = builder.get_int32(1);
let b = builder.get_int32(2);
let inst = builder.create_add(a, b, "");
assert!(inst.borrow().is_instruction());
}
#[test]
fn test_builder_create_and_set_block() {
let mut builder = IRBuilder::default();
let bb = builder.create_and_set_block("my_block");
assert!(builder.has_insert_point());
let insert_block = builder.get_insert_block();
assert!(insert_block.is_some());
}
#[test]
fn test_builder_default_new() {
let builder = IRBuilder::default();
assert!(!builder.has_insert_point());
}
}