Skip to content

Commit 041d65b

Browse files
committed
the rest rebased
1 parent 7307f9b commit 041d65b

File tree

6 files changed

+87
-41
lines changed

6 files changed

+87
-41
lines changed

src/Compiler/Checking/CheckBasics.fs

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

237237
// Do we lay down an implicit debug point?
238-
eIsControlFlow: bool
239-
240-
// In order to avoid checking implicit-yield expressions multiple times, we cache the resulting checked expressions.
241-
// This avoids exponential behavior in the type checker when nesting implicit-yield expressions.
242-
eCachedImplicitYieldExpressions : HashMultiMap<range, SynExpr * TType * Expr>
238+
eIsControlFlow: bool
243239
}
244240

245241
member tenv.DisplayEnv = tenv.eNameResEnv.DisplayEnv
@@ -311,8 +307,6 @@ type TcFileState =
311307

312308
diagnosticOptions: FSharpDiagnosticOptions
313309

314-
argInfoCache: ConcurrentDictionary<string * range, ArgReprInfo>
315-
316310
// forward call
317311
TcPat: WarnOnUpperFlag -> TcFileState -> TcEnv -> PrelimValReprInfo option -> TcPatValFlags -> TcPatLinearEnv -> TType -> SynPat -> (TcPatPhase2Input -> Pattern) * TcPatLinearEnv
318312

@@ -362,7 +356,6 @@ type TcFileState =
362356
conditionalDefines = conditionalDefines
363357
isInternalTestSpanStackReferring = isInternalTestSpanStackReferring
364358
diagnosticOptions = diagnosticOptions
365-
argInfoCache = ConcurrentDictionary()
366359
TcPat = tcPat
367360
TcSimplePats = tcSimplePats
368361
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
@@ -5609,8 +5609,7 @@ let emptyTcEnv g =
56095609
eCtorInfo = None
56105610
eCallerMemberName = None
56115611
eLambdaArgInfos = []
5612-
eIsControlFlow = false
5613-
eCachedImplicitYieldExpressions = HashMultiMap(HashIdentity.Structural, useConcurrentDictionary = true) }
5612+
eIsControlFlow = false }
56145613

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

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,10 @@ let AdjustValSynInfoInSignature g ty (SynValInfo(argsData, retData) as sigMD) =
958958
| _ ->
959959
sigMD
960960

961+
let getArgInfoCache =
962+
let options = Caches.CacheOptions.getDefault HashIdentity.Structural |> Caches.CacheOptions.withNoEviction
963+
let factory _ = new Caches.Cache<_, ArgReprInfo>(options, "argInfoCache")
964+
WeakMap.getOrCreate factory
961965

962966
let TranslateTopArgSynInfo (cenv: cenv) isArg m tcAttributes (SynArgInfo(Attributes attrs, isOpt, nm)) =
963967
// Synthesize an artificial "OptionalArgument" attribute for the parameter
@@ -982,18 +986,12 @@ let TranslateTopArgSynInfo (cenv: cenv) isArg m tcAttributes (SynArgInfo(Attribu
982986

983987
let key = nm |> Option.map (fun id -> id.idText, id.idRange)
984988

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

998996
// Set freshly computed attribs in case they are different in the cache
999997
argInfo.Attribs <- attribs
@@ -4054,6 +4052,13 @@ type ImplicitlyBoundTyparsAllowed =
40544052
| NewTyparsOK
40554053
| NoNewTypars
40564054

4055+
// In order to avoid checking implicit-yield expressions multiple times, we cache the resulting checked expressions.
4056+
// This avoids exponential behavior in the type checker when nesting implicit-yield expressions.
4057+
let getImplicitYieldExpressionsCache =
4058+
let options = Caches.CacheOptions.getReferenceIdentity() |> Caches.CacheOptions.withNoEviction
4059+
let factory _ = new Caches.Cache<SynExpr, _>(options, "implicitYieldExpressions")
4060+
WeakMap.getOrCreate factory
4061+
40574062
//-------------------------------------------------------------------------
40584063
// Checking types and type constraints
40594064
//-------------------------------------------------------------------------
@@ -5508,19 +5513,12 @@ and CheckForAdjacentListExpression (cenv: cenv) synExpr hpa isInfix delayed (arg
55085513
and TcExprThen (cenv: cenv) overallTy env tpenv isArg synExpr delayed =
55095514
let g = cenv.g
55105515

5511-
let cachedExpression =
5512-
env.eCachedImplicitYieldExpressions.FindAll synExpr.Range
5513-
|> List.tryPick (fun (se, ty, e) ->
5514-
if obj.ReferenceEquals(se, synExpr) then Some (ty, e) else None
5515-
)
5516-
5517-
match cachedExpression with
5518-
| Some (ty, expr) ->
5516+
match (getImplicitYieldExpressionsCache cenv).TryGetValue synExpr with
5517+
| true, (ty, expr) ->
55195518
UnifyOverallType cenv env synExpr.Range overallTy ty
55205519
expr, tpenv
55215520
| _ ->
55225521

5523-
55245522
match synExpr with
55255523

55265524
// A.
@@ -6382,9 +6380,8 @@ and TcExprSequentialOrImplicitYield (cenv: cenv) overallTy env tpenv (sp, synExp
63826380
| Expr.DebugPoint(_,e) -> e
63836381
| _ -> expr1
63846382

6385-
env.eCachedImplicitYieldExpressions.Add(synExpr1.Range, (synExpr1, expr1Ty, cachedExpr))
6386-
try TcExpr cenv overallTy env tpenv otherExpr
6387-
finally env.eCachedImplicitYieldExpressions.Remove synExpr1.Range
6383+
(getImplicitYieldExpressionsCache cenv).AddOrUpdate(synExpr1, (expr1Ty, cachedExpr))
6384+
TcExpr cenv overallTy env tpenv otherExpr
63886385

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

tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@
353353
<Compile Include="FSharpChecker\TransparentCompiler.fs" />
354354
<Compile Include="FSharpChecker\SymbolUse.fs" />
355355
<Compile Include="FSharpChecker\FindReferences.fs" />
356+
<Compile Include="Optimizer\NestedApplications.fs" />
356357
<Compile Include="Attributes\AttributeCtorSetPropAccess.fs" />
357358
</ItemGroup>
358359

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
namespace FSharp.Compiler.ComponentTests.Optimizer
2+
3+
open System.Text
4+
open Xunit
5+
open FSharp.Test
6+
open FSharp.Test.Compiler
7+
open FSharp.Test.Utilities
8+
9+
module private Gen =
10+
let nestedLetApps depth =
11+
// Builds: let v1 = id 0 in let v2 = id v1 in ... in ignore vN
12+
let sb = StringBuilder()
13+
sb.AppendLine("module M") |> ignore
14+
sb.AppendLine("let id x = x") |> ignore
15+
sb.AppendLine("let run () =") |> ignore
16+
for i in 1 .. depth do
17+
if i = 1 then
18+
sb.Append(" let v1 = id 0") |> ignore
19+
else
20+
sb.Append(" in let v").Append(i).Append(" = id v").Append(i-1) |> ignore
21+
sb.AppendLine(" in ()") |> ignore
22+
sb.ToString()
23+
24+
let nestedDirectApps depth =
25+
// Builds: let res = id(id(id(...(0)))) in ignore res
26+
let sb = StringBuilder()
27+
sb.AppendLine("module N") |> ignore
28+
sb.AppendLine("let id x = x") |> ignore
29+
sb.Append("let run () = let res = ") |> ignore
30+
for _ in 1 .. depth do
31+
sb.Append("id (") |> ignore
32+
sb.Append("0") |> ignore
33+
for _ in 1 .. depth do
34+
sb.Append(")") |> ignore
35+
sb.AppendLine(" in ignore res") |> ignore
36+
sb.ToString()
37+
38+
[<Collection(nameof NotThreadSafeResourceCollection)>]
39+
type ``Nested application optimizer``() =
40+
41+
// Moderate depths to keep CI stable while still exercising the quadratic shapes
42+
[<Theory>]
43+
[<InlineData(100)>]
44+
[<InlineData(1000)>]
45+
let ``let-chains of nested apps compile under --optimize+`` depth =
46+
let src = Gen.nestedLetApps depth
47+
FSharp src
48+
|> withOptions [ "--optimize+"; "--times" ]
49+
|> asExe
50+
|> ignoreWarnings
51+
|> compile
52+
|> shouldSucceed
53+
54+
[<Theory>]
55+
[<InlineData(100)>]
56+
[<InlineData(1000)>]
57+
let ``direct nested application compiles under --optimize+`` depth =
58+
let src = Gen.nestedDirectApps depth
59+
FSharp src
60+
|> withOptions [ "--optimize+"; "--times" ]
61+
|> asExe
62+
|> ignoreWarnings
63+
|> compile
64+
|> shouldSucceed

0 commit comments

Comments
 (0)