use crate::attributes::AttributeList;
use crate::types::Type;
use crate::types::TypeKind;
use crate::value::SubclassKind;
use crate::value::Value;
use crate::value::ValueRef;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::HashSet;
use std::rc::Rc;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Comdat {
pub name: String,
pub kind: ComdatKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ComdatKind {
Any,
ExactMatch,
Largest,
NoDeduplicate,
SameSize,
}
impl ComdatKind {
pub fn as_str(&self) -> &'static str {
match self {
ComdatKind::Any => "any",
ComdatKind::ExactMatch => "exactmatch",
ComdatKind::Largest => "largest",
ComdatKind::NoDeduplicate => "nodeduplicate",
ComdatKind::SameSize => "samesize",
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"any" => Some(ComdatKind::Any),
"exactmatch" => Some(ComdatKind::ExactMatch),
"largest" => Some(ComdatKind::Largest),
"nodeduplicate" => Some(ComdatKind::NoDeduplicate),
"samesize" => Some(ComdatKind::SameSize),
_ => None,
}
}
}
impl std::fmt::Display for ComdatKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone)]
pub struct ModuleFlag {
pub behavior: u32,
pub key: String,
pub value: MetadataValue,
}
#[derive(Debug, Clone)]
pub enum MetadataValue {
Int(i64),
String(String),
Node(Vec<u32>),
Null,
}
impl MetadataValue {
pub fn as_int(&self) -> Option<i64> {
match self {
MetadataValue::Int(v) => Some(*v),
_ => None,
}
}
pub fn as_string(&self) -> Option<&str> {
match self {
MetadataValue::String(s) => Some(s.as_str()),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Linkage {
External,
AvailableExternally,
LinkOnceAny,
LinkOnceODR,
WeakAny,
WeakODR,
Appending,
Internal,
Private,
ExternalWeak,
Common,
}
impl Linkage {
pub fn as_str(&self) -> &'static str {
match self {
Linkage::External => "external",
Linkage::AvailableExternally => "available_externally",
Linkage::LinkOnceAny => "linkonce",
Linkage::LinkOnceODR => "linkonce_odr",
Linkage::WeakAny => "weak",
Linkage::WeakODR => "weak_odr",
Linkage::Appending => "appending",
Linkage::Internal => "internal",
Linkage::Private => "private",
Linkage::ExternalWeak => "extern_weak",
Linkage::Common => "common",
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"external" => Some(Linkage::External),
"available_externally" => Some(Linkage::AvailableExternally),
"linkonce" => Some(Linkage::LinkOnceAny),
"linkonce_odr" => Some(Linkage::LinkOnceODR),
"weak" => Some(Linkage::WeakAny),
"weak_odr" => Some(Linkage::WeakODR),
"appending" => Some(Linkage::Appending),
"internal" => Some(Linkage::Internal),
"private" => Some(Linkage::Private),
"extern_weak" => Some(Linkage::ExternalWeak),
"common" => Some(Linkage::Common),
_ => None,
}
}
}
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)]
pub struct Alias {
pub name: String,
pub aliasee: String,
pub ty: Type,
pub linkage: Linkage,
pub visibility: Visibility,
}
impl Alias {
pub fn new(name: &str, aliasee: &str, ty: Type) -> Self {
Self {
name: name.to_string(),
aliasee: aliasee.to_string(),
ty,
linkage: Linkage::External,
visibility: Visibility::Default,
}
}
pub fn with_linkage(mut self, l: Linkage) -> Self {
self.linkage = l;
self
}
pub fn with_visibility(mut self, v: Visibility) -> Self {
self.visibility = v;
self
}
}
#[derive(Debug, Clone)]
pub struct IFunc {
pub name: String,
pub resolver: String,
pub ty: Type,
}
impl IFunc {
pub fn new(name: &str, resolver: &str, ty: Type) -> Self {
Self {
name: name.to_string(),
resolver: resolver.to_string(),
ty,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModuleFlagBehavior {
Error = 1,
Warning = 2,
Require = 3,
Override = 4,
Append = 5,
AppendUnique = 6,
Max = 7,
Min = 8,
}
impl ModuleFlagBehavior {
pub fn from_u32(v: u32) -> Option<Self> {
match v {
1 => Some(ModuleFlagBehavior::Error),
2 => Some(ModuleFlagBehavior::Warning),
3 => Some(ModuleFlagBehavior::Require),
4 => Some(ModuleFlagBehavior::Override),
5 => Some(ModuleFlagBehavior::Append),
6 => Some(ModuleFlagBehavior::AppendUnique),
7 => Some(ModuleFlagBehavior::Max),
8 => Some(ModuleFlagBehavior::Min),
_ => None,
}
}
pub fn to_u32(self) -> u32 {
self as u32
}
pub fn as_str(&self) -> &'static str {
match self {
ModuleFlagBehavior::Error => "Error",
ModuleFlagBehavior::Warning => "Warning",
ModuleFlagBehavior::Require => "Require",
ModuleFlagBehavior::Override => "Override",
ModuleFlagBehavior::Append => "Append",
ModuleFlagBehavior::AppendUnique => "AppendUnique",
ModuleFlagBehavior::Max => "Max",
ModuleFlagBehavior::Min => "Min",
}
}
}
impl std::fmt::Display for ModuleFlagBehavior {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone)]
pub struct TypeNameEntry {
pub name: String,
pub ty: Type,
}
impl TypeNameEntry {
pub fn new(name: &str, ty: Type) -> Self {
Self {
name: name.to_string(),
ty,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CodeModel {
Default,
Small,
Kernel,
Medium,
Large,
Tiny,
}
impl CodeModel {
pub fn as_str(&self) -> &'static str {
match self {
CodeModel::Default => "default",
CodeModel::Small => "small",
CodeModel::Kernel => "kernel",
CodeModel::Medium => "medium",
CodeModel::Large => "large",
CodeModel::Tiny => "tiny",
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"default" => Some(CodeModel::Default),
"small" => Some(CodeModel::Small),
"kernel" => Some(CodeModel::Kernel),
"medium" => Some(CodeModel::Medium),
"large" => Some(CodeModel::Large),
"tiny" => Some(CodeModel::Tiny),
_ => None,
}
}
}
impl std::fmt::Display for CodeModel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone)]
pub struct ProfileSummary {
pub kind: String,
pub total_count: u64,
pub max_count: u64,
pub max_internal_count: u64,
pub max_function_count: u64,
pub num_counts: u64,
pub num_functions: u64,
pub detailed_summary: Vec<(u64, u64, u64)>,
}
impl ProfileSummary {
pub fn new(kind: &str) -> Self {
Self {
kind: kind.to_string(),
total_count: 0,
max_count: 0,
max_internal_count: 0,
max_function_count: 0,
num_counts: 0,
num_functions: 0,
detailed_summary: Vec::new(),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ValueSymbolTable {
pub table: HashMap<String, ValueRef>,
}
impl ValueSymbolTable {
pub fn new() -> Self {
Self {
table: HashMap::new(),
}
}
pub fn len(&self) -> usize {
self.table.len()
}
pub fn is_empty(&self) -> bool {
self.table.is_empty()
}
pub fn insert(&mut self, name: &str, v: ValueRef) -> Option<ValueRef> {
self.table.insert(name.to_string(), v)
}
pub fn get(&self, name: &str) -> Option<&ValueRef> {
self.table.get(name)
}
pub fn remove(&mut self, name: &str) -> Option<ValueRef> {
self.table.remove(name)
}
pub fn contains(&self, name: &str) -> bool {
self.table.contains_key(name)
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &ValueRef)> {
self.table.iter()
}
pub fn names(&self) -> Vec<&String> {
self.table.keys().collect()
}
}
#[derive(Debug, Clone)]
pub struct Module {
pub name: String,
pub source_filename: String,
pub target_triple: Option<String>,
pub data_layout: Option<String>,
pub functions: Vec<ValueRef>,
pub globals: Vec<ValueRef>,
pub aliases: Vec<ValueRef>,
pub ifuncs: Vec<ValueRef>,
pub types: Vec<Type>,
pub named_types: HashMap<String, Type>,
pub named_metadata: HashMap<String, Vec<u32>>,
pub comdats: HashMap<String, Comdat>,
pub attr_groups: HashMap<u32, AttributeList>,
pub imported_functions: Vec<String>,
pub flags: Vec<ModuleFlag>,
next_md_id: u32,
next_attr_group_id: u32,
pub inline_asm: String,
pub module_identifier: String,
pub declarations: Vec<ValueRef>,
pub sdk_version: Option<String>,
pub code_model: Option<CodeModel>,
pub profile_summary: Option<ProfileSummary>,
pub value_symtab: ValueSymbolTable,
pub type_names: Vec<TypeNameEntry>,
pub module_summary: Option<String>,
pub is_materialized: bool,
pub materializer_name: Option<String>,
pub preserved_analyses: Vec<String>,
}
unsafe impl Send for Module {}
impl Module {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
source_filename: String::new(),
target_triple: None,
data_layout: None,
functions: Vec::new(),
globals: Vec::new(),
aliases: Vec::new(),
ifuncs: Vec::new(),
types: Vec::new(),
named_types: HashMap::new(),
named_metadata: HashMap::new(),
comdats: HashMap::new(),
attr_groups: HashMap::new(),
imported_functions: Vec::new(),
flags: Vec::new(),
next_md_id: 0,
next_attr_group_id: 0,
inline_asm: String::new(),
module_identifier: name.to_string(),
declarations: Vec::new(),
sdk_version: None,
code_model: None,
profile_summary: None,
value_symtab: ValueSymbolTable::new(),
type_names: Vec::new(),
module_summary: None,
is_materialized: true,
materializer_name: None,
preserved_analyses: Vec::new(),
}
}
pub fn set_source_filename(&mut self, filename: &str) {
self.source_filename = filename.to_string();
}
pub fn set_target_triple(&mut self, t: &str) {
self.target_triple = Some(t.to_string());
}
pub fn get_target_triple(&self) -> &str {
self.target_triple.as_deref().unwrap_or("")
}
pub fn set_data_layout(&mut self, dl: &str) {
self.data_layout = Some(dl.to_string());
}
pub fn get_data_layout(&self) -> &str {
self.data_layout.as_deref().unwrap_or("")
}
pub fn add_type(&mut self, ty: Type) -> u32 {
let existing = self.types.iter().position(|t| t.kind == ty.kind);
if let Some(idx) = existing {
return idx as u32;
}
let idx = self.types.len() as u32;
self.types.push(ty);
idx
}
pub fn get_type(&self, index: u32) -> Option<&Type> {
self.types.get(index as usize)
}
pub fn get_type_count(&self) -> usize {
self.types.len()
}
pub fn get_types(&self) -> &[Type] {
&self.types
}
pub fn add_named_type(&mut self, name: &str, ty: Type) {
self.named_types.insert(name.to_string(), ty);
}
pub fn get_named_type(&self, name: &str) -> Option<&Type> {
self.named_types.get(name)
}
pub fn remove_named_type(&mut self, name: &str) -> bool {
self.named_types.remove(name).is_some()
}
pub fn add_function(&mut self, f: ValueRef) -> bool {
let name = f.borrow().name.clone();
if self
.functions
.iter()
.any(|existing| existing.borrow().name == name)
{
return false;
}
self.functions.push(f);
true
}
pub fn add_function_unchecked(&mut self, f: ValueRef) {
self.functions.push(f);
}
pub fn get_function(&self, name: &str) -> Option<&ValueRef> {
self.functions.iter().find(|f| f.borrow().name == name)
}
pub fn get_function_mut(&mut self, name: &str) -> Option<&mut ValueRef> {
self.functions.iter_mut().find(|f| f.borrow().name == name)
}
pub fn remove_function(&mut self, name: &str) -> bool {
if let Some(pos) = self.functions.iter().position(|f| f.borrow().name == name) {
self.functions.remove(pos);
true
} else {
false
}
}
pub fn has_function(&self, name: &str) -> bool {
self.functions.iter().any(|f| f.borrow().name == name)
}
pub fn get_function_count(&self) -> usize {
self.functions.len()
}
pub fn get_function_names(&self) -> Vec<String> {
self.functions
.iter()
.map(|f| f.borrow().name.clone())
.collect()
}
pub fn add_imported_function(&mut self, name: &str) {
if !self.imported_functions.iter().any(|n| n == name) {
self.imported_functions.push(name.to_string());
}
}
pub fn is_imported_function(&self, name: &str) -> bool {
self.imported_functions.iter().any(|n| n == name)
}
pub fn add_global_variable(&mut self, gv: ValueRef) -> bool {
let name = gv.borrow().name.clone();
if self.globals.iter().any(|g| g.borrow().name == name) {
return false;
}
self.globals.push(gv);
true
}
pub fn get_global_variable(&self, name: &str) -> Option<&ValueRef> {
self.globals.iter().find(|g| g.borrow().name == name)
}
pub fn remove_global_variable(&mut self, name: &str) -> bool {
if let Some(pos) = self.globals.iter().position(|g| g.borrow().name == name) {
self.globals.remove(pos);
true
} else {
false
}
}
pub fn get_global_count(&self) -> usize {
self.globals.len()
}
pub fn add_alias(&mut self, alias: ValueRef) {
self.aliases.push(alias);
}
pub fn get_alias(&self, name: &str) -> Option<&ValueRef> {
self.aliases.iter().find(|a| a.borrow().name == name)
}
pub fn remove_alias(&mut self, name: &str) -> bool {
if let Some(pos) = self.aliases.iter().position(|a| a.borrow().name == name) {
self.aliases.remove(pos);
true
} else {
false
}
}
pub fn add_ifunc(&mut self, ifunc: ValueRef) {
self.ifuncs.push(ifunc);
}
pub fn get_ifunc(&self, name: &str) -> Option<&ValueRef> {
self.ifuncs.iter().find(|i| i.borrow().name == name)
}
pub fn add_named_metadata(&mut self, name: &str, operands: Vec<u32>) {
self.named_metadata
.entry(name.to_string())
.or_insert_with(Vec::new)
.extend(operands);
}
pub fn get_named_metadata(&self, name: &str) -> Option<&Vec<u32>> {
self.named_metadata.get(name)
}
pub fn remove_named_metadata(&mut self, name: &str) -> bool {
self.named_metadata.remove(name).is_some()
}
pub fn has_named_metadata(&self, name: &str) -> bool {
self.named_metadata.contains_key(name)
}
pub fn next_metadata_id(&mut self) -> u32 {
let id = self.next_md_id;
self.next_md_id += 1;
id
}
pub fn get_named_metadata_keys(&self) -> Vec<&String> {
self.named_metadata.keys().collect()
}
pub fn add_comdat(&mut self, name: &str, kind: ComdatKind) -> bool {
if self.comdats.contains_key(name) {
return false;
}
self.comdats.insert(
name.to_string(),
Comdat {
name: name.to_string(),
kind,
},
);
true
}
pub fn get_comdat(&self, name: &str) -> Option<&Comdat> {
self.comdats.get(name)
}
pub fn remove_comdat(&mut self, name: &str) -> bool {
self.comdats.remove(name).is_some()
}
pub fn next_attr_group_id(&mut self) -> u32 {
let id = self.next_attr_group_id;
self.next_attr_group_id += 1;
id
}
pub fn add_attr_group(&mut self, id: u32, attrs: AttributeList) {
self.attr_groups.insert(id, attrs);
}
pub fn get_attr_group(&self, id: u32) -> Option<&AttributeList> {
self.attr_groups.get(&id)
}
pub fn remove_attr_group(&mut self, id: u32) -> bool {
self.attr_groups.remove(&id).is_some()
}
pub fn add_flag(&mut self, flag: ModuleFlag) {
self.flags.push(flag);
}
pub fn get_flags(&self) -> &[ModuleFlag] {
&self.flags
}
pub fn get_flag(&self, key: &str) -> Option<&ModuleFlag> {
self.flags.iter().find(|f| f.key == key)
}
pub fn remove_flag(&mut self, key: &str) -> usize {
let before = self.flags.len();
self.flags.retain(|f| f.key != key);
before - self.flags.len()
}
pub fn is_valid(&self) -> bool {
if self.name.is_empty() {
return false;
}
true
}
pub fn dump(&self) -> String {
let mut out = String::new();
if !self.source_filename.is_empty() {
out.push_str(&format!("; ModuleID = '{}'\n", self.name));
out.push_str(&format!("source_filename = \"{}\"\n", self.source_filename));
} else {
out.push_str(&format!("; ModuleID = '{}'\n", self.name));
}
if let Some(ref triple) = self.target_triple {
out.push_str(&format!("target triple = \"{}\"\n", triple));
}
if let Some(ref dl) = self.data_layout {
out.push_str(&format!("target datalayout = \"{}\"\n", dl));
}
out.push('\n');
for comdat in self.comdats.values() {
out.push_str(&format!("{} = comdat {}\n", comdat.name, comdat.kind));
}
if !self.comdats.is_empty() {
out.push('\n');
}
for gv in &self.globals {
let name = gv.borrow().name.clone();
let ty = Type::pretty_print(&gv.borrow().ty);
out.push_str(&format!(
"@{} = global {} ...\n",
if name.is_empty() { "<unnamed>" } else { &name },
ty
));
}
if !self.globals.is_empty() {
out.push('\n');
}
for alias in &self.aliases {
let name = alias.borrow().name.clone();
out.push_str(&format!(
"@{} = alias ...\n",
if name.is_empty() { "<unnamed>" } else { &name }
));
}
if !self.aliases.is_empty() {
out.push('\n');
}
for ifunc in &self.ifuncs {
let name = ifunc.borrow().name.clone();
out.push_str(&format!(
"@{} = ifunc ...\n",
if name.is_empty() { "<unnamed>" } else { &name }
));
}
if !self.ifuncs.is_empty() {
out.push('\n');
}
for func in &self.functions {
let fname = func.borrow().name.clone();
let has_body = !func.borrow().operands.is_empty();
if has_body {
out.push_str(&format!("define ... @{}() {{\n", fname));
for block in &func.borrow().operands {
let bb_name = block.borrow().name.clone();
out.push_str(&format!(
"{}:\n",
if bb_name.is_empty() {
"<unnamed>"
} else {
&bb_name
}
));
for inst in &block.borrow().operands {
let iname = inst.borrow().name.clone();
out.push_str(&format!(
" {}\n",
if iname.is_empty() { "<inst>" } else { &iname }
));
}
}
out.push_str("}\n\n");
} else {
out.push_str(&format!("declare ... @{}()\n", fname));
}
}
for (key, nodes) in &self.named_metadata {
out.push_str(&format!("!{} = !{{", key));
let ids: Vec<String> = nodes.iter().map(|id| format!("!{}", id)).collect();
out.push_str(&ids.join(", "));
out.push_str("}\n");
}
if !self.named_metadata.is_empty() {
out.push('\n');
}
if !self.flags.is_empty() {
out.push_str("!llvm.module.flags = !{");
for (i, flag) in self.flags.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
let val_str = match &flag.value {
MetadataValue::Int(v) => format!("i64 {}", v),
MetadataValue::String(s) => format!("!\"{}\"", s),
MetadataValue::Node(ids) => {
let ids_str: Vec<String> =
ids.iter().map(|id| format!("!{}", id)).collect();
format!("!{{{}}}", ids_str.join(", "))
}
MetadataValue::Null => "null".to_string(),
};
out.push_str(&format!(
"!{{{}, !\"{}\", {}}}",
flag.behavior, flag.key, val_str
));
}
out.push_str("}\n");
}
out
}
pub fn clone_empty(&self) -> Self {
Self {
name: self.name.clone(),
source_filename: self.source_filename.clone(),
target_triple: self.target_triple.clone(),
data_layout: self.data_layout.clone(),
functions: Vec::new(),
globals: Vec::new(),
aliases: Vec::new(),
ifuncs: Vec::new(),
types: self.types.clone(),
named_types: self.named_types.clone(),
named_metadata: self.named_metadata.clone(),
comdats: self.comdats.clone(),
attr_groups: self.attr_groups.clone(),
imported_functions: self.imported_functions.clone(),
flags: self.flags.clone(),
next_md_id: self.next_md_id,
next_attr_group_id: self.next_attr_group_id,
inline_asm: self.inline_asm.clone(),
module_identifier: self.module_identifier.clone(),
declarations: Vec::new(),
sdk_version: self.sdk_version.clone(),
code_model: self.code_model,
profile_summary: self.profile_summary.clone(),
value_symtab: ValueSymbolTable::new(),
type_names: self.type_names.clone(),
module_summary: self.module_summary.clone(),
is_materialized: false,
materializer_name: self.materializer_name.clone(),
preserved_analyses: self.preserved_analyses.clone(),
}
}
pub fn clear_bodies(&mut self) {
self.functions.clear();
self.globals.clear();
self.aliases.clear();
self.ifuncs.clear();
}
pub fn get_total_global_count(&self) -> usize {
self.functions.len() + self.globals.len() + self.aliases.len() + self.ifuncs.len()
}
}
impl Module {
pub fn add_global(&mut self, gv: ValueRef) -> bool {
let name = gv.borrow().name.clone();
if self.globals.iter().any(|g| g.borrow().name == name) {
return false;
}
self.globals.push(gv);
true
}
pub fn remove_global(&mut self, name: &str) -> bool {
if let Some(pos) = self.globals.iter().position(|g| g.borrow().name == name) {
self.globals.remove(pos);
true
} else {
false
}
}
pub fn rename_global(&mut self, old_name: &str, new_name: &str) -> bool {
if self.globals.iter().any(|g| g.borrow().name == new_name) {
return false;
}
if let Some(pos) = self
.globals
.iter()
.position(|g| g.borrow().name == old_name)
{
self.globals[pos].borrow_mut().name = new_name.to_string();
true
} else {
false
}
}
pub fn has_global(&self, name: &str) -> bool {
self.globals.iter().any(|g| g.borrow().name == name)
}
pub fn get_or_insert_global(&mut self, name: &str, ty: Type) -> ValueRef {
if let Some(existing) = self.globals.iter().find(|g| g.borrow().name == name) {
return Rc::clone(existing);
}
let gv = ValueRef::new(RefCell::new(
Value::new(ty)
.named(name)
.with_subclass(SubclassKind::GlobalVariable),
));
self.globals.push(Rc::clone(&gv));
gv
}
pub fn set_named_metadata(&mut self, name: &str, operands: Vec<u32>) {
self.named_metadata.insert(name.to_string(), operands);
}
pub fn print(&self, w: &mut dyn std::io::Write) -> std::io::Result<()> {
let s = self.dump();
write!(w, "{}", s)
}
pub fn clone(&self) -> Self {
self.clone_empty()
}
pub fn materialize_all(&mut self) -> bool {
if self.is_materialized {
return false;
}
self.is_materialized = true;
self.materializer_name = None;
true
}
pub fn materialize_all_permanently(&mut self) -> bool {
let changed = self.materialize_all();
self.materializer_name = None;
changed
}
pub fn is_materialized(&self) -> bool {
self.is_materialized
}
pub fn set_materializer(&mut self, name: &str) {
self.materializer_name = Some(name.to_string());
self.is_materialized = false;
}
pub fn get_materializer(&self) -> Option<&str> {
self.materializer_name.as_deref()
}
pub fn dematerialize(&mut self) -> bool {
if !self.is_materialized {
return false;
}
for func in &self.functions {
func.borrow_mut().operands.clear();
func.borrow_mut().num_operands = 0;
}
self.is_materialized = false;
true
}
pub fn is_materializable(&self) -> bool {
self.materializer_name.is_some()
}
pub fn get_value_symbol_table(&self) -> &ValueSymbolTable {
&self.value_symtab
}
pub fn get_value_symbol_table_mut(&mut self) -> &mut ValueSymbolTable {
&mut self.value_symtab
}
pub fn has_value_symbol_table(&self) -> bool {
!self.value_symtab.is_empty()
}
pub fn lookup_value(&self, name: &str) -> Option<&ValueRef> {
self.value_symtab.get(name)
}
pub fn insert_value(&mut self, name: &str, v: ValueRef) {
self.value_symtab.insert(name, v);
}
pub fn remove_value(&mut self, name: &str) -> Option<ValueRef> {
self.value_symtab.remove(name)
}
pub fn rebuild_value_symtab(&mut self) {
self.value_symtab.table.clear();
for gv in &self.globals {
let name = gv.borrow().name.clone();
if !name.is_empty() {
self.value_symtab.insert(&name, Rc::clone(gv));
}
}
for func in &self.functions {
let name = func.borrow().name.clone();
if !name.is_empty() {
self.value_symtab.insert(&name, Rc::clone(func));
}
}
for alias in &self.aliases {
let name = alias.borrow().name.clone();
if !name.is_empty() {
self.value_symtab.insert(&name, Rc::clone(alias));
}
}
for ifunc in &self.ifuncs {
let name = ifunc.borrow().name.clone();
if !name.is_empty() {
self.value_symtab.insert(&name, Rc::clone(ifunc));
}
}
}
pub fn get_module_identifier(&self) -> &str {
&self.module_identifier
}
pub fn set_module_identifier(&mut self, id: &str) {
self.module_identifier = id.to_string();
}
pub fn get_sdk_version(&self) -> Option<&str> {
self.sdk_version.as_deref()
}
pub fn set_sdk_version(&mut self, version: &str) {
self.sdk_version = Some(version.to_string());
}
pub fn clear_sdk_version(&mut self) {
self.sdk_version = None;
}
pub fn get_code_model(&self) -> Option<CodeModel> {
self.code_model
}
pub fn set_code_model(&mut self, cm: CodeModel) {
self.code_model = Some(cm);
}
pub fn get_code_model_str(&self) -> &str {
self.code_model.map(|cm| cm.as_str()).unwrap_or("default")
}
pub fn set_code_model_str(&mut self, s: &str) -> bool {
if let Some(cm) = CodeModel::from_str(s) {
self.code_model = Some(cm);
true
} else {
false
}
}
pub fn get_profile_summary(&self) -> Option<&ProfileSummary> {
self.profile_summary.as_ref()
}
pub fn set_profile_summary(&mut self, ps: ProfileSummary) {
self.profile_summary = Some(ps);
}
pub fn has_profile_summary(&self) -> bool {
self.profile_summary.is_some()
}
pub fn clear_profile_summary(&mut self) {
self.profile_summary = None;
}
pub fn update_profile_total_count(&mut self, total: u64) {
if let Some(ref mut ps) = self.profile_summary {
ps.total_count = total;
}
}
pub fn get_module_summary(&self) -> Option<&str> {
self.module_summary.as_deref()
}
pub fn set_module_summary(&mut self, summary: &str) {
self.module_summary = Some(summary.to_string());
}
pub fn has_module_summary(&self) -> bool {
self.module_summary.is_some()
}
pub fn clear_module_summary(&mut self) {
self.module_summary = None;
}
pub fn preserve_analysis(&mut self, name: &str) {
if !self.preserved_analyses.iter().any(|n| n == name) {
self.preserved_analyses.push(name.to_string());
}
}
pub fn is_analysis_preserved(&self, name: &str) -> bool {
self.preserved_analyses.iter().any(|n| n == name)
}
pub fn clear_preserved_analyses(&mut self) {
self.preserved_analyses.clear();
}
pub fn get_preserved_analyses(&self) -> &[String] {
&self.preserved_analyses
}
pub fn has_attr_group(&self, id: u32) -> bool {
self.attr_groups.contains_key(&id)
}
pub fn get_attr_group_count(&self) -> usize {
self.attr_groups.len()
}
pub fn get_attr_group_ids(&self) -> Vec<u32> {
self.attr_groups.keys().copied().collect()
}
pub fn clear_attr_groups(&mut self) {
self.attr_groups.clear();
}
pub fn get_imported_functions(&self) -> &[String] {
&self.imported_functions
}
pub fn get_imported_function_count(&self) -> usize {
self.imported_functions.len()
}
pub fn clear_imported_functions(&mut self) {
self.imported_functions.clear();
}
pub fn remove_globals_of_kind(&mut self, kind: SubclassKind) -> usize {
let mut removed = 0;
match kind {
SubclassKind::Function => {
removed = self.functions.len();
self.functions.clear();
}
SubclassKind::GlobalVariable => {
removed = self.globals.len();
self.globals.clear();
}
SubclassKind::GlobalAlias => {
removed = self.aliases.len();
self.aliases.clear();
}
SubclassKind::GlobalIFunc => {
removed = self.ifuncs.len();
self.ifuncs.clear();
}
_ => {}
}
removed
}
pub fn is_empty(&self) -> bool {
self.functions.is_empty()
&& self.globals.is_empty()
&& self.aliases.is_empty()
&& self.ifuncs.is_empty()
}
pub fn get_global_object_count(&self) -> usize {
self.functions.len() + self.globals.len() + self.aliases.len() + self.ifuncs.len()
}
pub fn collect_global_names(&self) -> Vec<String> {
let mut names = Vec::new();
for f in &self.functions {
names.push(f.borrow().name.clone());
}
for g in &self.globals {
names.push(g.borrow().name.clone());
}
for a in &self.aliases {
names.push(a.borrow().name.clone());
}
for i in &self.ifuncs {
names.push(i.borrow().name.clone());
}
names
}
pub fn has_external_linkage_global(&self) -> bool {
!self.functions.is_empty() || !self.globals.is_empty()
}
pub fn declared_function_count(&self) -> usize {
self.functions
.iter()
.filter(|f| {
f.borrow().subclass == SubclassKind::Function && f.borrow().operands.is_empty()
})
.count()
}
pub fn defined_function_count(&self) -> usize {
self.functions
.iter()
.filter(|f| {
f.borrow().subclass == SubclassKind::Function && !f.borrow().operands.is_empty()
})
.count()
}
pub fn has_debug_info(&self) -> bool {
self.named_metadata.contains_key("llvm.dbg.cu")
|| self.named_metadata.contains_key("llvm.dbg")
}
pub fn has_intrinsics(&self) -> bool {
self.functions
.iter()
.any(|f| f.borrow().name.starts_with("llvm."))
}
pub fn mark_as_linked(&mut self, source_name: &str) {
self.preserved_analyses
.push(format!("linked_from:{}", source_name));
}
pub fn has_declarations(&self) -> bool {
self.functions.iter().any(|f| {
f.borrow().subclass == SubclassKind::Function && f.borrow().operands.is_empty()
})
}
pub fn get_declaration_names(&self) -> Vec<String> {
self.functions
.iter()
.filter(|f| {
f.borrow().subclass == SubclassKind::Function && f.borrow().operands.is_empty()
})
.map(|f| f.borrow().name.clone())
.collect()
}
pub fn strip_debug_info(&mut self) {
self.named_metadata.remove("llvm.dbg.cu");
self.named_metadata.remove("llvm.dbg");
self.flags.retain(|f| !f.key.starts_with("Debug Info"));
}
pub fn strip_non_debug_metadata(&mut self) {
let keep_keys: Vec<String> = self
.named_metadata
.keys()
.filter(|k| k.starts_with("llvm.dbg"))
.cloned()
.collect();
let mut to_keep = HashMap::new();
for key in &keep_keys {
if let Some(v) = self.named_metadata.remove(key) {
to_keep.insert(key.clone(), v);
}
}
self.named_metadata = to_keep;
}
pub fn get_name(&self) -> &str {
&self.name
}
pub fn describe(&self) -> String {
format!(
"Module '{}': {} fns, {} globals, {} aliases, {} ifuncs",
self.name,
self.functions.len(),
self.globals.len(),
self.aliases.len(),
self.ifuncs.len()
)
}
pub fn get_type_table_size(&self) -> usize {
self.types.len()
}
pub fn get_named_type_count(&self) -> usize {
self.named_types.len()
}
pub fn get_named_type_names(&self) -> Vec<&String> {
self.named_types.keys().collect()
}
pub fn drop_all_types(&mut self) {
self.types.clear();
self.named_types.clear();
self.type_names.clear();
}
pub fn reserve_types(&mut self, additional: usize) {
self.types.reserve(additional);
}
pub fn reserve_functions(&mut self, additional: usize) {
self.functions.reserve(additional);
}
pub fn reserve_globals(&mut self, additional: usize) {
self.globals.reserve(additional);
}
pub fn shrink_to_fit(&mut self) {
self.functions.shrink_to_fit();
self.globals.shrink_to_fit();
self.aliases.shrink_to_fit();
self.ifuncs.shrink_to_fit();
self.types.shrink_to_fit();
self.type_names.shrink_to_fit();
self.flags.shrink_to_fit();
self.preserved_analyses.shrink_to_fit();
}
pub fn dump_header(&self) -> String {
let mut out = String::new();
out.push_str(&format!("; ModuleID = '{}'\n", self.name));
if !self.source_filename.is_empty() {
out.push_str(&format!("source_filename = \"{}\"\n", self.source_filename));
}
if let Some(ref triple) = self.target_triple {
out.push_str(&format!("target triple = \"{}\"\n", triple));
}
if let Some(ref dl) = self.data_layout {
out.push_str(&format!("target datalayout = \"{}\"\n", dl));
}
out
}
pub fn dump_metadata(&self) -> String {
let mut out = String::new();
for (key, nodes) in &self.named_metadata {
out.push_str(&format!("!{} = !{{", key));
let ids: Vec<String> = nodes.iter().map(|id| format!("!{}", id)).collect();
out.push_str(&ids.join(", "));
out.push_str("}\n");
}
if !self.flags.is_empty() {
out.push_str("!llvm.module.flags = !{");
for (i, flag) in self.flags.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
let val_str = match &flag.value {
MetadataValue::Int(v) => format!("i64 {}", v),
MetadataValue::String(s) => format!("!\"{}\"", s),
MetadataValue::Node(ids) => {
let ids_str: Vec<String> =
ids.iter().map(|id| format!("!{}", id)).collect();
format!("!{{{}}}", ids_str.join(", "))
}
MetadataValue::Null => "null".to_string(),
};
out.push_str(&format!(
"!{{{}, !\"{}\", {}}}",
flag.behavior, flag.key, val_str
));
}
out.push_str("}\n");
}
out
}
pub fn dump_summary(&self) -> String {
let mut out = String::new();
out.push_str(&format!("Module: {}\n", self.name));
out.push_str(&format!(" Source: {}\n", self.source_filename));
out.push_str(&format!(" Triple: {}\n", self.get_target_triple()));
out.push_str(&format!(
" DataLayout: {}\n",
self.data_layout.as_deref().unwrap_or("")
));
out.push_str(&format!(" Functions: {}\n", self.functions.len()));
out.push_str(&format!(" Globals: {}\n", self.globals.len()));
out.push_str(&format!(" Aliases: {}\n", self.aliases.len()));
out.push_str(&format!(" IFuncs: {}\n", self.ifuncs.len()));
out.push_str(&format!(" Types: {}\n", self.types.len()));
out.push_str(&format!(" NamedTypes: {}\n", self.named_types.len()));
out.push_str(&format!(" Comdats: {}\n", self.comdats.len()));
out.push_str(&format!(" ModuleFlags: {}\n", self.flags.len()));
out.push_str(&format!(" NamedMetadata: {}\n", self.named_metadata.len()));
out.push_str(&format!(" InlineAsm: {} bytes\n", self.inline_asm.len()));
out
}
pub fn dump_globals(&self) -> String {
let mut out = String::new();
for gv in &self.globals {
let name = gv.borrow().name.clone();
let ty = Type::pretty_print(&gv.borrow().ty);
out.push_str(&format!(
"@{} = global {} ...\n",
if name.is_empty() { "<unnamed>" } else { &name },
ty
));
}
out
}
pub fn dump_functions(&self) -> String {
let mut out = String::new();
for func in &self.functions {
let fname = func.borrow().name.clone();
let has_body = !func.borrow().operands.is_empty();
if has_body {
out.push_str(&format!("define ... @{}() {{ ... }}\n", fname));
} else {
out.push_str(&format!("declare ... @{}()\n", fname));
}
}
out
}
pub fn has_duplicate_global_names(&self) -> bool {
let all_names = self.collect_global_names();
let mut seen = std::collections::HashSet::new();
for name in &all_names {
if !seen.insert(name) {
return true;
}
}
false
}
pub fn validate_structure(&self) -> Vec<String> {
let mut errors = Vec::new();
if self.has_duplicate_global_names() {
errors.push("duplicate global names detected".to_string());
}
if self.name.is_empty() {
errors.push("module has no name".to_string());
}
errors
}
pub fn is_structurally_valid(&self) -> bool {
self.validate_structure().is_empty()
}
}
pub struct ModuleFunctionIter {
inner: std::vec::IntoIter<ValueRef>,
}
impl Iterator for ModuleFunctionIter {
type Item = ValueRef;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl ExactSizeIterator for ModuleFunctionIter {}
pub struct ModuleGlobalIter {
inner: std::vec::IntoIter<ValueRef>,
}
impl Iterator for ModuleGlobalIter {
type Item = ValueRef;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl ExactSizeIterator for ModuleGlobalIter {}
pub struct ModuleAliasIter {
inner: std::vec::IntoIter<ValueRef>,
}
impl Iterator for ModuleAliasIter {
type Item = ValueRef;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl ExactSizeIterator for ModuleAliasIter {}
pub struct ModuleIFuncIter {
inner: std::vec::IntoIter<ValueRef>,
}
impl Iterator for ModuleIFuncIter {
type Item = ValueRef;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl ExactSizeIterator for ModuleIFuncIter {}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::Type;
use crate::value::{valref, SubclassKind, Value};
fn make_func(name: &str) -> ValueRef {
valref(
Value::new(Type::function_type_with(
crate::types::TypeId::new(),
vec![],
false,
))
.named(name)
.with_subclass(SubclassKind::Function),
)
}
fn make_global(name: &str) -> ValueRef {
valref(
Value::new(Type::i32())
.named(name)
.with_subclass(SubclassKind::GlobalVariable),
)
}
#[test]
fn test_new_module() {
let m = Module::new("test");
assert_eq!(m.name, "test");
assert!(m.source_filename.is_empty());
assert!(m.target_triple.is_none());
assert!(m.functions.is_empty());
assert!(m.globals.is_empty());
}
#[test]
fn test_set_source_filename() {
let mut m = Module::new("test");
m.set_source_filename("test.ll");
assert_eq!(m.source_filename, "test.ll");
}
#[test]
fn test_set_target_triple() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
assert_eq!(m.get_target_triple(), "x86_64-unknown-linux-gnu");
}
#[test]
fn test_set_data_layout() {
let mut m = Module::new("test");
m.set_data_layout("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128");
assert_eq!(
m.get_data_layout(),
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
);
}
#[test]
fn test_add_function_dedup() {
let mut m = Module::new("test");
let f1 = make_func("foo");
let f2 = make_func("foo"); assert!(m.add_function(f1));
assert!(!m.add_function(f2)); assert_eq!(m.get_function_count(), 1);
}
#[test]
fn test_add_and_get_function() {
let mut m = Module::new("test");
let f = make_func("myfunc");
m.add_function(f);
let found = m.get_function("myfunc").unwrap();
assert_eq!(found.borrow().name, "myfunc");
}
#[test]
fn test_remove_function() {
let mut m = Module::new("test");
m.add_function(make_func("foo"));
m.add_function(make_func("bar"));
assert_eq!(m.get_function_count(), 2);
assert!(m.remove_function("foo"));
assert_eq!(m.get_function_count(), 1);
assert!(!m.remove_function("foo")); assert!(m.has_function("bar"));
assert!(!m.has_function("foo"));
}
#[test]
fn test_has_function() {
let mut m = Module::new("test");
assert!(!m.has_function("foo"));
m.add_function(make_func("foo"));
assert!(m.has_function("foo"));
}
#[test]
fn test_get_function_names() {
let mut m = Module::new("test");
m.add_function(make_func("alpha"));
m.add_function(make_func("beta"));
let names = m.get_function_names();
assert!(names.contains(&"alpha".to_string()));
assert!(names.contains(&"beta".to_string()));
}
#[test]
fn test_global_variable() {
let mut m = Module::new("test");
let gv = make_global("myglobal");
assert!(m.add_global_variable(gv));
let found = m.get_global_variable("myglobal").unwrap();
assert_eq!(found.borrow().name, "myglobal");
let gv2 = make_global("myglobal");
assert!(!m.add_global_variable(gv2));
assert!(m.remove_global_variable("myglobal"));
assert_eq!(m.get_global_count(), 0);
}
#[test]
fn test_aliases() {
let mut m = Module::new("test");
let alias = make_global("myalias"); m.add_alias(alias);
assert!(m.get_alias("myalias").is_some());
assert!(m.remove_alias("myalias"));
assert!(m.get_alias("myalias").is_none());
}
#[test]
fn test_ifuncs() {
let mut m = Module::new("test");
let ifunc = make_global("myifunc");
m.add_ifunc(ifunc);
assert!(m.get_ifunc("myifunc").is_some());
}
#[test]
fn test_type_table() {
let mut m = Module::new("test");
let idx = m.add_type(Type::i32());
assert_eq!(idx, 0);
assert_eq!(m.get_type_count(), 1);
assert!(m.get_type(0).is_some());
assert!(m.get_type(0).unwrap().is_integer());
let idx2 = m.add_type(Type::i32());
assert_eq!(idx2, 0); assert_eq!(m.get_type_count(), 1);
}
#[test]
fn test_named_types() {
let mut m = Module::new("test");
let sty = Type::struct_named_with(
"MyStruct".to_string(),
false,
vec![Type::i32().id, Type::i64().id],
);
m.add_named_type("MyStruct", sty.clone());
let found = m.get_named_type("MyStruct").unwrap();
assert_eq!(found.id, sty.id);
assert!(m.remove_named_type("MyStruct"));
assert!(m.get_named_type("MyStruct").is_none());
}
#[test]
fn test_named_metadata() {
let mut m = Module::new("test");
m.add_named_metadata("llvm.dbg.cu", vec![1, 2, 3]);
let nodes = m.get_named_metadata("llvm.dbg.cu").unwrap();
assert_eq!(nodes.len(), 3);
assert_eq!(nodes[0], 1);
assert!(m.has_named_metadata("llvm.dbg.cu"));
m.add_named_metadata("llvm.dbg.cu", vec![4]);
let nodes = m.get_named_metadata("llvm.dbg.cu").unwrap();
assert_eq!(nodes.len(), 4);
assert!(m.remove_named_metadata("llvm.dbg.cu"));
assert!(!m.has_named_metadata("llvm.dbg.cu"));
}
#[test]
fn test_comdat() {
let mut m = Module::new("test");
assert!(m.add_comdat("mycomdat", ComdatKind::Any));
assert!(!m.add_comdat("mycomdat", ComdatKind::Largest));
let c = m.get_comdat("mycomdat").unwrap();
assert_eq!(c.kind, ComdatKind::Any);
assert!(m.remove_comdat("mycomdat"));
assert!(m.get_comdat("mycomdat").is_none());
}
#[test]
fn test_comdat_kind_display() {
assert_eq!(ComdatKind::Any.to_string(), "any");
assert_eq!(ComdatKind::ExactMatch.to_string(), "exactmatch");
assert_eq!(ComdatKind::Largest.to_string(), "largest");
assert_eq!(ComdatKind::NoDeduplicate.to_string(), "nodeduplicate");
assert_eq!(ComdatKind::SameSize.to_string(), "samesize");
}
#[test]
fn test_attr_groups() {
let mut m = Module::new("test");
let attrs = AttributeList::new();
let id = m.next_attr_group_id();
assert_eq!(id, 0);
m.add_attr_group(id, attrs);
assert!(m.get_attr_group(0).is_some());
assert!(m.remove_attr_group(0));
assert!(m.get_attr_group(0).is_none());
}
#[test]
fn test_module_flags() {
let mut m = Module::new("test");
let flag = ModuleFlag {
behavior: 1,
key: "wchar_size".to_string(),
value: MetadataValue::Int(4),
};
m.add_flag(flag);
assert_eq!(m.get_flags().len(), 1);
let found = m.get_flag("wchar_size").unwrap();
assert_eq!(found.behavior, 1);
assert_eq!(found.value.as_int(), Some(4));
}
#[test]
fn test_module_flag_string_value() {
let mut m = Module::new("test");
let flag = ModuleFlag {
behavior: 2,
key: "ident".to_string(),
value: MetadataValue::String("clang version 16".to_string()),
};
m.add_flag(flag);
let found = m.get_flag("ident").unwrap();
assert_eq!(found.value.as_string(), Some("clang version 16"));
}
#[test]
fn test_remove_flag() {
let mut m = Module::new("test");
m.add_flag(ModuleFlag {
behavior: 1,
key: "a".to_string(),
value: MetadataValue::Int(1),
});
m.add_flag(ModuleFlag {
behavior: 1,
key: "b".to_string(),
value: MetadataValue::Int(2),
});
let removed = m.remove_flag("a");
assert_eq!(removed, 1);
assert_eq!(m.get_flags().len(), 1);
assert!(m.get_flag("a").is_none());
assert!(m.get_flag("b").is_some());
}
#[test]
fn test_is_valid() {
let m = Module::new("valid_module");
assert!(m.is_valid());
let m2 = Module::new("");
assert!(!m2.is_valid());
}
#[test]
fn test_clone_empty() {
let mut m = Module::new("original");
m.set_target_triple("x86_64-linux");
m.set_data_layout("e-m:e-i64:64");
m.add_function(make_func("foo"));
m.add_global_variable(make_global("gv"));
let ty = Type::float();
m.add_type(ty);
m.add_comdat("c", ComdatKind::Any);
let cloned = m.clone_empty();
assert_eq!(cloned.name, "original");
assert_eq!(cloned.get_target_triple(), "x86_64-linux");
assert!(cloned.functions.is_empty());
assert!(cloned.globals.is_empty());
assert_eq!(cloned.get_type_count(), 1);
assert!(cloned.get_comdat("c").is_some());
}
#[test]
fn test_dump() {
let mut m = Module::new("dump_test");
m.set_source_filename("dump_test.ll");
m.set_target_triple("x86_64-linux");
m.add_function(make_func("main"));
m.add_global_variable(make_global("counter"));
m.add_named_metadata("llvm.ident", vec![0]);
let out = m.dump();
assert!(out.contains("ModuleID = 'dump_test'"));
assert!(out.contains("source_filename = \"dump_test.ll\""));
assert!(out.contains("target triple = \"x86_64-linux\""));
assert!(out.contains("@main"));
assert!(out.contains("@counter"));
assert!(out.contains("!llvm.ident"));
}
#[test]
fn test_next_metadata_id() {
let mut m = Module::new("test");
assert_eq!(m.next_metadata_id(), 0);
assert_eq!(m.next_metadata_id(), 1);
assert_eq!(m.next_metadata_id(), 2);
}
#[test]
fn test_comdat_kind_from_str() {
assert_eq!(ComdatKind::from_str("any"), Some(ComdatKind::Any));
assert_eq!(
ComdatKind::from_str("exactmatch"),
Some(ComdatKind::ExactMatch)
);
assert_eq!(ComdatKind::from_str("bogus"), None);
}
#[test]
fn test_metadata_value_as_int_none_for_string() {
let mv = MetadataValue::String("hello".to_string());
assert_eq!(mv.as_int(), None);
assert_eq!(mv.as_string(), Some("hello"));
}
#[test]
fn test_imported_functions() {
let mut m = Module::new("test");
m.add_imported_function("printf");
m.add_imported_function("malloc");
assert!(m.is_imported_function("printf"));
assert!(!m.is_imported_function("free"));
m.add_imported_function("printf");
assert_eq!(m.imported_functions.len(), 2);
}
#[test]
fn test_get_total_global_count() {
let mut m = Module::new("test");
assert_eq!(m.get_total_global_count(), 0);
m.add_function(make_func("f"));
m.add_global_variable(make_global("g"));
m.add_alias(make_global("a"));
m.add_ifunc(make_global("i"));
assert_eq!(m.get_total_global_count(), 4);
}
}