diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index a8c0c0285c..8996976818 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -6,6 +6,7 @@ module internal FSharp.Compiler.IlxGen open System.IO open System.Reflection open System.Collections.Generic +open System.Collections.Immutable open FSharp.Compiler.IO open Internal.Utilities @@ -1051,6 +1052,12 @@ and IlxGenEnv = /// Indicates that the .locals init flag should be set on a method and all its nested methods and lambdas initLocals: bool + + /// Delay code gen for files. + delayCodeGen: bool + + /// Collection of code-gen functions where each function represents a file. + delayedFileGen: ImmutableArray<(cenv -> unit) []> } override _.ToString() = "" @@ -2489,7 +2496,7 @@ let rec GenExpr cenv cgbuf eenv sp (expr: Expr) sequel = cenv.exprRecursionDepth <- cenv.exprRecursionDepth - 1 - if cenv.exprRecursionDepth = 0 then + if cenv.exprRecursionDepth = 0 && not eenv.delayCodeGen then ProcessDelayedGenMethods cenv and ProcessDelayedGenMethods cenv = @@ -2713,6 +2720,19 @@ and CodeGenMethodForExpr cenv mgbuf (spReq, entryPointInfo, methodName, eenv, al (fun cgbuf eenv -> GenExpr cenv cgbuf eenv spReq expr0 sequel0), expr0.Range) code + +and DelayCodeGenMethodForExpr cenv mgbuf (spReq, entryPointInfo, methodName, eenv, alreadyUsedArgs, expr0, sequel0) = + let ilLazyCode = + lazy + CodeGenMethodForExpr { cenv with exprRecursionDepth = 0; delayedGenMethods = Queue() } mgbuf (spReq, entryPointInfo, methodName, { eenv with delayCodeGen = false }, alreadyUsedArgs, expr0, sequel0) + + if cenv.exprRecursionDepth > 0 || eenv.delayCodeGen then + cenv.delayedGenMethods.Enqueue(fun _ -> ilLazyCode.Force() |> ignore) + else + // Eagerly codegen if we are not in an expression depth. + ilLazyCode.Force() |> ignore + + ilLazyCode //-------------------------------------------------------------------------- // Generate sequels @@ -6262,8 +6282,14 @@ and GenBindingAfterDebugPoint cenv cgbuf eenv sp (TBind(vspec, rhsExpr, _)) isSt cgbuf.mgbuf.AddOrMergePropertyDef(ilGetterMethSpec.MethodRef.DeclaringTypeRef, ilPropDef, m) let ilMethodDef = - let ilCode = CodeGenMethodForExpr cenv cgbuf.mgbuf (SPSuppress, [], ilGetterMethSpec.Name, eenv, 0, None, rhsExpr, Return) - let ilMethodBody = MethodBody.IL(lazy ilCode) + let ilLazyCode = + if eenv.delayCodeGen then + DelayCodeGenMethodForExpr cenv cgbuf.mgbuf (SPSuppress, [], ilGetterMethSpec.Name, eenv, 0, rhsExpr, Return) + else + let ilCode = CodeGenMethodForExpr cenv cgbuf.mgbuf (SPSuppress, [], ilGetterMethSpec.Name, eenv, 0, rhsExpr, Return) + lazy ilCode + + let ilMethodBody = MethodBody.IL(ilLazyCode) (mkILStaticMethod ([], ilGetterMethSpec.Name, access, [], mkILReturn ilTy, ilMethodBody)).WithSpecialName |> AddNonUserCompilerGeneratedAttribs g @@ -6828,20 +6854,10 @@ and GenMethodForBinding | [h] -> Some h | _ -> None - let ilCodeLazy = lazy CodeGenMethodForExpr cenv mgbuf (SPAlways, tailCallInfo, mspec.Name, eenvForMeth, 0, selfValOpt, bodyExpr, sequel) + let ilLazyCode = DelayCodeGenMethodForExpr cenv mgbuf (SPAlways, tailCallInfo, mspec.Name, eenvForMeth, 0, selfValOpt, bodyExpr, sequel) // This is the main code generation for most methods - false, MethodBody.IL(ilCodeLazy), false - - match ilMethodBody with - | MethodBody.IL(ilCodeLazy) -> - if cenv.exprRecursionDepth > 0 then - cenv.delayedGenMethods.Enqueue(fun _ -> ilCodeLazy.Force() |> ignore) - else - // Eagerly codegen if we are not in an expression depth. - ilCodeLazy.Force() |> ignore - | _ -> - () + false, MethodBody.IL(ilLazyCode), false // Do not generate DllImport attributes into the code - they are implicit from the P/Invoke let attrs = @@ -7748,7 +7764,9 @@ and GenImplFile cenv (mgbuf: AssemblyBuilder) mainInfoOpt eenv (implFile: TypedI let allocVal = ComputeAndAddStorageForLocalTopVal (cenv.amap, g, cenv.intraAssemblyInfo, cenv.opts.isInteractive, NoShadowLocal) AddBindingsForLocalModuleType allocVal clocCcu eenv mexpr.Type - eenvafter + let eenvfinal = { eenvafter with delayedFileGen = eenvafter.delayedFileGen.Add(cenv.delayedGenMethods |> Array.ofSeq) } + cenv.delayedGenMethods.Clear() + eenvfinal and GenForceWholeFileInitializationAsPartOfCCtor cenv (mgbuf: AssemblyBuilder) (lazyInitInfo: ResizeArray<_>) tref imports m = // Authoring a .cctor with effects forces the cctor for the 'initialization' module by doing a dummy store & load of a field @@ -8621,6 +8639,14 @@ let CodegenAssembly cenv eenv mgbuf implFiles = let eenv = List.fold (GenImplFile cenv mgbuf None) eenv a let eenv = GenImplFile cenv mgbuf cenv.opts.mainMethodInfo eenv b + let genMeths = eenv.delayedFileGen |> Array.ofSeq + + genMeths + |> ArrayParallel.iter (fun genMeths -> + genMeths + |> Array.iter (fun gen -> gen cenv) + ) + // Some constructs generate residue types and bindings. Generate these now. They don't result in any // top-level initialization code. let extraBindings = mgbuf.GrabExtraBindingsToGenerate() @@ -8663,6 +8689,8 @@ let GetEmptyIlxGenEnv (g: TcGlobals) ccu = isInLoop = false initLocals = true imports = None + delayCodeGen = true + delayedFileGen = ImmutableArray.Empty } type IlxGenResults = diff --git a/src/fsharp/ParseAndCheckInputs.fs b/src/fsharp/ParseAndCheckInputs.fs index 5ad43a5354..7ab4b20142 100644 --- a/src/fsharp/ParseAndCheckInputs.fs +++ b/src/fsharp/ParseAndCheckInputs.fs @@ -769,7 +769,6 @@ type TcState = { x with tcsTcSigEnv = tcEnvAtEndOfLastInput tcsTcImplEnv = tcEnvAtEndOfLastInput } - /// Create the initial type checking state for compiling an assembly let GetInitialTcState(m, ccuName, tcConfig: TcConfig, tcGlobals, tcImports: TcImports, niceNameGen, tcEnv0, openDecls0) = ignore tcImports @@ -955,6 +954,14 @@ let TypeCheckOneInputEntry (ctok, checkForErrors, tcConfig, tcImports, tcGlobals TypeCheckOneInput (checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, TcResultsSink.NoSink, tcState, inp, false) |> Cancellable.runWithoutCancellation +/// Typecheck a single file (or interactive entry into F# Interactive) +let TypeCheckOneInput (ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt) tcState inp = + TypeCheckOneInputAux(ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt) tcState inp false + +/// Typecheck a single file but skip it if the file is an impl and has a backing sig +let TypeCheckOneInputSkipImpl (ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt) tcState inp = + TypeCheckOneInputAux(ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt) tcState inp true + /// Finish checking multiple files (or one interactive entry into F# Interactive) let TypeCheckMultipleInputsFinish(results, tcState: TcState) = let tcEnvsAtEndFile, topAttrs, implFiles, ccuSigsForFiles = List.unzip4 results diff --git a/src/fsharp/ParseAndCheckInputs.fsi b/src/fsharp/ParseAndCheckInputs.fsi index d0906edbf2..40ba07d80f 100644 --- a/src/fsharp/ParseAndCheckInputs.fsi +++ b/src/fsharp/ParseAndCheckInputs.fsi @@ -85,6 +85,8 @@ type TcState = member CreatesGeneratedProvidedTypes: bool + member RemoveImpl: QualifiedNameOfFile -> TcState + /// Get the initial type checking state for a set of inputs val GetInitialTcState: range * diff --git a/src/fsharp/absil/bytes.fs b/src/fsharp/absil/bytes.fs index ef5556513c..ca8579e55c 100644 --- a/src/fsharp/absil/bytes.fs +++ b/src/fsharp/absil/bytes.fs @@ -5,4 +5,3 @@ namespace FSharp.Compiler.IO - diff --git a/src/fsharp/lib.fsi b/src/fsharp/lib.fsi index bfa6f20296..97924b25ae 100644 --- a/src/fsharp/lib.fsi +++ b/src/fsharp/lib.fsi @@ -305,6 +305,10 @@ type DisposablesTracker = [] module ArrayParallel = + val inline iter : ('T -> unit) -> 'T [] -> unit + + val inline iteri : (int -> 'T -> unit) -> 'T [] -> unit + val inline map : ('T -> 'U) -> 'T [] -> 'U [] val inline mapi : (int -> 'T -> 'U) -> 'T [] -> 'U []