use crate::attributes::{AttributeKind, AttributeList, ParamAttrKind};
use crate::opcode::Opcode;
use crate::types::Type;
use crate::value::{valref, SubclassKind, Value, ValueRef};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Linkage {
External,
Internal,
Private,
WeakAny,
WeakODR,
LinkOnceAny,
LinkOnceODR,
AvailableExternally,
Appending,
Common,
ExternalWeak,
}
impl Linkage {
pub fn as_str(&self) -> &'static str {
match self {
Linkage::External => "external",
Linkage::Internal => "internal",
Linkage::Private => "private",
Linkage::WeakAny => "weak",
Linkage::WeakODR => "weak_odr",
Linkage::LinkOnceAny => "linkonce",
Linkage::LinkOnceODR => "linkonce_odr",
Linkage::AvailableExternally => "available_externally",
Linkage::Appending => "appending",
Linkage::Common => "common",
Linkage::ExternalWeak => "extern_weak",
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"external" => Some(Linkage::External),
"internal" => Some(Linkage::Internal),
"private" => Some(Linkage::Private),
"weak" => Some(Linkage::WeakAny),
"weak_odr" => Some(Linkage::WeakODR),
"linkonce" => Some(Linkage::LinkOnceAny),
"linkonce_odr" => Some(Linkage::LinkOnceODR),
"available_externally" => Some(Linkage::AvailableExternally),
"appending" => Some(Linkage::Appending),
"common" => Some(Linkage::Common),
"extern_weak" => Some(Linkage::ExternalWeak),
_ => None,
}
}
pub fn is_local(&self) -> bool {
matches!(self, Linkage::Internal | Linkage::Private)
}
pub fn is_discardable_if_unused(&self) -> bool {
matches!(
self,
Linkage::LinkOnceAny
| Linkage::LinkOnceODR
| Linkage::WeakAny
| Linkage::WeakODR
| Linkage::AvailableExternally
)
}
}
impl std::fmt::Display for Linkage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Visibility {
Default,
Hidden,
Protected,
}
impl Visibility {
pub fn as_str(&self) -> &'static str {
match self {
Visibility::Default => "default",
Visibility::Hidden => "hidden",
Visibility::Protected => "protected",
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"default" => Some(Visibility::Default),
"hidden" => Some(Visibility::Hidden),
"protected" => Some(Visibility::Protected),
_ => None,
}
}
}
impl std::fmt::Display for Visibility {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DLLStorageClass {
Default,
DLLImport,
DLLExport,
}
impl DLLStorageClass {
pub fn as_str(&self) -> &'static str {
match self {
DLLStorageClass::Default => "default",
DLLStorageClass::DLLImport => "dllimport",
DLLStorageClass::DLLExport => "dllexport",
}
}
}
impl std::fmt::Display for DLLStorageClass {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum IntrinsicID {
VAStart = 1,
VAEnd = 2,
VACopy = 3,
GCResult = 10,
GCRelocate = 11,
MemCpy = 20,
MemMove = 21,
MemSet = 22,
LifetimeStart = 23,
LifetimeEnd = 24,
InvariantStart = 25,
InvariantEnd = 26,
LaunderInvariantGroup = 27,
StripInvariantGroup = 28,
FMA = 40,
FMulAdd = 41,
Abs = 42,
MinNum = 43,
MaxNum = 44,
Sqrt = 45,
Sin = 46,
Cos = 47,
Pow = 48,
Powi = 49,
Exp = 50,
Exp2 = 51,
Log = 52,
Log2 = 53,
Log10 = 54,
Floor = 55,
Ceil = 56,
Trunc = 57,
RInt = 58,
NearByInt = 59,
Round = 60,
RoundEven = 61,
BitReverse = 70,
BSwap = 71,
Ctlz = 72,
Cttz = 73,
CtPop = 74,
SAddWithOverflow = 80,
UAddWithOverflow = 81,
SSubWithOverflow = 82,
USubWithOverflow = 83,
SMulWithOverflow = 84,
UMulWithOverflow = 85,
SAddSat = 90,
UAddSat = 91,
SSubSat = 92,
USubSat = 93,
CoroAlloc = 100,
CoroFree = 101,
CoroBegin = 102,
CoroSuspend = 103,
CoroEnd = 104,
CoroFrame = 105,
CoroSize = 106,
CoroSave = 107,
EHTypeIDFor = 110,
}
impl IntrinsicID {
pub fn from_name(name: &str) -> Option<u32> {
if !name.starts_with("llvm.") {
return None;
}
let base = name.strip_prefix("llvm.")?;
let dot_p = base.find(".p");
let first_dot = base.find('.');
let end = match (dot_p, first_dot) {
(Some(p), _) => p, (None, Some(d)) => d, (None, None) => base.len(),
};
let base_name = &base[..end];
let id = match base_name {
"va_start" => IntrinsicID::VAStart,
"va_end" => IntrinsicID::VAEnd,
"va_copy" => IntrinsicID::VACopy,
"gcroot" => IntrinsicID::GCResult,
"gc.relocate" => IntrinsicID::GCRelocate,
"memcpy" => IntrinsicID::MemCpy,
"memmove" => IntrinsicID::MemMove,
"memset" => IntrinsicID::MemSet,
"lifetime.start" => IntrinsicID::LifetimeStart,
"lifetime.end" => IntrinsicID::LifetimeEnd,
"invariant.start" => IntrinsicID::InvariantStart,
"invariant.end" => IntrinsicID::InvariantEnd,
"launder.invariant.group" => IntrinsicID::LaunderInvariantGroup,
"strip.invariant.group" => IntrinsicID::StripInvariantGroup,
"fma" => IntrinsicID::FMA,
"fmuladd" => IntrinsicID::FMulAdd,
"abs" => IntrinsicID::Abs,
"minnum" => IntrinsicID::MinNum,
"maxnum" => IntrinsicID::MaxNum,
"sqrt" => IntrinsicID::Sqrt,
"sin" => IntrinsicID::Sin,
"cos" => IntrinsicID::Cos,
"pow" => IntrinsicID::Pow,
"powi" => IntrinsicID::Powi,
"exp" => IntrinsicID::Exp,
"exp2" => IntrinsicID::Exp2,
"log" => IntrinsicID::Log,
"log2" => IntrinsicID::Log2,
"log10" => IntrinsicID::Log10,
"floor" => IntrinsicID::Floor,
"ceil" => IntrinsicID::Ceil,
"trunc" => IntrinsicID::Trunc,
"rint" => IntrinsicID::RInt,
"nearbyint" => IntrinsicID::NearByInt,
"round" => IntrinsicID::Round,
"roundeven" => IntrinsicID::RoundEven,
"bitreverse" => IntrinsicID::BitReverse,
"bswap" => IntrinsicID::BSwap,
"ctlz" => IntrinsicID::Ctlz,
"cttz" => IntrinsicID::Cttz,
"ctpop" => IntrinsicID::CtPop,
"sadd.with.overflow" => IntrinsicID::SAddWithOverflow,
"uadd.with.overflow" => IntrinsicID::UAddWithOverflow,
"ssub.with.overflow" => IntrinsicID::SSubWithOverflow,
"usub.with.overflow" => IntrinsicID::USubWithOverflow,
"smul.with.overflow" => IntrinsicID::SMulWithOverflow,
"umul.with.overflow" => IntrinsicID::UMulWithOverflow,
"sadd.sat" => IntrinsicID::SAddSat,
"uadd.sat" => IntrinsicID::UAddSat,
"ssub.sat" => IntrinsicID::SSubSat,
"usub.sat" => IntrinsicID::USubSat,
"coro.alloc" => IntrinsicID::CoroAlloc,
"coro.free" => IntrinsicID::CoroFree,
"coro.begin" => IntrinsicID::CoroBegin,
"coro.suspend" => IntrinsicID::CoroSuspend,
"coro.end" => IntrinsicID::CoroEnd,
"coro.frame" => IntrinsicID::CoroFrame,
"coro.size" => IntrinsicID::CoroSize,
"coro.save" => IntrinsicID::CoroSave,
"eh.typeid.for" => IntrinsicID::EHTypeIDFor,
_ => return None,
};
Some(id as u32)
}
}
#[derive(Debug, Clone)]
pub struct Function {
pub value: ValueRef,
pub params: Vec<Type>,
pub is_vararg: bool,
pub linkage: Linkage,
pub visibility: Visibility,
pub dll_storage: DLLStorageClass,
pub calling_conv: u32,
pub is_declaration: bool,
pub section: Option<String>,
pub comdat: Option<String>,
pub alignment: u32,
pub gc: Option<String>,
pub personality: Option<ValueRef>,
pub subprogram: Option<u32>,
pub attributes: AttributeList,
pub prefix_data: Option<ValueRef>,
pub prologue_data: Option<ValueRef>,
pub arguments: Vec<ValueRef>,
pub basic_blocks: Vec<ValueRef>,
}
pub fn new_function(name: &str, return_type: Type, _param_types: &[Type]) -> ValueRef {
let mut v = Value::new(Type::function_type_with(return_type.id, vec![], false))
.named(name)
.with_subclass(SubclassKind::Function);
v.return_type = Some(return_type);
valref(v)
}
pub fn new_argument(name: &str, ty: Type) -> ValueRef {
valref(
Value::new(ty)
.named(name)
.with_subclass(SubclassKind::Argument),
)
}
impl Function {
pub fn new(name: &str, return_type: Type, param_types: &[Type]) -> Self {
let fn_ty = Type::function_type_with(
return_type.id,
param_types.iter().map(|t| t.id).collect(),
false,
);
let mut v = Value::new(fn_ty)
.named(name)
.with_subclass(SubclassKind::Function);
v.return_type = Some(return_type.clone());
let mut args = Vec::new();
for (i, pty) in param_types.iter().enumerate() {
let arg_name = format!("arg{}", i);
args.push(new_argument(&arg_name, pty.clone()));
}
Self {
value: valref(v),
params: param_types.to_vec(),
is_vararg: false,
linkage: Linkage::External,
visibility: Visibility::Default,
dll_storage: DLLStorageClass::Default,
calling_conv: 0,
is_declaration: true,
section: None,
comdat: None,
alignment: 0,
gc: None,
personality: None,
subprogram: None,
attributes: AttributeList::new(),
prefix_data: None,
prologue_data: None,
arguments: args,
basic_blocks: Vec::new(),
}
}
pub fn from_value(val: ValueRef) -> Self {
Self {
value: val,
params: Vec::new(),
is_vararg: false,
linkage: Linkage::External,
visibility: Visibility::Default,
dll_storage: DLLStorageClass::Default,
calling_conv: 0,
is_declaration: true,
section: None,
comdat: None,
alignment: 0,
gc: None,
personality: None,
subprogram: None,
attributes: AttributeList::new(),
prefix_data: None,
prologue_data: None,
arguments: Vec::new(),
basic_blocks: Vec::new(),
}
}
pub fn get_entry_block(&self) -> Option<ValueRef> {
self.basic_blocks.first().cloned()
}
pub fn get_basic_blocks(&self) -> Vec<ValueRef> {
self.basic_blocks.clone()
}
pub fn get_block_count(&self) -> usize {
self.basic_blocks.len()
}
pub fn add_basic_block(&mut self, bb: ValueRef) {
bb.borrow_mut().parent = Some(Rc::clone(&self.value));
self.basic_blocks.push(bb);
}
pub fn remove_basic_block(&mut self, bb: &ValueRef) -> bool {
if let Some(pos) = self.basic_blocks.iter().position(|b| Rc::ptr_eq(b, bb)) {
self.basic_blocks.remove(pos);
true
} else {
false
}
}
pub fn get_block_by_name(&self, name: &str) -> Option<ValueRef> {
self.basic_blocks
.iter()
.find(|b| b.borrow().name == name)
.cloned()
}
pub fn get_arg(&self, index: usize) -> Option<ValueRef> {
self.arguments.get(index).cloned()
}
pub fn get_arg_count(&self) -> usize {
self.arguments.len()
}
pub fn add_argument(&mut self, arg: ValueRef) {
self.arguments.push(arg);
}
pub fn get_arguments(&self) -> &[ValueRef] {
&self.arguments
}
pub fn set_linkage(&mut self, linkage: Linkage) {
self.linkage = linkage;
}
pub fn get_linkage(&self) -> Linkage {
self.linkage
}
pub fn set_visibility(&mut self, vis: Visibility) {
self.visibility = vis;
}
pub fn set_dll_storage_class(&mut self, cls: DLLStorageClass) {
self.dll_storage = cls;
}
pub fn set_section(&mut self, section: &str) {
self.section = Some(section.to_string());
}
pub fn clear_section(&mut self) {
self.section = None;
}
pub fn set_comdat(&mut self, comdat: &str) {
self.comdat = Some(comdat.to_string());
}
pub fn set_alignment(&mut self, align: u32) {
self.alignment = align;
}
pub fn set_calling_conv(&mut self, cc: u32) {
self.calling_conv = cc;
}
pub fn set_gc(&mut self, gc: &str) {
self.gc = Some(gc.to_string());
}
pub fn set_personality(&mut self, func: ValueRef) {
self.personality = Some(func);
}
pub fn set_subprogram(&mut self, id: u32) {
self.subprogram = Some(id);
}
pub fn has_exact_definition(&self) -> bool {
!self.is_declaration
}
pub fn is_declaration_fn(&self) -> bool {
self.is_declaration
}
pub fn set_declaration(&mut self, decl: bool) {
self.is_declaration = decl;
}
pub fn is_intrinsic(&self) -> bool {
self.value.borrow().name.starts_with("llvm.")
}
pub fn get_intrinsic_id(&self) -> Option<u32> {
IntrinsicID::from_name(&self.value.borrow().name)
}
pub fn is_vararg_fn(&self) -> bool {
self.is_vararg
}
pub fn set_vararg(&mut self, va: bool) {
self.is_vararg = va;
}
pub fn view_cfg(&self) -> String {
let mut out = String::new();
let name = self.get_name();
out.push_str(&format!("CFG for function '{}':\n", name));
out.push_str(&format!(" Blocks: {}\n\n", self.get_block_count()));
for bb in &self.basic_blocks {
let bb_name = bb.borrow().name.clone();
let bb_display = if bb_name.is_empty() {
"<unnamed>"
} else {
&bb_name
};
out.push_str(&format!("{}:\n", bb_display));
let succs: Vec<String> = bb
.borrow()
.successors
.iter()
.map(|s| {
let sn = s.borrow().name.clone();
if sn.is_empty() {
"<unnamed>".to_string()
} else {
sn
}
})
.collect();
if !succs.is_empty() {
out.push_str(&format!(" successors: {};\n", succs.join(", ")));
}
for inst in &bb.borrow().operands {
let iname = inst.borrow().name.clone();
out.push_str(&format!(
" {}\n",
if iname.is_empty() { "<inst>" } else { &iname }
));
}
out.push('\n');
}
out
}
pub fn verify(&self) -> Result<(), Vec<String>> {
let mut errors = Vec::new();
if self.value.borrow().name.is_empty() {
errors.push("Function has no name".to_string());
}
if self.basic_blocks.is_empty() && !self.is_declaration {
errors
.push("Function has no basic blocks but is not marked as declaration".to_string());
}
if !self.basic_blocks.is_empty() && self.is_declaration {
errors.push("Function has basic blocks but is marked as declaration".to_string());
}
if !self.basic_blocks.is_empty() {
if let Some(entry) = &self.get_entry_block() {
let has_term = entry.borrow().operands.last().map_or(false, |inst| {
inst.borrow()
.get_opcode()
.map_or(false, |op| op.is_terminator())
});
if !has_term {
errors.push("Entry block is not terminated".to_string());
}
}
}
for (i, bb) in self.basic_blocks.iter().enumerate() {
let has_term = bb.borrow().operands.last().map_or(false, |inst| {
inst.borrow()
.get_opcode()
.map_or(false, |op| op.is_terminator())
});
if !has_term {
let bb_name = bb.borrow().name.clone();
errors.push(format!(
"Block #{} ({}) is not terminated",
i,
if bb_name.is_empty() {
"<unnamed>"
} else {
&bb_name
}
));
}
}
if self.params.len() != self.arguments.len() {
errors.push(format!(
"Parameter count mismatch: params={}, args={}",
self.params.len(),
self.arguments.len()
));
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub fn get_name(&self) -> String {
self.value.borrow().name.clone()
}
pub fn set_name(&mut self, name: &str) {
self.value.borrow_mut().name = name.to_string();
}
pub fn get_return_type(&self) -> Option<Type> {
self.value.borrow().return_type.clone()
}
pub fn as_value_ref(&self) -> ValueRef {
Rc::clone(&self.value)
}
pub fn dump(&self) -> String {
let mut out = String::new();
let name = self.get_name();
let ret = self.get_return_type();
let ret_str = ret
.as_ref()
.map(|t| Type::pretty_print(t))
.unwrap_or_else(|| "void".to_string());
if self.linkage != Linkage::External {
out.push_str(&format!("{} ", self.linkage));
}
if self.visibility != Visibility::Default {
out.push_str(&format!("{} ", self.visibility));
}
if self.dll_storage != DLLStorageClass::Default {
out.push_str(&format!("{} ", self.dll_storage));
}
if self.is_declaration {
out.push_str("declare ");
} else {
out.push_str("define ");
}
out.push_str(&format!("{} @{}(", ret_str, name));
let param_strs: Vec<String> = self
.params
.iter()
.enumerate()
.map(|(i, t)| {
let arg_name = if i < self.arguments.len() {
self.arguments[i].borrow().name.clone()
} else {
format!("%arg{}", i)
};
format!("{} {}", Type::pretty_print(t), arg_name)
})
.collect();
out.push_str(¶m_strs.join(", "));
if self.is_vararg {
if !param_strs.is_empty() {
out.push_str(", ...");
} else {
out.push_str("...");
}
}
out.push(')');
if let Some(ref sec) = self.section {
out.push_str(&format!(" section \"{}\"", sec));
}
if self.alignment > 0 {
out.push_str(&format!(" align {}", self.alignment));
}
if let Some(ref gc) = self.gc {
out.push_str(&format!(" gc \"{}\"", gc));
}
if let Some(ref cd) = self.comdat {
out.push_str(&format!(" comdat({})", cd));
}
if self.is_declaration {
out.push('\n');
} else {
out.push_str(" {\n");
for bb in &self.basic_blocks {
let bb_name = bb.borrow().name.clone();
out.push_str(&format!(
"{}:\n",
if bb_name.is_empty() {
"<unnamed>"
} else {
&bb_name
}
));
for inst in &bb.borrow().operands {
let iname = inst.borrow().name.clone();
out.push_str(&format!(
" {}\n",
if iname.is_empty() { "<inst>" } else { &iname }
));
}
}
out.push_str("}\n");
}
out
}
pub fn add_fn_attr(&mut self, attr: AttributeKind) {
self.attributes.add_fn_attr(attr);
}
pub fn remove_fn_attr(&mut self, attr: &AttributeKind) {
self.attributes.remove_fn_attr(attr);
}
pub fn has_fn_attribute(&self, attr: &AttributeKind) -> bool {
self.attributes.has_fn_attr(attr)
}
pub fn get_fn_attribute(&self, kind: &AttributeKind) -> Option<&AttributeKind> {
self.attributes.fn_attrs.iter().find(|a| a == &kind)
}
pub fn add_ret_attr(&mut self, attr: ParamAttrKind) {
self.attributes.add_ret_attr(attr);
}
pub fn add_param_attr(&mut self, param_idx: u32, attr: ParamAttrKind) {
self.attributes.add_param_attr(param_idx, attr);
}
pub fn get_attributes(&self) -> &AttributeList {
&self.attributes
}
pub fn set_attributes(&mut self, attrs: AttributeList) {
self.attributes = attrs;
}
pub fn get_attribute_list(&self) -> &AttributeList {
&self.attributes
}
pub fn set_attribute_list(&mut self, attrs: AttributeList) {
self.attributes = attrs;
}
pub fn has_no_inline(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::NoInline)
}
pub fn has_always_inline(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::AlwaysInline)
}
pub fn has_no_unwind(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::NoUnwind)
}
pub fn has_no_return(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::NoReturn)
}
pub fn has_read_none(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::ReadNone)
}
pub fn has_read_only(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::ReadOnly)
}
pub fn has_write_only(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::WriteOnly)
}
pub fn has_opt_none(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::OptimizeNone)
}
pub fn has_opt_size(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::OptimizeForSize)
}
pub fn has_min_size(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::Minsize)
}
pub fn has_stack_protect(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::Ssp)
|| self.attributes.has_fn_attr(&AttributeKind::SspReq)
|| self.attributes.has_fn_attr(&AttributeKind::SspStrong)
}
pub fn has_sanitize_address(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::SanitizeAddress)
}
pub fn has_sanitize_thread(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::SanitizeThread)
}
pub fn has_sanitize_memory(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::SanitizeMemory)
}
pub fn has_uwtable(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::UWTable)
}
pub fn has_no_cf_check(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::NoCfCheck)
}
pub fn has_safe_stack(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::SafeStack)
}
pub fn has_shadow_call_stack(&self) -> bool {
self.attributes.has_fn_attr(&AttributeKind::ShadowCallStack)
}
pub fn has_fn_ret_thunk_extern(&self) -> bool {
self.attributes
.has_fn_attr(&AttributeKind::FnRetThunkExtern)
}
pub fn has_speculative_load_hardening(&self) -> bool {
self.attributes
.has_fn_attr(&AttributeKind::SpeculativeLoadHardening)
}
pub fn get_section(&self) -> Option<&str> {
self.section.as_deref()
}
pub fn set_section_name(&mut self, section: &str) {
self.section = Some(section.to_string());
}
pub fn get_comdat(&self) -> Option<&str> {
self.comdat.as_deref()
}
pub fn set_comdat_name(&mut self, comdat: &str) {
self.comdat = Some(comdat.to_string());
}
pub fn has_comdat(&self) -> bool {
self.comdat.is_some()
}
pub fn has_section(&self) -> bool {
self.section.is_some()
}
pub fn get_prefix_data(&self) -> Option<ValueRef> {
self.prefix_data.clone()
}
pub fn set_prefix_data(&mut self, data: ValueRef) {
self.prefix_data = Some(data);
}
pub fn has_prefix_data(&self) -> bool {
self.prefix_data.is_some()
}
pub fn get_prologue_data(&self) -> Option<ValueRef> {
self.prologue_data.clone()
}
pub fn set_prologue_data(&mut self, data: ValueRef) {
self.prologue_data = Some(data);
}
pub fn has_prologue_data(&self) -> bool {
self.prologue_data.is_some()
}
pub fn get_gc(&self) -> Option<&str> {
self.gc.as_deref()
}
pub fn has_gc(&self) -> bool {
self.gc.is_some()
}
pub fn get_personality_fn(&self) -> Option<ValueRef> {
self.personality.clone()
}
pub fn set_personality_fn(&mut self, func: ValueRef) {
self.personality = Some(func);
}
pub fn has_personality_fn(&self) -> bool {
self.personality.is_some()
}
pub fn arg_begin(&self) -> std::slice::Iter<'_, ValueRef> {
self.arguments.iter()
}
pub fn arg_end(&self) -> std::slice::Iter<'_, ValueRef> {
self.arguments.iter()
}
pub fn arg_size(&self) -> usize {
self.arguments.len()
}
pub fn has_args(&self) -> bool {
!self.arguments.is_empty()
}
pub fn get_arg_name(&self, index: usize) -> Option<String> {
self.arguments
.get(index)
.map(|a| a.borrow().name.clone())
.filter(|n| !n.is_empty())
}
pub fn get_arg_type(&self, index: usize) -> Option<Type> {
self.params.get(index).cloned()
}
pub fn is_var_arg(&self) -> bool {
self.is_vararg
}
pub fn get_function_type(&self) -> Type {
let ret_id = self
.value
.borrow()
.return_type
.as_ref()
.map(|t| t.id)
.unwrap_or_else(|| Type::void().id);
let param_ids: Vec<crate::types::TypeId> = self.params.iter().map(|t| t.id).collect();
Type::function_type_with(ret_id, param_ids, self.is_vararg)
}
pub fn get_all_metadata(&self) -> HashMap<u32, u32> {
self.value.borrow().metadata.clone()
}
pub fn set_metadata(&mut self, kind: u32, node: u32) {
self.value.borrow_mut().metadata.insert(kind, node);
}
pub fn get_metadata(&self, kind: u32) -> Option<u32> {
self.value.borrow().metadata.get(&kind).copied()
}
pub fn erase_metadata(&mut self, kind: u32) -> bool {
self.value.borrow_mut().metadata.remove(&kind).is_some()
}
pub fn has_metadata(&self) -> bool {
!self.value.borrow().metadata.is_empty()
}
pub fn get_subprogram(&self) -> Option<u32> {
self.subprogram
}
pub fn back(&self) -> Option<ValueRef> {
self.basic_blocks.last().cloned()
}
pub fn has_address_taken(&self) -> bool {
self.value.borrow().uses.iter().any(|u| {
u.user.upgrade().map_or(false, |user_ref| {
user_ref.borrow().subclass != SubclassKind::CallInst
})
})
}
pub fn get_num_uses(&self) -> usize {
self.value.borrow().uses.len()
}
pub fn is_declaration(&self) -> bool {
self.is_declaration
}
pub fn is_definition(&self) -> bool {
!self.is_declaration
}
pub fn get_calling_conv(&self) -> u32 {
self.calling_conv
}
pub fn set_calling_conv_id(&mut self, cc: u32) {
self.calling_conv = cc;
}
pub fn clone_function_into(&mut self, src: &Function) -> Result<(), String> {
if !self.basic_blocks.is_empty() && self.basic_blocks.len() != src.basic_blocks.len() {
return Err(
"clone_function_into: destination function must be empty or have matching block count"
.to_string(),
);
}
for src_bb in &src.basic_blocks {
let bb_name = src_bb.borrow().name.clone();
let mut v = Value::new(Type::label())
.named(&bb_name)
.with_subclass(SubclassKind::BasicBlock);
for inst in &src_bb.borrow().operands {
let src_val = inst.borrow();
let mut cloned_inst = Value::new(src_val.ty.clone())
.named(&src_val.name)
.with_subclass(src_val.subclass);
if let Some(op) = src_val.opcode {
cloned_inst.set_opcode(op);
}
cloned_inst.num_operands = src_val.num_operands;
cloned_inst.operands = src_val.operands.clone();
v.operands.push(valref(cloned_inst));
}
v.num_operands = v.operands.len();
self.add_basic_block(valref(v));
}
Ok(())
}
pub fn copy_attributes_from(&mut self, src: &Function) {
self.attributes = src.attributes.clone();
self.linkage = src.linkage;
self.visibility = src.visibility;
self.calling_conv = src.calling_conv;
self.dll_storage = src.dll_storage;
self.alignment = src.alignment;
self.section = src.section.clone();
self.comdat = src.comdat.clone();
self.gc = src.gc.clone();
self.personality = src.personality.clone();
self.subprogram = src.subprogram;
}
pub fn steal_argument_list_from(&mut self, src: &mut Function) {
self.arguments = std::mem::take(&mut src.arguments);
self.params = std::mem::take(&mut src.params);
self.is_vararg = src.is_vararg;
src.is_vararg = false;
}
pub fn get_dll_storage_class(&self) -> DLLStorageClass {
self.dll_storage
}
pub fn set_dll_storage_class_kind(&mut self, cls: DLLStorageClass) {
self.dll_storage = cls;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::basic_block::new_basic_block;
use crate::value::valref;
fn make_inst(name: &str, op: Opcode) -> ValueRef {
valref(
Value::new(Type::void())
.named(name)
.with_subclass(SubclassKind::Instruction)
.with_opcode(op),
)
}
#[test]
fn test_new_function_basic() {
let f = Function::new("myfunc", Type::i32(), &[Type::i32(), Type::i64()]);
assert_eq!(f.get_name(), "myfunc");
assert_eq!(f.get_arg_count(), 2);
assert!(f.get_return_type().unwrap().is_integer());
assert!(f.is_declaration_fn());
assert_eq!(f.get_linkage(), Linkage::External);
}
#[test]
fn test_from_value() {
let val = new_function("f", Type::void(), &[]);
let f = Function::from_value(val.clone());
assert!(Rc::ptr_eq(&f.value, &val));
}
#[test]
fn test_get_arg() {
let f = Function::new("f", Type::i32(), &[Type::i8(), Type::i16(), Type::i32()]);
assert!(f.get_arg(0).is_some());
assert!(f.get_arg(1).is_some());
assert!(f.get_arg(2).is_some());
assert!(f.get_arg(3).is_none());
assert_eq!(f.get_arg_count(), 3);
}
#[test]
fn test_set_linkage() {
let mut f = Function::new("f", Type::void(), &[]);
assert_eq!(f.get_linkage(), Linkage::External);
f.set_linkage(Linkage::Internal);
assert_eq!(f.get_linkage(), Linkage::Internal);
}
#[test]
fn test_linkage_is_local() {
assert!(!Linkage::External.is_local());
assert!(Linkage::Internal.is_local());
assert!(Linkage::Private.is_local());
assert!(!Linkage::WeakAny.is_local());
}
#[test]
fn test_linkage_discardable_if_unused() {
assert!(!Linkage::External.is_discardable_if_unused());
assert!(Linkage::LinkOnceAny.is_discardable_if_unused());
assert!(Linkage::WeakAny.is_discardable_if_unused());
assert!(Linkage::AvailableExternally.is_discardable_if_unused());
}
#[test]
fn test_linkage_display() {
assert_eq!(Linkage::Internal.to_string(), "internal");
assert_eq!(Linkage::External.to_string(), "external");
assert_eq!(Linkage::WeakAny.to_string(), "weak");
}
#[test]
fn test_linkage_from_str() {
assert_eq!(Linkage::from_str("private"), Some(Linkage::Private));
assert_eq!(Linkage::from_str("common"), Some(Linkage::Common));
assert_eq!(Linkage::from_str("bogus"), None);
}
#[test]
fn test_visibility_display() {
assert_eq!(Visibility::Default.to_string(), "default");
assert_eq!(Visibility::Hidden.to_string(), "hidden");
assert_eq!(Visibility::Protected.to_string(), "protected");
}
#[test]
fn test_dll_storage_class_display() {
assert_eq!(DLLStorageClass::Default.to_string(), "default");
assert_eq!(DLLStorageClass::DLLImport.to_string(), "dllimport");
assert_eq!(DLLStorageClass::DLLExport.to_string(), "dllexport");
}
#[test]
fn test_add_and_get_basic_blocks() {
let mut f = Function::new("f", Type::void(), &[]);
assert_eq!(f.get_block_count(), 0);
assert!(f.get_entry_block().is_none());
let entry = new_basic_block("entry");
f.add_basic_block(entry.clone());
assert_eq!(f.get_block_count(), 1);
let got = f.get_entry_block().unwrap();
assert!(Rc::ptr_eq(&got, &entry));
}
#[test]
fn test_remove_basic_block() {
let mut f = Function::new("f", Type::void(), &[]);
let bb1 = new_basic_block("bb1");
let bb2 = new_basic_block("bb2");
f.add_basic_block(bb1.clone());
f.add_basic_block(bb2.clone());
assert_eq!(f.get_block_count(), 2);
assert!(f.remove_basic_block(&bb1));
assert_eq!(f.get_block_count(), 1);
assert!(!f.remove_basic_block(&bb1)); }
#[test]
fn test_get_block_by_name() {
let mut f = Function::new("f", Type::void(), &[]);
let entry = new_basic_block("entry");
let body = new_basic_block("body");
f.add_basic_block(entry.clone());
f.add_basic_block(body.clone());
let found = f.get_block_by_name("entry");
assert!(found.is_some());
assert_eq!(found.unwrap().borrow().name, "entry");
assert!(f.get_block_by_name("nonexistent").is_none());
}
#[test]
fn test_is_intrinsic() {
let mut f = Function::new("llvm.memcpy.p0.p0.i64", Type::void(), &[]);
assert!(f.is_intrinsic());
let id = f.get_intrinsic_id();
assert!(id.is_some());
f.set_name("normal_func");
assert!(!f.is_intrinsic());
assert!(f.get_intrinsic_id().is_none());
}
#[test]
fn test_has_exact_definition() {
let mut f = Function::new("f", Type::void(), &[]);
assert!(f.is_declaration_fn());
assert!(!f.has_exact_definition());
f.set_declaration(false);
assert!(!f.is_declaration_fn());
assert!(f.has_exact_definition());
let bb = new_basic_block("entry");
f.add_basic_block(bb);
assert!(f.has_exact_definition());
}
#[test]
fn test_set_section() {
let mut f = Function::new("f", Type::void(), &[]);
assert!(f.section.is_none());
f.set_section(".text.hot");
assert_eq!(f.section.as_deref(), Some(".text.hot"));
f.clear_section();
assert!(f.section.is_none());
}
#[test]
fn test_set_alignment() {
let mut f = Function::new("f", Type::void(), &[]);
assert_eq!(f.alignment, 0);
f.set_alignment(16);
assert_eq!(f.alignment, 16);
}
#[test]
fn test_set_gc() {
let mut f = Function::new("f", Type::void(), &[]);
assert!(f.gc.is_none());
f.set_gc("statepoint-example");
assert_eq!(f.gc.as_deref(), Some("statepoint-example"));
}
#[test]
fn test_set_calling_conv() {
let mut f = Function::new("f", Type::void(), &[]);
assert_eq!(f.calling_conv, 0);
f.set_calling_conv(1); assert_eq!(f.calling_conv, 1);
}
#[test]
fn test_set_personality() {
let mut f = Function::new("f", Type::void(), &[]);
assert!(f.personality.is_none());
let pers_fn = new_function("__gxx_personality_v0", Type::i32(), &[]);
f.set_personality(pers_fn.clone());
assert!(f.personality.is_some());
assert!(Rc::ptr_eq(f.personality.as_ref().unwrap(), &pers_fn));
}
#[test]
fn test_set_subprogram() {
let mut f = Function::new("f", Type::void(), &[]);
assert!(f.subprogram.is_none());
f.set_subprogram(42);
assert_eq!(f.subprogram, Some(42));
}
#[test]
fn test_view_cfg() {
let mut f = Function::new("test_fn", Type::void(), &[]);
f.set_declaration(false);
let entry = new_basic_block("entry");
let exit = new_basic_block("exit");
let ret = make_inst("ret", Opcode::Ret);
exit.borrow_mut().operands.push(ret);
exit.borrow_mut().num_operands = 1;
f.add_basic_block(entry.clone());
f.add_basic_block(exit.clone());
let cfg = f.view_cfg();
assert!(cfg.contains("CFG for function 'test_fn'"));
assert!(cfg.contains("entry:"));
assert!(cfg.contains("exit:"));
assert!(cfg.contains("Blocks: 2"));
}
#[test]
fn test_verify_empty_declaration() {
let f = Function::new("f", Type::void(), &[]);
assert!(f.verify().is_ok());
}
#[test]
fn test_verify_blocks_not_declaration() {
let mut f = Function::new("f", Type::void(), &[]);
f.set_declaration(false);
let result = f.verify();
assert!(result.is_err());
let errs = result.unwrap_err();
assert!(errs.iter().any(|e| e.contains("no basic blocks")));
}
#[test]
fn test_verify_blocks_is_declaration() {
let mut f = Function::new("f", Type::void(), &[]);
f.set_declaration(true);
let bb = new_basic_block("entry");
f.add_basic_block(bb);
let result = f.verify();
assert!(result.is_err());
let errs = result.unwrap_err();
assert!(errs.iter().any(|e| e.contains("marked as declaration")));
}
#[test]
fn test_verify_unterminated_block() {
let mut f = Function::new("f", Type::void(), &[]);
f.set_declaration(false);
let entry = new_basic_block("entry");
f.add_basic_block(entry); let result = f.verify();
assert!(result.is_err());
let errs = result.unwrap_err();
assert!(errs.iter().any(|e| e.contains("not terminated")));
}
#[test]
fn test_verify_valid() {
let mut f = Function::new("f", Type::void(), &[]);
f.set_declaration(false);
let entry = new_basic_block("entry");
let ret = make_inst("ret", Opcode::Ret);
entry.borrow_mut().operands.push(ret);
entry.borrow_mut().num_operands = 1;
f.add_basic_block(entry);
assert!(f.verify().is_ok());
}
#[test]
fn test_dump_declaration() {
let mut f = Function::new("foo", Type::i32(), &[Type::i64()]);
f.set_linkage(Linkage::Internal);
f.set_section(".text");
let dump = f.dump();
assert!(dump.contains("declare"));
assert!(dump.contains("internal"));
assert!(dump.contains("@foo"));
assert!(dump.contains("i32"));
assert!(dump.contains(".text"));
}
#[test]
fn test_dump_definition() {
let mut f = Function::new("main", Type::i32(), &[]);
f.set_declaration(false);
let entry = new_basic_block("entry");
let ret = make_inst("ret", Opcode::Ret);
entry.borrow_mut().operands.push(ret);
entry.borrow_mut().num_operands = 1;
f.add_basic_block(entry);
let dump = f.dump();
assert!(dump.contains("define"));
assert!(dump.contains("@main"));
assert!(dump.contains("entry:"));
assert!(dump.contains("ret"));
}
#[test]
fn test_vararg() {
let mut f = Function::new("printf", Type::i32(), &[Type::i8()]);
assert!(!f.is_vararg_fn());
f.set_vararg(true);
assert!(f.is_vararg_fn());
}
#[test]
fn test_intrinsic_id_various() {
let mut f = Function::new("llvm.sqrt.f64", Type::double(), &[Type::double()]);
assert!(f.is_intrinsic());
assert_eq!(f.get_intrinsic_id(), Some(45));
let mut g = Function::new("llvm.cttz.i32", Type::i32(), &[Type::i32()]);
assert_eq!(g.get_intrinsic_id(), Some(73));
let mut h = Function::new("llvm.bswap.i64", Type::i64(), &[Type::i64()]);
assert_eq!(h.get_intrinsic_id(), Some(71)); }
}
use std::rc::Rc;