From 63eaf15d8403c5bd0b9639654f020d12cc0da9da Mon Sep 17 00:00:00 2001 From: kerams Date: Thu, 18 Mar 2021 16:16:11 +0100 Subject: [PATCH 1/9] Support SkipLocalsInit --- src/fsharp/IlxGen.fs | 13 +++++++++---- src/fsharp/TcGlobals.fs | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index 8578a9bef27..802de080aae 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -894,6 +894,9 @@ and IlxGenEnv = /// Are we inside of a recursive let binding, while loop, or a for loop? isInLoop: bool + + /// Indicates that the .locals init flag should be set on a method and all its nested methods and lambdas + initLocals: bool } override _.ToString() = "" @@ -2032,7 +2035,7 @@ let CodeGenMethod cenv mgbuf (entryPointInfo, methodName, eenv, alreadyUsedArgs, let maxStack = maxStack + 2 // Build an Abstract IL method - instrs, mkILMethodBody (true, locals, maxStack, code, sourceRange) + instrs, mkILMethodBody (eenv.initLocals, locals, maxStack, code, sourceRange) let StartDelayedLocalScope nm cgbuf = let startScope = CG.GenerateDelayMark cgbuf ("start_" + nm) @@ -6149,6 +6152,7 @@ and GenMethodForBinding let eenvForMeth = eenvForMeth |> AddStorageForLocalWitnesses (methLambdaWitnessInfos |> List.mapi (fun i w -> (w, Arg (numArgsUsed+i)))) let numArgsUsed = numArgsUsed + methLambdaWitnessInfos.Length let eenvForMeth = eenvForMeth |> AddStorageForLocalVals cenv.g (List.mapi (fun i v -> (v, Arg (numArgsUsed+i))) nonUnitNonSelfMethodVars) + let eenvForMeth = if eenvForMeth.initLocals && HasFSharpAttribute g g.attrib_SkipLocalsInitAttribute v.Attribs then { eenvForMeth with initLocals = false } else eenvForMeth eenvForMeth let tailCallInfo = @@ -6184,7 +6188,7 @@ and GenMethodForBinding mkThrow m returnTy exnExpr else body - + let ilCodeLazy = lazy CodeGenMethodForExpr cenv mgbuf (SPAlways, tailCallInfo, mspec.Name, eenvForMeth, 0, bodyExpr, sequel) // This is the main code generation for most methods @@ -6924,7 +6928,7 @@ and GenModuleBinding cenv (cgbuf: CodeGenBuffer) (qname: QualifiedNameOfFile) la let eenvinner = if mspec.IsNamespace then eenv else - {eenv with cloc = CompLocForFixedModule cenv.opts.fragName qname.Text mspec } + { eenv with cloc = CompLocForFixedModule cenv.opts.fragName qname.Text mspec; initLocals = HasFSharpAttribute cenv.g cenv.g.attrib_SkipLocalsInitAttribute mspec.Attribs |> not } // Create the class to hold the contents of this module. No class needed if // we're compiling it as a namespace. @@ -8015,7 +8019,8 @@ let GetEmptyIlxGenEnv (g: TcGlobals) ccu = innerVals = [] sigToImplRemapInfo = [] (* "module remap info" *) withinSEH = false - isInLoop = false } + isInLoop = false + initLocals = true } type IlxGenResults = { ilTypeDefs: ILTypeDef list diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index 9446966eff5..350fe35557e 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -1192,6 +1192,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member val attrib_CallerLineNumberAttribute = findSysAttrib "System.Runtime.CompilerServices.CallerLineNumberAttribute" member val attrib_CallerFilePathAttribute = findSysAttrib "System.Runtime.CompilerServices.CallerFilePathAttribute" member val attrib_CallerMemberNameAttribute = findSysAttrib "System.Runtime.CompilerServices.CallerMemberNameAttribute" + member val attrib_SkipLocalsInitAttribute = findSysAttrib "System.Runtime.CompilerServices.SkipLocalsInitAttribute" member val attrib_ProjectionParameterAttribute = mk_MFCore_attrib "ProjectionParameterAttribute" member val attrib_CustomOperationAttribute = mk_MFCore_attrib "CustomOperationAttribute" From f1bae102848a5ac8c25475cf52dd3d79dd266fce Mon Sep 17 00:00:00 2001 From: kerams Date: Thu, 18 Mar 2021 16:53:54 +0100 Subject: [PATCH 2/9] SkipLocalsInit on classes --- src/fsharp/IlxGen.fs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index 802de080aae..3f008ee0009 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -6928,7 +6928,7 @@ and GenModuleBinding cenv (cgbuf: CodeGenBuffer) (qname: QualifiedNameOfFile) la let eenvinner = if mspec.IsNamespace then eenv else - { eenv with cloc = CompLocForFixedModule cenv.opts.fragName qname.Text mspec; initLocals = HasFSharpAttribute cenv.g cenv.g.attrib_SkipLocalsInitAttribute mspec.Attribs |> not } + { eenv with cloc = CompLocForFixedModule cenv.opts.fragName qname.Text mspec; initLocals = eenv.initLocals && not (HasFSharpAttribute cenv.g cenv.g.attrib_SkipLocalsInitAttribute mspec.Attribs) } // Create the class to hold the contents of this module. No class needed if // we're compiling it as a namespace. @@ -7278,7 +7278,10 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) = | TNoRepr -> () | TAsmRepr _ | TILObjectRepr _ | TMeasureableRepr _ -> () | TFSharpObjectRepr _ | TRecdRepr _ | TUnionRepr _ -> - let eenvinner = ReplaceTyenv (TypeReprEnv.ForTycon tycon) eenv + let eenvinner = + let eenvinner = ReplaceTyenv (TypeReprEnv.ForTycon tycon) eenv + let eenvinner = if eenvinner.initLocals && HasFSharpAttribute g g.attrib_SkipLocalsInitAttribute tycon.Attribs then { eenvinner with initLocals = false } else eenvinner + eenvinner let thisTy = generalizedTyconRef tcref let ilThisTy = GenType cenv.amap m eenvinner.tyenv thisTy From 3afe0f46248db7538ae2ac4efa87ab8cdf39abac Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 23 Mar 2021 14:27:29 +0100 Subject: [PATCH 3/9] Add simple SkipLocalsInit test --- .../EmittedIL/SkipLocalsInit.fs | 35 +++++++++++++++++++ .../FSharp.Compiler.ComponentTests.fsproj | 1 + 2 files changed, 36 insertions(+) create mode 100644 tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs new file mode 100644 index 00000000000..4b295318eeb --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.ComponentTests.EmittedIL + +open Xunit +open FSharp.Test.Utilities.Compiler + +module ``SkipLocalsInit`` = + + [] + let ``Init in method not emitted when applied on method``() = + FSharp """ +module SkipLocalsInit + +[] +let x () = + let x = "ssa".Length + x + x + """ + |> compile + |> shouldSucceed + |> verifyIL [""" +.method public static int32 x() cil managed +{ + + .maxstack 4 + .locals (int32 V_0) + IL_0000: ldstr "ssa" + IL_0005: callvirt instance int32 [runtime]System.String::get_Length() + IL_000a: stloc.0 + IL_000b: ldloc.0 + IL_000c: ldloc.0 + IL_000d: add + IL_000e: ret +}"""] diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 32b9781dd62..47fd7222bb3 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -22,6 +22,7 @@ + From 1d1774ffd46d6b21867ef0a9f24ac19df315bccd Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 23 Mar 2021 14:44:21 +0100 Subject: [PATCH 4/9] Test fix --- .../FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs index 4b295318eeb..5565ec7b9b9 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs @@ -22,7 +22,8 @@ let x () = |> verifyIL [""" .method public static int32 x() cil managed { - + .custom instance void [runtime]System.Runtime.CompilerServices.SkipLocalsInitAttribute::.ctor() = ( 01 00 00 00 ) + .maxstack 4 .locals (int32 V_0) IL_0000: ldstr "ssa" From 5d8d0eca53d9ae731f632f4ce20eb5e68a864df0 Mon Sep 17 00:00:00 2001 From: kerams Date: Fri, 2 Apr 2021 13:05:41 +0200 Subject: [PATCH 5/9] Try fix test --- .../EmittedIL/SkipLocalsInit.fs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs index 5565ec7b9b9..35731f5f62a 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs @@ -6,6 +6,8 @@ open Xunit open FSharp.Test.Utilities.Compiler module ``SkipLocalsInit`` = + let private compileForNetCore opts = + opts |> ignoreWarnings |> withOptions ["-g"; "--targetprofile:netcore"] |> compile [] let ``Init in method not emitted when applied on method``() = @@ -17,7 +19,7 @@ let x () = let x = "ssa".Length x + x """ - |> compile + |> compileForNetCore |> shouldSucceed |> verifyIL [""" .method public static int32 x() cil managed From 1843f83cb692efdee20785a4559f2c4ec6c67993 Mon Sep 17 00:00:00 2001 From: kerams Date: Sat, 3 Apr 2021 11:29:34 +0200 Subject: [PATCH 6/9] Add more skip locals init tests --- .../EmittedIL/SkipLocalsInit.fs | 85 ++++++++++++++++--- 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs index 35731f5f62a..6a13494515e 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs @@ -6,33 +6,92 @@ open Xunit open FSharp.Test.Utilities.Compiler module ``SkipLocalsInit`` = - let private compileForNetCore opts = - opts |> ignoreWarnings |> withOptions ["-g"; "--targetprofile:netcore"] |> compile + [] + let ``Init in function and closure not emitted when applied on function``() = + FSharp """ +module SkipLocalsInit + +[] +let x () = + [||] |> Array.filter (fun x -> let y = "".Length in y + y = x) |> ignore +""" + |> compile + |> shouldSucceed + |> verifyIL [""" +.method public static int32 x() cil managed +{ + .custom instance void [runtime]System.Runtime.CompilerServices.SkipLocalsInitAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 4 + .locals (int32[] V_0) +""" + + """ +.method public strict virtual instance bool + Invoke(int32 x) cil managed +{ + + .maxstack 6 + .locals (int32 V_0)"""] [] - let ``Init in method not emitted when applied on method``() = + let ``Init in static method not emitted when applied on class``() = FSharp """ module SkipLocalsInit [] +type X () = + static member Y () = + let x = "ssa".Length + x + x + """ + |> compile + |> shouldSucceed + |> verifyIL [""" +.custom instance void [runtime]System.Runtime.CompilerServices.SkipLocalsInitAttribute::.ctor() = ( 01 00 00 00 ) +.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 ) +""" + + """ +.method public static int32 Y() cil managed +{ + + .maxstack 4 + .locals (int32 V_0)"""] + + [] + let ``Init in static method and function not emitted when applied on module``() = + FSharp """ +[] +module SkipLocalsInit + let x () = let x = "ssa".Length x + x + +type X () = + static member Y () = + let x = "ssa".Length + x + x """ - |> compileForNetCore + |> compile |> shouldSucceed |> verifyIL [""" +.custom instance void [runtime]System.Runtime.CompilerServices.SkipLocalsInitAttribute::.ctor() = ( 01 00 00 00 ) +.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) +""" + + """ .method public static int32 x() cil managed { - .custom instance void [runtime]System.Runtime.CompilerServices.SkipLocalsInitAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 4 .locals (int32 V_0) - IL_0000: ldstr "ssa" - IL_0005: callvirt instance int32 [runtime]System.String::get_Length() - IL_000a: stloc.0 - IL_000b: ldloc.0 - IL_000c: ldloc.0 - IL_000d: add - IL_000e: ret -}"""] +""" + + """ +.method public static int32 Y() cil managed +{ + + .maxstack 4 + .locals (int32 V_0)"""] From 0c646f30755ab22199dfb6ced17b4270cca33419 Mon Sep 17 00:00:00 2001 From: kerams Date: Sat, 3 Apr 2021 12:13:05 +0200 Subject: [PATCH 7/9] SkipLocalsInit on closures --- src/fsharp/ilx/EraseClosures.fs | 2 +- .../FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fsharp/ilx/EraseClosures.fs b/src/fsharp/ilx/EraseClosures.fs index 6d6165de949..9b2aa8f4cdd 100644 --- a/src/fsharp/ilx/EraseClosures.fs +++ b/src/fsharp/ilx/EraseClosures.fs @@ -311,7 +311,7 @@ let convILMethodBody (thisClo, boxReturnTy) (il: ILMethodBody) = match boxReturnTy with | None -> code | Some ty -> morphILInstrsInILCode (convReturnInstr ty) code - {il with MaxStack=newMax; IsZeroInit=true; Code= code } + { il with MaxStack = newMax; Code = code } let convMethodBody thisClo = function | MethodBody.IL il -> diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs index 6a13494515e..086cea4d3b0 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs @@ -18,7 +18,7 @@ let x () = |> compile |> shouldSucceed |> verifyIL [""" -.method public static int32 x() cil managed +.method public static void x() cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.SkipLocalsInitAttribute::.ctor() = ( 01 00 00 00 ) From bd82a2f7eab259d7daeac5ee448ea19b0cfa1fda Mon Sep 17 00:00:00 2001 From: kerams Date: Sat, 3 Apr 2021 13:02:18 +0200 Subject: [PATCH 8/9] SkipLocalsInit on methods --- src/fsharp/IlxGen.fs | 8 ++--- .../EmittedIL/SkipLocalsInit.fs | 34 +++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index 3f008ee0009..d29088d1163 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -5580,7 +5580,8 @@ and GenBindingAfterDebugPoint cenv cgbuf eenv sp (TBind(vspec, rhsExpr, _)) star | Some _, Some e -> cgbuf.mgbuf.AddReflectedDefinition(vspec, e) | _ -> () - let eenv = {eenv with letBoundVars= (mkLocalValRef vspec) :: eenv.letBoundVars} + let eenv = { eenv with letBoundVars = (mkLocalValRef vspec) :: eenv.letBoundVars; + initLocals = eenv.initLocals && (match vspec.ApparentEnclosingEntity with Parent ref -> not (HasFSharpAttribute g g.attrib_SkipLocalsInitAttribute ref.Attribs) | _ -> true) } let access = ComputeMethodAccessRestrictedBySig eenv vspec @@ -7278,10 +7279,7 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) = | TNoRepr -> () | TAsmRepr _ | TILObjectRepr _ | TMeasureableRepr _ -> () | TFSharpObjectRepr _ | TRecdRepr _ | TUnionRepr _ -> - let eenvinner = - let eenvinner = ReplaceTyenv (TypeReprEnv.ForTycon tycon) eenv - let eenvinner = if eenvinner.initLocals && HasFSharpAttribute g g.attrib_SkipLocalsInitAttribute tycon.Attribs then { eenvinner with initLocals = false } else eenvinner - eenvinner + let eenvinner = ReplaceTyenv (TypeReprEnv.ForTycon tycon) eenv let thisTy = generalizedTyconRef tcref let ilThisTy = GenType cenv.amap m eenvinner.tyenv thisTy diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs index 086cea4d3b0..6ea6cfdc6cb 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs @@ -95,3 +95,37 @@ type X () = .maxstack 4 .locals (int32 V_0)"""] + + [] + let ``Init in method and closure not emitted when applied on method``() = + FSharp """ +module SkipLocalsInit + +type X () = + [] + member _.Y () = + [||] |> Array.filter (fun x -> let y = "".Length in y + y = x) |> ignore + let x = "ssa".Length + x + x + """ + |> compile + |> shouldSucceed + |> verifyIL [""" +.method public hidebysig instance int32 + Y() cil managed +{ + .custom instance void [runtime]System.Runtime.CompilerServices.SkipLocalsInitAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 4 + .locals (int32[] V_0, + int32 V_1) +""" + + """ +.method public strict virtual instance bool + Invoke(int32 x) cil managed +{ + + .maxstack 6 + .locals (int32 V_0) +"""] \ No newline at end of file From 9479c37102e480b1122559bda247c3d0a46a552f Mon Sep 17 00:00:00 2001 From: kerams Date: Thu, 6 May 2021 18:17:47 +0200 Subject: [PATCH 9/9] Run SkipLocalsInit test on .NET Core only --- .../EmittedIL/SkipLocalsInit.fs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs index 6ea6cfdc6cb..cd484f2cfe7 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/SkipLocalsInit.fs @@ -5,6 +5,7 @@ namespace FSharp.Compiler.ComponentTests.EmittedIL open Xunit open FSharp.Test.Utilities.Compiler +#if NETCOREAPP module ``SkipLocalsInit`` = [] let ``Init in function and closure not emitted when applied on function``() = @@ -128,4 +129,5 @@ type X () = .maxstack 6 .locals (int32 V_0) -"""] \ No newline at end of file +"""] +#endif \ No newline at end of file