diff --git a/src/macro/eval/evalContext.ml b/src/macro/eval/evalContext.ml index cd238abf10e..cec69310c80 100644 --- a/src/macro/eval/evalContext.ml +++ b/src/macro/eval/evalContext.ml @@ -534,6 +534,7 @@ let is v path = v <> vnull else match v with | VInt32 _ -> path = key_Int || path = key_Float + | VInt64 _ -> path = key_eval_integers_Int64 | VFloat f -> path = key_Float || (path = key_Int && f = (float_of_int (int_of_float f)) && f <= 2147483647. && f >= -2147483648.) | VTrue | VFalse -> path = key_Bool | VPrototype {pkind = PClass _} -> path = key_Class diff --git a/src/macro/eval/evalEmitter.ml b/src/macro/eval/evalEmitter.ml index 84efa0f0d1b..6fc91a11bba 100644 --- a/src/macro/eval/evalEmitter.ml +++ b/src/macro/eval/evalEmitter.ml @@ -531,7 +531,8 @@ let emit_vector_write exec1 p1 exec2 p2 exec3 p env = (* Read + write *) let do_incr v p = match v with - | VInt32 i32 -> vint32 (Int32.add i32 Int32.one) + | VInt32 i32 -> vint32 (Int32.succ i32) + | VInt64 i64 -> vint64 (Int64.succ i64) | VFloat f -> vfloat (f +. 1.) | v -> unexpected_value_p v "number" p @@ -731,6 +732,7 @@ let emit_bool_or exec1 exec2 env = let emit_neg exec p env = match exec env with | VFloat f -> vfloat (-.f) | VInt32 i -> vint32 (Int32.neg i) + | VInt64 i -> vint64 (Int64.neg i) | _ -> throw_string "Invalid operation" p (* Function *) diff --git a/src/macro/eval/evalHash.ml b/src/macro/eval/evalHash.ml index 0711f6d3ac5..fa69a7474f2 100644 --- a/src/macro/eval/evalHash.ml +++ b/src/macro/eval/evalHash.ml @@ -213,6 +213,8 @@ let key_address = hash "address" let key_netmask = hash "netmask" let key_previous = hash "previous" let key_current = hash "current" -let key_haxe_atomic_AtomicBool = hash "haxe.atomic.AtomicBool" -let key_haxe_atomic_AtomicInt = hash "haxe.atomic.AtomicInt" -let key_haxe_atomic_AtomicObject = hash "haxe.atomic.AtomicObject" \ No newline at end of file +let key_haxe_atomic_AtomicBool = hash "haxe.atomic.AtomicBool" +let key_haxe_atomic_AtomicInt = hash "haxe.atomic.AtomicInt" +let key_haxe_atomic_AtomicObject = hash "haxe.atomic.AtomicObject" + +let key_eval_integers_Int64 = hash "eval.integers._Int64.Int64_Impl_" (* TODO: wonky... *) \ No newline at end of file diff --git a/src/macro/eval/evalIntegers.ml b/src/macro/eval/evalIntegers.ml index 8881ddf071a..8eea9caf1eb 100644 --- a/src/macro/eval/evalIntegers.ml +++ b/src/macro/eval/evalIntegers.ml @@ -12,36 +12,6 @@ open EvalMisc open Unsigned open Signed -let encode_haxe_i64 low high = - let vi = create_instance key_haxe__Int64____Int64 in - set_instance_field vi key_high (vint32 high); - set_instance_field vi key_low (vint32 low); - vinstance vi - -let encode_haxe_i64_int64 value = - let high = Stdlib.Int64.to_int32 (Stdlib.Int64.shift_right_logical value 32) in - let low = Stdlib.Int64.to_int32 value in - let vi = create_instance key_haxe__Int64____Int64 in - set_instance_field vi key_high (vint32 high); - set_instance_field vi key_low (vint32 low); - vinstance vi - -let encode_haxe_i64_direct i64 = - let low = GInt64.to_int32 i64 in - let high = GInt64.to_int32 (GInt64.shift_right_logical i64 32) in - encode_haxe_i64 low high - -let decode_haxe_i64 v = - match v with - | VInstance vi when is v key_haxe__Int64____Int64 -> - let high = decode_i32 (vi.ifields.(get_instance_field_index_raise vi.iproto key_high)) - and low = decode_i32 (vi.ifields.(get_instance_field_index_raise vi.iproto key_low)) in - let high64 = GInt64.shift_left (Int32.to_int64 high) 32 - and low64 = GInt64.logand (Int32.to_int64 low) 0xffffffffL in - GInt64.logor high64 low64 - | _ -> - unexpected_value v "haxe.Int64" - let decode_u64 = function | VUInt64 u -> u | v -> unexpected_value v "eval.integers.UInt64" @@ -163,6 +133,13 @@ let uint64_fields = [ ); ] +let make_int64 high low = + let high = GInt64.of_int32 high in + let high = Int64.shift_left high 32 in + let low = GInt64.of_int32 low in + let low = Int64.logand low (Int64.of_string "0xFFFFFFFF") in + Int64.logor high low + let int64_fields = [ "MAX", VInt64 Int64.max_int; "MIN", VInt64 Int64.min_int; @@ -177,9 +154,6 @@ let int64_fields = [ try VInt64 (Int64.of_string s) with Failure _ -> throw_string "The string is not a valid Int64 representation" null_pos ); - "ofHxInt64", vfun1 (fun v -> - VInt64 (decode_haxe_i64 v) - ); "max", vfun2 (fun v1 v2 -> let a = decode_i64 v1 and b = decode_i64 v2 in @@ -195,18 +169,23 @@ let int64_fields = [ and b = decode_i64 v2 in vint (Int64.compare a b) ); + "make", vfun2 (fun vhigh vlow -> + let high = decode_i32 vhigh in + let low = decode_i32 vlow in + let i = make_int64 high low in + VInt64 i + ); "toInt", vfun1 (fun v -> let i = decode_i64 v in vint32 (GInt64.to_int32 i) ); + "toInt32", vfun1 (fun v -> + vint32 (Int32.of_int64 (decode_i64 v)) + ); "toUInt64", vfun1 (fun v -> let i = decode_i64 v in VUInt64 (UInt64.of_int64 i) ); - "toHxInt64", vfun1 (fun v -> - let i = decode_i64 v in - encode_haxe_i64_direct i - ); "toString", vfun1 (fun v -> let i = decode_i64 v in EvalString.vstring (EvalString.create_ascii (Int64.to_string i)) @@ -271,6 +250,11 @@ let int64_fields = [ and i = decode_int v2 in VInt64 (Int64.shift_right i64 i) ); + "shift_right_logical", vfun2 (fun v1 v2 -> + let i64 = decode_i64 v1 + and i = decode_int v2 in + VInt64 (Int64.shift_right_logical i64 i) + ); "lognot", vfun1 (fun v -> let i = decode_i64 v in VInt64 (Int64.lognot i) diff --git a/src/macro/eval/evalMisc.ml b/src/macro/eval/evalMisc.ml index 0d7feadb70c..24fbc05479d 100644 --- a/src/macro/eval/evalMisc.ml +++ b/src/macro/eval/evalMisc.ml @@ -123,10 +123,13 @@ let fcmp (a:float) b = if a = b then CEq else if a < b then CInf else if a > b t let icmp (a:int32) b = let l = Int32.compare a b in if l = 0 then CEq else if l < 0 then CInf else CSup +let i64cmp (a:int64) b = let l = Int64.compare a b in if l = 0 then CEq else if l < 0 then CInf else CSup + let rec compare a b = match a, b with | VNull,VNull -> CEq | VInt32 a,VInt32 b -> icmp a b + | VInt64 a,VInt64 b -> i64cmp a b | VFloat a,VFloat b -> fcmp a b | VFloat a,VInt32 b -> fcmp a (Int32.to_float b) | VInt32 a,VFloat b -> fcmp (Int32.to_float a) b @@ -175,6 +178,7 @@ let rec arrays_equal cmp a1 a2 = and equals_structurally a b = match a,b with | VInt32 a,VInt32 b -> Int32.compare a b = 0 + | VInt64 a,VInt64 b -> Int64.compare a b = 0 | VFloat a,VFloat b -> a = b | VFloat a,VInt32 b -> a = (Int32.to_float b) | VInt32 a,VFloat b -> (Int32.to_float a) = b @@ -194,6 +198,11 @@ let is_true v = match v with let op_add p v1 v2 = match v1,v2 with | VInt32 i1,VInt32 i2 -> vint32 (Int32.add i1 i2) + | VInt64 i1,VInt64 i2 -> vint64 (Int64.add i1 i2) + (* These two cases should technically not exist, but the analyzer emits code like this because + our TConst only knows Int32 at the moment. *) + | VInt64 i1,VInt32 i2 -> vint64 (Int64.add i1 (Int64.of_int32 i2)) + | VInt32 i1,VInt64 i2 -> vint64 (Int64.add (Int64.of_int32 i1) i2) | VFloat f1,VFloat f2 -> vfloat (f1 +. f2) | VInt32 i,VFloat f | VFloat f,VInt32 i -> vfloat ((Int32.to_float i) +. f) | VNativeString s1,VNativeString s2 -> vnative_string (s1 ^ s2) @@ -204,6 +213,7 @@ let op_add p v1 v2 = match v1,v2 with let op_mult p v1 v2 = match v1,v2 with | VInt32 i1,VInt32 i2 -> vint32 (Int32.mul i1 i2) + | VInt64 i1,VInt64 i2 -> vint64 (Int64.mul i1 i2) | VFloat f1,VFloat f2 -> vfloat (f1 *. f2) | VInt32 i,VFloat f | VFloat f,VInt32 i -> vfloat ((Int32.to_float i) *. f) | _ -> invalid_binop OpMult v1 v2 p @@ -217,6 +227,10 @@ let op_div p v1 v2 = match v1,v2 with let op_sub p v1 v2 = match v1,v2 with | VInt32 i1,VInt32 i2 -> vint32 (Int32.sub i1 i2) + | VInt64 i1,VInt64 i2 -> vint64 (Int64.sub i1 i2) + (* See op_add remark. *) + | VInt64 i1,VInt32 i2 -> vint64 (Int64.sub i1 (Int64.of_int32 i2)) + | VInt32 i1,VInt64 i2 -> vint64 (Int64.sub (Int64.of_int32 i1) i2) | VFloat f1,VFloat f2 -> vfloat (f1 -. f2) | VInt32 i1,VFloat f2 -> vfloat ((Int32.to_float i1) -. f2) | VFloat f1,VInt32 i2 -> vfloat (f1 -. (Int32.to_float i2)) diff --git a/src/macro/eval/evalStdLib.ml b/src/macro/eval/evalStdLib.ml index 28e47e062d6..c5db97876a4 100644 --- a/src/macro/eval/evalStdLib.ml +++ b/src/macro/eval/evalStdLib.ml @@ -333,7 +333,7 @@ module StdBytes = struct try let low = read_i32 this pos in let high = read_i32 this (pos + 4) in - EvalIntegers.encode_haxe_i64 low high; + vint64 (EvalIntegers.make_int64 high low) with _ -> outside_bounds() ) @@ -500,9 +500,9 @@ module StdBytesBuffer = struct let addInt64 = vifun1 (fun vthis v -> let this = this vthis in - let v = decode_instance v in - let high = decode_i32 (instance_field v key_high) in - let low = decode_i32 (instance_field v key_low) in + let i64 = EvalIntegers.decode_i64 v in + let high = Int64.to_int32 (Int64.shift_right i64 32) in + let low = Int64.to_int32 i64 in add_i32 this low; add_i32 this high; vnull; @@ -1164,7 +1164,7 @@ module StdFPHelper = struct let doubleToI64 = vfun1 (fun v -> let f = num v in let i64 = Int64.bits_of_float f in - EvalIntegers.encode_haxe_i64_direct i64 + vint64 i64 ) let floatToI32 = vfun1 (fun f -> @@ -1581,11 +1581,11 @@ module StdInt64Map = struct ) let exists = vifun1 (fun vthis vkey -> - vbool (RuntimeInt64Hashtbl.mem (this vthis) (EvalIntegers.decode_haxe_i64 vkey)) + vbool (RuntimeInt64Hashtbl.mem (this vthis) (EvalIntegers.decode_i64 vkey)) ) let get = vifun1 (fun vthis vkey -> - try RuntimeInt64Hashtbl.find (this vthis) (EvalIntegers.decode_haxe_i64 vkey) + try RuntimeInt64Hashtbl.find (this vthis) (EvalIntegers.decode_i64 vkey) with Not_found -> vnull ) @@ -1595,7 +1595,7 @@ module StdInt64Map = struct ) let keys = vifun0 (fun vthis -> - let keys = RuntimeInt64Hashtbl.fold (fun k _ acc -> EvalIntegers.encode_haxe_i64_int64 k :: acc) (this vthis) [] in + let keys = RuntimeInt64Hashtbl.fold (fun k _ acc -> vint64 k :: acc) (this vthis) [] in encode_list_iterator keys ) @@ -1603,14 +1603,14 @@ module StdInt64Map = struct let remove = vifun1 (fun vthis vkey -> let this = this vthis in - let key = EvalIntegers.decode_haxe_i64 vkey in + let key = EvalIntegers.decode_i64 vkey in let b = RuntimeInt64Hashtbl.mem this key in RuntimeInt64Hashtbl.remove this key; vbool b ) let set = vifun2 (fun vthis vkey vvalue -> - RuntimeInt64Hashtbl.add (this vthis) (EvalIntegers.decode_haxe_i64 vkey) vvalue; + RuntimeInt64Hashtbl.add (this vthis) (EvalIntegers.decode_i64 vkey) vvalue; vnull ) @@ -2796,7 +2796,7 @@ module StdSys = struct let time = vfun0 (fun () -> vfloat (catch_unix_error Unix.gettimeofday())) - let timestamp_ms = vfun0 (fun () -> EvalIntegers.encode_haxe_i64_direct (* TODO: use vint64 once that works *) (Extc.timestamp_ms())) + let timestamp_ms = vfun0 (fun () -> vint64 (Extc.timestamp_ms())) end module StdThread = struct diff --git a/std/eval/_std/haxe/Int64.hx b/std/eval/_std/haxe/Int64.hx new file mode 100644 index 00000000000..c0beb9736d6 --- /dev/null +++ b/std/eval/_std/haxe/Int64.hx @@ -0,0 +1,220 @@ +package haxe; + +import eval.integers.Int64 as I64; + +private typedef __Int64 = I64; + +@:transitive +@:coreApi +abstract Int64(__Int64) from __Int64 to __Int64 { + public static inline function make(high:Int32, low:Int32):Int64 + return I64.make(high, low); + + private inline function new(x:__Int64) + this = x; + + private var val(get, set):__Int64; + + inline function get_val():__Int64 + return this; + + inline function set_val(x:__Int64):__Int64 + return this = x; + + public var high(get, never):Int32; + + inline function get_high():Int32 + return (this >> 32).toInt32(); + + public var low(get, never):Int32; + + inline function get_low():Int32 + return this.toInt32(); + + public inline function copy():Int64 + return new Int64(this); + + @:from public static inline function ofInt(x:Int):Int64 + return I64.ofInt(x); + + @:deprecated('haxe.Int64.is() is deprecated. Use haxe.Int64.isInt64() instead') + inline public static function is(val:Dynamic):Bool { + return isInt64(val); + } + + inline public static function isInt64(val:Dynamic):Bool + return Std.isOfType(val, eval.integers.Int64); + + public static inline function toInt(x:Int64):Int { + if (x.high != x.low >> 31) + throw "Overflow"; + return x.val.toInt(); + } + + public static inline function getHigh(x:Int64):Int32 + return x.high; + + public static inline function getLow(x:Int64):Int32 + return x.low; + + public static inline function isNeg(x:Int64):Bool + return x.val < 0i64; + + public static inline function isZero(x:Int64):Bool + return x.val == 0i64; + + public static inline function compare(a:Int64, b:Int64):Int { + if (a.val < b.val) + return -1; + if (a.val > b.val) + return 1; + return 0; + } + + public static inline function ucompare(a:Int64, b:Int64):Int { + if (a.val < 0i64) + return (b.val < 0i64) ? compare(a, b) : 1; + return (b.val < 0i64) ? -1 : compare(a, b); + } + + public static inline function toStr(x:Int64):String + return '${x.val}'; + + public static inline function divMod(dividend:Int64, divisor:Int64):{quotient:Int64, modulus:Int64} + return {quotient: dividend / divisor, modulus: dividend % divisor}; + + public inline function toString():String + return '$this'; + + public static function parseString(sParam:String):Int64 { + return Int64Helper.parseString(sParam); + } + + public static function fromFloat(f:Float):Int64 { + return Int64Helper.fromFloat(f); + } + + @:op(-A) public static function neg(x:Int64):Int64 + return -x.val; + + @:op(++A) private inline function preIncrement():Int64 + return ++this; + + @:op(A++) private inline function postIncrement():Int64 + return this++; + + @:op(--A) private inline function preDecrement():Int64 + return --this; + + @:op(A--) private inline function postDecrement():Int64 + return this + + --; + @:op(A + B) public static inline function add(a:Int64, b:Int64):Int64 + return a.val + b.val; + + @:op(A + B) @:commutative private static inline function addInt(a:Int64, b:Int):Int64 + return a.val + I64.ofInt(b); + + @:op(A - B) public static inline function sub(a:Int64, b:Int64):Int64 + return a.val - b.val; + + @:op(A - B) private static inline function subInt(a:Int64, b:Int):Int64 + return a.val - I64.ofInt(b); + + @:op(A - B) private static inline function intSub(a:Int, b:Int64):Int64 + return I64.ofInt(a) - b.val; + + @:op(A * B) public static inline function mul(a:Int64, b:Int64):Int64 + return a.val * b.val; + + @:op(A * B) @:commutative private static inline function mulInt(a:Int64, b:Int):Int64 + return a.val * I64.ofInt(b); + + @:op(A / B) public static inline function div(a:Int64, b:Int64):Int64 + return a.val / b.val; + + @:op(A / B) private static inline function divInt(a:Int64, b:Int):Int64 + return a.val / I64.ofInt(b); + + @:op(A / B) private static inline function intDiv(a:Int, b:Int64):Int64 + return I64.ofInt(a) / b.val; + + @:op(A % B) public static inline function mod(a:Int64, b:Int64):Int64 + return a.val % b.val; + + @:op(A % B) private static inline function modInt(a:Int64, b:Int):Int64 + return a.val % I64.ofInt(b); + + @:op(A % B) private static inline function intMod(a:Int, b:Int64):Int64 + return I64.ofInt(a) % b.val; + + @:op(A == B) public static inline function eq(a:Int64, b:Int64):Bool + return a.val == b.val; + + @:op(A == B) @:commutative private static inline function eqInt(a:Int64, b:Int):Bool + return a.val == I64.ofInt(b); + + @:op(A != B) public static inline function neq(a:Int64, b:Int64):Bool + return a.val != b.val; + + @:op(A != B) @:commutative private static inline function neqInt(a:Int64, b:Int):Bool + return a.val != I64.ofInt(b); + + @:op(A < B) private static inline function lt(a:Int64, b:Int64):Bool + return a.val < b.val; + + @:op(A < B) private static inline function ltInt(a:Int64, b:Int):Bool + return a.val < I64.ofInt(b); + + @:op(A < B) private static inline function intLt(a:Int, b:Int64):Bool + return I64.ofInt(a) < b.val; + + @:op(A <= B) private static inline function lte(a:Int64, b:Int64):Bool + return a.val <= b.val; + + @:op(A <= B) private static inline function lteInt(a:Int64, b:Int):Bool + return a.val <= I64.ofInt(b); + + @:op(A <= B) private static inline function intLte(a:Int, b:Int64):Bool + return I64.ofInt(a) <= b.val; + + @:op(A > B) private static inline function gt(a:Int64, b:Int64):Bool + return a.val > b.val; + + @:op(A > B) private static inline function gtInt(a:Int64, b:Int):Bool + return a.val > I64.ofInt(b); + + @:op(A > B) private static inline function intGt(a:Int, b:Int64):Bool + return I64.ofInt(a) > b.val; + + @:op(A >= B) private static inline function gte(a:Int64, b:Int64):Bool + return a.val >= b.val; + + @:op(A >= B) private static inline function gteInt(a:Int64, b:Int):Bool + return a.val >= I64.ofInt(b); + + @:op(A >= B) private static inline function intGte(a:Int, b:Int64):Bool + return I64.ofInt(a) >= b.val; + + @:op(~A) private static inline function complement(x:Int64):Int64 + return ~x.val; + + @:op(A & B) public static inline function and(a:Int64, b:Int64):Int64 + return a.val & b.val; + + @:op(A | B) public static inline function or(a:Int64, b:Int64):Int64 + return a.val | b.val; + + @:op(A ^ B) public static inline function xor(a:Int64, b:Int64):Int64 + return a.val ^ b.val; + + @:op(A << B) public static inline function shl(a:Int64, b:Int):Int64 + return a.val << b; + + @:op(A >> B) public static inline function shr(a:Int64, b:Int):Int64 + return a.val >> b; + + @:op(A >>> B) public static inline function ushr(a:Int64, b:Int):Int64 + return a.val >>> b; +} diff --git a/std/eval/_std/haxe/io/FPHelper.hx b/std/eval/_std/haxe/io/FPHelper.hx new file mode 100644 index 00000000000..f2769597edb --- /dev/null +++ b/std/eval/_std/haxe/io/FPHelper.hx @@ -0,0 +1,11 @@ +package haxe.io; + +extern class FPHelper { + public static function i32ToFloat(i:Int):Float; + + public static function floatToI32(f:Float):Int; + + public static function i64ToDouble(low:Int, high:Int):Float; + + public static function doubleToI64(v:Float):Int64; +} diff --git a/std/eval/integers/Int64.hx b/std/eval/integers/Int64.hx index 28807c910b5..f1d18ffc5d5 100644 --- a/std/eval/integers/Int64.hx +++ b/std/eval/integers/Int64.hx @@ -1,8 +1,11 @@ package eval.integers; +import haxe.Int32; + /** Signed 64-bit integer type and operations. **/ +@:runtimeValue @:coreType abstract Int64 { /** The greatest representable Int64 value. */ extern static public final MAX:Int64; @@ -47,6 +50,8 @@ package eval.integers; **/ static public function compare(a:Int64, b:Int64):Int; + static public function make(high:Int32, low:Int32):Int64; + /** Convert to an integer value. The 64-bit signed integer is taken modulo 2{^32}, i.e. the top 32 bits @@ -59,6 +64,8 @@ package eval.integers; **/ public function toUInt64():UInt64; + public function toInt32():Int32; + /** Convert to `haxe.Int64`. **/ @@ -72,12 +79,12 @@ package eval.integers; /** Successor. **/ - public function successor():String; + public function successor():Int64; /** Predecessor. **/ - public function predecessor():String; + public function predecessor():Int64; /** Integer remainder. @@ -94,8 +101,15 @@ package eval.integers; function logxor(u:Int64):Int64; function shift_left(i:Int):Int64; function shift_right(i:Int):Int64; + function shift_right_logical(i:Int):Int64; function lognot():Int64; + @:op(-A) function neg():Int64; + @:op(++A) function preIncr():Int64; + @:op(A++) function postIncr():Int64; + @:op(--A) function preDecr():Int64; + @:op(A--) function postDecr():Int64; + @:op(A + B) inline function _add(u:Int64):Int64 return this.add(u); @:op(A - B) inline function _sub(u:Int64):Int64 return this.sub(u); @:op(A * B) inline function _mul(u:Int64):Int64 return this.mul(u); @@ -106,6 +120,7 @@ package eval.integers; @:op(A ^ B) inline function _logxor(u:Int64):Int64 return this.logxor(u); @:op(A << B) inline function _shift_left(i:Int):Int64 return this.shift_left(i); @:op(A >> B) inline function _shift_right(i:Int):Int64 return this.shift_right(i); + @:op(A >>> B) inline function _shift_right_logical(i:Int):Int64 return this.shift_right_logical(i); @:op(~A) inline function _lognot():Int64 return this.lognot(); @:op(A != B) static inline function eq(a:Int64, b:Int64):Bool return compare(a, b) != 0; diff --git a/tests/unit/src/unit/issues/Issue12264.hx b/tests/unit/src/unit/issues/Issue12264.hx index bae1ca7201d..9b5ba0bca4f 100644 --- a/tests/unit/src/unit/issues/Issue12264.hx +++ b/tests/unit/src/unit/issues/Issue12264.hx @@ -1,6 +1,7 @@ package unit.issues; class Issue12264 extends Test { + #if !eval function test() { #if (cpp || jvm || hl) eq(true, Type.typeof(0i64).match(TInt)); @@ -8,4 +9,5 @@ class Issue12264 extends Test { eq(true, Type.typeof(0i64).match(TClass(_))); #end } + #end }