Skip to content

Commit

Permalink
Merge pull request #14 from binary-banter/asm_blocks
Browse files Browse the repository at this point in the history
Asm blocks (... and a lot more oops)
  • Loading branch information
JonathanBrouwer authored Dec 1, 2023
2 parents 8c9757c + d19f748 commit 2dc6387
Show file tree
Hide file tree
Showing 84 changed files with 2,274 additions and 1,205 deletions.
47 changes: 41 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# How to Use

```
Usage: rust_compiler_construction.exe [OPTIONS] [INPUT]
Expand All @@ -11,50 +12,84 @@ Options:
-V, --version Print version
```

# Examples
These examples demonstrate how to build and run the compiler using Cargo.
Note that our language returns its expression as the exit code, which is why we use `echo $?`.

These examples demonstrate how to build and run the compiler using Cargo.

Run with input from stdin:

```sh
echo "(+ 1 2)" | cargo run && ./output ; echo $?
echo "fn main() { print(42); }" | cargo run && ./output
```

Run with specified input path, without specifying an output path:

```sh
cargo run -- example.jj && ./example ; echo $?
cargo run -- example.jj && ./example
```

Run with specified input path and specified output path:

```sh
cargo run -- input.jj -o output && ./output ; echo $?
cargo run -- input.jj -o output && ./output
```

# Language Features

* Literals
* Var
* BinaryOp
* UnaryOp
* Let
* If
* Functions
* Return
* Loop
* Break
* Continue
* While
* Sequences
* Structs

# AoC Wishlist for Christmas

* Fixing that not very nice bug.
* Pass raw strings for testing (allows testing AoC problems).
* Compile + run

# Fixes

* [ ] Updated README, with 3 new colors!
* [ ] Add documentation where necessary.
* [x] Improve error handling for parsing pass.
* [x] Improve error handling for type checking pass.
* [ ] Make errors prettier.
* [ ] Improve algorithm for colouring the interference graph.
* [ ] Add read and write functionality to the bencher to update locally.
* [x] Add read and write functionality to the bencher to update locally.
* [x] Lots, and lots, of refactoring!
* [x] Write test input in comments.

# Upcoming Language Features

* [x] Type inference.
* [x] Implement comments in code.
* [ ] Algebraic Data Types.
* [x] Structs.
* [ ] Enums.
* [ ] First-class functions.
* [ ] Constants.

# Upcoming Optimizations

* [ ] Dead code.
* [ ] Constant folding.
* [ ] Improve prologue and epilogue.
* [ ] Tail calls.
* [ ] And probably more...

# Lofty Goals

* [ ] Make the compiler suggest hints.
* [ ] Nested definitions.
* [ ] Match statements.
Expand Down
8 changes: 3 additions & 5 deletions compiler/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::collections::HashMap;
use std::vec::IntoIter;

pub trait IO {
fn read(&mut self) -> TLit;
fn read(&mut self) -> Option<TLit>;
fn print(&mut self, v: TLit);
}

Expand All @@ -29,10 +29,8 @@ impl TestIO {
}

impl IO for TestIO {
fn read(&mut self) -> TLit {
self.inputs
.next()
.expect("Test tried to read more input than were available.")
fn read(&mut self) -> Option<TLit> {
self.inputs.next()
}

fn print(&mut self, v: TLit) {
Expand Down
50 changes: 50 additions & 0 deletions compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,21 @@ pub mod passes;
pub mod utils;

use crate::passes::parse::parse::parse_program;
use clap::ValueEnum;
use miette::{IntoDiagnostic, NamedSource, Report};
use std::fs::File;
use std::path::Path;

#[derive(ValueEnum, Clone, Debug)]
pub enum Pass {
Parse,
Validate,
Reveal,
Atomize,
Explicate,
Select,
}

pub fn compile(program: &str, filename: &str, output: &Path) -> miette::Result<()> {
let add_source =
|error| Report::with_source_code(error, NamedSource::new(filename, program.to_string()));
Expand All @@ -32,3 +43,42 @@ pub fn compile(program: &str, filename: &str, output: &Path) -> miette::Result<(

Ok(())
}

pub fn display(program: &str, filename: &str, pass: Pass) -> miette::Result<()> {
let add_source =
|error| Report::with_source_code(error, NamedSource::new(filename, program.to_string()));

let prg_parsed = display!(
parse_program(program)
.map_err(Into::into)
.map_err(add_source)?,
pass,
Parse
);
let prg_validated = display!(
prg_parsed
.validate()
.map_err(Into::into)
.map_err(add_source)?,
pass,
Validate
);
let prg_revealed = display!(prg_validated.reveal(), pass, Reveal);
let prg_atomized = display!(prg_revealed.atomize(), pass, Atomize);
let prg_explicated = display!(prg_atomized.explicate(), pass, Explicate);
let _prg_select = display!(prg_explicated.eliminate().select(), pass, Select);

Ok(())
}

#[macro_export]
macro_rules! display {
($prg:expr, $pass:expr, $pat:ident) => {
if matches!($pass, $crate::Pass::$pat) {
println!("{}", $prg);
return Ok(());
} else {
$prg
}
};
}
13 changes: 10 additions & 3 deletions compiler/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clap::Parser;
use compiler::compile;
use compiler::passes::parse::parse::PrettyParseError;
use compiler::passes::validate::error::TypeError;
use compiler::{compile, display, Pass};
use miette::{Diagnostic, IntoDiagnostic};
use std::fs;
use std::io::Read;
Expand All @@ -26,8 +26,11 @@ struct Args {

/// Specifies the path to an output file. If None, it uses the input filename.
/// If that's also None, it defaults to "output".
#[arg(short, long)]
#[arg(short, long, value_name = "FILE")]
output: Option<String>,

#[arg(value_enum, short, long, value_name = "PASS")]
display: Option<Pass>,
}

fn read_from_stdin() -> Result<String, std::io::Error> {
Expand All @@ -44,7 +47,11 @@ fn main() -> miette::Result<()> {
Some(file) => (fs::read_to_string(file).into_diagnostic()?, file.as_str()),
};

let output: &str = args.output.as_deref().unwrap_or_else(|| {
if let Some(pass) = args.display {
return display(&program, filename, pass);
}

let output = args.output.as_deref().unwrap_or_else(|| {
args.input.as_ref().map_or_else(
|| "output",
|s| Path::new(s).file_stem().unwrap().to_str().unwrap(),
Expand Down
36 changes: 6 additions & 30 deletions compiler/src/passes/assign/assign.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::passes::assign::{Arg, X86Assigned};
use crate::passes::select::{Block, Instr, VarArg, X86Selected};
use crate::passes::select::{Block, Instr, InstrSelected, VarArg, X86Selected};
use crate::utils::gen_sym::UniqueSym;
use crate::*;
use std::collections::HashMap;

impl<'p> X86Selected<'p> {
Expand All @@ -27,7 +26,7 @@ impl<'p> X86Selected<'p> {
}

fn assign_block<'p>(
block: Block<'p, VarArg>,
block: Block<'p, VarArg<UniqueSym<'p>>>,
color_map: &HashMap<UniqueSym, Arg>,
) -> Block<'p, Arg> {
Block {
Expand All @@ -40,39 +39,16 @@ fn assign_block<'p>(
}

fn assign_instr<'p>(
instr: Instr<'p, VarArg>,
instr: InstrSelected<'p>,
color_map: &HashMap<UniqueSym, Arg>,
) -> Instr<'p, Arg> {
let map = |arg: VarArg| -> Arg {
) -> Instr<Arg, UniqueSym<'p>> {
let map = |arg: VarArg<UniqueSym<'p>>| -> Arg {
match arg {
VarArg::Imm { val } => Arg::Imm { val },
VarArg::Reg { reg } => Arg::Reg { reg },
VarArg::Deref { reg, off } => Arg::Deref { reg, off },
VarArg::XVar { sym } => color_map[&sym].clone(),
}
};

match instr {
Instr::Addq { src, dst } => addq!(map(src), map(dst)),
Instr::Subq { src, dst } => subq!(map(src), map(dst)),
Instr::Divq { divisor } => divq!(map(divisor)),
Instr::Mulq { src } => mulq!(map(src)),
Instr::Negq { dst } => negq!(map(dst)),
Instr::Movq { src, dst } => movq!(map(src), map(dst)),
Instr::Pushq { src } => pushq!(map(src)),
Instr::Popq { dst } => popq!(map(dst)),
Instr::CallqDirect { lbl, arity } => callq_direct!(lbl, arity),
Instr::Retq => retq!(),
Instr::Syscall { arity } => syscall!(arity),
Instr::Jmp { lbl } => jmp!(lbl),
Instr::Jcc { lbl, cnd } => jcc!(lbl, cnd),
Instr::Cmpq { src, dst } => cmpq!(map(src), map(dst)),
Instr::Andq { src, dst } => andq!(map(src), map(dst)),
Instr::Orq { src, dst } => orq!(map(src), map(dst)),
Instr::Xorq { src, dst } => xorq!(map(src), map(dst)),
Instr::Notq { dst } => notq!(map(dst)),
Instr::Setcc { cnd } => setcc!(cnd),
Instr::LoadLbl { sym, dst } => load_lbl!(sym, map(dst)),
Instr::CallqIndirect { src, arity } => callq_indirect!(map(src), arity),
}
instr.fmap(map)
}
4 changes: 2 additions & 2 deletions compiler/src/passes/assign/color_interference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl<'p> InterferenceGraph<'p> {
.max()
.unwrap_or_default() as usize;

let stack_space = (8 * used_vars).div_ceil(16) * 16;
let stack_space = (8 * used_vars).div_ceil(16) * 16 + 256;

let colors = node_map
.into_iter()
Expand Down Expand Up @@ -107,7 +107,7 @@ fn arg_from_color(i: isize) -> Arg {
);
Arg::Deref {
reg: Reg::RBP,
off: (-8 * (i - 10)) as i64,
off: (-8 * (i - 10)) as i64 - 256,
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions compiler/src/passes/assign/include_liveness.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::utils::gen_sym::UniqueSym;

use crate::passes::assign::{LArg, LBlock, LX86VarProgram};
use crate::passes::select::{Block, Instr, Reg, VarArg, X86Selected, CALLER_SAVED, SYSCALL_REGS};
use crate::passes::select::{
Block, Instr, InstrSelected, Reg, VarArg, X86Selected, CALLER_SAVED, SYSCALL_REGS,
};
use std::collections::hash_map::Entry;
use std::collections::{HashMap, HashSet};

Expand Down Expand Up @@ -58,7 +60,7 @@ impl<'p> X86Selected<'p> {
}

fn block_liveness<'p>(
block: &Block<'p, VarArg<'p>>,
block: &Block<'p, VarArg<UniqueSym<'p>>>,
before_map: &HashMap<UniqueSym<'p>, HashSet<LArg<'p>>>,
) -> (LBlock<'p>, HashSet<LArg<'p>>) {
let mut instrs = Vec::new();
Expand Down Expand Up @@ -100,9 +102,9 @@ pub enum ReadWriteOp {
}

pub fn handle_instr<'p>(
instr: &Instr<'p, VarArg<'p>>,
instr: &InstrSelected<'p>,
before_map: &HashMap<UniqueSym<'p>, HashSet<LArg<'p>>>,
mut arg: impl FnMut(&VarArg<'p>, ReadWriteOp),
mut arg: impl FnMut(&VarArg<UniqueSym<'p>>, ReadWriteOp),
) {
use ReadWriteOp::Read as R;
use ReadWriteOp::ReadWrite as RW;
Expand Down
10 changes: 5 additions & 5 deletions compiler/src/passes/assign/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ mod include_liveness;
mod tests;

use crate::passes::select::std_lib::Std;
use crate::passes::select::{Block, Instr, Reg, VarArg, X86Selected};
use crate::passes::select::{Block, InstrSelected, Reg, VarArg, X86Selected};
use crate::utils::gen_sym::UniqueSym;
use derive_more::Display;
use functor_derive::Functor;
Expand Down Expand Up @@ -41,7 +41,7 @@ pub struct LX86VarProgram<'p> {

#[derive(PartialEq)]
pub struct LBlock<'p> {
pub instrs: Vec<(Instr<'p, VarArg<'p>>, HashSet<LArg<'p>>)>,
pub instrs: Vec<(InstrSelected<'p>, HashSet<LArg<'p>>)>,
}

#[derive(Hash, Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
Expand All @@ -50,7 +50,7 @@ pub enum LArg<'p> {
Reg { reg: Reg },
}

impl<'p> From<LArg<'p>> for VarArg<'p> {
impl<'p> From<LArg<'p>> for VarArg<UniqueSym<'p>> {
fn from(val: LArg<'p>) -> Self {
match val {
LArg::Var { sym } => VarArg::XVar { sym },
Expand All @@ -59,15 +59,15 @@ impl<'p> From<LArg<'p>> for VarArg<'p> {
}
}

impl<'p> From<LBlock<'p>> for Block<'p, VarArg<'p>> {
impl<'p> From<LBlock<'p>> for Block<'p, VarArg<UniqueSym<'p>>> {
fn from(value: LBlock<'p>) -> Self {
Block {
instrs: value.instrs.into_iter().map(|(instr, _)| instr).collect(),
}
}
}

impl<'p> From<Arg> for VarArg<'p> {
impl<'p> From<Arg> for VarArg<UniqueSym<'p>> {
fn from(value: Arg) -> Self {
match value {
Arg::Imm { val } => VarArg::Imm { val },
Expand Down
Loading

0 comments on commit 2dc6387

Please sign in to comment.