analyzer.ml 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666
  1. open Ast
  2. open Type
  3. open Common
  4. open Typecore
  5. let s_expr = s_expr (s_type (print_context()))
  6. let s_expr_pretty = s_expr_pretty "" (s_type (print_context()))
  7. let debug e = print_endline (s_expr e)
  8. let debug_pretty s e = Printf.printf "%s %s\n" s (s_expr_pretty e)
  9. let flag_no_check = "no_check"
  10. let flag_check = "check"
  11. let flag_no_const_propagation = "no_const_propagation"
  12. let flag_const_propagation = "const_propagation"
  13. let flag_no_local_dce = "no_local_dce"
  14. let flag_local_dce = "local_dce"
  15. let flag_ignore = "ignore"
  16. let flag_no_simplification = "no_simplification"
  17. let flag_check_has_effect = "check_has_effect"
  18. let flag_no_check_has_effect = "no_check_has_effect"
  19. let has_analyzer_option meta s =
  20. try
  21. let rec loop ml = match ml with
  22. | (Meta.Analyzer,el,_) :: ml ->
  23. if List.exists (fun (e,p) ->
  24. match e with
  25. | EConst(Ident s2) when s = s2 -> true
  26. | _ -> false
  27. ) el then
  28. true
  29. else
  30. loop ml
  31. | _ :: ml ->
  32. loop ml
  33. | [] ->
  34. false
  35. in
  36. loop meta
  37. with Not_found ->
  38. false
  39. let is_ignored meta =
  40. try
  41. let rec loop ml = match ml with
  42. | (Meta.Analyzer,el,_) :: ml ->
  43. if List.exists (fun (e,p) ->
  44. match e with
  45. | EConst(Ident s2) when flag_ignore = s2 -> true
  46. | _ -> false
  47. ) el then
  48. true
  49. else
  50. loop ml
  51. | (Meta.HasUntyped,_,_) :: _ ->
  52. true
  53. | _ :: ml ->
  54. loop ml
  55. | [] ->
  56. false
  57. in
  58. loop meta
  59. with Not_found ->
  60. false
  61. let rec get_type_meta t = match t with
  62. | TMono r ->
  63. begin match !r with
  64. | None -> raise Not_found
  65. | Some t -> get_type_meta t
  66. end
  67. | TLazy f ->
  68. get_type_meta (!f())
  69. | TInst(c,_) ->
  70. c.cl_meta
  71. | TEnum(en,_) ->
  72. en.e_meta
  73. | TAbstract(a,_) ->
  74. a.a_meta
  75. | TType(t,_) ->
  76. t.t_meta
  77. | TAnon _ | TFun _ | TDynamic _ ->
  78. raise Not_found
  79. let type_has_analyzer_option t s =
  80. try
  81. has_analyzer_option (get_type_meta t) s
  82. with Not_found ->
  83. false
  84. let is_enum_type t = match follow t with
  85. | TEnum(_) -> true
  86. | _ -> false
  87. let rec awkward_get_enum_index com e = match e.eexpr with
  88. | TArray(e1,{eexpr = TConst(TInt i)}) when com.platform = Js && Int32.to_int i = 1 && is_enum_type e1.etype ->
  89. e1
  90. | TCall({eexpr = TField(e1, FDynamic "__Index")},[]) when com.platform = Cpp && is_enum_type e1.etype ->
  91. e1
  92. | TField(e1,FDynamic "index") when com.platform = Neko && is_enum_type e1.etype ->
  93. e1
  94. | TParenthesis e1 | TCast(e1,None) | TMeta(_,e1) ->
  95. awkward_get_enum_index com e1
  96. | _ ->
  97. raise Not_found
  98. (*
  99. This module simplifies the AST by introducing temporary variables for complex expressions in many places.
  100. In particular, it ensures that no branching can occur in value-places so that we can later insert SSA PHI
  101. nodes without worrying about their placement.
  102. *)
  103. module Simplifier = struct
  104. let mk_block_context com =
  105. let block_el = ref [] in
  106. let push e = block_el := e :: !block_el in
  107. let assign ev e =
  108. let mk_assign e2 = match e2.eexpr with
  109. | TBreak | TContinue | TThrow _ | TReturn _ -> e2
  110. | _ -> mk (TBinop(OpAssign,ev,e2)) e2.etype e2.epos
  111. in
  112. let rec loop e = match e.eexpr with
  113. | TBlock el ->
  114. begin match List.rev el with
  115. | e1 :: el ->
  116. let el = List.rev ((loop e1) :: el) in
  117. {e with eexpr = TBlock el}
  118. | _ ->
  119. mk_assign e
  120. end
  121. | TIf(e1,e2,eo) ->
  122. let e2 = loop e2 in
  123. let eo = match eo with None -> None | Some e3 -> Some (loop e3) in
  124. {e with eexpr = TIf(e1,e2,eo)}
  125. | TSwitch(e1,cases,edef) ->
  126. let cases = List.map (fun (el,e) ->
  127. let e = loop e in
  128. el,e
  129. ) cases in
  130. let edef = match edef with None -> None | Some edef -> Some (loop edef) in
  131. {e with eexpr = TSwitch(e1,cases,edef)}
  132. | TTry(e1,catches) ->
  133. let e1 = loop e1 in
  134. let catches = List.map (fun (v,e) ->
  135. let e = loop e in
  136. v,e
  137. ) catches in
  138. {e with eexpr = TTry(e1,catches)}
  139. | TParenthesis e1 | TMeta(_,e1) ->
  140. loop e1 (* this is still weird, have to review *)
  141. (* | TBinop(OpAssign,({eexpr = TLocal _} as e1),e2) ->
  142. push e;
  143. mk_assign e1 *)
  144. (* | TBinop(OpAssignOp op,({eexpr = TLocal _} as e1),e2) ->
  145. push e;
  146. mk_assign e1 *)
  147. | _ ->
  148. mk_assign e
  149. in
  150. loop e
  151. in
  152. let declare_temp t eo p =
  153. let v = alloc_var "tmp" t in
  154. v.v_meta <- [Meta.CompilerGenerated,[],p];
  155. let e_v = mk (TLocal v) t p in
  156. let declare e_init =
  157. let e = mk (TVar (v,e_init)) com.basic.tvoid p in
  158. push e;
  159. in
  160. let e_v = match eo with
  161. | None ->
  162. declare None;
  163. e_v
  164. | Some e1 ->
  165. begin match e1.eexpr with
  166. | TThrow _ | TReturn _ | TBreak | TContinue ->
  167. e1
  168. | _ ->
  169. let rec loop e_v e = match e.eexpr with
  170. | TParenthesis e1 ->
  171. loop {e_v with eexpr = TParenthesis e_v} e1
  172. | TMeta(m,e1) ->
  173. loop {e_v with eexpr = TMeta(m,e_v)} e1
  174. | _ ->
  175. e_v,e
  176. in
  177. let e_v',e1 = loop e_v e1 in
  178. let e1 = assign e_v e1 in
  179. begin match e1.eexpr with
  180. | TBinop(OpAssign,{eexpr = TLocal v1},e2) when v == v1 ->
  181. declare (Some e2)
  182. | _ ->
  183. declare None;
  184. push e1
  185. end;
  186. e_v'
  187. end
  188. in
  189. e_v
  190. in
  191. let rec push_block () =
  192. let cur = !block_el in
  193. block_el := [];
  194. fun () ->
  195. let added = !block_el in
  196. block_el := cur;
  197. List.rev added
  198. and block f el =
  199. let close = push_block() in
  200. List.iter (fun e ->
  201. push (f e)
  202. ) el;
  203. close()
  204. in
  205. block,declare_temp,fun () -> !block_el
  206. let apply com e =
  207. let block,declare_temp,close_block = mk_block_context com in
  208. let skip_binding ?(allow_tlocal=false) e =
  209. let rec loop e =
  210. match e.eexpr with
  211. | TConst _ | TTypeExpr _ | TFunction _ -> ()
  212. | TLocal _ when allow_tlocal -> ()
  213. | TParenthesis e1 | TCast(e1,None) -> Type.iter loop e
  214. | TField(_,(FStatic(c,cf) | FInstance(c,_,cf))) when has_analyzer_option cf.cf_meta flag_no_simplification || has_analyzer_option c.cl_meta flag_no_simplification -> ()
  215. | TField({eexpr = TLocal _},_) when allow_tlocal -> ()
  216. | TCall({eexpr = TField(_,(FStatic(c,cf) | FInstance(c,_,cf)))},el) when has_analyzer_option cf.cf_meta flag_no_simplification || has_analyzer_option c.cl_meta flag_no_simplification -> ()
  217. | TCall({eexpr = TLocal { v_name = "__cpp__" } },_) -> ()
  218. | TField(_,FEnum _) -> ()
  219. | TField(_,FDynamic _) -> ()
  220. | _ when (try ignore(awkward_get_enum_index com e); true with Not_found -> false) -> ()
  221. | _ -> raise Exit
  222. in
  223. try
  224. loop e;
  225. true
  226. with Exit ->
  227. begin match follow e.etype with
  228. | TAbstract({a_path = [],"Void"},_) -> true
  229. | _ -> false
  230. end
  231. in
  232. let has_unbound = ref false in
  233. let rec loop e = match e.eexpr with
  234. | TCall({eexpr = TLocal v | TField({eexpr = TLocal v},_)},_) | TField({eexpr = TLocal v},_) | TLocal v when Meta.has Meta.Unbound v.v_meta && v.v_name <> "`trace" ->
  235. has_unbound := true;
  236. e
  237. | TBlock el ->
  238. {e with eexpr = TBlock (block loop el)}
  239. | TCall({eexpr = TField(_,(FStatic(c,cf) | FInstance(c,_,cf)))},el) when has_analyzer_option cf.cf_meta flag_no_simplification || has_analyzer_option c.cl_meta flag_no_simplification ->
  240. e
  241. | TField(_,(FStatic(c,cf) | FInstance(c,_,cf))) when has_analyzer_option cf.cf_meta flag_no_simplification || has_analyzer_option c.cl_meta flag_no_simplification ->
  242. e
  243. | TCall(e1,el) ->
  244. let rec is_valid_call_target e = match e.eexpr with
  245. | TFunction _ | TField _ | TLocal _ | TConst (TSuper) ->
  246. true
  247. | TParenthesis e1 | TCast(e1,None) | TMeta(_,e1) ->
  248. is_valid_call_target e1
  249. | _ ->
  250. false
  251. in
  252. let e1 = if is_valid_call_target e1 then
  253. loop e1
  254. else
  255. bind e1
  256. in
  257. let check e t =
  258. if type_has_analyzer_option t flag_no_simplification then e
  259. else bind e
  260. in
  261. let el = match e1.eexpr,follow e1.etype with
  262. | TConst TSuper,_ when com.platform = Java || com.platform = Cs ->
  263. (* they hate you if you mess up the super call *)
  264. el
  265. | _,TFun _ | TConst TSuper,_ ->
  266. Codegen.UnificationCallback.check_call check el e1.etype
  267. | _ ->
  268. (* too dangerous *)
  269. List.map loop el
  270. in
  271. {e with eexpr = TCall(e1,el)}
  272. | TNew(c,tl,el) ->
  273. {e with eexpr = TNew(c,tl,ordered_list el)}
  274. | TArrayDecl el ->
  275. {e with eexpr = TArrayDecl (ordered_list el)}
  276. | TObjectDecl fl ->
  277. let el = ordered_list (List.map snd fl) in
  278. {e with eexpr = TObjectDecl (List.map2 (fun (n,_) e -> n,e) fl el)}
  279. | TBinop(OpBoolAnd | OpBoolOr as op,e1,e2) ->
  280. let e1 = loop e1 in
  281. let e_then = mk (TBlock (block loop [e2])) e2.etype e2.epos in
  282. let e_if,e_else = if op = OpBoolOr then
  283. mk (TUnop(Not,Prefix,e1)) com.basic.tbool e.epos,mk (TConst (TBool(true))) com.basic.tbool e.epos
  284. else
  285. e1,mk (TConst (TBool(false))) com.basic.tbool e.epos
  286. in
  287. loop (mk (TIf(e_if,e_then,Some e_else)) com.basic.tbool e.epos)
  288. | TBinop((OpAssign | OpAssignOp _) as op,{eexpr = TArray(e11,e12)},e2) ->
  289. let e1 = match ordered_list [e11;e12] with
  290. | [e1;e2] ->
  291. {e with eexpr = TArray(e1,e2)}
  292. | _ ->
  293. assert false
  294. in
  295. let e2 = bind e2 in
  296. {e with eexpr = TBinop(op,e1,e2)}
  297. | TBinop((OpAssign | OpAssignOp _) as op,e1,e2) ->
  298. let e2 = bind ~allow_tlocal:true e2 in
  299. let e1 = loop e1 in
  300. {e with eexpr = TBinop(op,e1,e2)}
  301. | TBinop(op,e1,e2) ->
  302. begin match ordered_list [e1;e2] with
  303. | [e1;e2] ->
  304. {e with eexpr = TBinop(op,e1,e2)}
  305. | _ ->
  306. assert false
  307. end
  308. | TArray(e1,e2) ->
  309. begin match ordered_list [e1;e2] with
  310. | [e1;e2] ->
  311. {e with eexpr = TArray(e1,e2)}
  312. | _ ->
  313. assert false
  314. end
  315. | TWhile(e1,e2,flag) when (match e1.eexpr with TConst(TBool true) | TParenthesis {eexpr = TConst(TBool true)} -> false | _ -> true) ->
  316. let p = e.epos in
  317. let e_break = mk TBreak t_dynamic p in
  318. let e_not = mk (TUnop(Not,Prefix,Codegen.mk_parent e1)) e1.etype e1.epos in
  319. let e_if eo = mk (TIf(e_not,e_break,eo)) com.basic.tvoid p in
  320. let rec map_continue e = match e.eexpr with
  321. | TContinue ->
  322. (e_if (Some e))
  323. | TWhile _ | TFor _ ->
  324. e
  325. | _ ->
  326. Type.map_expr map_continue e
  327. in
  328. let e2 = if flag = NormalWhile then e2 else map_continue e2 in
  329. let e_if = e_if None in
  330. let e_if = mk (TMeta((Meta.Custom ":whileCond",[],e_if.epos), e_if)) e_if.etype e_if.epos in
  331. let e_block = if flag = NormalWhile then Type.concat e_if e2 else Type.concat e2 e_if in
  332. let e_true = mk (TConst (TBool true)) com.basic.tbool p in
  333. let e = mk (TWhile(Codegen.mk_parent e_true,e_block,NormalWhile)) e.etype p in
  334. loop e
  335. | TFor(v,e1,e2) ->
  336. let e1 = bind e1 in
  337. let e2 = loop e2 in
  338. {e with eexpr = TFor(v,e1,e2)}
  339. | TIf(e1,e2,eo) ->
  340. let e1 = bind e1 in
  341. let e2 = loop e2 in
  342. let eo = match eo with None -> None | Some e -> Some (loop e) in
  343. {e with eexpr = TIf(e1,e2,eo)}
  344. | TSwitch (e1,cases,eo) ->
  345. let e1 = bind e1 in
  346. let cases = List.map (fun (el,e) ->
  347. let el = List.map loop el in
  348. let e = loop e in
  349. el,e
  350. ) cases in
  351. let eo = match eo with None -> None | Some e -> Some (loop e) in
  352. {e with eexpr = TSwitch(e1,cases,eo)}
  353. | TVar(v,Some e1) ->
  354. let e1 = match e1.eexpr with
  355. | TFunction _ -> loop e1
  356. | TArrayDecl [{eexpr = TFunction _}] -> loop e1
  357. | _ -> bind ~allow_tlocal:true e1
  358. in
  359. {e with eexpr = TVar(v,Some e1)}
  360. | TUnop((Neg | NegBits | Not) as op,flag,e1) ->
  361. let e1 = bind e1 in
  362. {e with eexpr = TUnop(op,flag,e1)}
  363. | TField(e1,fa) ->
  364. let e1 = bind ~allow_tlocal:true e1 in
  365. {e with eexpr = TField(e1,fa)}
  366. | TReturn (Some ({eexpr = TThrow _ | TReturn _} as e1)) ->
  367. loop e1 (* this is a bit hackish *)
  368. | TReturn (Some e1) ->
  369. let e1 = bind e1 in
  370. {e with eexpr = TReturn (Some e1)}
  371. | TThrow e1 ->
  372. let e1 = bind e1 in
  373. {e with eexpr = TThrow e1}
  374. | TCast(e1,mto) ->
  375. let e1 = bind ~allow_tlocal:true e1 in
  376. {e with eexpr = TCast(e1,mto)}
  377. | _ ->
  378. Type.map_expr loop e
  379. and bind ?(allow_tlocal=false) e =
  380. let e = loop e in
  381. if skip_binding ~allow_tlocal e then
  382. e
  383. else
  384. declare_temp e.etype (Some e) e.epos
  385. and ordered_list el =
  386. if List.for_all (skip_binding ~allow_tlocal:true) el then
  387. List.map loop el
  388. else
  389. List.map bind el
  390. in
  391. let e = loop e in
  392. !has_unbound,match close_block() with
  393. | [] ->
  394. e
  395. | el ->
  396. mk (TBlock (List.rev (e :: el))) e.etype e.epos
  397. let unapply com e =
  398. let var_map = ref IntMap.empty in
  399. let rec get_assignment_to v e = match e.eexpr with
  400. | TBinop(OpAssign,{eexpr = TLocal v2},e2) when v == v2 -> Some e2
  401. | TBlock [e] -> get_assignment_to v e
  402. | TIf(e1,e2,Some e3) ->
  403. begin match get_assignment_to v e2,get_assignment_to v e3 with
  404. | Some e2,Some e3 -> Some ({e with eexpr = TIf(e1,e2,Some e3)})
  405. | _ -> None
  406. end
  407. | _ -> None
  408. in
  409. let if_or_op e e1 e2 e3 = match e1.eexpr,e3.eexpr with
  410. | TUnop(Not,Prefix,e1),TConst (TBool true) -> {e with eexpr = TBinop(OpBoolOr,e1,e2)}
  411. | _,TConst (TBool false) -> {e with eexpr = TBinop(OpBoolAnd,e1,e2)}
  412. | _ -> {e with eexpr = TIf(e1,e2,Some e3)}
  413. in
  414. let rec loop e = match e.eexpr with
  415. | TBlock el ->
  416. let rec loop2 el = match el with
  417. | e :: el ->
  418. begin match e.eexpr with
  419. | TVar(v,Some e1) when Meta.has Meta.CompilerGenerated v.v_meta ->
  420. if el = [] then
  421. [loop e1]
  422. else begin
  423. var_map := IntMap.add v.v_id (loop e1) !var_map;
  424. loop2 el
  425. end
  426. | TVar(v,None) when not (com.platform = Php) ->
  427. begin match el with
  428. | {eexpr = TBinop(OpAssign,{eexpr = TLocal v2},e2)} :: el when v == v2 ->
  429. let e = {e with eexpr = TVar(v,Some e2)} in
  430. loop2 (e :: el)
  431. | ({eexpr = TIf(e1,e2,Some e3)} as e_if) :: el ->
  432. let e1 = loop e1 in
  433. let e2 = loop e2 in
  434. let e3 = loop e3 in
  435. begin match get_assignment_to v e2,get_assignment_to v e3 with
  436. | Some e2,Some e3 ->
  437. let e_if = if_or_op e_if (loop e1) (loop e2) (loop e3) in
  438. let e = {e with eexpr = TVar(v,Some e_if)} in
  439. loop2 (e :: el)
  440. | _ ->
  441. let e_if = {e_if with eexpr = TIf(e1,e2,Some e3)} in
  442. e :: e_if :: loop2 el
  443. end
  444. | _ ->
  445. let e = loop e in
  446. e :: loop2 el
  447. end
  448. | TReturn (Some e1) when (match follow e1.etype with TAbstract({a_path=[],"Void"},_) -> true | _ -> false) ->
  449. [(loop e1);{e with eexpr = TReturn None}]
  450. | _ ->
  451. let e = loop e in
  452. e :: loop2 el
  453. end
  454. | [] ->
  455. []
  456. in
  457. let el = loop2 el in
  458. {e with eexpr = TBlock el}
  459. | TLocal v when Meta.has Meta.CompilerGenerated v.v_meta ->
  460. begin try IntMap.find v.v_id !var_map
  461. with Not_found -> e end
  462. | TWhile(e1,e2,flag) ->
  463. let e1 = loop e1 in
  464. let e2 = loop e2 in
  465. let extract_cond e = match e.eexpr with
  466. | TIf({eexpr = TUnop(Not,_,e1)},_,_) -> e1
  467. | TBreak -> raise Exit (* can happen due to optimization, not so easy to deal with because there might be other breaks/continues *)
  468. | _ -> assert false
  469. in
  470. let e1,e2,flag = try
  471. begin match e2.eexpr with
  472. | TBlock el ->
  473. begin match el with
  474. | {eexpr = TMeta((Meta.Custom ":whileCond",_,_),e1)} :: el ->
  475. let e1 = extract_cond e1 in
  476. e1,{e2 with eexpr = TBlock el},NormalWhile
  477. | _ ->
  478. e1,e2,flag
  479. (* issue 3844 *)
  480. (* begin match List.rev el with
  481. | {eexpr = TMeta((Meta.Custom ":whileCond",_,_),e1)} :: el ->
  482. let e1 = extract_cond e1 in
  483. e1,{e2 with eexpr = TBlock (List.rev el)},DoWhile
  484. | _ ->
  485. e1,e2,flag
  486. end *)
  487. end
  488. | _ ->
  489. e1,e2,flag
  490. end with Exit ->
  491. e1,e2,flag
  492. in
  493. {e with eexpr = TWhile(e1,e2,flag)}
  494. | TIf(e1,e2,Some e3) ->
  495. let e1 = loop e1 in
  496. let e2 = loop e2 in
  497. let e3 = loop e3 in
  498. if_or_op e e1 e2 e3
  499. | _ ->
  500. Type.map_expr loop e
  501. in
  502. loop e
  503. end
  504. module Ssa = struct
  505. type var_map = tvar IntMap.t
  506. type condition =
  507. | Equal of tvar * texpr
  508. | NotEqual of tvar * texpr
  509. type node_data = {
  510. nd_pos: pos;
  511. mutable nd_var_map : var_map;
  512. mutable nd_terminates : bool;
  513. }
  514. type join_node = {
  515. mutable branches : node_data list;
  516. }
  517. type ssa_context = {
  518. com : Common.context;
  519. mutable cleanup : (unit -> unit) list;
  520. mutable cur_data : node_data;
  521. mutable var_conds : (condition list) IntMap.t;
  522. mutable loop_stack : (join_node * join_node) list;
  523. mutable exception_stack : join_node list;
  524. mutable block_depth : int;
  525. }
  526. let s_cond = function
  527. | Equal(v,e) -> Printf.sprintf "%s == %s" v.v_name (s_expr_pretty e)
  528. | NotEqual(v,e) -> Printf.sprintf "%s != %s" v.v_name (s_expr_pretty e)
  529. let s_conds conds =
  530. String.concat " && " (List.map s_cond conds)
  531. let mk_loc v p = mk (TLocal v) v.v_type p
  532. let mk_phi =
  533. let v_phi = alloc_var "__ssa_phi__" t_dynamic in
  534. (fun vl p ->
  535. let e = mk (TCall(mk_loc v_phi p,(List.map (fun (v,p) -> mk_loc v p) vl))) t_dynamic p in
  536. e
  537. )
  538. (* TODO: make sure this is conservative *)
  539. let can_throw e =
  540. let rec loop e = match e.eexpr with
  541. | TConst _ | TLocal _ | TTypeExpr _ | TFunction _ | TBlock _ -> ()
  542. | TCall _ | TNew _ | TThrow _ | TCast(_,Some _) -> raise Exit
  543. | _ -> Type.iter loop e
  544. in
  545. try
  546. loop e; false
  547. with Exit ->
  548. true
  549. let mk_join_node() = {
  550. branches = []
  551. }
  552. let mk_node_data p = {
  553. nd_pos = p;
  554. nd_var_map = IntMap.empty;
  555. nd_terminates = false;
  556. }
  557. let add_branch join branch p =
  558. join.branches <- {branch with nd_pos = p} :: join.branches
  559. let branch ctx p =
  560. let old_map = ctx.cur_data.nd_var_map in
  561. let old_term = ctx.cur_data.nd_terminates in
  562. ctx.cur_data.nd_terminates <- false;
  563. (fun join ->
  564. add_branch join ctx.cur_data p;
  565. ctx.cur_data.nd_var_map <- old_map;
  566. ctx.cur_data.nd_terminates <- old_term;
  567. )
  568. let terminate ctx =
  569. ctx.cur_data.nd_terminates <- true
  570. let set_loop_join ctx join_top join_bottom =
  571. ctx.loop_stack <- (join_top,join_bottom) :: ctx.loop_stack;
  572. (fun () ->
  573. ctx.loop_stack <- List.tl ctx.loop_stack
  574. )
  575. let set_exception_join ctx join =
  576. ctx.exception_stack <- join :: ctx.exception_stack;
  577. (fun () ->
  578. ctx.exception_stack <- List.tl ctx.exception_stack;
  579. )
  580. let create_v_extra v =
  581. match v.v_extra with
  582. | Some (_,Some _) ->
  583. ()
  584. | Some (tl,None) ->
  585. let e_extra = mk (TObjectDecl []) t_dynamic null_pos in
  586. v.v_extra <- Some (tl,Some e_extra)
  587. | None ->
  588. let e_extra = mk (TObjectDecl []) t_dynamic null_pos in
  589. v.v_extra <- Some ([],Some e_extra)
  590. let set_v_extra_value v s e = match v.v_extra with
  591. | Some (tl, Some {eexpr = TObjectDecl fl}) ->
  592. let rec loop fl = match fl with
  593. | (s',_) :: fl when s' = s ->
  594. (s,e) :: fl
  595. | f1 :: fl ->
  596. f1 :: loop fl
  597. | [] ->
  598. [s,e]
  599. in
  600. let e_extra = mk (TObjectDecl (loop fl)) t_dynamic null_pos in
  601. v.v_extra <- Some (tl, Some e_extra)
  602. | _ ->
  603. assert false
  604. let get_origin_var v = match v.v_extra with
  605. | Some (_,Some {eexpr = TObjectDecl fl}) ->
  606. begin match List.assoc "origin_var" fl with
  607. | {eexpr = TLocal v'} -> v'
  608. | _ -> raise Not_found
  609. end
  610. | _ ->
  611. raise Not_found
  612. let set_origin_var v v_origin p =
  613. let ev = mk_loc v_origin p in
  614. set_v_extra_value v "origin_var" ev
  615. let get_var_value v = match v.v_extra with
  616. | Some (_,Some {eexpr = TObjectDecl fl}) ->
  617. List.assoc "var_value" fl
  618. | _ ->
  619. raise Not_found
  620. let set_var_value v e =
  621. set_v_extra_value v "var_value" e
  622. let get_var_usage_count v = match v.v_extra with
  623. | Some (_,Some {eexpr = TObjectDecl fl}) ->
  624. begin try
  625. begin match List.assoc "usage_count" fl with
  626. | {eexpr = TConst (TInt i32)} -> Int32.to_int i32
  627. | _ -> 0
  628. end
  629. with Not_found ->
  630. 0
  631. end
  632. | _ ->
  633. raise Not_found
  634. let set_var_usage_count v i =
  635. let e = mk (TConst (TInt (Int32.of_int i))) t_dynamic null_pos in
  636. set_v_extra_value v "usage_count" e
  637. let declare_var ctx v p =
  638. let old = v.v_extra in
  639. ctx.cleanup <- (fun () ->
  640. v.v_extra <- old
  641. ) :: ctx.cleanup;
  642. ctx.cur_data.nd_var_map <- IntMap.add v.v_id v ctx.cur_data.nd_var_map;
  643. v.v_meta <- ((Meta.Custom ":blockDepth",[EConst (Int (string_of_int ctx.block_depth)),p],p)) :: v.v_meta;
  644. v.v_extra <- None;
  645. create_v_extra v;
  646. set_origin_var v v p
  647. let assign_var ctx v e p =
  648. if v.v_capture then
  649. v
  650. else begin
  651. let i = match v.v_extra with
  652. | Some (l,eo) ->
  653. v.v_extra <- Some (("",t_dynamic) :: l,eo);
  654. List.length l + 1
  655. | _ ->
  656. error "Something went wrong" p
  657. in
  658. let v' = alloc_var (Printf.sprintf "%s<%i>" v.v_name i) v.v_type in
  659. create_v_extra v';
  660. v'.v_meta <- [(Meta.Custom ":ssa"),[],p];
  661. set_origin_var v' v p;
  662. ctx.cur_data.nd_var_map <- IntMap.add v.v_id v' ctx.cur_data.nd_var_map;
  663. set_var_value v' e;
  664. v'
  665. end
  666. let get_var ctx v p =
  667. try
  668. IntMap.find v.v_id ctx.cur_data.nd_var_map
  669. with Not_found ->
  670. if not (has_meta Meta.Unbound v.v_meta) then
  671. error (Printf.sprintf "Unbound variable %s" v.v_name) p;
  672. v
  673. let close_join_node ctx node p =
  674. let terminates = ref true in
  675. let branches = List.filter (fun branch ->
  676. if branch.nd_terminates then false
  677. else begin
  678. terminates := false;
  679. true
  680. end
  681. ) node.branches in
  682. match branches with
  683. | [] ->
  684. ()
  685. | branch :: branches ->
  686. let vars = ref (IntMap.map (fun v -> [v,branch.nd_pos]) branch.nd_var_map) in
  687. let rec handle_branch branch =
  688. IntMap.iter (fun i v ->
  689. try
  690. let vl = IntMap.find i !vars in
  691. if not (List.exists (fun (v',_) -> v == v') vl) then
  692. vars := IntMap.add i ((v,p) :: vl) !vars
  693. with Not_found ->
  694. ()
  695. ) branch.nd_var_map;
  696. in
  697. List.iter handle_branch branches;
  698. ctx.cur_data.nd_terminates <- !terminates;
  699. IntMap.iter (fun i vl -> match vl with
  700. | [v,p] ->
  701. ctx.cur_data.nd_var_map <- IntMap.add i v ctx.cur_data.nd_var_map;
  702. | (v',_) :: _ ->
  703. let v = get_origin_var v' in
  704. ignore(assign_var ctx v (mk_phi vl p) p)
  705. | _ ->
  706. assert false
  707. ) !vars
  708. let invert_cond = function
  709. | Equal(v,e) -> NotEqual(v,e)
  710. | NotEqual(v,e) -> Equal(v,e)
  711. let invert_conds =
  712. List.map invert_cond
  713. let rec eval_cond ctx e = match e.eexpr with
  714. | TBinop(OpNotEq,{eexpr = TLocal v},e1) ->
  715. [NotEqual(v,e1)]
  716. | TBinop(OpEq,{eexpr = TLocal v},e1) ->
  717. [Equal(v,e1)]
  718. | TUnop(Not,_,e1) ->
  719. invert_conds (eval_cond ctx e1)
  720. | TLocal v ->
  721. begin try eval_cond ctx (get_var_value v)
  722. with Not_found -> [] end
  723. | _ ->
  724. []
  725. let append_cond ctx v cond p =
  726. begin try
  727. let conds = IntMap.find v.v_id ctx.var_conds in
  728. ctx.var_conds <- IntMap.add v.v_id (cond :: conds) ctx.var_conds
  729. with Not_found ->
  730. ctx.var_conds <- IntMap.add v.v_id [cond] ctx.var_conds
  731. end
  732. (* let apply_cond ctx = function
  733. | Equal({v_extra = Some(_,Some {eexpr = TLocal v})} as v0,e1) ->
  734. let v' = assign_var ctx v (mk_loc v0 e1.epos) e1.epos in
  735. append_cond ctx v' (Equal(v',e1)) e1.epos
  736. | NotEqual({v_extra = Some(_,Some {eexpr = TLocal v})} as v0,e1) ->
  737. let v' = assign_var ctx v (mk_loc v0 e1.epos) e1.epos in
  738. append_cond ctx v' (NotEqual(v',e1)) e1.epos
  739. | _ -> ()
  740. let apply_not_null_cond ctx v p =
  741. apply_cond ctx (NotEqual(v,(mk (TConst TNull) t_dynamic p))) *)
  742. let apply com e =
  743. let rec handle_if ctx f econd eif eelse =
  744. let econd = loop ctx econd in
  745. (* let cond = eval_cond ctx econd in *)
  746. let join = mk_join_node() in
  747. let close = branch ctx eif.epos in
  748. (* List.iter (apply_cond ctx) cond; *)
  749. let eif = loop ctx eif in
  750. close join;
  751. let eelse = match eelse with
  752. | None ->
  753. (* let cond = invert_conds cond in *)
  754. (* List.iter (apply_cond ctx) cond; *)
  755. add_branch join ctx.cur_data e.epos;
  756. None
  757. | Some e ->
  758. let close = branch ctx e.epos in
  759. (* let cond = invert_conds cond in *)
  760. (* List.iter (apply_cond ctx) cond; *)
  761. let eelse = loop ctx e in
  762. close join;
  763. Some eelse
  764. in
  765. close_join_node ctx join e.epos;
  766. f econd eif eelse
  767. and handle_loop_body ctx e =
  768. let join_top = mk_join_node() in
  769. let join_bottom = mk_join_node() in
  770. let unset = set_loop_join ctx join_top join_bottom in
  771. let close = branch ctx e.epos in
  772. ignore(loop ctx e); (* TODO: I don't know if this is sane. *)
  773. close join_top;
  774. add_branch join_top ctx.cur_data e.epos;
  775. close_join_node ctx join_top e.epos;
  776. let ebody = loop ctx e in
  777. ctx.cur_data.nd_terminates <- false;
  778. unset();
  779. close_join_node ctx join_bottom e.epos;
  780. ebody
  781. and loop ctx e = match e.eexpr with
  782. (* var declarations *)
  783. | TVar(v,eo) ->
  784. declare_var ctx v e.epos;
  785. let eo = match eo with
  786. | None -> None
  787. | Some e ->
  788. let e = loop ctx e in
  789. set_var_value v e;
  790. Some e
  791. in
  792. {e with eexpr = TVar(v,eo)}
  793. | TFunction tf ->
  794. let close = branch ctx e.epos in
  795. List.iter (fun (v,co) ->
  796. declare_var ctx v e.epos;
  797. (* match co with
  798. | Some TNull when (match v.v_type with TType({t_path=["haxe"],"PosInfos"},_) -> false | _ -> true) -> ()
  799. | _ -> apply_not_null_cond ctx v e.epos *)
  800. ) tf.tf_args;
  801. let e' = loop ctx tf.tf_expr in
  802. close (mk_join_node());
  803. {e with eexpr = TFunction {tf with tf_expr = e'}}
  804. (* var modifications *)
  805. | TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) when v.v_name <> "this" ->
  806. let e2 = loop ctx e2 in
  807. let _ = assign_var ctx v e2 e1.epos in
  808. {e with eexpr = TBinop(OpAssign,e1,e2)}
  809. | TBinop(OpAssignOp op,({eexpr = TLocal v} as e1),e2) ->
  810. let e1 = loop ctx e1 in
  811. let e2 = loop ctx e2 in
  812. let e_op = mk (TBinop(op,e1,e2)) e.etype e.epos in
  813. let _ = assign_var ctx v e_op e1.epos in
  814. {e with eexpr = TBinop(OpAssignOp op,e1,e2)}
  815. | TUnop((Increment | Decrement as op),flag,({eexpr = TLocal v} as e1)) ->
  816. let op = match op with Increment -> OpAdd | Decrement -> OpSub | _ -> assert false in
  817. let e_one = mk (TConst (TInt (Int32.of_int 1))) com.basic.tint e.epos in
  818. let e1 = loop ctx e1 in
  819. let e_op = mk (TBinop(op,e1,e_one)) e.etype e.epos in
  820. let _ = assign_var ctx v e_op e1.epos in
  821. e
  822. (* var user *)
  823. | TLocal v ->
  824. let v = get_var ctx v e.epos in
  825. {e with eexpr = TLocal v}
  826. (* control flow *)
  827. | TIf(econd,eif,eelse) ->
  828. let f econd eif eelse = {e with eexpr = TIf(econd,eif,eelse)} in
  829. handle_if ctx f econd eif eelse
  830. | TSwitch(e1,cases,edef) ->
  831. let e1 = loop ctx e1 in
  832. let join = mk_join_node() in
  833. let cases = List.map (fun (el,e) ->
  834. let close = branch ctx e.epos in
  835. let el = List.map (loop ctx) el in
  836. let e = loop ctx e in
  837. close join;
  838. el,e
  839. ) cases in
  840. let edef = match edef with
  841. | Some e ->
  842. let close = branch ctx e.epos in
  843. let e = loop ctx e in
  844. close join;
  845. Some e
  846. | None ->
  847. begin match e1.eexpr with
  848. | TMeta((Meta.Exhaustive,_,_),_)
  849. | TParenthesis({eexpr = TMeta((Meta.Exhaustive,_,_),_)}) ->
  850. ()
  851. | _ ->
  852. add_branch join ctx.cur_data e.epos;
  853. end;
  854. None
  855. in
  856. close_join_node ctx join e.epos;
  857. let e = {e with eexpr = TSwitch(e1,cases,edef)} in
  858. e
  859. | TWhile(econd,ebody,mode) ->
  860. let econd = loop ctx econd in
  861. let ebody = handle_loop_body ctx ebody in
  862. let e = {e with eexpr = TWhile(econd,ebody,mode)} in
  863. e
  864. | TFor(v,e1,ebody) ->
  865. declare_var ctx v e.epos;
  866. (* apply_not_null_cond ctx v e1.epos; *)
  867. let v' = IntMap.find v.v_id ctx.cur_data.nd_var_map in
  868. let e1 = loop ctx e1 in
  869. let ebody = handle_loop_body ctx ebody in
  870. let e = {e with eexpr = TFor(v',e1,ebody)} in
  871. e
  872. | TTry(e1,catches) ->
  873. let join_ex = mk_join_node() in
  874. let join_bottom = mk_join_node() in
  875. let unset = set_exception_join ctx join_ex in
  876. let e1 = loop ctx e1 in
  877. unset();
  878. add_branch join_bottom ctx.cur_data e.epos;
  879. close_join_node ctx join_ex e.epos;
  880. let catches = List.map (fun (v,e) ->
  881. declare_var ctx v e.epos;
  882. (* apply_not_null_cond ctx v e.epos; *)
  883. let close = branch ctx e.epos in
  884. let e = loop ctx e in
  885. close join_bottom;
  886. v,e
  887. ) catches in
  888. close_join_node ctx join_bottom e.epos;
  889. let e = {e with eexpr = TTry(e1,catches)} in
  890. e
  891. | TBreak ->
  892. begin match ctx.loop_stack with
  893. | [] -> error "Break outside loop" e.epos
  894. | (_,join) :: _ -> add_branch join ctx.cur_data e.epos
  895. end;
  896. terminate ctx;
  897. e
  898. | TContinue ->
  899. begin match ctx.loop_stack with
  900. | [] -> error "Continue outside loop" e.epos
  901. | (join,_) :: _ -> add_branch join ctx.cur_data e.epos
  902. end;
  903. terminate ctx;
  904. e
  905. | TThrow e1 ->
  906. let e1 = loop ctx e1 in
  907. begin match ctx.exception_stack with
  908. | join :: _ -> add_branch join ctx.cur_data e.epos
  909. | _ -> ()
  910. end;
  911. terminate ctx;
  912. {e with eexpr = TThrow e1}
  913. | TReturn eo ->
  914. let eo = match eo with None -> None | Some e -> Some (loop ctx e) in
  915. terminate ctx;
  916. {e with eexpr = TReturn eo}
  917. | TBlock el ->
  918. let rec loop2 el = match el with
  919. | [] ->
  920. []
  921. | e :: el ->
  922. if ctx.cur_data.nd_terminates then begin
  923. (* ctx.com.warning (Printf.sprintf "Unreachable code: %s" (s_expr_pretty e)) e.epos; *)
  924. []
  925. end else
  926. let e = loop ctx e in
  927. e :: (loop2 el)
  928. in
  929. ctx.block_depth <- ctx.block_depth + 1;
  930. let el = loop2 el in
  931. ctx.block_depth <- ctx.block_depth - 1;
  932. {e with eexpr = TBlock(el)}
  933. | _ ->
  934. begin match ctx.exception_stack with
  935. | join :: _ when can_throw e -> add_branch join ctx.cur_data e.epos
  936. | _ -> ()
  937. end;
  938. Type.map_expr (loop ctx) e
  939. in
  940. let ctx = {
  941. com = com;
  942. cur_data = mk_node_data e.epos;
  943. var_conds = IntMap.empty;
  944. loop_stack = [];
  945. exception_stack = [];
  946. cleanup = [];
  947. block_depth = 0;
  948. } in
  949. let e = loop ctx e in
  950. e,ctx
  951. let unapply com e =
  952. let rec loop e = match e.eexpr with
  953. | TFor(v,e1,e2) when Meta.has (Meta.Custom ":ssa") v.v_meta ->
  954. let v' = get_origin_var v in
  955. let e1 = loop e1 in
  956. let e2 = loop e2 in
  957. {e with eexpr = TFor(v',e1,e2)}
  958. | TLocal v when Meta.has (Meta.Custom ":ssa") v.v_meta ->
  959. let v' = get_origin_var v in
  960. {e with eexpr = TLocal v'}
  961. | TBlock el ->
  962. let rec filter e = match e.eexpr with
  963. | TMeta((Meta.Custom ":ssa",_,_),_) ->
  964. false
  965. | _ ->
  966. true
  967. in
  968. let el = List.filter filter el in
  969. let el = List.map loop el in
  970. {e with eexpr = TBlock el}
  971. | _ ->
  972. Type.map_expr loop e
  973. in
  974. loop e
  975. end
  976. module ConstPropagation = struct
  977. open Ssa
  978. let expr_eq e1 e2 = match e1.eexpr,e2.eexpr with
  979. | TConst ct1, TConst ct2 ->
  980. ct1 = ct2
  981. | _ ->
  982. false
  983. let get_block_depth v = try
  984. let i = match Meta.get (Meta.Custom ":blockDepth") v.v_meta with
  985. | _,[EConst(Int s),_],_ -> int_of_string s
  986. | _ -> raise Not_found
  987. in
  988. i
  989. with Not_found ->
  990. -1
  991. let can_be_inlined com v0 e = type_iseq v0.v_type e.etype && match e.eexpr with
  992. | TConst ct ->
  993. begin match ct with
  994. | TThis | TSuper -> false
  995. (* Some targets don't like seeing null in certain places and won't even compile. We have to detect `if (x != null)
  996. in order for this to work. *)
  997. | TNull when (match com.platform with Php | Cpp -> true | _ -> false) -> false
  998. | _ -> true
  999. end
  1000. | TLocal v ->
  1001. not (Meta.has Meta.CompilerGenerated v.v_meta) &&
  1002. begin try
  1003. let v' = Ssa.get_origin_var v in
  1004. begin match v'.v_extra with
  1005. | Some ([],_) -> get_block_depth v <= get_block_depth v0
  1006. | _ -> false
  1007. end
  1008. with Not_found ->
  1009. false
  1010. end
  1011. | TEnumParameter _ when not (com.platform = Php) ->
  1012. Ssa.get_var_usage_count v0 <= 1
  1013. | _ ->
  1014. false
  1015. let semi_awkward_enum_value ssa e i = match e.eexpr with
  1016. | TCall({eexpr = TField(_,FEnum _)},el) -> (try List.nth el i with Failure _ -> raise Not_found)
  1017. | _ -> raise Not_found
  1018. let rec local ssa force v e =
  1019. begin try
  1020. if v.v_capture then raise Not_found;
  1021. if type_has_analyzer_option v.v_type flag_no_const_propagation then raise Not_found;
  1022. begin match follow v.v_type with
  1023. | TDynamic _ -> raise Not_found
  1024. | _ -> ()
  1025. end;
  1026. let e = Ssa.get_var_value v in
  1027. let old = v.v_extra in
  1028. v.v_extra <- None;
  1029. let e = value ssa force e in
  1030. v.v_extra <- old;
  1031. Ssa.set_var_value v e;
  1032. e
  1033. with Not_found ->
  1034. e
  1035. end
  1036. (* force must only be true if the value is not used in the output *)
  1037. and value ssa force e = match e.eexpr with
  1038. | TUnop((Increment | Decrement),_,_)
  1039. | TBinop(OpAssignOp _,_,_)
  1040. | TBinop(OpAssign,_,_) ->
  1041. e
  1042. | TBinop(op,e1,e2) ->
  1043. let e1 = value ssa force e1 in
  1044. let e2 = value ssa force e2 in
  1045. let e = {e with eexpr = TBinop(op,e1,e2)} in
  1046. let e' = Optimizer.optimize_binop e op e1 e2 in
  1047. if e == e' then
  1048. e
  1049. else
  1050. value ssa force e'
  1051. | TUnop(op,flag,e1) ->
  1052. let e1 = value ssa force e1 in
  1053. let e = {e with eexpr = TUnop(op,flag,e1)} in
  1054. let e' = Optimizer.optimize_unop e op flag e1 in
  1055. if e == e' then
  1056. e
  1057. else
  1058. value ssa force e'
  1059. | TCall (({eexpr = TLocal {v_name = "__ssa_phi__"}} as ephi),el) ->
  1060. let el = List.map (value ssa force) el in
  1061. begin match el with
  1062. | [] -> assert false
  1063. | e1 :: el ->
  1064. if List.for_all (fun e2 -> expr_eq e1 e2) el then
  1065. value ssa force e1
  1066. else
  1067. {e with eexpr = TCall(ephi, e1 :: el)}
  1068. end
  1069. | TParenthesis e1 | TMeta(_,e1) ->
  1070. value ssa force e1
  1071. | TLocal v ->
  1072. let e' = local ssa force v e in
  1073. if force || can_be_inlined ssa.com v e' then
  1074. e'
  1075. else
  1076. e
  1077. | TEnumParameter(e1,ef,i) ->
  1078. let ev = value ssa true e1 in
  1079. begin try
  1080. value ssa force (semi_awkward_enum_value ssa ev i)
  1081. with Not_found ->
  1082. e
  1083. end
  1084. | _ ->
  1085. e
  1086. (* TODO: the name is quite accurate *)
  1087. let awkward_get_enum_index ssa e =
  1088. let e = awkward_get_enum_index ssa.com e in
  1089. let ev = (value ssa true e) in
  1090. match ev.eexpr with
  1091. | TField(_,FEnum(_,ef)) -> TInt (Int32.of_int ef.ef_index)
  1092. | TCall({eexpr = TField(_,FEnum(_,ef))},_) -> TInt (Int32.of_int ef.ef_index)
  1093. | _ -> raise Not_found
  1094. let apply ssa e =
  1095. let rec loop e = match e.eexpr with
  1096. | TLocal v when not (Meta.has Meta.Unbound v.v_meta) ->
  1097. set_var_usage_count v (get_var_usage_count v + 1);
  1098. | _ ->
  1099. Type.iter loop e
  1100. in
  1101. loop e;
  1102. let had_function = ref false in
  1103. let rec loop e = match e.eexpr with
  1104. | TFunction _ when !had_function ->
  1105. e
  1106. | TFunction tf ->
  1107. had_function := true;
  1108. {e with eexpr = TFunction {tf with tf_expr = loop tf.tf_expr}}
  1109. | TLocal v ->
  1110. let e' = local ssa false v e in
  1111. if can_be_inlined ssa.com v e' then
  1112. e'
  1113. else
  1114. e
  1115. | TCall({eexpr = TField(_,(FStatic(_,cf) | FInstance(_,_,cf) | FAnon cf))},el) when has_analyzer_option cf.cf_meta flag_no_const_propagation ->
  1116. e
  1117. | TCall(e1,el) ->
  1118. let e1 = loop e1 in
  1119. let check e t =
  1120. if type_has_analyzer_option t flag_no_const_propagation then e
  1121. else loop e
  1122. in
  1123. let el = Codegen.UnificationCallback.check_call check el e1.etype in
  1124. {e with eexpr = TCall(e1,el)}
  1125. (* | TField(e1,fa) ->
  1126. let e1' = loop e1 in
  1127. let fa = if e1' != e1 then
  1128. begin try quick_field e1'.etype (field_name fa)
  1129. with Not_found -> fa end
  1130. else
  1131. fa
  1132. in
  1133. {e with eexpr = TField(e1',fa)} *)
  1134. | TUnop((Increment | Decrement),_,_) ->
  1135. e
  1136. | TBinop(OpAssignOp op,e1,e2) ->
  1137. let e2 = loop e2 in
  1138. {e with eexpr = TBinop(OpAssignOp op,e1,e2)}
  1139. | TBinop(OpAssign,({eexpr = TLocal _} as e1),e2) ->
  1140. let e2 = loop e2 in
  1141. {e with eexpr = TBinop(OpAssign,e1,e2)}
  1142. | TBinop(op,e1,e2) ->
  1143. let e1 = loop e1 in
  1144. let e2 = loop e2 in
  1145. let e = {e with eexpr = TBinop(op,e1,e2)} in
  1146. let e' = Optimizer.optimize_binop e op e1 e2 in
  1147. e'
  1148. | TUnop(op,flag,e1) ->
  1149. let e1 = loop e1 in
  1150. let e = {e with eexpr = TUnop(op,flag,e1)} in
  1151. let e' = Optimizer.optimize_unop e op flag e1 in
  1152. e'
  1153. | TIf(e1,e2,eo) ->
  1154. let e1 = loop e1 in
  1155. let e2 = loop e2 in
  1156. let rec check_const e1 = match e1.eexpr with
  1157. | TConst (TBool true) ->
  1158. e2
  1159. | TConst (TBool false) ->
  1160. begin match eo with
  1161. | None ->
  1162. mk (TConst TNull) t_dynamic e.epos
  1163. | Some e ->
  1164. loop e
  1165. end
  1166. | TParenthesis e1 ->
  1167. check_const e1
  1168. | _ ->
  1169. let eo = match eo with None -> None | Some e -> Some (loop e) in
  1170. {e with eexpr = TIf(e1,e2,eo)}
  1171. in
  1172. check_const e1
  1173. | TSwitch(e1,cases,edef) ->
  1174. let e1 = loop e1 in
  1175. let rec check_constant e = match e.eexpr with
  1176. | TConst ct -> ct
  1177. | TParenthesis e1 | TCast(e1,None) | TMeta(_,e1) -> check_constant e1
  1178. | _ -> awkward_get_enum_index ssa e
  1179. in
  1180. begin try
  1181. let ct = check_constant e1 in
  1182. begin try
  1183. let _,e = List.find (fun (el,_) ->
  1184. List.exists (fun e -> match e.eexpr with
  1185. | TConst ct2 -> ct = ct2
  1186. | _ -> false
  1187. ) el
  1188. ) cases in
  1189. loop e
  1190. with Not_found ->
  1191. begin match edef with None -> raise Not_found | Some e -> loop e end
  1192. end
  1193. with Not_found ->
  1194. let cases = List.map (fun (el,e) -> el,loop e) cases in
  1195. let edef = match edef with None -> None | Some e -> Some (loop e) in
  1196. {e with eexpr = TSwitch(e1,cases,edef)}
  1197. end
  1198. | _ ->
  1199. Type.map_expr loop e
  1200. in
  1201. loop e
  1202. end
  1203. module EffectChecker = struct
  1204. let run com is_var_expression e =
  1205. let has_effect e = match e.eexpr with
  1206. | TVar _ -> true
  1207. | _ -> Optimizer.has_side_effect e
  1208. in
  1209. let e = if is_var_expression then
  1210. (* var initialization expressions are like assignments, so let's cheat a bit here *)
  1211. snd (Simplifier.apply com (Codegen.binop OpAssign (mk (TConst TNull) t_dynamic e.epos) e e.etype e.epos))
  1212. else e
  1213. in
  1214. let rec loop e = match e.eexpr with
  1215. | TBlock el ->
  1216. List.iter (fun e ->
  1217. if not (has_effect e) then com.warning "This expression has no effect" e.epos
  1218. ) el
  1219. | _ ->
  1220. Type.iter loop e
  1221. in
  1222. loop e
  1223. end
  1224. module Checker = struct
  1225. open Ssa
  1226. let apply ssa e =
  1227. let given_warnings = ref PMap.empty in
  1228. let add_pos p =
  1229. given_warnings := PMap.add p true !given_warnings
  1230. in
  1231. let resolve_value v =
  1232. let e' = Ssa.get_var_value v in
  1233. begin match e'.eexpr with
  1234. | TLocal v' when v == v' -> e'
  1235. | _ -> e'
  1236. end
  1237. in
  1238. let rec is_null_expr e = match e.eexpr with
  1239. | TConst TNull ->
  1240. true
  1241. | TLocal v ->
  1242. (try is_null_expr (resolve_value v) with Not_found -> false)
  1243. | _ ->
  1244. false
  1245. in
  1246. let can_be_null v =
  1247. not (has_meta Meta.NotNull v.v_meta)
  1248. && try not (List.exists (fun cond -> match cond with
  1249. | NotEqual(v',e) when v == v' && is_null_expr e -> true
  1250. | _ -> false
  1251. ) (IntMap.find v.v_id ssa.var_conds)) with Not_found -> true
  1252. in
  1253. let return b p =
  1254. if b then add_pos p;
  1255. b
  1256. in
  1257. let rec can_be_null_expr vstack e =
  1258. if PMap.mem e.epos !given_warnings then
  1259. false
  1260. else match e.eexpr with
  1261. | TConst TNull ->
  1262. add_pos e.epos;
  1263. true
  1264. | TBinop((OpAssign | OpAssignOp _),_,e1) ->
  1265. can_be_null_expr vstack e1
  1266. | TBinop _ | TUnop _ ->
  1267. false
  1268. | TConst _ | TTypeExpr _ | TNew _ | TObjectDecl _ | TArrayDecl _ | TEnumParameter _ | TFunction _ | TVar _ ->
  1269. false
  1270. | TFor _ | TWhile _ | TIf _ | TSwitch _ | TTry _ | TReturn _ | TBreak | TContinue | TThrow _ ->
  1271. assert false
  1272. | TField _ | TBlock _ | TArray _ ->
  1273. false (* TODO *)
  1274. | TCall ({eexpr = TLocal {v_name = "__ssa_phi__"}},el) ->
  1275. List.exists (can_be_null_expr vstack) el
  1276. | TLocal v ->
  1277. if List.mem v.v_id vstack then
  1278. false (* not really, but let's not be a nuisance *)
  1279. else
  1280. return (can_be_null v && (try can_be_null_expr (v.v_id :: vstack) (resolve_value v) with Not_found -> true)) e.epos;
  1281. | TMeta(_,e1) | TParenthesis e1 | TCast(e1,_) ->
  1282. can_be_null_expr vstack e1
  1283. | TCall(e1,_) ->
  1284. begin match follow e1.etype with
  1285. | TFun(_,r) -> return (is_explicit_null r) e1.epos
  1286. | _ -> false
  1287. end
  1288. in
  1289. let check_null e p =
  1290. if can_be_null_expr [] e then begin
  1291. ssa.com.warning "Possible null exception" p;
  1292. end
  1293. in
  1294. let rec loop e = match e.eexpr with
  1295. | TField(e1,fa) ->
  1296. let e1 = loop e1 in
  1297. check_null e1 e.epos;
  1298. {e with eexpr = TField(e1,fa)}
  1299. | TMeta((Meta.Analyzer,[EConst(Ident "testIsNull"),_],_),e1) ->
  1300. if not (can_be_null_expr [] e) then error "Analyzer did not find a possible null exception" e.epos;
  1301. e
  1302. | TMeta((Meta.Analyzer,[EConst(Ident "testIsNotNull"),_],_),e1) ->
  1303. if (can_be_null_expr [] e) then error "Analyzer found a possible null exception" e.epos;
  1304. e
  1305. | _ ->
  1306. Type.map_expr loop e
  1307. in
  1308. loop e;
  1309. end
  1310. let rec lrev_iter f el = match el with
  1311. | e :: el ->
  1312. lrev_iter f el;
  1313. f e
  1314. | [] ->
  1315. ()
  1316. let rev_iter f e = match e.eexpr with
  1317. | TConst _
  1318. | TLocal _
  1319. | TBreak
  1320. | TContinue
  1321. | TTypeExpr _ ->
  1322. ()
  1323. | TArray (e1,e2)
  1324. | TBinop (_,e1,e2)
  1325. | TFor (_,e1,e2)
  1326. | TWhile (e1,e2,_) ->
  1327. f e2;
  1328. f e1;
  1329. | TThrow e
  1330. | TField (e,_)
  1331. | TEnumParameter (e,_,_)
  1332. | TParenthesis e
  1333. | TCast (e,_)
  1334. | TUnop (_,_,e)
  1335. | TMeta(_,e) ->
  1336. f e
  1337. | TArrayDecl el
  1338. | TNew (_,_,el)
  1339. | TBlock el ->
  1340. lrev_iter f el
  1341. | TObjectDecl fl ->
  1342. lrev_iter (fun (_,e) -> f e) fl
  1343. | TCall (e,el) ->
  1344. f e;
  1345. lrev_iter f el
  1346. | TVar (v,eo) ->
  1347. (match eo with None -> () | Some e -> f e)
  1348. | TFunction fu ->
  1349. f fu.tf_expr
  1350. | TIf (e,e1,e2) ->
  1351. (match e2 with None -> () | Some e -> f e);
  1352. f e1;
  1353. f e;
  1354. | TSwitch (e,cases,def) ->
  1355. (match def with None -> () | Some e -> f e);
  1356. lrev_iter (fun (el,e2) -> lrev_iter f el; f e2) cases;
  1357. f e;
  1358. | TTry (e,catches) ->
  1359. lrev_iter (fun (_,e) -> f e) catches;
  1360. f e;
  1361. | TReturn eo ->
  1362. (match eo with None -> () | Some e -> f e)
  1363. module LocalDce = struct
  1364. let apply e =
  1365. let is_used v = Meta.has Meta.Used v.v_meta || type_has_analyzer_option v.v_type flag_no_local_dce || v.v_capture in
  1366. let is_ref_type t = match t with
  1367. | TType({t_path = ["cs"],("Ref" | "Out")},_) -> true
  1368. | _ -> false
  1369. in
  1370. let rec use v =
  1371. if not (Meta.has Meta.Used v.v_meta) then begin
  1372. v.v_meta <- (Meta.Used,[],Ast.null_pos) :: v.v_meta;
  1373. try use (Ssa.get_origin_var v) with Not_found -> ()
  1374. end
  1375. in
  1376. let rec has_side_effect e =
  1377. let rec loop e =
  1378. match e.eexpr with
  1379. | TLocal v when Meta.has Meta.CompilerGenerated v.v_meta -> (try loop (Ssa.get_var_value v) with Not_found -> ())
  1380. | TBinop((OpAssign | OpAssignOp _),{eexpr = TLocal v},e2) when is_used v || has_side_effect e2 || is_ref_type v.v_type -> raise Exit
  1381. | TVar(v,None) when is_used v -> raise Exit
  1382. | TVar(v,Some e1) when is_used v || has_side_effect e1 -> raise Exit
  1383. | TConst _ | TLocal _ | TTypeExpr _ | TFunction _ -> ()
  1384. | TCall ({ eexpr = TField(_,FStatic({ cl_path = ([],"Std") },{ cf_name = "string" })) },args) -> Type.iter loop e
  1385. | TCall (({eexpr = TLocal {v_name = "__ssa_phi__"}}),el) -> ()
  1386. | TCall ({eexpr = TField(_,FEnum _)},_) -> Type.iter loop e
  1387. | TNew _ | TCall _ | TBinop ((OpAssignOp _ | OpAssign),_,_) | TUnop ((Increment|Decrement),_,_) -> raise Exit
  1388. | TReturn _ | TBreak | TContinue | TThrow _ | TCast (_,Some _) -> raise Exit
  1389. | TFor _ -> raise Exit
  1390. | TArray _ | TEnumParameter _ | TCast (_,None) | TBinop _ | TUnop _ | TParenthesis _ | TMeta _ | TWhile _
  1391. | TField _ | TIf _ | TTry _ | TSwitch _ | TArrayDecl _ | TBlock _ | TObjectDecl _ | TVar _ -> Type.iter loop e
  1392. in
  1393. try
  1394. loop e;
  1395. false
  1396. with Exit ->
  1397. true
  1398. in
  1399. let rec collect e = match e.eexpr with
  1400. | TLocal v ->
  1401. use v
  1402. | TVar(v,_) when not (is_used v) ->
  1403. (* TODO: this is probably dangerous *)
  1404. ()
  1405. | _ ->
  1406. rev_iter collect e
  1407. in
  1408. let rec loop need_val e =
  1409. match e.eexpr with
  1410. | TLocal v ->
  1411. use v;
  1412. e
  1413. | TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) ->
  1414. let e2 = loop false e2 in
  1415. if not (is_used v) && not (is_ref_type v.v_type) then
  1416. e2
  1417. else
  1418. {e with eexpr = TBinop(OpAssign,{e1 with eexpr = TLocal v},e2)}
  1419. | TVar(v,Some e1) when not (is_used v) ->
  1420. let e1 = if has_side_effect e1 then loop true e1 else e1 in
  1421. e1
  1422. | TWhile(e1,e2,flag) ->
  1423. collect e2;
  1424. let e2 = loop false e2 in
  1425. let e1 = loop false e1 in
  1426. {e with eexpr = TWhile(e1,e2,flag)}
  1427. | TFor(v,e1,e2) ->
  1428. collect e2;
  1429. let e2 = loop false e2 in
  1430. let e1 = loop false e1 in
  1431. {e with eexpr = TFor(v,e1,e2)}
  1432. | TBlock el ->
  1433. let rec block el = match el with
  1434. | e :: el ->
  1435. let el = block el in
  1436. if not need_val && not (has_side_effect e) then
  1437. el
  1438. else begin
  1439. let e = loop false e in
  1440. e :: el
  1441. end
  1442. | [] ->
  1443. []
  1444. in
  1445. {e with eexpr = TBlock (block el)}
  1446. | TCall(e1, el) ->
  1447. let el = List.rev_map (loop true) (List.rev el) in
  1448. let e1 = loop false e1 in
  1449. {e with eexpr = TCall(e1,el)}
  1450. | TIf(e1,e2,e3) ->
  1451. let e3 = match e3 with None -> None | Some e -> Some (loop need_val e) in
  1452. let e2 = loop need_val e2 in
  1453. let e1 = loop false e1 in
  1454. {e with eexpr = TIf(e1,e2,e3)}
  1455. | TArrayDecl el ->
  1456. let el = List.rev_map (loop true) (List.rev el) in
  1457. {e with eexpr = TArrayDecl el}
  1458. | TObjectDecl fl ->
  1459. let fl = List.rev_map (fun (s,e) -> s,loop true e) (List.rev fl) in
  1460. {e with eexpr = TObjectDecl fl}
  1461. | _ ->
  1462. Type.map_expr (loop false) e
  1463. in
  1464. loop false e
  1465. end
  1466. module Config = struct
  1467. type analyzer_config = {
  1468. analyzer_use : bool;
  1469. simplifier_apply : bool;
  1470. ssa_apply : bool;
  1471. const_propagation : bool;
  1472. check : bool;
  1473. check_has_effect : bool;
  1474. local_dce : bool;
  1475. ssa_unapply : bool;
  1476. simplifier_unapply : bool;
  1477. }
  1478. let get_base_config com =
  1479. {
  1480. analyzer_use = true;
  1481. simplifier_apply = true;
  1482. ssa_apply = true;
  1483. const_propagation = not (Common.raw_defined com "analyzer-no-const-propagation");
  1484. check_has_effect = (Common.raw_defined com "analyzer-check-has-effect");
  1485. check = not (Common.raw_defined com "analyzer-no-check");
  1486. local_dce = not (Common.raw_defined com "analyzer-no-local-dce") && not (Common.defined com Define.As3);
  1487. ssa_unapply = not (Common.raw_defined com "analyzer-no-ssa-unapply");
  1488. simplifier_unapply = not (Common.raw_defined com "analyzer-no-simplify-unapply");
  1489. }
  1490. let update_config_from_meta config meta =
  1491. List.fold_left (fun config meta -> match meta with
  1492. | (Meta.Analyzer,el,_) ->
  1493. List.fold_left (fun config e -> match fst e with
  1494. | EConst (Ident s) when s = flag_no_check -> { config with check = false}
  1495. | EConst (Ident s) when s = flag_check -> { config with check = true}
  1496. | EConst (Ident s) when s = flag_no_const_propagation -> { config with const_propagation = false}
  1497. | EConst (Ident s) when s = flag_const_propagation -> { config with const_propagation = true}
  1498. | EConst (Ident s) when s = flag_no_local_dce -> { config with local_dce = false}
  1499. | EConst (Ident s) when s = flag_local_dce -> { config with local_dce = true}
  1500. | EConst (Ident s) when s = flag_no_check_has_effect -> { config with check_has_effect = false}
  1501. | EConst (Ident s) when s = flag_check_has_effect -> { config with check_has_effect = true}
  1502. | _ -> config
  1503. ) config el
  1504. | _ ->
  1505. config
  1506. ) config meta
  1507. let get_class_config com c =
  1508. let config = get_base_config com in
  1509. update_config_from_meta config c.cl_meta
  1510. let get_field_config com c cf =
  1511. let config = get_class_config com c in
  1512. update_config_from_meta config cf.cf_meta
  1513. end
  1514. module Run = struct
  1515. open Config
  1516. let run_on_expr com config is_var_expression e =
  1517. let do_simplify = (not (Common.defined com Define.NoSimplify) ) && match com.platform with
  1518. | Cpp when Common.defined com Define.Cppia -> false
  1519. | Cpp | Flash8 | Python -> true
  1520. | _ -> false
  1521. in
  1522. let with_timer s f =
  1523. let timer = timer s in
  1524. let r = f() in
  1525. timer();
  1526. r
  1527. in
  1528. try
  1529. let has_unbound,e = if do_simplify || config.analyzer_use then
  1530. with_timer "analyzer-simplify-apply" (fun () -> Simplifier.apply com e)
  1531. else
  1532. false,e
  1533. in
  1534. let e = if config.analyzer_use && not has_unbound then begin
  1535. if config.check_has_effect then EffectChecker.run com is_var_expression e;
  1536. let e,ssa = with_timer "analyzer-ssa-apply" (fun () -> Ssa.apply com e) in
  1537. let e = if config.const_propagation then with_timer "analyzer-const-propagation" (fun () -> ConstPropagation.apply ssa e) else e in
  1538. (* let e = if config.check then with_timer "analyzer-checker" (fun () -> Checker.apply ssa e) else e in *)
  1539. let e = if config.local_dce && config.analyzer_use && not has_unbound && not is_var_expression then with_timer "analyzer-local-dce" (fun () -> LocalDce.apply e) else e in
  1540. let e = if config.ssa_unapply then with_timer "analyzer-ssa-unapply" (fun () -> Ssa.unapply com e) else e in
  1541. List.iter (fun f -> f()) ssa.Ssa.cleanup;
  1542. e
  1543. end else
  1544. e
  1545. in
  1546. let e = if not do_simplify && not (Common.raw_defined com "analyzer-no-simplify-unapply") then
  1547. with_timer "analyzer-simplify-unapply" (fun () -> Simplifier.unapply com e)
  1548. else
  1549. e
  1550. in
  1551. e
  1552. with Exit ->
  1553. e
  1554. let run_on_field ctx config cf =
  1555. match cf.cf_expr with
  1556. | Some e when not (is_ignored cf.cf_meta) && not (Codegen.is_removable_field ctx cf) ->
  1557. let config = update_config_from_meta config cf.cf_meta in
  1558. let is_var_expression = match cf.cf_kind with
  1559. | Var _ -> true
  1560. | _ -> false
  1561. in
  1562. cf.cf_expr <- Some (run_on_expr ctx.com config is_var_expression e);
  1563. | _ -> ()
  1564. let run_on_class ctx config c =
  1565. let config = update_config_from_meta config c.cl_meta in
  1566. let process_field cf = run_on_field ctx config cf in
  1567. List.iter process_field c.cl_ordered_fields;
  1568. List.iter process_field c.cl_ordered_statics;
  1569. (match c.cl_constructor with
  1570. | None -> ()
  1571. | Some f -> process_field f);
  1572. (match c.cl_init with
  1573. | None -> ()
  1574. | Some e ->
  1575. (* never optimize init expressions (too messy) *)
  1576. c.cl_init <- Some (run_on_expr ctx.com {config with analyzer_use = false} false e))
  1577. let run_on_type ctx config t =
  1578. match t with
  1579. | TClassDecl c when (is_ignored c.cl_meta) -> ()
  1580. | TClassDecl c -> run_on_class ctx config c
  1581. | TEnumDecl _ -> ()
  1582. | TTypeDecl _ -> ()
  1583. | TAbstractDecl _ -> ()
  1584. let run_on_types ctx types =
  1585. let com = ctx.com in
  1586. let config = get_base_config com in
  1587. List.iter (run_on_type ctx config) types
  1588. end