From ff6b02de74dbf1cd0671fb27b25441487e7765e9 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Tue, 5 Jun 2018 16:42:01 +1000 Subject: [PATCH 01/31] Helper function for FastGenericEqualityComparerTable --- src/fsharp/FSharp.Core/prim-types.fs | 42 ++++++++++++++++------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index d04a8f8b45c..08e74cda169 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -2112,27 +2112,33 @@ namespace Microsoft.FSharp.Core let Float32IEquality = MakeGenericEqualityComparer() let DecimalIEquality = MakeGenericEqualityComparer() + let tryGetFastGenericEqualityComparerTable (ty:Type) = + match ty with + | ty when ty.Equals typeof -> box BoolIEquality + | ty when ty.Equals typeof -> box ByteIEquality + | ty when ty.Equals typeof -> box Int32IEquality + | ty when ty.Equals typeof -> box UInt32IEquality + | ty when ty.Equals typeof -> box CharIEquality + | ty when ty.Equals typeof -> box SByteIEquality + | ty when ty.Equals typeof -> box Int16IEquality + | ty when ty.Equals typeof -> box Int64IEquality + | ty when ty.Equals typeof -> box IntPtrIEquality + | ty when ty.Equals typeof -> box UInt16IEquality + | ty when ty.Equals typeof -> box UInt64IEquality + | ty when ty.Equals typeof -> box UIntPtrIEquality + | ty when ty.Equals typeof -> box FloatIEquality + | ty when ty.Equals typeof -> box Float32IEquality + | ty when ty.Equals typeof -> box DecimalIEquality + | ty when ty.Equals typeof -> box StringIEquality + | _ -> null + [] - type FastGenericEqualityComparerTable<'T>() = + type FastGenericEqualityComparerTable<'T>() = static let f : System.Collections.Generic.IEqualityComparer<'T> = - match typeof<'T> with - | ty when ty.Equals(typeof) -> unboxPrim (box BoolIEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box ByteIEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box Int32IEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box UInt32IEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box CharIEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box SByteIEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box Int16IEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box Int64IEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box IntPtrIEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box UInt16IEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box UInt64IEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box UIntPtrIEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box FloatIEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box Float32IEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box DecimalIEquality) - | ty when ty.Equals(typeof) -> unboxPrim (box StringIEquality) + match tryGetFastGenericEqualityComparerTable typeof<'T> with + | :? System.Collections.Generic.IEqualityComparer<'T> as comp -> comp | _ -> MakeGenericEqualityComparer<'T>() + static member Function : System.Collections.Generic.IEqualityComparer<'T> = f let FastGenericEqualityComparerFromTable<'T> = FastGenericEqualityComparerTable<'T>.Function From fbf7cc05b35eb74b8c9d9cdd056c47dd1967f2d2 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Tue, 5 Jun 2018 17:37:55 +1000 Subject: [PATCH 02/31] Use the default equality comparer where applicable --- src/fsharp/FSharp.Core/prim-types.fs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 08e74cda169..418adde82b6 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -2132,12 +2132,23 @@ namespace Microsoft.FSharp.Core | ty when ty.Equals typeof -> box StringIEquality | _ -> null + let canUseDefaultEqualityComparer (ty:Type) = + // avoid any types that need special handling in GenericEqualityObj + true + && ty.IsSealed // covers enum and value types + && not (typeof.IsAssignableFrom ty) + && not ty.IsArray + [] type FastGenericEqualityComparerTable<'T>() = static let f : System.Collections.Generic.IEqualityComparer<'T> = - match tryGetFastGenericEqualityComparerTable typeof<'T> with + let ty = typeof<'T> + match tryGetFastGenericEqualityComparerTable ty with | :? System.Collections.Generic.IEqualityComparer<'T> as comp -> comp - | _ -> MakeGenericEqualityComparer<'T>() + | _ -> + if canUseDefaultEqualityComparer ty + then unboxPrim (box System.Collections.Generic.EqualityComparer<'T>.Default) + else MakeGenericEqualityComparer<'T>() static member Function : System.Collections.Generic.IEqualityComparer<'T> = f From 434559028922ad22ddc85eb0eee819edaf697676 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Tue, 5 Jun 2018 19:29:58 +1000 Subject: [PATCH 03/31] Avoid calls to GenericEqualityObj for known types --- src/fsharp/FSharp.Core/prim-types.fs | 56 +++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 418adde82b6..0e70a304037 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1532,12 +1532,63 @@ namespace Microsoft.FSharp.Core override iec.Equals(x:obj,y:obj) = GenericEqualityObj true iec (x,y) // ER Semantics override iec.GetHashCode(x:obj) = raise (InvalidOperationException (SR.GetString(SR.notUsedForHashing))) } + type IERorPER = interface end + type ER = inherit IERorPER + type PER = inherit IERorPER + + type GenericEqualityTCall<'T> = delegate of 'T * 'T -> bool + + let tryGetGenericEqualityTCall (er:bool) (ty:Type) : obj = + match er, ty with + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> System.String.Equals((# "" x : string #),(# "" y : string #)))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> System.Decimal.op_Equality((# "" x:decimal #), (# "" y:decimal #)))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall(fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | false, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | false, ty when ty.Equals typeof-> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> + if not (# "ceq" x x : bool #) && not (# "ceq" y y : bool #) then + true + else + (# "ceq" x y : bool #))) + | true, ty when ty.Equals typeof -> box (GenericEqualityTCall(fun x y -> + if not (# "ceq" x x : bool #) && not (# "ceq" y y : bool #) then + true + else + (# "ceq" x y : bool #))) + | _ -> null + + type GenericEqualityT<'T, 'ERorPER when 'ERorPER :> IERorPER> private () = + static let f : GenericEqualityTCall<'T> = + let er = + if typeof<'ERorPER>.Equals typeof then true + elif typeof<'ERorPER>.Equals typeof then false + else raise (Exception "logic error") + match tryGetGenericEqualityTCall er typeof<'T> with + | :? GenericEqualityTCall<'T> as call -> call + | _ -> + if er + then GenericEqualityTCall<'T>(fun x y -> GenericEqualityObj true fsEqualityComparerNoHashingER (box x, box y)) + else GenericEqualityTCall<'T>(fun x y -> GenericEqualityObj false fsEqualityComparerNoHashingPER (box x, box y)) + + static member Function = f + /// Implements generic equality between two values, with PER semantics for NaN (so equality on two NaN values returns false) // // The compiler optimizer is aware of this function (see use of generic_equality_per_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityIntrinsic (x : 'T) (y : 'T) : bool = - GenericEqualityObj false fsEqualityComparerNoHashingPER ((box x), (box y)) + GenericEqualityT<'T, PER>.Function.Invoke (x, y) /// Implements generic equality between two values, with ER semantics for NaN (so equality on two NaN values returns true) // @@ -1546,7 +1597,7 @@ namespace Microsoft.FSharp.Core // The compiler optimizer is aware of this function (see use of generic_equality_er_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityERIntrinsic (x : 'T) (y : 'T) : bool = - GenericEqualityObj true fsEqualityComparerNoHashingER ((box x), (box y)) + GenericEqualityT<'T, ER>.Function.Invoke (x, y) /// Implements generic equality between two values using "comp" for recursive calls. // @@ -2113,6 +2164,7 @@ namespace Microsoft.FSharp.Core let DecimalIEquality = MakeGenericEqualityComparer() let tryGetFastGenericEqualityComparerTable (ty:Type) = + // TODO: Remove the ones that don't have special handling and thus just used default match ty with | ty when ty.Equals typeof -> box BoolIEquality | ty when ty.Equals typeof -> box ByteIEquality From 02c028ae77debca6f135e7d35f624a3e452cf2e3 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Wed, 6 Jun 2018 15:54:12 +1000 Subject: [PATCH 04/31] Avoid boxing in the "standard" use of GenericEqualityWithComparerIntrinsic --- src/fsharp/FSharp.Core/prim-types.fs | 66 ++++++++++++++-------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 0e70a304037..ab5dc099a4a 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -771,6 +771,35 @@ namespace Microsoft.FSharp.Core let anyToStringShowingNull x = anyToString "null" x module HashCompare = + let defaultHashNodes = 18 + + /// The implementation of IEqualityComparer, using depth-limited for hashing and PER semantics for NaN equality. + type CountLimitedHasherPER(sz:int) = + [] + val mutable nodeCount : int + + member x.Fresh() = + if (System.Threading.Interlocked.CompareExchange(&(x.nodeCount), sz, 0) = 0) then + x + else + new CountLimitedHasherPER(sz) + + interface IEqualityComparer + + /// The implementation of IEqualityComparer, using unlimited depth for hashing and ER semantics for NaN equality. + type UnlimitedHasherER() = + interface IEqualityComparer + + /// The implementation of IEqualityComparer, using unlimited depth for hashing and PER semantics for NaN equality. + type UnlimitedHasherPER() = + interface IEqualityComparer + + + /// The unique object for unlimited depth for hashing and ER semantics for equality. + let fsEqualityComparerUnlimitedHashingER = UnlimitedHasherER() + + /// The unique object for unlimited depth for hashing and PER semantics for equality. + let fsEqualityComparerUnlimitedHashingPER = UnlimitedHasherPER() //------------------------------------------------------------------------- // LanguagePrimitives.HashCompare: Physical Equality @@ -1605,7 +1634,10 @@ namespace Microsoft.FSharp.Core // and devirtualizes calls to it based on "T", and under the assumption that "comp" // is either fsEqualityComparerNoHashingER or fsEqualityComparerNoHashingPER. let GenericEqualityWithComparerIntrinsic (comp : System.Collections.IEqualityComparer) (x : 'T) (y : 'T) : bool = - comp.Equals((box x),(box y)) + if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then + GenericEqualityT<'T, PER>.Function.Invoke (x, y) + else + comp.Equals (box x, box y) /// Implements generic equality between two values, with ER semantics for NaN (so equality on two NaN values returns true) @@ -1696,38 +1728,6 @@ namespace Microsoft.FSharp.Core //------------------------------------------------------------------------- // LanguagePrimitives.HashCompare: HASHING. //------------------------------------------------------------------------- - - - - let defaultHashNodes = 18 - - /// The implementation of IEqualityComparer, using depth-limited for hashing and PER semantics for NaN equality. - type CountLimitedHasherPER(sz:int) = - [] - val mutable nodeCount : int - - member x.Fresh() = - if (System.Threading.Interlocked.CompareExchange(&(x.nodeCount), sz, 0) = 0) then - x - else - new CountLimitedHasherPER(sz) - - interface IEqualityComparer - - /// The implementation of IEqualityComparer, using unlimited depth for hashing and ER semantics for NaN equality. - type UnlimitedHasherER() = - interface IEqualityComparer - - /// The implementation of IEqualityComparer, using unlimited depth for hashing and PER semantics for NaN equality. - type UnlimitedHasherPER() = - interface IEqualityComparer - - - /// The unique object for unlimited depth for hashing and ER semantics for equality. - let fsEqualityComparerUnlimitedHashingER = UnlimitedHasherER() - - /// The unique object for unlimited depth for hashing and PER semantics for equality. - let fsEqualityComparerUnlimitedHashingPER = UnlimitedHasherPER() let inline HashCombine nr x y = (x <<< 1) + y + 631 * nr From 0e0c69b66c872b6f38e890a7c2269b85ebb3e6ca Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Wed, 6 Jun 2018 16:21:18 +1000 Subject: [PATCH 05/31] Added method to avoid tail calls --- src/fsharp/FSharp.Core/prim-types.fs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index ab5dc099a4a..d275ef1f10a 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1612,12 +1612,16 @@ namespace Microsoft.FSharp.Core static member Function = f + // The FSharp compiler will not insert a tail call when this is used (this might be "fixed" + // in a future release) + let inline avoid_tail_call f = match f () with true -> true | _ -> false + /// Implements generic equality between two values, with PER semantics for NaN (so equality on two NaN values returns false) // // The compiler optimizer is aware of this function (see use of generic_equality_per_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityIntrinsic (x : 'T) (y : 'T) : bool = - GenericEqualityT<'T, PER>.Function.Invoke (x, y) + avoid_tail_call (fun () -> GenericEqualityT<'T, PER>.Function.Invoke (x, y)) /// Implements generic equality between two values, with ER semantics for NaN (so equality on two NaN values returns true) // @@ -1626,7 +1630,7 @@ namespace Microsoft.FSharp.Core // The compiler optimizer is aware of this function (see use of generic_equality_er_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityERIntrinsic (x : 'T) (y : 'T) : bool = - GenericEqualityT<'T, ER>.Function.Invoke (x, y) + avoid_tail_call (fun () -> GenericEqualityT<'T, ER>.Function.Invoke (x, y)) /// Implements generic equality between two values using "comp" for recursive calls. // @@ -1635,7 +1639,7 @@ namespace Microsoft.FSharp.Core // is either fsEqualityComparerNoHashingER or fsEqualityComparerNoHashingPER. let GenericEqualityWithComparerIntrinsic (comp : System.Collections.IEqualityComparer) (x : 'T) (y : 'T) : bool = if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then - GenericEqualityT<'T, PER>.Function.Invoke (x, y) + avoid_tail_call (fun () -> GenericEqualityT<'T, PER>.Function.Invoke (x, y)) else comp.Equals (box x, box y) From 61f79c66ec65e59211d51223c86551f591941646 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Wed, 6 Jun 2018 17:03:57 +1000 Subject: [PATCH 06/31] Implemented hashing --- src/fsharp/FSharp.Core/prim-types.fs | 66 ++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index d275ef1f10a..7af39de7969 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1642,7 +1642,6 @@ namespace Microsoft.FSharp.Core avoid_tail_call (fun () -> GenericEqualityT<'T, PER>.Function.Invoke (x, y)) else comp.Equals (box x, box y) - /// Implements generic equality between two values, with ER semantics for NaN (so equality on two NaN values returns true) // @@ -1843,11 +1842,54 @@ namespace Microsoft.FSharp.Core override iec.Equals(x:obj,y:obj) = GenericEqualityObj false iec (x,y) override iec.GetHashCode(x:obj) = GenericHashParamObj iec x + /// Direct call to GetHashCode on the string type + let inline HashString (s:string) = + match s with + | null -> 0 + | _ -> (# "call instance int32 [mscorlib]System.String::GetHashCode()" s : int #) + + // from mscorlib v4.0.30319 + let inline HashChar (x:char) = (# "or" (# "shl" x 16 : int #) x : int #) + let inline HashSByte (x:sbyte) = (# "xor" (# "shl" x 8 : int #) x : int #) + let inline HashInt16 (x:int16) = (# "or" (# "conv.u2" x : int #) (# "shl" x 16 : int #) : int #) + let inline HashInt64 (x:int64) = (# "xor" (# "conv.i4" x : int #) (# "conv.i4" (# "shr" x 32 : int #) : int #) : int #) + let inline HashUInt64 (x:uint64) = (# "xor" (# "conv.i4" x : int #) (# "conv.i4" (# "shr.un" x 32 : int #) : int #) : int #) + let inline HashIntPtr (x:nativeint) = (# "conv.i4" (# "conv.u8" x : uint64 #) : int #) + let inline HashUIntPtr (x:unativeint) = (# "and" (# "conv.i4" (# "conv.u8" x : uint64 #) : int #) 0x7fffffff : int #) + + type GenericHashTCall<'T> = delegate of 'T -> int + + let tryGetGenericHashTCall (ty:Type) : obj = + match ty with + | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) + | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) + | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) + | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) + | ty when ty.Equals typeof -> box (GenericHashTCall HashChar) + | ty when ty.Equals typeof -> box (GenericHashTCall HashSByte) + | ty when ty.Equals typeof -> box (GenericHashTCall HashInt16) + | ty when ty.Equals typeof -> box (GenericHashTCall HashInt64) + | ty when ty.Equals typeof -> box (GenericHashTCall HashUInt64) + | ty when ty.Equals typeof -> box (GenericHashTCall HashIntPtr) + | ty when ty.Equals typeof -> box (GenericHashTCallHashUIntPtr) + | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) + | ty when ty.Equals typeof -> box (GenericHashTCall HashString) + | _ -> null + + type GenericHashT<'T> private () = + static let f : GenericHashTCall<'T> = + match tryGetGenericHashTCall typeof<'T> with + | :? GenericHashTCall<'T> as call -> call + | _ -> GenericHashTCall<'T>(fun x -> GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x)) + + static member Function = f + /// Intrinsic for calls to depth-unlimited structural hashing that were not optimized by static conditionals. // // NOTE: The compiler optimizer is aware of this function (see uses of generic_hash_inner_vref in opt.fs) // and devirtualizes calls to it based on type "T". - let GenericHashIntrinsic input = GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box input) + let GenericHashIntrinsic input = + GenericHashT<'T>.Function.Invoke input /// Intrinsic for calls to depth-limited structural hashing that were not optimized by static conditionals. let LimitedGenericHashIntrinsic limit input = GenericHashParamObj (CountLimitedHasherPER(limit)) (box input) @@ -1860,23 +1902,11 @@ namespace Microsoft.FSharp.Core // NOTE: The compiler optimizer is aware of this function (see uses of generic_hash_withc_inner_vref in opt.fs) // and devirtualizes calls to it based on type "T". let GenericHashWithComparerIntrinsic<'T> (comp : System.Collections.IEqualityComparer) (input : 'T) : int = - GenericHashParamObj comp (box input) + if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then + GenericHashT<'T>.Function.Invoke input + else + GenericHashParamObj comp (box input) - /// Direct call to GetHashCode on the string type - let inline HashString (s:string) = - match s with - | null -> 0 - | _ -> (# "call instance int32 [mscorlib]System.String::GetHashCode()" s : int #) - - // from mscorlib v4.0.30319 - let inline HashChar (x:char) = (# "or" (# "shl" x 16 : int #) x : int #) - let inline HashSByte (x:sbyte) = (# "xor" (# "shl" x 8 : int #) x : int #) - let inline HashInt16 (x:int16) = (# "or" (# "conv.u2" x : int #) (# "shl" x 16 : int #) : int #) - let inline HashInt64 (x:int64) = (# "xor" (# "conv.i4" x : int #) (# "conv.i4" (# "shr" x 32 : int #) : int #) : int #) - let inline HashUInt64 (x:uint64) = (# "xor" (# "conv.i4" x : int #) (# "conv.i4" (# "shr.un" x 32 : int #) : int #) : int #) - let inline HashIntPtr (x:nativeint) = (# "conv.i4" (# "conv.u8" x : uint64 #) : int #) - let inline HashUIntPtr (x:unativeint) = (# "and" (# "conv.i4" (# "conv.u8" x : uint64 #) : int #) 0x7fffffff : int #) - /// Core entry into structural hashing for either limited or unlimited hashing. // // "iec" is assumed to be either fsEqualityComparerUnlimitedHashingER, fsEqualityComparerUnlimitedHashingPER or From 11a95c2dba24983de14210d690c33433eeb5a9bd Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Wed, 6 Jun 2018 17:24:41 +1000 Subject: [PATCH 07/31] Additional use of EqualityComparer.Default --- src/fsharp/FSharp.Core/prim-types.fs | 39 ++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 7af39de7969..57379a4e536 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1565,6 +1565,18 @@ namespace Microsoft.FSharp.Core type ER = inherit IERorPER type PER = inherit IERorPER + let canUseDefaultEqualityComparer (ty:Type) = + // avoid any types that need special handling in GenericEqualityObj + true + && ty.IsSealed // covers enum and value types + && not (typeof.IsAssignableFrom ty) + && not ty.IsArray + + // The FSharp compiler will not insert a tail call when this is used (this might be "fixed" + // in a future release) + let inline avoid_tail_call f = + match f () with true -> true | _ -> false + type GenericEqualityTCall<'T> = delegate of 'T * 'T -> bool let tryGetGenericEqualityTCall (er:bool) (ty:Type) : obj = @@ -1605,17 +1617,16 @@ namespace Microsoft.FSharp.Core else raise (Exception "logic error") match tryGetGenericEqualityTCall er typeof<'T> with | :? GenericEqualityTCall<'T> as call -> call - | _ -> - if er - then GenericEqualityTCall<'T>(fun x y -> GenericEqualityObj true fsEqualityComparerNoHashingER (box x, box y)) - else GenericEqualityTCall<'T>(fun x y -> GenericEqualityObj false fsEqualityComparerNoHashingPER (box x, box y)) + | _ when canUseDefaultEqualityComparer typeof<'T> -> + let comparer = System.Collections.Generic.EqualityComparer<'T>.Default + GenericEqualityTCall<'T>(fun x y -> avoid_tail_call (fun () -> comparer.Equals (x, y))) + | _ when er -> + GenericEqualityTCall<'T>(fun x y -> GenericEqualityObj true fsEqualityComparerNoHashingER (box x, box y)) + | _ -> + GenericEqualityTCall<'T>(fun x y -> GenericEqualityObj false fsEqualityComparerNoHashingPER (box x, box y)) static member Function = f - // The FSharp compiler will not insert a tail call when this is used (this might be "fixed" - // in a future release) - let inline avoid_tail_call f = match f () with true -> true | _ -> false - /// Implements generic equality between two values, with PER semantics for NaN (so equality on two NaN values returns false) // // The compiler optimizer is aware of this function (see use of generic_equality_per_inner_vref in opt.fs) @@ -1880,6 +1891,9 @@ namespace Microsoft.FSharp.Core static let f : GenericHashTCall<'T> = match tryGetGenericHashTCall typeof<'T> with | :? GenericHashTCall<'T> as call -> call + | _ when canUseDefaultEqualityComparer typeof<'T> -> + let comparer = System.Collections.Generic.EqualityComparer<'T>.Default + GenericHashTCall<'T> comparer.GetHashCode | _ -> GenericHashTCall<'T>(fun x -> GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x)) static member Function = f @@ -2218,13 +2232,6 @@ namespace Microsoft.FSharp.Core | ty when ty.Equals typeof -> box StringIEquality | _ -> null - let canUseDefaultEqualityComparer (ty:Type) = - // avoid any types that need special handling in GenericEqualityObj - true - && ty.IsSealed // covers enum and value types - && not (typeof.IsAssignableFrom ty) - && not ty.IsArray - [] type FastGenericEqualityComparerTable<'T>() = static let f : System.Collections.Generic.IEqualityComparer<'T> = @@ -2232,7 +2239,7 @@ namespace Microsoft.FSharp.Core match tryGetFastGenericEqualityComparerTable ty with | :? System.Collections.Generic.IEqualityComparer<'T> as comp -> comp | _ -> - if canUseDefaultEqualityComparer ty + if HashCompare.canUseDefaultEqualityComparer ty then unboxPrim (box System.Collections.Generic.EqualityComparer<'T>.Default) else MakeGenericEqualityComparer<'T>() From 09fcb461539f3784f7d57e2c1d4ad50b7f24920a Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Wed, 6 Jun 2018 17:46:52 +1000 Subject: [PATCH 08/31] Consistent naming --- src/fsharp/FSharp.Core/prim-types.fs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 57379a4e536..6c9e5f6a859 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1574,8 +1574,9 @@ namespace Microsoft.FSharp.Core // The FSharp compiler will not insert a tail call when this is used (this might be "fixed" // in a future release) - let inline avoid_tail_call f = - match f () with true -> true | _ -> false + let inline avoid_tail_call_bool f = match f () with true -> true | _ -> false + let inline avoid_tail_call_int f = 0 + f () + type GenericEqualityTCall<'T> = delegate of 'T * 'T -> bool @@ -1619,7 +1620,7 @@ namespace Microsoft.FSharp.Core | :? GenericEqualityTCall<'T> as call -> call | _ when canUseDefaultEqualityComparer typeof<'T> -> let comparer = System.Collections.Generic.EqualityComparer<'T>.Default - GenericEqualityTCall<'T>(fun x y -> avoid_tail_call (fun () -> comparer.Equals (x, y))) + GenericEqualityTCall<'T>(fun x y -> avoid_tail_call_bool (fun () -> comparer.Equals (x, y))) | _ when er -> GenericEqualityTCall<'T>(fun x y -> GenericEqualityObj true fsEqualityComparerNoHashingER (box x, box y)) | _ -> @@ -1632,7 +1633,7 @@ namespace Microsoft.FSharp.Core // The compiler optimizer is aware of this function (see use of generic_equality_per_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityIntrinsic (x : 'T) (y : 'T) : bool = - avoid_tail_call (fun () -> GenericEqualityT<'T, PER>.Function.Invoke (x, y)) + avoid_tail_call_bool (fun () -> GenericEqualityT<'T, PER>.Function.Invoke (x, y)) /// Implements generic equality between two values, with ER semantics for NaN (so equality on two NaN values returns true) // @@ -1641,7 +1642,7 @@ namespace Microsoft.FSharp.Core // The compiler optimizer is aware of this function (see use of generic_equality_er_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityERIntrinsic (x : 'T) (y : 'T) : bool = - avoid_tail_call (fun () -> GenericEqualityT<'T, ER>.Function.Invoke (x, y)) + avoid_tail_call_bool (fun () -> GenericEqualityT<'T, ER>.Function.Invoke (x, y)) /// Implements generic equality between two values using "comp" for recursive calls. // @@ -1650,7 +1651,7 @@ namespace Microsoft.FSharp.Core // is either fsEqualityComparerNoHashingER or fsEqualityComparerNoHashingPER. let GenericEqualityWithComparerIntrinsic (comp : System.Collections.IEqualityComparer) (x : 'T) (y : 'T) : bool = if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then - avoid_tail_call (fun () -> GenericEqualityT<'T, PER>.Function.Invoke (x, y)) + avoid_tail_call_bool (fun () -> GenericEqualityT<'T, PER>.Function.Invoke (x, y)) else comp.Equals (box x, box y) @@ -1893,8 +1894,9 @@ namespace Microsoft.FSharp.Core | :? GenericHashTCall<'T> as call -> call | _ when canUseDefaultEqualityComparer typeof<'T> -> let comparer = System.Collections.Generic.EqualityComparer<'T>.Default - GenericHashTCall<'T> comparer.GetHashCode - | _ -> GenericHashTCall<'T>(fun x -> GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x)) + GenericHashTCall<'T> (fun x -> avoid_tail_call_int (fun () -> comparer.GetHashCode x)) + | _ -> + GenericHashTCall<'T> (fun x -> GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x)) static member Function = f @@ -1903,7 +1905,7 @@ namespace Microsoft.FSharp.Core // NOTE: The compiler optimizer is aware of this function (see uses of generic_hash_inner_vref in opt.fs) // and devirtualizes calls to it based on type "T". let GenericHashIntrinsic input = - GenericHashT<'T>.Function.Invoke input + avoid_tail_call_int (fun () -> GenericHashT<'T>.Function.Invoke input) /// Intrinsic for calls to depth-limited structural hashing that were not optimized by static conditionals. let LimitedGenericHashIntrinsic limit input = GenericHashParamObj (CountLimitedHasherPER(limit)) (box input) @@ -1917,7 +1919,7 @@ namespace Microsoft.FSharp.Core // and devirtualizes calls to it based on type "T". let GenericHashWithComparerIntrinsic<'T> (comp : System.Collections.IEqualityComparer) (input : 'T) : int = if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then - GenericHashT<'T>.Function.Invoke input + avoid_tail_call_int (fun () -> GenericHashT<'T>.Function.Invoke input) else GenericHashParamObj comp (box input) From b83f0b9f393509dcacb62b3b3f12f79c80ec1cf6 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Thu, 7 Jun 2018 19:33:43 +1000 Subject: [PATCH 09/31] Fixed up by mix up my ERs with my PERs! --- src/fsharp/FSharp.Core/prim-types.fs | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 6c9e5f6a859..3b3a59ac3c6 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1582,28 +1582,28 @@ namespace Microsoft.FSharp.Core let tryGetGenericEqualityTCall (er:bool) (ty:Type) : obj = match er, ty with - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> System.String.Equals((# "" x : string #),(# "" y : string #)))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> System.Decimal.op_Equality((# "" x:decimal #), (# "" y:decimal #)))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall(fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | false, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | false, ty when ty.Equals typeof-> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> System.String.Equals((# "" x : string #),(# "" y : string #)))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> System.Decimal.op_Equality((# "" x:decimal #), (# "" y:decimal #)))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | false, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> if not (# "ceq" x x : bool #) && not (# "ceq" y y : bool #) then true else (# "ceq" x y : bool #))) - | true, ty when ty.Equals typeof -> box (GenericEqualityTCall(fun x y -> + | false, ty when ty.Equals typeof -> box (GenericEqualityTCall(fun x y -> if not (# "ceq" x x : bool #) && not (# "ceq" y y : bool #) then true else From abdc424f9c5ceb6ecaa6f05d235a6e6517e968d9 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Thu, 7 Jun 2018 19:36:27 +1000 Subject: [PATCH 10/31] Apply De Morgan's law to make it a bit cleaner --- src/fsharp/FSharp.Core/prim-types.fs | 44 ++++++++++++---------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 3b3a59ac3c6..7706b6a9d3c 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1582,32 +1582,24 @@ namespace Microsoft.FSharp.Core let tryGetGenericEqualityTCall (er:bool) (ty:Type) : obj = match er, ty with - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> System.String.Equals((# "" x : string #),(# "" y : string #)))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> System.Decimal.op_Equality((# "" x:decimal #), (# "" y:decimal #)))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | false, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> - if not (# "ceq" x x : bool #) && not (# "ceq" y y : bool #) then - true - else - (# "ceq" x y : bool #))) - | false, ty when ty.Equals typeof -> box (GenericEqualityTCall(fun x y -> - if not (# "ceq" x x : bool #) && not (# "ceq" y y : bool #) then - true - else - (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> System.String.Equals((# "" x : string #),(# "" y : string #)))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> System.Decimal.op_Equality((# "" x:decimal #), (# "" y:decimal #)))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | false, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))))) + | false, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))))) | _ -> null type GenericEqualityT<'T, 'ERorPER when 'ERorPER :> IERorPER> private () = From 3808bc52a15ca0323135b875341cd079f16350bf Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Fri, 8 Jun 2018 16:50:18 +1000 Subject: [PATCH 11/31] Argh, got the PER and ER mixed up even when I tried to fix. Now good (hopefully!) --- src/fsharp/FSharp.Core/prim-types.fs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 7706b6a9d3c..f95fc99afe9 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1577,7 +1577,6 @@ namespace Microsoft.FSharp.Core let inline avoid_tail_call_bool f = match f () with true -> true | _ -> false let inline avoid_tail_call_int f = 0 + f () - type GenericEqualityTCall<'T> = delegate of 'T * 'T -> bool let tryGetGenericEqualityTCall (er:bool) (ty:Type) : obj = @@ -1596,10 +1595,10 @@ namespace Microsoft.FSharp.Core | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | false, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))))) - | false, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))))) + | false, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | false, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) + | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))))) + | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))))) | _ -> null type GenericEqualityT<'T, 'ERorPER when 'ERorPER :> IERorPER> private () = @@ -1642,8 +1641,10 @@ namespace Microsoft.FSharp.Core // and devirtualizes calls to it based on "T", and under the assumption that "comp" // is either fsEqualityComparerNoHashingER or fsEqualityComparerNoHashingPER. let GenericEqualityWithComparerIntrinsic (comp : System.Collections.IEqualityComparer) (x : 'T) (y : 'T) : bool = - if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then + if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) || obj.ReferenceEquals (comp, fsEqualityComparerNoHashingPER) then avoid_tail_call_bool (fun () -> GenericEqualityT<'T, PER>.Function.Invoke (x, y)) + elif obj.ReferenceEquals (comp, fsEqualityComparerNoHashingER) then + avoid_tail_call_bool (fun () -> GenericEqualityT<'T, ER>.Function.Invoke (x, y)) else comp.Equals (box x, box y) From 0e70bda10f0b1e165a979c1e1352f07f7ded6816 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Fri, 8 Jun 2018 17:11:34 +1000 Subject: [PATCH 12/31] Removed custom delegates by using System.Func --- src/fsharp/FSharp.Core/prim-types.fs | 102 ++++++++++++++------------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index f95fc99afe9..d0bc1a5920d 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1577,7 +1577,7 @@ namespace Microsoft.FSharp.Core let inline avoid_tail_call_bool f = match f () with true -> true | _ -> false let inline avoid_tail_call_int f = 0 + f () - type GenericEqualityTCall<'T> = delegate of 'T * 'T -> bool + type GenericEqualityTCall<'T> = Func<'T, 'T, bool> let tryGetGenericEqualityTCall (er:bool) (ty:Type) : obj = match er, ty with @@ -1601,22 +1601,24 @@ namespace Microsoft.FSharp.Core | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))))) | _ -> null - type GenericEqualityT<'T, 'ERorPER when 'ERorPER :> IERorPER> private () = - static let f : GenericEqualityTCall<'T> = - let er = - if typeof<'ERorPER>.Equals typeof then true - elif typeof<'ERorPER>.Equals typeof then false - else raise (Exception "logic error") - match tryGetGenericEqualityTCall er typeof<'T> with - | :? GenericEqualityTCall<'T> as call -> call - | _ when canUseDefaultEqualityComparer typeof<'T> -> - let comparer = System.Collections.Generic.EqualityComparer<'T>.Default - GenericEqualityTCall<'T>(fun x y -> avoid_tail_call_bool (fun () -> comparer.Equals (x, y))) - | _ when er -> - GenericEqualityTCall<'T>(fun x y -> GenericEqualityObj true fsEqualityComparerNoHashingER (box x, box y)) - | _ -> - GenericEqualityTCall<'T>(fun x y -> GenericEqualityObj false fsEqualityComparerNoHashingPER (box x, box y)) + let getGenericEquality<'T, 'ERorPER when 'ERorPER :> IERorPER> () = + let er = + if typeof<'ERorPER>.Equals typeof then true + elif typeof<'ERorPER>.Equals typeof then false + else raise (Exception "logic error") + + match tryGetGenericEqualityTCall er typeof<'T> with + | :? GenericEqualityTCall<'T> as call -> call + | _ when canUseDefaultEqualityComparer typeof<'T> -> + let comparer = System.Collections.Generic.EqualityComparer<'T>.Default + GenericEqualityTCall<'T> (fun x y -> avoid_tail_call_bool (fun () -> comparer.Equals (x, y))) + | _ when er -> + GenericEqualityTCall<'T> (fun x y -> GenericEqualityObj true fsEqualityComparerNoHashingER (box x, box y)) + | _ -> + GenericEqualityTCall<'T> (fun x y -> GenericEqualityObj false fsEqualityComparerNoHashingPER (box x, box y)) + type GenericEqualityT<'T, 'ERorPER when 'ERorPER :> IERorPER> private () = + static let f = getGenericEquality<'T, 'ERorPER> () static member Function = f /// Implements generic equality between two values, with PER semantics for NaN (so equality on two NaN values returns false) @@ -1862,35 +1864,36 @@ namespace Microsoft.FSharp.Core let inline HashIntPtr (x:nativeint) = (# "conv.i4" (# "conv.u8" x : uint64 #) : int #) let inline HashUIntPtr (x:unativeint) = (# "and" (# "conv.i4" (# "conv.u8" x : uint64 #) : int #) 0x7fffffff : int #) - type GenericHashTCall<'T> = delegate of 'T -> int + type GenericHashTCall<'T> = Func<'T, int> let tryGetGenericHashTCall (ty:Type) : obj = match ty with - | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) - | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) - | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) - | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) - | ty when ty.Equals typeof -> box (GenericHashTCall HashChar) - | ty when ty.Equals typeof -> box (GenericHashTCall HashSByte) - | ty when ty.Equals typeof -> box (GenericHashTCall HashInt16) - | ty when ty.Equals typeof -> box (GenericHashTCall HashInt64) - | ty when ty.Equals typeof -> box (GenericHashTCall HashUInt64) - | ty when ty.Equals typeof -> box (GenericHashTCall HashIntPtr) - | ty when ty.Equals typeof -> box (GenericHashTCallHashUIntPtr) - | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) - | ty when ty.Equals typeof -> box (GenericHashTCall HashString) + | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) + | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) + | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) + | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) + | ty when ty.Equals typeof -> box (GenericHashTCall HashChar) + | ty when ty.Equals typeof -> box (GenericHashTCall HashSByte) + | ty when ty.Equals typeof -> box (GenericHashTCall HashInt16) + | ty when ty.Equals typeof -> box (GenericHashTCall HashInt64) + | ty when ty.Equals typeof -> box (GenericHashTCall HashUInt64) + | ty when ty.Equals typeof -> box (GenericHashTCall HashIntPtr) + | ty when ty.Equals typeof -> box (GenericHashTCall HashUIntPtr) + | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) + | ty when ty.Equals typeof -> box (GenericHashTCall HashString) | _ -> null - type GenericHashT<'T> private () = - static let f : GenericHashTCall<'T> = - match tryGetGenericHashTCall typeof<'T> with - | :? GenericHashTCall<'T> as call -> call - | _ when canUseDefaultEqualityComparer typeof<'T> -> - let comparer = System.Collections.Generic.EqualityComparer<'T>.Default - GenericHashTCall<'T> (fun x -> avoid_tail_call_int (fun () -> comparer.GetHashCode x)) - | _ -> - GenericHashTCall<'T> (fun x -> GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x)) + let getGenericHashTCall<'T> () = + match tryGetGenericHashTCall typeof<'T> with + | :? GenericHashTCall<'T> as call -> call + | _ when canUseDefaultEqualityComparer typeof<'T> -> + let comparer = System.Collections.Generic.EqualityComparer<'T>.Default + GenericHashTCall<'T> (fun x -> avoid_tail_call_int (fun () -> comparer.GetHashCode x)) + | _ -> + GenericHashTCall<'T> (fun x -> GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x)) + type GenericHashT<'T> private () = + static let f = getGenericHashTCall<'T> () static member Function = f /// Intrinsic for calls to depth-unlimited structural hashing that were not optimized by static conditionals. @@ -2227,18 +2230,19 @@ namespace Microsoft.FSharp.Core | ty when ty.Equals typeof -> box StringIEquality | _ -> null + let getFastGenericEqualityComparerTable<'T> () = + let ty = typeof<'T> + match tryGetFastGenericEqualityComparerTable ty with + | :? System.Collections.Generic.IEqualityComparer<'T> as comp -> comp + | _ when HashCompare.canUseDefaultEqualityComparer ty-> + unboxPrim (box System.Collections.Generic.EqualityComparer<'T>.Default) + | _ -> + MakeGenericEqualityComparer<'T>() + [] type FastGenericEqualityComparerTable<'T>() = - static let f : System.Collections.Generic.IEqualityComparer<'T> = - let ty = typeof<'T> - match tryGetFastGenericEqualityComparerTable ty with - | :? System.Collections.Generic.IEqualityComparer<'T> as comp -> comp - | _ -> - if HashCompare.canUseDefaultEqualityComparer ty - then unboxPrim (box System.Collections.Generic.EqualityComparer<'T>.Default) - else MakeGenericEqualityComparer<'T>() - - static member Function : System.Collections.Generic.IEqualityComparer<'T> = f + static let f = getFastGenericEqualityComparerTable<'T> () + static member Function = f let FastGenericEqualityComparerFromTable<'T> = FastGenericEqualityComparerTable<'T>.Function From ca285aa274a815051fc57ff99d685b6c59914a8e Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Fri, 8 Jun 2018 19:18:03 +1000 Subject: [PATCH 13/31] Disallow optimization on Nullable types --- src/fsharp/FSharp.Core/prim-types.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index d0bc1a5920d..9a89450fced 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1571,6 +1571,7 @@ namespace Microsoft.FSharp.Core && ty.IsSealed // covers enum and value types && not (typeof.IsAssignableFrom ty) && not ty.IsArray + && not (ty.IsGenericType && ty.GetGenericTypeDefinition().Equals (typedefof>)) // The FSharp compiler will not insert a tail call when this is used (this might be "fixed" // in a future release) From efc150c2c71118c73e8ddfd2715468f4557ddbed Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Sat, 9 Jun 2018 13:22:10 +1000 Subject: [PATCH 14/31] More inclusive check for canUseDefaultEqualityComparer --- src/fsharp/FSharp.Core/prim-types.fs | 40 +++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 9a89450fced..c07f59dd6bd 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1566,12 +1566,40 @@ namespace Microsoft.FSharp.Core type PER = inherit IERorPER let canUseDefaultEqualityComparer (ty:Type) = - // avoid any types that need special handling in GenericEqualityObj - true - && ty.IsSealed // covers enum and value types - && not (typeof.IsAssignableFrom ty) - && not ty.IsArray - && not (ty.IsGenericType && ty.GetGenericTypeDefinition().Equals (typedefof>)) + let processed = System.Collections.Generic.HashSet () + + let rec recurse idx (types:array) = + if idx = types.Length then true + else + let ty = get types idx + if not (processed.Add ty) then + recurse (idx+1) types + else + let isValidGenericType ifNotType fullname = + if not (ty.IsGenericType && ty.GetGenericTypeDefinition().FullName.Equals fullname) + then ifNotType + else recurse 0 (ty.GetGenericArguments ()) + + // avoid any types that need special handling in GenericEqualityObj + true + && ty.IsSealed // covers enum and value types + && not ty.IsArray + && not (ty.Equals typeof) + && not (ty.Equals typeof) + && isValidGenericType true "System.Nullable`1" + && not (typeof.IsAssignableFrom ty + && not (false + || isValidGenericType false "System.ValueTuple`1" + || isValidGenericType false "System.ValueTuple`2" + || isValidGenericType false "System.ValueTuple`3" + || isValidGenericType false "System.ValueTuple`4" + || isValidGenericType false "System.ValueTuple`5" + || isValidGenericType false "System.ValueTuple`6" + || isValidGenericType false "System.ValueTuple`7" + || isValidGenericType false "System.ValueTuple`8")) + && recurse (idx+1) types + + recurse 0 [|ty|] // The FSharp compiler will not insert a tail call when this is used (this might be "fixed" // in a future release) From 5b5e0ffc1884c280ebf8838f5fcca7c8facae4d3 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Sat, 9 Jun 2018 16:28:52 +1000 Subject: [PATCH 15/31] Updated il output files --- .../GenericComparison/Compare07.il.bsl | 25 +++++++++---------- .../GenericComparison/Compare10.il.bsl | 25 +++++++++---------- .../GenericComparison/Equals06.il.bsl | 25 +++++++++---------- .../GenericComparison/Equals09.il.bsl | 23 ++++++++--------- .../GenericComparison/Hash09.il.bsl | 25 +++++++++---------- .../GenericComparison/Hash12.il.bsl | 23 ++++++++--------- 6 files changed, 70 insertions(+), 76 deletions(-) diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl index 70335b05d20..b4c8337b1c2 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Compare07 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Compare07 { - // Offset: 0x00000000 Length: 0x0000089A + // Offset: 0x00000000 Length: 0x0000089E } .mresource public FSharpOptimizationData.Compare07 { - // Offset: 0x000008A0 Length: 0x00000692 + // Offset: 0x000008A8 Length: 0x0000069A } .module Compare07.dll -// MVID: {59B18AEE-05DE-F88E-A745-0383EE8AB159} +// MVID: {5B1B6346-05DE-F88E-A745-038346631B5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x02BA0000 +// Image base: 0x02B60000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] !a V_4, [5] !a V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Compare07.fsx' + .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Compare07.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -633,7 +633,7 @@ instance bool Equals(object obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 22 (0x16) + // Code size 20 (0x14) .maxstack 4 .locals init ([0] class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1 V_0) .line 4,4 : 10,20 '' @@ -641,18 +641,17 @@ IL_0001: isinst class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1 IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0014 + IL_0008: brfalse.s IL_0012 .line 16707566,16707566 : 0,0 '' IL_000a: ldarg.0 IL_000b: ldloc.0 - IL_000c: tail. - IL_000e: callvirt instance bool class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::Equals(class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1) - IL_0013: ret + IL_000c: callvirt instance bool class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::Equals(class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1) + IL_0011: ret .line 16707566,16707566 : 0,0 '' - IL_0014: ldc.i4.0 - IL_0015: ret + IL_0012: ldc.i4.0 + IL_0013: ret } // end of method GenericKey`1::Equals .property instance int32 Tag() diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare10.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare10.il.bsl index 16bada503c5..55ccbcf4cf7 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare10.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare10.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Compare10 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Compare10 { - // Offset: 0x00000000 Length: 0x00000AA4 + // Offset: 0x00000000 Length: 0x00000AA8 } .mresource public FSharpOptimizationData.Compare10 { - // Offset: 0x00000AA8 Length: 0x0000058E + // Offset: 0x00000AB0 Length: 0x0000058E } .module Compare10.dll -// MVID: {59B18AEE-04BF-1753-A745-0383EE8AB159} +// MVID: {5B1B6346-04BF-1753-A745-038346631B5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x002E0000 +// Image base: 0x01030000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] int32 V_4, [5] int32 V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Compare10.fsx' + .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Compare10.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -1330,7 +1330,7 @@ instance bool Equals(object obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 22 (0x16) + // Code size 20 (0x14) .maxstack 4 .locals init ([0] class Compare10/CompareMicroPerfAndCodeGenerationTests/KeyWithInnerKeys V_0) .line 5,5 : 10,26 '' @@ -1338,18 +1338,17 @@ IL_0001: isinst Compare10/CompareMicroPerfAndCodeGenerationTests/KeyWithInnerKeys IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0014 + IL_0008: brfalse.s IL_0012 .line 16707566,16707566 : 0,0 '' IL_000a: ldarg.0 IL_000b: ldloc.0 - IL_000c: tail. - IL_000e: callvirt instance bool Compare10/CompareMicroPerfAndCodeGenerationTests/KeyWithInnerKeys::Equals(class Compare10/CompareMicroPerfAndCodeGenerationTests/KeyWithInnerKeys) - IL_0013: ret + IL_000c: callvirt instance bool Compare10/CompareMicroPerfAndCodeGenerationTests/KeyWithInnerKeys::Equals(class Compare10/CompareMicroPerfAndCodeGenerationTests/KeyWithInnerKeys) + IL_0011: ret .line 16707566,16707566 : 0,0 '' - IL_0014: ldc.i4.0 - IL_0015: ret + IL_0012: ldc.i4.0 + IL_0013: ret } // end of method KeyWithInnerKeys::Equals .property instance int32 Tag() diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl index 91247bff24d..4254bb088fe 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Equals06 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Equals06 { - // Offset: 0x00000000 Length: 0x00000896 + // Offset: 0x00000000 Length: 0x0000089A } .mresource public FSharpOptimizationData.Equals06 { - // Offset: 0x000008A0 Length: 0x0000068E + // Offset: 0x000008A0 Length: 0x00000696 } .module Equals06.dll -// MVID: {59B18AEE-0759-31EC-A745-0383EE8AB159} +// MVID: {5B1B6346-0759-31EC-A745-038346631B5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x01B90000 +// Image base: 0x02B30000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] !a V_4, [5] !a V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals06.fsx' + .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals06.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -633,7 +633,7 @@ instance bool Equals(object obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 22 (0x16) + // Code size 20 (0x14) .maxstack 4 .locals init ([0] class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1 V_0) .line 4,4 : 10,20 '' @@ -641,18 +641,17 @@ IL_0001: isinst class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1 IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0014 + IL_0008: brfalse.s IL_0012 .line 16707566,16707566 : 0,0 '' IL_000a: ldarg.0 IL_000b: ldloc.0 - IL_000c: tail. - IL_000e: callvirt instance bool class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::Equals(class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1) - IL_0013: ret + IL_000c: callvirt instance bool class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::Equals(class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1) + IL_0011: ret .line 16707566,16707566 : 0,0 '' - IL_0014: ldc.i4.0 - IL_0015: ret + IL_0012: ldc.i4.0 + IL_0013: ret } // end of method GenericKey`1::Equals .property instance int32 Tag() diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals09.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals09.il.bsl index 8985acaa02b..cde1562a76b 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals09.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals09.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Equals09 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Equals09 { - // Offset: 0x00000000 Length: 0x00000AA0 + // Offset: 0x00000000 Length: 0x00000AA4 } .mresource public FSharpOptimizationData.Equals09 { // Offset: 0x00000AA8 Length: 0x0000058B } .module Equals09.dll -// MVID: {59B18AEE-0759-46D9-A745-0383EE8AB159} +// MVID: {5B1B6346-0759-46D9-A745-038346631B5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x02720000 +// Image base: 0x02560000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] int32 V_4, [5] int32 V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals09.fsx' + .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals09.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -1330,7 +1330,7 @@ instance bool Equals(object obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 22 (0x16) + // Code size 20 (0x14) .maxstack 4 .locals init ([0] class Equals09/EqualsMicroPerfAndCodeGenerationTests/KeyWithInnerKeys V_0) .line 5,5 : 10,26 '' @@ -1338,18 +1338,17 @@ IL_0001: isinst Equals09/EqualsMicroPerfAndCodeGenerationTests/KeyWithInnerKeys IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0014 + IL_0008: brfalse.s IL_0012 .line 16707566,16707566 : 0,0 '' IL_000a: ldarg.0 IL_000b: ldloc.0 - IL_000c: tail. - IL_000e: callvirt instance bool Equals09/EqualsMicroPerfAndCodeGenerationTests/KeyWithInnerKeys::Equals(class Equals09/EqualsMicroPerfAndCodeGenerationTests/KeyWithInnerKeys) - IL_0013: ret + IL_000c: callvirt instance bool Equals09/EqualsMicroPerfAndCodeGenerationTests/KeyWithInnerKeys::Equals(class Equals09/EqualsMicroPerfAndCodeGenerationTests/KeyWithInnerKeys) + IL_0011: ret .line 16707566,16707566 : 0,0 '' - IL_0014: ldc.i4.0 - IL_0015: ret + IL_0012: ldc.i4.0 + IL_0013: ret } // end of method KeyWithInnerKeys::Equals .property instance int32 Tag() diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl index df7b115d207..4cf28a5ff57 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Hash09 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Hash09 { - // Offset: 0x00000000 Length: 0x0000088E + // Offset: 0x00000000 Length: 0x00000892 } .mresource public FSharpOptimizationData.Hash09 { - // Offset: 0x00000898 Length: 0x00000686 + // Offset: 0x00000898 Length: 0x0000068E } .module Hash09.dll -// MVID: {59B18AEE-9642-77DB-A745-0383EE8AB159} +// MVID: {5B1B6346-9642-77DB-A745-038346631B5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x00690000 +// Image base: 0x025D0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] !a V_4, [5] !a V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash09.fsx' + .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash09.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -633,7 +633,7 @@ instance bool Equals(object obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 22 (0x16) + // Code size 20 (0x14) .maxstack 4 .locals init ([0] class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1 V_0) .line 4,4 : 10,20 '' @@ -641,18 +641,17 @@ IL_0001: isinst class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1 IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0014 + IL_0008: brfalse.s IL_0012 .line 16707566,16707566 : 0,0 '' IL_000a: ldarg.0 IL_000b: ldloc.0 - IL_000c: tail. - IL_000e: callvirt instance bool class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::Equals(class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1) - IL_0013: ret + IL_000c: callvirt instance bool class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::Equals(class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1) + IL_0011: ret .line 16707566,16707566 : 0,0 '' - IL_0014: ldc.i4.0 - IL_0015: ret + IL_0012: ldc.i4.0 + IL_0013: ret } // end of method GenericKey`1::Equals .property instance int32 Tag() diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash12.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash12.il.bsl index 9c5bfb73cf3..8836ff15748 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash12.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash12.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Hash12 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Hash12 { - // Offset: 0x00000000 Length: 0x00000A98 + // Offset: 0x00000000 Length: 0x00000A9C } .mresource public FSharpOptimizationData.Hash12 { // Offset: 0x00000AA0 Length: 0x00000585 } .module Hash12.dll -// MVID: {59B18AEE-9661-796E-A745-0383EE8AB159} +// MVID: {5B1B6346-9661-796E-A745-038346631B5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x01080000 +// Image base: 0x02810000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] int32 V_4, [5] int32 V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash12.fsx' + .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash12.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -1330,7 +1330,7 @@ instance bool Equals(object obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 22 (0x16) + // Code size 20 (0x14) .maxstack 4 .locals init ([0] class Hash12/HashMicroPerfAndCodeGenerationTests/KeyWithInnerKeys V_0) .line 5,5 : 10,26 '' @@ -1338,18 +1338,17 @@ IL_0001: isinst Hash12/HashMicroPerfAndCodeGenerationTests/KeyWithInnerKeys IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0014 + IL_0008: brfalse.s IL_0012 .line 16707566,16707566 : 0,0 '' IL_000a: ldarg.0 IL_000b: ldloc.0 - IL_000c: tail. - IL_000e: callvirt instance bool Hash12/HashMicroPerfAndCodeGenerationTests/KeyWithInnerKeys::Equals(class Hash12/HashMicroPerfAndCodeGenerationTests/KeyWithInnerKeys) - IL_0013: ret + IL_000c: callvirt instance bool Hash12/HashMicroPerfAndCodeGenerationTests/KeyWithInnerKeys::Equals(class Hash12/HashMicroPerfAndCodeGenerationTests/KeyWithInnerKeys) + IL_0011: ret .line 16707566,16707566 : 0,0 '' - IL_0014: ldc.i4.0 - IL_0015: ret + IL_0012: ldc.i4.0 + IL_0013: ret } // end of method KeyWithInnerKeys::Equals .property instance int32 Tag() From 7d85ee6971da709a4171039d1f4576d407e14b76 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Mon, 11 Jun 2018 17:27:49 +1000 Subject: [PATCH 16/31] Removed IERorPER by splitting calling class into 2, and added helper functions --- src/fsharp/FSharp.Core/prim-types.fs | 35 ++++++++++++---------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index c07f59dd6bd..056d986f838 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1561,10 +1561,6 @@ namespace Microsoft.FSharp.Core override iec.Equals(x:obj,y:obj) = GenericEqualityObj true iec (x,y) // ER Semantics override iec.GetHashCode(x:obj) = raise (InvalidOperationException (SR.GetString(SR.notUsedForHashing))) } - type IERorPER = interface end - type ER = inherit IERorPER - type PER = inherit IERorPER - let canUseDefaultEqualityComparer (ty:Type) = let processed = System.Collections.Generic.HashSet () @@ -1630,12 +1626,7 @@ namespace Microsoft.FSharp.Core | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))))) | _ -> null - let getGenericEquality<'T, 'ERorPER when 'ERorPER :> IERorPER> () = - let er = - if typeof<'ERorPER>.Equals typeof then true - elif typeof<'ERorPER>.Equals typeof then false - else raise (Exception "logic error") - + let getGenericEquality<'T> er = match tryGetGenericEqualityTCall er typeof<'T> with | :? GenericEqualityTCall<'T> as call -> call | _ when canUseDefaultEqualityComparer typeof<'T> -> @@ -1646,16 +1637,20 @@ namespace Microsoft.FSharp.Core | _ -> GenericEqualityTCall<'T> (fun x y -> GenericEqualityObj false fsEqualityComparerNoHashingPER (box x, box y)) - type GenericEqualityT<'T, 'ERorPER when 'ERorPER :> IERorPER> private () = - static let f = getGenericEquality<'T, 'ERorPER> () - static member Function = f + type GenericEqualityT_ER<'T> private () = + static let f = getGenericEquality<'T> true + static member inline Equals (x, y) = f.Invoke (x, y) + + type GenericEqualityT_PER<'T> private () = + static let f = getGenericEquality<'T> false + static member inline Equals (x, y) = f.Invoke (x, y) /// Implements generic equality between two values, with PER semantics for NaN (so equality on two NaN values returns false) // // The compiler optimizer is aware of this function (see use of generic_equality_per_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityIntrinsic (x : 'T) (y : 'T) : bool = - avoid_tail_call_bool (fun () -> GenericEqualityT<'T, PER>.Function.Invoke (x, y)) + avoid_tail_call_bool (fun () -> GenericEqualityT_PER<'T>.Equals (x, y)) /// Implements generic equality between two values, with ER semantics for NaN (so equality on two NaN values returns true) // @@ -1664,7 +1659,7 @@ namespace Microsoft.FSharp.Core // The compiler optimizer is aware of this function (see use of generic_equality_er_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityERIntrinsic (x : 'T) (y : 'T) : bool = - avoid_tail_call_bool (fun () -> GenericEqualityT<'T, ER>.Function.Invoke (x, y)) + avoid_tail_call_bool (fun () -> GenericEqualityT_ER<'T>.Equals (x, y)) /// Implements generic equality between two values using "comp" for recursive calls. // @@ -1673,9 +1668,9 @@ namespace Microsoft.FSharp.Core // is either fsEqualityComparerNoHashingER or fsEqualityComparerNoHashingPER. let GenericEqualityWithComparerIntrinsic (comp : System.Collections.IEqualityComparer) (x : 'T) (y : 'T) : bool = if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) || obj.ReferenceEquals (comp, fsEqualityComparerNoHashingPER) then - avoid_tail_call_bool (fun () -> GenericEqualityT<'T, PER>.Function.Invoke (x, y)) + avoid_tail_call_bool (fun () -> GenericEqualityT_PER<'T>.Equals (x, y)) elif obj.ReferenceEquals (comp, fsEqualityComparerNoHashingER) then - avoid_tail_call_bool (fun () -> GenericEqualityT<'T, ER>.Function.Invoke (x, y)) + avoid_tail_call_bool (fun () -> GenericEqualityT_ER<'T>.Equals (x, y)) else comp.Equals (box x, box y) @@ -1923,14 +1918,14 @@ namespace Microsoft.FSharp.Core type GenericHashT<'T> private () = static let f = getGenericHashTCall<'T> () - static member Function = f + static member inline GetHashCode x = f.Invoke x /// Intrinsic for calls to depth-unlimited structural hashing that were not optimized by static conditionals. // // NOTE: The compiler optimizer is aware of this function (see uses of generic_hash_inner_vref in opt.fs) // and devirtualizes calls to it based on type "T". let GenericHashIntrinsic input = - avoid_tail_call_int (fun () -> GenericHashT<'T>.Function.Invoke input) + avoid_tail_call_int (fun () -> GenericHashT<'T>.GetHashCode input) /// Intrinsic for calls to depth-limited structural hashing that were not optimized by static conditionals. let LimitedGenericHashIntrinsic limit input = GenericHashParamObj (CountLimitedHasherPER(limit)) (box input) @@ -1944,7 +1939,7 @@ namespace Microsoft.FSharp.Core // and devirtualizes calls to it based on type "T". let GenericHashWithComparerIntrinsic<'T> (comp : System.Collections.IEqualityComparer) (input : 'T) : int = if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then - avoid_tail_call_int (fun () -> GenericHashT<'T>.Function.Invoke input) + avoid_tail_call_int (fun () -> GenericHashT<'T>.GetHashCode input) else GenericHashParamObj comp (box input) From fb51116096f09c13dc42b7d64a8c5a8566a70d91 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Wed, 13 Jun 2018 19:39:39 +1000 Subject: [PATCH 17/31] Changed it all to used EqualityComparer derived classes and restored tail calls --- src/fsharp/FSharp.Core/prim-types.fs | 472 +++++++++--------- .../GenericComparison/Compare07.il.bsl | 25 +- .../GenericComparison/Compare10.il.bsl | 25 +- .../GenericComparison/Equals06.il.bsl | 25 +- .../GenericComparison/Equals09.il.bsl | 23 +- .../GenericComparison/Hash09.il.bsl | 25 +- .../GenericComparison/Hash12.il.bsl | 23 +- 7 files changed, 313 insertions(+), 305 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 056d986f838..885ce8b8505 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -771,6 +771,9 @@ namespace Microsoft.FSharp.Core let anyToStringShowingNull x = anyToString "null" x module HashCompare = + //------------------------------------------------------------------------- + // LanguagePrimitives.HashCompare: HASHING. + //------------------------------------------------------------------------- let defaultHashNodes = 18 /// The implementation of IEqualityComparer, using depth-limited for hashing and PER semantics for NaN equality. @@ -800,7 +803,106 @@ namespace Microsoft.FSharp.Core /// The unique object for unlimited depth for hashing and PER semantics for equality. let fsEqualityComparerUnlimitedHashingPER = UnlimitedHasherPER() - + + let inline HashCombine nr x y = (x <<< 1) + y + 631 * nr + + let GenericHashObjArray (iec : System.Collections.IEqualityComparer) (x: obj[]) : int = + let len = x.Length + let mutable i = len - 1 + if i > defaultHashNodes then i <- defaultHashNodes // limit the hash + let mutable acc = 0 + while (i >= 0) do + // NOTE: GenericHash* call decreases nr + acc <- HashCombine i acc (iec.GetHashCode(x.GetValue(i))); + i <- i - 1 + acc + + // optimized case - byte arrays + let GenericHashByteArray (x: byte[]) : int = + let len = length x + let mutable i = len - 1 + if i > defaultHashNodes then i <- defaultHashNodes // limit the hash + let mutable acc = 0 + while (i >= 0) do + acc <- HashCombine i acc (intOfByte (get x i)); + i <- i - 1 + acc + + // optimized case - int arrays + let GenericHashInt32Array (x: int[]) : int = + let len = length x + let mutable i = len - 1 + if i > defaultHashNodes then i <- defaultHashNodes // limit the hash + let mutable acc = 0 + while (i >= 0) do + acc <- HashCombine i acc (get x i); + i <- i - 1 + acc + + // optimized case - int arrays + let GenericHashInt64Array (x: int64[]) : int = + let len = length x + let mutable i = len - 1 + if i > defaultHashNodes then i <- defaultHashNodes // limit the hash + let mutable acc = 0 + while (i >= 0) do + acc <- HashCombine i acc (int32 (get x i)); + i <- i - 1 + acc + + // special case - arrays do not by default have a decent structural hashing function + let GenericHashArbArray (iec : System.Collections.IEqualityComparer) (x: System.Array) : int = + match x.Rank with + | 1 -> + let b = x.GetLowerBound(0) + let len = x.Length + let mutable i = b + len - 1 + if i > b + defaultHashNodes then i <- b + defaultHashNodes // limit the hash + let mutable acc = 0 + while (i >= b) do + // NOTE: GenericHash* call decreases nr + acc <- HashCombine i acc (iec.GetHashCode(x.GetValue(i))); + i <- i - 1 + acc + | _ -> + HashCombine 10 (x.GetLength(0)) (x.GetLength(1)) + + // Core implementation of structural hashing, corresponds to pseudo-code in the + // F# Language spec. Searches for the IStructuralHash interface, otherwise uses GetHashCode(). + // Arrays are structurally hashed through a separate technique. + // + // "iec" is either fsEqualityComparerUnlimitedHashingER, fsEqualityComparerUnlimitedHashingPER or a CountLimitedHasherPER. + let rec GenericHashParamObj (iec : System.Collections.IEqualityComparer) (x: obj) : int = + match x with + | null -> 0 + | (:? System.Array as a) -> + match a with + | :? (obj[]) as oa -> GenericHashObjArray iec oa + | :? (byte[]) as ba -> GenericHashByteArray ba + | :? (int[]) as ba -> GenericHashInt32Array ba + | :? (int64[]) as ba -> GenericHashInt64Array ba + | _ -> GenericHashArbArray iec a + | :? IStructuralEquatable as a -> + a.GetHashCode(iec) + | _ -> + x.GetHashCode() + + /// Direct call to GetHashCode on the string type + let inline HashString (s:string) = + match s with + | null -> 0 + | _ -> (# "call instance int32 [mscorlib]System.String::GetHashCode()" s : int #) + + // from mscorlib v4.0.30319 + let inline HashChar (x:char) = (# "or" (# "shl" x 16 : int #) x : int #) + let inline HashSByte (x:sbyte) = (# "xor" (# "shl" x 8 : int #) x : int #) + let inline HashInt16 (x:int16) = (# "or" (# "conv.u2" x : int #) (# "shl" x 16 : int #) : int #) + let inline HashInt64 (x:int64) = (# "xor" (# "conv.i4" x : int #) (# "conv.i4" (# "shr" x 32 : int #) : int #) : int #) + let inline HashUInt64 (x:uint64) = (# "xor" (# "conv.i4" x : int #) (# "conv.i4" (# "shr.un" x 32 : int #) : int #) : int #) + let inline HashIntPtr (x:nativeint) = (# "conv.i4" (# "conv.u8" x : uint64 #) : int #) + let inline HashUIntPtr (x:unativeint) = (# "and" (# "conv.i4" (# "conv.u8" x : uint64 #) : int #) 0x7fffffff : int #) + + //------------------------------------------------------------------------- // LanguagePrimitives.HashCompare: Physical Equality //------------------------------------------------------------------------- @@ -1580,6 +1682,8 @@ namespace Microsoft.FSharp.Core true && ty.IsSealed // covers enum and value types && not ty.IsArray + && not (ty.Equals typeof) + && not (ty.Equals typeof) && not (ty.Equals typeof) && not (ty.Equals typeof) && isValidGenericType true "System.Nullable`1" @@ -1597,60 +1701,141 @@ namespace Microsoft.FSharp.Core recurse 0 [|ty|] - // The FSharp compiler will not insert a tail call when this is used (this might be "fixed" - // in a future release) - let inline avoid_tail_call_bool f = match f () with true -> true | _ -> false - let inline avoid_tail_call_int f = 0 + f () - - type GenericEqualityTCall<'T> = Func<'T, 'T, bool> - - let tryGetGenericEqualityTCall (er:bool) (ty:Type) : obj = + let tryGetFSharpEqualityComparer (er:bool) (ty:Type) : obj = match er, ty with - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> System.String.Equals((# "" x : string #),(# "" y : string #)))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> System.Decimal.op_Equality((# "" x:decimal #), (# "" y:decimal #)))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | _, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | false, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | false, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #))) - | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))))) - | true, ty when ty.Equals typeof -> box (GenericEqualityTCall (fun x y -> (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))))) + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = System.String.Equals (x, y) + member __.GetHashCode x = HashString x } + + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = System.Decimal.op_Equality (x, y) + member __.GetHashCode x = x.GetHashCode () } + + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = (# "" x : int #) } + + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = HashSByte x } + + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = HashInt16 x } + + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = (# "" x : int #) } + + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = HashInt64 x } + + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = (# "" x : int #) } + + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = (# "" x : int #) } + + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = (# "" x : int #) } + + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = HashUInt64 x } + + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = HashIntPtr x } + + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = HashUIntPtr x } + + | _, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = HashChar x } + + | false, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = x.GetHashCode () } + + | false, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = x.GetHashCode () } + + | true, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))) + member __.GetHashCode x = x.GetHashCode () } + + | true, ty when ty.Equals typeof -> box { + new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))) + member __.GetHashCode x = x.GetHashCode () } + | _ -> null + let genericFSharpEqualityComparer_ER<'T> () = { + new EqualityComparer<'T>() with + member __.Equals (x,y) = GenericEqualityObj true fsEqualityComparerUnlimitedHashingER (box x, box y) + member __.GetHashCode x = GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x) + } + + let genericFSharpEqualityComparer_PER<'T> () = { + new EqualityComparer<'T>() with + member __.Equals (x,y) = GenericEqualityObj false fsEqualityComparerUnlimitedHashingPER (box x, box y) + member __.GetHashCode x = GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x) + } + let getGenericEquality<'T> er = - match tryGetGenericEqualityTCall er typeof<'T> with - | :? GenericEqualityTCall<'T> as call -> call - | _ when canUseDefaultEqualityComparer typeof<'T> -> - let comparer = System.Collections.Generic.EqualityComparer<'T>.Default - GenericEqualityTCall<'T> (fun x y -> avoid_tail_call_bool (fun () -> comparer.Equals (x, y))) - | _ when er -> - GenericEqualityTCall<'T> (fun x y -> GenericEqualityObj true fsEqualityComparerNoHashingER (box x, box y)) - | _ -> - GenericEqualityTCall<'T> (fun x y -> GenericEqualityObj false fsEqualityComparerNoHashingPER (box x, box y)) - - type GenericEqualityT_ER<'T> private () = - static let f = getGenericEquality<'T> true - static member inline Equals (x, y) = f.Invoke (x, y) - - type GenericEqualityT_PER<'T> private () = - static let f = getGenericEquality<'T> false - static member inline Equals (x, y) = f.Invoke (x, y) + match tryGetFSharpEqualityComparer er typeof<'T> with + | :? EqualityComparer<'T> as call -> call + | _ when canUseDefaultEqualityComparer typeof<'T> -> EqualityComparer<'T>.Default + | _ when er -> genericFSharpEqualityComparer_ER<'T> () + | _ -> genericFSharpEqualityComparer_PER<'T> () + + type FSharpEqualityComparer_ER<'T> private () = + static let comparer = getGenericEquality<'T> true + + static member Comparer = comparer + + static member inline Equals (x, y) = comparer.Equals (x, y) + static member inline GetHashCode x = comparer.GetHashCode x + + type FSharpEqualityComparer_PER<'T> private () = + static let comparer = getGenericEquality<'T> false + + static member Comparer = comparer + + static member inline Equals (x, y) = comparer.Equals (x, y) + static member inline GetHashCode x = comparer.GetHashCode x /// Implements generic equality between two values, with PER semantics for NaN (so equality on two NaN values returns false) // // The compiler optimizer is aware of this function (see use of generic_equality_per_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityIntrinsic (x : 'T) (y : 'T) : bool = - avoid_tail_call_bool (fun () -> GenericEqualityT_PER<'T>.Equals (x, y)) + FSharpEqualityComparer_PER<'T>.Equals (x, y) /// Implements generic equality between two values, with ER semantics for NaN (so equality on two NaN values returns true) // @@ -1659,7 +1844,7 @@ namespace Microsoft.FSharp.Core // The compiler optimizer is aware of this function (see use of generic_equality_er_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityERIntrinsic (x : 'T) (y : 'T) : bool = - avoid_tail_call_bool (fun () -> GenericEqualityT_ER<'T>.Equals (x, y)) + FSharpEqualityComparer_ER<'T>.Equals (x, y) /// Implements generic equality between two values using "comp" for recursive calls. // @@ -1668,9 +1853,9 @@ namespace Microsoft.FSharp.Core // is either fsEqualityComparerNoHashingER or fsEqualityComparerNoHashingPER. let GenericEqualityWithComparerIntrinsic (comp : System.Collections.IEqualityComparer) (x : 'T) (y : 'T) : bool = if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) || obj.ReferenceEquals (comp, fsEqualityComparerNoHashingPER) then - avoid_tail_call_bool (fun () -> GenericEqualityT_PER<'T>.Equals (x, y)) + FSharpEqualityComparer_PER<'T>.Equals (x, y) elif obj.ReferenceEquals (comp, fsEqualityComparerNoHashingER) then - avoid_tail_call_bool (fun () -> GenericEqualityT_ER<'T>.Equals (x, y)) + FSharpEqualityComparer_ER<'T>.Equals (x, y) else comp.Equals (box x, box y) @@ -1758,95 +1943,6 @@ namespace Microsoft.FSharp.Core let inline GenericInequalityFast (x:'T) (y:'T) = (not(GenericEqualityFast x y) : bool) let inline GenericInequalityERFast (x:'T) (y:'T) = (not(GenericEqualityERFast x y) : bool) - - //------------------------------------------------------------------------- - // LanguagePrimitives.HashCompare: HASHING. - //------------------------------------------------------------------------- - - let inline HashCombine nr x y = (x <<< 1) + y + 631 * nr - - let GenericHashObjArray (iec : System.Collections.IEqualityComparer) (x: obj[]) : int = - let len = x.Length - let mutable i = len - 1 - if i > defaultHashNodes then i <- defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= 0) do - // NOTE: GenericHash* call decreases nr - acc <- HashCombine i acc (iec.GetHashCode(x.GetValue(i))); - i <- i - 1 - acc - - // optimized case - byte arrays - let GenericHashByteArray (x: byte[]) : int = - let len = length x - let mutable i = len - 1 - if i > defaultHashNodes then i <- defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= 0) do - acc <- HashCombine i acc (intOfByte (get x i)); - i <- i - 1 - acc - - // optimized case - int arrays - let GenericHashInt32Array (x: int[]) : int = - let len = length x - let mutable i = len - 1 - if i > defaultHashNodes then i <- defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= 0) do - acc <- HashCombine i acc (get x i); - i <- i - 1 - acc - - // optimized case - int arrays - let GenericHashInt64Array (x: int64[]) : int = - let len = length x - let mutable i = len - 1 - if i > defaultHashNodes then i <- defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= 0) do - acc <- HashCombine i acc (int32 (get x i)); - i <- i - 1 - acc - - // special case - arrays do not by default have a decent structural hashing function - let GenericHashArbArray (iec : System.Collections.IEqualityComparer) (x: System.Array) : int = - match x.Rank with - | 1 -> - let b = x.GetLowerBound(0) - let len = x.Length - let mutable i = b + len - 1 - if i > b + defaultHashNodes then i <- b + defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= b) do - // NOTE: GenericHash* call decreases nr - acc <- HashCombine i acc (iec.GetHashCode(x.GetValue(i))); - i <- i - 1 - acc - | _ -> - HashCombine 10 (x.GetLength(0)) (x.GetLength(1)) - - // Core implementation of structural hashing, corresponds to pseudo-code in the - // F# Language spec. Searches for the IStructuralHash interface, otherwise uses GetHashCode(). - // Arrays are structurally hashed through a separate technique. - // - // "iec" is either fsEqualityComparerUnlimitedHashingER, fsEqualityComparerUnlimitedHashingPER or a CountLimitedHasherPER. - let rec GenericHashParamObj (iec : System.Collections.IEqualityComparer) (x: obj) : int = - match x with - | null -> 0 - | (:? System.Array as a) -> - match a with - | :? (obj[]) as oa -> GenericHashObjArray iec oa - | :? (byte[]) as ba -> GenericHashByteArray ba - | :? (int[]) as ba -> GenericHashInt32Array ba - | :? (int64[]) as ba -> GenericHashInt64Array ba - | _ -> GenericHashArbArray iec a - | :? IStructuralEquatable as a -> - a.GetHashCode(iec) - | _ -> - x.GetHashCode() - - /// Fill in the implementation of CountLimitedHasherPER type CountLimitedHasherPER with @@ -1873,59 +1969,12 @@ namespace Microsoft.FSharp.Core override iec.Equals(x:obj,y:obj) = GenericEqualityObj false iec (x,y) override iec.GetHashCode(x:obj) = GenericHashParamObj iec x - /// Direct call to GetHashCode on the string type - let inline HashString (s:string) = - match s with - | null -> 0 - | _ -> (# "call instance int32 [mscorlib]System.String::GetHashCode()" s : int #) - - // from mscorlib v4.0.30319 - let inline HashChar (x:char) = (# "or" (# "shl" x 16 : int #) x : int #) - let inline HashSByte (x:sbyte) = (# "xor" (# "shl" x 8 : int #) x : int #) - let inline HashInt16 (x:int16) = (# "or" (# "conv.u2" x : int #) (# "shl" x 16 : int #) : int #) - let inline HashInt64 (x:int64) = (# "xor" (# "conv.i4" x : int #) (# "conv.i4" (# "shr" x 32 : int #) : int #) : int #) - let inline HashUInt64 (x:uint64) = (# "xor" (# "conv.i4" x : int #) (# "conv.i4" (# "shr.un" x 32 : int #) : int #) : int #) - let inline HashIntPtr (x:nativeint) = (# "conv.i4" (# "conv.u8" x : uint64 #) : int #) - let inline HashUIntPtr (x:unativeint) = (# "and" (# "conv.i4" (# "conv.u8" x : uint64 #) : int #) 0x7fffffff : int #) - - type GenericHashTCall<'T> = Func<'T, int> - - let tryGetGenericHashTCall (ty:Type) : obj = - match ty with - | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) - | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) - | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) - | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) - | ty when ty.Equals typeof -> box (GenericHashTCall HashChar) - | ty when ty.Equals typeof -> box (GenericHashTCall HashSByte) - | ty when ty.Equals typeof -> box (GenericHashTCall HashInt16) - | ty when ty.Equals typeof -> box (GenericHashTCall HashInt64) - | ty when ty.Equals typeof -> box (GenericHashTCall HashUInt64) - | ty when ty.Equals typeof -> box (GenericHashTCall HashIntPtr) - | ty when ty.Equals typeof -> box (GenericHashTCall HashUIntPtr) - | ty when ty.Equals typeof -> box (GenericHashTCall (fun x -> (# "" x : int #))) - | ty when ty.Equals typeof -> box (GenericHashTCall HashString) - | _ -> null - - let getGenericHashTCall<'T> () = - match tryGetGenericHashTCall typeof<'T> with - | :? GenericHashTCall<'T> as call -> call - | _ when canUseDefaultEqualityComparer typeof<'T> -> - let comparer = System.Collections.Generic.EqualityComparer<'T>.Default - GenericHashTCall<'T> (fun x -> avoid_tail_call_int (fun () -> comparer.GetHashCode x)) - | _ -> - GenericHashTCall<'T> (fun x -> GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x)) - - type GenericHashT<'T> private () = - static let f = getGenericHashTCall<'T> () - static member inline GetHashCode x = f.Invoke x - /// Intrinsic for calls to depth-unlimited structural hashing that were not optimized by static conditionals. // // NOTE: The compiler optimizer is aware of this function (see uses of generic_hash_inner_vref in opt.fs) // and devirtualizes calls to it based on type "T". let GenericHashIntrinsic input = - avoid_tail_call_int (fun () -> GenericHashT<'T>.GetHashCode input) + FSharpEqualityComparer_PER<'T>.GetHashCode input /// Intrinsic for calls to depth-limited structural hashing that were not optimized by static conditionals. let LimitedGenericHashIntrinsic limit input = GenericHashParamObj (CountLimitedHasherPER(limit)) (box input) @@ -1939,7 +1988,7 @@ namespace Microsoft.FSharp.Core // and devirtualizes calls to it based on type "T". let GenericHashWithComparerIntrinsic<'T> (comp : System.Collections.IEqualityComparer) (input : 'T) : int = if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then - avoid_tail_call_int (fun () -> GenericHashT<'T>.GetHashCode input) + FSharpEqualityComparer_PER<'T>.GetHashCode input else GenericHashParamObj comp (box input) @@ -2216,59 +2265,12 @@ namespace Microsoft.FSharp.Core member self.GetHashCode(x) = GenericLimitedHash limit x member self.Equals(x,y) = GenericEquality x y } - let BoolIEquality = MakeGenericEqualityComparer() - let CharIEquality = MakeGenericEqualityComparer() - let StringIEquality = MakeGenericEqualityComparer() - let SByteIEquality = MakeGenericEqualityComparer() - let Int16IEquality = MakeGenericEqualityComparer() - let Int32IEquality = MakeGenericEqualityComparer() - let Int64IEquality = MakeGenericEqualityComparer() - let IntPtrIEquality = MakeGenericEqualityComparer() - let ByteIEquality = MakeGenericEqualityComparer() - let UInt16IEquality = MakeGenericEqualityComparer() - let UInt32IEquality = MakeGenericEqualityComparer() - let UInt64IEquality = MakeGenericEqualityComparer() - let UIntPtrIEquality = MakeGenericEqualityComparer() - let FloatIEquality = MakeGenericEqualityComparer() - let Float32IEquality = MakeGenericEqualityComparer() - let DecimalIEquality = MakeGenericEqualityComparer() - - let tryGetFastGenericEqualityComparerTable (ty:Type) = - // TODO: Remove the ones that don't have special handling and thus just used default - match ty with - | ty when ty.Equals typeof -> box BoolIEquality - | ty when ty.Equals typeof -> box ByteIEquality - | ty when ty.Equals typeof -> box Int32IEquality - | ty when ty.Equals typeof -> box UInt32IEquality - | ty when ty.Equals typeof -> box CharIEquality - | ty when ty.Equals typeof -> box SByteIEquality - | ty when ty.Equals typeof -> box Int16IEquality - | ty when ty.Equals typeof -> box Int64IEquality - | ty when ty.Equals typeof -> box IntPtrIEquality - | ty when ty.Equals typeof -> box UInt16IEquality - | ty when ty.Equals typeof -> box UInt64IEquality - | ty when ty.Equals typeof -> box UIntPtrIEquality - | ty when ty.Equals typeof -> box FloatIEquality - | ty when ty.Equals typeof -> box Float32IEquality - | ty when ty.Equals typeof -> box DecimalIEquality - | ty when ty.Equals typeof -> box StringIEquality - | _ -> null - - let getFastGenericEqualityComparerTable<'T> () = - let ty = typeof<'T> - match tryGetFastGenericEqualityComparerTable ty with - | :? System.Collections.Generic.IEqualityComparer<'T> as comp -> comp - | _ when HashCompare.canUseDefaultEqualityComparer ty-> - unboxPrim (box System.Collections.Generic.EqualityComparer<'T>.Default) - | _ -> - MakeGenericEqualityComparer<'T>() - [] type FastGenericEqualityComparerTable<'T>() = - static let f = getFastGenericEqualityComparerTable<'T> () + static let f = HashCompare.FSharpEqualityComparer_PER<'T>.Comparer static member Function = f - let FastGenericEqualityComparerFromTable<'T> = FastGenericEqualityComparerTable<'T>.Function + let FastGenericEqualityComparerFromTable<'T> = FastGenericEqualityComparerTable<'T>.Function :> IEqualityComparer<'T> // This is the implementation of HashIdentity.Structural. In most cases this just becomes // FastGenericEqualityComparerFromTable. diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl index b4c8337b1c2..70335b05d20 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:3:0 + .ver 4:4:1:0 } .assembly Compare07 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Compare07 { - // Offset: 0x00000000 Length: 0x0000089E + // Offset: 0x00000000 Length: 0x0000089A } .mresource public FSharpOptimizationData.Compare07 { - // Offset: 0x000008A8 Length: 0x0000069A + // Offset: 0x000008A0 Length: 0x00000692 } .module Compare07.dll -// MVID: {5B1B6346-05DE-F88E-A745-038346631B5B} +// MVID: {59B18AEE-05DE-F88E-A745-0383EE8AB159} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x02B60000 +// Image base: 0x02BA0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] !a V_4, [5] !a V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Compare07.fsx' + .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Compare07.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -633,7 +633,7 @@ instance bool Equals(object obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 20 (0x14) + // Code size 22 (0x16) .maxstack 4 .locals init ([0] class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1 V_0) .line 4,4 : 10,20 '' @@ -641,17 +641,18 @@ IL_0001: isinst class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1 IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0012 + IL_0008: brfalse.s IL_0014 .line 16707566,16707566 : 0,0 '' IL_000a: ldarg.0 IL_000b: ldloc.0 - IL_000c: callvirt instance bool class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::Equals(class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1) - IL_0011: ret + IL_000c: tail. + IL_000e: callvirt instance bool class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::Equals(class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1) + IL_0013: ret .line 16707566,16707566 : 0,0 '' - IL_0012: ldc.i4.0 - IL_0013: ret + IL_0014: ldc.i4.0 + IL_0015: ret } // end of method GenericKey`1::Equals .property instance int32 Tag() diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare10.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare10.il.bsl index 55ccbcf4cf7..16bada503c5 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare10.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare10.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:3:0 + .ver 4:4:1:0 } .assembly Compare10 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Compare10 { - // Offset: 0x00000000 Length: 0x00000AA8 + // Offset: 0x00000000 Length: 0x00000AA4 } .mresource public FSharpOptimizationData.Compare10 { - // Offset: 0x00000AB0 Length: 0x0000058E + // Offset: 0x00000AA8 Length: 0x0000058E } .module Compare10.dll -// MVID: {5B1B6346-04BF-1753-A745-038346631B5B} +// MVID: {59B18AEE-04BF-1753-A745-0383EE8AB159} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x01030000 +// Image base: 0x002E0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] int32 V_4, [5] int32 V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Compare10.fsx' + .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Compare10.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -1330,7 +1330,7 @@ instance bool Equals(object obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 20 (0x14) + // Code size 22 (0x16) .maxstack 4 .locals init ([0] class Compare10/CompareMicroPerfAndCodeGenerationTests/KeyWithInnerKeys V_0) .line 5,5 : 10,26 '' @@ -1338,17 +1338,18 @@ IL_0001: isinst Compare10/CompareMicroPerfAndCodeGenerationTests/KeyWithInnerKeys IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0012 + IL_0008: brfalse.s IL_0014 .line 16707566,16707566 : 0,0 '' IL_000a: ldarg.0 IL_000b: ldloc.0 - IL_000c: callvirt instance bool Compare10/CompareMicroPerfAndCodeGenerationTests/KeyWithInnerKeys::Equals(class Compare10/CompareMicroPerfAndCodeGenerationTests/KeyWithInnerKeys) - IL_0011: ret + IL_000c: tail. + IL_000e: callvirt instance bool Compare10/CompareMicroPerfAndCodeGenerationTests/KeyWithInnerKeys::Equals(class Compare10/CompareMicroPerfAndCodeGenerationTests/KeyWithInnerKeys) + IL_0013: ret .line 16707566,16707566 : 0,0 '' - IL_0012: ldc.i4.0 - IL_0013: ret + IL_0014: ldc.i4.0 + IL_0015: ret } // end of method KeyWithInnerKeys::Equals .property instance int32 Tag() diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl index 4254bb088fe..91247bff24d 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:3:0 + .ver 4:4:1:0 } .assembly Equals06 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Equals06 { - // Offset: 0x00000000 Length: 0x0000089A + // Offset: 0x00000000 Length: 0x00000896 } .mresource public FSharpOptimizationData.Equals06 { - // Offset: 0x000008A0 Length: 0x00000696 + // Offset: 0x000008A0 Length: 0x0000068E } .module Equals06.dll -// MVID: {5B1B6346-0759-31EC-A745-038346631B5B} +// MVID: {59B18AEE-0759-31EC-A745-0383EE8AB159} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x02B30000 +// Image base: 0x01B90000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] !a V_4, [5] !a V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals06.fsx' + .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals06.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -633,7 +633,7 @@ instance bool Equals(object obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 20 (0x14) + // Code size 22 (0x16) .maxstack 4 .locals init ([0] class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1 V_0) .line 4,4 : 10,20 '' @@ -641,17 +641,18 @@ IL_0001: isinst class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1 IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0012 + IL_0008: brfalse.s IL_0014 .line 16707566,16707566 : 0,0 '' IL_000a: ldarg.0 IL_000b: ldloc.0 - IL_000c: callvirt instance bool class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::Equals(class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1) - IL_0011: ret + IL_000c: tail. + IL_000e: callvirt instance bool class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::Equals(class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1) + IL_0013: ret .line 16707566,16707566 : 0,0 '' - IL_0012: ldc.i4.0 - IL_0013: ret + IL_0014: ldc.i4.0 + IL_0015: ret } // end of method GenericKey`1::Equals .property instance int32 Tag() diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals09.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals09.il.bsl index cde1562a76b..8985acaa02b 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals09.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals09.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:3:0 + .ver 4:4:1:0 } .assembly Equals09 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Equals09 { - // Offset: 0x00000000 Length: 0x00000AA4 + // Offset: 0x00000000 Length: 0x00000AA0 } .mresource public FSharpOptimizationData.Equals09 { // Offset: 0x00000AA8 Length: 0x0000058B } .module Equals09.dll -// MVID: {5B1B6346-0759-46D9-A745-038346631B5B} +// MVID: {59B18AEE-0759-46D9-A745-0383EE8AB159} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x02560000 +// Image base: 0x02720000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] int32 V_4, [5] int32 V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals09.fsx' + .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals09.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -1330,7 +1330,7 @@ instance bool Equals(object obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 20 (0x14) + // Code size 22 (0x16) .maxstack 4 .locals init ([0] class Equals09/EqualsMicroPerfAndCodeGenerationTests/KeyWithInnerKeys V_0) .line 5,5 : 10,26 '' @@ -1338,17 +1338,18 @@ IL_0001: isinst Equals09/EqualsMicroPerfAndCodeGenerationTests/KeyWithInnerKeys IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0012 + IL_0008: brfalse.s IL_0014 .line 16707566,16707566 : 0,0 '' IL_000a: ldarg.0 IL_000b: ldloc.0 - IL_000c: callvirt instance bool Equals09/EqualsMicroPerfAndCodeGenerationTests/KeyWithInnerKeys::Equals(class Equals09/EqualsMicroPerfAndCodeGenerationTests/KeyWithInnerKeys) - IL_0011: ret + IL_000c: tail. + IL_000e: callvirt instance bool Equals09/EqualsMicroPerfAndCodeGenerationTests/KeyWithInnerKeys::Equals(class Equals09/EqualsMicroPerfAndCodeGenerationTests/KeyWithInnerKeys) + IL_0013: ret .line 16707566,16707566 : 0,0 '' - IL_0012: ldc.i4.0 - IL_0013: ret + IL_0014: ldc.i4.0 + IL_0015: ret } // end of method KeyWithInnerKeys::Equals .property instance int32 Tag() diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl index 4cf28a5ff57..df7b115d207 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:3:0 + .ver 4:4:1:0 } .assembly Hash09 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Hash09 { - // Offset: 0x00000000 Length: 0x00000892 + // Offset: 0x00000000 Length: 0x0000088E } .mresource public FSharpOptimizationData.Hash09 { - // Offset: 0x00000898 Length: 0x0000068E + // Offset: 0x00000898 Length: 0x00000686 } .module Hash09.dll -// MVID: {5B1B6346-9642-77DB-A745-038346631B5B} +// MVID: {59B18AEE-9642-77DB-A745-0383EE8AB159} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x025D0000 +// Image base: 0x00690000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] !a V_4, [5] !a V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash09.fsx' + .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash09.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -633,7 +633,7 @@ instance bool Equals(object obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 20 (0x14) + // Code size 22 (0x16) .maxstack 4 .locals init ([0] class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1 V_0) .line 4,4 : 10,20 '' @@ -641,17 +641,18 @@ IL_0001: isinst class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1 IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0012 + IL_0008: brfalse.s IL_0014 .line 16707566,16707566 : 0,0 '' IL_000a: ldarg.0 IL_000b: ldloc.0 - IL_000c: callvirt instance bool class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::Equals(class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1) - IL_0011: ret + IL_000c: tail. + IL_000e: callvirt instance bool class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::Equals(class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1) + IL_0013: ret .line 16707566,16707566 : 0,0 '' - IL_0012: ldc.i4.0 - IL_0013: ret + IL_0014: ldc.i4.0 + IL_0015: ret } // end of method GenericKey`1::Equals .property instance int32 Tag() diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash12.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash12.il.bsl index 8836ff15748..9c5bfb73cf3 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash12.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash12.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:3:0 + .ver 4:4:1:0 } .assembly Hash12 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Hash12 { - // Offset: 0x00000000 Length: 0x00000A9C + // Offset: 0x00000000 Length: 0x00000A98 } .mresource public FSharpOptimizationData.Hash12 { // Offset: 0x00000AA0 Length: 0x00000585 } .module Hash12.dll -// MVID: {5B1B6346-9661-796E-A745-038346631B5B} +// MVID: {59B18AEE-9661-796E-A745-0383EE8AB159} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x02810000 +// Image base: 0x01080000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] int32 V_4, [5] int32 V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash12.fsx' + .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash12.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -1330,7 +1330,7 @@ instance bool Equals(object obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 20 (0x14) + // Code size 22 (0x16) .maxstack 4 .locals init ([0] class Hash12/HashMicroPerfAndCodeGenerationTests/KeyWithInnerKeys V_0) .line 5,5 : 10,26 '' @@ -1338,17 +1338,18 @@ IL_0001: isinst Hash12/HashMicroPerfAndCodeGenerationTests/KeyWithInnerKeys IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0012 + IL_0008: brfalse.s IL_0014 .line 16707566,16707566 : 0,0 '' IL_000a: ldarg.0 IL_000b: ldloc.0 - IL_000c: callvirt instance bool Hash12/HashMicroPerfAndCodeGenerationTests/KeyWithInnerKeys::Equals(class Hash12/HashMicroPerfAndCodeGenerationTests/KeyWithInnerKeys) - IL_0011: ret + IL_000c: tail. + IL_000e: callvirt instance bool Hash12/HashMicroPerfAndCodeGenerationTests/KeyWithInnerKeys::Equals(class Hash12/HashMicroPerfAndCodeGenerationTests/KeyWithInnerKeys) + IL_0013: ret .line 16707566,16707566 : 0,0 '' - IL_0012: ldc.i4.0 - IL_0013: ret + IL_0014: ldc.i4.0 + IL_0015: ret } // end of method KeyWithInnerKeys::Equals .property instance int32 Tag() From 6d560098b25812782cb5d518012c3fb5e66a4f41 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Thu, 14 Jun 2018 19:17:46 +1000 Subject: [PATCH 18/31] Removed comparers that matched EqualityComparer<>.Default and code review changes --- src/fsharp/FSharp.Core/prim-types.fs | 169 +++++++-------------------- 1 file changed, 43 insertions(+), 126 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 885ce8b8505..184ec962bfc 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1666,146 +1666,67 @@ namespace Microsoft.FSharp.Core let canUseDefaultEqualityComparer (ty:Type) = let processed = System.Collections.Generic.HashSet () - let rec recurse idx (types:array) = + let rec checkType idx (types:Type[]) = if idx = types.Length then true else let ty = get types idx if not (processed.Add ty) then - recurse (idx+1) types + checkType (idx+1) types else let isValidGenericType ifNotType fullname = if not (ty.IsGenericType && ty.GetGenericTypeDefinition().FullName.Equals fullname) then ifNotType - else recurse 0 (ty.GetGenericArguments ()) + else checkType 0 (ty.GetGenericArguments ()) + let isTypeAndGenericArgumentsOK fullname = isValidGenericType false fullname + let isNotTypeOrIsTypeAndGenericArgumentsOK fullname = isValidGenericType true fullname // avoid any types that need special handling in GenericEqualityObj - true - && ty.IsSealed // covers enum and value types + // GenericEqualityObj handles string as a special cases, but internally routes to same equality + + ty.IsSealed // covers enum and value types + // ref types need to be sealed as derived class might implement IStructuralEquatable && not ty.IsArray - && not (ty.Equals typeof) - && not (ty.Equals typeof) && not (ty.Equals typeof) && not (ty.Equals typeof) - && isValidGenericType true "System.Nullable`1" + && isNotTypeOrIsTypeAndGenericArgumentsOK "System.Nullable`1" && not (typeof.IsAssignableFrom ty - && not (false - || isValidGenericType false "System.ValueTuple`1" - || isValidGenericType false "System.ValueTuple`2" - || isValidGenericType false "System.ValueTuple`3" - || isValidGenericType false "System.ValueTuple`4" - || isValidGenericType false "System.ValueTuple`5" - || isValidGenericType false "System.ValueTuple`6" - || isValidGenericType false "System.ValueTuple`7" - || isValidGenericType false "System.ValueTuple`8")) - && recurse (idx+1) types - - recurse 0 [|ty|] + // we accept ValueTuple even though it supports IStructuralEquatable + // if all generic arguements pass check + && not ( isTypeAndGenericArgumentsOK "System.ValueTuple`1" + || isTypeAndGenericArgumentsOK "System.ValueTuple`2" + || isTypeAndGenericArgumentsOK "System.ValueTuple`3" + || isTypeAndGenericArgumentsOK "System.ValueTuple`4" + || isTypeAndGenericArgumentsOK "System.ValueTuple`5" + || isTypeAndGenericArgumentsOK "System.ValueTuple`6" + || isTypeAndGenericArgumentsOK "System.ValueTuple`7" + || isTypeAndGenericArgumentsOK "System.ValueTuple`8")) + && checkType (idx+1) types + + checkType 0 [|ty|] let tryGetFSharpEqualityComparer (er:bool) (ty:Type) : obj = match er, ty with - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = System.String.Equals (x, y) - member __.GetHashCode x = HashString x } - - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = System.Decimal.op_Equality (x, y) - member __.GetHashCode x = x.GetHashCode () } - - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = (# "" x : int #) } - - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = HashSByte x } - - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = HashInt16 x } - - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = (# "" x : int #) } - - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = HashInt64 x } - - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = (# "" x : int #) } - - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = (# "" x : int #) } - - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = (# "" x : int #) } - - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = HashUInt64 x } - - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = HashIntPtr x } - - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = HashUIntPtr x } - - | _, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = HashChar x } - - | false, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = x.GetHashCode () } - - | false, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) - member __.GetHashCode x = x.GetHashCode () } - - | true, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))) - member __.GetHashCode x = x.GetHashCode () } - - | true, ty when ty.Equals typeof -> box { - new EqualityComparer() with - member __.Equals (x,y) = (# "ceq" x y : bool #) || (not ((# "ceq" x x : bool #) || (# "ceq" y y : bool #))) - member __.GetHashCode x = x.GetHashCode () } - + | false, ty when ty.Equals typeof -> + box { new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = x.GetHashCode () } + | false, ty when ty.Equals typeof -> + box { new EqualityComparer() with + member __.Equals (x,y) = (# "ceq" x y : bool #) + member __.GetHashCode x = x.GetHashCode () } + | true, ty when ty.Equals typeof -> box EqualityComparer.Default + | true, ty when ty.Equals typeof -> box EqualityComparer.Default | _ -> null - let genericFSharpEqualityComparer_ER<'T> () = { - new EqualityComparer<'T>() with + let genericFSharpEqualityComparer_ER<'T> () = + { new EqualityComparer<'T>() with member __.Equals (x,y) = GenericEqualityObj true fsEqualityComparerUnlimitedHashingER (box x, box y) - member __.GetHashCode x = GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x) - } + member __.GetHashCode x = GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x) } - let genericFSharpEqualityComparer_PER<'T> () = { - new EqualityComparer<'T>() with + let genericFSharpEqualityComparer_PER<'T> () = + { new EqualityComparer<'T>() with member __.Equals (x,y) = GenericEqualityObj false fsEqualityComparerUnlimitedHashingPER (box x, box y) - member __.GetHashCode x = GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x) - } + member __.GetHashCode x = GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x) } let getGenericEquality<'T> er = match tryGetFSharpEqualityComparer er typeof<'T> with @@ -1977,7 +1898,8 @@ namespace Microsoft.FSharp.Core FSharpEqualityComparer_PER<'T>.GetHashCode input /// Intrinsic for calls to depth-limited structural hashing that were not optimized by static conditionals. - let LimitedGenericHashIntrinsic limit input = GenericHashParamObj (CountLimitedHasherPER(limit)) (box input) + let LimitedGenericHashIntrinsic limit input = + GenericHashParamObj (CountLimitedHasherPER(limit)) (box input) /// Intrinsic for a recursive call to structural hashing that was not optimized by static conditionals. // @@ -2252,7 +2174,6 @@ namespace Microsoft.FSharp.Core // LanguagePrimitives: PUBLISH IEqualityComparer AND IComparer OBJECTS //------------------------------------------------------------------------- - let inline MakeGenericEqualityComparer<'T>() = // type-specialize some common cases to generate more efficient functions { new System.Collections.Generic.IEqualityComparer<'T> with @@ -2265,12 +2186,8 @@ namespace Microsoft.FSharp.Core member self.GetHashCode(x) = GenericLimitedHash limit x member self.Equals(x,y) = GenericEquality x y } - [] - type FastGenericEqualityComparerTable<'T>() = - static let f = HashCompare.FSharpEqualityComparer_PER<'T>.Comparer - static member Function = f - - let FastGenericEqualityComparerFromTable<'T> = FastGenericEqualityComparerTable<'T>.Function :> IEqualityComparer<'T> + let FastGenericEqualityComparerFromTable<'T> = + HashCompare.FSharpEqualityComparer_PER<'T>.Comparer :> IEqualityComparer<'T> // This is the implementation of HashIdentity.Structural. In most cases this just becomes // FastGenericEqualityComparerFromTable. From 036b2c9797daa17ec66b8283326072429d0ee57d Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Sat, 16 Jun 2018 14:19:31 +1000 Subject: [PATCH 19/31] Added additional types where EqualityComparer.Default can be used --- src/fsharp/FSharp.Core/prim-types.fs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 184ec962bfc..6ad5a57a0e3 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1663,7 +1663,7 @@ namespace Microsoft.FSharp.Core override iec.Equals(x:obj,y:obj) = GenericEqualityObj true iec (x,y) // ER Semantics override iec.GetHashCode(x:obj) = raise (InvalidOperationException (SR.GetString(SR.notUsedForHashing))) } - let canUseDefaultEqualityComparer (ty:Type) = + let canUseDefaultEqualityComparer er (rootType:Type) = let processed = System.Collections.Generic.HashSet () let rec checkType idx (types:Type[]) = @@ -1686,8 +1686,8 @@ namespace Microsoft.FSharp.Core ty.IsSealed // covers enum and value types // ref types need to be sealed as derived class might implement IStructuralEquatable && not ty.IsArray - && not (ty.Equals typeof) - && not (ty.Equals typeof) + && (er || (not (ty.Equals typeof))) + && (er || (not (ty.Equals typeof))) && isNotTypeOrIsTypeAndGenericArgumentsOK "System.Nullable`1" && not (typeof.IsAssignableFrom ty // we accept ValueTuple even though it supports IStructuralEquatable @@ -1699,10 +1699,16 @@ namespace Microsoft.FSharp.Core || isTypeAndGenericArgumentsOK "System.ValueTuple`5" || isTypeAndGenericArgumentsOK "System.ValueTuple`6" || isTypeAndGenericArgumentsOK "System.ValueTuple`7" - || isTypeAndGenericArgumentsOK "System.ValueTuple`8")) + || isTypeAndGenericArgumentsOK "System.ValueTuple`8" + || isTypeAndGenericArgumentsOK "Microsoft.FSharp.Collections.FSharpList`1" + || isTypeAndGenericArgumentsOK "Microsoft.FSharp.Core.FSharpOption`1" + || isTypeAndGenericArgumentsOK "Microsoft.FSharp.Core.FSharpValueOption`1" + || isTypeAndGenericArgumentsOK "Microsoft.FSharp.Core.FSharpResult`2" + ) + ) && checkType (idx+1) types - checkType 0 [|ty|] + checkType 0 [|rootType|] let tryGetFSharpEqualityComparer (er:bool) (ty:Type) : obj = match er, ty with @@ -1731,7 +1737,7 @@ namespace Microsoft.FSharp.Core let getGenericEquality<'T> er = match tryGetFSharpEqualityComparer er typeof<'T> with | :? EqualityComparer<'T> as call -> call - | _ when canUseDefaultEqualityComparer typeof<'T> -> EqualityComparer<'T>.Default + | _ when canUseDefaultEqualityComparer er typeof<'T> -> EqualityComparer<'T>.Default | _ when er -> genericFSharpEqualityComparer_ER<'T> () | _ -> genericFSharpEqualityComparer_PER<'T> () From ef8aafe09456d137bac78bef122433826fb826be Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Sat, 16 Jun 2018 17:20:03 +1000 Subject: [PATCH 20/31] Save unnecessary type checks when more information is known --- src/fsharp/FSharp.Core/prim-types.fs | 66 ++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 6ad5a57a0e3..9b04318e7e4 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1663,6 +1663,9 @@ namespace Microsoft.FSharp.Core override iec.Equals(x:obj,y:obj) = GenericEqualityObj true iec (x,y) // ER Semantics override iec.GetHashCode(x:obj) = raise (InvalidOperationException (SR.GetString(SR.notUsedForHashing))) } + let isStructuralEquatable (ty:Type) = typeof.IsAssignableFrom ty + let isArray (ty:Type) = ty.IsArray || (typeof.IsAssignableFrom ty) + let canUseDefaultEqualityComparer er (rootType:Type) = let processed = System.Collections.Generic.HashSet () @@ -1685,11 +1688,11 @@ namespace Microsoft.FSharp.Core ty.IsSealed // covers enum and value types // ref types need to be sealed as derived class might implement IStructuralEquatable - && not ty.IsArray + && not (isArray ty) && (er || (not (ty.Equals typeof))) && (er || (not (ty.Equals typeof))) && isNotTypeOrIsTypeAndGenericArgumentsOK "System.Nullable`1" - && not (typeof.IsAssignableFrom ty + && not (isStructuralEquatable ty // we accept ValueTuple even though it supports IStructuralEquatable // if all generic arguements pass check && not ( isTypeAndGenericArgumentsOK "System.ValueTuple`1" @@ -1724,22 +1727,67 @@ namespace Microsoft.FSharp.Core | true, ty when ty.Equals typeof -> box EqualityComparer.Default | _ -> null - let genericFSharpEqualityComparer_ER<'T> () = + let arrayEqualityComparer<'T> er comparer = + let arrayEquals (er:bool) (iec:System.Collections.IEqualityComparer) (xobj:obj) (yobj:obj) : bool = + match xobj,yobj with + | null, null -> true + | null, _ -> false + | _, null -> false + | (:? (obj[]) as arr1), (:? (obj[]) as arr2) -> GenericEqualityObjArray er iec arr1 arr2 + | (:? (byte[]) as arr1), (:? (byte[]) as arr2) -> GenericEqualityByteArray arr1 arr2 + | (:? (int32[]) as arr1), (:? (int32[]) as arr2) -> GenericEqualityInt32Array arr1 arr2 + | (:? (int64[]) as arr1), (:? (int64[]) as arr2) -> GenericEqualityInt64Array arr1 arr2 + | (:? (char[]) as arr1), (:? (char[]) as arr2) -> GenericEqualityCharArray arr1 arr2 + | (:? (float32[]) as arr1), (:? (float32[]) as arr2) -> GenericEqualitySingleArray er arr1 arr2 + | (:? (float[]) as arr1), (:? (float[]) as arr2) -> GenericEqualityDoubleArray er arr1 arr2 + | (:? System.Array as arr1), (:? System.Array as arr2) -> GenericEqualityArbArray er iec arr1 arr2 + | _ -> raise (Exception "invalid logic - expected array") + + let getHashCode iec (xobj:obj) = + match xobj with + | null -> 0 + | :? (obj[]) as oa -> GenericHashObjArray iec oa + | :? (byte[]) as ba -> GenericHashByteArray ba + | :? (int[]) as ba -> GenericHashInt32Array ba + | :? (int64[]) as ba -> GenericHashInt64Array ba + | :? System.Array as a -> GenericHashArbArray iec a + | _ -> raise (Exception "invalid logic - expected array") + { new EqualityComparer<'T>() with - member __.Equals (x,y) = GenericEqualityObj true fsEqualityComparerUnlimitedHashingER (box x, box y) - member __.GetHashCode x = GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x) } + member __.Equals (x, y) = arrayEquals er comparer (box x) (box y) + member __.GetHashCode x = getHashCode fsEqualityComparerUnlimitedHashingPER (box x) } - let genericFSharpEqualityComparer_PER<'T> () = + let structuralEqualityComparer<'T> comparer = + { new EqualityComparer<'T>() with + member __.Equals (x,y) = + match box x, box y with + | null, null -> true + | null, _ -> false + | _, null -> false + | (:? IStructuralEquatable as x1), yobj -> x1.Equals (yobj, comparer) + | _ -> raise (Exception "invalid logic - expected IStructuralEquatable") + + member __.GetHashCode x = + match box x with + | null -> 0 + | :? IStructuralEquatable as a -> a.GetHashCode fsEqualityComparerUnlimitedHashingPER + | _ -> raise (Exception "invalid logic - expected IStructuralEquatable") } + + let unknownEqualityComparer<'T> er comparer = { new EqualityComparer<'T>() with - member __.Equals (x,y) = GenericEqualityObj false fsEqualityComparerUnlimitedHashingPER (box x, box y) + member __.Equals (x,y) = GenericEqualityObj er comparer (box x, box y) member __.GetHashCode x = GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x) } let getGenericEquality<'T> er = match tryGetFSharpEqualityComparer er typeof<'T> with | :? EqualityComparer<'T> as call -> call | _ when canUseDefaultEqualityComparer er typeof<'T> -> EqualityComparer<'T>.Default - | _ when er -> genericFSharpEqualityComparer_ER<'T> () - | _ -> genericFSharpEqualityComparer_PER<'T> () + | _ when isArray typeof<'T> && er -> arrayEqualityComparer true fsEqualityComparerUnlimitedHashingER + | _ when isArray typeof<'T> -> arrayEqualityComparer false fsEqualityComparerUnlimitedHashingPER + | _ when isStructuralEquatable typeof<'T> && er -> structuralEqualityComparer fsEqualityComparerUnlimitedHashingER + | _ when isStructuralEquatable typeof<'T> -> structuralEqualityComparer fsEqualityComparerUnlimitedHashingPER + | _ when er -> unknownEqualityComparer true fsEqualityComparerUnlimitedHashingER + | _ -> unknownEqualityComparer false fsEqualityComparerUnlimitedHashingPER type FSharpEqualityComparer_ER<'T> private () = static let comparer = getGenericEquality<'T> true From 4e4c60e73e1fb3fabb87ba4b1d3ee97a4377984e Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Mon, 18 Jun 2018 17:48:34 +1000 Subject: [PATCH 21/31] Removed now unused objects, and other minor cleanup --- src/fsharp/FSharp.Core/prim-types.fs | 45 +++++++--------------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 9b04318e7e4..ca0ef1398c8 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1484,8 +1484,8 @@ namespace Microsoft.FSharp.Core // Run in either PER or ER mode. In PER mode, equality involving a NaN returns "false". // In ER mode, equality on two NaNs returns "true". // - // If "er" is true the "iec" is fsEqualityComparerNoHashingER - // If "er" is false the "iec" is fsEqualityComparerNoHashingPER + // If "er" is true the "iec" is fsEqualityComparerUnlimitedHashingER + // If "er" is false the "iec" is fsEqualityComparerUnlimitedHashingPER let rec GenericEqualityObj (er:bool) (iec:System.Collections.IEqualityComparer) ((xobj:obj),(yobj:obj)) : bool = (*if objEq xobj yobj then true else *) match xobj,yobj with @@ -1648,21 +1648,6 @@ namespace Microsoft.FSharp.Core else i <- i + 1 res - - /// One of the two unique instances of System.Collections.IEqualityComparer. Implements PER semantics - /// where equality on NaN returns "false". - let fsEqualityComparerNoHashingPER = - { new System.Collections.IEqualityComparer with - override iec.Equals(x:obj,y:obj) = GenericEqualityObj false iec (x,y) // PER Semantics - override iec.GetHashCode(x:obj) = raise (InvalidOperationException (SR.GetString(SR.notUsedForHashing))) } - - /// One of the two unique instances of System.Collections.IEqualityComparer. Implements ER semantics - /// where equality on NaN returns "true". - let fsEqualityComparerNoHashingER = - { new System.Collections.IEqualityComparer with - override iec.Equals(x:obj,y:obj) = GenericEqualityObj true iec (x,y) // ER Semantics - override iec.GetHashCode(x:obj) = raise (InvalidOperationException (SR.GetString(SR.notUsedForHashing))) } - let isStructuralEquatable (ty:Type) = typeof.IsAssignableFrom ty let isArray (ty:Type) = ty.IsArray || (typeof.IsAssignableFrom ty) @@ -1791,26 +1776,18 @@ namespace Microsoft.FSharp.Core type FSharpEqualityComparer_ER<'T> private () = static let comparer = getGenericEquality<'T> true - static member Comparer = comparer - static member inline Equals (x, y) = comparer.Equals (x, y) - static member inline GetHashCode x = comparer.GetHashCode x - type FSharpEqualityComparer_PER<'T> private () = static let comparer = getGenericEquality<'T> false - static member Comparer = comparer - static member inline Equals (x, y) = comparer.Equals (x, y) - static member inline GetHashCode x = comparer.GetHashCode x - /// Implements generic equality between two values, with PER semantics for NaN (so equality on two NaN values returns false) // // The compiler optimizer is aware of this function (see use of generic_equality_per_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityIntrinsic (x : 'T) (y : 'T) : bool = - FSharpEqualityComparer_PER<'T>.Equals (x, y) + FSharpEqualityComparer_PER<'T>.Comparer.Equals (x, y) /// Implements generic equality between two values, with ER semantics for NaN (so equality on two NaN values returns true) // @@ -1819,18 +1796,18 @@ namespace Microsoft.FSharp.Core // The compiler optimizer is aware of this function (see use of generic_equality_er_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityERIntrinsic (x : 'T) (y : 'T) : bool = - FSharpEqualityComparer_ER<'T>.Equals (x, y) + FSharpEqualityComparer_ER<'T>.Comparer.Equals (x, y) /// Implements generic equality between two values using "comp" for recursive calls. // // The compiler optimizer is aware of this function (see use of generic_equality_withc_inner_vref in opt.fs) // and devirtualizes calls to it based on "T", and under the assumption that "comp" - // is either fsEqualityComparerNoHashingER or fsEqualityComparerNoHashingPER. + // is either fsEqualityComparerUnlimitedHashingER or fsEqualityComparerUnlimitedHashingPER. let GenericEqualityWithComparerIntrinsic (comp : System.Collections.IEqualityComparer) (x : 'T) (y : 'T) : bool = - if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) || obj.ReferenceEquals (comp, fsEqualityComparerNoHashingPER) then - FSharpEqualityComparer_PER<'T>.Equals (x, y) - elif obj.ReferenceEquals (comp, fsEqualityComparerNoHashingER) then - FSharpEqualityComparer_ER<'T>.Equals (x, y) + if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then + FSharpEqualityComparer_PER<'T>.Comparer.Equals (x, y) + elif obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingER) then + FSharpEqualityComparer_ER<'T>.Comparer.Equals (x, y) else comp.Equals (box x, box y) @@ -1949,7 +1926,7 @@ namespace Microsoft.FSharp.Core // NOTE: The compiler optimizer is aware of this function (see uses of generic_hash_inner_vref in opt.fs) // and devirtualizes calls to it based on type "T". let GenericHashIntrinsic input = - FSharpEqualityComparer_PER<'T>.GetHashCode input + FSharpEqualityComparer_PER<'T>.Comparer.GetHashCode input /// Intrinsic for calls to depth-limited structural hashing that were not optimized by static conditionals. let LimitedGenericHashIntrinsic limit input = @@ -1964,7 +1941,7 @@ namespace Microsoft.FSharp.Core // and devirtualizes calls to it based on type "T". let GenericHashWithComparerIntrinsic<'T> (comp : System.Collections.IEqualityComparer) (input : 'T) : int = if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then - FSharpEqualityComparer_PER<'T>.GetHashCode input + FSharpEqualityComparer_PER<'T>.Comparer.GetHashCode input else GenericHashParamObj comp (box input) From 01734b6249a080a31dc0bfbfee0eb2b81c1b0c1d Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Sat, 23 Jun 2018 10:34:03 +1000 Subject: [PATCH 22/31] Modified Optimizer to manually inline GenericEqualityIntrinsic and friends (as they have special handling) --- src/fsharp/FSharp.Core/prim-types.fs | 29 +++++-- src/fsharp/FSharp.Core/prim-types.fsi | 20 +++++ src/fsharp/Optimizer.fs | 25 ++++-- src/fsharp/TcGlobals.fs | 7 ++ .../Linq101Joins01.il.bsl | 83 +++++++++++-------- .../StaticInit/StaticInit_Struct01.il.bsl | 43 ++++++---- .../GenericComparison/Compare07.il.bsl | 80 +++++++++--------- .../GenericComparison/Equals06.il.bsl | 80 +++++++++--------- .../GenericComparison/Equals07.il.bsl | 45 +++++----- .../GenericComparison/Equals08.il.bsl | 45 +++++----- .../GenericComparison/Hash09.il.bsl | 80 +++++++++--------- .../GenericComparison/Hash10.il.bsl | 39 ++++----- .../GenericComparison/Hash11.il.bsl | 39 ++++----- 13 files changed, 351 insertions(+), 264 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index ca0ef1398c8..fd7c235cb58 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1774,20 +1774,31 @@ namespace Microsoft.FSharp.Core | _ when er -> unknownEqualityComparer true fsEqualityComparerUnlimitedHashingER | _ -> unknownEqualityComparer false fsEqualityComparerUnlimitedHashingPER + [] type FSharpEqualityComparer_ER<'T> private () = static let comparer = getGenericEquality<'T> true - static member Comparer = comparer + static member EqualityComparer = comparer + [] type FSharpEqualityComparer_PER<'T> private () = static let comparer = getGenericEquality<'T> false - static member Comparer = comparer + static member EqualityComparer = comparer + + let inline FSharpEqualityComparer_ER_Equals (x:'T) (y:'T) = + FSharpEqualityComparer_ER<'T>.EqualityComparer.Equals (x, y) + + let inline FSharpEqualityComparer_PER_Equals (x:'T) (y:'T) = + FSharpEqualityComparer_PER<'T>.EqualityComparer.Equals (x, y) + + let inline FSharpEqualityComparer_GetHashCode (x:'T) = + FSharpEqualityComparer_PER<'T>.EqualityComparer.GetHashCode x /// Implements generic equality between two values, with PER semantics for NaN (so equality on two NaN values returns false) // // The compiler optimizer is aware of this function (see use of generic_equality_per_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityIntrinsic (x : 'T) (y : 'T) : bool = - FSharpEqualityComparer_PER<'T>.Comparer.Equals (x, y) + FSharpEqualityComparer_PER<'T>.EqualityComparer.Equals (x, y) /// Implements generic equality between two values, with ER semantics for NaN (so equality on two NaN values returns true) // @@ -1796,7 +1807,7 @@ namespace Microsoft.FSharp.Core // The compiler optimizer is aware of this function (see use of generic_equality_er_inner_vref in opt.fs) // and devirtualizes calls to it based on "T". let GenericEqualityERIntrinsic (x : 'T) (y : 'T) : bool = - FSharpEqualityComparer_ER<'T>.Comparer.Equals (x, y) + FSharpEqualityComparer_ER<'T>.EqualityComparer.Equals (x, y) /// Implements generic equality between two values using "comp" for recursive calls. // @@ -1805,9 +1816,9 @@ namespace Microsoft.FSharp.Core // is either fsEqualityComparerUnlimitedHashingER or fsEqualityComparerUnlimitedHashingPER. let GenericEqualityWithComparerIntrinsic (comp : System.Collections.IEqualityComparer) (x : 'T) (y : 'T) : bool = if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then - FSharpEqualityComparer_PER<'T>.Comparer.Equals (x, y) + FSharpEqualityComparer_PER<'T>.EqualityComparer.Equals (x, y) elif obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingER) then - FSharpEqualityComparer_ER<'T>.Comparer.Equals (x, y) + FSharpEqualityComparer_ER<'T>.EqualityComparer.Equals (x, y) else comp.Equals (box x, box y) @@ -1926,7 +1937,7 @@ namespace Microsoft.FSharp.Core // NOTE: The compiler optimizer is aware of this function (see uses of generic_hash_inner_vref in opt.fs) // and devirtualizes calls to it based on type "T". let GenericHashIntrinsic input = - FSharpEqualityComparer_PER<'T>.Comparer.GetHashCode input + FSharpEqualityComparer_PER<'T>.EqualityComparer.GetHashCode input /// Intrinsic for calls to depth-limited structural hashing that were not optimized by static conditionals. let LimitedGenericHashIntrinsic limit input = @@ -1941,7 +1952,7 @@ namespace Microsoft.FSharp.Core // and devirtualizes calls to it based on type "T". let GenericHashWithComparerIntrinsic<'T> (comp : System.Collections.IEqualityComparer) (input : 'T) : int = if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then - FSharpEqualityComparer_PER<'T>.Comparer.GetHashCode input + FSharpEqualityComparer_PER<'T>.EqualityComparer.GetHashCode input else GenericHashParamObj comp (box input) @@ -2218,7 +2229,7 @@ namespace Microsoft.FSharp.Core member self.Equals(x,y) = GenericEquality x y } let FastGenericEqualityComparerFromTable<'T> = - HashCompare.FSharpEqualityComparer_PER<'T>.Comparer :> IEqualityComparer<'T> + HashCompare.FSharpEqualityComparer_PER<'T>.EqualityComparer :> IEqualityComparer<'T> // This is the implementation of HashIdentity.Structural. In most cases this just becomes // FastGenericEqualityComparerFromTable. diff --git a/src/fsharp/FSharp.Core/prim-types.fsi b/src/fsharp/FSharp.Core/prim-types.fsi index 54fdf39ba2c..52b2139725d 100644 --- a/src/fsharp/FSharp.Core/prim-types.fsi +++ b/src/fsharp/FSharp.Core/prim-types.fsi @@ -1238,6 +1238,26 @@ namespace Microsoft.FSharp.Core /// The F# compiler emits calls to some of the functions in this module as part of the compiled form of some language constructs module HashCompare = + [] + type FSharpEqualityComparer_ER<'T> = + static member EqualityComparer : System.Collections.Generic.EqualityComparer<'T> + + [] + type FSharpEqualityComparer_PER<'T> = + static member EqualityComparer : System.Collections.Generic.EqualityComparer<'T> + + /// A primitive entry point used by the F# compiler for optimization purposes. + [] + val inline FSharpEqualityComparer_ER_Equals : x:'T -> y:'T -> bool + + /// A primitive entry point used by the F# compiler for optimization purposes. + [] + val inline FSharpEqualityComparer_PER_Equals : x:'T -> y:'T -> bool + + /// A primitive entry point used by the F# compiler for optimization purposes. + [] + val inline FSharpEqualityComparer_GetHashCode : x:'T -> int + /// A primitive entry point used by the F# compiler for optimization purposes. [] val PhysicalHashIntrinsic : input:'T -> int when 'T : not struct diff --git a/src/fsharp/Optimizer.fs b/src/fsharp/Optimizer.fs index da19114bcc7..4921dcff817 100644 --- a/src/fsharp/Optimizer.fs +++ b/src/fsharp/Optimizer.fs @@ -2328,12 +2328,12 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) = // REVIEW: GenericEqualityIntrinsic (which has no comparer) implements PER semantics (5537: this should be ER semantics) // We are devirtualizing to a Equals(T) method which also implements PER semantics (5537: this should be ER semantics) | Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_equality_er_inner_vref ty args -> - + let tyargsOriginal = tyargs let tcref, tyargs = StripToNominalTyconRef cenv ty match tcref.GeneratedHashAndEqualsValues with | Some (_, vref) -> Some (DevirtualizeApplication cenv env vref ty tyargs args m) - | _ -> None - + | _ -> + Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_ER_Equals_vref ty tyargsOriginal args m) // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericEqualityWithComparerFast | Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_equality_withc_inner_vref ty args -> @@ -2345,23 +2345,27 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) = Some (DevirtualizeApplication cenv env withcEqualsVal ty tyargs args2 m) | _ -> None - // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericEqualityWithComparer + // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericEqualityIntrinsic | Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_equality_per_inner_vref ty args && not(isRefTupleTy cenv.g ty) -> + let tyargsOriginal = tyargs let tcref, tyargs = StripToNominalTyconRef cenv ty match tcref.GeneratedHashAndEqualsWithComparerValues, args with | Some (_, _, withcEqualsVal), [x; y] -> let args2 = [x; mkRefTupledNoTypes cenv.g m [mkCoerceExpr(y, cenv.g.obj_ty, m, ty); (mkCallGetGenericPEREqualityComparer cenv.g m)]] Some (DevirtualizeApplication cenv env withcEqualsVal ty tyargs args2 m) - | _ -> None + | _ -> + Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_PER_Equals_vref ty tyargsOriginal args m) // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericHashIntrinsic | Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_hash_inner_vref ty args -> + let tyargsOriginal = tyargs let tcref, tyargs = StripToNominalTyconRef cenv ty match tcref.GeneratedHashAndEqualsWithComparerValues, args with | Some (_, withcGetHashCodeVal, _), [x] -> let args2 = [x; mkCallGetGenericEREqualityComparer cenv.g m] Some (DevirtualizeApplication cenv env withcGetHashCodeVal ty tyargs args2 m) - | _ -> None + | _ -> + Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_GetHashCode_vref ty tyargsOriginal args m) // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericHashWithComparerIntrinsic | Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_hash_withc_inner_vref ty args -> @@ -2416,6 +2420,15 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) = match vref with | Some vref -> Some (DevirtualizeApplication cenv env vref ty tyargs (mkCallGetGenericPEREqualityComparer cenv.g m :: args) m) | None -> None + + | Expr.Val(v, _, _), [(TType_var t) as ty], _ when (not cenv.g.compilingFslib) && valRefEq cenv.g v cenv.g.generic_equality_per_inner_vref && t.Rigidity = TyparRigidity.Rigid -> + Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_PER_Equals_vref ty tyargs args m) + + | Expr.Val(v, _, _), [(TType_var t) as ty], _ when (not cenv.g.compilingFslib) && valRefEq cenv.g v cenv.g.generic_equality_er_inner_vref && t.Rigidity = TyparRigidity.Rigid -> + Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_ER_Equals_vref ty tyargs args m) + + | Expr.Val(v, _, _), [(TType_var t) as ty], _ when (not cenv.g.compilingFslib) && valRefEq cenv.g v cenv.g.generic_hash_inner_vref && t.Rigidity = TyparRigidity.Rigid -> + Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_GetHashCode_vref ty tyargs args m) // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericComparisonWithComparerIntrinsic for tuple types | Expr.Val(v, _, _), [ty], _ when valRefEq cenv.g v cenv.g.generic_comparison_withc_inner_vref && isRefTupleTy cenv.g ty -> diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index d6fb7cd3c73..70ff2912bfb 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -583,6 +583,10 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let v_generic_comparison_inner_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "GenericComparisonIntrinsic" , None , None , [vara], mk_compare_sig varaTy) let v_generic_comparison_withc_inner_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "GenericComparisonWithComparerIntrinsic", None , None , [vara], mk_compare_withc_sig varaTy) + let v_FSharpEqualityComparer_PER_Equals_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "FSharpEqualityComparer_PER_Equals" , None , None , [vara], mk_rel_sig varaTy) + let v_FSharpEqualityComparer_GetHashCode_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "FSharpEqualityComparer_GetHashCode", None , None , [vara], mk_hash_sig varaTy) + let v_FSharpEqualityComparer_ER_Equals_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "FSharpEqualityComparer_ER_Equals" , None , None , [vara], mk_rel_sig varaTy) + let v_generic_hash_inner_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "GenericHashIntrinsic" , None , None , [vara], mk_hash_sig varaTy) let v_generic_hash_withc_inner_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "GenericHashWithComparerIntrinsic" , None , None , [vara], mk_hash_withc_sig varaTy) @@ -1220,6 +1224,9 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member __.generic_hash_withc_outer_info = v_generic_hash_withc_outer_info member val generic_hash_inner_vref = ValRefForIntrinsic v_generic_hash_inner_info member val generic_hash_withc_inner_vref = ValRefForIntrinsic v_generic_hash_withc_inner_info + member val fsharpEqualityComparer_ER_Equals_vref = ValRefForIntrinsic v_FSharpEqualityComparer_ER_Equals_info + member val fsharpEqualityComparer_PER_Equals_vref = ValRefForIntrinsic v_FSharpEqualityComparer_PER_Equals_info + member val fsharpEqualityComparer_GetHashCode_vref = ValRefForIntrinsic v_FSharpEqualityComparer_GetHashCode_info member val reference_equality_inner_vref = ValRefForIntrinsic v_reference_equality_inner_info diff --git a/tests/fsharpqa/Source/CodeGen/EmittedIL/QueryExpressionStepping/Linq101Joins01.il.bsl b/tests/fsharpqa/Source/CodeGen/EmittedIL/QueryExpressionStepping/Linq101Joins01.il.bsl index 22342da9b92..603c997764a 100644 --- a/tests/fsharpqa/Source/CodeGen/EmittedIL/QueryExpressionStepping/Linq101Joins01.il.bsl +++ b/tests/fsharpqa/Source/CodeGen/EmittedIL/QueryExpressionStepping/Linq101Joins01.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly extern Utils { @@ -38,20 +38,20 @@ } .mresource public FSharpSignatureData.Linq101Joins01 { - // Offset: 0x00000000 Length: 0x000002F4 + // Offset: 0x00000000 Length: 0x00000326 } .mresource public FSharpOptimizationData.Linq101Joins01 { - // Offset: 0x000002F8 Length: 0x000000C3 + // Offset: 0x00000330 Length: 0x000000C3 } .module Linq101Joins01.exe -// MVID: {5A1F62A6-151B-685E-A745-0383A6621F5A} +// MVID: {5B2D78B8-151B-685E-A745-0383B8782D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x03830000 +// Image base: 0x027D0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -81,7 +81,7 @@ // Code size 2 (0x2) .maxstack 8 .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 14,14 : 32,33 'C:\\visualfsharp\\tests\\fsharpqa\\Source\\CodeGen\\EmittedIL\\QueryExpressionStepping\\Linq101Joins01.fs' + .line 14,14 : 32,33 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\CodeGen\\EmittedIL\\QueryExpressionStepping\\Linq101Joins01.fs' IL_0000: ldarg.1 IL_0001: ret } // end of method q@14::Invoke @@ -790,54 +790,67 @@ .method public strict virtual instance class [FSharp.Core]Microsoft.FSharp.Linq.QuerySource`2,class [Utils]Utils/Product,string>,object> Invoke(class [Utils]Utils/Product _arg2) cil managed { - // Code size 69 (0x45) + // Code size 86 (0x56) .maxstack 9 .locals init ([0] class [Utils]Utils/Product p, - [1] string t) + [1] string t, + [2] object V_2, + [3] object V_3, + [4] object V_4, + [5] object V_5) .line 40,40 : 9,40 '' IL_0000: ldarg.1 IL_0001: stloc.0 .line 41,41 : 17,39 '' IL_0002: ldloc.0 IL_0003: box [Utils]Utils/Product - IL_0008: ldnull - IL_0009: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityIntrinsic(!!0, - !!0) - IL_000e: brfalse.s IL_0012 - - IL_0010: br.s IL_0014 - - IL_0012: br.s IL_001c + IL_0008: stloc.2 + IL_0009: ldnull + IL_000a: stloc.3 + IL_000b: ldloc.2 + IL_000c: stloc.s V_4 + IL_000e: ldloc.3 + IL_000f: stloc.s V_5 + IL_0011: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_PER`1::get_EqualityComparer() + IL_0016: ldloc.s V_4 + IL_0018: ldloc.s V_5 + IL_001a: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_001f: brfalse.s IL_0023 + + IL_0021: br.s IL_0025 + + IL_0023: br.s IL_002d .line 41,41 : 40,55 '' - IL_0014: ldstr "(No products)" + IL_0025: ldstr "(No products)" .line 100001,100001 : 0,0 '' - IL_0019: nop - IL_001a: br.s IL_0023 + IL_002a: nop + IL_002b: br.s IL_0034 .line 41,41 : 61,74 '' - IL_001c: ldloc.0 - IL_001d: callvirt instance string [Utils]Utils/Product::get_ProductName() + IL_002d: ldloc.0 + IL_002e: callvirt instance string [Utils]Utils/Product::get_ProductName() .line 100001,100001 : 0,0 '' - IL_0022: nop + IL_0033: nop .line 100001,100001 : 0,0 '' - IL_0023: stloc.1 + IL_0034: stloc.1 .line 42,42 : 9,22 '' - IL_0024: ldarg.0 - IL_0025: ldfld class [FSharp.Core]Microsoft.FSharp.Linq.QueryBuilder Linq101Joins01/'q4@40-4'::builder@ - IL_002a: ldarg.0 - IL_002b: ldfld string Linq101Joins01/'q4@40-4'::c - IL_0030: ldarg.0 - IL_0031: ldfld class [mscorlib]System.Collections.Generic.IEnumerable`1 Linq101Joins01/'q4@40-4'::ps - IL_0036: ldloc.0 - IL_0037: ldloc.1 - IL_0038: newobj instance void class [mscorlib]System.Tuple`4,class [Utils]Utils/Product,string>::.ctor(!0, + IL_0035: ldarg.0 + IL_0036: ldfld class [FSharp.Core]Microsoft.FSharp.Linq.QueryBuilder Linq101Joins01/'q4@40-4'::builder@ + IL_003b: ldarg.0 + IL_003c: ldfld string Linq101Joins01/'q4@40-4'::c + IL_0041: ldarg.0 + IL_0042: ldfld class [mscorlib]System.Collections.Generic.IEnumerable`1 Linq101Joins01/'q4@40-4'::ps + IL_0047: ldloc.0 + IL_0048: ldloc.1 + IL_0049: newobj instance void class [mscorlib]System.Tuple`4,class [Utils]Utils/Product,string>::.ctor(!0, !1, !2, !3) - IL_003d: tail. - IL_003f: callvirt instance class [FSharp.Core]Microsoft.FSharp.Linq.QuerySource`2 [FSharp.Core]Microsoft.FSharp.Linq.QueryBuilder::Yield,class [Utils]Utils/Product,string>,object>(!!0) - IL_0044: ret + IL_004e: tail. + IL_0050: callvirt instance class [FSharp.Core]Microsoft.FSharp.Linq.QuerySource`2 [FSharp.Core]Microsoft.FSharp.Linq.QueryBuilder::Yield,class [Utils]Utils/Product,string>,object>(!!0) + IL_0055: ret } // end of method 'q4@40-4'::Invoke } // end of class 'q4@40-4' diff --git a/tests/fsharpqa/Source/CodeGen/EmittedIL/StaticInit/StaticInit_Struct01.il.bsl b/tests/fsharpqa/Source/CodeGen/EmittedIL/StaticInit/StaticInit_Struct01.il.bsl index 55664ad8b3a..17ce5430574 100644 --- a/tests/fsharpqa/Source/CodeGen/EmittedIL/StaticInit/StaticInit_Struct01.il.bsl +++ b/tests/fsharpqa/Source/CodeGen/EmittedIL/StaticInit/StaticInit_Struct01.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly StaticInit_Struct01 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.StaticInit_Struct01 { - // Offset: 0x00000000 Length: 0x000007B1 + // Offset: 0x00000000 Length: 0x000007B5 } .mresource public FSharpOptimizationData.StaticInit_Struct01 { - // Offset: 0x000007B8 Length: 0x0000021F + // Offset: 0x000007C0 Length: 0x0000021F } .module StaticInit_Struct01.dll -// MVID: {59B19250-05F6-D6CB-A745-03835092B159} +// MVID: {5B2D78C5-05F6-D6CB-A745-0383C5782D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x02BA0000 +// Image base: 0x02C80000 // =============== CLASS MEMBERS DECLARATION =================== @@ -71,7 +71,7 @@ .maxstack 5 .locals init ([0] valuetype StaticInit_Struct01/C& V_0) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 4,4 : 6,7 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\CodeGen\\EmittedIL\\StaticInit\\StaticInit_Struct01.fs' + .line 4,4 : 6,7 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\CodeGen\\EmittedIL\\StaticInit\\StaticInit_Struct01.fs' IL_0000: ldarga.s obj IL_0002: stloc.0 IL_0003: call class [mscorlib]System.Collections.IComparer [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives::get_GenericComparer() @@ -256,20 +256,33 @@ instance bool Equals(valuetype StaticInit_Struct01/C obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 23 (0x17) - .maxstack 4 - .locals init ([0] valuetype StaticInit_Struct01/C& V_0) + // Code size 38 (0x26) + .maxstack 5 + .locals init ([0] valuetype StaticInit_Struct01/C& V_0, + [1] valuetype [mscorlib]System.DateTime V_1, + [2] valuetype [mscorlib]System.DateTime V_2, + [3] valuetype [mscorlib]System.DateTime V_3, + [4] valuetype [mscorlib]System.DateTime V_4) .line 4,4 : 6,7 '' IL_0000: ldarga.s obj IL_0002: stloc.0 IL_0003: ldarg.0 IL_0004: ldfld valuetype [mscorlib]System.DateTime StaticInit_Struct01/C::s - IL_0009: ldloc.0 - IL_000a: ldfld valuetype [mscorlib]System.DateTime StaticInit_Struct01/C::s - IL_000f: tail. - IL_0011: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0016: ret + IL_0009: stloc.1 + IL_000a: ldloc.0 + IL_000b: ldfld valuetype [mscorlib]System.DateTime StaticInit_Struct01/C::s + IL_0010: stloc.2 + IL_0011: ldloc.1 + IL_0012: stloc.3 + IL_0013: ldloc.2 + IL_0014: stloc.s V_4 + IL_0016: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_001b: ldloc.3 + IL_001c: ldloc.s V_4 + IL_001e: tail. + IL_0020: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_0025: ret } // end of method C::Equals .method public hidebysig virtual final diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl index 70335b05d20..cb04fa9fb46 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Compare07 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Compare07 { - // Offset: 0x00000000 Length: 0x0000089A + // Offset: 0x00000000 Length: 0x0000089E } .mresource public FSharpOptimizationData.Compare07 { - // Offset: 0x000008A0 Length: 0x00000692 + // Offset: 0x000008A8 Length: 0x0000069A } .module Compare07.dll -// MVID: {59B18AEE-05DE-F88E-A745-0383EE8AB159} +// MVID: {5B2D7B11-05DE-F88E-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x02BA0000 +// Image base: 0x02640000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] !a V_4, [5] !a V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Compare07.fsx' + .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Compare07.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -560,8 +560,8 @@ instance bool Equals(class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1 obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 77 (0x4d) - .maxstack 4 + // Code size 87 (0x57) + .maxstack 5 .locals init ([0] class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1 V_0, [1] class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1 V_1, [2] !a V_2, @@ -570,13 +570,13 @@ IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un - IL_0004: brfalse.s IL_0045 + IL_0004: brfalse.s IL_004f .line 16707566,16707566 : 0,0 '' IL_0006: ldarg.1 IL_0007: ldnull IL_0008: cgt.un - IL_000a: brfalse.s IL_0043 + IL_000a: brfalse.s IL_004d .line 16707566,16707566 : 0,0 '' IL_000c: ldarg.0 @@ -592,41 +592,43 @@ IL_0019: ldloc.1 IL_001a: ldfld !0 class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::item1 IL_001f: stloc.3 - IL_0020: ldloc.2 - IL_0021: ldloc.3 - IL_0022: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0027: brfalse.s IL_0041 - - .line 16707566,16707566 : 0,0 '' - IL_0029: ldloc.0 - IL_002a: ldfld !0 class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::item2 - IL_002f: stloc.2 - IL_0030: ldloc.1 - IL_0031: ldfld !0 class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::item2 - IL_0036: stloc.3 - IL_0037: ldloc.2 - IL_0038: ldloc.3 - IL_0039: tail. - IL_003b: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0040: ret + IL_0020: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_0025: ldloc.2 + IL_0026: ldloc.3 + IL_0027: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_002c: brfalse.s IL_004b + + .line 16707566,16707566 : 0,0 '' + IL_002e: ldloc.0 + IL_002f: ldfld !0 class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::item2 + IL_0034: stloc.2 + IL_0035: ldloc.1 + IL_0036: ldfld !0 class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::item2 + IL_003b: stloc.3 + IL_003c: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_0041: ldloc.2 + IL_0042: ldloc.3 + IL_0043: tail. + IL_0045: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_004a: ret .line 16707566,16707566 : 0,0 '' - IL_0041: ldc.i4.0 - IL_0042: ret + IL_004b: ldc.i4.0 + IL_004c: ret .line 16707566,16707566 : 0,0 '' - IL_0043: ldc.i4.0 - IL_0044: ret + IL_004d: ldc.i4.0 + IL_004e: ret .line 16707566,16707566 : 0,0 '' - IL_0045: ldarg.1 - IL_0046: ldnull - IL_0047: cgt.un - IL_0049: ldc.i4.0 - IL_004a: ceq - IL_004c: ret + IL_004f: ldarg.1 + IL_0050: ldnull + IL_0051: cgt.un + IL_0053: ldc.i4.0 + IL_0054: ceq + IL_0056: ret } // end of method GenericKey`1::Equals .method public hidebysig virtual final diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl index 91247bff24d..e575063fd39 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Equals06 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Equals06 { - // Offset: 0x00000000 Length: 0x00000896 + // Offset: 0x00000000 Length: 0x0000089A } .mresource public FSharpOptimizationData.Equals06 { - // Offset: 0x000008A0 Length: 0x0000068E + // Offset: 0x000008A0 Length: 0x00000696 } .module Equals06.dll -// MVID: {59B18AEE-0759-31EC-A745-0383EE8AB159} +// MVID: {5B2D7B11-0759-31EC-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x01B90000 +// Image base: 0x02980000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] !a V_4, [5] !a V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals06.fsx' + .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals06.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -560,8 +560,8 @@ instance bool Equals(class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1 obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 77 (0x4d) - .maxstack 4 + // Code size 87 (0x57) + .maxstack 5 .locals init ([0] class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1 V_0, [1] class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1 V_1, [2] !a V_2, @@ -570,13 +570,13 @@ IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un - IL_0004: brfalse.s IL_0045 + IL_0004: brfalse.s IL_004f .line 16707566,16707566 : 0,0 '' IL_0006: ldarg.1 IL_0007: ldnull IL_0008: cgt.un - IL_000a: brfalse.s IL_0043 + IL_000a: brfalse.s IL_004d .line 16707566,16707566 : 0,0 '' IL_000c: ldarg.0 @@ -592,41 +592,43 @@ IL_0019: ldloc.1 IL_001a: ldfld !0 class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::item1 IL_001f: stloc.3 - IL_0020: ldloc.2 - IL_0021: ldloc.3 - IL_0022: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0027: brfalse.s IL_0041 - - .line 16707566,16707566 : 0,0 '' - IL_0029: ldloc.0 - IL_002a: ldfld !0 class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::item2 - IL_002f: stloc.2 - IL_0030: ldloc.1 - IL_0031: ldfld !0 class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::item2 - IL_0036: stloc.3 - IL_0037: ldloc.2 - IL_0038: ldloc.3 - IL_0039: tail. - IL_003b: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0040: ret + IL_0020: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_0025: ldloc.2 + IL_0026: ldloc.3 + IL_0027: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_002c: brfalse.s IL_004b + + .line 16707566,16707566 : 0,0 '' + IL_002e: ldloc.0 + IL_002f: ldfld !0 class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::item2 + IL_0034: stloc.2 + IL_0035: ldloc.1 + IL_0036: ldfld !0 class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::item2 + IL_003b: stloc.3 + IL_003c: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_0041: ldloc.2 + IL_0042: ldloc.3 + IL_0043: tail. + IL_0045: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_004a: ret .line 16707566,16707566 : 0,0 '' - IL_0041: ldc.i4.0 - IL_0042: ret + IL_004b: ldc.i4.0 + IL_004c: ret .line 16707566,16707566 : 0,0 '' - IL_0043: ldc.i4.0 - IL_0044: ret + IL_004d: ldc.i4.0 + IL_004e: ret .line 16707566,16707566 : 0,0 '' - IL_0045: ldarg.1 - IL_0046: ldnull - IL_0047: cgt.un - IL_0049: ldc.i4.0 - IL_004a: ceq - IL_004c: ret + IL_004f: ldarg.1 + IL_0050: ldnull + IL_0051: cgt.un + IL_0053: ldc.i4.0 + IL_0054: ceq + IL_0056: ret } // end of method GenericKey`1::Equals .method public hidebysig virtual final diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals07.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals07.il.bsl index 91f97c289a2..1ce42f2b348 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals07.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals07.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Equals07 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Equals07 { - // Offset: 0x00000000 Length: 0x0000022D + // Offset: 0x00000000 Length: 0x00000245 } .mresource public FSharpOptimizationData.Equals07 { - // Offset: 0x00000238 Length: 0x000000AF + // Offset: 0x00000250 Length: 0x000000AF } .module Equals07.dll -// MVID: {59B18AEE-0759-AE27-A745-0383EE8AB159} +// MVID: {5B2D7B11-0759-AE27-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x01C80000 +// Image base: 0x02D80000 // =============== CLASS MEMBERS DECLARATION =================== @@ -57,14 +57,14 @@ .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) .method public static bool f7() cil managed { - // Code size 68 (0x44) + // Code size 73 (0x49) .maxstack 5 .locals init ([0] bool x, [1] uint8[] t1, [2] uint8[] t2, [3] int32 i) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 5,5 : 8,29 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals07.fsx' + .line 5,5 : 8,29 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals07.fsx' IL_0000: ldc.i4.0 IL_0001: stloc.0 .line 6,6 : 8,35 '' @@ -90,26 +90,27 @@ .line 8,8 : 8,32 '' IL_002a: ldc.i4.0 IL_002b: stloc.3 - IL_002c: br.s IL_003a + IL_002c: br.s IL_003f .line 9,9 : 12,26 '' - IL_002e: ldloc.1 - IL_002f: ldloc.2 - IL_0030: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityIntrinsic(!!0, - !!0) - IL_0035: stloc.0 - IL_0036: ldloc.3 - IL_0037: ldc.i4.1 - IL_0038: add - IL_0039: stloc.3 + IL_002e: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_PER`1::get_EqualityComparer() + IL_0033: ldloc.1 + IL_0034: ldloc.2 + IL_0035: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_003a: stloc.0 + IL_003b: ldloc.3 + IL_003c: ldc.i4.1 + IL_003d: add + IL_003e: stloc.3 .line 8,8 : 8,32 '' - IL_003a: ldloc.3 - IL_003b: ldc.i4 0x989681 - IL_0040: blt.s IL_002e + IL_003f: ldloc.3 + IL_0040: ldc.i4 0x989681 + IL_0045: blt.s IL_002e .line 10,10 : 8,9 '' - IL_0042: ldloc.0 - IL_0043: ret + IL_0047: ldloc.0 + IL_0048: ret } // end of method EqualsMicroPerfAndCodeGenerationTests::f7 } // end of class EqualsMicroPerfAndCodeGenerationTests diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals08.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals08.il.bsl index 55da6ee102e..39e19138617 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals08.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals08.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Equals08 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Equals08 { - // Offset: 0x00000000 Length: 0x0000022D + // Offset: 0x00000000 Length: 0x00000245 } .mresource public FSharpOptimizationData.Equals08 { - // Offset: 0x00000238 Length: 0x000000AF + // Offset: 0x00000250 Length: 0x000000AF } .module Equals08.dll -// MVID: {59B18AEE-0759-659E-A745-0383EE8AB159} +// MVID: {5B2D7B11-0759-659E-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x01090000 +// Image base: 0x02FD0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -57,14 +57,14 @@ .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) .method public static bool f8() cil managed { - // Code size 68 (0x44) + // Code size 73 (0x49) .maxstack 5 .locals init ([0] bool x, [1] int32[] t1, [2] int32[] t2, [3] int32 i) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 5,5 : 8,29 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals08.fsx' + .line 5,5 : 8,29 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals08.fsx' IL_0000: ldc.i4.0 IL_0001: stloc.0 .line 6,6 : 8,31 '' @@ -90,26 +90,27 @@ .line 8,8 : 8,32 '' IL_002a: ldc.i4.0 IL_002b: stloc.3 - IL_002c: br.s IL_003a + IL_002c: br.s IL_003f .line 9,9 : 12,26 '' - IL_002e: ldloc.1 - IL_002f: ldloc.2 - IL_0030: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityIntrinsic(!!0, - !!0) - IL_0035: stloc.0 - IL_0036: ldloc.3 - IL_0037: ldc.i4.1 - IL_0038: add - IL_0039: stloc.3 + IL_002e: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_PER`1::get_EqualityComparer() + IL_0033: ldloc.1 + IL_0034: ldloc.2 + IL_0035: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_003a: stloc.0 + IL_003b: ldloc.3 + IL_003c: ldc.i4.1 + IL_003d: add + IL_003e: stloc.3 .line 8,8 : 8,32 '' - IL_003a: ldloc.3 - IL_003b: ldc.i4 0x989681 - IL_0040: blt.s IL_002e + IL_003f: ldloc.3 + IL_0040: ldc.i4 0x989681 + IL_0045: blt.s IL_002e .line 10,10 : 8,9 '' - IL_0042: ldloc.0 - IL_0043: ret + IL_0047: ldloc.0 + IL_0048: ret } // end of method EqualsMicroPerfAndCodeGenerationTests::f8 } // end of class EqualsMicroPerfAndCodeGenerationTests diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl index df7b115d207..726d2aea5e8 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Hash09 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Hash09 { - // Offset: 0x00000000 Length: 0x0000088E + // Offset: 0x00000000 Length: 0x00000892 } .mresource public FSharpOptimizationData.Hash09 { - // Offset: 0x00000898 Length: 0x00000686 + // Offset: 0x00000898 Length: 0x0000068E } .module Hash09.dll -// MVID: {59B18AEE-9642-77DB-A745-0383EE8AB159} +// MVID: {5B2D7B11-9642-77DB-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x00690000 +// Image base: 0x02990000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] !a V_4, [5] !a V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash09.fsx' + .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash09.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -560,8 +560,8 @@ instance bool Equals(class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1 obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 77 (0x4d) - .maxstack 4 + // Code size 87 (0x57) + .maxstack 5 .locals init ([0] class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1 V_0, [1] class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1 V_1, [2] !a V_2, @@ -570,13 +570,13 @@ IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un - IL_0004: brfalse.s IL_0045 + IL_0004: brfalse.s IL_004f .line 16707566,16707566 : 0,0 '' IL_0006: ldarg.1 IL_0007: ldnull IL_0008: cgt.un - IL_000a: brfalse.s IL_0043 + IL_000a: brfalse.s IL_004d .line 16707566,16707566 : 0,0 '' IL_000c: ldarg.0 @@ -592,41 +592,43 @@ IL_0019: ldloc.1 IL_001a: ldfld !0 class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::item1 IL_001f: stloc.3 - IL_0020: ldloc.2 - IL_0021: ldloc.3 - IL_0022: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0027: brfalse.s IL_0041 - - .line 16707566,16707566 : 0,0 '' - IL_0029: ldloc.0 - IL_002a: ldfld !0 class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::item2 - IL_002f: stloc.2 - IL_0030: ldloc.1 - IL_0031: ldfld !0 class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::item2 - IL_0036: stloc.3 - IL_0037: ldloc.2 - IL_0038: ldloc.3 - IL_0039: tail. - IL_003b: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0040: ret + IL_0020: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_0025: ldloc.2 + IL_0026: ldloc.3 + IL_0027: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_002c: brfalse.s IL_004b + + .line 16707566,16707566 : 0,0 '' + IL_002e: ldloc.0 + IL_002f: ldfld !0 class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::item2 + IL_0034: stloc.2 + IL_0035: ldloc.1 + IL_0036: ldfld !0 class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::item2 + IL_003b: stloc.3 + IL_003c: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_0041: ldloc.2 + IL_0042: ldloc.3 + IL_0043: tail. + IL_0045: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_004a: ret .line 16707566,16707566 : 0,0 '' - IL_0041: ldc.i4.0 - IL_0042: ret + IL_004b: ldc.i4.0 + IL_004c: ret .line 16707566,16707566 : 0,0 '' - IL_0043: ldc.i4.0 - IL_0044: ret + IL_004d: ldc.i4.0 + IL_004e: ret .line 16707566,16707566 : 0,0 '' - IL_0045: ldarg.1 - IL_0046: ldnull - IL_0047: cgt.un - IL_0049: ldc.i4.0 - IL_004a: ceq - IL_004c: ret + IL_004f: ldarg.1 + IL_0050: ldnull + IL_0051: cgt.un + IL_0053: ldc.i4.0 + IL_0054: ceq + IL_0056: ret } // end of method GenericKey`1::Equals .method public hidebysig virtual final diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash10.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash10.il.bsl index 20c3ceeb8f6..029eaf7814a 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash10.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash10.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Hash10 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Hash10 { - // Offset: 0x00000000 Length: 0x00000219 + // Offset: 0x00000000 Length: 0x00000231 } .mresource public FSharpOptimizationData.Hash10 { - // Offset: 0x00000220 Length: 0x000000A9 + // Offset: 0x00000238 Length: 0x000000A9 } .module Hash10.dll -// MVID: {59B18AEE-9661-78B4-A745-0383EE8AB159} +// MVID: {5B2D7B11-9661-78B4-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x01080000 +// Image base: 0x01690000 // =============== CLASS MEMBERS DECLARATION =================== @@ -57,13 +57,13 @@ .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) .method public static void f7() cil managed { - // Code size 44 (0x2c) + // Code size 49 (0x31) .maxstack 5 .locals init ([0] uint8[] arr, [1] int32 i, [2] int32 V_2) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 6,6 : 8,36 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash10.fsx' + .line 6,6 : 8,36 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash10.fsx' IL_0000: ldc.i4.0 IL_0001: ldc.i4.1 IL_0002: ldc.i4.s 100 @@ -76,22 +76,23 @@ .line 7,7 : 8,32 '' IL_0014: ldc.i4.0 IL_0015: stloc.1 - IL_0016: br.s IL_0023 + IL_0016: br.s IL_0028 .line 8,8 : 12,30 '' - IL_0018: ldloc.0 - IL_0019: call int32 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericHashIntrinsic(!!0) - IL_001e: stloc.2 - IL_001f: ldloc.1 - IL_0020: ldc.i4.1 - IL_0021: add - IL_0022: stloc.1 + IL_0018: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_PER`1::get_EqualityComparer() + IL_001d: ldloc.0 + IL_001e: callvirt instance int32 class [mscorlib]System.Collections.Generic.EqualityComparer`1::GetHashCode(!0) + IL_0023: stloc.2 + IL_0024: ldloc.1 + IL_0025: ldc.i4.1 + IL_0026: add + IL_0027: stloc.1 .line 7,7 : 8,32 '' - IL_0023: ldloc.1 - IL_0024: ldc.i4 0x989681 - IL_0029: blt.s IL_0018 + IL_0028: ldloc.1 + IL_0029: ldc.i4 0x989681 + IL_002e: blt.s IL_0018 - IL_002b: ret + IL_0030: ret } // end of method HashMicroPerfAndCodeGenerationTests::f7 } // end of class HashMicroPerfAndCodeGenerationTests diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash11.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash11.il.bsl index f4eb5020175..9848907a1b1 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash11.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash11.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Hash11 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Hash11 { - // Offset: 0x00000000 Length: 0x00000219 + // Offset: 0x00000000 Length: 0x00000231 } .mresource public FSharpOptimizationData.Hash11 { - // Offset: 0x00000220 Length: 0x000000A9 + // Offset: 0x00000238 Length: 0x000000A9 } .module Hash11.dll -// MVID: {59B18AEE-9661-78D3-A745-0383EE8AB159} +// MVID: {5B2D7B11-9661-78D3-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x002D0000 +// Image base: 0x027C0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -57,13 +57,13 @@ .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) .method public static void f8() cil managed { - // Code size 44 (0x2c) + // Code size 49 (0x31) .maxstack 5 .locals init ([0] int32[] arr, [1] int32 i, [2] int32 V_2) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 6,6 : 8,32 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash11.fsx' + .line 6,6 : 8,32 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash11.fsx' IL_0000: ldc.i4.0 IL_0001: ldc.i4.1 IL_0002: ldc.i4.s 100 @@ -76,22 +76,23 @@ .line 7,7 : 8,32 '' IL_0014: ldc.i4.0 IL_0015: stloc.1 - IL_0016: br.s IL_0023 + IL_0016: br.s IL_0028 .line 8,8 : 12,30 '' - IL_0018: ldloc.0 - IL_0019: call int32 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericHashIntrinsic(!!0) - IL_001e: stloc.2 - IL_001f: ldloc.1 - IL_0020: ldc.i4.1 - IL_0021: add - IL_0022: stloc.1 + IL_0018: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_PER`1::get_EqualityComparer() + IL_001d: ldloc.0 + IL_001e: callvirt instance int32 class [mscorlib]System.Collections.Generic.EqualityComparer`1::GetHashCode(!0) + IL_0023: stloc.2 + IL_0024: ldloc.1 + IL_0025: ldc.i4.1 + IL_0026: add + IL_0027: stloc.1 .line 7,7 : 8,32 '' - IL_0023: ldloc.1 - IL_0024: ldc.i4 0x989681 - IL_0029: blt.s IL_0018 + IL_0028: ldloc.1 + IL_0029: ldc.i4 0x989681 + IL_002e: blt.s IL_0018 - IL_002b: ret + IL_0030: ret } // end of method HashMicroPerfAndCodeGenerationTests::f8 } // end of class HashMicroPerfAndCodeGenerationTests From 3a30af3313e4ca1c1297333446cc73dfedef1649 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Sat, 23 Jun 2018 20:18:15 +1000 Subject: [PATCH 23/31] Fixed SurfaceArea --- .../SurfaceArea.coreclr.fs | 17 +++++++++++++++++ .../FSharp.Core.UnitTests/SurfaceArea.net40.fs | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs index 0f36e747976..ef363486f12 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs @@ -2229,11 +2229,25 @@ Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.String get_InputMu Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.String get_InputSequenceEmptyString() Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.String get_NoNegateMinValueString() Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.Type GetType() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: Boolean Equals(System.Object) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: Int32 GetHashCode() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.Collections.Generic.EqualityComparer`1[T] EqualityComparer +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.Collections.Generic.EqualityComparer`1[T] get_EqualityComparer() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.String ToString() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.Type GetType() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: Boolean Equals(System.Object) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: Int32 GetHashCode() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.Collections.Generic.EqualityComparer`1[T] EqualityComparer +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.Collections.Generic.EqualityComparer`1[T] get_EqualityComparer() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.String ToString() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.Type GetType() Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean Equals(System.Object) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple2[T1,T2](System.Collections.IEqualityComparer, System.Tuple`2[T1,T2], System.Tuple`2[T1,T2]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple3[T1,T2,T3](System.Collections.IEqualityComparer, System.Tuple`3[T1,T2,T3], System.Tuple`3[T1,T2,T3]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple4[T1,T2,T3,T4](System.Collections.IEqualityComparer, System.Tuple`4[T1,T2,T3,T4], System.Tuple`4[T1,T2,T3,T4]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple5[T1,T2,T3,T4,T5](System.Collections.IEqualityComparer, System.Tuple`5[T1,T2,T3,T4,T5], System.Tuple`5[T1,T2,T3,T4,T5]) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FSharpEqualityComparer_ER_Equals[T](T, T) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FSharpEqualityComparer_PER_Equals[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean GenericEqualityERIntrinsic[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean GenericEqualityIntrinsic[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean GenericEqualityWithComparerIntrinsic[T](System.Collections.IEqualityComparer, T, T) @@ -2250,6 +2264,7 @@ Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple2[T1,T2 Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple3[T1,T2,T3](System.Collections.IEqualityComparer, System.Tuple`3[T1,T2,T3]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple4[T1,T2,T3,T4](System.Collections.IEqualityComparer, System.Tuple`4[T1,T2,T3,T4]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple5[T1,T2,T3,T4,T5](System.Collections.IEqualityComparer, System.Tuple`5[T1,T2,T3,T4,T5]) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FSharpEqualityComparer_GetHashCode[T](T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericComparisonIntrinsic[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericComparisonWithComparerIntrinsic[T](System.Collections.IComparer, T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericHashIntrinsic[T](T) @@ -2257,6 +2272,8 @@ Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericHashWithCompa Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GetHashCode() Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 LimitedGenericHashIntrinsic[T](Int32, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 PhysicalHashIntrinsic[T](T) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T] +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T] Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: System.String ToString() Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: System.Type GetType() Microsoft.FSharp.Core.LanguagePrimitives+IntrinsicFunctions: Boolean Equals(System.Object) diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs index 54038feab93..7f08a8900ac 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs @@ -2316,11 +2316,25 @@ Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.String get_InputMu Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.String get_InputSequenceEmptyString() Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.String get_NoNegateMinValueString() Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.Type GetType() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: Boolean Equals(System.Object) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: Int32 GetHashCode() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.Collections.Generic.EqualityComparer`1[T] EqualityComparer +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.Collections.Generic.EqualityComparer`1[T] get_EqualityComparer() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.String ToString() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.Type GetType() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: Boolean Equals(System.Object) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: Int32 GetHashCode() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.Collections.Generic.EqualityComparer`1[T] EqualityComparer +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.Collections.Generic.EqualityComparer`1[T] get_EqualityComparer() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.String ToString() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.Type GetType() Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean Equals(System.Object) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple2[T1,T2](System.Collections.IEqualityComparer, System.Tuple`2[T1,T2], System.Tuple`2[T1,T2]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple3[T1,T2,T3](System.Collections.IEqualityComparer, System.Tuple`3[T1,T2,T3], System.Tuple`3[T1,T2,T3]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple4[T1,T2,T3,T4](System.Collections.IEqualityComparer, System.Tuple`4[T1,T2,T3,T4], System.Tuple`4[T1,T2,T3,T4]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple5[T1,T2,T3,T4,T5](System.Collections.IEqualityComparer, System.Tuple`5[T1,T2,T3,T4,T5], System.Tuple`5[T1,T2,T3,T4,T5]) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FSharpEqualityComparer_ER_Equals[T](T, T) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FSharpEqualityComparer_PER_Equals[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean GenericEqualityERIntrinsic[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean GenericEqualityIntrinsic[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean GenericEqualityWithComparerIntrinsic[T](System.Collections.IEqualityComparer, T, T) @@ -2337,6 +2351,7 @@ Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple2[T1,T2 Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple3[T1,T2,T3](System.Collections.IEqualityComparer, System.Tuple`3[T1,T2,T3]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple4[T1,T2,T3,T4](System.Collections.IEqualityComparer, System.Tuple`4[T1,T2,T3,T4]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple5[T1,T2,T3,T4,T5](System.Collections.IEqualityComparer, System.Tuple`5[T1,T2,T3,T4,T5]) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FSharpEqualityComparer_GetHashCode[T](T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericComparisonIntrinsic[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericComparisonWithComparerIntrinsic[T](System.Collections.IComparer, T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericHashIntrinsic[T](T) @@ -2344,6 +2359,8 @@ Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericHashWithCompa Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GetHashCode() Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 LimitedGenericHashIntrinsic[T](Int32, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 PhysicalHashIntrinsic[T](T) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T] +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T] Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: System.String ToString() Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: System.Type GetType() Microsoft.FSharp.Core.LanguagePrimitives+IntrinsicFunctions: Boolean Equals(System.Object) From 9e24a31b128ae9461726c68ad4b52492e541a2a7 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Sun, 24 Jun 2018 06:51:05 +1000 Subject: [PATCH 24/31] Ensure FSharp.Core has the optimization functions (and added comments) --- src/fsharp/Optimizer.fs | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/fsharp/Optimizer.fs b/src/fsharp/Optimizer.fs index 4921dcff817..d5bdf9a282d 100644 --- a/src/fsharp/Optimizer.fs +++ b/src/fsharp/Optimizer.fs @@ -2333,7 +2333,10 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) = match tcref.GeneratedHashAndEqualsValues with | Some (_, vref) -> Some (DevirtualizeApplication cenv env vref ty tyargs args m) | _ -> - Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_ER_Equals_vref ty tyargsOriginal args m) + // if type of generic argument has no generated equality operators, covert to "FSharpEqualityComparer_ER<'T>.EqualityComparer.Equals" + match cenv.g.fsharpEqualityComparer_ER_Equals_vref.TryDeref with + | ValueNone -> None // referencing old version of FSharp.Core.dll + | _ -> Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_ER_Equals_vref ty tyargsOriginal args m) // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericEqualityWithComparerFast | Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_equality_withc_inner_vref ty args -> @@ -2354,7 +2357,10 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) = let args2 = [x; mkRefTupledNoTypes cenv.g m [mkCoerceExpr(y, cenv.g.obj_ty, m, ty); (mkCallGetGenericPEREqualityComparer cenv.g m)]] Some (DevirtualizeApplication cenv env withcEqualsVal ty tyargs args2 m) | _ -> - Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_PER_Equals_vref ty tyargsOriginal args m) + // if type of generic argument has no generated equality operators, covert to "FSharpEqualityComparer_PER<'T>.EqualityComparer.Equals" + match cenv.g.fsharpEqualityComparer_PER_Equals_vref.TryDeref with + | ValueNone -> None // referencing old version of FSharp.Core.dll + | _ -> Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_PER_Equals_vref ty tyargsOriginal args m) // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericHashIntrinsic | Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_hash_inner_vref ty args -> @@ -2364,8 +2370,11 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) = | Some (_, withcGetHashCodeVal, _), [x] -> let args2 = [x; mkCallGetGenericEREqualityComparer cenv.g m] Some (DevirtualizeApplication cenv env withcGetHashCodeVal ty tyargs args2 m) - | _ -> - Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_GetHashCode_vref ty tyargsOriginal args m) + | _ -> + // if type of generic argument has no generated equality operators, covert to "FSharpEqualityComparer_PER<'T>.EqualityComparer.GetHashCode" + match cenv.g.fsharpEqualityComparer_GetHashCode_vref.TryDeref with + | ValueNone -> None // referencing old version of FSharp.Core.dll + | _ -> Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_GetHashCode_vref ty tyargsOriginal args m) // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericHashWithComparerIntrinsic | Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_hash_withc_inner_vref ty args -> @@ -2421,14 +2430,23 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) = | Some vref -> Some (DevirtualizeApplication cenv env vref ty tyargs (mkCallGetGenericPEREqualityComparer cenv.g m :: args) m) | None -> None + // "GenericEqualityIntrinsic" when found in a generic context, convert to "FSharpEqualityComparer_PER<'T>.EqualityComparer.Equals" | Expr.Val(v, _, _), [(TType_var t) as ty], _ when (not cenv.g.compilingFslib) && valRefEq cenv.g v cenv.g.generic_equality_per_inner_vref && t.Rigidity = TyparRigidity.Rigid -> - Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_PER_Equals_vref ty tyargs args m) + match cenv.g.fsharpEqualityComparer_PER_Equals_vref.TryDeref with + | ValueNone -> None // referencing old version of FSharp.Core.dll + | _ -> Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_PER_Equals_vref ty tyargs args m) + // "GenericEqualityERIntrinsic" when found in a generic context, convert to "FSharpEqualityComparer_ER<'T>.EqualityComparer.Equals" | Expr.Val(v, _, _), [(TType_var t) as ty], _ when (not cenv.g.compilingFslib) && valRefEq cenv.g v cenv.g.generic_equality_er_inner_vref && t.Rigidity = TyparRigidity.Rigid -> - Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_ER_Equals_vref ty tyargs args m) + match cenv.g.fsharpEqualityComparer_ER_Equals_vref.TryDeref with + | ValueNone -> None // referencing old version of FSharp.Core.dll + | _ -> Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_ER_Equals_vref ty tyargs args m) + // "GenericHashIntrinsic" when found in a generic context, convert to "FSharpEqualityComparer_PER<'T>.EqualityComparer.GetHashCode" | Expr.Val(v, _, _), [(TType_var t) as ty], _ when (not cenv.g.compilingFslib) && valRefEq cenv.g v cenv.g.generic_hash_inner_vref && t.Rigidity = TyparRigidity.Rigid -> - Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_GetHashCode_vref ty tyargs args m) + match cenv.g.fsharpEqualityComparer_GetHashCode_vref.TryDeref with + | ValueNone -> None // referencing old version of FSharp.Core.dll + | _ -> Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_GetHashCode_vref ty tyargs args m) // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericComparisonWithComparerIntrinsic for tuple types | Expr.Val(v, _, _), [ty], _ when valRefEq cenv.g v cenv.g.generic_comparison_withc_inner_vref && isRefTupleTy cenv.g ty -> From 4130d6a006db4d57f3a062738ffec097112c6e23 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Mon, 25 Jun 2018 19:20:17 +1000 Subject: [PATCH 25/31] Remove IL bloat from this PR as per https://github.com/Microsoft/visualfsharp/issues/5212 --- src/fsharp/FSharp.Core/prim-types.fs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index fd7c235cb58..c5a590da292 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1719,12 +1719,26 @@ namespace Microsoft.FSharp.Core | null, _ -> false | _, null -> false | (:? (obj[]) as arr1), (:? (obj[]) as arr2) -> GenericEqualityObjArray er iec arr1 arr2 + | _ -> + match xobj,yobj with | (:? (byte[]) as arr1), (:? (byte[]) as arr2) -> GenericEqualityByteArray arr1 arr2 + | _ -> + match xobj,yobj with | (:? (int32[]) as arr1), (:? (int32[]) as arr2) -> GenericEqualityInt32Array arr1 arr2 + | _ -> + match xobj,yobj with | (:? (int64[]) as arr1), (:? (int64[]) as arr2) -> GenericEqualityInt64Array arr1 arr2 + | _ -> + match xobj,yobj with | (:? (char[]) as arr1), (:? (char[]) as arr2) -> GenericEqualityCharArray arr1 arr2 + | _ -> + match xobj,yobj with | (:? (float32[]) as arr1), (:? (float32[]) as arr2) -> GenericEqualitySingleArray er arr1 arr2 + | _ -> + match xobj,yobj with | (:? (float[]) as arr1), (:? (float[]) as arr2) -> GenericEqualityDoubleArray er arr1 arr2 + | _ -> + match xobj,yobj with | (:? System.Array as arr1), (:? System.Array as arr2) -> GenericEqualityArbArray er iec arr1 arr2 | _ -> raise (Exception "invalid logic - expected array") From e7d618b1a8f643ff94a296925e53e37cdc42979c Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Tue, 26 Jun 2018 19:50:58 +1000 Subject: [PATCH 26/31] Remove null checks for IStructuralEquality Value Types --- src/fsharp/FSharp.Core/prim-types.fs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index c5a590da292..fe788bd3291 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1649,6 +1649,7 @@ namespace Microsoft.FSharp.Core res let isStructuralEquatable (ty:Type) = typeof.IsAssignableFrom ty + let isValueTypeStructuralEquatable (ty:Type) = isStructuralEquatable ty && ty.IsValueType let isArray (ty:Type) = ty.IsArray || (typeof.IsAssignableFrom ty) let canUseDefaultEqualityComparer er (rootType:Type) = @@ -1772,6 +1773,11 @@ namespace Microsoft.FSharp.Core | :? IStructuralEquatable as a -> a.GetHashCode fsEqualityComparerUnlimitedHashingPER | _ -> raise (Exception "invalid logic - expected IStructuralEquatable") } + let structuralEqualityComparerValueType<'T> comparer = + { new EqualityComparer<'T>() with + member __.Equals (x,y) = ((box x):?>IStructuralEquatable).Equals (y, comparer) + member __.GetHashCode x = ((box x):?>IStructuralEquatable).GetHashCode fsEqualityComparerUnlimitedHashingPER } + let unknownEqualityComparer<'T> er comparer = { new EqualityComparer<'T>() with member __.Equals (x,y) = GenericEqualityObj er comparer (box x, box y) @@ -1779,14 +1785,16 @@ namespace Microsoft.FSharp.Core let getGenericEquality<'T> er = match tryGetFSharpEqualityComparer er typeof<'T> with - | :? EqualityComparer<'T> as call -> call - | _ when canUseDefaultEqualityComparer er typeof<'T> -> EqualityComparer<'T>.Default - | _ when isArray typeof<'T> && er -> arrayEqualityComparer true fsEqualityComparerUnlimitedHashingER - | _ when isArray typeof<'T> -> arrayEqualityComparer false fsEqualityComparerUnlimitedHashingPER - | _ when isStructuralEquatable typeof<'T> && er -> structuralEqualityComparer fsEqualityComparerUnlimitedHashingER - | _ when isStructuralEquatable typeof<'T> -> structuralEqualityComparer fsEqualityComparerUnlimitedHashingPER - | _ when er -> unknownEqualityComparer true fsEqualityComparerUnlimitedHashingER - | _ -> unknownEqualityComparer false fsEqualityComparerUnlimitedHashingPER + | :? EqualityComparer<'T> as call -> call + | _ when canUseDefaultEqualityComparer er typeof<'T> -> EqualityComparer<'T>.Default + | _ when isArray typeof<'T> && er -> arrayEqualityComparer true fsEqualityComparerUnlimitedHashingER + | _ when isArray typeof<'T> -> arrayEqualityComparer false fsEqualityComparerUnlimitedHashingPER + | _ when isValueTypeStructuralEquatable typeof<'T> && er -> structuralEqualityComparerValueType fsEqualityComparerUnlimitedHashingER + | _ when isValueTypeStructuralEquatable typeof<'T> -> structuralEqualityComparerValueType fsEqualityComparerUnlimitedHashingPER + | _ when isStructuralEquatable typeof<'T> && er -> structuralEqualityComparer fsEqualityComparerUnlimitedHashingER + | _ when isStructuralEquatable typeof<'T> -> structuralEqualityComparer fsEqualityComparerUnlimitedHashingPER + | _ when er -> unknownEqualityComparer true fsEqualityComparerUnlimitedHashingER + | _ -> unknownEqualityComparer false fsEqualityComparerUnlimitedHashingPER [] type FSharpEqualityComparer_ER<'T> private () = From a72a1deb73137f0709043be55030288d17fdb337 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Wed, 27 Jun 2018 15:58:29 +1000 Subject: [PATCH 27/31] Consolidated Type Specific Array Equality functions --- src/fsharp/FSharp.Core/prim-types.fs | 126 +++++---------------------- 1 file changed, 20 insertions(+), 106 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index fe788bd3291..87ce50eb3d1 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1378,105 +1378,30 @@ namespace Microsoft.FSharp.Core when 'T : char = not (# "clt" x y : bool #) when 'T : decimal = System.Decimal.op_GreaterThanOrEqual ((# "" x:decimal #), (# "" y:decimal #)) - //------------------------------------------------------------------------- // LanguagePrimitives.HashCompare: EQUALITY //------------------------------------------------------------------------- - - /// optimized case: Core implementation of structural equality on arrays. - let GenericEqualityByteArray (x:byte[]) (y:byte[]) : bool= - let lenx = x.Length - let leny = y.Length - let c = (lenx = leny) - if not c then c - else - let mutable i = 0 - let mutable res = true - while i < lenx do - let c = byteEq (get x i) (get y i) - if not c then (res <- false; i <- lenx) - else i <- i + 1 - res - - /// optimized case: Core implementation of structural equality on arrays. - let GenericEqualityInt32Array (x:int[]) (y:int[]) : bool= - let lenx = x.Length - let leny = y.Length - let c = (lenx = leny) - if not c then c - else - let mutable i = 0 - let mutable res = true - while i < lenx do - let c = int32Eq (get x i) (get y i) - if not c then (res <- false; i <- lenx) - else i <- i + 1 - res - - /// optimized case: Core implementation of structural equality on arrays - let GenericEqualitySingleArray er (x:float32[]) (y:float32[]) : bool= - let lenx = x.Length - let leny = y.Length - let f32eq x y = if er && not(float32Eq x x) && not(float32Eq y y) then true else (float32Eq x y) - let c = (lenx = leny) - if not c then c - else - let mutable i = 0 - let mutable res = true - while i < lenx do - let c = f32eq (get x i) (get y i) - if not c then (res <- false; i <- lenx) - else i <- i + 1 - res - - /// optimized case: Core implementation of structural equality on arrays. - let GenericEqualityDoubleArray er (x:float[]) (y:float[]) : bool= - let lenx = x.Length - let leny = y.Length - let c = (lenx = leny) - let feq x y = if er && not(floatEq x x) && not(floatEq y y) then true else (floatEq x y) - if not c then c - else - let mutable i = 0 - let mutable res = true - while i < lenx do - let c = feq (get x i) (get y i) - if not c then (res <- false; i <- lenx) - else i <- i + 1 - res - - /// optimized case: Core implementation of structural equality on arrays. - let GenericEqualityCharArray (x:char[]) (y:char[]) : bool= - let lenx = x.Length - let leny = y.Length - let c = (lenx = leny) - if not c then c - else - let mutable i = 0 - let mutable res = true - while i < lenx do - let c = charEq (get x i) (get y i) - if not c then (res <- false; i <- lenx) - else i <- i + 1 - res - - /// optimized case: Core implementation of structural equality on arrays. - let GenericEqualityInt64Array (x:int64[]) (y:int64[]) : bool= + let inline ArrayEquality<'T> (f:'T->'T->bool) (x:'T[]) (y:'T[]) : bool = let lenx = x.Length let leny = y.Length - let c = (lenx = leny) - if not c then c - else - let mutable i = 0 - let mutable res = true - while i < lenx do - let c = int64Eq (get x i) (get y i) - if not c then (res <- false; i <- lenx) - else i <- i + 1 - res - - + let rec loop i = + if i = lenx then true + elif f (get x i) (get y i) then loop (i+1) + else false + (lenx = leny) && loop 0 + + let inline ArrayEqualityWithERFlag<'T> (er:bool) (f:'T->'T->bool) (x:'T[]) (y:'T[]) : bool = + if er + then ArrayEquality (fun x y -> (not (f x x) && not (f y y)) || (f x y)) x y + else ArrayEquality f x y + + let GenericEqualityByteArray x y = ArrayEquality byteEq x y + let GenericEqualityInt32Array x y = ArrayEquality int32Eq x y + let GenericEqualitySingleArray er x y = ArrayEqualityWithERFlag er float32Eq x y + let GenericEqualityDoubleArray er x y = ArrayEqualityWithERFlag er floatEq x y + let GenericEqualityCharArray x y = ArrayEquality charEq x y + let GenericEqualityInt64Array x y = ArrayEquality int64Eq x y /// The core implementation of generic equality between two objects. This corresponds /// to th e pseudo-code in the F# language spec. @@ -1634,19 +1559,8 @@ namespace Microsoft.FSharp.Core #endif /// optimized case: Core implementation of structural equality on object arrays. - and GenericEqualityObjArray er iec (x:obj[]) (y:obj[]) : bool = - let lenx = x.Length - let leny = y.Length - let c = (lenx = leny ) - if not c then c - else - let mutable i = 0 - let mutable res = true - while i < lenx do - let c = GenericEqualityObj er iec ((get x i),(get y i)) - if not c then (res <- false; i <- lenx) - else i <- i + 1 - res + and GenericEqualityObjArray er iec (xarray:obj[]) (yarray:obj[]) : bool = + ArrayEquality (fun x y -> GenericEqualityObj er iec (x, y)) xarray yarray let isStructuralEquatable (ty:Type) = typeof.IsAssignableFrom ty let isValueTypeStructuralEquatable (ty:Type) = isStructuralEquatable ty && ty.IsValueType From 469f55cd82548c9981905c2f4f95456c9ef6ce75 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Thu, 28 Jun 2018 08:59:46 +1000 Subject: [PATCH 28/31] Fix compiler call via FSharpFunc.Invoke rather than direct --- src/fsharp/FSharp.Core/prim-types.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 87ce50eb3d1..85079264009 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1657,7 +1657,7 @@ namespace Microsoft.FSharp.Core | (:? System.Array as arr1), (:? System.Array as arr2) -> GenericEqualityArbArray er iec arr1 arr2 | _ -> raise (Exception "invalid logic - expected array") - let getHashCode iec (xobj:obj) = + let getHashCode (iec, xobj:obj) = match xobj with | null -> 0 | :? (obj[]) as oa -> GenericHashObjArray iec oa @@ -1669,7 +1669,7 @@ namespace Microsoft.FSharp.Core { new EqualityComparer<'T>() with member __.Equals (x, y) = arrayEquals er comparer (box x) (box y) - member __.GetHashCode x = getHashCode fsEqualityComparerUnlimitedHashingPER (box x) } + member __.GetHashCode x = getHashCode (fsEqualityComparerUnlimitedHashingPER, box x) } let structuralEqualityComparer<'T> comparer = { new EqualityComparer<'T>() with From 0e7b688912dfbdb12e82d8908bba3199b3ef6dd8 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Thu, 28 Jun 2018 11:35:25 +1000 Subject: [PATCH 29/31] Replaced duplicated GetHashCode code with inline function --- src/fsharp/FSharp.Core/prim-types.fs | 77 ++++++++-------------------- 1 file changed, 20 insertions(+), 57 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 85079264009..7084299bde7 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -806,66 +806,29 @@ namespace Microsoft.FSharp.Core let inline HashCombine nr x y = (x <<< 1) + y + 631 * nr - let GenericHashObjArray (iec : System.Collections.IEqualityComparer) (x: obj[]) : int = - let len = x.Length - let mutable i = len - 1 - if i > defaultHashNodes then i <- defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= 0) do - // NOTE: GenericHash* call decreases nr - acc <- HashCombine i acc (iec.GetHashCode(x.GetValue(i))); - i <- i - 1 - acc - - // optimized case - byte arrays - let GenericHashByteArray (x: byte[]) : int = - let len = length x - let mutable i = len - 1 - if i > defaultHashNodes then i <- defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= 0) do - acc <- HashCombine i acc (intOfByte (get x i)); - i <- i - 1 - acc - - // optimized case - int arrays - let GenericHashInt32Array (x: int[]) : int = - let len = length x - let mutable i = len - 1 - if i > defaultHashNodes then i <- defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= 0) do - acc <- HashCombine i acc (get x i); - i <- i - 1 - acc - - // optimized case - int arrays - let GenericHashInt64Array (x: int64[]) : int = - let len = length x - let mutable i = len - 1 - if i > defaultHashNodes then i <- defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= 0) do - acc <- HashCombine i acc (int32 (get x i)); - i <- i - 1 - acc + let inline ArrayHashing<'element,'array when 'array :> System.Array> get lowerBound (f:'element->int) (x:'array) : int = + let rec loop acc i = + if i < lowerBound then acc + else loop (HashCombine i acc (f (get x i))) (i-1) + + let lastIdx = + let upperBound = lowerBound+defaultHashNodes + match lowerBound+x.Length-1 with + | oversized when oversized > upperBound -> upperBound + | good -> good + + loop 0 lastIdx + + let GenericHashObjArray (iec:System.Collections.IEqualityComparer) (x:obj[]) = ArrayHashing get 0 iec.GetHashCode x + let GenericHashByteArray (x:byte[]) = ArrayHashing get 0 intOfByte x + let GenericHashInt32Array (x:int32[]) = ArrayHashing get 0 (fun x -> x) x + let GenericHashInt64Array (x:int64[]) = ArrayHashing get 0 int32 x // special case - arrays do not by default have a decent structural hashing function let GenericHashArbArray (iec : System.Collections.IEqualityComparer) (x: System.Array) : int = - match x.Rank with - | 1 -> - let b = x.GetLowerBound(0) - let len = x.Length - let mutable i = b + len - 1 - if i > b + defaultHashNodes then i <- b + defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= b) do - // NOTE: GenericHash* call decreases nr - acc <- HashCombine i acc (iec.GetHashCode(x.GetValue(i))); - i <- i - 1 - acc - | _ -> - HashCombine 10 (x.GetLength(0)) (x.GetLength(1)) + match x.Rank with + | 1 -> ArrayHashing (fun a i -> a.GetValue i) (x.GetLowerBound 0) iec.GetHashCode x + | _ -> HashCombine 10 (x.GetLength(0)) (x.GetLength(1)) // Core implementation of structural hashing, corresponds to pseudo-code in the // F# Language spec. Searches for the IStructuralHash interface, otherwise uses GetHashCode(). From 4dc1a797a8f0e1200adf0127ec85bdca4fa6c6b4 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Thu, 28 Jun 2018 12:27:58 +1000 Subject: [PATCH 30/31] Common array EqualityComparers (for types that previously had specialized GetHashCode) --- src/fsharp/FSharp.Core/prim-types.fs | 90 +++++++++++++++------------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 7084299bde7..f512e7527b8 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1590,49 +1590,55 @@ namespace Microsoft.FSharp.Core | true, ty when ty.Equals typeof -> box EqualityComparer.Default | _ -> null - let arrayEqualityComparer<'T> er comparer = - let arrayEquals (er:bool) (iec:System.Collections.IEqualityComparer) (xobj:obj) (yobj:obj) : bool = - match xobj,yobj with - | null, null -> true - | null, _ -> false - | _, null -> false - | (:? (obj[]) as arr1), (:? (obj[]) as arr2) -> GenericEqualityObjArray er iec arr1 arr2 - | _ -> - match xobj,yobj with - | (:? (byte[]) as arr1), (:? (byte[]) as arr2) -> GenericEqualityByteArray arr1 arr2 - | _ -> - match xobj,yobj with - | (:? (int32[]) as arr1), (:? (int32[]) as arr2) -> GenericEqualityInt32Array arr1 arr2 - | _ -> - match xobj,yobj with - | (:? (int64[]) as arr1), (:? (int64[]) as arr2) -> GenericEqualityInt64Array arr1 arr2 - | _ -> - match xobj,yobj with - | (:? (char[]) as arr1), (:? (char[]) as arr2) -> GenericEqualityCharArray arr1 arr2 - | _ -> - match xobj,yobj with - | (:? (float32[]) as arr1), (:? (float32[]) as arr2) -> GenericEqualitySingleArray er arr1 arr2 - | _ -> - match xobj,yobj with - | (:? (float[]) as arr1), (:? (float[]) as arr2) -> GenericEqualityDoubleArray er arr1 arr2 - | _ -> - match xobj,yobj with - | (:? System.Array as arr1), (:? System.Array as arr2) -> GenericEqualityArbArray er iec arr1 arr2 - | _ -> raise (Exception "invalid logic - expected array") - - let getHashCode (iec, xobj:obj) = - match xobj with - | null -> 0 - | :? (obj[]) as oa -> GenericHashObjArray iec oa - | :? (byte[]) as ba -> GenericHashByteArray ba - | :? (int[]) as ba -> GenericHashInt32Array ba - | :? (int64[]) as ba -> GenericHashInt64Array ba - | :? System.Array as a -> GenericHashArbArray iec a - | _ -> raise (Exception "invalid logic - expected array") + let inline nullableEqualityComparer<'a when 'a : null> equals getHashCode = + box { new EqualityComparer<'a>() with + member __.Equals (x,y) = + match x, y with + | null, null -> true + | null, _ -> false + | _, null -> false + | _ -> equals x y + + member __.GetHashCode x = + match x with + | null -> 0 + | _ -> getHashCode x } + + let tryGetFSharpArrayEqualityComparer (ty:Type) er comparer : obj = + if ty.Equals typeof then nullableEqualityComparer (fun x y -> GenericEqualityObjArray er comparer x y) (GenericHashObjArray fsEqualityComparerUnlimitedHashingPER) + elif ty.Equals typeof then nullableEqualityComparer GenericEqualityByteArray GenericHashByteArray + elif ty.Equals typeof then nullableEqualityComparer GenericEqualityInt32Array GenericHashInt32Array + elif ty.Equals typeof then nullableEqualityComparer GenericEqualityInt64Array GenericHashInt64Array + else null - { new EqualityComparer<'T>() with - member __.Equals (x, y) = arrayEquals er comparer (box x) (box y) - member __.GetHashCode x = getHashCode (fsEqualityComparerUnlimitedHashingPER, box x) } + let arrayEqualityComparer<'T> er comparer = + match tryGetFSharpArrayEqualityComparer typeof<'T> er comparer with + | :? EqualityComparer<'T> as arrayComparer -> arrayComparer + | _ -> + { new EqualityComparer<'T>() with + member __.Equals (x, y) = + let xobj, yobj = box x, box y + match xobj,yobj with + | null, null -> true + | null, _ -> false + | _, null -> false + | (:? (char[]) as arr1), (:? (char[]) as arr2) -> GenericEqualityCharArray arr1 arr2 + | _ -> + match xobj,yobj with + | (:? (float32[]) as arr1), (:? (float32[]) as arr2) -> GenericEqualitySingleArray er arr1 arr2 + | _ -> + match xobj,yobj with + | (:? (float[]) as arr1), (:? (float[])as arr2) -> GenericEqualityDoubleArray er arr1 arr2 + | _ -> + match xobj,yobj with + | (:? System.Array as arr1), (:? System.Array as arr2) -> GenericEqualityArbArray er comparer arr1 arr2 + | _ -> raise (Exception "invalid logic - expected array") + + member __.GetHashCode x = + match box x with + | null -> 0 + | :? System.Array as a -> GenericHashArbArray fsEqualityComparerUnlimitedHashingPER a + | _ -> raise (Exception "invalid logic - expected array") } let structuralEqualityComparer<'T> comparer = { new EqualityComparer<'T>() with From 5b80e902e50d9ffc43de1d48748ad735d6319403 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Wed, 11 Jul 2018 16:59:13 +1000 Subject: [PATCH 31/31] Fix regression in regards to hash code on fast-path covariant arrays (+ comments) --- src/fsharp/FSharp.Core/prim-types.fs | 30 ++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index f512e7527b8..9da460dd5fa 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -839,6 +839,12 @@ namespace Microsoft.FSharp.Core match x with | null -> 0 | (:? System.Array as a) -> + // due to the rules of the CLI type system, array casts are "assignment compatible" + // see: https://blogs.msdn.microsoft.com/ericlippert/2009/09/24/why-is-covariance-of-value-typed-arrays-inconsistent/ + // this means that the cast and comparison for byte will also handle sbyte, int32 handle uint32, + // and int64 handle uint64. The hash code of an individual array element is different for the different + // types, but it is irrelevant for the creation of the hash code - but this is to be replicated in + // the tryGetFSharpArrayEqualityComparer function. match a with | :? (obj[]) as oa -> GenericHashObjArray iec oa | :? (byte[]) as ba -> GenericHashByteArray ba @@ -1383,10 +1389,12 @@ namespace Microsoft.FSharp.Core | (:? string as xs),(:? string as ys) -> System.String.Equals(xs,ys) // Permit structural equality on arrays | (:? System.Array as arr1),_ -> + // due to the rules of the CLI type system, array casts are "assignment compatible" + // see: https://blogs.msdn.microsoft.com/ericlippert/2009/09/24/why-is-covariance-of-value-typed-arrays-inconsistent/ + // this means that the cast and comparison for byte will also handle sbyte, int32 handle uint32, + // and int64 handle uint64. Equality will still be correct. match arr1,yobj with - // Fast path | (:? (obj[]) as arr1), (:? (obj[]) as arr2) -> GenericEqualityObjArray er iec arr1 arr2 - // Fast path | (:? (byte[]) as arr1), (:? (byte[]) as arr2) -> GenericEqualityByteArray arr1 arr2 | (:? (int32[]) as arr1), (:? (int32[]) as arr2) -> GenericEqualityInt32Array arr1 arr2 | (:? (int64[]) as arr1), (:? (int64[]) as arr2) -> GenericEqualityInt64Array arr1 arr2 @@ -1604,11 +1612,21 @@ namespace Microsoft.FSharp.Core | null -> 0 | _ -> getHashCode x } + let inline castNullableEqualityComparer<'fromType, 'toType when 'toType : null and 'fromType : null> (equals:'toType->'toType->bool) (getHashCode:'toType->int) = + let castEquals (lhs:'fromType) (rhs:'fromType) = equals (unboxPrim lhs) (unboxPrim rhs) + let castGetHashCode (o:'fromType) = getHashCode (unboxPrim o) + nullableEqualityComparer castEquals castGetHashCode + let tryGetFSharpArrayEqualityComparer (ty:Type) er comparer : obj = - if ty.Equals typeof then nullableEqualityComparer (fun x y -> GenericEqualityObjArray er comparer x y) (GenericHashObjArray fsEqualityComparerUnlimitedHashingPER) - elif ty.Equals typeof then nullableEqualityComparer GenericEqualityByteArray GenericHashByteArray - elif ty.Equals typeof then nullableEqualityComparer GenericEqualityInt32Array GenericHashInt32Array - elif ty.Equals typeof then nullableEqualityComparer GenericEqualityInt64Array GenericHashInt64Array + // the casts here between byte+sbyte, int32+uint32 and int64+uint64 are here to replicate the behaviour + // in GenericHashParamObj + if ty.Equals typeof then nullableEqualityComparer (fun x y -> GenericEqualityObjArray er comparer x y) (GenericHashObjArray fsEqualityComparerUnlimitedHashingPER) + elif ty.Equals typeof then nullableEqualityComparer GenericEqualityByteArray GenericHashByteArray + elif ty.Equals typeof then castNullableEqualityComparer GenericEqualityByteArray GenericHashByteArray + elif ty.Equals typeof then nullableEqualityComparer GenericEqualityInt32Array GenericHashInt32Array + elif ty.Equals typeof then castNullableEqualityComparer GenericEqualityInt32Array GenericHashInt32Array + elif ty.Equals typeof then nullableEqualityComparer GenericEqualityInt64Array GenericHashInt64Array + elif ty.Equals typeof then castNullableEqualityComparer GenericEqualityInt64Array GenericHashInt64Array else null let arrayEqualityComparer<'T> er comparer =