Update hw4 to a newer version.
Signed-off-by: jmug <u.g.a.mariano@gmail.com>
This commit is contained in:
parent
07d34c0cd8
commit
b24a264f7e
221 changed files with 846 additions and 746 deletions
2
hw4/.ocamlformat
Normal file
2
hw4/.ocamlformat
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
profile = janestreet
|
||||
version = 0.26.1
|
||||
|
|
@ -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";;
|
||||
|
|
|
|||
31
hw4/Makefile
31
hw4/Makefile
|
|
@ -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
|
||||
|
||||
#
|
||||
|
|
|
|||
76
hw4/README
76
hw4/README
|
|
@ -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
72
hw4/README.md
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
@ -3,11 +3,13 @@
|
|||
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.
|
||||
plan for implementing the compiler is provided on the project web page.
|
||||
*)
|
||||
|
||||
|
||||
|
|
@ -52,7 +54,7 @@ let rbp_offset (offset:int) : X86.operand =
|
|||
'stack layout'. A stack layout maps a uid to an X86 operand for
|
||||
accessing its contents. For this compilation strategy, the operand
|
||||
is always an offset from %rbp (in bytes) that represents a storage slot in
|
||||
the stack.
|
||||
the stack.
|
||||
*)
|
||||
|
||||
type layout = (uid * X86.operand) list
|
||||
|
|
@ -77,7 +79,7 @@ let lookup m x = List.assoc x m
|
|||
|
||||
NOTE: two important facts about global identifiers:
|
||||
|
||||
(1) You should use (Platform.mangle gid) to obtain a string
|
||||
(1) You should use (Platform.mangle gid) to obtain a string
|
||||
suitable for naming a global label on your platform (OS X expects
|
||||
"_main" while linux expects "main").
|
||||
|
||||
|
|
@ -85,14 +87,14 @@ 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
|
||||
manipulated by the LLVM IR instruction. You might find it useful to
|
||||
implement the following helper function, whose job is to generate
|
||||
the X86 instruction that moves an LLVM operand into a designated
|
||||
destination (usually a register).
|
||||
destination (usually a register).
|
||||
*)
|
||||
let compile_operand ctxt dest : Ll.operand -> ins =
|
||||
function
|
||||
|
|
@ -104,7 +106,7 @@ let compile_operand ctxt dest : Ll.operand -> ins =
|
|||
|
||||
(* compiling call ---------------------------------------------------------- *)
|
||||
|
||||
(* You will probably find it helpful to implement a helper function that
|
||||
(* You will probably find it helpful to implement a helper function that
|
||||
generates code for the LLVM IR call instruction.
|
||||
|
||||
The code you generate should follow the x64 System V AMD64 ABI
|
||||
|
|
@ -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,11 +169,11 @@ 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.
|
||||
(* [size_ty] maps an LLVMlite type to a size in bytes.
|
||||
(needed for getelementptr)
|
||||
|
||||
- the size of a struct is the sum of the sizes of each component
|
||||
|
|
@ -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
|
||||
|
|
@ -200,7 +205,7 @@ let index_into tdecls (ts:ty list) (n:int) : int * ty =
|
|||
in loop ts n 0
|
||||
|
||||
|
||||
(* Generates code that computes a pointer value.
|
||||
(* Generates code that computes a pointer value.
|
||||
|
||||
1. op must be of pointer type: t*
|
||||
|
||||
|
|
@ -211,14 +216,14 @@ let index_into tdecls (ts:ty list) (n:int) : int * ty =
|
|||
|
||||
4. subsequent indices are interpreted according to the type t:
|
||||
|
||||
- if t is a struct, the index must be a constant n and it
|
||||
- if t is a struct, the index must be a constant n and it
|
||||
picks out the n'th element of the struct. [ NOTE: the offset
|
||||
within the struct of the n'th element is determined by the
|
||||
within the struct of the n'th element is determined by the
|
||||
sizes of the types of the previous elements ]
|
||||
|
||||
- if t is an array, the index can be any operand, and its
|
||||
value determines the offset within the array.
|
||||
|
||||
|
||||
- if t is any other type, the path is invalid
|
||||
|
||||
5. if the index is valid, the remainder of the path is computed as
|
||||
|
|
@ -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"
|
||||
|
||||
|
|
@ -281,15 +290,15 @@ let compile_gep (ctxt:ctxt) (op : Ll.ty * Ll.operand) (path: Ll.operand list) :
|
|||
- Bitcast: does nothing interesting at the assembly level
|
||||
*)
|
||||
let compile_insn (ctxt:ctxt) ((uid:uid), (i:Ll.insn)) : X86.ins list =
|
||||
let op_to = compile_operand ctxt in
|
||||
let op_to = compile_operand ctxt in
|
||||
let op_to_rax = op_to (Reg Rax) in (* Move the value of op into rax *)
|
||||
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) ->
|
||||
let bin op =
|
||||
(op_to_rax op1) ::
|
||||
(op_to_rcx op2) ::
|
||||
| Binop (bop, _t, op1, op2) ->
|
||||
let bin op =
|
||||
(op_to_rax op1) ::
|
||||
(op_to_rcx op2) ::
|
||||
Asm.([ op, [~%Rcx; ~%Rax]
|
||||
; Movq, [~%Rax; dst] ])
|
||||
in
|
||||
|
|
@ -305,26 +314,26 @@ let compile_insn (ctxt:ctxt) ((uid:uid), (i:Ll.insn)) : X86.ins list =
|
|||
| Ll.Xor -> bin Xorq
|
||||
end
|
||||
|
||||
(* Alloca instructions allocate an fresh stack slot and
|
||||
(* Alloca instructions allocate an fresh stack slot and
|
||||
move the address of the newly allocated storage into the
|
||||
destination uid. *)
|
||||
| Alloca (_t) -> Asm.([ Pushq, [~$0]
|
||||
; Movq, [~%Rsp; dst] ])
|
||||
; Movq, [~%Rsp; dst] ])
|
||||
|
||||
(* 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]
|
||||
; Movq, [~%Rcx; dst] ])
|
||||
| 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]
|
||||
|
|
@ -405,13 +416,13 @@ let arg_loc (n : int) : operand =
|
|||
| None -> rbp_offset (n-4)
|
||||
end
|
||||
|
||||
(* We suggest that you create a helper function that computes the
|
||||
(* We suggest that you create a helper function that computes the
|
||||
stack layout for a given function declaration.
|
||||
|
||||
- each function argument should be copied into a stack slot
|
||||
- in this (inefficient) compilation strategy, each local id
|
||||
- in this (inefficient) compilation strategy, each local id
|
||||
is also stored as a stack slot.
|
||||
- see the discussion about locals
|
||||
- see the discussion about locals
|
||||
|
||||
*)
|
||||
let stack_layout (args : uid list) ((block, lbled_blocks):cfg) : layout =
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
31
hw4/bin/dune
Normal 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
|
||||
))
|
||||
|
|
@ -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 = [
|
||||
|
|
@ -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
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
open Ll
|
||||
open Arg
|
||||
open Assert
|
||||
open Driver
|
||||
open Util.Assert
|
||||
open Oat.Driver
|
||||
|
||||
(* testing harness ---------------------------------------------------------- *)
|
||||
exception Ran_tests
|
||||
|
|
@ -23,12 +23,12 @@ 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")
|
||||
; ("-v", Unit Platform.enable_verbose, "enables more verbose compilation output")
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
(* Files found on the command line *)
|
||||
|
|
@ -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 ()
|
||||
|
|
@ -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 }
|
||||
|
|
@ -94,7 +98,7 @@ ty:
|
|||
| PLUS { Add }
|
||||
| DASH { Sub }
|
||||
| STAR { Mul }
|
||||
| EQEQ { Eq }
|
||||
| EQEQ { Eq }
|
||||
|
||||
%inline uop:
|
||||
| DASH { Neg }
|
||||
|
|
@ -103,7 +107,7 @@ ty:
|
|||
|
||||
gexp:
|
||||
| t=rtyp NULL { loc $startpos $endpos @@ CNull t }
|
||||
| i=INT { loc $startpos $endpos @@ CInt i }
|
||||
| i=INT { loc $startpos $endpos @@ CInt i }
|
||||
|
||||
lhs:
|
||||
| id=IDENT { loc $startpos $endpos @@ Id id }
|
||||
3
hw4/dune-project
Normal file
3
hw4/dune-project
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
(lang dune 2.9)
|
||||
(name hw4)
|
||||
(using menhir 2.1)
|
||||
0
hw4/hw4.opam
Normal file
0
hw4/hw4.opam
Normal file
8
hw4/hw4programs/argassign.oat
Normal file
8
hw4/hw4programs/argassign.oat
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
int foo(int x) {
|
||||
x = x + 1;
|
||||
return x;
|
||||
}
|
||||
|
||||
int program (int argc, string[] argv) {
|
||||
return foo(17);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1,52 +1,52 @@
|
|||
global float_len = 2;
|
||||
int[] determine_shift(int[] float)
|
||||
{
|
||||
var dec = float[1];
|
||||
var count = 0;
|
||||
while(dec > 0)
|
||||
{
|
||||
var temp = float[0];
|
||||
float[0] = temp << 1;
|
||||
dec = dec >>> 1;
|
||||
count = count + 1;
|
||||
}
|
||||
var list = new int[2];
|
||||
list[0] = float[0] + float[1];
|
||||
list[1] = count;
|
||||
return list;
|
||||
}
|
||||
|
||||
int[] multiply_floats(int[] f1, int[] f2)
|
||||
{
|
||||
var f1_shifted = determine_shift(f1);
|
||||
var f2_shifted = determine_shift(f2);
|
||||
var product = f1_shifted[0] * f2_shifted[0];
|
||||
var num_left_shifts = f1_shifted[1] + f2_shifted[1];
|
||||
var remainder = 0;
|
||||
for(var i = 0; i < num_left_shifts; i=i+1;)
|
||||
{
|
||||
var lsb = product [&] 1;
|
||||
var shifted_lsb = lsb << i;
|
||||
product = product >>> 1;
|
||||
remainder = remainder + shifted_lsb;
|
||||
}
|
||||
var ans = new int[2];
|
||||
ans[0] = product;
|
||||
ans[1] = remainder;
|
||||
return ans;
|
||||
}
|
||||
|
||||
int program(int argc, string[] argv)
|
||||
{
|
||||
var pi = new int[2];
|
||||
pi[0] = 3;
|
||||
pi[1] = 14159;
|
||||
var diameter = new int[2];
|
||||
diameter[0] = 20;
|
||||
diameter[1] = 17;
|
||||
var prod = multiply_floats(pi, diameter);
|
||||
print_int(prod[0]);
|
||||
print_string(".");
|
||||
print_int(prod[1]);
|
||||
return 0;
|
||||
global float_len = 2;
|
||||
int[] determine_shift(int[] float)
|
||||
{
|
||||
var dec = float[1];
|
||||
var count = 0;
|
||||
while(dec > 0)
|
||||
{
|
||||
var temp = float[0];
|
||||
float[0] = temp << 1;
|
||||
dec = dec >>> 1;
|
||||
count = count + 1;
|
||||
}
|
||||
var list = new int[2];
|
||||
list[0] = float[0] + float[1];
|
||||
list[1] = count;
|
||||
return list;
|
||||
}
|
||||
|
||||
int[] multiply_floats(int[] f1, int[] f2)
|
||||
{
|
||||
var f1_shifted = determine_shift(f1);
|
||||
var f2_shifted = determine_shift(f2);
|
||||
var product = f1_shifted[0] * f2_shifted[0];
|
||||
var num_left_shifts = f1_shifted[1] + f2_shifted[1];
|
||||
var remainder = 0;
|
||||
for(var i = 0; i < num_left_shifts; i=i+1;)
|
||||
{
|
||||
var lsb = product [&] 1;
|
||||
var shifted_lsb = lsb << i;
|
||||
product = product >>> 1;
|
||||
remainder = remainder + shifted_lsb;
|
||||
}
|
||||
var ans = new int[2];
|
||||
ans[0] = product;
|
||||
ans[1] = remainder;
|
||||
return ans;
|
||||
}
|
||||
|
||||
int program(int argc, string[] argv)
|
||||
{
|
||||
var pi = new int[2];
|
||||
pi[0] = 3;
|
||||
pi[1] = 14159;
|
||||
var diameter = new int[2];
|
||||
diameter[0] = 20;
|
||||
diameter[1] = 17;
|
||||
var prod = multiply_floats(pi, diameter);
|
||||
print_int(prod[0]);
|
||||
print_string(".");
|
||||
print_int(prod[1]);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,3 +1,8 @@
|
|||
/*
|
||||
* CIS 341 Homework 4
|
||||
* Thomas Delacour & Max McCarthy
|
||||
*/
|
||||
|
||||
/**
|
||||
* Computes longest common subsequence of two strings a and b.
|
||||
*/
|
||||
|
|
@ -1,39 +1,39 @@
|
|||
int mod (int a, int b) {
|
||||
var t = a;
|
||||
while (t - b >= 0) {
|
||||
t = t - b;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
int div (int a, int b) {
|
||||
var result = 0;
|
||||
var num = a;
|
||||
var denom = b;
|
||||
while (num > 0) {
|
||||
num = num - denom;
|
||||
result = result + 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int no_of_factors(int n) {
|
||||
var num_fact = 1;
|
||||
var input = n;
|
||||
for (var i = 2; i * i < input + 1; i=i+1;) {
|
||||
var power = 0;
|
||||
while (mod(n, i) == 0) {
|
||||
n = div(n, i);
|
||||
power = power + 1;
|
||||
}
|
||||
num_fact = num_fact * (power + 1);
|
||||
}
|
||||
if (n > 1) {
|
||||
num_fact = num_fact * 2;
|
||||
}
|
||||
return num_fact;
|
||||
}
|
||||
|
||||
int program (int argc, string[] argv) {
|
||||
return no_of_factors(6400);
|
||||
}
|
||||
int mod (int a, int b) {
|
||||
var t = a;
|
||||
while (t - b >= 0) {
|
||||
t = t - b;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
int div (int a, int b) {
|
||||
var result = 0;
|
||||
var num = a;
|
||||
var denom = b;
|
||||
while (num > 0) {
|
||||
num = num - denom;
|
||||
result = result + 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int no_of_factors(int n) {
|
||||
var num_fact = 1;
|
||||
var input = n;
|
||||
for (var i = 2; i * i < input + 1; i=i+1;) {
|
||||
var power = 0;
|
||||
while (mod(n, i) == 0) {
|
||||
n = div(n, i);
|
||||
power = power + 1;
|
||||
}
|
||||
num_fact = num_fact * (power + 1);
|
||||
}
|
||||
if (n > 1) {
|
||||
num_fact = num_fact * 2;
|
||||
}
|
||||
return num_fact;
|
||||
}
|
||||
|
||||
int program (int argc, string[] argv) {
|
||||
return no_of_factors(6400);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue