use crate::circuit_compiler::{IRProgram, IRFunction, IRStmt, IRExpr, CircuitCompiler};
use std::collections::HashMap;
use std::fs;
pub struct LLVMIntegration {
context: LLVMContext,
}
#[derive(Debug, Clone)]
pub struct LLVMContext {
globals: HashMap<String, LLVMType>,
functions: HashMap<String, LLVMFunction>,
current_function: Option<String>,
labels: HashMap<String, usize>,
}
#[derive(Debug, Clone)]
pub enum LLVMType {
I1, I8, I32, I64, Void, Pointer(Box<LLVMType>), }
#[derive(Debug, Clone)]
pub struct LLVMFunction {
pub return_type: LLVMType,
pub parameters: Vec<(String, LLVMType)>,
pub instructions: Vec<LLVMInstruction>,
pub is_external: bool,
}
#[derive(Debug, Clone)]
pub enum LLVMValue {
Constant(i64),
Register(String),
Global(String),
}
#[derive(Debug, Clone)]
pub enum LLVMInstruction {
Alloca { result: String, ty: LLVMType },
Store { value: LLVMValue, ptr: LLVMValue },
Load { result: String, ty: LLVMType, ptr: LLVMValue },
Add { result: String, lhs: LLVMValue, rhs: LLVMValue },
Sub { result: String, lhs: LLVMValue, rhs: LLVMValue },
Mul { result: String, lhs: LLVMValue, rhs: LLVMValue },
ICmp { result: String, pred: String, lhs: LLVMValue, rhs: LLVMValue },
Br { cond: Option<LLVMValue>, true_label: String, false_label: Option<String> },
Ret { value: Option<LLVMValue> },
Call { result: Option<String>, func: String, args: Vec<LLVMValue> },
}
impl LLVMIntegration {
pub fn new() -> Self {
Self {
context: LLVMContext {
globals: HashMap::new(),
functions: HashMap::new(),
current_function: None,
labels: HashMap::new(),
},
}
}
pub fn parse_file(&mut self, filename: &str) -> Result<IRProgram, String> {
let content = fs::read_to_string(filename)
.map_err(|e| format!("Failed to read file: {}", e))?;
self.parse_llvm_ir(&content)
}
pub fn parse_llvm_ir(&mut self, llvm_ir: &str) -> Result<IRProgram, String> {
let mut spectral_program = IRProgram {
functions: Vec::new(),
globals: HashMap::new(),
};
let lines: Vec<&str> = llvm_ir.lines().map(|l| l.trim()).collect();
for line in lines {
if line.is_empty() || line.starts_with(';') {
continue; }
if line.starts_with("define ") {
let func = self.parse_function_definition(line)?;
self.context.functions.insert(func.0.clone(), func.1);
self.context.current_function = Some(func.0);
} else if line.starts_with("%") || line.starts_with("store ") ||
line.starts_with("ret ") || line.starts_with("br ") ||
line.starts_with("call ") {
if let Some(ref func_name) = self.context.current_function.clone() {
self.parse_instruction_full(line, func_name, &mut spectral_program)?;
}
}
}
let functions_clone = self.context.functions.clone();
for (func_name, llvm_func) in &functions_clone {
if !llvm_func.is_external {
let spectral_func = self.convert_function(func_name, llvm_func)?;
spectral_program.functions.push(spectral_func);
}
}
Ok(spectral_program)
}
fn parse_function_definition(&mut self, line: &str) -> Result<(String, LLVMFunction), String> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 3 {
return Err(format!("Invalid function definition: {}", line));
}
let return_type = self.parse_type(parts[1])?;
let func_part = parts[2];
let name_start = func_part.find('@').unwrap_or(0) + 1;
let name_end = func_part.find('(').unwrap_or(func_part.len());
let func_name = func_part[name_start..name_end].to_string();
let mut parameters = Vec::new();
if let Some(param_start) = func_part.find('(') {
if let Some(param_end) = func_part.find(')') {
let param_str = &func_part[param_start+1..param_end];
if !param_str.is_empty() {
let param_parts: Vec<&str> = param_str.split_whitespace().collect();
if param_parts.len() >= 2 {
let param_type = self.parse_type(param_parts[0])?;
let param_name = param_parts[1].trim_start_matches('%').to_string();
parameters.push((param_name, param_type));
}
}
}
}
let function = LLVMFunction {
return_type,
parameters,
instructions: Vec::new(),
is_external: false,
};
Ok((func_name, function))
}
fn parse_instruction_full(&mut self, line: &str, func_name: &str, program: &mut IRProgram) -> Result<(), String> {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with(';') {
return Ok(());
}
if trimmed.starts_with('%') || trimmed.starts_with("store ") || trimmed.starts_with("load ") ||
trimmed.starts_with("add ") || trimmed.starts_with("sub ") || trimmed.starts_with("mul ") ||
trimmed.starts_with("sdiv ") || trimmed.starts_with("udiv ") ||
trimmed.starts_with("and ") || trimmed.starts_with("or ") || trimmed.starts_with("xor ") ||
trimmed.contains("icmp ") || trimmed.starts_with("br ") || trimmed.starts_with("ret ") ||
trimmed.starts_with("call ") || trimmed.starts_with("alloca ") ||
trimmed.starts_with("sext ") || trimmed.starts_with("zext ") || trimmed.starts_with("trunc ") {
if trimmed.contains(" = phi ") {
return Ok(());
}
let instruction = self.parse_llvm_instruction(trimmed)?;
if let Some(ref func_name) = self.context.current_function {
if let Some(func) = self.context.functions.get_mut(func_name) {
func.instructions.push(instruction);
}
}
Ok(())
} else {
Ok(())
}
}
fn parse_llvm_instruction(&self, line: &str) -> Result<LLVMInstruction, String> {
if line.starts_with("alloca ") {
self.parse_alloca(line)
} else if line.starts_with("store ") {
self.parse_store(line)
} else if line.starts_with("load ") {
self.parse_load(line)
} else if line.contains(" = add ") {
self.parse_binary_op(line, "add")
} else if line.contains(" = sub ") {
self.parse_binary_op(line, "sub")
} else if line.contains(" = mul ") {
self.parse_binary_op(line, "mul")
} else if line.contains(" = sdiv ") {
self.parse_binary_op(line, "sdiv")
} else if line.contains(" = udiv ") {
self.parse_binary_op(line, "udiv")
} else if line.contains(" = and ") {
self.parse_binary_op(line, "and")
} else if line.contains(" = or ") {
self.parse_binary_op(line, "or")
} else if line.contains(" = xor ") {
self.parse_binary_op(line, "xor")
} else if line.contains("icmp ") {
self.parse_icmp(line)
} else if line.starts_with("br ") {
self.parse_br(line)
} else if line.starts_with("ret ") {
self.parse_ret(line)
} else if line.contains(" = call ") || line.starts_with("call ") {
self.parse_call(line)
} else if line.starts_with("sext ") || line.starts_with("zext ") || line.starts_with("trunc ") {
self.parse_cast(line)
} else {
Err(format!("Unsupported instruction: {}", line))
}
}
fn parse_binary_op(&self, line: &str, op: &str) -> Result<LLVMInstruction, String> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 6 {
return Err(format!("Invalid {} instruction: {}", op, line));
}
let result = parts[0].trim_end_matches(" =").to_string();
let lhs = self.parse_value(parts[4].trim_end_matches(','))?;
let rhs = self.parse_value(parts[5])?;
match op {
"add" => Ok(LLVMInstruction::Add { result, lhs, rhs }),
"sub" => Ok(LLVMInstruction::Sub { result, lhs, rhs }),
"mul" => Ok(LLVMInstruction::Mul { result, lhs, rhs }),
_ => Err(format!("Unsupported binary operation: {}", op))
}
}
fn parse_icmp(&self, line: &str) -> Result<LLVMInstruction, String> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 7 {
return Err(format!("Invalid icmp instruction: {}", line));
}
let result = parts[0].trim_end_matches(" =").to_string();
let pred = parts[3].to_string();
let lhs = self.parse_value(parts[5].trim_end_matches(','))?;
let rhs = self.parse_value(parts[6])?;
Ok(LLVMInstruction::ICmp { result, pred, lhs, rhs })
}
fn parse_br(&self, line: &str) -> Result<LLVMInstruction, String> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() == 3 && parts[1] == "label" {
let true_label = parts[2].trim_end_matches(',').to_string();
Ok(LLVMInstruction::Br {
cond: None,
true_label,
false_label: None,
})
} else if parts.len() >= 7 {
let cond = self.parse_value(parts[2].trim_end_matches(','))?;
let true_label = parts[4].trim_end_matches(',').to_string();
let false_label = parts[6].trim_end_matches(',').to_string();
Ok(LLVMInstruction::Br {
cond: Some(cond),
true_label,
false_label: Some(false_label),
})
} else {
Err(format!("Invalid br instruction: {}", line))
}
}
fn parse_ret(&self, line: &str) -> Result<LLVMInstruction, String> {
let parts: Vec<&str> = line.split_whitespace().collect();
let value = if parts.len() > 2 {
Some(self.parse_value(parts[2])?)
} else {
None
};
Ok(LLVMInstruction::Ret { value })
}
fn parse_call(&self, line: &str) -> Result<LLVMInstruction, String> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 3 {
return Err(format!("Invalid call instruction: {}", line));
}
let has_result = parts[0].contains(" = ");
let result = if has_result {
Some(parts[0].trim_end_matches(" =").to_string())
} else {
None
};
let mut func_idx = if has_result { 1 } else { 0 };
let calling_conv = if parts[func_idx].starts_with("call") {
if func_idx + 1 < parts.len() && self.is_calling_convention(parts[func_idx + 1]) {
func_idx += 2; Some(parts[func_idx - 1].to_string())
} else {
func_idx += 1; None
}
} else {
None
};
if func_idx >= parts.len() {
return Err(format!("Missing function name in call: {}", line));
}
let func_part = parts[func_idx];
let func_name = func_part.trim_start_matches('@').to_string();
let args_start = line.find('(').unwrap_or(line.len());
let args_end = line.rfind(')').unwrap_or(line.len());
let args_str = &line[args_start + 1..args_end];
let args: Vec<LLVMValue> = if args_str.trim().is_empty() {
Vec::new()
} else {
args_str.split(',')
.map(|arg| self.parse_value(arg.trim()))
.collect::<Result<Vec<_>, _>>()?
};
Ok(LLVMInstruction::Call { result, func: func_name, args })
}
fn is_calling_convention(&self, s: &str) -> bool {
matches!(s, "ccc" | "fastcc" | "coldcc" | "cc" | "webkit_jscc" | "anyregcc" | "preserve_mostcc" | "preserve_allcc" | "cxx_fast_tlscc" | "swiftcc" | "tailcc")
}
fn parse_store(&self, line: &str) -> Result<LLVMInstruction, String> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 4 {
return Err(format!("Invalid store instruction: {}", line));
}
let value = self.parse_value(parts[1])?;
let ptr = self.parse_value(parts[3].trim_end_matches(','))?;
Ok(LLVMInstruction::Store { value, ptr })
}
fn parse_load(&self, line: &str) -> Result<LLVMInstruction, String> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 5 {
return Err(format!("Invalid load instruction: {}", line));
}
let result = parts[0].trim_end_matches(" =").to_string();
let ty = self.parse_type(parts[2].trim_end_matches(','))?;
let ptr = self.parse_value(parts[4])?;
Ok(LLVMInstruction::Load { result, ty, ptr })
}
fn parse_alloca(&self, line: &str) -> Result<LLVMInstruction, String> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 3 {
return Err(format!("Invalid alloca instruction: {}", line));
}
let result = parts[0].trim_end_matches(" =").to_string();
let ty = self.parse_type(parts[2])?;
Ok(LLVMInstruction::Alloca { result, ty })
}
fn parse_cast(&self, line: &str) -> Result<LLVMInstruction, String> {
Ok(LLVMInstruction::Add {
result: "dummy".to_string(),
lhs: LLVMValue::Constant(0),
rhs: LLVMValue::Constant(0),
})
}
fn parse_value(&self, val_str: &str) -> Result<LLVMValue, String> {
if val_str.starts_with('%') {
Ok(LLVMValue::Register(val_str.to_string()))
} else if let Ok(num) = val_str.parse::<i64>() {
Ok(LLVMValue::Constant(num))
} else {
Ok(LLVMValue::Constant(0)) }
}
fn parse_type(&self, type_str: &str) -> Result<LLVMType, String> {
match type_str {
"i1" => Ok(LLVMType::I1),
"i8" => Ok(LLVMType::I8),
"i32" => Ok(LLVMType::I32),
"i64" => Ok(LLVMType::I64),
"void" => Ok(LLVMType::Void),
_ if type_str.ends_with('*') => {
let base_type = &type_str[..type_str.len()-1];
Ok(LLVMType::Pointer(Box::new(self.parse_type(base_type)?)))
}
_ => Err(format!("Unsupported type: {}", type_str))
}
}
fn convert_instruction_to_ir(&self, instruction: LLVMInstruction, func_name: &str, program: &mut IRProgram) -> Result<(), String> {
match instruction {
LLVMInstruction::Add { result, lhs, rhs } => {
let lhs_expr = self.convert_value_to_expr(lhs);
let rhs_expr = self.convert_value_to_expr(rhs);
let add_expr = IRExpr::BinOp(
Box::new(lhs_expr),
crate::vm::SpectralOp::S_ADD,
Box::new(rhs_expr)
);
program.functions.last_mut()
.ok_or("No current function")?
.body
.push(IRStmt::Assign(result, add_expr));
}
LLVMInstruction::Sub { result, lhs, rhs } => {
let lhs_expr = self.convert_value_to_expr(lhs);
let rhs_expr = self.convert_value_to_expr(rhs);
let sub_expr = IRExpr::BinOp(
Box::new(lhs_expr),
crate::vm::SpectralOp::S_SUB,
Box::new(rhs_expr)
);
program.functions.last_mut()
.ok_or("No current function")?
.body
.push(IRStmt::Assign(result, sub_expr));
}
LLVMInstruction::Mul { result, lhs, rhs } => {
let lhs_expr = self.convert_value_to_expr(lhs);
let rhs_expr = self.convert_value_to_expr(rhs);
let mul_expr = IRExpr::BinOp(
Box::new(lhs_expr),
crate::vm::SpectralOp::S_MUL,
Box::new(rhs_expr)
);
program.functions.last_mut()
.ok_or("No current function")?
.body
.push(IRStmt::Assign(result, mul_expr));
}
LLVMInstruction::Ret { value } => {
let ret_expr = if let Some(val) = value {
self.convert_value_to_expr(val)
} else {
IRExpr::Const(0) };
program.functions.last_mut()
.ok_or("No current function")?
.body
.push(IRStmt::Return(ret_expr));
}
LLVMInstruction::Call { result, func, args } => {
let call_args = args.iter()
.map(|arg| self.convert_value_to_expr(arg.clone()))
.collect();
let call_expr = IRExpr::Call(func.clone(), call_args);
if let Some(res) = result {
program.functions.last_mut()
.ok_or("No current function")?
.body
.push(IRStmt::Assign(res, call_expr));
} else {
let call_args2 = args.iter()
.map(|arg| self.convert_value_to_expr(arg.clone()))
.collect();
program.functions.last_mut()
.ok_or("No current function")?
.body
.push(IRStmt::Call(func, call_args2));
}
}
LLVMInstruction::ICmp { result, pred, lhs, rhs } => {
match pred.as_str() {
"sle" => {
let lhs_expr = self.convert_value_to_expr(lhs);
let rhs_expr = self.convert_value_to_expr(rhs);
let cmp_expr = IRExpr::BinOp(
Box::new(lhs_expr),
crate::vm::SpectralOp::S_SUB,
Box::new(rhs_expr)
);
program.functions.last_mut()
.ok_or("No current function")?
.body
.push(IRStmt::Assign(result, cmp_expr));
}
_ => {
}
}
}
_ => {} }
Ok(())
}
fn convert_value_to_expr(&self, value: LLVMValue) -> IRExpr {
match value {
LLVMValue::Constant(val) => IRExpr::Const(val),
LLVMValue::Register(reg) => IRExpr::Var(reg),
LLVMValue::Global(name) => IRExpr::Var(name),
}
}
fn map_calling_convention(&self, _llvm_conv: Option<&str>) -> String {
"standard".to_string()
}
fn convert_function(&mut self, func_name: &str, llvm_func: &LLVMFunction) -> Result<IRFunction, String> {
let mut body = Vec::new();
let params: Vec<String> = llvm_func.parameters.iter()
.map(|(name, _)| name.clone())
.collect();
for instruction in &llvm_func.instructions {
match instruction {
LLVMInstruction::Add { result, lhs, rhs } => {
let lhs_expr = self.convert_value_to_expr(lhs.clone());
let rhs_expr = self.convert_value_to_expr(rhs.clone());
let add_expr = IRExpr::BinOp(
Box::new(lhs_expr),
crate::vm::SpectralOp::S_ADD,
Box::new(rhs_expr)
);
body.push(IRStmt::Assign(result.clone(), add_expr));
}
LLVMInstruction::Sub { result, lhs, rhs } => {
let lhs_expr = self.convert_value_to_expr(lhs.clone());
let rhs_expr = self.convert_value_to_expr(rhs.clone());
let sub_expr = IRExpr::BinOp(
Box::new(lhs_expr),
crate::vm::SpectralOp::S_SUB,
Box::new(rhs_expr)
);
body.push(IRStmt::Assign(result.clone(), sub_expr));
}
LLVMInstruction::Mul { result, lhs, rhs } => {
let lhs_expr = self.convert_value_to_expr(lhs.clone());
let rhs_expr = self.convert_value_to_expr(rhs.clone());
let mul_expr = IRExpr::BinOp(
Box::new(lhs_expr),
crate::vm::SpectralOp::S_MUL,
Box::new(rhs_expr)
);
body.push(IRStmt::Assign(result.clone(), mul_expr));
}
LLVMInstruction::ICmp { result, pred, lhs, rhs } => {
match pred.as_str() {
"sle" => {
let lhs_expr = self.convert_value_to_expr(lhs.clone());
let rhs_expr = self.convert_value_to_expr(rhs.clone());
let cmp_expr = IRExpr::BinOp(
Box::new(lhs_expr),
crate::vm::SpectralOp::S_SUB,
Box::new(rhs_expr)
);
body.push(IRStmt::Assign(result.clone(), cmp_expr));
}
_ => {
}
}
}
LLVMInstruction::Ret { value } => {
let ret_expr = if let Some(val) = value {
self.convert_value_to_expr(val.clone())
} else {
IRExpr::Const(0) };
body.push(IRStmt::Return(ret_expr));
}
LLVMInstruction::Call { result, func, args } => {
let call_args = args.iter()
.map(|arg| self.convert_value_to_expr(arg.clone()))
.collect();
let call_expr = IRExpr::Call(func.clone(), call_args);
if let Some(res) = result {
body.push(IRStmt::Assign(res.clone(), call_expr));
} else {
let call_args2 = args.iter()
.map(|arg| self.convert_value_to_expr(arg.clone()))
.collect();
body.push(IRStmt::Call(func.clone(), call_args2));
}
}
_ => {}
}
}
if !body.iter().any(|stmt| matches!(stmt, IRStmt::Return(_))) {
let return_stmt = if params.is_empty() {
IRStmt::Return(IRExpr::Const(0))
} else {
IRStmt::Return(IRExpr::Var(params[0].clone()))
};
body.push(return_stmt);
}
let spectral_func = IRFunction {
name: func_name.to_string(),
params,
body,
return_type: self.convert_type_to_string(&llvm_func.return_type),
};
Ok(spectral_func)
}
fn convert_type_to_string(&self, llvm_type: &LLVMType) -> String {
match llvm_type {
LLVMType::I1 => "bool".to_string(),
LLVMType::I8 => "i8".to_string(),
LLVMType::I32 => "i32".to_string(),
LLVMType::I64 => "i64".to_string(),
LLVMType::Void => "void".to_string(),
LLVMType::Pointer(inner) => format!("{}*", self.convert_type_to_string(inner)),
}
}
pub fn compile_llvm_file(&mut self, filename: &str) -> Result<CircuitCompiler, String> {
let spectral_ir = self.parse_file(filename)?;
let mut compiler = CircuitCompiler::new();
compiler.compile(&spectral_ir);
Ok(compiler)
}
pub fn get_context(&self) -> &LLVMContext {
&self.context
}
}
pub fn create_sample_llvm_ir() -> String {
r#"
; Simple fibonacci function in LLVM IR
define i32 @fib(i32 %n) {
entry:
%cmp = icmp sle i32 %n, 1
br i1 %cmp, label %return, label %recurse
recurse:
%n1 = sub i32 %n, 1
%n2 = sub i32 %n, 2
%fib1 = call i32 @fib(i32 %n1)
%fib2 = call i32 @fib(i32 %n2)
%result = add i32 %fib1, %fib2
br label %return
return:
%retval = phi i32 [ %n, %entry ], [ %result, %recurse ]
ret i32 %retval
}
"#.to_string()
}
pub fn save_sample_llvm_ir(filename: &str) -> Result<(), String> {
let ir = create_sample_llvm_ir();
fs::write(filename, ir)
.map_err(|e| format!("Failed to write LLVM IR file: {}", e))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_llvm_ir_parsing() {
let mut integration = LLVMIntegration::new();
let ir = create_sample_llvm_ir();
let result = integration.parse_llvm_ir(&ir);
assert!(result.is_ok(), "Failed to parse LLVM IR: {:?}", result.err());
}
#[test]
fn test_sample_file_creation() {
let filename = "test_fib.ll";
let result = save_sample_llvm_ir(filename);
assert!(result.is_ok(), "Failed to create sample LLVM IR file");
let _ = fs::remove_file(filename);
}
}