DatStorm

Untitled

Dec 3rd, 2018
5,024
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 30.51 KB | None | 0 0
  1. (* AU Compilation 2016 *)
  2. (* DO NOT DISTRIBUTE *)
  3.  
  4.  
  5. (* LLVM-- to x86 backend skeleton file *)
  6. (* Based on LLVMLite X86 backend@UPenn *)
  7.  
  8.  
  9.  
  10.  
  11. (* ll ir compilation -------------------------------------------------------- *)
  12.  
  13.  
  14. signature X86BACKEND =
  15. sig
  16. val compile_prog: ll.prog -> X86.prog
  17. end
  18.  
  19.  
  20. structure X86Backend :> X86BACKEND =
  21. struct
  22.  
  23.  
  24. structure S = Symbol
  25.  
  26. open X86
  27. open ll
  28. open Asm
  29.  
  30.  
  31. exception IlegalPointer
  32. exception NotImplemented
  33. exception BackEndFatal
  34.  
  35. fun CallOwnPrint(s) = ()
  36. fun errorFunc s = (print ("==> ERROR: " ^ s ^ "\n"); raise BackEndFatal)
  37. fun TODO(s) = (print("We hit a TODO. Implement: " ^ s ^ " \n"); raise NotImplemented)
  38.  
  39.  
  40.  
  41. (* Helpers ------------------------------------------------------------------ *)
  42.  
  43.  
  44. (* Platform-specific generation of symbols *)
  45.  
  46. val mangle =
  47. fn s =>
  48. (case LocalOS.os of
  49. LocalOS.Linux => (Symbol.name s)
  50. | LocalOS.Darwin => "_" ^ (Symbol.name s))
  51.  
  52.  
  53. (* Map ll comparison operations to X86 condition codes *)
  54. fun compile_cnd (c:ll.cnd) : X86.cnd =
  55. case c of
  56. ll.Eq => X86.Eq
  57. | ll.Ne => X86.Neq
  58. | ll.Slt => X86.Lt
  59. | ll.Sle => X86.Le
  60. | ll.Sgt => X86.Gt
  61. | ll.Sge => X86.Ge
  62.  
  63.  
  64.  
  65. (* locals and layout -------------------------------------------------------- *)
  66. (* We call the datastructure that maps each %uid to its stack slot a
  67. 'stack layout'. A stack layout maps a uid to an X86 operand for
  68. accessing its contents. For this compilation strategy, the operand
  69. is always an offset from rbp (in bytes) that represents a storage slot in
  70. the stack.
  71. *)
  72.  
  73. type layout = (ll.uid * X86.operand) list
  74.  
  75. (* A context contains the global type declarations (needed for getelementptr
  76. calculations) and a stack layout. *)
  77. type ctxt = { tdecls : (ll.tid * ll.ty) list
  78. , layout : layout}
  79.  
  80. (* useful for looking up items in tdecls or layouts *)
  81. fun lookup layout (uid:ll.uid) =
  82. let
  83. val (_, operand) = (valOf (List.find (fn (y, _) => uid = y) layout))
  84. in
  85. operand
  86. end handle _ => errorFunc("lookup fail: " ^(Symbol.name uid) ^ " not found")
  87.  
  88. fun getListElement(list, i) =
  89. if ((List.length list) <= i) then
  90. errorFunc("getListElement " ^ Int.toString(i) ^ " called on list of length " ^ Int.toString(List.length list) ^ "\n")
  91. else List.nth(list, i)
  92.  
  93.  
  94. fun getFirstNelementOfList(list, i) =
  95. if ((List.length list) <= i) then
  96. errorFunc("getFirstNelementOfList " ^ Int.toString(i) ^ " called on list of length " ^ Int.toString(List.length list) ^ "\n")
  97. else List.take(list, i)
  98.  
  99. fun getLastNelements(list, i) =
  100. if ((List.length list) <= i) then
  101. errorFunc("getLastNelements " ^ Int.toString(i) ^ " called on list of length " ^ Int.toString(List.length list) ^ "\n")
  102. else List.drop(list, i)
  103.  
  104.  
  105. (* compiling operands ------------------------------------------------------ *)
  106.  
  107. (* LLVM IR instructions support several kinds of operands.
  108.  
  109. LL local %uids live in stack slots, whereas global ids live at
  110. global addresses that must be computed from a label. Constants are
  111. immediately available, and the operand Null is the 64-bit 0 value.
  112.  
  113. NOTE: two important facts about global identifiers:
  114.  
  115. (1) You should use (mangle gid) to obtain a string
  116. suitable for naming a global label on your platform (macOS expects
  117. "_main" while linux expects "main").
  118.  
  119. (2) 64-bit assembly labels are not allowed as immediate operands.
  120. That is, the X86 code: movq _gid %rax which looks like it should
  121. put the address denoted by _gid into %rax is not allowed.
  122. Instead, you need to compute an %rip-relative address using the
  123. leaq instruction: leaq _gid(%rip).
  124.  
  125. One strategy for compiling instruction operands is to use a
  126. designated register (or registers) for holding the values being
  127. manipulated by the LLVM IR instruction. You might find it useful to
  128. implement the following helper function, whose job is to generate
  129. the X86 instruction that moves an LLVM operand into a designated
  130. destination (usually a register).
  131. *)
  132.  
  133. fun compile_operand (ctxt as {tdecls, layout}) (dest:X86.reg) (oper:ll.operand) : X86.ins =
  134. let
  135. (* val _ = CallOwnPrint("Called='compile_operand'\n") *)
  136. val casing = case oper of
  137. Null => (Movq, [(Imm (Lit 0)) , Reg dest]) (*As described in comments null is 64 bit 0 value*)
  138. | Gid (gid) => (Leaq, [Ind3 ((Lbl (mangle gid )), Rip), Reg dest]) (*LEA = load effitice address*)
  139. | Const(int_o)=> (Movq, [(Imm (Lit int_o)) , Reg dest]) (*Const is an int ready to convert*)
  140. | Id(uid) => (Movq, [(lookup layout uid ) , Reg dest]) (*find the id in the lookup*)
  141. in
  142. casing
  143. end
  144.  
  145. fun trans_operand (ctxt as {tdecls, layout}) oper : X86.operand =
  146. let
  147. val _ = CallOwnPrint("Called='trans_operand'\n")
  148. val casing = case oper of
  149. Gid (gid) => TODO("trans_operand= Gid") (*Remember leaq*)
  150. | Null => Imm (Lit 0)
  151. | Const(int_o)=> Imm (Lit int_o)
  152. | Id(uid) => lookup layout uid
  153. in
  154. casing
  155. end
  156.  
  157. (* compiling call ---------------------------------------------------------- *)
  158.  
  159. (* You will probably find it helpful to implement a helper function that
  160. generates code for the LLVM IR call instruction.
  161.  
  162. The code you generate should follow the x64 System V AMD64 ABI
  163. calling conventions, which places the first six 64-bit (or smaller)
  164. values in registers and pushes the rest onto the stack. Note that,
  165. since all LLVM IR operands are 64-bit values, the first six
  166. operands will always be placed in registers. (See the notes about
  167. compiling fdecl below.)
  168.  
  169. [ NOTE: It is the caller's responsibility to clean up arguments
  170. pushed onto the stack, so you must free the stack space after the
  171. call returns. ]
  172.  
  173. [ NOTE: Don't forget to preserve caller-save registers (only if
  174. needed). ]
  175. *)
  176.  
  177. (*Calling conventions on page 136. *)
  178. fun compile_call (uid_opt, ctxt as {tdecls, layout}, ty, oper, tyOperList) =
  179. let
  180. val _ = CallOwnPrint("Called='compile_insn.ll.Call.compile_call'\n")
  181. val gid = case oper of
  182. Gid x => x
  183. | _ => errorFunc("compile_call gid not found.")
  184.  
  185. (* val _ = CallOwnPrint("GID: " ^ (S.name gid) ^ "\n") *)
  186. val funName = mangle gid
  187. val numberOfArguments = ref 0
  188. val tyOperListFirst6 = if (List.length tyOperList < 6) then
  189. tyOperList
  190. else
  191. getFirstNelementOfList(tyOperList, 6)
  192.  
  193. (*Handle the first six arguments to function call *)
  194. val first6insns =
  195. map (fn (tyArg, operArg) =>
  196. let
  197. val target = case (!numberOfArguments) of
  198. 0 => Rdi
  199. | 1 => Rsi
  200. | 2 => Rdx
  201. | 3 => Rcx
  202. | 4 => R08
  203. | 5 => R09
  204. | _ => errorFunc("compile_call: we fucked up found to many arguments. Errorcode 4")
  205. val insn_src = compile_operand ctxt target operArg
  206. val temp = !numberOfArguments
  207. in
  208. (numberOfArguments := temp +1; [insn_src])
  209. end
  210. ) tyOperListFirst6
  211.  
  212. (*Remove the first six since we have allready done those... *)
  213. val removeFirstSix = if (List.length tyOperList > 6) then
  214. getLastNelements(tyOperList, 6)
  215. else
  216. []
  217.  
  218. (*Get the last argumetns to function *)
  219. val lastInsns =
  220. map (fn (tyArg, operArg) =>
  221. let
  222. val insn_src = compile_operand ctxt R11 operArg
  223. val push_insn = (Pushq, [Reg R11])
  224. (* val _ = CallOwnPrint("-------> " ^ (ll.string_of_ty tyArg) ^ ":" ^(ll.string_of_operand operArg) ^ "\n") *)
  225. (* val source = trans_operand ctxt operArg *)
  226. (* val push_insn = (Pushq, [source]) kan evt. bruges når Mark har løst den.*)
  227. in
  228. [insn_src, push_insn]
  229. end
  230. (*Reverse the list so the order they get on the stack is correct. *)
  231. ) (List.rev removeFirstSix)
  232.  
  233.  
  234. val allInsns = (List.concat first6insns) @ (List.concat lastInsns)
  235.  
  236. (*call the function*)
  237. val call_insn = (Callq, [(Imm (Lbl funName))])
  238.  
  239. (*Get how many arguments we pushed to the stack*)
  240. val offset = (List.length removeFirstSix ) * 8
  241.  
  242. (* val _ = CallOwnPrint("-------> Offset: " ^ (Int.toString offset) ^ "\n") *)
  243.  
  244. (* Reset stackpointer *)
  245. val resetSP = (Addq, [ Asm.~$ offset, Reg(Rsp) ] )
  246. val res = case uid_opt of
  247. SOME x =>
  248. let
  249. val target = lookup layout x
  250. val save_res_insn = (Movq, [(Reg Rax), target])
  251. in
  252. allInsns @ [call_insn, resetSP, save_res_insn]
  253. end
  254. | _ =>
  255. allInsns @ [call_insn, resetSP]
  256.  
  257. in
  258. res
  259. end
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.  
  268.  
  269.  
  270.  
  271.  
  272.  
  273.  
  274. (* Function size_ty maps an LLVMlite type to a size in bytes.
  275. (needed for getelementptr)
  276.  
  277. - the size of a struct is the sum of the sizes of each component
  278. - the size of an array of t's with n elements is n * the size of t
  279. - all pointers, I1, and I64 are 8 bytes
  280. - the size of a named type is the size of its definition
  281.  
  282. - Void, i8, and functions have undefined sizes according to LLVMlite
  283. your function should simply return 0
  284. *)
  285.  
  286. fun size_ty (tdecls, ty) : int =
  287. case ty of
  288. ll.Void => 0
  289. | ll.I1 => 8
  290. | ll.I8 => 0
  291. | ll.I64 => 8
  292. | ll.Ptr ty => 8
  293. | ll.Struct tys => 8*(List.length tys)
  294. | ll.Array (size, ty) => (size_ty(tdecls, ty)) * size
  295. | ll.Fun fty => 0
  296. | ll.Namedt tid => size_ty(tdecls, (lookup tdecls tid))
  297.  
  298.  
  299. fun get_namedt(tdecls, typeid:ll.tid) =
  300. let
  301. val type_opt = List.find (fn (tid, ty) => tid=typeid) tdecls
  302. val _ = if not (isSome type_opt) then errorFunc("get_namdt failed. tid not found") else ()
  303. in
  304. #2 (valOf type_opt)
  305. end
  306.  
  307.  
  308.  
  309. (* compiling getelementptr (gep) ------------------------------------------- *)
  310.  
  311. (* The getelementptr instruction computes an address by indexing into
  312. a datastructure, following a path of offsets. It computes the
  313. address based on the size of the data, which is dictated by the
  314. data's type.
  315.  
  316. To compile getelmentptr, you must generate x86 code that performs
  317. the appropriate arithemetic calculations.
  318. *)
  319.  
  320.  
  321. (* Function compile_gep generates code that computes a pointer value.
  322.  
  323. 1. op must be of pointer type: t*
  324.  
  325. 2. the value of op is the base address of the calculation
  326.  
  327. 3. the first index in the path is treated as the index into an array
  328. of elements of type t located at the base address
  329.  
  330. 4. subsequent indices are interpreted according to the type t:
  331.  
  332. - if t is a struct, the index must be a constant n and it
  333. picks out the n'th element of the struct. [ NOTE: the offset
  334. within the struct of the n'th element is determined by the
  335. sizes of the types of the previous elements ]
  336.  
  337. - if t is an array, the index can be any operand, and its
  338. value determines the offset within the array.
  339.  
  340. - if t is any other type, the path is invalid
  341.  
  342. 5. if the index is valid, the remainder of the path is computed as
  343. in (4), but relative to the type of the sub-element picked out
  344. by the path so far
  345. *)
  346.  
  347. fun compile_gep (ctxt as {tdecls, layout} :ctxt)
  348. (oper : ll.ty * ll.operand)
  349. ([]: ll.operand list) : X86.ins list = errorFunc("Gep called with empty path.")
  350. | compile_gep (ctxt as {tdecls, layout} :ctxt)
  351. (oper : ll.ty * ll.operand)
  352. (paths: ll.operand list) : X86.ins list =
  353. let
  354. val _ = CallOwnPrint(".compile_gep'\n")
  355. val base_ty = (#1 oper)
  356.  
  357. val base:X86.operand = case (#2 oper) of
  358. Id uid => lookup layout uid
  359. | Null => ~$ 0
  360. | _ => errorFunc("Gep operand not an Id: " ^ (ll.string_of_operand (#2 oper)))
  361.  
  362. (* Init result register to base *)
  363. val initbase_insn = (Movq, [base, Reg R11])
  364.  
  365. (* Store param in R11 *)
  366. val a = compile_operand ctxt Rax (hd paths)
  367.  
  368. (* Store size in R10 *)
  369. val elem_size = size_ty(tdecls, base_ty)
  370. val b = (Movq, [Imm (Lit elem_size), Reg R10])
  371.  
  372. (* compute offset *)
  373. (* %rax = %rax * %r10 *)
  374. val c = (Imulq, [Reg R10])
  375.  
  376. (* add offset *)
  377. (* %r11 = %r11 - %rax *)
  378. val d = (Addq, [Reg Rax, Reg R11])
  379.  
  380.  
  381. (* Compute pointer, stores result in %r11 *)
  382. fun gep (ty_old, []) = []
  383. | gep (ty_old, path::paths) =
  384. let
  385. (* val _ = CallOwnPrint("gep recursion on " ^ (ll.string_of_ty(ty_old)) ^ " with oper: " ^(ll.string_of_operand path) ^ "\n") *)
  386. in
  387. case ty_old of
  388. ll.Struct tys =>
  389. let
  390. (* Operand is always const in struct gep *)
  391. val num : int =
  392. case path of ll.Const num => num
  393. | _ => errorFunc("Gep path on struct must be ll.Const")
  394.  
  395. val ty = getListElement(tys, num)
  396. val offset = 8 * num (* All values have size 8 *)
  397.  
  398. (* add offset from result register r11 *)
  399. val a = (Addq, [Imm (Lit offset), Reg R11])
  400.  
  401. val rest = (gep(ty, paths))
  402. in
  403. a::rest
  404. end
  405. | ll.Ptr ty =>
  406. let
  407. (* Store param in R11 *)
  408. val a = compile_operand ctxt Rax path
  409.  
  410. (* Store size in R10 *)
  411. val elem_size = size_ty(tdecls, ty_old)
  412. val b = (Movq, [Imm (Lit elem_size), Reg R10])
  413.  
  414. (* compute offset *)
  415. (* %rax = %rax * %r10 *)
  416. val c = (Imulq, [Reg R10])
  417.  
  418. (* add offset *)
  419. (* %r11 = %r11 - %rax *)
  420. val d = (Addq, [Reg Rax, Reg R11])
  421.  
  422. val rest = (gep(ty, paths))
  423. in
  424. a::b::c::d::rest
  425. end
  426. | ll.Namedt tid => gep(get_namedt(tdecls, tid), path::paths)
  427. | ll.Array (size, ty)=> TODO("gep array not implemented, and not used in our LLVM implementation\n")
  428. | _ => errorFunc("Illegal gep type in path. Type = " ^ (ll.string_of_ty(ty_old)))
  429. end
  430.  
  431. in
  432. (* Stores ptr result in %r11 *)
  433. initbase_insn::a::b::c::d::gep(base_ty, tl paths)
  434. end
  435.  
  436.  
  437.  
  438.  
  439. (* compiling instructions -------------------------------------------------- *)
  440.  
  441. (* The result of compiling a single LLVM instruction might be many x86
  442. instructions. We have not determined the structure of this code
  443. for you. Some of the instructions require only a couple assembly
  444. instructions, while others require more. We have suggested that
  445. you need at least compile_operand, compile_call, and compile_gep
  446. helpers; you may introduce more as you see fit.
  447.  
  448. Here are a few notes:
  449.  
  450. - Icmp: the Set instruction may be of use. Depending on how you
  451. compile Cbr, you may want to ensure that the value produced by
  452. Icmp is exactly 0 or 1.
  453.  
  454. - Load & Store: these need to dereference the pointers. Const and
  455. Null operands aren't valid pointers. Don't forget to mangle
  456. the global identifier.
  457.  
  458. - Alloca: needs to return a pointer into the stack
  459.  
  460. - Bitcast, Ptrtoint, Zext: do nothing interesting at the assembly level
  461. *)
  462.  
  463. fun lookup_uid_opt(uid_opt,layout ,s) = case uid_opt of
  464. SOME x => (lookup layout x )
  465. | _ => errorFunc("Could not find 'uid_opt' in " ^ s ^ " errorcode 3")
  466.  
  467.  
  468.  
  469. (* Should dereference?, as in (%reg), not %reg, as we do now??? *)
  470. fun deref_ptr (ctxt as {tdecls, layout}) (oper:ll.operand) : X86.operand =
  471. let
  472. val _ = CallOwnPrint("Called='trans_operand'\n")
  473. val casing = case oper of
  474. Gid (gid) => TODO("trans_operand= Gid") (*Remember leaq*)
  475. | Null => raise IlegalPointer
  476. | Const(int_o)=> raise IlegalPointer
  477. | Id(uid) => (lookup layout uid)
  478. in
  479. casing
  480. end
  481.  
  482. fun compile_insn (ctxt as {tdecls, layout}) (uid_opt, insn:ll.insn) : X86.ins list =
  483. case insn of
  484. ll.Binop (bop, ty, oper1, oper2) =>
  485. let
  486. val _ = CallOwnPrint("Called='compile_insn.ll.Binop'\n")
  487.  
  488. (*Find et register til oper1 og oper2*)
  489. val oper1_insn = compile_operand ctxt Rax oper1 (*dest. register Should be Rax*)
  490. val oper2_insn = compile_operand ctxt R10 oper2
  491.  
  492. (* Where should be put the result on the stack *)
  493. val destBop = lookup_uid_opt(uid_opt, layout, "binop uid_opt")
  494.  
  495. val binop_insns = case bop of
  496. Add => [(Addq , [Reg(R10), Reg(Rax)])]
  497. | Sub => [(Subq , [Reg(R10), Reg(Rax)])]
  498. | Mul => [(Imulq , [Reg(R10)])]
  499. | SDiv => [(Cqto , []), (Idivq, [Reg(R10)])]
  500. | Shl => [(Shlq , [Reg(R10), Reg(Rax)])]
  501. | Lshr => [(Shrq , [Reg(R10), Reg(Rax)])]
  502. | Ashr => [(Sarq , [Reg(R10), Reg(Rax)])]
  503. | And => [(Andq , [Reg(R10), Reg(Rax)])]
  504. | Or => [(Orq , [Reg(R10), Reg(Rax)])]
  505. | Xor => [(Xorq , [Reg(R10), Reg(Rax)])]
  506.  
  507. val c = (Movq, [Reg(Rax), destBop])
  508. in
  509. (* TODO("Binop") *)
  510. oper1_insn::oper2_insn::(binop_insns @ [c])
  511. end
  512. | ll.Alloca ty =>
  513. let
  514. val _ = CallOwnPrint("Called='compile_insn.ll.Alloca'\n")
  515.  
  516. val alloc_oper = lookup_uid_opt(uid_opt, layout, "binop uid_opt")
  517. val byte_size = size_ty(tdecls, ty)
  518.  
  519. val grow_stack_insn = (Subq, [Asm.~$ byte_size, Asm.~% Rsp]) (* Alloc on heap *)
  520. val store_ptr_insn = (Movq, [Reg Rsp, alloc_oper])
  521. in
  522. [grow_stack_insn, store_ptr_insn]
  523. end
  524.  
  525. | ll.Load (ty, oper) =>
  526. let
  527. val _ = CallOwnPrint("Called='compile_insn.ll.Load'\n")
  528. val oper_insn = compile_operand ctxt Rax oper
  529. val deref_insn = (Movq, [Ind2 Rax, Reg Rax]) (* We dereference rax to load value into rax again*)
  530. val dest_oper = lookup layout (valOf uid_opt)
  531. val load_insn = (Movq, [Reg Rax, dest_oper])
  532. in
  533. [oper_insn, deref_insn, load_insn]
  534. end
  535.  
  536. | ll.Store (ty, val_oper, dest_oper) =>
  537. let
  538. val _ = CallOwnPrint("Called='compile_insn.ll.Store'\n")
  539. val oper_insn = compile_operand ctxt Rax val_oper
  540. (* Move pointer into register, so we can dereference with Ind2 *)
  541. val move_ptr_insn = compile_operand ctxt R10 dest_oper
  542.  
  543. (* Move the value from the compiled lloperand, into the dereferenced destination *)
  544. val move_insn = (Movq, [Reg Rax, Ind2 R10])
  545.  
  546. in
  547. [oper_insn, move_ptr_insn, move_insn]
  548. end
  549.  
  550. | ll.Icmp (cnd, ty, oper1, oper2) =>
  551. let
  552. val _ = CallOwnPrint("Called='compile_insn.ll.Icmp'\n")
  553.  
  554. (* Where should be put the result on the stack *)
  555. val dest = lookup_uid_opt(uid_opt, layout ,"cmp uid_opt")
  556.  
  557. val oper1_insn = compile_operand ctxt Rax oper1 (*dest. register Should be Rax*)
  558. val oper2_insn = compile_operand ctxt R11 oper2
  559. val compare_insn = (Cmpq, [Reg(R11), Reg(Rax)]) (* compares and sets flags *)
  560.  
  561. val clear_insn = (Movq, [~$ 0, dest])
  562. val set_insn = (Set (compile_cnd cnd), [dest])
  563. in
  564. [oper1_insn, oper2_insn, compare_insn, clear_insn, set_insn]
  565. end
  566. | ll.Gep (ty, oper, operList) =>
  567. let
  568. val _ = CallOwnPrint("Called='compile_insn.ll.Gep")
  569.  
  570. (* Where should be put the result on the stack *)
  571. val dest = lookup_uid_opt(uid_opt, layout ,"ll.Gep")
  572. val insns:X86.ins list = compile_gep ctxt (ty, oper) operList
  573. val endInsn = (Movq, [(Reg R11), dest])
  574. in
  575. insns @ [endInsn]
  576. end
  577. | ll.Call(ty, oper, tyOperList) => compile_call(uid_opt, ctxt, ty, oper, tyOperList)
  578. | ll.Bitcast (ty1, oper, ty2) => handlePtrtoIntZextAndBitcast(uid_opt, ctxt, oper, layout, "Bitcast")
  579. | ll.Zext (ty1, oper, ty2) => handlePtrtoIntZextAndBitcast(uid_opt, ctxt, oper, layout, "Zext")
  580. | ll.Ptrtoint (ty1, oper, ty2) => handlePtrtoIntZextAndBitcast(uid_opt, ctxt, oper, layout, "Ptrtoint")
  581.  
  582.  
  583. and handlePtrtoIntZextAndBitcast(uid_opt, ctxt, oper ,layout, s) =
  584. let
  585. val _ = CallOwnPrint("Called='compile_insn.ll."^ s ^"'\n")
  586.  
  587. (* Where should be put the result on the stack *)
  588. val target = lookup_uid_opt(uid_opt, layout ,s)
  589. val src = compile_operand ctxt R11 oper
  590. val insn_move = (Movq, [Reg R11, target])
  591. in
  592. [src, insn_move ]
  593. end
  594.  
  595. (* compiling terminators --------------------------------------------------- *)
  596.  
  597. (* Compile block terminators is not too difficult:
  598.  
  599. - Ret should properly exit the function: freeing stack space,
  600. restoring the value of %rbp, and putting the return value (if
  601. any) in %rax.
  602.  
  603. - Br should jump
  604.  
  605. - Cbr branch should treat its operand as a boolean conditional
  606. *)
  607.  
  608. fun compile_terminator ctxt terminator =
  609. let
  610. val _ = CallOwnPrint("Called='compile_terminator'\n")
  611.  
  612. (**************
  613. (* Block terminators *)
  614. datatype terminator
  615. = Ret of ty * operand option (* ret i64 %s *)
  616. | Br of lbl (* br label %lbl *)
  617. | Cbr of operand * lbl * lbl (* br i1 %s, label %l1, label %l2 *)
  618. *)
  619. val res =
  620. case terminator of
  621. ll.Ret (ty, oper_option) =>
  622. let
  623. (*
  624. *)
  625. val _ = CallOwnPrint("Called='compile_terminator.ll.Ret'\n")
  626.  
  627. (* If we should return a value, *)
  628. val optional_move_insn =
  629. if isSome oper_option then
  630. [compile_operand ctxt Rax (valOf oper_option)]
  631. else
  632. [(Movq, [~$ 0, ~% Rax])]
  633.  
  634. val a = (Movq, [~% Rbp, ~% Rsp]) (* Reset %rsp to the value stored in %rbp *)
  635. val b = (Popq, [~% Rbp]) (* Pop old base pointer from stack into %rbp*)
  636. val c = (Retq, []) (* Return instruction *)
  637. in
  638. optional_move_insn @ [a,b,c]
  639. end
  640.  
  641. | ll.Br (lbl) => [(X86.Jmp, [X86.Imm (X86.Lbl (S.name lbl))])] (* Jump to label *)
  642. | ll.Cbr (oper, lbl1, lbl2) =>
  643. let
  644. val _ = CallOwnPrint("Called='compile_terminator.ll.Cbr'\n")
  645. val oper_insn = compile_operand ctxt Rax oper
  646. val cmp_insn = (Cmpq, [ ~$ 0, ~% Rax])
  647.  
  648. val jmp1 = (J(X86.Neq) , [~$$ (S.name lbl1)])
  649. val jmp2 = (Jmp, [~$$ (S.name lbl2)])
  650. in
  651. [oper_insn, cmp_insn, jmp1, jmp2]
  652. end
  653. in
  654. res
  655. end
  656.  
  657.  
  658. (* compiling blocks --------------------------------------------------------- *)
  659.  
  660. (* We have left this helper function here for you to complete. *)
  661. fun compile_block ctxt (block as {insns, terminator}) : X86.ins list =
  662. let
  663. val _ = CallOwnPrint("Called='compile_block'\n")
  664.  
  665. val block_insns = map (fn (uid_opt, insn) => compile_insn ctxt (uid_opt, insn)) insns
  666. val terminator_insn = compile_terminator ctxt terminator
  667.  
  668. in
  669. ((List.concat block_insns) @ terminator_insn)
  670. (* TODO("compile_block") *)
  671. end
  672.  
  673. fun compile_lbl_block (lbl:ll.lbl) (ctxt:ctxt) (block:ll.block) : X86.elem =
  674. X86.Asm.text (S.name lbl) (compile_block ctxt block)
  675.  
  676.  
  677. (* compile_fdecl ------------------------------------------------------------ *)
  678.  
  679.  
  680. (* This helper function computes the location of the nth incoming
  681. function argument: either in a register or relative to %rbp,
  682. according to the calling conventions. You might find it useful for
  683. compile_fdecl.
  684.  
  685. [ NOTE: the first six arguments are numbered 0 .. 5 ]
  686. *)
  687.  
  688. fun arg_loc (n : int) : X86.operand =
  689. case n of
  690. 0 => ~% Rdi
  691. | 1 => ~% Rsi
  692. | 2 => ~% Rdx
  693. | 3 => ~% Rcx
  694. | 4 => ~% R08
  695. | 5 => ~% R09
  696. | _ => errorFunc("arg_loc called with number: " ^ (Int.toString n))
  697.  
  698. (* The code for the entry-point of a function must do several things:
  699.  
  700. - since our simple compiler maps local %uids to stack slots,
  701. compiling the control-flow-graph body of an fdecl requires us to
  702. compute the layout (see the discussion of locals and layout)
  703.  
  704. - the function code should also comply with the calling
  705. conventions, typically by moving arguments out of the parameter
  706. registers (or stack slots) into local storage space. For our
  707. simple compilation strategy, that local storage space should be
  708. in the stack. (So the function parameters can also be accounted
  709. for in the layout.)
  710.  
  711. - the function entry code should allocate the stack storage needed
  712. to hold all of the local stack slots.
  713. *)
  714. (*
  715. tdecls = (tid * ty) list
  716. *)
  717.  
  718.  
  719. fun compile_fdecl tdecls (name:ll.gid) ({fty, param=params, cfg as (block, labled_blocks)}:ll.fdecl) : X86.prog =
  720. let
  721. val _ = CallOwnPrint("\n"^(S.name name)^":\nCalled='compile_fdecl' with: \n")
  722. (*
  723. val head = hd tdecls (*tiger main locals struct*)
  724. val (tid, ty) = head
  725. val _ = CallOwnPrint("tid: " ^ (S.name tid)^ "\n") *)
  726.  
  727. fun getUidOptFromCfg ((block, lbl_blocks):ll.cfg) : (ll.uid option) list =
  728. let
  729. (* Extract uid*insns from lbl_block, and concat to one (uid*insn)list *)
  730. val lbl_block_insns_list = map (fn(lbl, block) => (#insns block)) lbl_blocks (* lblblock => insns *)
  731. val uidOption_insn_list = List.concat((#insns block)::lbl_block_insns_list) (* Make long list of insns *)
  732. val uidOption_list = map (fn (uid_opt, _) => uid_opt) uidOption_insn_list
  733. in
  734. uidOption_list
  735. end
  736.  
  737. (*create the layout *)
  738. fun createLayout (insns) =
  739. let
  740. val result = foldl (fn (uid_option, layout) =>
  741. case uid_option
  742. of NONE => layout
  743. | SOME uid =>
  744. let
  745. val length = List.length layout
  746. val entry = (uid, Ind3 (Lit (~8*(length + 1)), Rbp ))
  747. (* val _ = CallOwnPrint((Int.toString length) ^" " ^ (S.name uid) ^" \n" ) *)
  748. in
  749. entry::layout
  750. end
  751. ) [] insns
  752. in
  753. result
  754. end
  755.  
  756.  
  757. (* params are uids, we make them uid options to fit. *)
  758. val params_uidOption_list = map (fn uid => SOME uid) params
  759.  
  760. val bodyUidOption_list = getUidOptFromCfg(block, labled_blocks)
  761. val uidOption_list = params_uidOption_list @ bodyUidOption_list
  762. val layout = createLayout(uidOption_list)
  763.  
  764. (* Create the instructions that stores the arguments on the stack *)
  765. fun store_arg(i):X86.ins list =
  766. let
  767. val param:ll.uid = getListElement(params, i)
  768. val dest = (lookup layout param)
  769.  
  770. val insns =
  771. if i < 6 then
  772. (*First*)
  773. [(Movq, [arg_loc i, dest])]
  774. else
  775. (*Rest*)
  776. (* Remaining params will be on stack above Rbp after the prologue *)
  777. [(Movq, [Ind3(Lit(16+8*(i-6)), Rbp), ~% Rax]),
  778. (Movq, [~% Rax, dest])]
  779. in
  780. insns
  781. end
  782.  
  783. (* Function prologue *)
  784. val a = (Pushq, [Reg Rbp])
  785. val b = (Movq, [Reg Rsp, Reg Rbp])
  786. val c = (Subq, [Asm.~$ (8*List.length layout), Reg Rsp])
  787.  
  788. val store_args_insns_list = List.tabulate(List.length params, store_arg)
  789. val store_args_insns = List.concat store_args_insns_list
  790. val prologue = a::b::c::store_args_insns
  791.  
  792. val ctxt: ctxt = {tdecls = tdecls, layout = layout}
  793.  
  794. val firstInstructions : X86.ins list = compile_block ctxt block
  795. val firstElement : elem = Asm.gtext (mangle name) (prologue @ firstInstructions) (* Prepend calling conventions *)
  796.  
  797. val restElements = map (fn (lbl, block_arg) => compile_lbl_block lbl ctxt block_arg) labled_blocks
  798. (* val _ = CallOwnPrint("Restelements.length=" ^ (Int.toString(List.length(restElements)) ^ "\n")) *)
  799. in
  800. firstElement::restElements (* {lbl, global:bool, asm}list *)
  801. end
  802.  
  803. (*
  804. val uid = S.symbol "uid_anders_test"
  805. val layoutP:layout = [(uid, Imm (Lbl "====tester===="))] (* Get from cfg*)
  806. *)
  807.  
  808. (*
  809. val lengthParams = List.length params
  810. (*create the initial layout *)
  811. fun createLayout (block, blocks) =
  812. let
  813. fun visit {insns, terminator} layout =
  814. foldl (fn ((uid_option, _), lay_out) =>
  815. case uid_option
  816. of SOME uid =>
  817. let
  818. val length = List.length lay_out
  819. val entry = (uid, Ind3 (Lit (~8*(length + lengthParams + 1 )), Rbp ))
  820. val _ = CallOwnPrint((Int.toString length) ^" " ^ (S.name uid) ^" \n" )
  821. in
  822. entry :: lay_out
  823. end
  824. | NONE => lay_out
  825. ) layout insns
  826.  
  827. val init_layout = visit block []
  828. val result = foldl (fn ((_, blk), layout_foldl) => visit blk layout_foldl) init_layout labled_blocks
  829. in
  830. result
  831. end
  832. *)
  833.  
  834. (* compile_gdecl ------------------------------------------------------------ *)
  835.  
  836. (* Compile a global value into an X86 global data declaration and map
  837. a global uid to its associated X86 label.
  838. Returns asm
  839. *)
  840.  
  841. fun compile_ginit ll.GNull = [X86.Quad (X86.Lit 0)]
  842. | compile_ginit (ll.GGid gid) = [X86.Quad (X86.Lbl (mangle gid))]
  843. | compile_ginit (ll.GInt c) = [X86.Quad (X86.Lit c)]
  844. | compile_ginit (ll.GString s) = [X86.Asciz s] (* Asciz = string *)
  845. | compile_ginit (ll.GArray gs) = List.concat (List.map compile_gdecl gs)
  846. | compile_ginit (ll.GStruct gs) = List.concat (List.map compile_gdecl gs)
  847.  
  848. and compile_gdecl (llty, ginit) = compile_ginit ginit
  849.  
  850. (* compile_prog ------------------------------------------------------------- *)
  851.  
  852. fun compile_prog ({tdecls, gdecls, fdecls}:ll.prog) : X86.prog =
  853. let
  854. fun g (gid, gdecl) = X86.Asm.data (mangle gid) (compile_gdecl gdecl) (* Translate a global to a {lbl:S.symbol, global:bool, asm:asm} *)
  855. fun f (gid_name, fdecl) = compile_fdecl tdecls gid_name fdecl (* Translate a function to a {same...} *)
  856. in
  857. (List.map g gdecls) @ (List.concat (List.map f fdecls)) (* program returns elem list AKA {lbl, bool. asm} *)
  858. end
  859. handle _ => errorFunc("x86 crashed\n")
  860.  
  861. end (* structure X86Backend *)
  862.  
  863.  
  864.  
  865. (* TO PRINT THE BASTARD
  866.  
  867.  
  868. val a = Parse.parse "../testcases/test55.tig";
  869. val b = Semant.transProg a;
  870. val c = AugmentOffset.offsetAugmentProg b;
  871. val d = LLCodegen.codegen_prog c;
  872. val e = X86Backend.compile_prog d;
  873.  
  874.  
  875. print:
  876. X86Backend.compile_prog (LLCodegen.codegen_prog(AugmentOffset.offsetAugmentProg(Semant.transProg(Parse.parse "../testcases/test55.tig"))));
  877. X86.string_of_prog (X86Backend.compile_prog Example.prog);
  878.  
  879.  
  880. To use with gdb
  881.  
  882. disassemble tigermain
  883. break *tigermain+21 (or otherthing)
  884. info registers
  885.  
  886.  
  887. To use with lldb
  888.  
  889. lldb ./out/test55.bin
  890. disassemble --name tigermain
  891. breakpoint set --name tigermain
  892. run
  893. register read
  894. step
  895. register read r11
  896. register read/d r11
  897. *)
Advertisement
Add Comment
Please, Sign In to add comment