desug.ml 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. open Printf
  2. open Types
  3. open Util
  4. (* Generate new variables for array dimensions, to avoid re-evalutation when
  5. * array dimensions are used (e.g., after array dimension reduction). *)
  6. let move_array_dims node =
  7. let patch_dims basename values make_decs make_dimname =
  8. let names = mapi (fun i _ -> make_dimname basename i) values in
  9. let decs = List.concat (List.map2 make_decs values names) in
  10. let make_dim value name = Dim (name, annof value) in
  11. let dims = List.map2 make_dim values names in
  12. (decs, dims)
  13. in
  14. let fresh_dim name _ = fresh_const name in
  15. let rec trav = function
  16. | VarDec (ArrayDims (ctype, values), name, init, ann) ->
  17. let make_decs value name = [VarDec (Int, name, Some value, [])] in
  18. let decs, dims = patch_dims name values make_decs fresh_dim in
  19. Block (decs @ [VarDec (ArrayDims (ctype, dims), name, init, ann)])
  20. (* Omit the trailing "_" for exported variables since they should not be
  21. * pruned by optimisations *)
  22. | GlobalDef (export, ArrayDims (ctype, values), name, init, ann) ->
  23. let make_dimname = if export then generate_array_dim else fresh_dim in
  24. let make_decs value name = [GlobalDef (export, Int, name, Some value, [])] in
  25. let decs, dims = patch_dims name values make_decs make_dimname in
  26. Block (decs @ [GlobalDef (export, ArrayDims (ctype, dims), name, init, ann)])
  27. | node -> traverse_unit trav node
  28. in
  29. trav node
  30. (* Create new constant variables for scalar initialisations on arrays so that
  31. * they are only evaluated once *)
  32. let rec move_scalar_inits = function
  33. (* Prevent next match for ArrayConst initialisations *)
  34. | VarDec (ArrayDims _, _, Some (ArrayConst _), _) as node ->
  35. node
  36. (* Add vardec for scalar value *)
  37. | VarDec (ArrayDims (ctype, dims) as atype, name, Some value, ann) as node ->
  38. let scalar_dec = VarDec (ctype, fresh_const "scalar", Some value, ann) in
  39. let scalar_use = VarUse (scalar_dec, None, annof value) in
  40. Block [scalar_dec; VarDec (atype, name, Some scalar_use, ann)]
  41. | node -> traverse_unit move_scalar_inits node
  42. (* Split variable initialisation into declaration and assignment *)
  43. let rec split_inits = function
  44. | VarDec (ctype, name, Some init, ann) ->
  45. let dec = VarDec (ctype, name, None, ann) in
  46. Block [dec; VarLet (dec, None, init, ann)]
  47. | GlobalDef (export, ctype, name, Some init, ann) ->
  48. let dec = GlobalDef (export, ctype, name, None, ann) in
  49. Block [dec; VarLet (dec, None, init, ann)]
  50. | node -> traverse_unit split_inits node
  51. (* Add __allocate statements after array declarations *)
  52. let rec add_allocs node =
  53. let create_dimvar = function
  54. | Dim (name, _) as dim -> VarUse (dim, None, [])
  55. | _ -> raise InvalidNode
  56. in
  57. match node with
  58. | VarDec (ArrayDims (_, dims), _, _, ann) ->
  59. Block [node; Allocate (node, List.map create_dimvar dims, ann)]
  60. | GlobalDef (_, ArrayDims (_, dims), _, _, ann) ->
  61. Block [node; Allocate (node, List.map create_dimvar dims, ann)]
  62. | node -> traverse_unit add_allocs node
  63. let dimsof = function
  64. | GlobalDef (_, ArrayDims (_, dims), _, _, _)
  65. | VarDec (ArrayDims (_, dims), _, _, _) -> dims
  66. | _ -> raise InvalidNode
  67. let rec array_init = function
  68. (* Transform array constant initialisation into separate assign statements
  69. * for all entries in the array literal *)
  70. | VarLet (dec, None, (ArrayConst _ as value), ann) ->
  71. let name = nameof dec in
  72. let intconst i = Const (IntVal (Int32.of_int i), []) in
  73. let ndims = List.length (dimsof dec) in
  74. let rec make_assigns depth i indices = function
  75. | [] -> []
  76. | hd :: tl ->
  77. let assigns = trav depth (i :: indices) hd in
  78. make_assigns depth (i + 1) indices tl @ assigns
  79. and trav depth indices = function
  80. | ArrayConst (values, _) ->
  81. make_assigns (depth + 1) 0 indices values
  82. | value when depth = ndims ->
  83. let indices = List.map intconst indices in
  84. [VarLet (dec, Some (List.rev indices), value, ann)]
  85. | node ->
  86. raise (FatalError (NodeMsg (node, sprintf
  87. "dimension mismatch: expected %d nesting levels, got %d"
  88. ndims depth)))
  89. in
  90. Block (List.rev (trav 0 [] value))
  91. (* Scalar initialisation *)
  92. | VarLet (dec, None, scalar, ann) when is_array dec ->
  93. let create_loop dim body =
  94. let counter = fresh_id "i" in
  95. let start = Const (IntVal 0l, []) in
  96. let stop = VarUse (dim, None, ann) in
  97. let step = Const (IntVal 1l, []) in
  98. For (counter, start, stop, step, body, [])
  99. in
  100. let rec nest_loops indices = function
  101. | [] -> Block [VarLet (dec, Some (List.rev indices), scalar, [])]
  102. | dim :: tl ->
  103. let counter = fresh_id "i" in
  104. let start = Const (IntVal 0l, []) in
  105. let stop = VarUse (dim, None, ann) in
  106. let step = Const (IntVal 1l, []) in
  107. let body = nest_loops (Var (counter, None, []) :: indices) tl in
  108. For (counter, start, stop, step, body, [])
  109. in
  110. nest_loops [] (dimsof dec)
  111. | node -> traverse_unit array_init node
  112. let rec for_to_while = function
  113. (* Transform for-loops to while-loops *)
  114. | For (counter, start, stop, step, body, ann) ->
  115. let dec name init = VarDec (Int, name, Some init, []) in
  116. let _i = dec counter start in
  117. let _stop = dec (fresh_const "stop") stop in
  118. let _step = dec (fresh_const "step") step in
  119. let vi = VarUse (_i, None, []) in
  120. let vstop = VarUse (_stop, None, annof stop) in
  121. let vstep = VarUse (_step, None, annof step) in
  122. let cond =
  123. Cond (
  124. Binop (Gt, vstep, Const (IntVal 0l, []), []),
  125. Binop (Lt, vi, vstop, []),
  126. Binop (Gt, vi, vstop, []),
  127. [])
  128. in
  129. Block [
  130. _i; _stop; _step;
  131. While (cond, (Block (
  132. [body; VarLet (_i, None, Binop (Add, vi, vstep, []), [])]
  133. )), ann) |> for_to_while;
  134. ]
  135. (* Transform while-loops to do-while loops in if-statements *)
  136. (* DISABLED, while-loops are explicitly supported by the assembly phase
  137. | While (cond, body, ann) ->
  138. If (cond, DoWhile (cond, for_to_while body, ann), ann)
  139. *)
  140. | node -> traverse_unit for_to_while node
  141. let rec move_vardecs = function
  142. | FunDef (export, ret_type, name, params, body, ann) ->
  143. let rec trav = function
  144. | FunDef _ as node -> (move_vardecs node, [])
  145. | VarDec _ as node -> (DummyNode, [node])
  146. | node -> traverse_list trav node
  147. in
  148. let body, decs = traverse_list trav body in
  149. let body = Block (decs @ (block_body body)) in
  150. FunDef (export, ret_type, name, params, body, ann)
  151. | node -> traverse_unit move_vardecs node
  152. let rec move_global_inits = function
  153. (* Move global initialisations to __init function *)
  154. | Program (decls, ann) ->
  155. let decls = List.map move_global_inits decls in
  156. let rec extract_inits inits = function
  157. | [] ->
  158. (List.rev inits, [])
  159. | (VarLet _ as hd) :: tl
  160. | (Allocate _ as hd) :: tl ->
  161. extract_inits (hd :: inits) tl
  162. | hd :: tl ->
  163. let inits, tl = extract_inits inits tl in
  164. (inits, (hd :: tl))
  165. in
  166. begin match extract_inits [] decls with
  167. | ([], _) -> Program (decls, ann)
  168. | (inits, decls) ->
  169. let init_func = FunDef (true, Void, "__init", [], Block inits, []) in
  170. Program (init_func :: decls, ann)
  171. end
  172. | node -> traverse_unit move_global_inits node
  173. let rec group_vardecs = function
  174. | FunDef (export, ret_type, name, params, Block body, ann) ->
  175. let rec create = function
  176. | (VarDec _ as hd) :: tl -> VarDecs [hd] :: create tl
  177. | tl -> tl
  178. in
  179. let rec merge = function
  180. | VarDecs [a] :: VarDecs b :: tl -> merge (VarDecs (a :: b) :: tl)
  181. | VarDecs a :: VarDecs b :: tl -> merge (VarDecs (a @ b) :: tl)
  182. | tl -> tl
  183. in
  184. let body = Block (create body |> merge) |> group_vardecs in
  185. FunDef (export, ret_type, name, params, body, ann)
  186. | node -> traverse_unit group_vardecs node
  187. let phase = function
  188. | Ast node ->
  189. Ast begin
  190. (* Move array dimensions and scalar initialisations into new variables as
  191. * initialisations, so that they are evaluated exactly once, and so that
  192. * dimension names are consistent with the array name *)
  193. move_array_dims node |> move_scalar_inits |>
  194. (* Split variable initialisations into declarations and assignments, and
  195. * move the assignments to the function body *)
  196. split_inits |> add_allocs |>
  197. (* Transform ArrayConst assignment to assignments into for-loops *)
  198. array_init |>
  199. (* Transform for-loops to while-loops *)
  200. for_to_while |> split_inits |>
  201. (* Move variable declarations to the beginning of the function *)
  202. move_vardecs |>
  203. (* Move global initialisation assignments to __init *)
  204. move_global_inits |>
  205. (* Create and merge VarDecs nodes at the start of each function *)
  206. group_vardecs |>
  207. (* Propagate new declaration properties to uses (since we have no
  208. * pointers) *)
  209. Context.analyse false
  210. end
  211. | _ -> raise InvalidInput