vermilion-codegen 0.1.0

A code generator for the Vermilion virtual machine.
Documentation
//! A module for compiling Vermilion IR to bytecode.

use crate::ir::{Module, Function, FunctionValue};
use crate::entities::{Symbol};
use crate::instruction::{InstructionData, Opcode};
use std::collections::HashMap;
use vermilion_object::{Artifact};

/// A struct for emitting binary.
pub struct Binemit {

    /// The module to compile from.
    pub module: Module,

    /// The artifact that the compiler is emitting to.
    artifact: Artifact,

    /// Offset for compilation
    offset: usize,

    /// A list of symbols and their IDs.
    symbols: HashMap<String, i64>,

}

impl Binemit {

    /// Creates a new Binemit instance from a module.  This itself
    /// doesn't actually compile the module.
    pub fn new(module: Module) -> Self {
        Binemit {
            module,
            artifact: Artifact::new(),
            offset: 0,
            symbols: HashMap::new()
        }
    }

    /// Gets the byte for an opcode.
    fn opcode_byte(opcode: Opcode) -> u8 {
        // TODO: GET OPCODEW
        match opcode {
            Opcode::Pop => 0x05,
            Opcode::Clone => 0x06,
            Opcode::Clear => 0x07,
            Opcode::Trap => 0x0a,
            Opcode::BooleanAdd => 0x0e,
            Opcode::BooleanSubtract => 0x0f,
            Opcode::ByteAdd => 0x10,
            Opcode::ByteSubtract => 0x11,
            Opcode::ByteMultiply => 0x12,
            Opcode::ByteDivide => 0x13,
            Opcode::ByteRemainder => 0x14,
            Opcode::IntegerAdd => 0x15,
            Opcode::IntegerSubtract => 0x16,
            Opcode::IntegerMultiply => 0x17,
            Opcode::IntegerDivide => 0x18,
            Opcode::IntegerRemainder => 0x19,
            Opcode::FloatAdd => 0x1a,
            Opcode::FloatSubtract => 0x1b,
            Opcode::FloatMultiply => 0x1c,
            Opcode::FloatDivide => 0x1d,
            Opcode::FloatRemainder => 0x1e,
            Opcode::CastBoolean => 0x1f,
            Opcode::CastByte => 0x20,
            Opcode::CastInteger => 0x21,
            Opcode::CastFloat => 0x22,
            Opcode::NegateBoolean => 0x23,
            Opcode::NegateByte => 0x0, // unsupported instruction
            Opcode::NegateInteger => 0x24,
            Opcode::NegateFloat => 0x25,
            Opcode::Load => 0x26,
            Opcode::Store => 0x27,
            Opcode::Realloc => 0x28,
            Opcode::HeapSize => 0x29,
            Opcode::Alloc => 0x2a,
            Opcode::ReallocSection => 0x2b,
            Opcode::SectionAddr => 0x2c,
            Opcode::SectionShiftLeft => 0x2d,
            Opcode::SectionShiftRight => 0x2e,
            Opcode::Free => 0x2f,
            Opcode::FreeAndShift => 0x30,
            Opcode::Branch => 0x31,
            Opcode::BranchIfZero => 0x32,
            Opcode::BranchIfNotZero => 0x33,
            Opcode::Equal => 0x34,
            Opcode::GreaterThan => 0x35,
            Opcode::LessThan => 0x36,
            Opcode::GreaterThanOrEqual => 0x37,
            Opcode::LessThanOrEqual => 0x38,
            Opcode::Call => 0x39,
            Opcode::Return => 0x3a
        }
    }

    fn get_instruction_size(&self, func: &Function, inst: &InstructionData) -> i64 {
        let mut size = 0;
        for item in &inst.args {
            size += self.get_size(func, func.values.get(item.0 as usize).unwrap());
        }
        size += 1;

        size
    }

    /// Recursively checks the size of a value.
    pub fn get_size(&self, func: &Function, value: &FunctionValue) -> i64 {
        let size: i64;

        match value {
            FunctionValue::Block(_) => {
                size = 9;
            },
            FunctionValue::Boolean(_) => {
                size = 2;
            },
            FunctionValue::Byte(_) => {
                size = 2;
            },
            FunctionValue::Integer(_) => {
                size = 9;
            },
            FunctionValue::Float(_) => {
                size = 9;
            },
            FunctionValue::Symbol(_) => {
                size = 9;
            },
            FunctionValue::Instruction(data) => {
                size = self.get_instruction_size(func, data);
            }
        }

        size
    }

    fn compile_instruction(&self, func: &Function, blocks: &Vec<usize>, inst: &InstructionData) -> Vec<u8> {
        let mut val = Vec::new();

        for arg in &inst.args {
            val.append(&mut self.compile_value(func, blocks, &func.values[arg.0 as usize]));
        }

        val.push(Binemit::opcode_byte(inst.opcode.clone()));

        val
    }

    /// Compiles a value.
    fn compile_value(&self, func: &Function, blocks: &Vec<usize>, value: &FunctionValue) -> Vec<u8> {
        let mut val = Vec::new();
        match value {
            FunctionValue::Block(bl) => {
                //size = 9;
                // push integer
                val.push(3);
                
                let mut addr = (bl.0 as i64).to_le_bytes().to_vec();
                val.append(&mut addr);
            },
            FunctionValue::Boolean(r) => {
                val.push(1);
                if *r {
                    val.push(1);
                } else {
                    val.push(0);
                }
                //val.push(r ? 1 : 0);
            },
            FunctionValue::Byte(r) => {
                val.push(2);
                val.push(*r);
            },
            FunctionValue::Integer(r) => {
                val.push(3);
                
                let mut addr = r.to_le_bytes().to_vec();
                val.append(&mut addr);
            },
            FunctionValue::Float(r) => {
                val.push(4);
                
                let mut addr = r.to_le_bytes().to_vec();
                val.append(&mut addr);
            },
            FunctionValue::Symbol(r) => {
                val.push(3);
                
                let mut addr = self.symbols.get(r).unwrap().to_le_bytes().to_vec();
                val.append(&mut addr);
            },
            FunctionValue::Instruction(data) => {
                val = self.compile_instruction(func, blocks, &data);
            }
        }
        val
    }

    /// Calculates the addresses of all blocks in a function
    fn calc_addresses(&self, func: &Function) -> Vec<usize> {
        let mut blocks = Vec::new();
        let mut end = self.offset;

        for block in &func.blocks {
            let insts = &block.instructions;
            let mut size = 0;

            for inst in insts {
                size += self.get_instruction_size(&func, &inst);
            }

            blocks.push(end);
            end += size as usize;
        }

        blocks
    }

    /// Compiles a single function to bytecode.
    fn compile_function(&self, func: &Function) -> Vec<u8> {
        let mut bytes = Vec::new();

        let blocks = self.calc_addresses(&func);

        for block in &func.blocks {
            for inst in &block.instructions {
                bytes.append(&mut self.compile_instruction(func, &blocks, &inst))
            }
        }

        bytes
    }

    /// Compiles the module to a list of bytes.
    pub fn emit(&mut self) -> Artifact {
        let mut artifact = self.artifact.clone();
        let mut bytes = Vec::new();

        // Resolve symbols
        let mut id = 0;
        for symbol in &self.module.symbols {
            self.symbols.insert(symbol.0.to_string(), id);
            id += 1;
        }

        for symbol in &self.module.symbols {
            match symbol.1 {
                Symbol::Local(func) => {
                    artifact.symbols.insert(self.symbols[&symbol.0.clone()], vermilion_object::Symbol::Local(symbol.0.to_string(), self.offset));
                    bytes.append(&mut self.compile_function(&func));
                },
                Symbol::External => {
                    artifact.declare_function(self.symbols[&symbol.0.clone()], symbol.0.to_string());
                }
            }
        }

        artifact.program = bytes;

        artifact
    }

}