constprop.ml 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. (**
  2. * The compiler sometimes generates variables of the form __foo_1__, to make
  3. * sure that expressions are only executed once. In many cases, this leads to
  4. * over-complex constructions, for example when converting for-loops to
  5. * while-loops. We use the knowledge of these variables being constant by
  6. * propagation the constant values to their occurrences, and then apply
  7. * arithmetic simplification to operators to reduce the size and complexity of
  8. * the generated code. Note that this can only be applied to constants. For
  9. * variables in general, some form of liveness analysis would be required (e.g.
  10. * Static Single Assignment form). Expressions can only be propagated when they
  11. * have no side effects, i.e. when they do not contain function calls.
  12. *
  13. * Constant propagation is merged with some some arithmetic simplification here,
  14. * specifically targeting optimization oppertunities created bij earlier
  15. * constant propagation. This is utilized, for example, in array index
  16. * calculation when array dimensions are constant.
  17. *)
  18. open Types
  19. open Util
  20. let is_const = function
  21. | Const _ -> true
  22. | VarUse (dec, None, _) -> is_const_id (nameof dec)
  23. | Var (name, _, _) -> is_const_id name
  24. | _ -> false
  25. (* Play-it-safe side effect analysis: only return true for variables and
  26. * constants, since these are targeted in arithmetic simplification (in
  27. * particular targeting array indices that can be simplified after array
  28. * dimension reduction). *)
  29. let no_side_effect = function
  30. | Const _ | VarUse _ | Var _ -> true
  31. | _ -> false
  32. (* Redefine integer operators within this module since they are only used on
  33. * IntVal values, which have type int32 *)
  34. let (+) = Int32.add
  35. let (-) = Int32.sub
  36. let (/) = Int32.div
  37. let ( * ) = Int32.mul
  38. (* Constand folding *)
  39. let eval = function
  40. (* Binop - arithmetic *)
  41. | Binop (Add, Const (IntVal left, _), Const (IntVal right, _), ann) ->
  42. Const (IntVal (left + right), ann)
  43. | Binop (Add, Const (FloatVal left, _), Const (FloatVal right, _), ann) ->
  44. Const (FloatVal (left +. right), ann)
  45. | Binop (Sub, Const (IntVal left, _), Const (IntVal right, _), ann) ->
  46. Const (IntVal (left - right), ann)
  47. | Binop (Sub, Const (FloatVal left, _), Const (FloatVal right, _), ann) ->
  48. Const (FloatVal (left -. right), ann)
  49. | Binop (Mul, Const (IntVal left, _), Const (IntVal right, _), ann) ->
  50. Const (IntVal (left * right), ann)
  51. | Binop (Mul, Const (FloatVal left, _), Const (FloatVal right, _), ann) ->
  52. Const (FloatVal (left *. right), ann)
  53. | Binop (Div, Const (IntVal left, _), Const (IntVal right, _), ann) when right <> 0l ->
  54. Const (IntVal (left / right), ann)
  55. | Binop (Div, Const (FloatVal left, _), Const (FloatVal right, _), ann) ->
  56. Const (FloatVal (left /. right), ann)
  57. (*| Binop (Mod, Const (IntVal left, _), Const (IntVal right, _), ann) ->
  58. Const (IntVal (left mod right), ann)
  59. *)
  60. (* Binop - relational *)
  61. | Binop (Eq, Const (IntVal left, _), Const (IntVal right, _), ann) ->
  62. Const (BoolVal (left = right), ann)
  63. | Binop (Eq, Const (FloatVal left, _), Const (FloatVal right, _), ann) ->
  64. Const (BoolVal (left = right), ann)
  65. | Binop (Ne, Const (IntVal left, _), Const (IntVal right, _), ann) ->
  66. Const (BoolVal (left <> right), ann)
  67. | Binop (Ne, Const (FloatVal left, _), Const (FloatVal right, _), ann) ->
  68. Const (BoolVal (left <> right), ann)
  69. | Binop (Gt, Const (IntVal left, _), Const (IntVal right, _), ann) ->
  70. Const (BoolVal (left > right), ann)
  71. | Binop (Gt, Const (FloatVal left, _), Const (FloatVal right, _), ann) ->
  72. Const (BoolVal (left > right), ann)
  73. | Binop (Lt, Const (IntVal left, _), Const (IntVal right, _), ann) ->
  74. Const (BoolVal (left < right), ann)
  75. | Binop (Lt, Const (FloatVal left, _), Const (FloatVal right, _), ann) ->
  76. Const (BoolVal (left < right), ann)
  77. | Binop (Ge, Const (IntVal left, _), Const (IntVal right, _), ann) ->
  78. Const (BoolVal (left >= right), ann)
  79. | Binop (Ge, Const (FloatVal left, _), Const (FloatVal right, _), ann) ->
  80. Const (BoolVal (left >= right), ann)
  81. | Binop (Le, Const (IntVal left, _), Const (IntVal right, _), ann) ->
  82. Const (BoolVal (left <= right), ann)
  83. | Binop (Le, Const (FloatVal left, _), Const (FloatVal right, _), ann) ->
  84. Const (BoolVal (left <= right), ann)
  85. (* Binop - logical *)
  86. | Binop (And, Const (BoolVal left, _), Const (BoolVal right, _), ann) ->
  87. Const (BoolVal (left && right), ann)
  88. | Binop (Or, Const (BoolVal left, _), Const (BoolVal right, _), ann) ->
  89. Const (BoolVal (left || right), ann)
  90. (* Monary operations *)
  91. | Monop (Not, Const (BoolVal value, _), ann) ->
  92. Const (BoolVal (not value), ann)
  93. | Monop (Neg, Const (IntVal value, _), ann) ->
  94. Const (IntVal (Int32.neg value), ann)
  95. | Monop (Neg, Const (FloatVal value, _), ann) ->
  96. Const (FloatVal (-.value), ann)
  97. (* 0 * a --> 0 *)
  98. | Binop (Mul, Const (IntVal 0l, _), other, ann)
  99. | Binop (Mul, other, Const (IntVal 0l, _), ann) when no_side_effect other ->
  100. Const (IntVal 0l, ann)
  101. (* 0 + a --> a *)
  102. | Binop (Add, Const (IntVal 0l, _), other, _)
  103. | Binop (Add, other, Const (IntVal 0l, _), _) ->
  104. other
  105. (* 1 * a --> a *)
  106. | Binop (Mul, Const (IntVal 1l, _), other, _)
  107. | Binop (Mul, other, Const (IntVal 1l, _), _) ->
  108. other
  109. (* true|false ? texp : fexp --> texp|fexp*)
  110. | Cond (Const (BoolVal value, _), texp, fexp, _) ->
  111. if value then texp else fexp
  112. | node -> node
  113. let rec propagate consts node =
  114. let propagate = propagate consts in
  115. match node with
  116. (* Constant assignments are added to constants table *)
  117. | Assign (name, None, value, ann) when is_const_id name ->
  118. let value = propagate value in
  119. if is_const value then begin
  120. Hashtbl.add consts name value;
  121. DummyNode
  122. end else
  123. Assign (name, None, value, ann)
  124. | VarLet (dec, None, value, ann) when is_const_id (nameof dec) ->
  125. let value = propagate value in
  126. if is_const value then begin
  127. Hashtbl.add consts (nameof dec) value;
  128. DummyNode
  129. end else
  130. VarLet (dec, None, value, ann)
  131. (* Variables that are in the constant table are replaced with their constant
  132. * value *)
  133. | Var (name, None, ann) when Hashtbl.mem consts name ->
  134. Hashtbl.find consts name
  135. | VarUse (dec, None, ann) when Hashtbl.mem consts (nameof dec) ->
  136. Hashtbl.find consts (nameof dec)
  137. | Dim (name, ann) when Hashtbl.mem consts name ->
  138. Hashtbl.find consts name
  139. (* Apply arithmetic simplification to constant operands *)
  140. | Monop (op, opnd, ann) ->
  141. eval (Monop (op, propagate opnd, ann))
  142. | Binop (op, left, right, ann) ->
  143. eval (Binop (op, propagate left, propagate right, ann))
  144. | Cond (cond, texp, fexp, ann) ->
  145. eval (Cond (propagate cond, propagate texp, propagate fexp, ann))
  146. | TypeCast (ctype, value, ann) ->
  147. let value = propagate value in
  148. begin match (ctype, value) with
  149. | (Bool, Const (BoolVal value, _)) -> Const (BoolVal value, ann)
  150. | (Bool, Const (IntVal value, _)) -> Const (BoolVal (value != 1l), ann)
  151. | (Bool, Const (FloatVal value, _)) -> Const (BoolVal (value != 1.0), ann)
  152. | (Int, Const (BoolVal value, _)) -> Const (IntVal (if value then 1l else 0l), ann)
  153. | (Int, Const (IntVal value, _)) -> Const (IntVal value, ann)
  154. | (Int, Const (FloatVal value, _)) -> Const (IntVal (Int32.of_float value), ann)
  155. | (Float, Const (BoolVal value, _)) -> Const (FloatVal (if value then 1. else 0.), ann)
  156. | (Float, Const (IntVal value, _)) -> Const (FloatVal (Int32.to_float value), ann)
  157. | (Float, Const (FloatVal value, _)) -> Const (FloatVal value, ann)
  158. | _ -> TypeCast (ctype, value, ann)
  159. end
  160. | _ -> traverse_unit propagate node
  161. let rec prune_vardecs consts = function
  162. | VarDec (_, name, _, _) when Hashtbl.mem consts name -> DummyNode
  163. | node -> traverse_unit (prune_vardecs consts) node
  164. let propagate_consts node =
  165. let consts = Hashtbl.create 32 in
  166. let node = propagate consts node in
  167. prune_vardecs consts node
  168. let phase = function
  169. | Ast node -> Ast (propagate_consts node)
  170. | _ -> raise InvalidInput