Add base structure for dev compiler backend

Currently we only generate a single static function, but it is probably
loaded and run with jit. This is the base to start generating proper
code.
This commit is contained in:
Brendan Hansknecht 2020-11-14 18:43:02 -08:00
parent 2204ee82d5
commit 86c3c0a409
10 changed files with 1341 additions and 20 deletions

View file

@ -0,0 +1,65 @@
#![warn(clippy::all, clippy::dbg_macro)]
// I'm skeptical that clippy:large_enum_variant is a good lint to have globally enabled.
//
// It warns about a performance problem where the only quick remediation is
// to allocate more on the heap, which has lots of tradeoffs - including making it
// long-term unclear which allocations *need* to happen for compilation's sake
// (e.g. recursive structures) versus those which were only added to appease clippy.
//
// Effectively optimizing data struture memory layout isn't a quick fix,
// and encouraging shortcuts here creates bad incentives. I would rather temporarily
// re-enable this when working on performance optimizations than have it block PRs.
#![allow(clippy::large_enum_variant)]
use bumpalo::Bump;
use object::write::Object;
use roc_collections::all::{MutMap, MutSet};
use roc_module::symbol::{Interns, Symbol};
use roc_mono::ir::Proc;
use roc_mono::layout::Layout;
use target_lexicon::{BinaryFormat, Triple};
pub mod elf;
pub mod run_roc;
pub mod x86_64;
pub struct Env<'a> {
pub arena: &'a Bump,
pub interns: Interns,
pub exposed_to_host: MutSet<Symbol>,
}
/// build_module is the high level builder/delegator.
/// It takes the request to build a module and output the object file for the module.
pub fn build_module<'a>(
env: &'a Env,
target: &Triple,
procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>,
) -> Result<Object, String> {
match target.binary_format {
BinaryFormat::Elf => elf::build_module(env, target, procedures),
x => Err(format! {
"the binary format, {:?}, is not yet implemented",
x}),
}
}
trait Backend {
/// new creates a new backend that will output to the specific Object.
fn new() -> Self;
/// reset resets any registers or other values that may be occupied at the end of a procedure.
fn reset(&mut self);
/// build_proc creates a procedure and outputs it to the wrapped object writer.
/// This will need to return the list of relocations because they have to be added differently based on file format.
/// Also, assembly will of course be generated by individual calls on backend like may setup_stack.
fn build_proc<'a>(&mut self, env: &'a Env, _proc: Proc<'a>) -> &'a [u8] {
let mut out = bumpalo::vec![in env.arena; 0x55, 0x48, 0x89, 0xE5];
let body = [0xb8, 0x06, 0x00, 0x00, 0x00];
out.extend(body.iter());
out.push(0x5D);
out.push(0xC3);
out.into_bump_slice()
}
}