Implementing a compiler for tiger
as described in the book Modern Compiler Implementation in ML by Andrew Appel. This implementation uses OCaml as opposed to SML as I thought that it'd be easier to pull this off in OCaml.
- Lexing
- Parsing to AST
- Semantic analysis
- Production of activation records
- Translation to intermediate representation (IR)
- Producing basic blocks and traces from IR
- Instruction selection (RISC-V)
- Liveness analysis
- Register allocation
A few steps need to be taken before the code can actually be run (this is only necessary if certain files are edited). All of the below operations are idempotent. A ./run_postprocessing.sh
bash file is included to run all of the below steps.
# Remove extraneous lines (tests dont work otherwise)
ocamllex lib/lexer.mll && sed -i '' "/^#.*$/c\\" lib/lexer.ml
This generates lib/lexer.ml
(this file is typically checked into the repository).
ocamlyacc lib/grammar.mly && sed -i '' "/^#.*$/c\\" lib/grammar.ml
# Additional something that we need to add to the mli file
cat lib/grammar.mli_ >> lib/grammar.mli
This generates lib/grammar.ml
(this file is also typically checked into the repository).
To run unit tests and integration tests
dune runtest
This command compiles an input tiger
file and produces a RISC-V assembly file.
dune exec bin/main.exe -- input.tig -o output.s
The output file needs to be linked with the runtime library. Here, we use riscv64-unknown-elf-gcc
as our RISC-V assembler and linker. We also remap the getchar
and strcmp
functions using a wrapper, i.e. redirecting calls to these functions to our version of them.
riscv64-unknown-elf-gcc output.s btl/runtime.c -Wl,--wrap=getchar,--wrap=strcmp -o ./a.out
On a machine that does not run on the RISC-V architecture, we may run the executable using spike
and pk
.
spike pk ./a.out