Skip to content

Commit f96890a

Browse files
committed
use new cache api
1 parent 9425e4d commit f96890a

File tree

12 files changed

+125
-121
lines changed

12 files changed

+125
-121
lines changed

src/Compiler/Checking/CheckBasics.fs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,7 @@ type TcEnv =
243243
eLambdaArgInfos: ArgReprInfo list list
244244

245245
// Do we lay down an implicit debug point?
246-
eIsControlFlow: bool
247-
248-
// In order to avoid checking implicit-yield expressions multiple times, we cache the resulting checked expressions.
249-
// This avoids exponential behavior in the type checker when nesting implicit-yield expressions.
250-
eCachedImplicitYieldExpressions : HashMultiMap<range, SynExpr * TType * Expr>
246+
eIsControlFlow: bool
251247
}
252248

253249
member tenv.DisplayEnv = tenv.eNameResEnv.DisplayEnv
@@ -319,8 +315,6 @@ type TcFileState =
319315

320316
diagnosticOptions: FSharpDiagnosticOptions
321317

322-
argInfoCache: ConcurrentDictionary<(string * range), ArgReprInfo>
323-
324318
// forward call
325319
TcPat: WarnOnUpperFlag -> TcFileState -> TcEnv -> PrelimValReprInfo option -> TcPatValFlags -> TcPatLinearEnv -> TType -> SynPat -> (TcPatPhase2Input -> Pattern) * TcPatLinearEnv
326320

@@ -370,7 +364,6 @@ type TcFileState =
370364
conditionalDefines = conditionalDefines
371365
isInternalTestSpanStackReferring = isInternalTestSpanStackReferring
372366
diagnosticOptions = diagnosticOptions
373-
argInfoCache = ConcurrentDictionary()
374367
TcPat = tcPat
375368
TcSimplePats = tcSimplePats
376369
TcSequenceExpressionEntry = tcSequenceExpressionEntry

src/Compiler/Checking/CheckBasics.fsi

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,6 @@ type TcEnv =
130130

131131
eIsControlFlow: bool
132132

133-
// In order to avoid checking implicit-yield expressions multiple times, we cache the resulting checked expressions.
134-
// This avoids exponential behavior in the type checker when nesting implicit-yield expressions.
135-
eCachedImplicitYieldExpressions: HashMultiMap<range, SynExpr * TType * Expr>
136133
}
137134

138135
member DisplayEnv: DisplayEnv
@@ -269,11 +266,6 @@ type TcFileState =
269266

270267
diagnosticOptions: FSharpDiagnosticOptions
271268

272-
/// A cache for ArgReprInfos which get created multiple times for the same values
273-
/// Since they need to be later mutated with updates from signature files this should make sure
274-
/// we're always dealing with the same instance and the updates don't get lost
275-
argInfoCache: ConcurrentDictionary<(string * range), ArgReprInfo>
276-
277269
// forward call
278270
TcPat:
279271
WarnOnUpperFlag

src/Compiler/Checking/CheckDeclarations.fs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5620,8 +5620,7 @@ let emptyTcEnv g =
56205620
eCtorInfo = None
56215621
eCallerMemberName = None
56225622
eLambdaArgInfos = []
5623-
eIsControlFlow = false
5624-
eCachedImplicitYieldExpressions = HashMultiMap(HashIdentity.Structural, useConcurrentDictionary = true) }
5623+
eIsControlFlow = false }
56255624

56265625
let CreateInitialTcEnv(g, amap, scopem, assemblyName, ccus) =
56275626
(emptyTcEnv g, ccus) ||> List.collectFold (fun env (ccu, autoOpens, internalsVisible) ->

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -955,8 +955,12 @@ let AdjustValSynInfoInSignature g ty (SynValInfo(argsData, retData) as sigMD) =
955955
| _ ->
956956
sigMD
957957

958+
let getArgInfoCache =
959+
let options = Caches.CacheOptions.getDefault() |> Caches.CacheOptions.withNoEviction
960+
let factory _ = new Caches.Cache<_, ArgReprInfo>(options, "argInfoCache")
961+
WeakMap.getOrCreate factory
958962

959-
let TranslateTopArgSynInfo (cenv: cenv) isArg m tcAttributes (SynArgInfo(Attributes attrs, isOpt, nm)) =
963+
let TranslateTopArgSynInfo cenv isArg m tcAttributes (SynArgInfo(Attributes attrs, isOpt, nm)) =
960964
// Synthesize an artificial "OptionalArgument" attribute for the parameter
961965
let optAttrs =
962966
if isOpt then
@@ -977,20 +981,14 @@ let TranslateTopArgSynInfo (cenv: cenv) isArg m tcAttributes (SynArgInfo(Attribu
977981
// Call the attribute checking function
978982
let attribs = tcAttributes (optAttrs@attrs)
979983

980-
let key = nm |> Option.map (fun id -> id.idText, id.idRange)
984+
let key = nm |> Option.map (fun id -> (id.idText, id.idRange))
985+
986+
let mkDefaultArgInfo _ : ArgReprInfo = { Attribs = attribs; Name = nm; OtherRange = None }
981987

982988
let argInfo =
983-
key
984-
|> Option.map cenv.argInfoCache.TryGetValue
985-
|> Option.bind (fun (found, info) ->
986-
if found then
987-
Some info
988-
else None)
989-
|> Option.defaultValue ({ Attribs = attribs; Name = nm; OtherRange = None }: ArgReprInfo)
990-
991-
match key with
992-
| Some k -> cenv.argInfoCache.[k] <- argInfo
993-
| None -> ()
989+
match key with
990+
| Some key -> (getArgInfoCache cenv).GetOrAdd(key, mkDefaultArgInfo)
991+
| _ -> mkDefaultArgInfo ()
994992

995993
// Set freshly computed attribs in case they are different in the cache
996994
argInfo.Attribs <- attribs
@@ -4051,6 +4049,13 @@ type ImplicitlyBoundTyparsAllowed =
40514049
| NewTyparsOK
40524050
| NoNewTypars
40534051

4052+
// In order to avoid checking implicit-yield expressions multiple times, we cache the resulting checked expressions.
4053+
// This avoids exponential behavior in the type checker when nesting implicit-yield expressions.
4054+
let getImplicitYieldExpressionsCache =
4055+
let options = Caches.CacheOptions.getReferenceIdentity() |> Caches.CacheOptions.withNoEviction
4056+
let factory _ = new Caches.Cache<SynExpr, _>(options, "implicitYieldExpressions")
4057+
WeakMap.getOrCreate factory
4058+
40544059
//-------------------------------------------------------------------------
40554060
// Checking types and type constraints
40564061
//-------------------------------------------------------------------------
@@ -5503,19 +5508,12 @@ and CheckForAdjacentListExpression (cenv: cenv) synExpr hpa isInfix delayed (arg
55035508
and TcExprThen (cenv: cenv) overallTy env tpenv isArg synExpr delayed =
55045509
let g = cenv.g
55055510

5506-
let cachedExpression =
5507-
env.eCachedImplicitYieldExpressions.FindAll synExpr.Range
5508-
|> List.tryPick (fun (se, ty, e) ->
5509-
if obj.ReferenceEquals(se, synExpr) then Some (ty, e) else None
5510-
)
5511-
5512-
match cachedExpression with
5513-
| Some (ty, expr) ->
5511+
match (getImplicitYieldExpressionsCache cenv).TryGetValue synExpr with
5512+
| true, (ty, expr) ->
55145513
UnifyOverallType cenv env synExpr.Range overallTy ty
55155514
expr, tpenv
55165515
| _ ->
55175516

5518-
55195517
match synExpr with
55205518

55215519
// A.
@@ -6378,9 +6376,8 @@ and TcExprSequentialOrImplicitYield (cenv: cenv) overallTy env tpenv (sp, synExp
63786376
| Expr.DebugPoint(_,e) -> e
63796377
| _ -> expr1
63806378

6381-
env.eCachedImplicitYieldExpressions.Add(synExpr1.Range, (synExpr1, expr1Ty, cachedExpr))
6382-
try TcExpr cenv overallTy env tpenv otherExpr
6383-
finally env.eCachedImplicitYieldExpressions.Remove synExpr1.Range
6379+
(getImplicitYieldExpressionsCache cenv).AddOrUpdate(synExpr1, (expr1Ty, cachedExpr))
6380+
TcExpr cenv overallTy env tpenv otherExpr
63846381

63856382
and TcExprStaticOptimization (cenv: cenv) overallTy env tpenv (constraints, synExpr2, expr3, m) =
63866383
let constraintsR, tpenv = List.mapFold (TcStaticOptimizationConstraint cenv env) tpenv constraints

src/Compiler/Checking/TypeRelations.fs

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module internal FSharp.Compiler.TypeRelations
77
open FSharp.Compiler.Features
88
open Internal.Utilities.Collections
99
open Internal.Utilities.Library
10+
open Internal.Utilities.TypeHashing
1011

1112
open FSharp.Compiler.DiagnosticsLogger
1213
open FSharp.Compiler.TcGlobals
@@ -19,6 +20,55 @@ open Import
1920

2021
#nowarn "3391"
2122

23+
[<Struct; NoComparison>]
24+
type CanCoerce =
25+
| CanCoerce
26+
| NoCoerce
27+
28+
[<Struct; NoComparison; CustomEquality; System.Diagnostics.DebuggerDisplay("{ToString()}")>]
29+
type TTypeCacheKey =
30+
31+
val ty1: TType
32+
val ty2: TType
33+
val canCoerce: CanCoerce
34+
35+
private new (ty1, ty2, canCoerce) =
36+
{ ty1 = ty1; ty2 = ty2; canCoerce = canCoerce }
37+
38+
static member FromStrippedTypes (ty1, ty2, canCoerce) =
39+
TTypeCacheKey(ty1, ty2, canCoerce)
40+
41+
interface System.IEquatable<TTypeCacheKey> with
42+
member this.Equals other =
43+
if this.canCoerce <> other.canCoerce then
44+
false
45+
elif this.ty1 === other.ty1 && this.ty2 === other.ty2 then
46+
true
47+
else
48+
HashStamps.stampEquals this.ty1 other.ty1
49+
&& HashStamps.stampEquals this.ty2 other.ty2
50+
51+
override this.Equals(other:objnull) =
52+
match other with
53+
| :? TTypeCacheKey as p -> (this :> System.IEquatable<TTypeCacheKey>).Equals p
54+
| _ -> false
55+
56+
override this.GetHashCode () : int =
57+
HashStamps.hashTType this.ty1
58+
|> pipeToHash (HashStamps.hashTType this.ty2)
59+
|> pipeToHash (hash this.canCoerce)
60+
61+
override this.ToString () = $"{this.ty1.DebugText}-{this.ty2.DebugText}"
62+
63+
let getTypeSubsumptionCache =
64+
let factory (g: TcGlobals) =
65+
let options =
66+
match g.compilationMode with
67+
| CompilationMode.OneOff -> Caches.CacheOptions.getDefault() |> Caches.CacheOptions.withNoEviction
68+
| _ -> { Caches.CacheOptions.getDefault() with TotalCapacity = 131072; HeadroomPercentage = 75 }
69+
new Caches.Cache<TTypeCacheKey, bool>(options, "typeSubsumptionCache")
70+
Extras.WeakMap.getOrCreate factory
71+
2272
/// Implements a :> b without coercion based on finalized (no type variable) types
2373
// Note: This relation is approximate and not part of the language specification.
2474
//
@@ -137,13 +187,7 @@ let rec TypeFeasiblySubsumesType ndeep (g: TcGlobals) (amap: ImportMap) m (ty1:
137187

138188
if g.langVersion.SupportsFeature LanguageFeature.UseTypeSubsumptionCache then
139189
let key = TTypeCacheKey.FromStrippedTypes (ty1, ty2, canCoerce)
140-
141-
match amap.TypeSubsumptionCache.TryGetValue(key) with
142-
| true, subsumes -> subsumes
143-
| false, _ ->
144-
let subsumes = checkSubsumes ty1 ty2
145-
amap.TypeSubsumptionCache.TryAdd(key, subsumes) |> ignore
146-
subsumes
190+
(getTypeSubsumptionCache g).GetOrAdd(key, fun key -> checkSubsumes key.ty1 key.ty2)
147191
else
148192
checkSubsumes ty1 ty2
149193

src/Compiler/Checking/TypeRelations.fsi

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,24 @@ open FSharp.Compiler.TcGlobals
99
open FSharp.Compiler.Text
1010
open FSharp.Compiler.TypedTree
1111

12+
[<Struct; NoComparison>]
13+
type CanCoerce =
14+
| CanCoerce
15+
| NoCoerce
16+
17+
[<Struct; NoComparison; CustomEquality>]
18+
type TTypeCacheKey =
19+
interface System.IEquatable<TTypeCacheKey>
20+
private new: ty1: TType * ty2: TType * canCoerce: CanCoerce -> TTypeCacheKey
21+
22+
static member FromStrippedTypes: ty1: TType * ty2: TType * canCoerce: CanCoerce -> TTypeCacheKey
23+
24+
val ty1: TType
25+
val ty2: TType
26+
val canCoerce: CanCoerce
27+
28+
override GetHashCode: unit -> int
29+
1230
/// Implements a :> b without coercion based on finalized (no type variable) types
1331
val TypeDefinitelySubsumesTypeNoCoercion:
1432
ndeep: int -> g: TcGlobals -> amap: ImportMap -> m: range -> ty1: TType -> ty2: TType -> bool

src/Compiler/Checking/import.fs

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -50,51 +50,6 @@ type AssemblyLoader =
5050
abstract RecordGeneratedTypeRoot : ProviderGeneratedType -> unit
5151
#endif
5252

53-
[<Struct; NoComparison>]
54-
type CanCoerce =
55-
| CanCoerce
56-
| NoCoerce
57-
58-
[<Struct; NoComparison; CustomEquality; DebuggerDisplay("{ToString()}")>]
59-
type TTypeCacheKey =
60-
61-
val ty1: TType
62-
val ty2: TType
63-
val canCoerce: CanCoerce
64-
65-
private new (ty1, ty2, canCoerce) =
66-
{ ty1 = ty1; ty2 = ty2; canCoerce = canCoerce }
67-
68-
static member FromStrippedTypes (ty1, ty2, canCoerce) =
69-
TTypeCacheKey(ty1, ty2, canCoerce)
70-
71-
interface System.IEquatable<TTypeCacheKey> with
72-
member this.Equals other =
73-
if this.canCoerce <> other.canCoerce then
74-
false
75-
elif this.ty1 === other.ty1 && this.ty2 === other.ty2 then
76-
true
77-
else
78-
HashStamps.stampEquals this.ty1 other.ty1
79-
&& HashStamps.stampEquals this.ty2 other.ty2
80-
81-
override this.Equals(other:objnull) =
82-
match other with
83-
| :? TTypeCacheKey as p -> (this :> System.IEquatable<TTypeCacheKey>).Equals p
84-
| _ -> false
85-
86-
override this.GetHashCode () : int =
87-
HashStamps.hashTType this.ty1
88-
|> pipeToHash (HashStamps.hashTType this.ty2)
89-
|> pipeToHash (hash this.canCoerce)
90-
91-
override this.ToString () = $"{this.ty1.DebugText}-{this.ty2.DebugText}"
92-
93-
let typeSubsumptionCache =
94-
// Leave most of the capacity in reserve for bursts.
95-
let options = { CacheOptions.getDefault() with TotalCapacity = 131072; HeadroomPercentage = 75 }
96-
lazy new Cache<TTypeCacheKey, bool>(options, "typeSubsumptionCache")
97-
9853
//-------------------------------------------------------------------------
9954
// Import an IL types as F# types.
10055
//-------------------------------------------------------------------------
@@ -117,8 +72,6 @@ type ImportMap(g: TcGlobals, assemblyLoader: AssemblyLoader) =
11772

11873
member _.ILTypeRefToTyconRefCache = typeRefToTyconRefCache
11974

120-
member val TypeSubsumptionCache: Cache<TTypeCacheKey, bool> = typeSubsumptionCache.Value
121-
12275
let CanImportILScopeRef (env: ImportMap) m scoref =
12376

12477
let isResolved assemblyRef =

src/Compiler/Checking/import.fsi

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,6 @@ type AssemblyLoader =
3636
abstract RecordGeneratedTypeRoot: ProviderGeneratedType -> unit
3737
#endif
3838

39-
[<Struct; NoComparison>]
40-
type CanCoerce =
41-
| CanCoerce
42-
| NoCoerce
43-
44-
[<Struct; NoComparison; CustomEquality>]
45-
type TTypeCacheKey =
46-
interface System.IEquatable<TTypeCacheKey>
47-
private new: ty1: TType * ty2: TType * canCoerce: CanCoerce -> TTypeCacheKey
48-
49-
static member FromStrippedTypes: ty1: TType * ty2: TType * canCoerce: CanCoerce -> TTypeCacheKey
50-
51-
val ty1: TType
52-
val ty2: TType
53-
val canCoerce: CanCoerce
54-
55-
override GetHashCode: unit -> int
56-
5739
/// Represents a context used for converting AbstractIL .NET and provided types to F# internal compiler data structures.
5840
/// Also cache the conversion of AbstractIL ILTypeRef nodes, based on hashes of these.
5941
///
@@ -70,9 +52,6 @@ type ImportMap =
7052
/// The TcGlobals for the import context
7153
member g: TcGlobals
7254

73-
/// Type subsumption cache
74-
member TypeSubsumptionCache: Cache<TTypeCacheKey, bool>
75-
7655
module Nullness =
7756

7857
[<Struct; NoEquality; NoComparison>]

src/Compiler/Optimize/Optimizer.fs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ open Internal.Utilities.Collections
99
open Internal.Utilities.Library
1010
open Internal.Utilities.Library.Extras
1111
open FSharp.Compiler
12+
open FSharp.Compiler.Caches
1213
open FSharp.Compiler.AbstractIL.Diagnostics
1314
open FSharp.Compiler.AbstractIL.IL
1415
open FSharp.Compiler.AttributeChecking
@@ -36,6 +37,10 @@ open System.Collections.ObjectModel
3637

3738
let OptimizerStackGuardDepth = GetEnvInteger "FSHARP_Optimizer" 50
3839

40+
let getFreeLocalsCache =
41+
let options = CacheOptions.getReferenceIdentity() |> CacheOptions.withNoEviction
42+
WeakMap.getOrCreate <| fun _ -> new Cache<_, _>(options, "freeLocalsCache")
43+
3944
let i_ldlen = [ I_ldlen; (AI_conv DT_I4) ]
4045

4146
/// size of a function call
@@ -2898,10 +2903,11 @@ and OptimizeLinearExpr cenv env expr contf =
28982903

28992904
let (bindR, bindingInfo), env = OptimizeBinding cenv false env bind
29002905

2901-
OptimizeLinearExpr cenv env body (contf << (fun (bodyR, bodyInfo) ->
2906+
OptimizeLinearExpr cenv env body (contf << (fun (bodyR, bodyInfo) ->
29022907
// PERF: This call to ValueIsUsedOrHasEffect/freeInExpr amounts to 9% of all optimization time.
29032908
// Is it quadratic or quasi-quadratic?
2904-
if ValueIsUsedOrHasEffect cenv (fun () -> (freeInExpr (CollectLocalsWithStackGuard()) bodyR).FreeLocals) (bindR, bindingInfo) then
2909+
let collect expr = (freeInExpr (CollectLocalsWithStackGuard()) expr).FreeLocals
2910+
if ValueIsUsedOrHasEffect cenv (fun () -> (getFreeLocalsCache cenv).GetOrAdd(bodyR, collect)) (bindR, bindingInfo) then
29052911
// Eliminate let bindings on the way back up
29062912
let exprR, adjust = TryEliminateLet cenv env bindR bodyR m
29072913
exprR,

src/Compiler/Service/FSharpCheckerResults.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ type internal TypeCheckInfo
538538
// check that type of value is the same or subtype of tcref
539539
// yes - allow access to protected members
540540
// no - strip ability to access protected members
541-
if TypeRelations.TypeFeasiblySubsumesType 0 g amap m thisTy Import.CanCoerce ty then
541+
if TypeRelations.TypeFeasiblySubsumesType 0 g amap m thisTy TypeRelations.CanCoerce ty then
542542
ad
543543
else
544544
AccessibleFrom(paths, None)

0 commit comments

Comments
 (0)