diff --git a/src/fsharp/CompileOptions.fs b/src/fsharp/CompileOptions.fs index c4595d2dc2433ec63294b3e3dc23665eb2dc18ab..c7ef18c94b8dc5e4ea9e6e5634e9b4ca1788f7ea 100644 --- a/src/fsharp/CompileOptions.fs +++ b/src/fsharp/CompileOptions.fs @@ -395,7 +395,7 @@ let SetOptimizeOn(tcConfigB: TcConfigBuilder) = tcConfigB.optSettings <- { tcConfigB.optSettings with jitOptUser = Some true } tcConfigB.optSettings <- { tcConfigB.optSettings with localOptUser = Some true } tcConfigB.optSettings <- { tcConfigB.optSettings with crossModuleOptUser = Some true } - tcConfigB.optSettings <- { tcConfigB.optSettings with lambdaInlineThreshold = 6 } + tcConfigB.optSettings <- { tcConfigB.optSettings with lambdaInlineThreshold = Optimizer.LambdaInlineThresholdDefault } tcConfigB.doDetuple <- true tcConfigB.doTLR <- true tcConfigB.doFinalSimplify <- true diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index d743c30e525a2e4bfa836460d777cd03cdd327e5..857365a6fca3a2c0f1d2a2203b3728140f52dbce 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -2353,9 +2353,7 @@ and CodeGenMethodForExpr cenv mgbuf (spReq, entryPointInfo, methodName, eenv, al CodeGenMethod cenv mgbuf (entryPointInfo, methodName, eenv, alreadyUsedArgs, (fun cgbuf eenv -> GenExpr cenv cgbuf eenv spReq expr0 sequel0), expr0.Range) - code - - + code //-------------------------------------------------------------------------- // Generate sequels @@ -4305,9 +4303,65 @@ and GenGenericParams cenv eenv tps = and GenGenericArgs m (tyenv: TypeReprEnv) tps = tps |> DropErasedTypars |> List.map (fun c -> (mkILTyvarTy tyenv.[c, m])) +and DelayGenMethodForLambda cenv mgbuf eenv args = + cenv.delayedGenMethods.Enqueue(fun cenv -> GenMethodForLambda cenv mgbuf eenv args) + +and GenMethodForLambda cenv mgbuf eenv (entryPointInfo, cloinfo, eenvinner, body, isLocalTypeFunc, m) = + let g = cenv.g + let ilCloBody = CodeGenMethodForExpr cenv mgbuf (SPAlways, entryPointInfo, cloinfo.cloName, eenvinner, 1, body, Return) + let ilCloTypeRef = cloinfo.cloSpec.TypeRef + let cloTypeDefs = + if isLocalTypeFunc then + + // Work out the contract type and generate a class with an abstract method for this type + let (ilContractGenericParams, ilContractMethTyargs, ilContractTySpec: ILTypeSpec, ilContractFormalRetTy) = GenNamedLocalTypeFuncContractInfo cenv eenv m cloinfo + let ilContractTypeRef = ilContractTySpec.TypeRef + let ilContractTy = mkILFormalBoxedTy ilContractTypeRef ilContractGenericParams + let ilContractCtor = mkILNonGenericEmptyCtor None g.ilg.typ_Object + + let ilContractMeths = [ilContractCtor; mkILGenericVirtualMethod("DirectInvoke", ILMemberAccess.Assembly, ilContractMethTyargs, [], mkILReturn ilContractFormalRetTy, MethodBody.Abstract) ] + let ilContractTypeDef = + ILTypeDef(name = ilContractTypeRef.Name, + layout = ILTypeDefLayout.Auto, + attributes = enum 0, + genericParams = ilContractGenericParams, + customAttrs = mkILCustomAttrs [mkCompilationMappingAttr g (int SourceConstructFlags.Closure) ], + fields = emptyILFields, + events= emptyILEvents, + properties = emptyILProperties, + methods= mkILMethods ilContractMeths, + methodImpls= emptyILMethodImpls, + nestedTypes=emptyILTypeDefs, + implements = [], + extends= Some g.ilg.typ_Object, + securityDecls= emptyILSecurityDecls) + + // the contract type is an abstract type and not sealed + let ilContractTypeDef = + ilContractTypeDef + .WithAbstract(true) + .WithAccess(ComputeTypeAccess ilContractTypeRef true) + .WithSerializable(true) + .WithSpecialName(true) + .WithLayout(ILTypeDefLayout.Auto) + .WithInitSemantics(ILTypeInit.BeforeField) + .WithEncoding(ILDefaultPInvokeEncoding.Auto) + + mgbuf.AddTypeDef(ilContractTypeRef, ilContractTypeDef, false, false, None) + + let ilCtorBody = mkILMethodBody (true, [], 8, nonBranchingInstrsToCode (mkCallBaseConstructor(ilContractTy, [])), None ) + let cloMethods = [ mkILGenericVirtualMethod("DirectInvoke", ILMemberAccess.Assembly, cloinfo.localTypeFuncDirectILGenericParams, [], mkILReturn (cloinfo.cloILFormalRetTy), MethodBody.IL ilCloBody) ] + let cloTypeDefs = GenClosureTypeDefs cenv (ilCloTypeRef, cloinfo.cloILGenericParams, [], cloinfo.cloILFreeVars, cloinfo.ilCloLambdas, ilCtorBody, cloMethods, [], ilContractTy, []) + cloTypeDefs + + else + GenClosureTypeDefs cenv (ilCloTypeRef, cloinfo.cloILGenericParams, [], cloinfo.cloILFreeVars, cloinfo.ilCloLambdas, ilCloBody, [], [], g.ilg.typ_Object, []) + CountClosure() + for cloTypeDef in cloTypeDefs do + mgbuf.AddTypeDef(ilCloTypeRef, cloTypeDef, false, false, None) + /// Generate the closure class for a function and GenLambdaClosure cenv (cgbuf: CodeGenBuffer) eenv isLocalTypeFunc selfv expr = - let g = cenv.g match expr with | Expr.Lambda (_, _, _, _, _, m, _) | Expr.TyLambda (_, _, _, m, _) -> @@ -4319,57 +4373,7 @@ and GenLambdaClosure cenv (cgbuf: CodeGenBuffer) eenv isLocalTypeFunc selfv expr | Some v -> [(v, BranchCallClosure (cloinfo.cloArityInfo))] | _ -> [] - let ilCloBody = CodeGenMethodForExpr cenv cgbuf.mgbuf (SPAlways, entryPointInfo, cloinfo.cloName, eenvinner, 1, body, Return) - let ilCloTypeRef = cloinfo.cloSpec.TypeRef - let cloTypeDefs = - if isLocalTypeFunc then - - // Work out the contract type and generate a class with an abstract method for this type - let (ilContractGenericParams, ilContractMethTyargs, ilContractTySpec: ILTypeSpec, ilContractFormalRetTy) = GenNamedLocalTypeFuncContractInfo cenv eenv m cloinfo - let ilContractTypeRef = ilContractTySpec.TypeRef - let ilContractTy = mkILFormalBoxedTy ilContractTypeRef ilContractGenericParams - let ilContractCtor = mkILNonGenericEmptyCtor None g.ilg.typ_Object - - let ilContractMeths = [ilContractCtor; mkILGenericVirtualMethod("DirectInvoke", ILMemberAccess.Assembly, ilContractMethTyargs, [], mkILReturn ilContractFormalRetTy, MethodBody.Abstract) ] - let ilContractTypeDef = - ILTypeDef(name = ilContractTypeRef.Name, - layout = ILTypeDefLayout.Auto, - attributes = enum 0, - genericParams = ilContractGenericParams, - customAttrs = mkILCustomAttrs [mkCompilationMappingAttr g (int SourceConstructFlags.Closure) ], - fields = emptyILFields, - events= emptyILEvents, - properties = emptyILProperties, - methods= mkILMethods ilContractMeths, - methodImpls= emptyILMethodImpls, - nestedTypes=emptyILTypeDefs, - implements = [], - extends= Some g.ilg.typ_Object, - securityDecls= emptyILSecurityDecls) - - // the contract type is an abstract type and not sealed - let ilContractTypeDef = - ilContractTypeDef - .WithAbstract(true) - .WithAccess(ComputeTypeAccess ilContractTypeRef true) - .WithSerializable(true) - .WithSpecialName(true) - .WithLayout(ILTypeDefLayout.Auto) - .WithInitSemantics(ILTypeInit.BeforeField) - .WithEncoding(ILDefaultPInvokeEncoding.Auto) - - cgbuf.mgbuf.AddTypeDef(ilContractTypeRef, ilContractTypeDef, false, false, None) - - let ilCtorBody = mkILMethodBody (true, [], 8, nonBranchingInstrsToCode (mkCallBaseConstructor(ilContractTy, [])), None ) - let cloMethods = [ mkILGenericVirtualMethod("DirectInvoke", ILMemberAccess.Assembly, cloinfo.localTypeFuncDirectILGenericParams, [], mkILReturn (cloinfo.cloILFormalRetTy), MethodBody.IL ilCloBody) ] - let cloTypeDefs = GenClosureTypeDefs cenv (ilCloTypeRef, cloinfo.cloILGenericParams, [], cloinfo.cloILFreeVars, cloinfo.ilCloLambdas, ilCtorBody, cloMethods, [], ilContractTy, []) - cloTypeDefs - - else - GenClosureTypeDefs cenv (ilCloTypeRef, cloinfo.cloILGenericParams, [], cloinfo.cloILFreeVars, cloinfo.ilCloLambdas, ilCloBody, [], [], g.ilg.typ_Object, []) - CountClosure() - for cloTypeDef in cloTypeDefs do - cgbuf.mgbuf.AddTypeDef(ilCloTypeRef, cloTypeDef, false, false, None) + DelayGenMethodForLambda cenv cgbuf.mgbuf eenv (entryPointInfo, cloinfo, eenvinner, body, isLocalTypeFunc, m) cloinfo, m | _ -> failwith "GenLambda: not a lambda" diff --git a/src/fsharp/Optimizer.fs b/src/fsharp/Optimizer.fs index f1df754803eba024a33f9192e9f09f316168bd27..1fbc5338f49b821c0fb9c5da697e8a646290ef41 100644 --- a/src/fsharp/Optimizer.fs +++ b/src/fsharp/Optimizer.fs @@ -278,6 +278,8 @@ let [] localOptDefault = true let [] crossModuleOptDefault = true +let [] LambdaInlineThresholdDefault = 6 + type OptimizationSettings = { abstractBigTargets : bool @@ -315,7 +317,7 @@ type OptimizationSettings = bigTargetSize = 100 veryBigExprSize = 3000 crossModuleOptUser = None - lambdaInlineThreshold = 6 + lambdaInlineThreshold = LambdaInlineThresholdDefault reportingPhase = false reportNoNeedToTailcall = false reportFunctionSizes = false @@ -419,6 +421,14 @@ type IncrementalOptimizationEnv = override x.ToString() = "" +let SetAbstractBigTargetsOn cenv = + { cenv with + settings = + { cenv.settings with + abstractBigTargets = true + } + } + //------------------------------------------------------------------------- // IsPartialExprVal - is the expr fully known? //------------------------------------------------------------------------- @@ -2911,6 +2921,15 @@ and OptimizeLambdas (vspec: Val option) cenv env topValInfo e ety = let env = Option.foldBack (BindInternalValToUnknown cenv) baseValOpt env let env = BindTypeVarsToUnknown tps env let env = List.foldBack (BindInternalValsToUnknown cenv) vsl env + + let cenv = + match env.functionVal with + // If the lambda is compiler generated and we are in the reporing phase, allow lambda to be split. + // As an example, allows generated GetHashCode/Equals/CompareTo/etc methods to be split even if optimizations were off. + // This helps prevent stack overflows in IlxGen.fs. + | Some (v, _) when v.IsCompilerGenerated && cenv.settings.reportingPhase -> SetAbstractBigTargetsOn cenv + | _ -> cenv + let env = BindInternalValsToUnknown cenv (Option.toList baseValOpt) env let bodyR, bodyinfo = OptimizeExpr cenv env body let exprR = mkMemberLambdas m tps ctorThisValOpt baseValOpt vsl (bodyR, bodyty) diff --git a/src/fsharp/Optimizer.fsi b/src/fsharp/Optimizer.fsi index e156a26d3ea24c9a7bfdaebcabfec2fb54f5beb1..d489878da1aa671c88465931de1265bf95a4a41d 100644 --- a/src/fsharp/Optimizer.fsi +++ b/src/fsharp/Optimizer.fsi @@ -62,3 +62,7 @@ val UnionOptimizationInfos: seq -> CcuOptimizationInfo val ExprHasEffect: TcGlobals -> Expr -> bool val internal u_CcuOptimizationInfo : TastPickle.ReaderState -> CcuOptimizationInfo + +// REVIEW: We need to put the literal at the end of a file due to a bug that causes a compiler error when a literal is put in the middle other signature constructs. +[] +val LambdaInlineThresholdDefault : int = 6 \ No newline at end of file