diff --git a/src/Compiler/Driver/CompilerConfig.fs b/src/Compiler/Driver/CompilerConfig.fs index 774cb954ca9..08946b84f63 100644 --- a/src/Compiler/Driver/CompilerConfig.fs +++ b/src/Compiler/Driver/CompilerConfig.fs @@ -388,6 +388,11 @@ type MetadataAssemblyGeneration = | ReferenceOut of outputPath: string | ReferenceOnly +[] +type ParallelReferenceResolution = + | On + | Off + [] type TcConfigBuilder = { @@ -580,6 +585,8 @@ type TcConfigBuilder = mutable xmlDocInfoLoader: IXmlDocumentationInfoLoader option mutable exiter: Exiter + + mutable parallelReferenceResolution: ParallelReferenceResolution } // Directories to start probing in @@ -767,6 +774,7 @@ type TcConfigBuilder = sdkDirOverride = sdkDirOverride xmlDocInfoLoader = None exiter = QuitProcessExiter + parallelReferenceResolution = ParallelReferenceResolution.Off } member tcConfigB.FxResolver = @@ -1310,6 +1318,7 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) = member _.applyLineDirectives = data.applyLineDirectives member _.xmlDocInfoLoader = data.xmlDocInfoLoader member _.exiter = data.exiter + member _.parallelReferenceResolution = data.parallelReferenceResolution static member Create(builder, validate) = use _ = UseBuildPhase BuildPhase.Parameter diff --git a/src/Compiler/Driver/CompilerConfig.fsi b/src/Compiler/Driver/CompilerConfig.fsi index 342c767bbb2..70abf7beb63 100644 --- a/src/Compiler/Driver/CompilerConfig.fsi +++ b/src/Compiler/Driver/CompilerConfig.fsi @@ -198,6 +198,11 @@ type MetadataAssemblyGeneration = /// Only emits the assembly as a reference assembly. | ReferenceOnly +[] +type ParallelReferenceResolution = + | On + | Off + [] type TcConfigBuilder = { @@ -482,6 +487,8 @@ type TcConfigBuilder = mutable xmlDocInfoLoader: IXmlDocumentationInfoLoader option mutable exiter: Exiter + + mutable parallelReferenceResolution: ParallelReferenceResolution } static member CreateNew: @@ -845,6 +852,8 @@ type TcConfig = member exiter: Exiter + member parallelReferenceResolution: ParallelReferenceResolution + /// Represents a computation to return a TcConfig. Normally this is just a constant immutable TcConfig, /// but for F# Interactive it may be based on an underlying mutable TcConfigBuilder. [] diff --git a/src/Compiler/Driver/CompilerImports.fs b/src/Compiler/Driver/CompilerImports.fs index 538ccd0817b..56513446bd3 100644 --- a/src/Compiler/Driver/CompilerImports.fs +++ b/src/Compiler/Driver/CompilerImports.fs @@ -2141,6 +2141,12 @@ and [] TcImports node { CheckDisposed() + let tcConfig = tcConfigP.Get ctok + let runMethod = + match tcConfig.parallelReferenceResolution with + | ParallelReferenceResolution.On -> NodeCode.Parallel + | ParallelReferenceResolution.Off -> NodeCode.Sequential + let! results = nms |> List.map (fun nm -> @@ -2151,7 +2157,7 @@ and [] TcImports errorR (Error(FSComp.SR.buildProblemReadingAssembly (nm.resolvedPath, e.Message), nm.originalReference.Range)) return None }) - |> NodeCode.Sequential + |> runMethod let dllinfos, phase2s = results |> Array.choose id |> List.ofArray |> List.unzip fixupOrphanCcus () diff --git a/src/Compiler/Driver/CompilerOptions.fs b/src/Compiler/Driver/CompilerOptions.fs index 090131c2e3c..15abdfb5106 100644 --- a/src/Compiler/Driver/CompilerOptions.fs +++ b/src/Compiler/Driver/CompilerOptions.fs @@ -1680,6 +1680,14 @@ let internalFlags (tcConfigB: TcConfigBuilder) = None ) + CompilerOption( + "parallelreferenceresolution", + tagNone, + OptionUnit(fun () -> tcConfigB.parallelReferenceResolution <- ParallelReferenceResolution.On), + Some(InternalCommandLineOption("--parallelreferenceresolution", rangeCmdArgs)), + None + ) + testFlag tcConfigB ] @ diff --git a/src/Compiler/Driver/fsc.fs b/src/Compiler/Driver/fsc.fs index fce47af281e..270f6e948b9 100644 --- a/src/Compiler/Driver/fsc.fs +++ b/src/Compiler/Driver/fsc.fs @@ -446,6 +446,18 @@ let TryFindVersionAttribute g attrib attribName attribs deterministic = [] type Args<'T> = Args of 'T +let getParallelReferenceResolutionFromEnvironment () = + Environment.GetEnvironmentVariable("FCS_ParallelReferenceResolution") + |> Option.ofObj + |> Option.bind (fun flag -> + match bool.TryParse flag with + | true, runInParallel -> + if runInParallel then + Some ParallelReferenceResolution.On + else + Some ParallelReferenceResolution.Off + | false, _ -> None) + /// First phase of compilation. /// - Set up console encoding and code page settings /// - Process command line, flags and collect filenames @@ -533,6 +545,11 @@ let main1 tcConfigB.conditionalDefines <- "COMPILED" :: tcConfigB.conditionalDefines + // Override ParallelReferenceResolution set on the CLI with an environment setting if present. + match getParallelReferenceResolutionFromEnvironment () with + | Some parallelReferenceResolution -> tcConfigB.parallelReferenceResolution <- parallelReferenceResolution + | None -> () + // Display the banner text, if necessary if not bannerAlreadyPrinted then Console.Write(GetBannerText tcConfigB) diff --git a/src/Compiler/Driver/fsc.fsi b/src/Compiler/Driver/fsc.fsi index 5b37ace9224..8731b5fae0c 100644 --- a/src/Compiler/Driver/fsc.fsi +++ b/src/Compiler/Driver/fsc.fsi @@ -55,3 +55,6 @@ val CompileFromCommandLineArguments: tcImportsCapture: (TcImports -> unit) option * dynamicAssemblyCreator: (TcConfig * TcGlobals * string * ILModuleDef -> unit) option -> unit + +/// Read the parallelReferenceResolution flag from environment variables +val internal getParallelReferenceResolutionFromEnvironment: unit -> ParallelReferenceResolution option diff --git a/src/Compiler/Facilities/BuildGraph.fs b/src/Compiler/Facilities/BuildGraph.fs index 8227b96043f..6170d726d75 100644 --- a/src/Compiler/Facilities/BuildGraph.fs +++ b/src/Compiler/Facilities/BuildGraph.fs @@ -182,6 +182,12 @@ type NodeCode private () = return results.ToArray() } + + static member Parallel (computations: NodeCode<'T> seq) = + computations + |> Seq.map (fun (Node x) -> x) + |> Async.Parallel + |> Node type private AgentMessage<'T> = GetValue of AsyncReplyChannel> * callerCancellationToken: CancellationToken @@ -331,7 +337,7 @@ type GraphNode<'T>(retryCompute: bool, computation: NodeCode<'T>) = // occur, making sure we are under the protection of the 'try'. // For example, NodeCode's 'try/finally' (TryFinally) uses async.TryFinally which does // implicit cancellation checks even before the try is entered, as do the - // de-sugaring of 'do!' and other CodeCode constructs. + // de-sugaring of 'do!' and other NodeCode constructs. let mutable taken = false try diff --git a/src/Compiler/Facilities/BuildGraph.fsi b/src/Compiler/Facilities/BuildGraph.fsi index 798653f5f4b..76001d940da 100644 --- a/src/Compiler/Facilities/BuildGraph.fsi +++ b/src/Compiler/Facilities/BuildGraph.fsi @@ -65,6 +65,8 @@ type NodeCode = static member Sequential: computations: NodeCode<'T> seq -> NodeCode<'T[]> + static member Parallel: computations: (NodeCode<'T> seq) -> NodeCode<'T[]> + /// Execute the cancellable computation synchronously using the ambient cancellation token of /// the NodeCode. static member FromCancellable: computation: Cancellable<'T> -> NodeCode<'T> diff --git a/src/Compiler/Service/IncrementalBuild.fs b/src/Compiler/Service/IncrementalBuild.fs index a1673102dc2..f3655c4cab3 100644 --- a/src/Compiler/Service/IncrementalBuild.fs +++ b/src/Compiler/Service/IncrementalBuild.fs @@ -1432,7 +1432,8 @@ type IncrementalBuilder(initialState: IncrementalBuilderInitialState, state: Inc enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking: bool, enableParallelCheckingWithSignatureFiles: bool, - dependencyProvider + dependencyProvider, + parallelReferenceResolution ) = let useSimpleResolutionSwitch = "--simpleresolution" @@ -1514,6 +1515,7 @@ type IncrementalBuilder(initialState: IncrementalBuilderInitialState, state: Inc |> Some tcConfigB.parallelCheckingWithSignatureFiles <- enableParallelCheckingWithSignatureFiles + tcConfigB.parallelReferenceResolution <- parallelReferenceResolution tcConfigB, sourceFilesNew diff --git a/src/Compiler/Service/IncrementalBuild.fsi b/src/Compiler/Service/IncrementalBuild.fsi index 4843a5061e7..481ed50689e 100755 --- a/src/Compiler/Service/IncrementalBuild.fsi +++ b/src/Compiler/Service/IncrementalBuild.fsi @@ -264,7 +264,8 @@ type internal IncrementalBuilder = enableBackgroundItemKeyStoreAndSemanticClassification: bool * enablePartialTypeChecking: bool * enableParallelCheckingWithSignatureFiles: bool * - dependencyProvider: DependencyProvider option -> + dependencyProvider: DependencyProvider option * + parallelReferenceResolution: ParallelReferenceResolution -> NodeCode /// Generalized Incremental Builder. This is exposed only for unit testing purposes. diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 42f0be91ea0..9328b513aa5 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -175,7 +175,8 @@ type BackgroundCompiler keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking, - enableParallelCheckingWithSignatureFiles + enableParallelCheckingWithSignatureFiles, + parallelReferenceResolution ) as self = let beforeFileChecked = Event() @@ -303,7 +304,8 @@ type BackgroundCompiler enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking, enableParallelCheckingWithSignatureFiles, - dependencyProvider + dependencyProvider, + parallelReferenceResolution ) match builderOpt with @@ -1096,7 +1098,8 @@ type FSharpChecker keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking, - enableParallelCheckingWithSignatureFiles + enableParallelCheckingWithSignatureFiles, + parallelReferenceResolution ) = let backgroundCompiler = @@ -1110,7 +1113,8 @@ type FSharpChecker keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking, - enableParallelCheckingWithSignatureFiles + enableParallelCheckingWithSignatureFiles, + parallelReferenceResolution ) static let globalInstance = lazy FSharpChecker.Create() @@ -1122,6 +1126,24 @@ type FSharpChecker let braceMatchCache = MruCache(braceMatchCacheSize, areSimilar = AreSimilarForParsing, areSame = AreSameForParsing) + static let inferParallelReferenceResolution (parallelReferenceResolution: bool option) = + let explicitValue = + parallelReferenceResolution + |> Option.defaultValue false + |> function + | true -> ParallelReferenceResolution.On + | false -> ParallelReferenceResolution.Off + + let withEnvOverride = + // Override ParallelReferenceResolution set on the constructor with an environment setting if present. + getParallelReferenceResolutionFromEnvironment () + |> Option.defaultValue explicitValue + + withEnvOverride + + static member getParallelReferenceResolutionFromEnvironment() = + getParallelReferenceResolutionFromEnvironment () + /// Instantiate an interactive checker. static member Create ( @@ -1134,7 +1156,8 @@ type FSharpChecker ?keepAllBackgroundSymbolUses, ?enableBackgroundItemKeyStoreAndSemanticClassification, ?enablePartialTypeChecking, - ?enableParallelCheckingWithSignatureFiles + ?enableParallelCheckingWithSignatureFiles, + ?parallelReferenceResolution ) = let legacyReferenceResolver = @@ -1158,6 +1181,8 @@ type FSharpChecker if keepAssemblyContents && enablePartialTypeChecking then invalidArg "enablePartialTypeChecking" "'keepAssemblyContents' and 'enablePartialTypeChecking' cannot be both enabled." + let parallelReferenceResolution = inferParallelReferenceResolution parallelReferenceResolution + FSharpChecker( legacyReferenceResolver, projectCacheSizeReal, @@ -1168,7 +1193,8 @@ type FSharpChecker keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking, - enableParallelCheckingWithSignatureFiles + enableParallelCheckingWithSignatureFiles, + parallelReferenceResolution ) member _.ReferenceResolver = legacyReferenceResolver diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index 50b0a89eb11..31801bfbf46 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -8,6 +8,7 @@ open System open System.IO open FSharp.Compiler.AbstractIL.ILBinaryReader open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CompilerConfig open FSharp.Compiler.Diagnostics open FSharp.Compiler.EditorServices open FSharp.Compiler.Symbols @@ -32,6 +33,7 @@ type public FSharpChecker = /// Indicates whether a table of symbol keys should be kept for background compilation /// Indicates whether to perform partial type checking. Cannot be set to true if keepAssmeblyContents is true. If set to true, can cause duplicate type-checks when richer information on a file is needed, but can skip background type-checking entirely on implementation files with signature files. /// Type check implementation files that are backed by a signature file in parallel. + /// Indicates whether to resolve references in parallel. static member Create: ?projectCacheSize: int * ?keepAssemblyContents: bool * @@ -42,7 +44,8 @@ type public FSharpChecker = ?keepAllBackgroundSymbolUses: bool * ?enableBackgroundItemKeyStoreAndSemanticClassification: bool * ?enablePartialTypeChecking: bool * - ?enableParallelCheckingWithSignatureFiles: bool -> + ?enableParallelCheckingWithSignatureFiles: bool * + ?parallelReferenceResolution: bool -> FSharpChecker /// diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected b/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected index 96da7c4c474..736193358e7 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected @@ -2009,7 +2009,7 @@ FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] DependencyFiles FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] get_DependencyFiles() FSharp.Compiler.CodeAnalysis.FSharpChecker -FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Instance FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker get_Instance() FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions GetProjectOptionsFromCommandLineArgs(System.String, System.String[], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean])