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 X86
module Platform = Util.Platform
(* Overview ----------------------------------------------------------------- *)
@ -12,19 +11,17 @@ module Platform = Util.Platform
plan for implementing the compiler is provided on the project web page.
*)
(* helpers ------------------------------------------------------------------ *)
(* Map LL comparison operations to X86 condition codes *)
let compile_cnd = function
| Ll.Eq -> X86.Eq
| Ll.Ne -> X86.Neq
| Ll.Eq -> X86.Eq
| Ll.Ne -> X86.Neq
| Ll.Slt -> X86.Lt
| Ll.Sle -> X86.Le
| Ll.Sgt -> X86.Gt
| Ll.Sge -> X86.Ge
;;
(* locals and layout -------------------------------------------------------- *)
@ -56,14 +53,14 @@ type layout = (uid * X86.operand) list
(* A context contains the global type declarations (needed for getelementptr
calculations) and a stack layout. *)
type ctxt = { tdecls : (tid * ty) list
; layout : layout
}
type ctxt =
{ tdecls : (tid * ty) list
; layout : layout
}
(* useful for looking up items in tdecls or layouts *)
let lookup m x = List.assoc x m
(* compiling 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
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
suitable for naming a global label on your platform (OS X expects
"_main" while linux expects "main").
(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").
(2) 64-bit assembly labels are not allowed as immediate operands.
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) %rax.
(2) 64-bit assembly labels are not allowed as immediate operands.
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) %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
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
destination (usually a register).
*)
let compile_operand (ctxt:ctxt) (dest:X86.operand) : Ll.operand -> ins =
function _ -> failwith "compile_operand unimplemented"
let compile_operand (ctxt : ctxt) (dest : X86.operand) : Ll.operand -> ins = function
| _ -> failwith "compile_operand unimplemented"
;;
(* compiling call ---------------------------------------------------------- *)
@ -147,9 +150,6 @@ let compile_operand (ctxt:ctxt) (dest:X86.operand) : Ll.operand -> ins =
]
*)
(* compiling getelementptr (gep) ------------------------------------------- *)
(* 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.
(needed for getelementptr)
(needed for getelementptr)
- 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
@ -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.
Your function should simply return 0 in those cases
*)
let rec size_ty (tdecls:(tid * ty) list) (t:Ll.ty) : int =
failwith "size_ty not implemented"
let rec size_ty (tdecls : (tid * ty) list) (t : Ll.ty) : int =
failwith "size_ty not implemented"
;;
(* 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
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:
- 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
- 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
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 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
- if t is any other type, the path is invalid
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
by the path so far
in (4), but relative to the type f the sub-element picked out
by the path so far
*)
let compile_gep (ctxt:ctxt) (op : Ll.ty * Ll.operand) (path: Ll.operand list) : ins list =
failwith "compile_gep not implemented"
let compile_gep (ctxt : ctxt) (op : Ll.ty * Ll.operand) (path : Ll.operand list)
: ins list
=
failwith "compile_gep not implemented"
;;
(* compiling instructions -------------------------------------------------- *)
@ -231,16 +230,15 @@ failwith "compile_gep not implemented"
- Bitcast: does nothing interesting at the assembly level
*)
let compile_insn (ctxt:ctxt) ((uid:uid), (i:Ll.insn)) : X86.ins list =
failwith "compile_insn not implemented"
let compile_insn (ctxt : ctxt) ((uid : uid), (i : Ll.insn)) : X86.ins list =
failwith "compile_insn not implemented"
;;
(* compiling terminators --------------------------------------------------- *)
(* prefix the function name [fn] to a label to ensure that the X86 labels are
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:
@ -254,9 +252,9 @@ let mk_lbl (fn:string) (l:string) = fn ^ "." ^ l
[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"
;;
(* compiling blocks --------------------------------------------------------- *)
@ -265,17 +263,16 @@ let compile_terminator (fn:string) (ctxt:ctxt) (t:Ll.terminator) : ins list =
[ctxt] - the current context
[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"
;;
let compile_lbl_block fn lbl ctxt blk : elem =
Asm.text (mk_lbl fn lbl) (compile_block fn ctxt blk)
;;
(* compile_fdecl ------------------------------------------------------------ *)
(* Complete this helper function, which computes the location of the nth incoming
function argument: either in a register or relative to %rbp,
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 ]
*)
let arg_loc (n : int) : operand =
failwith "arg_loc not implemented"
let arg_loc (n : int) : operand = failwith "arg_loc not implemented"
(* We suggest that you create a helper function that computes the
stack layout for a given function declaration.
@ -296,10 +291,10 @@ failwith "arg_loc not implemented"
- in this (inefficient) compilation strategy, each local id
is also stored as a stack slot.
- see the discussion about locals
*)
let stack_layout (args : uid list) ((block, lbled_blocks):cfg) : layout =
failwith "stack_layout not implemented"
let stack_layout (args : uid list) ((block, lbled_blocks) : cfg) : layout =
failwith "stack_layout not implemented"
;;
(* 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
to hold all of the local stack slots.
*)
let compile_fdecl (tdecls:(tid * ty) list) (name:string) ({ f_param; f_cfg; _ }:fdecl) : prog =
failwith "compile_fdecl unimplemented"
let compile_fdecl
(tdecls : (tid * ty) list)
(name : string)
({ f_param; f_cfg; _ } : fdecl)
: prog
=
failwith "compile_fdecl unimplemented"
;;
(* compile_gdecl ------------------------------------------------------------ *)
(* Compile a global value into an X86 global data declaration and map
a global uid to its associated X86 label.
*)
let rec compile_ginit : ginit -> X86.data list = function
| GNull -> [Quad (Lit 0L)]
| GGid gid -> [Quad (Lbl (Platform.mangle gid))]
| GInt c -> [Quad (Lit c)]
| GString s -> [Asciz s]
| GNull -> [ Quad (Lit 0L) ]
| GGid gid -> [ Quad (Lbl (Platform.mangle gid)) ]
| 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
(* compile_prog ------------------------------------------------------------- *)
let compile_prog {tdecls; gdecls; fdecls; _} : X86.prog =
let g = fun (lbl, gdecl) -> Asm.data (Platform.mangle lbl) (compile_gdecl gdecl) in
let f = fun (name, fdecl) -> compile_fdecl tdecls name fdecl in
(List.map g gdecls) @ (List.map f fdecls |> List.flatten)
let compile_prog { tdecls; gdecls; fdecls; _ } : X86.prog =
let g (lbl, gdecl) = Asm.data (Platform.mangle lbl) (compile_gdecl gdecl) in
let f (name, fdecl) = compile_fdecl tdecls name fdecl in
List.map g gdecls @ (List.map f fdecls |> List.flatten)
;;