Skip to content

Commit c7ec82a

Browse files
authored
[mono][wasm] Allow methods with finally clauses to be AOTed. (#63065)
* [mono][wasm] Allow methods with finally clauses to be AOTed. This is implemented by running the finally clause with the interpreter. Methods with clauses have additional code generated, which: * Saves the IL state (pc+arguments+locals) into a MonoMethodILState structure. * Pushes an LMF frame on the LMF stack of type MONO_LMFEXT_IL_STATE. The LMF frame points to the il state. During EH, if such an LMF frame is found, and the IL pc in the il state points inside a clause, then an interpreted version of the method is created, and the finally clause is ran using the interpreter using the il state as the starting state. * Disable a few test suites which now cause emscripten to OOM when building with AOT.
1 parent 82fd67a commit c7ec82a

File tree

15 files changed

+558
-36
lines changed

15 files changed

+558
-36
lines changed

src/libraries/tests.proj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
<ItemGroup Condition="'$(TargetOS)' == 'Browser' and '$(BuildAOTTestsOnHelix)' == 'true' and '$(RunDisabledWasmTests)' != 'true' and '$(RunAOTCompilation)' == 'true' and '$(BrowserHost)' != 'Windows'">
1818
<!-- Exceeds VM resources in CI on compilation: https://github.com/dotnet/runtime/issues/61339 -->
1919
<ProjectExclusions Include="$(MSBuildThisFileDirectory)Microsoft.Extensions.Logging.Abstractions\tests\Microsoft.Extensions.Logging.Generators.Tests\Microsoft.Extensions.Logging.Generators.Roslyn3.11.Tests.csproj" />
20+
<ProjectExclusions Include="$(MSBuildThisFileDirectory)Microsoft.Extensions.Logging.Abstractions\tests\Microsoft.Extensions.Logging.Generators.Tests\Microsoft.Extensions.Logging.Generators.Roslyn4.0.Tests.csproj" />
21+
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Text.Json\tests\System.Text.Json.SourceGeneration.Tests\System.Text.Json.SourceGeneration.
22+
Roslyn4.0.Tests.csproj" />
23+
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Text.Json\tests\System.Text.Json.SourceGeneration.Unit.Tests\System.Text.Json.SourceGeneration.Roslyn4.0.Unit.Tests.csproj" />
24+
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Text.RegularExpressions\tests\System.Text.RegularExpressions.Generators.Tests\System.Text.RegularExpressions.Generators.Tests.csproj" />
2025

2126
<!-- https://github.com/dotnet/runtime/issues/61756 -->
2227
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Text.RegularExpressions\tests\System.Text.RegularExpressions.Tests.csproj" />

src/mono/mono/metadata/object-offsets.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,18 @@ DECL_OFFSET(SgenThreadInfo, tlab_temp_end)
149149
#ifndef DISABLE_JIT_OFFSETS
150150
DECL_SIZE(MonoMethodRuntimeGenericContext)
151151
DECL_SIZE(MonoLMF)
152+
DECL_SIZE(MonoLMFExt)
152153
DECL_SIZE(MonoTypedRef)
153154
DECL_SIZE(CallContext)
154155
DECL_SIZE(MonoContext)
155156

156157
DECL_OFFSET(MonoLMF, previous_lmf)
158+
DECL_OFFSET(MonoLMFExt, kind)
159+
DECL_OFFSET(MonoLMFExt, il_state)
160+
161+
DECL_OFFSET(MonoMethodILState, method)
162+
DECL_OFFSET(MonoMethodILState, il_offset)
163+
DECL_OFFSET(MonoMethodILState, data)
157164

158165
DECL_OFFSET(MonoMethodRuntimeGenericContext, class_vtable)
159166

src/mono/mono/mini/ee.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#ifndef __MONO_EE_H__
1515
#define __MONO_EE_H__
1616

17-
#define MONO_EE_API_VERSION 0x14
17+
#define MONO_EE_API_VERSION 0x15
1818

1919
typedef struct _MonoInterpStackIter MonoInterpStackIter;
2020

@@ -38,6 +38,7 @@ typedef gpointer MonoInterpFrameHandle;
3838
MONO_EE_CALLBACK (void, get_resume_state, (const MonoJitTlsData *jit_tls, gboolean *has_resume_state, MonoInterpFrameHandle *interp_frame, gpointer *handler_ip)) \
3939
MONO_EE_CALLBACK (gboolean, run_finally, (StackFrameInfo *frame, int clause_index, gpointer handler_ip, gpointer handler_ip_end)) \
4040
MONO_EE_CALLBACK (gboolean, run_filter, (StackFrameInfo *frame, MonoException *ex, int clause_index, gpointer handler_ip, gpointer handler_ip_end)) \
41+
MONO_EE_CALLBACK (gboolean, run_finally_with_il_state, (gpointer il_state, int clause_index, gpointer handler_ip, gpointer handler_ip_end)) \
4142
MONO_EE_CALLBACK (void, frame_iter_init, (MonoInterpStackIter *iter, gpointer interp_exit_data)) \
4243
MONO_EE_CALLBACK (gboolean, frame_iter_next, (MonoInterpStackIter *iter, StackFrameInfo *frame)) \
4344
MONO_EE_CALLBACK (MonoJitInfo*, find_jit_info, (MonoMethod *method)) \
@@ -63,6 +64,7 @@ typedef gpointer MonoInterpFrameHandle;
6364
MONO_EE_CALLBACK (gboolean, sufficient_stack, (gsize size)) \
6465
MONO_EE_CALLBACK (void, entry_llvmonly, (gpointer res, gpointer *args, gpointer imethod)) \
6566
MONO_EE_CALLBACK (gpointer, get_interp_method, (MonoMethod *method, MonoError *error)) \
67+
MONO_EE_CALLBACK (MonoJitInfo*, compile_interp_method, (MonoMethod *method, MonoError *error)) \
6668

6769
typedef struct _MonoEECallbacks {
6870

src/mono/mono/mini/exceptions.cs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3029,6 +3029,76 @@ public static int test_0_unsigned_ct_div () {
30293029

30303030
return 0;
30313031
}
3032+
3033+
struct AStruct {
3034+
public int i1, i2, i3, i4;
3035+
}
3036+
3037+
// Running finally clauses with the interpreter in llvmonly-interp mode
3038+
public static int test_0_finally_deopt () {
3039+
int arg_i = 2;
3040+
var o = new ExceptionTests ();
3041+
try {
3042+
o.finally_deopt (1, 0, ref arg_i, new AStruct () { i1 = 1, i2 = 2, i3 = 3, i4 = 4 });
3043+
} catch (Exception) {
3044+
}
3045+
return finally_deopt_res;
3046+
}
3047+
3048+
static int dummy_static = 5;
3049+
3050+
[MethodImplAttribute (MethodImplOptions.NoInlining)]
3051+
static void throw_inner () {
3052+
// Avoid warnings/errors
3053+
if (dummy_static == 5)
3054+
throw new Exception ();
3055+
// Run with the interpreter
3056+
try {
3057+
throw new Exception ();
3058+
} catch (Exception) {
3059+
}
3060+
}
3061+
3062+
static int finally_deopt_res;
3063+
3064+
void finally_deopt (int arg_i, int unused_arg_i, ref int ref_arg_i, AStruct s) {
3065+
int i = 3;
3066+
3067+
try {
3068+
try {
3069+
i = 5;
3070+
throw_inner ();
3071+
} finally {
3072+
// Check that arguments/locals are copied correctly to the interpreter
3073+
object o = this;
3074+
if (!(o is ExceptionTests))
3075+
finally_deopt_res = 1;
3076+
if (arg_i != 1)
3077+
finally_deopt_res = 2;
3078+
arg_i ++;
3079+
if (i != 5)
3080+
finally_deopt_res = 3;
3081+
i ++;
3082+
if (ref_arg_i != 2)
3083+
finally_deopt_res = 4;
3084+
ref_arg_i ++;
3085+
if (s.i1 != 1 || s.i2 != 2)
3086+
finally_deopt_res = 5;
3087+
s.i1 ++;
3088+
s.i2 ++;
3089+
}
3090+
} finally {
3091+
// Check that arguments/locals were copied back after the first call to the interpreter
3092+
if (arg_i != 2)
3093+
finally_deopt_res = 10;
3094+
if (ref_arg_i != 3)
3095+
finally_deopt_res = 11;
3096+
if (i != 6)
3097+
finally_deopt_res = 12;
3098+
if (s.i1 != 2 || s.i2 != 3)
3099+
finally_deopt_res = 13;
3100+
}
3101+
}
30323102
}
30333103

30343104
#if !__MOBILE__

src/mono/mono/mini/interp-stubs.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ stub_run_filter (StackFrameInfo *frame, MonoException *ex, int clause_index, gpo
114114
return FALSE;
115115
}
116116

117+
static gboolean
118+
stub_run_finally_with_il_state (gpointer il_state, int clause_index, gpointer handler_ip, gpointer handler_ip_end)
119+
{
120+
g_assert_not_reached ();
121+
}
122+
117123
static void
118124
stub_frame_iter_init (MonoInterpStackIter *iter, gpointer interp_exit_data)
119125
{
@@ -239,6 +245,13 @@ stub_get_interp_method (MonoMethod *method, MonoError *error)
239245
return NULL;
240246
}
241247

248+
static MonoJitInfo*
249+
stub_compile_interp_method (MonoMethod *method, MonoError *error)
250+
{
251+
g_assert_not_reached ();
252+
return NULL;
253+
}
254+
242255
#undef MONO_EE_CALLBACK
243256
#define MONO_EE_CALLBACK(ret, name, sig) stub_ ## name,
244257

src/mono/mono/mini/interp/interp.c

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,70 @@ get_virtual_method_fast (InterpMethod *imethod, MonoVTable *vtable, int offset)
756756
}
757757
}
758758

759+
// Returns the size it uses on the interpreter stack
760+
static int
761+
stackval_size (MonoType *type, gboolean pinvoke)
762+
{
763+
type = mini_native_type_replace_type (type);
764+
if (m_type_is_byref (type))
765+
return MINT_STACK_SLOT_SIZE;
766+
switch (type->type) {
767+
case MONO_TYPE_VOID:
768+
return 0;
769+
case MONO_TYPE_I1:
770+
case MONO_TYPE_U1:
771+
case MONO_TYPE_BOOLEAN:
772+
case MONO_TYPE_I2:
773+
case MONO_TYPE_U2:
774+
case MONO_TYPE_CHAR:
775+
case MONO_TYPE_I4:
776+
case MONO_TYPE_U:
777+
case MONO_TYPE_I:
778+
case MONO_TYPE_PTR:
779+
case MONO_TYPE_FNPTR:
780+
case MONO_TYPE_U4:
781+
return MINT_STACK_SLOT_SIZE;
782+
case MONO_TYPE_R4:
783+
return MINT_STACK_SLOT_SIZE;
784+
case MONO_TYPE_I8:
785+
case MONO_TYPE_U8:
786+
return MINT_STACK_SLOT_SIZE;
787+
case MONO_TYPE_R8:
788+
return MINT_STACK_SLOT_SIZE;
789+
case MONO_TYPE_STRING:
790+
case MONO_TYPE_SZARRAY:
791+
case MONO_TYPE_CLASS:
792+
case MONO_TYPE_OBJECT:
793+
case MONO_TYPE_ARRAY:
794+
return MINT_STACK_SLOT_SIZE;
795+
case MONO_TYPE_VALUETYPE:
796+
if (m_class_is_enumtype (type->data.klass)) {
797+
return stackval_size (mono_class_enum_basetype_internal (type->data.klass), pinvoke);
798+
} else {
799+
int size;
800+
if (pinvoke)
801+
size = mono_class_native_size (type->data.klass, NULL);
802+
else
803+
size = mono_class_value_size (type->data.klass, NULL);
804+
return ALIGN_TO (size, MINT_STACK_SLOT_SIZE);
805+
}
806+
case MONO_TYPE_GENERICINST: {
807+
if (mono_type_generic_inst_is_valuetype (type)) {
808+
MonoClass *klass = mono_class_from_mono_type_internal (type);
809+
int size;
810+
if (pinvoke)
811+
size = mono_class_native_size (klass, NULL);
812+
else
813+
size = mono_class_value_size (klass, NULL);
814+
return ALIGN_TO (size, MINT_STACK_SLOT_SIZE);
815+
}
816+
return stackval_size (m_class_get_byval_arg (type->data.generic_class->container_class), pinvoke);
817+
}
818+
default:
819+
g_error ("got type 0x%02x", type->type);
820+
}
821+
}
822+
759823
// Returns the size it uses on the interpreter stack
760824
static int
761825
stackval_from_data (MonoType *type, stackval *result, const void *data, gboolean pinvoke)
@@ -2859,6 +2923,18 @@ interp_get_interp_method (MonoMethod *method, MonoError *error)
28592923
return mono_interp_get_imethod (method, error);
28602924
}
28612925

2926+
static MonoJitInfo*
2927+
interp_compile_interp_method (MonoMethod *method, MonoError *error)
2928+
{
2929+
InterpMethod *imethod = mono_interp_get_imethod (method, error);
2930+
return_val_if_nok (error, NULL);
2931+
2932+
mono_interp_transform_method (imethod, get_context (), error);
2933+
return_val_if_nok (error, NULL);
2934+
2935+
return imethod->jinfo;
2936+
}
2937+
28622938
static InterpMethod*
28632939
lookup_method_pointer (gpointer addr)
28642940
{
@@ -7187,6 +7263,112 @@ interp_run_filter (StackFrameInfo *frame, MonoException *ex, int clause_index, g
71877263
return retval.data.i ? TRUE : FALSE;
71887264
}
71897265

7266+
static gboolean
7267+
interp_run_finally_with_il_state (gpointer il_state_ptr, int clause_index, gpointer handler_ip, gpointer handler_ip_end)
7268+
{
7269+
MonoMethodILState *il_state = (MonoMethodILState*)il_state_ptr;
7270+
MonoMethodSignature *sig;
7271+
ThreadContext *context = get_context ();
7272+
stackval *sp, *sp_args;
7273+
InterpMethod *imethod;
7274+
FrameClauseArgs clause_args;
7275+
ERROR_DECL (error);
7276+
7277+
// FIXME: Optimize this ? Its only used during EH
7278+
7279+
sig = mono_method_signature_internal (il_state->method);
7280+
g_assert (sig);
7281+
7282+
imethod = mono_interp_get_imethod (il_state->method, error);
7283+
mono_error_assert_ok (error);
7284+
7285+
sp_args = sp = (stackval*)context->stack_pointer;
7286+
7287+
int findex = 0;
7288+
if (sig->hasthis) {
7289+
if (il_state->data [findex])
7290+
sp_args->data.p = *(gpointer*)il_state->data [findex];
7291+
sp_args++;
7292+
findex ++;
7293+
}
7294+
7295+
for (int i = 0; i < sig->param_count; ++i) {
7296+
if (il_state->data [findex]) {
7297+
int size = stackval_from_data (sig->params [i], sp_args, il_state->data [findex], FALSE);
7298+
sp_args = STACK_ADD_BYTES (sp_args, size);
7299+
} else {
7300+
int size = stackval_size (sig->params [i], FALSE);
7301+
sp_args = STACK_ADD_BYTES (sp_args, size);
7302+
}
7303+
findex ++;
7304+
}
7305+
7306+
/* Allocate frame */
7307+
InterpFrame frame = {0};
7308+
frame.imethod = imethod;
7309+
frame.stack = sp;
7310+
frame.retval = sp;
7311+
7312+
context->stack_pointer = (guchar*)sp_args;
7313+
context->stack_pointer += imethod->alloca_size;
7314+
7315+
MonoMethodHeader *header = mono_method_get_header_internal (il_state->method, error);
7316+
mono_error_assert_ok (error);
7317+
7318+
/* Init locals */
7319+
if (header->num_locals)
7320+
memset (frame_locals (&frame) + imethod->local_offsets [0], 0, imethod->locals_size);
7321+
/* Copy locals from il_state */
7322+
int locals_start = sig->hasthis + sig->param_count;
7323+
for (int i = 0; i < header->num_locals; ++i) {
7324+
if (il_state->data [locals_start + i])
7325+
stackval_from_data (header->locals [i], (stackval*)(frame_locals (&frame) + imethod->local_offsets [i]), il_state->data [locals_start + i], FALSE);
7326+
}
7327+
7328+
memset (&clause_args, 0, sizeof (FrameClauseArgs));
7329+
clause_args.start_with_ip = (const guint16*)handler_ip;
7330+
clause_args.end_at_ip = (const guint16*)handler_ip_end;
7331+
clause_args.exit_clause = clause_index;
7332+
clause_args.exec_frame = &frame;
7333+
7334+
// this informs MINT_ENDFINALLY to return to EH
7335+
*(guint16**)(frame_locals (&frame) + imethod->clause_data_offsets [clause_index]) = NULL;
7336+
7337+
interp_exec_method (&frame, context, &clause_args);
7338+
7339+
/* Write back args */
7340+
sp_args = sp;
7341+
findex = 0;
7342+
if (sig->hasthis) {
7343+
// FIXME: This
7344+
sp_args++;
7345+
findex ++;
7346+
}
7347+
findex = sig->hasthis ? 1 : 0;
7348+
for (int i = 0; i < sig->param_count; ++i) {
7349+
if (il_state->data [findex]) {
7350+
int size = stackval_to_data (sig->params [i], sp_args, il_state->data [findex], FALSE);
7351+
sp_args = STACK_ADD_BYTES (sp_args, size);
7352+
} else {
7353+
int size = stackval_size (sig->params [i], FALSE);
7354+
sp_args = STACK_ADD_BYTES (sp_args, size);
7355+
}
7356+
findex ++;
7357+
}
7358+
/* Write back locals */
7359+
for (int i = 0; i < header->num_locals; ++i) {
7360+
if (il_state->data [locals_start + i])
7361+
stackval_to_data (header->locals [i], (stackval*)(frame_locals (&frame) + imethod->local_offsets [i]), il_state->data [locals_start + i], FALSE);
7362+
}
7363+
mono_metadata_free_mh (header);
7364+
7365+
// FIXME: Restore stack ?
7366+
if (context->has_resume_state)
7367+
return TRUE;
7368+
else
7369+
return FALSE;
7370+
}
7371+
71907372
typedef struct {
71917373
InterpFrame *current;
71927374
} StackIter;

0 commit comments

Comments
 (0)