Jelajahi Sumber

invert `if(e1) {} else e2` to `if(!e1) e2`

This is safe to do, right?
Simon Krajewski 9 tahun lalu
induk
melakukan
1efcb2609f
3 mengubah file dengan 36 tambahan dan 1 penghapusan
  1. 7 1
      analyzer.ml
  2. 15 0
      optimizer.ml
  3. 14 0
      tests/optimization/src/TestJs.hx

+ 7 - 1
analyzer.ml

@@ -432,7 +432,13 @@ module TexprFilter = struct
 			| TUnop(Not,Prefix,e1),TConst (TBool true) -> {e with eexpr = TBinop(OpBoolOr,e1,e2)}
 			| _,TConst (TBool false) -> {e with eexpr = TBinop(OpBoolAnd,e1,e2)}
 			| _,TBlock [] -> {e with eexpr = TIf(e1,e2,None)}
-			| _ -> {e with eexpr = TIf(e1,e2,Some e3)}
+			| _ -> match (skip e2).eexpr with
+				| TBlock [] ->
+					let e1' = mk (TUnop(Not,Prefix,e1)) e1.etype e1.epos in
+					let e1' = Optimizer.optimize_unop e1' Not Prefix e1 in
+					{e with eexpr = TIf(e1',e3,None)}
+				| _ ->
+					{e with eexpr = TIf(e1,e2,Some e3)}
 		in
 		let rec loop e = match e.eexpr with
 			| TIf(e1,e2,Some e3) ->

+ 15 - 0
optimizer.ml

@@ -1206,6 +1206,21 @@ let optimize_binop e op e1 e2 =
 let optimize_unop e op flag esub =
 	match op, esub.eexpr with
 		| Not, (TConst (TBool f) | TParenthesis({eexpr = TConst (TBool f)})) -> { e with eexpr = TConst (TBool (not f)) }
+		| Not, (TBinop(op,e1,e2) | TParenthesis({eexpr = TBinop(op,e1,e2)})) ->
+			begin try
+				let op = match op with
+					| OpGt -> OpLte
+					| OpGte -> OpLt
+					| OpLt -> OpGte
+					| OpLte -> OpGt
+					| OpEq -> OpNotEq
+					| OpNotEq -> OpEq
+					| _ -> raise Exit
+				in
+				{e with eexpr = TBinop(op,e1,e2)}
+			with Exit ->
+				e
+			end
 		| Neg, TConst (TInt i) -> { e with eexpr = TConst (TInt (Int32.neg i)) }
 		| NegBits, TConst (TInt i) -> { e with eexpr = TConst (TInt (Int32.lognot i)) }
 		| Neg, TConst (TFloat f) ->

+ 14 - 0
tests/optimization/src/TestJs.hx

@@ -428,6 +428,20 @@ class TestJs {
 		use(a);
 	}
 
+	@:js('
+		TestJs.getInt();
+		if(TestJs.getInt() != 0) throw new js__$Boot_HaxeError("meh");
+	')
+	static function testIfInvert() {
+		var tmp;
+		var tmp2 = getInt();
+		if (getInt() == 0) {
+			tmp = tmp2;
+		} else {
+			throw "meh";
+		}
+	}
+
 	static function getInt(?d:Dynamic) { return 1; }
 	static function call(d1:Dynamic, d2:Dynamic) { return d1; }
 	static function use<T>(t:T) { }