assemble.ml 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. open Printf
  2. open Types
  3. open Util
  4. open Stringify
  5. let get_scope node = function
  6. | GlobalDec _ -> Extern
  7. | dec ->
  8. match (depthof dec, depthof node) with
  9. | (0, _) -> Glob
  10. | (a, b) when a = b -> Current
  11. | (a, b) -> Rel (b - a)
  12. let comline comment = InlineComment (EmptyLine, comment)
  13. let assemble program =
  14. let labcounter = ref 0 in
  15. let genlabel suffix =
  16. labcounter := !labcounter + 1;
  17. string_of_int !labcounter ^ "_" ^ suffix
  18. in
  19. let consts = Hashtbl.create 20 in
  20. let rec trav node =
  21. let rec trav_all = function
  22. | [] -> []
  23. | hd :: tl -> trav hd @ (trav_all tl)
  24. in
  25. let rec traverse_localfuns = function
  26. | LocalFuns body -> List.concat (List.map trav body)
  27. | Block body -> List.concat (List.map traverse_localfuns body)
  28. | _ -> []
  29. in
  30. match node with
  31. (* Global *)
  32. | Program (decls, _) ->
  33. trav_all decls
  34. | GlobalDef (false, ctype, name, _, _) ->
  35. [Comment (sprintf "global var \"%s\" at index %d" name (indexof node));
  36. Global ctype]
  37. | GlobalDef (true, ctype, name, _, _) ->
  38. [Comment (sprintf "exported var \"%s\" at index %d" name (indexof node));
  39. Global ctype;
  40. ExportVar (name, indexof node)]
  41. | GlobalDec (ctype, name, _) ->
  42. [Comment (sprintf "imported var \"%s\" at index %d" name (indexof node));
  43. ImportVar (name, ctype)]
  44. | FunDec (ret_type, name, params, _) ->
  45. [Comment (sprintf "imported fun \"%s\" at index %d" name (indexof node));
  46. ImportFun (name, ret_type, List.map typeof params)]
  47. | FunDef (export, ret_type, name, params, body, _) ->
  48. let last_if_return body =
  49. match List.fold_left (fun _ x -> x) DummyNode (block_body body) with
  50. | Return _ as ret -> Some ret
  51. | _ -> None
  52. in
  53. let add_explicit_return body =
  54. match last_if_return body with
  55. | Some _ -> body
  56. | None -> Block (block_body body @ [Return (None, [])])
  57. in
  58. let label = labelof node in
  59. begin
  60. if export
  61. then [ExportFun (name, ret_type, List.map typeof params, label)]
  62. else []
  63. end @
  64. [Comment (sprintf "function \"%s\" with %d parameters and %d local vars"
  65. label (List.length params) (indexof node));
  66. Label label] @
  67. (match indexof node with 0 -> [] | n -> [RtnEnter n]) @
  68. (trav_all params) @
  69. (trav (add_explicit_return body)) @
  70. [EmptyLine] @
  71. (traverse_localfuns body)
  72. | VarDec (_, name, _, _) ->
  73. [comline (sprintf "local var \"%s\" at index %d" name (indexof node))]
  74. | Param (_, name, _) ->
  75. [comline (sprintf "parameter \"%s\" at index %d" name (indexof node))]
  76. | LocalFuns _ -> []
  77. | Block body | VarDecs body -> trav_all body
  78. (* Statements *)
  79. | VarLet (dec, None, value, _) ->
  80. let store = Store (typeof dec, get_scope node dec, indexof dec) in
  81. trav value @ [InlineComment (store, node2str node)]
  82. | Return (None, _) ->
  83. [InlineComment (Ret Void, node2str node)]
  84. | Return (Some value, _) ->
  85. trav value @ [InlineComment (Ret (typeof value), node2str node)]
  86. | If (cond, body, _) ->
  87. let endlabel = genlabel "end" in
  88. (trav cond) @
  89. [Branch (false, endlabel);
  90. comline ("if (" ^ (node2str cond) ^ ") {")] @
  91. (trav body) @
  92. [comline "}";
  93. Label endlabel]
  94. | IfElse (cond, true_body, false_body, _) ->
  95. let elselabel = genlabel "else" in
  96. let endlabel = genlabel "end" in
  97. (trav cond) @
  98. [Branch (false, elselabel);
  99. comline ("if (" ^ (node2str cond) ^ ") {")] @
  100. (trav true_body) @
  101. [Jump endlabel;
  102. comline "} else {";
  103. Label elselabel] @
  104. (trav false_body) @
  105. [comline "}";
  106. Label endlabel]
  107. | While (cond, body, _) ->
  108. let startlabel = genlabel "while" in
  109. let endlabel = genlabel "end" in
  110. let com = ("while (" ^ (node2str cond) ^ ") {") in
  111. [Label startlabel] @
  112. (trav cond) @
  113. [InlineComment (Branch (false, endlabel), com)] @
  114. (trav body) @
  115. [Jump startlabel;
  116. Label endlabel;
  117. comline "}"]
  118. | DoWhile (cond, body, _) ->
  119. let startlabel = genlabel "dowhile" in
  120. let com = ("} while (" ^ (node2str cond) ^ ");") in
  121. [comline "do {";
  122. Label startlabel] @
  123. (trav body) @
  124. (trav cond) @
  125. [InlineComment (Branch (true, startlabel), com)]
  126. (* Expression statement pops the disregarded expression value from the
  127. * stack, if any *)
  128. | Expr value ->
  129. let pop = match typeof value with
  130. | Void -> []
  131. | ctype -> [InlineComment (Pop ctype, "disregard return value")]
  132. in
  133. trav value @ pop
  134. (* Expressions *)
  135. (* Immediate values are handled here, and not in the peephole optimizer, for
  136. * convenience: the indices in the constant table would be altered, so
  137. * entries cannot be removed. By this early detection (also during index
  138. * analysis), they are not added in the first place *)
  139. | Const (value, _) when is_immediate_const value ->
  140. [InlineComment (LoadImm value, node2str node)]
  141. | Const (value, _) ->
  142. Hashtbl.replace consts value (indexof node);
  143. let load = LoadConst (typeof node, indexof node) in
  144. [InlineComment (load, node2str node)]
  145. | VarUse (GlobalDec (ctype, _, _) as dec, None, _) ->
  146. let load = Load (ctype, Extern, indexof dec) in
  147. [InlineComment (load, node2str node)]
  148. | VarUse (dec, None, _) ->
  149. let load =
  150. match (depthof dec, depthof node) with
  151. | (0, _) -> Load (typeof dec, Glob, indexof dec)
  152. | (a, b) when a = b -> Load (typeof dec, Current, indexof dec)
  153. | (a, b) -> Load (typeof dec, Rel (b - a), indexof dec)
  154. in
  155. [InlineComment (load, node2str node)]
  156. | Monop (op, value, _) ->
  157. (trav value) @
  158. [InlineComment (Op (op, typeof value), op2str op)]
  159. | Binop (op, left, right, _) ->
  160. (trav left) @
  161. (trav right) @
  162. [InlineComment (Op (op, typeof left), op2str op)]
  163. | TypeCast (ctype, value, _) ->
  164. let vtype = typeof value in
  165. begin
  166. match (ctype, vtype) with
  167. | (Float, Int) | (Int, Float) -> ()
  168. | _ ->
  169. let msg = sprintf
  170. "invalid typecast: %s -> %s"
  171. (type2str vtype) (type2str ctype)
  172. in
  173. raise (FatalError (NodeMsg (node, msg)))
  174. end;
  175. trav value @ [Convert (vtype, ctype)]
  176. (* Function calls *)
  177. | FunUse (dec, args, _) ->
  178. let init =
  179. match (depthof dec, depthof node) with
  180. | (0, _) -> RtnInit Glob
  181. | (a, b) when a = b - 1 -> RtnInit Current
  182. | (a, b) when a = b -> RtnInit Local
  183. | (a, b) -> RtnInit (Rel (b - a - 1))
  184. in
  185. let jmp =
  186. match dec with
  187. | FunDec _ -> RtnJmp (ExternFun (indexof dec))
  188. | FunDef _ -> RtnJmp (LocalFun (List.length args, labelof dec))
  189. | _ -> raise InvalidNode
  190. in
  191. InlineComment (init, nameof dec) ::
  192. (trav_all args) @
  193. [InlineComment (jmp, node2str node)]
  194. | Arg value -> trav value
  195. (* Conditional expression (short-circuit evaluation) *)
  196. (* <cond>
  197. * branch_f else
  198. * <true_expr>
  199. * jump end
  200. * else:
  201. * <false_expr>
  202. * end:
  203. *)
  204. | Cond (cond, texp, fexp, _) ->
  205. let elselabel = genlabel "false_expr" in
  206. let endlabel = genlabel "end" in
  207. (trav cond) @
  208. [Branch (false, elselabel)] @
  209. (trav texp) @
  210. [Jump (endlabel);
  211. Label (elselabel)] @
  212. (trav fexp) @
  213. [InlineComment (Label (endlabel), node2str node)]
  214. (* Arrays *)
  215. | Allocate (dec, [dim], _) ->
  216. let store =
  217. match (depthof dec, depthof node) with
  218. | (0, _) -> Store (typeof dec, Glob, indexof dec)
  219. | (a, b) when a = b -> Store (typeof dec, Current, indexof dec)
  220. | _ -> raise InvalidNode
  221. in
  222. trav dim @
  223. [NewArray (basetypeof dec);
  224. InlineComment (store, node2str node)]
  225. | Allocate _ ->
  226. raise (FatalError (NodeMsg (node, "invalid number of array dimensions \
  227. (should be one-dimensional)")))
  228. | VarUse (dec, Some dims, _) ->
  229. let load = Load (typeof dec, get_scope node dec, indexof dec) in
  230. (trav_all (List.rev dims)) @ (* push dimensions *)
  231. [InlineComment (load, nameof dec)] @ (* push array reference *)
  232. [InlineComment (LoadArray (basetypeof dec), node2str node)]
  233. | VarLet (dec, Some dims, value, _) ->
  234. let load = Load (typeof dec, get_scope node dec, indexof dec) in
  235. (trav_all (List.rev dims)) @ (* push dimensions *)
  236. (trav value) @ (* push value *)
  237. (trav_all dims) @ (* push dimensions *)
  238. [InlineComment (load, nameof dec)] @ (* push array reference *)
  239. [InlineComment (StoreArray (basetypeof dec), node2str node)]
  240. | _ -> raise InvalidNode
  241. in
  242. let instrs = trav program in
  243. (* Sort aggregated constants and add definitions
  244. * We might want to rewrite this in the future because it's a little
  245. * cumbersome right now... *)
  246. let pairs = ref [] in
  247. let add_pair value index =
  248. let com = sprintf "index %d" index in
  249. pairs := (InlineComment (ConstDef value, com), index) :: !pairs
  250. in
  251. Hashtbl.iter add_pair consts;
  252. let cmp (_, i) (_, j) = compare i j in
  253. let sorted_pairs = List.sort cmp !pairs in
  254. let const_defs = List.map fst sorted_pairs in
  255. instrs @ const_defs
  256. let phase = function
  257. | Ast node -> Assembly (assemble node)
  258. | _ -> raise InvalidInput