Skip to content

Commit

Permalink
Parallel type-checking in compilation (behind a feature flag) (dotnet…
Browse files Browse the repository at this point in the history
…#14494)

Parallel type-checking in compilation (behind a feature flag)
---------
Co-authored-by: nojaf <florian.verdonck@outlook.com>
  • Loading branch information
safesparrow committed Feb 10, 2023
1 parent a2c0c22 commit 10c1eff
Show file tree
Hide file tree
Showing 70 changed files with 5,270 additions and 232 deletions.
1 change: 0 additions & 1 deletion FSharpBuild.Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
<WarningsAsErrors>1182;0025;$(WarningsAsErrors)</WarningsAsErrors>
<OtherFlags>$(OtherFlags) --nowarn:3384</OtherFlags>
<OtherFlags>$(OtherFlags) --times --nowarn:75</OtherFlags>
<OtherFlags Condition="$(ParallelCheckingWithSignatureFilesOn) == 'true'">$(OtherFlags) --test:ParallelCheckingWithSignatureFilesOn</OtherFlags>
<OtherFlags Condition="$(AdditionalFscCmdFlags) != ''">$(OtherFlags) $(AdditionalFscCmdFlags)</OtherFlags>
</PropertyGroup>

Expand Down
13 changes: 12 additions & 1 deletion src/Compiler/Checking/CheckDeclarations.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3028,6 +3028,17 @@ module EstablishTypeDefinitionCores =
let kind = if hasMeasureAttr then TyparKind.Measure else TyparKind.Type
let ty, _ = TcTypeOrMeasureAndRecover (Some kind) cenv NoNewTypars checkConstraints ItemOccurence.UseInType WarnOnIWSAM.No envinner tpenv rhsType

// Give a warning if `AutoOpenAttribute` is being aliased.
// If the user were to alias the `Microsoft.FSharp.Core.AutoOpenAttribute` type, it would not be detected by the project graph dependency resolution algorithm.
match stripTyEqns g ty with
| AppTy g (tcref, _) when not tcref.IsErased ->
match tcref.CompiledRepresentation with
| CompiledTypeRepr.ILAsmOpen _ -> ()
| CompiledTypeRepr.ILAsmNamed _ ->
if tcref.CompiledRepresentationForNamedType.FullName = g.attrib_AutoOpenAttribute.TypeRef.FullName then
warning(Error(FSComp.SR.chkAutoOpenAttributeInTypeAbbrev(), tycon.Id.idRange))
| _ -> ()

if not firstPass then
let ftyvs = freeInTypeLeftToRight g false ty
let typars = tycon.Typars m
Expand Down Expand Up @@ -4278,7 +4289,7 @@ module TcDeclarations =

if not (isNil members) && tcref.IsTypeAbbrev then
errorR(Error(FSComp.SR.tcTypeAbbreviationsCannotHaveAugmentations(), tyDeclRange))

let (SynComponentInfo (attributes, _, _, _, _, _, _, _)) = synTyconInfo
if not (List.isEmpty attributes) && (declKind = ExtrinsicExtensionBinding || declKind = IntrinsicExtensionBinding) then
let attributeRange = (List.head attributes).Range
Expand Down
27 changes: 24 additions & 3 deletions src/Compiler/Driver/CompilerConfig.fs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,18 @@ type ParallelReferenceResolution =
| On
| Off

[<RequireQualifiedAccess>]
type TypeCheckingMode =
| Sequential
| Graph

[<RequireQualifiedAccess>]
type TypeCheckingConfig =
{
Mode: TypeCheckingMode
DumpGraph: bool
}

[<NoEquality; NoComparison>]
type TcConfigBuilder =
{
Expand Down Expand Up @@ -507,7 +519,6 @@ type TcConfigBuilder =
mutable emitTailcalls: bool
mutable deterministic: bool
mutable concurrentBuild: bool
mutable parallelCheckingWithSignatureFiles: bool
mutable parallelIlxGen: bool
mutable emitMetadataAssembly: MetadataAssemblyGeneration
mutable preferredUiLang: string option
Expand Down Expand Up @@ -591,6 +602,8 @@ type TcConfigBuilder =
mutable parallelReferenceResolution: ParallelReferenceResolution

mutable captureIdentifiersWhenParsing: bool

mutable typeCheckingConfig: TypeCheckingConfig
}

// Directories to start probing in
Expand Down Expand Up @@ -737,7 +750,6 @@ type TcConfigBuilder =
emitTailcalls = true
deterministic = false
concurrentBuild = true
parallelCheckingWithSignatureFiles = FSharpExperimentalFeaturesEnabledAutomatically
parallelIlxGen = FSharpExperimentalFeaturesEnabledAutomatically
emitMetadataAssembly = MetadataAssemblyGeneration.None
preferredUiLang = None
Expand Down Expand Up @@ -782,6 +794,15 @@ type TcConfigBuilder =
exiter = QuitProcessExiter
parallelReferenceResolution = ParallelReferenceResolution.Off
captureIdentifiersWhenParsing = false
typeCheckingConfig =
{
TypeCheckingConfig.Mode =
if FSharpExperimentalFeaturesEnabledAutomatically then
TypeCheckingMode.Graph
else
TypeCheckingMode.Sequential
DumpGraph = false
}
}

member tcConfigB.FxResolver =
Expand Down Expand Up @@ -1286,7 +1307,6 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) =
member _.emitTailcalls = data.emitTailcalls
member _.deterministic = data.deterministic
member _.concurrentBuild = data.concurrentBuild
member _.parallelCheckingWithSignatureFiles = data.parallelCheckingWithSignatureFiles
member _.parallelIlxGen = data.parallelIlxGen
member _.emitMetadataAssembly = data.emitMetadataAssembly
member _.pathMap = data.pathMap
Expand Down Expand Up @@ -1322,6 +1342,7 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) =
member _.exiter = data.exiter
member _.parallelReferenceResolution = data.parallelReferenceResolution
member _.captureIdentifiersWhenParsing = data.captureIdentifiersWhenParsing
member _.typeCheckingConfig = data.typeCheckingConfig

static member Create(builder, validate) =
use _ = UseBuildPhase BuildPhase.Parameter
Expand Down
26 changes: 22 additions & 4 deletions src/Compiler/Driver/CompilerConfig.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,24 @@ type ParallelReferenceResolution =
| On
| Off

/// Determines the algorithm used for type-checking.
[<RequireQualifiedAccess>]
type TypeCheckingMode =
/// Default mode where all source files are processed sequentially in compilation order.
| Sequential
/// Parallel type-checking that uses automated file-to-file dependency detection to construct a file graph processed in parallel.
| Graph

/// Some of the information dedicated to type-checking.
[<RequireQualifiedAccess>]
type TypeCheckingConfig =
{
Mode: TypeCheckingMode
/// When using TypeCheckingMode.Graph, this flag determines whether the
/// resolved file graph should be serialised as a Mermaid diagram into a file next to the output dll.
DumpGraph: bool
}

[<NoEquality; NoComparison>]
type TcConfigBuilder =
{
Expand Down Expand Up @@ -412,8 +430,6 @@ type TcConfigBuilder =

mutable concurrentBuild: bool

mutable parallelCheckingWithSignatureFiles: bool

mutable parallelIlxGen: bool

mutable emitMetadataAssembly: MetadataAssemblyGeneration
Expand Down Expand Up @@ -495,6 +511,8 @@ type TcConfigBuilder =
mutable parallelReferenceResolution: ParallelReferenceResolution

mutable captureIdentifiersWhenParsing: bool

mutable typeCheckingConfig: TypeCheckingConfig
}

static member CreateNew:
Expand Down Expand Up @@ -738,8 +756,6 @@ type TcConfig =

member concurrentBuild: bool

member parallelCheckingWithSignatureFiles: bool

member parallelIlxGen: bool

member emitMetadataAssembly: MetadataAssemblyGeneration
Expand Down Expand Up @@ -866,6 +882,8 @@ type TcConfig =

member captureIdentifiersWhenParsing: bool

member typeCheckingConfig: TypeCheckingConfig

/// 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.
[<Sealed>]
Expand Down
11 changes: 10 additions & 1 deletion src/Compiler/Driver/CompilerOptions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1372,8 +1372,17 @@ let testFlag tcConfigB =
| "ShowLoadedAssemblies" -> tcConfigB.showLoadedAssemblies <- true
| "ContinueAfterParseFailure" -> tcConfigB.continueAfterParseFailure <- true
| "ParallelOff" -> tcConfigB.concurrentBuild <- false
| "ParallelCheckingWithSignatureFilesOn" -> tcConfigB.parallelCheckingWithSignatureFiles <- true
| "ParallelIlxGen" -> tcConfigB.parallelIlxGen <- true
| "GraphBasedChecking" ->
tcConfigB.typeCheckingConfig <-
{ tcConfigB.typeCheckingConfig with
Mode = TypeCheckingMode.Graph
}
| "DumpCheckingGraph" ->
tcConfigB.typeCheckingConfig <-
{ tcConfigB.typeCheckingConfig with
DumpGraph = true
}
#if DEBUG
| "ShowParserStackOnParseError" -> showParserStackOnParseError <- true
#endif
Expand Down
15 changes: 15 additions & 0 deletions src/Compiler/Driver/GraphChecking/Continuation.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[<RequireQualifiedAccess>]
module internal Continuation

let rec sequence<'T, 'TReturn> (recursions: (('T -> 'TReturn) -> 'TReturn) list) (finalContinuation: 'T list -> 'TReturn) : 'TReturn =
match recursions with
| [] -> finalContinuation []
| andThenInner :: andThenInners ->
fun (results: 'T list) ->
fun (result: 'T) -> result :: results |> finalContinuation
|> andThenInner
|> sequence andThenInners

let concatenate<'T, 'TReturn> (recursions: (('T list -> 'TReturn) -> 'TReturn) list) (finalContinuation: 'T list -> 'TReturn) : 'TReturn =
let ultimateContinuation = List.concat >> finalContinuation
sequence recursions ultimateContinuation
23 changes: 23 additions & 0 deletions src/Compiler/Driver/GraphChecking/Continuation.fsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[<RequireQualifiedAccess>]
module internal Continuation

/// This function sequences computations that have been expressed in continuation-passing style.
/// Concretely, when 'T is `int` as an example, can be expressed in continuation-passing style as a function,
/// taking as its input another function that is "how to proceed with a computation given the value of the integer",
/// and returning "the result of that computation".
/// That is, an integer is equivalently represented as a generic function (howToProceed : int -> 'TReturn) -> 'TReturn,
/// and the effect of the function corresponding to the integer 3 is simply to apply the input `howToProceed` to the value 3.
///
/// The motivation for Continuation.sequence is most easily understood when it is viewed without its second argument:
/// it is a higher-order function that takes "a list of 'T expressed in continuation-passing style", and returns "a 'T list expressed in continuation-passing style".
/// The resulting "continuation-passing 'T list" operates by chaining the input 'Ts together, and finally returning the result of continuing the computation after first sequencing the inputs.
///
/// Crucially, this technique can be used to enable unbounded recursion:
/// it constructs and invokes closures representing intermediate stages of the sequenced computation on the heap, rather than consuming space on the (more constrained) stack.
val sequence<'T, 'TReturn> :
recursions: (('T -> 'TReturn) -> 'TReturn) list -> finalContinuation: ('T list -> 'TReturn) -> 'TReturn

/// Auxiliary function for `Continuation.sequence` that assumes the recursions return a 'T list.
/// In the final continuation the `'T list list` will first be concatenated into one list, before being passed to the (final) `continuation`.
val concatenate<'T, 'TReturn> :
recursions: (('T list -> 'TReturn) -> 'TReturn) list -> finalContinuation: ('T list -> 'TReturn) -> 'TReturn

0 comments on commit 10c1eff

Please sign in to comment.