Browse Source

[java/cs] Add experimental -D fast_cast to minimize the amount of casts and their execution cost

 * Closes #3527
 * Closes #3932
 * Related #4048 (doesn't fix it)
 * Related #3379
Cauê Waneck 9 years ago
parent
commit
ec3afeec42

+ 1 - 0
extra/CHANGES.txt

@@ -18,6 +18,7 @@
 	js : introduced new jQuery extern (js.jquery.*) for jQuery 1.12.2 / 2.2.2 support. (#4377)
 	js : introduced new jQuery extern (js.jquery.*) for jQuery 1.12.2 / 2.2.2 support. (#4377)
 	js : introduced new SWFObject extern (js.swfobject.SWFObject) for SWFObject 2.3.20130521 (#4451)
 	js : introduced new SWFObject extern (js.swfobject.SWFObject) for SWFObject 2.3.20130521 (#4451)
 	js : added js.Lib.rethrow (#4551)
 	js : added js.Lib.rethrow (#4551)
+	cs/java : added -D fast_cast as an experimental feature to cleanup casts
 
 
 	Bugfixes:
 	Bugfixes:
 
 

+ 163 - 37
src/generators/gencommon.ml

@@ -241,6 +241,11 @@ let get_fun t =
 
 
 let mk_cast t e = Type.mk_cast e t e.epos
 let mk_cast t e = Type.mk_cast e t e.epos
 
 
+(** TODO: when adding new AST, make a new cast type for those fast casts. For now, we're using this hack
+ *        of using null_class to tell a fast cast from a normal one. Also note that this only works since both
+ *        C# and Java do not use the second part of TCast for anything *)
+let mk_castfast t e = { e with eexpr = TCast(e, Some (TClassDecl null_class)); etype = t }
+
 let mk_classtype_access cl pos = Codegen.ExprBuilder.make_static_this cl pos
 let mk_classtype_access cl pos = Codegen.ExprBuilder.make_static_this cl pos
 
 
 let mk_static_field_access_infer cl field pos params =
 let mk_static_field_access_infer cl field pos params =
@@ -3046,7 +3051,7 @@ struct
 										| _ ->
 										| _ ->
 											let i = ref 0 in
 											let i = ref 0 in
 											let t = TFun(List.map (fun e -> incr i; "arg" ^ (string_of_int !i), false, e.etype) params, e.etype) in
 											let t = TFun(List.map (fun e -> incr i; "arg" ^ (string_of_int !i), false, e.etype) params, e.etype) in
-											dynamic_func_call { e with eexpr = TCall( mk_cast t (run e1), List.map run params ) }
+											dynamic_func_call { e with eexpr = TCall( mk_castfast t (run e1), List.map run params ) }
 							)
 							)
 						(* | FNotFound ->
 						(* | FNotFound ->
 							{ e with eexpr = TCall({ e1 with eexpr = TField(run ecl, f) }, List.map run params) }
 							{ e with eexpr = TCall({ e1 with eexpr = TField(run ecl, f) }, List.map run params) }
@@ -3058,7 +3063,7 @@ struct
 								| _ ->
 								| _ ->
 									let i = ref 0 in
 									let i = ref 0 in
 									let t = TFun(List.map (fun e -> incr i; "arg" ^ (string_of_int !i), false, e.etype) params, e.etype) in
 									let t = TFun(List.map (fun e -> incr i; "arg" ^ (string_of_int !i), false, e.etype) params, e.etype) in
-									dynamic_func_call { e with eexpr = TCall( mk_cast t (run e1), List.map run params ) }
+									dynamic_func_call { e with eexpr = TCall( mk_castfast t (run e1), List.map run params ) }
 					)
 					)
 				| TField(ecl, FClosure (_,cf)) ->
 				| TField(ecl, FClosure (_,cf)) ->
 					transform_closure e (run ecl) cf.cf_name
 					transform_closure e (run ecl) cf.cf_name
@@ -3078,7 +3083,7 @@ struct
 									("p" ^ (string_of_int !i), false, e.etype)
 									("p" ^ (string_of_int !i), false, e.etype)
 								) params, e.etype)
 								) params, e.etype)
 							in
 							in
-							fun e -> mk_cast t e
+							fun e -> mk_castfast t e
 					in
 					in
 					dynamic_func_call { e with eexpr = TCall(run (may_cast tc), List.map run params) }
 					dynamic_func_call { e with eexpr = TCall(run (may_cast tc), List.map run params) }
 				| _ -> Type.map_expr run e
 				| _ -> Type.map_expr run e
@@ -5913,6 +5918,16 @@ struct
 		let is_cl_related cl tl super superl = map_cls gen (gen.guse_tp_constraints || (match cl.cl_kind,super.cl_kind with KTypeParameter _, _ | _,KTypeParameter _ -> false | _ -> true)) (fun _ _ -> true) super cl tl in
 		let is_cl_related cl tl super superl = map_cls gen (gen.guse_tp_constraints || (match cl.cl_kind,super.cl_kind with KTypeParameter _, _ | _,KTypeParameter _ -> false | _ -> true)) (fun _ _ -> true) super cl tl in
 		is_cl_related cl tl super superl || is_cl_related super superl cl tl
 		is_cl_related cl tl super superl || is_cl_related super superl cl tl
 
 
+	let is_exactly_basic gen t1 t2 =
+		match gen.gfollow#run_f t1, gen.gfollow#run_f t2 with
+			| TAbstract(a1, []), TAbstract(a2, []) ->
+				a1 == a2 && Common.defined gen.gcon Define.FastCast
+			| TInst(c1, []), TInst(c2, []) ->
+				c1 == c2 && Common.defined gen.gcon Define.FastCast
+			| TEnum(e1, []), TEnum(e2, []) ->
+				e1 == e2 && Common.defined gen.gcon Define.FastCast
+			| _ ->
+				false
 
 
 	let rec is_unsafe_cast gen to_t from_t =
 	let rec is_unsafe_cast gen to_t from_t =
 		match (follow to_t, follow from_t) with
 		match (follow to_t, follow from_t) with
@@ -5998,19 +6013,21 @@ struct
 		let do_unsafe_cast () = do_unsafe_cast gen real_from_t real_to_t { e with etype = real_from_t } in
 		let do_unsafe_cast () = do_unsafe_cast gen real_from_t real_to_t { e with etype = real_from_t } in
 		let to_t, from_t = real_to_t, real_from_t in
 		let to_t, from_t = real_to_t, real_from_t in
 
 
-		let mk_cast t e =
+		let mk_cast fast t e =
 			match e.eexpr with
 			match e.eexpr with
 				(* TThrow is always typed as Dynamic, we just need to type it accordingly *)
 				(* TThrow is always typed as Dynamic, we just need to type it accordingly *)
 				| TThrow _ -> { e with etype = t }
 				| TThrow _ -> { e with etype = t }
-				| _ -> mk_cast t e
+				| _ -> if fast then mk_castfast t e else mk_cast t e
 		in
 		in
 
 
 		let e = { e with etype = real_from_t } in
 		let e = { e with etype = real_from_t } in
 		if try fast_eq real_to_t real_from_t with Invalid_argument("List.for_all2") -> false then e else
 		if try fast_eq real_to_t real_from_t with Invalid_argument("List.for_all2") -> false then e else
 		match real_to_t, real_from_t with
 		match real_to_t, real_from_t with
 			(* string is the only type that can be implicitly converted from any other *)
 			(* string is the only type that can be implicitly converted from any other *)
+			| TInst( { cl_path = ([], "String") }, []), TInst( { cl_path = ([], "String") }, [] ) ->
+				mk_cast true to_t e
 			| TInst( { cl_path = ([], "String") }, []), _ ->
 			| TInst( { cl_path = ([], "String") }, []), _ ->
-				mk_cast to_t e
+				mk_cast false to_t e
 			| TInst(cl_to, params_to), TInst(cl_from, params_from) ->
 			| TInst(cl_to, params_to), TInst(cl_from, params_from) ->
 				let ret = ref None in
 				let ret = ref None in
 				(*
 				(*
@@ -6034,7 +6051,7 @@ struct
 								if we are already handling type parameter casts on other part of code (e.g. RealTypeParameters),
 								if we are already handling type parameter casts on other part of code (e.g. RealTypeParameters),
 								we'll just make a cast to indicate that this place needs type parameter-involved casting
 								we'll just make a cast to indicate that this place needs type parameter-involved casting
 							*)
 							*)
-							ret := Some (mk_cast to_t e);
+							ret := Some (mk_cast true to_t e);
 							true
 							true
 						end else
 						end else
 							(*
 							(*
@@ -6043,16 +6060,16 @@ struct
 							*)
 							*)
 							try
 							try
 								List.iter2 (type_eq gen EqRightDynamic) tl params_to;
 								List.iter2 (type_eq gen EqRightDynamic) tl params_to;
-								ret := Some (mk_cast to_t e);
+								ret := Some (mk_cast true to_t e);
 								true
 								true
 							with | Unify_error _ ->
 							with | Unify_error _ ->
-								ret := Some (mk_cast to_t (mk_cast (TInst(cl_to, List.map (fun _ -> t_dynamic) params_to)) e));
+								ret := Some (mk_cast true to_t (mk_cast true (TInst(cl_to, List.map (fun _ -> t_dynamic) params_to)) e));
 								true
 								true
 				) cl_to cl_from params_from);
 				) cl_to cl_from params_from);
 				if is_some !ret then
 				if is_some !ret then
 					get !ret
 					get !ret
 				else if is_cl_related gen cl_from params_from cl_to params_to then
 				else if is_cl_related gen cl_from params_from cl_to params_to then
-					mk_cast to_t e
+					mk_cast true to_t e
 				else
 				else
 					(* potential unsafe cast *)
 					(* potential unsafe cast *)
 					(do_unsafe_cast ())
 					(do_unsafe_cast ())
@@ -6064,18 +6081,18 @@ struct
 			| TMono _, _
 			| TMono _, _
 			| TDynamic _, _
 			| TDynamic _, _
 			| TAnon _, _ when gen.gneeds_box real_from_t ->
 			| TAnon _, _ when gen.gneeds_box real_from_t ->
-				mk_cast to_t e
+				mk_cast false to_t e
 			| TMono _, _
 			| TMono _, _
 			| TDynamic _, _ -> e
 			| TDynamic _, _ -> e
 			| _, TMono _
 			| _, TMono _
-			| _, TDynamic _ -> mk_cast to_t e
+			| _, TDynamic _ -> mk_cast false to_t e
 			| TAnon (a_to), TAnon (a_from) ->
 			| TAnon (a_to), TAnon (a_from) ->
 				if a_to == a_from then
 				if a_to == a_from then
 					e
 					e
 				else if type_iseq gen to_t from_t then (* FIXME apply unify correctly *)
 				else if type_iseq gen to_t from_t then (* FIXME apply unify correctly *)
 					e
 					e
 				else
 				else
-					mk_cast to_t e
+					mk_cast true to_t e
 			| _, TAnon(anon) -> (try
 			| _, TAnon(anon) -> (try
 				let p2 = match !(anon.a_status) with
 				let p2 = match !(anon.a_status) with
 				| Statics c -> TInst(c,List.map (fun _ -> t_dynamic) c.cl_params)
 				| Statics c -> TInst(c,List.map (fun _ -> t_dynamic) c.cl_params)
@@ -6088,7 +6105,7 @@ struct
 				| _ -> assert false in
 				| _ -> assert false in
 				handle_cast gen e real_to_t (gen.greal_type (TAbstract(tclass, [p2])))
 				handle_cast gen e real_to_t (gen.greal_type (TAbstract(tclass, [p2])))
 			with | Not_found ->
 			with | Not_found ->
-				mk_cast to_t e)
+				mk_cast false to_t e)
 			| TAbstract (a_to, _), TAbstract(a_from, _) when a_to == a_from ->
 			| TAbstract (a_to, _), TAbstract(a_from, _) when a_to == a_from ->
 				e
 				e
 			| TAbstract _, TInst({ cl_kind = KTypeParameter _ }, _)
 			| TAbstract _, TInst({ cl_kind = KTypeParameter _ }, _)
@@ -6098,11 +6115,11 @@ struct
 			| _, TAbstract _ ->
 			| _, TAbstract _ ->
 				(try
 				(try
 					unify from_t to_t;
 					unify from_t to_t;
-					mk_cast to_t e
+					mk_cast true to_t e
 				with | Unify_error _ ->
 				with | Unify_error _ ->
 					try
 					try
 						unify to_t from_t;
 						unify to_t from_t;
-						mk_cast to_t e
+						mk_cast true to_t e
 					with | Unify_error _ ->
 					with | Unify_error _ ->
 						do_unsafe_cast())
 						do_unsafe_cast())
 			| TEnum(e_to, []), TEnum(e_from, []) ->
 			| TEnum(e_to, []), TEnum(e_from, []) ->
@@ -6149,13 +6166,13 @@ struct
 					e
 					e
 			| TType(t_to, _), TType(t_from,_) ->
 			| TType(t_to, _), TType(t_from,_) ->
 				if gen.gspecial_needs_cast real_to_t real_from_t then
 				if gen.gspecial_needs_cast real_to_t real_from_t then
-					mk_cast to_t e
+					mk_cast false to_t e
 				else
 				else
 					e
 					e
 			| TType _, _ when gen.gspecial_needs_cast real_to_t real_from_t ->
 			| TType _, _ when gen.gspecial_needs_cast real_to_t real_from_t ->
-				mk_cast to_t e
+				mk_cast false to_t e
 			| _, TType _ when gen.gspecial_needs_cast real_to_t real_from_t ->
 			| _, TType _ when gen.gspecial_needs_cast real_to_t real_from_t ->
-				mk_cast to_t e
+				mk_cast false to_t e
 			(*| TType(t_to, _), TType(t_from, _) ->
 			(*| TType(t_to, _), TType(t_from, _) ->
 				if t_to.t_path = t_from.t_path then
 				if t_to.t_path = t_from.t_path then
 					e
 					e
@@ -6168,15 +6185,15 @@ struct
 				if is_unsafe_cast gen real_to_t real_from_t then (* is_unsafe_cast will already follow both *)
 				if is_unsafe_cast gen real_to_t real_from_t then (* is_unsafe_cast will already follow both *)
 					(do_unsafe_cast ())
 					(do_unsafe_cast ())
 				else
 				else
-					mk_cast to_t e
+					mk_cast false to_t e
 			| TAnon anon, _ ->
 			| TAnon anon, _ ->
 				if PMap.is_empty anon.a_fields then
 				if PMap.is_empty anon.a_fields then
 					e
 					e
 				else
 				else
-					mk_cast to_t e
+					mk_cast true to_t e
 			| TFun(args, ret), TFun(args2, ret2) ->
 			| TFun(args, ret), TFun(args2, ret2) ->
 				let get_args = List.map (fun (_,_,t) -> t) in
 				let get_args = List.map (fun (_,_,t) -> t) in
-				(try List.iter2 (type_eq gen (EqBothDynamic)) (ret :: get_args args) (ret2 :: get_args args2); e with | Unify_error _ | Invalid_argument("List.iter2") -> mk_cast to_t e)
+				(try List.iter2 (type_eq gen (EqBothDynamic)) (ret :: get_args args) (ret2 :: get_args args2); e with | Unify_error _ | Invalid_argument("List.iter2") -> mk_cast true to_t e)
 			| _, _ ->
 			| _, _ ->
 				do_unsafe_cast ()
 				do_unsafe_cast ()
 
 
@@ -6310,8 +6327,16 @@ struct
 		let args,ret = get_fun tfun in
 		let args,ret = get_fun tfun in
 		TFun(loop [] args elist, ret)
 		TFun(loop [] args elist, ret)
 
 
-	(*
+	let fastcast_if_needed gen expr real_to_t real_from_t =
+		if Common.defined gen.gcon Define.FastCast then begin
+			if type_iseq gen real_to_t real_from_t then
+				{ expr with etype = real_to_t }
+			else
+				mk_castfast real_to_t { expr with etype=real_from_t }
+		end else
+			handle_cast gen expr real_to_t real_from_t
 
 
+	(*
 		Type parameter handling
 		Type parameter handling
 		It will detect if/what type parameters were used, and call the cast handler
 		It will detect if/what type parameters were used, and call the cast handler
 		It will handle both TCall(TField) and TCall by receiving a texpr option field: e
 		It will handle both TCall(TField) and TCall by receiving a texpr option field: e
@@ -6351,6 +6376,14 @@ struct
 					| TInst(_,params) -> params
 					| TInst(_,params) -> params
 					| _ -> params
 					| _ -> params
 				in
 				in
+				let local_mk_cast t expr =
+					(* handle_cast gen expr t expr.etype *)
+					if is_exactly_basic gen t expr.etype then
+						expr
+					else
+						mk_castfast t expr
+				in
+
 				let ecall = get e in
 				let ecall = get e in
 				let ef = ref ef in
 				let ef = ref ef in
 				let is_overload = cf.cf_overloads <> [] || Meta.has Meta.Overload cf.cf_meta || (is_static && is_static_overload cl (field_name f)) in
 				let is_overload = cf.cf_overloads <> [] || Meta.has Meta.Overload cf.cf_meta || (is_static && is_static_overload cl (field_name f)) in
@@ -6419,7 +6452,7 @@ struct
 					if is_void ecall.etype then
 					if is_void ecall.etype then
 						{ ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
 						{ ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
 					else
 					else
-						mk_cast ecall.etype { ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
+						local_mk_cast ecall.etype { ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
 				else begin
 				else begin
 					(* infer arguments *)
 					(* infer arguments *)
 					(* let called_t = TFun(List.map (fun e -> "arg",false,e.etype) elist, ecall.etype) in *)
 					(* let called_t = TFun(List.map (fun e -> "arg",false,e.etype) elist, ecall.etype) in *)
@@ -6444,12 +6477,12 @@ struct
 						let elist = List.map2 (fun applied (_,_,funct) ->
 						let elist = List.map2 (fun applied (_,_,funct) ->
 							match is_overload, applied.eexpr with
 							match is_overload, applied.eexpr with
 							| true, TConst TNull ->
 							| true, TConst TNull ->
-								mk_cast (gen.greal_type funct) applied
+								local_mk_cast (gen.greal_type funct) applied
 							| true, _ -> (* when not (type_iseq gen (gen.greal_type applied.etype) funct) -> *)
 							| true, _ -> (* when not (type_iseq gen (gen.greal_type applied.etype) funct) -> *)
 								let ret = handle_cast gen applied (funct) (gen.greal_type applied.etype) in
 								let ret = handle_cast gen applied (funct) (gen.greal_type applied.etype) in
 								(match ret.eexpr with
 								(match ret.eexpr with
 								| TCast _ -> ret
 								| TCast _ -> ret
-								| _ -> mk_cast (funct) ret)
+								| _ -> local_mk_cast (funct) ret)
 							| _ ->
 							| _ ->
 								handle_cast gen applied (funct) (gen.greal_type applied.etype)
 								handle_cast gen applied (funct) (gen.greal_type applied.etype)
 						) applied args_ft in
 						) applied args_ft in
@@ -6553,6 +6586,89 @@ struct
 			| _ -> false
 			| _ -> false
 		in
 		in
 
 
+		let binop_type op main_expr e1 e2 =
+			let name = Common.platform_name gen.gcon.platform in
+			let basic = gen.gcon.basic in
+			(* If either operand is of type decimal, the other operand is converted to type decimal, or a compile-time error occurs if the other operand is of type float or double.
+			 * Otherwise, if either operand is of type double, the other operand is converted to type double.
+			 * Otherwise, if either operand is of type float, the other operand is converted to type float.
+			 * Otherwise, if either operand is of type ulong, the other operand is converted to type ulong, or a compile-time error occurs if the other operand is of type sbyte, short, int, or long.
+			 * Otherwise, if either operand is of type long, the other operand is converted to type long.
+			 * Otherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.
+			 * Otherwise, if either operand is of type uint, the other operand is converted to type uint.
+			 * Otherwise, both operands are converted to type int.
+			 *  *)
+			let t1, t2 = follow (run_follow gen e1.etype), follow (run_follow gen e2.etype) in
+			match t1, t2 with
+				| TAbstract(a1,[]), TAbstract(a2,[]) when a1 == a2 ->
+					(* { main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| TInst(i1,[]), TInst(i2,[]) when i1 == i2 ->
+					(* { main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| TInst({ cl_path = ([],"String") },[]), _ when op = OpAdd ->
+					{ main_expr with eexpr = TBinop(op, e1, mk_cast basic.tstring e2); etype = basic.tstring }
+				| _, TInst({ cl_path = ([],"String") },[]) when op = OpAdd ->
+					{ main_expr with eexpr = TBinop(op, mk_cast basic.tstring e1, e2); etype = basic.tstring }
+				| TAbstract({ a_path = ([], "Float") }, []), _ ->
+					(* { main_expr with eexpr = TBinop(op, e1, mk_castfast t1 e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([], "Float") }, []) ->
+					(* { main_expr with eexpr = TBinop(op, mk_castfast t2 e1, e2); etype = e2.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract({ a_path = ([], "Single") }, []), _ ->
+					(* { main_expr with eexpr = TBinop(op, e1, mk_castfast t1 e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([], "Single") }, []) ->
+					(* { main_expr with eexpr = TBinop(op, mk_castfast t2 e1, e2); etype = e2.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract({ a_path = ([pf], "UInt64") }, []), _ when pf = name ->
+					(* { main_expr with eexpr = TBinop(op, e1, mk_castfast t1 e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([pf], "UInt64") }, []) when pf = name ->
+					(* { main_expr with eexpr = TBinop(op, mk_castfast t2 e1, e2); etype = e2.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract({ a_path = ([pf], "Int64") }, []), _ when pf = name ->
+					(* { main_expr with eexpr = TBinop(op, e1, mk_castfast t1 e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([pf], "Int64") }, []) when pf = name ->
+					(* { main_expr with eexpr = TBinop(op, mk_castfast t2 e1, e2); etype = e2.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract({ a_path = ([], "UInt") }, []), tother when like_int tother ->
+					let ti64 = mt_to_t_dyn ( get_type gen ([name], "Int64") ) in
+					(* { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } *)
+					let ret = { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } in
+					if op <> OpDiv then
+						mk_cast t1 ret
+					else
+						ret
+				| tother, TAbstract({ a_path = ([], "UInt") }, []) when like_int tother ->
+					let ti64 = mt_to_t_dyn ( get_type gen ([name], "Int64") ) in
+					(* { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } *)
+					let ret = { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } in
+					if op <> OpDiv then
+						mk_cast t2 ret
+					else
+						ret
+				| TAbstract({ a_path = ([], "UInt") }, []), _ ->
+					(* { main_expr with eexpr = TBinop(op, e1, mk_castfast t1 e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([], "UInt") }, []) ->
+					(* { main_expr with eexpr = TBinop(op, mk_castfast t2 e1, e2); etype = e2.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract(a1,[]), TAbstract(a2,[]) ->
+					(* { main_expr with eexpr = TBinop(op, mk_castfast basic.tint e1, mk_castfast basic.tint e2); etype = basic.tint } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = basic.tint }
+				| _ ->
+					(* { main_expr with eexpr = TBinop(op, e1, e2) } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2) }
+		in
+		let binop_type = if Common.defined gen.gcon Define.FastCast then
+			binop_type
+		else
+			fun op main_expr e1 e2 -> { main_expr with eexpr = TBinop(op, e1, e2) }
+		in
+
 		let rec run ?(just_type = false) e =
 		let rec run ?(just_type = false) e =
 			let handle = if not just_type then handle else fun e t1 t2 -> { e with etype = gen.greal_type t2 } in
 			let handle = if not just_type then handle else fun e t1 t2 -> { e with etype = gen.greal_type t2 } in
 			let was_in_value = !in_value in
 			let was_in_value = !in_value in
@@ -6581,7 +6697,12 @@ struct
 				| TBinop ( (Ast.OpShl | Ast.OpShr | Ast.OpUShr as op), e1, e2 ) ->
 				| TBinop ( (Ast.OpShl | Ast.OpShr | Ast.OpUShr as op), e1, e2 ) ->
 					let e1 = run e1 in
 					let e1 = run e1 in
 					let e2 = handle (run e2) (gen.gcon.basic.tint) e2.etype in
 					let e2 = handle (run e2) (gen.gcon.basic.tint) e2.etype in
-					{ e with eexpr = TBinop(op, e1, e2) }
+					let rett = binop_type op e e1 e2 in
+					{ e with eexpr = TBinop(op, e1, e2); etype = rett.etype }
+				| TBinop( (OpAdd | OpMult | OpDiv | OpSub | OpAnd | OpOr | OpXor | OpMod) as op, e1, e2 ) ->
+					binop_type op e (run e1) (run e2)
+				| TBinop( (OpEq | OpNotEq | OpGt | OpGte | OpLt | OpLte | OpBoolAnd | OpBoolOr) as op, e1, e2 ) ->
+					handle { e with eexpr = TBinop(op, run e1, run e2) } e.etype gen.gcon.basic.tbool
 				| TField(ef, f) ->
 				| TField(ef, f) ->
 					handle_type_parameter gen None e (run ef) ~clean_ef:ef ~overloads_cast_to_base:overloads_cast_to_base f [] calls_parameters_explicitly
 					handle_type_parameter gen None e (run ef) ~clean_ef:ef ~overloads_cast_to_base:overloads_cast_to_base f [] calls_parameters_explicitly
 				| TArrayDecl el ->
 				| TArrayDecl el ->
@@ -6667,15 +6788,17 @@ struct
 					{ e with eexpr = TNew(cl, tparams, List.map run eparams) })
 					{ e with eexpr = TNew(cl, tparams, List.map run eparams) })
 				| TArray(arr, idx) ->
 				| TArray(arr, idx) ->
 					let arr_etype = match follow arr.etype with
 					let arr_etype = match follow arr.etype with
-					| (TInst _ as t) -> t
-					| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
-						follow (Abstract.get_underlying_type a pl)
-					| t -> t in
+						| (TInst _ as t) -> t
+						| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
+							follow (Abstract.get_underlying_type a pl)
+						| t -> t
+					in
+					let idx = run idx in
 					let idx = match gen.greal_type idx.etype with
 					let idx = match gen.greal_type idx.etype with
-					| TAbstract({ a_path = [],"Int" },_) -> run idx
-					| _ -> match handle (run idx) gen.gcon.basic.tint (gen.greal_type idx.etype) with
-					| ({ eexpr = TCast _ } as idx) -> idx
-					| idx -> mk_cast gen.gcon.basic.tint idx
+						| TAbstract({ a_path = [],"Int" },_) -> idx
+						| _ -> match handle idx gen.gcon.basic.tint (gen.greal_type idx.etype) with
+							| ({ eexpr = TCast _ } as idx) -> idx
+							| idx -> mk_cast gen.gcon.basic.tint idx
 					in
 					in
 					let e = { e with eexpr = TArray(run arr, idx) } in
 					let e = { e with eexpr = TArray(run arr, idx) } in
 					(* get underlying class (if it's a class *)
 					(* get underlying class (if it's a class *)
@@ -6691,7 +6814,8 @@ struct
 									let real_t = apply_params cl.cl_params params param in
 									let real_t = apply_params cl.cl_params params param in
 									(* see if it needs a cast *)
 									(* see if it needs a cast *)
 
 
-									handle (e) (gen.greal_type e.etype) (gen.greal_type real_t)
+									fastcast_if_needed gen e (gen.greal_type e.etype) (gen.greal_type real_t)
+									(* handle (e) (gen.greal_type e.etype) (gen.greal_type real_t) *)
 							)
 							)
 						| _ -> Type.map_expr run e)
 						| _ -> Type.map_expr run e)
 				| TVar (v, eopt) ->
 				| TVar (v, eopt) ->
@@ -6741,6 +6865,8 @@ struct
 								mk_cast (gen.greal_type e.etype) enull
 								mk_cast (gen.greal_type e.etype) enull
 					| _ when is_abstract_to_struct expr.etype && type_iseq gen e.etype (get_abstract_impl expr.etype) ->
 					| _ when is_abstract_to_struct expr.etype && type_iseq gen e.etype (get_abstract_impl expr.etype) ->
 						run { expr with etype = expr.etype }
 						run { expr with etype = expr.etype }
+					| _ when is_exactly_basic gen expr.etype e.etype ->
+						run { expr with etype = expr.etype }
 					| _ ->
 					| _ ->
 						match gen.greal_type e.etype, gen.greal_type expr.etype with
 						match gen.greal_type e.etype, gen.greal_type expr.etype with
 							| (TInst(c,tl) as tinst1), TAbstract({ a_path = ["cs"],"Pointer" }, [tinst2]) when type_iseq gen tinst1 (gen.greal_type tinst2) ->
 							| (TInst(c,tl) as tinst1), TAbstract({ a_path = ["cs"],"Pointer" }, [tinst2]) when type_iseq gen tinst1 (gen.greal_type tinst2) ->
@@ -10752,7 +10878,7 @@ struct
 
 
 	let priority = solve_deps name [ DAfter ExpressionUnwrap.priority; DAfter ObjectDeclMap.priority; DAfter ArrayDeclSynf.priority ]
 	let priority = solve_deps name [ DAfter ExpressionUnwrap.priority; DAfter ObjectDeclMap.priority; DAfter ArrayDeclSynf.priority ]
 
 
-	let is_int = like_int
+	let is_int t = like_int t && not (like_i64 t)
 
 
 	let is_exactly_int t = match follow t with
 	let is_exactly_int t = match follow t with
 		| TAbstract ({ a_path=[],"Int" }, []) -> true
 		| TAbstract ({ a_path=[],"Int" }, []) -> true

+ 9 - 0
src/generators/gencs.ml

@@ -426,6 +426,9 @@ struct
 			| _ -> ""
 			| _ -> ""
 		in
 		in
 
 
+		let as_var = alloc_var "__as__" t_dynamic in
+		let fast_cast = Common.defined gen.gcon Define.FastCast in
+
 		let rec run e =
 		let rec run e =
 			match e.eexpr with
 			match e.eexpr with
 
 
@@ -516,6 +519,12 @@ struct
 						epos = expr.epos
 						epos = expr.epos
 					}
 					}
 
 
+				| TCast(expr, Some(TClassDecl cls)) when fast_cast && cls == null_class ->
+					if is_cs_basic_type (gen.greal_type e.etype) || is_tparam (gen.greal_type e.etype) then
+						{ e with eexpr = TCast(run expr, Some(TClassDecl null_class)) }
+					else
+						{ e with eexpr = TCall(mk_local as_var e.epos, [run expr]) }
+
 				| TCast(expr, _) when (is_string e.etype) && (not (is_string expr.etype)) && name() <> "haxe.lang.Runtime" ->
 				| TCast(expr, _) when (is_string e.etype) && (not (is_string expr.etype)) && name() <> "haxe.lang.Runtime" ->
 					{ e with eexpr = TCall( mk_static_field_access_infer runtime_cl "toString" expr.epos [], [run expr] ) }
 					{ e with eexpr = TCall( mk_static_field_access_infer runtime_cl "toString" expr.epos [], [run expr] ) }
 				| TBinop( (Ast.OpNotEq as op), e1, e2)
 				| TBinop( (Ast.OpNotEq as op), e1, e2)

+ 5 - 1
src/generators/genjava.ml

@@ -570,6 +570,7 @@ struct
 		(* let tsingle = mt_to_t_dyn ( get_type gen ([], "Single") ) in *)
 		(* let tsingle = mt_to_t_dyn ( get_type gen ([], "Single") ) in *)
 		let ti64 = mt_to_t_dyn ( get_type gen (["java"], "Int64") ) in
 		let ti64 = mt_to_t_dyn ( get_type gen (["java"], "Int64") ) in
 		let string_ext = get_cl ( get_type gen (["haxe";"lang"], "StringExt")) in
 		let string_ext = get_cl ( get_type gen (["haxe";"lang"], "StringExt")) in
+		let fast_cast = Common.defined gen.gcon Define.FastCast in
 
 
 		let is_string t = match follow t with | TInst({ cl_path = ([], "String") }, []) -> true | _ -> false in
 		let is_string t = match follow t with | TInst({ cl_path = ([], "String") }, []) -> true | _ -> false in
 
 
@@ -657,8 +658,11 @@ struct
 						epos = expr.epos
 						epos = expr.epos
 					}
 					}
 
 
+				| TCast(expr, Some(TClassDecl cls)) when fast_cast && cls == null_class ->
+					{ e with eexpr = TCast(run expr, Some(TClassDecl null_class)) }
+
 				| TBinop( (Ast.OpAssignOp OpAdd as op), e1, e2)
 				| TBinop( (Ast.OpAssignOp OpAdd as op), e1, e2)
-				| TBinop( (Ast.OpAdd as op), e1, e2) when is_string e.etype || is_string e1.etype || is_string e2.etype ->
+				| TBinop( (Ast.OpAdd as op), e1, e2) when not fast_cast && (is_string e.etype || is_string e1.etype || is_string e2.etype) ->
 						let is_assign = match op with Ast.OpAssignOp _ -> true | _ -> false in
 						let is_assign = match op with Ast.OpAssignOp _ -> true | _ -> false in
 						let mk_to_string e = { e with eexpr = TCall( mk_static_field_access_infer runtime_cl "toString" e.epos [], [run e] ); etype = gen.gcon.basic.tstring	} in
 						let mk_to_string e = { e with eexpr = TCall( mk_static_field_access_infer runtime_cl "toString" e.epos [], [run e] ); etype = gen.gcon.basic.tstring	} in
 						let check_cast e = match gen.greal_type e.etype with
 						let check_cast e = match gen.greal_type e.etype with

+ 2 - 0
src/typing/common.ml

@@ -185,6 +185,7 @@ module Define = struct
 		| DumpIgnoreVarIds
 		| DumpIgnoreVarIds
 		| DynamicInterfaceClosures
 		| DynamicInterfaceClosures
 		| EraseGenerics
 		| EraseGenerics
+		| FastCast
 		| Fdb
 		| Fdb
 		| FileExtension
 		| FileExtension
 		| FlashStrict
 		| FlashStrict
@@ -274,6 +275,7 @@ module Define = struct
 		| DumpIgnoreVarIds -> ("dump_ignore_var_ids","Remove variable IDs from non-pretty dumps (helps with diff)")
 		| DumpIgnoreVarIds -> ("dump_ignore_var_ids","Remove variable IDs from non-pretty dumps (helps with diff)")
 		| DynamicInterfaceClosures -> ("dynamic_interface_closures","Use slow path for interface closures to save space")
 		| DynamicInterfaceClosures -> ("dynamic_interface_closures","Use slow path for interface closures to save space")
 		| EraseGenerics -> ("erase_generics","Erase generic classes on C#")
 		| EraseGenerics -> ("erase_generics","Erase generic classes on C#")
+		| FastCast -> ("fast_cast","Enables an experimental casts cleanup on C# and Java")
 		| Fdb -> ("fdb","Enable full flash debug infos for FDB interactive debugging")
 		| Fdb -> ("fdb","Enable full flash debug infos for FDB interactive debugging")
 		| FileExtension -> ("file_extension","Output filename extension for cpp source code")
 		| FileExtension -> ("file_extension","Output filename extension for cpp source code")
 		| FlashStrict -> ("flash_strict","More strict typing for flash target")
 		| FlashStrict -> ("flash_strict","More strict typing for flash target")

+ 3 - 1
std/cs/internal/Runtime.hx

@@ -107,7 +107,9 @@ import cs.system.Object;
 			{
 			{
 				case [Decimal, _] | [_, Decimal]:
 				case [Decimal, _] | [_, Decimal]:
 					return v1c.ToDecimal(null) == v2c.ToDecimal(null);
 					return v1c.ToDecimal(null) == v2c.ToDecimal(null);
-				case [UInt64 | Int64 | DateTime, _] | [_, UInt64 | Int64 | DateTime]:
+				case [Int64, _] | [_, Int64]:
+					return v1c.ToInt64(null) == v2c.ToInt64(null);
+				case [UInt64 | DateTime, _] | [_, UInt64 | DateTime]:
 					return v1c.ToUInt64(null) == v2c.ToUInt64(null);
 					return v1c.ToUInt64(null) == v2c.ToUInt64(null);
 				case [Double | Single, _] | [_, Double | Single]:
 				case [Double | Single, _] | [_, Double | Single]:
 					return v1c.ToDouble(null) == v2c.ToDouble(null);
 					return v1c.ToDouble(null) == v2c.ToDouble(null);

+ 10 - 10
std/java/internal/Runtime.hx

@@ -161,18 +161,18 @@ package java.internal;
 		return false;
 		return false;
 	}
 	}
 
 
-	@:functionCode('
-		if (obj != null && obj instanceof java.lang.Number)
-		{
-			java.lang.Number n = (java.lang.Number) obj;
+	@:overload public static function isInt(obj:Dynamic):Bool
+	{
+		if (Std.is(obj, java.lang.Number)) {
+			var n:java.lang.Number = obj;
 			return n.doubleValue() == n.intValue();
 			return n.doubleValue() == n.intValue();
 		} else {
 		} else {
 			return false;
 			return false;
 		}
 		}
-	')
-	public static function isInt(obj:Dynamic):Bool
-	{
-		return false;
+	}
+
+	@:overload public static function isInt(num:java.lang.Number):Bool {
+		return num != null && num.doubleValue() == num.intValue();
 	}
 	}
 
 
 	@:functionCode('
 	@:functionCode('
@@ -573,8 +573,8 @@ package java.internal;
 		if (obj == null)
 		if (obj == null)
 			return null;
 			return null;
 
 
-		if (isInt(obj))
-			return (cast(obj, Int)) + "";
+		if (Std.is(obj, java.lang.Number) && !Std.is(obj, java.lang.Integer.IntegerClass) && isInt( (obj : java.lang.Number) ))
+			return java.lang.Integer._toString(toInt(obj));
 		return untyped obj.toString();
 		return untyped obj.toString();
 	}
 	}
 
 

+ 11 - 8
tests/RunCi.hx

@@ -1106,32 +1106,35 @@ class RunCi {
 					runCommand("haxe", ['compile-cs$compl.hxml']);
 					runCommand("haxe", ['compile-cs$compl.hxml']);
 					runCs("bin/cs/bin/Test-Debug.exe");
 					runCs("bin/cs/bin/Test-Debug.exe");
 
 
-					runCommand("haxe", ['compile-cs$compl.hxml','-dce','no']);
+					runCommand("haxe", ['compile-cs$compl.hxml','-D','fast_cast']);
 					runCs("bin/cs/bin/Test-Debug.exe");
 					runCs("bin/cs/bin/Test-Debug.exe");
 
 
-					runCommand("haxe", ['compile-cs-unsafe$compl.hxml']);
+					runCommand("haxe", ['compile-cs$compl.hxml','-dce','no','-D','fast_cast']);
+					runCs("bin/cs/bin/Test-Debug.exe");
+
+					runCommand("haxe", ['compile-cs-unsafe$compl.hxml','-D','fast_cast']);
 					runCs("bin/cs_unsafe/bin/Test-Debug.exe");
 					runCs("bin/cs_unsafe/bin/Test-Debug.exe");
 
 
-					runCommand("haxe", ['compile-cs$compl.hxml',"-D","erase_generics"]);
+					runCommand("haxe", ['compile-cs$compl.hxml',"-D","erase_generics",'-D','fast_cast']);
 					runCs("bin/cs/bin/Test-Debug.exe");
 					runCs("bin/cs/bin/Test-Debug.exe");
 
 
-					runCommand("haxe", ['compile-cs-unsafe$compl.hxml',"-D","erase_generics"]);
+					runCommand("haxe", ['compile-cs-unsafe$compl.hxml',"-D","erase_generics",'-D','fast_cast']);
 					runCs("bin/cs_unsafe/bin/Test-Debug.exe");
 					runCs("bin/cs_unsafe/bin/Test-Debug.exe");
 
 
-					runCommand("haxe", ['compile-cs$compl.hxml',"-D","no_root"]);
+					runCommand("haxe", ['compile-cs$compl.hxml',"-D","no_root",'-D','fast_cast']);
 					runCs("bin/cs/bin/Test-Debug.exe");
 					runCs("bin/cs/bin/Test-Debug.exe");
 
 
-					runCommand("haxe", ['compile-cs-unsafe$compl.hxml',"-D","no_root","-D","erase_generics"]);
+					runCommand("haxe", ['compile-cs-unsafe$compl.hxml',"-D","no_root","-D","erase_generics",'-D','fast_cast']);
 					runCs("bin/cs_unsafe/bin/Test-Debug.exe");
 					runCs("bin/cs_unsafe/bin/Test-Debug.exe");
 
 
 					changeDirectory(sysDir);
 					changeDirectory(sysDir);
-					runCommand("haxe", ["compile-cs.hxml"]);
+					runCommand("haxe", ["compile-cs.hxml",'-D','fast_cast']);
 					runCs("bin/cs/bin/Main-Debug.exe", []);
 					runCs("bin/cs/bin/Main-Debug.exe", []);
 
 
 					changeDirectory(miscDir + "csTwoLibs");
 					changeDirectory(miscDir + "csTwoLibs");
 					for (i in 1...5)
 					for (i in 1...5)
 					{
 					{
-						runCommand("haxe", ['compile-$i.hxml']);
+						runCommand("haxe", ['compile-$i.hxml','-D','fast_cast']);
 						runCs("bin/main/bin/Main.exe");
 						runCs("bin/main/bin/Main.exe");
 					}
 					}
 
 

+ 1 - 0
tests/unit/compile-java.hxml

@@ -9,3 +9,4 @@ compile-each.hxml
 -java-lib native_java/native.jar
 -java-lib native_java/native.jar
 -java-lib java_drivers/mysql-connector-java-5.1.32-bin.jar
 -java-lib java_drivers/mysql-connector-java-5.1.32-bin.jar
 -java-lib java_drivers/sqlite-jdbc-3.7.2.jar
 -java-lib java_drivers/sqlite-jdbc-3.7.2.jar
+-D dump