Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/mono/mono/mini/interp/interp.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -3459,6 +3459,11 @@ interp_free_method (MonoMethod *method)

jit_mm_lock (jit_mm);
imethod = (InterpMethod*)mono_internal_hash_table_lookup (&jit_mm->interp_code_hash, method);

#if HOST_BROWSER
mono_jiterp_free_method_data (method, imethod);
#endif

mono_internal_hash_table_remove (&jit_mm->interp_code_hash, method);
if (imethod && jit_mm->interp_method_pointer_hash) {
if (imethod->jit_entry)
Expand Down
187 changes: 187 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,10 @@ void jiterp_preserve_module (void);
#include <stdlib.h>
#include <math.h>

#ifndef DISABLE_THREADS
#include <pthread.h>
#endif

#include <mono/metadata/mono-config.h>
#include <mono/utils/mono-threads.h>
#include <mono/metadata/gc-internals.h>
Expand All@@ -48,6 +52,7 @@ void jiterp_preserve_module (void);
#include <mono/mini/llvmonly-runtime.h>
#include <mono/utils/options.h>
#include <mono/utils/atomic.h>
#include <mono/utils/mono-tls.h>

#include "jiterpreter.h"

Expand DownExpand Up@@ -478,6 +483,7 @@ mono_jiterp_type_get_raw_value_size (MonoType *type) {
EMSCRIPTEN_KEEPALIVE void
mono_jiterp_trace_bailout (int reason)
{
// FIXME: Harmless race condition if threads are in use
if (reason < 256)
jiterp_trace_bailout_counts[reason]++;
}
Expand DownExpand Up@@ -670,6 +676,7 @@ should_generate_trace_here (InterpBasicBlock *bb) {
for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) {
int value = jiterp_get_opcode_value(ins, &inside_branch_block);
if (value < 0) {
// FIXME: Harmless race condition if threads are in use
jiterpreter_abort_counts[ins->opcode]++;
return current_trace_value >= mono_opt_jiterpreter_minimum_trace_value;
} else if (value >= VALUE_SIMD) {
Expand DownExpand Up@@ -743,7 +750,11 @@ trace_info_get (gint32 index) {

static gint32
trace_info_alloc () {
#ifdef DISABLE_THREADS
gint32 index = trace_count++,
#else
gint32 index = atomic_fetch_add ((atomic_int *)&trace_count, 1),
Comment thread
lambdageek marked this conversation as resolved.
#endif
limit = (MAX_TRACE_SEGMENTS * TRACE_SEGMENT_SIZE);
// Make sure we're not out of space in the trace info table.
if (index == limit)
Expand DownExpand Up@@ -931,6 +942,52 @@ mono_interp_tier_prepare_jiterpreter_fast (
}
}

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod)
{
MonoJitInfo *jinfo = imethod->jinfo;
const guint8 *start;
gboolean need_extra_free = TRUE;

// Erase the method and interpmethod from all of the thread local JIT queues
mono_jiterp_tlqueue_purge_all (method);
mono_jiterp_tlqueue_purge_all (imethod);

// FIXME: Enumerate all active threads and ensure we perform the free_method_data_js
// call on every thread.

// Scan through the interp opcodes for the method and ensure that any jiterp traces
// owned by it are cleaned up. This will automatically clean up any AOT related data for
// the method in the process
if (jinfo) {
start = (const guint8*) jinfo->code_start;
const guint16 *p = (const guint16 *)start,
*end = (const guint16 *)(start + jinfo->code_size);

while (p < end) {
switch (*p) {
case MINT_TIER_PREPARE_JITERPRETER:
case MINT_TIER_NOP_JITERPRETER:
case MINT_TIER_ENTER_JITERPRETER:
case MINT_TIER_MONITOR_JITERPRETER: {
JiterpreterOpcode *opcode = (JiterpreterOpcode *)p;
guint32 trace_index = opcode->trace_index;
need_extra_free = FALSE;
mono_jiterp_free_method_data_js (method, imethod, trace_index);
break;
}
}
p = mono_interp_dis_mintop_len (p);
}
}

if (need_extra_free) {
// HACK: Perform a single free operation to clear out any stuff from the jit queues
// This will happen if we didn't encounter any jiterpreter traces in the method
mono_jiterp_free_method_data_js (method, imethod, 0);
}
}

// Used to parse runtime options that control the jiterpreter. This is *also* used at runtime
// by the jiterpreter typescript to reconfigure the jiterpreter, for example if WASM EH is not
// actually available even though it was enabled (to turn it off).
Expand DownExpand Up@@ -1339,6 +1396,7 @@ mono_jiterp_monitor_trace (const guint16 *ip, void *_frame, void *locals)
if (mono_opt_jiterpreter_trace_monitoring_log > 1)
g_print ("trace #%d @%d '%s' accepted; average_penalty %f <= %f\n", opcode->trace_index, ip, frame->imethod->method->name, average_penalty, threshold);
} else {
// FIXME: Harmless race condition if threads are in use
traces_rejected++;
if (mono_opt_jiterpreter_trace_monitoring_log > 0) {
char * full_name = mono_method_get_full_name (frame->imethod->method);
Expand DownExpand Up@@ -1491,6 +1549,135 @@ mono_jiterp_patch_opcode (volatile JiterpreterOpcode *ip, guint16 old_opcode, gu
#endif
}

/*
* Unordered thread-local pointer queue (used for do_jit_call and interp_entry wrappers)
* The queues are all tracked in a global list so that it is possible to perform a global
* 'purge item with this value from all queues' operation, which means queue operations
* are protected by a mutex
*/

#define NUM_QUEUES 2
static MonoNativeTlsKey queue_keys[NUM_QUEUES] = { 0 };
#ifdef DISABLE_THREADS
gboolean queue_keys_initialized = FALSE;
#else
pthread_once_t queue_keys_initialized = PTHREAD_ONCE_INIT;
#endif
// NOTE: We're using OS mutexes here not coop mutexes, because we need to be able to run
// during a GC and at any point (before runtime startup or after shutdown)
// This means if we aren't careful we could deadlock, so we have to be cautious about
// what operations we perform while holding this mutex.
static mono_mutex_t queue_mutex;
static GPtrArray *shared_queues = NULL;

static void
free_queue (void *ptr) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_remove_fast (shared_queues, ptr);
g_ptr_array_free ((GPtrArray *)ptr, TRUE);
mono_os_mutex_unlock (&queue_mutex);
}

static void
initialize_queue_keys () {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
shared_queues = g_ptr_array_new ();

for (int i = 0; i < NUM_QUEUES; i++)
g_assert (mono_native_tls_alloc (&queue_keys[i], free_queue));

#ifdef DISABLE_THREADS
queue_keys_initialized = TRUE;
#endif

mono_os_mutex_unlock (&queue_mutex);
}

static MonoNativeTlsKey
get_queue_key (int queue) {
g_assert ((queue >= 0) && (queue < NUM_QUEUES));

#ifdef DISABLE_THREADS
if (!queue_keys_initialized)
initialize_queue_keys ();
#else
pthread_once (&queue_keys_initialized, initialize_queue_keys);
#endif

return queue_keys[queue];
}

static GPtrArray *
get_queue (int queue) {
MonoNativeTlsKey key = get_queue_key (queue);
GPtrArray *result = NULL;
if ((result = (GPtrArray *)mono_native_tls_get_value (key)) == NULL) {
g_assert (mono_native_tls_set_value (key, result = g_ptr_array_new ()));
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (shared_queues, result);
mono_os_mutex_unlock (&queue_mutex);
}
return result;
}

// Purges this item from all queues
void
mono_jiterp_tlqueue_purge_all (gpointer item) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
for (int i = 0; i < shared_queues->len; i++) {
GPtrArray *queue = (GPtrArray *)g_ptr_array_index (shared_queues, i);
gboolean ok = g_ptr_array_remove_fast (queue, item);
if (ok) {
// g_printf ("Purged %x from queue %x\n", (unsigned int)item, (unsigned int)queue);
}
}
mono_os_mutex_unlock (&queue_mutex);
}

// Removes the next item from the queue, if any, and returns it (NULL if empty)
EMSCRIPTEN_KEEPALIVE gpointer
mono_jiterp_tlqueue_next (int queue) {
// This lock-per-call is unpleasant, but the queue is usually only going to contain
// a handful of items and will only be enumerated a few hundred times total during
// execution, so performance is not especially important compared to safety/simplicity
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
gpointer result = NULL;
if (items->len) {
result = g_ptr_array_index (items, 0);
g_ptr_array_remove_index_fast (items, 0);
}
mono_os_mutex_unlock (&queue_mutex);
return result;
}

// Adds a new item to the end of the queue and returns the new size of the queue
EMSCRIPTEN_KEEPALIVE int
mono_jiterp_tlqueue_add (int queue, gpointer item) {
int result;
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (items, item);
result = items->len;
mono_os_mutex_unlock (&queue_mutex);
return result;
}

EMSCRIPTEN_KEEPALIVE void
mono_jiterp_tlqueue_clear (int queue) {
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_set_size (items, 0);
mono_os_mutex_unlock (&queue_mutex);
}

// HACK: fix C4206
EMSCRIPTEN_KEEPALIVE
#endif // HOST_BROWSER
Expand Down
11 changes: 11 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -172,6 +172,11 @@ mono_jiterp_do_jit_call_indirect (

#ifdef __MONO_MINI_INTERPRETER_INTERNALS_H__

extern void
mono_jiterp_free_method_data_js (
MonoMethod *method, InterpMethod *imethod, int trace_index
);

typedef struct {
InterpMethod *rmethod;
ThreadContext *context;
Expand DownExpand Up@@ -231,9 +236,15 @@ mono_jiterp_placeholder_trace (void *frame, void *pLocals, JiterpreterCallInfo *
void
mono_jiterp_placeholder_jit_call (void *ret_sp, void *sp, void *ftndesc, gboolean *thrown);

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod);

void *
mono_jiterp_get_interp_entry_func (int table);

void
mono_jiterp_tlqueue_purge_all (gpointer item);

#endif // __MONO_MINI_INTERPRETER_INTERNALS_H__

extern WasmDoJitCall jiterpreter_do_jit_call;
Expand Down
8 changes: 8 additions & 0 deletions src/mono/wasm/runtime/cwraps.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -144,6 +144,9 @@ const fn_signatures: SigLine[] = [
[true, "mono_jiterp_get_interp_entry_func", "number", ["number"]],
[true, "mono_jiterp_get_counter", "number", ["number"]],
[true, "mono_jiterp_modify_counter", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_next", "number", ["number"]],
[true, "mono_jiterp_tlqueue_add", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_clear", "void", ["number"]],

...diagnostics_cwraps,
...legacy_interop_cwraps
Expand DownExpand Up@@ -283,6 +286,11 @@ export interface t_Cwraps {
mono_jiterp_get_interp_entry_func(type: number): number;
mono_jiterp_get_counter(counter: number): number;
mono_jiterp_modify_counter(counter: number, delta: number): number;
// returns value or, if queue is empty, VoidPtrNull
mono_jiterp_tlqueue_next(queue: number): VoidPtr;
// returns new size of queue after add
mono_jiterp_tlqueue_add(queue: number, value: VoidPtr): number;
mono_jiterp_tlqueue_clear(queue: number): void;
}

const wrapped_c_functions: t_Cwraps = <any>{};
Expand Down
5 changes: 3 additions & 2 deletions src/mono/wasm/runtime/exports-binding.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@ import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_s
import { mono_wasm_release_cs_owned_object } from "./gc-handles";
import { mono_wasm_bind_cs_function } from "./invoke-cs";
import { mono_wasm_bind_js_function, mono_wasm_invoke_bound_function, mono_wasm_invoke_import } from "./invoke-js";
import { mono_interp_tier_prepare_jiterpreter } from "./jiterpreter";
import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter";
import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry";
import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, mono_jiterp_do_jit_call_indirect } from "./jiterpreter-jit-call";
import { mono_wasm_marshal_promise } from "./marshal-to-js";
Expand DownExpand Up@@ -89,6 +89,7 @@ export const mono_wasm_imports = [
mono_interp_invoke_wasm_jit_call_trampoline,
mono_interp_flush_jitcall_queue,
mono_jiterp_do_jit_call_indirect,
mono_jiterp_free_method_data_js,

mono_wasm_profiler_enter,
mono_wasm_profiler_leave,
Expand DownExpand Up@@ -163,4 +164,4 @@ export function replace_linker_placeholders(imports: WebAssembly.Imports) {
}
}

}
}
5 changes: 5 additions & 0 deletions src/mono/wasm/runtime/jiterpreter-enums.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -157,3 +157,8 @@ export const BailoutReasonNames = [
"UnexpectedRetIp",
"LeaveCheck",
];

export const enum JitQueue {
JitCall = 0,
InterpEntry = 1
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/mono/mono/mini/interp/interp.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -3459,6 +3459,11 @@ interp_free_method (MonoMethod *method)

jit_mm_lock (jit_mm);
imethod = (InterpMethod*)mono_internal_hash_table_lookup (&jit_mm->interp_code_hash, method);

#if HOST_BROWSER
mono_jiterp_free_method_data (method, imethod);
#endif

mono_internal_hash_table_remove (&jit_mm->interp_code_hash, method);
if (imethod && jit_mm->interp_method_pointer_hash) {
if (imethod->jit_entry)
Expand Down
187 changes: 187 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,10 @@ void jiterp_preserve_module (void);
#include <stdlib.h>
#include <math.h>

#ifndef DISABLE_THREADS
#include <pthread.h>
#endif

#include <mono/metadata/mono-config.h>
#include <mono/utils/mono-threads.h>
#include <mono/metadata/gc-internals.h>
Expand All@@ -48,6 +52,7 @@ void jiterp_preserve_module (void);
#include <mono/mini/llvmonly-runtime.h>
#include <mono/utils/options.h>
#include <mono/utils/atomic.h>
#include <mono/utils/mono-tls.h>

#include "jiterpreter.h"

Expand DownExpand Up@@ -478,6 +483,7 @@ mono_jiterp_type_get_raw_value_size (MonoType *type) {
EMSCRIPTEN_KEEPALIVE void
mono_jiterp_trace_bailout (int reason)
{
// FIXME: Harmless race condition if threads are in use
if (reason < 256)
jiterp_trace_bailout_counts[reason]++;
}
Expand DownExpand Up@@ -670,6 +676,7 @@ should_generate_trace_here (InterpBasicBlock *bb) {
for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) {
int value = jiterp_get_opcode_value(ins, &inside_branch_block);
if (value < 0) {
// FIXME: Harmless race condition if threads are in use
jiterpreter_abort_counts[ins->opcode]++;
return current_trace_value >= mono_opt_jiterpreter_minimum_trace_value;
} else if (value >= VALUE_SIMD) {
Expand DownExpand Up@@ -743,7 +750,11 @@ trace_info_get (gint32 index) {

static gint32
trace_info_alloc () {
#ifdef DISABLE_THREADS
gint32 index = trace_count++,
#else
gint32 index = atomic_fetch_add ((atomic_int *)&trace_count, 1),
Comment thread
lambdageek marked this conversation as resolved.
#endif
limit = (MAX_TRACE_SEGMENTS * TRACE_SEGMENT_SIZE);
// Make sure we're not out of space in the trace info table.
if (index == limit)
Expand DownExpand Up@@ -931,6 +942,52 @@ mono_interp_tier_prepare_jiterpreter_fast (
}
}

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod)
{
MonoJitInfo *jinfo = imethod->jinfo;
const guint8 *start;
gboolean need_extra_free = TRUE;

// Erase the method and interpmethod from all of the thread local JIT queues
mono_jiterp_tlqueue_purge_all (method);
mono_jiterp_tlqueue_purge_all (imethod);

// FIXME: Enumerate all active threads and ensure we perform the free_method_data_js
// call on every thread.

// Scan through the interp opcodes for the method and ensure that any jiterp traces
// owned by it are cleaned up. This will automatically clean up any AOT related data for
// the method in the process
if (jinfo) {
start = (const guint8*) jinfo->code_start;
const guint16 *p = (const guint16 *)start,
*end = (const guint16 *)(start + jinfo->code_size);

while (p < end) {
switch (*p) {
case MINT_TIER_PREPARE_JITERPRETER:
case MINT_TIER_NOP_JITERPRETER:
case MINT_TIER_ENTER_JITERPRETER:
case MINT_TIER_MONITOR_JITERPRETER: {
JiterpreterOpcode *opcode = (JiterpreterOpcode *)p;
guint32 trace_index = opcode->trace_index;
need_extra_free = FALSE;
mono_jiterp_free_method_data_js (method, imethod, trace_index);
break;
}
}
p = mono_interp_dis_mintop_len (p);
}
}

if (need_extra_free) {
// HACK: Perform a single free operation to clear out any stuff from the jit queues
// This will happen if we didn't encounter any jiterpreter traces in the method
mono_jiterp_free_method_data_js (method, imethod, 0);
}
}

// Used to parse runtime options that control the jiterpreter. This is *also* used at runtime
// by the jiterpreter typescript to reconfigure the jiterpreter, for example if WASM EH is not
// actually available even though it was enabled (to turn it off).
Expand DownExpand Up@@ -1339,6 +1396,7 @@ mono_jiterp_monitor_trace (const guint16 *ip, void *_frame, void *locals)
if (mono_opt_jiterpreter_trace_monitoring_log > 1)
g_print ("trace #%d @%d '%s' accepted; average_penalty %f <= %f\n", opcode->trace_index, ip, frame->imethod->method->name, average_penalty, threshold);
} else {
// FIXME: Harmless race condition if threads are in use
traces_rejected++;
if (mono_opt_jiterpreter_trace_monitoring_log > 0) {
char * full_name = mono_method_get_full_name (frame->imethod->method);
Expand DownExpand Up@@ -1491,6 +1549,135 @@ mono_jiterp_patch_opcode (volatile JiterpreterOpcode *ip, guint16 old_opcode, gu
#endif
}

/*
* Unordered thread-local pointer queue (used for do_jit_call and interp_entry wrappers)
* The queues are all tracked in a global list so that it is possible to perform a global
* 'purge item with this value from all queues' operation, which means queue operations
* are protected by a mutex
*/

#define NUM_QUEUES 2
static MonoNativeTlsKey queue_keys[NUM_QUEUES] = { 0 };
#ifdef DISABLE_THREADS
gboolean queue_keys_initialized = FALSE;
#else
pthread_once_t queue_keys_initialized = PTHREAD_ONCE_INIT;
#endif
// NOTE: We're using OS mutexes here not coop mutexes, because we need to be able to run
// during a GC and at any point (before runtime startup or after shutdown)
// This means if we aren't careful we could deadlock, so we have to be cautious about
// what operations we perform while holding this mutex.
static mono_mutex_t queue_mutex;
static GPtrArray *shared_queues = NULL;

static void
free_queue (void *ptr) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_remove_fast (shared_queues, ptr);
g_ptr_array_free ((GPtrArray *)ptr, TRUE);
mono_os_mutex_unlock (&queue_mutex);
}

static void
initialize_queue_keys () {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
shared_queues = g_ptr_array_new ();

for (int i = 0; i < NUM_QUEUES; i++)
g_assert (mono_native_tls_alloc (&queue_keys[i], free_queue));

#ifdef DISABLE_THREADS
queue_keys_initialized = TRUE;
#endif

mono_os_mutex_unlock (&queue_mutex);
}

static MonoNativeTlsKey
get_queue_key (int queue) {
g_assert ((queue >= 0) && (queue < NUM_QUEUES));

#ifdef DISABLE_THREADS
if (!queue_keys_initialized)
initialize_queue_keys ();
#else
pthread_once (&queue_keys_initialized, initialize_queue_keys);
#endif

return queue_keys[queue];
}

static GPtrArray *
get_queue (int queue) {
MonoNativeTlsKey key = get_queue_key (queue);
GPtrArray *result = NULL;
if ((result = (GPtrArray *)mono_native_tls_get_value (key)) == NULL) {
g_assert (mono_native_tls_set_value (key, result = g_ptr_array_new ()));
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (shared_queues, result);
mono_os_mutex_unlock (&queue_mutex);
}
return result;
}

// Purges this item from all queues
void
mono_jiterp_tlqueue_purge_all (gpointer item) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
for (int i = 0; i < shared_queues->len; i++) {
GPtrArray *queue = (GPtrArray *)g_ptr_array_index (shared_queues, i);
gboolean ok = g_ptr_array_remove_fast (queue, item);
if (ok) {
// g_printf ("Purged %x from queue %x\n", (unsigned int)item, (unsigned int)queue);
}
}
mono_os_mutex_unlock (&queue_mutex);
}

// Removes the next item from the queue, if any, and returns it (NULL if empty)
EMSCRIPTEN_KEEPALIVE gpointer
mono_jiterp_tlqueue_next (int queue) {
// This lock-per-call is unpleasant, but the queue is usually only going to contain
// a handful of items and will only be enumerated a few hundred times total during
// execution, so performance is not especially important compared to safety/simplicity
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
gpointer result = NULL;
if (items->len) {
result = g_ptr_array_index (items, 0);
g_ptr_array_remove_index_fast (items, 0);
}
mono_os_mutex_unlock (&queue_mutex);
return result;
}

// Adds a new item to the end of the queue and returns the new size of the queue
EMSCRIPTEN_KEEPALIVE int
mono_jiterp_tlqueue_add (int queue, gpointer item) {
int result;
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (items, item);
result = items->len;
mono_os_mutex_unlock (&queue_mutex);
return result;
}

EMSCRIPTEN_KEEPALIVE void
mono_jiterp_tlqueue_clear (int queue) {
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_set_size (items, 0);
mono_os_mutex_unlock (&queue_mutex);
}

// HACK: fix C4206
EMSCRIPTEN_KEEPALIVE
#endif // HOST_BROWSER
Expand Down
11 changes: 11 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -172,6 +172,11 @@ mono_jiterp_do_jit_call_indirect (

#ifdef __MONO_MINI_INTERPRETER_INTERNALS_H__

extern void
mono_jiterp_free_method_data_js (
MonoMethod *method, InterpMethod *imethod, int trace_index
);

typedef struct {
InterpMethod *rmethod;
ThreadContext *context;
Expand DownExpand Up@@ -231,9 +236,15 @@ mono_jiterp_placeholder_trace (void *frame, void *pLocals, JiterpreterCallInfo *
void
mono_jiterp_placeholder_jit_call (void *ret_sp, void *sp, void *ftndesc, gboolean *thrown);

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod);

void *
mono_jiterp_get_interp_entry_func (int table);

void
mono_jiterp_tlqueue_purge_all (gpointer item);

#endif // __MONO_MINI_INTERPRETER_INTERNALS_H__

extern WasmDoJitCall jiterpreter_do_jit_call;
Expand Down
8 changes: 8 additions & 0 deletions src/mono/wasm/runtime/cwraps.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -144,6 +144,9 @@ const fn_signatures: SigLine[] = [
[true, "mono_jiterp_get_interp_entry_func", "number", ["number"]],
[true, "mono_jiterp_get_counter", "number", ["number"]],
[true, "mono_jiterp_modify_counter", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_next", "number", ["number"]],
[true, "mono_jiterp_tlqueue_add", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_clear", "void", ["number"]],

...diagnostics_cwraps,
...legacy_interop_cwraps
Expand DownExpand Up@@ -283,6 +286,11 @@ export interface t_Cwraps {
mono_jiterp_get_interp_entry_func(type: number): number;
mono_jiterp_get_counter(counter: number): number;
mono_jiterp_modify_counter(counter: number, delta: number): number;
// returns value or, if queue is empty, VoidPtrNull
mono_jiterp_tlqueue_next(queue: number): VoidPtr;
// returns new size of queue after add
mono_jiterp_tlqueue_add(queue: number, value: VoidPtr): number;
mono_jiterp_tlqueue_clear(queue: number): void;
}

const wrapped_c_functions: t_Cwraps = <any>{};
Expand Down
5 changes: 3 additions & 2 deletions src/mono/wasm/runtime/exports-binding.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@ import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_s
import { mono_wasm_release_cs_owned_object } from "./gc-handles";
import { mono_wasm_bind_cs_function } from "./invoke-cs";
import { mono_wasm_bind_js_function, mono_wasm_invoke_bound_function, mono_wasm_invoke_import } from "./invoke-js";
import { mono_interp_tier_prepare_jiterpreter } from "./jiterpreter";
import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter";
import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry";
import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, mono_jiterp_do_jit_call_indirect } from "./jiterpreter-jit-call";
import { mono_wasm_marshal_promise } from "./marshal-to-js";
Expand DownExpand Up@@ -89,6 +89,7 @@ export const mono_wasm_imports = [
mono_interp_invoke_wasm_jit_call_trampoline,
mono_interp_flush_jitcall_queue,
mono_jiterp_do_jit_call_indirect,
mono_jiterp_free_method_data_js,

mono_wasm_profiler_enter,
mono_wasm_profiler_leave,
Expand DownExpand Up@@ -163,4 +164,4 @@ export function replace_linker_placeholders(imports: WebAssembly.Imports) {
}
}

}
}
5 changes: 5 additions & 0 deletions src/mono/wasm/runtime/jiterpreter-enums.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -157,3 +157,8 @@ export const BailoutReasonNames = [
"UnexpectedRetIp",
"LeaveCheck",
];

export const enum JitQueue {
JitCall = 0,
InterpEntry = 1
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/mono/mono/mini/interp/interp.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -3459,6 +3459,11 @@ interp_free_method (MonoMethod *method)

jit_mm_lock (jit_mm);
imethod = (InterpMethod*)mono_internal_hash_table_lookup (&jit_mm->interp_code_hash, method);

#if HOST_BROWSER
mono_jiterp_free_method_data (method, imethod);
#endif

mono_internal_hash_table_remove (&jit_mm->interp_code_hash, method);
if (imethod && jit_mm->interp_method_pointer_hash) {
if (imethod->jit_entry)
Expand Down
187 changes: 187 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,10 @@ void jiterp_preserve_module (void);
#include <stdlib.h>
#include <math.h>

#ifndef DISABLE_THREADS
#include <pthread.h>
#endif

#include <mono/metadata/mono-config.h>
#include <mono/utils/mono-threads.h>
#include <mono/metadata/gc-internals.h>
Expand All@@ -48,6 +52,7 @@ void jiterp_preserve_module (void);
#include <mono/mini/llvmonly-runtime.h>
#include <mono/utils/options.h>
#include <mono/utils/atomic.h>
#include <mono/utils/mono-tls.h>

#include "jiterpreter.h"

Expand DownExpand Up@@ -478,6 +483,7 @@ mono_jiterp_type_get_raw_value_size (MonoType *type) {
EMSCRIPTEN_KEEPALIVE void
mono_jiterp_trace_bailout (int reason)
{
// FIXME: Harmless race condition if threads are in use
if (reason < 256)
jiterp_trace_bailout_counts[reason]++;
}
Expand DownExpand Up@@ -670,6 +676,7 @@ should_generate_trace_here (InterpBasicBlock *bb) {
for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) {
int value = jiterp_get_opcode_value(ins, &inside_branch_block);
if (value < 0) {
// FIXME: Harmless race condition if threads are in use
jiterpreter_abort_counts[ins->opcode]++;
return current_trace_value >= mono_opt_jiterpreter_minimum_trace_value;
} else if (value >= VALUE_SIMD) {
Expand DownExpand Up@@ -743,7 +750,11 @@ trace_info_get (gint32 index) {

static gint32
trace_info_alloc () {
#ifdef DISABLE_THREADS
gint32 index = trace_count++,
#else
gint32 index = atomic_fetch_add ((atomic_int *)&trace_count, 1),
Comment thread
lambdageek marked this conversation as resolved.
#endif
limit = (MAX_TRACE_SEGMENTS * TRACE_SEGMENT_SIZE);
// Make sure we're not out of space in the trace info table.
if (index == limit)
Expand DownExpand Up@@ -931,6 +942,52 @@ mono_interp_tier_prepare_jiterpreter_fast (
}
}

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod)
{
MonoJitInfo *jinfo = imethod->jinfo;
const guint8 *start;
gboolean need_extra_free = TRUE;

// Erase the method and interpmethod from all of the thread local JIT queues
mono_jiterp_tlqueue_purge_all (method);
mono_jiterp_tlqueue_purge_all (imethod);

// FIXME: Enumerate all active threads and ensure we perform the free_method_data_js
// call on every thread.

// Scan through the interp opcodes for the method and ensure that any jiterp traces
// owned by it are cleaned up. This will automatically clean up any AOT related data for
// the method in the process
if (jinfo) {
start = (const guint8*) jinfo->code_start;
const guint16 *p = (const guint16 *)start,
*end = (const guint16 *)(start + jinfo->code_size);

while (p < end) {
switch (*p) {
case MINT_TIER_PREPARE_JITERPRETER:
case MINT_TIER_NOP_JITERPRETER:
case MINT_TIER_ENTER_JITERPRETER:
case MINT_TIER_MONITOR_JITERPRETER: {
JiterpreterOpcode *opcode = (JiterpreterOpcode *)p;
guint32 trace_index = opcode->trace_index;
need_extra_free = FALSE;
mono_jiterp_free_method_data_js (method, imethod, trace_index);
break;
}
}
p = mono_interp_dis_mintop_len (p);
}
}

if (need_extra_free) {
// HACK: Perform a single free operation to clear out any stuff from the jit queues
// This will happen if we didn't encounter any jiterpreter traces in the method
mono_jiterp_free_method_data_js (method, imethod, 0);
}
}

// Used to parse runtime options that control the jiterpreter. This is *also* used at runtime
// by the jiterpreter typescript to reconfigure the jiterpreter, for example if WASM EH is not
// actually available even though it was enabled (to turn it off).
Expand DownExpand Up@@ -1339,6 +1396,7 @@ mono_jiterp_monitor_trace (const guint16 *ip, void *_frame, void *locals)
if (mono_opt_jiterpreter_trace_monitoring_log > 1)
g_print ("trace #%d @%d '%s' accepted; average_penalty %f <= %f\n", opcode->trace_index, ip, frame->imethod->method->name, average_penalty, threshold);
} else {
// FIXME: Harmless race condition if threads are in use
traces_rejected++;
if (mono_opt_jiterpreter_trace_monitoring_log > 0) {
char * full_name = mono_method_get_full_name (frame->imethod->method);
Expand DownExpand Up@@ -1491,6 +1549,135 @@ mono_jiterp_patch_opcode (volatile JiterpreterOpcode *ip, guint16 old_opcode, gu
#endif
}

/*
* Unordered thread-local pointer queue (used for do_jit_call and interp_entry wrappers)
* The queues are all tracked in a global list so that it is possible to perform a global
* 'purge item with this value from all queues' operation, which means queue operations
* are protected by a mutex
*/

#define NUM_QUEUES 2
static MonoNativeTlsKey queue_keys[NUM_QUEUES] = { 0 };
#ifdef DISABLE_THREADS
gboolean queue_keys_initialized = FALSE;
#else
pthread_once_t queue_keys_initialized = PTHREAD_ONCE_INIT;
#endif
// NOTE: We're using OS mutexes here not coop mutexes, because we need to be able to run
// during a GC and at any point (before runtime startup or after shutdown)
// This means if we aren't careful we could deadlock, so we have to be cautious about
// what operations we perform while holding this mutex.
static mono_mutex_t queue_mutex;
static GPtrArray *shared_queues = NULL;

static void
free_queue (void *ptr) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_remove_fast (shared_queues, ptr);
g_ptr_array_free ((GPtrArray *)ptr, TRUE);
mono_os_mutex_unlock (&queue_mutex);
}

static void
initialize_queue_keys () {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
shared_queues = g_ptr_array_new ();

for (int i = 0; i < NUM_QUEUES; i++)
g_assert (mono_native_tls_alloc (&queue_keys[i], free_queue));

#ifdef DISABLE_THREADS
queue_keys_initialized = TRUE;
#endif

mono_os_mutex_unlock (&queue_mutex);
}

static MonoNativeTlsKey
get_queue_key (int queue) {
g_assert ((queue >= 0) && (queue < NUM_QUEUES));

#ifdef DISABLE_THREADS
if (!queue_keys_initialized)
initialize_queue_keys ();
#else
pthread_once (&queue_keys_initialized, initialize_queue_keys);
#endif

return queue_keys[queue];
}

static GPtrArray *
get_queue (int queue) {
MonoNativeTlsKey key = get_queue_key (queue);
GPtrArray *result = NULL;
if ((result = (GPtrArray *)mono_native_tls_get_value (key)) == NULL) {
g_assert (mono_native_tls_set_value (key, result = g_ptr_array_new ()));
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (shared_queues, result);
mono_os_mutex_unlock (&queue_mutex);
}
return result;
}

// Purges this item from all queues
void
mono_jiterp_tlqueue_purge_all (gpointer item) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
for (int i = 0; i < shared_queues->len; i++) {
GPtrArray *queue = (GPtrArray *)g_ptr_array_index (shared_queues, i);
gboolean ok = g_ptr_array_remove_fast (queue, item);
if (ok) {
// g_printf ("Purged %x from queue %x\n", (unsigned int)item, (unsigned int)queue);
}
}
mono_os_mutex_unlock (&queue_mutex);
}

// Removes the next item from the queue, if any, and returns it (NULL if empty)
EMSCRIPTEN_KEEPALIVE gpointer
mono_jiterp_tlqueue_next (int queue) {
// This lock-per-call is unpleasant, but the queue is usually only going to contain
// a handful of items and will only be enumerated a few hundred times total during
// execution, so performance is not especially important compared to safety/simplicity
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
gpointer result = NULL;
if (items->len) {
result = g_ptr_array_index (items, 0);
g_ptr_array_remove_index_fast (items, 0);
}
mono_os_mutex_unlock (&queue_mutex);
return result;
}

// Adds a new item to the end of the queue and returns the new size of the queue
EMSCRIPTEN_KEEPALIVE int
mono_jiterp_tlqueue_add (int queue, gpointer item) {
int result;
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (items, item);
result = items->len;
mono_os_mutex_unlock (&queue_mutex);
return result;
}

EMSCRIPTEN_KEEPALIVE void
mono_jiterp_tlqueue_clear (int queue) {
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_set_size (items, 0);
mono_os_mutex_unlock (&queue_mutex);
}

// HACK: fix C4206
EMSCRIPTEN_KEEPALIVE
#endif // HOST_BROWSER
Expand Down
11 changes: 11 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -172,6 +172,11 @@ mono_jiterp_do_jit_call_indirect (

#ifdef __MONO_MINI_INTERPRETER_INTERNALS_H__

extern void
mono_jiterp_free_method_data_js (
MonoMethod *method, InterpMethod *imethod, int trace_index
);

typedef struct {
InterpMethod *rmethod;
ThreadContext *context;
Expand DownExpand Up@@ -231,9 +236,15 @@ mono_jiterp_placeholder_trace (void *frame, void *pLocals, JiterpreterCallInfo *
void
mono_jiterp_placeholder_jit_call (void *ret_sp, void *sp, void *ftndesc, gboolean *thrown);

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod);

void *
mono_jiterp_get_interp_entry_func (int table);

void
mono_jiterp_tlqueue_purge_all (gpointer item);

#endif // __MONO_MINI_INTERPRETER_INTERNALS_H__

extern WasmDoJitCall jiterpreter_do_jit_call;
Expand Down
8 changes: 8 additions & 0 deletions src/mono/wasm/runtime/cwraps.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -144,6 +144,9 @@ const fn_signatures: SigLine[] = [
[true, "mono_jiterp_get_interp_entry_func", "number", ["number"]],
[true, "mono_jiterp_get_counter", "number", ["number"]],
[true, "mono_jiterp_modify_counter", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_next", "number", ["number"]],
[true, "mono_jiterp_tlqueue_add", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_clear", "void", ["number"]],

...diagnostics_cwraps,
...legacy_interop_cwraps
Expand DownExpand Up@@ -283,6 +286,11 @@ export interface t_Cwraps {
mono_jiterp_get_interp_entry_func(type: number): number;
mono_jiterp_get_counter(counter: number): number;
mono_jiterp_modify_counter(counter: number, delta: number): number;
// returns value or, if queue is empty, VoidPtrNull
mono_jiterp_tlqueue_next(queue: number): VoidPtr;
// returns new size of queue after add
mono_jiterp_tlqueue_add(queue: number, value: VoidPtr): number;
mono_jiterp_tlqueue_clear(queue: number): void;
}

const wrapped_c_functions: t_Cwraps = <any>{};
Expand Down
5 changes: 3 additions & 2 deletions src/mono/wasm/runtime/exports-binding.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@ import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_s
import { mono_wasm_release_cs_owned_object } from "./gc-handles";
import { mono_wasm_bind_cs_function } from "./invoke-cs";
import { mono_wasm_bind_js_function, mono_wasm_invoke_bound_function, mono_wasm_invoke_import } from "./invoke-js";
import { mono_interp_tier_prepare_jiterpreter } from "./jiterpreter";
import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter";
import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry";
import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, mono_jiterp_do_jit_call_indirect } from "./jiterpreter-jit-call";
import { mono_wasm_marshal_promise } from "./marshal-to-js";
Expand DownExpand Up@@ -89,6 +89,7 @@ export const mono_wasm_imports = [
mono_interp_invoke_wasm_jit_call_trampoline,
mono_interp_flush_jitcall_queue,
mono_jiterp_do_jit_call_indirect,
mono_jiterp_free_method_data_js,

mono_wasm_profiler_enter,
mono_wasm_profiler_leave,
Expand DownExpand Up@@ -163,4 +164,4 @@ export function replace_linker_placeholders(imports: WebAssembly.Imports) {
}
}

}
}
5 changes: 5 additions & 0 deletions src/mono/wasm/runtime/jiterpreter-enums.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -157,3 +157,8 @@ export const BailoutReasonNames = [
"UnexpectedRetIp",
"LeaveCheck",
];

export const enum JitQueue {
JitCall = 0,
InterpEntry = 1
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/mono/mono/mini/interp/interp.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -3459,6 +3459,11 @@ interp_free_method (MonoMethod *method)

jit_mm_lock (jit_mm);
imethod = (InterpMethod*)mono_internal_hash_table_lookup (&jit_mm->interp_code_hash, method);

#if HOST_BROWSER
mono_jiterp_free_method_data (method, imethod);
#endif

mono_internal_hash_table_remove (&jit_mm->interp_code_hash, method);
if (imethod && jit_mm->interp_method_pointer_hash) {
if (imethod->jit_entry)
Expand Down
187 changes: 187 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,10 @@ void jiterp_preserve_module (void);
#include <stdlib.h>
#include <math.h>

#ifndef DISABLE_THREADS
#include <pthread.h>
#endif

#include <mono/metadata/mono-config.h>
#include <mono/utils/mono-threads.h>
#include <mono/metadata/gc-internals.h>
Expand All@@ -48,6 +52,7 @@ void jiterp_preserve_module (void);
#include <mono/mini/llvmonly-runtime.h>
#include <mono/utils/options.h>
#include <mono/utils/atomic.h>
#include <mono/utils/mono-tls.h>

#include "jiterpreter.h"

Expand DownExpand Up@@ -478,6 +483,7 @@ mono_jiterp_type_get_raw_value_size (MonoType *type) {
EMSCRIPTEN_KEEPALIVE void
mono_jiterp_trace_bailout (int reason)
{
// FIXME: Harmless race condition if threads are in use
if (reason < 256)
jiterp_trace_bailout_counts[reason]++;
}
Expand DownExpand Up@@ -670,6 +676,7 @@ should_generate_trace_here (InterpBasicBlock *bb) {
for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) {
int value = jiterp_get_opcode_value(ins, &inside_branch_block);
if (value < 0) {
// FIXME: Harmless race condition if threads are in use
jiterpreter_abort_counts[ins->opcode]++;
return current_trace_value >= mono_opt_jiterpreter_minimum_trace_value;
} else if (value >= VALUE_SIMD) {
Expand DownExpand Up@@ -743,7 +750,11 @@ trace_info_get (gint32 index) {

static gint32
trace_info_alloc () {
#ifdef DISABLE_THREADS
gint32 index = trace_count++,
#else
gint32 index = atomic_fetch_add ((atomic_int *)&trace_count, 1),
Comment thread
lambdageek marked this conversation as resolved.
#endif
limit = (MAX_TRACE_SEGMENTS * TRACE_SEGMENT_SIZE);
// Make sure we're not out of space in the trace info table.
if (index == limit)
Expand DownExpand Up@@ -931,6 +942,52 @@ mono_interp_tier_prepare_jiterpreter_fast (
}
}

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod)
{
MonoJitInfo *jinfo = imethod->jinfo;
const guint8 *start;
gboolean need_extra_free = TRUE;

// Erase the method and interpmethod from all of the thread local JIT queues
mono_jiterp_tlqueue_purge_all (method);
mono_jiterp_tlqueue_purge_all (imethod);

// FIXME: Enumerate all active threads and ensure we perform the free_method_data_js
// call on every thread.

// Scan through the interp opcodes for the method and ensure that any jiterp traces
// owned by it are cleaned up. This will automatically clean up any AOT related data for
// the method in the process
if (jinfo) {
start = (const guint8*) jinfo->code_start;
const guint16 *p = (const guint16 *)start,
*end = (const guint16 *)(start + jinfo->code_size);

while (p < end) {
switch (*p) {
case MINT_TIER_PREPARE_JITERPRETER:
case MINT_TIER_NOP_JITERPRETER:
case MINT_TIER_ENTER_JITERPRETER:
case MINT_TIER_MONITOR_JITERPRETER: {
JiterpreterOpcode *opcode = (JiterpreterOpcode *)p;
guint32 trace_index = opcode->trace_index;
need_extra_free = FALSE;
mono_jiterp_free_method_data_js (method, imethod, trace_index);
break;
}
}
p = mono_interp_dis_mintop_len (p);
}
}

if (need_extra_free) {
// HACK: Perform a single free operation to clear out any stuff from the jit queues
// This will happen if we didn't encounter any jiterpreter traces in the method
mono_jiterp_free_method_data_js (method, imethod, 0);
}
}

// Used to parse runtime options that control the jiterpreter. This is *also* used at runtime
// by the jiterpreter typescript to reconfigure the jiterpreter, for example if WASM EH is not
// actually available even though it was enabled (to turn it off).
Expand DownExpand Up@@ -1339,6 +1396,7 @@ mono_jiterp_monitor_trace (const guint16 *ip, void *_frame, void *locals)
if (mono_opt_jiterpreter_trace_monitoring_log > 1)
g_print ("trace #%d @%d '%s' accepted; average_penalty %f <= %f\n", opcode->trace_index, ip, frame->imethod->method->name, average_penalty, threshold);
} else {
// FIXME: Harmless race condition if threads are in use
traces_rejected++;
if (mono_opt_jiterpreter_trace_monitoring_log > 0) {
char * full_name = mono_method_get_full_name (frame->imethod->method);
Expand DownExpand Up@@ -1491,6 +1549,135 @@ mono_jiterp_patch_opcode (volatile JiterpreterOpcode *ip, guint16 old_opcode, gu
#endif
}

/*
* Unordered thread-local pointer queue (used for do_jit_call and interp_entry wrappers)
* The queues are all tracked in a global list so that it is possible to perform a global
* 'purge item with this value from all queues' operation, which means queue operations
* are protected by a mutex
*/

#define NUM_QUEUES 2
static MonoNativeTlsKey queue_keys[NUM_QUEUES] = { 0 };
#ifdef DISABLE_THREADS
gboolean queue_keys_initialized = FALSE;
#else
pthread_once_t queue_keys_initialized = PTHREAD_ONCE_INIT;
#endif
// NOTE: We're using OS mutexes here not coop mutexes, because we need to be able to run
// during a GC and at any point (before runtime startup or after shutdown)
// This means if we aren't careful we could deadlock, so we have to be cautious about
// what operations we perform while holding this mutex.
static mono_mutex_t queue_mutex;
static GPtrArray *shared_queues = NULL;

static void
free_queue (void *ptr) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_remove_fast (shared_queues, ptr);
g_ptr_array_free ((GPtrArray *)ptr, TRUE);
mono_os_mutex_unlock (&queue_mutex);
}

static void
initialize_queue_keys () {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
shared_queues = g_ptr_array_new ();

for (int i = 0; i < NUM_QUEUES; i++)
g_assert (mono_native_tls_alloc (&queue_keys[i], free_queue));

#ifdef DISABLE_THREADS
queue_keys_initialized = TRUE;
#endif

mono_os_mutex_unlock (&queue_mutex);
}

static MonoNativeTlsKey
get_queue_key (int queue) {
g_assert ((queue >= 0) && (queue < NUM_QUEUES));

#ifdef DISABLE_THREADS
if (!queue_keys_initialized)
initialize_queue_keys ();
#else
pthread_once (&queue_keys_initialized, initialize_queue_keys);
#endif

return queue_keys[queue];
}

static GPtrArray *
get_queue (int queue) {
MonoNativeTlsKey key = get_queue_key (queue);
GPtrArray *result = NULL;
if ((result = (GPtrArray *)mono_native_tls_get_value (key)) == NULL) {
g_assert (mono_native_tls_set_value (key, result = g_ptr_array_new ()));
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (shared_queues, result);
mono_os_mutex_unlock (&queue_mutex);
}
return result;
}

// Purges this item from all queues
void
mono_jiterp_tlqueue_purge_all (gpointer item) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
for (int i = 0; i < shared_queues->len; i++) {
GPtrArray *queue = (GPtrArray *)g_ptr_array_index (shared_queues, i);
gboolean ok = g_ptr_array_remove_fast (queue, item);
if (ok) {
// g_printf ("Purged %x from queue %x\n", (unsigned int)item, (unsigned int)queue);
}
}
mono_os_mutex_unlock (&queue_mutex);
}

// Removes the next item from the queue, if any, and returns it (NULL if empty)
EMSCRIPTEN_KEEPALIVE gpointer
mono_jiterp_tlqueue_next (int queue) {
// This lock-per-call is unpleasant, but the queue is usually only going to contain
// a handful of items and will only be enumerated a few hundred times total during
// execution, so performance is not especially important compared to safety/simplicity
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
gpointer result = NULL;
if (items->len) {
result = g_ptr_array_index (items, 0);
g_ptr_array_remove_index_fast (items, 0);
}
mono_os_mutex_unlock (&queue_mutex);
return result;
}

// Adds a new item to the end of the queue and returns the new size of the queue
EMSCRIPTEN_KEEPALIVE int
mono_jiterp_tlqueue_add (int queue, gpointer item) {
int result;
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (items, item);
result = items->len;
mono_os_mutex_unlock (&queue_mutex);
return result;
}

EMSCRIPTEN_KEEPALIVE void
mono_jiterp_tlqueue_clear (int queue) {
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_set_size (items, 0);
mono_os_mutex_unlock (&queue_mutex);
}

// HACK: fix C4206
EMSCRIPTEN_KEEPALIVE
#endif // HOST_BROWSER
Expand Down
11 changes: 11 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -172,6 +172,11 @@ mono_jiterp_do_jit_call_indirect (

#ifdef __MONO_MINI_INTERPRETER_INTERNALS_H__

extern void
mono_jiterp_free_method_data_js (
MonoMethod *method, InterpMethod *imethod, int trace_index
);

typedef struct {
InterpMethod *rmethod;
ThreadContext *context;
Expand DownExpand Up@@ -231,9 +236,15 @@ mono_jiterp_placeholder_trace (void *frame, void *pLocals, JiterpreterCallInfo *
void
mono_jiterp_placeholder_jit_call (void *ret_sp, void *sp, void *ftndesc, gboolean *thrown);

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod);

void *
mono_jiterp_get_interp_entry_func (int table);

void
mono_jiterp_tlqueue_purge_all (gpointer item);

#endif // __MONO_MINI_INTERPRETER_INTERNALS_H__

extern WasmDoJitCall jiterpreter_do_jit_call;
Expand Down
8 changes: 8 additions & 0 deletions src/mono/wasm/runtime/cwraps.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -144,6 +144,9 @@ const fn_signatures: SigLine[] = [
[true, "mono_jiterp_get_interp_entry_func", "number", ["number"]],
[true, "mono_jiterp_get_counter", "number", ["number"]],
[true, "mono_jiterp_modify_counter", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_next", "number", ["number"]],
[true, "mono_jiterp_tlqueue_add", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_clear", "void", ["number"]],

...diagnostics_cwraps,
...legacy_interop_cwraps
Expand DownExpand Up@@ -283,6 +286,11 @@ export interface t_Cwraps {
mono_jiterp_get_interp_entry_func(type: number): number;
mono_jiterp_get_counter(counter: number): number;
mono_jiterp_modify_counter(counter: number, delta: number): number;
// returns value or, if queue is empty, VoidPtrNull
mono_jiterp_tlqueue_next(queue: number): VoidPtr;
// returns new size of queue after add
mono_jiterp_tlqueue_add(queue: number, value: VoidPtr): number;
mono_jiterp_tlqueue_clear(queue: number): void;
}

const wrapped_c_functions: t_Cwraps = <any>{};
Expand Down
5 changes: 3 additions & 2 deletions src/mono/wasm/runtime/exports-binding.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@ import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_s
import { mono_wasm_release_cs_owned_object } from "./gc-handles";
import { mono_wasm_bind_cs_function } from "./invoke-cs";
import { mono_wasm_bind_js_function, mono_wasm_invoke_bound_function, mono_wasm_invoke_import } from "./invoke-js";
import { mono_interp_tier_prepare_jiterpreter } from "./jiterpreter";
import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter";
import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry";
import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, mono_jiterp_do_jit_call_indirect } from "./jiterpreter-jit-call";
import { mono_wasm_marshal_promise } from "./marshal-to-js";
Expand DownExpand Up@@ -89,6 +89,7 @@ export const mono_wasm_imports = [
mono_interp_invoke_wasm_jit_call_trampoline,
mono_interp_flush_jitcall_queue,
mono_jiterp_do_jit_call_indirect,
mono_jiterp_free_method_data_js,

mono_wasm_profiler_enter,
mono_wasm_profiler_leave,
Expand DownExpand Up@@ -163,4 +164,4 @@ export function replace_linker_placeholders(imports: WebAssembly.Imports) {
}
}

}
}
5 changes: 5 additions & 0 deletions src/mono/wasm/runtime/jiterpreter-enums.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -157,3 +157,8 @@ export const BailoutReasonNames = [
"UnexpectedRetIp",
"LeaveCheck",
];

export const enum JitQueue {
JitCall = 0,
InterpEntry = 1
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/mono/mono/mini/interp/interp.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -3459,6 +3459,11 @@ interp_free_method (MonoMethod *method)

jit_mm_lock (jit_mm);
imethod = (InterpMethod*)mono_internal_hash_table_lookup (&jit_mm->interp_code_hash, method);

#if HOST_BROWSER
mono_jiterp_free_method_data (method, imethod);
#endif

mono_internal_hash_table_remove (&jit_mm->interp_code_hash, method);
if (imethod && jit_mm->interp_method_pointer_hash) {
if (imethod->jit_entry)
Expand Down
187 changes: 187 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,10 @@ void jiterp_preserve_module (void);
#include <stdlib.h>
#include <math.h>

#ifndef DISABLE_THREADS
#include <pthread.h>
#endif

#include <mono/metadata/mono-config.h>
#include <mono/utils/mono-threads.h>
#include <mono/metadata/gc-internals.h>
Expand All@@ -48,6 +52,7 @@ void jiterp_preserve_module (void);
#include <mono/mini/llvmonly-runtime.h>
#include <mono/utils/options.h>
#include <mono/utils/atomic.h>
#include <mono/utils/mono-tls.h>

#include "jiterpreter.h"

Expand DownExpand Up@@ -478,6 +483,7 @@ mono_jiterp_type_get_raw_value_size (MonoType *type) {
EMSCRIPTEN_KEEPALIVE void
mono_jiterp_trace_bailout (int reason)
{
// FIXME: Harmless race condition if threads are in use
if (reason < 256)
jiterp_trace_bailout_counts[reason]++;
}
Expand DownExpand Up@@ -670,6 +676,7 @@ should_generate_trace_here (InterpBasicBlock *bb) {
for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) {
int value = jiterp_get_opcode_value(ins, &inside_branch_block);
if (value < 0) {
// FIXME: Harmless race condition if threads are in use
jiterpreter_abort_counts[ins->opcode]++;
return current_trace_value >= mono_opt_jiterpreter_minimum_trace_value;
} else if (value >= VALUE_SIMD) {
Expand DownExpand Up@@ -743,7 +750,11 @@ trace_info_get (gint32 index) {

static gint32
trace_info_alloc () {
#ifdef DISABLE_THREADS
gint32 index = trace_count++,
#else
gint32 index = atomic_fetch_add ((atomic_int *)&trace_count, 1),
Comment thread
lambdageek marked this conversation as resolved.
#endif
limit = (MAX_TRACE_SEGMENTS * TRACE_SEGMENT_SIZE);
// Make sure we're not out of space in the trace info table.
if (index == limit)
Expand DownExpand Up@@ -931,6 +942,52 @@ mono_interp_tier_prepare_jiterpreter_fast (
}
}

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod)
{
MonoJitInfo *jinfo = imethod->jinfo;
const guint8 *start;
gboolean need_extra_free = TRUE;

// Erase the method and interpmethod from all of the thread local JIT queues
mono_jiterp_tlqueue_purge_all (method);
mono_jiterp_tlqueue_purge_all (imethod);

// FIXME: Enumerate all active threads and ensure we perform the free_method_data_js
// call on every thread.

// Scan through the interp opcodes for the method and ensure that any jiterp traces
// owned by it are cleaned up. This will automatically clean up any AOT related data for
// the method in the process
if (jinfo) {
start = (const guint8*) jinfo->code_start;
const guint16 *p = (const guint16 *)start,
*end = (const guint16 *)(start + jinfo->code_size);

while (p < end) {
switch (*p) {
case MINT_TIER_PREPARE_JITERPRETER:
case MINT_TIER_NOP_JITERPRETER:
case MINT_TIER_ENTER_JITERPRETER:
case MINT_TIER_MONITOR_JITERPRETER: {
JiterpreterOpcode *opcode = (JiterpreterOpcode *)p;
guint32 trace_index = opcode->trace_index;
need_extra_free = FALSE;
mono_jiterp_free_method_data_js (method, imethod, trace_index);
break;
}
}
p = mono_interp_dis_mintop_len (p);
}
}

if (need_extra_free) {
// HACK: Perform a single free operation to clear out any stuff from the jit queues
// This will happen if we didn't encounter any jiterpreter traces in the method
mono_jiterp_free_method_data_js (method, imethod, 0);
}
}

// Used to parse runtime options that control the jiterpreter. This is *also* used at runtime
// by the jiterpreter typescript to reconfigure the jiterpreter, for example if WASM EH is not
// actually available even though it was enabled (to turn it off).
Expand DownExpand Up@@ -1339,6 +1396,7 @@ mono_jiterp_monitor_trace (const guint16 *ip, void *_frame, void *locals)
if (mono_opt_jiterpreter_trace_monitoring_log > 1)
g_print ("trace #%d @%d '%s' accepted; average_penalty %f <= %f\n", opcode->trace_index, ip, frame->imethod->method->name, average_penalty, threshold);
} else {
// FIXME: Harmless race condition if threads are in use
traces_rejected++;
if (mono_opt_jiterpreter_trace_monitoring_log > 0) {
char * full_name = mono_method_get_full_name (frame->imethod->method);
Expand DownExpand Up@@ -1491,6 +1549,135 @@ mono_jiterp_patch_opcode (volatile JiterpreterOpcode *ip, guint16 old_opcode, gu
#endif
}

/*
* Unordered thread-local pointer queue (used for do_jit_call and interp_entry wrappers)
* The queues are all tracked in a global list so that it is possible to perform a global
* 'purge item with this value from all queues' operation, which means queue operations
* are protected by a mutex
*/

#define NUM_QUEUES 2
static MonoNativeTlsKey queue_keys[NUM_QUEUES] = { 0 };
#ifdef DISABLE_THREADS
gboolean queue_keys_initialized = FALSE;
#else
pthread_once_t queue_keys_initialized = PTHREAD_ONCE_INIT;
#endif
// NOTE: We're using OS mutexes here not coop mutexes, because we need to be able to run
// during a GC and at any point (before runtime startup or after shutdown)
// This means if we aren't careful we could deadlock, so we have to be cautious about
// what operations we perform while holding this mutex.
static mono_mutex_t queue_mutex;
static GPtrArray *shared_queues = NULL;

static void
free_queue (void *ptr) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_remove_fast (shared_queues, ptr);
g_ptr_array_free ((GPtrArray *)ptr, TRUE);
mono_os_mutex_unlock (&queue_mutex);
}

static void
initialize_queue_keys () {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
shared_queues = g_ptr_array_new ();

for (int i = 0; i < NUM_QUEUES; i++)
g_assert (mono_native_tls_alloc (&queue_keys[i], free_queue));

#ifdef DISABLE_THREADS
queue_keys_initialized = TRUE;
#endif

mono_os_mutex_unlock (&queue_mutex);
}

static MonoNativeTlsKey
get_queue_key (int queue) {
g_assert ((queue >= 0) && (queue < NUM_QUEUES));

#ifdef DISABLE_THREADS
if (!queue_keys_initialized)
initialize_queue_keys ();
#else
pthread_once (&queue_keys_initialized, initialize_queue_keys);
#endif

return queue_keys[queue];
}

static GPtrArray *
get_queue (int queue) {
MonoNativeTlsKey key = get_queue_key (queue);
GPtrArray *result = NULL;
if ((result = (GPtrArray *)mono_native_tls_get_value (key)) == NULL) {
g_assert (mono_native_tls_set_value (key, result = g_ptr_array_new ()));
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (shared_queues, result);
mono_os_mutex_unlock (&queue_mutex);
}
return result;
}

// Purges this item from all queues
void
mono_jiterp_tlqueue_purge_all (gpointer item) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
for (int i = 0; i < shared_queues->len; i++) {
GPtrArray *queue = (GPtrArray *)g_ptr_array_index (shared_queues, i);
gboolean ok = g_ptr_array_remove_fast (queue, item);
if (ok) {
// g_printf ("Purged %x from queue %x\n", (unsigned int)item, (unsigned int)queue);
}
}
mono_os_mutex_unlock (&queue_mutex);
}

// Removes the next item from the queue, if any, and returns it (NULL if empty)
EMSCRIPTEN_KEEPALIVE gpointer
mono_jiterp_tlqueue_next (int queue) {
// This lock-per-call is unpleasant, but the queue is usually only going to contain
// a handful of items and will only be enumerated a few hundred times total during
// execution, so performance is not especially important compared to safety/simplicity
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
gpointer result = NULL;
if (items->len) {
result = g_ptr_array_index (items, 0);
g_ptr_array_remove_index_fast (items, 0);
}
mono_os_mutex_unlock (&queue_mutex);
return result;
}

// Adds a new item to the end of the queue and returns the new size of the queue
EMSCRIPTEN_KEEPALIVE int
mono_jiterp_tlqueue_add (int queue, gpointer item) {
int result;
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (items, item);
result = items->len;
mono_os_mutex_unlock (&queue_mutex);
return result;
}

EMSCRIPTEN_KEEPALIVE void
mono_jiterp_tlqueue_clear (int queue) {
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_set_size (items, 0);
mono_os_mutex_unlock (&queue_mutex);
}

// HACK: fix C4206
EMSCRIPTEN_KEEPALIVE
#endif // HOST_BROWSER
Expand Down
11 changes: 11 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -172,6 +172,11 @@ mono_jiterp_do_jit_call_indirect (

#ifdef __MONO_MINI_INTERPRETER_INTERNALS_H__

extern void
mono_jiterp_free_method_data_js (
MonoMethod *method, InterpMethod *imethod, int trace_index
);

typedef struct {
InterpMethod *rmethod;
ThreadContext *context;
Expand DownExpand Up@@ -231,9 +236,15 @@ mono_jiterp_placeholder_trace (void *frame, void *pLocals, JiterpreterCallInfo *
void
mono_jiterp_placeholder_jit_call (void *ret_sp, void *sp, void *ftndesc, gboolean *thrown);

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod);

void *
mono_jiterp_get_interp_entry_func (int table);

void
mono_jiterp_tlqueue_purge_all (gpointer item);

#endif // __MONO_MINI_INTERPRETER_INTERNALS_H__

extern WasmDoJitCall jiterpreter_do_jit_call;
Expand Down
8 changes: 8 additions & 0 deletions src/mono/wasm/runtime/cwraps.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -144,6 +144,9 @@ const fn_signatures: SigLine[] = [
[true, "mono_jiterp_get_interp_entry_func", "number", ["number"]],
[true, "mono_jiterp_get_counter", "number", ["number"]],
[true, "mono_jiterp_modify_counter", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_next", "number", ["number"]],
[true, "mono_jiterp_tlqueue_add", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_clear", "void", ["number"]],

...diagnostics_cwraps,
...legacy_interop_cwraps
Expand DownExpand Up@@ -283,6 +286,11 @@ export interface t_Cwraps {
mono_jiterp_get_interp_entry_func(type: number): number;
mono_jiterp_get_counter(counter: number): number;
mono_jiterp_modify_counter(counter: number, delta: number): number;
// returns value or, if queue is empty, VoidPtrNull
mono_jiterp_tlqueue_next(queue: number): VoidPtr;
// returns new size of queue after add
mono_jiterp_tlqueue_add(queue: number, value: VoidPtr): number;
mono_jiterp_tlqueue_clear(queue: number): void;
}

const wrapped_c_functions: t_Cwraps = <any>{};
Expand Down
5 changes: 3 additions & 2 deletions src/mono/wasm/runtime/exports-binding.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@ import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_s
import { mono_wasm_release_cs_owned_object } from "./gc-handles";
import { mono_wasm_bind_cs_function } from "./invoke-cs";
import { mono_wasm_bind_js_function, mono_wasm_invoke_bound_function, mono_wasm_invoke_import } from "./invoke-js";
import { mono_interp_tier_prepare_jiterpreter } from "./jiterpreter";
import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter";
import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry";
import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, mono_jiterp_do_jit_call_indirect } from "./jiterpreter-jit-call";
import { mono_wasm_marshal_promise } from "./marshal-to-js";
Expand DownExpand Up@@ -89,6 +89,7 @@ export const mono_wasm_imports = [
mono_interp_invoke_wasm_jit_call_trampoline,
mono_interp_flush_jitcall_queue,
mono_jiterp_do_jit_call_indirect,
mono_jiterp_free_method_data_js,

mono_wasm_profiler_enter,
mono_wasm_profiler_leave,
Expand DownExpand Up@@ -163,4 +164,4 @@ export function replace_linker_placeholders(imports: WebAssembly.Imports) {
}
}

}
}
5 changes: 5 additions & 0 deletions src/mono/wasm/runtime/jiterpreter-enums.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -157,3 +157,8 @@ export const BailoutReasonNames = [
"UnexpectedRetIp",
"LeaveCheck",
];

export const enum JitQueue {
JitCall = 0,
InterpEntry = 1
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/mono/mono/mini/interp/interp.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -3459,6 +3459,11 @@ interp_free_method (MonoMethod *method)

jit_mm_lock (jit_mm);
imethod = (InterpMethod*)mono_internal_hash_table_lookup (&jit_mm->interp_code_hash, method);

#if HOST_BROWSER
mono_jiterp_free_method_data (method, imethod);
#endif

mono_internal_hash_table_remove (&jit_mm->interp_code_hash, method);
if (imethod && jit_mm->interp_method_pointer_hash) {
if (imethod->jit_entry)
Expand Down
187 changes: 187 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,10 @@ void jiterp_preserve_module (void);
#include <stdlib.h>
#include <math.h>

#ifndef DISABLE_THREADS
#include <pthread.h>
#endif

#include <mono/metadata/mono-config.h>
#include <mono/utils/mono-threads.h>
#include <mono/metadata/gc-internals.h>
Expand All@@ -48,6 +52,7 @@ void jiterp_preserve_module (void);
#include <mono/mini/llvmonly-runtime.h>
#include <mono/utils/options.h>
#include <mono/utils/atomic.h>
#include <mono/utils/mono-tls.h>

#include "jiterpreter.h"

Expand DownExpand Up@@ -478,6 +483,7 @@ mono_jiterp_type_get_raw_value_size (MonoType *type) {
EMSCRIPTEN_KEEPALIVE void
mono_jiterp_trace_bailout (int reason)
{
// FIXME: Harmless race condition if threads are in use
if (reason < 256)
jiterp_trace_bailout_counts[reason]++;
}
Expand DownExpand Up@@ -670,6 +676,7 @@ should_generate_trace_here (InterpBasicBlock *bb) {
for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) {
int value = jiterp_get_opcode_value(ins, &inside_branch_block);
if (value < 0) {
// FIXME: Harmless race condition if threads are in use
jiterpreter_abort_counts[ins->opcode]++;
return current_trace_value >= mono_opt_jiterpreter_minimum_trace_value;
} else if (value >= VALUE_SIMD) {
Expand DownExpand Up@@ -743,7 +750,11 @@ trace_info_get (gint32 index) {

static gint32
trace_info_alloc () {
#ifdef DISABLE_THREADS
gint32 index = trace_count++,
#else
gint32 index = atomic_fetch_add ((atomic_int *)&trace_count, 1),
Comment thread
lambdageek marked this conversation as resolved.
#endif
limit = (MAX_TRACE_SEGMENTS * TRACE_SEGMENT_SIZE);
// Make sure we're not out of space in the trace info table.
if (index == limit)
Expand DownExpand Up@@ -931,6 +942,52 @@ mono_interp_tier_prepare_jiterpreter_fast (
}
}

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod)
{
MonoJitInfo *jinfo = imethod->jinfo;
const guint8 *start;
gboolean need_extra_free = TRUE;

// Erase the method and interpmethod from all of the thread local JIT queues
mono_jiterp_tlqueue_purge_all (method);
mono_jiterp_tlqueue_purge_all (imethod);

// FIXME: Enumerate all active threads and ensure we perform the free_method_data_js
// call on every thread.

// Scan through the interp opcodes for the method and ensure that any jiterp traces
// owned by it are cleaned up. This will automatically clean up any AOT related data for
// the method in the process
if (jinfo) {
start = (const guint8*) jinfo->code_start;
const guint16 *p = (const guint16 *)start,
*end = (const guint16 *)(start + jinfo->code_size);

while (p < end) {
switch (*p) {
case MINT_TIER_PREPARE_JITERPRETER:
case MINT_TIER_NOP_JITERPRETER:
case MINT_TIER_ENTER_JITERPRETER:
case MINT_TIER_MONITOR_JITERPRETER: {
JiterpreterOpcode *opcode = (JiterpreterOpcode *)p;
guint32 trace_index = opcode->trace_index;
need_extra_free = FALSE;
mono_jiterp_free_method_data_js (method, imethod, trace_index);
break;
}
}
p = mono_interp_dis_mintop_len (p);
}
}

if (need_extra_free) {
// HACK: Perform a single free operation to clear out any stuff from the jit queues
// This will happen if we didn't encounter any jiterpreter traces in the method
mono_jiterp_free_method_data_js (method, imethod, 0);
}
}

// Used to parse runtime options that control the jiterpreter. This is *also* used at runtime
// by the jiterpreter typescript to reconfigure the jiterpreter, for example if WASM EH is not
// actually available even though it was enabled (to turn it off).
Expand DownExpand Up@@ -1339,6 +1396,7 @@ mono_jiterp_monitor_trace (const guint16 *ip, void *_frame, void *locals)
if (mono_opt_jiterpreter_trace_monitoring_log > 1)
g_print ("trace #%d @%d '%s' accepted; average_penalty %f <= %f\n", opcode->trace_index, ip, frame->imethod->method->name, average_penalty, threshold);
} else {
// FIXME: Harmless race condition if threads are in use
traces_rejected++;
if (mono_opt_jiterpreter_trace_monitoring_log > 0) {
char * full_name = mono_method_get_full_name (frame->imethod->method);
Expand DownExpand Up@@ -1491,6 +1549,135 @@ mono_jiterp_patch_opcode (volatile JiterpreterOpcode *ip, guint16 old_opcode, gu
#endif
}

/*
* Unordered thread-local pointer queue (used for do_jit_call and interp_entry wrappers)
* The queues are all tracked in a global list so that it is possible to perform a global
* 'purge item with this value from all queues' operation, which means queue operations
* are protected by a mutex
*/

#define NUM_QUEUES 2
static MonoNativeTlsKey queue_keys[NUM_QUEUES] = { 0 };
#ifdef DISABLE_THREADS
gboolean queue_keys_initialized = FALSE;
#else
pthread_once_t queue_keys_initialized = PTHREAD_ONCE_INIT;
#endif
// NOTE: We're using OS mutexes here not coop mutexes, because we need to be able to run
// during a GC and at any point (before runtime startup or after shutdown)
// This means if we aren't careful we could deadlock, so we have to be cautious about
// what operations we perform while holding this mutex.
static mono_mutex_t queue_mutex;
static GPtrArray *shared_queues = NULL;

static void
free_queue (void *ptr) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_remove_fast (shared_queues, ptr);
g_ptr_array_free ((GPtrArray *)ptr, TRUE);
mono_os_mutex_unlock (&queue_mutex);
}

static void
initialize_queue_keys () {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
shared_queues = g_ptr_array_new ();

for (int i = 0; i < NUM_QUEUES; i++)
g_assert (mono_native_tls_alloc (&queue_keys[i], free_queue));

#ifdef DISABLE_THREADS
queue_keys_initialized = TRUE;
#endif

mono_os_mutex_unlock (&queue_mutex);
}

static MonoNativeTlsKey
get_queue_key (int queue) {
g_assert ((queue >= 0) && (queue < NUM_QUEUES));

#ifdef DISABLE_THREADS
if (!queue_keys_initialized)
initialize_queue_keys ();
#else
pthread_once (&queue_keys_initialized, initialize_queue_keys);
#endif

return queue_keys[queue];
}

static GPtrArray *
get_queue (int queue) {
MonoNativeTlsKey key = get_queue_key (queue);
GPtrArray *result = NULL;
if ((result = (GPtrArray *)mono_native_tls_get_value (key)) == NULL) {
g_assert (mono_native_tls_set_value (key, result = g_ptr_array_new ()));
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (shared_queues, result);
mono_os_mutex_unlock (&queue_mutex);
}
return result;
}

// Purges this item from all queues
void
mono_jiterp_tlqueue_purge_all (gpointer item) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
for (int i = 0; i < shared_queues->len; i++) {
GPtrArray *queue = (GPtrArray *)g_ptr_array_index (shared_queues, i);
gboolean ok = g_ptr_array_remove_fast (queue, item);
if (ok) {
// g_printf ("Purged %x from queue %x\n", (unsigned int)item, (unsigned int)queue);
}
}
mono_os_mutex_unlock (&queue_mutex);
}

// Removes the next item from the queue, if any, and returns it (NULL if empty)
EMSCRIPTEN_KEEPALIVE gpointer
mono_jiterp_tlqueue_next (int queue) {
// This lock-per-call is unpleasant, but the queue is usually only going to contain
// a handful of items and will only be enumerated a few hundred times total during
// execution, so performance is not especially important compared to safety/simplicity
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
gpointer result = NULL;
if (items->len) {
result = g_ptr_array_index (items, 0);
g_ptr_array_remove_index_fast (items, 0);
}
mono_os_mutex_unlock (&queue_mutex);
return result;
}

// Adds a new item to the end of the queue and returns the new size of the queue
EMSCRIPTEN_KEEPALIVE int
mono_jiterp_tlqueue_add (int queue, gpointer item) {
int result;
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (items, item);
result = items->len;
mono_os_mutex_unlock (&queue_mutex);
return result;
}

EMSCRIPTEN_KEEPALIVE void
mono_jiterp_tlqueue_clear (int queue) {
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_set_size (items, 0);
mono_os_mutex_unlock (&queue_mutex);
}

// HACK: fix C4206
EMSCRIPTEN_KEEPALIVE
#endif // HOST_BROWSER
Expand Down
11 changes: 11 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -172,6 +172,11 @@ mono_jiterp_do_jit_call_indirect (

#ifdef __MONO_MINI_INTERPRETER_INTERNALS_H__

extern void
mono_jiterp_free_method_data_js (
MonoMethod *method, InterpMethod *imethod, int trace_index
);

typedef struct {
InterpMethod *rmethod;
ThreadContext *context;
Expand DownExpand Up@@ -231,9 +236,15 @@ mono_jiterp_placeholder_trace (void *frame, void *pLocals, JiterpreterCallInfo *
void
mono_jiterp_placeholder_jit_call (void *ret_sp, void *sp, void *ftndesc, gboolean *thrown);

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod);

void *
mono_jiterp_get_interp_entry_func (int table);

void
mono_jiterp_tlqueue_purge_all (gpointer item);

#endif // __MONO_MINI_INTERPRETER_INTERNALS_H__

extern WasmDoJitCall jiterpreter_do_jit_call;
Expand Down
8 changes: 8 additions & 0 deletions src/mono/wasm/runtime/cwraps.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -144,6 +144,9 @@ const fn_signatures: SigLine[] = [
[true, "mono_jiterp_get_interp_entry_func", "number", ["number"]],
[true, "mono_jiterp_get_counter", "number", ["number"]],
[true, "mono_jiterp_modify_counter", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_next", "number", ["number"]],
[true, "mono_jiterp_tlqueue_add", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_clear", "void", ["number"]],

...diagnostics_cwraps,
...legacy_interop_cwraps
Expand DownExpand Up@@ -283,6 +286,11 @@ export interface t_Cwraps {
mono_jiterp_get_interp_entry_func(type: number): number;
mono_jiterp_get_counter(counter: number): number;
mono_jiterp_modify_counter(counter: number, delta: number): number;
// returns value or, if queue is empty, VoidPtrNull
mono_jiterp_tlqueue_next(queue: number): VoidPtr;
// returns new size of queue after add
mono_jiterp_tlqueue_add(queue: number, value: VoidPtr): number;
mono_jiterp_tlqueue_clear(queue: number): void;
}

const wrapped_c_functions: t_Cwraps = <any>{};
Expand Down
5 changes: 3 additions & 2 deletions src/mono/wasm/runtime/exports-binding.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@ import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_s
import { mono_wasm_release_cs_owned_object } from "./gc-handles";
import { mono_wasm_bind_cs_function } from "./invoke-cs";
import { mono_wasm_bind_js_function, mono_wasm_invoke_bound_function, mono_wasm_invoke_import } from "./invoke-js";
import { mono_interp_tier_prepare_jiterpreter } from "./jiterpreter";
import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter";
import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry";
import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, mono_jiterp_do_jit_call_indirect } from "./jiterpreter-jit-call";
import { mono_wasm_marshal_promise } from "./marshal-to-js";
Expand DownExpand Up@@ -89,6 +89,7 @@ export const mono_wasm_imports = [
mono_interp_invoke_wasm_jit_call_trampoline,
mono_interp_flush_jitcall_queue,
mono_jiterp_do_jit_call_indirect,
mono_jiterp_free_method_data_js,

mono_wasm_profiler_enter,
mono_wasm_profiler_leave,
Expand DownExpand Up@@ -163,4 +164,4 @@ export function replace_linker_placeholders(imports: WebAssembly.Imports) {
}
}

}
}
5 changes: 5 additions & 0 deletions src/mono/wasm/runtime/jiterpreter-enums.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -157,3 +157,8 @@ export const BailoutReasonNames = [
"UnexpectedRetIp",
"LeaveCheck",
];

export const enum JitQueue {
JitCall = 0,
InterpEntry = 1
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/mono/mono/mini/interp/interp.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -3459,6 +3459,11 @@ interp_free_method (MonoMethod *method)

jit_mm_lock (jit_mm);
imethod = (InterpMethod*)mono_internal_hash_table_lookup (&jit_mm->interp_code_hash, method);

#if HOST_BROWSER
mono_jiterp_free_method_data (method, imethod);
#endif

mono_internal_hash_table_remove (&jit_mm->interp_code_hash, method);
if (imethod && jit_mm->interp_method_pointer_hash) {
if (imethod->jit_entry)
Expand Down
187 changes: 187 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,10 @@ void jiterp_preserve_module (void);
#include <stdlib.h>
#include <math.h>

#ifndef DISABLE_THREADS
#include <pthread.h>
#endif

#include <mono/metadata/mono-config.h>
#include <mono/utils/mono-threads.h>
#include <mono/metadata/gc-internals.h>
Expand All@@ -48,6 +52,7 @@ void jiterp_preserve_module (void);
#include <mono/mini/llvmonly-runtime.h>
#include <mono/utils/options.h>
#include <mono/utils/atomic.h>
#include <mono/utils/mono-tls.h>

#include "jiterpreter.h"

Expand DownExpand Up@@ -478,6 +483,7 @@ mono_jiterp_type_get_raw_value_size (MonoType *type) {
EMSCRIPTEN_KEEPALIVE void
mono_jiterp_trace_bailout (int reason)
{
// FIXME: Harmless race condition if threads are in use
if (reason < 256)
jiterp_trace_bailout_counts[reason]++;
}
Expand DownExpand Up@@ -670,6 +676,7 @@ should_generate_trace_here (InterpBasicBlock *bb) {
for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) {
int value = jiterp_get_opcode_value(ins, &inside_branch_block);
if (value < 0) {
// FIXME: Harmless race condition if threads are in use
jiterpreter_abort_counts[ins->opcode]++;
return current_trace_value >= mono_opt_jiterpreter_minimum_trace_value;
} else if (value >= VALUE_SIMD) {
Expand DownExpand Up@@ -743,7 +750,11 @@ trace_info_get (gint32 index) {

static gint32
trace_info_alloc () {
#ifdef DISABLE_THREADS
gint32 index = trace_count++,
#else
gint32 index = atomic_fetch_add ((atomic_int *)&trace_count, 1),
Comment thread
lambdageek marked this conversation as resolved.
#endif
limit = (MAX_TRACE_SEGMENTS * TRACE_SEGMENT_SIZE);
// Make sure we're not out of space in the trace info table.
if (index == limit)
Expand DownExpand Up@@ -931,6 +942,52 @@ mono_interp_tier_prepare_jiterpreter_fast (
}
}

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod)
{
MonoJitInfo *jinfo = imethod->jinfo;
const guint8 *start;
gboolean need_extra_free = TRUE;

// Erase the method and interpmethod from all of the thread local JIT queues
mono_jiterp_tlqueue_purge_all (method);
mono_jiterp_tlqueue_purge_all (imethod);

// FIXME: Enumerate all active threads and ensure we perform the free_method_data_js
// call on every thread.

// Scan through the interp opcodes for the method and ensure that any jiterp traces
// owned by it are cleaned up. This will automatically clean up any AOT related data for
// the method in the process
if (jinfo) {
start = (const guint8*) jinfo->code_start;
const guint16 *p = (const guint16 *)start,
*end = (const guint16 *)(start + jinfo->code_size);

while (p < end) {
switch (*p) {
case MINT_TIER_PREPARE_JITERPRETER:
case MINT_TIER_NOP_JITERPRETER:
case MINT_TIER_ENTER_JITERPRETER:
case MINT_TIER_MONITOR_JITERPRETER: {
JiterpreterOpcode *opcode = (JiterpreterOpcode *)p;
guint32 trace_index = opcode->trace_index;
need_extra_free = FALSE;
mono_jiterp_free_method_data_js (method, imethod, trace_index);
break;
}
}
p = mono_interp_dis_mintop_len (p);
}
}

if (need_extra_free) {
// HACK: Perform a single free operation to clear out any stuff from the jit queues
// This will happen if we didn't encounter any jiterpreter traces in the method
mono_jiterp_free_method_data_js (method, imethod, 0);
}
}

// Used to parse runtime options that control the jiterpreter. This is *also* used at runtime
// by the jiterpreter typescript to reconfigure the jiterpreter, for example if WASM EH is not
// actually available even though it was enabled (to turn it off).
Expand DownExpand Up@@ -1339,6 +1396,7 @@ mono_jiterp_monitor_trace (const guint16 *ip, void *_frame, void *locals)
if (mono_opt_jiterpreter_trace_monitoring_log > 1)
g_print ("trace #%d @%d '%s' accepted; average_penalty %f <= %f\n", opcode->trace_index, ip, frame->imethod->method->name, average_penalty, threshold);
} else {
// FIXME: Harmless race condition if threads are in use
traces_rejected++;
if (mono_opt_jiterpreter_trace_monitoring_log > 0) {
char * full_name = mono_method_get_full_name (frame->imethod->method);
Expand DownExpand Up@@ -1491,6 +1549,135 @@ mono_jiterp_patch_opcode (volatile JiterpreterOpcode *ip, guint16 old_opcode, gu
#endif
}

/*
* Unordered thread-local pointer queue (used for do_jit_call and interp_entry wrappers)
* The queues are all tracked in a global list so that it is possible to perform a global
* 'purge item with this value from all queues' operation, which means queue operations
* are protected by a mutex
*/

#define NUM_QUEUES 2
static MonoNativeTlsKey queue_keys[NUM_QUEUES] = { 0 };
#ifdef DISABLE_THREADS
gboolean queue_keys_initialized = FALSE;
#else
pthread_once_t queue_keys_initialized = PTHREAD_ONCE_INIT;
#endif
// NOTE: We're using OS mutexes here not coop mutexes, because we need to be able to run
// during a GC and at any point (before runtime startup or after shutdown)
// This means if we aren't careful we could deadlock, so we have to be cautious about
// what operations we perform while holding this mutex.
static mono_mutex_t queue_mutex;
static GPtrArray *shared_queues = NULL;

static void
free_queue (void *ptr) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_remove_fast (shared_queues, ptr);
g_ptr_array_free ((GPtrArray *)ptr, TRUE);
mono_os_mutex_unlock (&queue_mutex);
}

static void
initialize_queue_keys () {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
shared_queues = g_ptr_array_new ();

for (int i = 0; i < NUM_QUEUES; i++)
g_assert (mono_native_tls_alloc (&queue_keys[i], free_queue));

#ifdef DISABLE_THREADS
queue_keys_initialized = TRUE;
#endif

mono_os_mutex_unlock (&queue_mutex);
}

static MonoNativeTlsKey
get_queue_key (int queue) {
g_assert ((queue >= 0) && (queue < NUM_QUEUES));

#ifdef DISABLE_THREADS
if (!queue_keys_initialized)
initialize_queue_keys ();
#else
pthread_once (&queue_keys_initialized, initialize_queue_keys);
#endif

return queue_keys[queue];
}

static GPtrArray *
get_queue (int queue) {
MonoNativeTlsKey key = get_queue_key (queue);
GPtrArray *result = NULL;
if ((result = (GPtrArray *)mono_native_tls_get_value (key)) == NULL) {
g_assert (mono_native_tls_set_value (key, result = g_ptr_array_new ()));
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (shared_queues, result);
mono_os_mutex_unlock (&queue_mutex);
}
return result;
}

// Purges this item from all queues
void
mono_jiterp_tlqueue_purge_all (gpointer item) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
for (int i = 0; i < shared_queues->len; i++) {
GPtrArray *queue = (GPtrArray *)g_ptr_array_index (shared_queues, i);
gboolean ok = g_ptr_array_remove_fast (queue, item);
if (ok) {
// g_printf ("Purged %x from queue %x\n", (unsigned int)item, (unsigned int)queue);
}
}
mono_os_mutex_unlock (&queue_mutex);
}

// Removes the next item from the queue, if any, and returns it (NULL if empty)
EMSCRIPTEN_KEEPALIVE gpointer
mono_jiterp_tlqueue_next (int queue) {
// This lock-per-call is unpleasant, but the queue is usually only going to contain
// a handful of items and will only be enumerated a few hundred times total during
// execution, so performance is not especially important compared to safety/simplicity
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
gpointer result = NULL;
if (items->len) {
result = g_ptr_array_index (items, 0);
g_ptr_array_remove_index_fast (items, 0);
}
mono_os_mutex_unlock (&queue_mutex);
return result;
}

// Adds a new item to the end of the queue and returns the new size of the queue
EMSCRIPTEN_KEEPALIVE int
mono_jiterp_tlqueue_add (int queue, gpointer item) {
int result;
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (items, item);
result = items->len;
mono_os_mutex_unlock (&queue_mutex);
return result;
}

EMSCRIPTEN_KEEPALIVE void
mono_jiterp_tlqueue_clear (int queue) {
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_set_size (items, 0);
mono_os_mutex_unlock (&queue_mutex);
}

// HACK: fix C4206
EMSCRIPTEN_KEEPALIVE
#endif // HOST_BROWSER
Expand Down
11 changes: 11 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -172,6 +172,11 @@ mono_jiterp_do_jit_call_indirect (

#ifdef __MONO_MINI_INTERPRETER_INTERNALS_H__

extern void
mono_jiterp_free_method_data_js (
MonoMethod *method, InterpMethod *imethod, int trace_index
);

typedef struct {
InterpMethod *rmethod;
ThreadContext *context;
Expand DownExpand Up@@ -231,9 +236,15 @@ mono_jiterp_placeholder_trace (void *frame, void *pLocals, JiterpreterCallInfo *
void
mono_jiterp_placeholder_jit_call (void *ret_sp, void *sp, void *ftndesc, gboolean *thrown);

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod);

void *
mono_jiterp_get_interp_entry_func (int table);

void
mono_jiterp_tlqueue_purge_all (gpointer item);

#endif // __MONO_MINI_INTERPRETER_INTERNALS_H__

extern WasmDoJitCall jiterpreter_do_jit_call;
Expand Down
8 changes: 8 additions & 0 deletions src/mono/wasm/runtime/cwraps.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -144,6 +144,9 @@ const fn_signatures: SigLine[] = [
[true, "mono_jiterp_get_interp_entry_func", "number", ["number"]],
[true, "mono_jiterp_get_counter", "number", ["number"]],
[true, "mono_jiterp_modify_counter", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_next", "number", ["number"]],
[true, "mono_jiterp_tlqueue_add", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_clear", "void", ["number"]],

...diagnostics_cwraps,
...legacy_interop_cwraps
Expand DownExpand Up@@ -283,6 +286,11 @@ export interface t_Cwraps {
mono_jiterp_get_interp_entry_func(type: number): number;
mono_jiterp_get_counter(counter: number): number;
mono_jiterp_modify_counter(counter: number, delta: number): number;
// returns value or, if queue is empty, VoidPtrNull
mono_jiterp_tlqueue_next(queue: number): VoidPtr;
// returns new size of queue after add
mono_jiterp_tlqueue_add(queue: number, value: VoidPtr): number;
mono_jiterp_tlqueue_clear(queue: number): void;
}

const wrapped_c_functions: t_Cwraps = <any>{};
Expand Down
5 changes: 3 additions & 2 deletions src/mono/wasm/runtime/exports-binding.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@ import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_s
import { mono_wasm_release_cs_owned_object } from "./gc-handles";
import { mono_wasm_bind_cs_function } from "./invoke-cs";
import { mono_wasm_bind_js_function, mono_wasm_invoke_bound_function, mono_wasm_invoke_import } from "./invoke-js";
import { mono_interp_tier_prepare_jiterpreter } from "./jiterpreter";
import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter";
import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry";
import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, mono_jiterp_do_jit_call_indirect } from "./jiterpreter-jit-call";
import { mono_wasm_marshal_promise } from "./marshal-to-js";
Expand DownExpand Up@@ -89,6 +89,7 @@ export const mono_wasm_imports = [
mono_interp_invoke_wasm_jit_call_trampoline,
mono_interp_flush_jitcall_queue,
mono_jiterp_do_jit_call_indirect,
mono_jiterp_free_method_data_js,

mono_wasm_profiler_enter,
mono_wasm_profiler_leave,
Expand DownExpand Up@@ -163,4 +164,4 @@ export function replace_linker_placeholders(imports: WebAssembly.Imports) {
}
}

}
}
5 changes: 5 additions & 0 deletions src/mono/wasm/runtime/jiterpreter-enums.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -157,3 +157,8 @@ export const BailoutReasonNames = [
"UnexpectedRetIp",
"LeaveCheck",
];

export const enum JitQueue {
JitCall = 0,
InterpEntry = 1
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/mono/mono/mini/interp/interp.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -3459,6 +3459,11 @@ interp_free_method (MonoMethod *method)

jit_mm_lock (jit_mm);
imethod = (InterpMethod*)mono_internal_hash_table_lookup (&jit_mm->interp_code_hash, method);

#if HOST_BROWSER
mono_jiterp_free_method_data (method, imethod);
#endif

mono_internal_hash_table_remove (&jit_mm->interp_code_hash, method);
if (imethod && jit_mm->interp_method_pointer_hash) {
if (imethod->jit_entry)
Expand Down
187 changes: 187 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,10 @@ void jiterp_preserve_module (void);
#include <stdlib.h>
#include <math.h>

#ifndef DISABLE_THREADS
#include <pthread.h>
#endif

#include <mono/metadata/mono-config.h>
#include <mono/utils/mono-threads.h>
#include <mono/metadata/gc-internals.h>
Expand All@@ -48,6 +52,7 @@ void jiterp_preserve_module (void);
#include <mono/mini/llvmonly-runtime.h>
#include <mono/utils/options.h>
#include <mono/utils/atomic.h>
#include <mono/utils/mono-tls.h>

#include "jiterpreter.h"

Expand DownExpand Up@@ -478,6 +483,7 @@ mono_jiterp_type_get_raw_value_size (MonoType *type) {
EMSCRIPTEN_KEEPALIVE void
mono_jiterp_trace_bailout (int reason)
{
// FIXME: Harmless race condition if threads are in use
if (reason < 256)
jiterp_trace_bailout_counts[reason]++;
}
Expand DownExpand Up@@ -670,6 +676,7 @@ should_generate_trace_here (InterpBasicBlock *bb) {
for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) {
int value = jiterp_get_opcode_value(ins, &inside_branch_block);
if (value < 0) {
// FIXME: Harmless race condition if threads are in use
jiterpreter_abort_counts[ins->opcode]++;
return current_trace_value >= mono_opt_jiterpreter_minimum_trace_value;
} else if (value >= VALUE_SIMD) {
Expand DownExpand Up@@ -743,7 +750,11 @@ trace_info_get (gint32 index) {

static gint32
trace_info_alloc () {
#ifdef DISABLE_THREADS
gint32 index = trace_count++,
#else
gint32 index = atomic_fetch_add ((atomic_int *)&trace_count, 1),
Comment thread
lambdageek marked this conversation as resolved.
#endif
limit = (MAX_TRACE_SEGMENTS * TRACE_SEGMENT_SIZE);
// Make sure we're not out of space in the trace info table.
if (index == limit)
Expand DownExpand Up@@ -931,6 +942,52 @@ mono_interp_tier_prepare_jiterpreter_fast (
}
}

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod)
{
MonoJitInfo *jinfo = imethod->jinfo;
const guint8 *start;
gboolean need_extra_free = TRUE;

// Erase the method and interpmethod from all of the thread local JIT queues
mono_jiterp_tlqueue_purge_all (method);
mono_jiterp_tlqueue_purge_all (imethod);

// FIXME: Enumerate all active threads and ensure we perform the free_method_data_js
// call on every thread.

// Scan through the interp opcodes for the method and ensure that any jiterp traces
// owned by it are cleaned up. This will automatically clean up any AOT related data for
// the method in the process
if (jinfo) {
start = (const guint8*) jinfo->code_start;
const guint16 *p = (const guint16 *)start,
*end = (const guint16 *)(start + jinfo->code_size);

while (p < end) {
switch (*p) {
case MINT_TIER_PREPARE_JITERPRETER:
case MINT_TIER_NOP_JITERPRETER:
case MINT_TIER_ENTER_JITERPRETER:
case MINT_TIER_MONITOR_JITERPRETER: {
JiterpreterOpcode *opcode = (JiterpreterOpcode *)p;
guint32 trace_index = opcode->trace_index;
need_extra_free = FALSE;
mono_jiterp_free_method_data_js (method, imethod, trace_index);
break;
}
}
p = mono_interp_dis_mintop_len (p);
}
}

if (need_extra_free) {
// HACK: Perform a single free operation to clear out any stuff from the jit queues
// This will happen if we didn't encounter any jiterpreter traces in the method
mono_jiterp_free_method_data_js (method, imethod, 0);
}
}

// Used to parse runtime options that control the jiterpreter. This is *also* used at runtime
// by the jiterpreter typescript to reconfigure the jiterpreter, for example if WASM EH is not
// actually available even though it was enabled (to turn it off).
Expand DownExpand Up@@ -1339,6 +1396,7 @@ mono_jiterp_monitor_trace (const guint16 *ip, void *_frame, void *locals)
if (mono_opt_jiterpreter_trace_monitoring_log > 1)
g_print ("trace #%d @%d '%s' accepted; average_penalty %f <= %f\n", opcode->trace_index, ip, frame->imethod->method->name, average_penalty, threshold);
} else {
// FIXME: Harmless race condition if threads are in use
traces_rejected++;
if (mono_opt_jiterpreter_trace_monitoring_log > 0) {
char * full_name = mono_method_get_full_name (frame->imethod->method);
Expand DownExpand Up@@ -1491,6 +1549,135 @@ mono_jiterp_patch_opcode (volatile JiterpreterOpcode *ip, guint16 old_opcode, gu
#endif
}

/*
* Unordered thread-local pointer queue (used for do_jit_call and interp_entry wrappers)
* The queues are all tracked in a global list so that it is possible to perform a global
* 'purge item with this value from all queues' operation, which means queue operations
* are protected by a mutex
*/

#define NUM_QUEUES 2
static MonoNativeTlsKey queue_keys[NUM_QUEUES] = { 0 };
#ifdef DISABLE_THREADS
gboolean queue_keys_initialized = FALSE;
#else
pthread_once_t queue_keys_initialized = PTHREAD_ONCE_INIT;
#endif
// NOTE: We're using OS mutexes here not coop mutexes, because we need to be able to run
// during a GC and at any point (before runtime startup or after shutdown)
// This means if we aren't careful we could deadlock, so we have to be cautious about
// what operations we perform while holding this mutex.
static mono_mutex_t queue_mutex;
static GPtrArray *shared_queues = NULL;

static void
free_queue (void *ptr) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_remove_fast (shared_queues, ptr);
g_ptr_array_free ((GPtrArray *)ptr, TRUE);
mono_os_mutex_unlock (&queue_mutex);
}

static void
initialize_queue_keys () {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
shared_queues = g_ptr_array_new ();

for (int i = 0; i < NUM_QUEUES; i++)
g_assert (mono_native_tls_alloc (&queue_keys[i], free_queue));

#ifdef DISABLE_THREADS
queue_keys_initialized = TRUE;
#endif

mono_os_mutex_unlock (&queue_mutex);
}

static MonoNativeTlsKey
get_queue_key (int queue) {
g_assert ((queue >= 0) && (queue < NUM_QUEUES));

#ifdef DISABLE_THREADS
if (!queue_keys_initialized)
initialize_queue_keys ();
#else
pthread_once (&queue_keys_initialized, initialize_queue_keys);
#endif

return queue_keys[queue];
}

static GPtrArray *
get_queue (int queue) {
MonoNativeTlsKey key = get_queue_key (queue);
GPtrArray *result = NULL;
if ((result = (GPtrArray *)mono_native_tls_get_value (key)) == NULL) {
g_assert (mono_native_tls_set_value (key, result = g_ptr_array_new ()));
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (shared_queues, result);
mono_os_mutex_unlock (&queue_mutex);
}
return result;
}

// Purges this item from all queues
void
mono_jiterp_tlqueue_purge_all (gpointer item) {
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
for (int i = 0; i < shared_queues->len; i++) {
GPtrArray *queue = (GPtrArray *)g_ptr_array_index (shared_queues, i);
gboolean ok = g_ptr_array_remove_fast (queue, item);
if (ok) {
// g_printf ("Purged %x from queue %x\n", (unsigned int)item, (unsigned int)queue);
}
}
mono_os_mutex_unlock (&queue_mutex);
}

// Removes the next item from the queue, if any, and returns it (NULL if empty)
EMSCRIPTEN_KEEPALIVE gpointer
mono_jiterp_tlqueue_next (int queue) {
// This lock-per-call is unpleasant, but the queue is usually only going to contain
// a handful of items and will only be enumerated a few hundred times total during
// execution, so performance is not especially important compared to safety/simplicity
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
gpointer result = NULL;
if (items->len) {
result = g_ptr_array_index (items, 0);
g_ptr_array_remove_index_fast (items, 0);
}
mono_os_mutex_unlock (&queue_mutex);
return result;
}

// Adds a new item to the end of the queue and returns the new size of the queue
EMSCRIPTEN_KEEPALIVE int
mono_jiterp_tlqueue_add (int queue, gpointer item) {
int result;
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_add (items, item);
result = items->len;
mono_os_mutex_unlock (&queue_mutex);
return result;
}

EMSCRIPTEN_KEEPALIVE void
mono_jiterp_tlqueue_clear (int queue) {
GPtrArray *items = get_queue (queue);
mono_os_mutex_lock (&queue_mutex);
// WARNING: Ensure we do not call into the runtime or JS while holding this mutex!
g_ptr_array_set_size (items, 0);
mono_os_mutex_unlock (&queue_mutex);
}

// HACK: fix C4206
EMSCRIPTEN_KEEPALIVE
#endif // HOST_BROWSER
Expand Down
11 changes: 11 additions & 0 deletions src/mono/mono/mini/interp/jiterpreter.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -172,6 +172,11 @@ mono_jiterp_do_jit_call_indirect (

#ifdef __MONO_MINI_INTERPRETER_INTERNALS_H__

extern void
mono_jiterp_free_method_data_js (
MonoMethod *method, InterpMethod *imethod, int trace_index
);

typedef struct {
InterpMethod *rmethod;
ThreadContext *context;
Expand DownExpand Up@@ -231,9 +236,15 @@ mono_jiterp_placeholder_trace (void *frame, void *pLocals, JiterpreterCallInfo *
void
mono_jiterp_placeholder_jit_call (void *ret_sp, void *sp, void *ftndesc, gboolean *thrown);

void
mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod);

void *
mono_jiterp_get_interp_entry_func (int table);

void
mono_jiterp_tlqueue_purge_all (gpointer item);

#endif // __MONO_MINI_INTERPRETER_INTERNALS_H__

extern WasmDoJitCall jiterpreter_do_jit_call;
Expand Down
8 changes: 8 additions & 0 deletions src/mono/wasm/runtime/cwraps.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -144,6 +144,9 @@ const fn_signatures: SigLine[] = [
[true, "mono_jiterp_get_interp_entry_func", "number", ["number"]],
[true, "mono_jiterp_get_counter", "number", ["number"]],
[true, "mono_jiterp_modify_counter", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_next", "number", ["number"]],
[true, "mono_jiterp_tlqueue_add", "number", ["number", "number"]],
[true, "mono_jiterp_tlqueue_clear", "void", ["number"]],

...diagnostics_cwraps,
...legacy_interop_cwraps
Expand DownExpand Up@@ -283,6 +286,11 @@ export interface t_Cwraps {
mono_jiterp_get_interp_entry_func(type: number): number;
mono_jiterp_get_counter(counter: number): number;
mono_jiterp_modify_counter(counter: number, delta: number): number;
// returns value or, if queue is empty, VoidPtrNull
mono_jiterp_tlqueue_next(queue: number): VoidPtr;
// returns new size of queue after add
mono_jiterp_tlqueue_add(queue: number, value: VoidPtr): number;
mono_jiterp_tlqueue_clear(queue: number): void;
}

const wrapped_c_functions: t_Cwraps = <any>{};
Expand Down
5 changes: 3 additions & 2 deletions src/mono/wasm/runtime/exports-binding.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@ import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_s
import { mono_wasm_release_cs_owned_object } from "./gc-handles";
import { mono_wasm_bind_cs_function } from "./invoke-cs";
import { mono_wasm_bind_js_function, mono_wasm_invoke_bound_function, mono_wasm_invoke_import } from "./invoke-js";
import { mono_interp_tier_prepare_jiterpreter } from "./jiterpreter";
import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter";
import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry";
import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, mono_jiterp_do_jit_call_indirect } from "./jiterpreter-jit-call";
import { mono_wasm_marshal_promise } from "./marshal-to-js";
Expand DownExpand Up@@ -89,6 +89,7 @@ export const mono_wasm_imports = [
mono_interp_invoke_wasm_jit_call_trampoline,
mono_interp_flush_jitcall_queue,
mono_jiterp_do_jit_call_indirect,
mono_jiterp_free_method_data_js,

mono_wasm_profiler_enter,
mono_wasm_profiler_leave,
Expand DownExpand Up@@ -163,4 +164,4 @@ export function replace_linker_placeholders(imports: WebAssembly.Imports) {
}
}

}
}
5 changes: 5 additions & 0 deletions src/mono/wasm/runtime/jiterpreter-enums.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -157,3 +157,8 @@ export const BailoutReasonNames = [
"UnexpectedRetIp",
"LeaveCheck",
];

export const enum JitQueue {
JitCall = 0,
InterpEntry = 1
}
Loading