analyzer.ml 48 KB

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