diff --git a/src/Compiler/AbstractIL/il.fs b/src/Compiler/AbstractIL/il.fs index 5d7848f246..fe67991f80 100644 --- a/src/Compiler/AbstractIL/il.fs +++ b/src/Compiler/AbstractIL/il.fs @@ -2113,6 +2113,11 @@ type ILMethodDef x.ImplAttributes &&& MethodImplAttributes.AggressiveInlining <> enum 0 member x.IsMustRun = x.ImplAttributes &&& MethodImplAttributes.NoOptimization <> enum 0 + + // Async is defined as 0x2000 or 8192 + // https://github.com/dotnet/runtime/blob/main/docs/design/specs/runtime-async.md + member x.IsAsync = + x.ImplAttributes &&& enum (0x2000) <> enum 0 member x.WithSpecialName = x.With(attributes = (x.Attributes ||| MethodAttributes.SpecialName)) @@ -2170,6 +2175,9 @@ type ILMethodDef |> conditionalAdd condition MethodImplAttributes.AggressiveInlining) ) + member x.WithAsync(condition) = + x.With(implAttributes = (x.ImplAttributes |> conditionalAdd condition (enum 0x2000))) + member x.WithRuntime(condition) = x.With(implAttributes = (x.ImplAttributes |> conditionalAdd condition MethodImplAttributes.Runtime)) diff --git a/src/Compiler/AbstractIL/il.fsi b/src/Compiler/AbstractIL/il.fsi index 3d6f88bb6c..4a927a61e9 100644 --- a/src/Compiler/AbstractIL/il.fsi +++ b/src/Compiler/AbstractIL/il.fsi @@ -1157,6 +1157,9 @@ type ILMethodDef = /// SafeHandle finalizer must be run. member IsMustRun: bool + /// https://github.com/dotnet/runtime/blob/main/docs/design/specs/runtime-async.md + member IsAsync: bool + /// Functional update of the value member internal With: ?name: string * @@ -1200,6 +1203,8 @@ type ILMethodDef = member internal WithAggressiveInlining: bool -> ILMethodDef + member internal WithAsync: bool -> ILMethodDef + member internal WithRuntime: bool -> ILMethodDef /// Tables of methods. Logically equivalent to a list of methods but diff --git a/src/Compiler/AbstractIL/ilprint.fs b/src/Compiler/AbstractIL/ilprint.fs index 12e421f682..abb3bd7a6f 100644 --- a/src/Compiler/AbstractIL/ilprint.fs +++ b/src/Compiler/AbstractIL/ilprint.fs @@ -610,6 +610,9 @@ let goutput_mbody is_entrypoint env os (md: ILMethodDef) = output_string os "native " elif md.ImplAttributes &&& MethodImplAttributes.IL <> enum 0 then output_string os "cil " + if md.IsAsync then + output_string os "async " + else output_string os "runtime " @@ -728,6 +731,7 @@ let goutput_mdef env os (md: ILMethodDef) = if md.IsAggressiveInline then output_string os "aggressiveinlining " + (goutput_mbody is_entrypoint menv) os md output_string os "\n" diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 553e6ac9aa..49028db0c2 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -9148,7 +9148,8 @@ and ComputeMethodImplAttribs cenv (_v: Val) attrs = let hasSynchronizedImplFlag = (implflags &&& 0x20) <> 0x0 let hasNoInliningImplFlag = (implflags &&& 0x08) <> 0x0 let hasAggressiveInliningImplFlag = (implflags &&& 0x0100) <> 0x0 - hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningImplFlag, hasAggressiveInliningImplFlag, attrs + let hasAsyncImplFlag = (implflags &&& 0x2000) <> 0x0 + hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningImplFlag, hasAggressiveInliningImplFlag, hasAsyncImplFlag, attrs and GenMethodForBinding cenv @@ -9332,7 +9333,7 @@ and GenMethodForBinding | _ -> [], None // check if the hasPreserveSigNamedArg and hasSynchronizedImplFlag implementation flags have been specified - let hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningFlag, hasAggressiveInliningImplFlag, attrs = + let hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningFlag, hasAggressiveInliningImplFlag, hasAsyncImplFlag, attrs = ComputeMethodImplAttribs cenv v attrs let securityAttributes, attrs = @@ -9607,6 +9608,7 @@ and GenMethodForBinding .WithSynchronized(hasSynchronizedImplFlag) .WithNoInlining(hasNoInliningFlag) .WithAggressiveInlining(hasAggressiveInliningImplFlag) + .WithAsync(hasAsyncImplFlag) .With(isEntryPoint = isExplicitEntryPoint, securityDecls = secDecls) let mdef = @@ -10673,7 +10675,7 @@ and GenAbstractBinding cenv eenv tref (vref: ValRef) = let memberInfo = Option.get vref.MemberInfo let attribs = vref.Attribs - let hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningFlag, hasAggressiveInliningImplFlag, attribs = + let hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningFlag, hasAggressiveInliningImplFlag, hasAsyncImplFlag, attribs = ComputeMethodImplAttribs cenv vref.Deref attribs if memberInfo.MemberFlags.IsDispatchSlot && not memberInfo.IsImplemented then @@ -10727,6 +10729,7 @@ and GenAbstractBinding cenv eenv tref (vref: ValRef) = .WithSynchronized(hasSynchronizedImplFlag) .WithNoInlining(hasNoInliningFlag) .WithAggressiveInlining(hasAggressiveInliningImplFlag) + .WithAsync(hasAsyncImplFlag) match memberInfo.MemberFlags.MemberKind with | SynMemberKind.ClassConstructor diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.Async.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.Async.fs new file mode 100644 index 0000000000..8d130eab93 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.Async.fs @@ -0,0 +1,7 @@ +// Regression test for DevDiv:212424 +// "NoInlining attribute not emitted into IL" +module M +open System.Threading.Tasks +open System.Runtime.CompilerServices +[] +let getUnit (f : unit -> Task) = AsyncHelpers.Await(f()) \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.Async.fs.il.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.Async.fs.il.bsl new file mode 100644 index 0000000000..54f1c3e5d6 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.Async.fs.il.bsl @@ -0,0 +1,67 @@ + + + + + +.assembly extern runtime { } +.assembly extern FSharp.Core { } +.assembly assembly +{ + .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 ) + + + + + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module assembly.exe + +.imagebase {value} +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 +.corflags 0x00000001 + + + + + +.class public abstract auto ansi sealed M + extends [runtime]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 async + { + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>::Invoke(!0) + IL_0007: call !!0 [runtime]System.Runtime.CompilerServices.AsyncHelpers::Await(class [runtime]System.Threading.Tasks.Task`1) + IL_000c: pop + IL_000d: ret + } + +} + +.class private abstract auto ansi sealed ''.$M + extends [runtime]System.Object +{ + .method public static void main@() cil managed + { + .entrypoint + + .maxstack 8 + IL_0000: ret + } + +} + + + + + + diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.fs index adbf38b92d..29df42c280 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.fs @@ -60,6 +60,15 @@ module MethodImplAttribute = compilation |> getCompilation |> verifyCompilation + + + // SOURCE=MethodImplAttribute.Async.fs SCFLAGS="-a -g --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd MethodImplAttribute.Async.dll" # MethodImplAttribute.Async.fs + [] + let ``Async_fs`` compilation = + compilation + |> getCompilation + |> verifyCompilation + // SOURCE=MethodImplAttribute.NoOptimization.fs SCFLAGS="-a -g --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd MethodImplAttribute.NoOptimization.dll" # MethodImplAttribute.NoOptimization.fs [] diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl index e2aaca46ac..076b1de364 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl @@ -754,6 +754,7 @@ FSharp.Compiler.AbstractIL.IL+ILMemberAccess: System.String ToString() FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean HasSecurity FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsAbstract FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsAggressiveInline +FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsAsync FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsCheckAccessOnOverride FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsClassInitializer FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsConstructor @@ -779,6 +780,7 @@ FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsZeroInit FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_HasSecurity() FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_IsAbstract() FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_IsAggressiveInline() +FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_IsAsync() FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_IsCheckAccessOnOverride() FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_IsClassInitializer() FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_IsConstructor()