use crate::opcode::Opcode;
use crate::types::Type;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::{Rc, Weak};
pub type ValueRef = Rc<RefCell<Value>>;
pub type WeakValueRef = Weak<RefCell<Value>>;
#[derive(Debug, Clone)]
pub struct Use {
pub user: WeakValueRef,
pub operand_no: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SubclassKind {
Value = 0,
User = 1,
Instruction = 2,
Constant = 3,
BasicBlock = 4,
Function = 5,
Argument = 6,
GlobalVariable = 7,
GlobalAlias = 8,
GlobalIFunc = 9,
ConstantInt = 10,
ConstantFP = 11,
ConstantAggregate = 12,
ConstantData = 13,
BinaryOp = 14,
CastOp = 15,
CmpOp = 16,
PHINode = 17,
CallInst = 18,
LoadInst = 19,
StoreInst = 20,
AllocaInst = 21,
GEPOperator = 22,
ExtractValue = 23,
InsertValue = 24,
InlineAsm = 25,
MetadataAsValue = 26,
BlockAddress = 27,
}
impl SubclassKind {
pub fn as_str(&self) -> &'static str {
match self {
SubclassKind::Value => "Value",
SubclassKind::User => "User",
SubclassKind::Instruction => "Instruction",
SubclassKind::Constant => "Constant",
SubclassKind::BasicBlock => "BasicBlock",
SubclassKind::Function => "Function",
SubclassKind::Argument => "Argument",
SubclassKind::GlobalVariable => "GlobalVariable",
SubclassKind::GlobalAlias => "GlobalAlias",
SubclassKind::GlobalIFunc => "GlobalIFunc",
SubclassKind::ConstantInt => "ConstantInt",
SubclassKind::ConstantFP => "ConstantFP",
SubclassKind::ConstantAggregate => "ConstantAggregate",
SubclassKind::ConstantData => "ConstantData",
SubclassKind::BinaryOp => "BinaryOp",
SubclassKind::CastOp => "CastOp",
SubclassKind::CmpOp => "CmpOp",
SubclassKind::PHINode => "PHINode",
SubclassKind::CallInst => "CallInst",
SubclassKind::LoadInst => "LoadInst",
SubclassKind::StoreInst => "StoreInst",
SubclassKind::AllocaInst => "AllocaInst",
SubclassKind::GEPOperator => "GEPOperator",
SubclassKind::ExtractValue => "ExtractValue",
SubclassKind::InsertValue => "InsertValue",
SubclassKind::InlineAsm => "InlineAsm",
SubclassKind::MetadataAsValue => "MetadataAsValue",
SubclassKind::BlockAddress => "BlockAddress",
}
}
pub fn is_constant(&self) -> bool {
matches!(
self,
SubclassKind::Constant
| SubclassKind::ConstantInt
| SubclassKind::ConstantFP
| SubclassKind::ConstantAggregate
| SubclassKind::ConstantData
)
}
pub fn is_instruction(&self) -> bool {
matches!(
self,
SubclassKind::Instruction
| SubclassKind::BinaryOp
| SubclassKind::CastOp
| SubclassKind::CmpOp
| SubclassKind::PHINode
| SubclassKind::CallInst
| SubclassKind::LoadInst
| SubclassKind::StoreInst
| SubclassKind::AllocaInst
| SubclassKind::GEPOperator
| SubclassKind::ExtractValue
| SubclassKind::InsertValue
)
}
}
impl std::fmt::Display for SubclassKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone)]
pub struct Value {
pub name: String,
pub ty: Type,
pub vid: u64,
pub subclass: SubclassKind,
pub uses: Vec<Use>,
pub opcode: Option<Opcode>,
pub num_operands: usize,
pub operands: Vec<ValueRef>,
pub parent: Option<ValueRef>,
pub return_type: Option<Type>,
pub successors: Vec<ValueRef>,
pub subclass_data: u32,
pub subclass_extra: Vec<u64>,
pub metadata: HashMap<u32, u32>,
pub is_used_by_md: bool,
pub blocks: Vec<ValueRef>,
pub params: Vec<Type>,
pub instructions: Vec<ValueRef>,
pub initializer: Option<ValueRef>,
pub is_constant: bool,
pub is_vararg: bool,
pub result: Option<String>,
pub is_internal: bool,
}
use std::sync::atomic::{AtomicU64, Ordering};
static NEXT_VID: AtomicU64 = AtomicU64::new(0);
impl Default for Value {
fn default() -> Self {
Value::new(Type::void())
}
}
impl Value {
pub fn new(ty: Type) -> Self {
Self {
name: String::new(),
ty,
vid: NEXT_VID.fetch_add(1, Ordering::SeqCst),
subclass: SubclassKind::Value,
uses: Vec::new(),
opcode: None,
num_operands: 0,
operands: Vec::new(),
parent: None,
return_type: None,
successors: Vec::new(),
subclass_data: 0,
subclass_extra: Vec::new(),
metadata: HashMap::new(),
is_used_by_md: false,
blocks: Vec::new(),
params: Vec::new(),
instructions: Vec::new(),
initializer: None,
is_constant: false,
is_vararg: false,
result: None,
is_internal: false,
}
}
pub fn named(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
self
}
pub fn with_subclass(mut self, kind: SubclassKind) -> Self {
self.subclass = kind;
self
}
pub fn get_opcode(&self) -> Option<Opcode> {
self.opcode
}
pub fn set_opcode(&mut self, op: Opcode) {
self.opcode = Some(op);
}
pub fn add_use(&mut self, user: WeakValueRef, operand_no: usize) {
let existing = self
.uses
.iter()
.any(|u| match (u.user.upgrade(), user.upgrade()) {
(Some(a), Some(b)) => Rc::ptr_eq(&a, &b) && u.operand_no == operand_no,
_ => false,
});
if !existing {
self.uses.push(Use { user, operand_no });
}
}
pub fn remove_use(&mut self, user: &ValueRef) -> bool {
let before = self.uses.len();
self.uses.retain(|u| match u.user.upgrade() {
Some(rc) => !Rc::ptr_eq(&rc, user),
_ => false,
});
self.uses.len() < before
}
pub fn num_uses(&self) -> usize {
self.uses.len()
}
pub fn use_empty(&self) -> bool {
self.uses.is_empty()
}
pub fn has_one_use(&self) -> bool {
self.uses.len() == 1
}
pub fn has_n_uses(&self, n: usize) -> bool {
self.uses.len() == n
}
pub fn get_num_uses(&self) -> usize {
self.num_uses()
}
pub fn replace_all_uses_with(&mut self, new_val: &ValueRef) {
for u in &self.uses {
if let Some(user_rc) = u.user.upgrade() {
let mut user = user_rc.borrow_mut();
if u.operand_no < user.operands.len() {
user.operands[u.operand_no] = Rc::clone(new_val);
new_val
.borrow_mut()
.add_use(Rc::downgrade(&user_rc), u.operand_no);
}
}
}
self.uses.clear();
}
pub fn get_uses(&self) -> Vec<ValueRef> {
self.uses.iter().filter_map(|u| u.user.upgrade()).collect()
}
pub fn get_unique_use(&self) -> Option<ValueRef> {
if self.uses.len() == 1 {
self.uses[0].user.upgrade()
} else {
None
}
}
pub fn is_used_outside_of_block(&self, bb: &ValueRef) -> bool {
for u in &self.uses {
if let Some(user_rc) = u.user.upgrade() {
let user = user_rc.borrow();
if let Some(ref user_parent) = user.parent {
if !Rc::ptr_eq(user_parent, bb) {
return true;
}
} else {
return true;
}
}
}
false
}
pub fn is_used_in_basic_block(&self, bb: &ValueRef) -> bool {
for u in &self.uses {
if let Some(user_rc) = u.user.upgrade() {
let user = user_rc.borrow();
if let Some(ref user_parent) = user.parent {
if Rc::ptr_eq(user_parent, bb) {
return true;
}
}
}
}
false
}
pub fn push_operand(&mut self, val: ValueRef) {
let idx = self.operands.len();
val.borrow_mut().add_use(
Rc::downgrade(&Rc::new(RefCell::new(Value::new(Type::void())))),
idx,
);
self.operands.push(val);
self.num_operands = self.operands.len();
}
pub fn set_operand(&mut self, index: usize, val: ValueRef) {
if index < self.operands.len() {
self.operands[index] = val;
}
}
pub fn operand(&self, index: usize) -> Option<ValueRef> {
self.operands.get(index).cloned()
}
pub fn get_num_operands(&self) -> usize {
self.num_operands
}
pub fn get_all_operands(&self) -> Vec<ValueRef> {
self.operands.clone()
}
pub fn isa(&self, kind: SubclassKind) -> bool {
self.subclass == kind
}
pub fn is_instruction(&self) -> bool {
self.subclass == SubclassKind::Instruction || self.subclass.is_instruction()
}
pub fn is_basic_block(&self) -> bool {
self.subclass == SubclassKind::BasicBlock
}
pub fn is_function(&self) -> bool {
self.subclass == SubclassKind::Function
}
pub fn is_constant(&self) -> bool {
self.subclass == SubclassKind::Constant || self.subclass.is_constant()
}
pub fn is_argument(&self) -> bool {
self.subclass == SubclassKind::Argument
}
pub fn is_global_variable(&self) -> bool {
self.subclass == SubclassKind::GlobalVariable
}
pub fn get_subclass_kind(&self) -> SubclassKind {
self.subclass
}
pub fn mutate_type(&mut self, new_type: Type) {
self.ty = new_type;
}
pub fn add_metadata(&mut self, kind: u32, md_id: u32) {
self.metadata.insert(kind, md_id);
}
pub fn get_metadata(&self, kind: u32) -> Option<u32> {
self.metadata.get(&kind).copied()
}
pub fn has_metadata(&self) -> bool {
!self.metadata.is_empty()
}
pub fn has_metadata_kind(&self, kind: u32) -> bool {
self.metadata.contains_key(&kind)
}
pub fn clear_metadata(&mut self) {
self.metadata.clear();
}
pub fn erase_metadata(&mut self, kind: u32) -> bool {
self.metadata.remove(&kind).is_some()
}
pub fn get_all_metadata(&self) -> Vec<(u32, u32)> {
self.metadata.iter().map(|(k, v)| (*k, *v)).collect()
}
pub fn set_used_by_md(&mut self, flag: bool) {
self.is_used_by_md = flag;
}
pub fn set_subclass_data(&mut self, data: u32) {
self.subclass_data = data;
}
pub fn get_subclass_data(&self) -> u32 {
self.subclass_data
}
pub fn is_terminator(&self) -> bool {
self.opcode.map_or(false, |op| op.is_terminator())
}
pub fn has_name(&self) -> bool {
!self.name.is_empty()
}
pub fn get_name_or(&self, default: &str) -> String {
if self.name.is_empty() {
default.to_string()
} else {
self.name.clone()
}
}
}
pub fn valref(v: Value) -> ValueRef {
Rc::new(RefCell::new(v))
}
#[derive(Debug, Clone, Default)]
pub struct ValueSymbolTable {
entries: std::collections::HashMap<String, ValueRef>,
next_id: u64,
}
impl ValueSymbolTable {
pub fn new() -> Self {
Self {
entries: std::collections::HashMap::new(),
next_id: 0,
}
}
pub fn lookup(&self, name: &str) -> Option<ValueRef> {
self.entries.get(name).cloned()
}
pub fn insert(&mut self, name: &str, val: ValueRef) -> String {
let key = if self.entries.contains_key(name) {
let new_name = format!("{}.{}", name, self.next_id);
self.next_id += 1;
val.borrow_mut().name = new_name.clone();
new_name
} else {
name.to_string()
};
self.entries.insert(key.clone(), val);
key
}
pub fn remove(&mut self, name: &str) -> Option<ValueRef> {
self.entries.remove(name)
}
pub fn contains(&self, name: &str) -> bool {
self.entries.contains_key(name)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &ValueRef)> {
self.entries.iter()
}
pub fn clear(&mut self) {
self.entries.clear();
self.next_id = 0;
}
}
#[derive(Debug, Clone, Default)]
pub struct ValueMap {
entries: std::collections::HashMap<*const Value, ValueRef>,
}
impl ValueMap {
pub fn new() -> Self {
Self {
entries: std::collections::HashMap::new(),
}
}
pub fn insert(&mut self, from: &ValueRef, to: ValueRef) {
let key = Rc::as_ptr(from) as *const Value;
self.entries.insert(key, to);
}
pub fn lookup(&self, from: &ValueRef) -> Option<ValueRef> {
let key = Rc::as_ptr(from) as *const Value;
self.entries.get(&key).cloned()
}
pub fn contains(&self, from: &ValueRef) -> bool {
let key = Rc::as_ptr(from) as *const Value;
self.entries.contains_key(&key)
}
pub fn remove(&mut self, from: &ValueRef) -> Option<ValueRef> {
let key = Rc::as_ptr(from) as *const Value;
self.entries.remove(&key)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn clear(&mut self) {
self.entries.clear();
}
pub fn map_value(&self, val: &ValueRef) -> ValueRef {
self.lookup(val).unwrap_or_else(|| val.clone())
}
}
pub fn strip_pointer_casts(val: &ValueRef) -> ValueRef {
let mut current = val.clone();
loop {
let next = {
let v = current.borrow();
if let Some(opcode) = &v.opcode {
match opcode {
crate::opcode::Opcode::BitCast
| crate::opcode::Opcode::IntToPtr
| crate::opcode::Opcode::PtrToInt => v.operands.first().cloned(),
_ => None,
}
} else {
None
}
};
match next {
Some(n) => current = n,
None => break,
}
}
current
}
pub fn is_constant(val: &ValueRef) -> bool {
let v = val.borrow();
matches!(
v.subclass,
SubclassKind::Constant | SubclassKind::GlobalVariable
)
}
pub fn is_instruction(val: &ValueRef) -> bool {
val.borrow().subclass == SubclassKind::Instruction
}
pub fn is_argument(val: &ValueRef) -> bool {
val.borrow().subclass == SubclassKind::Argument
}
pub fn is_basic_block(val: &ValueRef) -> bool {
val.borrow().subclass == SubclassKind::BasicBlock
}
pub fn is_function(val: &ValueRef) -> bool {
val.borrow().subclass == SubclassKind::Function
}
#[cfg(test)]
mod tests {
use super::*;
fn make_value(name: &str, ty: Type) -> ValueRef {
valref(Value::new(ty).named(name))
}
fn make_user(name: &str) -> (ValueRef, ValueRef) {
let self_rc = valref(
Value::new(Type::void())
.named(name)
.with_subclass(SubclassKind::User),
);
(self_rc.clone(), self_rc)
}
#[test]
fn test_value_creation() {
let v = Value::new(Type::i32()).named("x");
assert_eq!(v.name, "x");
assert!(v.ty.is_integer());
assert_eq!(v.subclass, SubclassKind::Value);
assert_eq!(v.subclass_data, 0);
}
#[test]
fn test_valref() {
let v = valref(Value::new(Type::float()).named("f"));
assert_eq!(v.borrow().name, "f");
}
#[test]
fn test_subclass_kind() {
let v = Value::new(Type::void()).with_subclass(SubclassKind::GlobalVariable);
assert_eq!(v.get_subclass_kind(), SubclassKind::GlobalVariable);
assert!(v.is_global_variable());
assert!(!v.is_instruction());
}
#[test]
fn test_subclass_kind_display() {
assert_eq!(SubclassKind::Function.to_string(), "Function");
assert_eq!(SubclassKind::PHINode.to_string(), "PHINode");
assert_eq!(SubclassKind::BlockAddress.to_string(), "BlockAddress");
}
#[test]
fn test_subclass_kind_is_constant() {
assert!(SubclassKind::Constant.is_constant());
assert!(SubclassKind::ConstantInt.is_constant());
assert!(SubclassKind::ConstantFP.is_constant());
assert!(!SubclassKind::Instruction.is_constant());
assert!(!SubclassKind::Function.is_constant());
}
#[test]
fn test_subclass_kind_is_instruction() {
assert!(SubclassKind::Instruction.is_instruction());
assert!(SubclassKind::BinaryOp.is_instruction());
assert!(SubclassKind::PHINode.is_instruction());
assert!(!SubclassKind::Constant.is_instruction());
assert!(!SubclassKind::Function.is_instruction());
}
#[test]
fn test_add_use_and_num_uses() {
let val = make_value("used", Type::i32());
let user = make_value("user", Type::void());
{
val.borrow_mut().add_use(Rc::downgrade(&user), 0);
}
assert_eq!(val.borrow().num_uses(), 1);
assert!(!val.borrow().use_empty());
}
#[test]
fn test_has_one_use() {
let val = make_value("used", Type::i32());
let user1 = make_value("user1", Type::void());
{
val.borrow_mut().add_use(Rc::downgrade(&user1), 0);
}
assert!(val.borrow().has_one_use());
assert!(val.borrow().has_n_uses(1));
assert!(!val.borrow().has_n_uses(2));
}
#[test]
fn test_remove_use() {
let val = make_value("used", Type::i32());
let user1 = make_value("user1", Type::void());
let user2 = make_value("user2", Type::void());
{
let mut v = val.borrow_mut();
v.add_use(Rc::downgrade(&user1), 0);
v.add_use(Rc::downgrade(&user2), 1);
}
assert_eq!(val.borrow().num_uses(), 2);
{
let mut v = val.borrow_mut();
assert!(v.remove_use(&user1));
}
assert_eq!(val.borrow().num_uses(), 1);
}
#[test]
fn test_get_uses() {
let val = make_value("used", Type::i32());
let user = make_value("user", Type::void());
{
val.borrow_mut().add_use(Rc::downgrade(&user), 0);
}
let users = val.borrow().get_uses();
assert_eq!(users.len(), 1);
assert!(Rc::ptr_eq(&users[0], &user));
}
#[test]
fn test_get_unique_use() {
let val = make_value("used", Type::i32());
let user = make_value("user", Type::void());
{
val.borrow_mut().add_use(Rc::downgrade(&user), 0);
}
let unique = val.borrow().get_unique_use().unwrap();
assert!(Rc::ptr_eq(&unique, &user));
let user2 = make_value("user2", Type::void());
{
val.borrow_mut().add_use(Rc::downgrade(&user2), 0);
}
assert!(val.borrow().get_unique_use().is_none());
}
#[test]
fn test_replace_all_uses_with() {
let val = make_value("old", Type::i32());
let new_val = make_value("new", Type::i32());
let user = make_value("user", Type::void());
{
let mut u = user.borrow_mut();
u.operands.push(Rc::clone(&val));
u.num_operands = 1;
}
{
val.borrow_mut().add_use(Rc::downgrade(&user), 0);
}
{
val.borrow_mut().replace_all_uses_with(&new_val);
}
assert!(val.borrow().use_empty());
let u = user.borrow();
assert!(Rc::ptr_eq(&u.operands[0], &new_val));
assert_eq!(new_val.borrow().num_uses(), 1);
}
#[test]
fn test_mutate_type() {
let mut v = Value::new(Type::i32());
assert!(v.ty.is_integer());
v.mutate_type(Type::i64());
assert_eq!(v.ty.integer_bit_width(), 64);
}
#[test]
fn test_metadata() {
let mut v = Value::new(Type::i32());
assert!(!v.has_metadata());
v.add_metadata(1, 42); assert!(v.has_metadata());
assert!(v.has_metadata_kind(1));
assert!(!v.has_metadata_kind(2));
assert_eq!(v.get_metadata(1), Some(42));
assert_eq!(v.get_metadata(2), None);
let all = v.get_all_metadata();
assert_eq!(all.len(), 1);
assert_eq!(all[0], (1, 42));
assert!(v.erase_metadata(1));
assert!(!v.has_metadata());
assert!(!v.erase_metadata(1)); }
#[test]
fn test_clear_metadata() {
let mut v = Value::new(Type::i32());
v.add_metadata(1, 10);
v.add_metadata(2, 20);
assert_eq!(v.get_all_metadata().len(), 2);
v.clear_metadata();
assert!(!v.has_metadata());
}
#[test]
fn test_subclass_data() {
let mut v = Value::new(Type::i32());
assert_eq!(v.get_subclass_data(), 0);
v.set_subclass_data(0xDEAD);
assert_eq!(v.get_subclass_data(), 0xDEAD);
}
#[test]
fn test_is_used_by_md() {
let mut v = Value::new(Type::i32());
assert!(!v.is_used_by_md);
v.set_used_by_md(true);
assert!(v.is_used_by_md);
v.set_used_by_md(false);
assert!(!v.is_used_by_md);
}
#[test]
fn test_is_used_outside_of_block() {
let val = make_value("v", Type::i32());
let bb1 = make_value("bb1", Type::label());
let bb2 = make_value("bb2", Type::label());
let user_in_bb1 = make_value("u1", Type::void());
user_in_bb1.borrow_mut().parent = Some(Rc::clone(&bb1));
let user_in_bb2 = make_value("u2", Type::void());
user_in_bb2.borrow_mut().parent = Some(Rc::clone(&bb2));
{
let mut v = val.borrow_mut();
v.add_use(Rc::downgrade(&user_in_bb1), 0);
v.add_use(Rc::downgrade(&user_in_bb2), 1);
}
assert!(val.borrow().is_used_outside_of_block(&bb1));
assert!(val.borrow().is_used_in_basic_block(&bb1));
assert!(val.borrow().is_used_in_basic_block(&bb2));
}
#[test]
fn test_is_terminator() {
let mut v = Value::new(Type::void());
assert!(!v.is_terminator());
v.set_opcode(Opcode::Ret);
assert!(v.is_terminator());
v.set_opcode(Opcode::Add);
assert!(!v.is_terminator());
}
#[test]
fn test_has_name() {
let v = Value::new(Type::i32());
assert!(!v.has_name());
let v = Value::new(Type::i32()).named("x");
assert!(v.has_name());
}
#[test]
fn test_get_name_or() {
let v = Value::new(Type::i32()).named("real");
assert_eq!(v.get_name_or("default"), "real");
let v = Value::new(Type::i32());
assert_eq!(v.get_name_or("default"), "default");
}
#[test]
fn test_get_num_operands() {
let mut v = Value::new(Type::void());
assert_eq!(v.get_num_operands(), 0);
let op = make_value("op", Type::i32());
v.push_operand(op);
assert_eq!(v.get_num_operands(), 1);
}
#[test]
fn test_get_all_operands() {
let mut v = Value::new(Type::void());
let op1 = make_value("op1", Type::i32());
let op2 = make_value("op2", Type::i32());
v.push_operand(op1.clone());
v.push_operand(op2.clone());
let ops = v.get_all_operands();
assert_eq!(ops.len(), 2);
assert!(Rc::ptr_eq(&ops[0], &op1));
assert!(Rc::ptr_eq(&ops[1], &op2));
}
#[test]
fn test_add_use_dedup() {
let val = make_value("v", Type::i32());
let user = make_value("u", Type::void());
{
let mut v = val.borrow_mut();
v.add_use(Rc::downgrade(&user), 0);
v.add_use(Rc::downgrade(&user), 0); }
assert_eq!(val.borrow().num_uses(), 1);
}
#[test]
fn test_empty_use_list_queries() {
let val = make_value("v", Type::i32());
assert!(val.borrow().get_uses().is_empty());
assert!(val.borrow().get_unique_use().is_none());
assert_eq!(val.borrow().get_num_uses(), 0);
assert!(val.borrow().use_empty());
}
}