use llvm_native_core::opcode::{FCmpPred, ICmpPred, Opcode};
use llvm_native_core::types::{Type, TypeKind};
use llvm_native_core::value::{SubclassKind, ValueRef};
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Debug, Clone, PartialEq)]
pub enum InterpValue {
Int(i64, u32),
Float(f64),
Float32(f32),
Pointer(usize),
Aggregate(Vec<InterpValue>),
Undef,
Poison,
}
impl InterpValue {
pub fn int(val: i64, bit_width: u32) -> Self {
if bit_width < 64 && bit_width > 0 {
let shift = 64 - bit_width;
let sext = (val << shift) >> shift;
InterpValue::Int(sext, bit_width)
} else {
InterpValue::Int(val, bit_width)
}
}
pub fn as_int(&self) -> i64 {
match self {
InterpValue::Int(v, _) => *v,
other => panic!("Expected Int, got {:?}", other),
}
}
pub fn as_pointer(&self) -> usize {
match self {
InterpValue::Pointer(addr) => *addr,
other => panic!("Expected Pointer, got {:?}", other),
}
}
pub fn as_float(&self) -> f64 {
match self {
InterpValue::Float(v) => *v,
other => panic!("Expected Float, got {:?}", other),
}
}
pub fn is_undef_or_poison(&self) -> bool {
matches!(self, InterpValue::Undef | InterpValue::Poison)
}
}
#[derive(Debug, Clone)]
pub struct StackFrame {
pub func_name: String,
pub return_bb: Option<usize>,
pub return_pc: usize,
pub saved_locals: HashMap<usize, InterpValue>,
}
impl StackFrame {
pub fn new(func_name: &str, return_bb: Option<usize>, return_pc: usize) -> Self {
Self {
func_name: func_name.to_string(),
return_bb,
return_pc,
saved_locals: HashMap::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct ExecutionState {
pub memory: Vec<u8>,
pub locals: HashMap<usize, InterpValue>,
pub globals: HashMap<String, InterpValue>,
pub current_block: Option<usize>,
pub pc: usize,
pub call_stack: Vec<StackFrame>,
pub is_terminated: bool,
pub phi_incoming: HashMap<usize, InterpValue>,
}
impl ExecutionState {
pub fn new() -> Self {
Self {
memory: vec![0u8; 1024 * 1024],
locals: HashMap::new(),
globals: HashMap::new(),
current_block: None,
pc: 0,
call_stack: Vec::new(),
is_terminated: false,
phi_incoming: HashMap::new(),
}
}
pub fn allocate(&mut self, size: usize) -> usize {
let addr = self.memory.len();
self.memory.resize(addr + size, 0);
addr
}
}
pub struct IRInterpreter {
pub state: ExecutionState,
pub max_steps: usize,
pub trace: bool,
step_count: usize,
}
impl IRInterpreter {
pub fn new() -> Self {
Self {
state: ExecutionState::new(),
max_steps: 1_000_000,
trace: false,
step_count: 0,
}
}
pub fn set_max_steps(&mut self, max: usize) {
self.max_steps = max;
}
pub fn set_trace(&mut self, trace: bool) {
self.trace = trace;
}
pub fn execute_function(
&mut self,
func: &ValueRef,
args: &[InterpValue],
) -> Result<InterpValue, String> {
let f = func.borrow();
if f.subclass != SubclassKind::Function {
return Err(format!("Value '{}' is not a function", f.name));
}
self.state = ExecutionState::new();
self.step_count = 0;
let entry_bb = match f.operands.first() {
Some(bb) if bb.borrow().subclass == SubclassKind::BasicBlock => Rc::clone(bb),
_ => {
return Err(format!("Function '{}' has no entry basic block", f.name));
}
};
for (i, arg) in args.iter().enumerate() {
self.state
.locals
.insert(func_vid_with_offset(func, i as i64), arg.clone());
}
self.state.current_block = Some(entry_bb.borrow().vid as usize);
self.state.pc = 0;
loop {
if self.state.is_terminated {
break;
}
if self.step_count >= self.max_steps {
return Err(format!("Exceeded maximum step count ({})", self.max_steps));
}
let block_vid = match self.state.current_block {
Some(vid) => vid,
None => return Err("No current basic block".to_string()),
};
let bb = match self.find_block_by_vid(&f.operands, block_vid) {
Some(b) => b,
None => {
return Err(format!("Basic block with vid {} not found", block_vid));
}
};
let block = bb.borrow();
if self.state.pc >= block.operands.len() {
if !self.state.is_terminated {
return Err(format!("Block '{}' has no terminator", block.name));
}
break;
}
let inst = Rc::clone(&block.operands[self.state.pc]);
if self.trace {
let i = inst.borrow();
eprintln!(
"TRACE: step={} block={} pc={} inst={}",
self.step_count, block.name, self.state.pc, i.name
);
}
self.execute_instruction(&inst)?;
self.step_count = self.step_count.saturating_add(1);
self.state.pc = self.state.pc.wrapping_add(1);
}
Ok(self
.state
.locals
.values()
.last()
.cloned()
.unwrap_or(InterpValue::Undef))
}
pub fn execute_block(&mut self, bb: &ValueRef) -> Result<(), String> {
let block = bb.borrow();
self.state.current_block = Some(block.vid as usize);
self.state.pc = 0;
while self.state.pc < block.operands.len() && !self.state.is_terminated {
let inst = Rc::clone(&block.operands[self.state.pc]);
self.execute_instruction(&inst)?;
self.state.pc += 1;
}
Ok(())
}
pub fn execute_instruction(&mut self, inst: &ValueRef) -> Result<(), String> {
let i = inst.borrow();
let opcode = match i.get_opcode() {
Some(op) => op,
None => return Ok(()),
};
let result = match opcode {
Opcode::Ret => {
self.execute_ret(inst)?;
return Ok(());
}
Opcode::Br => {
self.execute_br(inst)?;
return Ok(());
}
Opcode::Switch => {
self.execute_switch(inst)?;
return Ok(());
}
Opcode::Unreachable => {
self.state.is_terminated = true;
return Ok(());
}
Opcode::Add => self.execute_add(inst)?,
Opcode::FAdd => self.execute_fadd(inst)?,
Opcode::Sub => self.execute_sub(inst)?,
Opcode::FSub => self.execute_fsub(inst)?,
Opcode::Mul => self.execute_mul(inst)?,
Opcode::FMul => self.execute_fmul(inst)?,
Opcode::SDiv => self.execute_sdiv(inst)?,
Opcode::UDiv => self.execute_udiv(inst)?,
Opcode::FDiv => self.execute_fdiv(inst)?,
Opcode::SRem => self.execute_srem(inst)?,
Opcode::URem => self.execute_urem(inst)?,
Opcode::And => self.execute_and(inst)?,
Opcode::Or => self.execute_or(inst)?,
Opcode::Xor => self.execute_xor(inst)?,
Opcode::Shl => self.execute_shl(inst)?,
Opcode::LShr => self.execute_lshr(inst)?,
Opcode::AShr => self.execute_ashr(inst)?,
Opcode::Alloca => self.execute_alloca(inst)?,
Opcode::Load => self.execute_load(inst)?,
Opcode::Store => {
self.execute_store(inst)?;
return Ok(());
}
Opcode::GetElementPtr => self.execute_gep(inst)?,
Opcode::ICmp => self.execute_icmp(inst)?,
Opcode::FCmp => self.execute_fcmp(inst)?,
Opcode::Trunc => self.execute_trunc(inst)?,
Opcode::ZExt => self.execute_zext(inst)?,
Opcode::SExt => self.execute_sext(inst)?,
Opcode::BitCast => self.execute_bitcast(inst)?,
Opcode::Call => self.execute_call(inst)?,
Opcode::Phi => self.execute_phi(inst)?,
Opcode::Select => self.execute_select(inst)?,
_ => {
return Err(format!(
"Unsupported opcode {:?} in instruction '{}'",
opcode, i.name
));
}
};
self.set_result(inst, result);
Ok(())
}
fn execute_add(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Int(va, w), InterpValue::Int(vb, _)) => {
Ok(InterpValue::int(va.wrapping_add(vb), w))
}
_ => Err("Add: expected integer operands".to_string()),
}
}
fn execute_sub(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Int(va, w), InterpValue::Int(vb, _)) => {
Ok(InterpValue::int(va.wrapping_sub(vb), w))
}
_ => Err("Sub: expected integer operands".to_string()),
}
}
fn execute_mul(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Int(va, w), InterpValue::Int(vb, _)) => {
Ok(InterpValue::int(va.wrapping_mul(vb), w))
}
_ => Err("Mul: expected integer operands".to_string()),
}
}
fn execute_sdiv(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Int(va, w), InterpValue::Int(vb, _)) => {
if vb == 0 {
return Ok(InterpValue::Poison);
}
Ok(InterpValue::int(va.wrapping_div(vb), w))
}
_ => Err("SDiv: expected integer operands".to_string()),
}
}
fn execute_udiv(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Int(va, w), InterpValue::Int(vb, _)) => {
if vb == 0 {
return Ok(InterpValue::Poison);
}
let ua = va as u64;
let ub = vb as u64;
Ok(InterpValue::int(ua.wrapping_div(ub) as i64, w))
}
_ => Err("UDiv: expected integer operands".to_string()),
}
}
fn execute_srem(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Int(va, w), InterpValue::Int(vb, _)) => {
if vb == 0 {
return Ok(InterpValue::Poison);
}
Ok(InterpValue::int(va.wrapping_rem(vb), w))
}
_ => Err("SRem: expected integer operands".to_string()),
}
}
fn execute_urem(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Int(va, w), InterpValue::Int(vb, _)) => {
if vb == 0 {
return Ok(InterpValue::Poison);
}
let ua = va as u64;
let ub = vb as u64;
Ok(InterpValue::int(ua.wrapping_rem(ub) as i64, w))
}
_ => Err("URem: expected integer operands".to_string()),
}
}
fn execute_fadd(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Float(va), InterpValue::Float(vb)) => Ok(InterpValue::Float(va + vb)),
(InterpValue::Float32(va), InterpValue::Float32(vb)) => {
Ok(InterpValue::Float32(va + vb))
}
_ => Err("FAdd: expected float operands".to_string()),
}
}
fn execute_fsub(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Float(va), InterpValue::Float(vb)) => Ok(InterpValue::Float(va - vb)),
(InterpValue::Float32(va), InterpValue::Float32(vb)) => {
Ok(InterpValue::Float32(va - vb))
}
_ => Err("FSub: expected float operands".to_string()),
}
}
fn execute_fmul(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Float(va), InterpValue::Float(vb)) => Ok(InterpValue::Float(va * vb)),
(InterpValue::Float32(va), InterpValue::Float32(vb)) => {
Ok(InterpValue::Float32(va * vb))
}
_ => Err("FMul: expected float operands".to_string()),
}
}
fn execute_fdiv(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Float(va), InterpValue::Float(vb)) => Ok(InterpValue::Float(va / vb)),
(InterpValue::Float32(va), InterpValue::Float32(vb)) => {
Ok(InterpValue::Float32(va / vb))
}
_ => Err("FDiv: expected float operands".to_string()),
}
}
fn execute_and(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Int(va, w), InterpValue::Int(vb, _)) => Ok(InterpValue::int(va & vb, w)),
_ => Err("And: expected integer operands".to_string()),
}
}
fn execute_or(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Int(va, w), InterpValue::Int(vb, _)) => Ok(InterpValue::int(va | vb, w)),
_ => Err("Or: expected integer operands".to_string()),
}
}
fn execute_xor(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Int(va, w), InterpValue::Int(vb, _)) => Ok(InterpValue::int(va ^ vb, w)),
_ => Err("Xor: expected integer operands".to_string()),
}
}
fn execute_shl(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Int(va, w), InterpValue::Int(vb, _)) => {
let shift = vb as u32;
if shift >= w {
Ok(InterpValue::Poison)
} else {
Ok(InterpValue::int(va << shift, w))
}
}
_ => Err("Shl: expected integer operands".to_string()),
}
}
fn execute_lshr(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Int(va, w), InterpValue::Int(vb, _)) => {
let shift = vb as u32;
if shift >= w {
Ok(InterpValue::Poison)
} else {
let ua = va as u64;
Ok(InterpValue::int((ua >> shift) as i64, w))
}
}
_ => Err("LShr: expected integer operands".to_string()),
}
}
fn execute_ashr(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
match (a, b) {
(InterpValue::Int(va, w), InterpValue::Int(vb, _)) => {
let shift = vb as u32;
if shift >= w {
Ok(InterpValue::Poison)
} else {
Ok(InterpValue::int(va >> shift, w))
}
}
_ => Err("AShr: expected integer operands".to_string()),
}
}
fn execute_alloca(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let i = inst.borrow();
let alloc_size = get_type_size(&i.ty);
let addr = self.state.allocate(alloc_size as usize);
Ok(InterpValue::Pointer(addr))
}
fn execute_load(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let ptr_val = self.get_operand_value(inst, 0);
let addr = ptr_val.as_pointer();
let i = inst.borrow();
let size = get_type_size(&i.ty) as usize;
self.read_memory(addr, size)
}
fn execute_store(&mut self, inst: &ValueRef) -> Result<(), String> {
let val = self.get_operand_value(inst, 0);
let ptr_val = self.get_operand_value(inst, 1);
let addr = ptr_val.as_pointer();
self.write_memory(addr, &val);
Ok(())
}
fn execute_gep(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let base = self.get_operand_value(inst, 0);
let base_addr = base.as_pointer();
let i = inst.borrow();
let mut offset: i64 = 0;
for idx_op in &i.operands[1..] {
let idx_val = self.get_local_value(idx_op);
if let InterpValue::Int(idx, _) = idx_val {
offset += idx * 8;
} else {
return Err("GEP: non-constant index not fully supported".to_string());
}
}
let new_addr = (base_addr as i64 + offset) as usize;
Ok(InterpValue::Pointer(new_addr))
}
fn execute_br(&mut self, inst: &ValueRef) -> Result<(), String> {
let i = inst.borrow();
if i.operands.len() == 1 {
let target_vid = i.operands[0].borrow().vid as usize;
self.state.current_block = Some(target_vid);
self.state.pc = usize::MAX; } else if i.operands.len() >= 3 {
let cond = self.get_operand_value(inst, 0);
let cond_val = cond.as_int();
let target = if cond_val != 0 {
&i.operands[1]
} else {
&i.operands[2]
};
let target_vid = target.borrow().vid as usize;
self.state.current_block = Some(target_vid);
self.state.pc = usize::MAX; } else {
return Err("Br: invalid operand count".to_string());
}
Ok(())
}
fn execute_switch(&mut self, _inst: &ValueRef) -> Result<(), String> {
self.state.is_terminated = true;
Ok(())
}
fn execute_ret(&mut self, inst: &ValueRef) -> Result<(), String> {
let i = inst.borrow();
if i.operands.is_empty() {
self.state.is_terminated = true;
} else {
let ret_val = self.get_operand_value(inst, 0);
self.state.locals.insert(0, ret_val);
self.state.is_terminated = true;
}
Ok(())
}
fn execute_call(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let i = inst.borrow();
if i.operands.is_empty() {
return Err("Call: no callee".to_string());
}
let callee = i.operands[0].borrow();
let callee_name = callee.name.clone();
match callee_name.as_str() {
"llvm.sqrt.f64" => {
let arg = self.get_operand_value(inst, 1);
return Ok(InterpValue::Float(arg.as_float().sqrt()));
}
"llvm.fabs.f64" => {
let arg = self.get_operand_value(inst, 1);
return Ok(InterpValue::Float(arg.as_float().abs()));
}
_ => {}
}
if callee.subclass == SubclassKind::Function {
let mut call_args: Vec<InterpValue> = Vec::new();
for op in &i.operands[1..] {
call_args.push(self.get_local_value(op));
}
self.push_frame(
&callee_name,
self.state.current_block.unwrap_or(0),
self.state.pc + 1,
);
let result = self.execute_function(&i.operands[0], &call_args)?;
self.pop_frame();
return Ok(result);
}
Ok(InterpValue::Undef)
}
fn execute_phi(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let i = inst.borrow();
if i.operands.len() < 2 {
return Err("Phi: need at least one value/block pair".to_string());
}
for chunk in i.operands.chunks(2) {
if chunk.len() == 2 {
let val = self.get_local_value(&chunk[0]);
return Ok(val);
}
}
Err("Phi: no matching incoming value".to_string())
}
fn execute_select(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let cond = self.get_operand_value(inst, 0);
let true_val = self.get_operand_value(inst, 1);
let false_val = self.get_operand_value(inst, 2);
match cond {
InterpValue::Int(c, _) => {
if c != 0 {
Ok(true_val)
} else {
Ok(false_val)
}
}
_ => Err("Select: condition must be integer".to_string()),
}
}
fn execute_icmp(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
let (va, _) = match a {
InterpValue::Int(v, w) => (v, w),
_ => return Err("ICmp: expected integer operands".to_string()),
};
let (vb, _) = match b {
InterpValue::Int(v, w) => (v, w),
_ => return Err("ICmp: expected integer operands".to_string()),
};
let pred = get_icmp_pred(inst);
let result = match pred {
ICmpPred::Eq => va == vb,
ICmpPred::Ne => va != vb,
ICmpPred::Ugt => (va as u64) > (vb as u64),
ICmpPred::Uge => (va as u64) >= (vb as u64),
ICmpPred::Ult => (va as u64) < (vb as u64),
ICmpPred::Ule => (va as u64) <= (vb as u64),
ICmpPred::Sgt => va > vb,
ICmpPred::Sge => va >= vb,
ICmpPred::Slt => va < vb,
ICmpPred::Sle => va <= vb,
};
Ok(InterpValue::int(if result { 1 } else { 0 }, 1))
}
fn execute_fcmp(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let a = self.get_operand_value(inst, 0);
let b = self.get_operand_value(inst, 1);
let (va, vb) = match (&a, &b) {
(InterpValue::Float(fa), InterpValue::Float(fb)) => (*fa, *fb),
(InterpValue::Float32(fa), InterpValue::Float32(fb)) => (*fa as f64, *fb as f64),
_ => return Err("FCmp: expected float operands".to_string()),
};
let pred = get_fcmp_pred(inst);
let result = match pred {
FCmpPred::Oeq | FCmpPred::Ueq => va == vb,
FCmpPred::One | FCmpPred::Une => va != vb,
FCmpPred::Ogt | FCmpPred::Ugt => va > vb,
FCmpPred::Oge | FCmpPred::Uge => va >= vb,
FCmpPred::Olt | FCmpPred::Ult => va < vb,
FCmpPred::Ole | FCmpPred::Ule => va <= vb,
_ => false,
};
Ok(InterpValue::int(if result { 1 } else { 0 }, 1))
}
fn execute_trunc(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let val = self.get_operand_value(inst, 0);
let i = inst.borrow();
let dest_width = get_integer_bit_width(&i.ty);
match val {
InterpValue::Int(v, _) => Ok(InterpValue::int(v, dest_width)),
_ => Err("Trunc: expected integer operand".to_string()),
}
}
fn execute_zext(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let val = self.get_operand_value(inst, 0);
let i = inst.borrow();
let dest_width = get_integer_bit_width(&i.ty);
match val {
InterpValue::Int(v, _) => Ok(InterpValue::int(v, dest_width)),
_ => Err("ZExt: expected integer operand".to_string()),
}
}
fn execute_sext(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let val = self.get_operand_value(inst, 0);
let i = inst.borrow();
let dest_width = get_integer_bit_width(&i.ty);
match val {
InterpValue::Int(v, src_width) => {
let shift = 64 - src_width;
let extended = (v << shift) >> shift;
Ok(InterpValue::int(extended, dest_width))
}
_ => Err("SExt: expected integer operand".to_string()),
}
}
fn execute_bitcast(&mut self, inst: &ValueRef) -> Result<InterpValue, String> {
let val = self.get_operand_value(inst, 0);
Ok(val)
}
pub fn get_operand_value(&self, inst: &ValueRef, op_idx: usize) -> InterpValue {
let i = inst.borrow();
if op_idx >= i.operands.len() {
return InterpValue::Undef;
}
self.get_local_value(&i.operands[op_idx])
}
fn get_local_value(&self, val: &ValueRef) -> InterpValue {
let v = val.borrow();
if v.subclass == SubclassKind::Constant {
if let Ok(int_val) = v.name.parse::<i64>() {
let width = get_integer_bit_width(&v.ty);
return InterpValue::int(int_val, width);
}
if let Ok(float_val) = v.name.parse::<f64>() {
return InterpValue::Float(float_val);
}
}
self.state
.locals
.get(&(v.vid as usize))
.cloned()
.unwrap_or(InterpValue::Undef)
}
pub fn set_result(&mut self, inst: &ValueRef, val: InterpValue) {
let vid = inst.borrow().vid as usize;
self.state.locals.insert(vid, val);
}
pub fn read_memory(&self, addr: usize, size: usize) -> Result<InterpValue, String> {
if addr + size > self.state.memory.len() {
return Err(format!(
"Memory read out of bounds: addr={}, size={}, mem_len={}",
addr,
size,
self.state.memory.len()
));
}
if size <= 8 {
let mut bytes = [0u8; 8];
for i in 0..size {
bytes[i] = self.state.memory[addr + i];
}
let val = i64::from_le_bytes(bytes);
Ok(InterpValue::int(val, (size * 8) as u32))
} else {
Err("Reading more than 8 bytes not yet supported".to_string())
}
}
pub fn write_memory(&mut self, addr: usize, val: &InterpValue) {
match val {
InterpValue::Int(v, width) => {
let size = (*width as usize + 7) / 8;
if addr + size <= self.state.memory.len() {
let bytes = v.to_le_bytes();
for i in 0..size.min(8) {
self.state.memory[addr + i] = bytes[i];
}
}
}
InterpValue::Float(v) => {
let bytes = v.to_le_bytes();
if addr + 8 <= self.state.memory.len() {
for i in 0..8 {
self.state.memory[addr + i] = bytes[i];
}
}
}
InterpValue::Float32(v) => {
let bytes = v.to_le_bytes();
if addr + 4 <= self.state.memory.len() {
for i in 0..4 {
self.state.memory[addr + i] = bytes[i];
}
}
}
_ => {}
}
}
pub fn push_frame(&mut self, func_name: &str, return_bb: usize, return_pc: usize) {
let frame = StackFrame::new(func_name, Some(return_bb), return_pc);
self.state.call_stack.push(frame);
}
pub fn pop_frame(&mut self) {
if let Some(frame) = self.state.call_stack.pop() {
self.state.locals = frame.saved_locals;
self.state.current_block = frame.return_bb;
self.state.pc = frame.return_pc;
}
}
fn find_block_by_vid(&self, operands: &[ValueRef], vid: usize) -> Option<ValueRef> {
for op in operands {
let bb = op.borrow();
if bb.vid as usize == vid && bb.subclass == SubclassKind::BasicBlock {
return Some(Rc::clone(op));
}
}
None
}
}
pub fn interpret_function(func: &ValueRef, args: &[InterpValue]) -> Result<InterpValue, String> {
let mut interp = IRInterpreter::new();
interp.execute_function(func, args)
}
fn func_vid_with_offset(func: &ValueRef, offset: i64) -> usize {
let vid = func.borrow().vid as i64;
((vid) + offset + 1_000_000) as usize
}
fn get_type_size(ty: &Type) -> u64 {
match &ty.kind {
TypeKind::Integer { bits } => (*bits as u64 + 7) / 8,
TypeKind::Float => 4,
TypeKind::Double => 8,
TypeKind::Half => 2,
TypeKind::BFloat => 2,
TypeKind::Pointer { .. } => 8,
TypeKind::Array { len, .. } => *len as u64 * 8,
_ => 8,
}
}
fn get_integer_bit_width(ty: &Type) -> u32 {
match &ty.kind {
TypeKind::Integer { bits } => *bits,
_ => 32,
}
}
fn get_icmp_pred(inst: &ValueRef) -> ICmpPred {
let i = inst.borrow();
match i.subclass_data {
0 => ICmpPred::Eq,
1 => ICmpPred::Ne,
2 => ICmpPred::Ugt,
3 => ICmpPred::Uge,
4 => ICmpPred::Ult,
5 => ICmpPred::Ule,
6 => ICmpPred::Sgt,
7 => ICmpPred::Sge,
8 => ICmpPred::Slt,
9 => ICmpPred::Sle,
_ => ICmpPred::Eq,
}
}
fn get_fcmp_pred(inst: &ValueRef) -> FCmpPred {
let i = inst.borrow();
match i.subclass_data {
1 => FCmpPred::Oeq,
2 => FCmpPred::Ogt,
3 => FCmpPred::Oge,
4 => FCmpPred::Olt,
5 => FCmpPred::Ole,
6 => FCmpPred::One,
7 => FCmpPred::Ord,
8 => FCmpPred::Ueq,
9 => FCmpPred::Ugt,
10 => FCmpPred::Uge,
11 => FCmpPred::Ult,
12 => FCmpPred::Ule,
13 => FCmpPred::Une,
14 => FCmpPred::Uno,
_ => FCmpPred::False,
}
}
#[cfg(test)]
mod tests {
use super::*;
use llvm_native_core::basic_block::BasicBlock;
use llvm_native_core::types::Type;
use llvm_native_core::value::valref;
fn make_executable_func(name: &str, bbs: &[ValueRef], _param_types: Vec<Type>) -> ValueRef {
let fn_ty = Type::function_type_with(
Type::i32().id,
_param_types.iter().map(|t| t.id).collect(),
false,
);
let mut v = llvm_native_core::value::Value::new(fn_ty).named(name);
v.subclass = SubclassKind::Function;
for bb in bbs {
v.operands.push(Rc::clone(bb));
}
valref(v)
}
fn make_bb(name: &str, insts: &[ValueRef]) -> ValueRef {
let mut bb = BasicBlock::new(name);
for inst in insts {
bb.instructions.push(Rc::clone(inst));
bb.value.borrow_mut().operands.push(Rc::clone(inst));
}
bb.value
}
fn make_const_i32(name: &str, val: i32) -> ValueRef {
let mut v = llvm_native_core::value::Value::new(Type::i32()).named(name);
v.subclass = SubclassKind::Constant;
v.name = format!("{}", val);
valref(v)
}
fn make_inst(name: &str, opcode: Opcode, ty: Type, operands: Vec<ValueRef>) -> ValueRef {
let mut v = llvm_native_core::value::Value::new(ty).named(name);
v.subclass = SubclassKind::Instruction;
v.opcode = Some(opcode);
v.operands = operands;
v.num_operands = v.operands.len();
valref(v)
}
#[test]
fn test_interp_value_int() {
let v = InterpValue::int(42, 32);
assert_eq!(v.as_int(), 42);
}
#[test]
fn test_interp_value_int_sign_extends() {
let v = InterpValue::int(0xFF, 8);
assert_eq!(v.as_int(), -1);
}
#[test]
fn test_interp_value_int_positive() {
let v = InterpValue::int(42, 8);
assert_eq!(v.as_int(), 42);
}
#[test]
fn test_interp_value_float() {
let v = InterpValue::Float(3.14);
assert!((v.as_float() - 3.14).abs() < 0.001);
}
#[test]
fn test_interp_value_pointer() {
let v = InterpValue::Pointer(0x1000);
assert_eq!(v.as_pointer(), 0x1000);
}
#[test]
fn test_interp_value_is_undef_or_poison() {
assert!(InterpValue::Undef.is_undef_or_poison());
assert!(InterpValue::Poison.is_undef_or_poison());
assert!(!InterpValue::int(0, 32).is_undef_or_poison());
}
#[test]
fn test_execution_state_new() {
let state = ExecutionState::new();
assert_eq!(state.memory.len(), 1024 * 1024);
assert!(state.locals.is_empty());
assert!(!state.is_terminated);
}
#[test]
fn test_execution_state_allocate() {
let mut state = ExecutionState::new();
let addr = state.allocate(16);
assert_eq!(addr, 1024 * 1024);
assert_eq!(state.memory.len(), 1024 * 1024 + 16);
}
#[test]
fn test_interpreter_new() {
let interp = IRInterpreter::new();
assert_eq!(interp.max_steps, 1_000_000);
assert!(!interp.trace);
}
#[test]
fn test_set_max_steps() {
let mut interp = IRInterpreter::new();
interp.set_max_steps(100);
assert_eq!(interp.max_steps, 100);
}
#[test]
fn test_execute_add() {
let a = make_const_i32("a", 10);
let b = make_const_i32("b", 20);
let add_inst = make_inst("add", Opcode::Add, Type::i32(), vec![a, b]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
add_inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(10, 32),
);
interp.state.locals.insert(
add_inst.borrow().operands[1].borrow().vid as usize,
InterpValue::int(20, 32),
);
let result = interp.execute_add(&add_inst).unwrap();
assert_eq!(result.as_int(), 30);
}
#[test]
fn test_execute_sub() {
let a = make_const_i32("a", 100);
let b = make_const_i32("b", 40);
let inst = make_inst("sub", Opcode::Sub, Type::i32(), vec![a, b]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(100, 32),
);
interp.state.locals.insert(
inst.borrow().operands[1].borrow().vid as usize,
InterpValue::int(40, 32),
);
let result = interp.execute_sub(&inst).unwrap();
assert_eq!(result.as_int(), 60);
}
#[test]
fn test_execute_mul() {
let a = make_const_i32("a", 7);
let b = make_const_i32("b", 8);
let inst = make_inst("mul", Opcode::Mul, Type::i32(), vec![a, b]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(7, 32),
);
interp.state.locals.insert(
inst.borrow().operands[1].borrow().vid as usize,
InterpValue::int(8, 32),
);
let result = interp.execute_mul(&inst).unwrap();
assert_eq!(result.as_int(), 56);
}
#[test]
fn test_execute_and() {
let a = make_const_i32("a", 0xFF00);
let b = make_const_i32("b", 0x0FF0);
let inst = make_inst("and", Opcode::And, Type::i32(), vec![a, b]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(0xFF00, 32),
);
interp.state.locals.insert(
inst.borrow().operands[1].borrow().vid as usize,
InterpValue::int(0x0FF0, 32),
);
let result = interp.execute_and(&inst).unwrap();
assert_eq!(result.as_int(), 0x0F00);
}
#[test]
fn test_execute_or() {
let a = make_const_i32("a", 0x0F00);
let b = make_const_i32("b", 0x00F0);
let inst = make_inst("or", Opcode::Or, Type::i32(), vec![a, b]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(0x0F00, 32),
);
interp.state.locals.insert(
inst.borrow().operands[1].borrow().vid as usize,
InterpValue::int(0x00F0, 32),
);
let result = interp.execute_or(&inst).unwrap();
assert_eq!(result.as_int(), 0x0FF0);
}
#[test]
fn test_execute_xor() {
let a = make_const_i32("a", 0x0FF0);
let b = make_const_i32("b", 0x0F00);
let inst = make_inst("xor", Opcode::Xor, Type::i32(), vec![a, b]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(0x0FF0, 32),
);
interp.state.locals.insert(
inst.borrow().operands[1].borrow().vid as usize,
InterpValue::int(0x0F00, 32),
);
let result = interp.execute_xor(&inst).unwrap();
assert_eq!(result.as_int(), 0x00F0);
}
#[test]
fn test_execute_shl() {
let a = make_const_i32("a", 1);
let b = make_const_i32("b", 3);
let inst = make_inst("shl", Opcode::Shl, Type::i32(), vec![a, b]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(1, 32),
);
interp.state.locals.insert(
inst.borrow().operands[1].borrow().vid as usize,
InterpValue::int(3, 32),
);
let result = interp.execute_shl(&inst).unwrap();
assert_eq!(result.as_int(), 8);
}
#[test]
fn test_execute_lshr() {
let a = make_const_i32("a", 16);
let b = make_const_i32("b", 2);
let inst = make_inst("lshr", Opcode::LShr, Type::i32(), vec![a, b]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(16, 32),
);
interp.state.locals.insert(
inst.borrow().operands[1].borrow().vid as usize,
InterpValue::int(2, 32),
);
let result = interp.execute_lshr(&inst).unwrap();
assert_eq!(result.as_int(), 4);
}
#[test]
fn test_execute_ashr() {
let a = make_const_i32("a", -16);
let b = make_const_i32("b", 2);
let inst = make_inst("ashr", Opcode::AShr, Type::i32(), vec![a, b]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(-16, 32),
);
interp.state.locals.insert(
inst.borrow().operands[1].borrow().vid as usize,
InterpValue::int(2, 32),
);
let result = interp.execute_ashr(&inst).unwrap();
assert_eq!(result.as_int(), -4);
}
#[test]
fn test_execute_sdiv_zero_is_poison() {
let a = make_const_i32("a", 42);
let b = make_const_i32("b", 0);
let inst = make_inst("sdiv", Opcode::SDiv, Type::i32(), vec![a, b]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(42, 32),
);
interp.state.locals.insert(
inst.borrow().operands[1].borrow().vid as usize,
InterpValue::int(0, 32),
);
let result = interp.execute_sdiv(&inst).unwrap();
assert!(result.is_undef_or_poison());
}
#[test]
fn test_execute_load_store_roundtrip() {
let alloca_inst = make_inst("alloc", Opcode::Alloca, Type::pointer(0), vec![]);
let store_inst = make_inst(
"store",
Opcode::Store,
Type::void(),
vec![make_const_i32("val", 123), Rc::clone(&alloca_inst)],
);
let load_inst = make_inst(
"load",
Opcode::Load,
Type::i32(),
vec![Rc::clone(&alloca_inst)],
);
let mut interp = IRInterpreter::new();
let ptr = interp.execute_alloca(&alloca_inst).unwrap();
let ptr_addr = ptr.as_pointer();
interp.set_result(&alloca_inst, ptr);
interp.state.locals.insert(
store_inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(123, 32),
);
interp.state.locals.insert(
store_inst.borrow().operands[1].borrow().vid as usize,
InterpValue::Pointer(ptr_addr),
);
interp.execute_store(&store_inst).unwrap();
interp.state.locals.insert(
load_inst.borrow().operands[0].borrow().vid as usize,
InterpValue::Pointer(ptr_addr),
);
let loaded = interp.execute_load(&load_inst).unwrap();
assert_eq!(loaded.as_int(), 123);
}
#[test]
fn test_execute_trunc() {
let a = make_const_i32("a", 0x1234);
let inst = make_inst("trunc", Opcode::Trunc, Type::i8(), vec![a]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(0x1234, 32),
);
let result = interp.execute_trunc(&inst).unwrap();
assert_eq!(result.as_int(), 0x34);
}
#[test]
fn test_execute_zext() {
let a = make_const_i32("a", 0x34);
let inst = make_inst("zext", Opcode::ZExt, Type::i32(), vec![a]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(0x34, 8),
);
let result = interp.execute_zext(&inst).unwrap();
assert_eq!(result.as_int(), 0x34);
}
#[test]
fn test_execute_sext_positive() {
let a = make_const_i32("a", 42);
let inst = make_inst("sext", Opcode::SExt, Type::i32(), vec![a]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(42, 8),
);
let result = interp.execute_sext(&inst).unwrap();
assert_eq!(result.as_int(), 42);
}
#[test]
fn test_execute_bitcast_passthrough() {
let a = make_const_i32("a", 0x40490FDB);
let inst = make_inst("bitcast", Opcode::BitCast, Type::float(), vec![a]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(0x40490FDB, 32),
);
let result = interp.execute_bitcast(&inst).unwrap();
assert_eq!(result.as_int(), 0x40490FDB);
}
#[test]
fn test_execute_select_true() {
let cond = make_const_i32("cond", 1);
let tv = make_const_i32("tv", 100);
let fv = make_const_i32("fv", 200);
let inst = make_inst("sel", Opcode::Select, Type::i32(), vec![cond, tv, fv]);
let mut interp = IRInterpreter::new();
interp.state.locals.insert(
inst.borrow().operands[0].borrow().vid as usize,
InterpValue::int(1, 1),
);
interp.state.locals.insert(
inst.borrow().operands[1].borrow().vid as usize,
InterpValue::int(100, 32),
);
interp.state.locals.insert(
inst.borrow().operands[2].borrow().vid as usize,
InterpValue::int(200, 32),
);
let result = interp.execute_select(&inst).unwrap();
assert_eq!(result.as_int(), 100);
}
#[test]
fn test_interpret_function_simple() {
let ret_val = make_const_i32("retval", 42);
let ret_inst = make_inst("ret", Opcode::Ret, Type::void(), vec![Rc::clone(&ret_val)]);
let entry = make_bb("entry", &[ret_inst]);
let func = make_executable_func("simple", &[entry], vec![]);
let result = interpret_function(&func, &[]).unwrap();
assert_eq!(result, InterpValue::int(42, 32));
}
#[test]
fn test_interpret_function_add_two_blocks() {
let body_bb = make_bb(
"body",
&[make_inst(
"ret",
Opcode::Ret,
Type::void(),
vec![make_const_i32("retval", 10)],
)],
);
let entry_bb = make_bb(
"entry",
&[make_inst(
"br",
Opcode::Br,
Type::void(),
vec![Rc::clone(&body_bb)],
)],
);
let func = make_executable_func("add_two", &[entry_bb, body_bb], vec![]);
let result = interpret_function(&func, &[]).unwrap();
assert_eq!(result, InterpValue::int(10, 32));
}
#[test]
fn test_execute_function_non_function_errors() {
let val = make_const_i32("x", 1);
let result = interpret_function(&val, &[]);
assert!(result.is_err());
}
#[test]
fn test_stack_frame_new() {
let frame = StackFrame::new("test_fn", Some(42), 10);
assert_eq!(frame.func_name, "test_fn");
assert_eq!(frame.return_bb, Some(42));
assert_eq!(frame.return_pc, 10);
}
}