alduin 0.0.1

WIP: A toy compiler backend
Documentation
use std::collections::HashMap;
use std::fmt::Debug;
use std::fmt::Write;

use crate::compiler::code_space::CodeBuffer;
use crate::compiler::compiled_code::Symbol;
use crate::compiler::compiled_code::{CallSite, CompiledCode};
use crate::compiler::graph::Signature;
use crate::rt::Value;

/// Compiled code object
pub(super) struct X64CompiledCode {
    symbol: Symbol,
    signature: Signature,
    buf: CodeBuffer,
    call_sites: HashMap<usize, Box<dyn CallSite>>,
}

impl X64CompiledCode {
    pub fn new(
        symbol: Symbol,
        signature: Signature,
        buf: CodeBuffer,
        call_sites: HashMap<usize, Box<dyn CallSite>>,
    ) -> Self {
        let code = Self {
            symbol,
            signature,
            buf,
            call_sites,
        };
        // super::rt::register_unwind_info(&code.buf);
        code
    }

    pub(super) fn link_lazy_compilation_trampoline(&self, addr: *const u8) {
        for (_, call_site) in &self.call_sites {
            call_site.patch(addr);
        }
    }
}

impl Debug for X64CompiledCode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "X64CompiledCode[{:?}]", self.signature)
    }
}

impl CompiledCode for X64CompiledCode {
    fn symbol(&self) -> &Symbol {
        &self.symbol
    }

    fn signature(&self) -> &Signature {
        &self.signature
    }

    fn code_buffer(&self) -> &CodeBuffer {
        &self.buf
    }

    fn entry_offset(&self) -> usize {
        0
    }

    fn get_call_site(&self, return_address: *const u8) -> Option<&dyn CallSite> {
        let offset = return_address as usize - self.buf.start() as usize;
        self.call_sites.get(&offset).map(|x| x.as_ref())
    }

    fn link(&self, symbols: &HashMap<Symbol, *const u8>) {
        for (_, call_site) in &self.call_sites {
            let value = symbols[call_site.symbol()];
            call_site.patch(value)
        }
    }

    fn invoke(&self, args: &[Value]) -> Option<Value> {
        let entry = self.entry();
        super::rt::invoke_compiled_method(&self.signature, entry, args)
    }

    fn dump(&self) {
        if !log_enabled!(target: "asm", log::Level::Trace) {
            return;
        }
        const DUMP_RAW_BINARY: bool = false;
        use capstone::prelude::*;
        let cs = Capstone::new()
            .x86()
            .mode(arch::x86::ArchMode::Mode64)
            .syntax(arch::x86::ArchSyntax::Intel)
            .detail(true)
            .build()
            .expect("Failed to create Capstone object");
        let mut log = String::new();
        writeln!(log, "\n{:?}", self).unwrap();
        let code: &[u8] = &self.buf;
        if DUMP_RAW_BINARY {
            for chunk in code.chunks(32) {
                write!(log, "   ").unwrap();
                for byte in chunk {
                    write!(log, " {:02x?}", byte).unwrap();
                }
                writeln!(log).unwrap();
            }
        }
        let start = code.as_ptr() as u64;
        let insns = cs.disasm_all(code, start).expect("Failed to disassemble");
        for i in insns.as_ref() {
            writeln!(log, "  {}", i).unwrap();
        }
        trace!(target: "asm", "{}", log);
    }
}