diff --git a/src/absil/il.fs b/src/absil/il.fs index e70a0b34c79..8d4bc021325 100644 --- a/src/absil/il.fs +++ b/src/absil/il.fs @@ -1075,6 +1075,7 @@ type ILMethodBody = { IsZeroInit: bool; MaxStack: int32; NoInlining: bool; + AggressiveInlining: bool; Locals: ILLocals; Code: ILCode; SourceMarker: ILSourceMarker option } @@ -1374,6 +1375,7 @@ type ILMethodDef = IsPreserveSig: bool; IsMustRun: bool; IsNoInline: bool; + IsAggressiveInline : bool GenericParams: ILGenericParameterDefs; CustomAttrs: ILAttributes; } member x.ParameterTypes = typesOfILParams x.Parameters @@ -2279,6 +2281,7 @@ let mkILMethodBody (zeroinit,locals,maxstack,code,tag) : ILMethodBody = { IsZeroInit=zeroinit MaxStack=maxstack NoInlining=false + AggressiveInlining=false Locals= locals Code= code SourceMarker=tag } @@ -2314,6 +2317,7 @@ let mkILCtor (access,args,impl) = IsUnmanagedExport=false; IsSynchronized=false; IsNoInline=false; + IsAggressiveInline=false IsMustRun=false; IsPreserveSig=false; CustomAttrs = emptyILCustomAttrs; } @@ -2367,6 +2371,7 @@ let mkILStaticMethod (genparams,nm,access,args,ret,impl) = IsUnmanagedExport=false; IsSynchronized=false; IsNoInline=false; + IsAggressiveInline=false; IsMustRun=false; IsPreserveSig=false; } @@ -2396,6 +2401,7 @@ let mkILClassCtor impl = IsUnmanagedExport=false; IsSynchronized=false; IsNoInline=false; + IsAggressiveInline=false IsMustRun=false; IsPreserveSig=false; } @@ -2436,6 +2442,7 @@ let mkILGenericVirtualMethod (nm,access,genparams,actual_args,actual_ret,impl) = IsUnmanagedExport=false; IsSynchronized=false; IsNoInline=false; + IsAggressiveInline=false IsMustRun=false; IsPreserveSig=false; } @@ -2465,6 +2472,7 @@ let mkILGenericNonVirtualMethod (nm,access,genparams, actual_args,actual_ret, im IsUnmanagedExport=false; IsSynchronized=false; IsNoInline=false; + IsAggressiveInline=false IsMustRun=false; IsPreserveSig=false; } diff --git a/src/absil/il.fsi b/src/absil/il.fsi index d133be55375..11574eed33a 100644 --- a/src/absil/il.fsi +++ b/src/absil/il.fsi @@ -804,9 +804,10 @@ type ILLocals = list [] type ILMethodBody = { IsZeroInit: bool; - /// strictly speakin should be a uint16 + /// strictly speaking should be a uint16 MaxStack: int32; NoInlining: bool; + AggressiveInlining: bool; Locals: ILLocals; Code: ILCode; SourceMarker: ILSourceMarker option } @@ -1054,6 +1055,7 @@ type ILMethodDef = /// .NET 2.0 feature: SafeHandle finalizer must be run. IsMustRun: bool; IsNoInline: bool; + IsAggressiveInline: bool; GenericParams: ILGenericParameterDefs; CustomAttrs: ILAttributes; } diff --git a/src/absil/ilprint.fs b/src/absil/ilprint.fs index 75cc2a0bf97..79c4fe0b914 100644 --- a/src/absil/ilprint.fs +++ b/src/absil/ilprint.fs @@ -846,10 +846,11 @@ let goutput_mdef env os md = output_string os " "; (goutput_params menv) os md.Parameters; output_string os " "; - if md.IsSynchronized then output_string os "synchronized "; - if md.IsMustRun then output_string os "/* mustrun */ "; - if md.IsPreserveSig then output_string os "preservesig "; - if md.IsNoInline then output_string os "noinlining "; + if md.IsSynchronized then output_string os "synchronized " + if md.IsMustRun then output_string os "/* mustrun */ " + if md.IsPreserveSig then output_string os "preservesig " + if md.IsNoInline then output_string os "noinlining " + if md.IsAggressiveInline then output_string os "aggressiveinlining " (goutput_mbody is_entrypoint menv) os md; output_string os "\n" diff --git a/src/absil/ilread.fs b/src/absil/ilread.fs index 7f37e6484de..41e534aedba 100644 --- a/src/absil/ilread.fs +++ b/src/absil/ilread.fs @@ -2301,6 +2301,7 @@ and seekReadMethod ctxt numtypars (idx:int) = let internalcall = (implflags &&& 0x1000) <> 0x0 let synchronized = (implflags &&& 0x0020) <> 0x0 let noinline = (implflags &&& 0x0008) <> 0x0 + let aggressiveinline = (implflags &&& 0x0100) <> 0x0 let mustrun = (implflags &&& 0x0040) <> 0x0 let cctor = (nm = ".cctor") let ctor = (nm = ".ctor") @@ -2338,6 +2339,7 @@ and seekReadMethod ctxt numtypars (idx:int) = IsUnmanagedExport=export IsSynchronized=synchronized IsNoInline=noinline + IsAggressiveInline=aggressiveinline IsMustRun=mustrun IsPreserveSig=preservesig IsManaged = not unmanaged @@ -2358,7 +2360,7 @@ and seekReadMethod ctxt numtypars (idx:int) = //if codeRVA <> 0x0 then dprintn "non-IL or abstract method with non-zero RVA" mkMethBodyLazyAux (notlazy MethodBody.Abstract) else - seekReadMethodRVA ctxt (idx,nm,internalcall,noinline,numtypars) codeRVA + seekReadMethodRVA ctxt (idx,nm,internalcall,noinline,aggressiveinline,numtypars) codeRVA } @@ -2877,9 +2879,9 @@ and seekReadTopCode ctxt numtypars (sz:int) start seqpoints = instrs,rawToLabel, lab2pc, raw2nextLab #if FX_NO_PDB_READER -and seekReadMethodRVA ctxt (_idx,nm,_internalcall,noinline,numtypars) rva = +and seekReadMethodRVA ctxt (_idx,nm,_internalcall,noinline,aggressiveinline,numtypars) rva = #else -and seekReadMethodRVA ctxt (idx,nm,_internalcall,noinline,numtypars) rva = +and seekReadMethodRVA ctxt (idx,nm,_internalcall,noinline,aggressiveinline,numtypars) rva = #endif mkMethBodyLazyAux (lazy @@ -2965,6 +2967,7 @@ and seekReadMethodRVA ctxt (idx,nm,_internalcall,noinline,numtypars) rva = { IsZeroInit=false MaxStack= 8 NoInlining=noinline + AggressiveInlining=aggressiveinline Locals=List.empty SourceMarker=methRangePdbInfo Code=code } @@ -3090,6 +3093,7 @@ and seekReadMethodRVA ctxt (idx,nm,_internalcall,noinline,numtypars) rva = { IsZeroInit=initlocals MaxStack= maxstack NoInlining=noinline + AggressiveInlining=aggressiveinline Locals = locals Code=code SourceMarker=methRangePdbInfo} diff --git a/src/absil/ilreflect.fs b/src/absil/ilreflect.fs index bd4d7b20d46..8a67b5e0843 100644 --- a/src/absil/ilreflect.fs +++ b/src/absil/ilreflect.fs @@ -1466,6 +1466,7 @@ let convMethodImplFlags mdef = ||| flagsIf mdef.IsPreserveSig MethodImplAttributes.PreserveSig ||| flagsIf mdef.IsSynchronized MethodImplAttributes.Synchronized ||| flagsIf (match mdef.mdBody.Contents with MethodBody.IL b -> b.NoInlining | _ -> false) MethodImplAttributes.NoInlining + ||| flagsIf (match mdef.mdBody.Contents with MethodBody.IL b -> b.AggressiveInlining | _ -> false) MethodImplAttributes.AggressiveInlining //---------------------------------------------------------------------------- // buildMethodPass2 diff --git a/src/absil/ilwrite.fs b/src/absil/ilwrite.fs index 3a9e861915c..c75b4a77353 100644 --- a/src/absil/ilwrite.fs +++ b/src/absil/ilwrite.fs @@ -2539,6 +2539,7 @@ let GenMethodDefAsRow cenv env midx (md: ILMethodDef) = | _ -> false) then 0x1000 else 0x0) ||| // RTSpecialName (if md.IsReqSecObj then 0x8000 else 0x0) ||| (if md.HasSecurity || not md.SecurityDecls.AsList.IsEmpty then 0x4000 else 0x0) + let implflags = (match md.mdCodeKind with | MethodCodeKind.Native -> 0x0001 @@ -2550,7 +2551,8 @@ let GenMethodDefAsRow cenv env midx (md: ILMethodDef) = (if md.IsPreserveSig then 0x0080 else 0x0000) ||| (if md.IsSynchronized then 0x0020 else 0x0000) ||| (if md.IsMustRun then 0x0040 else 0x0000) ||| - (if (md.IsNoInline || (match md.mdBody.Contents with MethodBody.IL il -> il.NoInlining | _ -> false)) then 0x0008 else 0x0000) + (if (md.IsNoInline || (match md.mdBody.Contents with MethodBody.IL il -> il.NoInlining | _ -> false)) then 0x0008 else 0x0000) ||| + (if (md.IsAggressiveInline || (match md.mdBody.Contents with MethodBody.IL il -> il.AggressiveInlining | _ -> false)) then 0x0100 else 0x0000) if md.IsEntryPoint then if cenv.entrypoint <> None then failwith "duplicate entrypoint" diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index 688b1a41afa..faac928243d 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -5061,13 +5061,16 @@ and ComputeMethodImplAttribs cenv (_v:Val) attrs = // 0x80 - hasPreserveSigImplFlag // 0x20 - synchronize // (See ECMA 335, Partition II, section 23.1.11 - Flags for methods [MethodImplAttributes]) - let attrs = attrs - |> List.filter (IsMatchingFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute >> not) - |> List.filter (IsMatchingFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute >> not) + let attrs = + attrs + |> List.filter (IsMatchingFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute >> not) + |> List.filter (IsMatchingFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute >> not) + let hasPreserveSigImplFlag = ((implflags &&& 0x80) <> 0x0) || hasPreserveSigAttr let hasSynchronizedImplFlag = (implflags &&& 0x20) <> 0x0 let hasNoInliningImplFlag = (implflags &&& 0x08) <> 0x0 - hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningImplFlag, attrs + let hasAggressiveInliningImplFlag = (implflags &&& 0x0100) <> 0x0 + hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningImplFlag, hasAggressiveInliningImplFlag, attrs and GenMethodForBinding cenv cgbuf eenv @@ -5152,7 +5155,7 @@ and GenMethodForBinding | _ -> [],None // check if the hasPreserveSigNamedArg and hasSynchronizedImplFlag implementation flags have been specified - let hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningFlag, attrs = ComputeMethodImplAttribs cenv v attrs + let hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningFlag, hasAggressiveInliningImplFlag, attrs = ComputeMethodImplAttribs cenv v attrs let securityAttributes,attrs = attrs |> List.partition (fun a -> IsSecurityAttribute cenv.g cenv.amap cenv.casApplied a m) @@ -5183,6 +5186,7 @@ and GenMethodForBinding IsSynchronized = hasSynchronizedImplFlag IsEntryPoint = isExplicitEntryPoint IsNoInline = hasNoInliningFlag + IsAggressiveInline = hasAggressiveInliningImplFlag HasSecurity = mdef.HasSecurity || (securityAttributes.Length > 0) SecurityDecls = secDecls } @@ -6012,7 +6016,7 @@ and GenAbstractBinding cenv eenv tref (vref:ValRef) = let m = vref.Range let memberInfo = Option.get vref.MemberInfo let attribs = vref.Attribs - let hasPreserveSigImplFlag,hasSynchronizedImplFlag,hasNoInliningFlag,attribs = ComputeMethodImplAttribs cenv vref.Deref attribs + let hasPreserveSigImplFlag,hasSynchronizedImplFlag,hasNoInliningFlag,hasAggressiveInliningImplFlag,attribs = ComputeMethodImplAttribs cenv vref.Deref attribs if memberInfo.MemberFlags.IsDispatchSlot && not memberInfo.IsImplemented then let ilAttrs = [ yield! GenAttrs cenv eenv attribs @@ -6033,6 +6037,7 @@ and GenAbstractBinding cenv eenv tref (vref:ValRef) = IsPreserveSig=hasPreserveSigImplFlag IsSynchronized=hasSynchronizedImplFlag IsNoInline=hasNoInliningFlag + IsAggressiveInline=hasAggressiveInliningImplFlag mdKind=match mdef.mdKind with | MethodKind.Virtual vinfo -> MethodKind.Virtual {vinfo with IsFinal=memberInfo.MemberFlags.IsFinal diff --git a/src/fsharp/symbols/Symbols.fs b/src/fsharp/symbols/Symbols.fs index 12d13cc03c6..3e40754b00a 100644 --- a/src/fsharp/symbols/Symbols.fs +++ b/src/fsharp/symbols/Symbols.fs @@ -1159,6 +1159,7 @@ and FSharpInlineAnnotation = | AlwaysInline | OptionalInline | NeverInline + | AggressiveInline and FSharpMemberOrValData = | E of EventInfo diff --git a/src/fsharp/symbols/Symbols.fsi b/src/fsharp/symbols/Symbols.fsi index e5174a7a0a5..516b2554dd1 100644 --- a/src/fsharp/symbols/Symbols.fsi +++ b/src/fsharp/symbols/Symbols.fsi @@ -694,14 +694,16 @@ and [] FSharpInlineAnnotation = #else and [] internal FSharpInlineAnnotation = #endif - /// Indictes the value is inlined and compiled code for the function does not exist + /// Indicates the value is inlined and compiled code for the function does not exist | PseudoValue - /// Indictes the value is inlined but compiled code for the function still exists, e.g. to satisfy interfaces on objects, but that it is also always inlined + /// Indicates the value is inlined but compiled code for the function still exists, e.g. to satisfy interfaces on objects, but that it is also always inlined | AlwaysInline - /// Indictes the value is optionally inlined + /// Indicates the value is optionally inlined | OptionalInline - /// Indictes the value is never inlined - | NeverInline + /// Indicates the value is never inlined + | NeverInline + /// Indicates the value is aggressively inlined by the .NET runtime + | AggressiveInline /// A subtype of F# symbol that represents an F# method, property, event, function or value, including extension members. #if COMPILER_PUBLIC_API diff --git a/tests/fsharpqa/Source/CodeGen/EmittedIL/MethodImplAttribute/MethodImplAttribute.AggressiveInlining.fs b/tests/fsharpqa/Source/CodeGen/EmittedIL/MethodImplAttribute/MethodImplAttribute.AggressiveInlining.fs new file mode 100644 index 00000000000..5fbb3463287 --- /dev/null +++ b/tests/fsharpqa/Source/CodeGen/EmittedIL/MethodImplAttribute/MethodImplAttribute.AggressiveInlining.fs @@ -0,0 +1,5 @@ +// Regression test for DevDiv:212424 +// "NoInlining attribute not emitted into IL" +module M +[] +let getUnit (f : unit -> unit) = f() \ No newline at end of file diff --git a/tests/fsharpqa/Source/CodeGen/EmittedIL/MethodImplAttribute/MethodImplAttribute.AggressiveInlining.il.bsl b/tests/fsharpqa/Source/CodeGen/EmittedIL/MethodImplAttribute/MethodImplAttribute.AggressiveInlining.il.bsl new file mode 100644 index 00000000000..01288e3c58b --- /dev/null +++ b/tests/fsharpqa/Source/CodeGen/EmittedIL/MethodImplAttribute/MethodImplAttribute.AggressiveInlining.il.bsl @@ -0,0 +1,78 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17295 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly extern FSharp.Core +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: + .ver 4:3:0:0 +} +.assembly MethodImplAttribute.AggressiveInlining +{ + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.FSharpInterfaceDataVersionAttribute::.ctor(int32, + int32, + int32) = ( 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 ) + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 01 01 00 00 00 00 ) + + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.mresource public FSharpSignatureData.MethodImplAttribute.AggressiveInlining +{ + // Offset: 0x00000000 Length: 0x0000035A +} +.mresource public FSharpOptimizationData.MethodImplAttribute.AggressiveInlining +{ + // Offset: 0x00000360 Length: 0x0000007D +} +.module MethodImplAttribute.AggressiveInlining.dll +// MVID: {4E66F6A9-F47B-58B3-A745-0383A9F6664E} +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x0000000000180000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public abstract auto ansi sealed M + extends [mscorlib]System.Object +{ + .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 void getUnit(class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2 f) cil managed aggressiveinlining + { + // Code size 10 (0xa) + .maxstack 8 + .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' + .line 5,5 : 34,37 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldnull + IL_0003: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2::Invoke(!0) + IL_0008: pop + IL_0009: ret + } // end of method M::getUnit + +} // end of class M + +.class private abstract auto ansi sealed ''.$M + extends [mscorlib]System.Object +{ +} // end of class ''.$M + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** diff --git a/tests/fsharpqa/Source/CodeGen/EmittedIL/MethodImplAttribute/env.lst b/tests/fsharpqa/Source/CodeGen/EmittedIL/MethodImplAttribute/env.lst index a8ce825221b..b8109477852 100644 --- a/tests/fsharpqa/Source/CodeGen/EmittedIL/MethodImplAttribute/env.lst +++ b/tests/fsharpqa/Source/CodeGen/EmittedIL/MethodImplAttribute/env.lst @@ -2,6 +2,7 @@ SOURCE=MethodImplAttribute.ForwardRef.fs SCFLAGS="-a -g --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd MethodImplAttribute.ForwardRef.dll" # MethodImplAttribute.ForwardRef.fs SOURCE=MethodImplAttribute.InternalCall.fs SCFLAGS="-a -g --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd MethodImplAttribute.InternalCall.dll" # MethodImplAttribute.InternalCall.fs SOURCE=MethodImplAttribute.NoInlining.fs SCFLAGS="-a -g --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd MethodImplAttribute.NoInlining.dll" # MethodImplAttribute.NoInlining.fs + SOURCE=MethodImplAttribute.AggressiveInlining.fs SCFLAGS="-a -g --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd MethodImplAttribute.AggressiveInlining.dll" # MethodImplAttribute.AggressiveInlining.fs SOURCE=MethodImplAttribute.NoOptimization.fs SCFLAGS="-a -g --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd MethodImplAttribute.NoOptimization.dll" # MethodImplAttribute.NoOptimization.fs SOURCE=MethodImplAttribute.PreserveSig.fs SCFLAGS="-a -g --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd MethodImplAttribute.PreserveSig.dll" # MethodImplAttribute.PreserveSig.fs SOURCE=MethodImplAttribute.Synchronized.fs SCFLAGS="-a -g --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd MethodImplAttribute.Synchronized.dll" # MethodImplAttribute.Synchronized.fs