Skip to content

Commit 56f7ce5

Browse files
authored
Reuse typechecking results - stage 1
1 parent c13108d commit 56f7ce5

38 files changed

+517
-40
lines changed

docs/release-notes/.FSharp.Compiler.Service/9.0.200.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
### Added
2525

26+
* New flag `--reusetypecheckingresults`, for skipping recompilation in some cases
2627
* Let `dotnet fsi --help` print a link to the documentation website. ([PR #18006](https://github.com/dotnet/fsharp/pull/18006))
2728
* 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))
2829
* Support literal attribute on decimals ([PR #17769](https://github.com/dotnet/fsharp/pull/17769))

src/Compiler/Driver/CompilerConfig.fs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,11 @@ type TypeCheckingMode =
444444
| Sequential
445445
| Graph
446446

447+
[<RequireQualifiedAccess>]
448+
type ReuseTcResults =
449+
| On
450+
| Off
451+
447452
[<RequireQualifiedAccess>]
448453
type TypeCheckingConfig =
449454
{
@@ -651,6 +656,8 @@ type TcConfigBuilder =
651656

652657
mutable parallelReferenceResolution: ParallelReferenceResolution
653658

659+
mutable reuseTcResults: ReuseTcResults
660+
654661
mutable captureIdentifiersWhenParsing: bool
655662

656663
mutable typeCheckingConfig: TypeCheckingConfig
@@ -660,6 +667,8 @@ type TcConfigBuilder =
660667
mutable realsig: bool
661668

662669
mutable compilationMode: TcGlobals.CompilationMode
670+
671+
mutable cmdLineArgs: string array
663672
}
664673

665674
// Directories to start probing in
@@ -861,6 +870,7 @@ type TcConfigBuilder =
861870
xmlDocInfoLoader = None
862871
exiter = QuitProcessExiter
863872
parallelReferenceResolution = ParallelReferenceResolution.Off
873+
reuseTcResults = ReuseTcResults.Off
864874
captureIdentifiersWhenParsing = false
865875
typeCheckingConfig =
866876
{
@@ -875,6 +885,7 @@ type TcConfigBuilder =
875885
realsig = false
876886
strictIndentation = None
877887
compilationMode = TcGlobals.CompilationMode.Unset
888+
cmdLineArgs = [||]
878889
}
879890

880891
member tcConfigB.FxResolver =
@@ -1415,11 +1426,13 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) =
14151426
member _.xmlDocInfoLoader = data.xmlDocInfoLoader
14161427
member _.exiter = data.exiter
14171428
member _.parallelReferenceResolution = data.parallelReferenceResolution
1429+
member _.reuseTcResults = data.reuseTcResults
14181430
member _.captureIdentifiersWhenParsing = data.captureIdentifiersWhenParsing
14191431
member _.typeCheckingConfig = data.typeCheckingConfig
14201432
member _.dumpSignatureData = data.dumpSignatureData
14211433
member _.realsig = data.realsig
14221434
member _.compilationMode = data.compilationMode
1435+
member _.cmdLineArgs = data.cmdLineArgs
14231436

14241437
static member Create(builder, validate) =
14251438
use _ = UseBuildPhase BuildPhase.Parameter

src/Compiler/Driver/CompilerConfig.fsi

Lines changed: 13 additions & 0 deletions
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>]

src/Compiler/Driver/CompilerOptions.fs

Lines changed: 8 additions & 0 deletions
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)

src/Compiler/Driver/ParseAndCheckInputs.fs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ open FSharp.Compiler.LexerStore
3131
open FSharp.Compiler.Lexhelp
3232
open FSharp.Compiler.NameResolution
3333
open FSharp.Compiler.ParseHelpers
34+
open FSharp.Compiler.ReuseTcResults
3435
open FSharp.Compiler.Syntax
3536
open FSharp.Compiler.SyntaxTrivia
3637
open FSharp.Compiler.Syntax.PrettyNaming
@@ -1960,6 +1961,10 @@ let CheckMultipleInputsUsingGraphMode
19601961
partialResults, tcState)
19611962

19621963
let CheckClosedInputSet (ctok, checkForErrors, tcConfig: TcConfig, tcImports, tcGlobals, prefixPathOpt, tcState, eagerFormat, inputs) =
1964+
1965+
if tcConfig.reuseTcResults = ReuseTcResults.On then
1966+
CachingDriver(tcConfig).TryReuseTcResults(inputs)
1967+
19631968
// tcEnvAtEndOfLastFile is the environment required by fsi.exe when incrementally adding definitions
19641969
let results, tcState =
19651970
match tcConfig.typeCheckingConfig.Mode with
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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 _.TryReuseTcResults 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+
133+
() // do nothing, yet
134+
else
135+
use _ = Activity.start Activity.Events.reuseTcResultsCacheMissed []
136+
137+
writeThisTcData thisTcData
138+
139+
| None ->
140+
use _ = Activity.start Activity.Events.reuseTcResultsCacheAbsent []
141+
142+
writeThisTcData thisTcData
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module internal FSharp.Compiler.ReuseTcResults
2+
3+
open FSharp.Compiler.CompilerConfig
4+
open FSharp.Compiler.Syntax
5+
6+
[<Sealed>]
7+
type CachingDriver =
8+
9+
new: tcConfig: TcConfig -> CachingDriver
10+
11+
member TryReuseTcResults: inputs: ParsedInput seq -> unit

src/Compiler/Driver/fsc.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,7 @@ let main1
511511
)
512512

513513
tcConfigB.exiter <- exiter
514+
tcConfigB.cmdLineArgs <- argv
514515

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

src/Compiler/FSComp.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,3 +1790,4 @@ featureUseTypeSubsumptionCache,"Use type conversion cache during compilation"
17901790
featureDontWarnOnUppercaseIdentifiersInBindingPatterns,"Don't warn on uppercase identifiers in binding patterns"
17911791
3873,chkDeprecatePlacesWhereSeqCanBeOmitted,"This construct is deprecated. Sequence expressions should be of the form 'seq {{ ... }}'"
17921792
featureDeprecatePlacesWhereSeqCanBeOmitted,"Deprecate places where 'seq' can be omitted"
1793+
optsReuseTcResults,"Reuse previous typechecking results for faster compilation"

src/Compiler/FSharp.Compiler.Service.fsproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,8 @@
462462
<Compile Include="Driver\GraphChecking\GraphProcessing.fsi" />
463463
<Compile Include="Driver\GraphChecking\GraphProcessing.fs" />
464464
<Content Include="Driver\GraphChecking\Docs.md" />
465+
<Compile Include="Driver\ReuseTcResults\CachingDriver.fsi" />
466+
<Compile Include="Driver\ReuseTcResults\CachingDriver.fs" />
465467
<Compile Include="Driver\ParseAndCheckInputs.fsi" />
466468
<Compile Include="Driver\ParseAndCheckInputs.fs" />
467469
<Compile Include="Driver\ScriptClosure.fsi" />

0 commit comments

Comments
 (0)