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