diff --git a/examples/fib/fib.rs b/examples/fib/fib.rs index 44e960c..7e0d5ad 100644 --- a/examples/fib/fib.rs +++ b/examples/fib/fib.rs @@ -1,4 +1,4 @@ #[no_mangle] -pub fn fib(a: u32, b: u32, n: u32) -> u32 { +pub fn fib(a: u128, b: u128, n: u128) -> u128 { if n == 0 { a } else { fib(b, a + b, n - 1) } } diff --git a/src/builder/function/binary.rs b/src/builder/function/binary.rs index 8d8a0be..7cad714 100644 --- a/src/builder/function/binary.rs +++ b/src/builder/function/binary.rs @@ -1,7 +1,7 @@ +use inkwell::basic_block::BasicBlock; use inkwell::values::{AnyValue, BasicValueEnum, InstructionValue, IntValue}; use super::CairoFunctionBuilder; -use crate::builder::get_name; impl<'ctx> CairoFunctionBuilder<'ctx> { fn extract_const_int_value(val: IntValue) -> String { @@ -27,7 +27,7 @@ impl<'ctx> CairoFunctionBuilder<'ctx> { &mut self, instruction: &InstructionValue<'ctx>, operator: &str, - is_loop: bool, + bb: &BasicBlock<'ctx>, ) -> String { // Get th left operand. let left = unsafe { @@ -49,33 +49,35 @@ impl<'ctx> CairoFunctionBuilder<'ctx> { let instr_name = { let basic_val: BasicValueEnum = instruction.as_any_value_enum().try_into().unwrap(); - self.variables - .get(&basic_val) - .cloned() - .unwrap_or_else(|| get_name(instruction.get_name().unwrap_or_default()).unwrap_or("result".to_owned())) + // Try to get the variable from our variables mapping. If not found create it and insert it in the + // mmaping. + self.variables.get(&basic_val).cloned().unwrap_or_else(|| { + let instr_name = self.get_name(instruction.get_name().unwrap_or_default()); + if let Ok(basic_value_enum) = instruction.as_any_value_enum().try_into() { + // Save the result variable in our mapping to be able to use later. + self.variables.insert(basic_value_enum, instr_name.clone()); + } + format!("let {}", instr_name) + }) }; - if let Ok(basic_value_enum) = instruction.as_any_value_enum().try_into() { - // Save the result variable in our mapping to be able to use later. - self.variables.insert(basic_value_enum, instr_name.clone()); - } + let annoying_phis = self.bblock_variables.get(bb).cloned().unwrap_or_default(); // The operand is either a variable or a constant so either we get it from our mapping or it's // unnamed and it's a const literal. - let left_name = get_name(left.get_name()).unwrap_or_else(|| { - if left.into_int_value().is_const() { - Self::extract_const_int_value(left.into_int_value()) - } else { - unreachable!("Left operand should either be a variable or a constant") - } - }); - let right_name = get_name(right.get_name()).unwrap_or_else(|| { - if right.into_int_value().is_const() { - Self::extract_const_int_value(right.into_int_value()) - } else { - unreachable!("Right should either be a variable or a constant") - } - }); + // TODO(Lucas): a variable can surely be in the variables mapping, try to get it from there as well. + let left_name = if left.into_int_value().is_const() { + Self::extract_const_int_value(left.into_int_value()) + } else { + // If it's not a const might be in our annoying phi mapping. + annoying_phis.get(&left).cloned().unwrap_or_else(|| self.get_name(left.get_name())) + }; + let right_name = if right.into_int_value().is_const() { + Self::extract_const_int_value(right.into_int_value()) + } else { + // If it's not a const might be in our annoying phi mapping. + annoying_phis.get(&right).cloned().unwrap_or_else(|| self.get_name(right.get_name())) + }; - format!("{}{} = {} {} {};", if is_loop { "" } else { "let " }, instr_name, left_name, operator, right_name) + format!("{} = {} {} {};", instr_name, left_name, operator, right_name) } } diff --git a/src/builder/function/branch.rs b/src/builder/function/branch.rs index 95fa865..6690b29 100644 --- a/src/builder/function/branch.rs +++ b/src/builder/function/branch.rs @@ -1,19 +1,47 @@ +use inkwell::basic_block::BasicBlock; use inkwell::values::InstructionValue; use super::CairoFunctionBuilder; impl<'ctx> CairoFunctionBuilder<'ctx> { - pub fn process_branch(&mut self, instruction: &InstructionValue<'ctx>, is_loop: &bool) -> String { - let cond = instruction.get_operand(0).unwrap().left().unwrap(); - // If we're in a loop this is the exit condition so we break. - if *is_loop { - format!("if {}\n{{break;}}", self.variables.get(&cond).unwrap()) + /// Process a branch instruction. If there is only 1 operand without condition it'll translate + /// the basic block it jumps to and will move on to the next basic block. + /// If there is an if/else it will process all the basic blocks that are involved. + pub fn process_branch( + &mut self, + instruction: &InstructionValue<'ctx>, + bb: &BasicBlock<'ctx>, + is_loop: &bool, + is_else: &bool, + ) -> String { + // Get all the annoying variables that require to be declared in a bigger scope and will update + // their value. + self.bblock_variables.get(bb).cloned().unwrap_or_default().into_values().for_each(|val| { + self.push_body_line(format!("{} = {};", val.trim_end_matches("_temp"), val)); + }); + self.set_basic_block_booleans(bb); + // Case were there is an inconditionnal jump. + if instruction.get_num_operands() == 1 { + self.process_basic_block(&instruction.get_operand(0).unwrap().right().unwrap()); + "".to_owned() } else { - // else it means that we're in a if/else case and the first block is the if the 2nd is the else. - self.if_blocks.insert(instruction.get_operand(1).unwrap().right().unwrap(), cond); - self.else_blocks.insert(instruction.get_operand(2).unwrap().right().unwrap()); + // There is a condition could either be a loop break or if/else + let cond = instruction.get_operand(0).unwrap().left().unwrap(); + // If we're in a loop this is the exit condition so we break. + if *is_loop { + let res = format!("if {}\n{{break;}}", self.variables.get(&cond).unwrap()); - "".to_owned() + res + } else { + self.close_scopes(bb, is_else, is_loop); + // else it means that we're in a if/else case and the first block is the if the 2nd is the else. + self.if_blocks.insert(instruction.get_operand(1).unwrap().right().unwrap(), cond); + self.process_basic_block(&instruction.get_operand(1).unwrap().right().unwrap()); + self.else_blocks.insert(instruction.get_operand(2).unwrap().right().unwrap()); + self.process_basic_block(&instruction.get_operand(2).unwrap().right().unwrap()); + + "".to_owned() + } } } } diff --git a/src/builder/function/mod.rs b/src/builder/function/mod.rs index 386376d..3fd61d8 100644 --- a/src/builder/function/mod.rs +++ b/src/builder/function/mod.rs @@ -1,12 +1,15 @@ use std::collections::{HashMap, HashSet}; +use std::ffi::CStr; use std::fmt::Display; use inkwell::basic_block::BasicBlock; -use inkwell::values::BasicValueEnum; +use inkwell::values::{BasicValueEnum, InstructionOpcode}; +use inkwell::IntPredicate; use petgraph::graph::{DiGraph, NodeIndex}; pub mod binary; pub mod branch; +pub mod extend; pub mod phi; pub mod preprocessing; pub mod types; @@ -19,6 +22,7 @@ pub struct CairoFunctionBuilder<'ctx> { pub(crate) node_id_from_name: HashMap, NodeIndex>, pub(crate) function: CairoFunction, pub(crate) phis_bblock: HashSet>, + pub(crate) bblock_variables: HashMap, HashMap, String>>, pub(crate) if_blocks: HashMap, BasicValueEnum<'ctx>>, pub(crate) else_blocks: HashSet>, pub(crate) return_block: Option>, @@ -40,6 +44,73 @@ impl<'ctx> CairoFunctionBuilder<'ctx> { pub fn push_body_line(&mut self, line: String) { self.function.body.push_line(line) } + pub fn get_name(&self, name: &CStr) -> String { + (!name.is_empty()) + .then(|| name.to_str().expect("Variable name for binary op should be uft-8").replace('.', "_")) + .unwrap_or_else(|| format!("var{}", self.variables.keys().count())) + } + + /// Set all the basic block booleans to the correct value. This should be used at the end of a + /// basic block before jump to know from which basic block we're coming from at runtime. + pub fn set_basic_block_booleans(&mut self, bb: &BasicBlock<'ctx>) { + // If we're not in the last basic block set all the booleans to the right value to know what basic + // block we were in so we can process the phi instruction can work correctly + if self.return_block.is_some_and(|bblock| bblock != *bb) || self.return_block.is_none() { + for bblock in self.phis_bblock.iter() { + // If we were in this basic block + if self.get_name(bblock.get_name()) == self.get_name(bb.get_name()) { + let code_line = format!("is_from_{} = true;", self.get_name(bblock.get_name()),); + self.function.body.push_line(code_line); + } else { + // if we were not in this basic block + let code_line = format!("is_from_{} = false;", self.get_name(bblock.get_name()),); + self.function.body.push_line(code_line); + } + } + } + } + /// Process a basic block and convert it to cairo. It will call itself recursively through the + /// [CairoFunctionBuilder::process_branch] function. + pub fn process_basic_block(&mut self, bb: &BasicBlock<'ctx>) { + // Boolean that let's us know if we need to wrap our basic block translation with a loop { bbcode }; + let is_loop = self.bb_loop.contains(bb); + // Is this block the else clause of an if/else + let is_else = self.else_blocks.contains(bb); + // TODO(Lucas): in preprocess function declare all the variables that will be in subscopes and then + // stop worrying about it + only use is_subscope + let _is_subscope = is_loop || is_else || self.if_blocks.contains_key(bb); + + // Prepare for loops/if/else + self.prepare_new_scopes(bb, &is_else, &is_loop); + + // Iterate over each instruction of the basic block. 1 instruction == 1 LLVM code line + for instruction in bb.get_instructions() { + // Get the opcode of the instruction + let code_line = match instruction.get_opcode() { + InstructionOpcode::Add => self.process_binary_int_op(&instruction, "+", bb), + InstructionOpcode::Sub => self.process_binary_int_op(&instruction, "-", bb), + InstructionOpcode::Return => self.process_return(&instruction), + InstructionOpcode::ICmp => { + // we just matched on ICmp so it will never fail + match instruction.get_icmp_predicate().unwrap() { + IntPredicate::EQ => self.process_binary_int_op(&instruction, "==", bb), + IntPredicate::NE => self.process_binary_int_op(&instruction, "!=", bb), + IntPredicate::ULT => self.process_binary_int_op(&instruction, "<", bb), + _ => "".to_owned(), + } + } + InstructionOpcode::Br => self.process_branch(&instruction, bb, &is_loop, &is_else), + InstructionOpcode::ZExt => self.process_zext(&instruction, &is_loop), + InstructionOpcode::Phi => self.process_phi(&instruction, bb), + _ => "".to_owned(), + }; + self.push_body_line(code_line); + if is_loop && instruction.get_opcode() == InstructionOpcode::Br { + self.close_scopes(bb, &is_else, &is_loop); + } + // Add the line to the function body + } + } } #[derive(Default, Clone, PartialEq, Debug)] diff --git a/src/builder/function/phi.rs b/src/builder/function/phi.rs index 2314f16..da4ec69 100644 --- a/src/builder/function/phi.rs +++ b/src/builder/function/phi.rs @@ -1,36 +1,52 @@ -use inkwell::values::{AsValueRef, InstructionValue, PhiValue}; +use inkwell::basic_block::BasicBlock; +use inkwell::values::{AsValueRef, BasicValueEnum, InstructionValue, PhiValue}; use super::CairoFunctionBuilder; -use crate::builder::get_name; impl<'ctx> CairoFunctionBuilder<'ctx> { - pub fn process_phi(&mut self, instruction: &InstructionValue<'ctx>, is_loop: &bool) -> String { + pub fn process_phi(&mut self, instruction: &InstructionValue<'ctx>, bb: &BasicBlock<'ctx>) -> String { + let annoying_phis = self.bblock_variables.get(bb).cloned().unwrap_or_default(); let phi = unsafe { PhiValue::new(instruction.as_value_ref()) }; // name of the result variable - let phi_name = get_name(phi.get_name()).unwrap(); // variable to store the result in + let phi_name = annoying_phis + .get(unsafe { &BasicValueEnum::new(instruction.as_value_ref()) }) + .cloned() + .unwrap_or_else(|| { + let name = self.get_name(phi.get_name()); + // if it was not in the mapping insert it. In theory we could insert it in any case but we don't + // want to do that it would poisin the regular variable mapping with the annoying phis and would + // mess everything up + self.variables.insert(phi.as_basic_value(), name.clone()); + name + }); // variable to store the result in + // Incomming values (basic block + variable to set the value to) let first = phi.get_incoming(0).unwrap(); // Name of the variable we should set the value to. - let left_var = get_name(first.0.get_name()).unwrap(); // phi left variable + let left_var = self.variables.get(&first.0).cloned().unwrap_or_else(|| { + let name = self.get_name(first.0.get_name()); + self.variables.insert(first.0, name.clone()); + name + }); // phi right variable // Incomming values (basic block + variable to set the value to) let second = phi.get_incoming(1).unwrap(); // Name of the variable we should set the value to. - let right_var = get_name(second.0.get_name()).unwrap(); // phi right variable - - self.variables.insert(first.0, left_var.clone()); - self.variables.insert(second.0, right_var.clone()); - self.variables.insert(phi.as_basic_value(), right_var.clone()); + let right_var = self.variables.get(&second.0).cloned().unwrap_or_else(|| { + let name = self.get_name(second.0.get_name()); + self.variables.insert(second.0, name.clone()); + name + }); // phi right variable // If we're in a subscope we don't need the `let` because we declared the variable above the scope. format!( - "{}{} = if is_from_{} {{ {} }} else if is_from_{} {{ {} }} else {{ panic!(\"There is a bug in the \ - compiler please report it\")}};", - if *is_loop { "" } else { "let " }, + "let {} = if is_from_{} {{ {} }} else if is_from_{} {{ {} }} else {{ panic!(\"There is a bug in the \ + compiler at var {} please report it\")}};", phi_name, - get_name(first.1.get_name()).unwrap(), // phi left basic block + self.get_name(first.1.get_name()), // phi left basic block left_var, - get_name(second.1.get_name()).unwrap(), // phi right basic block - right_var + self.get_name(second.1.get_name()), // phi right basic block + right_var, + phi_name ) } } diff --git a/src/builder/function/preprocessing.rs b/src/builder/function/preprocessing.rs index f126844..71c0056 100644 --- a/src/builder/function/preprocessing.rs +++ b/src/builder/function/preprocessing.rs @@ -1,11 +1,10 @@ -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use inkwell::basic_block::BasicBlock; use inkwell::values::{AnyValue, AsValueRef, BasicValueEnum, FunctionValue, InstructionOpcode, PhiValue}; use petgraph::algo::{has_path_connecting, tarjan_scc}; use super::CairoFunctionBuilder; -use crate::builder::get_name; impl<'ctx> CairoFunctionBuilder<'ctx> { /// Construct a graph of basic blocks and detects loops. It will detect if bb1 jumps to bb2 @@ -20,6 +19,16 @@ impl<'ctx> CairoFunctionBuilder<'ctx> { } for bblock in function.get_basic_block_iter() { + // Those 2 helper variables will help detect if a phi depends on a previous phi because this + // amazingly well though language can have blocks like that: + // %res1 = phi i128 [ %var1 , %bb2 ], [ %var2, %start ] + // %res2 = phi i128 [ %res1, %bb2 ], [ %var3, %start ] + // And yes you guessed it it doesn't want the latest value of res1 but the value from the last time + // we executed this block. Well done Chris + // So we need to detect those to have temp variables to store the new value until we reach the end + // of the BB and then we'll update variables that live in a greater scope. + let mut bblock_phis_inc: HashSet = HashSet::default(); + let mut bblock_phis: HashSet = HashSet::default(); for instr in bblock.get_instructions() { match instr.get_opcode() { InstructionOpcode::Br => { @@ -33,25 +42,42 @@ impl<'ctx> CairoFunctionBuilder<'ctx> { // stuff // we'd add a link from bb1 to bb2 and from bb1 to bb3 let bb_index = *self.node_id_from_name.get(&bblock).unwrap(); - if let Some(target) = instr.get_operand(1).unwrap().right() { - let target_index = *self.node_id_from_name.get(&target).unwrap(); - self.bb_graph.add_edge(bb_index, target_index, ()); - } - if let Some(target) = instr.get_operand(2).unwrap().right() { - let target_index = *self.node_id_from_name.get(&target).unwrap(); - self.bb_graph.add_edge(bb_index, target_index, ()); + for operand in instr.get_operands().flatten() { + if let Some(target) = operand.right() { + let target_index = *self.node_id_from_name.get(&target).unwrap(); + self.bb_graph.add_edge(bb_index, target_index, ()); + } } } InstructionOpcode::Phi => { // Get the phis incomming basic blocks because we'll add booleans to track from which block // we're comming from as this doesn't exist in cairo. - let dest1 = unsafe { PhiValue::new(instr.as_value_ref()).get_incoming(0).unwrap().1 }; - let dest2 = unsafe { PhiValue::new(instr.as_value_ref()).get_incoming(1).unwrap().1 }; - self.phis_bblock.extend([dest1, dest2]); + let inc1 = unsafe { PhiValue::new(instr.as_value_ref()).get_incoming(0).unwrap() }; + let inc2 = unsafe { PhiValue::new(instr.as_value_ref()).get_incoming(1).unwrap() }; + bblock_phis_inc.extend([inc1.0, inc2.0]); + bblock_phis.insert(unsafe { BasicValueEnum::new(instr.as_value_ref()) }); + + self.phis_bblock.extend([inc1.1, inc2.1]); } _ => (), }; } + // Get the variables that are referenced by a phi in the bb they're declared and add them in the + // annoying phi hashmap with a suffix so we can create the correct variable in the previous scope + // and update it. + bblock_phis.intersection(&bblock_phis_inc).for_each(|annoying_phi| { + let phi_name = format!("{}_temp", self.get_name(annoying_phi.get_name())); + self.bblock_variables + .entry(bblock) + .and_modify(|hm| { + hm.insert(*annoying_phi, phi_name.clone()); + }) + .or_insert_with(|| { + let mut nhm = HashMap::new(); + nhm.insert(*annoying_phi, phi_name); + nhm + }); + }) } // Detect the strongly connected components (strongly connected basic blocks == loops) self.bb_loop = tarjan_scc(&self.bb_graph) @@ -91,6 +117,22 @@ impl<'ctx> CairoFunctionBuilder<'ctx> { } } + /// If we were in a loop close it. Will also close the scope if we were in an else. Also close + /// if this is the return basic block. + pub fn close_scopes(&mut self, bb: &BasicBlock<'ctx>, is_else: &bool, is_loop: &bool) { + // If we're in an else close it or if we're in the return block close it. + // TODO(Lucas): Fix that it's 100% wrong as the return block isn't always the last block. + if self.return_block.is_some_and(|bblock| &bblock != bb) && *is_else + || self.return_block.is_some_and(|bblock| &bblock == bb) + { + self.push_body_line("}".to_string()); + } + // If we were in a loop, close it + if *is_loop { + self.push_body_line("};".to_string()); + } + } + /// Create variables outside of the new scope (if/else/loop) so we can still access the value /// when we're out of it or at the next iteration. pub fn prepare_new_scopes(&mut self, bb: &BasicBlock<'ctx>, is_else: &bool, is_loop: &bool) { @@ -107,8 +149,7 @@ impl<'ctx> CairoFunctionBuilder<'ctx> { && instruction.get_opcode() != InstructionOpcode::Return { // Get the variable name, if it's unnamed generate a var{index} string. - let res_name = get_name(instruction.get_name().unwrap()) - .unwrap_or_else(|| format!("var{}", self.variables.keys().count())); + let res_name = self.get_name(instruction.get_name().unwrap_or_default()); // Get the type of the variable because we'll add it to the definition to get more safety. let ty = instruction.get_type().print_to_string().to_string(); // i1 are 1 bit integers meaning that they can only be {0, 1} they represent booleans. LLVM can diff --git a/src/builder/function/types.rs b/src/builder/function/types.rs index 4b1e5a9..4d7561b 100644 --- a/src/builder/function/types.rs +++ b/src/builder/function/types.rs @@ -1,7 +1,6 @@ use inkwell::values::{FunctionValue, InstructionValue}; use super::{CairoFunctionBuilder, CairoFunctionSignature, CairoParameter}; -use crate::builder::get_name; impl<'ctx> CairoFunctionBuilder<'ctx> { /// Translate the LLVM function signature into a Cairo function signature. @@ -22,11 +21,11 @@ impl<'ctx> CairoFunctionBuilder<'ctx> { fn_id: usize, ) -> CairoFunctionSignature { // Get the function name and if it's empty call it "function{fn_id}" - let name = get_name(function.get_name()).unwrap_or(format!("function{fn_id}")); + let name = function.get_name().to_str().map(|val| val.to_string()).unwrap_or(format!("function{fn_id}")); let mut parameters = Vec::::with_capacity(function.count_params() as usize); // Extract each parameter and its type. - function.get_param_iter().enumerate().for_each(|(index, param)| { - let param_name = get_name(param.get_name()).unwrap_or(index.to_string()); + function.get_param_iter().for_each(|param| { + let param_name = self.get_name(param.get_name()); let param_type = param.get_type().print_to_string().to_string(); self.variables.insert(param, param_name.clone()); parameters.push(CairoParameter { name: param_name, ty: param_type }); diff --git a/src/builder/mod.rs b/src/builder/mod.rs index 7388d4e..197ebf1 100644 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -1,10 +1,8 @@ -use std::collections::{HashMap}; -use std::ffi::CStr; +use std::collections::HashMap; use std::fmt::Display; use function::{CairoFunction, CairoFunctionBuilder}; -use inkwell::values::{FunctionValue, InstructionOpcode}; -use inkwell::IntPredicate; +use inkwell::values::FunctionValue; pub mod function; @@ -40,13 +38,6 @@ impl Display for CairoFunctions { } } -fn get_name(name: &CStr) -> Option { - (!name.is_empty()).then(|| { - let mut val = name.to_str().expect("Variable name for binary op should be uft-8").to_owned(); - val.retain(|c| c.is_alphanumeric() || c == '_'); - val - }) -} impl<'ctx> CairoBuilder<'ctx> { /// Translates an LLVM function to a cairo function and return it as a string. pub fn translate_function(&mut self, func: &FunctionValue) -> CairoFunction { @@ -70,81 +61,15 @@ impl<'ctx> CairoBuilder<'ctx> { // We'll create a mutable boolean that we'll initiate to false to know from which basic block we're // coming from as once again basic blocks don't exist in cairo. for bb in function_builder.phis_bblock.iter() { - let code_line = - format!("let mut is_from_{} = false;", get_name(bb.get_name()).expect("Basic block should be named"),); + let code_line = format!("let mut is_from_{} = false;", function_builder.get_name(bb.get_name()),); function_builder.function.body.push_line(code_line); } - // Gets the index of the last basic block. - let bb_num = func.count_basic_blocks() - 1; - // Iterate over the basic blocks of the function. Each function is composed of one or more basic - // blocks. Basic blocks have one entry and one exit. If there was no return - // instruction in the bb you'll need to jump to another bb at the end. For more information - // read this https://llvm.org/doxygen/group__LLVMCCoreValueBasicBlock.html#details - for (index, bb) in func.get_basic_block_iter().enumerate() { - // Boolean that let's us know if we need to wrap our basic block translation with a loop { bbcode }; - let is_loop = function_builder.bb_loop.contains(&bb); - // Is this block the else clause of an if/else - let is_else = function_builder.else_blocks.contains(&bb); - // TODO(Lucas): in preprocess function declare all the variables that will be in subscopes and then - // stop worrying about it + only use is_subscope - let _is_subscope = is_loop || is_else || function_builder.if_blocks.contains_key(&bb); - - // Prepare for loops/if/else - function_builder.prepare_new_scopes(&bb, &is_else, &is_loop); - - // Iterate over each instruction of the basic block. 1 instruction == 1 LLVM code line - for instruction in bb.get_instructions() { - // Get the opcode of the instruction - let code_line = match instruction.get_opcode() { - InstructionOpcode::Add => function_builder.process_binary_int_op(&instruction, "+", is_loop), - InstructionOpcode::Sub => function_builder.process_binary_int_op(&instruction, "-", is_loop), - InstructionOpcode::Return => function_builder.process_return(&instruction), - InstructionOpcode::ICmp => { - // we just matched on ICmp so it will never fail - match instruction.get_icmp_predicate().unwrap() { - IntPredicate::EQ => function_builder.process_binary_int_op(&instruction, "==", is_loop), - IntPredicate::NE => function_builder.process_binary_int_op(&instruction, "!=", is_loop), - _ => "".to_owned(), - } - } - InstructionOpcode::Br => function_builder.process_branch(&instruction, &is_loop), - InstructionOpcode::Phi => function_builder.process_phi(&instruction, &is_loop), - _ => "".to_owned(), - }; - // Add the line to the function body - function_builder.push_body_line(code_line); - } - // If we're in an else close it, if we're in the return block don't do anything because we didn't - // open the else. - if is_else && function_builder.return_block.unwrap() != bb { - function_builder.push_body_line("}".to_string()); - } - // If we're not in the last basic block set all the booleans to the right value to know what basic - // block we were in so we can process the phi instruction can work correctly - if index as u32 != bb_num { - for bblock in function_builder.phis_bblock.iter() { - // If we were in this basic block - if get_name(bblock.get_name()).unwrap() == get_name(bb.get_name()).unwrap() { - let code_line = format!( - "is_from_{} = true;", - get_name(bblock.get_name()).expect("Basic block should be named"), - ); - function_builder.function.body.push_line(code_line); - } else { - // if we were not in this basic block - let code_line = format!( - "is_from_{} = false;", - get_name(bblock.get_name()).expect("Basic block should be named"), - ); - function_builder.function.body.push_line(code_line); - } - } - } - // If we were in a loop, close it - if is_loop { - function_builder.push_body_line("};".to_string()); - } - } + // get the first basic block and process it. As they are all linked together we'll process all of + // them recursively by calling `process_basic_block` before jumping to another block. Each function + // is composed of one or more basic blocks. Basic blocks have one entry and one exit. If + // there was no return instruction in the bb you'll need to jump to another bb at the end. + // For more information read this https://llvm.org/doxygen/group__LLVMCCoreValueBasicBlock.html#details + function_builder.process_basic_block(&func.get_first_basic_block().unwrap()); function_builder.function } }