Update hw4 to a newer version.

Signed-off-by: jmug <u.g.a.mariano@gmail.com>
This commit is contained in:
Mariano Uvalle 2025-01-24 21:01:32 -08:00
parent 07d34c0cd8
commit b24a264f7e
221 changed files with 846 additions and 746 deletions

2
hw4/.ocamlformat Normal file
View file

@ -0,0 +1,2 @@
profile = janestreet
version = 0.26.1

View file

@ -1,9 +1 @@
#use "topfind";;
#directory "_build"
#directory "_build/util"
#directory "_build/x86"
#directory "_build/grading"
#load_rec "x86.cmo"
#use "llinterp.ml"
#use_output "dune top";;

View file

@ -1,24 +1,27 @@
INCLUDES= util,x86,grading,ll
LIBS = unix,str
SUBMIT := lexer.mll parser.mly frontend.ml team.txt
SUBMIT := $(shell cat submit_zip_contents.txt)
HWNAME := hw4
TIMESTAMP := $(shell /bin/date "+%Y-%m-%d-%H:%M:%S")
ZIPNAME := $(HWNAME)-submit-$(TIMESTAMP).zip
HWNAME := hw04
ZIPNAME := $(HWNAME)-submit.zip
.PHONY: all oatc test clean zip
all: oatc
all: main.native
oatc:
dune build
@cp bin/main.exe oatc
.PHONY: test
test: main.native
./main.native --test
test: oatc
./oatc --test
main.native: $(SUBMIT) ast.ml astlib.ml backend.ml driver.ml main.ml progasts.ml runtime.c
ocamlbuild -Is $(INCLUDES) -libs $(LIBS) -pkg num main.native -use-menhir -yaccflag --explain
utop:
dune utop
zip: $(SUBMIT)
zip '$(ZIPNAME)' $(SUBMIT)
.PHONY: clean
clean:
ocamlbuild -clean
rm -rf output a.out
dune clean
rm -rf oatc ocamlbin bin/main.exe
#

View file

@ -1,76 +0,0 @@
Using main.native:
main.native acts like the clang compiler. Given several .ll, .c, and .o files,
it will compile the .ll files to .s files (using the Compiler Design backend)
and then combine the results with the .c and .o files to produce an executable
named a.out. You can also compile the .ll files using clang instead of the
Compiler Design backend, which can be useful for testing purposes.
* To run the automated test harness do:
./main.native --test
* To compile ll files using the Compiler Design backend:
./main.native path/to/foo.ll
- creates output/foo.s backend assembly code
- creates output/foo.o assembled object file
- creates a.out linked executable
NOTE: by default the .s and .o files are created in
a directory called output, and the filenames are
chosen so that multiple runs of the compiler will
not overwrite previous outputs. foo.ll will be
compiled first to foo.s then foo_1.s, foo_2.s, etc.
* To compile ll files using the clang backend:
./main.native --clang path/to/foo.ll
* Useful flags:
--print-oat
pretty prints the Oat abstract syntax to the terminal
--print-ll
echoes the ll program to the terminal
--print-x86
echoes the resulting .s file to the terminal
--interpret-ll
runs the ll file through the reference interpreter
and outputs the results to the console
--execute-x86
runs the resulting a.out file natively
(applies to either the Compiler Design backend or clang-compiled code)
--clang compiles to assembly using clang, not the Compiler Design backend
-v
generates verbose output, showing which commands are used
for linking, etc.
-op <dirname>
change the output path [DEFAULT=output]
-o
change the generated executable's name [DEFAULT=a.out]
-S
stop after generating .s files
-c
stop after generating .o files
-h or --help
display the list of options
* Example uses:
Run the test case /programs/factrect.ll using the Compiler Design backend:
./main.native --execute-x86 programs/factrect.ll
--------------------------------------------------------------- Executing: a.out
* a.out returned 120

72
hw4/README.md Normal file
View file

@ -0,0 +1,72 @@
# HW4: Oat v.1
Quick Start:
1. open the folder in VSCode
2. start an OCaml sandbox terminal
3. run `make test` from the command line
4. open `bin/frontend.ml`
See the general toolchain and project instructions on the course web site. The
course web pages have a link to the html version of the homework instructions.
Using ``oatc``
--------------
``oatc`` acts like the clang compiler. Given several .oat, .ll, .c, and .o
files, it will compile the .oat and .ll files to .s files (using the CIS 341
frontend and backend) and then combine the results with the .c and .o files to
produce an executable named a.out. You can also compile the .ll files using
clang instead of the CS131 backend, which can be useful for testing purposes.
* To run the automated test harness do:
./oatc --test
* To compile oat files using the 341 backend:
./oatc path/to/foo.oat
- creates output/foo.ll frontend ll code
- creates output/foo.s backend assembly code
- creates output/foo.o assembled object file
- creates a.out linked executable
NOTE: by default the .s and .o files are created in
a directory called output, and the filenames are
chosen so that multiple runs of the compiler will
not overwrite previous outputs. foo.ll will be
compiled first to foo.s then foo_1.s, foo_2.s, etc.
* To compile oat files using the clang backend:
./oatc --clang path/to/foo.oat
* Useful flags:
| Flag | Description |
|-------------------|---------------------------------------------------------------------------------------------------|
| --print-oat | pretty prints the Oat abstract syntax to the terminal |
| --print-ll | echoes the ll program to the terminal |
| --print-x86 | echoes the resulting .s file to the terminal |
| --interpret-ll | runs the ll file through the reference interpreter and outputs the results to the console |
| --execute-x86 | runs the resulting a.out file natively (applies to either the 341 backend or clang-compiled code) |
| --clang | compiles to assembly using clang, not the 341 backend |
| -v | generates verbose output, showing which commands are used for linking, etc. |
| -op ``<dirname>`` | change the output path [DEFAULT=output] |
| -o | change the generated executable's name [DEFAULT=a.out] |
| -S | stop after generating .s files |
| -c | stop after generating .o files |
| -h or --help | display the list of options |
* Example uses:
Run the test case hw4programs/fact.oat using the 341 backend:
/oatc --execute-x86 hw4programs/fact.oat bin/runtime.c
120--------------------------------------------------------------- Executing: a.out
* a.out returned 0

View file

@ -1,13 +1,27 @@
(* The type declarations in this file correspond to productions in the Oat
* grammar provided in the Oat v.1 Language Specification. *)
module Range = Util.Range
(* The `node` type represents a node in an abstract syntax tree.
* The `elt` is the AST element, the `loc` contains the line number
* info of the corresponding concrete syntax element. The parser uses
* the `loc` to give line numbers with error messages, and you can do
* so too in the compiler if you like!
*)
type 'a node = { elt : 'a; loc : Range.t }
(** val no_loc : 'a1 -> 'a1 node **)
(* This function might be useful if some of your helper functions
* have to return a `node` type.
*)
let no_loc x =
{ elt = x; loc = Range.norange }
type id = string
(* Oat types *)
type ty =
| TBool
| TInt
@ -43,6 +57,7 @@ type binop =
| Shr
| Sar
(* Oat expressions *)
type exp =
| CNull of rty
| CBool of bool

View file

@ -3,9 +3,11 @@
open Ll
open X86
module Platform = Util.Platform
(* Overview ----------------------------------------------------------------- *)
(* We suggest that you spend some time understinging this entire file and
(* We suggest that you spend some time understanding this entire file and
how it fits with the compiler pipeline before making changes. The suggested
plan for implementing the compiler is provided on the project web page.
*)
@ -85,7 +87,7 @@ let lookup m x = List.assoc x m
That is, the X86 code: movq _gid %rax which looks like it should
put the address denoted by _gid into %rax is not allowed.
Instead, you need to compute an %rip-relative address using the
leaq instruction: leaq _gid(%rip).
leaq instruction: leaq _gid(%rip) %rax.
One strategy for compiling instruction operands is to use a
designated register (or registers) for holding the values being
@ -120,6 +122,9 @@ let compile_operand ctxt dest : Ll.operand -> ins =
[ NOTE: Don't forget to preserve caller-save registers (only if
needed). ]
[ NOTE: Remember, call can use labels as immediates! You shouldn't
need to perform any RIP-relative addressing for this one. ]
*)
@ -164,8 +169,8 @@ let compile_call ctxt fop args =
address based on the size of the data, which is dictated by the
data's type.
To compile getelmentptr, you must generate x86 code that performs
the appropriate arithemetic calculations.
To compile getelementptr, you must generate x86 code that performs
the appropriate arithmetic calculations.
*)
(* [size_ty] maps an LLVMlite type to a size in bytes.
@ -185,7 +190,7 @@ let rec size_ty (tdecls:(tid * ty) list) (t:Ll.ty) : int =
| I1 | I64 | Ptr _ -> 8 (* Target 64-bit only subset of X86 *)
| Struct ts -> List.fold_left (fun acc t -> acc + (size_ty tdecls t)) 0 ts
| Array (n, t) -> n * (size_ty tdecls t)
| Namedt id -> size_ty tdecls (List.assoc id tdecls)
| Namedt id -> size_ty tdecls (lookup tdecls id)
end
(* Compute the size of the offset (in bytes) of the nth element of a region
@ -227,6 +232,10 @@ let index_into tdecls (ts:ty list) (n:int) : int * ty =
*)
let compile_gep (ctxt:ctxt) (op : Ll.ty * Ll.operand) (path: Ll.operand list) : ins list =
let op_to_rax = compile_operand ctxt (Reg Rax) in
let rec effective_type = function
| Namedt id -> effective_type @@ lookup ctxt.tdecls id
| t -> t
in
let rec loop ty path code =
match (ty, path) with
| (_, []) -> List.rev code
@ -248,11 +257,11 @@ let compile_gep (ctxt:ctxt) (op : Ll.ty * Ll.operand) (path: Ll.operand list) :
Asm.(Movq, [~%Rax; ~%Rcx])
:: code
| (Namedt t, p) -> loop (List.assoc t ctxt.tdecls) p code
| (Namedt t, p) -> loop (lookup ctxt.tdecls t) p code
| _ -> failwith "compile_gep encountered unsupported getelementptr data" in
match op with
match (effective_type @@ fst op, snd op) with
| (Ptr t, op) -> loop (Array(0, t)) path [op_to_rax op]
| _ -> failwith "compile_gep got incorrect parameters"
@ -286,7 +295,7 @@ let compile_insn (ctxt:ctxt) ((uid:uid), (i:Ll.insn)) : X86.ins list =
let op_to_rcx = op_to (Reg Rcx) in (* Move the value of op into rax *)
let dst = lookup ctxt.layout uid in
match i with
| Binop (bop, t, op1, op2) ->
| Binop (bop, _t, op1, op2) ->
let bin op =
(op_to_rax op1) ::
(op_to_rcx op2) ::
@ -313,18 +322,18 @@ let compile_insn (ctxt:ctxt) ((uid:uid), (i:Ll.insn)) : X86.ins list =
(* Load dereferences the pointer value stored in a local.
Global and constant pointers don't need indirection. *)
| Load (t, op) -> (op_to_rax op) :: Asm.([ Movq, [Ind2 Rax; ~%Rcx]
| Load (_t, op) -> (op_to_rax op) :: Asm.([ Movq, [Ind2 Rax; ~%Rcx]
; Movq, [~%Rcx; dst] ])
(* Store also needs to dereference the destination pointer if it's a global *)
| Store (_, src, (Id uid as dest)) ->
| Store (_, src, (Id _ as dest)) ->
(op_to_rcx src) ::
(op_to_rax dest) :: Asm.([Movq, [~%Rcx; Ind2 Rax]])
| Store (_, src, Gid gid) ->
(op_to_rax src) :: Asm.([Movq, [~%Rax; Ind3 (Lbl (Platform.mangle gid), Rip)]])
| Store (_, _, _) -> failwith "store destination was not a local or global id"
(* Treat LL i1 values as words, so zero-out the rest of the bits *)
(* Treat LL i1 values as quadwords, so zero-out the rest of the bits *)
| Icmp (cnd, _, op1, op2) -> (op_to_rax op1) ::
(op_to_rcx op2) ::
Asm.([ Cmpq, [~%Rcx; ~%Rax]
@ -362,6 +371,8 @@ let mk_lbl (fn:string) (l:string) = fn ^ "." ^ l
- Br should jump
- Cbr branch should treat its operand as a boolean conditional
[fn] - the name of the function containing this terminator
*)
let compile_terminator (fn:string) (ctxt:ctxt) (t:Ll.terminator) : ins list =
let epilogue = Asm.([ Movq, [~%Rbp; ~%Rsp]
@ -467,7 +478,7 @@ let rec compile_ginit : ginit -> X86.data list = function
| GInt c -> [Quad (Lit c)]
| GString s -> [Asciz s]
| GArray gs | GStruct gs -> List.map compile_gdecl gs |> List.flatten
| GBitcast (t1,g,t2) -> compile_ginit g
| GBitcast (_t1,g,_t2) -> compile_ginit g
and compile_gdecl (_, g) = compile_ginit g

View file

@ -1,4 +1,6 @@
open Printf
module Platform = Util.Platform
module Range = Util.Range
open Platform
(* configuration flags ------------------------------------------------------ *)
@ -76,7 +78,7 @@ let run_program (args:string) (executable:string) (tmp_out:string) : string =
read_file tmp_out
let run_program_error (args:string) (executable:string) (tmp_out:string) : string =
let cmd = sprintf "timeout 60 %s%s %s > %s 2>&1" dot_path executable args tmp_out in
let cmd = sprintf "%s%s %s > %s 2>&1" dot_path executable args tmp_out in
let result = sh cmd (fun _ i -> i)
in
(read_file tmp_out) ^ (string_of_int result)
@ -107,7 +109,7 @@ let string_of_ll_ast path ll_ast =
let process_ll_ast path file ll_ast =
let _ = if !print_ll_flag then print_ll file ll_ast in
(* Optionally interpret it using the reference interpreter. *)
(* Optionally interpret it using the cis341 reference interperter. *)
let _ = if !interpret_ll then
let result = interpret ll_ast [] in
Printf.printf "Interpreter Result: %s\n" result
@ -126,7 +128,7 @@ let process_ll_ast path file ll_ast =
Platform.sh (Printf.sprintf "cat %s" dot_s_file) Platform.raise_error
end
end else begin
Platform.verb "* compiling with Compiler Design backend";
Platform.verb "* compiling with cis341 backend";
let asm_ast = Backend.compile_prog ll_ast in
let asm_str = X86.string_of_prog asm_ast in
let _ = if !print_x86_flag then print_x86 dot_s_file asm_str in
@ -198,7 +200,7 @@ let process_files files =
List.iter process_file files;
( if !assemble && !link then
Platform.link (List.rev !link_files@["runtime.c"]) !executable_filename );
Platform.link (List.rev !link_files) !executable_filename );
( if !assemble && !link && !execute_x86 then
let ret = run_executable "" !executable_filename in

31
hw4/bin/dune Normal file
View file

@ -0,0 +1,31 @@
(library
(name oat)
(modules driver backend frontend lexer parser ast astlib)
(libraries str num util x86 ll))
(ocamllex lexer)
(menhir (modules parser))
(env
(dev
(flags
(:standard -g -w "+a-4-7-9-26-27-29-30-32..42-44-45-48-50-60-66..70")
)))
(executable
(public_name main)
(name main)
(modules main)
(promote (until-clean))
(libraries
; OCaml standard libraries
; project libraries
str
num
util
x86
ll
studenttests
gradedtests
; sp24_hw4_tests
))

View file

@ -2,11 +2,17 @@ open Ll
open Llutil
open Ast
(* This file is where much of the work of the project will be carried out.
Follow the instructions on the project web site, but first skim through
this file to see what it contains.
*)
(* instruction streams ------------------------------------------------------ *)
(* As in the last project, we'll be working with a flattened representation
of LLVMlite programs to make emitting code easier. This version
additionally makes it possible to emit elements will be gathered up and
additionally makes it possible to emit elements that will be gathered up and
"hoisted" to specific parts of the constructed CFG
- G of gid * Ll.gdecl: allows you to output global definitions in the middle
of the instruction stream. You will find this useful for compiling string
@ -23,6 +29,16 @@ type elt =
| G of gid * Ll.gdecl (* hoisted globals (usually strings) *)
| E of uid * Ll.insn (* hoisted entry block instructions *)
(* The type of streams of LLVMLite instructions. Note that to improve performance,
* we will emit the instructions in reverse order. That is, the LLVMLite code:
* %1 = mul i64 2, 2
* %2 = add i64 1, %1
* br label %l1
* would be constructed as a stream as follows:
* I ("1", Binop (Mul, I64, Const 2L, Const 2L))
* >:: I ("2", Binop (Add, I64, Const 1L, Id "1"))
* >:: T (Br "l1")
*)
type stream = elt list
let ( >@ ) x y = y @ x
let ( >:: ) x y = y :: x
@ -160,10 +176,10 @@ let typ_of_unop : Ast.unop -> Ast.ty * Ast.ty = function
the array), we can simply compile e1[e2] as a left-hand-side and then do the
load. So cmp_exp and cmp_lhs are mutually recursive. [[Actually, as I am
writing this, I think it could make sense to factor the Oat grammar in this
way, which would make things clearer, I may do that for next time around.]]
way, which would make things clearerhw, I may do that for next time around.]]
Consider globals7.oat
Consider globals7.oat (in hw4programs)
/--------------- globals7.oat ------------------
global arr = int[] null;
@ -247,7 +263,7 @@ let typ_of_unop : Ast.unop -> Ast.ty * Ast.ty = function
(* Global initialized arrays:
There is another wrinkle: To compile global initialized arrays like in the
There is another wrinkle: to compile global initialized arrays like in the
globals4.oat, it is helpful to do a bitcast once at the global scope to
convert the "precise type" required by the LLVM initializer to the actual
translation type (which sets the array length to 0). So for globals4.oat,
@ -287,6 +303,9 @@ let oat_alloc_array (t:Ast.ty) (size:Ll.operand) : Ll.ty * operand * stream =
[ arr_id, Call(arr_ty, Gid "oat_alloc_array", [I64, size])
; ans_id, Bitcast(arr_ty, Id arr_id, ans_ty) ]
(* Compiles an expression exp in context c, outputting the Ll operand that will
recieve the value of the expression, and the stream of instructions
implementing the expression.
@ -303,9 +322,9 @@ let oat_alloc_array (t:Ast.ty) (size:Ll.operand) : Ll.ty * operand * stream =
(CArr) and the (NewArr) expressions
*)
let rec cmp_exp (c:Ctxt.t) (exp:Ast.exp node) : Ll.ty * Ll.operand * stream =
failwith "cmp_exp not implemented"
failwith "cmp_exp unimplemented"
(* Compile a statement in context c with return typ rt. Return a new context,
possibly extended with new local bindings, and the instruction stream
@ -333,10 +352,10 @@ let rec cmp_exp (c:Ctxt.t) (exp:Ast.exp node) : Ll.ty * Ll.operand * stream =
pointer, you just need to store to it!
*)
let rec cmp_stmt (c:Ctxt.t) (rt:Ll.ty) (stmt:Ast.stmt node) : Ctxt.t * stream =
failwith "cmp_stmt not implemented"
(* Compile a series of statements *)
and cmp_block (c:Ctxt.t) (rt:Ll.ty) (stmts:Ast.block) : Ctxt.t * stream =
List.fold_left (fun (c, code) s ->
@ -366,7 +385,7 @@ let cmp_function_ctxt (c:Ctxt.t) (p:Ast.prog) : Ctxt.t =
in well-formed programs. (The constructors starting with C).
*)
let cmp_global_ctxt (c:Ctxt.t) (p:Ast.prog) : Ctxt.t =
failwith "cmp_global_ctxt not implemented"
failwith "cmp_global_ctxt unimplemented"
(* Compile a function declaration in global context c. Return the LLVMlite cfg
and a list of global declarations containing the string literals appearing
@ -379,10 +398,10 @@ let cmp_global_ctxt (c:Ctxt.t) (p:Ast.prog) : Ctxt.t =
4. Compile the body of the function using cmp_block
5. Use cfg_of_stream to produce a LLVMlite cfg from
*)
let cmp_fdecl (c:Ctxt.t) (f:Ast.fdecl node) : Ll.fdecl * (Ll.gid * Ll.gdecl) list =
failwith "cmp_fdecl not implemented"
(* Compile a global initializer, returning the resulting LLVMlite global
declaration, and a list of additional global declarations.
@ -394,9 +413,9 @@ let cmp_fdecl (c:Ctxt.t) (f:Ast.fdecl node) : Ll.fdecl * (Ll.gid * Ll.gdecl) lis
- OAT arrays are always handled via pointers. A global array of arrays will
be an array of pointers to arrays emitted as additional global declarations.
*)
let rec cmp_gexp c (e:Ast.exp node) : Ll.gdecl * (Ll.gid * Ll.gdecl) list =
failwith "cmp_gexp not implemented"
failwith "cmp_init not implemented"
(* Oat internals function context ------------------------------------------- *)
let internals = [

View file

@ -1,6 +1,7 @@
{
open Lexing
open Parser
module Range = Util.Range
open Range
exception Lexer_error of Range.t * string
@ -53,6 +54,7 @@
( ")", RPAREN);
( "[", LBRACKET);
( "]", RBRACKET);
]
let (symbol_table : (string, Parser.token) Hashtbl.t) = Hashtbl.create 1024

View file

@ -1,7 +1,7 @@
open Ll
open Arg
open Assert
open Driver
open Util.Assert
open Oat.Driver
(* testing harness ---------------------------------------------------------- *)
exception Ran_tests
@ -23,7 +23,7 @@ let args =
; ("--interpret-ll", Set interpret_ll, "runs each LL program through the LL interpreter")
; ("--print-ll", Set print_ll_flag, "prints the program LL code")
; ("--print-x86", Set print_x86_flag, "prints the program's assembly code")
; ("--clang", Set clang, "compiles to assembly using clang, not the Compiler Design backend")
; ("--clang", Set clang, "compiles to assembly using clang, not the 341 backend")
; ("--execute-x86", Set execute_x86, "run the resulting executable file")
; ("--print-ast", Set print_ast_flag , "print the program's AST as ML code")
; ("--print-oat", Set print_oat_flag , "print the program's OAT code")
@ -39,12 +39,13 @@ let main () =
Platform.create_output_dir ();
try
Arg.parse args (fun filename -> files := filename :: !files)
"Compiler Design main test harness\n\
"CS 131 main test harness\n\
USAGE: ./main.native [options] <files>\n\
see README for details about using the compiler";
process_files !files
with Ran_tests -> ()
with Ran_tests ->
()
;; main ()

View file

@ -1,5 +1,6 @@
%{
open Ast
open Lexing
let loc (startpos:Lexing.position) (endpos:Lexing.position) (elt:'a) : 'a node =
{ elt ; loc=Range.mk_lex_range startpos endpos }
@ -38,6 +39,8 @@ let loc (startpos:Lexing.position) (endpos:Lexing.position) (elt:'a) : 'a node =
%token BANG /* ! */
%token GLOBAL /* global */
%left PLUS DASH
%left STAR
%nonassoc BANG
@ -82,6 +85,7 @@ ty:
| TINT { TInt }
| r=rtyp { TRef r }
%inline ret_ty:
| TVOID { RetVoid }
| t=ty { RetVal t }

3
hw4/dune-project Normal file
View file

@ -0,0 +1,3 @@
(lang dune 2.9)
(name hw4)
(using menhir 2.1)

0
hw4/hw4.opam Normal file
View file

View file

@ -0,0 +1,8 @@
int foo(int x) {
x = x + 1;
return x;
}
int program (int argc, string[] argv) {
return foo(17);
}

View file

@ -16,7 +16,7 @@ void proc2 ( ) {
}
bool foo ( int x, int[] y ) {
var s = bar (x, "compilerdesign");
var s = bar (x, "cis341");
proc1 ();
return true;
}

View file

@ -1,6 +1,6 @@
int program (int argc, string[] argv) {
if(6 != 5) {
return ~(5 << 17 >> 2 >>> 10) * 2 - 100 + 6;
return ~(5 >> --6 << 9 >>> 10) * 2 - 100 + 6;
} else {
return 2;
}

View file

@ -1,3 +1,8 @@
/*
* CIS 341 Homework 4
* Thomas Delacour & Max McCarthy
*/
/**
* Computes longest common subsequence of two strings a and b.
*/

Some files were not shown because too many files have changed in this diff Show more