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";; #use_output "dune top";;
#directory "_build"
#directory "_build/util"
#directory "_build/x86"
#directory "_build/grading"
#load_rec "x86.cmo"
#use "llinterp.ml"

View file

@ -1,24 +1,27 @@
INCLUDES= util,x86,grading,ll SUBMIT := $(shell cat submit_zip_contents.txt)
LIBS = unix,str HWNAME := hw4
SUBMIT := lexer.mll parser.mly frontend.ml team.txt TIMESTAMP := $(shell /bin/date "+%Y-%m-%d-%H:%M:%S")
ZIPNAME := $(HWNAME)-submit-$(TIMESTAMP).zip
HWNAME := hw04 .PHONY: all oatc test clean zip
ZIPNAME := $(HWNAME)-submit.zip
all: oatc
all: main.native oatc:
dune build
@cp bin/main.exe oatc
.PHONY: test test: oatc
test: main.native ./oatc --test
./main.native --test
main.native: $(SUBMIT) ast.ml astlib.ml backend.ml driver.ml main.ml progasts.ml runtime.c utop:
ocamlbuild -Is $(INCLUDES) -libs $(LIBS) -pkg num main.native -use-menhir -yaccflag --explain dune utop
zip: $(SUBMIT) zip: $(SUBMIT)
zip '$(ZIPNAME)' $(SUBMIT) zip '$(ZIPNAME)' $(SUBMIT)
.PHONY: clean
clean: clean:
ocamlbuild -clean dune clean
rm -rf output a.out 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 } type 'a node = { elt : 'a; loc : Range.t }
(** val no_loc : 'a1 -> 'a1 node **) (** 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 = let no_loc x =
{ elt = x; loc = Range.norange } { elt = x; loc = Range.norange }
type id = string type id = string
(* Oat types *)
type ty = type ty =
| TBool | TBool
| TInt | TInt
@ -43,6 +57,7 @@ type binop =
| Shr | Shr
| Sar | Sar
(* Oat expressions *)
type exp = type exp =
| CNull of rty | CNull of rty
| CBool of bool | CBool of bool

View file

@ -3,11 +3,13 @@
open Ll open Ll
open X86 open X86
module Platform = Util.Platform
(* Overview ----------------------------------------------------------------- *) (* 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 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 'stack layout'. A stack layout maps a uid to an X86 operand for
accessing its contents. For this compilation strategy, the operand accessing its contents. For this compilation strategy, the operand
is always an offset from %rbp (in bytes) that represents a storage slot in is always an offset from %rbp (in bytes) that represents a storage slot in
the stack. the stack.
*) *)
type layout = (uid * X86.operand) list 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: 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 suitable for naming a global label on your platform (OS X expects
"_main" while linux expects "main"). "_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 That is, the X86 code: movq _gid %rax which looks like it should
put the address denoted by _gid into %rax is not allowed. put the address denoted by _gid into %rax is not allowed.
Instead, you need to compute an %rip-relative address using the 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 One strategy for compiling instruction operands is to use a
designated register (or registers) for holding the values being designated register (or registers) for holding the values being
manipulated by the LLVM IR instruction. You might find it useful to manipulated by the LLVM IR instruction. You might find it useful to
implement the following helper function, whose job is to generate implement the following helper function, whose job is to generate
the X86 instruction that moves an LLVM operand into a designated 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 = let compile_operand ctxt dest : Ll.operand -> ins =
function function
@ -104,7 +106,7 @@ let compile_operand ctxt dest : Ll.operand -> ins =
(* compiling call ---------------------------------------------------------- *) (* 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. generates code for the LLVM IR call instruction.
The code you generate should follow the x64 System V AMD64 ABI 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 [ NOTE: Don't forget to preserve caller-save registers (only if
needed). ] 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 address based on the size of the data, which is dictated by the
data's type. data's type.
To compile getelmentptr, you must generate x86 code that performs To compile getelementptr, you must generate x86 code that performs
the appropriate arithemetic calculations. 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) (needed for getelementptr)
- the size of a struct is the sum of the sizes of each component - 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 *) | 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 | Struct ts -> List.fold_left (fun acc t -> acc + (size_ty tdecls t)) 0 ts
| Array (n, t) -> n * (size_ty tdecls t) | 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 end
(* Compute the size of the offset (in bytes) of the nth element of a region (* 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 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* 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: 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 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 ] sizes of the types of the previous elements ]
- if t is an array, the index can be any operand, and its - if t is an array, the index can be any operand, and its
value determines the offset within the array. value determines the offset within the array.
- if t is any other type, the path is invalid - if t is any other type, the path is invalid
5. if the index is valid, the remainder of the path is computed as 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 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 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 = let rec loop ty path code =
match (ty, path) with match (ty, path) with
| (_, []) -> List.rev code | (_, []) -> 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]) Asm.(Movq, [~%Rax; ~%Rcx])
:: code :: 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 | _ -> 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] | (Ptr t, op) -> loop (Array(0, t)) path [op_to_rax op]
| _ -> failwith "compile_gep got incorrect parameters" | _ -> 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 - Bitcast: does nothing interesting at the assembly level
*) *)
let compile_insn (ctxt:ctxt) ((uid:uid), (i:Ll.insn)) : X86.ins list = 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_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 op_to_rcx = op_to (Reg Rcx) in (* Move the value of op into rax *)
let dst = lookup ctxt.layout uid in let dst = lookup ctxt.layout uid in
match i with match i with
| Binop (bop, t, op1, op2) -> | Binop (bop, _t, op1, op2) ->
let bin op = let bin op =
(op_to_rax op1) :: (op_to_rax op1) ::
(op_to_rcx op2) :: (op_to_rcx op2) ::
Asm.([ op, [~%Rcx; ~%Rax] Asm.([ op, [~%Rcx; ~%Rax]
; Movq, [~%Rax; dst] ]) ; Movq, [~%Rax; dst] ])
in in
@ -305,26 +314,26 @@ let compile_insn (ctxt:ctxt) ((uid:uid), (i:Ll.insn)) : X86.ins list =
| Ll.Xor -> bin Xorq | Ll.Xor -> bin Xorq
end 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 move the address of the newly allocated storage into the
destination uid. *) destination uid. *)
| Alloca (_t) -> Asm.([ Pushq, [~$0] | Alloca (_t) -> Asm.([ Pushq, [~$0]
; Movq, [~%Rsp; dst] ]) ; Movq, [~%Rsp; dst] ])
(* Load dereferences the pointer value stored in a local. (* Load dereferences the pointer value stored in a local.
Global and constant pointers don't need indirection. *) 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] ]) ; Movq, [~%Rcx; dst] ])
(* Store also needs to dereference the destination pointer if it's a global *) (* 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_rcx src) ::
(op_to_rax dest) :: Asm.([Movq, [~%Rcx; Ind2 Rax]]) (op_to_rax dest) :: Asm.([Movq, [~%Rcx; Ind2 Rax]])
| Store (_, src, Gid gid) -> | Store (_, src, Gid gid) ->
(op_to_rax src) :: Asm.([Movq, [~%Rax; Ind3 (Lbl (Platform.mangle gid), Rip)]]) (op_to_rax src) :: Asm.([Movq, [~%Rax; Ind3 (Lbl (Platform.mangle gid), Rip)]])
| Store (_, _, _) -> failwith "store destination was not a local or global id" | 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) :: | Icmp (cnd, _, op1, op2) -> (op_to_rax op1) ::
(op_to_rcx op2) :: (op_to_rcx op2) ::
Asm.([ Cmpq, [~%Rcx; ~%Rax] Asm.([ Cmpq, [~%Rcx; ~%Rax]
@ -362,6 +371,8 @@ let mk_lbl (fn:string) (l:string) = fn ^ "." ^ l
- Br should jump - Br should jump
- Cbr branch should treat its operand as a boolean conditional - 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 compile_terminator (fn:string) (ctxt:ctxt) (t:Ll.terminator) : ins list =
let epilogue = Asm.([ Movq, [~%Rbp; ~%Rsp] let epilogue = Asm.([ Movq, [~%Rbp; ~%Rsp]
@ -405,13 +416,13 @@ let arg_loc (n : int) : operand =
| None -> rbp_offset (n-4) | None -> rbp_offset (n-4)
end 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. stack layout for a given function declaration.
- each function argument should be copied into a stack slot - 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. 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 = 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)] | GInt c -> [Quad (Lit c)]
| GString s -> [Asciz s] | GString s -> [Asciz s]
| GArray gs | GStruct gs -> List.map compile_gdecl gs |> List.flatten | 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 and compile_gdecl (_, g) = compile_ginit g

View file

@ -1,4 +1,6 @@
open Printf open Printf
module Platform = Util.Platform
module Range = Util.Range
open Platform open Platform
(* configuration flags ------------------------------------------------------ *) (* configuration flags ------------------------------------------------------ *)
@ -76,7 +78,7 @@ let run_program (args:string) (executable:string) (tmp_out:string) : string =
read_file tmp_out read_file tmp_out
let run_program_error (args:string) (executable:string) (tmp_out:string) : string = 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) let result = sh cmd (fun _ i -> i)
in in
(read_file tmp_out) ^ (string_of_int result) (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 process_ll_ast path file ll_ast =
let _ = if !print_ll_flag then print_ll file ll_ast in 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 _ = if !interpret_ll then
let result = interpret ll_ast [] in let result = interpret ll_ast [] in
Printf.printf "Interpreter Result: %s\n" result 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 Platform.sh (Printf.sprintf "cat %s" dot_s_file) Platform.raise_error
end end
end else begin 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_ast = Backend.compile_prog ll_ast in
let asm_str = X86.string_of_prog asm_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 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; List.iter process_file files;
( if !assemble && !link then ( 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 ( if !assemble && !link && !execute_x86 then
let ret = run_executable "" !executable_filename in 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 Llutil
open Ast 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 ------------------------------------------------------ *) (* instruction streams ------------------------------------------------------ *)
(* As in the last project, we'll be working with a flattened representation (* As in the last project, we'll be working with a flattened representation
of LLVMlite programs to make emitting code easier. This version 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 "hoisted" to specific parts of the constructed CFG
- G of gid * Ll.gdecl: allows you to output global definitions in the middle - 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 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) *) | G of gid * Ll.gdecl (* hoisted globals (usually strings) *)
| E of uid * Ll.insn (* hoisted entry block instructions *) | 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 type stream = elt list
let ( >@ ) x y = y @ x let ( >@ ) x y = y @ x
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 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 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 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 ------------------ /--------------- globals7.oat ------------------
global arr = int[] null; global arr = int[] null;
@ -247,7 +263,7 @@ let typ_of_unop : Ast.unop -> Ast.ty * Ast.ty = function
(* Global initialized arrays: (* 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 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 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, 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]) [ arr_id, Call(arr_ty, Gid "oat_alloc_array", [I64, size])
; ans_id, Bitcast(arr_ty, Id arr_id, ans_ty) ] ; ans_id, Bitcast(arr_ty, Id arr_id, ans_ty) ]
(* Compiles an expression exp in context c, outputting the Ll operand that will (* Compiles an expression exp in context c, outputting the Ll operand that will
recieve the value of the expression, and the stream of instructions recieve the value of the expression, and the stream of instructions
implementing the expression. 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 (CArr) and the (NewArr) expressions
*) *)
let rec cmp_exp (c:Ctxt.t) (exp:Ast.exp node) : Ll.ty * Ll.operand * stream = 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, (* Compile a statement in context c with return typ rt. Return a new context,
possibly extended with new local bindings, and the instruction stream 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! 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 = let rec cmp_stmt (c:Ctxt.t) (rt:Ll.ty) (stmt:Ast.stmt node) : Ctxt.t * stream =
failwith "cmp_stmt not implemented" failwith "cmp_stmt not implemented"
(* Compile a series of statements *) (* Compile a series of statements *)
and cmp_block (c:Ctxt.t) (rt:Ll.ty) (stmts:Ast.block) : Ctxt.t * stream = and cmp_block (c:Ctxt.t) (rt:Ll.ty) (stmts:Ast.block) : Ctxt.t * stream =
List.fold_left (fun (c, code) s -> 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). in well-formed programs. (The constructors starting with C).
*) *)
let cmp_global_ctxt (c:Ctxt.t) (p:Ast.prog) : Ctxt.t = 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 (* Compile a function declaration in global context c. Return the LLVMlite cfg
and a list of global declarations containing the string literals appearing 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 4. Compile the body of the function using cmp_block
5. Use cfg_of_stream to produce a LLVMlite cfg from 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 = let cmp_fdecl (c:Ctxt.t) (f:Ast.fdecl node) : Ll.fdecl * (Ll.gid * Ll.gdecl) list =
failwith "cmp_fdecl not implemented" failwith "cmp_fdecl not implemented"
(* Compile a global initializer, returning the resulting LLVMlite global (* Compile a global initializer, returning the resulting LLVMlite global
declaration, and a list of additional global declarations. 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 - 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. 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 = 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 ------------------------------------------- *) (* Oat internals function context ------------------------------------------- *)
let internals = [ let internals = [

View file

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

View file

@ -1,7 +1,7 @@
open Ll open Ll
open Arg open Arg
open Assert open Util.Assert
open Driver open Oat.Driver
(* testing harness ---------------------------------------------------------- *) (* testing harness ---------------------------------------------------------- *)
exception Ran_tests exception Ran_tests
@ -23,12 +23,12 @@ let args =
; ("--interpret-ll", Set interpret_ll, "runs each LL program through the LL interpreter") ; ("--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-ll", Set print_ll_flag, "prints the program LL code")
; ("--print-x86", Set print_x86_flag, "prints the program's assembly 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") ; ("--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-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") ; ("--print-oat", Set print_oat_flag , "print the program's OAT code")
; ("-v", Unit Platform.enable_verbose, "enables more verbose compilation output") ; ("-v", Unit Platform.enable_verbose, "enables more verbose compilation output")
] ]
(* Files found on the command line *) (* Files found on the command line *)
@ -39,12 +39,13 @@ let main () =
Platform.create_output_dir (); Platform.create_output_dir ();
try try
Arg.parse args (fun filename -> files := filename :: !files) 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\ USAGE: ./main.native [options] <files>\n\
see README for details about using the compiler"; see README for details about using the compiler";
process_files !files process_files !files
with Ran_tests -> () with Ran_tests ->
()
;; main () ;; main ()

View file

@ -1,5 +1,6 @@
%{ %{
open Ast open Ast
open Lexing
let loc (startpos:Lexing.position) (endpos:Lexing.position) (elt:'a) : 'a node = let loc (startpos:Lexing.position) (endpos:Lexing.position) (elt:'a) : 'a node =
{ elt ; loc=Range.mk_lex_range startpos endpos } { 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 BANG /* ! */
%token GLOBAL /* global */ %token GLOBAL /* global */
%left PLUS DASH %left PLUS DASH
%left STAR %left STAR
%nonassoc BANG %nonassoc BANG
@ -82,6 +85,7 @@ ty:
| TINT { TInt } | TINT { TInt }
| r=rtyp { TRef r } | r=rtyp { TRef r }
%inline ret_ty: %inline ret_ty:
| TVOID { RetVoid } | TVOID { RetVoid }
| t=ty { RetVal t } | t=ty { RetVal t }
@ -94,7 +98,7 @@ ty:
| PLUS { Add } | PLUS { Add }
| DASH { Sub } | DASH { Sub }
| STAR { Mul } | STAR { Mul }
| EQEQ { Eq } | EQEQ { Eq }
%inline uop: %inline uop:
| DASH { Neg } | DASH { Neg }
@ -103,7 +107,7 @@ ty:
gexp: gexp:
| t=rtyp NULL { loc $startpos $endpos @@ CNull t } | t=rtyp NULL { loc $startpos $endpos @@ CNull t }
| i=INT { loc $startpos $endpos @@ CInt i } | i=INT { loc $startpos $endpos @@ CInt i }
lhs: lhs:
| id=IDENT { loc $startpos $endpos @@ Id id } | id=IDENT { loc $startpos $endpos @@ Id id }

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 ) { bool foo ( int x, int[] y ) {
var s = bar (x, "compilerdesign"); var s = bar (x, "cis341");
proc1 (); proc1 ();
return true; return true;
} }

View file

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

View file

@ -1,52 +1,52 @@
global float_len = 2; global float_len = 2;
int[] determine_shift(int[] float) int[] determine_shift(int[] float)
{ {
var dec = float[1]; var dec = float[1];
var count = 0; var count = 0;
while(dec > 0) while(dec > 0)
{ {
var temp = float[0]; var temp = float[0];
float[0] = temp << 1; float[0] = temp << 1;
dec = dec >>> 1; dec = dec >>> 1;
count = count + 1; count = count + 1;
} }
var list = new int[2]; var list = new int[2];
list[0] = float[0] + float[1]; list[0] = float[0] + float[1];
list[1] = count; list[1] = count;
return list; return list;
} }
int[] multiply_floats(int[] f1, int[] f2) int[] multiply_floats(int[] f1, int[] f2)
{ {
var f1_shifted = determine_shift(f1); var f1_shifted = determine_shift(f1);
var f2_shifted = determine_shift(f2); var f2_shifted = determine_shift(f2);
var product = f1_shifted[0] * f2_shifted[0]; var product = f1_shifted[0] * f2_shifted[0];
var num_left_shifts = f1_shifted[1] + f2_shifted[1]; var num_left_shifts = f1_shifted[1] + f2_shifted[1];
var remainder = 0; var remainder = 0;
for(var i = 0; i < num_left_shifts; i=i+1;) for(var i = 0; i < num_left_shifts; i=i+1;)
{ {
var lsb = product [&] 1; var lsb = product [&] 1;
var shifted_lsb = lsb << i; var shifted_lsb = lsb << i;
product = product >>> 1; product = product >>> 1;
remainder = remainder + shifted_lsb; remainder = remainder + shifted_lsb;
} }
var ans = new int[2]; var ans = new int[2];
ans[0] = product; ans[0] = product;
ans[1] = remainder; ans[1] = remainder;
return ans; return ans;
} }
int program(int argc, string[] argv) int program(int argc, string[] argv)
{ {
var pi = new int[2]; var pi = new int[2];
pi[0] = 3; pi[0] = 3;
pi[1] = 14159; pi[1] = 14159;
var diameter = new int[2]; var diameter = new int[2];
diameter[0] = 20; diameter[0] = 20;
diameter[1] = 17; diameter[1] = 17;
var prod = multiply_floats(pi, diameter); var prod = multiply_floats(pi, diameter);
print_int(prod[0]); print_int(prod[0]);
print_string("."); print_string(".");
print_int(prod[1]); print_int(prod[1]);
return 0; return 0;
} }

View file

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

View file

@ -1,39 +1,39 @@
int mod (int a, int b) { int mod (int a, int b) {
var t = a; var t = a;
while (t - b >= 0) { while (t - b >= 0) {
t = t - b; t = t - b;
} }
return t; return t;
} }
int div (int a, int b) { int div (int a, int b) {
var result = 0; var result = 0;
var num = a; var num = a;
var denom = b; var denom = b;
while (num > 0) { while (num > 0) {
num = num - denom; num = num - denom;
result = result + 1; result = result + 1;
} }
return result; return result;
} }
int no_of_factors(int n) { int no_of_factors(int n) {
var num_fact = 1; var num_fact = 1;
var input = n; var input = n;
for (var i = 2; i * i < input + 1; i=i+1;) { for (var i = 2; i * i < input + 1; i=i+1;) {
var power = 0; var power = 0;
while (mod(n, i) == 0) { while (mod(n, i) == 0) {
n = div(n, i); n = div(n, i);
power = power + 1; power = power + 1;
} }
num_fact = num_fact * (power + 1); num_fact = num_fact * (power + 1);
} }
if (n > 1) { if (n > 1) {
num_fact = num_fact * 2; num_fact = num_fact * 2;
} }
return num_fact; return num_fact;
} }
int program (int argc, string[] argv) { int program (int argc, string[] argv) {
return no_of_factors(6400); return no_of_factors(6400);
} }

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