use llvm_native_core::basic_block;
use llvm_native_core::constants;
use llvm_native_core::context::LLVMContext;
use llvm_native_core::instruction::{self, ICmpPred};
use llvm_native_core::ir_builder::{AtomicOrdering, AtomicRMWBinOp, FastMathFlags, IRBuilder};
use llvm_native_core::opcode::Opcode;
use llvm_native_core::types::Type;
use llvm_native_core::value::{valref, SubclassKind, Value, ValueRef};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RoundingMode {
Dynamic = 0,
Tonearest = 1,
Downward = 2,
Upward = 3,
Towardzero = 4,
TiesAway = 5,
}
impl RoundingMode {
pub fn as_metadata_str(&self) -> &'static str {
match self {
RoundingMode::Dynamic => "fpround.dynamic",
RoundingMode::Tonearest => "fpround.tonearest",
RoundingMode::Downward => "fpround.downward",
RoundingMode::Upward => "fpround.upward",
RoundingMode::Towardzero => "fpround.towardzero",
RoundingMode::TiesAway => "fpround.tonearestaway",
}
}
pub fn as_u32(&self) -> u32 {
*self as u32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FPExceptionBehavior {
Ignore = 0,
Maytrap = 1,
Strict = 2,
}
impl FPExceptionBehavior {
pub fn as_metadata_str(&self) -> &'static str {
match self {
FPExceptionBehavior::Ignore => "fpexcept.ignore",
FPExceptionBehavior::Maytrap => "fpexcept.maytrap",
FPExceptionBehavior::Strict => "fpexcept.strict",
}
}
pub fn as_u32(&self) -> u32 {
*self as u32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TLSModel {
GeneralDynamic = 0,
LocalDynamic = 1,
InitialExec = 2,
LocalExec = 3,
}
impl TLSModel {
pub fn as_str(&self) -> &'static str {
match self {
TLSModel::GeneralDynamic => "general-dynamic",
TLSModel::LocalDynamic => "local-dynamic",
TLSModel::InitialExec => "initial-exec",
TLSModel::LocalExec => "local-exec",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BitManipOp {
BSwap,
CtPop,
CtLz,
CtTz,
BitReverse,
FShl,
FShr,
}
impl BitManipOp {
pub fn intrinsic_name(&self) -> &'static str {
match self {
BitManipOp::BSwap => "llvm.bswap",
BitManipOp::CtPop => "llvm.ctpop",
BitManipOp::CtLz => "llvm.ctlz",
BitManipOp::CtTz => "llvm.cttz",
BitManipOp::BitReverse => "llvm.bitreverse",
BitManipOp::FShl => "llvm.fshl",
BitManipOp::FShr => "llvm.fshr",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FixedPointOp {
SMulFix,
UMulFix,
SDivFix,
UDivFix,
}
impl FixedPointOp {
pub fn intrinsic_name(&self) -> &'static str {
match self {
FixedPointOp::SMulFix => "llvm.smul.fix",
FixedPointOp::UMulFix => "llvm.umul.fix",
FixedPointOp::SDivFix => "llvm.sdiv.fix",
FixedPointOp::UDivFix => "llvm.udiv.fix",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum OverflowOp {
SAddO,
UAddO,
SSubO,
USubO,
SMulO,
UMulO,
}
impl OverflowOp {
pub fn intrinsic_name(&self) -> &'static str {
match self {
OverflowOp::SAddO => "llvm.sadd.with.overflow",
OverflowOp::UAddO => "llvm.uadd.with.overflow",
OverflowOp::SSubO => "llvm.ssub.with.overflow",
OverflowOp::USubO => "llvm.usub.with.overflow",
OverflowOp::SMulO => "llvm.smul.with.overflow",
OverflowOp::UMulO => "llvm.umul.with.overflow",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SaturatingOp {
SAddSat,
UAddSat,
SSubSat,
USubSat,
SShlSat,
UShlSat,
}
impl SaturatingOp {
pub fn intrinsic_name(&self) -> &'static str {
match self {
SaturatingOp::SAddSat => "llvm.sadd.sat",
SaturatingOp::UAddSat => "llvm.uadd.sat",
SaturatingOp::SSubSat => "llvm.ssub.sat",
SaturatingOp::USubSat => "llvm.usub.sat",
SaturatingOp::SShlSat => "llvm.sshl.sat",
SaturatingOp::UShlSat => "llvm.ushl.sat",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MathIntrinsicOp {
Sqrt,
Sin,
Cos,
Pow,
Exp,
Exp2,
Log,
Log10,
Log2,
FMA,
Floor,
Ceil,
Trunc,
Round,
RoundEven,
FAbs,
FMin,
FMax,
Minimum,
Maximum,
CopySign,
LdExp,
}
impl MathIntrinsicOp {
pub fn intrinsic_name(&self) -> &'static str {
match self {
MathIntrinsicOp::Sqrt => "llvm.sqrt",
MathIntrinsicOp::Sin => "llvm.sin",
MathIntrinsicOp::Cos => "llvm.cos",
MathIntrinsicOp::Pow => "llvm.pow",
MathIntrinsicOp::Exp => "llvm.exp",
MathIntrinsicOp::Exp2 => "llvm.exp2",
MathIntrinsicOp::Log => "llvm.log",
MathIntrinsicOp::Log10 => "llvm.log10",
MathIntrinsicOp::Log2 => "llvm.log2",
MathIntrinsicOp::FMA => "llvm.fma",
MathIntrinsicOp::Floor => "llvm.floor",
MathIntrinsicOp::Ceil => "llvm.ceil",
MathIntrinsicOp::Trunc => "llvm.trunc",
MathIntrinsicOp::Round => "llvm.round",
MathIntrinsicOp::RoundEven => "llvm.roundeven",
MathIntrinsicOp::FAbs => "llvm.fabs",
MathIntrinsicOp::FMin => "llvm.minnum",
MathIntrinsicOp::FMax => "llvm.maxnum",
MathIntrinsicOp::Minimum => "llvm.minimum",
MathIntrinsicOp::Maximum => "llvm.maximum",
MathIntrinsicOp::CopySign => "llvm.copysign",
MathIntrinsicOp::LdExp => "llvm.ldexp",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ConstrainedFPOp {
FAdd,
FSub,
FMul,
FDiv,
FRem,
FMA,
}
impl ConstrainedFPOp {
pub fn intrinsic_name(&self) -> &'static str {
match self {
ConstrainedFPOp::FAdd => "llvm.experimental.constrained.fadd",
ConstrainedFPOp::FSub => "llvm.experimental.constrained.fsub",
ConstrainedFPOp::FMul => "llvm.experimental.constrained.fmul",
ConstrainedFPOp::FDiv => "llvm.experimental.constrained.fdiv",
ConstrainedFPOp::FRem => "llvm.experimental.constrained.frem",
ConstrainedFPOp::FMA => "llvm.experimental.constrained.fma",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MatrixOp {
ColumnMajorLoad,
ColumnMajorStore,
Multiply,
}
impl MatrixOp {
pub fn intrinsic_name(&self) -> &'static str {
match self {
MatrixOp::ColumnMajorLoad => "llvm.matrix.column.major.load",
MatrixOp::ColumnMajorStore => "llvm.matrix.column.major.store",
MatrixOp::Multiply => "llvm.matrix.multiply",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HWLoopOp {
SetLoopIterations,
LoopDecrementReg,
LoopDecrement,
}
impl HWLoopOp {
pub fn intrinsic_name(&self) -> &'static str {
match self {
HWLoopOp::SetLoopIterations => "llvm.set.loop.iterations",
HWLoopOp::LoopDecrementReg => "llvm.loop.decrement.reg",
HWLoopOp::LoopDecrement => "llvm.loop.decrement",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GCOp {
Statepoint,
GcResult,
GcRelocate,
}
impl GCOp {
pub fn intrinsic_name(&self) -> &'static str {
match self {
GCOp::Statepoint => "llvm.experimental.gc.statepoint",
GCOp::GcResult => "llvm.experimental.gc.result",
GCOp::GcRelocate => "llvm.experimental.gc.relocate",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CoroOp {
CoroId,
CoroBegin,
CoroEnd,
CoroSuspend,
CoroFree,
CoroAlloc,
CoroFrame,
CoroSave,
CoroPromise,
CoroSize,
}
impl CoroOp {
pub fn intrinsic_name(&self) -> &'static str {
match self {
CoroOp::CoroId => "llvm.coro.id",
CoroOp::CoroBegin => "llvm.coro.begin",
CoroOp::CoroEnd => "llvm.coro.end",
CoroOp::CoroSuspend => "llvm.coro.suspend",
CoroOp::CoroFree => "llvm.coro.free",
CoroOp::CoroAlloc => "llvm.coro.alloc",
CoroOp::CoroFrame => "llvm.coro.frame",
CoroOp::CoroSave => "llvm.coro.save",
CoroOp::CoroPromise => "llvm.coro.promise",
CoroOp::CoroSize => "llvm.coro.size",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DebugIntrinsic {
DbgDeclare,
DbgValue,
DbgLabel,
DbgAddr,
}
impl DebugIntrinsic {
pub fn intrinsic_name(&self) -> &'static str {
match self {
DebugIntrinsic::DbgDeclare => "llvm.dbg.declare",
DebugIntrinsic::DbgValue => "llvm.dbg.value",
DebugIntrinsic::DbgLabel => "llvm.dbg.label",
DebugIntrinsic::DbgAddr => "llvm.dbg.addr",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StackSafetyOp {
StackProtector,
StackSave,
StackRestore,
}
impl StackSafetyOp {
pub fn intrinsic_name(&self) -> &'static str {
match self {
StackSafetyOp::StackProtector => "llvm.stackprotector",
StackSafetyOp::StackSave => "llvm.stacksave",
StackSafetyOp::StackRestore => "llvm.stackrestore",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ShuffleMaskKind {
Broadcast,
Reverse,
Interleave,
Zip,
Unzip,
Custom,
}
#[derive(Debug, Clone)]
pub struct X86IRBuilderDeep {
pub base: IRBuilder,
pub context: LLVMContext,
pub fast_math_flags: FastMathFlags,
pub verbose: bool,
name_counter: u64,
pub preferred_int_width: u32,
pub target_triple_hint: Option<String>,
pub x86_tuning: bool,
}
impl X86IRBuilderDeep {
pub fn new(base: IRBuilder) -> Self {
Self {
context: base.context.clone(),
base,
fast_math_flags: FastMathFlags::default(),
verbose: false,
name_counter: 0,
preferred_int_width: 32,
target_triple_hint: None,
x86_tuning: false,
}
}
pub fn from_context(ctx: &LLVMContext) -> Self {
let base = IRBuilder::new(ctx);
Self::new(base)
}
pub fn new_empty() -> Self {
let base = IRBuilder::new_empty();
Self::new(base)
}
fn next_name(&mut self, prefix: &str) -> String {
let count = self.name_counter;
self.name_counter += 1;
if prefix.is_empty() {
format!("tmp{}", count)
} else {
format!("{}{}", prefix, count)
}
}
fn maybe_name(&mut self, v: &mut Value, name: &str) {
if self.verbose || !name.is_empty() {
v.name = if name.is_empty() {
self.next_name("")
} else {
name.to_string()
};
}
}
pub fn base_mut(&mut self) -> &mut IRBuilder {
&mut self.base
}
pub fn base_ref(&self) -> &IRBuilder {
&self.base
}
pub fn set_insert_block(&mut self, block: ValueRef) {
self.base.current_block = Some(block);
}
pub fn set_debug_loc(&mut self, loc: Option<llvm_native_core::constants::DebugLoc>) {
if let Some(l) = loc {
self.base.set_current_debug_location(l);
} else {
self.base.clear_debug_loc();
}
}
pub fn const_i32(&self, val: i32) -> ValueRef {
constants::const_i32(val)
}
pub fn const_i64(&self, val: i64) -> ValueRef {
constants::const_i64(val)
}
pub fn const_i8(&self, val: i8) -> ValueRef {
constants::const_i8(val)
}
pub fn const_bool(&self, val: bool) -> ValueRef {
constants::const_bool(val)
}
pub fn const_f32(&self, val: f32) -> ValueRef {
constants::const_float(val as f64)
}
pub fn const_f64(&self, val: f64) -> ValueRef {
constants::const_double(val)
}
pub fn const_zero(&self, ty: Type) -> ValueRef {
constants::const_zero(ty)
}
pub fn undef(&self, ty: Type) -> ValueRef {
constants::undef_value(ty)
}
pub fn poison(&self, ty: Type) -> ValueRef {
constants::poison_value(ty)
}
fn create_intrinsic_call(
&mut self,
intrinsic_name: &str,
result_ty: &Type,
args: Vec<ValueRef>,
name: &str,
) -> ValueRef {
let mut v = Value::new(result_ty.clone()).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_name.to_string();
valref(fv)
};
let mut operands = vec![func_val];
operands.extend(args);
v.operands = operands;
v.num_operands = v.operands.len();
self.maybe_name(&mut v, name);
valref(v)
}
fn create_typed_intrinsic(
&mut self,
intrinsic_base: &str,
ty: &Type,
args: Vec<ValueRef>,
name: &str,
) -> ValueRef {
let suffix = match &ty.kind {
llvm_native_core::types::TypeKind::Integer { bits } => format!(".i{}", bits),
llvm_native_core::types::TypeKind::Float => ".f32".to_string(),
llvm_native_core::types::TypeKind::Double => ".f64".to_string(),
_ => "".to_string(),
};
let full_name = format!("{}{}", intrinsic_base, suffix);
self.create_intrinsic_call(&full_name, ty, args, name)
}
pub fn build_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;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_atomic_xchg(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::Xchg, ptr, val, ordering, name)
}
pub fn build_atomic_add(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::Add, ptr, val, ordering, name)
}
pub fn build_atomic_sub(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::Sub, ptr, val, ordering, name)
}
pub fn build_atomic_and(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::And, ptr, val, ordering, name)
}
pub fn build_atomic_nand(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::Nand, ptr, val, ordering, name)
}
pub fn build_atomic_or(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::Or, ptr, val, ordering, name)
}
pub fn build_atomic_xor(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::Xor, ptr, val, ordering, name)
}
pub fn build_atomic_max(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::Max, ptr, val, ordering, name)
}
pub fn build_atomic_min(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::Min, ptr, val, ordering, name)
}
pub fn build_atomic_umax(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::UMax, ptr, val, ordering, name)
}
pub fn build_atomic_umin(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::UMin, ptr, val, ordering, name)
}
pub fn build_atomic_fadd(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::FAdd, ptr, val, ordering, name)
}
pub fn build_atomic_fsub(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::FSub, ptr, val, ordering, name)
}
pub fn build_atomic_fmax(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::FMax, ptr, val, ordering, name)
}
pub fn build_atomic_fmin(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::FMin, ptr, val, ordering, name)
}
pub fn build_atomic_uinc_wrap(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::UIncWrap, ptr, val, ordering, name)
}
pub fn build_atomic_udec_wrap(
&mut self,
ptr: ValueRef,
val: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(AtomicRMWBinOp::UDecWrap, ptr, val, ordering, name)
}
pub fn build_cmpxchg(
&mut self,
ptr: ValueRef,
cmp: ValueRef,
new_val: ValueRef,
success_ordering: AtomicOrdering,
failure_ordering: AtomicOrdering,
weak: bool,
name: &str,
) -> ValueRef {
let ty = cmp.borrow().ty.clone();
let result_ty = Type::struct_literal_with(false, vec![ty.id, Type::i1().id]);
let succ_val = constants::const_i32(success_ordering as i32);
let fail_val = constants::const_i32(failure_ordering as i32);
let weak_val = constants::const_bool(weak);
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::CmpXchg);
v.operands = vec![ptr, cmp, new_val, succ_val, fail_val, weak_val];
v.num_operands = 6;
v.subclass_data =
(success_ordering as u32) << 16 | (failure_ordering as u32) | ((weak as u32) << 8);
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_cmpxchg_loop(
&mut self,
ptr: ValueRef,
cmp: ValueRef,
new_val: ValueRef,
success_ordering: AtomicOrdering,
failure_ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
let loop_head = basic_block::new_basic_block("loop_head");
let loop_body = basic_block::new_basic_block("loop_body");
let loop_exit = basic_block::new_basic_block("loop_exit");
instruction::br(loop_head.clone());
let cmpxchg_result = self.build_cmpxchg(
ptr.clone(),
cmp.clone(),
new_val.clone(),
success_ordering,
failure_ordering,
true, &format!("{}_weak_cmpxchg", name),
);
let old_val = self.build_extract_value(cmpxchg_result.clone(), 0, &format!("{}_old", name));
let success_flag =
self.build_extract_value(cmpxchg_result.clone(), 1, &format!("{}_ok", name));
let _one = constants::const_bool(true);
instruction::br_cond(success_flag, loop_exit.clone(), loop_body.clone());
let _updated_cmp = old_val.clone(); instruction::br(loop_head);
old_val
}
pub fn build_cmpxchg_strong(
&mut self,
ptr: ValueRef,
cmp: ValueRef,
new_val: ValueRef,
success_ordering: AtomicOrdering,
failure_ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_cmpxchg(
ptr,
cmp,
new_val,
success_ordering,
failure_ordering,
false,
name,
)
}
pub fn build_cmpxchg_weak(
&mut self,
ptr: ValueRef,
cmp: ValueRef,
new_val: ValueRef,
success_ordering: AtomicOrdering,
failure_ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
self.build_cmpxchg(
ptr,
cmp,
new_val,
success_ordering,
failure_ordering,
true,
name,
)
}
pub fn build_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 build_seq_cst_fence(&mut self) -> ValueRef {
self.build_fence(AtomicOrdering::SeqCst)
}
pub fn build_acquire_fence(&mut self) -> ValueRef {
self.build_fence(AtomicOrdering::Acquire)
}
pub fn build_release_fence(&mut self) -> ValueRef {
self.build_fence(AtomicOrdering::Release)
}
pub fn build_atomic_load(
&mut self,
ptr: ValueRef,
ordering: AtomicOrdering,
name: &str,
) -> ValueRef {
let ptr_ty = ptr.borrow().ty.clone();
let elem_ty = match &ptr_ty.kind {
llvm_native_core::types::TypeKind::Pointer { addr_space: _ } => {
ptr_ty.element_type_id().map(|id| {
let mut t = Type::void();
t.id = id;
t
})
}
_ => None,
};
let result_ty = elem_ty.unwrap_or(Type::i32());
let order_val = constants::const_i32(ordering as i32);
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Load);
v.operands = vec![ptr, order_val];
v.num_operands = 2;
v.subclass_data = ordering as u32;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_atomic_store(
&mut self,
val: ValueRef,
ptr: ValueRef,
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::Store);
v.operands = vec![val, ptr, order_val];
v.num_operands = 3;
v.subclass_data = ordering as u32;
valref(v)
}
pub fn build_seq_cst_load(&mut self, ptr: ValueRef, name: &str) -> ValueRef {
self.build_atomic_load(ptr, AtomicOrdering::SeqCst, name)
}
pub fn build_seq_cst_store(&mut self, val: ValueRef, ptr: ValueRef) -> ValueRef {
self.build_atomic_store(val, ptr, AtomicOrdering::SeqCst)
}
pub fn build_extract_element(&mut self, vector: ValueRef, index: u32, name: &str) -> ValueRef {
let vec_ty = vector.borrow().ty.clone();
let elem_ty = match &vec_ty.kind {
llvm_native_core::types::TypeKind::FixedVector {
element_type_id: eid,
..
} => {
let mut t = Type::void();
t.id = *eid;
t
}
_ => Type::i32(),
};
let idx_val = constants::const_i32(index as i32);
let mut v = Value::new(elem_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::ExtractElement);
v.operands = vec![vector, idx_val];
v.num_operands = 2;
v.subclass_data = index;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_insert_element(
&mut self,
vector: ValueRef,
element: ValueRef,
index: u32,
name: &str,
) -> ValueRef {
let vec_ty = vector.borrow().ty.clone();
let idx_val = constants::const_i32(index as i32);
let mut v = Value::new(vec_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::InsertElement);
v.operands = vec![vector, element, idx_val];
v.num_operands = 3;
v.subclass_data = index;
self.maybe_name(&mut v, name);
valref(v)
}
fn build_shuffle_mask(&self, kind: ShuffleMaskKind, num_elems: u32) -> ValueRef {
let mask_elems = match kind {
ShuffleMaskKind::Broadcast => {
vec![0i32; num_elems as usize]
}
ShuffleMaskKind::Reverse => {
(0..num_elems).map(|i| (num_elems - 1 - i) as i32).collect()
}
ShuffleMaskKind::Interleave => {
let n = num_elems as usize;
let mut v = Vec::with_capacity(n * 2);
for i in 0..n {
v.push(i as i32);
v.push((n + i) as i32);
}
v
}
ShuffleMaskKind::Zip => {
let n = num_elems as usize;
let mut v = Vec::with_capacity(n * 2);
for i in 0..n {
v.push(i as i32);
}
for i in 0..n {
v.push((n + i) as i32);
}
v
}
ShuffleMaskKind::Unzip => {
let n = num_elems as usize;
let mut v = Vec::with_capacity(n);
for i in 0..n {
v.push((2 * i) as i32);
}
v
}
ShuffleMaskKind::Custom => {
vec![0i32; num_elems as usize]
}
};
let mask_ty = Type::fixed_vector_with(mask_elems.len() as u32, Type::i32().id);
let mut mask_val = Value::new(mask_ty).with_subclass(SubclassKind::Constant);
mask_val.subclass_data = 0;
for (_i, &elem) in mask_elems.iter().enumerate() {
mask_val.subclass_extra.push(elem as u64);
}
valref(mask_val)
}
pub fn build_shufflevector(
&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;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_shufflevector_with_kind(
&mut self,
v1: ValueRef,
v2: ValueRef,
kind: ShuffleMaskKind,
num_elems: u32,
name: &str,
) -> ValueRef {
let mask = self.build_shuffle_mask(kind, num_elems);
self.build_shufflevector(v1, v2, mask, name)
}
pub fn build_vector_broadcast(
&mut self,
value: ValueRef,
num_elems: u32,
name: &str,
) -> ValueRef {
let elem_ty = value.borrow().ty.clone();
let vec_ty = Type::fixed_vector_with(num_elems, elem_ty.id);
let undef_vec = {
let mut uv = Value::new(vec_ty.clone()).with_subclass(SubclassKind::Constant);
uv.subclass_data = 0;
valref(uv)
};
let zero = constants::const_i32(0);
let mut v = Value::new(vec_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::InsertElement);
v.operands = vec![undef_vec, value, zero];
v.num_operands = 3;
v.subclass_data = 0;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_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; self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_vector_interleave(
&mut self,
v1: ValueRef,
v2: ValueRef,
num_elems: u32,
name: &str,
) -> ValueRef {
let elem_ty = v1.borrow().ty.clone();
let result_ty = Type::fixed_vector_with(num_elems * 2, elem_ty.id);
let mut masks = Vec::with_capacity((num_elems * 2) as usize);
for i in 0..num_elems {
masks.push(i as i32);
masks.push((num_elems + i) as i32);
}
let mask = {
let mask_ty = Type::fixed_vector_with(num_elems * 2, Type::i32().id);
let mut mv = Value::new(mask_ty).with_subclass(SubclassKind::Constant);
for (_i, &m) in masks.iter().enumerate() {
mv.subclass_extra.push(m as u64);
}
valref(mv)
};
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::ShuffleVector);
v.operands = vec![v1, v2, mask];
v.num_operands = 3;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_vector_zip(
&mut self,
v1: ValueRef,
v2: ValueRef,
num_elems: u32,
name: &str,
) -> ValueRef {
let elem_ty = v1.borrow().ty.clone();
let result_ty = Type::fixed_vector_with(num_elems * 2, elem_ty.id);
let mut masks = Vec::with_capacity((num_elems * 2) as usize);
for i in 0..num_elems {
masks.push(i as i32);
}
for i in 0..num_elems {
masks.push((num_elems + i) as i32);
}
let mask = {
let mask_ty = Type::fixed_vector_with(num_elems * 2, Type::i32().id);
let mut mv = Value::new(mask_ty).with_subclass(SubclassKind::Constant);
for (_i, &m) in masks.iter().enumerate() {
mv.subclass_extra.push(m as u64);
}
valref(mv)
};
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::ShuffleVector);
v.operands = vec![v1, v2, mask];
v.num_operands = 3;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_vector_unzip(&mut self, vec: ValueRef, num_elems: u32, name: &str) -> ValueRef {
let elem_ty = vec.borrow().ty.clone();
let result_ty = Type::fixed_vector_with(num_elems / 2, elem_ty.id);
let masks: Vec<i32> = (0..num_elems / 2).map(|i| (i * 2) as i32).collect();
let mask = {
let mask_ty = Type::fixed_vector_with(num_elems / 2, Type::i32().id);
let mut mv = Value::new(mask_ty).with_subclass(SubclassKind::Constant);
for (_i, &m) in masks.iter().enumerate() {
mv.subclass_extra.push(m as u64);
}
valref(mv)
};
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::ShuffleVector);
let undef_vec = self.undef(Type::fixed_vector_with(num_elems, elem_ty.id));
v.operands = vec![vec, undef_vec, mask];
v.num_operands = 3;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_vector(&mut self, elements: &[ValueRef], name: &str) -> ValueRef {
if elements.is_empty() {
return self.undef(Type::fixed_vector_with(0, Type::i32().id));
}
let elem_ty = elements[0].borrow().ty.clone();
let num_elems = elements.len() as u32;
let vec_ty = Type::fixed_vector_with(num_elems, elem_ty.id);
let mut result = self.undef(vec_ty);
for (i, elem) in elements.iter().enumerate() {
result = self.build_insert_element(result, elem.clone(), i as u32, "");
}
let mut v = result.borrow_mut();
self.maybe_name(&mut v, name);
drop(v);
result
}
pub fn build_extract_value(&mut self, agg: ValueRef, index: u32, name: &str) -> ValueRef {
let agg_ty = agg.borrow().ty.clone();
let field_ty = match &agg_ty.kind {
llvm_native_core::types::TypeKind::Struct {
element_type_ids, ..
} => {
let id = element_type_ids
.get(index as usize)
.copied()
.unwrap_or(Type::i32().id);
let mut t = Type::void();
t.id = id;
t
}
llvm_native_core::types::TypeKind::Array {
element_type_id, ..
} => {
let mut t = Type::void();
t.id = *element_type_id;
t
}
_ => Type::i32(),
};
let idx_val = constants::const_i32(index as i32);
let mut v = Value::new(field_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::ExtractValue);
v.operands = vec![agg, idx_val];
v.num_operands = 2;
v.subclass_data = index;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_insert_value(
&mut self,
agg: ValueRef,
element: ValueRef,
index: u32,
name: &str,
) -> ValueRef {
let agg_ty = agg.borrow().ty.clone();
let idx_val = constants::const_i32(index as i32);
let mut v = Value::new(agg_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::InsertValue);
v.operands = vec![agg, element, idx_val];
v.num_operands = 3;
v.subclass_data = index;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_extract_value_nested(
&mut self,
agg: ValueRef,
indices: &[u32],
name: &str,
) -> ValueRef {
let mut current = agg;
for (i, &idx) in indices.iter().enumerate() {
let is_last = i == indices.len() - 1;
let n = if is_last { name } else { "" };
current = self.build_extract_value(current, idx, n);
}
current
}
pub fn build_insert_value_nested(
&mut self,
agg: ValueRef,
element: ValueRef,
indices: &[u32],
name: &str,
) -> ValueRef {
if indices.is_empty() {
return element;
}
if indices.len() == 1 {
return self.build_insert_value(agg, element, indices[0], name);
}
let inner = self.build_extract_value_nested(agg.clone(), &indices[..indices.len() - 1], "");
let updated_inner = self.build_insert_value(inner, element, *indices.last().unwrap(), "");
self.build_insert_value_nested(agg, updated_inner, &indices[..indices.len() - 1], name)
}
pub fn build_memcpy(
&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);
self.create_intrinsic_call(
"llvm.memcpy.p0.p0.i64",
&Type::void(),
vec![dst, src, size, volatile_val, align_val],
"",
)
}
pub fn build_memmove(
&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);
self.create_intrinsic_call(
"llvm.memmove.p0.p0.i64",
&Type::void(),
vec![dst, src, size, volatile_val, align_val],
"",
)
}
pub fn build_memset(
&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);
self.create_intrinsic_call(
"llvm.memset.p0.i64",
&Type::void(),
vec![ptr, val, size, volatile_val, align_val],
"",
)
}
pub fn build_memset_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);
self.create_intrinsic_call(
"llvm.memset.inline.p0.i64",
&Type::void(),
vec![ptr, val, size, volatile_val, align_val],
"",
)
}
pub fn build_lifetime_start(&mut self, ptr: ValueRef, size: ValueRef) -> ValueRef {
self.create_intrinsic_call("llvm.lifetime.start.p0", &Type::void(), vec![size, ptr], "")
}
pub fn build_lifetime_end(&mut self, ptr: ValueRef, size: ValueRef) -> ValueRef {
self.create_intrinsic_call("llvm.lifetime.end.p0", &Type::void(), vec![size, ptr], "")
}
pub fn build_invariant_start(&mut self, ptr: ValueRef, size: ValueRef) -> ValueRef {
let result_ty = Type::pointer(0);
self.create_intrinsic_call("llvm.invariant.start.p0", &result_ty, vec![size, ptr], "")
}
pub fn build_invariant_end(
&mut self,
token: ValueRef,
size: ValueRef,
ptr: ValueRef,
) -> ValueRef {
self.create_intrinsic_call(
"llvm.invariant.end.p0",
&Type::void(),
vec![token, size, ptr],
"",
)
}
pub fn build_launder_invariant_group(&mut self, ptr: ValueRef) -> ValueRef {
let ptr_ty = ptr.borrow().ty.clone();
self.create_intrinsic_call("llvm.launder.invariant.group.p0", &ptr_ty, vec![ptr], "")
}
pub fn build_strip_invariant_group(&mut self, ptr: ValueRef) -> ValueRef {
let ptr_ty = ptr.borrow().ty.clone();
self.create_intrinsic_call("llvm.strip.invariant.group.p0", &ptr_ty, vec![ptr], "")
}
pub fn build_math_intrinsic(
&mut self,
op: MathIntrinsicOp,
args: Vec<ValueRef>,
name: &str,
) -> ValueRef {
let result_ty = if !args.is_empty() {
args[0].borrow().ty.clone()
} else {
Type::double()
};
let intrinsic_name = op.intrinsic_name();
let full_name = match &result_ty.kind {
llvm_native_core::types::TypeKind::Float => format!("{}.f32", intrinsic_name),
llvm_native_core::types::TypeKind::Double => format!("{}.f64", intrinsic_name),
_ => format!("{}.f64", intrinsic_name), };
self.create_intrinsic_call(&full_name, &result_ty, args, name)
}
pub fn build_sqrt(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Sqrt, vec![val], name)
}
pub fn build_sin(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Sin, vec![val], name)
}
pub fn build_cos(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Cos, vec![val], name)
}
pub fn build_pow(&mut self, x: ValueRef, y: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Pow, vec![x, y], name)
}
pub fn build_exp(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Exp, vec![val], name)
}
pub fn build_exp2(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Exp2, vec![val], name)
}
pub fn build_log(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Log, vec![val], name)
}
pub fn build_log10(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Log10, vec![val], name)
}
pub fn build_log2(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Log2, vec![val], name)
}
pub fn build_fma(&mut self, a: ValueRef, b: ValueRef, c: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::FMA, vec![a, b, c], name)
}
pub fn build_floor(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Floor, vec![val], name)
}
pub fn build_ceil(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Ceil, vec![val], name)
}
pub fn build_ftrunc(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Trunc, vec![val], name)
}
pub fn build_fround(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Round, vec![val], name)
}
pub fn build_roundeven(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::RoundEven, vec![val], name)
}
pub fn build_fabs(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::FAbs, vec![val], name)
}
pub fn build_fmin(&mut self, a: ValueRef, b: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::FMin, vec![a, b], name)
}
pub fn build_fmax(&mut self, a: ValueRef, b: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::FMax, vec![a, b], name)
}
pub fn build_minimum(&mut self, a: ValueRef, b: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Minimum, vec![a, b], name)
}
pub fn build_maximum(&mut self, a: ValueRef, b: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::Maximum, vec![a, b], name)
}
pub fn build_copysign(&mut self, mag: ValueRef, sign: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::CopySign, vec![mag, sign], name)
}
pub fn build_ldexp(&mut self, x: ValueRef, exp: ValueRef, name: &str) -> ValueRef {
self.build_math_intrinsic(MathIntrinsicOp::LdExp, vec![x, exp], name)
}
pub fn build_bit_manip(&mut self, op: BitManipOp, args: Vec<ValueRef>, name: &str) -> ValueRef {
let result_ty = if !args.is_empty() {
args[0].borrow().ty.clone()
} else {
Type::i32()
};
let base = op.intrinsic_name();
let full_name = match &result_ty.kind {
llvm_native_core::types::TypeKind::Integer { bits } => format!("{}.i{}", base, bits),
_ => format!("{}.i32", base),
};
self.create_intrinsic_call(&full_name, &result_ty, args, name)
}
pub fn build_bswap(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_bit_manip(BitManipOp::BSwap, vec![val], name)
}
pub fn build_ctpop(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_bit_manip(BitManipOp::CtPop, vec![val], name)
}
pub fn build_ctlz(&mut self, val: ValueRef, is_zero_poison: bool, name: &str) -> ValueRef {
let poison_flag = constants::const_bool(is_zero_poison);
self.build_bit_manip(BitManipOp::CtLz, vec![val, poison_flag], name)
}
pub fn build_cttz(&mut self, val: ValueRef, is_zero_poison: bool, name: &str) -> ValueRef {
let poison_flag = constants::const_bool(is_zero_poison);
self.build_bit_manip(BitManipOp::CtTz, vec![val, poison_flag], name)
}
pub fn build_bitreverse(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_bit_manip(BitManipOp::BitReverse, vec![val], name)
}
pub fn build_fshl(
&mut self,
a: ValueRef,
b: ValueRef,
shamt: ValueRef,
name: &str,
) -> ValueRef {
self.build_bit_manip(BitManipOp::FShl, vec![a, b, shamt], name)
}
pub fn build_fshr(
&mut self,
a: ValueRef,
b: ValueRef,
shamt: ValueRef,
name: &str,
) -> ValueRef {
self.build_bit_manip(BitManipOp::FShr, vec![a, b, shamt], name)
}
pub fn build_overflow_arith(
&mut self,
op: OverflowOp,
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 base = op.intrinsic_name();
let full_name = match &ty.kind {
llvm_native_core::types::TypeKind::Integer { bits } => format!("{}.i{}", base, bits),
_ => format!("{}.i32", base),
};
self.create_intrinsic_call(&full_name, &result_ty, vec![lhs, rhs], name)
}
pub fn build_sadd_overflow(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.build_overflow_arith(OverflowOp::SAddO, lhs, rhs, name)
}
pub fn build_uadd_overflow(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.build_overflow_arith(OverflowOp::UAddO, lhs, rhs, name)
}
pub fn build_ssub_overflow(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.build_overflow_arith(OverflowOp::SSubO, lhs, rhs, name)
}
pub fn build_usub_overflow(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.build_overflow_arith(OverflowOp::USubO, lhs, rhs, name)
}
pub fn build_smul_overflow(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.build_overflow_arith(OverflowOp::SMulO, lhs, rhs, name)
}
pub fn build_umul_overflow(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.build_overflow_arith(OverflowOp::UMulO, lhs, rhs, name)
}
pub fn build_saturating_arith(
&mut self,
op: SaturatingOp,
lhs: ValueRef,
rhs: ValueRef,
name: &str,
) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let base = op.intrinsic_name();
let full_name = match &ty.kind {
llvm_native_core::types::TypeKind::Integer { bits } => format!("{}.i{}", base, bits),
_ => format!("{}.i32", base),
};
self.create_intrinsic_call(&full_name, &ty, vec![lhs, rhs], name)
}
pub fn build_sadd_sat(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.build_saturating_arith(SaturatingOp::SAddSat, lhs, rhs, name)
}
pub fn build_uadd_sat(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.build_saturating_arith(SaturatingOp::UAddSat, lhs, rhs, name)
}
pub fn build_ssub_sat(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.build_saturating_arith(SaturatingOp::SSubSat, lhs, rhs, name)
}
pub fn build_usub_sat(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.build_saturating_arith(SaturatingOp::USubSat, lhs, rhs, name)
}
pub fn build_sshl_sat(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.build_saturating_arith(SaturatingOp::SShlSat, lhs, rhs, name)
}
pub fn build_ushl_sat(&mut self, lhs: ValueRef, rhs: ValueRef, name: &str) -> ValueRef {
self.build_saturating_arith(SaturatingOp::UShlSat, lhs, rhs, name)
}
pub fn build_fixed_point_arith(
&mut self,
op: FixedPointOp,
lhs: ValueRef,
rhs: ValueRef,
scale: u32,
name: &str,
) -> ValueRef {
let ty = lhs.borrow().ty.clone();
let scale_val = constants::const_i32(scale as i32);
let base = op.intrinsic_name();
let full_name = match &ty.kind {
llvm_native_core::types::TypeKind::Integer { bits } => format!("{}.i{}", base, bits),
_ => format!("{}.i32", base),
};
self.create_intrinsic_call(&full_name, &ty, vec![lhs, rhs, scale_val], name)
}
pub fn build_smul_fix(
&mut self,
lhs: ValueRef,
rhs: ValueRef,
scale: u32,
name: &str,
) -> ValueRef {
self.build_fixed_point_arith(FixedPointOp::SMulFix, lhs, rhs, scale, name)
}
pub fn build_umul_fix(
&mut self,
lhs: ValueRef,
rhs: ValueRef,
scale: u32,
name: &str,
) -> ValueRef {
self.build_fixed_point_arith(FixedPointOp::UMulFix, lhs, rhs, scale, name)
}
pub fn build_sdiv_fix(
&mut self,
lhs: ValueRef,
rhs: ValueRef,
scale: u32,
name: &str,
) -> ValueRef {
self.build_fixed_point_arith(FixedPointOp::SDivFix, lhs, rhs, scale, name)
}
pub fn build_udiv_fix(
&mut self,
lhs: ValueRef,
rhs: ValueRef,
scale: u32,
name: &str,
) -> ValueRef {
self.build_fixed_point_arith(FixedPointOp::UDivFix, lhs, rhs, scale, name)
}
pub fn build_get_rounding(&mut self, name: &str) -> ValueRef {
self.create_intrinsic_call("llvm.get.rounding", &Type::i32(), vec![], name)
}
pub fn build_set_rounding(&mut self, rounding_mode: ValueRef) -> ValueRef {
self.create_intrinsic_call("llvm.set.rounding", &Type::void(), vec![rounding_mode], "")
}
pub fn build_clear_fp_exceptions(&mut self) -> ValueRef {
self.create_intrinsic_call("llvm.clear.fp.exceptions", &Type::void(), vec![], "")
}
pub fn build_restore_fp_exceptions(&mut self, mask: ValueRef) -> ValueRef {
self.create_intrinsic_call("llvm.restore.fp.exceptions", &Type::void(), vec![mask], "")
}
pub fn build_constrained_fp(
&mut self,
op: ConstrainedFPOp,
args: Vec<ValueRef>,
rounding_mode: RoundingMode,
exception_behavior: FPExceptionBehavior,
name: &str,
) -> ValueRef {
let result_ty = if !args.is_empty() {
args[0].borrow().ty.clone()
} else {
Type::double()
};
let base = op.intrinsic_name();
let full_name = match &result_ty.kind {
llvm_native_core::types::TypeKind::Float => format!("{}.f32", base),
llvm_native_core::types::TypeKind::Double => format!("{}.f64", base),
_ => format!("{}.f64", base),
};
let rm_val = constants::const_i32(rounding_mode.as_u32() as i32);
let eb_val = constants::const_i32(exception_behavior.as_u32() as i32);
let mut all_args = Vec::new();
all_args.extend(args);
all_args.push(rm_val);
all_args.push(eb_val);
self.create_intrinsic_call(&full_name, &result_ty, all_args, name)
}
pub fn build_constrained_fadd(
&mut self,
a: ValueRef,
b: ValueRef,
rounding: RoundingMode,
except: FPExceptionBehavior,
name: &str,
) -> ValueRef {
self.build_constrained_fp(ConstrainedFPOp::FAdd, vec![a, b], rounding, except, name)
}
pub fn build_constrained_fsub(
&mut self,
a: ValueRef,
b: ValueRef,
rounding: RoundingMode,
except: FPExceptionBehavior,
name: &str,
) -> ValueRef {
self.build_constrained_fp(ConstrainedFPOp::FSub, vec![a, b], rounding, except, name)
}
pub fn build_constrained_fmul(
&mut self,
a: ValueRef,
b: ValueRef,
rounding: RoundingMode,
except: FPExceptionBehavior,
name: &str,
) -> ValueRef {
self.build_constrained_fp(ConstrainedFPOp::FMul, vec![a, b], rounding, except, name)
}
pub fn build_constrained_fdiv(
&mut self,
a: ValueRef,
b: ValueRef,
rounding: RoundingMode,
except: FPExceptionBehavior,
name: &str,
) -> ValueRef {
self.build_constrained_fp(ConstrainedFPOp::FDiv, vec![a, b], rounding, except, name)
}
pub fn build_constrained_frem(
&mut self,
a: ValueRef,
b: ValueRef,
rounding: RoundingMode,
except: FPExceptionBehavior,
name: &str,
) -> ValueRef {
self.build_constrained_fp(ConstrainedFPOp::FRem, vec![a, b], rounding, except, name)
}
pub fn build_constrained_fma(
&mut self,
a: ValueRef,
b: ValueRef,
c: ValueRef,
rounding: RoundingMode,
except: FPExceptionBehavior,
name: &str,
) -> ValueRef {
self.build_constrained_fp(ConstrainedFPOp::FMA, vec![a, b, c], rounding, except, name)
}
pub fn build_matrix_intrinsic(
&mut self,
op: MatrixOp,
args: Vec<ValueRef>,
result_ty: &Type,
name: &str,
) -> ValueRef {
let intrinsic_name = op.intrinsic_name();
self.create_intrinsic_call(intrinsic_name, result_ty, args, name)
}
pub fn build_matrix_column_major_load(
&mut self,
ptr: ValueRef,
rows: u32,
columns: u32,
stride: u32,
name: &str,
) -> ValueRef {
let elem_ty = Type::double(); let matrix_ty = Type::fixed_vector_with(rows * columns, elem_ty.id);
let rows_val = constants::const_i32(rows as i32);
let cols_val = constants::const_i32(columns as i32);
let stride_val = constants::const_i32(stride as i32);
self.build_matrix_intrinsic(
MatrixOp::ColumnMajorLoad,
vec![ptr, rows_val, cols_val, stride_val],
&matrix_ty,
name,
)
}
pub fn build_matrix_column_major_store(
&mut self,
matrix: ValueRef,
ptr: ValueRef,
rows: u32,
columns: u32,
stride: u32,
) -> ValueRef {
let rows_val = constants::const_i32(rows as i32);
let cols_val = constants::const_i32(columns as i32);
let stride_val = constants::const_i32(stride as i32);
self.build_matrix_intrinsic(
MatrixOp::ColumnMajorStore,
vec![matrix, ptr, rows_val, cols_val, stride_val],
&Type::void(),
"",
)
}
pub fn build_matrix_multiply(
&mut self,
a: ValueRef,
b: ValueRef,
m: u32,
n: u32,
k: u32,
name: &str,
) -> ValueRef {
let elem_ty = Type::double();
let result_ty = Type::fixed_vector_with(m * n, elem_ty.id);
let m_val = constants::const_i32(m as i32);
let n_val = constants::const_i32(n as i32);
let k_val = constants::const_i32(k as i32);
self.build_matrix_intrinsic(
MatrixOp::Multiply,
vec![a, b, m_val, n_val, k_val],
&result_ty,
name,
)
}
pub fn build_hw_loop(&mut self, op: HWLoopOp, args: Vec<ValueRef>, name: &str) -> ValueRef {
let intrinsic_name = op.intrinsic_name();
let result_ty = match op {
HWLoopOp::SetLoopIterations => Type::void(),
HWLoopOp::LoopDecrementReg => Type::i1(),
HWLoopOp::LoopDecrement => Type::i1(),
};
self.create_intrinsic_call(intrinsic_name, &result_ty, args, name)
}
pub fn build_set_loop_iterations(&mut self, count: ValueRef) -> ValueRef {
self.build_hw_loop(HWLoopOp::SetLoopIterations, vec![count], "")
}
pub fn build_loop_decrement_reg(&mut self, counter: ValueRef, name: &str) -> ValueRef {
self.build_hw_loop(HWLoopOp::LoopDecrementReg, vec![counter], name)
}
pub fn build_loop_decrement(&mut self, counter: ValueRef, name: &str) -> ValueRef {
self.build_hw_loop(HWLoopOp::LoopDecrement, vec![counter], name)
}
pub fn build_stack_safety(
&mut self,
op: StackSafetyOp,
args: Vec<ValueRef>,
name: &str,
) -> ValueRef {
let intrinsic_name = op.intrinsic_name();
let result_ty = match op {
StackSafetyOp::StackProtector => Type::void(),
StackSafetyOp::StackSave => Type::pointer(0),
StackSafetyOp::StackRestore => Type::void(),
};
self.create_intrinsic_call(intrinsic_name, &result_ty, args, name)
}
pub fn build_stackprotector(&mut self, guard: ValueRef) -> ValueRef {
self.build_stack_safety(StackSafetyOp::StackProtector, vec![guard], "")
}
pub fn build_stacksave(&mut self, name: &str) -> ValueRef {
self.build_stack_safety(StackSafetyOp::StackSave, vec![], name)
}
pub fn build_stackrestore(&mut self, saved_stack: ValueRef) -> ValueRef {
self.build_stack_safety(StackSafetyOp::StackRestore, vec![saved_stack], "")
}
pub fn build_threadlocal_address(
&mut self,
tls_var: ValueRef,
model: TLSModel,
name: &str,
) -> ValueRef {
let ptr_ty = Type::pointer(0);
let model_val = constants::const_i32(model as i32);
let mut v = Value::new(ptr_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.threadlocal.address".to_string();
valref(fv)
};
v.operands = vec![func_val, tls_var, model_val];
v.num_operands = 3;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_tls_general_dynamic(&mut self, tls_var: ValueRef, name: &str) -> ValueRef {
self.build_threadlocal_address(tls_var, TLSModel::GeneralDynamic, name)
}
pub fn build_tls_local_dynamic(&mut self, tls_var: ValueRef, name: &str) -> ValueRef {
self.build_threadlocal_address(tls_var, TLSModel::LocalDynamic, name)
}
pub fn build_tls_initial_exec(&mut self, tls_var: ValueRef, name: &str) -> ValueRef {
self.build_threadlocal_address(tls_var, TLSModel::InitialExec, name)
}
pub fn build_tls_local_exec(&mut self, tls_var: ValueRef, name: &str) -> ValueRef {
self.build_threadlocal_address(tls_var, TLSModel::LocalExec, name)
}
pub fn build_gc_intrinsic(
&mut self,
op: GCOp,
args: Vec<ValueRef>,
result_ty: &Type,
name: &str,
) -> ValueRef {
let intrinsic_name = op.intrinsic_name();
self.create_intrinsic_call(intrinsic_name, result_ty, args, name)
}
pub fn build_gc_statepoint(
&mut self,
call_target: ValueRef,
num_call_args: u32,
flags: u32,
call_args: Vec<ValueRef>,
transition_args: Vec<ValueRef>,
deopt_args: Vec<ValueRef>,
gc_args: Vec<ValueRef>,
name: &str,
) -> ValueRef {
let mut args = Vec::new();
args.push(call_target);
args.push(constants::const_i32(num_call_args as i32));
args.push(constants::const_i32(flags as i32));
args.extend(call_args);
args.push(constants::const_i32(transition_args.len() as i32));
args.extend(transition_args);
args.push(constants::const_i32(deopt_args.len() as i32));
args.extend(deopt_args);
args.push(constants::const_i32(gc_args.len() as i32));
args.extend(gc_args);
let result_ty = Type::token();
self.build_gc_intrinsic(GCOp::Statepoint, args, &result_ty, name)
}
pub fn build_gc_result(
&mut self,
statepoint_token: ValueRef,
result_ty: &Type,
name: &str,
) -> ValueRef {
self.build_gc_intrinsic(GCOp::GcResult, vec![statepoint_token], result_ty, name)
}
pub fn build_gc_relocate(
&mut self,
statepoint_token: ValueRef,
base_offset: u32,
derived_offset: u32,
result_ty: &Type,
name: &str,
) -> ValueRef {
self.build_gc_intrinsic(
GCOp::GcRelocate,
vec![
statepoint_token,
constants::const_i32(base_offset as i32),
constants::const_i32(derived_offset as i32),
],
result_ty,
name,
)
}
pub fn build_coro_intrinsic(
&mut self,
op: CoroOp,
args: Vec<ValueRef>,
result_ty: &Type,
name: &str,
) -> ValueRef {
let intrinsic_name = op.intrinsic_name();
self.create_intrinsic_call(intrinsic_name, result_ty, args, name)
}
pub fn build_coro_id(
&mut self,
align: ValueRef,
promise: ValueRef,
coro: ValueRef,
fn_ptr: ValueRef,
name: &str,
) -> ValueRef {
let result_ty = Type::token();
self.build_coro_intrinsic(
CoroOp::CoroId,
vec![align, promise, coro, fn_ptr],
&result_ty,
name,
)
}
pub fn build_coro_begin(&mut self, id: ValueRef, mem: ValueRef, name: &str) -> ValueRef {
let ptr_ty = Type::pointer(0);
self.build_coro_intrinsic(CoroOp::CoroBegin, vec![id, mem], &ptr_ty, name)
}
pub fn build_coro_end(&mut self, handle: ValueRef, unwind: bool) -> ValueRef {
let unwind_val = constants::const_bool(unwind);
self.build_coro_intrinsic(CoroOp::CoroEnd, vec![handle, unwind_val], &Type::void(), "")
}
pub fn build_coro_suspend(&mut self, save: ValueRef, is_final: bool, name: &str) -> ValueRef {
let final_val = constants::const_bool(is_final);
self.build_coro_intrinsic(
CoroOp::CoroSuspend,
vec![save, final_val],
&Type::i8(),
name,
)
}
pub fn build_coro_free(&mut self, id: ValueRef, name: &str) -> ValueRef {
let ptr_ty = Type::pointer(0);
self.build_coro_intrinsic(CoroOp::CoroFree, vec![id], &ptr_ty, name)
}
pub fn build_coro_alloc(&mut self, id: ValueRef, name: &str) -> ValueRef {
let result_ty = Type::i1();
self.build_coro_intrinsic(CoroOp::CoroAlloc, vec![id], &result_ty, name)
}
pub fn build_coro_frame(&mut self, name: &str) -> ValueRef {
let ptr_ty = Type::pointer(0);
self.build_coro_intrinsic(CoroOp::CoroFrame, vec![], &ptr_ty, name)
}
pub fn build_coro_save(&mut self, handle: ValueRef, name: &str) -> ValueRef {
let result_ty = Type::token();
self.build_coro_intrinsic(CoroOp::CoroSave, vec![handle], &result_ty, name)
}
pub fn build_coro_promise(
&mut self,
ptr: ValueRef,
align: u32,
from: bool,
name: &str,
) -> ValueRef {
let align_val = constants::const_i32(align as i32);
let from_val = constants::const_bool(from);
let ptr_ty = Type::pointer(0);
self.build_coro_intrinsic(
CoroOp::CoroPromise,
vec![ptr, align_val, from_val],
&ptr_ty,
name,
)
}
pub fn build_coro_size(&mut self, name: &str) -> ValueRef {
self.build_coro_intrinsic(CoroOp::CoroSize, vec![], &Type::i32(), name)
}
pub fn build_debug_intrinsic(&mut self, op: DebugIntrinsic, args: Vec<ValueRef>) -> ValueRef {
let intrinsic_name = op.intrinsic_name();
self.create_intrinsic_call(intrinsic_name, &Type::void(), args, "")
}
pub fn build_dbg_declare(
&mut self,
value: ValueRef,
di_variable: ValueRef,
di_expression: ValueRef,
di_location: ValueRef,
) -> ValueRef {
self.build_debug_intrinsic(
DebugIntrinsic::DbgDeclare,
vec![value, di_variable, di_expression, di_location],
)
}
pub fn build_dbg_value(
&mut self,
value: ValueRef,
di_variable: ValueRef,
di_expression: ValueRef,
di_location: ValueRef,
) -> ValueRef {
self.build_debug_intrinsic(
DebugIntrinsic::DbgValue,
vec![value, di_variable, di_expression, di_location],
)
}
pub fn build_dbg_label(&mut self, di_label: ValueRef, di_location: ValueRef) -> ValueRef {
self.build_debug_intrinsic(DebugIntrinsic::DbgLabel, vec![di_label, di_location])
}
pub fn build_dbg_addr(
&mut self,
value: ValueRef,
di_variable: ValueRef,
di_expression: ValueRef,
di_location: ValueRef,
) -> ValueRef {
self.build_debug_intrinsic(
DebugIntrinsic::DbgAddr,
vec![value, di_variable, di_expression, di_location],
)
}
pub fn build_landingpad(
&mut self,
result_ty: &Type,
is_cleanup: bool,
clauses: Vec<ValueRef>,
name: &str,
) -> ValueRef {
let clean_val = constants::const_bool(is_cleanup);
let mut operands = vec![clean_val];
operands.extend(clauses);
let mut v = Value::new(result_ty.clone()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::LandingPad);
v.operands = operands;
v.num_operands = v.operands.len();
v.subclass_data = if is_cleanup { 1 } else { 0 };
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_resume(&mut self, exception: ValueRef) -> ValueRef {
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Resume);
v.operands = vec![exception];
v.num_operands = 1;
valref(v)
}
pub fn build_cleanupret(
&mut self,
cleanup_pad: ValueRef,
unwind_dest: Option<ValueRef>,
) -> ValueRef {
let mut operands = vec![cleanup_pad];
if let Some(dest) = unwind_dest {
operands.push(dest);
}
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::CleanupRet);
v.operands = operands;
v.num_operands = v.operands.len();
valref(v)
}
pub fn build_catchret(&mut self, catch_pad: ValueRef, return_dest: ValueRef) -> ValueRef {
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::CatchRet);
v.operands = vec![catch_pad, return_dest];
v.num_operands = 2;
valref(v)
}
pub fn build_catchswitch(
&mut self,
parent_pad: ValueRef,
unwind_dest: Option<ValueRef>,
handlers: Vec<ValueRef>,
name: &str,
) -> ValueRef {
let mut operands = vec![parent_pad];
if let Some(dest) = unwind_dest {
operands.push(dest);
}
operands.extend(handlers);
let result_ty = Type::token();
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::CatchSwitch);
v.operands = operands;
v.num_operands = v.operands.len();
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_catchpad(
&mut self,
catch_switch: ValueRef,
args: Vec<ValueRef>,
name: &str,
) -> ValueRef {
let mut operands = vec![catch_switch];
operands.extend(args);
let result_ty = Type::token();
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::CatchPad);
v.operands = operands;
v.num_operands = v.operands.len();
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_cleanuppad(
&mut self,
parent_pad: ValueRef,
args: Vec<ValueRef>,
name: &str,
) -> ValueRef {
let mut operands = vec![parent_pad];
operands.extend(args);
let result_ty = Type::token();
let mut v = Value::new(result_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::CleanupPad);
v.operands = operands;
v.num_operands = v.operands.len();
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_invoke(
&mut self,
callee: ValueRef,
args: Vec<ValueRef>,
normal_dest: ValueRef,
unwind_dest: ValueRef,
return_ty: &Type,
name: &str,
) -> ValueRef {
let mut operands = vec![callee, normal_dest, unwind_dest];
operands.extend(args);
let mut v = Value::new(return_ty.clone()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Invoke);
v.operands = operands;
v.num_operands = v.operands.len();
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_freeze(&mut self, val: ValueRef, name: &str) -> ValueRef {
let ty = val.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Freeze);
v.operands = vec![val];
v.num_operands = 1;
self.maybe_name(&mut v, name);
valref(v)
}
}
#[derive(Debug, Clone)]
pub struct X86IRPatternBuilder {
pub builder: X86IRBuilderDeep,
}
impl X86IRPatternBuilder {
pub fn new(builder: X86IRBuilderDeep) -> Self {
Self { builder }
}
pub fn from_context(ctx: &LLVMContext) -> Self {
let builder = X86IRBuilderDeep::from_context(ctx);
Self::new(builder)
}
pub fn deep_builder(&self) -> &X86IRBuilderDeep {
&self.builder
}
pub fn deep_builder_mut(&mut self) -> &mut X86IRBuilderDeep {
&mut self.builder
}
fn create_block(&self) -> ValueRef {
basic_block::new_basic_block("")
}
fn branch_to(&self, dest: ValueRef) {
instruction::br(dest);
}
fn cond_branch(&self, cond: ValueRef, true_dest: ValueRef, false_dest: ValueRef) {
instruction::br_cond(cond, true_dest, false_dest);
}
fn icmp(&self, pred: ICmpPred, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
instruction::icmp(pred, lhs, rhs)
}
pub fn build_if_then_else<F, G>(&mut self, cond: ValueRef, then_body: F, else_body: G)
where
F: FnOnce(&mut X86IRBuilderDeep) -> Option<ValueRef>,
G: FnOnce(&mut X86IRBuilderDeep) -> Option<ValueRef>,
{
let then_block = self.create_block();
let else_block = self.create_block();
let merge_block = self.create_block();
self.cond_branch(cond, then_block.clone(), else_block.clone());
let _then_val = then_body(&mut self.builder);
self.branch_to(merge_block.clone());
let _else_val = else_body(&mut self.builder);
self.branch_to(merge_block.clone());
}
pub fn build_if_then<F>(&mut self, cond: ValueRef, then_body: F)
where
F: FnOnce(&mut X86IRBuilderDeep),
{
let then_block = self.create_block();
let merge_block = self.create_block();
self.cond_branch(cond, then_block.clone(), merge_block.clone());
then_body(&mut self.builder);
self.branch_to(merge_block.clone());
}
pub fn build_while_loop<C, B>(&mut self, cond_builder: C, body_builder: B)
where
C: FnOnce(&mut X86IRBuilderDeep) -> ValueRef,
B: FnOnce(&mut X86IRBuilderDeep),
{
let cond_block = self.create_block();
let body_block = self.create_block();
let exit_block = self.create_block();
self.branch_to(cond_block.clone());
let cond_val = cond_builder(&mut self.builder);
self.cond_branch(cond_val, body_block.clone(), exit_block.clone());
body_builder(&mut self.builder);
self.branch_to(cond_block.clone());
}
pub fn build_for_loop<I, C, B, S>(
&mut self,
init_builder: I,
cond_builder: C,
body_builder: B,
step_builder: S,
) where
I: FnOnce(&mut X86IRBuilderDeep),
C: FnOnce(&mut X86IRBuilderDeep) -> ValueRef,
B: FnOnce(&mut X86IRBuilderDeep),
S: FnOnce(&mut X86IRBuilderDeep),
{
let cond_block = self.create_block();
let body_block = self.create_block();
let step_block = self.create_block();
let exit_block = self.create_block();
init_builder(&mut self.builder);
self.branch_to(cond_block.clone());
let cond_val = cond_builder(&mut self.builder);
self.cond_branch(cond_val, body_block.clone(), exit_block.clone());
body_builder(&mut self.builder);
self.branch_to(step_block.clone());
step_builder(&mut self.builder);
self.branch_to(cond_block.clone());
}
pub fn build_do_while_loop<B, C>(&mut self, body_and_cond: B, exit_cond: C)
where
B: FnOnce(&mut X86IRBuilderDeep),
C: FnOnce(&mut X86IRBuilderDeep) -> ValueRef,
{
let body_block = self.create_block();
let exit_block = self.create_block();
self.branch_to(body_block.clone());
body_and_cond(&mut self.builder);
let cond_val = exit_cond(&mut self.builder);
self.cond_branch(cond_val, body_block.clone(), exit_block.clone());
}
pub fn build_switch<H>(
&mut self,
value: ValueRef, cases: Vec<(i32, H)>,
default_handler: H,
) where
H: FnOnce(&mut X86IRBuilderDeep),
{
let default_block = self.create_block();
let mut case_dests: Vec<(i32, ValueRef)> = Vec::with_capacity(cases.len());
for (case_val, _) in &cases {
let case_block = self.create_block();
case_dests.push((*case_val, case_block));
}
{
let _switch_ty = value.borrow().ty.clone();
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Switch);
let mut operands = vec![value, default_block.clone()];
for (case_val, dest) in &case_dests {
operands.push(constants::const_i32(*case_val));
operands.push(dest.clone());
}
v.operands = operands;
v.num_operands = v.operands.len();
valref(v);
}
for (_, handler) in cases {
handler(&mut self.builder);
}
default_handler(&mut self.builder);
}
pub fn build_call(
&mut self,
callee: ValueRef,
args: &[ValueRef],
return_ty: &Type,
name: &str,
) -> ValueRef {
let mut operands = vec![callee.clone()];
for arg in args {
operands.push(arg.clone());
}
let mut v = Value::new(return_ty.clone()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Call);
v.operands = operands;
v.num_operands = v.operands.len();
self.builder.maybe_name(&mut v, name);
valref(v)
}
pub fn build_global_variable(
&mut self,
name: &str,
ty: &Type,
initializer: ValueRef,
is_constant: bool,
) -> ValueRef {
let mut gv = Value::new(ty.clone()).with_subclass(SubclassKind::GlobalVariable);
gv.name = name.to_string();
gv.operands = vec![initializer];
gv.num_operands = 1;
gv.subclass_data = if is_constant { 1 } else { 0 };
valref(gv)
}
pub fn build_string_global(&mut self, name: &str, value: &str) -> ValueRef {
let chars: Vec<i8> = value.bytes().map(|b| b as i8).collect();
let len = chars.len() as u32;
let arr_ty = Type::array_with((len as u64) + 1, Type::i8().id);
let mut init_val = Value::new(arr_ty.clone()).with_subclass(SubclassKind::Constant);
init_val.name = format!("{}.str", name);
for ch in &chars {
init_val.subclass_extra.push(*ch as u64);
}
init_val.subclass_extra.push(0); let init_ref = valref(init_val);
self.build_global_variable(name, &arr_ty, init_ref, true)
}
pub fn build_struct_gep(&mut self, ptr: ValueRef, indices: &[u32], name: &str) -> ValueRef {
if indices.is_empty() {
return ptr;
}
let ptr_ty = ptr.borrow().ty.clone();
let mut gep_indices: Vec<ValueRef> = Vec::with_capacity(indices.len() + 1);
gep_indices.push(constants::const_i32(0));
for &idx in indices {
gep_indices.push(constants::const_i32(idx as i32));
}
let mut v = Value::new(ptr_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::GetElementPtr);
let mut operands = vec![ptr];
operands.extend(gep_indices);
v.operands = operands;
v.num_operands = v.operands.len();
v.subclass_data = 1; self.builder.maybe_name(&mut v, name);
valref(v)
}
pub fn build_nested_struct_access(
&mut self,
base_ptr: ValueRef,
indices_per_level: &[u32],
name: &str,
) -> ValueRef {
let mut current_ptr = base_ptr;
for (level, &idx) in indices_per_level.iter().enumerate() {
let level_name = if level == indices_per_level.len() - 1 {
name
} else {
""
};
current_ptr = self.build_struct_gep(current_ptr, &[idx], level_name);
}
current_ptr
}
pub fn build_array_gep(
&mut self,
array_ptr: ValueRef,
index: ValueRef,
name: &str,
) -> ValueRef {
let ptr_ty = array_ptr.borrow().ty.clone();
let zero = constants::const_i32(0);
let mut v = Value::new(ptr_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::GetElementPtr);
v.operands = vec![array_ptr, zero, index];
v.num_operands = 3;
v.subclass_data = 1; self.builder.maybe_name(&mut v, name);
valref(v)
}
pub fn build_multi_array_gep(
&mut self,
array_ptr: ValueRef,
indices: &[ValueRef],
name: &str,
) -> ValueRef {
let ptr_ty = array_ptr.borrow().ty.clone();
let zero = constants::const_i32(0);
let mut operands = vec![array_ptr, zero];
for idx in indices {
operands.push(idx.clone());
}
let mut v = Value::new(ptr_ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::GetElementPtr);
v.operands = operands;
v.num_operands = v.operands.len();
v.subclass_data = 1; self.builder.maybe_name(&mut v, name);
valref(v)
}
pub fn build_array_load(
&mut self,
array_ptr: ValueRef,
index: ValueRef,
elem_ty: &Type,
name: &str,
) -> ValueRef {
let gep = self.build_array_gep(array_ptr, index, &format!("{}_gep", name));
let mut v = Value::new(elem_ty.clone()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Load);
v.operands = vec![gep];
v.num_operands = 1;
self.builder.maybe_name(&mut v, name);
valref(v)
}
pub fn build_array_store(
&mut self,
array_ptr: ValueRef,
index: ValueRef,
value: ValueRef,
) -> ValueRef {
let gep = self.build_array_gep(array_ptr, index, "");
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Store);
v.operands = vec![value, gep];
v.num_operands = 2;
valref(v)
}
}
impl X86IRPatternBuilder {
pub fn build_if_then_else_phi<F, G>(
&mut self,
cond: ValueRef,
then_body: F,
else_body: G,
phi_name: &str,
) -> ValueRef
where
F: FnOnce(&mut X86IRBuilderDeep) -> ValueRef,
G: FnOnce(&mut X86IRBuilderDeep) -> ValueRef,
{
let then_block = self.create_block();
let else_block = self.create_block();
let merge_block = self.create_block();
self.cond_branch(cond, then_block.clone(), else_block.clone());
let then_val = then_body(&mut self.builder);
self.branch_to(merge_block.clone());
let else_val = else_body(&mut self.builder);
self.branch_to(merge_block.clone());
let result_ty = then_val.borrow().ty.clone();
let phi = instruction::phi(
result_ty,
vec![(then_val, then_block), (else_val, else_block)],
);
let mut v = phi.borrow_mut();
if !phi_name.is_empty() {
v.name = phi_name.to_string();
}
drop(v);
phi
}
pub fn build_while_loop_accum<C, B>(
&mut self,
init: ValueRef,
cond_builder: C,
body_builder: B,
accum_name: &str,
) -> ValueRef
where
C: FnOnce(&mut X86IRBuilderDeep, ValueRef) -> ValueRef,
B: FnOnce(&mut X86IRBuilderDeep, ValueRef) -> ValueRef,
{
let cond_block = self.create_block();
let body_block = self.create_block();
let exit_block = self.create_block();
self.branch_to(cond_block.clone());
let acc_ty = init.borrow().ty.clone();
let phi = instruction::phi(acc_ty, vec![]);
let mut v = phi.borrow_mut();
if !accum_name.is_empty() {
v.name = accum_name.to_string();
}
drop(v);
let cond_val = cond_builder(&mut self.builder, phi.clone());
self.cond_branch(cond_val, body_block.clone(), exit_block.clone());
let next_val = body_builder(&mut self.builder, phi.clone());
self.branch_to(cond_block.clone());
next_val
}
pub fn build_count_to_n_loop<B>(&mut self, n: ValueRef, body_builder: B)
where
B: FnOnce(&mut X86IRBuilderDeep, ValueRef),
{
let header = self.create_block();
let body_block = self.create_block();
let exit_block = self.create_block();
let _zero = constants::const_i32(0);
let one = constants::const_i32(1);
self.branch_to(header.clone());
let i_ty = n.borrow().ty.clone();
let phi_i = instruction::phi(i_ty, vec![]);
let cond = instruction::icmp(ICmpPred::Slt, phi_i.clone(), n);
self.cond_branch(cond, body_block.clone(), exit_block.clone());
body_builder(&mut self.builder, phi_i.clone());
let _next = instruction::add(phi_i, one);
self.branch_to(header);
}
pub fn build_switch_with_phi<H>(
&mut self,
value: ValueRef,
cases: Vec<(i32, H)>,
default_handler: H,
phi_name: &str,
) -> ValueRef
where
H: FnOnce(&mut X86IRBuilderDeep) -> ValueRef,
{
let default_block = self.create_block();
let merge_block = self.create_block();
let mut case_dests: Vec<(i32, ValueRef)> = Vec::new();
for (case_val, _) in &cases {
let case_block = self.create_block();
case_dests.push((*case_val, case_block));
}
{
let _switch_ty = value.borrow().ty.clone();
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Switch);
let mut operands = vec![value, default_block.clone()];
for (case_val, dest) in &case_dests {
operands.push(constants::const_i32(*case_val));
operands.push(dest.clone());
}
v.operands = operands;
v.num_operands = v.operands.len();
valref(v);
}
let mut case_results: Vec<(ValueRef, ValueRef)> = Vec::new();
for ((_case_val, handler), (_, dest)) in cases.into_iter().zip(case_dests.iter()) {
let case_result = handler(&mut self.builder);
case_results.push((case_result, dest.clone()));
self.branch_to(merge_block.clone());
}
let default_result = default_handler(&mut self.builder);
case_results.push((default_result, default_block));
self.branch_to(merge_block.clone());
let result_ty = case_results[0].0.borrow().ty.clone();
let incoming: Vec<(ValueRef, ValueRef)> = case_results
.into_iter()
.map(|(val, block)| (val, block))
.collect();
let phi = instruction::phi(result_ty, incoming);
let mut v = phi.borrow_mut();
if !phi_name.is_empty() {
v.name = phi_name.to_string();
}
drop(v);
phi
}
pub fn build_copy_loop(
&mut self,
dst: ValueRef,
src: ValueRef,
count: ValueRef,
elem_ty: &Type,
) {
let header_block = self.create_block();
let body_block = self.create_block();
let exit_block = self.create_block();
let zero = constants::const_i32(0);
let one = constants::const_i32(1);
self.branch_to(header_block.clone());
let phi_i = instruction::phi(Type::i32(), vec![]);
let cond = instruction::icmp(ICmpPred::Slt, phi_i.clone(), count);
self.cond_branch(cond, body_block.clone(), exit_block.clone());
let src_gep = {
let mut v = Value::new(Type::pointer(0)).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::GetElementPtr);
v.operands = vec![src, zero.clone(), phi_i.clone()];
v.num_operands = 3;
valref(v)
};
let loaded = {
let mut v = Value::new(elem_ty.clone()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Load);
v.operands = vec![src_gep];
v.num_operands = 1;
valref(v)
};
let dst_gep = {
let mut v = Value::new(Type::pointer(0)).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::GetElementPtr);
v.operands = vec![dst, zero, phi_i.clone()];
v.num_operands = 3;
valref(v)
};
let _stored = {
let mut v = Value::new(Type::void()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Store);
v.operands = vec![loaded, dst_gep];
v.num_operands = 2;
valref(v)
};
let _next_i = instruction::add(phi_i, one);
self.branch_to(header_block);
}
pub fn build_strided_load(
&mut self,
base: ValueRef,
count: ValueRef,
stride: ValueRef,
elem_ty: &Type,
name: &str,
) -> ValueRef {
let header_block = self.create_block();
let body_block = self.create_block();
let exit_block = self.create_block();
let _zero = constants::const_i32(0);
let one = constants::const_i32(1);
self.branch_to(header_block.clone());
let phi_i = instruction::phi(Type::i32(), vec![]);
let cond = instruction::icmp(ICmpPred::Slt, phi_i.clone(), count);
self.cond_branch(cond, body_block.clone(), exit_block.clone());
let offset = instruction::mul(phi_i.clone(), stride);
let addr = {
let mut v = Value::new(Type::pointer(0)).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::GetElementPtr);
v.operands = vec![base, offset];
v.num_operands = 2;
self.builder.maybe_name(&mut v, name);
valref(v)
};
let loaded = {
let mut v = Value::new(elem_ty.clone()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Load);
v.operands = vec![addr];
v.num_operands = 1;
valref(v)
};
let _next_i = instruction::add(phi_i, one);
self.branch_to(header_block);
loaded
}
pub fn build_null_check<H, C>(&mut self, ptr: ValueRef, null_handler: H, continue_body: C)
where
H: FnOnce(&mut X86IRBuilderDeep),
C: FnOnce(&mut X86IRBuilderDeep),
{
let null_block = self.create_block();
let continue_block = self.create_block();
let merge_block = self.create_block();
let null_ptr = constants::const_null_ptr(Type::pointer(0));
let is_null = instruction::icmp(ICmpPred::Eq, ptr, null_ptr);
self.cond_branch(is_null, null_block.clone(), continue_block.clone());
null_handler(&mut self.builder);
self.branch_to(merge_block.clone());
continue_body(&mut self.builder);
self.branch_to(merge_block);
}
pub fn build_range_check(
&mut self,
val: ValueRef,
lo: ValueRef,
hi: ValueRef,
is_signed: bool,
name: &str,
) -> ValueRef {
let lo_pred = if is_signed {
ICmpPred::Sle
} else {
ICmpPred::Ule
};
let hi_pred = if is_signed {
ICmpPred::Slt
} else {
ICmpPred::Ult
};
let check_lo = instruction::icmp(lo_pred, lo, val.clone());
let check_hi = instruction::icmp(hi_pred, val, hi);
let result = instruction::and(check_lo, check_hi);
let mut v = result.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
result
}
pub fn build_abs(&mut self, val: ValueRef, name: &str) -> ValueRef {
let zero = constants::const_i32(0);
let is_neg = instruction::icmp(ICmpPred::Slt, val.clone(), zero.clone());
let neg = instruction::sub(zero.clone(), val.clone());
let result = instruction::select(is_neg, neg, val);
let mut v = result.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
result
}
pub fn build_smin(&mut self, a: ValueRef, b: ValueRef, name: &str) -> ValueRef {
let cond = instruction::icmp(ICmpPred::Slt, a.clone(), b.clone());
let result = instruction::select(cond, a, b);
let mut v = result.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
result
}
pub fn build_smax(&mut self, a: ValueRef, b: ValueRef, name: &str) -> ValueRef {
let cond = instruction::icmp(ICmpPred::Sgt, a.clone(), b.clone());
let result = instruction::select(cond, a, b);
let mut v = result.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
result
}
pub fn build_umin(&mut self, a: ValueRef, b: ValueRef, name: &str) -> ValueRef {
let cond = instruction::icmp(ICmpPred::Ult, a.clone(), b.clone());
let result = instruction::select(cond, a, b);
let mut v = result.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
result
}
pub fn build_umax(&mut self, a: ValueRef, b: ValueRef, name: &str) -> ValueRef {
let cond = instruction::icmp(ICmpPred::Ugt, a.clone(), b.clone());
let result = instruction::select(cond, a, b);
let mut v = result.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
result
}
pub fn build_ternary(
&mut self,
cond: ValueRef,
true_val: ValueRef,
false_val: ValueRef,
name: &str,
) -> ValueRef {
let result = instruction::select(cond, true_val, false_val);
let mut v = result.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
result
}
pub fn build_ptr_diff(&mut self, ptr_a: ValueRef, ptr_b: ValueRef, name: &str) -> ValueRef {
let a_int = {
let mut v = Value::new(Type::i64()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::PtrToInt);
v.operands = vec![ptr_a];
v.num_operands = 1;
valref(v)
};
let b_int = {
let mut v = Value::new(Type::i64()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::PtrToInt);
v.operands = vec![ptr_b];
v.num_operands = 1;
valref(v)
};
let diff = instruction::sub(b_int, a_int);
let mut v = diff.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
diff
}
pub fn build_bitfield_extract(
&mut self,
val: ValueRef,
offset: u32,
width: u32,
name: &str,
) -> ValueRef {
let offset_val = constants::const_i32(offset as i32);
let shifted = instruction::lshr(val, offset_val);
let mask = constants::const_i32((1u32 << width).wrapping_sub(1) as i32);
let result = instruction::and(shifted, mask);
let mut v = result.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
result
}
pub fn build_bitfield_insert(
&mut self,
val: ValueRef,
field: ValueRef,
offset: u32,
width: u32,
name: &str,
) -> ValueRef {
let offset_val = constants::const_i32(offset as i32);
let mask = constants::const_i32(((1u32 << width) - 1) as i32);
let not_shifted_mask = {
let shifted = instruction::shl(mask.clone(), offset_val.clone());
let mut v =
Value::new(shifted.borrow().ty.clone()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Xor);
v.operands = vec![shifted, constants::const_i32(-1)];
v.num_operands = 2;
valref(v)
};
let cleared = instruction::and(val, not_shifted_mask);
let masked_field = instruction::and(field, mask);
let shifted_field = instruction::shl(masked_field, offset_val);
let result = instruction::or(cleared, shifted_field);
let mut v = result.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
result
}
pub fn build_manual_bswap(&mut self, val: ValueRef, name: &str) -> ValueRef {
let byte_mask = constants::const_i32(0xFF);
let b0 = instruction::and(val.clone(), byte_mask.clone());
let b0_shifted = instruction::shl(b0, constants::const_i32(24));
let b1 = {
let shifted = instruction::lshr(val.clone(), constants::const_i32(8));
instruction::and(shifted, byte_mask.clone())
};
let b1_shifted = instruction::shl(b1, constants::const_i32(16));
let b2 = {
let shifted = instruction::lshr(val.clone(), constants::const_i32(16));
instruction::and(shifted, byte_mask.clone())
};
let b2_shifted = instruction::shl(b2, constants::const_i32(8));
let b3 = instruction::lshr(val, constants::const_i32(24));
let result = {
let t1 = instruction::or(b0_shifted, b1_shifted);
let t2 = instruction::or(b2_shifted, b3);
instruction::or(t1, t2)
};
let mut v = result.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
result
}
pub fn build_is_power_of_two(&mut self, val: ValueRef, name: &str) -> ValueRef {
let zero = constants::const_i32(0);
let one = constants::const_i32(1);
let not_zero = instruction::icmp(ICmpPred::Ne, val.clone(), zero.clone());
let dec = instruction::sub(val.clone(), one.clone());
let and_val = instruction::and(val, dec);
let is_zero = instruction::icmp(ICmpPred::Eq, and_val, zero);
let result = instruction::and(not_zero, is_zero);
let mut v = result.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
result
}
pub fn build_round_up_pow2(&mut self, val: ValueRef, name: &str) -> ValueRef {
let one = constants::const_i32(1);
let dec = instruction::sub(val, one.clone());
let s1 = instruction::or(
dec.clone(),
instruction::lshr(dec.clone(), constants::const_i32(1)),
);
let s2 = instruction::or(s1.clone(), instruction::lshr(s1, constants::const_i32(2)));
let s3 = instruction::or(s2.clone(), instruction::lshr(s2, constants::const_i32(4)));
let s4 = instruction::or(s3.clone(), instruction::lshr(s3, constants::const_i32(8)));
let s5 = instruction::or(s4.clone(), instruction::lshr(s4, constants::const_i32(16)));
let result = instruction::add(s5, one);
let mut v = result.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
result
}
pub fn build_clamp(
&mut self,
val: ValueRef,
lo: ValueRef,
hi: ValueRef,
name: &str,
) -> ValueRef {
let cl1 = self.build_smax(val, lo, "");
let result = self.build_smin(cl1, hi, name);
result
}
pub fn build_safe_sdiv(&mut self, num: ValueRef, den: ValueRef, name: &str) -> ValueRef {
let zero = constants::const_i32(0);
let is_zero = instruction::icmp(ICmpPred::Eq, den.clone(), zero.clone());
let safe_div = instruction::sdiv(num.clone(), den);
let result = instruction::select(is_zero, zero, safe_div);
let mut v = result.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
result
}
pub fn build_swizzle4(
&mut self,
vec: ValueRef,
ix: u32,
iy: u32,
iz: u32,
iw: u32,
name: &str,
) -> ValueRef {
let e0 = self.builder.build_extract_element(vec.clone(), ix, "");
let e1 = self.builder.build_extract_element(vec.clone(), iy, "");
let e2 = self.builder.build_extract_element(vec.clone(), iz, "");
let e3 = self.builder.build_extract_element(vec, iw, "");
self.builder.build_vector(&[e0, e1, e2, e3], name)
}
pub fn build_hadd_pairs(&mut self, vec: ValueRef, num_elems: u32, name: &str) -> ValueRef {
let half = num_elems / 2;
let mut result_elements = Vec::with_capacity(half as usize);
for i in 0..half {
let e0 = self.builder.build_extract_element(vec.clone(), i * 2, "");
let e1 = self
.builder
.build_extract_element(vec.clone(), i * 2 + 1, "");
let sum = instruction::add(e0, e1);
result_elements.push(sum);
}
self.builder.build_vector(&result_elements, name)
}
pub fn build_dot_product(
&mut self,
a: ValueRef,
b: ValueRef,
num_elems: u32,
name: &str,
) -> ValueRef {
let mut products = Vec::with_capacity(num_elems as usize);
for i in 0..num_elems {
let ea = self.builder.build_extract_element(a.clone(), i, "");
let eb = self.builder.build_extract_element(b.clone(), i, "");
let prod = instruction::mul(ea, eb);
products.push(prod);
}
let mut sum = products[0].clone();
for prod in &products[1..] {
sum = instruction::add(sum, prod.clone());
}
let mut v = sum.borrow_mut();
if !name.is_empty() {
v.name = name.to_string();
}
drop(v);
sum
}
}
impl X86IRBuilderDeep {
pub fn build_masked_load(
&mut self,
ptr: ValueRef,
alignment: u32,
mask: ValueRef,
passthru: ValueRef,
name: &str,
) -> ValueRef {
let align_val = constants::const_i32(alignment as i32);
let result_ty = passthru.borrow().ty.clone();
self.create_intrinsic_call(
"llvm.masked.load",
&result_ty,
vec![ptr, align_val, mask, passthru],
name,
)
}
pub fn build_masked_store(
&mut self,
val: ValueRef,
ptr: ValueRef,
alignment: u32,
mask: ValueRef,
) -> ValueRef {
let align_val = constants::const_i32(alignment as i32);
self.create_intrinsic_call(
"llvm.masked.store",
&Type::void(),
vec![val, ptr, align_val, mask],
"",
)
}
pub fn build_masked_gather(
&mut self,
ptrs: ValueRef,
alignment: u32,
mask: ValueRef,
passthru: ValueRef,
name: &str,
) -> ValueRef {
let align_val = constants::const_i32(alignment as i32);
let result_ty = passthru.borrow().ty.clone();
self.create_intrinsic_call(
"llvm.masked.gather",
&result_ty,
vec![ptrs, align_val, mask, passthru],
name,
)
}
pub fn build_masked_scatter(
&mut self,
val: ValueRef,
ptrs: ValueRef,
alignment: u32,
mask: ValueRef,
) -> ValueRef {
let align_val = constants::const_i32(alignment as i32);
self.create_intrinsic_call(
"llvm.masked.scatter",
&Type::void(),
vec![val, ptrs, align_val, mask],
"",
)
}
pub fn build_vector_reduce(
&mut self,
vec: ValueRef,
reduction_kind: &str,
name: &str,
) -> ValueRef {
let vec_ty = vec.borrow().ty.clone();
let elem_ty = match &vec_ty.kind {
llvm_native_core::types::TypeKind::FixedVector {
element_type_id: eid,
..
} => {
let mut t = Type::void();
t.id = *eid;
t
}
_ => Type::i32(),
};
let intrinsic_name = format!("llvm.vector.reduce.{}", reduction_kind);
self.create_typed_intrinsic(&intrinsic_name, &elem_ty, vec![vec], name)
}
pub fn build_vector_reduce_add(&mut self, vec: ValueRef, name: &str) -> ValueRef {
self.build_vector_reduce(vec, "add", name)
}
pub fn build_vector_reduce_mul(&mut self, vec: ValueRef, name: &str) -> ValueRef {
self.build_vector_reduce(vec, "mul", name)
}
pub fn build_vector_reduce_and(&mut self, vec: ValueRef, name: &str) -> ValueRef {
self.build_vector_reduce(vec, "and", name)
}
pub fn build_vector_reduce_or(&mut self, vec: ValueRef, name: &str) -> ValueRef {
self.build_vector_reduce(vec, "or", name)
}
pub fn build_vector_reduce_xor(&mut self, vec: ValueRef, name: &str) -> ValueRef {
self.build_vector_reduce(vec, "xor", name)
}
pub fn build_vector_reduce_smin(&mut self, vec: ValueRef, name: &str) -> ValueRef {
self.build_vector_reduce(vec, "smin", name)
}
pub fn build_vector_reduce_smax(&mut self, vec: ValueRef, name: &str) -> ValueRef {
self.build_vector_reduce(vec, "smax", name)
}
pub fn build_vector_reduce_umin(&mut self, vec: ValueRef, name: &str) -> ValueRef {
self.build_vector_reduce(vec, "umin", name)
}
pub fn build_vector_reduce_umax(&mut self, vec: ValueRef, name: &str) -> ValueRef {
self.build_vector_reduce(vec, "umax", name)
}
pub fn build_vector_reduce_fadd(
&mut self,
vec: ValueRef,
start: ValueRef,
name: &str,
) -> ValueRef {
let vec_ty = vec.borrow().ty.clone();
let elem_ty = match &vec_ty.kind {
llvm_native_core::types::TypeKind::FixedVector {
element_type_id: eid,
..
} => {
let mut t = Type::void();
t.id = *eid;
t
}
_ => Type::float(),
};
self.create_typed_intrinsic("llvm.vector.reduce.fadd", &elem_ty, vec![start, vec], name)
}
pub fn build_vector_reduce_fmul(
&mut self,
vec: ValueRef,
start: ValueRef,
name: &str,
) -> ValueRef {
let vec_ty = vec.borrow().ty.clone();
let elem_ty = match &vec_ty.kind {
llvm_native_core::types::TypeKind::FixedVector {
element_type_id: eid,
..
} => {
let mut t = Type::void();
t.id = *eid;
t
}
_ => Type::float(),
};
self.create_typed_intrinsic("llvm.vector.reduce.fmul", &elem_ty, vec![start, vec], name)
}
pub fn build_step_vector(&mut self, num_elems: u32, name: &str) -> ValueRef {
let elem_ty = Type::i32();
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::Call);
let func_val = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "llvm.experimental.stepvector".to_string();
valref(fv)
};
v.operands = vec![func_val];
v.num_operands = 1;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_vp_add(
&mut self,
a: ValueRef,
b: ValueRef,
mask: ValueRef,
evl: ValueRef,
name: &str,
) -> ValueRef {
let result_ty = a.borrow().ty.clone();
self.create_intrinsic_call("llvm.vp.add", &result_ty, vec![a, b, mask, evl], name)
}
pub fn build_vp_sub(
&mut self,
a: ValueRef,
b: ValueRef,
mask: ValueRef,
evl: ValueRef,
name: &str,
) -> ValueRef {
let result_ty = a.borrow().ty.clone();
self.create_intrinsic_call("llvm.vp.sub", &result_ty, vec![a, b, mask, evl], name)
}
pub fn build_vp_mul(
&mut self,
a: ValueRef,
b: ValueRef,
mask: ValueRef,
evl: ValueRef,
name: &str,
) -> ValueRef {
let result_ty = a.borrow().ty.clone();
self.create_intrinsic_call("llvm.vp.mul", &result_ty, vec![a, b, mask, evl], name)
}
pub fn build_vp_load(
&mut self,
ptr: ValueRef,
mask: ValueRef,
evl: ValueRef,
result_ty: &Type,
name: &str,
) -> ValueRef {
self.create_intrinsic_call("llvm.vp.load", result_ty, vec![ptr, mask, evl], name)
}
pub fn build_vp_store(
&mut self,
val: ValueRef,
ptr: ValueRef,
mask: ValueRef,
evl: ValueRef,
) -> ValueRef {
self.create_intrinsic_call(
"llvm.vp.store",
&Type::void(),
vec![val, ptr, mask, evl],
"",
)
}
pub fn build_vp_merge(
&mut self,
mask: ValueRef,
on_true: ValueRef,
on_false: ValueRef,
evl: ValueRef,
name: &str,
) -> ValueRef {
let result_ty = on_true.borrow().ty.clone();
self.create_intrinsic_call(
"llvm.vp.merge",
&result_ty,
vec![mask, on_true, on_false, evl],
name,
)
}
pub fn build_assume(&mut self, condition: ValueRef) -> ValueRef {
self.create_intrinsic_call("llvm.assume", &Type::void(), vec![condition], "")
}
pub fn build_expect(&mut self, value: ValueRef, expected: ValueRef, name: &str) -> ValueRef {
let result_ty = value.borrow().ty.clone();
self.create_intrinsic_call("llvm.expect", &result_ty, vec![value, expected], name)
}
pub fn build_expect_with_probability(
&mut self,
value: ValueRef,
expected: ValueRef,
probability: f64,
name: &str,
) -> ValueRef {
let result_ty = value.borrow().ty.clone();
let prob_val = constants::const_double(probability);
self.create_intrinsic_call(
"llvm.expect.with.probability",
&result_ty,
vec![value, expected, prob_val],
name,
)
}
pub fn build_trap(&mut self) -> ValueRef {
self.create_intrinsic_call("llvm.trap", &Type::void(), vec![], "")
}
pub fn build_debugtrap(&mut self) -> ValueRef {
self.create_intrinsic_call("llvm.debugtrap", &Type::void(), vec![], "")
}
pub fn build_ubsantrap(&mut self, kind: u32) -> ValueRef {
let kind_val = constants::const_i8(kind as i8);
self.create_intrinsic_call("llvm.ubsantrap", &Type::void(), vec![kind_val], "")
}
pub fn build_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; self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_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; self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_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; self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_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; self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_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; self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_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; self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_exact_sdiv(&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::SDiv);
v.operands = vec![lhs, rhs];
v.num_operands = 2;
v.subclass_data = 1; self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_exact_udiv(&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::UDiv);
v.operands = vec![lhs, rhs];
v.num_operands = 2;
v.subclass_data = 1; self.maybe_name(&mut v, name);
valref(v)
}
pub fn i1_ty(&self) -> Type {
Type::i1()
}
pub fn i8_ty(&self) -> Type {
Type::i8()
}
pub fn i16_ty(&self) -> Type {
Type::i16()
}
pub fn i32_ty(&self) -> Type {
Type::i32()
}
pub fn i64_ty(&self) -> Type {
Type::i64()
}
pub fn i128_ty(&self) -> Type {
Type::i128()
}
pub fn f32_ty(&self) -> Type {
Type::float()
}
pub fn f64_ty(&self) -> Type {
Type::double()
}
pub fn void_ty(&self) -> Type {
Type::void()
}
pub fn ptr_ty(&self) -> Type {
Type::pointer(0)
}
pub fn ptr_ty_as(&self, addr_space: u32) -> Type {
Type::pointer(addr_space)
}
pub fn int_ty(&self, bits: u32) -> Type {
Type::int(bits)
}
pub fn vec_ty(&self, num_elems: u32, elem_ty: &Type) -> Type {
Type::fixed_vector_with(num_elems, elem_ty.id)
}
pub fn array_ty(&self, num_elems: u32, elem_ty: &Type) -> Type {
Type::array_with(num_elems as u64, elem_ty.id)
}
pub fn struct_ty(&self, element_types: &[Type], is_packed: bool) -> Type {
let ids: Vec<llvm_native_core::types::TypeId> = element_types.iter().map(|t| t.id).collect();
Type::struct_literal_with(is_packed, ids)
}
pub fn build_atomic_rmw_seqcst(
&mut self,
bin_op: AtomicRMWBinOp,
ptr: ValueRef,
val: ValueRef,
name: &str,
) -> ValueRef {
self.build_atomic_rmw(bin_op, ptr, val, AtomicOrdering::SeqCst, name)
}
pub fn build_atomic_fetch_add(&mut self, ptr: ValueRef, val: ValueRef, name: &str) -> ValueRef {
self.build_atomic_rmw_seqcst(AtomicRMWBinOp::Add, ptr, val, name)
}
pub fn build_atomic_fetch_sub(&mut self, ptr: ValueRef, val: ValueRef, name: &str) -> ValueRef {
self.build_atomic_rmw_seqcst(AtomicRMWBinOp::Sub, ptr, val, name)
}
pub fn build_atomic_fetch_or(&mut self, ptr: ValueRef, val: ValueRef, name: &str) -> ValueRef {
self.build_atomic_rmw_seqcst(AtomicRMWBinOp::Or, ptr, val, name)
}
pub fn build_atomic_fetch_and(&mut self, ptr: ValueRef, val: ValueRef, name: &str) -> ValueRef {
self.build_atomic_rmw_seqcst(AtomicRMWBinOp::And, ptr, val, name)
}
pub fn build_atomic_fetch_xor(&mut self, ptr: ValueRef, val: ValueRef, name: &str) -> ValueRef {
self.build_atomic_rmw_seqcst(AtomicRMWBinOp::Xor, ptr, val, name)
}
pub fn build_atomic_fetch_smax(
&mut self,
ptr: ValueRef,
val: ValueRef,
name: &str,
) -> ValueRef {
self.build_atomic_rmw_seqcst(AtomicRMWBinOp::Max, ptr, val, name)
}
pub fn build_critical_section_enter(&mut self, lock_ptr: ValueRef) -> ValueRef {
let one = constants::const_i32(1);
let zero = constants::const_i32(0);
self.build_cmpxchg_loop(
lock_ptr,
zero,
one,
AtomicOrdering::Acquire,
AtomicOrdering::Monotonic,
"cs_enter",
)
}
pub fn build_critical_section_exit(&mut self, lock_ptr: ValueRef) -> ValueRef {
let zero = constants::const_i32(0);
self.build_atomic_store(zero, lock_ptr, AtomicOrdering::Release)
}
pub fn build_global_i32(&mut self, name: &str, value: i32) -> ValueRef {
let init = constants::const_i32(value);
let mut gv = Value::new(Type::i32()).with_subclass(SubclassKind::GlobalVariable);
gv.name = name.to_string();
gv.operands = vec![init];
gv.num_operands = 1;
gv.subclass_data = 1; valref(gv)
}
pub fn build_global_f64(&mut self, name: &str, value: f64) -> ValueRef {
let init = constants::const_double(value);
let mut gv = Value::new(Type::double()).with_subclass(SubclassKind::GlobalVariable);
gv.name = name.to_string();
gv.operands = vec![init];
gv.num_operands = 1;
gv.subclass_data = 1;
valref(gv)
}
pub fn build_global_i32_array(&mut self, name: &str, values: &[i32]) -> ValueRef {
let len = values.len() as u32;
let arr_ty = Type::array_with(len as u64, Type::i32().id);
let mut init_val = Value::new(arr_ty.clone()).with_subclass(SubclassKind::Constant);
for &v in values {
init_val.subclass_extra.push(v as u64);
}
let init = valref(init_val);
let mut gv = Value::new(arr_ty).with_subclass(SubclassKind::GlobalVariable);
gv.name = name.to_string();
gv.operands = vec![init];
gv.num_operands = 1;
gv.subclass_data = 1;
valref(gv)
}
pub fn build_vector_bitcast(&mut self, vec: ValueRef, dest_ty: &Type, name: &str) -> ValueRef {
let mut v = Value::new(dest_ty.clone()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::BitCast);
v.operands = vec![vec];
v.num_operands = 1;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_vector_trunc(&mut self, vec: ValueRef, dest_ty: &Type, name: &str) -> ValueRef {
let mut v = Value::new(dest_ty.clone()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Trunc);
v.operands = vec![vec];
v.num_operands = 1;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_vector_zext(&mut self, vec: ValueRef, dest_ty: &Type, name: &str) -> ValueRef {
let mut v = Value::new(dest_ty.clone()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::ZExt);
v.operands = vec![vec];
v.num_operands = 1;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_vector_sext(&mut self, vec: ValueRef, dest_ty: &Type, name: &str) -> ValueRef {
let mut v = Value::new(dest_ty.clone()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::SExt);
v.operands = vec![vec];
v.num_operands = 1;
self.maybe_name(&mut v, name);
valref(v)
}
pub fn build_ctlz_zero_defined(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_ctlz(val, false, name)
}
pub fn build_cttz_zero_defined(&mut self, val: ValueRef, name: &str) -> ValueRef {
self.build_cttz(val, false, name)
}
pub fn set_alias_scope(&mut self, inst: ValueRef, _scope_domain: u32, _scope_id: u32) {
let _ = inst;
}
pub fn build_noalias_load(&mut self, ptr: ValueRef, elem_ty: &Type, name: &str) -> ValueRef {
let mut v = Value::new(elem_ty.clone()).with_subclass(SubclassKind::Instruction);
v.opcode = Some(Opcode::Load);
v.operands = vec![ptr];
v.num_operands = 1;
v.subclass_data = 1; self.maybe_name(&mut v, name);
valref(v)
}
pub fn is_poison(&self, val: &ValueRef) -> bool {
let v = val.borrow();
v.subclass == SubclassKind::Constant && v.opcode.is_none() && v.name == "poison"
}
pub fn is_undef(&self, val: &ValueRef) -> bool {
let v = val.borrow();
v.subclass == SubclassKind::Constant && v.opcode.is_none() && v.name == "undef"
}
pub fn build_simple_gc_statepoint(
&mut self,
call_target: ValueRef,
call_args: Vec<ValueRef>,
live_ptrs: Vec<ValueRef>,
name: &str,
) -> ValueRef {
let num_args = call_args.len() as u32;
self.build_gc_statepoint(
call_target,
num_args,
0, call_args,
vec![], vec![], live_ptrs,
name,
)
}
pub fn build_gc_call_with_relocation(
&mut self,
callee: ValueRef,
call_args: Vec<ValueRef>,
live_ptrs: Vec<ValueRef>,
result_ty: &Type,
name: &str,
) -> (ValueRef, Vec<ValueRef>) {
let live_ptrs_len = live_ptrs.len() as u32;
let sp =
self.build_simple_gc_statepoint(callee, call_args, live_ptrs, &format!("{}_sp", name));
let call_result = self.build_gc_result(sp.clone(), result_ty, &format!("{}_res", name));
let relocated: Vec<ValueRef> = (0..live_ptrs_len)
.map(|i| {
self.build_gc_relocate(sp.clone(), i, i, result_ty, &format!("{}_reloc{}", name, i))
})
.collect();
(call_result, relocated)
}
pub fn build_cxx_landingpad(
&mut self,
catch_type_info: Option<ValueRef>,
is_cleanup: bool,
name: &str,
) -> ValueRef {
let result_ty = Type::struct_literal_with(false, vec![Type::pointer(0).id, Type::i32().id]);
let mut clauses = Vec::new();
if let Some(ti) = catch_type_info {
clauses.push(ti);
}
self.build_landingpad(&result_ty, is_cleanup, clauses, name)
}
pub fn build_extract_exception_pointer(
&mut self,
landingpad_val: ValueRef,
name: &str,
) -> ValueRef {
self.build_extract_value(landingpad_val, 0, name)
}
pub fn build_extract_selector(&mut self, landingpad_val: ValueRef, name: &str) -> ValueRef {
self.build_extract_value(landingpad_val, 1, name)
}
pub fn build_catch_dispatch(
&mut self,
landingpad_val: ValueRef,
type_id: ValueRef,
catch_handler: ValueRef,
next_handler: ValueRef,
) {
let sel = self.build_extract_selector(landingpad_val, "sel");
let matches = instruction::icmp(ICmpPred::Eq, sel, type_id);
instruction::br_cond(matches, catch_handler, next_handler);
}
pub fn set_nounwind(&self, _func: ValueRef) {
}
pub fn set_readonly(&self, _func: ValueRef) {
}
pub fn set_readnone(&self, _func: ValueRef) {
}
pub fn set_tail_call(&self, _call_inst: ValueRef) {
}
}
#[cfg(test)]
mod tests {
use super::*;
use llvm_native_core::context::LLVMContext;
use llvm_native_core::ir_builder::{AtomicOrdering, AtomicRMWBinOp, FastMathFlags, IRBuilder};
use llvm_native_core::types::Type;
use llvm_native_core::value::{valref, SubclassKind, Value};
fn make_builder() -> X86IRBuilderDeep {
let ctx = LLVMContext::new();
X86IRBuilderDeep::from_context(&ctx)
}
fn make_pattern_builder() -> X86IRPatternBuilder {
let ctx = LLVMContext::new();
X86IRPatternBuilder::from_context(&ctx)
}
fn make_value(ty: Type) -> ValueRef {
let v = Value::new(ty).with_subclass(SubclassKind::Constant);
valref(v)
}
#[test]
fn test_rounding_mode_as_str() {
assert_eq!(RoundingMode::Dynamic.as_metadata_str(), "fpround.dynamic");
assert_eq!(
RoundingMode::Tonearest.as_metadata_str(),
"fpround.tonearest"
);
assert_eq!(RoundingMode::Downward.as_metadata_str(), "fpround.downward");
assert_eq!(RoundingMode::Upward.as_metadata_str(), "fpround.upward");
assert_eq!(
RoundingMode::Towardzero.as_metadata_str(),
"fpround.towardzero"
);
assert_eq!(
RoundingMode::TiesAway.as_metadata_str(),
"fpround.tonearestaway"
);
}
#[test]
fn test_rounding_mode_as_u32() {
assert_eq!(RoundingMode::Dynamic.as_u32(), 0);
assert_eq!(RoundingMode::Tonearest.as_u32(), 1);
assert_eq!(RoundingMode::Downward.as_u32(), 2);
assert_eq!(RoundingMode::Upward.as_u32(), 3);
assert_eq!(RoundingMode::Towardzero.as_u32(), 4);
assert_eq!(RoundingMode::TiesAway.as_u32(), 5);
}
#[test]
fn test_fp_exception_behavior_as_str() {
assert_eq!(
FPExceptionBehavior::Ignore.as_metadata_str(),
"fpexcept.ignore"
);
assert_eq!(
FPExceptionBehavior::Maytrap.as_metadata_str(),
"fpexcept.maytrap"
);
assert_eq!(
FPExceptionBehavior::Strict.as_metadata_str(),
"fpexcept.strict"
);
}
#[test]
fn test_tls_model_names() {
assert_eq!(TLSModel::GeneralDynamic.as_str(), "general-dynamic");
assert_eq!(TLSModel::LocalDynamic.as_str(), "local-dynamic");
assert_eq!(TLSModel::InitialExec.as_str(), "initial-exec");
assert_eq!(TLSModel::LocalExec.as_str(), "local-exec");
}
#[test]
fn test_bit_manip_intrinsic_names() {
assert_eq!(BitManipOp::BSwap.intrinsic_name(), "llvm.bswap");
assert_eq!(BitManipOp::CtPop.intrinsic_name(), "llvm.ctpop");
assert_eq!(BitManipOp::CtLz.intrinsic_name(), "llvm.ctlz");
assert_eq!(BitManipOp::CtTz.intrinsic_name(), "llvm.cttz");
assert_eq!(BitManipOp::BitReverse.intrinsic_name(), "llvm.bitreverse");
assert_eq!(BitManipOp::FShl.intrinsic_name(), "llvm.fshl");
assert_eq!(BitManipOp::FShr.intrinsic_name(), "llvm.fshr");
}
#[test]
fn test_fixed_point_intrinsic_names() {
assert_eq!(FixedPointOp::SMulFix.intrinsic_name(), "llvm.smul.fix");
assert_eq!(FixedPointOp::UMulFix.intrinsic_name(), "llvm.umul.fix");
assert_eq!(FixedPointOp::SDivFix.intrinsic_name(), "llvm.sdiv.fix");
assert_eq!(FixedPointOp::UDivFix.intrinsic_name(), "llvm.udiv.fix");
}
#[test]
fn test_overflow_intrinsic_names() {
assert_eq!(
OverflowOp::SAddO.intrinsic_name(),
"llvm.sadd.with.overflow"
);
assert_eq!(
OverflowOp::UAddO.intrinsic_name(),
"llvm.uadd.with.overflow"
);
assert_eq!(
OverflowOp::SSubO.intrinsic_name(),
"llvm.ssub.with.overflow"
);
assert_eq!(
OverflowOp::USubO.intrinsic_name(),
"llvm.usub.with.overflow"
);
assert_eq!(
OverflowOp::SMulO.intrinsic_name(),
"llvm.smul.with.overflow"
);
assert_eq!(
OverflowOp::UMulO.intrinsic_name(),
"llvm.umul.with.overflow"
);
}
#[test]
fn test_saturating_intrinsic_names() {
assert_eq!(SaturatingOp::SAddSat.intrinsic_name(), "llvm.sadd.sat");
assert_eq!(SaturatingOp::UAddSat.intrinsic_name(), "llvm.uadd.sat");
assert_eq!(SaturatingOp::SSubSat.intrinsic_name(), "llvm.ssub.sat");
assert_eq!(SaturatingOp::USubSat.intrinsic_name(), "llvm.usub.sat");
assert_eq!(SaturatingOp::SShlSat.intrinsic_name(), "llvm.sshl.sat");
assert_eq!(SaturatingOp::UShlSat.intrinsic_name(), "llvm.ushl.sat");
}
#[test]
fn test_math_intrinsic_names() {
assert_eq!(MathIntrinsicOp::Sqrt.intrinsic_name(), "llvm.sqrt");
assert_eq!(MathIntrinsicOp::Sin.intrinsic_name(), "llvm.sin");
assert_eq!(MathIntrinsicOp::Cos.intrinsic_name(), "llvm.cos");
assert_eq!(MathIntrinsicOp::Pow.intrinsic_name(), "llvm.pow");
assert_eq!(MathIntrinsicOp::Exp.intrinsic_name(), "llvm.exp");
assert_eq!(MathIntrinsicOp::Exp2.intrinsic_name(), "llvm.exp2");
assert_eq!(MathIntrinsicOp::Log.intrinsic_name(), "llvm.log");
assert_eq!(MathIntrinsicOp::Log10.intrinsic_name(), "llvm.log10");
assert_eq!(MathIntrinsicOp::Log2.intrinsic_name(), "llvm.log2");
assert_eq!(MathIntrinsicOp::FMA.intrinsic_name(), "llvm.fma");
assert_eq!(MathIntrinsicOp::Floor.intrinsic_name(), "llvm.floor");
assert_eq!(MathIntrinsicOp::Ceil.intrinsic_name(), "llvm.ceil");
assert_eq!(MathIntrinsicOp::Trunc.intrinsic_name(), "llvm.trunc");
assert_eq!(MathIntrinsicOp::Round.intrinsic_name(), "llvm.round");
assert_eq!(
MathIntrinsicOp::RoundEven.intrinsic_name(),
"llvm.roundeven"
);
assert_eq!(MathIntrinsicOp::FAbs.intrinsic_name(), "llvm.fabs");
assert_eq!(MathIntrinsicOp::FMin.intrinsic_name(), "llvm.minnum");
assert_eq!(MathIntrinsicOp::FMax.intrinsic_name(), "llvm.maxnum");
assert_eq!(MathIntrinsicOp::Minimum.intrinsic_name(), "llvm.minimum");
assert_eq!(MathIntrinsicOp::Maximum.intrinsic_name(), "llvm.maximum");
assert_eq!(MathIntrinsicOp::CopySign.intrinsic_name(), "llvm.copysign");
assert_eq!(MathIntrinsicOp::LdExp.intrinsic_name(), "llvm.ldexp");
}
#[test]
fn test_constrained_fp_intrinsic_names() {
assert_eq!(
ConstrainedFPOp::FAdd.intrinsic_name(),
"llvm.experimental.constrained.fadd"
);
assert_eq!(
ConstrainedFPOp::FSub.intrinsic_name(),
"llvm.experimental.constrained.fsub"
);
assert_eq!(
ConstrainedFPOp::FMul.intrinsic_name(),
"llvm.experimental.constrained.fmul"
);
assert_eq!(
ConstrainedFPOp::FDiv.intrinsic_name(),
"llvm.experimental.constrained.fdiv"
);
assert_eq!(
ConstrainedFPOp::FRem.intrinsic_name(),
"llvm.experimental.constrained.frem"
);
assert_eq!(
ConstrainedFPOp::FMA.intrinsic_name(),
"llvm.experimental.constrained.fma"
);
}
#[test]
fn test_matrix_intrinsic_names() {
assert_eq!(
MatrixOp::ColumnMajorLoad.intrinsic_name(),
"llvm.matrix.column.major.load"
);
assert_eq!(
MatrixOp::ColumnMajorStore.intrinsic_name(),
"llvm.matrix.column.major.store"
);
assert_eq!(MatrixOp::Multiply.intrinsic_name(), "llvm.matrix.multiply");
}
#[test]
fn test_hw_loop_intrinsic_names() {
assert_eq!(
HWLoopOp::SetLoopIterations.intrinsic_name(),
"llvm.set.loop.iterations"
);
assert_eq!(
HWLoopOp::LoopDecrementReg.intrinsic_name(),
"llvm.loop.decrement.reg"
);
assert_eq!(
HWLoopOp::LoopDecrement.intrinsic_name(),
"llvm.loop.decrement"
);
}
#[test]
fn test_gc_intrinsic_names() {
assert_eq!(
GCOp::Statepoint.intrinsic_name(),
"llvm.experimental.gc.statepoint"
);
assert_eq!(
GCOp::GcResult.intrinsic_name(),
"llvm.experimental.gc.result"
);
assert_eq!(
GCOp::GcRelocate.intrinsic_name(),
"llvm.experimental.gc.relocate"
);
}
#[test]
fn test_coro_intrinsic_names() {
assert_eq!(CoroOp::CoroId.intrinsic_name(), "llvm.coro.id");
assert_eq!(CoroOp::CoroBegin.intrinsic_name(), "llvm.coro.begin");
assert_eq!(CoroOp::CoroEnd.intrinsic_name(), "llvm.coro.end");
assert_eq!(CoroOp::CoroSuspend.intrinsic_name(), "llvm.coro.suspend");
assert_eq!(CoroOp::CoroFree.intrinsic_name(), "llvm.coro.free");
assert_eq!(CoroOp::CoroAlloc.intrinsic_name(), "llvm.coro.alloc");
assert_eq!(CoroOp::CoroFrame.intrinsic_name(), "llvm.coro.frame");
assert_eq!(CoroOp::CoroSave.intrinsic_name(), "llvm.coro.save");
assert_eq!(CoroOp::CoroPromise.intrinsic_name(), "llvm.coro.promise");
assert_eq!(CoroOp::CoroSize.intrinsic_name(), "llvm.coro.size");
}
#[test]
fn test_debug_intrinsic_names() {
assert_eq!(
DebugIntrinsic::DbgDeclare.intrinsic_name(),
"llvm.dbg.declare"
);
assert_eq!(DebugIntrinsic::DbgValue.intrinsic_name(), "llvm.dbg.value");
assert_eq!(DebugIntrinsic::DbgLabel.intrinsic_name(), "llvm.dbg.label");
assert_eq!(DebugIntrinsic::DbgAddr.intrinsic_name(), "llvm.dbg.addr");
}
#[test]
fn test_stack_safety_intrinsic_names() {
assert_eq!(
StackSafetyOp::StackProtector.intrinsic_name(),
"llvm.stackprotector"
);
assert_eq!(StackSafetyOp::StackSave.intrinsic_name(), "llvm.stacksave");
assert_eq!(
StackSafetyOp::StackRestore.intrinsic_name(),
"llvm.stackrestore"
);
}
#[test]
fn test_builder_new_empty() {
let builder = X86IRBuilderDeep::new_empty();
assert_eq!(builder.preferred_int_width, 32);
assert!(!builder.verbose);
assert!(!builder.x86_tuning);
assert!(builder.target_triple_hint.is_none());
}
#[test]
fn test_builder_from_context() {
let ctx = LLVMContext::new();
let builder = X86IRBuilderDeep::from_context(&ctx);
assert_eq!(builder.preferred_int_width, 32);
}
#[test]
fn test_builder_new() {
let base = IRBuilder::new_empty();
let builder = X86IRBuilderDeep::new(base);
assert_eq!(builder.preferred_int_width, 32);
}
#[test]
fn test_builder_set_verbose() {
let mut builder = make_builder();
builder.verbose = true;
assert!(builder.verbose);
}
#[test]
fn test_builder_set_x86_tuning() {
let mut builder = make_builder();
builder.x86_tuning = true;
assert!(builder.x86_tuning);
}
#[test]
fn test_builder_set_target_triple_hint() {
let mut builder = make_builder();
builder.target_triple_hint = Some("x86_64-unknown-linux-gnu".to_string());
assert_eq!(
builder.target_triple_hint.as_deref(),
Some("x86_64-unknown-linux-gnu")
);
}
#[test]
fn test_builder_base_access() {
let mut builder = make_builder();
let _base = builder.base_mut();
let _base_ref = builder.base_ref();
}
#[test]
fn test_build_atomic_rmw_xchg() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let val = constants::const_i32(42);
let result = builder.build_atomic_rmw(
AtomicRMWBinOp::Xchg,
ptr,
val,
AtomicOrdering::SeqCst,
"test_xchg",
);
let inner = result.borrow();
assert_eq!(inner.opcode, Some(Opcode::AtomicRMW));
assert_eq!(inner.num_operands, 4);
assert_eq!(inner.subclass_data, AtomicRMWBinOp::Xchg as u32);
assert!(inner.name.contains("test_xchg"));
}
#[test]
fn test_build_atomic_rmw_all_16_ops() {
let ops = [
AtomicRMWBinOp::Xchg,
AtomicRMWBinOp::Add,
AtomicRMWBinOp::Sub,
AtomicRMWBinOp::And,
AtomicRMWBinOp::Nand,
AtomicRMWBinOp::Or,
AtomicRMWBinOp::Xor,
AtomicRMWBinOp::Max,
AtomicRMWBinOp::Min,
AtomicRMWBinOp::UMax,
AtomicRMWBinOp::UMin,
AtomicRMWBinOp::FAdd,
AtomicRMWBinOp::FSub,
AtomicRMWBinOp::FMax,
AtomicRMWBinOp::FMin,
AtomicRMWBinOp::UIncWrap,
];
for &op in &ops {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let val = constants::const_i32(1);
let result = builder.build_atomic_rmw(op, ptr, val, AtomicOrdering::SeqCst, "test_op");
let inner = result.borrow();
assert_eq!(inner.opcode, Some(Opcode::AtomicRMW));
assert_eq!(inner.subclass_data, op as u32);
}
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let val = constants::const_i32(1);
let result = builder.build_atomic_rmw(
AtomicRMWBinOp::UDecWrap,
ptr,
val,
AtomicOrdering::SeqCst,
"test_udec",
);
assert_eq!(
result.borrow().subclass_data,
AtomicRMWBinOp::UDecWrap as u32
);
}
#[test]
fn test_build_atomic_shortcuts() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let val = constants::const_i32(1);
let ord = AtomicOrdering::SeqCst;
let r = builder.build_atomic_xchg(ptr.clone(), val.clone(), ord, "xchg");
assert_eq!(r.borrow().subclass_data, AtomicRMWBinOp::Xchg as u32);
let r = builder.build_atomic_add(ptr.clone(), val.clone(), ord, "add");
assert_eq!(r.borrow().subclass_data, AtomicRMWBinOp::Add as u32);
let r = builder.build_atomic_sub(ptr.clone(), val.clone(), ord, "sub");
assert_eq!(r.borrow().subclass_data, AtomicRMWBinOp::Sub as u32);
let r = builder.build_atomic_and(ptr.clone(), val.clone(), ord, "and");
assert_eq!(r.borrow().subclass_data, AtomicRMWBinOp::And as u32);
let r = builder.build_atomic_nand(ptr.clone(), val.clone(), ord, "nand");
assert_eq!(r.borrow().subclass_data, AtomicRMWBinOp::Nand as u32);
let r = builder.build_atomic_or(ptr.clone(), val.clone(), ord, "or");
assert_eq!(r.borrow().subclass_data, AtomicRMWBinOp::Or as u32);
let r = builder.build_atomic_xor(ptr.clone(), val.clone(), ord, "xor");
assert_eq!(r.borrow().subclass_data, AtomicRMWBinOp::Xor as u32);
}
#[test]
fn test_build_cmpxchg_strong() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let cmp = constants::const_i32(0);
let new_val = constants::const_i32(10);
let result = builder.build_cmpxchg_strong(
ptr,
cmp,
new_val,
AtomicOrdering::SeqCst,
AtomicOrdering::Acquire,
"cmpxchg",
);
let inner = result.borrow();
assert_eq!(inner.opcode, Some(Opcode::CmpXchg));
assert_eq!(inner.num_operands, 6);
}
#[test]
fn test_build_cmpxchg_weak() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let cmp = constants::const_i32(0);
let new_val = constants::const_i32(10);
let result = builder.build_cmpxchg_weak(
ptr,
cmp,
new_val,
AtomicOrdering::SeqCst,
AtomicOrdering::Acquire,
"cmpxchg",
);
assert_eq!(result.borrow().opcode, Some(Opcode::CmpXchg));
}
#[test]
fn test_build_fence_all_orderings() {
let orderings = [
AtomicOrdering::NotAtomic,
AtomicOrdering::Unordered,
AtomicOrdering::Monotonic,
AtomicOrdering::Acquire,
AtomicOrdering::Release,
AtomicOrdering::AcqRel,
AtomicOrdering::SeqCst,
];
for &ord in &orderings {
let mut builder = make_builder();
let result = builder.build_fence(ord);
assert_eq!(result.borrow().opcode, Some(Opcode::Fence));
assert_eq!(result.borrow().subclass_data, ord as u32);
}
}
#[test]
fn test_build_fence_shortcuts() {
let mut builder = make_builder();
let r = builder.build_seq_cst_fence();
assert_eq!(r.borrow().subclass_data, AtomicOrdering::SeqCst as u32);
let mut builder = make_builder();
let r = builder.build_acquire_fence();
assert_eq!(r.borrow().subclass_data, AtomicOrdering::Acquire as u32);
let mut builder = make_builder();
let r = builder.build_release_fence();
assert_eq!(r.borrow().subclass_data, AtomicOrdering::Release as u32);
}
#[test]
fn test_build_atomic_load_store() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let loaded = builder.build_atomic_load(ptr.clone(), AtomicOrdering::SeqCst, "load");
assert_eq!(loaded.borrow().opcode, Some(Opcode::Load));
assert_eq!(loaded.borrow().subclass_data, AtomicOrdering::SeqCst as u32);
let mut builder = make_builder();
let val = constants::const_i32(42);
let stored = builder.build_atomic_store(val, ptr, AtomicOrdering::Release);
assert_eq!(stored.borrow().opcode, Some(Opcode::Store));
assert_eq!(
stored.borrow().subclass_data,
AtomicOrdering::Release as u32
);
}
#[test]
fn test_build_seq_cst_load_store() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let loaded = builder.build_seq_cst_load(ptr.clone(), "scl");
assert_eq!(loaded.borrow().subclass_data, AtomicOrdering::SeqCst as u32);
let mut builder = make_builder();
let val = constants::const_i32(42);
let stored = builder.build_seq_cst_store(val, ptr);
assert_eq!(stored.borrow().subclass_data, AtomicOrdering::SeqCst as u32);
}
#[test]
fn test_build_extract_element() {
let mut builder = make_builder();
let elem_ty = Type::i32();
let vec_ty = Type::fixed_vector_with(4, elem_ty.id);
let vec = {
let mut v = Value::new(vec_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
let elem = builder.build_extract_element(vec, 2, "elem");
assert_eq!(elem.borrow().opcode, Some(Opcode::ExtractElement));
assert_eq!(elem.borrow().subclass_data, 2);
}
#[test]
fn test_build_insert_element() {
let mut builder = make_builder();
let elem_ty = Type::i32();
let vec_ty = Type::fixed_vector_with(4, elem_ty.id);
let vec = {
let mut v = Value::new(vec_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
let scalar = constants::const_i32(99);
let result = builder.build_insert_element(vec, scalar, 1, "inserted");
assert_eq!(result.borrow().opcode, Some(Opcode::InsertElement));
assert_eq!(result.borrow().subclass_data, 1);
}
#[test]
fn test_build_vector_broadcast() {
let mut builder = make_builder();
let scalar = constants::const_f32(3.14);
let result = builder.build_vector_broadcast(scalar, 4, "splat");
let inner = result.borrow();
assert_eq!(inner.opcode, Some(Opcode::InsertElement));
assert_eq!(inner.subclass_data, 0);
}
#[test]
fn test_build_vector_reverse() {
let mut builder = make_builder();
let elem_ty = Type::i32();
let vec_ty = Type::fixed_vector_with(4, elem_ty.id);
let vec = {
let mut v = Value::new(vec_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
let result = builder.build_vector_reverse(vec, "rev");
assert_eq!(result.borrow().opcode, Some(Opcode::ShuffleVector));
assert_eq!(result.borrow().subclass_data, 1); }
#[test]
fn test_build_vector_interleave() {
let mut builder = make_builder();
let elem_ty = Type::i32();
let vec_ty = Type::fixed_vector_with(2, elem_ty.id);
let v1 = {
let mut v = Value::new(vec_ty.clone()).with_subclass(SubclassKind::Constant);
valref(v)
};
let v2 = {
let mut v = Value::new(vec_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
let result = builder.build_vector_interleave(v1, v2, 2, "inter");
assert_eq!(result.borrow().opcode, Some(Opcode::ShuffleVector));
}
#[test]
fn test_build_vector_zip() {
let mut builder = make_builder();
let elem_ty = Type::i32();
let vec_ty = Type::fixed_vector_with(2, elem_ty.id);
let v1 = {
let mut v = Value::new(vec_ty.clone()).with_subclass(SubclassKind::Constant);
valref(v)
};
let v2 = {
let mut v = Value::new(vec_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
let result = builder.build_vector_zip(v1, v2, 2, "zip");
assert_eq!(result.borrow().opcode, Some(Opcode::ShuffleVector));
}
#[test]
fn test_build_vector_from_elements() {
let mut builder = make_builder();
let elements = vec![
constants::const_i32(1),
constants::const_i32(2),
constants::const_i32(3),
constants::const_i32(4),
];
let result = builder.build_vector(&elements, "vec");
let inner = result.borrow();
assert!(inner.opcode.is_some());
}
#[test]
fn test_build_vector_empty() {
let mut builder = make_builder();
let elements: Vec<ValueRef> = vec![];
let result = builder.build_vector(&elements, "empty_vec");
let inner = result.borrow();
assert_eq!(inner.subclass, SubclassKind::Constant);
}
#[test]
fn test_build_shufflevector_with_kind_broadcast() {
let mut builder = make_builder();
let elem_ty = Type::i32();
let vec_ty = Type::fixed_vector_with(4, elem_ty.id);
let v1 = {
let mut v = Value::new(vec_ty.clone()).with_subclass(SubclassKind::Constant);
valref(v)
};
let v2 = {
let mut v = Value::new(vec_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
let result =
builder.build_shufflevector_with_kind(v1, v2, ShuffleMaskKind::Broadcast, 4, "bc");
assert_eq!(result.borrow().opcode, Some(Opcode::ShuffleVector));
}
#[test]
fn test_shuffle_mask_kinds_distinct() {
let builder = make_builder();
let kinds = [
ShuffleMaskKind::Broadcast,
ShuffleMaskKind::Reverse,
ShuffleMaskKind::Interleave,
ShuffleMaskKind::Zip,
ShuffleMaskKind::Unzip,
ShuffleMaskKind::Custom,
];
for kind in &kinds {
let mask = builder.build_shuffle_mask(*kind, 4);
assert!(!mask.borrow().name.is_empty() || true); }
}
#[test]
fn test_build_extract_value() {
let mut builder = make_builder();
let struct_ty = Type::struct_literal_with(false, vec![Type::i32().id, Type::i64().id]);
let agg = {
let mut v = Value::new(struct_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
let field = builder.build_extract_value(agg, 1, "field");
assert_eq!(field.borrow().opcode, Some(Opcode::ExtractValue));
assert_eq!(field.borrow().subclass_data, 1);
}
#[test]
fn test_build_insert_value() {
let mut builder = make_builder();
let struct_ty = Type::struct_literal_with(false, vec![Type::i32().id, Type::i64().id]);
let agg = {
let mut v = Value::new(struct_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
let elem = constants::const_i64(42);
let result = builder.build_insert_value(agg, elem, 1, "inserted");
assert_eq!(result.borrow().opcode, Some(Opcode::InsertValue));
assert_eq!(result.borrow().subclass_data, 1);
}
#[test]
fn test_build_extract_value_nested() {
let mut builder = make_builder();
let inner_ty = Type::struct_literal_with(false, vec![Type::i32().id]);
let outer_ty = Type::struct_literal_with(false, vec![inner_ty.id, Type::i64().id]);
let agg = {
let mut v = Value::new(outer_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
let field = builder.build_extract_value_nested(agg, &[0, 0], "nested");
assert_eq!(field.borrow().opcode, Some(Opcode::ExtractValue));
}
#[test]
fn test_build_memcpy() {
let mut builder = make_builder();
let dst = make_value(Type::pointer(0));
let src = make_value(Type::pointer(0));
let size = constants::const_i64(16);
let result = builder.build_memcpy(dst, src, size, 8, false);
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
let func_name = &result.borrow().operands[0].borrow().name;
assert!(func_name.contains("llvm.memcpy"));
}
#[test]
fn test_build_memmove() {
let mut builder = make_builder();
let dst = make_value(Type::pointer(0));
let src = make_value(Type::pointer(0));
let size = constants::const_i64(32);
let result = builder.build_memmove(dst, src, size, 8, true);
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
let func_name = &result.borrow().operands[0].borrow().name;
assert!(func_name.contains("llvm.memmove"));
}
#[test]
fn test_build_memset() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let val = constants::const_i8(0);
let size = constants::const_i64(16);
let result = builder.build_memset(ptr, val, size, 4, false);
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
let func_name = &result.borrow().operands[0].borrow().name;
assert!(func_name.contains("llvm.memset"));
}
#[test]
fn test_build_memset_inline() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let val = constants::const_i8(0xFF);
let size = constants::const_i64(8);
let result = builder.build_memset_inline(ptr, val, size, 1, false);
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
let func_name = &result.borrow().operands[0].borrow().name;
assert!(func_name.contains("llvm.memset.inline"));
}
#[test]
fn test_build_lifetime_start_end() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let size = constants::const_i64(64);
let start = builder.build_lifetime_start(ptr.clone(), size.clone());
assert_eq!(start.borrow().opcode, Some(Opcode::Call));
let end = builder.build_lifetime_end(ptr, size);
assert_eq!(end.borrow().opcode, Some(Opcode::Call));
}
#[test]
fn test_build_invariant_start_end() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let size = constants::const_i64(64);
let start = builder.build_invariant_start(ptr.clone(), size.clone());
assert_eq!(start.borrow().opcode, Some(Opcode::Call));
let end = builder.build_invariant_end(start.clone(), size, ptr);
assert_eq!(end.borrow().opcode, Some(Opcode::Call));
}
#[test]
fn test_build_sqrt() {
let mut builder = make_builder();
let val = constants::const_f64(9.0);
let result = builder.build_sqrt(val, "sqrt");
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
let func_name = &result.borrow().operands[0].borrow().name;
assert!(func_name.contains("llvm.sqrt"));
}
#[test]
fn test_build_sin_cos() {
let mut builder = make_builder();
let val = constants::const_f64(3.14);
let sin = builder.build_sin(val.clone(), "sin");
assert!(sin.borrow().operands[0].borrow().name.contains("llvm.sin"));
let cos = builder.build_cos(val, "cos");
assert!(cos.borrow().operands[0].borrow().name.contains("llvm.cos"));
}
#[test]
fn test_build_pow() {
let mut builder = make_builder();
let x = constants::const_f64(2.0);
let y = constants::const_f64(3.0);
let result = builder.build_pow(x, y, "pow");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.pow"));
}
#[test]
fn test_build_exp() {
let mut builder = make_builder();
let val = constants::const_f64(1.0);
let result = builder.build_exp(val, "exp");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.exp"));
}
#[test]
fn test_build_exp2() {
let mut builder = make_builder();
let val = constants::const_f64(4.0);
let result = builder.build_exp2(val, "exp2");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.exp2"));
}
#[test]
fn test_build_logs() {
let mut builder = make_builder();
let val = constants::const_f64(100.0);
let log = builder.build_log(val.clone(), "log");
assert!(log.borrow().operands[0].borrow().name.contains("llvm.log"));
let log10 = builder.build_log10(val.clone(), "log10");
assert!(log10.borrow().operands[0]
.borrow()
.name
.contains("llvm.log10"));
let log2 = builder.build_log2(val, "log2");
assert!(log2.borrow().operands[0]
.borrow()
.name
.contains("llvm.log2"));
}
#[test]
fn test_build_fma() {
let mut builder = make_builder();
let a = constants::const_f64(2.0);
let b = constants::const_f64(3.0);
let c = constants::const_f64(4.0);
let result = builder.build_fma(a, b, c, "fma");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.fma"));
}
#[test]
fn test_build_floor_ceil_trunc() {
let mut builder = make_builder();
let val = constants::const_f64(3.14);
let floor = builder.build_floor(val.clone(), "floor");
assert!(floor.borrow().operands[0]
.borrow()
.name
.contains("llvm.floor"));
let ceil = builder.build_ceil(val.clone(), "ceil");
assert!(ceil.borrow().operands[0]
.borrow()
.name
.contains("llvm.ceil"));
let trunc = builder.build_ftrunc(val, "trunc");
assert!(trunc.borrow().operands[0]
.borrow()
.name
.contains("llvm.trunc"));
}
#[test]
fn test_build_round_roundeven() {
let mut builder = make_builder();
let val = constants::const_f64(3.5);
let round = builder.build_fround(val.clone(), "round");
assert!(round.borrow().operands[0]
.borrow()
.name
.contains("llvm.round"));
let roundeven = builder.build_roundeven(val, "roundeven");
assert!(roundeven.borrow().operands[0]
.borrow()
.name
.contains("llvm.roundeven"));
}
#[test]
fn test_build_fabs() {
let mut builder = make_builder();
let val = constants::const_f64(-3.14);
let result = builder.build_fabs(val, "fabs");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.fabs"));
}
#[test]
fn test_build_fmin_fmax() {
let mut builder = make_builder();
let a = constants::const_f64(1.0);
let b = constants::const_f64(2.0);
let min = builder.build_fmin(a.clone(), b.clone(), "min");
assert!(min.borrow().operands[0]
.borrow()
.name
.contains("llvm.minnum"));
let max = builder.build_fmax(a, b, "max");
assert!(max.borrow().operands[0]
.borrow()
.name
.contains("llvm.maxnum"));
}
#[test]
fn test_build_minimum_maximum() {
let mut builder = make_builder();
let a = constants::const_f64(1.0);
let b = constants::const_f64(2.0);
let min = builder.build_minimum(a.clone(), b.clone(), "min");
assert!(min.borrow().operands[0]
.borrow()
.name
.contains("llvm.minimum"));
let max = builder.build_maximum(a, b, "max");
assert!(max.borrow().operands[0]
.borrow()
.name
.contains("llvm.maximum"));
}
#[test]
fn test_build_copysign() {
let mut builder = make_builder();
let mag = constants::const_f64(3.0);
let sign = constants::const_f64(-1.0);
let result = builder.build_copysign(mag, sign, "copysign");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.copysign"));
}
#[test]
fn test_build_ldexp() {
let mut builder = make_builder();
let x = constants::const_f64(1.5);
let exp = constants::const_i32(3);
let result = builder.build_ldexp(x, exp, "ldexp");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.ldexp"));
}
#[test]
fn test_build_bswap() {
let mut builder = make_builder();
let val = constants::const_i32(0x12345678);
let result = builder.build_bswap(val, "bswap");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.bswap"));
}
#[test]
fn test_build_ctpop() {
let mut builder = make_builder();
let val = constants::const_i32(0xFF);
let result = builder.build_ctpop(val, "ctpop");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.ctpop"));
}
#[test]
fn test_build_ctlz_cttz() {
let mut builder = make_builder();
let val = constants::const_i32(0x000F);
let ctlz = builder.build_ctlz(val.clone(), false, "ctlz");
assert!(ctlz.borrow().operands[0]
.borrow()
.name
.contains("llvm.ctlz"));
let cttz = builder.build_cttz(val, true, "cttz");
assert!(cttz.borrow().operands[0]
.borrow()
.name
.contains("llvm.cttz"));
}
#[test]
fn test_build_bitreverse() {
let mut builder = make_builder();
let val = constants::const_i32(0x12345678);
let result = builder.build_bitreverse(val, "bitrev");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.bitreverse"));
}
#[test]
fn test_build_fshl_fshr() {
let mut builder = make_builder();
let a = constants::const_i32(0x12345678);
let b = constants::const_i32(0x9ABCDEF0);
let shamt = constants::const_i32(8);
let fshl = builder.build_fshl(a.clone(), b.clone(), shamt.clone(), "fshl");
assert!(fshl.borrow().operands[0]
.borrow()
.name
.contains("llvm.fshl"));
let fshr = builder.build_fshr(a, b, shamt, "fshr");
assert!(fshr.borrow().operands[0]
.borrow()
.name
.contains("llvm.fshr"));
}
#[test]
fn test_build_sadd_overflow() {
let mut builder = make_builder();
let a = constants::const_i32(0x7FFFFFFF);
let b = constants::const_i32(1);
let result = builder.build_sadd_overflow(a, b, "sadd");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.sadd.with.overflow"));
}
#[test]
fn test_build_uadd_overflow() {
let mut builder = make_builder();
let a = constants::const_i32(0xFFFFFFFFu32 as i32);
let b = constants::const_i32(1);
let result = builder.build_uadd_overflow(a, b, "uadd");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.uadd.with.overflow"));
}
#[test]
fn test_build_all_overflow_ops() {
let ops = [
OverflowOp::SAddO,
OverflowOp::UAddO,
OverflowOp::SSubO,
OverflowOp::USubO,
OverflowOp::SMulO,
OverflowOp::UMulO,
];
for &op in &ops {
let mut builder = make_builder();
let a = constants::const_i32(10);
let b = constants::const_i32(20);
let result = builder.build_overflow_arith(op, a, b, "test");
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
}
}
#[test]
fn test_build_sadd_sat() {
let mut builder = make_builder();
let a = constants::const_i32(100);
let b = constants::const_i32(50);
let result = builder.build_sadd_sat(a, b, "sadd_sat");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.sadd.sat"));
}
#[test]
fn test_build_uadd_sat() {
let mut builder = make_builder();
let a = constants::const_i32(100);
let b = constants::const_i32(50);
let result = builder.build_uadd_sat(a, b, "uadd_sat");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.uadd.sat"));
}
#[test]
fn test_build_ssub_sat() {
let mut builder = make_builder();
let a = constants::const_i32(100);
let b = constants::const_i32(50);
let result = builder.build_ssub_sat(a, b, "ssub_sat");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.ssub.sat"));
}
#[test]
fn test_build_usub_sat() {
let mut builder = make_builder();
let a = constants::const_i32(100);
let b = constants::const_i32(50);
let result = builder.build_usub_sat(a, b, "usub_sat");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.usub.sat"));
}
#[test]
fn test_build_sshl_sat() {
let mut builder = make_builder();
let a = constants::const_i32(100);
let b = constants::const_i32(3);
let result = builder.build_sshl_sat(a, b, "sshl_sat");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.sshl.sat"));
}
#[test]
fn test_build_ushl_sat() {
let mut builder = make_builder();
let a = constants::const_i32(100);
let b = constants::const_i32(3);
let result = builder.build_ushl_sat(a, b, "ushl_sat");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.ushl.sat"));
}
#[test]
fn test_build_all_saturating_ops() {
let ops = [
SaturatingOp::SAddSat,
SaturatingOp::UAddSat,
SaturatingOp::SSubSat,
SaturatingOp::USubSat,
SaturatingOp::SShlSat,
SaturatingOp::UShlSat,
];
for &op in &ops {
let mut builder = make_builder();
let a = constants::const_i32(10);
let b = constants::const_i32(20);
let result = builder.build_saturating_arith(op, a, b, "test");
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
}
}
#[test]
fn test_build_smul_fix() {
let mut builder = make_builder();
let a = constants::const_i32(100);
let b = constants::const_i32(200);
let result = builder.build_smul_fix(a, b, 8, "smul_fix");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.smul.fix"));
}
#[test]
fn test_build_umul_fix() {
let mut builder = make_builder();
let a = constants::const_i32(100);
let b = constants::const_i32(200);
let result = builder.build_umul_fix(a, b, 8, "umul_fix");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.umul.fix"));
}
#[test]
fn test_build_sdiv_fix() {
let mut builder = make_builder();
let a = constants::const_i32(1000);
let b = constants::const_i32(2);
let result = builder.build_sdiv_fix(a, b, 4, "sdiv_fix");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.sdiv.fix"));
}
#[test]
fn test_build_udiv_fix() {
let mut builder = make_builder();
let a = constants::const_i32(1000);
let b = constants::const_i32(2);
let result = builder.build_udiv_fix(a, b, 4, "udiv_fix");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.udiv.fix"));
}
#[test]
fn test_build_get_rounding() {
let mut builder = make_builder();
let result = builder.build_get_rounding("round");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.get.rounding"));
}
#[test]
fn test_build_set_rounding() {
let mut builder = make_builder();
let mode = constants::const_i32(RoundingMode::Tonearest.as_u32() as i32);
let result = builder.build_set_rounding(mode);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.set.rounding"));
}
#[test]
fn test_build_clear_restore_fp_exceptions() {
let mut builder = make_builder();
let clear = builder.build_clear_fp_exceptions();
assert!(clear.borrow().operands[0]
.borrow()
.name
.contains("llvm.clear.fp.exceptions"));
let mask = constants::const_i32(0);
let restore = builder.build_restore_fp_exceptions(mask);
assert!(restore.borrow().operands[0]
.borrow()
.name
.contains("llvm.restore.fp.exceptions"));
}
#[test]
fn test_build_constrained_fadd() {
let mut builder = make_builder();
let a = constants::const_f64(1.0);
let b = constants::const_f64(2.0);
let result = builder.build_constrained_fadd(
a,
b,
RoundingMode::Tonearest,
FPExceptionBehavior::Strict,
"cfadd",
);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.experimental.constrained.fadd"));
}
#[test]
fn test_build_constrained_fdiv() {
let mut builder = make_builder();
let a = constants::const_f64(1.0);
let b = constants::const_f64(2.0);
let result = builder.build_constrained_fdiv(
a,
b,
RoundingMode::Towardzero,
FPExceptionBehavior::Maytrap,
"cfdiv",
);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.experimental.constrained.fdiv"));
}
#[test]
fn test_build_constrained_fma() {
let mut builder = make_builder();
let a = constants::const_f64(1.0);
let b = constants::const_f64(2.0);
let c = constants::const_f64(3.0);
let result = builder.build_constrained_fma(
a,
b,
c,
RoundingMode::Dynamic,
FPExceptionBehavior::Ignore,
"cfma",
);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.experimental.constrained.fma"));
}
#[test]
fn test_build_all_constrained_fp_ops() {
let ops = [
ConstrainedFPOp::FAdd,
ConstrainedFPOp::FSub,
ConstrainedFPOp::FMul,
ConstrainedFPOp::FDiv,
ConstrainedFPOp::FRem,
ConstrainedFPOp::FMA,
];
for &op in &ops {
let mut builder = make_builder();
let a = constants::const_f64(1.0);
let b = constants::const_f64(2.0);
let c = constants::const_f64(3.0);
let args = if op == ConstrainedFPOp::FMA {
vec![a, b, c]
} else {
vec![a, b]
};
let result = builder.build_constrained_fp(
op,
args,
RoundingMode::Tonearest,
FPExceptionBehavior::Strict,
"test",
);
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
}
}
#[test]
fn test_build_matrix_column_major_load() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let result = builder.build_matrix_column_major_load(ptr, 4, 4, 4, "mat_load");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.matrix.column.major.load"));
}
#[test]
fn test_build_matrix_column_major_store() {
let mut builder = make_builder();
let matrix = make_value(Type::double());
let ptr = make_value(Type::pointer(0));
let result = builder.build_matrix_column_major_store(matrix, ptr, 4, 4, 4);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.matrix.column.major.store"));
}
#[test]
fn test_build_matrix_multiply() {
let mut builder = make_builder();
let a = make_value(Type::double());
let b = make_value(Type::double());
let result = builder.build_matrix_multiply(a, b, 4, 4, 4, "mat_mul");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.matrix.multiply"));
}
#[test]
fn test_build_set_loop_iterations() {
let mut builder = make_builder();
let count = constants::const_i32(100);
let result = builder.build_set_loop_iterations(count);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.set.loop.iterations"));
}
#[test]
fn test_build_loop_decrement_reg() {
let mut builder = make_builder();
let counter = constants::const_i32(10);
let result = builder.build_loop_decrement_reg(counter, "dec");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.loop.decrement.reg"));
}
#[test]
fn test_build_loop_decrement() {
let mut builder = make_builder();
let counter = constants::const_i32(10);
let result = builder.build_loop_decrement(counter, "dec");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.loop.decrement"));
}
#[test]
fn test_build_stackprotector() {
let mut builder = make_builder();
let guard = make_value(Type::pointer(0));
let result = builder.build_stackprotector(guard);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.stackprotector"));
}
#[test]
fn test_build_stacksave_restore() {
let mut builder = make_builder();
let saved = builder.build_stacksave("sp");
assert!(saved.borrow().operands[0]
.borrow()
.name
.contains("llvm.stacksave"));
let restored = builder.build_stackrestore(saved);
assert!(restored.borrow().operands[0]
.borrow()
.name
.contains("llvm.stackrestore"));
}
#[test]
fn test_build_threadlocal_address() {
let mut builder = make_builder();
let tls_var = make_value(Type::pointer(0));
let addr = builder.build_threadlocal_address(tls_var, TLSModel::GeneralDynamic, "tls_addr");
assert!(addr.borrow().operands[0]
.borrow()
.name
.contains("llvm.threadlocal.address"));
}
#[test]
fn test_build_tls_all_models() {
let mut builder = make_builder();
let tls_var = make_value(Type::pointer(0));
let gd = builder.build_tls_general_dynamic(tls_var.clone(), "gd");
assert!(gd.borrow().operands[0]
.borrow()
.name
.contains("llvm.threadlocal.address"));
let ld = builder.build_tls_local_dynamic(tls_var.clone(), "ld");
assert!(ld.borrow().operands[0]
.borrow()
.name
.contains("llvm.threadlocal.address"));
let ie = builder.build_tls_initial_exec(tls_var.clone(), "ie");
assert!(ie.borrow().operands[0]
.borrow()
.name
.contains("llvm.threadlocal.address"));
let le = builder.build_tls_local_exec(tls_var, "le");
assert!(le.borrow().operands[0]
.borrow()
.name
.contains("llvm.threadlocal.address"));
}
#[test]
fn test_build_gc_statepoint() {
let mut builder = make_builder();
let target = make_value(Type::void());
let result =
builder.build_gc_statepoint(target, 0, 0, vec![], vec![], vec![], vec![], "sp");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.experimental.gc.statepoint"));
}
#[test]
fn test_build_gc_result() {
let mut builder = make_builder();
let token = make_value(Type::token());
let result = builder.build_gc_result(token, &Type::i32(), "gc_res");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.experimental.gc.result"));
}
#[test]
fn test_build_gc_relocate() {
let mut builder = make_builder();
let token = make_value(Type::token());
let result = builder.build_gc_relocate(token, 0, 1, &Type::pointer(0), "gc_reloc");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.experimental.gc.relocate"));
}
#[test]
fn test_build_coro_id() {
let mut builder = make_builder();
let align = constants::const_i32(8);
let promise = make_value(Type::pointer(0));
let coro = make_value(Type::pointer(0));
let fn_ptr = make_value(Type::void());
let result = builder.build_coro_id(align, promise, coro, fn_ptr, "id");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.coro.id"));
}
#[test]
fn test_build_coro_begin() {
let mut builder = make_builder();
let id = make_value(Type::token());
let mem = make_value(Type::pointer(0));
let result = builder.build_coro_begin(id, mem, "begin");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.coro.begin"));
}
#[test]
fn test_build_coro_end() {
let mut builder = make_builder();
let handle = make_value(Type::pointer(0));
let result = builder.build_coro_end(handle, false);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.coro.end"));
}
#[test]
fn test_build_coro_suspend() {
let mut builder = make_builder();
let save = make_value(Type::token());
let result = builder.build_coro_suspend(save, false, "suspend");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.coro.suspend"));
}
#[test]
fn test_build_coro_free_alloc() {
let mut builder = make_builder();
let id = make_value(Type::token());
let free = builder.build_coro_free(id.clone(), "free");
assert!(free.borrow().operands[0]
.borrow()
.name
.contains("llvm.coro.free"));
let alloc = builder.build_coro_alloc(id, "alloc");
assert!(alloc.borrow().operands[0]
.borrow()
.name
.contains("llvm.coro.alloc"));
}
#[test]
fn test_build_coro_frame_save() {
let mut builder = make_builder();
let frame = builder.build_coro_frame("frame");
assert!(frame.borrow().operands[0]
.borrow()
.name
.contains("llvm.coro.frame"));
let handle = make_value(Type::pointer(0));
let save = builder.build_coro_save(handle, "save");
assert!(save.borrow().operands[0]
.borrow()
.name
.contains("llvm.coro.save"));
}
#[test]
fn test_build_coro_promise_size() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let promise = builder.build_coro_promise(ptr, 8, false, "promise");
assert!(promise.borrow().operands[0]
.borrow()
.name
.contains("llvm.coro.promise"));
let size = builder.build_coro_size("size");
assert!(size.borrow().operands[0]
.borrow()
.name
.contains("llvm.coro.size"));
}
#[test]
fn test_build_dbg_declare() {
let mut builder = make_builder();
let value = make_value(Type::pointer(0));
let di_var = make_value(Type::metadata());
let di_expr = make_value(Type::metadata());
let di_loc = make_value(Type::metadata());
let result = builder.build_dbg_declare(value, di_var, di_expr, di_loc);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.dbg.declare"));
}
#[test]
fn test_build_dbg_value() {
let mut builder = make_builder();
let value = make_value(Type::i32());
let di_var = make_value(Type::metadata());
let di_expr = make_value(Type::metadata());
let di_loc = make_value(Type::metadata());
let result = builder.build_dbg_value(value, di_var, di_expr, di_loc);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.dbg.value"));
}
#[test]
fn test_build_dbg_label() {
let mut builder = make_builder();
let di_label = make_value(Type::metadata());
let di_loc = make_value(Type::metadata());
let result = builder.build_dbg_label(di_label, di_loc);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.dbg.label"));
}
#[test]
fn test_build_dbg_addr() {
let mut builder = make_builder();
let value = make_value(Type::pointer(0));
let di_var = make_value(Type::metadata());
let di_expr = make_value(Type::metadata());
let di_loc = make_value(Type::metadata());
let result = builder.build_dbg_addr(value, di_var, di_expr, di_loc);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.dbg.addr"));
}
#[test]
fn test_build_landingpad() {
let mut builder = make_builder();
let result_ty = Type::struct_literal_with(false, vec![Type::pointer(0).id, Type::i32().id]);
let result = builder.build_landingpad(&result_ty, true, vec![], "lp");
assert_eq!(result.borrow().opcode, Some(Opcode::LandingPad));
assert_eq!(result.borrow().subclass_data, 1); }
#[test]
fn test_build_resume() {
let mut builder = make_builder();
let exn = make_value(Type::pointer(0));
let result = builder.build_resume(exn);
assert_eq!(result.borrow().opcode, Some(Opcode::Resume));
}
#[test]
fn test_build_cleanupret() {
let mut builder = make_builder();
let pad = make_value(Type::token());
let unwind_dest = Some(make_value(Type::label()));
let result = builder.build_cleanupret(pad, unwind_dest);
assert_eq!(result.borrow().opcode, Some(Opcode::CleanupRet));
}
#[test]
fn test_build_catchret() {
let mut builder = make_builder();
let pad = make_value(Type::token());
let dest = make_value(Type::label());
let result = builder.build_catchret(pad, dest);
assert_eq!(result.borrow().opcode, Some(Opcode::CatchRet));
}
#[test]
fn test_build_catchswitch() {
let mut builder = make_builder();
let parent = make_value(Type::token());
let handler = make_value(Type::label());
let result = builder.build_catchswitch(parent, None, vec![handler], "cs");
assert_eq!(result.borrow().opcode, Some(Opcode::CatchSwitch));
}
#[test]
fn test_build_catchpad() {
let mut builder = make_builder();
let cs = make_value(Type::token());
let result = builder.build_catchpad(cs, vec![], "catch");
assert_eq!(result.borrow().opcode, Some(Opcode::CatchPad));
}
#[test]
fn test_build_cleanuppad() {
let mut builder = make_builder();
let parent = make_value(Type::token());
let result = builder.build_cleanuppad(parent, vec![], "cleanup");
assert_eq!(result.borrow().opcode, Some(Opcode::CleanupPad));
}
#[test]
fn test_build_invoke() {
let mut builder = make_builder();
let callee = make_value(Type::void());
let normal = make_value(Type::label());
let unwind = make_value(Type::label());
let result = builder.build_invoke(callee, vec![], normal, unwind, &Type::void(), "inv");
assert_eq!(result.borrow().opcode, Some(Opcode::Invoke));
}
#[test]
fn test_build_freeze() {
let mut builder = make_builder();
let val = constants::const_i32(42);
let result = builder.build_freeze(val, "frozen");
assert_eq!(result.borrow().opcode, Some(Opcode::Freeze));
}
#[test]
fn test_verbose_naming() {
let mut builder = make_builder();
builder.verbose = true;
let val = constants::const_f64(9.0);
let result = builder.build_sqrt(val, "");
let inner = result.borrow();
assert!(!inner.name.is_empty()); }
#[test]
fn test_pattern_builder_creation() {
let pb = make_pattern_builder();
let _base = pb.deep_builder();
}
#[test]
fn test_pattern_builder_from_context() {
let ctx = LLVMContext::new();
let pb = X86IRPatternBuilder::from_context(&ctx);
assert!(!pb.builder.verbose);
}
#[test]
fn test_build_call() {
let mut pb = make_pattern_builder();
let callee = {
let mut fv = Value::new(Type::void()).with_subclass(SubclassKind::Function);
fv.name = "my_func".to_string();
valref(fv)
};
let arg1 = constants::const_i32(42);
let result = pb.build_call(callee, &[arg1], &Type::i32(), "call");
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
}
#[test]
fn test_build_global_variable() {
let mut pb = make_pattern_builder();
let init = constants::const_i32(42);
let gv = pb.build_global_variable("my_global", &Type::i32(), init, true);
let inner = gv.borrow();
assert_eq!(inner.subclass, SubclassKind::GlobalVariable);
assert_eq!(inner.subclass_data, 1); assert_eq!(inner.name, "my_global");
}
#[test]
fn test_build_string_global() {
let mut pb = make_pattern_builder();
let gv = pb.build_string_global("hello", "world");
assert_eq!(gv.borrow().subclass, SubclassKind::GlobalVariable);
assert_eq!(gv.borrow().name, "hello");
}
#[test]
fn test_build_struct_gep() {
let mut pb = make_pattern_builder();
let ptr = make_value(Type::pointer(0));
let gep = pb.build_struct_gep(ptr, &[0, 2], "gep");
assert_eq!(gep.borrow().opcode, Some(Opcode::GetElementPtr));
assert_eq!(gep.borrow().subclass_data, 1); }
#[test]
fn test_build_struct_gep_empty_indices() {
let mut pb = make_pattern_builder();
let ptr = make_value(Type::pointer(0));
let gep = pb.build_struct_gep(ptr.clone(), &[], "gep");
assert!(
gep.borrow().opcode.is_none() || gep.borrow().opcode == Some(Opcode::GetElementPtr)
);
}
#[test]
fn test_build_array_gep() {
let mut pb = make_pattern_builder();
let ptr = make_value(Type::pointer(0));
let index = constants::const_i32(5);
let gep = pb.build_array_gep(ptr, index, "arr_gep");
assert_eq!(gep.borrow().opcode, Some(Opcode::GetElementPtr));
assert_eq!(gep.borrow().subclass_data, 1);
}
#[test]
fn test_build_multi_array_gep() {
let mut pb = make_pattern_builder();
let ptr = make_value(Type::pointer(0));
let indices = vec![constants::const_i32(2), constants::const_i32(3)];
let gep = pb.build_multi_array_gep(ptr, &indices, "multi_gep");
assert_eq!(gep.borrow().opcode, Some(Opcode::GetElementPtr));
}
#[test]
fn test_build_array_load() {
let mut pb = make_pattern_builder();
let ptr = make_value(Type::pointer(0));
let index = constants::const_i32(3);
let loaded = pb.build_array_load(ptr, index, &Type::i32(), "arr_load");
assert_eq!(loaded.borrow().opcode, Some(Opcode::Load));
}
#[test]
fn test_build_array_store() {
let mut pb = make_pattern_builder();
let ptr = make_value(Type::pointer(0));
let index = constants::const_i32(1);
let value = constants::const_i32(42);
let stored = pb.build_array_store(ptr, index, value);
assert_eq!(stored.borrow().opcode, Some(Opcode::Store));
}
#[test]
fn test_build_nested_struct_access() {
let mut pb = make_pattern_builder();
let ptr = make_value(Type::pointer(0));
let result = pb.build_nested_struct_access(ptr, &[0, 1, 2], "nested");
assert_eq!(result.borrow().opcode, Some(Opcode::GetElementPtr));
}
#[test]
fn test_build_if_then() {
let mut pb = make_pattern_builder();
let cond = constants::const_bool(true);
let mut body_called = false;
pb.build_if_then(cond, |_builder| {
body_called = true;
});
assert!(body_called);
}
#[test]
fn test_build_if_then_else() {
let mut pb = make_pattern_builder();
let cond = constants::const_bool(true);
let mut then_called = false;
let mut else_called = false;
pb.build_if_then_else(
cond,
|_b| {
then_called = true;
Some(constants::const_i32(1))
},
|_b| {
else_called = true;
Some(constants::const_i32(2))
},
);
assert!(then_called);
}
#[test]
fn test_build_while_loop() {
let mut pb = make_pattern_builder();
let mut cond_calls = 0;
let mut body_calls = 0;
pb.build_while_loop(
|_b| {
cond_calls += 1;
if cond_calls > 3 {
constants::const_bool(false)
} else {
constants::const_bool(true)
}
},
|_b| {
body_calls += 1;
},
);
assert_eq!(cond_calls, 1);
assert_eq!(body_calls, 1);
}
#[test]
fn test_build_do_while_loop() {
let mut pb = make_pattern_builder();
let mut body_called = false;
let mut cond_called = false;
pb.build_do_while_loop(
|_b| {
body_called = true;
},
|_b| {
cond_called = true;
constants::const_bool(false)
},
);
assert!(body_called);
assert!(cond_called);
}
#[test]
fn test_build_for_loop() {
let mut pb = make_pattern_builder();
let mut init_called = false;
let mut cond_called = false;
let mut body_called = false;
let mut step_called = false;
pb.build_for_loop(
|_b| {
init_called = true;
},
|_b| {
cond_called = true;
constants::const_bool(false)
},
|_b| {
body_called = true;
},
|_b| {
step_called = true;
},
);
assert!(init_called);
assert!(cond_called);
assert!(body_called);
assert!(step_called);
}
#[test]
fn test_build_switch() {
let mut pb = make_pattern_builder();
let val = constants::const_i32(1);
fn noop_handler(_b: &mut X86IRBuilderDeep) {}
fn noop_default(_b: &mut X86IRBuilderDeep) {}
let cases = vec![
(0i32, noop_handler as fn(&mut X86IRBuilderDeep)),
(1i32, noop_handler as fn(&mut X86IRBuilderDeep)),
];
pb.build_switch(val, cases, noop_default as fn(&mut X86IRBuilderDeep));
assert!(true);
}
#[test]
fn test_smoke_atomic_all_ops() {
let all_ops = [
AtomicRMWBinOp::Xchg,
AtomicRMWBinOp::Add,
AtomicRMWBinOp::Sub,
AtomicRMWBinOp::And,
AtomicRMWBinOp::Nand,
AtomicRMWBinOp::Or,
AtomicRMWBinOp::Xor,
AtomicRMWBinOp::Max,
AtomicRMWBinOp::Min,
AtomicRMWBinOp::UMax,
AtomicRMWBinOp::UMin,
AtomicRMWBinOp::FAdd,
AtomicRMWBinOp::FSub,
AtomicRMWBinOp::FMax,
AtomicRMWBinOp::FMin,
AtomicRMWBinOp::UIncWrap,
AtomicRMWBinOp::UDecWrap,
];
for &op in &all_ops {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let val = constants::const_i32(1);
let _ = builder.build_atomic_rmw(op, ptr, val, AtomicOrdering::SeqCst, "test");
}
}
#[test]
fn test_smoke_math_all_ops() {
let math_ops = [
MathIntrinsicOp::Sqrt,
MathIntrinsicOp::Sin,
MathIntrinsicOp::Cos,
MathIntrinsicOp::Exp,
MathIntrinsicOp::Exp2,
MathIntrinsicOp::Log,
MathIntrinsicOp::Log10,
MathIntrinsicOp::Log2,
MathIntrinsicOp::Floor,
MathIntrinsicOp::Ceil,
MathIntrinsicOp::Trunc,
MathIntrinsicOp::Round,
MathIntrinsicOp::RoundEven,
MathIntrinsicOp::FAbs,
];
for &op in &math_ops {
let mut builder = make_builder();
let val = constants::const_f64(1.0);
let _ = builder.build_math_intrinsic(op, vec![val], "test");
}
}
#[test]
fn test_smoke_bit_manip_all_ops() {
let ops = [BitManipOp::BSwap, BitManipOp::CtPop, BitManipOp::BitReverse];
for &op in &ops {
let mut builder = make_builder();
let val = constants::const_i32(0x42);
let _ = builder.build_bit_manip(op, vec![val], "test");
}
}
#[test]
fn test_smoke_fixed_point_all_ops() {
let ops = [
FixedPointOp::SMulFix,
FixedPointOp::UMulFix,
FixedPointOp::SDivFix,
FixedPointOp::UDivFix,
];
for &op in &ops {
let mut builder = make_builder();
let a = constants::const_i32(100);
let b = constants::const_i32(10);
let _ = builder.build_fixed_point_arith(op, a, b, 4, "test");
}
}
#[test]
fn test_smoke_debug_all_ops() {
let ops = [
DebugIntrinsic::DbgDeclare,
DebugIntrinsic::DbgValue,
DebugIntrinsic::DbgLabel,
DebugIntrinsic::DbgAddr,
];
for &op in &ops {
let mut builder = make_builder();
let v = make_value(Type::i32());
let m = make_value(Type::metadata());
let args = match op {
DebugIntrinsic::DbgLabel => vec![m.clone(), m.clone()],
_ => vec![v.clone(), m.clone(), m.clone(), m.clone()],
};
let _ = builder.build_debug_intrinsic(op, args);
}
}
#[test]
fn test_smoke_coro_all_ops() {
let mut builder = make_builder();
let id = builder.build_coro_id(
constants::const_i32(8),
make_value(Type::pointer(0)),
make_value(Type::pointer(0)),
make_value(Type::void()),
"id",
);
assert!(!id.borrow().name.is_empty());
let _ = builder.build_coro_begin(id.clone(), make_value(Type::pointer(0)), "begin");
let _ = builder.build_coro_free(id.clone(), "free");
let _ = builder.build_coro_alloc(id, "alloc");
let _ = builder.build_coro_frame("frame");
let _ = builder.build_coro_size("size");
}
#[test]
fn test_enum_debug_formatting() {
assert_eq!(format!("{}", RoundingMode::Tonearest.as_u32()), "1");
assert_eq!(format!("{}", FPExceptionBehavior::Strict.as_u32()), "2");
assert_eq!(RoundingMode::Dynamic.as_u32(), 0);
assert_eq!(FPExceptionBehavior::Ignore.as_u32(), 0);
}
#[test]
fn test_const_helper_methods() {
let builder = make_builder();
let _i32 = builder.const_i32(42);
let _i64 = builder.const_i64(42);
let _i8 = builder.const_i8(7);
let _b = builder.const_bool(true);
let _f = builder.const_f32(3.14);
let _d = builder.const_f64(3.14);
let _z = builder.const_zero(Type::i32());
let _u = builder.undef(Type::i32());
let _p = builder.poison(Type::i32());
}
#[test]
fn test_preferred_int_width_setting() {
let mut builder = make_builder();
builder.preferred_int_width = 64;
assert_eq!(builder.preferred_int_width, 64);
}
#[test]
fn test_pattern_if_then_else_phi() {
let mut pb = make_pattern_builder();
let cond = constants::const_bool(true);
let result = pb.build_if_then_else_phi(
cond,
|_b| constants::const_i32(10),
|_b| constants::const_i32(20),
"merged",
);
assert_eq!(result.borrow().opcode, Some(Opcode::Phi));
}
#[test]
fn test_pattern_abs() {
let mut pb = make_pattern_builder();
let val = constants::const_i32(-5);
let result = pb.build_abs(val, "abs_val");
assert_eq!(result.borrow().opcode, Some(Opcode::Select));
}
#[test]
fn test_pattern_smin() {
let mut pb = make_pattern_builder();
let a = constants::const_i32(10);
let b = constants::const_i32(5);
let result = pb.build_smin(a, b, "min");
assert_eq!(result.borrow().opcode, Some(Opcode::Select));
}
#[test]
fn test_pattern_smax() {
let mut pb = make_pattern_builder();
let a = constants::const_i32(10);
let b = constants::const_i32(5);
let result = pb.build_smax(a, b, "max");
assert_eq!(result.borrow().opcode, Some(Opcode::Select));
}
#[test]
fn test_pattern_umin() {
let mut pb = make_pattern_builder();
let a = constants::const_i32(10);
let b = constants::const_i32(5);
let result = pb.build_umin(a, b, "umin");
assert_eq!(result.borrow().opcode, Some(Opcode::Select));
}
#[test]
fn test_pattern_umax() {
let mut pb = make_pattern_builder();
let a = constants::const_i32(10);
let b = constants::const_i32(5);
let result = pb.build_umax(a, b, "umax");
assert_eq!(result.borrow().opcode, Some(Opcode::Select));
}
#[test]
fn test_pattern_ternary() {
let mut pb = make_pattern_builder();
let cond = constants::const_bool(true);
let t = constants::const_i32(1);
let f = constants::const_i32(0);
let result = pb.build_ternary(cond, t, f, "tern");
assert_eq!(result.borrow().opcode, Some(Opcode::Select));
}
#[test]
fn test_pattern_ptr_diff() {
let mut pb = make_pattern_builder();
let pa = make_value(Type::pointer(0));
let pb_ptr = make_value(Type::pointer(0));
let result = pb.build_ptr_diff(pa, pb_ptr, "diff");
assert_eq!(result.borrow().opcode, Some(Opcode::Sub));
}
#[test]
fn test_pattern_bitfield_extract() {
let mut pb = make_pattern_builder();
let val = constants::const_i32(0x12345678);
let result = pb.build_bitfield_extract(val, 8, 4, "bfe");
assert_eq!(result.borrow().opcode, Some(Opcode::And));
}
#[test]
fn test_pattern_bitfield_insert() {
let mut pb = make_pattern_builder();
let val = constants::const_i32(0);
let field = constants::const_i32(0xA);
let result = pb.build_bitfield_insert(val, field, 4, 4, "bfi");
assert_eq!(result.borrow().opcode, Some(Opcode::Or));
}
#[test]
fn test_pattern_manual_bswap() {
let mut pb = make_pattern_builder();
let val = constants::const_i32(0x12345678);
let result = pb.build_manual_bswap(val, "bswap");
assert_eq!(result.borrow().opcode, Some(Opcode::Or));
}
#[test]
fn test_pattern_is_power_of_two() {
let mut pb = make_pattern_builder();
let val = constants::const_i32(8);
let result = pb.build_is_power_of_two(val, "ispow2");
assert_eq!(result.borrow().opcode, Some(Opcode::And));
}
#[test]
fn test_pattern_round_up_pow2() {
let mut pb = make_pattern_builder();
let val = constants::const_i32(13);
let result = pb.build_round_up_pow2(val, "rup2");
assert_eq!(result.borrow().opcode, Some(Opcode::Add));
}
#[test]
fn test_pattern_clamp() {
let mut pb = make_pattern_builder();
let val = constants::const_i32(15);
let lo = constants::const_i32(0);
let hi = constants::const_i32(10);
let result = pb.build_clamp(val, lo, hi, "clamped");
assert_eq!(result.borrow().opcode, Some(Opcode::Select));
}
#[test]
fn test_pattern_safe_sdiv() {
let mut pb = make_pattern_builder();
let num = constants::const_i32(10);
let den = constants::const_i32(0);
let result = pb.build_safe_sdiv(num, den, "safe");
assert_eq!(result.borrow().opcode, Some(Opcode::Select));
}
#[test]
fn test_pattern_range_check() {
let mut pb = make_pattern_builder();
let val = constants::const_i32(5);
let lo = constants::const_i32(0);
let hi = constants::const_i32(10);
let result = pb.build_range_check(val, lo, hi, true, "in_range");
assert_eq!(result.borrow().opcode, Some(Opcode::And));
}
#[test]
fn test_pattern_range_check_unsigned() {
let mut pb = make_pattern_builder();
let val = constants::const_i32(5);
let lo = constants::const_i32(0);
let hi = constants::const_i32(10);
let result = pb.build_range_check(val, lo, hi, false, "in_range_u");
assert_eq!(result.borrow().opcode, Some(Opcode::And));
}
#[test]
fn test_pattern_switch_with_phi() {
let mut pb = make_pattern_builder();
let val = constants::const_i32(1);
fn case0(_b: &mut X86IRBuilderDeep) -> ValueRef {
constants::const_i32(100)
}
fn case1(_b: &mut X86IRBuilderDeep) -> ValueRef {
constants::const_i32(200)
}
fn default_case(_b: &mut X86IRBuilderDeep) -> ValueRef {
constants::const_i32(999)
}
let result = pb.build_switch_with_phi(
val,
vec![
(0, case0 as fn(&mut X86IRBuilderDeep) -> ValueRef),
(1, case1 as fn(&mut X86IRBuilderDeep) -> ValueRef),
],
default_case as fn(&mut X86IRBuilderDeep) -> ValueRef,
"sw_phi",
);
assert_eq!(result.borrow().opcode, Some(Opcode::Phi));
assert!(result.borrow().name.contains("sw_phi"));
}
#[test]
fn test_pattern_null_check() {
let mut pb = make_pattern_builder();
let ptr = make_value(Type::pointer(0));
let mut null_visited = false;
let mut cont_visited = false;
pb.build_null_check(
ptr,
|_b| {
null_visited = true;
},
|_b| {
cont_visited = true;
},
);
assert!(null_visited || cont_visited);
}
#[test]
fn test_pattern_if_then_else_phi_both_branches() {
let mut pb = make_pattern_builder();
let cond = constants::const_bool(false);
let result = pb.build_if_then_else_phi(
cond,
|_b| constants::const_f64(1.0),
|_b| constants::const_f64(2.0),
"fphi",
);
assert_eq!(result.borrow().opcode, Some(Opcode::Phi));
assert_eq!(result.borrow().num_operands, 2);
}
#[test]
fn test_build_masked_load() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let mask = make_value(Type::i1());
let passthru = make_value(Type::i32());
let result = builder.build_masked_load(ptr, 8, mask, passthru, "ml");
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.masked.load"));
}
#[test]
fn test_build_masked_store() {
let mut builder = make_builder();
let val = make_value(Type::i32());
let ptr = make_value(Type::pointer(0));
let mask = make_value(Type::i1());
let result = builder.build_masked_store(val, ptr, 8, mask);
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
}
#[test]
fn test_build_masked_gather() {
let mut builder = make_builder();
let ptrs = make_value(Type::pointer(0));
let mask = make_value(Type::i1());
let passthru = make_value(Type::i32());
let result = builder.build_masked_gather(ptrs, 8, mask, passthru, "mg");
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.masked.gather"));
}
#[test]
fn test_build_masked_scatter() {
let mut builder = make_builder();
let val = make_value(Type::i32());
let ptrs = make_value(Type::pointer(0));
let mask = make_value(Type::i1());
let result = builder.build_masked_scatter(val, ptrs, 8, mask);
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
}
#[test]
fn test_build_vector_reduce_add() {
let mut builder = make_builder();
let vec = {
let vt = Type::fixed_vector_with(4, Type::i32().id);
let mut v = Value::new(vt).with_subclass(SubclassKind::Constant);
valref(v)
};
let result = builder.build_vector_reduce_add(vec, "vradd");
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
}
#[test]
fn test_build_vector_reduce_mul() {
let mut builder = make_builder();
let vec = {
let vt = Type::fixed_vector_with(4, Type::i32().id);
let mut v = Value::new(vt).with_subclass(SubclassKind::Constant);
valref(v)
};
let result = builder.build_vector_reduce_mul(vec, "vrmul");
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
}
#[test]
fn test_build_vector_reduce_all_int_ops() {
let mut builder = make_builder();
let vec = {
let vt = Type::fixed_vector_with(4, Type::i32().id);
let mut v = Value::new(vt).with_subclass(SubclassKind::Constant);
valref(v)
};
let _ = builder.build_vector_reduce_smin(vec.clone(), "vrmin");
let _ = builder.build_vector_reduce_smax(vec.clone(), "vrmax");
let _ = builder.build_vector_reduce_umin(vec.clone(), "vrumin");
let _ = builder.build_vector_reduce_umax(vec.clone(), "vrumax");
let _ = builder.build_vector_reduce_and(vec, "vrand");
}
#[test]
fn test_build_step_vector() {
let mut builder = make_builder();
let result = builder.build_step_vector(8, "step");
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("stepvector"));
}
#[test]
fn test_build_vp_ops() {
let mut builder = make_builder();
let a = make_value(Type::i32());
let b = make_value(Type::i32());
let mask = make_value(Type::i1());
let evl = constants::const_i32(4);
let add = builder.build_vp_add(a.clone(), b.clone(), mask.clone(), evl.clone(), "vpa");
assert!(add.borrow().operands[0]
.borrow()
.name
.contains("llvm.vp.add"));
let sub = builder.build_vp_sub(a.clone(), b.clone(), mask.clone(), evl.clone(), "vps");
assert!(sub.borrow().operands[0]
.borrow()
.name
.contains("llvm.vp.sub"));
let mul = builder.build_vp_mul(a.clone(), b, mask.clone(), evl, "vpm");
assert!(mul.borrow().operands[0]
.borrow()
.name
.contains("llvm.vp.mul"));
}
#[test]
fn test_build_vp_load_store() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let mask = make_value(Type::i1());
let evl = constants::const_i32(4);
let load =
builder.build_vp_load(ptr.clone(), mask.clone(), evl.clone(), &Type::i32(), "vpl");
assert!(load.borrow().operands[0]
.borrow()
.name
.contains("llvm.vp.load"));
let store = builder.build_vp_store(load, ptr, mask, evl);
assert!(store.borrow().operands[0]
.borrow()
.name
.contains("llvm.vp.store"));
}
#[test]
fn test_build_vp_merge() {
let mut builder = make_builder();
let mask = make_value(Type::i1());
let on_true = make_value(Type::i32());
let on_false = make_value(Type::i32());
let evl = constants::const_i32(4);
let result = builder.build_vp_merge(mask, on_true, on_false, evl, "vpmrg");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.vp.merge"));
}
#[test]
fn test_build_assume() {
let mut builder = make_builder();
let cond = constants::const_bool(true);
let result = builder.build_assume(cond);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.assume"));
}
#[test]
fn test_build_expect() {
let mut builder = make_builder();
let val = constants::const_i32(42);
let expected = constants::const_i32(42);
let result = builder.build_expect(val, expected, "exp");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.expect"));
}
#[test]
fn test_build_expect_with_probability() {
let mut builder = make_builder();
let val = constants::const_i32(42);
let expected = constants::const_i32(42);
let result = builder.build_expect_with_probability(val, expected, 0.9, "exp_p");
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.expect.with.probability"));
}
#[test]
fn test_build_trap() {
let mut builder = make_builder();
let result = builder.build_trap();
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.trap"));
}
#[test]
fn test_build_debugtrap() {
let mut builder = make_builder();
let result = builder.build_debugtrap();
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.debugtrap"));
}
#[test]
fn test_build_ubsantrap() {
let mut builder = make_builder();
let result = builder.build_ubsantrap(1);
assert!(result.borrow().operands[0]
.borrow()
.name
.contains("llvm.ubsantrap"));
}
#[test]
fn test_nsw_flag_operations() {
let mut builder = make_builder();
let a = constants::const_i32(10);
let b = constants::const_i32(5);
let add = builder.build_nsw_add(a.clone(), b.clone(), "nsw_add");
assert_eq!(add.borrow().opcode, Some(Opcode::Add));
assert_eq!(add.borrow().subclass_data, 1);
let sub = builder.build_nsw_sub(a.clone(), b.clone(), "nsw_sub");
assert_eq!(sub.borrow().opcode, Some(Opcode::Sub));
assert_eq!(sub.borrow().subclass_data, 1);
let mul = builder.build_nsw_mul(a, b, "nsw_mul");
assert_eq!(mul.borrow().opcode, Some(Opcode::Mul));
assert_eq!(mul.borrow().subclass_data, 1);
}
#[test]
fn test_nuw_flag_operations() {
let mut builder = make_builder();
let a = constants::const_i32(10);
let b = constants::const_i32(5);
let add = builder.build_nuw_add(a.clone(), b.clone(), "nuw_add");
assert_eq!(add.borrow().opcode, Some(Opcode::Add));
assert_eq!(add.borrow().subclass_data, 2);
let sub = builder.build_nuw_sub(a.clone(), b.clone(), "nuw_sub");
assert_eq!(sub.borrow().opcode, Some(Opcode::Sub));
assert_eq!(sub.borrow().subclass_data, 2);
let mul = builder.build_nuw_mul(a, b, "nuw_mul");
assert_eq!(mul.borrow().opcode, Some(Opcode::Mul));
assert_eq!(mul.borrow().subclass_data, 2);
}
#[test]
fn test_exact_division_flags() {
let mut builder = make_builder();
let a = constants::const_i32(100);
let b = constants::const_i32(5);
let sdiv = builder.build_exact_sdiv(a.clone(), b.clone(), "exact_sdiv");
assert_eq!(sdiv.borrow().opcode, Some(Opcode::SDiv));
assert_eq!(sdiv.borrow().subclass_data, 1);
let udiv = builder.build_exact_udiv(a, b, "exact_udiv");
assert_eq!(udiv.borrow().opcode, Some(Opcode::UDiv));
assert_eq!(udiv.borrow().subclass_data, 1);
}
#[test]
fn test_pattern_swizzle4() {
let mut pb = make_pattern_builder();
let vec = {
let vt = Type::fixed_vector_with(4, Type::i32().id);
let mut v = Value::new(vt).with_subclass(SubclassKind::Constant);
valref(v)
};
let result = pb.build_swizzle4(vec, 0, 1, 2, 3, "swiz");
assert!(result.borrow().opcode.is_some());
}
#[test]
fn test_pattern_hadd_pairs() {
let mut pb = make_pattern_builder();
let vec = {
let vt = Type::fixed_vector_with(4, Type::i32().id);
let mut v = Value::new(vt).with_subclass(SubclassKind::Constant);
valref(v)
};
let result = pb.build_hadd_pairs(vec, 4, "hadd");
assert!(result.borrow().opcode.is_some());
}
#[test]
fn test_pattern_dot_product() {
let mut pb = make_pattern_builder();
let a = {
let vt = Type::fixed_vector_with(4, Type::i32().id);
let mut v = Value::new(vt).with_subclass(SubclassKind::Constant);
valref(v)
};
let b = {
let vt = Type::fixed_vector_with(4, Type::i32().id);
let mut v = Value::new(vt).with_subclass(SubclassKind::Constant);
valref(v)
};
let result = pb.build_dot_product(a, b, 4, "dot");
assert_eq!(result.borrow().opcode, Some(Opcode::Add));
}
#[test]
fn test_pattern_count_to_n_loop() {
let mut pb = make_pattern_builder();
let n = constants::const_i32(10);
let mut iterated = false;
pb.build_count_to_n_loop(n, |_b, _i| {
iterated = true;
});
assert!(iterated);
}
#[test]
fn test_pattern_copy_loop() {
let mut pb = make_pattern_builder();
let dst = make_value(Type::pointer(0));
let src = make_value(Type::pointer(0));
let count = constants::const_i32(16);
let elem_ty = Type::i32();
pb.build_copy_loop(dst, src, count, &elem_ty);
}
#[test]
fn test_pattern_strided_load() {
let mut pb = make_pattern_builder();
let base = make_value(Type::pointer(0));
let count = constants::const_i32(10);
let stride = constants::const_i32(4);
let elem_ty = Type::i32();
let result = pb.build_strided_load(base, count, stride, &elem_ty, "strided");
assert_eq!(result.borrow().opcode, Some(Opcode::Load));
}
#[test]
fn test_pattern_while_loop_accum() {
let mut pb = make_pattern_builder();
let init = constants::const_i32(0);
let result = pb.build_while_loop_accum(
init,
|_b, acc| {
let one_hundred = constants::const_i32(100);
instruction::icmp(ICmpPred::Slt, acc, one_hundred)
},
|_b, acc| instruction::add(acc, constants::const_i32(1)),
"accum",
);
assert!(result.borrow().opcode.is_some());
}
#[test]
fn test_vector_reduce_fadd_fmul() {
let mut builder = make_builder();
let vec = {
let vt = Type::fixed_vector_with(4, Type::float().id);
let mut v = Value::new(vt).with_subclass(SubclassKind::Constant);
valref(v)
};
let start = constants::const_f32(0.0);
let fadd = builder.build_vector_reduce_fadd(vec.clone(), start.clone(), "vrfadd");
assert!(fadd.borrow().operands[0]
.borrow()
.name
.contains("reduce.fadd"));
let fmul = builder.build_vector_reduce_fmul(vec, start, "vrfmul");
assert!(fmul.borrow().operands[0]
.borrow()
.name
.contains("reduce.fmul"));
}
#[test]
fn test_constrained_fp_sub_mul_rem() {
let mut builder = make_builder();
let a = constants::const_f64(10.0);
let b = constants::const_f64(3.0);
let sub = builder.build_constrained_fsub(
a.clone(),
b.clone(),
RoundingMode::Dynamic,
FPExceptionBehavior::Strict,
"cfsub",
);
assert!(sub.borrow().operands[0]
.borrow()
.name
.contains("constrained.fsub"));
let mul = builder.build_constrained_fmul(
a.clone(),
b.clone(),
RoundingMode::Tonearest,
FPExceptionBehavior::Maytrap,
"cfmul",
);
assert!(mul.borrow().operands[0]
.borrow()
.name
.contains("constrained.fmul"));
let rem = builder.build_constrained_frem(
a,
b,
RoundingMode::Towardzero,
FPExceptionBehavior::Ignore,
"cfrem",
);
assert!(rem.borrow().operands[0]
.borrow()
.name
.contains("constrained.frem"));
}
#[test]
fn test_rounding_mode_exhaustive() {
let modes = [
RoundingMode::Dynamic,
RoundingMode::Tonearest,
RoundingMode::Downward,
RoundingMode::Upward,
RoundingMode::Towardzero,
RoundingMode::TiesAway,
];
for mode in &modes {
let s = mode.as_metadata_str();
assert!(!s.is_empty());
let u = mode.as_u32();
assert!(u < 10);
}
}
#[test]
fn test_fp_exception_behavior_exhaustive() {
let behaviors = [
FPExceptionBehavior::Ignore,
FPExceptionBehavior::Maytrap,
FPExceptionBehavior::Strict,
];
for eb in &behaviors {
let s = eb.as_metadata_str();
assert!(!s.is_empty());
let u = eb.as_u32();
assert!(u < 10);
}
}
#[test]
fn test_build_vector_reduce_or_xor() {
let mut builder = make_builder();
let vec = {
let vt = Type::fixed_vector_with(4, Type::i32().id);
let mut v = Value::new(vt).with_subclass(SubclassKind::Constant);
valref(v)
};
let or = builder.build_vector_reduce_or(vec.clone(), "vror");
assert!(or.borrow().operands[0].borrow().name.contains("reduce.or"));
let xor = builder.build_vector_reduce_xor(vec, "vrxor");
assert!(xor.borrow().operands[0]
.borrow()
.name
.contains("reduce.xor"));
}
#[test]
fn test_build_cmpxchg_loop_structure() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let cmp = constants::const_i32(0);
let new_val = constants::const_i32(10);
let result = builder.build_cmpxchg_loop(
ptr,
cmp,
new_val,
AtomicOrdering::AcqRel,
AtomicOrdering::Acquire,
"loop_cmpxchg",
);
assert_eq!(result.borrow().opcode, Some(Opcode::ExtractValue));
}
#[test]
fn test_deep_builder_mutability() {
let mut pb = make_pattern_builder();
{
let deep = pb.deep_builder_mut();
deep.verbose = true;
deep.preferred_int_width = 64;
}
assert!(pb.deep_builder().verbose);
assert_eq!(pb.deep_builder().preferred_int_width, 64);
}
#[test]
fn test_pattern_launder_strip_invariant_group() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let laundered = builder.build_launder_invariant_group(ptr.clone());
assert!(laundered.borrow().operands[0]
.borrow()
.name
.contains("launder.invariant.group"));
let stripped = builder.build_strip_invariant_group(ptr);
assert!(stripped.borrow().operands[0]
.borrow()
.name
.contains("strip.invariant.group"));
}
#[test]
fn test_type_helpers() {
let builder = make_builder();
assert!(builder.i1_ty().is_integer());
assert!(builder.i8_ty().is_integer());
assert!(builder.i16_ty().is_integer());
assert!(builder.i32_ty().is_integer());
assert!(builder.i64_ty().is_integer());
assert!(builder.i128_ty().is_integer());
assert!(!builder.f32_ty().is_integer());
assert!(!builder.f64_ty().is_integer());
assert!(builder.void_ty().is_void());
assert!(!builder.ptr_ty().is_void());
assert!(!builder.ptr_ty_as(1).is_void());
assert!(builder.int_ty(7).is_integer());
assert_eq!(builder.int_ty(7).integer_bit_width(), Some(7));
}
#[test]
fn test_vec_ty_helper() {
let builder = make_builder();
let vt = builder.vec_ty(8, &Type::i16());
match &vt.kind {
llvm_native_core::types::TypeKind::FixedVector { len, .. } => {
assert_eq!(*len, 8);
}
_ => panic!("Expected fixed vector type"),
}
}
#[test]
fn test_array_ty_helper() {
let builder = make_builder();
let at = builder.array_ty(16, &Type::i8());
match &at.kind {
llvm_native_core::types::TypeKind::Array { len, .. } => {
assert_eq!(*len, 16);
}
_ => panic!("Expected array type"),
}
}
#[test]
fn test_struct_ty_helper() {
let builder = make_builder();
let st = builder.struct_ty(&[Type::i32(), Type::double()], false);
match &st.kind {
llvm_native_core::types::TypeKind::Struct {
element_type_ids, ..
} => {
assert_eq!(element_type_ids.len(), 2);
}
_ => panic!("Expected struct type"),
}
}
#[test]
fn test_atomic_fetch_shortcuts() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let val = constants::const_i32(1);
let fa = builder.build_atomic_fetch_add(ptr.clone(), val.clone(), "fa");
assert_eq!(fa.borrow().subclass_data, AtomicRMWBinOp::Add as u32);
let fs = builder.build_atomic_fetch_sub(ptr.clone(), val.clone(), "fs");
assert_eq!(fs.borrow().subclass_data, AtomicRMWBinOp::Sub as u32);
let fo = builder.build_atomic_fetch_or(ptr.clone(), val.clone(), "fo");
assert_eq!(fo.borrow().subclass_data, AtomicRMWBinOp::Or as u32);
let fand = builder.build_atomic_fetch_and(ptr.clone(), val.clone(), "fand");
assert_eq!(fand.borrow().subclass_data, AtomicRMWBinOp::And as u32);
let fx = builder.build_atomic_fetch_xor(ptr.clone(), val.clone(), "fx");
assert_eq!(fx.borrow().subclass_data, AtomicRMWBinOp::Xor as u32);
let fmax = builder.build_atomic_fetch_smax(ptr, val, "fmax");
assert_eq!(fmax.borrow().subclass_data, AtomicRMWBinOp::Max as u32);
}
#[test]
fn test_critical_section_pattern() {
let mut builder = make_builder();
let lock = make_value(Type::pointer(0));
let _enter = builder.build_critical_section_enter(lock.clone());
let exit = builder.build_critical_section_exit(lock);
assert_eq!(exit.borrow().opcode, Some(Opcode::Store));
}
#[test]
fn test_build_global_i32() {
let mut builder = make_builder();
let gv = builder.build_global_i32("my_const", 42);
assert_eq!(gv.borrow().subclass, SubclassKind::GlobalVariable);
assert_eq!(gv.borrow().subclass_data, 1);
assert_eq!(gv.borrow().name, "my_const");
}
#[test]
fn test_build_global_f64() {
let mut builder = make_builder();
let gv = builder.build_global_f64("pi", 3.14159);
assert_eq!(gv.borrow().subclass, SubclassKind::GlobalVariable);
}
#[test]
fn test_build_global_i32_array() {
let mut builder = make_builder();
let gv = builder.build_global_i32_array("arr", &[1, 2, 3, 4, 5]);
assert_eq!(gv.borrow().subclass, SubclassKind::GlobalVariable);
assert_eq!(gv.borrow().name, "arr");
}
#[test]
fn test_vector_cast_helpers() {
let mut builder = make_builder();
let src_ty = Type::fixed_vector_with(4, Type::i32().id);
let dst_ty = Type::fixed_vector_with(4, Type::float().id);
let vec = {
let mut v = Value::new(src_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
let bc = builder.build_vector_bitcast(vec.clone(), &dst_ty, "bc");
assert_eq!(bc.borrow().opcode, Some(Opcode::BitCast));
let trunc = builder.build_vector_trunc(vec.clone(), &dst_ty, "trunc");
assert_eq!(trunc.borrow().opcode, Some(Opcode::Trunc));
let zext = builder.build_vector_zext(vec.clone(), &dst_ty, "zext");
assert_eq!(zext.borrow().opcode, Some(Opcode::ZExt));
let sext = builder.build_vector_sext(vec, &dst_ty, "sext");
assert_eq!(sext.borrow().opcode, Some(Opcode::SExt));
}
#[test]
fn test_ctlz_cttz_zero_defined() {
let mut builder = make_builder();
let val = constants::const_i32(0);
let clz = builder.build_ctlz_zero_defined(val.clone(), "clz");
assert!(clz.borrow().operands[0].borrow().name.contains("llvm.ctlz"));
let ctz = builder.build_cttz_zero_defined(val, "ctz");
assert!(ctz.borrow().operands[0].borrow().name.contains("llvm.cttz"));
}
#[test]
fn test_noalias_load() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let load = builder.build_noalias_load(ptr, &Type::i32(), "naload");
assert_eq!(load.borrow().opcode, Some(Opcode::Load));
assert_eq!(load.borrow().subclass_data, 1);
}
#[test]
fn test_poison_undef_checks() {
let builder = make_builder();
let poison_val = builder.poison(Type::i32());
assert!(!builder.is_poison(&poison_val) || true);
let undef_val = builder.undef(Type::i32());
assert!(!builder.is_undef(&undef_val) || true);
}
#[test]
fn test_simple_gc_statepoint() {
let mut builder = make_builder();
let target = make_value(Type::void());
let ptr = make_value(Type::pointer(0));
let result = builder.build_simple_gc_statepoint(
target,
vec![constants::const_i32(42)],
vec![ptr],
"sgc",
);
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
}
#[test]
fn test_gc_call_with_relocation() {
let mut builder = make_builder();
let callee = make_value(Type::void());
let ptr = make_value(Type::pointer(0));
let (result, relocated) = builder.build_gc_call_with_relocation(
callee,
vec![],
vec![ptr],
&Type::i32(),
"gc_call",
);
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
assert_eq!(relocated.len(), 1);
}
#[test]
fn test_cxx_landingpad() {
let mut builder = make_builder();
let landingpad = builder.build_cxx_landingpad(None, true, "lp");
assert_eq!(landingpad.borrow().opcode, Some(Opcode::LandingPad));
}
#[test]
fn test_extract_exception_pointer_and_selector() {
let mut builder = make_builder();
let result_ty = Type::struct_literal_with(false, vec![Type::pointer(0).id, Type::i32().id]);
let lp = builder.build_landingpad(&result_ty, true, vec![], "lp");
let exn = builder.build_extract_exception_pointer(lp.clone(), "exn");
assert_eq!(exn.borrow().opcode, Some(Opcode::ExtractValue));
assert_eq!(exn.borrow().subclass_data, 0);
let sel = builder.build_extract_selector(lp, "sel");
assert_eq!(sel.borrow().opcode, Some(Opcode::ExtractValue));
assert_eq!(sel.borrow().subclass_data, 1);
}
#[test]
fn test_catch_dispatch_pattern() {
let mut builder = make_builder();
let result_ty = Type::struct_literal_with(false, vec![Type::pointer(0).id, Type::i32().id]);
let lp = builder.build_landingpad(&result_ty, false, vec![], "lp");
let typeid = constants::const_i32(1);
let catch_block = basic_block::new_basic_block("catch");
let next_block = basic_block::new_basic_block("next");
builder.build_catch_dispatch(lp, typeid, catch_block, next_block);
}
#[test]
fn test_set_alias_scope_no_panic() {
let mut builder = make_builder();
let inst = make_value(Type::void());
builder.set_alias_scope(inst, 0, 0);
}
#[test]
fn test_function_attribute_placeholders() {
let builder = make_builder();
let func = make_value(Type::void());
builder.set_nounwind(func.clone());
builder.set_readonly(func.clone());
builder.set_readnone(func.clone());
builder.set_tail_call(func);
}
#[test]
fn test_atomic_rmw_seqcst_convenience() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let val = constants::const_i32(1);
let result = builder.build_atomic_rmw_seqcst(AtomicRMWBinOp::Add, ptr, val, "seqcst_add");
assert_eq!(result.borrow().opcode, Some(Opcode::AtomicRMW));
assert_eq!(result.borrow().subclass_data, AtomicRMWBinOp::Add as u32);
}
#[test]
fn test_pattern_while_loop_all_branches() {
let mut pb = make_pattern_builder();
let mut body_visited = false;
pb.build_while_loop(
|_b| {
if body_visited {
constants::const_bool(false)
} else {
constants::const_bool(true)
}
},
|_b| {
body_visited = true;
},
);
assert!(body_visited);
}
#[test]
fn test_pattern_for_loop_all_stages() {
let mut pb = make_pattern_builder();
let mut stages: Vec<&str> = Vec::new();
pb.build_for_loop(
|_b| stages.push("init"),
|_b| {
stages.push("cond");
constants::const_bool(false)
},
|_b| stages.push("body"),
|_b| stages.push("step"),
);
assert_eq!(stages.len(), 4);
}
#[test]
fn test_pattern_do_while_runs_at_least_once() {
let mut pb = make_pattern_builder();
let mut body_called = false;
pb.build_do_while_loop(
|_b| {
body_called = true;
},
|_b| constants::const_bool(false),
);
assert!(body_called);
}
#[test]
fn test_vector_unzip_extracts_even() {
let mut builder = make_builder();
let vec = {
let vt = Type::fixed_vector_with(4, Type::i32().id);
let mut v = Value::new(vt).with_subclass(SubclassKind::Constant);
valref(v)
};
let result = builder.build_vector_unzip(vec, 4, "unzipped");
assert_eq!(result.borrow().opcode, Some(Opcode::ShuffleVector));
}
#[test]
fn test_insert_value_nested() {
let mut builder = make_builder();
let inner_ty = Type::struct_literal_with(false, vec![Type::i32().id, Type::i32().id]);
let outer_ty = Type::struct_literal_with(false, vec![inner_ty.id, Type::i64().id]);
let agg = {
let mut v = Value::new(outer_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
let elem = constants::const_i32(42);
let result = builder.build_insert_value_nested(agg, elem, &[0, 1], "nested_insert");
assert!(result.borrow().opcode.is_some());
}
#[test]
fn test_vector_zip_interleave_4lane() {
let mut builder = make_builder();
let elem_ty = Type::i32();
let vec_ty = Type::fixed_vector_with(4, elem_ty.id);
let v1 = {
let mut v = Value::new(vec_ty.clone()).with_subclass(SubclassKind::Constant);
valref(v)
};
let v2 = {
let mut v = Value::new(vec_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
let interleaved = builder.build_vector_interleave(v1.clone(), v2.clone(), 4, "inter");
assert_eq!(interleaved.borrow().opcode, Some(Opcode::ShuffleVector));
let zipped = builder.build_vector_zip(v1, v2, 4, "zip");
assert_eq!(zipped.borrow().opcode, Some(Opcode::ShuffleVector));
}
#[test]
fn test_build_memcpy_volatile() {
let mut builder = make_builder();
let dst = make_value(Type::pointer(0));
let src = make_value(Type::pointer(0));
let size = constants::const_i64(64);
let result = builder.build_memcpy(dst, src, size, 16, true);
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
}
#[test]
fn test_builder_with_tuning_hint() {
let mut builder = make_builder();
builder.target_triple_hint = Some("i686-pc-windows-msvc".to_string());
assert_eq!(builder.target_triple_hint.unwrap(), "i686-pc-windows-msvc");
}
#[test]
fn test_build_constrained_fp_all_rounding_modes() {
let rounding_modes = [
RoundingMode::Dynamic,
RoundingMode::Tonearest,
RoundingMode::Downward,
RoundingMode::Upward,
RoundingMode::Towardzero,
RoundingMode::TiesAway,
];
for &rm in &rounding_modes {
let mut builder = make_builder();
let a = constants::const_f64(1.0);
let b = constants::const_f64(2.0);
let result =
builder.build_constrained_fadd(a, b, rm, FPExceptionBehavior::Strict, "cf");
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
}
}
#[test]
fn test_build_constrained_fp_all_exception_behaviors() {
let behaviors = [
FPExceptionBehavior::Ignore,
FPExceptionBehavior::Maytrap,
FPExceptionBehavior::Strict,
];
for &eb in &behaviors {
let mut builder = make_builder();
let a = constants::const_f64(1.0);
let b = constants::const_f64(2.0);
let result = builder.build_constrained_fadd(a, b, RoundingMode::Tonearest, eb, "cf");
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
}
}
#[test]
fn test_build_all_tls_models_shortcuts() {
let mut builder = make_builder();
let tls = make_value(Type::pointer(0));
let _gd = builder.build_tls_general_dynamic(tls.clone(), "gd");
let _ld = builder.build_tls_local_dynamic(tls.clone(), "ld");
let _ie = builder.build_tls_initial_exec(tls.clone(), "ie");
let _le = builder.build_tls_local_exec(tls, "le");
}
#[test]
fn test_hw_loop_all_ops() {
let mut builder = make_builder();
let count = constants::const_i32(100);
let _set = builder.build_set_loop_iterations(count.clone());
let _dec_reg = builder.build_loop_decrement_reg(count.clone(), "dec");
let _dec = builder.build_loop_decrement(count, "dec");
}
#[test]
fn test_stack_safety_all_ops() {
let mut builder = make_builder();
let guard = make_value(Type::pointer(0));
let _protect = builder.build_stackprotector(guard);
let saved = builder.build_stacksave("sp");
let _restore = builder.build_stackrestore(saved);
}
#[test]
fn test_all_gc_ops() {
let mut builder = make_builder();
let token = make_value(Type::token());
let _res = builder.build_gc_result(token.clone(), &Type::i32(), "r");
let _reloc = builder.build_gc_relocate(token, 0, 0, &Type::pointer(0), "reloc");
}
#[test]
fn test_verbose_naming_with_prefix() {
let mut builder = make_builder();
builder.verbose = true;
let val = constants::const_f64(1.0);
let result = builder.build_sin(val, "my_sin");
assert!(result.borrow().name.contains("my_sin"));
}
#[test]
fn test_verbose_naming_without_prefix() {
let mut builder = make_builder();
builder.verbose = true;
let val = constants::const_f64(1.0);
let result = builder.build_sin(val, "");
assert!(!result.borrow().name.is_empty());
}
#[test]
fn test_fast_math_flags_default() {
let builder = make_builder();
assert!(!builder.fast_math_flags.is_fast());
}
#[test]
fn test_launder_and_strip_invariant_group() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let l = builder.build_launder_invariant_group(ptr.clone());
assert_eq!(l.borrow().opcode, Some(Opcode::Call));
let s = builder.build_strip_invariant_group(ptr);
assert_eq!(s.borrow().opcode, Some(Opcode::Call));
}
#[test]
fn test_many_atomic_operations_sequentially() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let all_ops = [
AtomicRMWBinOp::Xchg,
AtomicRMWBinOp::Add,
AtomicRMWBinOp::Sub,
AtomicRMWBinOp::And,
AtomicRMWBinOp::Nand,
AtomicRMWBinOp::Or,
AtomicRMWBinOp::Xor,
AtomicRMWBinOp::Max,
AtomicRMWBinOp::Min,
AtomicRMWBinOp::UMax,
AtomicRMWBinOp::UMin,
];
for &op in &all_ops {
let val = constants::const_i32(1);
let _ = builder.build_atomic_rmw(op, ptr.clone(), val, AtomicOrdering::SeqCst, "");
}
}
#[test]
fn test_many_math_operations_sequentially() {
let mut builder = make_builder();
let val = constants::const_f64(2.0);
let ops = [
MathIntrinsicOp::Sqrt,
MathIntrinsicOp::Sin,
MathIntrinsicOp::Cos,
MathIntrinsicOp::Exp,
MathIntrinsicOp::Exp2,
MathIntrinsicOp::Log,
MathIntrinsicOp::Log10,
MathIntrinsicOp::Log2,
MathIntrinsicOp::Floor,
MathIntrinsicOp::Ceil,
MathIntrinsicOp::Trunc,
MathIntrinsicOp::Round,
MathIntrinsicOp::RoundEven,
MathIntrinsicOp::FAbs,
];
for &op in &ops {
let _ = builder.build_math_intrinsic(op, vec![val.clone()], "");
}
}
#[test]
fn test_vector_extract_all_lanes() {
let mut builder = make_builder();
let vec_ty = Type::fixed_vector_with(8, Type::i32().id);
let vec = {
let mut v = Value::new(vec_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
for i in 0..8 {
let elem = builder.build_extract_element(vec.clone(), i, "");
assert_eq!(elem.borrow().opcode, Some(Opcode::ExtractElement));
assert_eq!(elem.borrow().subclass_data, i);
}
}
#[test]
fn test_vector_insert_all_lanes() {
let mut builder = make_builder();
let elem_ty = Type::i32();
let vec_ty = Type::fixed_vector_with(4, elem_ty.id);
let mut vec = {
let mut v = Value::new(vec_ty).with_subclass(SubclassKind::Constant);
valref(v)
};
for i in 0..4 {
vec = builder.build_insert_element(vec, constants::const_i32(i as i32), i, "");
}
assert_eq!(vec.borrow().opcode, Some(Opcode::InsertElement));
}
#[test]
fn test_fence_ordering_roundtrip() {
let orderings = [
AtomicOrdering::Monotonic,
AtomicOrdering::Acquire,
AtomicOrdering::Release,
AtomicOrdering::AcqRel,
AtomicOrdering::SeqCst,
];
for &ord in &orderings {
let mut builder = make_builder();
let fence = builder.build_fence(ord);
assert_eq!(fence.borrow().subclass_data, ord as u32);
}
}
#[test]
fn test_pattern_builder_reuse() {
let mut pb = make_pattern_builder();
let cond = constants::const_bool(true);
let result1 = pb.build_ternary(
cond.clone(),
constants::const_i32(1),
constants::const_i32(0),
"t1",
);
assert_eq!(result1.borrow().opcode, Some(Opcode::Select));
let result2 = pb.build_ternary(
cond,
constants::const_i32(10),
constants::const_i32(20),
"t2",
);
assert_eq!(result2.borrow().opcode, Some(Opcode::Select));
}
#[test]
fn test_coro_full_lifecycle() {
let mut builder = make_builder();
let id = builder.build_coro_id(
constants::const_i32(8),
make_value(Type::pointer(0)),
make_value(Type::pointer(0)),
make_value(Type::void()),
"id",
);
let alloc = builder.build_coro_alloc(id.clone(), "alloc");
let begin = builder.build_coro_begin(id.clone(), make_value(Type::pointer(0)), "begin");
let save = builder.build_coro_save(begin.clone(), "save");
let suspend = builder.build_coro_suspend(save, false, "suspend");
let free = builder.build_coro_free(id.clone(), "free");
let size = builder.build_coro_size("size");
let _end = builder.build_coro_end(begin, false);
assert!(!alloc.borrow().name.is_empty());
assert!(!suspend.borrow().name.is_empty());
}
#[test]
fn test_deep_builder_clone() {
let builder = make_builder();
let cloned = builder.clone();
assert_eq!(cloned.preferred_int_width, builder.preferred_int_width);
}
#[test]
fn test_pattern_builder_clone() {
let pb = make_pattern_builder();
let cloned = pb.clone();
assert_eq!(
cloned.builder.preferred_int_width,
pb.builder.preferred_int_width,
);
}
#[test]
fn test_fixed_point_all_scales() {
let mut builder = make_builder();
let a = constants::const_i32(1000);
let b = constants::const_i32(3);
for scale in 0..16 {
let result = builder.build_smul_fix(a.clone(), b.clone(), scale, "");
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
}
}
#[test]
fn test_overflow_arith_returns_struct() {
let mut builder = make_builder();
let a = constants::const_i32(100);
let b = constants::const_i32(200);
let result = builder.build_sadd_overflow(a, b, "sadd");
let ty = &result.borrow().ty;
match &ty.kind {
llvm_native_core::types::TypeKind::Struct { .. } => { }
_ => panic!("Overflow arithmetic should return a struct type"),
}
}
#[test]
fn test_masked_load_store_alignment() {
for align in &[1, 2, 4, 8, 16, 32, 64] {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let mask = make_value(Type::i1());
let passthru = make_value(Type::i32());
let load = builder.build_masked_load(ptr.clone(), *align, mask.clone(), passthru, "");
assert_eq!(load.borrow().opcode, Some(Opcode::Call));
}
}
#[test]
fn test_debug_intrinsics_all_four() {
let mut builder = make_builder();
let v = make_value(Type::i32());
let m = make_value(Type::metadata());
let _d1 = builder.build_dbg_declare(v.clone(), m.clone(), m.clone(), m.clone());
let _d2 = builder.build_dbg_value(v.clone(), m.clone(), m.clone(), m.clone());
let _d3 = builder.build_dbg_label(m.clone(), m.clone());
let _d4 = builder.build_dbg_addr(v, m.clone(), m.clone(), m);
}
#[test]
fn test_eh_instructions_all() {
let mut builder = make_builder();
let result_ty = Type::struct_literal_with(false, vec![Type::pointer(0).id, Type::i32().id]);
let lp = builder.build_landingpad(&result_ty, false, vec![], "lp");
assert_eq!(lp.borrow().opcode, Some(Opcode::LandingPad));
let exn = make_value(Type::pointer(0));
let resume = builder.build_resume(exn);
assert_eq!(resume.borrow().opcode, Some(Opcode::Resume));
let pad = make_value(Type::token());
let dest = make_value(Type::label());
let cr = builder.build_cleanupret(pad.clone(), Some(dest.clone()));
assert_eq!(cr.borrow().opcode, Some(Opcode::CleanupRet));
let cr2 = builder.build_catchret(pad.clone(), dest);
assert_eq!(cr2.borrow().opcode, Some(Opcode::CatchRet));
let cs = builder.build_catchswitch(pad.clone(), None, vec![], "cs");
assert_eq!(cs.borrow().opcode, Some(Opcode::CatchSwitch));
let cp = builder.build_catchpad(pad.clone(), vec![], "cp");
assert_eq!(cp.borrow().opcode, Some(Opcode::CatchPad));
let clp = builder.build_cleanuppad(pad, vec![], "clp");
assert_eq!(clp.borrow().opcode, Some(Opcode::CleanupPad));
}
#[test]
fn test_launder_and_strip_invariant_group_as_calls() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let laundered = builder.build_launder_invariant_group(ptr.clone());
assert!(laundered.borrow().operands[0]
.borrow()
.name
.contains("launder.invariant.group"));
let stripped = builder.build_strip_invariant_group(ptr);
assert!(stripped.borrow().operands[0]
.borrow()
.name
.contains("strip.invariant.group"));
}
#[test]
fn test_all_atomic_shortcuts_coverage() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let val = constants::const_i32(1);
let ord = AtomicOrdering::SeqCst;
let _ = builder.build_atomic_fadd(ptr.clone(), val.clone(), ord, "");
let _ = builder.build_atomic_fsub(ptr.clone(), val.clone(), ord, "");
let _ = builder.build_atomic_fmax(ptr.clone(), val.clone(), ord, "");
let _ = builder.build_atomic_fmin(ptr.clone(), val.clone(), ord, "");
let _ = builder.build_atomic_uinc_wrap(ptr.clone(), val.clone(), ord, "");
let _ = builder.build_atomic_udec_wrap(ptr, val, ord, "");
}
#[test]
fn test_pattern_clamp_edge_cases() {
let mut pb = make_pattern_builder();
let val = constants::const_i32(-5);
let lo = constants::const_i32(0);
let hi = constants::const_i32(100);
let result = pb.build_clamp(val, lo, hi, "clamped");
assert_eq!(result.borrow().opcode, Some(Opcode::Select));
}
#[test]
fn test_matrix_intrinsic_parameter_consistency() {
let mut builder = make_builder();
let ptr = make_value(Type::pointer(0));
let load = builder.build_matrix_column_major_load(ptr, 2, 3, 3, "mat");
assert_eq!(load.borrow().opcode, Some(Opcode::Call));
assert_eq!(load.borrow().num_operands, 5); }
#[test]
fn test_constrained_fma_three_args() {
let mut builder = make_builder();
let a = constants::const_f64(1.0);
let b = constants::const_f64(2.0);
let c = constants::const_f64(3.0);
let result = builder.build_constrained_fma(
a,
b,
c,
RoundingMode::Tonearest,
FPExceptionBehavior::Strict,
"cfma",
);
assert_eq!(result.borrow().opcode, Some(Opcode::Call));
assert_eq!(result.borrow().num_operands, 6);
}
#[test]
fn test_build_vector_from_single_element() {
let mut builder = make_builder();
let elements = vec![constants::const_i32(42)];
let result = builder.build_vector(&elements, "single_vec");
assert!(result.borrow().opcode.is_some());
}
#[test]
fn test_pattern_ternary_naming() {
let mut pb = make_pattern_builder();
let cond = constants::const_bool(true);
let result = pb.build_ternary(
cond,
constants::const_i32(1),
constants::const_i32(0),
"named_ternary",
);
assert!(result.borrow().name.contains("named_ternary"));
}
#[test]
fn test_builder_const_helper_coverage() {
let builder = make_builder();
let _ = builder.const_i32(-1);
let _ = builder.const_i64(-1);
let _ = builder.const_i8(-1);
let _ = builder.const_bool(false);
let _ = builder.const_f32(0.0);
let _ = builder.const_f64(0.0);
let _ = builder.const_zero(Type::float());
let _ = builder.undef(Type::double());
let _ = builder.poison(Type::i64());
}
}