Browse Source

[eval] bring back call optimizations in interp-mode

Simon Krajewski 7 years ago
parent
commit
75f53d6665
2 changed files with 86 additions and 11 deletions
  1. 12 6
      src/macro/eval/evalJit.ml
  2. 74 5
      tests/benchs/src/cases/Calls.hx

+ 12 - 6
src/macro/eval/evalJit.ml

@@ -363,7 +363,13 @@ and jit_expr jit return e =
 		| TField(ef,fa) ->
 			let name = hash (field_name fa) in
 			let execs = List.map (jit_expr jit false) el in
-			let is_final c cf = c.cl_final || cf.cf_final in
+			let is_final c cf =
+				c.cl_final || cf.cf_final ||
+				(* In interp mode we can assume that a field is final if it is not overridden.
+				   We cannot do that in macro mode because overriding fields might be added
+				   after jitting this call. *)
+				(not ctx.is_macro && not (Hashtbl.mem ctx.overrides (c.cl_path,cf.cf_name)))
+			in
 			let is_proper_method cf = match cf.cf_kind with
 				| Method MethDynamic -> false
 				| Method _ -> true
@@ -398,13 +404,13 @@ and jit_expr jit return e =
 						default()
 					else if not c.cl_interface then
 						instance_call c
-					(* still can't do this because of incomplete information *)
-					(* else if c.cl_implements = [] && c.cl_super = None then begin match c.cl_descendants with
-						| [c'] when not c'.cl_interface && not (is_overridden c' cf.cf_name) ->
+					(* If we have exactly one implementer, use it instead of the super class/interface. *)
+					else if not ctx.is_macro && c.cl_implements = [] && c.cl_super = None then begin match c.cl_descendants with
+						| [c'] when not c'.cl_interface && is_final c' cf ->
 							instance_call c'
 						| _ ->
-							default() *)
-					else
+							default()
+					end else
 						default()
 				| _ ->
 					let exec = jit_expr jit false ef in

+ 74 - 5
tests/benchs/src/cases/Calls.hx

@@ -5,24 +5,39 @@ import hxbenchmark.Suite;
 
 interface CallInterface {
 	function instanceCall0():String;
+	function instanceCall1(s1:String):String;
+	function instanceCall2(s1:String, s2:String):String;
 }
 
 class CallClass implements CallInterface {
-	@:pure(false) static public function staticCall0() { return null; }
-	@:pure(false) public function instanceCall0() { return null; }
-	@:pure(false) public final function finalCall0() { return null; }
-	@:pure(false) public function overrideCall0() { return null; }
+	static public function staticCall0() { return null; }
+	static public function staticCall1(s1:String) { return null; }
+	static public function staticCall2(s1:String, s2:String) { return null; }
+
+	public function instanceCall0() { return null; }
+	public function instanceCall1(s1:String) { return null; }
+	public function instanceCall2(s1:String, s1:String) { return null; }
+
+	public final function finalCall0() { return null; }
+	public final function finalCall1(s1:String) { return null; }
+	public final function finalCall2(s1:String, s2:String) { return null; }
+
+	public function overrideCall0() { return null; }
+	public function overrideCall1(s1:String) { return null; }
+	public function overrideCall2(s2:String, s2:String) { return null; }
 
 	public function new() { }
 }
 
 class CallClassChild extends CallClass {
 	override function overrideCall0() { return null; }
+	override function overrideCall1(s1:String) { return null; }
+	override function overrideCall2(s2:String, s2:String) { return null; }
 }
 
 class Calls extends TestCase {
 	@:analyzer(ignore)
-	function measureCreate() {
+	function measureCall0() {
 		var c = new CallClass();
 		var cSub:CallClass = new CallClassChild();
 		var cInterface:CallInterface = c;
@@ -45,4 +60,58 @@ class Calls extends TestCase {
 		suite.add("field closure", memberClosureCall0());
 		return suite.run();
 	}
+
+	@:analyzer(ignore)
+	function measureCall1() {
+		var c = new CallClass();
+		var cSub:CallClass = new CallClassChild();
+		var cInterface:CallInterface = c;
+		var cAnon:{
+			function instanceCall1(s1:String):String;
+		} = c;
+		var cDynamic:Dynamic = c;
+		var staticClosureCall1 = CallClass.staticCall1;
+		var memberClosureCall1 = c.instanceCall1;
+		function closureCall1(s1:String) { return null; }
+
+		var suite = new Suite("call with 1 arg");
+		suite.add("static", CallClass.staticCall1("foo"));
+		suite.add("instance", c.instanceCall1("foo"));
+		suite.add("override", cSub.overrideCall1("foo"));
+		suite.add("final", cSub.finalCall1("foo"));
+		suite.add("interface", cInterface.instanceCall1("foo"));
+		suite.add("anon", cAnon.instanceCall1("foo"));
+		suite.add("dynamic", cDynamic.instanceCall1("foo"));
+		suite.add("local closure", closureCall1("foo"));
+		suite.add("static closure", staticClosureCall1("foo"));
+		suite.add("field closure", memberClosureCall1("foo"));
+		return suite.run();
+	}
+
+	@:analyzer(ignore)
+	function measureCall2() {
+		var c = new CallClass();
+		var cSub:CallClass = new CallClassChild();
+		var cInterface:CallInterface = c;
+		var cAnon:{
+			function instanceCall2(s1:String, s2:String):String;
+		} = c;
+		var cDynamic:Dynamic = c;
+		var staticClosureCall2 = CallClass.staticCall2;
+		var memberClosureCall2 = c.instanceCall2;
+		function closureCall2(s1:String, s2:String) { return null; }
+
+		var suite = new Suite("call with 2 args");
+		suite.add("static", CallClass.staticCall2("foo", "bar"));
+		suite.add("instance", c.instanceCall2("foo", "bar"));
+		suite.add("override", cSub.overrideCall2("foo", "bar"));
+		suite.add("final", cSub.finalCall2("foo", "bar"));
+		suite.add("interface", cInterface.instanceCall2("foo", "bar"));
+		suite.add("anon", cAnon.instanceCall2("foo", "bar"));
+		suite.add("dynamic", cDynamic.instanceCall2("foo", "bar"));
+		suite.add("local closure", closureCall2("foo", "bar"));
+		suite.add("static closure", staticClosureCall2("foo", "bar"));
+		suite.add("field closure", memberClosureCall2("foo", "bar"));
+		return suite.run();
+	}
 }