Skip to content

Commit 34a3e84

Browse files
committed
Added optimize equality switch. Figuring out rules for equality optimizations. Thank you Don.
1 parent 3225121 commit 34a3e84

9 files changed

Lines changed: 74 additions & 3 deletions

File tree

src/absil/il.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,6 +2582,8 @@ type ILGlobals(primaryScopeRef) =
25822582
let m_typ_IntPtr = ILType.Value (mkILNonGenericTySpec (m_mkSysILTypeRef tname_IntPtr))
25832583
let m_typ_UIntPtr = ILType.Value (mkILNonGenericTySpec (m_mkSysILTypeRef tname_UIntPtr))
25842584

2585+
let m_typ_IEquatableT = mkILBoxedType (mkILTySpec (m_mkSysILTypeRef "System.IEquatable`1", [ mkILTyvarTy 1us ]))
2586+
25852587
member x.primaryAssemblyScopeRef = m_typ_Object.TypeRef.Scope
25862588
member x.primaryAssemblyName = m_typ_Object.TypeRef.Scope.AssemblyRef.Name
25872589
member x.typ_Object = m_typ_Object
@@ -2602,6 +2604,7 @@ type ILGlobals(primaryScopeRef) =
26022604
member x.typ_Double = m_typ_Double
26032605
member x.typ_Bool = m_typ_Bool
26042606
member x.typ_Char = m_typ_Char
2607+
member x.typ_IEquatableT = m_typ_IEquatableT
26052608

26062609
/// For debugging
26072610
[<DebuggerBrowsable(DebuggerBrowsableState.Never)>]

src/absil/il.fsi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,6 +1583,7 @@ type ILGlobals =
15831583
member typ_Double: ILType
15841584
member typ_Bool: ILType
15851585
member typ_Char: ILType
1586+
member typ_IEquatableT: ILType
15861587

15871588

15881589
/// Build the table of commonly used references given functions to find types in system assemblies

src/fsharp/CompileOptions.fs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,11 @@ let SetOptimizeOn(tcConfigB : TcConfigBuilder) =
416416
let SetOptimizeSwitch (tcConfigB : TcConfigBuilder) switch =
417417
if (switch = OptionSwitch.On) then SetOptimizeOn(tcConfigB) else SetOptimizeOff(tcConfigB)
418418

419+
let SetSpecificOptimizeSwitch (tcConfigB : TcConfigBuilder) n switch =
420+
match n with
421+
| "equality" -> tcConfigB.optSettings <- { tcConfigB.optSettings with optimizeComparisonLogic = (switch = OptionSwitch.On) }
422+
| _ -> failwithf "dodgy flag %s" n
423+
419424
let SetTailcallSwitch (tcConfigB : TcConfigBuilder) switch =
420425
tcConfigB.emitTailcalls <- (switch = OptionSwitch.On)
421426

@@ -696,6 +701,8 @@ let codeGenerationFlags isFsi (tcConfigB : TcConfigBuilder) =
696701
let codegen =
697702
[CompilerOption("optimize", tagNone, OptionSwitch (SetOptimizeSwitch tcConfigB) , None,
698703
Some (FSComp.SR.optsOptimize()))
704+
CompilerOption("optimize", tagNone, OptionStringListSwitch (SetSpecificOptimizeSwitch tcConfigB) , None,
705+
Some (FSComp.SR.optsOptimize()))
699706
CompilerOption("tailcalls", tagNone, OptionSwitch (SetTailcallSwitch tcConfigB), None,
700707
Some (FSComp.SR.optsTailcalls()))
701708
CompilerOption("deterministic", tagNone, OptionSwitch (SetDeterministicSwitch tcConfigB), None,

src/fsharp/Optimizer.fs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ type OptimizationSettings =
261261
jitOptUser : bool option
262262
localOptUser : bool option
263263
crossModuleOptUser : bool option
264+
optimizeComparisonLogic : bool
264265
/// size after which we start chopping methods in two, though only at match targets
265266
bigTargetSize : int
266267
/// size after which we start enforcing splitting sub-expressions to new methods, to avoid hitting .NET IL limitations
@@ -278,6 +279,7 @@ type OptimizationSettings =
278279
{ abstractBigTargets = false
279280
jitOptUser = None
280281
localOptUser = None
282+
optimizeComparisonLogic = false
281283
/// size after which we start chopping methods in two, though only at match targets
282284
bigTargetSize = 100
283285
/// size after which we start enforcing splitting sub-expressions to new methods, to avoid hitting .NET IL limitations
@@ -2472,7 +2474,34 @@ and DevirtualizeApplication cenv env (vref:ValRef) ty tyargs args m =
24722474
let wrap, args = TakeAddressOfStructArgumentIfNeeded cenv vref ty args m
24732475
let transformedExpr = wrap (MakeApplicationAndBetaReduce cenv.g (exprForValRef m vref, vref.Type, (if isNil tyargs then [] else [tyargs]), args, m))
24742476
OptimizeExpr cenv env transformedExpr
2475-
2477+
2478+
and DevirtualizeGenericEqualityIntrinsic cenv env receiver arg m =
2479+
let call = mkCall_IEquatableT_Equals cenv.g m receiver arg
2480+
OptimizeExpr cenv env call
2481+
2482+
/// Check if a type 'ty' implements 'IEquatable<ty>'
2483+
and IsIEquatableTy cenv m ty =
2484+
let searchTy = mkAppTy cenv.g.system_GenericIEquatable_tcref [ty]
2485+
ExistsInEntireHierarchyOfType (fun t -> typeEquiv cenv.g t searchTy) cenv.g cenv.amap m AllowMultiIntfInstantiations.Yes ty
2486+
2487+
/// Check if a type 'ty' implements 'IStructuralEquatable'
2488+
and IsIStructuralEquatableTy cenv m ty =
2489+
let searchTy = mkAppTy cenv.g.tcref_System_IStructuralEquatable []
2490+
ExistsInEntireHierarchyOfType (fun t -> typeEquiv cenv.g t searchTy) cenv.g cenv.amap m AllowMultiIntfInstantiations.Yes ty
2491+
2492+
/// Check if a type 'ty' is a structural F# type with default structural equality semantics
2493+
and IsGeneratedHashAndEqualsTy g ty =
2494+
isAnonRecdTy g ty ||
2495+
(isAppTy g ty &&
2496+
(let tcref = tcrefOfAppTy g ty
2497+
tcref.GeneratedHashAndEqualsValues.IsSome && tcref.GeneratedHashAndEqualsWithComparerValues.IsSome))
2498+
2499+
/// Check if we can (perhaps optimistically) convert the reduced optimization of 'a = b' to '(a :> IEquatable).Equals(b)'
2500+
and CanOptimizeGenericEqualityIntrinsicToIEquatableEquals cenv m ty =
2501+
IsIEquatableTy cenv m ty &&
2502+
not (isAnyTupleTy cenv.g ty) &&
2503+
(IsGeneratedHashAndEqualsTy cenv.g ty || (cenv.settings.optimizeComparisonLogic && not (IsIStructuralEquatableTy cenv m ty)))
2504+
24762505
and TryDevirtualizeApplication cenv env (f, tyargs, args, m) =
24772506
match f, tyargs, args with
24782507

@@ -2529,7 +2558,7 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) =
25292558
| _ -> None
25302559

25312560
// Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericEqualityWithComparer
2532-
| Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_equality_per_inner_vref ty args && not(isRefTupleTy cenv.g ty) ->
2561+
| Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_equality_withc_outer_vref ty args && not(isRefTupleTy cenv.g ty) ->
25332562
let tcref, tyargs = StripToNominalTyconRef cenv ty
25342563
match tcref.GeneratedHashAndEqualsWithComparerValues, args with
25352564
| Some (_, _, withcEqualsVal), [x; y] ->
@@ -2599,7 +2628,7 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) =
25992628
match vref with
26002629
| Some vref -> Some (DevirtualizeApplication cenv env vref ty tyargs (mkCallGetGenericPEREqualityComparer cenv.g m :: args) m)
26012630
| None -> None
2602-
2631+
26032632
// Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericComparisonWithComparerIntrinsic for tuple types
26042633
| Expr.Val(v, _, _), [ty], _ when valRefEq cenv.g v cenv.g.generic_comparison_withc_inner_vref && isRefTupleTy cenv.g ty ->
26052634
let tyargs = destRefTupleTy cenv.g ty
@@ -2668,6 +2697,9 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) =
26682697
MightMakeCriticalTailcall = false
26692698
Info=UnknownValue})
26702699

2700+
| Expr.Val(v, _, _), [_], [receiver; arg] when valRefEq cenv.g v cenv.g.generic_equality_per_inner_vref && CanOptimizeGenericEqualityIntrinsicToIEquatableEquals cenv m (tyOfExpr cenv.g receiver) ->
2701+
Some(DevirtualizeGenericEqualityIntrinsic cenv env receiver arg m)
2702+
26712703
| _ -> None
26722704

26732705
/// Attempt to inline an application of a known value at callsites

src/fsharp/Optimizer.fsi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type OptimizationSettings =
1212
jitOptUser : bool option
1313
localOptUser : bool option
1414
crossModuleOptUser : bool option
15+
optimizeComparisonLogic : bool
1516
bigTargetSize : int
1617
veryBigExprSize : int
1718
lambdaInlineThreshold : int

src/fsharp/TastOps.fs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6385,6 +6385,9 @@ let mspec_String_Concat4 (g: TcGlobals) =
63856385
let mspec_String_Concat_Array (g: TcGlobals) =
63866386
mkILNonGenericStaticMethSpecInTy (g.ilg.typ_String, "Concat", [ mkILArr1DTy g.ilg.typ_String ], g.ilg.typ_String)
63876387

6388+
let mspec_IEquatableT_Equals (g: TcGlobals) =
6389+
mkILNonGenericMethSpecInTy (g.ilg.typ_IEquatableT, ILCallingConv.Instance, "Equals", [ mkILTyvarTy 0us ], g.ilg.typ_Bool)
6390+
63886391
let fspec_Missing_Value (g: TcGlobals) = IL.mkILFieldSpecInTy(g.iltyp_Missing, "Value", g.iltyp_Missing)
63896392

63906393
let mkInitializeArrayMethSpec (g: TcGlobals) =
@@ -6622,6 +6625,21 @@ let mkStaticCall_String_Concat_Array g m arg =
66226625
let mspec = mspec_String_Concat_Array g
66236626
Expr.Op(TOp.ILCall(false, false, false, false, ValUseFlag.NormalValUse, false, false, mspec.MethodRef, [], [], [g.string_ty]), [], [arg], m)
66246627

6628+
let mkCall_IEquatableT_Equals g m receiver arg =
6629+
let mspec = mspec_IEquatableT_Equals g
6630+
let receiverTy = tyOfExpr g receiver
6631+
let isStruct = isStructTy g receiverTy
6632+
6633+
let wrap, finalExpr, valUseFlag =
6634+
if isStruct then
6635+
let wrap, addrOfReceiver, _, _ = mkExprAddrOfExpr g true false Mutates.NeverMutates receiver None m
6636+
wrap, addrOfReceiver, ValUseFlag.PossibleConstrainedCall(receiverTy)
6637+
else
6638+
id, receiver, ValUseFlag.NormalValUse
6639+
6640+
Expr.Op(TOp.ILCall(isStruct, false, false, false, valUseFlag, false, false, mspec.MethodRef, [receiverTy], [], [g.bool_ty]), [], [finalExpr; arg], m)
6641+
|> wrap
6642+
66256643
// Quotations can't contain any IL.
66266644
// As a result, we aim to get rid of all IL generation in the typechecker and pattern match
66276645
// compiler, or else train the quotation generator to understand the generated IL.

src/fsharp/TastOps.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,8 @@ val mkStaticCall_String_Concat3 : TcGlobals -> range -> Expr -> Expr -> Expr ->
13911391
val mkStaticCall_String_Concat4 : TcGlobals -> range -> Expr -> Expr -> Expr -> Expr -> Expr
13921392
val mkStaticCall_String_Concat_Array : TcGlobals -> range -> Expr -> Expr
13931393

1394+
val mkCall_IEquatableT_Equals : TcGlobals -> range -> Expr -> Expr -> Expr
1395+
13941396
//-------------------------------------------------------------------------
13951397
// operations primarily associated with the optimization to fix
13961398
// up loops to generate .NET code that does not include array bound checks

src/fsharp/range.fs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ type range(code1:int64, code2: int64) =
195195

196196
override r.ToString() = sprintf "%s (%d,%d--%d,%d) IsSynthetic=%b" r.FileName r.StartLine r.StartColumn r.EndLine r.EndColumn r.IsSynthetic
197197

198+
interface IEquatable<range> with
199+
200+
member this.Equals(m) = this.Code1 = m.Code1 && this.Code2 = m.Code2
201+
198202
let mkRange f b e =
199203
// remove relative parts from full path
200204
let normalizedFilePath = if Path.IsPathRooted f then try Path.GetFullPath f with _ -> f else f

src/fsharp/range.fsi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
module public Microsoft.FSharp.Compiler.Range
44

5+
open System
56
open System.Text
67
open System.Collections.Generic
78
open Internal.Utilities
@@ -49,6 +50,8 @@ type range =
4950
member MakeSynthetic : unit -> range
5051
member ToShortString : unit -> string
5152
static member Zero : range
53+
54+
interface IEquatable<range>
5255

5356
/// This view of range marks uses file indexes explicitly
5457
val mkFileIndexRange : FileIndex -> pos -> pos -> range

0 commit comments

Comments
 (0)