@@ -245,6 +245,10 @@ private static void EmitNoArgsMethodCallPopReturn(
245245 private TypeBuilder runnableBuilder ;
246246 private ConsumableTypeInfo consumableInfo ;
247247 private ConsumeEmitter consumeEmitter ;
248+ private ConsumableTypeInfo globalSetupReturnInfo ;
249+ private ConsumableTypeInfo globalCleanupReturnInfo ;
250+ private ConsumableTypeInfo iterationSetupReturnInfo ;
251+ private ConsumableTypeInfo iterationCleanupReturnInfo ;
248252
249253 private FieldBuilder globalSetupActionField ;
250254 private FieldBuilder globalCleanupActionField ;
@@ -357,13 +361,22 @@ private void InitForEmitRunnable(BenchmarkBuildInfo newBenchmark)
357361
358362 consumableInfo = new ConsumableTypeInfo ( benchmark . BenchmarkCase . Descriptor . WorkloadMethod . ReturnType ) ;
359363 consumeEmitter = ConsumeEmitter . GetConsumeEmitter ( consumableInfo ) ;
364+ globalSetupReturnInfo = GetConsumableTypeInfo ( benchmark . BenchmarkCase . Descriptor . GlobalSetupMethod ? . ReturnType ) ;
365+ globalCleanupReturnInfo = GetConsumableTypeInfo ( benchmark . BenchmarkCase . Descriptor . GlobalCleanupMethod ? . ReturnType ) ;
366+ iterationSetupReturnInfo = GetConsumableTypeInfo ( benchmark . BenchmarkCase . Descriptor . IterationSetupMethod ? . ReturnType ) ;
367+ iterationCleanupReturnInfo = GetConsumableTypeInfo ( benchmark . BenchmarkCase . Descriptor . IterationCleanupMethod ? . ReturnType ) ;
360368
361369 // Init types
362370 runnableBuilder = DefineRunnableTypeBuilder ( benchmark , moduleBuilder ) ;
363371 overheadDelegateType = EmitOverheadDelegateType ( ) ;
364372 workloadDelegateType = EmitWorkloadDelegateType ( ) ;
365373 }
366374
375+ private static ConsumableTypeInfo GetConsumableTypeInfo ( Type methodReturnType )
376+ {
377+ return methodReturnType == null ? null : new ConsumableTypeInfo ( methodReturnType ) ;
378+ }
379+
367380 private Type EmitOverheadDelegateType ( )
368381 {
369382 // .class public auto ansi sealed BenchmarkDotNet.Autogenerated.Runnable_0OverheadDelegate
@@ -888,34 +901,84 @@ private void EmitSetupCleanupMethods()
888901 {
889902 // Emit Setup/Cleanup methods
890903 // We emit empty method instead of EmptyAction = "() => { }"
891- globalSetupMethod = EmitWrapperMethod (
892- GlobalSetupMethodName ,
893- Descriptor . GlobalSetupMethod ) ;
894- globalCleanupMethod = EmitWrapperMethod (
895- GlobalCleanupMethodName ,
896- Descriptor . GlobalCleanupMethod ) ;
897- iterationSetupMethod = EmitWrapperMethod (
898- IterationSetupMethodName ,
899- Descriptor . IterationSetupMethod ) ;
900- iterationCleanupMethod = EmitWrapperMethod (
901- IterationCleanupMethodName ,
902- Descriptor . IterationCleanupMethod ) ;
904+ globalSetupMethod = EmitWrapperMethod ( GlobalSetupMethodName , Descriptor . GlobalSetupMethod , globalSetupReturnInfo ) ;
905+ globalCleanupMethod = EmitWrapperMethod ( GlobalCleanupMethodName , Descriptor . GlobalCleanupMethod , globalCleanupReturnInfo ) ;
906+ iterationSetupMethod = EmitWrapperMethod ( IterationSetupMethodName , Descriptor . IterationSetupMethod , iterationSetupReturnInfo ) ;
907+ iterationCleanupMethod = EmitWrapperMethod ( IterationCleanupMethodName , Descriptor . IterationCleanupMethod , iterationCleanupReturnInfo ) ;
903908 }
904909
905- private MethodBuilder EmitWrapperMethod ( string methodName , MethodInfo optionalTargetMethod )
910+ private MethodBuilder EmitWrapperMethod ( string methodName , MethodInfo optionalTargetMethod , ConsumableTypeInfo returnTypeInfo )
906911 {
907912 var methodBuilder = runnableBuilder . DefinePrivateVoidInstanceMethod ( methodName ) ;
908913
909914 var ilBuilder = methodBuilder . GetILGenerator ( ) ;
910915
911916 if ( optionalTargetMethod != null )
912- EmitNoArgsMethodCallPopReturn ( methodBuilder , optionalTargetMethod , ilBuilder , forceDirectCall : true ) ;
917+ {
918+ if ( returnTypeInfo ? . IsAwaitable == true )
919+ {
920+ EmitAwaitableSetupTeardown ( methodBuilder , optionalTargetMethod , ilBuilder , returnTypeInfo ) ;
921+ }
922+ else
923+ {
924+ EmitNoArgsMethodCallPopReturn ( methodBuilder , optionalTargetMethod , ilBuilder , forceDirectCall : true ) ;
925+ }
926+ }
913927
914928 ilBuilder . EmitVoidReturn ( methodBuilder ) ;
915929
916930 return methodBuilder ;
917931 }
918932
933+ private void EmitAwaitableSetupTeardown (
934+ MethodBuilder methodBuilder ,
935+ MethodInfo targetMethod ,
936+ ILGenerator ilBuilder ,
937+ ConsumableTypeInfo returnTypeInfo )
938+ {
939+ if ( targetMethod == null )
940+ throw new ArgumentNullException ( nameof ( targetMethod ) ) ;
941+
942+ if ( returnTypeInfo . WorkloadMethodReturnType == typeof ( void ) )
943+ {
944+ ilBuilder . Emit ( OpCodes . Ldarg_0 ) ;
945+ }
946+ /*
947+ // call for instance
948+ // GlobalSetup();
949+ IL_0006: ldarg.0
950+ IL_0007: call instance void [BenchmarkDotNet]BenchmarkDotNet.Samples.SampleBenchmark::GlobalSetup()
951+ */
952+ /*
953+ // call for static
954+ // GlobalSetup();
955+ IL_0006: call string [BenchmarkDotNet]BenchmarkDotNet.Samples.SampleBenchmark::GlobalCleanup()
956+ */
957+ if ( targetMethod . IsStatic )
958+ {
959+ ilBuilder . Emit ( OpCodes . Call , targetMethod ) ;
960+
961+ }
962+ else if ( methodBuilder . IsStatic )
963+ {
964+ throw new InvalidOperationException (
965+ $ "[BUG] Static method { methodBuilder . Name } tries to call instance member { targetMethod . Name } ") ;
966+ }
967+ else
968+ {
969+ ilBuilder . Emit ( OpCodes . Ldarg_0 ) ;
970+ ilBuilder . Emit ( OpCodes . Call , targetMethod ) ;
971+ }
972+
973+ /*
974+ // BenchmarkDotNet.Helpers.AwaitHelper.GetResult(...);
975+ IL_000e: call !!0 BenchmarkDotNet.Helpers.AwaitHelper::GetResult<int32>(valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1<!!0>)
976+ */
977+
978+ ilBuilder . Emit ( OpCodes . Call , returnTypeInfo . GetResultMethod ) ;
979+ ilBuilder . Emit ( OpCodes . Pop ) ;
980+ }
981+
919982 private void EmitCtorBody ( )
920983 {
921984 var ilBuilder = ctorMethod . GetILGenerator ( ) ;
0 commit comments