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