Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update main to reflect latest changes in emission that work #25

Merged
merged 22 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ build:
@chmod u+x .githooks/pre-commit
@make README

.PHONY: protect
protect:
sudo chmod +x .githooks/pre-commit

.PHONY: README
README:
$(PY) readme.py
Expand All @@ -21,6 +25,10 @@ test: build
quick_test: build
opam exec -- dune exec ./test/test_x86ISTMB.exe -- -q

.PHONY: utop
utop:
dune utop

.PHONY: bisect
bisect:
@find . -name '*.coverage' | xargs rm -f
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
![CI Status](https://github.com/ethanuppal/cs3110_compiler/actions/workflows/ci.yaml/badge.svg)

> "x86 is simple trust me bro"
> Last updated: 2024-04-26 11:20:14.268605
> Last updated: 2024-05-06 00:50:43.998646

```
$ ./main -h
Expand Down
6 changes: 3 additions & 3 deletions bin/main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,18 @@ let show_ir statements =
List.iter
(fun b ->
let lst = Basic_block.to_list b in
Printf.printf "Block %i:\n" (Basic_block.id_of b);
Printf.printf "Block %i:\n" (Basic_block.id_of b |> Id.int_of);
List.iter (fun bir -> print_endline (Ir.to_string bir)) lst;
print_newline ())
blocks;
let edges = Cfg.edges_of main_cfg in
List.iter
(fun (b1, e, b2) ->
Printf.printf "Block %i" (Basic_block.id_of b1);
Printf.printf "Block %i" (Basic_block.id_of b1 |> Id.int_of);
Printf.printf " --%s (%s)--> "
(Basic_block.condition_of b1 |> Branch_condition.to_string)
(if e then "t" else "f");
Printf.printf "Block %i" (Basic_block.id_of b2);
Printf.printf "Block %i" (Basic_block.id_of b2 |> Id.int_of);
print_newline ())
edges

Expand Down
3 changes: 3 additions & 0 deletions docs/introduction.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# x86ISTMB Programming Language - Introduction and Tutorial

> [!NOTE]
> This document is outdated.

## Overview

x86ISTMB is a minimalistic, strongly typed programming language designed for simplicity and clarity. It focuses on providing a straightforward approach to programming by limiting its features to the core essentials. This document serves as an introductory guide and tutorial to the x86ISTMB programming language.
Expand Down
2 changes: 2 additions & 0 deletions lib/backend/x86.ml → lib/backend/asm.ml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ module Instruction = struct
| Cmp of Operand.t * Operand.t
| Jmp of Operand.t
| Je of Operand.t
| Jne of Operand.t
| Ret
| Syscall
| Label of Label.t
Expand All @@ -112,6 +113,7 @@ module Instruction = struct
"cmp " ^ Operand.to_nasm op1 ^ ", " ^ Operand.to_nasm op2
| Jmp op -> "jmp " ^ Operand.to_nasm op
| Je op -> "je " ^ Operand.to_nasm op
| Jne op -> "jne " ^ Operand.to_nasm op
| Ret -> "ret"
| Syscall -> "syscall"
| Label label -> Label.to_nasm label
Expand Down
12 changes: 12 additions & 0 deletions lib/backend/asm_emit.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
let emit_ir section =
ignore section;
failwith "i hate reg alloc"

let emit_bb section cfg bb =
let ir = Basic_block.to_list bb in
let jumps = Cfg.out_edges cfg bb in
ignore jumps;
List.iter (emit_ir section) ir;
failwith "emit jumps"

let emit section cfg = Cfg.blocks_of cfg |> List.iter (emit_bb section cfg)
3 changes: 3 additions & 0 deletions lib/backend/asm_emit.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(** [emit section cfg] emits the function [cfg] into the assembly section
[section]. *)
val emit : Asm.Section.t -> Cfg.t -> unit
176 changes: 176 additions & 0 deletions lib/backend/liveliness.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
open Util
open Ir

(** A set of IR variables. *)
module VariableSet = Set.Make (Variable)

(** Liveliness analysis of an IR operation. *)
type instr_analysis = {
mutable live_in : VariableSet.t;
mutable live_out : VariableSet.t option;
}

module BasicBlockAnalysis = struct
(** AF: An array of instruction analyses
`[| { li0; lo0 }; { li1; lo1; }; ... |]` represents the liveliness
analysis of a basic block. [lin] is the set of variables live before
instruction [n]. If [lon] is [None] and [n] is not the index of the last
element in the array, and [li(n+1)] is the set of live variables live
after instruction [n]; if [n] is the last valid index in the array, then
then the basic block analyzed is an exit point; otherwise, it is
[Some lon], and [lon] is that set.

RI: Let [analysis] be a value of type [t]. Then, if [lon] is [None] for
some [n], then [n < Array.length analysis - 1]. *)
type t = instr_analysis Array.t

let rep_ok analysis =
if Rep_ok.check && Array.length analysis > 0 then
analysis
|> Array.iteri (fun i ia ->
if i < Array.length analysis - 1 && ia.live_out <> None then
failwith "rep_ok");
analysis

let make bb =
Array.init (Basic_block.length_of bb) (fun _ ->
{ live_in = VariableSet.empty; live_out = None })
|> rep_ok

let live_before_instr analysis index =
let analysis = rep_ok analysis in
analysis.(index).live_in

let live_after_instr analysis index =
let analysis = rep_ok analysis in
match analysis.(index).live_out with
| None ->
if index = Array.length analysis - 1 then VariableSet.empty
else analysis.(index + 1).live_in
| Some live_out -> live_out

let live_in analysis =
let analysis = rep_ok analysis in
live_before_instr analysis 0

let live_out analysis =
let analysis = rep_ok analysis in
live_after_instr analysis (Array.length analysis - 1)

let to_string analysis =
let analysis = rep_ok analysis in
let set_to_string set =
let elements_string =
VariableSet.elements set
|> List.map Variable.to_string
|> String.concat ", "
in
"{" ^ elements_string ^ "}"
in
"BasicBlockAnalysis {"
^ (Seq.init (Array.length analysis) id
|> List.of_seq
|> List.map (fun i ->
"\n ir[" ^ string_of_int i ^ "] <=> {live_in = "
^ set_to_string (live_before_instr analysis i)
^ ", live_out = "
^ set_to_string (live_after_instr analysis i)
^ "}")
|> String.concat "")
^ "\n}"
end

(** [apply_rules liveliness analysis cfg bb ir ir_index ~is_final] applies
liveliness rules for instruction [ir] at index [ir_index] in basic block
[bb], where [bb] is in [cfg] and has associated liveliness analysis
[analysis = IdMap.find liveliness (Basic_block.id_of bb)], and where
[is_final] if and only if [ir] is the final instruction in [bb], and returns
whether any updates were made to liveliness information. *)
let apply_rules liveliness analysis cfg bb ir ir_idx ~is_final =
let result = ref false in
let instr_analysis = analysis.(ir_idx) in
let update_live_out new_live_out =
let old_live_out = instr_analysis.live_out in
instr_analysis.live_out <- new_live_out;
if old_live_out <> new_live_out then result := true
in
let update_live_in new_live_in =
let old_live_in = instr_analysis.live_in in
instr_analysis.live_in <- new_live_in;
if old_live_in <> new_live_in then result := true
in
let read_var var =
update_live_in (VariableSet.add var instr_analysis.live_in)
in
let read_op op = Operand.var_of_opt op |> Option.map read_var |> ignore in
let write_var var =
let incoming_live =
VariableSet.remove var
(BasicBlockAnalysis.live_after_instr analysis ir_idx)
in
update_live_in (VariableSet.union instr_analysis.live_in incoming_live)
in
(if is_final then
let live_out_of_succ =
Cfg.out_edges cfg bb
|> List.fold_left
(fun acc (bb_succ, _) ->
let incoming_live_partial =
IdMap.find liveliness (Basic_block.id_of bb_succ)
|> BasicBlockAnalysis.live_in
in
VariableSet.union acc incoming_live_partial)
VariableSet.empty
in
update_live_out (Some live_out_of_succ));
(match ir with
| DebugPrint op -> read_op op
| Assign (var, op) | Deref (var, op) | Ref (var, op) ->
write_var var;
read_op op
| Add (var, op1, op2) | Sub (var, op1, op2) | TestEqual (var, op1, op2) ->
write_var var;
read_op op1;
read_op op2);
!result

(** [pass work_list liveliness cfg bb] performs a single pass of liveliness
analysis on a basic block *)
let pass work_list liveliness cfg bb =
let result = ref false in
let analysis = IdMap.find liveliness (Basic_block.id_of bb) in
let ir_view = Basic_block.as_view bb in
let ir_count = Basic_block.length_of bb in
for rev_i = 1 to ir_count do
let i = ir_count - rev_i in
result :=
apply_rules liveliness analysis cfg bb (ArrayView.get ir_view i) i
~is_final:(rev_i = 1)
|| !result
done;
if !result then
List.iter (fun (bb, _) -> Queue.add bb work_list) (Cfg.in_edges cfg bb);
!result

(** [iterate liveliness cfg] performs an iteration of liveliness analysis on
[cfg], updating partial results in [liveliness], and returning whether any
changes were made. *)
let iterate liveliness cfg =
let work_list = Queue.create () in
List.iter (fun bb -> Queue.add bb work_list) (Cfg.exit_points cfg);
let result = ref false in
while not (Queue.is_empty work_list) do
let top_bb = Queue.take work_list in
result := pass work_list liveliness cfg top_bb || !result
done;
!result

let analysis_of cfg =
let liveliness = IdMap.create 16 in
Cfg.iter
(fun bb ->
IdMap.add liveliness (Basic_block.id_of bb) (BasicBlockAnalysis.make bb))
cfg;
let rec converge () = if iterate liveliness cfg then converge () in
converge ();
liveliness
54 changes: 54 additions & 0 deletions lib/backend/liveliness.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
open Util

(** A value of type [VariableSet.t] is a set of IR variables. *)
module VariableSet : sig
include Set.S with type elt = Variable.t
end

(** Liveliness analysis of a basic block. *)
module BasicBlockAnalysis : sig
(** Values of type [t] are liveliness analyses for a given basic block. *)
type t

(** [make bb] is an empty liveliness analysis for the basic block [bb]. It is
guaranteed to never mutate or copy [bb] internally.

Requires: [Basic_block.length_of bb > 0]. *)
val make : Basic_block.t -> t

(** [live_in analysis] is the set of variables live at the start of the the
analyzed basic block.

Time Complexity: [O(1)]. *)
val live_in : t -> VariableSet.t

(** [live_out analysis] is the set of variables live at the end of the the
analyzed basic block.

Time Complexity: [O(1)]. *)
val live_out : t -> VariableSet.t

(** [live_before_instr analysis index] is the set of variables live before the
[index]th instruction in the basic block for which [analysis] was created.

Time Complexity: [O(1)]. *)
val live_before_instr : t -> int -> VariableSet.t

(** [live_after_instr analysis index] is the set of variables live after the
[index]th instruction in the basic block for which [analysis] was created.

Time Complexity: [O(1)]. *)
val live_after_instr : t -> int -> VariableSet.t

(** [to_string analysis] is a string representation of [analysis]. *)
val to_string : t -> string
end

(** [analysis_of cfg] is an association between the basic blocks in [cfg] and
their liveliness analyses. In particular, let [a] be the result of this
function and let [bb] be a basic block in [cfg] Then,
[Util.IdMap.find a (Basic_block.id_of bb)] is the liveliness analysis of
[bb].

Requires: every basic block in [cfg] has at least one IR instruction. *)
val analysis_of : Cfg.t -> BasicBlockAnalysis.t IdMap.t
4 changes: 3 additions & 1 deletion lib/dune
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
(preprocess
(pps ppx_inline_test))
(instrumentation
(backend bisect_ppx)))
(backend bisect_ppx))
(flags
(:standard -w -69)))

(rule
(targets project_root.ml)
Expand Down
12 changes: 11 additions & 1 deletion lib/ir/basic_block.ml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,19 @@ let make () =
}

let id_of basic_block = basic_block.id
let length_of bb = BatDynArray.length bb.contents
let condition_of bb = bb.condition
let set_condition bb cond = bb.condition <- cond
let add_ir basic_block ir = BatDynArray.add basic_block.contents ir
let to_list basic_block = BatDynArray.to_list basic_block.contents
let equal bb1 bb2 = bb1.id = bb2.id
let hash bb = bb.id
let hash bb = Id.int_of bb.id |> Int.hash
let as_view bb = Util.ArrayView.from_bat_dyn_arr bb.contents

let to_string bb =
Printf.sprintf ".L%d:" (id_of bb |> Id.int_of)
^ BatDynArray.fold_left
(fun acc ir -> acc ^ "\n " ^ Ir.to_string ir)
"" bb.contents
^ "\n br "
^ Branch_condition.to_string bb.condition
8 changes: 8 additions & 0 deletions lib/ir/basic_block.mli
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ val make : unit -> t
(** [id_of basic_block] is the id of [basic_block]. *)
val id_of : t -> Id.id

(** [length_of basic_block] is the number of instructions in [basic_block]. *)
val length_of : t -> int

(** [condition_of basic_block] is the condition associated with branching away
from this basic block. *)
val condition_of : t -> Branch_condition.t
Expand All @@ -21,9 +24,14 @@ val add_ir : t -> Ir.t -> unit
list. *)
val to_list : t -> Ir.t list

(** [as_view bb] is a *)
val as_view : t -> Ir.t Util.ArrayView.t

(** [equal bb1 bb2] is whether bb1 and bb2 have the same id. *)
val equal : t -> t -> bool

(** [hash bb] is a hash representing this basic block. [hash bb1 = hash bb2] iff
[bb1 = bb2]. *)
val hash : t -> int

val to_string : t -> string
Loading