desug.ml 11 KB

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