desug.ml 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. open Printf
  2. open Types
  3. open Util
  4. let rec var_init = function
  5. (* Move global initialisations to __init function *)
  6. | Program (decls, ann) ->
  7. let decls = flatten_blocks (List.map var_init decls) in
  8. let rec trav assigns = function
  9. | [] -> (assigns, [])
  10. | (Assign _ as hd) :: tl
  11. | (Allocate _ as hd) :: tl -> trav (assigns @ [hd]) tl
  12. | hd :: tl ->
  13. let (assigns, decls) = trav assigns tl in
  14. (assigns, (hd :: decls))
  15. in
  16. let (assigns, decls) = trav [] decls in (
  17. match assigns with
  18. | [] -> Program (decls, ann)
  19. | assigns ->
  20. let init_func = FunDef (true, Void, "__init", [], Block assigns, []) in
  21. Program (init_func :: decls, ann)
  22. )
  23. (* Global variable initialisation:
  24. * Add an assign statement and the Program node will remove it later on *)
  25. | GlobalDef (export, ctype, name, Some init, ann) ->
  26. Block [GlobalDef (export, ctype, name, None, ann);
  27. Assign (name, None, init, ann)]
  28. (* Global array definition:
  29. * - Create a new global variable for each dimension and initialise it to
  30. * the given expression
  31. * - create __allocate statement in __init *)
  32. | GlobalDef (export, Array (ctype, dims), name, None, ann) as dec ->
  33. let rec create_dimvars i = function
  34. | [] -> []
  35. | hd :: tl ->
  36. let dimname = name ^ "$" ^ string_of_int i in
  37. let var = Var (dimname, None, ann) in
  38. var :: (create_dimvars (i + 1) tl)
  39. in
  40. let dimvars = create_dimvars 1 dims in
  41. let create_globaldef dim = function
  42. | Var (dimname, None, ann) ->
  43. var_init (GlobalDef (export, Int, dimname, Some dim, ann))
  44. | _ -> raise InvalidNode
  45. in
  46. let vardecs = List.map2 create_globaldef dims dimvars in
  47. let alloc = [Allocate (dec, dimvars, ann)] in
  48. Block (vardecs @
  49. [GlobalDef (export, Array (ctype, dimvars), name, None, ann)] @
  50. alloc)
  51. (* Split local variable initialisations in declaration and assignment *)
  52. | FunDef (export, ret_type, name, params, body, ann) ->
  53. let inits = ref [] in
  54. let rec extract_inits = function
  55. (* Translate scalar array initialisation to ArrayScalar node,
  56. * for easy replacement later on *)
  57. | VarDec (Array _ as vtype, name, Some (Const _ as v), ann) ->
  58. let init = Some (ArrayInit (ArrayScalar v, vtype)) in
  59. extract_inits (VarDec (vtype, name, init, ann))
  60. (* Wrap ArrayConst in ArrayInit to pass dimensions *)
  61. | VarDec (Array _ as vtype, name, Some (ArrayConst _ as v), ann) ->
  62. let init = Some (ArrayInit (v, vtype)) in
  63. extract_inits (VarDec (vtype, name, init, ann))
  64. | VarDec (ctype, name, init, ann) as dec ->
  65. (* array definition: create __allocate statement *)
  66. let alloc = match ctype with
  67. | Array (_, dims) ->
  68. let create_dimvar = function
  69. | Dim (name, _) -> Var (name, None, [])
  70. | _ -> raise InvalidNode
  71. in [Allocate (dec, List.map create_dimvar dims, ann)]
  72. | _ -> []
  73. in
  74. (* initialisation: create assign statement *)
  75. let add = match init with
  76. | Some value -> alloc @ [Assign (name, None, value, ann)]
  77. | None -> alloc
  78. in
  79. inits := !inits @ add;
  80. VarDec (ctype, name, None, ann)
  81. | DimDec (name, Some init, ann) ->
  82. (* dimension initialisation: create assign statement *)
  83. inits := !inits @ [Assign (name, None, init, ann)];
  84. DimDec (name, None, ann)
  85. | LocalFuns funs -> LocalFuns (List.map var_init funs)
  86. | node -> transform_children extract_inits node
  87. in
  88. let rec place_inits = function
  89. (* initialisations need to be placed after local functions *)
  90. | (LocalFuns _ as hd) :: tl -> hd :: !inits @ tl
  91. | hd :: tl -> hd :: (place_inits tl)
  92. | [] -> []
  93. in
  94. let params = flatten_blocks (List.map var_init params) in
  95. let body = flatten_blocks (place_inits (block_body (extract_inits body))) in
  96. FunDef (export, ret_type, name, params, Block body, ann)
  97. | node -> transform_children var_init node
  98. let rec replace_var var replacement node =
  99. let trav = (replace_var var replacement) in
  100. match node with
  101. | Var (name, None, ann) when name = var ->
  102. Var (replacement, None, ann)
  103. | For (counter, start, stop, step, body, ann) when counter = var ->
  104. For (replacement, trav start, trav stop, trav step, trav body, ann)
  105. | node ->
  106. transform_children trav node
  107. let for_to_while node =
  108. let rec traverse new_vars = function
  109. | FunDef (export, ret_type, name, params, body, ann) ->
  110. let new_vars = ref [] in
  111. let body = traverse new_vars body in
  112. let create_vardec name = VarDec (Int, name, None, []) in
  113. let new_vardecs = List.map create_vardec !new_vars in
  114. let _body = new_vardecs @ (flatten_blocks (block_body body)) in
  115. FunDef (export, ret_type, name, params, Block _body, ann)
  116. (* Transform for-loops to while-loops *)
  117. | For (counter, start, stop, step, body, ann) ->
  118. let _i = fresh_var counter in
  119. let _stop = fresh_const "stop" in
  120. let _step = fresh_const "step" in
  121. new_vars := !new_vars @ [_i; _stop; _step];
  122. let vi = Var (_i, None, []) in
  123. let vstop = Var (_stop, None, annof stop) in
  124. let vstep = Var (_step, None, annof step) in
  125. let cond = Cond (
  126. Binop (Gt, vstep, Const (IntVal 0, []), []),
  127. Binop (Lt, vi, vstop, []),
  128. Binop (Gt, vi, vstop, []),
  129. []
  130. ) in
  131. Block [
  132. Assign (_i, None, start, annof start);
  133. Assign (_stop, None, stop, annof stop);
  134. Assign (_step, None, step, annof step);
  135. traverse new_vars (While (cond, (Block (
  136. block_body (replace_var counter _i body) @
  137. [Assign (_i, None, Binop (Add, vi, vstep, []), [])]
  138. )), ann));
  139. ]
  140. (* DISABLED, while-loops are explicittly supported by the assembly phase
  141. (* Transform while-loops to do-while loops in if-statements *)
  142. | While (cond, body, ann) ->
  143. let cond = traverse new_vars cond in
  144. let body = traverse new_vars body in
  145. Block [If (cond, Block [DoWhile (cond, body, ann)], ann)]
  146. *)
  147. | node -> transform_children (traverse new_vars) node
  148. in
  149. traverse (ref []) node
  150. let rec array_init = function
  151. (* Transform scalar assignment into nested for-loops *)
  152. | Assign (name, None, ArrayInit (ArrayScalar value, Array (_, dims)), ann) ->
  153. let rec add_loop indices = function
  154. | [] ->
  155. Assign (name, Some indices, value, ann)
  156. | dim :: rest ->
  157. let counter = fresh_var "i" in
  158. let body = Block [add_loop (indices @ [Var (counter, None, [])]) rest] in
  159. For (counter, Const (IntVal 0, []), dim, Const (IntVal 1, []), body, [])
  160. in
  161. add_loop [] dims
  162. (* Transform array constant inisialisation into separate assign statements
  163. * for all entries in the constant array *)
  164. (* TODO: only allow when array dimensions are constant? *)
  165. | Assign (name, None, ArrayInit (ArrayConst _ as value, Array (_, dims)), ann) ->
  166. let ndims = list_size dims in
  167. let rec make_assigns depth i indices = function
  168. | [] -> []
  169. | hd :: tl ->
  170. let assigns = traverse depth (i :: indices) hd in
  171. make_assigns depth (i + 1) indices tl @ assigns
  172. and traverse depth indices = function
  173. | ArrayConst (values, _) ->
  174. make_assigns (depth + 1) 0 indices values
  175. | value when depth = ndims ->
  176. let indices = List.map (fun i -> Const (IntVal i, [])) indices in
  177. [Assign (name, Some (List.rev indices), value, ann)]
  178. | node ->
  179. let msg = sprintf
  180. "dimension mismatch: expected %d nesting levels, got %d"
  181. ndims depth
  182. in
  183. raise (NodeError (node, msg))
  184. in
  185. Block (List.rev (traverse 0 [] value))
  186. | node -> transform_children array_init node
  187. (* Generate new variables for array dimensions in function bodies, to avoid
  188. * re-evalutation after array dimension reduction. For example:
  189. *
  190. * int dims = 0;
  191. *
  192. * int dim() {
  193. * dims = dims 1; // Side effect => dims() should be called once
  194. * return 10;
  195. * }
  196. *
  197. * void foo() {
  198. * int[10, dim()] arr;
  199. * arr[0, 1] = 1;
  200. * }
  201. *
  202. * After dimension reduction, this would become:
  203. * void foo() {
  204. * int[] arr;
  205. * arr = allocate(10, dim());
  206. * arr[1 * dim() + 0] = 1;
  207. * }
  208. *
  209. * This behaviour is of course incorrect. To avoid dim() from being evaluated
  210. * twice, the snippet above is transformed into the following code: (note the $$
  211. * which will help later during constant propagation)
  212. * void foo() {
  213. * int[a$dim$$1, a$dim$$2] arr;
  214. * a$dim$$1 = 10;
  215. * a$dim$$2 = dim();
  216. * arr[1, 2] = 1;
  217. * }
  218. *
  219. * ... which later becomes:
  220. * void foo() {
  221. * int[a$dim$$1, a$dim$$2] arr;
  222. * a$dim$$1 = 10;
  223. * a$dim$$2 = dim();
  224. * arr = __allocate(a$dim$$1 * a$dim$$2);
  225. * arr[1 * a$dim$2 * 0] = 1;
  226. * }
  227. * *)
  228. let rec array_dims = function
  229. | VarDec (Array (ctype, values), name, init, ann) ->
  230. let make_dimname i _ = name ^ "$dim$$" ^ string_of_int (i + 1) in
  231. let dimnames = mapi make_dimname values in
  232. let make_dimvar value name = Dim (name, annof value) in
  233. let dims = List.map2 make_dimvar values dimnames in
  234. let make_dimdec name value = DimDec (name, Some value, annof value) in
  235. let dimdecs = List.map2 make_dimdec dimnames values in
  236. Block (dimdecs @ [VarDec (Array (ctype, dims), name, init, ann)])
  237. | node -> transform_children array_dims node
  238. let phase = function
  239. | Ast node -> Ast (for_to_while (array_init (var_init (array_dims node))))
  240. | _ -> raise (InvalidInput "desugar")