analyzerTexpr.ml 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318
  1. (*
  2. The Haxe Compiler
  3. Copyright (C) 2005-2019 Haxe Foundation
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  15. *)
  16. open Ast
  17. open Type
  18. open Common
  19. open OptimizerTexpr
  20. open Globals
  21. let s_expr_pretty e = s_expr_pretty false "" false (s_type (print_context())) e
  22. let rec is_true_expr e1 = match e1.eexpr with
  23. | TConst(TBool true) -> true
  24. | TParenthesis e1 -> is_true_expr e1
  25. | _ -> false
  26. let is_stack_allocated c = Meta.has Meta.StructAccess c.cl_meta
  27. let map_values ?(allow_control_flow=true) f e =
  28. let branching = ref false in
  29. let efinal = ref None in
  30. let f e =
  31. if !branching then
  32. f e
  33. else begin
  34. efinal := Some e;
  35. mk (TConst TNull) e.etype e.epos
  36. end
  37. in
  38. let rec loop complex e = match e.eexpr with
  39. | TIf(e1,e2,Some e3) ->
  40. branching := true;
  41. let e2 = loop true e2 in
  42. let e3 = loop true e3 in
  43. {e with eexpr = TIf(e1,e2,Some e3)}
  44. | TSwitch(e1,cases,edef) ->
  45. branching := true;
  46. let cases = List.map (fun (el,e) -> el,loop true e) cases in
  47. let edef = Option.map (loop true) edef in
  48. {e with eexpr = TSwitch(e1,cases,edef)}
  49. | TBlock [e1] ->
  50. loop complex e1
  51. | TBlock el ->
  52. begin match List.rev el with
  53. | e1 :: el ->
  54. let e1 = loop true e1 in
  55. let e = {e with eexpr = TBlock (List.rev (e1 :: el))} in
  56. {e with eexpr = TMeta((Meta.MergeBlock,[],e.epos),e)}
  57. | [] ->
  58. f e
  59. end
  60. | TTry(e1,catches) ->
  61. branching := true;
  62. let e1 = loop true e1 in
  63. let catches = List.map (fun (v,e) -> v,loop true e) catches in
  64. {e with eexpr = TTry(e1,catches)}
  65. | TMeta(m,e1) ->
  66. {e with eexpr = TMeta(m,loop complex e1)}
  67. | TParenthesis e1 ->
  68. {e with eexpr = TParenthesis (loop complex e1)}
  69. | TBreak | TContinue | TThrow _ | TReturn _ ->
  70. if not allow_control_flow then raise Exit;
  71. e
  72. | _ ->
  73. if not complex then raise Exit;
  74. f e
  75. in
  76. let e = loop false e in
  77. e,!efinal
  78. let can_throw e =
  79. let rec loop e = match e.eexpr with
  80. | TConst _ | TLocal _ | TTypeExpr _ | TFunction _ | TBlock _ -> ()
  81. | TCall _ | TNew _ | TThrow _ | TCast(_,Some _) -> raise Exit
  82. | TField _ | TArray _ -> raise Exit (* sigh *)
  83. | _ -> Type.iter loop e
  84. in
  85. try
  86. loop e; false
  87. with Exit ->
  88. true
  89. let rec can_be_inlined e = match e.eexpr with
  90. | TConst _ -> true
  91. | TParenthesis e1 | TMeta(_,e1) -> can_be_inlined e1
  92. | _ -> false
  93. let target_handles_unops com = match com.platform with
  94. | Lua | Python -> false
  95. | _ -> true
  96. let target_handles_assign_ops com e2 = match com.platform with
  97. | Php -> not (has_side_effect e2)
  98. | Lua -> false
  99. | Cpp when not (Common.defined com Define.Cppia) -> false
  100. | _ -> true
  101. let target_handles_side_effect_order com = match com.platform with
  102. | Cpp -> Common.defined com Define.Cppia
  103. | Php -> false
  104. | _ -> true
  105. let rec can_be_used_as_value com e =
  106. let rec loop e = match e.eexpr with
  107. | TBlock [e] -> loop e
  108. | TBlock _ | TSwitch _ | TTry _ -> raise Exit
  109. | TCall({eexpr = TConst (TString "phi")},_) -> raise Exit
  110. (* | TCall _ | TNew _ when (match com.platform with Cpp | Php -> true | _ -> false) -> raise Exit *)
  111. | TReturn _ | TThrow _ | TBreak | TContinue -> raise Exit
  112. | TUnop((Increment | Decrement),_,_) when not (target_handles_unops com) -> raise Exit
  113. | TFunction _ -> ()
  114. | _ -> Type.iter loop e
  115. in
  116. try
  117. begin match com.platform,e.eexpr with
  118. | (Cs | Cpp | Java | Flash | Lua),TConst TNull -> raise Exit
  119. | _ -> ()
  120. end;
  121. loop e;
  122. true
  123. with Exit ->
  124. false
  125. let wrap_meta s e =
  126. mk (TMeta((Meta.Custom s,[],e.epos),e)) e.etype e.epos
  127. let is_really_unbound s = match s with
  128. | "`trace" | "__int__" -> false
  129. | _ -> true
  130. let r = Str.regexp "^\\([A-Za-z0-9_]\\)+$"
  131. let is_unbound_call_that_might_have_side_effects s el = match s,el with
  132. | "__js__",[{eexpr = TConst (TString s)}] when Str.string_match r s 0 -> false
  133. | _ -> true
  134. let type_change_ok com t1 t2 =
  135. if t1 == t2 then
  136. true
  137. else begin
  138. let rec map t = match t with
  139. | TMono r -> (match r.tm_type with None -> t_dynamic | Some t -> map t)
  140. | _ -> Type.map map t
  141. in
  142. let t1 = map t1 in
  143. let t2 = map t2 in
  144. let rec is_nullable_or_whatever = function
  145. | TMono r ->
  146. (match r.tm_type with None -> false | Some t -> is_nullable_or_whatever t)
  147. | TAbstract ({ a_path = ([],"Null") },[_]) ->
  148. true
  149. | TLazy f ->
  150. is_nullable_or_whatever (lazy_type f)
  151. | TType (t,tl) ->
  152. is_nullable_or_whatever (apply_params t.t_params tl t.t_type)
  153. | TFun _ ->
  154. false
  155. | TInst ({ cl_kind = KTypeParameter _ },_) ->
  156. false
  157. | TAbstract (a,_) when Meta.has Meta.CoreType a.a_meta ->
  158. not (Meta.has Meta.NotNull a.a_meta)
  159. | TAbstract (a,tl) ->
  160. not (Meta.has Meta.NotNull a.a_meta) && is_nullable_or_whatever (apply_params a.a_params tl a.a_this)
  161. | _ ->
  162. true
  163. in
  164. (* Check equality again to cover cases where TMono became t_dynamic *)
  165. t1 == t2 || match follow t1,follow t2 with
  166. | TDynamic _,_ | _,TDynamic _ -> false
  167. | _ ->
  168. if com.config.pf_static && is_nullable_or_whatever t1 <> is_nullable_or_whatever t2 then false
  169. else type_iseq t1 t2
  170. end
  171. let dynarray_map f d =
  172. DynArray.iteri (fun i e -> DynArray.unsafe_set d i (f e)) d
  173. let dynarray_mapi f d =
  174. DynArray.iteri (fun i e -> DynArray.unsafe_set d i (f i e)) d
  175. module TexprKindMapper = struct
  176. type kind =
  177. | KRead (* Expression is read. *)
  178. | KAccess (* Structure of expression is accessed. *)
  179. | KWrite (* Expression is lhs of =. *)
  180. | KReadWrite (* Expression is lhs of += .*)
  181. | KStore (* Expression is stored (via =, += or in array/object declaration). *)
  182. | KEq (* Expression is lhs or rhs of == or != *)
  183. | KEqNull (* Expression is lhs or rhs of == null or != null *)
  184. | KCalled (* Expression is being called. *)
  185. | KCallArgument (* Expression is call argument (leaves context). *)
  186. | KReturn (* Expression is returned (leaves context). *)
  187. | KThrow (* Expression is thrown (leaves context). *)
  188. let rec map kind f e = match e.eexpr with
  189. | TConst _
  190. | TLocal _
  191. | TBreak
  192. | TContinue
  193. | TTypeExpr _
  194. | TIdent _ ->
  195. e
  196. | TArray(e1,e2) ->
  197. let e1 = f KAccess e1 in
  198. let e2 = f KRead e2 in
  199. { e with eexpr = TArray (e1,e2) }
  200. | TBinop(OpAssign,e1,e2) ->
  201. let e1 = f KWrite e1 in
  202. let e2 = f KStore e2 in
  203. { e with eexpr = TBinop(OpAssign,e1,e2) }
  204. | TBinop(OpAssignOp op,e1,e2) ->
  205. let e1 = f KReadWrite e1 in
  206. let e2 = f KStore e2 in
  207. { e with eexpr = TBinop(OpAssignOp op,e1,e2) }
  208. | TBinop((OpEq | OpNotEq) as op,e1,e2) ->
  209. let e1,e2 = match (Texpr.skip e1).eexpr,(Texpr.skip e2).eexpr with
  210. | TConst TNull,TConst TNull ->
  211. let e1 = f KRead e1 in
  212. let e2 = f KRead e2 in
  213. e1,e2
  214. | TConst TNull,_ ->
  215. let e1 = f KRead e1 in
  216. let e2 = f KEqNull e2 in
  217. e1,e2
  218. | _,TConst TNull ->
  219. let e1 = f KEqNull e1 in
  220. let e2 = f KRead e2 in
  221. e1,e2
  222. | _ ->
  223. let e1 = f KEq e1 in
  224. let e2 = f KEq e2 in
  225. e1,e2
  226. in
  227. {e with eexpr = TBinop(op,e1,e2)}
  228. | TBinop(op,e1,e2) ->
  229. let e1 = f KRead e1 in
  230. let e2 = f KRead e2 in
  231. { e with eexpr = TBinop(op,e1,e2) }
  232. | TFor (v,e1,e2) ->
  233. let e1 = f KRead e1 in
  234. { e with eexpr = TFor (v,e1,f KRead e2) }
  235. | TWhile (e1,e2,flag) ->
  236. let e1 = f KRead e1 in
  237. { e with eexpr = TWhile (e1,f KRead e2,flag) }
  238. | TThrow e1 ->
  239. { e with eexpr = TThrow (f KThrow e1) }
  240. | TEnumParameter (e1,ef,i) ->
  241. { e with eexpr = TEnumParameter(f KAccess e1,ef,i) }
  242. | TEnumIndex e1 ->
  243. { e with eexpr = TEnumIndex (f KAccess e1) }
  244. | TField (e1,v) ->
  245. { e with eexpr = TField (f KAccess e1,v) }
  246. | TParenthesis e1 ->
  247. { e with eexpr = TParenthesis (f kind e1) }
  248. | TUnop (op,pre,e1) ->
  249. { e with eexpr = TUnop (op,pre,f KRead e1) }
  250. | TArrayDecl el ->
  251. { e with eexpr = TArrayDecl (List.map (f KStore) el) }
  252. | TNew (t,pl,el) ->
  253. { e with eexpr = TNew (t,pl,List.map (f KCallArgument) el) }
  254. | TBlock el ->
  255. let rec loop acc el = match el with
  256. | [e] -> f kind e :: acc
  257. | e1 :: el -> loop (f KRead e1 :: acc) el
  258. | [] -> []
  259. in
  260. let el = List.rev (loop [] el) in
  261. { e with eexpr = TBlock el }
  262. | TObjectDecl el ->
  263. { e with eexpr = TObjectDecl (List.map (fun (v,e) -> v, f KStore e) el) }
  264. | TCall (e1,el) ->
  265. let e1 = f KCalled e1 in
  266. { e with eexpr = TCall (e1, List.map (f KCallArgument) el) }
  267. | TVar (v,eo) ->
  268. { e with eexpr = TVar (v, match eo with None -> None | Some e -> Some (f KStore e)) }
  269. | TFunction fu ->
  270. { e with eexpr = TFunction { fu with tf_expr = f KRead fu.tf_expr } }
  271. | TIf (ec,e1,e2) ->
  272. let ec = f KRead ec in
  273. let e1 = f kind e1 in
  274. { e with eexpr = TIf (ec,e1,match e2 with None -> None | Some e -> Some (f kind e)) }
  275. | TSwitch (e1,cases,def) ->
  276. let e1 = f KRead e1 in
  277. let cases = List.map (fun (el,e2) -> List.map (f KRead) el, f kind e2) cases in
  278. { e with eexpr = TSwitch (e1, cases, match def with None -> None | Some e -> Some (f kind e)) }
  279. | TTry (e1,catches) ->
  280. let e1 = f kind e1 in
  281. { e with eexpr = TTry (e1, List.map (fun (v,e) -> v, f kind e) catches) }
  282. | TReturn eo ->
  283. { e with eexpr = TReturn (match eo with None -> None | Some e -> Some (f KReturn e)) }
  284. | TCast (e1,t) ->
  285. { e with eexpr = TCast (f kind e1,t) }
  286. | TMeta (m,e1) ->
  287. {e with eexpr = TMeta(m,f kind e1)}
  288. end
  289. (*
  290. This module rewrites some expressions to reduce the amount of special cases for subsequent analysis. After analysis
  291. it restores some of these expressions back to their original form.
  292. The following expressions are removed from the AST after `apply` has run:
  293. - OpBoolAnd and OpBoolOr binary operations are rewritten to TIf
  294. - OpAssignOp on a variable is rewritten to OpAssign
  295. - Prefix increment/decrement operations are rewritten to OpAssign
  296. - Postfix increment/decrement operations are rewritten to a TBlock with OpAssign and OpAdd/OpSub
  297. - `do {} while(true)` is rewritten to `while(true) {}`
  298. - TWhile expressions are rewritten to `while (true)` with appropriate conditional TBreak
  299. - TFor is rewritten to TWhile
  300. *)
  301. module TexprFilter = struct
  302. let apply com e =
  303. let rec loop e = match e.eexpr with
  304. | TBinop(OpBoolAnd | OpBoolOr as op,e1,e2) ->
  305. let e_then = e2 in
  306. let e_if,e_else = if op = OpBoolOr then
  307. mk (TUnop(Not,Prefix,e1)) com.basic.tbool e.epos,mk (TConst (TBool(true))) com.basic.tbool e.epos
  308. else
  309. e1,mk (TConst (TBool(false))) com.basic.tbool e.epos
  310. in
  311. loop (mk (TIf(e_if,e_then,Some e_else)) e.etype e.epos)
  312. | TBinop(OpAssignOp op,({eexpr = TLocal _} as e1),e2) ->
  313. let e = {e with eexpr = TBinop(op,e1,e2)} in
  314. loop {e with eexpr = TBinop(OpAssign,e1,e)}
  315. | TUnop((Increment | Decrement as op),flag,({eexpr = TLocal _} as e1)) ->
  316. let e_one = mk (TConst (TInt (Int32.of_int 1))) com.basic.tint e1.epos in
  317. let e = {e with eexpr = TBinop(OpAssignOp (if op = Increment then OpAdd else OpSub),e1,e_one)} in
  318. let e = if flag = Prefix then
  319. e
  320. else
  321. mk (TBlock [
  322. {e with eexpr = TBinop(OpAssignOp (if op = Increment then OpAdd else OpSub),e1,e_one)};
  323. {e with eexpr = TBinop((if op = Increment then OpSub else OpAdd),e1,e_one)};
  324. ]) e.etype e.epos
  325. in
  326. loop e
  327. | TWhile(e1,e2,DoWhile) when is_true_expr e1 ->
  328. loop {e with eexpr = TWhile(e1,e2,NormalWhile)}
  329. | TWhile(e1,e2,flag) when not (is_true_expr e1) ->
  330. let p = e.epos in
  331. let e_break = mk TBreak t_dynamic p in
  332. let e_not = mk (TUnop(Not,Prefix,Texpr.Builder.mk_parent e1)) e1.etype e1.epos in
  333. let e_if eo = mk (TIf(e_not,e_break,eo)) com.basic.tvoid p in
  334. let rec map_continue e = match e.eexpr with
  335. | TContinue ->
  336. Texpr.duplicate_tvars (e_if (Some e))
  337. | TWhile _ | TFor _ ->
  338. e
  339. | _ ->
  340. Type.map_expr map_continue e
  341. in
  342. let e2 = if flag = NormalWhile then e2 else map_continue e2 in
  343. let e_if = e_if None in
  344. let e_block = if flag = NormalWhile then Type.concat e_if e2 else Type.concat e2 e_if in
  345. let e_true = mk (TConst (TBool true)) com.basic.tbool p in
  346. let e = mk (TWhile(Texpr.Builder.mk_parent e_true,e_block,NormalWhile)) e.etype p in
  347. loop e
  348. | TFor(v,e1,e2) ->
  349. let e = Texpr.for_remap com.basic v e1 e2 e.epos in
  350. loop e
  351. | _ ->
  352. Type.map_expr loop e
  353. in
  354. loop e
  355. end
  356. (*
  357. An InterferenceReport represents in which way a given code may be influenced and
  358. how it might influence other code itself. It keeps track of read and write operations
  359. for both variable and fields, as well as a generic state read and write.
  360. *)
  361. module InterferenceReport = struct
  362. type interference_report = {
  363. mutable ir_var_reads : bool IntMap.t;
  364. mutable ir_var_writes : bool IntMap.t;
  365. mutable ir_field_reads : bool StringMap.t;
  366. mutable ir_field_writes : bool StringMap.t;
  367. mutable ir_state_read : bool;
  368. mutable ir_state_write : bool;
  369. }
  370. let create () = {
  371. ir_var_reads = IntMap.empty;
  372. ir_var_writes = IntMap.empty;
  373. ir_field_reads = StringMap.empty;
  374. ir_field_writes = StringMap.empty;
  375. ir_state_read = false;
  376. ir_state_write = false;
  377. }
  378. let set_var_read ir v = ir.ir_var_reads <- IntMap.add v.v_id true ir.ir_var_reads
  379. let set_var_write ir v = ir.ir_var_writes <- IntMap.add v.v_id true ir.ir_var_writes
  380. let set_field_read ir s = ir.ir_field_reads <- StringMap.add s true ir.ir_field_reads
  381. let set_field_write ir s = ir.ir_field_writes <- StringMap.add s true ir.ir_field_writes
  382. let set_state_read ir = ir.ir_state_read <- true
  383. let set_state_write ir = ir.ir_state_write <- true
  384. let has_var_read ir v = IntMap.mem v.v_id ir.ir_var_reads
  385. let has_var_write ir v = IntMap.mem v.v_id ir.ir_var_writes
  386. let has_field_read ir s = StringMap.mem s ir.ir_field_reads
  387. let has_field_write ir s = StringMap.mem s ir.ir_field_writes
  388. let has_state_read ir = ir.ir_state_read
  389. let has_state_write ir = ir.ir_state_write
  390. let has_any_field_read ir = not (StringMap.is_empty ir.ir_field_reads)
  391. let has_any_field_write ir = not (StringMap.is_empty ir.ir_field_writes)
  392. let has_any_var_read ir = not (IntMap.is_empty ir.ir_var_reads)
  393. let has_any_var_write ir = not (IntMap.is_empty ir.ir_var_writes)
  394. let from_texpr e =
  395. let ir = create () in
  396. let rec loop e = match e.eexpr with
  397. (* vars *)
  398. | TLocal v ->
  399. set_var_read ir v;
  400. if v.v_capture then set_state_read ir;
  401. | TBinop(OpAssign,{eexpr = TLocal v},e2) ->
  402. set_var_write ir v;
  403. if v.v_capture then set_state_write ir;
  404. loop e2
  405. | TBinop(OpAssignOp _,{eexpr = TLocal v},e2) ->
  406. set_var_read ir v;
  407. set_var_write ir v;
  408. if v.v_capture then begin
  409. set_state_read ir;
  410. set_state_write ir;
  411. end;
  412. loop e2
  413. | TUnop((Increment | Decrement),_,{eexpr = TLocal v}) ->
  414. set_var_read ir v;
  415. set_var_write ir v;
  416. (* fields *)
  417. | TField(e1,fa) ->
  418. loop e1;
  419. if not (is_read_only_field_access e1 fa) then set_field_read ir (field_name fa);
  420. | TBinop(OpAssign,{eexpr = TField(e1,fa)},e2) ->
  421. set_field_write ir (field_name fa);
  422. loop e1;
  423. loop e2;
  424. | TBinop(OpAssignOp _,{eexpr = TField(e1,fa)},e2) ->
  425. let name = field_name fa in
  426. set_field_read ir name;
  427. set_field_write ir name;
  428. loop e1;
  429. loop e2;
  430. | TUnop((Increment | Decrement),_,{eexpr = TField(e1,fa)}) ->
  431. let name = field_name fa in
  432. set_field_read ir name;
  433. set_field_write ir name;
  434. loop e1
  435. (* array *)
  436. | TArray(e1,e2) ->
  437. set_state_read ir;
  438. loop e1;
  439. loop e2;
  440. | TBinop(OpAssign,{eexpr = TArray(e1,e2)},e3) ->
  441. set_state_write ir;
  442. loop e1;
  443. loop e2;
  444. loop e3;
  445. | TBinop(OpAssignOp _,{eexpr = TArray(e1,e2)},e3) ->
  446. set_state_read ir;
  447. set_state_write ir;
  448. loop e1;
  449. loop e2;
  450. loop e3;
  451. | TUnop((Increment | Decrement),_,{eexpr = TArray(e1,e2)}) ->
  452. set_state_read ir;
  453. set_state_write ir;
  454. loop e1;
  455. loop e2;
  456. (* state *)
  457. | TCall({eexpr = TIdent s},el) when not (is_unbound_call_that_might_have_side_effects s el) ->
  458. List.iter loop el
  459. | TNew(c,_,el) when (match c.cl_constructor with Some cf when PurityState.is_pure c cf -> true | _ -> false) ->
  460. set_state_read ir;
  461. List.iter loop el;
  462. | TCall({eexpr = TField(e1,FEnum _)},el) ->
  463. loop e1;
  464. List.iter loop el;
  465. | TCall({eexpr = TField(e1,fa)},el) when PurityState.is_pure_field_access fa ->
  466. set_state_read ir;
  467. loop e1;
  468. List.iter loop el
  469. | TCall(e1,el) ->
  470. set_state_read ir;
  471. set_state_write ir;
  472. loop e1;
  473. List.iter loop el
  474. | TNew(_,_,el) ->
  475. set_state_read ir;
  476. set_state_write ir;
  477. List.iter loop el
  478. | TBinop(OpAssign,e1,e2) ->
  479. set_state_write ir;
  480. loop e1;
  481. loop e2;
  482. | TBinop(OpAssignOp _,e1,e2) ->
  483. set_state_read ir;
  484. set_state_write ir;
  485. loop e1;
  486. loop e2;
  487. | TUnop((Increment | Decrement),_,e1) ->
  488. set_state_read ir;
  489. set_state_write ir;
  490. loop e1
  491. | _ ->
  492. Type.iter loop e
  493. in
  494. loop e;
  495. ir
  496. let to_string ir =
  497. let s_intmap f h =
  498. String.concat ", " (IntMap.fold (fun k _ acc -> (f k) :: acc) h [])
  499. in
  500. let s_stringmap f h =
  501. String.concat ", " (StringMap.fold (fun k _ acc -> (f k) :: acc) h [])
  502. in
  503. Type.Printer.s_record_fields "" [
  504. "ir_var_reads",s_intmap string_of_int ir.ir_var_reads;
  505. "ir_var_writes",s_intmap string_of_int ir.ir_var_writes;
  506. "ir_field_reads",s_stringmap (fun x -> x) ir.ir_field_reads;
  507. "ir_field_writes",s_stringmap (fun x -> x) ir.ir_field_writes;
  508. "ir_state_read",string_of_bool ir.ir_state_read;
  509. "ir_state_write",string_of_bool ir.ir_state_write;
  510. ]
  511. end
  512. class fusion_state = object(self)
  513. val mutable _changed = false
  514. val var_reads = Hashtbl.create 0
  515. val var_writes = Hashtbl.create 0
  516. method private change map v delta =
  517. Hashtbl.replace map v.v_id ((try Hashtbl.find map v.v_id with Not_found -> 0) + delta);
  518. method inc_reads (v : tvar) : unit = self#change var_reads v 1
  519. method dec_reads (v : tvar) : unit = self#change var_reads v (-1)
  520. method inc_writes (v : tvar) : unit = self#change var_writes v 1
  521. method dec_writes (v : tvar) : unit = self#change var_writes v (-1)
  522. method get_reads (v : tvar) = try Hashtbl.find var_reads v.v_id with Not_found -> 0
  523. method get_writes (v : tvar) = try Hashtbl.find var_writes v.v_id with Not_found -> 0
  524. method change_writes (v : tvar) delta = self#change var_writes v delta
  525. method changed = _changed <- true
  526. method reset = _changed <- false
  527. method did_change = _changed
  528. method infer_from_texpr (e : texpr) =
  529. let rec loop e = match e.eexpr with
  530. | TLocal v ->
  531. self#inc_reads v;
  532. | TBinop(OpAssign,{eexpr = TLocal v},e2) ->
  533. self#inc_writes v;
  534. loop e2
  535. | _ ->
  536. Type.iter loop e
  537. in
  538. loop e
  539. end
  540. (*
  541. Fusion tries to join expressions together in order to make the output "look nicer". To that end,
  542. several transformations occur:
  543. - `var x; x = e;` is transformed to `var x = e;`
  544. - `var x; if(e1) x = e2 else x = e3` is transformed to `var x = e1 ? e2 : e3` on targets that
  545. deal well with that.
  546. - `var x = e;` is transformed to `e` if `x` is unused.
  547. - Some block-level increment/decrement unary operators are put back into value places and the
  548. transformation of their postfix variant is reversed.
  549. - `x = x op y` is transformed (back) to `x op= y` on targets that deal well with that.
  550. Most importantly, any `var v = e;` might be fused into expressions that follow it in the same
  551. block if there is no interference.
  552. *)
  553. module Fusion = struct
  554. open AnalyzerConfig
  555. open InterferenceReport
  556. let is_assign_op = function
  557. | OpAdd
  558. | OpMult
  559. | OpDiv
  560. | OpSub
  561. | OpAnd
  562. | OpOr
  563. | OpXor
  564. | OpShl
  565. | OpShr
  566. | OpUShr
  567. | OpMod ->
  568. true
  569. | OpAssign
  570. | OpEq
  571. | OpNotEq
  572. | OpGt
  573. | OpGte
  574. | OpLt
  575. | OpLte
  576. | OpBoolAnd
  577. | OpBoolOr
  578. | OpAssignOp _
  579. | OpInterval
  580. | OpIn
  581. | OpArrow ->
  582. false
  583. let use_assign_op com op e1 e2 e3 =
  584. let skip e = match com.platform with
  585. | Eval -> Texpr.skip e
  586. | _ -> e
  587. in
  588. let e1 = skip e1 in
  589. let e2 = skip e2 in
  590. is_assign_op op && target_handles_assign_ops com e3 && Texpr.equal e1 e2 && not (has_side_effect e1) && match com.platform with
  591. | Cs when is_null e1.etype || is_null e2.etype -> false (* C# hates OpAssignOp on Null<T> *)
  592. | _ -> true
  593. let apply com config e =
  594. let state = new fusion_state in
  595. state#infer_from_texpr e;
  596. (* Handles block-level expressions, e.g. by removing side-effect-free ones and recursing into compound constructs like
  597. array or object declarations. The resulting element list is reversed.
  598. INFO: `el` is a reversed list of expressions in a block.
  599. *)
  600. let rec block_element ?(loop_bottom=false) acc el = match el with
  601. | {eexpr = TBinop(OpAssign, { eexpr = TLocal v1 }, { eexpr = TLocal v2 })} :: el when v1 == v2 ->
  602. block_element acc el
  603. | {eexpr = TBinop((OpAssign | OpAssignOp _),_,_) | TUnop((Increment | Decrement),_,_)} as e1 :: el ->
  604. block_element (e1 :: acc) el
  605. | {eexpr = TLocal _} as e1 :: el when not config.local_dce ->
  606. block_element (e1 :: acc) el
  607. | {eexpr = TLocal v} :: el ->
  608. state#dec_reads v;
  609. block_element acc el
  610. | {eexpr = TField (_,fa)} as e1 :: el when PurityState.is_explicitly_impure fa ->
  611. block_element (e1 :: acc) el
  612. (* no-side-effect *)
  613. | {eexpr = TEnumParameter _ | TEnumIndex _ | TFunction _ | TConst _ | TTypeExpr _} :: el ->
  614. block_element acc el
  615. | {eexpr = TMeta((Meta.Pure,_,_),_)} :: el ->
  616. block_element acc el
  617. | {eexpr = TCall({eexpr = TField(e1,fa)},el1)} :: el2 when PurityState.is_pure_field_access fa && config.local_dce ->
  618. block_element acc (e1 :: el1 @ el2)
  619. | {eexpr = TNew(c,tl,el1)} :: el2 when (match c.cl_constructor with Some cf when PurityState.is_pure c cf -> true | _ -> false) && config.local_dce ->
  620. block_element acc (el1 @ el2)
  621. | {eexpr = TIf ({ eexpr = TConst (TBool t) },e1,e2)} :: el ->
  622. if t then
  623. block_element acc (e1 :: el)
  624. else begin match e2 with
  625. | None ->
  626. block_element acc el
  627. | Some e ->
  628. block_element acc (e :: el)
  629. end
  630. | ({eexpr = TSwitch(e1,cases,def)} as e) :: el ->
  631. begin match Optimizer.check_constant_switch e1 cases def with
  632. | Some e -> block_element acc (e :: el)
  633. | None -> block_element (e :: acc) el
  634. end
  635. (* no-side-effect composites *)
  636. | {eexpr = TParenthesis e1 | TMeta(_,e1) | TCast(e1,None) | TField(e1,_) | TUnop(_,_,e1)} :: el ->
  637. block_element acc (e1 :: el)
  638. | {eexpr = TArray(e1,e2) | TBinop(_,e1,e2)} :: el ->
  639. block_element acc (e1 :: e2 :: el)
  640. | {eexpr = TArrayDecl el1 | TCall({eexpr = TField(_,FEnum _)},el1)} :: el2 -> (* TODO: check e1 of FEnum *)
  641. block_element acc (el1 @ el2)
  642. | {eexpr = TObjectDecl fl} :: el ->
  643. block_element acc ((List.map snd fl) @ el)
  644. | {eexpr = TIf(e1,e2,None)} :: el when not (has_side_effect e2) ->
  645. block_element acc (e1 :: el)
  646. | {eexpr = TIf(e1,e2,Some e3)} :: el when not (has_side_effect e2) && not (has_side_effect e3) ->
  647. block_element acc (e1 :: el)
  648. | {eexpr = TBlock [e1]} :: el ->
  649. block_element acc (e1 :: el)
  650. | {eexpr = TBlock []} :: el ->
  651. block_element acc el
  652. | { eexpr = TContinue } :: el when loop_bottom ->
  653. block_element [] el
  654. | e1 :: el ->
  655. block_element (e1 :: acc) el
  656. | [] ->
  657. acc
  658. in
  659. let can_be_fused v e =
  660. let num_uses = state#get_reads v in
  661. let num_writes = state#get_writes v in
  662. let can_be_used_as_value = can_be_used_as_value com e in
  663. let is_compiler_generated = match v.v_kind with VUser _ | VInlined -> false | _ -> true in
  664. let has_type_params = match v.v_extra with Some (tl,_) when tl <> [] -> true | _ -> false in
  665. let b = num_uses <= 1 &&
  666. num_writes = 0 &&
  667. can_be_used_as_value &&
  668. not (
  669. ExtType.has_variable_semantics v.v_type &&
  670. (match e.eexpr with TLocal { v_kind = VUser _ } -> false | _ -> true)
  671. ) &&
  672. (is_compiler_generated || config.optimize && config.fusion && config.user_var_fusion && not has_type_params)
  673. in
  674. if config.fusion_debug then begin
  675. print_endline (Printf.sprintf "\nFUSION: %s\n\tvar %s<%i> = %s" (if b then "true" else "false") v.v_name v.v_id (s_expr_pretty e));
  676. print_endline (Printf.sprintf "CONDITION:\n\tnum_uses:%i <= 1 && num_writes:%i = 0 && can_be_used_as_value:%b && (is_compiler_generated:%b || config.optimize:%b && config.fusion:%b && config.user_var_fusion:%b)"
  677. num_uses num_writes can_be_used_as_value is_compiler_generated config.optimize config.fusion config.user_var_fusion)
  678. end;
  679. b
  680. in
  681. let rec fuse acc el = match el with
  682. | ({eexpr = TVar(v1,None)} as e1) :: {eexpr = TBinop(OpAssign,{eexpr = TLocal v2},e2)} :: el when v1 == v2 ->
  683. state#changed;
  684. let e1 = {e1 with eexpr = TVar(v1,Some e2)} in
  685. state#dec_writes v1;
  686. fuse (e1 :: acc) el
  687. | ({eexpr = TIf(eif,ethen,Some eelse)} as e1) :: el when
  688. can_be_used_as_value com e1 &&
  689. not (ExtType.is_void e1.etype) &&
  690. (match com.platform with
  691. | Cpp when not (Common.defined com Define.Cppia) -> false
  692. | _ -> true)
  693. ->
  694. begin try
  695. let i = ref 0 in
  696. let e' = ref None in
  697. let check e1 f1 e2 = match !e' with
  698. | None ->
  699. e' := Some (e1,f1);
  700. e2
  701. | Some (e',_) ->
  702. if Texpr.equal e' e1 then e2 else raise Exit
  703. in
  704. let check_assign e =
  705. match e.eexpr with
  706. | TBinop(OpAssign,e1,e2) -> incr i; check e1 (fun e' -> {e with eexpr = TBinop(OpAssign,e1,e')}) e2
  707. | _ -> raise Exit
  708. in
  709. let e,_ = map_values check_assign e1 in
  710. let e = match !e' with
  711. | None -> die "" __LOC__
  712. | Some(e1,f) ->
  713. begin match e1.eexpr with
  714. | TLocal v -> state#change_writes v (- !i + 1)
  715. | _ -> ()
  716. end;
  717. f e
  718. in
  719. state#changed;
  720. fuse (e :: acc) el
  721. with Exit ->
  722. fuse (e1 :: acc) el
  723. end
  724. | {eexpr = TVar(v1,Some e1)} :: el when config.optimize && config.local_dce && state#get_reads v1 = 0 && state#get_writes v1 = 0 ->
  725. fuse acc (e1 :: el)
  726. | ({eexpr = TVar(v1,None)} as ev) :: el when not v1.v_capture ->
  727. let found = ref false in
  728. let rec replace deep e = match e.eexpr with
  729. | TBinop(OpAssign,{eexpr = TLocal v2},e2) when v1 == v2 ->
  730. if deep then raise Exit;
  731. found := true;
  732. {ev with eexpr = TVar(v1,Some e2)}
  733. | TLocal v2 when v1 == v2 -> raise Exit
  734. | _ -> Type.map_expr (replace true) e
  735. in
  736. begin try
  737. let rec loop acc el = match el with
  738. | e :: el ->
  739. let e = replace false e in
  740. if !found then (List.rev (e :: acc)) @ el
  741. else loop (e :: acc) el
  742. | [] ->
  743. List.rev acc
  744. in
  745. let el = loop [] el in
  746. if not !found then raise Exit;
  747. state#changed;
  748. state#dec_writes v1;
  749. fuse acc el
  750. with Exit ->
  751. fuse (ev :: acc) el
  752. end
  753. | ({eexpr = TVar(v1,Some e1)} as ev) :: el when can_be_fused v1 e1 ->
  754. let found = ref false in
  755. let blocked = ref false in
  756. let ir = InterferenceReport.from_texpr e1 in
  757. if config.fusion_debug then print_endline (Printf.sprintf "INTERFERENCE: %s\nINTO: %s"
  758. (InterferenceReport.to_string ir) (Type.s_expr_pretty true "" false (s_type (print_context())) (mk (TBlock el) t_dynamic null_pos)));
  759. (* This function walks the AST in order of evaluation and tries to find an occurrence of v1. If successful, that occurrence is
  760. replaced with e1. If there's an interference "on the way" the replacement is canceled. *)
  761. let rec replace e =
  762. let explore e =
  763. let old = !blocked in
  764. blocked := true;
  765. let e = replace e in
  766. blocked := old;
  767. e
  768. in
  769. let handle_el' el =
  770. (* This mess deals with the fact that the order of evaluation is undefined for call
  771. arguments on these targets. Even if we find a replacement, we pretend that we
  772. didn't in order to find possible interferences in later call arguments. *)
  773. let temp_found = false in
  774. let really_found = ref !found in
  775. let el = List.map (fun e ->
  776. found := temp_found;
  777. let e = replace e in
  778. if !found then really_found := true;
  779. e
  780. ) el in
  781. found := !really_found;
  782. el
  783. in
  784. let handle_el = if not (target_handles_side_effect_order com) then handle_el' else List.map replace in
  785. let handle_call e2 el = match com.platform with
  786. | Neko ->
  787. (* Neko has this reversed at the moment (issue #4787) *)
  788. let el = List.map replace el in
  789. let e2 = replace e2 in
  790. e2,el
  791. | Cpp ->
  792. let e2 = replace e2 in
  793. let el = handle_el el in
  794. e2,el
  795. | _ ->
  796. let e2 = replace e2 in
  797. let el = List.map replace el in
  798. e2,el
  799. in
  800. if !found then e else match e.eexpr with
  801. | TWhile _ | TTry _ ->
  802. raise Exit
  803. | TFunction _ ->
  804. e
  805. | TIf(e1,e2,eo) ->
  806. let e1 = replace e1 in
  807. if not !found && (has_state_write ir || has_any_field_write ir || has_any_var_write ir) then raise Exit;
  808. let e2 = replace e2 in
  809. let eo = Option.map replace eo in
  810. {e with eexpr = TIf(e1,e2,eo)}
  811. | TSwitch(e1,cases,edef) ->
  812. let e1 = match com.platform with
  813. | Lua | Python -> explore e1
  814. | _ -> replace e1
  815. in
  816. if not !found then raise Exit;
  817. {e with eexpr = TSwitch(e1,cases,edef)}
  818. (* locals *)
  819. | TLocal v2 when v1 == v2 && not !blocked ->
  820. found := true;
  821. if type_change_ok com v1.v_type e1.etype then e1 else mk (TCast(e1,None)) v1.v_type e.epos
  822. | TLocal v ->
  823. if has_var_write ir v || ((v.v_capture || ExtType.has_reference_semantics v.v_type) && (has_state_write ir)) then raise Exit;
  824. e
  825. | TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) ->
  826. let e2 = replace e2 in
  827. if not !found && has_var_read ir v then raise Exit;
  828. {e with eexpr = TBinop(OpAssign,e1,e2)}
  829. (* Never fuse into write-positions (issue #7298) *)
  830. | TBinop(OpAssignOp _,{eexpr = TLocal v2},_) | TUnop((Increment | Decrement),_,{eexpr = TLocal v2}) when v1 == v2 ->
  831. raise Exit
  832. | TBinop(OpAssignOp _ as op,({eexpr = TLocal v} as e1),e2) ->
  833. let e2 = replace e2 in
  834. if not !found && (has_var_read ir v || has_var_write ir v) then raise Exit;
  835. {e with eexpr = TBinop(op,e1,e2)}
  836. | TUnop((Increment | Decrement),_,{eexpr = TLocal v}) when has_var_read ir v || has_var_write ir v ->
  837. raise Exit
  838. (* fields *)
  839. | TField(e1,fa) ->
  840. let e1 = replace e1 in
  841. if not !found && not (is_read_only_field_access e1 fa) && (has_field_write ir (field_name fa) || has_state_write ir) then raise Exit;
  842. {e with eexpr = TField(e1,fa)}
  843. | TBinop(OpAssign,({eexpr = TField(e1,fa)} as ef),e2) ->
  844. let e1 = replace e1 in
  845. let e2 = replace e2 in
  846. if not !found && (has_field_read ir (field_name fa) || has_state_read ir) then raise Exit;
  847. {e with eexpr = TBinop(OpAssign,{ef with eexpr = TField(e1,fa)},e2)}
  848. | TBinop(OpAssignOp _ as op,({eexpr = TField(e1,fa)} as ef),e2) ->
  849. let e1 = replace e1 in
  850. let s = field_name fa in
  851. if not !found && (has_field_write ir s || has_state_write ir) then raise Exit;
  852. let e2 = replace e2 in
  853. if not !found && (has_field_read ir s || has_state_read ir) then raise Exit;
  854. {e with eexpr = TBinop(op,{ef with eexpr = TField(e1,fa)},e2)}
  855. | TUnop((Increment | Decrement),_,{eexpr = TField(e1,fa)}) when has_field_read ir (field_name fa) || has_state_read ir
  856. || has_field_write ir (field_name fa) || has_state_write ir ->
  857. raise Exit
  858. (* array *)
  859. | TArray(e1,e2) ->
  860. let e1 = replace e1 in
  861. let e2 = replace e2 in
  862. if not !found && has_state_write ir then raise Exit;
  863. {e with eexpr = TArray(e1,e2)}
  864. | TBinop(OpAssign,({eexpr = TArray(e1,e2)} as ef),e3) ->
  865. let e1 = replace e1 in
  866. let e2 = replace e2 in
  867. let e3 = replace e3 in
  868. if not !found && (has_state_read ir) then raise Exit;
  869. {e with eexpr = TBinop(OpAssign,{ef with eexpr = TArray(e1,e2)},e3)}
  870. | TBinop(OpAssignOp _ as op,({eexpr = TArray(e1,e2)} as ef),e3) ->
  871. let e1 = replace e1 in
  872. let e2 = replace e2 in
  873. if not !found && has_state_write ir then raise Exit;
  874. let e3 = replace e3 in
  875. if not !found && has_state_read ir then raise Exit;
  876. {e with eexpr = TBinop(op,{ef with eexpr = TArray(e1,e2)},e3)}
  877. | TUnop((Increment | Decrement),_,{eexpr = TArray _}) when has_state_read ir || has_state_write ir ->
  878. raise Exit
  879. (* state *)
  880. | TCall({eexpr = TIdent s},el) when not (is_unbound_call_that_might_have_side_effects s el) ->
  881. e
  882. | TNew(c,tl,el) when (match c.cl_constructor with Some cf when PurityState.is_pure c cf -> true | _ -> false) ->
  883. let el = handle_el el in
  884. if not !found && (has_state_write ir || has_any_field_write ir) then raise Exit;
  885. {e with eexpr = TNew(c,tl,el)}
  886. | TNew(c,tl,el) ->
  887. let el = handle_el el in
  888. if not !found && (has_state_write ir || has_state_read ir || has_any_field_read ir || has_any_field_write ir) then raise Exit;
  889. {e with eexpr = TNew(c,tl,el)}
  890. | TCall({eexpr = TField(_,FEnum _)} as ef,el) ->
  891. let el = handle_el el in
  892. {e with eexpr = TCall(ef,el)}
  893. | TCall({eexpr = TField(_,fa)} as ef,el) when PurityState.is_pure_field_access fa ->
  894. let ef,el = handle_call ef el in
  895. if not !found && (has_state_write ir || has_any_field_write ir) then raise Exit;
  896. {e with eexpr = TCall(ef,el)}
  897. | TCall(e1,el) ->
  898. let e1,el = match e1.eexpr with
  899. | TIdent s when s <> "`trace" && s <> "__int__" -> e1,el
  900. | _ -> handle_call e1 el
  901. in
  902. if not !found && (((has_state_read ir || has_any_field_read ir)) || has_state_write ir || has_any_field_write ir) then raise Exit;
  903. {e with eexpr = TCall(e1,el)}
  904. | TObjectDecl fl ->
  905. (* The C# generator has trouble with evaluation order in structures (#7531). *)
  906. let el = (match com.platform with Cs -> handle_el' | _ -> handle_el) (List.map snd fl) in
  907. if not !found && (has_state_write ir || has_any_field_write ir) then raise Exit;
  908. {e with eexpr = TObjectDecl (List.map2 (fun (s,_) e -> s,e) fl el)}
  909. | TArrayDecl el ->
  910. let el = handle_el el in
  911. (*if not !found && (has_state_write ir || has_any_field_write ir) then raise Exit;*)
  912. {e with eexpr = TArrayDecl el}
  913. | TBinop(op,e1,e2) when (match com.platform with Cpp -> true | _ -> false) ->
  914. let e1 = replace e1 in
  915. let temp_found = !found in
  916. found := false;
  917. let e2 = replace e2 in
  918. found := !found || temp_found;
  919. {e with eexpr = TBinop(op,e1,e2)}
  920. | _ ->
  921. Type.map_expr replace e
  922. in
  923. begin try
  924. let rec loop acc el = match el with
  925. | e :: el ->
  926. let e = replace e in
  927. if !found then (List.rev (e :: acc)) @ el
  928. else loop (e :: acc) el
  929. | [] ->
  930. List.rev acc
  931. in
  932. let el = loop [] el in
  933. if not !found then raise Exit;
  934. state#changed;
  935. state#dec_reads v1;
  936. if config.fusion_debug then print_endline (Printf.sprintf "YES: %s" (s_expr_pretty (mk (TBlock el) t_dynamic null_pos)));
  937. fuse acc el
  938. with Exit ->
  939. if config.fusion_debug then print_endline (Printf.sprintf "NO: %s" (Printexc.get_backtrace()));
  940. begin match el with
  941. | ({eexpr = TUnop((Increment | Decrement) as op,Prefix,{eexpr = TLocal v1})} as e2) :: el ->
  942. let found = ref false in
  943. let rec replace e = match e.eexpr with
  944. | TLocal v2 when v1 == v2 ->
  945. if !found then raise Exit;
  946. found := true;
  947. {e with eexpr = TUnop(op,Postfix,e)}
  948. | TIf _ | TSwitch _ | TTry _ | TWhile _ | TFor _ ->
  949. raise Exit
  950. | _ ->
  951. Type.map_expr replace e
  952. in
  953. begin try
  954. let ev = replace ev in
  955. if not !found then raise Exit;
  956. state#changed;
  957. fuse acc (ev :: el)
  958. with Exit ->
  959. fuse (ev :: acc) (e2 :: el)
  960. end
  961. | _ ->
  962. fuse (ev :: acc) el
  963. end
  964. end
  965. | {eexpr = TUnop((Increment | Decrement as op,Prefix,({eexpr = TLocal v} as ev)))} as e1 :: e2 :: el ->
  966. begin try
  967. let e2,f = match e2.eexpr with
  968. | TReturn (Some e2) -> e2,(fun e -> {e2 with eexpr = TReturn (Some e)})
  969. (* This is not sound if e21 contains the variable (issue #7704) *)
  970. (* | TBinop(OpAssign,e21,e22) -> e22,(fun e -> {e2 with eexpr = TBinop(OpAssign,e21,e)}) *)
  971. | TVar(v,Some e2) -> e2,(fun e -> {e2 with eexpr = TVar(v,Some e)})
  972. | _ -> raise Exit
  973. in
  974. let ops_match op1 op2 = match op1,op2 with
  975. | Increment,OpSub
  976. | Decrement,OpAdd ->
  977. true
  978. | _ ->
  979. false
  980. in
  981. begin match e2.eexpr with
  982. | TBinop(op2,{eexpr = TLocal v2},{eexpr = TConst (TInt i32)}) when v == v2 && Int32.to_int i32 = 1 && ops_match op op2 ->
  983. state#changed;
  984. state#dec_reads v2;
  985. let e = (f {e1 with eexpr = TUnop(op,Postfix,ev)}) in
  986. fuse (e :: acc) el
  987. | TLocal v2 when v == v2 ->
  988. state#changed;
  989. state#dec_reads v2;
  990. let e = (f {e1 with eexpr = TUnop(op,Prefix,ev)}) in
  991. fuse (e :: acc) el
  992. | _ ->
  993. raise Exit
  994. end
  995. with Exit ->
  996. fuse (e1 :: acc) (e2 :: el)
  997. end
  998. | {eexpr = TBinop(OpAssign,e1,{eexpr = TBinop(op,e2,e3)})} as e :: el when use_assign_op com op e1 e2 e3 ->
  999. let rec loop e = match e.eexpr with
  1000. | TLocal v -> state#dec_reads v;
  1001. | _ -> Type.iter loop e
  1002. in
  1003. loop e1;
  1004. state#changed;
  1005. fuse acc ({e with eexpr = TBinop(OpAssignOp op,e1,e3)} :: el)
  1006. | {eexpr = TBinop(OpAssignOp _,e1,_)} as eop :: ({eexpr = TVar(v,Some e2)} as evar) :: el when Texpr.equal e1 e2 ->
  1007. state#changed;
  1008. fuse ({evar with eexpr = TVar(v,Some eop)} :: acc) el
  1009. | e1 :: el ->
  1010. fuse (e1 :: acc) el
  1011. | [] ->
  1012. acc
  1013. in
  1014. let rec loop e = match e.eexpr with
  1015. | TWhile(condition,{ eexpr = TBlock el; etype = t; epos = p },flag) ->
  1016. let condition = loop condition
  1017. and body = block true el t p in
  1018. { e with eexpr = TWhile(condition,body,flag) }
  1019. | TBlock el ->
  1020. block false el e.etype e.epos
  1021. | TCall({eexpr = TIdent s},_) when is_really_unbound s ->
  1022. e
  1023. | _ ->
  1024. Type.map_expr loop e
  1025. and block loop_body el t p =
  1026. let el = List.rev_map loop el in
  1027. let el = block_element ~loop_bottom:loop_body [] el in
  1028. (* fuse flips element order, but block_element doesn't care and flips it back *)
  1029. let el = fuse [] el in
  1030. let el = block_element [] el in
  1031. let rec fuse_loop el =
  1032. state#reset;
  1033. let el = fuse [] el in
  1034. let el = block_element [] el in
  1035. if state#did_change then fuse_loop el else el
  1036. in
  1037. let el = fuse_loop el in
  1038. mk (TBlock el) t p
  1039. in
  1040. loop e
  1041. end
  1042. module Cleanup = struct
  1043. open TexprKindMapper
  1044. let apply com e =
  1045. let if_or_op e e1 e2 e3 = match (Texpr.skip e1).eexpr,(Texpr.skip e3).eexpr with
  1046. | TUnop(Not,Prefix,e1),TConst (TBool true) -> optimize_binop {e with eexpr = TBinop(OpBoolOr,e1,e2)} OpBoolOr e1 e2
  1047. | _,TConst (TBool false) -> optimize_binop {e with eexpr = TBinop(OpBoolAnd,e1,e2)} OpBoolAnd e1 e2
  1048. | _,TBlock [] -> {e with eexpr = TIf(e1,e2,None)}
  1049. | _ -> match (Texpr.skip e2).eexpr with
  1050. | TBlock [] when com.platform <> Cs ->
  1051. let e1' = mk (TUnop(Not,Prefix,e1)) e1.etype e1.epos in
  1052. let e1' = optimize_unop e1' Not Prefix e1 in
  1053. {e with eexpr = TIf(e1',e3,None)}
  1054. | _ ->
  1055. {e with eexpr = TIf(e1,e2,Some e3)}
  1056. in
  1057. let rec loop e = match e.eexpr with
  1058. | TIf(e1,e2,Some e3) ->
  1059. let e1 = loop e1 in
  1060. let e2 = loop e2 in
  1061. let e3 = loop e3 in
  1062. if_or_op e e1 e2 e3;
  1063. | TUnop((Increment | Decrement),_,e1) when (match (Texpr.skip e1).eexpr with TConst _ -> true | _ -> false) ->
  1064. loop e1
  1065. | TCall({eexpr = TIdent s},_) when is_really_unbound s ->
  1066. e
  1067. | TBlock el ->
  1068. let el = List.map (fun e ->
  1069. let e = loop e in
  1070. match e.eexpr with
  1071. | TIf _ -> {e with etype = com.basic.tvoid}
  1072. | _ -> e
  1073. ) el in
  1074. {e with eexpr = TBlock el}
  1075. | TWhile(e1,e2,NormalWhile) ->
  1076. let e1 = loop e1 in
  1077. let e2 = loop e2 in
  1078. begin match e2.eexpr with
  1079. | TBlock ({eexpr = TIf(e1,({eexpr = TBlock[{eexpr = TBreak}]} as eb),None)} :: el2) ->
  1080. let e1 = Texpr.skip e1 in
  1081. let e1 = match e1.eexpr with TUnop(_,_,e1) -> e1 | _ -> {e1 with eexpr = TUnop(Not,Prefix,e1)} in
  1082. {e with eexpr = TWhile(e1,{eb with eexpr = TBlock el2},NormalWhile)}
  1083. | TBlock el ->
  1084. let rec loop2 el = match el with
  1085. | {eexpr = TBreak | TContinue | TReturn _ | TThrow _} as e :: el ->
  1086. [e]
  1087. | e :: el ->
  1088. e :: (loop2 el)
  1089. | [] ->
  1090. []
  1091. in
  1092. let el = loop2 el in
  1093. {e with eexpr = TWhile(e1,{e2 with eexpr = TBlock el},NormalWhile)}
  1094. | _ ->
  1095. {e with eexpr = TWhile(e1,e2,NormalWhile)}
  1096. end
  1097. | TField(e1,(FAnon {cf_name = s} | FDynamic s)) ->
  1098. let e1 = loop e1 in
  1099. let fa = quick_field_dynamic e1.etype s in
  1100. {e with eexpr = TField(e1,fa)}
  1101. | TField({eexpr = TTypeExpr _},_) ->
  1102. e
  1103. | TTypeExpr (TClassDecl c) ->
  1104. e
  1105. | TMeta((Meta.Ast,_,_),e1) when (match e1.eexpr with TSwitch _ -> false | _ -> true) ->
  1106. loop e1
  1107. | _ ->
  1108. Type.map_expr loop e
  1109. in
  1110. let e = loop e in
  1111. let rec loop kind e = match kind,e.eexpr with
  1112. | KEqNull,TField(e1,FClosure(Some(c,tl),cf)) ->
  1113. let e1 = loop KAccess e1 in
  1114. {e with eexpr = TField(e1,FInstance(c,tl,cf))}
  1115. | _ ->
  1116. TexprKindMapper.map kind loop e
  1117. in
  1118. TexprKindMapper.map KRead loop e
  1119. end
  1120. module Purity = struct
  1121. open PurityState
  1122. type purity_node = {
  1123. pn_class : tclass;
  1124. pn_field : tclass_field;
  1125. mutable pn_purity : PurityState.t;
  1126. mutable pn_dependents : purity_node list;
  1127. }
  1128. exception Purity_conflict of purity_node * pos
  1129. let node_lut = Hashtbl.create 0
  1130. let get_field_id c cf = Printf.sprintf "%s.%s" (s_type_path c.cl_path) cf.cf_name
  1131. let get_node c cf =
  1132. try
  1133. Hashtbl.find node_lut (get_field_id c cf)
  1134. with Not_found ->
  1135. let node = {
  1136. pn_class = c;
  1137. pn_field = cf;
  1138. pn_purity = PurityState.get_purity c cf;
  1139. pn_dependents = []
  1140. } in
  1141. Hashtbl.replace node_lut (get_field_id c cf) node;
  1142. node
  1143. let rec taint node = match node.pn_purity with
  1144. | Impure -> ()
  1145. | ExpectPure p -> raise (Purity_conflict(node,p));
  1146. | MaybePure | Pure ->
  1147. node.pn_purity <- Impure;
  1148. List.iter taint node.pn_dependents;
  1149. let rec loop c = match c.cl_super with
  1150. | None -> ()
  1151. | Some(c,_) ->
  1152. begin try
  1153. let cf = PMap.find node.pn_field.cf_name c.cl_fields in
  1154. taint (get_node c cf);
  1155. with Not_found ->
  1156. ()
  1157. end;
  1158. loop c
  1159. in
  1160. loop node.pn_class
  1161. let taint_raise node =
  1162. taint node;
  1163. raise Exit
  1164. let apply_to_field com is_ctor is_static c cf =
  1165. let node = get_node c cf in
  1166. let check_field c cf =
  1167. let node' = get_node c cf in
  1168. match node'.pn_purity with
  1169. | Pure | ExpectPure _ -> ()
  1170. | Impure -> taint_raise node;
  1171. | MaybePure -> node'.pn_dependents <- node :: node'.pn_dependents
  1172. in
  1173. let rec check_write e1 =
  1174. begin match e1.eexpr with
  1175. | TLocal v ->
  1176. if ExtType.has_reference_semantics v.v_type then taint_raise node; (* Writing to a ref type means impurity. *)
  1177. () (* Writing to locals does not violate purity. *)
  1178. | TField({eexpr = TConst TThis},_) when is_ctor ->
  1179. () (* A constructor can write to its own fields without violating purity. *)
  1180. | _ ->
  1181. taint_raise node
  1182. end
  1183. and loop e = match e.eexpr with
  1184. | TMeta((Meta.Pure,_,_) as m,_) ->
  1185. if get_purity_from_meta [m] = Impure then taint_raise node
  1186. else ()
  1187. | TThrow _ ->
  1188. taint_raise node;
  1189. | TBinop((OpAssign | OpAssignOp _),e1,e2) ->
  1190. check_write e1;
  1191. loop e2;
  1192. | TUnop((Increment | Decrement),_,e1) ->
  1193. check_write e1;
  1194. | TCall({eexpr = TField(_,FStatic(c,cf))},el) ->
  1195. List.iter loop el;
  1196. check_field c cf;
  1197. | TNew(c,_,el) ->
  1198. List.iter loop el;
  1199. begin match c.cl_constructor with
  1200. | Some cf -> check_field c cf
  1201. | None -> taint_raise node
  1202. end
  1203. | TCall({eexpr = TConst TSuper},el) ->
  1204. begin match c.cl_super with
  1205. | Some({cl_constructor = Some cf} as c,_) ->
  1206. check_field c cf;
  1207. List.iter loop el
  1208. | _ ->
  1209. taint_raise node (* Can that even happen? *)
  1210. end
  1211. | TCall({eexpr = TIdent s},el) when not (is_unbound_call_that_might_have_side_effects s el) ->
  1212. List.iter loop el;
  1213. | TCall _ ->
  1214. taint_raise node
  1215. | _ ->
  1216. Type.iter loop e
  1217. in
  1218. match cf.cf_kind with
  1219. | Method MethDynamic | Var _ ->
  1220. taint node;
  1221. | Method MethNormal when not (is_static || is_ctor || has_class_field_flag cf CfFinal) ->
  1222. taint node
  1223. | _ ->
  1224. match cf.cf_expr with
  1225. | None ->
  1226. if not (is_pure c cf) then taint node
  1227. (* TODO: The function code check shouldn't be here I guess. *)
  1228. | Some _ when (has_class_field_flag cf CfExtern || Meta.has Meta.FunctionCode cf.cf_meta || Meta.has (Meta.HlNative) cf.cf_meta || Meta.has (Meta.HlNative) c.cl_meta) ->
  1229. if not (is_pure c cf) then taint node
  1230. | Some e ->
  1231. try
  1232. begin match node.pn_purity with
  1233. | Impure -> taint_raise node
  1234. | Pure -> raise Exit
  1235. | _ -> loop e
  1236. end
  1237. with Exit ->
  1238. ()
  1239. let apply_to_class com c =
  1240. List.iter (apply_to_field com false false c) c.cl_ordered_fields;
  1241. List.iter (apply_to_field com false true c) c.cl_ordered_statics;
  1242. (match c.cl_constructor with Some cf -> apply_to_field com true false c cf | None -> ())
  1243. let infer com =
  1244. Hashtbl.clear node_lut;
  1245. List.iter (fun mt -> match mt with
  1246. | TClassDecl c ->
  1247. begin try
  1248. apply_to_class com c
  1249. with Purity_conflict(impure,p) ->
  1250. com.error "Impure field overrides/implements field which was explicitly marked as @:pure" impure.pn_field.cf_pos;
  1251. Error.error "Pure field is here" p;
  1252. end
  1253. | _ -> ()
  1254. ) com.types;
  1255. Hashtbl.iter (fun _ node ->
  1256. match node.pn_purity with
  1257. | Pure | MaybePure when not (List.exists (fun (m,_,_) -> m = Meta.Pure) node.pn_field.cf_meta) ->
  1258. node.pn_field.cf_meta <- (Meta.Pure,[EConst(Ident "true"),node.pn_field.cf_pos],node.pn_field.cf_pos) :: node.pn_field.cf_meta
  1259. | _ -> ()
  1260. ) node_lut;
  1261. end