diff --git a/src/collections.rs b/src/collections.rs index eb1a35b..0fa5fae 100644 --- a/src/collections.rs +++ b/src/collections.rs @@ -79,6 +79,10 @@ where self.nodes.contains_key(&key) } + pub fn front(&self) -> Option { + self.head + } + pub fn node(&self, key: K) -> Option<&N> { self.nodes.get(&key) } diff --git a/src/ir/builder.rs b/src/ir/builder.rs index c804fbe..d795ea8 100644 --- a/src/ir/builder.rs +++ b/src/ir/builder.rs @@ -1,3 +1,5 @@ +use std::{error::Error, fmt}; + use super::{ entities::{BlockData, FunctionData, FunctionKind, ValueData, ValueKind}, module::{DataFlowGraph, Module}, @@ -61,6 +63,34 @@ pub enum BuilderErr { IncompatibleBlockArgType, } +impl fmt::Display for BuilderErr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use BuilderErr::*; + match self { + ValueNotFound => write!(f, "value not found"), + BlockNotFound => write!(f, "block not found"), + InvalidType => write!(f, "invalid value type"), + InvalidKind => write!(f, "invalid value kind"), + InvalidMutability => write!(f, "invalid mutability"), + InvalidGlobalValue => write!(f, "invalid global value"), + InvalidLocalValue => write!(f, "invalid local value"), + IncompatibleType => write!(f, "incompatible value type"), + IncompatibleArraySize => write!(f, "incompatible array size"), + IncompatibleArrayElemType => write!(f, "incompatible array element type"), + IncompatibleStructFieldNumber => { + write!(f, "incompatible struct field number") + } + IncompatibleStructFieldType => write!(f, "incompatible struct field type"), + IncompatibleBlockArgNumber => { + write!(f, "incompatible block argument number") + } + IncompatibleBlockArgType => write!(f, "incompatible block argument type"), + } + } +} + +impl Error for BuilderErr {} + pub trait QueryValueData { /// Get the type of a value. fn value_type(&self, value: Value) -> Result; @@ -108,6 +138,28 @@ pub trait ConstantBuilder: QueryValueData + AddValue { self.add_value(ValueData::new(ty, ValueKind::Bytes(bytes))) } + /// Build an integer constant of i32 type. + fn integer(&mut self, value: i32) -> Result { + let bytes = value.to_le_bytes().to_vec(); + + // remove leading zeros + let bytes = bytes + .into_iter() + .rev() + .skip_while(|&b| b == 0) + .collect::>(); + + self.bytes(Type::mk_int(32), bytes) + } + + /// Build a float constant. + /// + /// This is a shorthand for `bytes` with the bytes of the float. + fn float(&mut self, value: f32) -> Result { + let bytes = value.to_le_bytes().to_vec(); + self.bytes(Type::mk_float(), bytes) + } + /// Build an array constant. fn array(&mut self, ty: Type, values: Vec) -> Result { let (size, elem_type) = ty.as_array().ok_or(BuilderErr::InvalidType)?; @@ -200,7 +252,12 @@ pub trait LocalValueBuilder: QueryDfgData + AddValue + ConstantBuilder { } } - self.add_value(Binary::new_value_data(lhs_type, op, lhs, rhs)) + let res_type = match op { + BinaryOp::ICmp(_) | BinaryOp::FCmp(_) => Type::mk_int(1), + _ => lhs_type, + }; + + self.add_value(Binary::new_value_data(res_type, op, lhs, rhs)) } /// Build a unary instruction. diff --git a/src/ir/layout.rs b/src/ir/layout.rs index 761e404..75e1d1f 100644 --- a/src/ir/layout.rs +++ b/src/ir/layout.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, error::Error, fmt}; use crate::collections::{BiLinkedList, BiLinkedListErr, BiLinkedNode}; @@ -113,6 +113,24 @@ pub enum LayoutOpErr { InstNodeNotFound(Inst), } +impl fmt::Display for LayoutOpErr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LayoutOpErr::InstDuplicated(inst) => write!(f, "duplicated instruction: {:?}", inst), + LayoutOpErr::BlockDuplicated(block) => write!(f, "duplicated block: {:?}", block), + LayoutOpErr::ParentBlockNotFound(inst) => { + write!(f, "parent block not found for instruction: {:?}", inst) + } + LayoutOpErr::BlockNodeNotFound(block) => write!(f, "block node not found: {:?}", block), + LayoutOpErr::InstNodeNotFound(inst) => { + write!(f, "instruction node not found: {:?}", inst) + } + } + } +} + +impl Error for LayoutOpErr {} + impl Layout { pub fn new() -> Self { Self { @@ -121,6 +139,10 @@ impl Layout { } } + pub fn entry_block(&self) -> Option { + self.blocks.front() + } + pub fn blocks(&self) -> &BlockList { &self.blocks } diff --git a/src/ir/module.rs b/src/ir/module.rs index 3c6bbe2..c8cf43a 100644 --- a/src/ir/module.rs +++ b/src/ir/module.rs @@ -1,6 +1,8 @@ use std::{ cell::RefCell, collections::{HashMap, HashSet}, + error::Error, + fmt, hash::Hash, rc::{Rc, Weak}, }; @@ -103,12 +105,15 @@ impl DataFlowGraph { /// Get the name of a local or global value pub fn value_name(&self, value: Value) -> String { - self.global_name_allocator - .upgrade() - .expect("global name allocator should be alive.") - .borrow_mut() - .try_get(value) - .unwrap_or_else(|| self.value_name_allocator.borrow_mut().get(value)) + if let Some(_) = self.local_value_data(value) { + self.value_name_allocator.borrow_mut().get(value) + } else { + self.global_name_allocator + .upgrade() + .expect("global name allocator should be alive.") + .borrow_mut() + .get(value) + } } /// Get the name of a block @@ -244,8 +249,27 @@ impl Module { value } - pub(super) fn add_function(&mut self, value_data: ValueData, function_data: FunctionData) -> Function { + pub(super) fn add_function( + &mut self, + value_data: ValueData, + function_data: FunctionData, + ) -> Function { let function = Value::new(self.allocate_id()); + let function_name = function_data.name().to_string(); + + if function_name.starts_with(GLOBAL_PREFIX) { + self.name_allocator + .borrow_mut() + .assign(function, function_name) + .unwrap(); + } else { + let name = format!("{}{}", GLOBAL_PREFIX, function_name); + self.name_allocator + .borrow_mut() + .assign(function, name) + .unwrap(); + } + self.globals.borrow_mut().insert(function, value_data); self.functions.insert(function.into(), function_data); @@ -321,6 +345,17 @@ pub enum NameAllocErr { NameDuplicated, } +impl fmt::Display for NameAllocErr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::KeyDuplicated => write!(f, "key is already assigned or allocated"), + Self::NameDuplicated => write!(f, "name is already assigned or allocated"), + } + } +} + +impl Error for NameAllocErr {} + impl NameAllocator where T: Hash + Eq + Copy, diff --git a/src/ir/pass.rs b/src/ir/pass.rs index 9a8c4c2..3b2ed8f 100644 --- a/src/ir/pass.rs +++ b/src/ir/pass.rs @@ -1,9 +1,15 @@ use super::{entities::FunctionData, module::Module, values::Function}; pub trait GlobalPass { - fn run(&mut self, module: &mut Module); + type Ok; + type Err; + + fn run(&mut self, module: &mut Module) -> Result; } pub trait LocalPass { - fn run(&mut self, function: Function, data: &mut FunctionData); + type Ok; + type Err; + + fn run(&mut self, function: Function, data: &mut FunctionData) -> Result; } diff --git a/src/ir/types.rs b/src/ir/types.rs index ff694e1..b7b5ac3 100644 --- a/src/ir/types.rs +++ b/src/ir/types.rs @@ -118,6 +118,14 @@ impl Type { Type::make(TypeKind::Int(bits)) } + pub fn mk_i32() -> Type { + Type::mk_int(32) + } + + pub fn mk_i1() -> Type { + Type::mk_int(1) + } + pub fn mk_void() -> Type { Type::make(TypeKind::Void) } diff --git a/src/main.rs b/src/main.rs index e9e1b42..09377a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -use std::io; - use ir::{ builder::{ConstantBuilder, GlobalValueBuilder, LocalBlockBuilder, LocalValueBuilder}, module::Module, @@ -8,110 +6,83 @@ use ir::{ values::{BinaryOp, ICmpCond}, }; use passes::printer::Printer; +use std::io; pub mod collections; pub mod ir; pub mod passes; -fn main() -> io::Result<()> { +fn main() -> Result<(), Box> { let mut module = Module::new("fibonacci".to_string()); - let fn_fib = module - .builder() - .function_def( - "fib".to_string(), - Type::mk_function(vec![Type::mk_int(32)], Type::mk_int(32)), - ) - .unwrap(); + let fn_fib = module.builder().function_def( + "fib".to_string(), + Type::mk_function(vec![Type::mk_i32()], Type::mk_i32()), + )?; let dfg = module.function_data_mut(fn_fib).unwrap().dfg_mut(); - - let entry_block_param = dfg.builder().block_param(Type::mk_int(32)).unwrap(); - let ret_block_param = dfg.builder().block_param(Type::mk_int(32)).unwrap(); - let else_block_param = dfg.builder().block_param(Type::mk_int(32)).unwrap(); - - let entry_block = dfg.builder().block(vec![entry_block_param.into()]).unwrap(); - let ret_block = dfg.builder().block(vec![ret_block_param.into()]).unwrap(); - let else_block = dfg.builder().block(vec![else_block_param]).unwrap(); - - let one0 = dfg.builder().bytes(Type::mk_int(32), vec![1]).unwrap(); - let one1 = dfg.builder().bytes(Type::mk_int(32), vec![1]).unwrap(); - let one2 = dfg.builder().bytes(Type::mk_int(32), vec![1]).unwrap(); - let two0 = dfg.builder().bytes(Type::mk_int(32), vec![2]).unwrap(); - - let icmp = dfg - .builder() - .binary( - BinaryOp::ICmp(ICmpCond::Sle), - entry_block_param, - one0.into(), - ) - .unwrap(); - let br = dfg - .builder() - .branch( - icmp, - ret_block, - else_block, - vec![one1], - vec![entry_block_param], - ) - .unwrap(); - - let sub0 = dfg - .builder() - .binary(BinaryOp::Sub, else_block_param, one2.into()) - .unwrap(); - let sub1 = dfg - .builder() - .binary(BinaryOp::Sub, else_block_param, two0.into()) - .unwrap(); - let call0 = dfg - .builder() - .call(Type::mk_int(32), fn_fib.into(), vec![sub0.into()]) - .unwrap(); - let call1 = dfg - .builder() - .call(Type::mk_int(32), fn_fib.into(), vec![sub1.into()]) - .unwrap(); - let add = dfg - .builder() - .binary(BinaryOp::Add, call0.into(), call1.into()) - .unwrap(); - let jump = dfg.builder().jump(ret_block, vec![add.into()]).unwrap(); - - let ret = dfg.builder().return_(Some(ret_block_param)).unwrap(); - - dfg.assign_block_name(entry_block, "entry".to_string()).ok(); - dfg.assign_block_name(ret_block, "ret".to_string()).ok(); - dfg.assign_block_name(else_block, "else".to_string()).ok(); - - dfg.assign_local_value_name(icmp, "cond".to_string()).ok(); - dfg.assign_local_value_name(ret_block_param, "result".to_string()) - .ok(); + let mut dfg_builder = dfg.builder(); + + let entry_block_param = dfg_builder.block_param(Type::mk_i32())?; + let ret_block_param = dfg_builder.block_param(Type::mk_i32())?; + let else_block_param = dfg_builder.block_param(Type::mk_i32())?; + + let entry_block = dfg_builder.block(vec![entry_block_param])?; + let ret_block = dfg_builder.block(vec![ret_block_param])?; + let else_block = dfg_builder.block(vec![else_block_param])?; + + let one0 = dfg_builder.integer(1)?; + let one1 = dfg_builder.integer(1)?; + let one2 = dfg_builder.integer(1)?; + let two0 = dfg_builder.integer(2)?; + + let icmp = dfg_builder.binary(BinaryOp::ICmp(ICmpCond::Sle), entry_block_param, one0)?; + let br = dfg_builder.branch( + icmp, + ret_block, + else_block, + vec![one1], + vec![entry_block_param], + )?; + + let sub0 = dfg_builder.binary(BinaryOp::Sub, else_block_param, one2)?; + let sub1 = dfg_builder.binary(BinaryOp::Sub, else_block_param, two0)?; + let call0 = dfg_builder.call(Type::mk_i32(), fn_fib.into(), vec![sub0])?; + let call1 = dfg_builder.call(Type::mk_i32(), fn_fib.into(), vec![sub1])?; + let add = dfg_builder.binary(BinaryOp::Add, call0, call1)?; + let jump = dfg_builder.jump(ret_block, vec![add])?; + + let ret = dfg_builder.return_(Some(ret_block_param))?; + + dfg.assign_block_name(entry_block, "entry".to_string())?; + dfg.assign_block_name(ret_block, "ret".to_string())?; + dfg.assign_block_name(else_block, "else".to_string())?; + + dfg.assign_local_value_name(icmp, "cond".to_string())?; + dfg.assign_local_value_name(ret_block_param, "result".to_string())?; let layout = module.function_data_mut(fn_fib).unwrap().layout_mut(); - layout.append_block(entry_block).ok(); - layout.append_block(else_block).ok(); - layout.append_block(ret_block).ok(); + layout.append_block(entry_block)?; + layout.append_block(else_block)?; + layout.append_block(ret_block)?; - layout.append_inst(icmp.into(), entry_block).ok(); - layout.append_inst(br.into(), entry_block).ok(); + layout.append_inst(icmp.into(), entry_block)?; + layout.append_inst(br.into(), entry_block)?; - layout.append_inst(sub0.into(), else_block).ok(); - layout.append_inst(sub1.into(), else_block).ok(); - layout.append_inst(call0.into(), else_block).ok(); - layout.append_inst(call1.into(), else_block).ok(); - layout.append_inst(add.into(), else_block).ok(); - layout.append_inst(jump.into(), else_block).ok(); + layout.append_inst(sub0.into(), else_block)?; + layout.append_inst(sub1.into(), else_block)?; + layout.append_inst(call0.into(), else_block)?; + layout.append_inst(call1.into(), else_block)?; + layout.append_inst(add.into(), else_block)?; + layout.append_inst(jump.into(), else_block)?; - layout.append_inst(ret.into(), ret_block).ok(); + layout.append_inst(ret.into(), ret_block)?; let mut stdout = io::stdout(); let mut printer = Printer::new(&mut stdout); - printer.run(&mut module); + printer.run(&mut module)?; Ok(()) } diff --git a/src/passes/printer.rs b/src/passes/printer.rs index e292b7a..ca70c83 100644 --- a/src/passes/printer.rs +++ b/src/passes/printer.rs @@ -286,7 +286,11 @@ impl GlobalPass for Printer<'_, T> where T: io::Write, { - fn run(&mut self, module: &mut Module) { - self.print_module(module).unwrap(); + type Ok = (); + type Err = io::Error; + + fn run(&mut self, module: &mut Module) -> Result { + self.print_module(module)?; + Ok(()) } }