From 833e518ce092669aa37070992b5604d8ec49eee2 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 20 Nov 2024 23:18:32 +0100 Subject: [PATCH 01/11] Add documentation of the functions in transform.c This change adds document describing how the functions in the transform.c will be converted to use the Jit2EEInterface methods instead of Mono APIs. --- docs/design/interpreter/execution.md | 576 ++++++++++++++++++++++ docs/design/interpreter/transformation.md | 391 +++++++++++++++ 2 files changed, 967 insertions(+) create mode 100644 docs/design/interpreter/execution.md create mode 100644 docs/design/interpreter/transformation.md diff --git a/docs/design/interpreter/execution.md b/docs/design/interpreter/execution.md new file mode 100644 index 000000000000..7a2798c1df6a --- /dev/null +++ b/docs/design/interpreter/execution.md @@ -0,0 +1,576 @@ +# Execution phase + +This document explicitly doesn't list stuff like locks, hashtable, memory allocations, mono_compiler_barrier + +## Execution interface candidate methods: +1. GCWriteBarrier(destAddress, object*) +2. GCCopyValues(destAddress, sourceAddress, count, classHandle) +3. GCSetField(destObject*, field*, srcObject*) +4. ThrowException(exceptionObject*) +5. CreateSimpleArray(classHandle, length) - Create a single dimensional zero based array with element type specified by the classHandle and the given length +6. CreateArray(classHandle, numArgs, args) - Create an array that's not single dimensional zero based one. The args represent the array bounds. +7. GetArrayElementAddress(object*, index) - Get array element address. Even for multi-dim arrays, the index is a linear index in the array data +8. DisablePreemptiveGC() +9. EnablePreemptiveGC() +10. IsInst(object*, classHandle) +11. CastClass(object*, classHandle) +12. ClearWithReferences(address, length) +13. Box(data*, classHandle) +14. UnBox(object*) +15. SuspendEE() +16. RestartEE() +17. GetExceptionById(id) - id: null_reference, divide_by_zero, overflow, invalid_cast, index_out_of_range, array_type_mismatch, arithmetic, argument_out_of_range. Maybe use the CoreCLR RuntimeExceptionKind as the id. +18. GetVirtualMethod(methodHandle, classHandle) +19. NewObject(classHandle) +20. GetThreadStaticFieldAddress(offset) - Offset is relative to the managed TLS storage +21. GetHashCode(tryGet, object*) +22. GCPoll() + +## Debugger interface methods + * BreakPoint - breakpoint set by the debugger triggered in the interpreted code + * Break - System.Diagnostics.Debugger.Break() invoked by the interpreted code + * ??? Others + +## Profiler interface methods + * TraceEnterMethod(methodHandle, ???) + * TraceLeaveMethod(methodHandle, ???) + +## JIT2EEInterface methods + * getMethodInfo + * getArgNext + * getArgType + * isEnum - may not be needed if the CORINFO_METHOD_INFO::args are already enum free (I think I've seen a comment that it is that case somewhere) + * getClassSize + * getMethodAttribs - only used at one place to check if a class is abstract when initializing a delegate + * getClassNameFromMetadata - diagnostic purposes in debug builds only + * getMethodNameFromMetadata - diagnostic purposes in debug builds only + * getChildType + * getParentType + * isValueClass + * asCorInfoType + * getArrayRank + * getClassAttribs + + +## MonoClass, MonoMethod and MonoType member access helpers +The following functions are helpers used to access fields in MonoClass, MonoMethod and MonoType. They are used all over the place, so they are described here instead of at the specific places they are used. + * m_class_get_byval_arg - N.A. on CoreCLR + * m_class_get_element_class -> JIT2EEInterface::getChildType + * m_class_get_image -> JIT2EEInterface::getMethodInfo, CORINFO_METHOD_INFO::scope + * m_class_get_name -> JIT2EEInterface::getClassNameFromMetadata + * m_class_get_name_space -> JIT2EEInterface::getClassNameFromMetadata + * m_class_get_parent -> JIT2EEInterface::getParentType + * m_class_get_rank -> JIT2EEInterface::getArrayRank + * m_class_is_byreflike -> JIT2EEInterface::getClassAttribs() & CORINFO_FLG_BYREF_LIKE + * m_class_is_enumtype -> JIT2EEInterface::isEnum + * m_class_is_valuetype -> JIT2EEInterface::isValueClass + * m_method_is_static -> JIT2EEInterface::getMethodAttribs() & CORINFO_FLG_STATIC + * m_method_is_virtual -> JIT2EEInterface::getMethodAttribs() & CORINFO_FLG_VIRTUAL + * m_type_is_byref -> JIT2EEInterface::asCorInfoType() == CORINFO_TYPE_BYREF + +## Functions in the interp.c + +### db_match_method + * Used just for debug build tracing + * mono_method_desc_full_match - match a method by name + +### debug_enter, DEBUG_LEAVE() + * Used just for debug build tracing + * mono_method_full_name -> JIT2EEInterface::getMethodNameFromMetadata + * mono_thread_internal_current - used for logging purposes only + +### clear_resume_state + * Resume state is a state where the interpreter will continue to execute from after execution returns to the interpreter + * mono_gchandle_free_internal - context holds handle to the managed Exception. Not needed for CoreCLR, the EH managed the exception object lifetime. + +### set_context + * ThreadContext (interpreter specific thread context) storage initialization. + * mono_native_tls_set_value + * mono_tls_get_jit_tls + * MonoJitTlsData::interp_context stores the same ThreadContext* as the context thread local + +### get_context + * Get interpreter specific thread context data + * mono_native_tls_get_value + * pthread_getspecific on Unix + * Direct TEB access on Windows + * Probably switch it to a regular thread local variables for the two usages that are ThreadContext* and MonoJitTlsData* + +### interp_free_context + * Free interpreter specific thread context data, occurs at shutdown. + * mono_native_tls_get_value + +### mono_interp_error_cleanup + * MonoError holds various details on the last error, including some dynamically allocated stuff. This function deallocates all of that. + * Some Mono APIs that interpreter calls set the MonoError to carry details on the error that occured + * N.A. on CoreCLR + * mono_error_cleanup + +### mono_interp_get_imethod + * Get an existing or create a new InterpMethod for a MonoMethod + * Creating a new one: + * mono_method_signature_internal - to get various details like param_count, has_this etc. to store them in the InterpMethod -> JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args, CORINFO_METHOD_INFO::args.hasThis() etc. + * m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class && !strcmp(method->name, "Invoke") - set the InterpMethod::is_invoke + * There is a special handling of String..ctor method to override its return value to be String for some reason + * mono_profiler_get_call_instrumentation_flags - call all registered profilers to get the flags of events they are interested in for the specific method and store those on the InterpMethod instance + * MONO_PROFILER_CALL_INSTRUMENTATION_ENTER, MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE, MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL, MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE + * CoreCLR profiler doesn't have such a filtering capability + +### interp_push_lmf + * LMF means Last Managed Frame. There is a per thread linked list of those. Interpreter pushes and pops it around some calls to Mono runtime. + * Pushed for + * exception throwing + * mono_error_convert_to_exception + * mono_get_exception_{x} + * mono_threads_safepoint + * pinvoke calls + * icalls + * JITerpreter jitting call + * ??? JIT calls + * Debugger trampolines calls + * mono_interp_transform_method + * mono_runtime_class_init_full + * mono_push_lmf + +### interp_pop_lmf + * mono_pop_lmf + +### get_virtual_method + * **Use ExecutionAPI::GetVirtualMethod** + * For a virtual method call, get the actual method to be called + +### stackval_from_data + * Sets stackval::data::{x} to data bytes from the data argument based on the MonoType passed in. The MonoType argument determines the size of data to copy. + * The converted form would use CorInfoType as argument. Major part of the usages of stackval_from_data pass in return value type / argument type from a signature. The CORINFO_SIG_INFO contains the CorInfoType directly for return type and JIT2EEInterface::getArgType also returns CorInfoType. + * It seems that CoreCLR won't need to handle case that's MONO_TYPE_GENERICINST and generic instantiations don't require special handling here. + +### stackval_to_data + * Reverse of stackval_from_data. An addiitonal thing is that it calls GC write barriers for reference types and value types + * mono_gc_wbarrier_generic_store_internal -> GCWriteBarrier + * mono_value_copy_internal -> GCCopyValues + * It seems that CoreCLR won't need to handle case that's MONO_TYPE_GENERICINST and generic instantiations don't require special handling here. + +### handle_exception_cb + * Unused dead code + * mono_handle_exception + +### interp_throw + * **Use ExecutionAPI::ThrowException** + * Call exception handling code in the runtime + * It expects that when execution should resume after catch, the exception handling returns here so that the interpreter can restore the state to the resume location in case it was in the interpreted code. The interpreter would then pop interpreter frames if necessary and then set the interpreter state ip / sp appropriately. The interpreter loop doesn't recurse for calls to interpreted methods and it allocates new interpreter frame instances using alloca. When returning and then calling again, the interpreter frames are reused. So when resuming at certain interpreted frame, the interpreter frames of the "unwound" frames need to be pushed to the stack of interpreter frames for reuse. + +### interp_error_convert_to_exception + * Converts MonoError received from a Mono API to managed exception. This is N.A. for CoreCLR + * There are also several cases when the interpreter creates the MonoError on its own using the following functions. Those will be changed to get the exception directly using **ExecutionAPI::GetExceptionById** + * mono_error_set_out_of_memory + * mono_error_set_argument + * mono_error_set_platform_not_supported + * mono_error_convert_to_exception -> N.A. on CoreCLR + +### EXCEPTION_CHECKPOINT + * Checks for thread abort and throws the ThreadAbortException if it was requested + * mono_threads_is_critical_method - N.A. on CoreCLR + * mono_thread_interruption_checkpoint - check for thread abort and return the exception + +### do_safepoint + * **Use ExecutionAPI::GCPoll** + +### ves_array_create + * mono_array_new_jagged_checked -> **ExecutionAPI::CreateArray** + * mono_array_new_full_checked -> **ExecutionAPI::CreateArray** + +### ves_array_element_address + * **Use ExecutionAPI::GetArrayElementAddress** + +### compute_arg_offset + * Computes offset of a specific argument in a method signature + * mono_mint_type - N.A. on CoreCLR, the JIT2EEInterface::getArgType used for iterating over args returns CorInfoType which is an equivalent + +### initialize_arg_offsets + * Ensures that the arg_offsets member in the InterpMethod is filled in with offsets of arguments in a method signature + * mono_method_signature_internal -> JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args + * mono_mint_type - N.A. on CoreCLR, the JIT2EEInterface::getArgType used for iterating over args returns CorInfoType which is an equivalent + +### get_build_args_from_sig_info + * Build signature describing structure for PInvoke usage. + * mono_class_enum_basetype_internal - JIT2EEInterface::isEnum() returns this too + +### ves_pinvoke_method + * Call a PInvoke + * mono_wasm_get_interp_to_native_trampoline + * mono_jit_compile_method_jit_only + * mini_get_interp_lmf_wrapper + * mono_interp_to_native_trampoline + * mono_arch_get_interp_native_call_info + * mono_arch_set_native_call_context_args + * For SWIFT + * mono_method_signature_has_ext_callconv + * mono_arch_get_swift_error + * mono_arch_get_native_call_context_ret + +### interp_init_delegate + * Initialize del->interp_method + * mono_class_is_abstract -> JIT2EEInterface::getMethodAttribs() & CORINFO_FLG_ABSTRACT + * m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class && !strcmp (name, "Invoke") + * mono_marshal_get_delegate_invoke + * mono_create_delegate_trampoline_info + +### interp_delegate_ctor + * Construct a delegate pointing to an InterpMethod + * **Use JIT2EEInterface::GetDelegateCtor** + * Since this is only called from the transform.c, move it to that file + +### dump_stackval + * Debug build diagnostics + * mono_type_get_desc -> JIT2EEInterface::getClassNameFromMetadata() + +### dump_retval + * Debug build diagnostics + * mono_method_signature_internal -> JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args::retType + +### dump_args + * Debug build diagnostics + * mono_method_signature_internal -> JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args + +### interp_runtime_invoke + * ??? What is it used for? Seems like reflection, but I'm not sure + * mono_method_signature_internal - only used to get "hasThis" -> JIT2EEInterface::getMethodInfo, CORINFO_METHOD_INFO::args, CORINFO_SIG_INFO::hasThis() + * mono_marshal_get_native_wrapper + * mono_marshal_get_runtime_invoke_full + * mono_llvm_start_native_unwind + +### interp_entry + * Main function for entering the interpreter from compiled code + * mono_object_unbox_internal - "this" may be passed in as boxed for value types. + * mono_threads_attach_coop - for cases when the interpreted method is invoked by native code -> **ExecutionAPI::DisablePreemptiveGC** + * mono_domain_get - N.A., obsolete + * mono_marshal_get_delegate_invoke - when the method to interpret is Invoke method on a class derived from MultiCastDelegate. + * mono_method_signature_internal -> JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args + * mono_threads_detach_coop -> **ExecutionAPI::EnablePreemptiveGC** + * mono_llvm_start_native_unwind - throws C++ exception + +### do_icall + * Invoke internal call + * mono_marshal_clear_last_error + * mono_marshal_set_last_error + +### init_jit_call_info + * mono_method_signature_internal -> JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args + * mono_jit_compile_method_jit_only - Compile METHOD using the JIT/AOT(???), even in interpreted mode. + * mono_aot_get_method_flags - checks for generic shared value type + * mono_mint_type -> the CORINFO_METHOD_INFO::args::retType is already what we need here + * mono_class_from_mono_type_internal - no op on CoreCLR + * mono_class_value_size - extracts return value size -> JIT2EEInterface::getClassSize(CORINFO_METHOD_INFO::args::retTypeClass) + +### do_jit_call + * mono_llvm_catch_exception + * mono_get_jit_tls + * mono_error_set_exception_instance + +### init_arglist + * mono_type_stack_size + +### interp_entry_from_trampoline + * High level overview: Iterate over all arguments of the method to execute and copy them from the trampoline to the appropriate slots on the interpreter stack. Then interpret the method using mono_interp_exec_method. After the interpreted method exits, copy its result back to the trampoline and return to the caller. + * mono_threads_attach_coop -> **ExecutionAPI::DisablePreemptiveGC** + * mono_domain_get - N.A., obsolete + * mono_method_signature_internal -> JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args + * mono_metadata_signature_size + * mono_arch_get_interp_native_call_info + * mono_arch_get_native_call_context_args + * mono_method_signature_has_ext_callconv + * mono_arch_get_swift_error + * mono_class_value_size -> JIT2EEInterface::getClassSize(CORINFO_METHOD_INFO::args::retTypeClass) + * mono_class_from_mono_type_internal + * mono_class_native_size -> JIT2EEInterface::getClassSize(CORINFO_METHOD_INFO::args::retTypeClass) + * mono_threads_detach_coop -> **ExecutionAPI::EnablePreemptiveGC** + * mono_llvm_start_native_unwind + * mono_arch_set_native_call_context_ret + * mono_arch_free_interp_native_call_info + +### interp_create_method_pointer_llvmonly + * Return an ftndesc for entering the interpreter and executing METHOD. + * Used externally only + * mono_method_signature_internal -> JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args + * mono_jit_compile_method_jit_only + * mono_method_get_name_full + +### interp_create_method_pointer + * Return a function pointer which can be used to call METHOD using the interpreter. Return NULL for methods which are not supported. + * Used externally only + * mono_method_signature_internal -> JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args + * mono_metadata_signature_size + * mono_marshal_get_wrapper_info + * mono_wasm_get_native_to_interp_trampoline + * mono_method_get_full_name + * mono_error_set_platform_not_supported + * mono_method_signature_has_ext_callconv + * mono_jit_compile_method_jit_only + * mono_method_get_name_full + * mono_aot_get_trampoline + * mono_arch_get_native_to_interp_trampoline + * mono_tramp_info_register + * mono_create_ftnptr_arg_trampoline + +### DUMP_INSTR + * Debug build diagnostics + * mono_method_full_name -> JIT2EEInterface::getMethodNameFromMetadata + * mono_thread_internal_current + +### do_init_vtable + * Mono specific -> N.A. on CoreCLR + * mono_runtime_class_init_full + * mono_error_convert_to_exception + +### mono_interp_new + * Mono specific -> N.A. on CoreCLR + * mono_object_new_checked -> **ExecutionAPI::NewObject** + * mono_error_cleanup + +### mono_interp_isinst + * **Use ExecutionAPI::IsInst** + * mono_object_class + * mono_class_is_assignable_from_checked + * mono_error_cleanup + +### mono_interp_get_native_func_wrapper + * mono_marshal_get_native_func_wrapper + * mono_metadata_free_marshal_spec + +### mono_interp_exec_method + * mono_threads_safepoint -> **ExecutionAPI::GCPoll** + * MINT_NIY + * mono_method_full_name - for debug logging -> JIT2EEInterface::getMethodNameFromMetadata + * MINT_BREAK + * mono_component_debugger ()->user_break -> **DebuggerAPI::Break** + * MINT_BREAKPOINT + * mono_break -> **DebuggerAPI::BreakPoint** + * MINT_TAILCALL_VIRT + * mono_object_unbox_internal -> **ExecutionAPI::UnBox** + * mono_method_signature_internal -> JIT2EEInterface::getMethodInfo, CORINFO_METHOD_INFO::args + * MINT_TAILCALL, MINT_TAILCALL_VIRT, MINT_JMP + * mono_domain_get ()->stack_overflow_ex -> **ExecutionAPI::GetExceptionById** + * MINT_CALL_DELEGATE + * mono_get_delegate_invoke_internal + * mono_marshal_get_delegate_invoke + * mono_marshal_get_native_wrapper + * mono_object_unbox_internal -> **ExecutionAPI::UnBox** + * MINT_CALLI + * mono_marshal_get_native_wrapper + * mono_object_unbox_internal -> **ExecutionAPI::UnBox** + * MINT_CALLVIRT_FAST + * mono_object_unbox_internal -> **ExecutionAPI::UnBox** + * mono_method_signature_internal -> JIT2EEInterface::getMethodInfo, CORINFO_METHOD_INFO::args + * MINT_CALL + * mono_domain_get ()->stack_overflow_ex -> **ExecutionAPI::GetExceptionById** + * MINT_B{cc}_R*, MINT_B{cc}_UN_R* + * mono_isunordered - copy the mono implementation + * MINT_STIND_REF + * mono_gc_wbarrier_generic_store_internal -> **ExecutionAPI::GCWriteBarrier** + * MINT_CONV_U4_R4 + * mono_rconv_u4 - copy the mono implementation + * MINT_CONV_U4_R8 + * mono_fconv_u4 - copy the mono implementation + * MINT_CONV_U8_R4 + * mono_rconv_u8 - copy the mono implementation + * MINT_CONV_U8_R8 + * mono_fconv_u8 - copy the mono implementation + * MINT_CPOBJ_VT + * mono_value_copy_internal -> **ExecutionAPI::GCCopyValues** + * MINT_LDSTR_DYNAMIC + * Mono specific, N.A. on CoreCLR + * mono_method_get_wrapper_data + * MINT_LDSTR_CSTR + * Mono specific, N.A. on CoreCLR + * mono_string_new_wrapper_internal + * MINT_NEWOBJ + * mono_gc_alloc_obj -> **ExecutionAPI::NewObject** + * mono_error_set_out_of_memory + * MINT_NEWOBJ_SLOW + * mono_class_vtable_checked - N.A. on CoreCLR + * mono_runtime_class_init_full - N.A. on CoreCLR, classes are already fully loaded + * mono_object_new_checked -> **ExecutionAPI::NewObject** + * MINT_INTRINS_CLEAR_WITH_REFERENCES + * mono_gc_bzero_aligned -> **ExecutionAPI::ClearWithReferences** + * MINT_ISINST_COMMON/MINT_CASTCLASS_COMMON + * **Use ExecutionAPI::CastClass** + * MINT_UNBOX + * **Use ExecutionAPI::UnBox** + * MINT_STFLD_* for I, U, R, O + * mono_gc_wbarrier_set_field_internal -> **ExecutionAPI::GCSetField** + * MINT_STFLD_VT + * mono_value_copy_internal -> **ExecutionAPI::GCCopyValues** + * MINT_LDTSFLDA + * **Use ExecutionAPI::GetThreadStaticFieldAddress** + * MINT_STOBJ_VT + * mono_value_copy_internal -> **ExecutionAPI::GCCopyValues** + * MINT_CONV_OVF_U8_R4, MINT_CONV_OVF_U8_R8, MINT_CONV_OVF_I8_R4, MINT_CONV_OVF_I8_R8 + * mono_try_trunc_i64 - copy the mono implementation + * MINT_BOX + * **Use ExecutionAPI::Box** + * MINT_BOX_VT + * **Use ExecutionAPI::Box** + * MINT_BOX_PTR + * **Use ExecutionAPI::Box** + * MINT_BOX_NULLABLE_PTR + * **Use ExecutionAPI::Box** + * MINT_NEWARR + * **Use ExecutionAPI::CreateSimpleArray** + * MINT_NEWSTR + * Used only by System.String.FastAllocateString intrinsic + * mono_string_new_size_checked + * MINT_LDLEN + * Used only by System.Array.get_Length intrinsic + * mono_array_length_internal + * MINT_GETCHR + * Used only by System.String.get_Chars intrinsic + * mono_string_length_internal + * mono_string_chars_internal + * MINT_STRLEN + * Used only by System.String.get_Length intrinsic + * mono_string_length_internal + * MINT_ARRAY_RANK + * Used only by System.Array.get_Rank intrinsic + * mono_object_class + * MINT_ARRAY_ELEMENT_SIZE + * Used only by System.Array.GetElementSize intrinsic + * mono_object_class + * mono_array_element_size + * MINT_LDELEMA1 + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_LDELEMA + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_LDELEM_* for I, U, R, REF + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_LDELEM_VT + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_STELEM_* for I, U, R + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_STELEM_REF_UNCHECKED + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_STELEM_REF + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_STELEM_VT + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_STELEM_VT_NOREF + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_CKFINITE_R4, MINT_CKFINITE_R8 + * mono_isfinite - copy mono implementation or use std::isfinite if we move to C++ + * MINT_MONO_RETOBJ + * Mono specific -> N.A. on CoreCLR + * mono_method_signature_internal + * MINT_MONO_LDDOMAIN + * Obsolete and mono specific -> N.A. on CoreCLR + * mono_domain_get + * MINT_C{cc}_R* + * mono_isunordered - copy mono implementation + * MINT_LDFTN_DYNAMIC + * Used only by System.RuntimeMethodHandle.GetFunctionPointer intrinsic + * Comment in transform.c says: We must intrinsify this method on interp so we don't return a pointer to native code entering interpreter. + * Q: why is the implementation checking for attributes / generic type definition when there is only one use case? + * mono_class_is_gtd // generic type definition + * mono_exception_from_name_msg + * mono_method_has_unmanaged_callers_only_attribute + * MINT_PROF_ENTER + * mono_trace_enter_method -> **ProfilerAPI::TraceEnterMethod** + * MINT_PROF_EXIT, MINT_PROF_EXIT_VOID + * mono_trace_leave_method -> **ProfilerAPI::TraceLeaveMethod** + * MINT_INTRINS_GET_HASHCODE + * **Use ExecutionAPI::GetHashCode** + * MINT_INTRINS_TRY_GET_HASHCODE + * **Use ExecutionAPI::GetHashCode** + * MINT_METADATA_UPDATE_LDFLDA + * Load address of a field that was added by EnC + * On CoreCLR, it will likely need to call a JIT helper. So we may let the transformation phase store the helper address in the MINT_METADATA_UPDATE_LDFLDA instruction, maybe even create a new IR opcode for all field accesses via a helper in general. Or add an ExecutionAPI method to invoke the JIT_GetFieldAddr which is the helper used here. + * mono_metadata_update_added_field_ldflda + * MINT_TIER_PREPARE_JITERPRETER + * WASM specific (JITerpreter) + * mono_jiterp_patch_opcode + * mono_jiterp_patch_opcode + * MINT_TIER_MONITOR_JITERPRETER + * WASM specific (JITerpreter) + * mono_jiterp_monitor_trace +### interp_set_resume_state + * mono_gchandle_free_internal - N.A. on CoreCLR, the handle is used to keep the exception object alive and the EH in CoreCLR handles that on its own + * mono_gchandle_new_internal - dtto +### interp_run_finally + * Run the finally clause identified by CLAUSE_INDEX in the interpreter frame given by frame->interp_frame. + * mono_llvm_start_native_unwind - propagate an exception from the finally +### interp_run_filter + * Run the filter clause identified by CLAUSE_INDEX in the interpreter frame given by frame->interp_frame + * mono_llvm_start_native_unwind - strange, it seems to propagate an exception from the filter while it should be swallowed +### interp_run_clause_with_il_state + * This is used to run clauses that are located in AOTed code + * ??? Why does it need to do anything interpreter specific? Can there be a case when an AOT-ed clause has interpreted parent method? + * Run exception handling clause + * mono_method_signature_internal -> JIT2EEInterface::getMethodInfo, CORINFO_METHOD_INFO::args + * mono_method_get_header_internal + * mono_metadata_free_mh + * mono_llvm_start_native_unwind +### interp_print_method_counts + * Internal interpreter diagnostic prints + * mono_method_full_name -> JIT2EEInterface::getMethodNameFromMetadata +### metadata_update_backup_frames + * mono_trace + * mono_method_full_name -> JIT2EEInterface::getMethodNameFromMetadata +### interp_invalidate_transformed + * This is used to invalidate "transformed" state of all InterpMethod instances. It is used when EnC updates stuff. + * mono_metadata_has_updates + * mono_stop_world -> **ExecutionAPI::SuspendEE** + * mono_alc_get_all - this is specific to how mono stores all the InterpMethod instances + * mono_restart_world -> **ExecutionAPI::RestartEE** +### interp_jit_info_foreach + * This iterates over all InterpMethod instances and copies out some mono specific JIT info. Mono uses it at one place only for eventpipe rundown. + * mono_alc_get_all - this is specific to how mono stores all the InterpMethod instances +### mono_ee_interp_init + * mono_ee_api_version - for assert that the runtime API version matches what the interpreter was built against, + * mono_native_tls_alloc - reserve thread specific data id for thread context (~ pthread_key_create on Unix). For CoreCLR, we should probably use regular TLS variable instead. +### mono_jiterp_check_pending_unwind + * WASM specific (JITerpreter) + * mono_llvm_start_native_unwind - throw C++ exception to unwind out of the current block of interpreter frames +### mono_jiterp_interp_entry + * WASM specific (JITerpreter). It is called when a call from interpreter to JITerpreted code returns. + * mono_threads_detach_coop -> **ExecutionAPI::EnablePreemptiveGC** + * mono_llvm_start_native_unwind - throw C++ exception to unwind out of the current block of interpreter frames + +## Functions that Mono runtime invokes +The list below contains all the functions from interp.c that the runtime can invoke. These include debugger related methods too. + * interp_entry_from_trampoline + * interp_to_native_trampoline + * interp_create_method_pointer + * interp_create_method_pointer_llvmonly + * interp_free_method + * interp_runtime_invoke + * interp_init_delegate + * interp_delegate_ctor + * interp_set_resume_state + * interp_get_resume_state + * interp_run_finally + * interp_run_filter + * interp_run_clause_with_il_state + * interp_frame_iter_init + * interp_frame_iter_next + * interp_find_jit_info + * interp_set_breakpoint + * interp_clear_breakpoint + * interp_frame_get_jit_info + * interp_frame_get_ip + * interp_frame_get_arg + * interp_frame_get_local + * interp_frame_get_this + * interp_frame_arg_to_data + * interp_data_to_frame_arg + * interp_frame_arg_to_storage + * interp_frame_get_parent + * interp_start_single_stepping + * interp_stop_single_stepping + * interp_free_context + * interp_set_optimizations + * interp_invalidate_transformed + * interp_cleanup + * interp_mark_stack + * interp_jit_info_foreach + * interp_sufficient_stack + * interp_entry_llvmonly + * interp_get_interp_method + * interp_compile_interp_method \ No newline at end of file diff --git a/docs/design/interpreter/transformation.md b/docs/design/interpreter/transformation.md new file mode 100644 index 000000000000..63ee8b109648 --- /dev/null +++ b/docs/design/interpreter/transformation.md @@ -0,0 +1,391 @@ +# Transformation phase +The transformation phase "JITs" the IL code into an IR representation that the execution phase executes later. +This documents lists all functions in the transform.c file in the Mono interpreter codebase. + +# Considerations for coreclr + +There are various cases when a call to runtime helper is needed to perform some operation. This is likely different for Mono (may use a different set of helpers or may not need a helper). So handling such cases will likely need to be added to the transformation and execution phases. Precise details on where and how are beyond the scope of this document. + +# MonoClass, MonoField, MonoMethod and MonoType member access helpers +The following functions are helpers used to access fields in MonoClass, MonoField, MonoMethod and MonoType. They are used all over the place, so they are described here instead of at the specific places they are used. + * m_class_get_byval_arg - N.A. on CoreCLR + * m_class_get_element_class -> getChildType + * m_class_get_image -> getMethodInfo, CORINFO_METHOD_INFO::scope + * m_class_get_name -> getClassNameFromMetadata + * m_class_get_name_space -> getClassNameFromMetadata + * m_class_get_nested_in -> N.A., it is used in interp_handle_intrinsics only to get namespace name for nested classes, getClassNameFromMetadata just works for nested classes too + * m_class_get_parent -> getParentType + * m_class_get_rank -> getArrayRank + * m_class_get_runtime_vtable - N.A. on CoreCLR + * m_class_get_this_arg - N.A. on CoreCLR + * m_class_has_references -> getClassAttribs() & CORINFO_FLG_CONTAINS_GC_PTR + * m_class_has_ref_fields -> getClassGClayout(classHnd) > 0 + * m_class_has_weak_fields - N.A. on CoreCLR + * m_class_is_array -> getClassAttribs() & CORINFO_FLG_ARRAY + * m_class_is_byreflike -> getClassAttribs() & CORINFO_FLG_BYREF_LIKE + * m_class_is_enumtype -> isEnum + * m_class_is_inited - N.A. on CoreCLR + * m_class_is_sealed -> getMethodAttribs() & CORINFO_FLG_FINAL + * m_class_is_simd_type -> getClassNameFromMetadata + * compare namespace to "System.Numerics" and method to "Vector2", "Vector3", "Vector4", "Quaternion", "Plane" + * compare namespace to "System.Runtime.Intrinsics" and method to "Vector64\`1" (Arm64 only), "Vector128\`1", "Vector256\`1" (x64 only) or "Vector512\`1" (x64 only) and then check if getTypeForPrimitiveNumericClass(getTypeInstantiationArgument()) is >= CORINFO_TYPE_BYTE && <= CORINFO_TYPE_DOUBLE. See Compiler::getBaseJitTypeAndSizeOfSIMDType for more details on how CoreCLR JIT does it. + * m_class_is_valuetype -> isValueClass + * m_field_get_offset -> getFieldOffset + * m_field_get_parent -> getFieldClass + * m_field_is_from_update -> getFieldInfo, CORINFO_FIELD_INFO::fieldFlags & CorInfoFlag.CORINFO_FLG_EnC + * m_method_is_static -> getMethodAttribs() & CORINFO_FLG_STATIC + * m_type_is_byref -> asCorInfoType() == CORINFO_TYPE_BYREF + +# Functions that have calls to Mono APIs +For each Mono API or a Mono specific code sequence, it describes how to replace it using JIT2EEInterface methods. In cases when the whole function would be reimplemented in a slightly different way instead of just replacing Mono API calls by their JIT2EEInterface equivalents, a high level description of the function behavior with details on what JIT2EEInterface methods to use is provided. + +### tiered_patcher +* mono_method_signature_internal -> getMethodSig + +### interp_mark_ref_slots_for_var +* Use getClassGClayout + +### interp_mark_ref_slots_for_vt +* Use getClassGClayout without having to drill through the value type fields and their types + +### is_ip_protected +* Use getEHinfo, getMethodInfo, CORINFO_METHOD_INFO::Ehcount + +### handle_stelem +* mono_class_from_mono_type_internal - N.A., just use CORINFO_CLASS_HANDLE +* The intent of this function is to rewrite MINT_STELEM_REF with MINT_STELEM_REF_UNCHECKED if lhs is T[] and rhs is T and T is sealed. JIT2EEInterface::getChildType for T[] returns the T (in clsRet output arg), so we can then match it to the element type + +### initialize_clause_bblocks +* Use getEHinfo, getMethodInfo and then CORINFO_METHOD_INFO::Ehcount + +### interp_handle_box_patterns +* cmethod->klass == mono_defaults.object_class -> cmethod->klass == getBuiltinClass(CLASSID_SYSTEM_OBJECT) +* mono_type_get_object_checked -> getRuntimeTypePointer +* mono_defaults.runtimetype_class -> getBuiltinClass(CLASSID_RUNTIME_TYPE) +* m_class_is_byreflike -> getClassAttribs(clsHnd) & CORINFO_FLG_BYREF_LIKE +* mono_class_is_assignable_from_internal -> compareTypesForCast returns TypeCompareState::Must in this case + +### get_class_from_token +* resolveToken, getTokenTypeAsHandle + +### interp_emit_sfld_access +* Use getFieldInfo to replace all the Mono specific machinery in this function. + * For fields created by EnC -CORINFO_FIELD_INFO::fieldFlags & CORINFO_FLG_EnC + * CORINFO_FIELD_INFO::fieldAccessor to figure out how to access the field + * CORINFO_FIELD_INFO::helper to get the helper needed to access the field (if any) + +### interp_emit_ldsflda +* m_field_get_parent -> getFieldClass +* mono_class_vtable_checked - n.a., use CORINFO_TYPE_CLASS +* mono_class_field_is_special_static - see [interp_emit_sfld_access](#interp_emit_sfld_access) +* mono_special_static_field_get_offset - see [interp_emit_sfld_access](#interp_emit_sfld_access) + +### interp_handle_isinst +* This handles both isinst and castclass +* It has special handling (generates different IR opcodes) for + * Non-generic interfaces + * For non-generic non-arrays that are not nullable +* Generic type can be checked by getTypeInstantiationArgument(clsHnd, 0) returning NO_CLASS_HANDLE +* Array can be checked by getArrayRank returning non-zero or by getClassFlags() & CORINFO_FLG_ARRAY + +### type_has_references +* Use getClassFlags() & CORINFO_FLG_CONTAINS_GC_PTR + +### interp_method_compute_offsets +* Computes offsets of arguments in the interpreter stack and fill in the variable types +* Iterates over arguments, the ported version would iterate like this: + * CORINFO_ARG_LIST_HANDLE arg = 0 + * getArgNext(arg) to get next arg + * getArgType +* Swift specific - get special swift_error argument - implement the same way as Compiler::impPopArgsForSwiftCall +* mono_class_has_failure - N.A., all the classes we get are fully loaded +* mono_error_set_for_class_failure - N.A. +* header->num_clauses -> getMethodInfo, CORINFO_METHOD_INFO::Ehcount + +### mono_interp_type_size +* Use getClassSize + getClassAlignmentRequirement + +### interp_save_debug_info +* This function generates debug info for a specified method. It comprises argument info, local vars info, line numbers and code start and size. +* Use setBoundaries, setVars + +### get_basic_blocks +* Iteration over EH clauses: + * header->num_clauses -> getMethodInfo, CORINFO_METHOD_INFO::Ehcount + * getEHinfo to get details for each EH clause + +### interp_field_from_token +* Use resolveToken, getFieldInfo + +### interp_emit_swiftcall_struct_lowering +* Swift only +* Implement it the way Compiler::impPopArgsForSwiftCall does + +### interp_try_devirt +* Use resolveVirtualMethod + +### get_virt_method_slot +* Use getMethodVTableOffset + +### emit_convert +* mini_get_underlying_type -> asCorInfoType + +### interp_get_method +* Use resolveToken, CORINFO_RESOLVED_TOKEN::hMethod + +### interp_constrained_box +* mono_class_is_nullable -> isNullableType +* mono_class_vtable_checked - N.A, use CORINFO_CLASS_HANDLE in the data for MINT_BOX_PTR. + +### interp_inline_newobj +* Bails out for classes with finalizers (and weak fields which is not something that coreclr would support) +* mono_class_has_finalizer -> getNewHelper, pHasSideEffects output argument is set to true. +* m_class_is_valuetype -> isValueClass +* mono_class_vtable_checked -> N.A., use the CORINFO_CLASS_HANDLE +* interp_method_get_header -> N.A., use the CORINFO_METHOD_HANDLE +* mono_metadata_free_mh - N.A. for coreclr + +### interp_inline_method +* mono_method_signature_internal -> getMethodSig +* mono_method_get_generic_container->context -> CORINFO_SIG_INFO::sigInst +* has_intrinsic_attribute -> isIntrinsic +* target_method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING -> getMethodAttrs & CORINFO_FLG_FORCEINLINE +* MONO_PROFILER_RAISE(inline_method) macro invocation (calls mono_profiler_raise_inline_method) for InterpMethods marked by MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL. + +### interp_method_check_inlining +* Use canInline instead of all the machinery below + +### is_metadata_update_disabled +* Checks for hot reload enabled in general -> there is no equivalent on the Jit2EEInterface + +### interp_get_icall_sig +* Mono supports limited number of signature types for icalls (internal calls). This function gets an enum value describing the specific convention by analyzing the passed in signature. For example MINT_ICALLSIG_P_V represents a void returning function (V) with a single argument that can be passed as pointer sized value (P) like int, boolean, enum, reference, pointer, ... +* The execution part then switches over this when invoking the icall to cast the icall address to a function pointer matching the signature and pass the correct number of arguments to it. +* Use CORINFO_SIG_INFO::numArgs, CORINFO_SIG_INFO::args + +### is_scalar_vtype +* Return whenever TYPE represents a vtype with only one scalar member +* Only used by interp_get_icall_sig +* Implement like Compiler::isTrivialPointerSizedStruct + +### interp_transform_internal_calls +* Looks mono specific - gets a wrapper for calling the internal call - native wrapper or synchronized wrapper +* Might need something like this for QCALLs though. + +### interp_transform_call +* This function figures out the type of the call and emits one of the following IR call instructions: MINT_JIT_CALL, MINT_CALL_DELEGATE, MINT_CALLI_NAT_FAST, MINT_CALLI_NAT_DYNAMIC, MINT_CALLI_NAT, MINT_CALLI, MINT_CALL_VARARG, MINT_CALLVIRT_FAST, MINT_CALL. +* Use getCallInfo as a source of information +* It is possible that for coreclr, the set of call types that we would want to distinguish would be different. +* Here is what it does in high level view: + * Get the signature + * If swift interop is compiled in, do swift lowering, which gets a transformed signature + * Perform access check + * For string ctors, get a different signature (modify the return type to string) + * For intrinsics, call interp_handle_intrinsics and be done + * For constrained_class that is enum and the target method is GetHashCode, replace the method called by a base type one, comment says it is to avoid boxing. + * Further for constrained_class + * get a new target_method based on the constraint + * One more time for intrinsics, call interp_handle_intrinsics and be done + * mono_class_has_dim_conflicts (constrained_class) && mono_class_is_method_ambiguous (constrained_class, virt_method) -> generate throw IR + * Follow the rules for constrained calls from ECMA spec + * For abstract methods generate throw IR + * Handle tail calls + * Try to devirtualize the call + * interp_transform_internal_calls + * Optionally inline the call and be done + * If this is called while inlining a method do some heuristic that may trigger rejection of the inlining + * convert delegate invoke to a indirect call on the interp_invoke_impl field + * Create and align call arguments on the stack, also create call_args array using create_call_args (this represents an array of all call arg vars in the order they are pushed to the stack. This makes it easy to find all source vars for these types of opcodes. This is terminated with -1) + * Allocate interp stack slot for the return value + * For intrinsics, emit the IR instruction that the intrinsic required and set dreg / sregs on the interpreter instruction + * For JIT calls, emit MINT_JIT_CALL + * For delegate calls, emit MINT_CALL_DELEGATE + * For calli + * Handle calls to native code + * Handle icalls emission, use MINT_CALLI_NAT_FAST + * Handle pinvokes in dynamically emitted modules via MINT_CALLI_NAT_DYNAMIC + * Handle other calls to native code via MINT_CALLI_NAT + * Otherwise emit MINT_CALLI + * For other than calli + * If the callee has vararg calling convention, emit MINT_CALL_VARARG + * If it is a virtual method, emit MINT_CALLVIRT_FAST + * Otherwise emit MINT_CALL + * Tiered compilation: For all of the three cases above, if the call is patchable, mark the IR instruction by INTERP_INST_FLAG_RECORD_CALL_PATCH flag and add the IR instruction as a key to patchsite_hash table with the target method as a value + * Alocate some call info data structure attached to the just generated IR instruction + +### mono_interp_transform_method +* This function "compiles" a method into the IR. It has a special handling for MultiCastDelegate invocation methods and internal / runtime calls. +* mono_metadata_update_thread_expose_published +* Return error if the class containing the method is not fully instantiated + * mono_class_is_open_constructed_type -> N.A., open types are never passed over the Jit2EEInterface + * mono_error_set_invalid_operation -> N.A., see ^^^ +* Ensure the class containing the method is initialized + * mono_class_vtable_checked - N.A., use the CORINFO_CLASS_HANDLE instead of vtable + * method_class_vt->initialized, mono_runtime_class_init_full -> initClass +* Get generic context of the method - N.A., open types are never passed over the Jit2EEInterface + * mono_method_signature_internal (method)->is_inflated + * mono_method_get_context + * mono_method_get_generic_container + * generic_container->context +* If method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME) -> internal calls and runtime methods + * This means the method is represented by a native method, the code changes the method to call to a wrapper that invokes the native method + * If the InterpMethod is already marked as transformed, return + * If it is internal call that's not a member of Array + * mono_marshal_get_native_wrapper + * Else + * If it is MultiCastDelegate..ctor + * mono_marshal_get_icall_wrapper(mono_get_jit_icall_info ()->ves_icall_mono_delegate_ctor_interp) + * Else if it is MultiCastDelegate.Invoke + * mono_marshal_get_delegate_invoke (method, NULL); + * Else if it is MultiCastDelegate.BeginInvoke + * mono_marshal_get_delegate_begin_invoke (method); + * Else if it is MultiCastDelegate.EndInvoke + * mono_marshal_get_delegate_end_invoke (method); + * Mark the InterpMethod as transformed +* If the method is marked with UnsafeAccessor attribute + * method = mono_marshal_get_unsafe_accessor_wrapper + * In Jit2EEInterface, the getMethodInfo seems to handle unsafe accessors and some intrinsics +* Call the [generate](#generate) function + +### generate +* This is the main function that translates IL to IR +* First it does some initialization of TransformData structure that holds all the info needed during the transformation +* It calls [generate_code](#generate_code) to generate the IR of the method +* It calls generate_compacted_code to perform some relocations in the IR generated by the previous call +* Finally it fills some InterpMethod fields like EH info, seq points etc. + +### generate_code +* m_class_get_image -> getMethodInfo, CORINFO_METHOD_INFO::scope +* mono_method_signature_internal -> getMethodInfo, CORINFO_METHOD_INFO::args +* mono_basic_block_split + * Generate list of basic blocks from the IL of the method + * We may want to copy this function from Mono +* Get Debug seq points for the method being translated + * Replace all the sequence points extraction by getBoundaries + * mono_debug_lookup_method - lookup symbol information for the method + * mono_debug_lookup_method_async_debug_info - lookup debug info for async method in portable PDB file + * mono_debug_get_seq_points -> getBoundaries + * mono_debug_image_has_debug_info - check if there is a PDB debug file for the assembly containing the method being transformed + * mono_debug_generate_enc_seq_points_without_debug_info - returns true if there is no debug info, so the interpreter should generate seq points as it processes the IL. In that case, the transformation phase inserts MINT_SDB_SEQ_POINT IR opcode on each CEE_NOP, CEE_CALL, CEE_CALVIRT and CEE_CALLI. +* mono_debugger_method_has_breakpoint - check if there is a breakpoint set at method being transformed. + * Interpreter inserts MINT_BREAKPOINT IR opcode at the beginning of the method if there was a breakpoint set +* If verbose_level + * mono_disasm_code + * mono_method_full_name +* mono_trace_eval +* mono_threads_are_safepoints_enabled +* mono_opcode_size +* If verbose_level + * mono_opcode_name +* Switch over all IL codes +* m_class_get_byval_arg(klass) + * This basically just gets MonoType representing the MonoClass. E.g. System.Int32 would get a MonoType with MONO_TYPE_I4. + * The naming comes from the fact that it returns type (MonoType - ~TypeSpec) used to pass an object of the specified class as argument by value, but the usage in the mono interpreter is wider than that. + * Used by + * CEE_LDTOKEN + * CEE_STELEM + * CEE_LDELEM + * CEE_BOX + * CEE_STFLD + * CEE_UNBOX_ANY + * CEE_UNBOX + * CEE_NEWOBJ + * CEE_CPOBJ +* m_class_get_parent == mono_defaults.array_class -> getArrayRank() != 0 ? +* mini_get_class -> resolveToken, CORINFO_RESOLVED_TOKEN::hClass +* mini_type_get_underlying_type -> asCorInfoType + * Used by CEE_RET only +* mono_class_array_element_size -> getChildType, getClassSize +* mono_class_get_and_inflate_typespec_checked -> resolveToken, CORINFO_RESOLVED_TOKEN::hClass +* mono_class_get_flags -> getClassAttribs +* mono_class_get_method_from_name_checked + * Used to get method by name, but it is used in places where we can use other technique to get the result we want + * In CEE_BOX, use getBoxHelper + * In CEE_UNBOX, use getUnboxHelper + * In interp_transform_call, it is used to get GetHashCode method on an enum base type. Use isEnum, the underlyingType output argument. This is not needed for coreclr, the getCallInfo takes care of that. +* mono_class_get_nullable_param_internal -> getTypeInstantiationArgument(clsHandle, 0) +* mono_class_has_dim_conflicts - N.A., handling such stuff is hidden behind the Jit2EEInterface + * It checks if class has conflicting default interface methods + * Used by CEE_LDFTN +* mono_class_has_finalizer -> getNewHelper, pHasSideEffects output argument is set to true. +* mono_class_inflate_generic_type_checked - N.A., generic types are always passed as inflated over the Jit2EEInterface +* mono_class_init_internal - N.A., classes passed over the Jit2EEInterface are always fully loaded +* mono_class_is_assignable_from_internal -> compareTypesForCast + * It is used by CEE_ISINST only to optimize the case when the previous IR was one of the MINT_BOX* +* mono_class_is_method_ambiguous -> N.A., handling such stuff is hidden behind the Jit2EEInterface +* mono_class_is_nullable -> isNullable +* mono_class_native_size - N.A., used by mono specific IL opcodes only +* mono_class_setup_fields - N.A., handling such stuff is hidden behind the Jit2EEInterface +* mono_class_value_size -> getClassSize +* mono_class_vtable_checked - N.A. for coreclr +* mono_error_set_for_class_failure - N.A., classes passed over the Jit2EEInterface are always fully loaded +* mono_error_set_generic_error - error representing System.InvalidProgramException is returned from the generate_code +* mono_error_set_member_access - when attempt to use CEE_NEWOBJ on an abstract class, error with message "Cannot create an abstract class: XYZ" is returned from the generate_code. +* mono_field_get_type_internal -> getFieldInfo, CORINFO_FIELD_INFO::fieldType +* mono_get_method_checked -> resolveToken, CORINFO_RESOLVED_TOKEN::hMethod +* mono_get_method_constrained_with_method + * In interp_transform_call with preceeding CEE_CONSTRAINED -> pass the token from the constrained to getCallInfo as the pConstrainedResolvedToken argument + * In CEE_LDFTN with preceeding CEE_CONSTRAINED -> use getCallInfo with the token from the constrained as the pConstrainedResolvedToken argument +* mono_ldstr_checked -> getStringLiteral +* mono_marshal_get_managed_wrapper - N.A. for coreclr +* mono_metadata_token_index -> constructStringLiteral or getLazyStringLiteralHelper + * Used in CEE_LDSTR +* mono_metadata_token_table - N.A., these details are hidden behind token resolving + * Used in CEE_SIZEOF +* mono_method_can_access_method -> getCallInfo, CORINFO_CALL_INFO::accessAllowed + * Used by CEE_LDFTN +* mono_method_get_context + * It is needed to get the instantiation argument 0 for Span and ReadOnlySpan constructor. + * So we can get it via getTypeInstantiationArgument(clsHandle, 0) +* mono_method_get_wrapper_data - N.A., Mono specific +* mono_method_has_unmanaged_callers_only_attribute -> N.A., it was used only for sanity checks and the the getCallInfo does those internally + * Used by CEE_LDFTN +* mono_mint_type -> asCorInfoType +* mono_trace_eval + * Mono specific, will need to have something similar for profiler for coreclr +* mono_type_size(mono_type_create_from_typespec_checked) -> resolveToken, getClassSize( CORINFO_RESOLVED_TOKEN::hClass) + * Used in CEE_SIZEOF +* mono_type_get_desc -> getClassNameFromMetadata +* mono_type_get_full_name -> getClassNameFromMetadata +* mono_type_get_object_checked -> getRuntimeTypePointer + * gets System.RuntimeType object for a MonoType + * Used by CEE_LDTOKEN + +## Functions that don't use any Mono APIs +These functions might still use glib APIs for memory allocation, bitset, hashtable or linked list. + +* generate_compacted_code +* mono_jiterp_insert_ins +* mono_interp_transform_init +* mono_test_interp_generate_code +* get_native_offset +* interp_squash_initlocals +* interp_fix_localloc_ret +* add_patchpoint_data +* get_var_offset +* get_short_brop +* interp_is_short_offset +* interp_compute_native_offset_estimates +* interp_foreach_ins_var +* interp_foreach_ins_svar +* interp_get_ins_length +* alloc_unopt_global_local +* handle_relocations +* should_insert_seq_point +* handle_ldelem +* handle_stind +* handle_ldind +* interp_emit_load_const +* get_unaligned_opcode +* mono_test_interp_method_compute_offsets +* interp_emit_memory_barrier +* collect_pred_seq_points +* interp_realign_simd_params +* interp_emit_arg_conv +* save_seq_points +* insert_pred_seq_point +* get_bb +* interp_alloc_bb +* create_call_args +* interp_type_as_ptr +* recursively_make_pred_seq_points From a4cff10dd9f5b23518ce817a16a683f436a22bf1 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 27 Nov 2024 16:25:05 +0100 Subject: [PATCH 02/11] Add methods that don't call mono_ functions or m_ macros --- docs/design/interpreter/transformation.md | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/design/interpreter/transformation.md b/docs/design/interpreter/transformation.md index 63ee8b109648..1f56fff57295 100644 --- a/docs/design/interpreter/transformation.md +++ b/docs/design/interpreter/transformation.md @@ -354,6 +354,33 @@ For each Mono API or a Mono specific code sequence, it describes how to replace ## Functions that don't use any Mono APIs These functions might still use glib APIs for memory allocation, bitset, hashtable or linked list. +* interp_add_ins_explicit +* interp_insert_ins +* interp_clear_ins +* interp_ins_is_nop +* interp_prev_ins +* interp_next_ins +* get_stack_size +* get_tos_offset +* interp_create_stack_var +* set_type_and_var +* set_simple_type_and_var +* push_type +* push_simple_type +* push_type_vt +* push_types +* interp_get_mov_for_type +* get_mint_type_size +* try_fold_one_arg_branch +* interp_add_conv +* try_fold_two_arg_branch +* unary_arith_op(TransformData *td, int mint_op) +* can_store +* emit_ldptr +* mono_interp_print_td_code +* interp_ip_in_cbb +* interp_ins_is_ldc +* get_type_comparison_op * generate_compacted_code * mono_jiterp_insert_ins * mono_interp_transform_init From a1ba34af5a0da84b3f63f91eebcf60723a2c76c2 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 27 Nov 2024 16:27:14 +0100 Subject: [PATCH 03/11] Add methods which use just glib or memory allocations --- docs/design/interpreter/transformation.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/design/interpreter/transformation.md b/docs/design/interpreter/transformation.md index 1f56fff57295..0033185c804a 100644 --- a/docs/design/interpreter/transformation.md +++ b/docs/design/interpreter/transformation.md @@ -381,6 +381,23 @@ These functions might still use glib APIs for memory allocation, bitset, hashtab * interp_ip_in_cbb * interp_ins_is_ldc * get_type_comparison_op +* check_stack_helper +* ensure_stack +* push_type_explicit +* fixup_newbb_stack_locals +* init_bb_stack_state +* one_arg_branch +* store_local +* get_data_item_wide_index +* get_data_item_index +* get_data_item_index_imethod +* is_data_item_wide_index +* get_data_item_index_nonshared +* interp_dump_bb +* interp_get_const_from_ldc_i4 +* interp_get_ldc_i4_from_const +* interp_get_ldind_for_mt +* interp_get_stind_for_mt * generate_compacted_code * mono_jiterp_insert_ins * mono_interp_transform_init From 64da4f2bd340a0dfe53507be6310f9f8f5fbf280 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 27 Nov 2024 17:18:16 +0100 Subject: [PATCH 04/11] Added functions from my notes, first part --- docs/design/interpreter/transformation.md | 70 ++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/docs/design/interpreter/transformation.md b/docs/design/interpreter/transformation.md index 0033185c804a..606566dc2723 100644 --- a/docs/design/interpreter/transformation.md +++ b/docs/design/interpreter/transformation.md @@ -6,7 +6,7 @@ This documents lists all functions in the transform.c file in the Mono interpret There are various cases when a call to runtime helper is needed to perform some operation. This is likely different for Mono (may use a different set of helpers or may not need a helper). So handling such cases will likely need to be added to the transformation and execution phases. Precise details on where and how are beyond the scope of this document. -# MonoClass, MonoField, MonoMethod and MonoType member access helpers +# MonoClass, MonoField, MonoMethod and MonoType member access helpers (m_ macros map) The following functions are helpers used to access fields in MonoClass, MonoField, MonoMethod and MonoType. They are used all over the place, so they are described here instead of at the specific places they are used. * m_class_get_byval_arg - N.A. on CoreCLR * m_class_get_element_class -> getChildType @@ -39,6 +39,66 @@ The following functions are helpers used to access fields in MonoClass, MonoFiel # Functions that have calls to Mono APIs For each Mono API or a Mono specific code sequence, it describes how to replace it using JIT2EEInterface methods. In cases when the whole function would be reimplemented in a slightly different way instead of just replacing Mono API calls by their JIT2EEInterface equivalents, a high level description of the function behavior with details on what JIT2EEInterface methods to use is provided. +### has_intrinsic_attribute +* replace with isIntrinsic call + +### has_doesnotreturn_attribute +* Checks whether the method has "System.Diagnostics.CodeAnalysis", "DoesNotReturnAttribute" +* Use getMethodAttribs and check for the "DoesNotReturnAttribute" attribute + +### get_type_from_stack +* Gets mono type from interp's stack type. uses mono_defaults (src/mono/mono/metadata/class-internals.h) to return types from mono_defaults classes (mapped from stack type) +* Update to return CoreCLR class handle +* We might need to extend ICorStaticInfo API to get the default types or implement local helper. + +### mono_mint_type +* Returns interp's mint type for type +* m_class_is_enumtype -> isEnum +* mono_class_enum_basetype_internal -> isEnum - underlyingType parameter +* m_class_get_byval_arg - N.A. on CoreCLR + +### interp_create_var_explicit +* m_class_is_simd_type - check the m_ macros map above +* mono_class_from_mono_type_internal - N.A., just use CORINFO_CLASS_HANDLE + +### interp_create_dummy_var +* m_class_get_byval_arg (mono_defaults.void_class) -> see get_type_from_stack +* Update to use CoreCLR void class + +### interp_create_ref_handle_var +* m_class_get_byval_arg (mono_defaults.int_class) -> see get_type_from_stack +* Update to use CoreCLR int class + +### push_var +* mono_class_from_mono_type_internal - N.A., just use CORINFO_CLASS_HANDLE + +### push_mono_type +* mono_class_from_mono_type_internal - N.A., just use CORINFO_CLASS_HANDLE +* m_type_is_byref -> asCorInfoType() == CORINFO_TYPE_BYREF + +### merge_stack_type_information +* This uses mono class field in the state, that should be resolved with state containing CoreCLR class handles instead + +### handle_branch +* mono_threads_are_safepoints_enabled -> N.A. on CoreCLR? TODO: Jan V., do you have more insight from the CoreCLR side and also from execution phase context? +* Mono sets this flag by: + switch (p) { + case MONO_THREADS_SUSPEND_FULL_COOP: + case MONO_THREADS_SUSPEND_HYBRID: + return TRUE; + default: + return FALSE; + } + +### two_arg_branch +* m_class_get_name -> getClassNameFromMetadata + +### binary_arith_op +* m_class_get_name -> getClassNameFromMetadata + +### shift_op +* m_class_get_name -> getClassNameFromMetadata + ### tiered_patcher * mono_method_signature_internal -> getMethodSig @@ -352,10 +412,13 @@ For each Mono API or a Mono specific code sequence, it describes how to replace * Used by CEE_LDTOKEN ## Functions that don't use any Mono APIs -These functions might still use glib APIs for memory allocation, bitset, hashtable or linked list. +These functions might still use glib APIs for memory allocation, bitset, hashtable or linked list. Or they use mono_interp_* or mono_mint_* functions, which are local to the interpreter. +* interp_new_ins +* interp_add_ins * interp_add_ins_explicit * interp_insert_ins +* interp_insert_ins_bb * interp_clear_ins * interp_ins_is_nop * interp_prev_ins @@ -365,6 +428,8 @@ These functions might still use glib APIs for memory allocation, bitset, hashtab * interp_create_stack_var * set_type_and_var * set_simple_type_and_var +* interp_make_var_renamable +* interp_create_renamed_fixed_var * push_type * push_simple_type * push_type_vt @@ -383,6 +448,7 @@ These functions might still use glib APIs for memory allocation, bitset, hashtab * get_type_comparison_op * check_stack_helper * ensure_stack +* realloc_stack * push_type_explicit * fixup_newbb_stack_locals * init_bb_stack_state From 3bceb5a51d7ae65a4c2f0a09219e5abeb9a45c9c Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Thu, 28 Nov 2024 16:51:49 +0100 Subject: [PATCH 05/11] Feedback --- docs/design/interpreter/transformation.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/design/interpreter/transformation.md b/docs/design/interpreter/transformation.md index 606566dc2723..a8813dd2302a 100644 --- a/docs/design/interpreter/transformation.md +++ b/docs/design/interpreter/transformation.md @@ -44,7 +44,9 @@ For each Mono API or a Mono specific code sequence, it describes how to replace ### has_doesnotreturn_attribute * Checks whether the method has "System.Diagnostics.CodeAnalysis", "DoesNotReturnAttribute" -* Use getMethodAttribs and check for the "DoesNotReturnAttribute" attribute +* ICorStaticInfo doesn't contain API to access custom attributes. Possible solutions: + - avoid calls to has_doesnotreturn_attribute, it is part of inlining process, use just canInline in these places + - add new method to the ICorStaticInfo, similar to isIntrinsic (that might involve extending the WellKnownAttributes enum) ### get_type_from_stack * Gets mono type from interp's stack type. uses mono_defaults (src/mono/mono/metadata/class-internals.h) to return types from mono_defaults classes (mapped from stack type) @@ -80,7 +82,7 @@ For each Mono API or a Mono specific code sequence, it describes how to replace * This uses mono class field in the state, that should be resolved with state containing CoreCLR class handles instead ### handle_branch -* mono_threads_are_safepoints_enabled -> N.A. on CoreCLR? TODO: Jan V., do you have more insight from the CoreCLR side and also from execution phase context? +* mono_threads_are_safepoints_enabled -> N.A. on CoreCLR * Mono sets this flag by: switch (p) { case MONO_THREADS_SUSPEND_FULL_COOP: @@ -89,6 +91,7 @@ For each Mono API or a Mono specific code sequence, it describes how to replace default: return FALSE; } +* Always add safepoints (as if the mono_threads_are_safepoints_enabled always returned true) ### two_arg_branch * m_class_get_name -> getClassNameFromMetadata From 04bae85883ea187a25384e50684b252acb36da61 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Thu, 28 Nov 2024 17:48:52 +0100 Subject: [PATCH 06/11] Add next bunch of functions --- docs/design/interpreter/transformation.md | 55 +++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/docs/design/interpreter/transformation.md b/docs/design/interpreter/transformation.md index a8813dd2302a..4860670642f5 100644 --- a/docs/design/interpreter/transformation.md +++ b/docs/design/interpreter/transformation.md @@ -102,6 +102,55 @@ For each Mono API or a Mono specific code sequence, it describes how to replace ### shift_op * m_class_get_name -> getClassNameFromMetadata +### get_arg_type_exact +* mono_method_signature_internal -> getMethodSig +* m_class_get_byval_arg - N.A. on CoreCLR + +### load_arg +* mono_method_signature_internal -> getMethodSig + mono_class_from_mono_type_internal - N.A., just use CORINFO_CLASS_HANDLE +* mono_method_signature_internal (td->method)->pinvoke && !mono_method_signature_internal (td->method)->marshalling_disabled -> (getMethodAttribs(...) & CORINFO_FLG_PINVOKE) && pInvokeMarshalingRequired(...) +* Simplify code with 2 following calls to use getClassSize +* mono_class_native_size +* mono_class_value_size + +### store_arg +* Same changes as load_arg above + +### load_local (TransformData *td, int local) +* mono_class_from_mono_type_internal - N.A., just use CORINFO_CLASS_HANDLE + +### mono_interp_jit_call_supported +* Return false, we will not support JIT (at least not in first version) + +### jit_call2_supported +* Remove or return false, we will not support JIT (at least not in first version) + +### interp_generate_mae_throw +### interp_generate_void_throw +### interp_generate_ipe_throw_with_msg +* Calls method access error icall +* mono_get_jit_icall_info +* Reimplement as part of the error handling replacement + +### interp_generate_ipe_bad_fallthru +* mono_disasm_code_one -> N.A. on CoreCLR? + - ILCode is available with getMethodInfo + - TODO: check whether JIT does IL disassembly +* Reimplement as part of the error handling replacement + +### interp_create_var +* mono_type_size -> getClassSize + +### interp_dump_ins_data +* m_class_get_name -> getClassNameFromMetadata +* m_class_get_name_space -> getClassNameFromMetadata +* mono_method_full_name -> printMethodName + - the mono method is part of mono's debug helpers. Either replace with printMethodName or implement debug helper + +### mono_interp_print_code +* mono_method_full_name -> printMethodName (see more info above in interp_dump_ins_data) + ### tiered_patcher * mono_method_signature_internal -> getMethodSig @@ -457,6 +506,12 @@ These functions might still use glib APIs for memory allocation, bitset, hashtab * init_bb_stack_state * one_arg_branch * store_local +* init_last_ins_call +* imethod_alloc0 +* interp_generate_icall_throw +* interp_dump_compacted_ins +* interp_dump_code +* interp_dump_ins * get_data_item_wide_index * get_data_item_index * get_data_item_index_imethod From 9c624b5f05085d8efb3ea5962dbb174fc8a41ffa Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 20 Nov 2024 23:18:32 +0100 Subject: [PATCH 07/11] Add documentation of the functions in transform.c This change adds document describing how the functions in the transform.c will be converted to use the Jit2EEInterface methods instead of Mono APIs. --- docs/design/interpreter/debugger.md | 42 ++ docs/design/interpreter/execution.md | 674 ++++++++++++++++++++++ docs/design/interpreter/transformation.md | 391 +++++++++++++ 3 files changed, 1107 insertions(+) create mode 100644 docs/design/interpreter/debugger.md create mode 100644 docs/design/interpreter/execution.md create mode 100644 docs/design/interpreter/transformation.md diff --git a/docs/design/interpreter/debugger.md b/docs/design/interpreter/debugger.md new file mode 100644 index 000000000000..b4520f05a0a8 --- /dev/null +++ b/docs/design/interpreter/debugger.md @@ -0,0 +1,42 @@ +## Debugger to interpreter calls +### Mono +The interpreter exposes a subset of functions implemented in the interp.c as an interface that Mono runtime calls into. The functions listed below are called by the debugger related code. +* interp_set_resume_state +* interp_get_resume_state +* interp_set_breakpoint +* interp_clear_breakpoint +* interp_frame_get_jit_info +* interp_frame_get_ip - dtto +* interp_frame_get_local +* interp_frame_get_this +* interp_frame_get_arg +* interp_start_single_stepping +* interp_stop_single_stepping + +Mono supports debugger connection via the ICorDebug interface. The calls to that interface are translated to Mono debugging protocol that delivers messages to the debuggee side and calls some of the functions listed above to handle the specific operations. +The interp_set_resume_state and interp_get_resume_state don't seem to be used in the ICorDebug related code paths. +The interp_frame_get_jit_info and interp_frame_get_ip seem to be used during single step / breakpoint processing, but it is not clear how they precisely relate to the ICorDebug stuff. + +Here is how relevant ICorDebug interface methods are wired to the interpreter functions: +* CordbJITILFrame::GetLocalVariable -> interp_frame_get_local +* CordbJITILFrame::GetArgument(0) -> interp_frame_get_this +* CordbJITILFrame::GetArgument(1..n) -> interp_frame_get_arg +* CordbFunctionBreakpoint::Activate(true) -> interp_set_breakpoint +* CordbFunctionBreakpoint::Activate(false) -> interp_clear_breakpoint +* CordbStepper::Step, StepRange, StepOut -> interp_start_single_stepping +* CordbStepper::Deactivate -> interp_stop_single_stepping + +### CoreCLR +CoreCLR also uses the ICorDebug interface for debugger connection. Most of the calls to that interface are translated to IPC events and the debuggee side handles the events in Debugger::HandleIPCEvent. So we can handle the events stemming from some of the above mentioned ICorDebug interface methods there by calling the same interpreter functions that Mono calls. +The CordbJITILFrame methods don't send IPC events though and rather uses DAC and remote memory access to get the variable and argument data. It ends up getting the variable locations using the IJitManager::GetBoundariesAndVars method. We would have a JIT manager for the interpreted code and the implementation of this method could use the DAC-ified interp_frame_get_local, interp_frame_get_this and interp_frame_get_arg to get the actual details. + +## Interpreter to debugger calls +### Mono +There are just two "events" that the interpreter notifies the debugger about: +* Single stepping: when MINT_SDB_INTR_LOC IR opcode is executed, a trampoline obtained from mini_get_single_step_trampoline() is called. This trampoline ends up calling mono_component_debugger()->single_step_from_context. That in turn end up calling CordbProcess()->GetCallback()->StepComplete +* Breakpoint hit: when MINT_SDB_BREAKPOINT IR opcode is executed a trampoline obtained from mini_get_breakpoint_trampoline() is called. This trampoline ends up calling mono_component_debugger()->breakpoint_from_context. That in turn end up calling CordbProcess()->GetCallback()->Breakpoint +* System.Diagnostics.Debugger.Break(): when MINT_BREAK IR opcode is executed, a trampoline obtained from mono_component_debugger()->user_break is called. That in turn end up calling CordbProcess()->GetCallback()->Break +### CoreCLR +* Single stepping: when MINT_SDB_INTR_LOC IR opcode is executed, Debugger::SendStep will be called. That sends DB_IPCE_STEP_COMPLETE event and the debugger processes it by ICorDebugManagedCallback::StepComplete) +* Breakpoint hit: when MINT_SDB_BREAKPOINT IR opcode is executed, Debugger::SendBreakpoint will be called (that sends DB_IPCE_BREAKPOINT and the debugger processes it by calling ICorDebugManagedCallback::Breakpoint) +* System.Diagnostics.Debugger.Break(): when MINT_BREAK IR opcode is executed, Debugger::SendRawUserBreakpoint will be called (that sends DB_IPCE_USER_BREAKPOINT and the debugger processes it by calling ICorDebugManagedCallback::Break) diff --git a/docs/design/interpreter/execution.md b/docs/design/interpreter/execution.md new file mode 100644 index 000000000000..ecfe4e87f08c --- /dev/null +++ b/docs/design/interpreter/execution.md @@ -0,0 +1,674 @@ +# Execution phase + +This document explicitly doesn't list stuff like locks, hashtable, memory allocations, mono_compiler_barrier + +## Execution interface candidate methods: +1. GCWriteBarrier(destAddress, object*) +2. GCCopyValues(destAddress, sourceAddress, count, classHandle) +3. GCSetField(destObject*, field*, srcObject*) +4. ThrowException(exceptionObject*) +5. CreateSimpleArray(classHandle, length) - Create a single dimensional zero based array with element type specified by the classHandle and the given length +6. CreateArray(classHandle, numArgs, args) - Create an array that's not single dimensional zero based one. The args represent the array bounds. +7. GetArrayElementAddress(object*, index) - Get array element address. Even for multi-dim arrays, the index is a linear index in the array data +8. DisablePreemptiveGC() +9. EnablePreemptiveGC() +10. IsInst(object*, classHandle) +11. CastClass(object*, classHandle) +12. ClearWithReferences(address, length) +13. Box(data*, classHandle) +14. UnBox(object*) +15. SuspendEE() +16. RestartEE() +17. GetExceptionById(id) - id: null_reference, divide_by_zero, overflow, invalid_cast, index_out_of_range, array_type_mismatch, arithmetic, argument_out_of_range. Maybe use the CoreCLR RuntimeExceptionKind as the id. +18. GetVirtualMethod(methodHandle, classHandle) +19. NewObject(classHandle) +20. GetThreadStaticFieldAddress(offset) - Offset is relative to the managed TLS storage +21. GetHashCode(tryGet, object*) +22. GCPoll() + +## Debugger interface methods + * BreakPoint - breakpoint set by the debugger triggered in the interpreted code + * SingleStep - single step completed + * Break - System.Diagnostics.Debugger.Break() invoked by the interpreted code + +## Profiler interface methods + * TraceEnterMethod(methodHandle, ???) + * TraceLeaveMethod(methodHandle, ???) + +## JIT2EEInterface methods + * getMethodInfo + * getArgNext + * getArgType + * isEnum - may not be needed if the CORINFO_METHOD_INFO::args are already enum free (I think I've seen a comment that it is that case somewhere) + * getClassSize + * getMethodAttribs - only used at one place to check if a class is abstract when initializing a delegate + * getClassNameFromMetadata - diagnostic purposes in debug builds only + * getMethodNameFromMetadata - diagnostic purposes in debug builds only + * getChildType + * getParentType + * isValueClass + * asCorInfoType + * getArrayRank + * getClassAttribs + + +## MonoClass, MonoMethod and MonoType member access helpers +The following functions are helpers used to access fields in MonoClass, MonoMethod and MonoType. They are used all over the place, so they are described here instead of at the specific places they are used. + * m_class_get_byval_arg - N.A. on CoreCLR + * m_class_get_element_class -> **JIT2EEInterface::getChildType** + * m_class_get_image -> **JIT2EEInterface::getMethodInfo, CORINFO_METHOD_INFO::scope** + * m_class_get_name -> **JIT2EEInterface::getClassNameFromMetadata** + * m_class_get_name_space -> **JIT2EEInterface::getClassNameFromMetadata** + * m_class_get_parent -> **JIT2EEInterface::getParentType** + * m_class_get_rank -> **JIT2EEInterface::getArrayRank** + * m_class_is_byreflike -> **JIT2EEInterface::getClassAttribs() & CORINFO_FLG_BYREF_LIKE** + * m_class_is_enumtype -> **JIT2EEInterface::isEnum** + * m_class_is_valuetype -> **JIT2EEInterface::isValueClass** + * m_method_is_static -> **JIT2EEInterface::getMethodAttribs() & CORINFO_FLG_STATIC** + * m_method_is_virtual -> **JIT2EEInterface::getMethodAttribs() & CORINFO_FLG_VIRTUAL** + * m_type_is_byref -> **JIT2EEInterface::asCorInfoType() == CORINFO_TYPE_BYREF** + +## Functions in the interp.c + +### db_match_method + * Used just for debug build tracing + * mono_method_desc_full_match - match a method by name + +### debug_enter, DEBUG_LEAVE() + * Used just for debug build tracing + * mono_method_full_name -> **JIT2EEInterface::getMethodNameFromMetadata** + * mono_thread_internal_current - used for logging purposes only + +### clear_resume_state + * Resume state is a state where the interpreter will continue to execute from after execution returns to the interpreter + * mono_gchandle_free_internal - context holds handle to the managed Exception. Not needed for CoreCLR, the EH managed the exception object lifetime. + +### set_context + * ThreadContext (interpreter specific thread context) storage initialization. + * mono_native_tls_set_value + * mono_tls_get_jit_tls + * MonoJitTlsData::interp_context stores the same ThreadContext* as the context thread local + +### get_context + * Get interpreter specific thread context data + * mono_native_tls_get_value + * pthread_getspecific on Unix + * Direct TEB access on Windows + * Probably switch it to a regular thread local variables for the two usages that are ThreadContext* and MonoJitTlsData* + +### interp_free_context + * Free interpreter specific thread context data, occurs at shutdown. + * mono_native_tls_get_value + +### mono_interp_error_cleanup + * MonoError holds various details on the last error, including some dynamically allocated stuff. This function deallocates all of that. + * Some Mono APIs that interpreter calls set the MonoError to carry details on the error that occured + * N.A. on CoreCLR + * mono_error_cleanup + +### mono_interp_get_imethod + * Get an existing or create a new InterpMethod for a MonoMethod + * Creating a new one: + * mono_method_signature_internal - to get various details like param_count, has_this etc. to store them in the InterpMethod -> **JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args, CORINFO_METHOD_INFO::args.hasThis() etc.** + * m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class && !strcmp(method->name, "Invoke") - set the InterpMethod::is_invoke + * There is a special handling of String..ctor method to override its return value to be String for some reason + * mono_profiler_get_call_instrumentation_flags - call all registered profilers to get the flags of events they are interested in for the specific method and store those on the InterpMethod instance + * MONO_PROFILER_CALL_INSTRUMENTATION_ENTER, MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE, MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL, MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE + * CoreCLR profiler doesn't have such a filtering capability + +### interp_push_lmf + * LMF means Last Managed Frame. There is a per thread linked list of those. Interpreter pushes and pops it around some calls to Mono runtime. + * Pushed for + * exception throwing + * mono_error_convert_to_exception + * mono_get_exception_{x} + * mono_threads_safepoint + * pinvoke calls + * icalls + * JITerpreter jitting call + * JIT calls (these are in fact AOT code calls) + * Debugger trampolines calls + * mono_interp_transform_method + * mono_runtime_class_init_full + * mono_push_lmf + +### interp_pop_lmf + * mono_pop_lmf + +### get_virtual_method + * **Use ExecutionAPI::GetVirtualMethod** + * For a virtual method call, get the actual method to be called + +### stackval_from_data + * Sets stackval::data::{x} to data bytes from the data argument based on the MonoType passed in. The MonoType argument determines the size of data to copy. + * The converted form would use CorInfoType as argument. Major part of the usages of stackval_from_data pass in return value type / argument type from a signature. The CORINFO_SIG_INFO contains the CorInfoType directly for return type and **JIT2EEInterface::getArgType also returns CorInfoType.** + * It seems that CoreCLR won't need to handle case that's MONO_TYPE_GENERICINST and generic instantiations don't require special handling here. + +### stackval_to_data + * Reverse of stackval_from_data. An addiitonal thing is that it calls GC write barriers for reference types and value types + * mono_gc_wbarrier_generic_store_internal -> GCWriteBarrier + * mono_value_copy_internal -> GCCopyValues + * It seems that CoreCLR won't need to handle case that's MONO_TYPE_GENERICINST and generic instantiations don't require special handling here. + +### handle_exception_cb + * Unused dead code + * mono_handle_exception + +### interp_throw + * **Use ExecutionAPI::ThrowException** + * Call exception handling code in the runtime + * It expects that when execution should resume after catch, the exception handling returns here so that the interpreter can restore the state to the resume location in case it was in the interpreted code. The interpreter would then pop interpreter frames if necessary and then set the interpreter state ip / sp appropriately. The interpreter loop doesn't recurse for calls to interpreted methods and it allocates new interpreter frame instances using alloca. When returning and then calling again, the interpreter frames are reused. So when resuming at certain interpreted frame, the interpreter frames of the "unwound" frames need to be pushed to the stack of interpreter frames for reuse. + +### interp_error_convert_to_exception + * Converts MonoError received from a Mono API to managed exception. This is N.A. for CoreCLR + * There are also several cases when the interpreter creates the MonoError on its own using the following functions. Those will be changed to get the exception directly using **ExecutionAPI::GetExceptionById** + * mono_error_set_out_of_memory + * mono_error_set_argument + * mono_error_set_platform_not_supported + * mono_error_convert_to_exception -> N.A. on CoreCLR + +### EXCEPTION_CHECKPOINT + * Checks for thread abort and throws the ThreadAbortException if it was requested + * mono_threads_is_critical_method - N.A. on CoreCLR + * mono_thread_interruption_checkpoint - check for thread abort and return the exception + +### do_safepoint + * **Use ExecutionAPI::GCPoll** + +### ves_array_create + * mono_array_new_jagged_checked -> **ExecutionAPI::CreateArray** + * mono_array_new_full_checked -> **ExecutionAPI::CreateArray** + +### ves_array_element_address + * **Use ExecutionAPI::GetArrayElementAddress** + +### compute_arg_offset + * Computes offset of a specific argument in a method signature + * mono_mint_type - N.A. on CoreCLR, the **JIT2EEInterface::getArgType used for iterating over args returns CorInfoType which is an equivalent** + +### initialize_arg_offsets + * Ensures that the arg_offsets member in the InterpMethod is filled in with offsets of arguments in a method signature + * mono_method_signature_internal -> **JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args** + * mono_mint_type - N.A. on CoreCLR, the **JIT2EEInterface::getArgType** used for iterating over args returns CorInfoType which is an equivalent + +### get_build_args_from_sig_info + * Build signature describing structure for PInvoke usage. + * mono_class_enum_basetype_internal - **JIT2EEInterface::isEnum()** returns this too + +### get_interp_to_native_trampoline + * mono_aot_get_trampoline + * mono_arch_get_interp_to_native_trampoline + * mono_tramp_info_register + + + +### ves_pinvoke_method + * Call a PInvoke + * mono_wasm_get_interp_to_native_trampoline + * mono_jit_compile_method_jit_only + * mini_get_interp_lmf_wrapper + * mono_interp_to_native_trampoline + * mono_arch_get_interp_native_call_info + * mono_arch_set_native_call_context_args + * For SWIFT + * mono_method_signature_has_ext_callconv + * mono_arch_get_swift_error + * mono_arch_get_native_call_context_ret + +### interp_init_delegate + * Initialize del->interp_method + * mono_class_is_abstract -> **JIT2EEInterface::getMethodAttribs() & CORINFO_FLG_ABSTRACT** + * m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class && !strcmp (name, "Invoke") + * mono_marshal_get_delegate_invoke + * mono_create_delegate_trampoline_info + +### interp_delegate_ctor + * Construct a delegate pointing to an InterpMethod + * **Use **JIT2EEInterface::GetDelegateCtor** + * Since this is only called from the transform.c, move it to that file + +### dump_stackval + * Debug build diagnostics + * mono_type_get_desc -> **JIT2EEInterface::getClassNameFromMetadata()** + +### dump_retval + * Debug build diagnostics + * mono_method_signature_internal -> **JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args::retType** + +### dump_args + * Debug build diagnostics + * mono_method_signature_internal -> **JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args** + +### interp_runtime_invoke + * Invoke a method specified by a MonoMethod passing it arguments passed to interp_runtime_invoke + * mono_method_signature_internal - only used to get "hasThis" -> **JIT2EEInterface::getMethodInfo, CORINFO_METHOD_INFO::args, CORINFO_SIG_INFO::hasThis()** + * mono_marshal_get_native_wrapper + * mono_marshal_get_runtime_invoke_full + * mono_llvm_start_native_unwind + +### interp_entry + * Main function for entering the interpreter from compiled code + * mono_object_unbox_internal - "this" may be passed in as boxed for value types. + * mono_threads_attach_coop - for cases when the interpreted method is invoked by native code -> **ExecutionAPI::DisablePreemptiveGC** + * mono_domain_get - N.A., obsolete + * mono_marshal_get_delegate_invoke - when the method to interpret is Invoke method on a class derived from MultiCastDelegate. + * mono_method_signature_internal -> **JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args** + * mono_threads_detach_coop -> **ExecutionAPI::EnablePreemptiveGC** + * mono_llvm_start_native_unwind - throws C++ exception + +### do_icall + * Invoke internal call + * mono_marshal_clear_last_error + * mono_marshal_set_last_error + +### init_jit_call_info + * Fill in JitCallInfo data structure with details on a call to AOT compiled managed code, like target address, signature, etc. + * mono_method_signature_internal -> **JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args** + * mono_jit_compile_method_jit_only - Fetch the AOTed code address. + * mono_aot_get_method_flags - checks for generic shared value type + * mono_mint_type -> the CORINFO_METHOD_INFO::args::retType is already what we need here + * mono_class_from_mono_type_internal - no op on CoreCLR + * mono_class_value_size - extracts return value size -> **JIT2EEInterface::getClassSize(CORINFO_METHOD_INFO::args::retTypeClass)** + +### do_jit_call + * Invoke AOT compiled managed code + * mono_llvm_catch_exception + * mono_get_jit_tls + * mono_error_set_exception_instance + +### init_arglist + * mono_type_stack_size + +### interp_entry_from_trampoline + * High level overview: Iterate over all arguments of the method to execute and copy them from the trampoline to the appropriate slots on the interpreter stack. Then interpret the method using mono_interp_exec_method. After the interpreted method exits, copy its result back to the trampoline and return to the caller. + * mono_threads_attach_coop -> **ExecutionAPI::DisablePreemptiveGC** + * mono_domain_get - N.A., obsolete + * mono_method_signature_internal -> **JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args** + * mono_metadata_signature_size + * mono_arch_get_interp_native_call_info + * mono_arch_get_native_call_context_args + * mono_method_signature_has_ext_callconv + * mono_arch_get_swift_error + * mono_class_value_size -> **JIT2EEInterface::getClassSize(CORINFO_METHOD_INFO::args::retTypeClass)** + * mono_class_from_mono_type_internal + * mono_class_native_size -> **JIT2EEInterface::getClassSize(CORINFO_METHOD_INFO::args::retTypeClass)** + * mono_threads_detach_coop -> **ExecutionAPI::EnablePreemptiveGC** + * mono_llvm_start_native_unwind + * mono_arch_set_native_call_context_ret + * mono_arch_free_interp_native_call_info + +### interp_create_method_pointer_llvmonly + * Return an ftndesc for entering the interpreter and executing METHOD. + * Used externally only + * mono_method_signature_internal -> **JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args** + * mono_jit_compile_method_jit_only + * mono_method_get_name_full + +### interp_create_method_pointer + * Return a function pointer which can be used to call METHOD using the interpreter. Return NULL for methods which are not supported. + * Used externally only + * mono_method_signature_internal -> **JIT2EEInterface::getMethodInfo(), CORINFO_METHOD_INFO::args** + * mono_metadata_signature_size + * mono_marshal_get_wrapper_info + * mono_wasm_get_native_to_interp_trampoline + * mono_method_get_full_name + * mono_error_set_platform_not_supported + * mono_method_signature_has_ext_callconv + * mono_jit_compile_method_jit_only + * mono_method_get_name_full + * mono_aot_get_trampoline + * mono_arch_get_native_to_interp_trampoline + * mono_tramp_info_register + * mono_create_ftnptr_arg_trampoline + +### DUMP_INSTR + * Debug build diagnostics + * mono_method_full_name -> **JIT2EEInterface::getMethodNameFromMetadata** + * mono_thread_internal_current + +### do_init_vtable + * Mono specific -> N.A. on CoreCLR + * mono_runtime_class_init_full + * mono_error_convert_to_exception + +### mono_interp_new + * Mono specific -> N.A. on CoreCLR + * mono_object_new_checked -> **ExecutionAPI::NewObject** + * mono_error_cleanup + +### mono_interp_isinst + * **Use ExecutionAPI::IsInst** + * mono_object_class + * mono_class_is_assignable_from_checked + * mono_error_cleanup + +### mono_interp_get_native_func_wrapper + * mono_marshal_get_native_func_wrapper + * mono_metadata_free_marshal_spec + +### mono_interp_exec_method + * mono_threads_safepoint -> **ExecutionAPI::GCPoll** + * MINT_NIY + * mono_method_full_name - for debug logging -> **JIT2EEInterface::getMethodNameFromMetadata** + * MINT_BREAK + * mono_component_debugger ()->user_break -> **DebuggerAPI::Break** + * MINT_BREAKPOINT + * mono_break -> **DebuggerAPI::BreakPoint** + * MINT_TAILCALL_VIRT + * mono_object_unbox_internal -> **ExecutionAPI::UnBox** + * mono_method_signature_internal -> **JIT2EEInterface::getMethodInfo, CORINFO_METHOD_INFO::args** + * MINT_TAILCALL, MINT_TAILCALL_VIRT, MINT_JMP + * mono_domain_get ()->stack_overflow_ex -> **ExecutionAPI::GetExceptionById** + * MINT_CALL_DELEGATE + * mono_get_delegate_invoke_internal + * mono_marshal_get_delegate_invoke + * mono_marshal_get_native_wrapper + * mono_object_unbox_internal -> **ExecutionAPI::UnBox** + * MINT_CALLI + * mono_marshal_get_native_wrapper + * mono_object_unbox_internal -> **ExecutionAPI::UnBox** + * MINT_CALLVIRT_FAST + * mono_object_unbox_internal -> **ExecutionAPI::UnBox** + * mono_method_signature_internal -> **JIT2EEInterface::getMethodInfo, CORINFO_METHOD_INFO::args** + * MINT_CALL + * mono_domain_get ()->stack_overflow_ex -> **ExecutionAPI::GetExceptionById** + * MINT_B{cc}_R*, MINT_B{cc}_UN_R* + * mono_isunordered - copy the mono implementation + * MINT_STIND_REF + * mono_gc_wbarrier_generic_store_internal -> **ExecutionAPI::GCWriteBarrier** + * MINT_CONV_U4_R4 + * mono_rconv_u4 - copy the mono implementation + * MINT_CONV_U4_R8 + * mono_fconv_u4 - copy the mono implementation + * MINT_CONV_U8_R4 + * mono_rconv_u8 - copy the mono implementation + * MINT_CONV_U8_R8 + * mono_fconv_u8 - copy the mono implementation + * MINT_CPOBJ_VT + * mono_value_copy_internal -> **ExecutionAPI::GCCopyValues** + * MINT_LDSTR_DYNAMIC + * Mono specific, N.A. on CoreCLR + * mono_method_get_wrapper_data + * MINT_LDSTR_CSTR + * Mono specific, N.A. on CoreCLR + * mono_string_new_wrapper_internal + * MINT_NEWOBJ + * mono_gc_alloc_obj -> **ExecutionAPI::NewObject** + * mono_error_set_out_of_memory + * MINT_NEWOBJ_SLOW + * mono_class_vtable_checked - N.A. on CoreCLR + * mono_runtime_class_init_full - N.A. on CoreCLR, classes are already fully loaded + * mono_object_new_checked -> **ExecutionAPI::NewObject** + * MINT_INTRINS_CLEAR_WITH_REFERENCES + * mono_gc_bzero_aligned -> **ExecutionAPI::ClearWithReferences** + * MINT_ISINST_COMMON/MINT_CASTCLASS_COMMON + * **Use ExecutionAPI::CastClass** + * MINT_UNBOX + * **Use ExecutionAPI::UnBox** + * MINT_STFLD_* for I, U, R, O + * mono_gc_wbarrier_set_field_internal -> **ExecutionAPI::GCSetField** + * MINT_STFLD_VT + * mono_value_copy_internal -> **ExecutionAPI::GCCopyValues** + * MINT_LDTSFLDA + * **Use ExecutionAPI::GetThreadStaticFieldAddress** + * MINT_STOBJ_VT + * mono_value_copy_internal -> **ExecutionAPI::GCCopyValues** + * MINT_CONV_OVF_U8_R4, MINT_CONV_OVF_U8_R8, MINT_CONV_OVF_I8_R4, MINT_CONV_OVF_I8_R8 + * mono_try_trunc_i64 - copy the mono implementation + * MINT_BOX + * **Use ExecutionAPI::Box** + * MINT_BOX_VT + * **Use ExecutionAPI::Box** + * MINT_BOX_PTR + * **Use ExecutionAPI::Box** + * MINT_BOX_NULLABLE_PTR + * **Use ExecutionAPI::Box** + * MINT_NEWARR + * **Use ExecutionAPI::CreateSimpleArray** + * MINT_NEWSTR + * Used only by System.String.FastAllocateString intrinsic + * mono_string_new_size_checked + * MINT_LDLEN + * Used only by System.Array.get_Length intrinsic + * mono_array_length_internal + * MINT_GETCHR + * Used only by System.String.get_Chars intrinsic + * mono_string_length_internal + * mono_string_chars_internal + * MINT_STRLEN + * Used only by System.String.get_Length intrinsic + * mono_string_length_internal + * MINT_ARRAY_RANK + * Used only by System.Array.get_Rank intrinsic + * mono_object_class + * MINT_ARRAY_ELEMENT_SIZE + * Used only by System.Array.GetElementSize intrinsic + * mono_object_class + * mono_array_element_size + * MINT_LDELEMA1 + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_LDELEMA + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_LDELEM_* for I, U, R, REF + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_LDELEM_VT + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_STELEM_* for I, U, R + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_STELEM_REF_UNCHECKED + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_STELEM_REF + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_STELEM_VT + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_STELEM_VT_NOREF + * **Use ExecutionAPI::to get GetArrayElementAddress** + * MINT_CKFINITE_R4, MINT_CKFINITE_R8 + * mono_isfinite - copy mono implementation or use std::isfinite if we move to C++ + * MINT_MONO_RETOBJ + * Mono specific -> N.A. on CoreCLR + * mono_method_signature_internal + * MINT_MONO_LDDOMAIN + * Obsolete and mono specific -> N.A. on CoreCLR + * mono_domain_get + * MINT_C{cc}_R* + * mono_isunordered - copy mono implementation + * MINT_LDFTN_DYNAMIC + * Used only by System.RuntimeMethodHandle.GetFunctionPointer intrinsic + * Comment in transform.c says: We must intrinsify this method on interp so we don't return a pointer to native code entering interpreter. + * Q: why is the implementation checking for attributes / generic type definition when there is only one use case? + * mono_class_is_gtd // generic type definition + * mono_exception_from_name_msg + * mono_method_has_unmanaged_callers_only_attribute + * MINT_PROF_ENTER + * mono_trace_enter_method -> **ProfilerAPI::TraceEnterMethod** + * MINT_PROF_EXIT, MINT_PROF_EXIT_VOID + * mono_trace_leave_method -> **ProfilerAPI::TraceLeaveMethod** + * MINT_INTRINS_GET_HASHCODE + * **Use ExecutionAPI::GetHashCode** + * MINT_INTRINS_TRY_GET_HASHCODE + * **Use ExecutionAPI::GetHashCode** + * MINT_METADATA_UPDATE_LDFLDA + * Load address of a field that was added by EnC + * On CoreCLR, it will likely need to call a JIT helper. So we may let the transformation phase store the helper address in the MINT_METADATA_UPDATE_LDFLDA instruction, maybe even create a new IR opcode for all field accesses via a helper in general. Or add an ExecutionAPI method to invoke the JIT_GetFieldAddr which is the helper used here. + * mono_metadata_update_added_field_ldflda + * MINT_TIER_PREPARE_JITERPRETER + * WASM specific (JITerpreter) + * mono_jiterp_patch_opcode + * mono_jiterp_patch_opcode + * MINT_TIER_MONITOR_JITERPRETER + * WASM specific (JITerpreter) + * mono_jiterp_monitor_trace +### interp_set_resume_state + * mono_gchandle_free_internal - N.A. on CoreCLR, the handle is used to keep the exception object alive and the EH in CoreCLR handles that on its own + * mono_gchandle_new_internal - dtto +### interp_run_finally + * Run the finally clause identified by CLAUSE_INDEX in the interpreter frame given by frame->interp_frame. + * mono_llvm_start_native_unwind - propagate an exception from the finally +### interp_run_filter + * Run the filter clause identified by CLAUSE_INDEX in the interpreter frame given by frame->interp_frame + * mono_llvm_start_native_unwind - strange, it seems to propagate an exception from the filter while it should be swallowed +### interp_run_clause_with_il_state + * This is used to run clauses that are located in AOTed code + * It is Mono specific and it is used to run filters and finallys when running LLVM AOT compiled code, as it uses standard native exception handling for AOT code and filters would not work there due to the nature of the native EH. + * Run exception handling clause + * mono_method_signature_internal -> **JIT2EEInterface::getMethodInfo, CORINFO_METHOD_INFO::args** + * mono_method_get_header_internal + * mono_metadata_free_mh + * mono_llvm_start_native_unwind +### interp_print_method_counts + * Internal interpreter diagnostic prints + * mono_method_full_name -> **JIT2EEInterface::getMethodNameFromMetadata** +### metadata_update_backup_frames + * mono_trace + * mono_method_full_name -> **JIT2EEInterface::getMethodNameFromMetadata** +### interp_invalidate_transformed + * This is used to invalidate "transformed" state of all InterpMethod instances. It is used when EnC updates stuff. + * mono_metadata_has_updates + * mono_stop_world -> **ExecutionAPI::SuspendEE** + * mono_alc_get_all - this is specific to how mono stores all the InterpMethod instances + * mono_restart_world -> **ExecutionAPI::RestartEE** +### interp_jit_info_foreach + * This iterates over all InterpMethod instances and copies out some mono specific JIT info. Mono uses it at one place only for eventpipe rundown. + * mono_alc_get_all - this is specific to how mono stores all the InterpMethod instances +### mono_ee_interp_init + * mono_ee_api_version - for assert that the runtime API version matches what the interpreter was built against, + * mono_native_tls_alloc - reserve thread specific data id for thread context (~ pthread_key_create on Unix). For CoreCLR, we should probably use regular TLS variable instead. +### mono_jiterp_check_pending_unwind + * WASM specific (JITerpreter) + * mono_llvm_start_native_unwind - throw C++ exception to unwind out of the current block of interpreter frames +### mono_jiterp_interp_entry + * WASM specific (JITerpreter). It is called when a call from interpreter to JITerpreted code returns. + * mono_threads_detach_coop -> **ExecutionAPI::EnablePreemptiveGC** + * mono_llvm_start_native_unwind - throw C++ exception to unwind out of the current block of interpreter frames + +## Functions that Mono runtime invokes +The list below contains all the functions from interp.c that the runtime can invoke. These include debugger related methods too. + * interp_entry_from_trampoline + * interp_to_native_trampoline + * interp_create_method_pointer + * interp_create_method_pointer_llvmonly + * interp_free_method + * interp_runtime_invoke + * interp_init_delegate + * interp_delegate_ctor + * interp_set_resume_state + * interp_get_resume_state + * interp_run_finally + * interp_run_filter + * interp_run_clause_with_il_state + * interp_frame_iter_init + * interp_frame_iter_next + * interp_find_jit_info + * interp_set_breakpoint + * interp_clear_breakpoint + * interp_frame_get_jit_info + * interp_frame_get_ip + * interp_frame_get_arg + * interp_frame_get_local + * interp_frame_get_this + * interp_frame_arg_to_data + * interp_data_to_frame_arg + * interp_frame_arg_to_storage + * interp_frame_get_parent + * interp_start_single_stepping + * interp_stop_single_stepping + * interp_free_context + * interp_set_optimizations + * interp_invalidate_transformed + * interp_cleanup + * interp_mark_stack + * interp_jit_info_foreach + * interp_sufficient_stack + * interp_entry_llvmonly + * interp_get_interp_method + * interp_compile_interp_method + + ## Functions that don't use any Mono APIs +These functions might still use glib APIs for memory allocation, bitset, hashtable or linked list. + * need_native_unwind + * lookup_imethod + * append_imethod + * get_target_imethod + * get_vtable_ee_data + * get_method_table + * alloc_method_table + * get_virtual_method_fast + * interp_throw_ex_general + * ves_array_calculate_index + * imethod_alloc0 + * get_arg_offset_fast + * get_arg_offset + * filter_type_for_args_from_sig + * build_args_from_sig + * interp_frame_arg_to_data + * interp_data_to_frame_arg + * interp_frame_arg_to_storage + * interp_to_native_trampoline + * ftnptr_to_imethod + * imethod_to_ftnptr + * jit_call_cb + * do_icall_wrapper + * interp_entry_general + * interp_entry_llvmonly + * interp_get_interp_method + * interp_compile_interp_method + * interp_no_native_to_managed + * no_llvmonly_interp_method_pointer + * interp_free_method + * mono_interp_enum_hasflag + * interp_simd_create + * g_warning_d + * interp_error_xsx + * method_entry + * min_f, max_f, min_d, max_d + * interp_parse_options + * interp_frame_get_ip + * interp_frame_iter_init + * interp_frame_iter_next + * interp_find_jit_info + * interp_set_breakpoint + * interp_clear_breakpoint + * interp_frame_get_jit_info + * interp_frame_get_arg + * interp_frame_get_local + * interp_frame_get_this + * interp_frame_get_parent + * interp_start_single_stepping + * interp_stop_single_stepping + * interp_mark_frame_no_ref_slots + * interp_mark_no_ref_slots + * interp_mark_stack + * opcode_count_comparer + * interp_print_op_count + * interp_add_imethod + * imethod_opcount_comparer + * interp_print_method_counts + * interp_set_optimizations + * invalidate_transform + * copy_imethod_for_frame + * metadata_update_prepare_to_invalidate + * interp_copy_jit_info_func + * interp_sufficient_stack + * interp_cleanup + * mono_jiterp_stackval_to_data + * mono_jiterp_stackval_from_data + * mono_jiterp_get_arg_offset + * mono_jiterp_overflow_check_i4 + * mono_jiterp_overflow_check_u4 + * mono_jiterp_ld_delegate_method_ptr + * mono_jiterp_check_pending_unwind + * mono_jiterp_get_context + * mono_jiterp_frame_data_allocator_alloc + * mono_jiterp_isinst + * mono_jiterp_interp_entry + * mono_jiterp_get_polling_required_address + * mono_jiterp_do_safepoint + * mono_jiterp_imethod_to_ftnptr + * mono_jiterp_enum_hasflag + * mono_jiterp_get_simd_intrinsic + * mono_jiterp_get_simd_opcode + * mono_jiterp_get_opcode_info + * mono_jiterp_placeholder_trace + * mono_jiterp_placeholder_jit_call + * mono_jiterp_get_interp_entry_func diff --git a/docs/design/interpreter/transformation.md b/docs/design/interpreter/transformation.md new file mode 100644 index 000000000000..63ee8b109648 --- /dev/null +++ b/docs/design/interpreter/transformation.md @@ -0,0 +1,391 @@ +# Transformation phase +The transformation phase "JITs" the IL code into an IR representation that the execution phase executes later. +This documents lists all functions in the transform.c file in the Mono interpreter codebase. + +# Considerations for coreclr + +There are various cases when a call to runtime helper is needed to perform some operation. This is likely different for Mono (may use a different set of helpers or may not need a helper). So handling such cases will likely need to be added to the transformation and execution phases. Precise details on where and how are beyond the scope of this document. + +# MonoClass, MonoField, MonoMethod and MonoType member access helpers +The following functions are helpers used to access fields in MonoClass, MonoField, MonoMethod and MonoType. They are used all over the place, so they are described here instead of at the specific places they are used. + * m_class_get_byval_arg - N.A. on CoreCLR + * m_class_get_element_class -> getChildType + * m_class_get_image -> getMethodInfo, CORINFO_METHOD_INFO::scope + * m_class_get_name -> getClassNameFromMetadata + * m_class_get_name_space -> getClassNameFromMetadata + * m_class_get_nested_in -> N.A., it is used in interp_handle_intrinsics only to get namespace name for nested classes, getClassNameFromMetadata just works for nested classes too + * m_class_get_parent -> getParentType + * m_class_get_rank -> getArrayRank + * m_class_get_runtime_vtable - N.A. on CoreCLR + * m_class_get_this_arg - N.A. on CoreCLR + * m_class_has_references -> getClassAttribs() & CORINFO_FLG_CONTAINS_GC_PTR + * m_class_has_ref_fields -> getClassGClayout(classHnd) > 0 + * m_class_has_weak_fields - N.A. on CoreCLR + * m_class_is_array -> getClassAttribs() & CORINFO_FLG_ARRAY + * m_class_is_byreflike -> getClassAttribs() & CORINFO_FLG_BYREF_LIKE + * m_class_is_enumtype -> isEnum + * m_class_is_inited - N.A. on CoreCLR + * m_class_is_sealed -> getMethodAttribs() & CORINFO_FLG_FINAL + * m_class_is_simd_type -> getClassNameFromMetadata + * compare namespace to "System.Numerics" and method to "Vector2", "Vector3", "Vector4", "Quaternion", "Plane" + * compare namespace to "System.Runtime.Intrinsics" and method to "Vector64\`1" (Arm64 only), "Vector128\`1", "Vector256\`1" (x64 only) or "Vector512\`1" (x64 only) and then check if getTypeForPrimitiveNumericClass(getTypeInstantiationArgument()) is >= CORINFO_TYPE_BYTE && <= CORINFO_TYPE_DOUBLE. See Compiler::getBaseJitTypeAndSizeOfSIMDType for more details on how CoreCLR JIT does it. + * m_class_is_valuetype -> isValueClass + * m_field_get_offset -> getFieldOffset + * m_field_get_parent -> getFieldClass + * m_field_is_from_update -> getFieldInfo, CORINFO_FIELD_INFO::fieldFlags & CorInfoFlag.CORINFO_FLG_EnC + * m_method_is_static -> getMethodAttribs() & CORINFO_FLG_STATIC + * m_type_is_byref -> asCorInfoType() == CORINFO_TYPE_BYREF + +# Functions that have calls to Mono APIs +For each Mono API or a Mono specific code sequence, it describes how to replace it using JIT2EEInterface methods. In cases when the whole function would be reimplemented in a slightly different way instead of just replacing Mono API calls by their JIT2EEInterface equivalents, a high level description of the function behavior with details on what JIT2EEInterface methods to use is provided. + +### tiered_patcher +* mono_method_signature_internal -> getMethodSig + +### interp_mark_ref_slots_for_var +* Use getClassGClayout + +### interp_mark_ref_slots_for_vt +* Use getClassGClayout without having to drill through the value type fields and their types + +### is_ip_protected +* Use getEHinfo, getMethodInfo, CORINFO_METHOD_INFO::Ehcount + +### handle_stelem +* mono_class_from_mono_type_internal - N.A., just use CORINFO_CLASS_HANDLE +* The intent of this function is to rewrite MINT_STELEM_REF with MINT_STELEM_REF_UNCHECKED if lhs is T[] and rhs is T and T is sealed. JIT2EEInterface::getChildType for T[] returns the T (in clsRet output arg), so we can then match it to the element type + +### initialize_clause_bblocks +* Use getEHinfo, getMethodInfo and then CORINFO_METHOD_INFO::Ehcount + +### interp_handle_box_patterns +* cmethod->klass == mono_defaults.object_class -> cmethod->klass == getBuiltinClass(CLASSID_SYSTEM_OBJECT) +* mono_type_get_object_checked -> getRuntimeTypePointer +* mono_defaults.runtimetype_class -> getBuiltinClass(CLASSID_RUNTIME_TYPE) +* m_class_is_byreflike -> getClassAttribs(clsHnd) & CORINFO_FLG_BYREF_LIKE +* mono_class_is_assignable_from_internal -> compareTypesForCast returns TypeCompareState::Must in this case + +### get_class_from_token +* resolveToken, getTokenTypeAsHandle + +### interp_emit_sfld_access +* Use getFieldInfo to replace all the Mono specific machinery in this function. + * For fields created by EnC -CORINFO_FIELD_INFO::fieldFlags & CORINFO_FLG_EnC + * CORINFO_FIELD_INFO::fieldAccessor to figure out how to access the field + * CORINFO_FIELD_INFO::helper to get the helper needed to access the field (if any) + +### interp_emit_ldsflda +* m_field_get_parent -> getFieldClass +* mono_class_vtable_checked - n.a., use CORINFO_TYPE_CLASS +* mono_class_field_is_special_static - see [interp_emit_sfld_access](#interp_emit_sfld_access) +* mono_special_static_field_get_offset - see [interp_emit_sfld_access](#interp_emit_sfld_access) + +### interp_handle_isinst +* This handles both isinst and castclass +* It has special handling (generates different IR opcodes) for + * Non-generic interfaces + * For non-generic non-arrays that are not nullable +* Generic type can be checked by getTypeInstantiationArgument(clsHnd, 0) returning NO_CLASS_HANDLE +* Array can be checked by getArrayRank returning non-zero or by getClassFlags() & CORINFO_FLG_ARRAY + +### type_has_references +* Use getClassFlags() & CORINFO_FLG_CONTAINS_GC_PTR + +### interp_method_compute_offsets +* Computes offsets of arguments in the interpreter stack and fill in the variable types +* Iterates over arguments, the ported version would iterate like this: + * CORINFO_ARG_LIST_HANDLE arg = 0 + * getArgNext(arg) to get next arg + * getArgType +* Swift specific - get special swift_error argument - implement the same way as Compiler::impPopArgsForSwiftCall +* mono_class_has_failure - N.A., all the classes we get are fully loaded +* mono_error_set_for_class_failure - N.A. +* header->num_clauses -> getMethodInfo, CORINFO_METHOD_INFO::Ehcount + +### mono_interp_type_size +* Use getClassSize + getClassAlignmentRequirement + +### interp_save_debug_info +* This function generates debug info for a specified method. It comprises argument info, local vars info, line numbers and code start and size. +* Use setBoundaries, setVars + +### get_basic_blocks +* Iteration over EH clauses: + * header->num_clauses -> getMethodInfo, CORINFO_METHOD_INFO::Ehcount + * getEHinfo to get details for each EH clause + +### interp_field_from_token +* Use resolveToken, getFieldInfo + +### interp_emit_swiftcall_struct_lowering +* Swift only +* Implement it the way Compiler::impPopArgsForSwiftCall does + +### interp_try_devirt +* Use resolveVirtualMethod + +### get_virt_method_slot +* Use getMethodVTableOffset + +### emit_convert +* mini_get_underlying_type -> asCorInfoType + +### interp_get_method +* Use resolveToken, CORINFO_RESOLVED_TOKEN::hMethod + +### interp_constrained_box +* mono_class_is_nullable -> isNullableType +* mono_class_vtable_checked - N.A, use CORINFO_CLASS_HANDLE in the data for MINT_BOX_PTR. + +### interp_inline_newobj +* Bails out for classes with finalizers (and weak fields which is not something that coreclr would support) +* mono_class_has_finalizer -> getNewHelper, pHasSideEffects output argument is set to true. +* m_class_is_valuetype -> isValueClass +* mono_class_vtable_checked -> N.A., use the CORINFO_CLASS_HANDLE +* interp_method_get_header -> N.A., use the CORINFO_METHOD_HANDLE +* mono_metadata_free_mh - N.A. for coreclr + +### interp_inline_method +* mono_method_signature_internal -> getMethodSig +* mono_method_get_generic_container->context -> CORINFO_SIG_INFO::sigInst +* has_intrinsic_attribute -> isIntrinsic +* target_method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING -> getMethodAttrs & CORINFO_FLG_FORCEINLINE +* MONO_PROFILER_RAISE(inline_method) macro invocation (calls mono_profiler_raise_inline_method) for InterpMethods marked by MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL. + +### interp_method_check_inlining +* Use canInline instead of all the machinery below + +### is_metadata_update_disabled +* Checks for hot reload enabled in general -> there is no equivalent on the Jit2EEInterface + +### interp_get_icall_sig +* Mono supports limited number of signature types for icalls (internal calls). This function gets an enum value describing the specific convention by analyzing the passed in signature. For example MINT_ICALLSIG_P_V represents a void returning function (V) with a single argument that can be passed as pointer sized value (P) like int, boolean, enum, reference, pointer, ... +* The execution part then switches over this when invoking the icall to cast the icall address to a function pointer matching the signature and pass the correct number of arguments to it. +* Use CORINFO_SIG_INFO::numArgs, CORINFO_SIG_INFO::args + +### is_scalar_vtype +* Return whenever TYPE represents a vtype with only one scalar member +* Only used by interp_get_icall_sig +* Implement like Compiler::isTrivialPointerSizedStruct + +### interp_transform_internal_calls +* Looks mono specific - gets a wrapper for calling the internal call - native wrapper or synchronized wrapper +* Might need something like this for QCALLs though. + +### interp_transform_call +* This function figures out the type of the call and emits one of the following IR call instructions: MINT_JIT_CALL, MINT_CALL_DELEGATE, MINT_CALLI_NAT_FAST, MINT_CALLI_NAT_DYNAMIC, MINT_CALLI_NAT, MINT_CALLI, MINT_CALL_VARARG, MINT_CALLVIRT_FAST, MINT_CALL. +* Use getCallInfo as a source of information +* It is possible that for coreclr, the set of call types that we would want to distinguish would be different. +* Here is what it does in high level view: + * Get the signature + * If swift interop is compiled in, do swift lowering, which gets a transformed signature + * Perform access check + * For string ctors, get a different signature (modify the return type to string) + * For intrinsics, call interp_handle_intrinsics and be done + * For constrained_class that is enum and the target method is GetHashCode, replace the method called by a base type one, comment says it is to avoid boxing. + * Further for constrained_class + * get a new target_method based on the constraint + * One more time for intrinsics, call interp_handle_intrinsics and be done + * mono_class_has_dim_conflicts (constrained_class) && mono_class_is_method_ambiguous (constrained_class, virt_method) -> generate throw IR + * Follow the rules for constrained calls from ECMA spec + * For abstract methods generate throw IR + * Handle tail calls + * Try to devirtualize the call + * interp_transform_internal_calls + * Optionally inline the call and be done + * If this is called while inlining a method do some heuristic that may trigger rejection of the inlining + * convert delegate invoke to a indirect call on the interp_invoke_impl field + * Create and align call arguments on the stack, also create call_args array using create_call_args (this represents an array of all call arg vars in the order they are pushed to the stack. This makes it easy to find all source vars for these types of opcodes. This is terminated with -1) + * Allocate interp stack slot for the return value + * For intrinsics, emit the IR instruction that the intrinsic required and set dreg / sregs on the interpreter instruction + * For JIT calls, emit MINT_JIT_CALL + * For delegate calls, emit MINT_CALL_DELEGATE + * For calli + * Handle calls to native code + * Handle icalls emission, use MINT_CALLI_NAT_FAST + * Handle pinvokes in dynamically emitted modules via MINT_CALLI_NAT_DYNAMIC + * Handle other calls to native code via MINT_CALLI_NAT + * Otherwise emit MINT_CALLI + * For other than calli + * If the callee has vararg calling convention, emit MINT_CALL_VARARG + * If it is a virtual method, emit MINT_CALLVIRT_FAST + * Otherwise emit MINT_CALL + * Tiered compilation: For all of the three cases above, if the call is patchable, mark the IR instruction by INTERP_INST_FLAG_RECORD_CALL_PATCH flag and add the IR instruction as a key to patchsite_hash table with the target method as a value + * Alocate some call info data structure attached to the just generated IR instruction + +### mono_interp_transform_method +* This function "compiles" a method into the IR. It has a special handling for MultiCastDelegate invocation methods and internal / runtime calls. +* mono_metadata_update_thread_expose_published +* Return error if the class containing the method is not fully instantiated + * mono_class_is_open_constructed_type -> N.A., open types are never passed over the Jit2EEInterface + * mono_error_set_invalid_operation -> N.A., see ^^^ +* Ensure the class containing the method is initialized + * mono_class_vtable_checked - N.A., use the CORINFO_CLASS_HANDLE instead of vtable + * method_class_vt->initialized, mono_runtime_class_init_full -> initClass +* Get generic context of the method - N.A., open types are never passed over the Jit2EEInterface + * mono_method_signature_internal (method)->is_inflated + * mono_method_get_context + * mono_method_get_generic_container + * generic_container->context +* If method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME) -> internal calls and runtime methods + * This means the method is represented by a native method, the code changes the method to call to a wrapper that invokes the native method + * If the InterpMethod is already marked as transformed, return + * If it is internal call that's not a member of Array + * mono_marshal_get_native_wrapper + * Else + * If it is MultiCastDelegate..ctor + * mono_marshal_get_icall_wrapper(mono_get_jit_icall_info ()->ves_icall_mono_delegate_ctor_interp) + * Else if it is MultiCastDelegate.Invoke + * mono_marshal_get_delegate_invoke (method, NULL); + * Else if it is MultiCastDelegate.BeginInvoke + * mono_marshal_get_delegate_begin_invoke (method); + * Else if it is MultiCastDelegate.EndInvoke + * mono_marshal_get_delegate_end_invoke (method); + * Mark the InterpMethod as transformed +* If the method is marked with UnsafeAccessor attribute + * method = mono_marshal_get_unsafe_accessor_wrapper + * In Jit2EEInterface, the getMethodInfo seems to handle unsafe accessors and some intrinsics +* Call the [generate](#generate) function + +### generate +* This is the main function that translates IL to IR +* First it does some initialization of TransformData structure that holds all the info needed during the transformation +* It calls [generate_code](#generate_code) to generate the IR of the method +* It calls generate_compacted_code to perform some relocations in the IR generated by the previous call +* Finally it fills some InterpMethod fields like EH info, seq points etc. + +### generate_code +* m_class_get_image -> getMethodInfo, CORINFO_METHOD_INFO::scope +* mono_method_signature_internal -> getMethodInfo, CORINFO_METHOD_INFO::args +* mono_basic_block_split + * Generate list of basic blocks from the IL of the method + * We may want to copy this function from Mono +* Get Debug seq points for the method being translated + * Replace all the sequence points extraction by getBoundaries + * mono_debug_lookup_method - lookup symbol information for the method + * mono_debug_lookup_method_async_debug_info - lookup debug info for async method in portable PDB file + * mono_debug_get_seq_points -> getBoundaries + * mono_debug_image_has_debug_info - check if there is a PDB debug file for the assembly containing the method being transformed + * mono_debug_generate_enc_seq_points_without_debug_info - returns true if there is no debug info, so the interpreter should generate seq points as it processes the IL. In that case, the transformation phase inserts MINT_SDB_SEQ_POINT IR opcode on each CEE_NOP, CEE_CALL, CEE_CALVIRT and CEE_CALLI. +* mono_debugger_method_has_breakpoint - check if there is a breakpoint set at method being transformed. + * Interpreter inserts MINT_BREAKPOINT IR opcode at the beginning of the method if there was a breakpoint set +* If verbose_level + * mono_disasm_code + * mono_method_full_name +* mono_trace_eval +* mono_threads_are_safepoints_enabled +* mono_opcode_size +* If verbose_level + * mono_opcode_name +* Switch over all IL codes +* m_class_get_byval_arg(klass) + * This basically just gets MonoType representing the MonoClass. E.g. System.Int32 would get a MonoType with MONO_TYPE_I4. + * The naming comes from the fact that it returns type (MonoType - ~TypeSpec) used to pass an object of the specified class as argument by value, but the usage in the mono interpreter is wider than that. + * Used by + * CEE_LDTOKEN + * CEE_STELEM + * CEE_LDELEM + * CEE_BOX + * CEE_STFLD + * CEE_UNBOX_ANY + * CEE_UNBOX + * CEE_NEWOBJ + * CEE_CPOBJ +* m_class_get_parent == mono_defaults.array_class -> getArrayRank() != 0 ? +* mini_get_class -> resolveToken, CORINFO_RESOLVED_TOKEN::hClass +* mini_type_get_underlying_type -> asCorInfoType + * Used by CEE_RET only +* mono_class_array_element_size -> getChildType, getClassSize +* mono_class_get_and_inflate_typespec_checked -> resolveToken, CORINFO_RESOLVED_TOKEN::hClass +* mono_class_get_flags -> getClassAttribs +* mono_class_get_method_from_name_checked + * Used to get method by name, but it is used in places where we can use other technique to get the result we want + * In CEE_BOX, use getBoxHelper + * In CEE_UNBOX, use getUnboxHelper + * In interp_transform_call, it is used to get GetHashCode method on an enum base type. Use isEnum, the underlyingType output argument. This is not needed for coreclr, the getCallInfo takes care of that. +* mono_class_get_nullable_param_internal -> getTypeInstantiationArgument(clsHandle, 0) +* mono_class_has_dim_conflicts - N.A., handling such stuff is hidden behind the Jit2EEInterface + * It checks if class has conflicting default interface methods + * Used by CEE_LDFTN +* mono_class_has_finalizer -> getNewHelper, pHasSideEffects output argument is set to true. +* mono_class_inflate_generic_type_checked - N.A., generic types are always passed as inflated over the Jit2EEInterface +* mono_class_init_internal - N.A., classes passed over the Jit2EEInterface are always fully loaded +* mono_class_is_assignable_from_internal -> compareTypesForCast + * It is used by CEE_ISINST only to optimize the case when the previous IR was one of the MINT_BOX* +* mono_class_is_method_ambiguous -> N.A., handling such stuff is hidden behind the Jit2EEInterface +* mono_class_is_nullable -> isNullable +* mono_class_native_size - N.A., used by mono specific IL opcodes only +* mono_class_setup_fields - N.A., handling such stuff is hidden behind the Jit2EEInterface +* mono_class_value_size -> getClassSize +* mono_class_vtable_checked - N.A. for coreclr +* mono_error_set_for_class_failure - N.A., classes passed over the Jit2EEInterface are always fully loaded +* mono_error_set_generic_error - error representing System.InvalidProgramException is returned from the generate_code +* mono_error_set_member_access - when attempt to use CEE_NEWOBJ on an abstract class, error with message "Cannot create an abstract class: XYZ" is returned from the generate_code. +* mono_field_get_type_internal -> getFieldInfo, CORINFO_FIELD_INFO::fieldType +* mono_get_method_checked -> resolveToken, CORINFO_RESOLVED_TOKEN::hMethod +* mono_get_method_constrained_with_method + * In interp_transform_call with preceeding CEE_CONSTRAINED -> pass the token from the constrained to getCallInfo as the pConstrainedResolvedToken argument + * In CEE_LDFTN with preceeding CEE_CONSTRAINED -> use getCallInfo with the token from the constrained as the pConstrainedResolvedToken argument +* mono_ldstr_checked -> getStringLiteral +* mono_marshal_get_managed_wrapper - N.A. for coreclr +* mono_metadata_token_index -> constructStringLiteral or getLazyStringLiteralHelper + * Used in CEE_LDSTR +* mono_metadata_token_table - N.A., these details are hidden behind token resolving + * Used in CEE_SIZEOF +* mono_method_can_access_method -> getCallInfo, CORINFO_CALL_INFO::accessAllowed + * Used by CEE_LDFTN +* mono_method_get_context + * It is needed to get the instantiation argument 0 for Span and ReadOnlySpan constructor. + * So we can get it via getTypeInstantiationArgument(clsHandle, 0) +* mono_method_get_wrapper_data - N.A., Mono specific +* mono_method_has_unmanaged_callers_only_attribute -> N.A., it was used only for sanity checks and the the getCallInfo does those internally + * Used by CEE_LDFTN +* mono_mint_type -> asCorInfoType +* mono_trace_eval + * Mono specific, will need to have something similar for profiler for coreclr +* mono_type_size(mono_type_create_from_typespec_checked) -> resolveToken, getClassSize( CORINFO_RESOLVED_TOKEN::hClass) + * Used in CEE_SIZEOF +* mono_type_get_desc -> getClassNameFromMetadata +* mono_type_get_full_name -> getClassNameFromMetadata +* mono_type_get_object_checked -> getRuntimeTypePointer + * gets System.RuntimeType object for a MonoType + * Used by CEE_LDTOKEN + +## Functions that don't use any Mono APIs +These functions might still use glib APIs for memory allocation, bitset, hashtable or linked list. + +* generate_compacted_code +* mono_jiterp_insert_ins +* mono_interp_transform_init +* mono_test_interp_generate_code +* get_native_offset +* interp_squash_initlocals +* interp_fix_localloc_ret +* add_patchpoint_data +* get_var_offset +* get_short_brop +* interp_is_short_offset +* interp_compute_native_offset_estimates +* interp_foreach_ins_var +* interp_foreach_ins_svar +* interp_get_ins_length +* alloc_unopt_global_local +* handle_relocations +* should_insert_seq_point +* handle_ldelem +* handle_stind +* handle_ldind +* interp_emit_load_const +* get_unaligned_opcode +* mono_test_interp_method_compute_offsets +* interp_emit_memory_barrier +* collect_pred_seq_points +* interp_realign_simd_params +* interp_emit_arg_conv +* save_seq_points +* insert_pred_seq_point +* get_bb +* interp_alloc_bb +* create_call_args +* interp_type_as_ptr +* recursively_make_pred_seq_points From 5cb1d3d0bdc900648aedb50fdb42c006a5a12ce8 Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Mon, 2 Dec 2024 18:31:48 +0100 Subject: [PATCH 08/11] Last bunch of functions --- docs/design/interpreter/transformation.md | 65 +++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/docs/design/interpreter/transformation.md b/docs/design/interpreter/transformation.md index 4860670642f5..1820493f5dc2 100644 --- a/docs/design/interpreter/transformation.md +++ b/docs/design/interpreter/transformation.md @@ -110,7 +110,7 @@ For each Mono API or a Mono specific code sequence, it describes how to replace * mono_method_signature_internal -> getMethodSig mono_class_from_mono_type_internal - N.A., just use CORINFO_CLASS_HANDLE * mono_method_signature_internal (td->method)->pinvoke && !mono_method_signature_internal (td->method)->marshalling_disabled -> (getMethodAttribs(...) & CORINFO_FLG_PINVOKE) && pInvokeMarshalingRequired(...) -* Simplify code with 2 following calls to use getClassSize +* Simplify code with 2 following calls to just use getClassSize * mono_class_native_size * mono_class_value_size @@ -136,7 +136,7 @@ For each Mono API or a Mono specific code sequence, it describes how to replace ### interp_generate_ipe_bad_fallthru * mono_disasm_code_one -> N.A. on CoreCLR? - ILCode is available with getMethodInfo - - TODO: check whether JIT does IL disassembly + - we can reuse code from JIT to disassemble IL * Reimplement as part of the error handling replacement ### interp_create_var @@ -151,6 +151,65 @@ For each Mono API or a Mono specific code sequence, it describes how to replace ### mono_interp_print_code * mono_method_full_name -> printMethodName (see more info above in interp_dump_ins_data) +### interp_method_get_header +* This can be removed and replace calls to it with getMethodInfo and use CORINFO_METHOD_INFO fields + +### interp_emit_ldobj +* m_class_get_byval_arg - N.A. on CoreCLR +* mono_class_value_size -> getClassSize + +### interp_emit_stobj +* m_class_get_byval_arg - N.A. on CoreCLR +* m_class_has_references -> getClassAttribs() & CORINFO_FLG_CONTAINS_GC_PTR +* mono_class_value_size -> getClassSize + +### interp_emit_ldelema +* m_class_get_element_class -> getChildType +* m_class_get_rank -> getArrayRank +* mono_class_array_element_size -> getChildType, getClassSize +* m_class_get_byval_arg - N.A. on CoreCLR +* m_class_is_valuetype -> isValueClass + +### interp_emit_metadata_update_ldflda +* m_field_is_from_update -> getFieldInfo, CORINFO_FIELD_INFO::fieldFlags & CorInfoFlag.CORINFO_FLG_EnC +* m_type_is_byref -> asCorInfoType() == CORINFO_TYPE_BYREF +* mono_class_from_mono_type_internal - N.A., just use CORINFO_CLASS_HANDLE +* m_class_get_byval_arg - N.A. on CoreCLR +* mono_metadata_make_token - not needed, we will not use the tokens here anymore +* mono_metadata_update_get_field_idx -> N.A. on CoreClr, we will change the MINT_METADATA_UPDATE_LDFLDA instruction to use helper function instead of the token, CORINFO_HELP_GETFIELDADDR in this case + +### interp_handle_intrinsics +Emits code to handle intrinsics or sets the op output parameter +* m_class_get_byval_arg - N.A. on CoreCLR +* m_class_get_element_class -> getChildType +* m_class_get_image -> getMethodInfo, CORINFO_METHOD_INFO::scope +* m_class_get_name -> getClassNameFromMetadata +* m_class_get_name_space -> getClassNameFromMetadata +* m_class_get_nested_in -> N.A., it is used only to get namespace name for nested classes, getClassNameFromMetadata just works for nested classes too +* m_class_has_references -> getClassAttribs() & CORINFO_FLG_CONTAINS_GC_PTR +* m_class_has_ref_fields -> getClassGClayout(classHnd) > 0 +* m_class_is_enumtype -> isEnum +* m_class_is_valuetype -> isValueClass +* m_field_get_offset -> getFieldOffset +* m_mono_type_internal - N.A., just use CORINFO_CLASS_HANDLE +* m_type_is_byref -> asCorInfoType() == CORINFO_TYPE_BYREF +* mini_get_underlying_type -> asCorInfoType +* mini_is_gsharedvt_variable_klass - N.A. on CoreCLR -> treat as always FALSE +* mini_should_insert_breakpoint - replace by TRUE, always insert breakpoint for S.D.Debugger.Break +* mono_class_array_element_size -> getChildType, getClassSize +* mono_class_from_mono_type_internal - N.A., just use CORINFO_CLASS_HANDLE +* mono_class_get_field_from_name_full -> getFieldInClass and use num instead of name +* mono_class_get_generic_class -> not needed, only used for mini_is_gsharedvt_variable_klass, which will be always FALSE. it is used here to get generic class parameter type +* mono_class_init_internal - N.A., classes passed over the Jit2EEInterface are always fully loaded +* mono_class_is_nullable -> isNullableType +* mono_class_is_subclass_of_internal (target_method->klass, mono_defaults.array_class, FALSE) +* mono_class_value_size -> getClassSize +* mono_field_get_rva -> getFieldInfo and use fieldInfo->fieldLookup.addr +* mono_method_get_context - it is used to get instantiation argument 0 type - use getTypeInstantiationArgument(cls, 0) +* mono_type_get_object_checked -> getRuntimeTypePointer +* mono_type_get_underlying_type -> isEnum - underlyingType parameter or the class itself, when not enum +* mono_type_size -> getClassSize + ### tiered_patcher * mono_method_signature_internal -> getMethodSig @@ -425,7 +484,7 @@ For each Mono API or a Mono specific code sequence, it describes how to replace * mono_class_is_assignable_from_internal -> compareTypesForCast * It is used by CEE_ISINST only to optimize the case when the previous IR was one of the MINT_BOX* * mono_class_is_method_ambiguous -> N.A., handling such stuff is hidden behind the Jit2EEInterface -* mono_class_is_nullable -> isNullable +* mono_class_is_nullable -> isNullableType * mono_class_native_size - N.A., used by mono specific IL opcodes only * mono_class_setup_fields - N.A., handling such stuff is hidden behind the Jit2EEInterface * mono_class_value_size -> getClassSize From 96a9d00253c8f0783571a2084d7ef4dc1d72ba16 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 5 Dec 2024 14:19:59 +0100 Subject: [PATCH 09/11] Add docs on debugger, EH, stack walk, byte code --- docs/design/interpreter/debugger.md | 4 + docs/design/interpreter/exception-handling.md | 18 + docs/design/interpreter/experiment.md | 57 +++ docs/design/interpreter/ir-byte-code.md | 399 ++++++++++++++++++ docs/design/interpreter/stackwalk.md | 25 ++ 5 files changed, 503 insertions(+) create mode 100644 docs/design/interpreter/exception-handling.md create mode 100644 docs/design/interpreter/experiment.md create mode 100644 docs/design/interpreter/ir-byte-code.md create mode 100644 docs/design/interpreter/stackwalk.md diff --git a/docs/design/interpreter/debugger.md b/docs/design/interpreter/debugger.md index b4520f05a0a8..9fffe6c1876e 100644 --- a/docs/design/interpreter/debugger.md +++ b/docs/design/interpreter/debugger.md @@ -1,3 +1,4 @@ +# Debugger integration with the interpreter ## Debugger to interpreter calls ### Mono The interpreter exposes a subset of functions implemented in the interp.c as an interface that Mono runtime calls into. The functions listed below are called by the debugger related code. @@ -40,3 +41,6 @@ There are just two "events" that the interpreter notifies the debugger about: * Single stepping: when MINT_SDB_INTR_LOC IR opcode is executed, Debugger::SendStep will be called. That sends DB_IPCE_STEP_COMPLETE event and the debugger processes it by ICorDebugManagedCallback::StepComplete) * Breakpoint hit: when MINT_SDB_BREAKPOINT IR opcode is executed, Debugger::SendBreakpoint will be called (that sends DB_IPCE_BREAKPOINT and the debugger processes it by calling ICorDebugManagedCallback::Breakpoint) * System.Diagnostics.Debugger.Break(): when MINT_BREAK IR opcode is executed, Debugger::SendRawUserBreakpoint will be called (that sends DB_IPCE_USER_BREAKPOINT and the debugger processes it by calling ICorDebugManagedCallback::Break) + +## WASM debugger event loop +The Mono debugger runs a debugger event loop that receives commands from the debugger in a separate thread. Since WASM is a single threaded environment, the debugger has to use a different mechanism. The Mono interpreter calls mono_component_debugger()->receive_and_process_command_from_debugger_agent() for each MINT_SDB_SEQ_POINT it interprets. \ No newline at end of file diff --git a/docs/design/interpreter/exception-handling.md b/docs/design/interpreter/exception-handling.md new file mode 100644 index 000000000000..de8a19758ddc --- /dev/null +++ b/docs/design/interpreter/exception-handling.md @@ -0,0 +1,18 @@ +# CoreCLR exception handling integration with the interpreter +There are several parts of the exception handling that need to take into consideration presence of interpreter frames on the call stack. +* Stack walking +* Exception clauses enumeration +* Exception handlers invocation +* Resuming execution after a catch handler exits + +The stack walking changes are described in the [stack walking](stackwalk.md) document. +Exception clauses enumeration in CoreCLR is agnostic of the actual low level details of EH clauses information storage. It uses an interface to a JIT manager that does the real work. As described in the stack walking document, a new InterpreterJitManager will be implemented for the interpreted code. +To enumerate the EH clauses, it will use data provided by the InterpMethod::clauses/num_clauses. + +Regarding the exception handlers invocation, CoreCLR uses native helpers CallCatchFunclet, CallFinallyFunclet and CallFilterFunclet. These will need to be modified to recognize interpreted frames and call the related interpreter functions. CallFinallyFunclet would call interp_run_finally and CallFilterFunclet would call the interp_run_filter. As for the CallCatchFunclet, Mono handles calling catch funclets by calling interp_set_resume_state to record the catch handler IR code address and then returning to the mono_interp_exec_method that checks for this stored state and redirects execution to the catch handler if it is set. In CoreCLR, we may want to handle it differently, in a manner close to how interp_run_finally works. + +Resuming execution in the parent of a catch handler after the catch handler exits depends on whether the catch handler is in an interpreted code or in compiled managed code. +For the compiled code case, the existing CoreCLR EH code will handle it without changes. It would just restore the context at the resume location. +For the interpreted code though, CoreCLR will resume execution in the mono_interp_exec_method, then pop some InterpFrame instances from the local linked list (depending on how many interpreted managed frames need to be removed) and restore the interpreter SP from the last popped one and the interpreter IP will be set to the resume location IP. + +WASM doesn't support stack unwinding and context manipulation, so the resuming mechanism cannot use context restoring. Mono currently returns from the mono_handle_exception after the catch is executed back to the interp_throw. When the resume frame is in the interpreted frames belonging to the current mono_interp_exec_method, it uses the mechanism described in the previous paragraph to "restore" to the resume context. But when the resume frame is above all the frames belonging to the current mono_interp_exec_method, it exits from the mono_interp_exec_method and then throws a C++ exception (of int32 * type set to NULL) that will propagate through native frames until it is caught in a compiled managed code or in another interpreter function up the call chain (see usage of mono_llvm_catch_exception) where the propagation through the interpreted frames continues the same way as in the previous interpreted frames block. diff --git a/docs/design/interpreter/experiment.md b/docs/design/interpreter/experiment.md new file mode 100644 index 000000000000..92faf15fec15 --- /dev/null +++ b/docs/design/interpreter/experiment.md @@ -0,0 +1,57 @@ +# Experiment: Evaluate the Mono interpreter +The goal of this experiment is to evaluate usability of the Mono interpreter as a standalone component that can be used for CoreCLR, NativeAOT and possibly others. Its goal is to give answers to the following questions: +* Is the existing byte code the interpreter uses as an internal representation reasonable? Should it be changed in any way? +* What should be the interface between the interpreter, a runtime, a GC and the debugger? +* How do we abstract the runtime specific dependencies? +* What frontend should be used for coreclr? +* What executor should be used for coreclr? +* How should transitions between the interpreter and runtime / AOT code work? + +The expected outcome of the experiment is a set of documents, no coding will be done. +The documentation should also allow us to estimate the amount of work needed to move the interpreter to the recommended state. + +## Basic structure of the Mono interpreter +### Transformation phase +This phase converts IL into an intermediate representation. Most of the IR byte codes directly match the IL opcodes. The main difference is in how arguments are referenced. On contrary to IL where the IL opcodes use implicitly last N pushed values on the stack as arguments, the IR explicitly contains values or references to local variables, arguments, etc. +Besides a trivial translation of IL opcodes to matching IR byte codes, the transformation phase also performs some basic constant folding. For example, ldc.1 followed by add is transformed into MINT_ADD1_I4. Some IL opcodes are converted to specialized variants of IR opcodes based on what's on the stack. So for example CEE_CONV_U1 can become MINT_CONV_U1_R4, MINT_CONV_U1_R8, MINT_CONV_U1_I4 or MINT_CONV_U1_I8. + +The transformation phase can also inline IR of a callee into a caller. + +When running with debugger connected, MINT_SDB_SEQ_POINT IR instruction is injected at every sequence point. That instruction is dummy, but the debugger can overwrite it by MINT_SDB_BREAKPOINT instruction when it needs to set a breakpoint in the interpreted code. + +When optimizations are turned on, additional more complex optimizations are performed. SSA is used during the process. The optimizations that can be performed are as follows: + + * Removing stores to vars that are not read after + * Constant folding + * Binary ops with constants only + * Unary ops with constants + * ldc + ret -> ret.imm + * Ldc + add / mul / or / and -> ins.imm + * ldc + sub -> add.-imm - what is the benefit of saving one instr here? + * ldc + sh -> sh.imm + * ldc + div.un -> shr.imm + * MINT_LDIND_I1 + MINT_ADD_P -> MINT_LDIND_OFFSET_I1 + * Similar with STIND + * MINT_LDIND_I1 + MINT_ADD_P_IMM -> MINT_LDIND_OFFSET_IMM_I1 + * MINT_LDIND_OFFSET_I1 + MINT_MUL_P_IMM / MINT_ADD_P_IMM / MINT_ADD_MUL_P_IMM -> MINT_LDIND_OFFSET_ADD_MUL_IMM_I1 + * cknull + ldfld -> ldfld + * MINT_SAFEPOINT folding into some instructions, forming _SP variants + * MINT_LDOBJ_VT + MINT_STOBJ_VT_NOREF -> MINT_CPOBJ_VT_NOREF + * SIMD intrinsics + +### Execution phase +The execution phase executes the IR bytecode generated by the transformation phase. It is essentially a loop with a giant switch statement with cases for all the IR opcodes. Code for each case extracts values of arguments of the specific IR instruction from the byte code and the local variables / per method data items tables and then it performs the specific operation. Some of them, like arithmetic operations or branches are trivial, resulting in execution of only a couple of machine code instructions, some of them like calls are quite complex. +There is also support for tiering when a frequently executed method with non-optimized IR can be transformed again with optimizations turned on. + +## Analysis results and plans for CoreCLR +The following documents describe the mapping of the IL opcodes to the IR opcodes, the Mono APIs used by the transformation and execution phases and how the debugger cooperates with the interpreter. They also describe the way these should work in CoreCLR. +* [IR byte code](ir-byte-code.md) +* TODO: Interpreter Mono runtime dependencies (Vlad - commit the gist he has initially created) +* [Transformation phase analysis](transformation.md) +* [Execution phase analysis](execution.md) +* [Debugger integration](debugger.md) +* [Stack walking for GC, exception handling and debugger](stackwalk.md) +* [Precise GC in the interpreter](precise-gc.md) +* [Interop of the interpreter with compiled managed code](compiled-code-interop.md) +* [CoreCLR exception handling integration with the interpreter](exception-handling.md) +* TODO: link doc on Calls, delegates, function pointers (Vlad) diff --git a/docs/design/interpreter/ir-byte-code.md b/docs/design/interpreter/ir-byte-code.md new file mode 100644 index 000000000000..55f5425fb9bc --- /dev/null +++ b/docs/design/interpreter/ir-byte-code.md @@ -0,0 +1,399 @@ +# The IR byte code +## Introduction + * There are 677 byte codes vs 263 IL opcodes + * Some of them are handling intrinsics (includes also stuff like String.Length, System.Diagnostics.Debugger.Break) + * Each opcode is directly followed by arguments that have variable size depending on the opcode. + * Both the transformation and interpretation use Mono type / method / vtable etc data structures + * The arguments to the opcode are immediate constants (16, 32 or 64 bit), indexes into local variables table or indexes into a per-method special data items array. +## Limitations + * The local variable offsets in most of the IR codes are 16 bit unsigned integers, so a method can have max 65536 kB of local variables + * The indices into the per-method special data items array in most of the IR codes are also 16 bit unsigned integers, thus a method can have max 65536 of these. + +## IR codes corresponding to IL opcodes +This section describes mapping of IL opcodes to IR opcodes and their arguments. The naming convention of the arguments is as follows: + * "_local" suffix represents a local variable offset in the local variables area + * "_index" suffix represents index into the per-method data items table + * The rest are just immediate values +To eliminate repeating, the IL / IR opcodes can have list of strings specified in curly braces. These mean that there are multiple opcodes each having have one of the strings from the list. For example, CEE_LDC_{I4|I8|R4|R8} represents CEE_LDC_I4, CEE_LDC_I8, CEE_LDC_R4 and CEE_LDC_R8. + +### CEE_BREAK, System.Diagnostics.Debugger.Break intrinsic + * MINT_BREAK() +### CEE_LDC_{I4|I8|R4|R8} + * MINT_LDC_I4_0(u16 target_local) + * MINT_LDC_I4_1(u16 target_local local) + * MINT_LDC_I4_S(u16 target_local local, i16 value) + * MINT_LDC_I4(u16 target_local local, i32 value) + * MINT_LDC_I8_0(u16 target_local local) + * MINT_LDC_I8_S(u16 target_local local, i16 value) + * MINT_LDC_I8(u16 local, i64 value) + * MINT_LDC_R4(u16 local, float32 value) + * MINT_LDC_R8(u16 local, float64 value) + Calls with CEE_TAIL prefix + * MINT_TAILCALL(u16 params_offset, u16 method, u16 params_size) + * MINT_TAILCALL_VIRT(u16 params_offset, u16 method_index, u16 params_size, u16 slot) +### CEE_JMP + * MINT_JMP(u16 method_index) +### CEE_CALL, CEE_CALLI, CEE_CALLVIRT + * MINT_CALL_DELEGATE(u16 return_offset, u16 call_args_offset, u16 call_args_count, u16 param_count, u16 call_args_offset) + * MINT_CALLI(u16 return_offset, u16 fn_ptr_local, u16 call_args_offset) + * MINT_CALLI_NAT_FAST(u16 return_offset, u16 fn_ptr_local, u16 args_local, u16 icall_sig, u16 signature_index, bool save_last_error) + * MINT_CALLI_NAT_DYNAMIC(u16 return_offset, u16 fn_ptr_local, u16 call_args_local, u16 signature_index) + * MINT_CALLI_NAT(u16 return_offset, u16 fn_ptr_local, u16 call_args_local, u16 signature_index , u16 imethod_index, bool save_last_error, u16 cache_index) + * MINT_CALLVIRT_FAST(u16 return_offset, u16 call_args_local, u16 imethod_index, int16 slot) + * MINT_CALL_VARARG(u16 return_offset, u16 call_args_local, u16 imethod_index) + * MINT_CALL(u16 return_offset, u16 call_args_local, u16 imethod_index) + * MINT_ICALL(u16 return_offset, u16 call_args_local, u16 signature_index, u16 target_ip_index) + * MINT_JIT_CALL(u16 return_offset, u16 call_args_local, u16 imethod_index) + * MINT_JIT_CALL2 - not sure, somehow used by tiered compilation +### CEE_RET + * MINT_RET(u16 return_local) + * MINT_RET_LOCALLOC(u16 return_local) + * MINT_RET_I1(u16 return_local) + * MINT_RET_I2(u16 return_local) + * MINT_RET_U1(u16 return_local) + * MINT_RET_U2(u16 return_local) + * MINT_RET_I4_IMM(int16 return_value) + * MINT_RET_I8_IMM(int16 return_value) + * MINT_RET_VOID() + * MINT_RET_VOID_LOCALLOC() + * MINT_RET_VT(u16 return_local, u16 size) + * MINT_RET_VT_LOCALLOC(u16 return_local, u16 size) +### CEE_BR_S, CEE_BR + * MINT_BR_S(int16 relative_offset) + * MINT_BR(i32 relative_offset) +### CEE_BRTRUE_S, CEE_BRTRUE + * MINT_BR{TRUE|FALSE}_I{4|8}_S(u16 bool_local, int16 offset) + * MINT_BR{TRUE|FALSE}_I{4|8}_SP(u16 bool_local, int16 offset) + * MINT_BR{TRUE|FALSE}_I{4|8}(u16 bool_local, i32 offset) +They get folded to MINT_BR/NOP if the operand is constant +### CEE_{BEQ|BGE|BGT|BLT|BLE|BNE_UN|BGE_UN|BGT_UN|BLE_UN|BLT_UN}_S + * MINT_{BEQ|BGE|BGT|BLT|BLE|BNE_UN|BGE_UN|BGT_UN|BLE_UN|BLT_UN}_{I4|I8|R4|R8}_S(u16 left_local, u16 right_local, i16 offset) +### CEE_{BEQ|BGE|BGT|BLT|BLE|BNE_UN|BGE_UN|BGT_UN|BLE_UN|BLT_UN} + * MINT_{BEQ|BGE|BGT|BLT|BLE|BNE_UN|BGE_UN|BGT_UN|BLE_UN|BLT_UN}_{I4|I8|R4|R8}(u16 left_local, u16 right_local, i32 offset) + * MINT_{BEQ|BGE|BGT|BLT|BLE|BNE_UN|BGE_UN|BGT_UN|BLE_UN|BLT_UN}_{I4|I8}_SP(u16 left_local, u16 right_local, i16 offset) + * MINT_{BEQ|BGE|BGT|BLT|BLE|BNE_UN|BGE_UN|BGT_UN|BLE_UN|BLT_UN}_{I4|I8}_IMM_SP(u16 left_local, u16 right_immediate, i16 offset) +These CEE_BXX can prepend MINT_CONV_I8_I4 or MINT_CONV_R8_R4 +They get folded to MINT_BR/NOP if both operands are constant +### CEE_CEQ + * MINT_CEQ0_I4(u16 target_local, u16 source_local) +### CEE_{CEQ|CNE|CGT|CGE|CGT_UN|CGE_UN|CLT|CLT_UN|CLE} + * MINT_{CEQ|CNE|CGT|CGE|CGT_UN|CGE_UN|CLT|CLT_UN|CLE}_{I4|I8|R4|R8}(u16 target_local, u16 source_left_local, u16 source_right_local) +### CEE_CEQ can prepend MINT_CONV_R8_R4 +### CEE_SWITCH + * MINT_SWITCH(u16 source_local, u32 count, {i32 offset}[count]) +### CEE_LDIND_{I1|U1|I2|U2|I4|I8|R4|R8|REF} + * MINT_LDIND_{I1|U1|I2|U2|I4|I8|R4|R8}(u16 target_local, u16 pointer_local) + * MINT_LDIND_OFFSET_{I1|U1|I2|U2|I4|I8|R4|R8}(u16 target_local, u16 pointer_local, u16 offset_local) + * MINT_LDIND_OFFSET_ADD_MUL_IMM_{I1|U1|I2|U2|I4|I8}(u16 target_local, u16 pointer_local, u16 offset_local, i16 offset_immediate, i16 offset_multiplier_immediate) + * offset= (local[offset_local}+offset_immediate)*offset_multiplier_immediate + * MINT_LDIND_OFFSET_IMM_{I1|U1|I2|U2|I4|I8}(u16 target_local, u16 pointer_local, i16 offset_immediate) +### CEE_STIND_REF + * MINT_STIND_REF(u16 pointer_local, u16 object_local) +### CEE_STIND_{I1|I2|I4|I8|R4|R8} + * MINT_STIND_{I1|I2|I4|I8|R4|R8}(u16 pointer_local, u16 value_local) + * MINT_STIND_OFFSET_{I1|I2|I4|I8}(u16 pointer_local, u16 offset_local, u16 value_local) +### CEE_{ADD|SUB|MUL|DIV|REM} + * MINT_{ADD|SUB|MUL|DIV|REM}_{I4|I8|R4|R8}(u16 target_local, u16 source_left_local, u16 source_right_local) + * MINT_{ADD|SUB|MUL}_{I4|I8|R4|R8}_IMM(u16 target_local, u16 source_local, i16 immediate) + * MINT_{ADD|SUB|MUL}_{I4|I8|R4|R8}_IMM2(u16 target_local, u16 source_local, i32 immediate) + * MINT_{AND|OR|XOR}_{I4|I8}(u16 target_local, u16 source_left_local, u16 source_right_local) + * MINT_{AND|OR}_I4_IMM(u16 target_local, u16 source_local, i16 immediate) + * MINT_{AND|OR}_I4_IMM2(u16 target_local, u16 source_local, i32 immediate) + * MINT_NEG_{I4|I8|R4|R8}(u16 target_local, u16 source_local) + * MINT_NOT_{I4|I8}(u16 target_local, u16 source_local) + * MINT_{SHL|SHR|SHR_UN|ROL|ROR}_{I4|I8}(u16 target_local, u16 source_local, u16 shift_local) + * MINT_{SHL|SHR|SHR_UN|ROL|ROR}_{I4|I8}_IMM(u16 target_local, u16 source_local, u16 shift) + * MINT_SHL_AND_{I4|I8}(u16 target_local, u16 source_local, u16 shift_local) +### CEE_{DIV|REM}_UN + * MINT_{DIV|REM}_UN_{I4|I8}(u16 target_local, u16 source_left_local, u16 source_right_local) +### CEE_{ADD|SUB|MUL}_OVF + * MINT_{ADD|SUB|MUL}_OVF_{I4|I8}(u16 target_local, u16 source_left_local, u16 source_right_local) +### CEE_{ADD|SUB|MUL}_OVF_UN + * MINT_{ADD|SUB|MUL}_OVF_UN_{I4|I8}(u16 target_local, u16 source_left_local, u16 source_right_local) + * MINT_{ADD|SUB}1_{I4|I8}(u16 target_local, u16 source_left_local, u16 source_right_local) + * MINT_ADD_MUL_{I4|I8}_IMM(u16 target_local, u16 source_local, i16 immediate_offset, i16 immediate_multiplier) +### CEE_CONV_{I,U,I1|U1|I2|U2|I4|U4|I8|U8} + * MINT_CONV_{I1|U1|I2|U2|I8}_{I4|I8|R4|R8}(u16 target_local, u16 source_local) + * MINT_CONV_{I4|U4|U8}_{R4|R8}(u16 target_local, u16 source_local) + * MINT_CONV_I8_U4(u16 target_local, u16 source_local) +### CEE_CONV_{R4|R8} + * MINT_CONV_{R4}_{I4|I8|R8}(u16 target_local, u16 source_local) + * MINT_CONV_{R8}_{I4|I8|R4}(u16 target_local, u16 source_local) +### CEE_CONV_R_UN + * MINT_CONV_R_UN_{I4|I8}(u16 target_local, u16 source_local) +### CEE_CONV_OVF_{I,U,I1|U1|I2|U2|I4|U4|I8|U8}, CEE_CONV_OVF_{I,U,I1|U1|I2|U2|I4|U4|I8|U8}_UN + * MINT_CONV_OVF_U8_{I4|I8|R4|R8}(u16 target_local, u16 source_local) + * MINT_CONV_OVF_I8_{U8|R4|R8}(u16 target_local, u16 source_local) + * MINT_CONV_OVF_I4_{U4|I8|U8|R4|R8}(u16 target_local, u16 source_local) + * MINT_CONV_OVF_U4_{I4|I8|R4|R8}(u16 target_local, u16 source_local) + * MINT_CONV_OVF_I1_{I4|U4|I8|U8|R4|R8}(u16 target_local, u16 source_local) + * MINT_CONV_OVF_I2_{I4|U4|I8|U8|R4|R8}(u16 target_local, u16 source_local) + * MINT_CONV_OVF_U1_{I4|I8|R4|R8}(u16 target_local, u16 source_local) + * MINT_CONV_OVF_U2_{I4|I8|R4|R8}(u16 target_local, u16 source_local) +These conversion IRs are also generated in cases the transform decides it needs to add a conversion, i.e. for CEE_Bxx. +### CEE_CPOBJ + * MINT_CPOBJ(u16 target_local, u16 source_local, u16 class_index) + * MINT_CPOBJ_VT(u16 target_local, u16 source_local, u16 class_index) + * MINT_CPOBJ_VT_NOREF(u16 target_local, u16 source_local, u16 size) +### CEE_CPOBJ gets expanded into MINT_LDIND_I + MINT_STIND_REF instead for reference types +### CEE_LDOBJ + * MINT_LDOBJ_VT(u16 target_local, u16 source_address_local, u16 size) +It can get translated to MINT_LDIND for integer types +### CEE_LDSTR + * MINT_LDSTR(u16 target_local, u16 string_index) + * MINT_LDSTR_DYNAMIC(u16 target_local, u16 strtoken_index) + * MINT_LDSTR_CSTR(u16 target_local, u16 cstr_index) +### CEE_LDTOKEN + * MINT_LDPTR(u16 target_local, u16 ptr_index) +### CEE_MONO_JIT_ICALL_ADDR + * MINT_LDFTN_ADDR(u16 target_local, u16 ftn_addr_index) +### CEE_LDFTN + * MINT_LDFTN(u16 target_local, u16 interpmethod_index) +### CEE_LDVIRTFTN + * MINT_LDVIRTFTN(u16 target_local, u16 object_local, u16 interpmethod_index) +### CEE_MONO_LD_DELEGATE_METHOD_PTR + * MINT_LD_DELEGATE_METHOD_PTR(u16 target_local, u16 delegate_local) +### CEE_LDARGA, CEE_LDARGA_S, CEE_LDLOCA, CEE_LDLOCA_S + * MINT_LDLOCA_S(u16 target_local, u16 source_local) +### CEE_LDELEMA + * MINT_LDELEMA1(u16 target_local, u16 array_local, u16 index_local, u16 element_size) + * MINT_LDELEMA(u16 target_local, u16 array_local, u16 rank, u16 element_size) + * MINT_LDELEMA_TC(u16 target_local, u16 array_local, u16 class_index) +### CEE_LDELEM_{I1|U1|I2|U2|I4|U4|I8|R4|R8|REF}, CEE_LDELEM + * MINT_LDELEM_{I1|U1|I2|U2|I4|U4|I8|R4|R8|REF}(u16 target_local, u16 array_local, u16 index_local) + * MINT_LDELEM_VT(u16 target_local, u16 array_local, u16 index_local, u16 element_size) +### CEE_STELEM_{I1|U1|I2|U2|I4|I8|R4|R8|REF}, CEE_STELEM + * MINT_STELEM_{I1|U1|I2|U2|I4|I8|R4|R8|REF|REF_UNCHECKED }(u16 array_local, u16 index_local, u16 source_local) + * MINT_STELEM_{ VT|VT_NOREF}(u16 array_local, u16 index_local, u16 source_local, u16 class_index, u16 element_size) +### CEE_LDLEN + * MINT_LDLEN(u16 target_local, u16 source_local) +### CEE_LOCALLOC + * MINT_LOCALLOC(u16 target_local, u16 size_local) +### CEE_NEWOBJ and other locations not directly related to specific CEE_xxx + * MINT_NEWOBJ_ARRAY(u16 target_local, u16 first_param_local, u32 token, u16 param_count) + * MINT_NEWOBJ_STRING(???) + * MINT_NEWOBJ_STRING_UNOPT(???) + * MINT_NEWOBJ(???) + * MINT_NEWOBJ_INLINED(u16 target_local, u16 vtable_index) + * MINT_NEWOBJ_VT(???) + * MINT_NEWOBJ_SLOW(???) +### CEE_NEWARR + * MINT_NEWARR(u16 target_local, u16 size_local, u16 vtable_index) +### CEE_CASTCLASS + * MINT_CASTCLASS_INTERFACE(u16 target_local, u16 source_local, u16 class_index) + * MINT_CASTCLASS_COMMON(u16 target_local, u16 source_local, u16 class_index) + * MINT_CASTCLASS(u16 target_local, u16 source_local, u16 class_index) +### CEE_ISINST + * MINT_ISINST_INTERFACE(u16 target_local, u16 source_local, u16 class_index) + * MINT_ISINST_COMMON(u16 target_local, u16 source_local, u16 class_index) + * MINT_ISINST(u16 target_local, u16 source_local, u16 class_index) +### CEE_BOX + * MINT_BOX(u16 target_local, u16 source_local, u16 vtable_index) + * MINT_BOX_VT(u16 target_local, u16 source_local, u16 vtable_index) + * MINT_BOX_PTR(u16 target_local, u16 source_local, u16 vtable_index) + * MINT_BOX_NULLABLE_PTR(u16 target_local, u16 source_local, u16 vtable_index) +### CEE_UNBOX, CEE_UNBOX_ANY + * MINT_UNBOX(u16 target_local, u16 source_local, u16 class_index) +### CEE_THROW + * MINT_THROW(u16 exception_local) +### CEE_LDFLDA + * MINT_LDFLDA_UNSAFE(u16 target_local, u16 source_local, u16 source_offset) + * MINT_LDFLDA(u16 target_local, u16 source_local, u16 source_offset) +### CEE_LDFLD + * MINT_LDFLD_{I1|U1|I2|U2|I4|I8|R4|R8|O|I8_UNALIGNED|R8_UNALIGNED}(u16 target_local, u16 source_local, u16 source_offset) + * MINT_LDFLD_VT(u16 target_local, u16 source_local, u16 source_offset, u16 size) +### CEE_LDSFLD + * MINT_LDSFLD_{I1|U1|I2|U2|I4|I8|R4|R8|O}(u16 target_local, u16 class_index, u16 source_index) + * MINT_LDSFLD_VT(u16 target_local, u16 vtable_index, u16 source_index, u16 size) + * MINT_LDSFLD_W(u16 target_local, u32 vtable_index, u32 source_index, u32 class_index) +### CEE_STFLD + * MINT_STFLD_{I1|U1|I2|U2|I4|I8|R4|R8|O|I8_UNALIGNED|R8_UNALIGNED}(u16 target_object_local, u16 source_local, u16 source_offset) + * MINT_STFLD_VT_NOREF(u16 target_object_local, u16 source_local, u16 source_offset, u16 size) + * MINT_STFLD_VT(u16 target_object_local, u16 source_local, u16 source_offset, u16 class_index) +### CEE_STSFLD + * MINT_STSFLD_{I1|U1|I2|U2|I4|I8|R4|R8|O}( u16 source_local, u16 vtable_index, u16 target_index) + * MINT_STFLD_VT( u16 source_local, u16 vtable_index, u16 target_index, u16 size) + * MINT_STFLD_W( u16 source_local, u32 vtable_index, u32 target_index, u32 class_index) +### CEE_LDSFLDA + * MINT_LDSFLDA(u16 target_local, u16 vtable_index, u16 source_index) + * MINT_LDTSFLDA(u16 target_local, u32 tls_offset) +### CEE_STOBJ + * MINT_STOBJ_VT(u16 target_local, u16 source_local, u16 class_index) + * MINT_STOBJ_VT_NOREF(u16 target_local, u16 source_local, u16 size) +### CEE_CKFINITE + * MINT_CKFINITE_{R4|R8}(u16 target_local, u16 source_local) +### CEE_MKREFANY + * MINT_MKREFANY(u16 target_local, u16 source_addr_local, u16 class_index) +### CEE_REFANYTYPE + * MINT_REFANYTYPE(u16 target_local, u16 source_local) +### CEE_REFANYVAL + * MINT_REFANYVAL(u16 target_local, u16 source_addr_local, u16 class_index) +### CEE_ENDFINALLY + * MINT_ENDFINALLY(u16 clause) +### CEE_ENDFILTER + * MINT_ENDFILTER(u16 filter_result_local) +### CEE_LEAVE, CEE_LEAVE_S + * MINT_CALL_HANDLER(u32 offset, u16 clause) + * MINT_CALL_HANDLER_S(u16 offset, u16 clause) + * MINT_LEAVE_CHECK(u32 offset) + * MINT_LEAVE_S_CHECK(u16 offset) +### CEE_RETHROW + * MINT_RETHROW(u16 exception_object_stack_offset) +### CEE_MONO_RETHROW + * MINT_MONO_RETHROW(u16 exception_object _local) + CEE_INITOBJ + * MINT_ZEROBLK_IMM(u16 target_local, u16 size) +### CEE_CPBLK + * MINT_CPBLK(u16 target_local, u16 source_local, u16 size_local) +### CEE_INITBLK + * MINT_INITBLK(u16 target_local, u16 fill_local, u16 size_local) +### CEE_xxx float math functions 1:1 + * MINT_ASINH(u16 target_local, u16 source_local) + * MINT_ACOS(u16 target_local, u16 source_local) + * MINT_ACOSH(u16 target_local, u16 source_local) + * MINT_ASIN(u16 target_local, u16 source_local) + * MINT_ATAN(u16 target_local, u16 source_local) + * MINT_ATANH(u16 target_local, u16 source_local) + * MINT_CEILING(u16 target_local, u16 source_local) + * MINT_COS(u16 target_local, u16 source_local) + * MINT_CBRT(u16 target_local, u16 source_local) + * MINT_COSH(u16 target_local, u16 source_local) + * MINT_EXP(u16 target_local, u16 source_local) + * MINT_FLOOR(u16 target_local, u16 source_local) + * MINT_LOG(u16 target_local, u16 source_local) + * MINT_LOG2(u16 target_local, u16 source_local) + * MINT_LOG10(u16 target_local, u16 source_local) + * MINT_SIN(u16 target_local, u16 source_local) + * MINT_SQRT(u16 target_local, u16 source_local) + * MINT_SINH(u16 target_local, u16 source_local) + * MINT_TAN(u16 target_local, u16 source_local) + * MINT_TANH(u16 target_local, u16 source_local) + * MINT_ABS(u16 target_local, u16 source_local) + * MINT_ATAN2(u16 target_local, u16 source_local) + * MINT_POW(u16 target_local, u16 base_local, u16 exponent_local) + * MINT_MIN(u16 target_local, u16 source_1_local, u16 source_2_local) + * MINT_MAX(u16 target_local, u16 source_1_local, u16 source_2_local) + * MINT_FMA(u16 target_local, u16 addend_1_local, u16 addend_2_local, u16 multiplicant_local) + * MINT_SCALEB(u16 target_local, u16 argument_local, u16 exponent_local) + * MINT_ASINF(u16 target_local, u16 source_local) + * MINT_ASINHF(u16 target_local, u16 source_local) + * MINT_ACOSF(u16 target_local, u16 source_local) + * MINT_ACOSHF(u16 target_local, u16 source_local) + * MINT_ATANF(u16 target_local, u16 source_local) + * MINT_ATANHF(u16 target_local, u16 source_local) + * MINT_CEILINGF(u16 target_local, u16 source_local) + * MINT_COSF(u16 target_local, u16 source_local) + * MINT_CBRTF(u16 target_local, u16 source_local) + * MINT_COSHF(u16 target_local, u16 source_local) + * MINT_EXPF(u16 target_local, u16 source_local) + * MINT_FLOORF(u16 target_local, u16 source_local) + * MINT_LOGF(u16 target_local, u16 source_local) + * MINT_LOG2F(u16 target_local, u16 source_local) + * MINT_LOG10F(u16 target_local, u16 source_local) + * MINT_SINF(u16 target_local, u16 source_local) + * MINT_SQRTF(u16 target_local, u16 source_local) + * MINT_SINHF(u16 target_local, u16 source_local) + * MINT_TANF(u16 target_local, u16 source_local) + * MINT_TANHF(u16 target_local, u16 source_local) + * MINT_ABSF(u16 target_local, u16 source_local) + * MINT_ATAN2F(u16 target_local, u16 source_local) + * MINT_POWF(u16 target_local, u16 base_local, u16 exponent_local) + * MINT_MINF(u16 target_local, u16 source_1_local, u16 source_2_local) + * MINT_MAXF(u16 target_local, u16 source_1_local, u16 source_2_local) + * MINT_FMAF(u16 target_local, u16 addend_1_local, u16 addend_2_local, u16 multiplicant_local) + * MINT_SCALEBF(u16 target_local, u16 argument_local, u16 exponent_local) +### CEE_MONO_NEWOBJ + * MINT_MONO_NEWOBJ(u16 target_local, u16 monoclass_index) +### CEE_MONO_RETOBJ + * MINT_MONO_RETOBJ(u16 return_local) +Various CEE_xxx with CEE_VOLATILE_ prefix, CEE_MONO_MEMORY_BARRIER + * MINT_MONO_MEMORY_BARRIER() +### CEE_MONO_LDDOMAIN + * MINT_MONO_LDDOMAIN(u16 domain_local) +### CEE_MONO_GET_SP + * MINT_MONO_ENABLE_GCTRANS() +## IR codes not directly corresponding to IL opcodes + +### Start of methods + * MINT_INITLOCAL(u16 offset, u16 count) + * MINT_INITLOCALS(u16 offset, u16 count) +### System.Threading.Interlocked.CompareExchange intrinsics + * MINT_MONO_CMPXCHG_{U1|I1|U2|I2|I4|I8}(u16 result_local, u16 dest_local, u16 value_local, u16 comparand_local) +### System.Threading.Interlocked.Exchange intrinsics + * MINT_MONO_EXCHANGE_{U1|I1|U2|I2|I4|I8}(u16 result_local, u16 dest_local, u16 source_local) +### Vararg calls + * MINT_INIT_ARGLIST(u16 argListLocal, u16 size) +### Various argument moves on stack when needed + * MINT_MOV_STACK_UNOPT(u16 src_offset, u16 dst_to_src_offset, u16 size) +### System.RuntimeMethodHandle.GetFunctionPointer intrinsic + * MINT_LDFTN_DYNAMIC(u16 target_local, u16 method_local) +### System.SpanHelpers.ClearWithoutReferences intrinsic + * MINT_ZEROBLK(u16 target_local, u16 size_local) +### Throw if NULL + * MINT_CKNULL(u16 target_local, u16 source_local) +### Mark GC safe point + * MINT_SAFEPOINT() +### Local variables moves + * MINT_MOV_I4_{I1|U1|I2|U2}(u16 target_local, u16 source_local) + * MINT_MOV_{1|2|4|8}(u16 target_local, u16 source_local) + * MINT_MOV_8_{2|3|4}(u16 target_1_local, u16 source_1_local, u16 target_2_local, u16 source_2_local) + * MINT_MOV_VT(u16 target_local, u16 source_local, u16 size) +### Hot reload + * MINT_METADATA_UPDATE_LDFLDA(u16 target_local, u16 source_local, u16 type_index, u16 fielddef_token_index) +### JITerpreter + * MINT_TIER_NOP_JITERPRETER(???) + * MINT_TIER_PREPARE_JITERPRETER(???) + * MINT_TIER_MONITOR_JITERPRETER(???) + * MINT_TIER_ENTER_JITERPRETER(???) +### System.Array.Rank intrinsic + * MINT_ARRAY_RANK(u16 target_local, u16 array_local) +### System.Array.GetElementSize intrinsic + * MINT_ARRAY_ELEMENT_SIZE(u16 target_local, u16 array_local) +### System.String.FastAllocateString intrinsic + * MINT_NEWSTR(u16 target_local, u16 size_local) +### System.Numerics.BitOperations.xxx intrinsics + * MINT_{CLZ|CTZ|POPCNT|LOG2}_{I4|I8}(u16 target_local, u16 size_local) +### SIMD + * MINT_SIMD_V128_LDC(u16 target_local, v128 value) + * MINT_SIMD_V128_I1_CREATE(u16 target_local, u16 source_local) + * MINT_SIMD_V128_I2_CREATE(u16 target_local, u16 source_local) + * MINT_SIMD_V128_I4_CREATE(u16 target_local, u16 source_local) + * MINT_SIMD_V128_I8_CREATE(u16 target_local, u16 source_local) + * MINT_SIMD_INTRINS_P_P(u16 target_local, u16 arg1_local, u16 intrin_opcode) + * MINT_SIMD_INTRINS_P_PP(u16 target_local, u16 arg1_local, u16 arg2_local, u16 intrin_opcode) + * MINT_SIMD_INTRINS_P_PPP(u16 target_local, u16 arg1_local, u16 arg2_local, u16 arg3_local, u16 intrin_opcode) +### General intrinsics + * MINT_INTRINS_SPAN_CTOR(u16 target_local, u16 source_local, u16 size_local) + * MINT_INTRINS_CLEAR_WITH_REFERENCES(u16 target_local, u16 count_local) + * MINT_INTRINS_MARVIN_BLOCK(u16 pp0_local, u16 pp1_local, u16 dest0_local, u16 dest1_local,) + * MINT_INTRINS_ASCII_CHARS_TO_UPPERCASE(u16 target_local, u16 source_local) + * MINT_INTRINS_MEMORYMARSHAL_GETARRAYDATAREF(u16 target_local, u16 source_local) + * MINT_INTRINS_ORDINAL_IGNORE_CASE_ASCII(u16 target_local, u16 source_1_local, u16 source_2_local) + * MINT_INTRINS_64ORDINAL_IGNORE_CASE_ASCII(u16 target_local, u16 source_1_local, u16 source_2_local) + * MINT_INTRINS_WIDEN_ASCII_TO_UTF16(u16 result_local, u16 ascii_local, u16 wide_local, u16 count_local) + * MINT_INTRINS_RUNTIMEHELPERS_OBJECT_HAS_COMPONENT_SIZE(u16 target_local, u16 source_local) + * MINT_INTRINS_ENUM_HASFLAG(u16 target_local, u16 enum_local, u16 flag_local, u16 class_index) + * MINT_INTRINS_GET_HASHCODE(u16 target_local, u16 source_local) + * MINT_INTRINS_TRY_GET_HASHCODE(u16 target_local, u16 source_local) + * MINT_INTRINS_GET_TYPE(u16 target_local, u16 source_local) +### System.String.Chars intrinsic + * MINT_GETCHR(u16 target_local, u16 source_local, u16 index_local) +### System.{Span|ReadOnlySpan}.Item + * MINT_GETITEM_SPAN(u16 target_local, u16 source_local, u16 index_local, u16 element_size_local) +### System.{Span|ReadOnlySpan}.Item only when optimizations are on + * MINT_GETITEM_LOCALSPAN(u16 target_local, u16 source_local, u16 index_local, u16 element_size_local) +### System.String.Length intrinsic + * MINT_STRLEN(u16 target_local, u16 source_local) +### Profiler + * MINT_PROF_ENTER(u16 flag) + * MINT_PROF_EXIT(u16 source_local, u16 flag, u32 size) + * MINT_PROF_EXIT_VOID(u16 flag) + * MINT_PROF_COVERAGE_STORE(u64 pointer) +### Tiered interpreting + * MINT_TIER_ENTER_METHOD() + * MINT_TIER_PATCHPOINT(u16 bb) +### Debugger + * MINT_SDB_INTR_LOC() + * MINT_SDB_SEQ_POINT() + * MINT_SDB_BREAKPOINT() + * MINT_BREAKPOINT() diff --git a/docs/design/interpreter/stackwalk.md b/docs/design/interpreter/stackwalk.md new file mode 100644 index 000000000000..4d9d97a40518 --- /dev/null +++ b/docs/design/interpreter/stackwalk.md @@ -0,0 +1,25 @@ +# Stack walking for GC, exception handling and debugger +To enable seamless integration of interpreter with GC, exception handling and debugger, we need to be able to walk stack that contains interleaved sequences of interpreted and AOT / JIT compiled frames. Fortunately, the same StackFrameIterator is used by the GC, exception handling and debugger, so adding a support to walk stack containing interpreter frames to this StackFrameIterator will give us support for all of the three scenarios. + +Here is the list of necessary changes: +* Add a new explicit frame, the InterpreterFrame. Instance of this frame would be created in the mono_interp_exec_method. It will enable stack frame iterator to move through the interpreted frames managed by that mono_interp_exec_method. Please note that interpreter calls to other interpreted code are not recursively calling mono_interp_exec_method and each interpreted frame is represented by an instance of InterpFrame (these form a linked list). +* Add a new code manager derived from IJitManager, the InterpreterJitManager that will take care of the IR code ranges and enable getting GC info, enumerating EH clauses etc. +* Add RangeSectionFlags::RANGE_SECTION_INTERPRETER to represent interpreter IR code ranges. The RangeSection::_pjit would point to the InterpreterJitManager for the ranges in the interpreted code. So EECodeInfo::Init with an address in the interpreted IR code would get the InterpreterJitManager. +* It may need some changes in the EECodeInfo too +* Add the current InterpFrame to the CrawlFrame. When stack walk hits the InterpreterFrame, it would set that to the first InterpFrame +* Modify the EECodeManager::UnwindStackFrame to walk the list of InterpFrame instances for interpreted frames belonging to single mono_interp_exec_method. The Mono interpreter functions interp_frame_iter_next performs the stack walking for interpreted frames. It will likely need to be modified to be better suited for CoreCLR needs. + +The interpreter frames would be reported as StackFrameIterator::SFITER_FRAMELESS_METHOD like AOT / JIT compiled ones. +The REGDISPLAY::ControlPC would carry the PC of the interpreter IR code, the REGDISPLAY::SP the InterpFrame instance address. + +## GC +* The GC reporting should mostly just work using the GC info provided by the InterpreterJitManager. The offsets of the GC references would be relative to the interpreter stack that the InterpFrame::stack points to. + +## Exception handling +* It seems no or minimal changes will be needed, all the details related to the interpreter frame should be hidden behind the InterpreterJitManager + +## Debugger +* Add FrameType::kInterpreterStackFrame. The debugger code needs to know which frames represent interpreted code so that it can call proper implementations of functions to set a breakpoint, single step, get local variables etc. in the interpreter. +* Modify DacDbiInterfaceImpl::GetStackWalkCurrentFrameInfo to figure the frame type is kInterpreterStackFrame +* Modify DacDbiInterfaceImpl::InitFrameData to handle that frame type. The handling of this frame type will be likely very similar to the handling of the FrameType::kManagedStackFrame +* For more details, see the [debugger](debugger.md) document. From 828398442c4b96afdaff1fd0fae2c2ebbfb920f7 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 10 Dec 2024 18:11:47 +0100 Subject: [PATCH 10/11] Reflect feedback --- docs/design/interpreter/ir-byte-code.md | 12 ++++++------ docs/design/interpreter/stackwalk.md | 10 +++++----- docs/design/interpreter/transformation.md | 10 ++++++++-- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/design/interpreter/ir-byte-code.md b/docs/design/interpreter/ir-byte-code.md index 55f5425fb9bc..318df5f8e042 100644 --- a/docs/design/interpreter/ir-byte-code.md +++ b/docs/design/interpreter/ir-byte-code.md @@ -6,7 +6,7 @@ * Both the transformation and interpretation use Mono type / method / vtable etc data structures * The arguments to the opcode are immediate constants (16, 32 or 64 bit), indexes into local variables table or indexes into a per-method special data items array. ## Limitations - * The local variable offsets in most of the IR codes are 16 bit unsigned integers, so a method can have max 65536 kB of local variables + * The local variable offsets in most of the IR codes are 16 bit unsigned integers, so a method can have max 65536 kB stack frame size * The indices into the per-method special data items array in most of the IR codes are also 16 bit unsigned integers, thus a method can have max 65536 of these. ## IR codes corresponding to IL opcodes @@ -340,11 +340,11 @@ Various CEE_xxx with CEE_VOLATILE_ prefix, CEE_MONO_MEMORY_BARRIER * MINT_MOV_VT(u16 target_local, u16 source_local, u16 size) ### Hot reload * MINT_METADATA_UPDATE_LDFLDA(u16 target_local, u16 source_local, u16 type_index, u16 fielddef_token_index) -### JITerpreter - * MINT_TIER_NOP_JITERPRETER(???) - * MINT_TIER_PREPARE_JITERPRETER(???) - * MINT_TIER_MONITOR_JITERPRETER(???) - * MINT_TIER_ENTER_JITERPRETER(???) +### JITerpreter - WASM only + * MINT_TIER_NOP_JITERPRETER + * MINT_TIER_PREPARE_JITERPRETER + * MINT_TIER_MONITOR_JITERPRETER + * MINT_TIER_ENTER_JITERPRETER ### System.Array.Rank intrinsic * MINT_ARRAY_RANK(u16 target_local, u16 array_local) ### System.Array.GetElementSize intrinsic diff --git a/docs/design/interpreter/stackwalk.md b/docs/design/interpreter/stackwalk.md index 4d9d97a40518..ffe18c957ca9 100644 --- a/docs/design/interpreter/stackwalk.md +++ b/docs/design/interpreter/stackwalk.md @@ -2,18 +2,18 @@ To enable seamless integration of interpreter with GC, exception handling and debugger, we need to be able to walk stack that contains interleaved sequences of interpreted and AOT / JIT compiled frames. Fortunately, the same StackFrameIterator is used by the GC, exception handling and debugger, so adding a support to walk stack containing interpreter frames to this StackFrameIterator will give us support for all of the three scenarios. Here is the list of necessary changes: -* Add a new explicit frame, the InterpreterFrame. Instance of this frame would be created in the mono_interp_exec_method. It will enable stack frame iterator to move through the interpreted frames managed by that mono_interp_exec_method. Please note that interpreter calls to other interpreted code are not recursively calling mono_interp_exec_method and each interpreted frame is represented by an instance of InterpFrame (these form a linked list). +* Add a new explicit frame, the for the sake of this document named ClrInterpreterFrame. Instance of this frame would be created in the mono_interp_exec_method. It will enable stack frame iterator to move through the interpreted frames managed by that mono_interp_exec_method. Please note that interpreter calls to other interpreted code are not recursively calling mono_interp_exec_method and each interpreted frame is represented by an instance of Mono InterpFrame (these form a linked list). * Add a new code manager derived from IJitManager, the InterpreterJitManager that will take care of the IR code ranges and enable getting GC info, enumerating EH clauses etc. * Add RangeSectionFlags::RANGE_SECTION_INTERPRETER to represent interpreter IR code ranges. The RangeSection::_pjit would point to the InterpreterJitManager for the ranges in the interpreted code. So EECodeInfo::Init with an address in the interpreted IR code would get the InterpreterJitManager. * It may need some changes in the EECodeInfo too -* Add the current InterpFrame to the CrawlFrame. When stack walk hits the InterpreterFrame, it would set that to the first InterpFrame -* Modify the EECodeManager::UnwindStackFrame to walk the list of InterpFrame instances for interpreted frames belonging to single mono_interp_exec_method. The Mono interpreter functions interp_frame_iter_next performs the stack walking for interpreted frames. It will likely need to be modified to be better suited for CoreCLR needs. +* Add the current Mono InterpFrame (the name will likely change) to the CrawlFrame. When stack walk hits the ClrInterpreterFrame, it would set that to the first Mono InterpFrame +* Modify the EECodeManager::UnwindStackFrame to walk the list of Mono InterpFrame instances for interpreted frames belonging to single mono_interp_exec_method. The Mono interpreter functions interp_frame_iter_next performs the stack walking for interpreted frames. It will likely need to be modified to be better suited for CoreCLR needs. The interpreter frames would be reported as StackFrameIterator::SFITER_FRAMELESS_METHOD like AOT / JIT compiled ones. -The REGDISPLAY::ControlPC would carry the PC of the interpreter IR code, the REGDISPLAY::SP the InterpFrame instance address. +The REGDISPLAY::ControlPC would carry the PC of the interpreter IR code, the REGDISPLAY::SP the Mono InterpFrame instance address. ## GC -* The GC reporting should mostly just work using the GC info provided by the InterpreterJitManager. The offsets of the GC references would be relative to the interpreter stack that the InterpFrame::stack points to. +* The GC reporting should mostly just work using the GC info provided by the InterpreterJitManager. The offsets of the GC references would be relative to the interpreter stack that the Mono InterpFrame::stack points to. ## Exception handling * It seems no or minimal changes will be needed, all the details related to the interpreter frame should be hidden behind the InterpreterJitManager diff --git a/docs/design/interpreter/transformation.md b/docs/design/interpreter/transformation.md index 1820493f5dc2..bc4cc9ee9fbd 100644 --- a/docs/design/interpreter/transformation.md +++ b/docs/design/interpreter/transformation.md @@ -121,10 +121,16 @@ For each Mono API or a Mono specific code sequence, it describes how to replace * mono_class_from_mono_type_internal - N.A., just use CORINFO_CLASS_HANDLE ### mono_interp_jit_call_supported -* Return false, we will not support JIT (at least not in first version) +* Checks if a specific call to AOT compiled code (the JIT in the name is due to historical reasons) is supported by the interpreter +* mono_jit_call_can_be_supported_by_interp - rejects calls with more than 10 arguments, pinvokes, internal calls, string constructor, methods with wrappers and methods with StackCrawlMark locals. +* Then the function returns true if: + * the target method is in AOTed module and it isn't marked as "interpret only" + * or if the method's class is on an explicit list of classes to JIT (testing feature) +* Otherwise it returns false, indicating the call should be interpreted ### jit_call2_supported -* Remove or return false, we will not support JIT (at least not in first version) +* Checks if the target function is a pinvoke, internal call, generic method or string constructor and return false for those +* Otherwise force JITting of the target method and invoking the jitted form when tiered compilation is enabled. It emits the MINT_JIT_CALL2 IR code in this case. ### interp_generate_mae_throw ### interp_generate_void_throw From bea8538190f4bfa3cbad78cd4f5121f2727d99ad Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 10 Dec 2024 19:05:21 +0100 Subject: [PATCH 11/11] Add two missing links --- docs/design/interpreter/experiment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/design/interpreter/experiment.md b/docs/design/interpreter/experiment.md index 92faf15fec15..dfc223be4eea 100644 --- a/docs/design/interpreter/experiment.md +++ b/docs/design/interpreter/experiment.md @@ -46,7 +46,7 @@ There is also support for tiering when a frequently executed method with non-opt ## Analysis results and plans for CoreCLR The following documents describe the mapping of the IL opcodes to the IR opcodes, the Mono APIs used by the transformation and execution phases and how the debugger cooperates with the interpreter. They also describe the way these should work in CoreCLR. * [IR byte code](ir-byte-code.md) -* TODO: Interpreter Mono runtime dependencies (Vlad - commit the gist he has initially created) +* [Interpreter Mono runtime dependencies](mono-runtime-dependencies.md) * [Transformation phase analysis](transformation.md) * [Execution phase analysis](execution.md) * [Debugger integration](debugger.md) @@ -54,4 +54,4 @@ The following documents describe the mapping of the IL opcodes to the IR opcodes * [Precise GC in the interpreter](precise-gc.md) * [Interop of the interpreter with compiled managed code](compiled-code-interop.md) * [CoreCLR exception handling integration with the interpreter](exception-handling.md) -* TODO: link doc on Calls, delegates, function pointers (Vlad) +* [Calls implementation in the interpreter](calls.md)