Skip to content

Commit cae1069

Browse files
committed
Reuse typechecking results - stage 1
1 parent 8e773e7 commit cae1069

36 files changed

+504
-36
lines changed

Diff for: docs/release-notes/.FSharp.Compiler.Service/9.0.200.md

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
### Added
2929

30+
* New flag `--reusetypecheckingresults`, for skipping recompilation in some cases
3031
* Let `dotnet fsi --help` print a link to the documentation website. ([PR #18006](https://github.com/dotnet/fsharp/pull/18006))
3132
* Deprecate places where `seq` can be omitted. ([Language suggestion #1033](https://github.com/fsharp/fslang-suggestions/issues/1033), [PR #17772](https://github.com/dotnet/fsharp/pull/17772))
3233
* Support literal attribute on decimals ([PR #17769](https://github.com/dotnet/fsharp/pull/17769))

Diff for: src/Compiler/Driver/CompilerConfig.fs

+13
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,11 @@ type TypeCheckingMode =
445445
| Sequential
446446
| Graph
447447

448+
[<RequireQualifiedAccess>]
449+
type ReuseTcResults =
450+
| On
451+
| Off
452+
448453
[<RequireQualifiedAccess>]
449454
type TypeCheckingConfig =
450455
{
@@ -652,6 +657,8 @@ type TcConfigBuilder =
652657

653658
mutable parallelReferenceResolution: ParallelReferenceResolution
654659

660+
mutable reuseTcResults: ReuseTcResults
661+
655662
mutable captureIdentifiersWhenParsing: bool
656663

657664
mutable typeCheckingConfig: TypeCheckingConfig
@@ -661,6 +668,8 @@ type TcConfigBuilder =
661668
mutable realsig: bool
662669

663670
mutable compilationMode: TcGlobals.CompilationMode
671+
672+
mutable cmdLineArgs: string array
664673
}
665674

666675
// Directories to start probing in
@@ -859,6 +868,7 @@ type TcConfigBuilder =
859868
xmlDocInfoLoader = None
860869
exiter = QuitProcessExiter
861870
parallelReferenceResolution = ParallelReferenceResolution.Off
871+
reuseTcResults = ReuseTcResults.Off
862872
captureIdentifiersWhenParsing = false
863873
typeCheckingConfig =
864874
{
@@ -873,6 +883,7 @@ type TcConfigBuilder =
873883
realsig = false
874884
strictIndentation = None
875885
compilationMode = TcGlobals.CompilationMode.Unset
886+
cmdLineArgs = [||]
876887
}
877888

878889
member tcConfigB.FxResolver =
@@ -1413,11 +1424,13 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) =
14131424
member _.xmlDocInfoLoader = data.xmlDocInfoLoader
14141425
member _.exiter = data.exiter
14151426
member _.parallelReferenceResolution = data.parallelReferenceResolution
1427+
member _.reuseTcResults = data.reuseTcResults
14161428
member _.captureIdentifiersWhenParsing = data.captureIdentifiersWhenParsing
14171429
member _.typeCheckingConfig = data.typeCheckingConfig
14181430
member _.dumpSignatureData = data.dumpSignatureData
14191431
member _.realsig = data.realsig
14201432
member _.compilationMode = data.compilationMode
1433+
member _.cmdLineArgs = data.cmdLineArgs
14211434

14221435
static member Create(builder, validate) =
14231436
use _ = UseBuildPhase BuildPhase.Parameter

Diff for: src/Compiler/Driver/CompilerConfig.fsi

+13
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,11 @@ type ParallelReferenceResolution =
208208
| On
209209
| Off
210210

211+
[<RequireQualifiedAccess>]
212+
type ReuseTcResults =
213+
| On
214+
| Off
215+
211216
/// Determines the algorithm used for type-checking.
212217
[<RequireQualifiedAccess>]
213218
type TypeCheckingMode =
@@ -519,6 +524,8 @@ type TcConfigBuilder =
519524

520525
mutable parallelReferenceResolution: ParallelReferenceResolution
521526

527+
mutable reuseTcResults: ReuseTcResults
528+
522529
mutable captureIdentifiersWhenParsing: bool
523530

524531
mutable typeCheckingConfig: TypeCheckingConfig
@@ -528,6 +535,8 @@ type TcConfigBuilder =
528535
mutable realsig: bool
529536

530537
mutable compilationMode: TcGlobals.CompilationMode
538+
539+
mutable cmdLineArgs: string array
531540
}
532541

533542
static member CreateNew:
@@ -899,6 +908,8 @@ type TcConfig =
899908

900909
member parallelReferenceResolution: ParallelReferenceResolution
901910

911+
member reuseTcResults: ReuseTcResults
912+
902913
member captureIdentifiersWhenParsing: bool
903914

904915
member typeCheckingConfig: TypeCheckingConfig
@@ -909,6 +920,8 @@ type TcConfig =
909920

910921
member compilationMode: TcGlobals.CompilationMode
911922

923+
member cmdLineArgs: string array
924+
912925
/// Represents a computation to return a TcConfig. Normally this is just a constant immutable TcConfig,
913926
/// but for F# Interactive it may be based on an underlying mutable TcConfigBuilder.
914927
[<Sealed>]

Diff for: src/Compiler/Driver/CompilerOptions.fs

+8
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,14 @@ let advancedFlagsFsc tcConfigB =
13881388
None,
13891389
Some(FSComp.SR.optsEmitDebugInfoInQuotations (formatOptionSwitch tcConfigB.emitDebugInfoInQuotations))
13901390
)
1391+
1392+
CompilerOption(
1393+
"reusetypecheckingresults",
1394+
tagNone,
1395+
OptionUnit(fun () -> tcConfigB.reuseTcResults <- ReuseTcResults.On),
1396+
None,
1397+
Some(FSComp.SR.optsReuseTcResults ())
1398+
)
13911399
]
13921400

13931401
// OptionBlock: Internal options (test use only)

Diff for: src/Compiler/Driver/ReuseTcResults/CachingDriver.fs

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
module internal FSharp.Compiler.ReuseTcResults
2+
3+
open System.Collections.Generic
4+
open System.IO
5+
6+
open FSharp.Compiler.CompilerConfig
7+
open FSharp.Compiler.Diagnostics
8+
open FSharp.Compiler.GraphChecking
9+
open FSharp.Compiler.IO
10+
open FSharp.Compiler.Syntax
11+
open FSharp.Compiler.Syntax.PrettyNaming
12+
13+
type TcData =
14+
{
15+
CmdLine: string array
16+
Graph: string array
17+
References: string array
18+
}
19+
20+
[<Sealed>]
21+
type CachingDriver(tcConfig: TcConfig) =
22+
23+
let outputDir = tcConfig.outputDir |> Option.defaultValue ""
24+
let tcDataFilePath = Path.Combine(outputDir, FSharpTcDataResourceName)
25+
26+
[<Literal>]
27+
let CmdLineHeader = "CMDLINE"
28+
29+
[<Literal>]
30+
let GraphHeader = "GRAPH"
31+
32+
[<Literal>]
33+
let ReferencesHeader = "REFERENCES"
34+
35+
let writeThisTcData (tcData: TcData) =
36+
use tcDataFile = FileSystem.OpenFileForWriteShim tcDataFilePath
37+
38+
let lines = ResizeArray<string>()
39+
lines.Add $"BEGIN {CmdLineHeader}"
40+
lines.AddRange tcData.CmdLine
41+
lines.Add $"BEGIN {GraphHeader}"
42+
lines.AddRange tcData.Graph
43+
lines.Add $"BEGIN {ReferencesHeader}"
44+
lines.AddRange tcData.References
45+
46+
tcDataFile.WriteAllLines lines
47+
48+
let readPrevTcData () =
49+
if FileSystem.FileExistsShim tcDataFilePath then
50+
use tcDataFile = FileSystem.OpenFileForReadShim tcDataFilePath
51+
52+
let cmdLine = ResizeArray<string>()
53+
let graph = ResizeArray<string>()
54+
let refs = ResizeArray<string>()
55+
56+
let mutable currentHeader = ""
57+
58+
tcDataFile.ReadLines()
59+
|> Seq.iter (fun line ->
60+
match line with
61+
| line when line.StartsWith "BEGIN" -> currentHeader <- line.Split ' ' |> Array.last
62+
| line ->
63+
match currentHeader with
64+
| CmdLineHeader -> cmdLine.Add line
65+
| GraphHeader -> graph.Add line
66+
| ReferencesHeader -> refs.Add line
67+
| _ -> invalidOp "broken tc cache")
68+
69+
Some
70+
{
71+
CmdLine = cmdLine.ToArray()
72+
Graph = graph.ToArray()
73+
References = refs.ToArray()
74+
}
75+
76+
else
77+
None
78+
79+
let formatAssemblyReference (r: AssemblyReference) =
80+
let fileName = r.Text
81+
let lastWriteTime = FileSystem.GetLastWriteTimeShim fileName
82+
sprintf "%s,%i" fileName lastWriteTime.Ticks
83+
84+
let getThisCompilationCmdLine args = args
85+
86+
// maybe split into two things?
87+
let getThisCompilationGraph inputs =
88+
let sourceFiles =
89+
inputs
90+
|> Seq.toArray
91+
|> Array.mapi (fun idx (input: ParsedInput) ->
92+
{
93+
Idx = idx
94+
FileName = input.FileName
95+
ParsedInput = input
96+
})
97+
98+
let filePairs = FilePairMap sourceFiles
99+
let graph, _ = DependencyResolution.mkGraph filePairs sourceFiles
100+
101+
let list = List<string>()
102+
103+
for KeyValue(idx, _) in graph do
104+
let fileName = sourceFiles[idx].FileName
105+
let lastWriteTime = FileSystem.GetLastWriteTimeShim fileName
106+
list.Add(sprintf "%i,%s,%i" idx fileName lastWriteTime.Ticks)
107+
108+
for KeyValue(idx, deps) in graph do
109+
for depIdx in deps do
110+
list.Add $"%i{idx} --> %i{depIdx}"
111+
112+
list.ToArray()
113+
114+
let getThisCompilationReferences = Seq.map formatAssemblyReference >> Seq.toArray
115+
116+
member _.CanReuseTcResults inputs =
117+
let prevTcDataOpt = readPrevTcData ()
118+
119+
let thisTcData =
120+
{
121+
CmdLine = getThisCompilationCmdLine tcConfig.cmdLineArgs
122+
Graph = getThisCompilationGraph inputs
123+
References = getThisCompilationReferences tcConfig.referencedDLLs
124+
}
125+
126+
match prevTcDataOpt with
127+
| Some prevTcData ->
128+
use _ = Activity.start Activity.Events.reuseTcResultsCachePresent []
129+
130+
if prevTcData = thisTcData then
131+
use _ = Activity.start Activity.Events.reuseTcResultsCacheHit []
132+
true
133+
else
134+
use _ = Activity.start Activity.Events.reuseTcResultsCacheMissed []
135+
writeThisTcData thisTcData
136+
false
137+
138+
| None ->
139+
use _ = Activity.start Activity.Events.reuseTcResultsCacheAbsent []
140+
writeThisTcData thisTcData
141+
false

Diff for: src/Compiler/Driver/fsc.fs

+9
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ open FSharp.Compiler.TypedTree
5454
open FSharp.Compiler.TypedTreeOps
5555
open FSharp.Compiler.XmlDocFileWriter
5656
open FSharp.Compiler.CheckExpressionsOps
57+
open ReuseTcResults
5758

5859
//----------------------------------------------------------------------------
5960
// Reporting - warnings, errors
@@ -162,6 +163,13 @@ let TypeCheck
162163

163164
let eagerFormat (diag: PhasedDiagnostic) = diag.EagerlyFormatCore true
164165

166+
if tcConfig.reuseTcResults = ReuseTcResults.On then
167+
let cachingDriver = CachingDriver(tcConfig)
168+
169+
if cachingDriver.CanReuseTcResults(inputs) then
170+
// do nothing, yet
171+
()
172+
165173
CheckClosedInputSet(
166174
ctok,
167175
(fun () -> diagnosticsLogger.CheckForRealErrorsIgnoringWarnings),
@@ -511,6 +519,7 @@ let main1
511519
)
512520

513521
tcConfigB.exiter <- exiter
522+
tcConfigB.cmdLineArgs <- argv
514523

515524
// Preset: --optimize+ -g --tailcalls+ (see 4505)
516525
SetOptimizeSwitch tcConfigB OptionSwitch.On

Diff for: src/Compiler/FSComp.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1794,3 +1794,4 @@ featureDontWarnOnUppercaseIdentifiersInBindingPatterns,"Don't warn on uppercase
17941794
3874,tcExpectedTypeParamMarkedWithUnitOfMeasureAttribute,"Expected unit-of-measure type parameter must be marked with the [<Measure>] attribute."
17951795
featureDeprecatePlacesWhereSeqCanBeOmitted,"Deprecate places where 'seq' can be omitted"
17961796
featureSupportValueOptionsAsOptionalParameters,"Support ValueOption as valid type for optional member parameters"
1797+
optsReuseTcResults,"Reuse previous typechecking results for faster compilation"

Diff for: src/Compiler/FSharp.Compiler.Service.fsproj

+1
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@
464464
<Content Include="Driver\GraphChecking\Docs.md" />
465465
<Compile Include="Driver\ParseAndCheckInputs.fsi" />
466466
<Compile Include="Driver\ParseAndCheckInputs.fs" />
467+
<Compile Include="Driver\ReuseTcResults\CachingDriver.fs" />
467468
<Compile Include="Driver\ScriptClosure.fsi" />
468469
<Compile Include="Driver\ScriptClosure.fs" />
469470
<Compile Include="Driver\CompilerOptions.fsi" />

Diff for: src/Compiler/SyntaxTree/PrettyNaming.fs

+2
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,8 @@ let FSharpSignatureCompressedDataResourceNameB = "FSharpSignatureCompressedDataB
11321132
let FSharpOptimizationDataResourceName2 = "FSharpOptimizationInfo."
11331133
let FSharpSignatureDataResourceName2 = "FSharpSignatureInfo."
11341134

1135+
let FSharpTcDataResourceName = "FSharpTypecheckingData"
1136+
11351137
[<Literal>]
11361138
let suffixForVariablesThatMayNotBeEliminated = "$cont"
11371139

Diff for: src/Compiler/SyntaxTree/PrettyNaming.fsi

+2
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,8 @@ val internal FSharpOptimizationDataResourceName2: string
284284

285285
val internal FSharpSignatureDataResourceName2: string
286286

287+
val internal FSharpTcDataResourceName: string
288+
287289
val GetLongNameFromString: string -> string list
288290

289291
val FormatAndOtherOverloadsString: int -> string

Diff for: src/Compiler/Utilities/Activity.fs

+6
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ module internal Activity =
6969
module Events =
7070
let cacheHit = "cacheHit"
7171

72+
let reuseTcResultsCachePrefix = "reuseTcResultsCache"
73+
let reuseTcResultsCachePresent = $"{reuseTcResultsCachePrefix}Present"
74+
let reuseTcResultsCacheAbsent = $"{reuseTcResultsCachePrefix}Absent"
75+
let reuseTcResultsCacheHit = $"{reuseTcResultsCachePrefix}Hit"
76+
let reuseTcResultsCacheMissed = $"{reuseTcResultsCachePrefix}Missed"
77+
7278
type Diagnostics.Activity with
7379

7480
member this.RootId =

Diff for: src/Compiler/Utilities/Activity.fsi

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ module internal Activity =
3939

4040
module Events =
4141
val cacheHit: string
42+
val reuseTcResultsCachePrefix: string
43+
val reuseTcResultsCachePresent: string
44+
val reuseTcResultsCacheAbsent: string
45+
val reuseTcResultsCacheHit: string
46+
val reuseTcResultsCacheMissed: string
4247

4348
val startNoTags: name: string -> IDisposable MaybeNull
4449

Diff for: src/Compiler/xlf/FSComp.txt.cs.xlf

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: src/Compiler/xlf/FSComp.txt.de.xlf

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: src/Compiler/xlf/FSComp.txt.es.xlf

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)