desug.ml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. open Printf
  2. open Types
  3. open Util
  4. (* Check if a function defines a variable name *)
  5. let defines var = function
  6. | FunDef (export, ret_type, name, params, Block (VarDecs decs :: tl), ann) ->
  7. let rec trav_decs = function
  8. | [] -> false
  9. | Param (ArrayDims (_, dims), name, _) :: tl ->
  10. name = var || trav_decs dims || trav_decs tl
  11. | (Dim (name, _) | VarDec (_, name, _, _) | Param (_, name, _)) :: _
  12. when name = var -> true
  13. | _ :: tl -> trav_decs tl
  14. in
  15. trav_decs params || trav_decs decs
  16. | _ -> raise InvalidNode
  17. (* Replace all occurences of a variable name with another name *)
  18. let rec replace_var var replacement node =
  19. let trav = (replace_var var replacement) in
  20. let trav_all = List.map trav in
  21. let trav_opt = function None -> None | Some node -> Some (trav node) in
  22. match node with
  23. (* Replace variable name on match *)
  24. | Var (name, ind, ann) when name = var ->
  25. let ind = match ind with None -> None | Some ind -> Some (trav_all ind) in
  26. Var (replacement, ind, ann)
  27. (* Don't enter a function body if it redefines the variable *)
  28. | FunDef _ when defines var node -> node
  29. (* Don't traverse into a for-loop body if the loop counter redefines var *)
  30. | For (counter, start, stop, step, body, ann) when counter = var ->
  31. For (counter, trav start, trav stop, trav step, body, ann)
  32. (* At this point, array dimension expressions may not have been moved to new
  33. * variables yet, so traverse them explicitly *)
  34. | VarDec (ArrayDims (ctype, dims), name, init, ann) ->
  35. VarDec (ArrayDims (ctype, trav_all dims), name, trav_opt init, ann)
  36. | node -> traverse_unit trav node
  37. (* Create new constant variables for scalar initialisations on arrays so that
  38. * they are only evaluated once *)
  39. let rec move_scalars = function
  40. (* Prevent next match for ArrayConst initialisations *)
  41. | VarDec (ArrayDims _, _, Some (ArrayConst _), _) as node ->
  42. node
  43. (* Add vardec for scalar value *)
  44. | VarDec (ArrayDims _ as ctype, name, Some value, ann) as node ->
  45. let scalar_name = fresh_const "scalar" in
  46. Block [
  47. VarDec (basetypeof node, scalar_name, Some value, ann);
  48. VarDec (ctype, name, Some (Var (scalar_name, None, annof value)), ann);
  49. ]
  50. | node -> traverse_unit move_scalars node
  51. (* Generate new variables for array dimensions, to avoid re-evalutation when
  52. * array dimensions are used (e.g., after array dimension reduction). *)
  53. let array_dims node =
  54. (*
  55. let make_dimname basename i = generate_const basename (i + 1) in
  56. *)
  57. let make_dimname = generate_const in
  58. let patch_dims basename values make_decs =
  59. let names = mapi (fun i _ -> make_dimname basename i) values in
  60. let decs = List.concat (List.map2 make_decs values names) in
  61. let make_dim value name = Dim (name, annof value) in
  62. let dims = List.map2 make_dim values names in
  63. (decs, dims)
  64. in
  65. (* Save dimension replacements in one global hash table (we are not replacing
  66. * local vars, so everything is in the global scope) *)
  67. let replacements = Hashtbl.create 10 in
  68. let rec trav = function
  69. | VarDec (ArrayDims (ctype, values), name, init, ann) ->
  70. let make_decs value name = [VarDec (Int, name, Some value, [])] in
  71. let decs, dims = patch_dims name values make_decs in
  72. Block (decs @ [VarDec (ArrayDims (ctype, dims), name, init, ann)])
  73. | GlobalDef (export, ArrayDims (ctype, values), name, init, ann) ->
  74. (* Move array dimensions into new variables to avoid double evaluation of
  75. * expressions with side effects (i.e. function calls) *)
  76. let make_decs value name = [GlobalDef (export, Int, name, Some value, [])] in
  77. let decs, dims = patch_dims name values make_decs in
  78. Block (decs @ [GlobalDef (export, ArrayDims (ctype, dims), name, init, ann)])
  79. | GlobalDec (ArrayDims (ctype, dims), name, ann) ->
  80. (* Create an 'extern int ...' definition for each dimension with a
  81. * consistent import name, and replace all local uses with the imported
  82. * variable name *)
  83. let make_decs dim impname =
  84. match dim with
  85. | Dim (dimname, _) ->
  86. (* Fix name clashes (needed because context analysis has not been done
  87. * yet) *)
  88. if Hashtbl.mem replacements dimname then begin
  89. raise (FatalError (NodeMsg (dim, "duplicate dimension name")))
  90. end;
  91. (* Occurences of dimension names are replaced after the traversal *)
  92. Hashtbl.add replacements dimname impname;
  93. [GlobalDec (Int, impname, [])]
  94. | _ -> raise InvalidNode
  95. in
  96. let decs, dims = patch_dims name dims make_decs in
  97. Block (decs @ [GlobalDec (ArrayDims (ctype, dims), name, ann)])
  98. (*
  99. let make_decs i = function
  100. | Dim (dimname, dimann) ->
  101. let impname = generate_id name (i + 1) in
  102. let decs = [
  103. GlobalDec (Int, impname, []);
  104. GlobalDef (false, Int, dimname, Some (Var (impname, None, [])), [])
  105. ] in
  106. (decs, Dim (impname, dimann))
  107. | _ -> raise InvalidNode
  108. in
  109. let decs, dims = List.split (mapi make_decs dims) in
  110. Block (List.concat decs @ [GlobalDec (ArrayDims (ctype, dims), name, ann)])
  111. *)
  112. | node -> traverse_unit trav node
  113. in
  114. Hashtbl.fold replace_var replacements (trav node)
  115. (* Split variable initialisation into declaration and assignment *)
  116. let rec split_inits = function
  117. (* Wrap array initialisation in ArrayInit to pass dimensions *)
  118. | VarDec (ArrayDims (_, dims) as ctype, name, Some value, ann) ->
  119. Block [
  120. VarDec (ctype, name, None, ann);
  121. Assign (name, None, ArrayInit (value, dims), ann);
  122. ]
  123. | GlobalDef (export, (ArrayDims (_, dims) as ctype), name, Some value, ann) ->
  124. Block [
  125. GlobalDef (export, ctype, name, None, ann);
  126. Assign (name, None, ArrayInit (value, dims), ann);
  127. ]
  128. | VarDec (ctype, name, Some init, ann) ->
  129. Block [
  130. VarDec (ctype, name, None, ann);
  131. Assign (name, None, init, ann);
  132. ]
  133. | GlobalDef (export, ctype, name, Some init, ann) ->
  134. Block [
  135. GlobalDef (export, ctype, name, None, ann);
  136. Assign (name, None, init, ann);
  137. ]
  138. | node -> traverse_unit split_inits node
  139. (* Add <allocate> statements after array declarations *)
  140. let rec add_allocs node =
  141. let create_dimvar = function
  142. | Dim (name, _) -> Var (name, None, [])
  143. | _ -> raise InvalidNode
  144. in
  145. match node with
  146. | VarDec (ArrayDims (_, dims), _, _, ann) ->
  147. Block [node; Allocate (node, List.map create_dimvar dims, ann)]
  148. | GlobalDef (_, ArrayDims (_, dims), _, _, ann) ->
  149. Block [node; Allocate (node, List.map create_dimvar dims, ann)]
  150. | node -> traverse_unit add_allocs node
  151. let extract_inits lst =
  152. let rec trav inits = function
  153. | [] ->
  154. (List.rev inits, [])
  155. | (Assign _ as hd) :: tl
  156. | (Allocate _ as hd) :: tl ->
  157. trav (hd :: inits) tl
  158. | hd :: tl ->
  159. let inits, tl = trav inits tl in
  160. (inits, (hd :: tl))
  161. in trav [] lst
  162. let rec move_inits = function
  163. (* Move global initialisations to __init function *)
  164. | Program (decls, ann) ->
  165. let decls = List.map move_inits decls in
  166. begin match extract_inits decls with
  167. | ([], _) -> Program (decls, ann)
  168. | (inits, decls) ->
  169. let body = Block (VarDecs [] :: LocalFuns [] :: inits) in
  170. let init_func = FunDef (true, Void, "__init", [], body, []) in
  171. Program (init_func :: decls, ann)
  172. end
  173. (* Split local variable initialisations in declaration and assignment *)
  174. | FunDef (export, ret_type, name, params, Block body, ann) ->
  175. let rec place_inits inits = function
  176. | VarDecs lst :: tl ->
  177. let inits, decs = extract_inits lst in
  178. VarDecs decs :: (place_inits inits tl)
  179. | LocalFuns _ as hd :: tl ->
  180. hd :: inits @ tl
  181. | _ -> raise InvalidNode
  182. in
  183. let body = Block (place_inits [] body) in
  184. FunDef (export, ret_type, name, params, body, ann)
  185. | node -> traverse_unit move_inits node
  186. let for_to_while node =
  187. let rec trav new_vars = function
  188. | FunDef (export, ret_type, name, params, body, ann) ->
  189. let rec place_decs decs = function
  190. | Block (VarDecs lst :: tl) -> Block (VarDecs (decs @ lst) :: tl)
  191. | _ -> raise InvalidNode
  192. in
  193. let new_vars = ref [] in
  194. let body = trav new_vars body in
  195. let create_vardec name = VarDec (Int, name, None, []) in
  196. let new_vardecs = List.map create_vardec !new_vars in
  197. let body = place_decs new_vardecs body in
  198. FunDef (export, ret_type, name, params, body, ann)
  199. (* Transform for-loops to while-loops *)
  200. | For (counter, start, stop, step, body, ann) ->
  201. let _i = fresh_id counter in
  202. let _stop = fresh_const "stop" in
  203. let _step = fresh_const "step" in
  204. new_vars := !new_vars @ [_i; _stop; _step];
  205. let vi = Var (_i, None, []) in
  206. let vstop = Var (_stop, None, annof stop) in
  207. let vstep = Var (_step, None, annof step) in
  208. let cond = Cond (
  209. Binop (Gt, vstep, Const (IntVal 0l, []), []),
  210. Binop (Lt, vi, vstop, []),
  211. Binop (Gt, vi, vstop, []),
  212. []
  213. ) in
  214. Block [
  215. Assign (_i, None, start, annof start);
  216. Assign (_stop, None, stop, annof stop);
  217. Assign (_step, None, step, annof step);
  218. trav new_vars (While (cond, (Block (
  219. block_body (replace_var counter _i body) @
  220. [Assign (_i, None, Binop (Add, vi, vstep, []), [])]
  221. )), ann));
  222. ]
  223. (* Transform while-loops to do-while loops in if-statements *)
  224. (* DISABLED, while-loops are explicitly supported by the assembly phase
  225. | While (cond, body, ann) ->
  226. let cond = trav new_vars cond in
  227. let body = trav new_vars body in
  228. Block [If (cond, Block [DoWhile (cond, body, ann)], ann)]
  229. *)
  230. | node -> traverse_unit (trav new_vars) node
  231. in
  232. trav (ref []) node
  233. let rec array_init = function
  234. (* Transform array constant initialisation into separate assign statements
  235. * for all entries in the array literal *)
  236. | Assign (name, None, ArrayInit (ArrayConst _ as value, dims), ann) ->
  237. let intconst i = Const (IntVal (Int32.of_int i), []) in
  238. let ndims = List.length dims in
  239. let rec make_assigns depth i indices = function
  240. | [] -> []
  241. | hd :: tl ->
  242. let assigns = trav depth (i :: indices) hd in
  243. make_assigns depth (i + 1) indices tl @ assigns
  244. and trav depth indices = function
  245. | ArrayConst (values, _) ->
  246. make_assigns (depth + 1) 0 indices values
  247. | value when depth = ndims ->
  248. let indices = List.map intconst indices in
  249. [Assign (name, Some (List.rev indices), value, ann)]
  250. (* DISABLED: nesting level must now be equal to number of dimensions
  251. | value when depth < ndims ->
  252. (* Use for-loops for scalar assignment on sub-array *)
  253. let value = ArrayInit (value, dims) in
  254. let indices = List.map intconst indices in
  255. [array_init (Assign (name, Some (List.rev indices), value, ann))]
  256. *)
  257. | node ->
  258. raise (FatalError (NodeMsg (node, sprintf
  259. "dimension mismatch: expected %d nesting levels, got %d"
  260. ndims depth)))
  261. in
  262. Block (List.rev (trav 0 [] value))
  263. (* Replace no indices with empty indices to have a list below *)
  264. | Assign (name, None, (ArrayInit _ as value), ann) ->
  265. array_init (Assign (name, Some [], value, ann))
  266. | Assign (name, Some indices, ArrayInit (value, dims), ann) ->
  267. let rec add_loop indices = function
  268. | [] ->
  269. array_init (Assign (name, Some indices, value, ann))
  270. | dim :: rest ->
  271. let counter = fresh_id "i" in
  272. let start = Const (IntVal 0l, []) in
  273. let step = Const (IntVal 1l, []) in
  274. let body = Block [add_loop (indices @ [Var (counter, None, [])]) rest] in
  275. let stop = match dim with
  276. | Dim (name, ann) -> Var (name, None, ann)
  277. | _ -> dim
  278. in
  279. For (counter, start, stop, step, body, [])
  280. in
  281. let rec sublist n = function
  282. | [] when n > 0 -> raise (Invalid_argument "n")
  283. | [] -> []
  284. | lst when n = 0 -> lst
  285. | _ :: tl -> sublist (n - 1) tl
  286. in
  287. let dims_left = sublist (List.length indices) dims in
  288. add_loop indices dims_left
  289. | node -> traverse_unit array_init node
  290. let phase = function
  291. | Ast node ->
  292. (* Move array dimensions and scalar initialisations into new variables as
  293. * initialisations, so that they are evaluated exactly once, and so that
  294. * dimension names are consistent with the array name *)
  295. let node = move_scalars (array_dims node) in
  296. (* Split variable initialisations into declarations and assignments, and
  297. * move the assignments to the function body *)
  298. let node = move_inits (add_allocs (split_inits node)) in
  299. (* Transform ArrayConst assignment to assignments in for-loops, and
  300. * transform all for-loops to while-loops afterwards *)
  301. Ast (for_to_while (array_init node))
  302. | _ -> raise InvalidInput