Format backend.ml in hw3.

Signed-off-by: Mariano Uvalle <u.g.a.mariano@gmail.com>
This commit is contained in:
Mariano Uvalle 2025-02-12 18:55:15 -08:00
parent ee8564b72b
commit ca3e1df031

View file

@ -2,7 +2,6 @@
open Ll open Ll
open X86 open X86
module Platform = Util.Platform module Platform = Util.Platform
(* Overview ----------------------------------------------------------------- *) (* Overview ----------------------------------------------------------------- *)
@ -12,19 +11,17 @@ module Platform = Util.Platform
plan for implementing the compiler is provided on the project web page. plan for implementing the compiler is provided on the project web page.
*) *)
(* helpers ------------------------------------------------------------------ *) (* helpers ------------------------------------------------------------------ *)
(* Map LL comparison operations to X86 condition codes *) (* Map LL comparison operations to X86 condition codes *)
let compile_cnd = function let compile_cnd = function
| Ll.Eq -> X86.Eq | Ll.Eq -> X86.Eq
| Ll.Ne -> X86.Neq | Ll.Ne -> X86.Neq
| Ll.Slt -> X86.Lt | Ll.Slt -> X86.Lt
| Ll.Sle -> X86.Le | Ll.Sle -> X86.Le
| Ll.Sgt -> X86.Gt | Ll.Sgt -> X86.Gt
| Ll.Sge -> X86.Ge | Ll.Sge -> X86.Ge
;;
(* locals and layout -------------------------------------------------------- *) (* locals and layout -------------------------------------------------------- *)
@ -56,14 +53,14 @@ type layout = (uid * X86.operand) list
(* A context contains the global type declarations (needed for getelementptr (* A context contains the global type declarations (needed for getelementptr
calculations) and a stack layout. *) calculations) and a stack layout. *)
type ctxt = { tdecls : (tid * ty) list type ctxt =
; layout : layout { tdecls : (tid * ty) list
} ; layout : layout
}
(* useful for looking up items in tdecls or layouts *) (* useful for looking up items in tdecls or layouts *)
let lookup m x = List.assoc x m let lookup m x = List.assoc x m
(* compiling operands ------------------------------------------------------ *) (* compiling operands ------------------------------------------------------ *)
(* LLVM IR instructions support several kinds of operands. (* LLVM IR instructions support several kinds of operands.
@ -72,17 +69,24 @@ let lookup m x = List.assoc x m
global addresses that must be computed from a label. Constants are global addresses that must be computed from a label. Constants are
immediately available, and the operand Null is the 64-bit 0 value. immediately available, and the operand Null is the 64-bit 0 value.
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").
(2) 64-bit assembly labels are not allowed as immediate operands. (2) 64-bit assembly labels are not allowed as immediate operands.
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) %rax. leaq instruction: leaq _gid(%rip) %rax.
NOTE(jmug): _gid(%rip) is interpreted as simply _gid ONLY when
the register is %rip and is called RIP relative addressing, read
more about it here: https://www.cs.unc.edu/~porter/courses/cse506/s16/ref/assembly.html#:~:text=RIP%20relative%20addressing,of%20the%20redundant%20SIB%20form.
TODO: The section below still reads like giberish,
mabye reading the rest of the code/tests will help.
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
@ -91,10 +95,9 @@ let lookup m x = List.assoc x m
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:ctxt) (dest:X86.operand) : Ll.operand -> ins = let compile_operand (ctxt : ctxt) (dest : X86.operand) : Ll.operand -> ins = function
function _ -> failwith "compile_operand unimplemented" | _ -> failwith "compile_operand unimplemented"
;;
(* compiling call ---------------------------------------------------------- *) (* compiling call ---------------------------------------------------------- *)
@ -147,9 +150,6 @@ let compile_operand (ctxt:ctxt) (dest:X86.operand) : Ll.operand -> ins =
] ]
*) *)
(* compiling getelementptr (gep) ------------------------------------------- *) (* compiling getelementptr (gep) ------------------------------------------- *)
(* The getelementptr instruction computes an address by indexing into (* The getelementptr instruction computes an address by indexing into
@ -162,7 +162,7 @@ let compile_operand (ctxt:ctxt) (dest:X86.operand) : Ll.operand -> ins =
*) *)
(* [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
- the size of an array of t's with n elements is n * the size of t - the size of an array of t's with n elements is n * the size of t
@ -172,11 +172,9 @@ let compile_operand (ctxt:ctxt) (dest:X86.operand) : Ll.operand -> ins =
- Void, i8, and functions have undefined sizes according to LLVMlite. - Void, i8, and functions have undefined sizes according to LLVMlite.
Your function should simply return 0 in those cases Your function should simply return 0 in those cases
*) *)
let rec size_ty (tdecls:(tid * ty) list) (t:Ll.ty) : int = let rec size_ty (tdecls : (tid * ty) list) (t : Ll.ty) : int =
failwith "size_ty not implemented" failwith "size_ty not implemented"
;;
(* Generates code that computes a pointer value. (* Generates code that computes a pointer value.
@ -185,28 +183,29 @@ failwith "size_ty not implemented"
2. the value of op is the base address of the calculation 2. the value of op is the base address of the calculation
3. the first index in the path is treated as the index into an array 3. the first index in the path is treated as the index into an array
of elements of type t located at the base address of elements of type t located at the base address
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
in (4), but relative to the type f the sub-element picked out in (4), but relative to the type f the sub-element picked out
by the path so far by the path so far
*) *)
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)
failwith "compile_gep not implemented" : ins list
=
failwith "compile_gep not implemented"
;;
(* compiling instructions -------------------------------------------------- *) (* compiling instructions -------------------------------------------------- *)
@ -231,16 +230,15 @@ failwith "compile_gep not implemented"
- 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 =
failwith "compile_insn not implemented" failwith "compile_insn not implemented"
;;
(* compiling terminators --------------------------------------------------- *) (* compiling terminators --------------------------------------------------- *)
(* prefix the function name [fn] to a label to ensure that the X86 labels are (* prefix the function name [fn] to a label to ensure that the X86 labels are
globally unique . *) globally unique . *)
let mk_lbl (fn:string) (l:string) = fn ^ "." ^ l let mk_lbl (fn : string) (l : string) = fn ^ "." ^ l
(* Compile block terminators is not too difficult: (* Compile block terminators is not too difficult:
@ -254,9 +252,9 @@ let mk_lbl (fn:string) (l:string) = fn ^ "." ^ l
[fn] - the name of the function containing this terminator [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 =
failwith "compile_terminator not implemented" failwith "compile_terminator not implemented"
;;
(* compiling blocks --------------------------------------------------------- *) (* compiling blocks --------------------------------------------------------- *)
@ -265,17 +263,16 @@ let compile_terminator (fn:string) (ctxt:ctxt) (t:Ll.terminator) : ins list =
[ctxt] - the current context [ctxt] - the current context
[blk] - LLVM IR code for the block [blk] - LLVM IR code for the block
*) *)
let compile_block (fn:string) (ctxt:ctxt) (blk:Ll.block) : ins list = let compile_block (fn : string) (ctxt : ctxt) (blk : Ll.block) : ins list =
failwith "compile_block not implemented" failwith "compile_block not implemented"
;;
let compile_lbl_block fn lbl ctxt blk : elem = let compile_lbl_block fn lbl ctxt blk : elem =
Asm.text (mk_lbl fn lbl) (compile_block fn ctxt blk) Asm.text (mk_lbl fn lbl) (compile_block fn ctxt blk)
;;
(* compile_fdecl ------------------------------------------------------------ *) (* compile_fdecl ------------------------------------------------------------ *)
(* Complete this helper function, which computes the location of the nth incoming (* Complete this helper function, which computes the location of the nth incoming
function argument: either in a register or relative to %rbp, function argument: either in a register or relative to %rbp,
according to the calling conventions. We will test this function as part of according to the calling conventions. We will test this function as part of
@ -285,9 +282,7 @@ let compile_lbl_block fn lbl ctxt blk : elem =
[ NOTE: the first six arguments are numbered 0 .. 5 ] [ NOTE: the first six arguments are numbered 0 .. 5 ]
*) *)
let arg_loc (n : int) : operand = let arg_loc (n : int) : operand = failwith "arg_loc not implemented"
failwith "arg_loc not implemented"
(* 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.
@ -296,10 +291,10 @@ failwith "arg_loc not implemented"
- 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 =
failwith "stack_layout not implemented" failwith "stack_layout not implemented"
;;
(* The code for the entry-point of a function must do several things: (* The code for the entry-point of a function must do several things:
@ -317,28 +312,32 @@ failwith "stack_layout not implemented"
- the function entry code should allocate the stack storage needed - the function entry code should allocate the stack storage needed
to hold all of the local stack slots. to hold all of the local stack slots.
*) *)
let compile_fdecl (tdecls:(tid * ty) list) (name:string) ({ f_param; f_cfg; _ }:fdecl) : prog = let compile_fdecl
failwith "compile_fdecl unimplemented" (tdecls : (tid * ty) list)
(name : string)
({ f_param; f_cfg; _ } : fdecl)
: prog
=
failwith "compile_fdecl unimplemented"
;;
(* compile_gdecl ------------------------------------------------------------ *) (* compile_gdecl ------------------------------------------------------------ *)
(* Compile a global value into an X86 global data declaration and map (* Compile a global value into an X86 global data declaration and map
a global uid to its associated X86 label. a global uid to its associated X86 label.
*) *)
let rec compile_ginit : ginit -> X86.data list = function let rec compile_ginit : ginit -> X86.data list = function
| GNull -> [Quad (Lit 0L)] | GNull -> [ Quad (Lit 0L) ]
| GGid gid -> [Quad (Lbl (Platform.mangle gid))] | GGid gid -> [ Quad (Lbl (Platform.mangle gid)) ]
| 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
(* compile_prog ------------------------------------------------------------- *) (* compile_prog ------------------------------------------------------------- *)
let compile_prog {tdecls; gdecls; fdecls; _} : X86.prog = let compile_prog { tdecls; gdecls; fdecls; _ } : X86.prog =
let g = fun (lbl, gdecl) -> Asm.data (Platform.mangle lbl) (compile_gdecl gdecl) in let g (lbl, gdecl) = Asm.data (Platform.mangle lbl) (compile_gdecl gdecl) in
let f = fun (name, fdecl) -> compile_fdecl tdecls name fdecl in let f (name, fdecl) = compile_fdecl tdecls name fdecl in
(List.map g gdecls) @ (List.map f fdecls |> List.flatten) List.map g gdecls @ (List.map f fdecls |> List.flatten)
;;