#define MINT_TYPE_I MINT_TYPE_I8
#endif
-
-/*
- * GC SAFETY:
- *
- * The interpreter executes in gc unsafe (non-preempt) mode. On wasm, the C stack is
- * scannable but the wasm stack is not, so to make the code GC safe, the following rules
- * should be followed:
- * - every objref handled by the code needs to either be stored volatile or stored
- * into a volatile; volatile stores are stack packable, volatile values are not.
- * Use either OBJREF or stackval->data.o.
- * This will ensure the objects are pinned. A volatile local
- * is on the stack and not in registers. Volatile stores ditto.
- * - minimize the number of MonoObject* locals/arguments (or make them volatile).
- *
- * Volatile on a type/local forces all reads and writes to go to memory/stack,
- * and each such local to have a unique address.
- *
- * Volatile absence on a type/local allows multiple locals to share storage,
- * if their lifetimes do not overlap. This is called "stack packing".
- *
- * Volatile absence on a type/local allows the variable to live in
- * both stack and register, for fast reads and "write through".
- */
#ifdef TARGET_WASM
-
-#define WASM_VOLATILE volatile
-
-static inline MonoObject * WASM_VOLATILE *
-mono_interp_objref (MonoObject **o)
-{
- return o;
-}
-
-#define OBJREF(x) (*mono_interp_objref (&x))
-
-#else
-
-#define WASM_VOLATILE /* nothing */
-
-#define OBJREF(x) x
-
+#define INTERP_NO_STACK_SCAN 1
#endif
-
/*
* Value types are represented on the eval stack as pointers to the
* actual storage. A value type cannot be larger than 16 MB.
} pair;
float f_r4;
double f;
- MonoObject * WASM_VOLATILE o;
+#ifdef INTERP_NO_STACK_SCAN
+ /* Ensure objref is always flushed to interp stack */
+ MonoObject * volatile o;
+#else
+ MonoObject *o;
+#endif
/* native size integer and pointer types */
gpointer p;
mono_u nati;
g_assert (context->has_resume_state);
}
+// We conservatively pin exception object here to avoid tweaking the
+// numerous call sites of this macro, even though, in a few cases,
+// this is not needed.
#define THROW_EX_GENERAL(exception,ex_ip, rethrow) \
do { \
- interp_throw (context, (exception), (frame), (ex_ip), (rethrow)); \
+ MonoException *__ex = (exception); \
+ MONO_HANDLE_ASSIGN_RAW (tmp_handle, (MonoObject*)__ex); \
+ interp_throw (context, __ex, (frame), (ex_ip), (rethrow)); \
+ MONO_HANDLE_ASSIGN_RAW (tmp_handle, (MonoObject*)NULL); \
goto resume; \
} while (0)
}
static int
-mono_interp_box_vt (InterpFrame* frame, const guint16* ip, stackval* sp)
+mono_interp_box_vt (InterpFrame* frame, const guint16* ip, stackval* sp, MonoObjectHandle tmp_handle)
{
InterpMethod* const imethod = frame->imethod;
- MonoObject* o; // See the comment about GC safety.
MonoVTable * const vtable = (MonoVTable*)imethod->data_items [ip [1]];
MonoClass* const c = vtable->klass;
guint16 offset = ip [2];
guint16 pop_vt_sp = !ip [3];
- OBJREF (o) = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass));
+ MonoObject* o = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass));
+ MONO_HANDLE_ASSIGN_RAW (tmp_handle, o);
mono_value_copy_internal (mono_object_get_data (o), sp [-1 - offset].data.p, c);
+ MONO_HANDLE_ASSIGN_RAW (tmp_handle, NULL);
- sp [-1 - offset].data.p = o;
+ sp [-1 - offset].data.o = o;
return pop_vt_sp ? ALIGN_TO (size, MINT_VT_ALIGNMENT) : 0;
}
static void
-mono_interp_box (InterpFrame* frame, const guint16* ip, stackval* sp)
+mono_interp_box (InterpFrame* frame, const guint16* ip, stackval* sp, MonoObjectHandle tmp_handle)
{
- MonoObject *o; // See the comment about GC safety.
MonoVTable * const vtable = (MonoVTable*)frame->imethod->data_items [ip [1]];
-
- OBJREF (o) = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass));
-
guint16 const offset = ip [2];
+ MonoObject *o = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass));
+ MONO_HANDLE_ASSIGN_RAW (tmp_handle, o);
stackval_to_data (m_class_get_byval_arg (vtable->klass), &sp [-1 - offset], mono_object_get_data (o), FALSE);
+ MONO_HANDLE_ASSIGN_RAW (tmp_handle, NULL);
- sp [-1 - offset].data.p = o;
+ sp [-1 - offset].data.o = o;
}
static int
};
#endif
+ HANDLE_FUNCTION_ENTER ();
+ /*
+ * GC SAFETY:
+ *
+ * The interpreter executes in gc unsafe (non-preempt) mode. On wasm, we cannot rely on
+ * scanning the stack or any registers. In order to make the code GC safe, every objref
+ * handled by the code needs to be kept alive and pinned in any of the following ways:
+ * - the object needs to be stored on the interpreter stack. In order to make sure the
+ * object actually gets stored on the interp stack and the store is not optimized out,
+ * the store/variable should be volatile.
+ * - if the execution of an opcode requires an object not coming from interp stack to be
+ * kept alive, the tmp_handle below can be used. This handle will keep only one object
+ * pinned by the GC. Ideally, once this object is no longer needed, the handle should be
+ * cleared. If we will need to have more objects pinned simultaneously, additional handles
+ * can be reserved here.
+ */
+ MonoObjectHandle tmp_handle = MONO_HANDLE_NEW (MonoObject, NULL);
+
if (method_entry (context, frame,
#if DEBUG_INTERP
&tracing,
MINT_IN_CASE(MINT_NEWOBJ_FAST) {
MonoVTable *vtable = (MonoVTable*) frame->imethod->data_items [ip [3]];
INIT_VTABLE (vtable);
- MonoObject *o; // See the comment about GC safety.
guint16 param_count;
guint16 imethod_index = ip [1];
memmove (sp + 2, sp, param_count * sizeof (stackval));
}
- OBJREF (o) = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass));
+ MonoObject *o = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass));
if (G_UNLIKELY (!o)) {
mono_error_set_out_of_memory (error, "Could not allocate %i bytes", m_class_get_instance_size (vtable->klass));
THROW_EX (mono_error_convert_to_exception (error), ip);
THROW_EX (exc, ip);
}
error_init_reuse (error);
- MonoObject* o = NULL; // See the comment about GC safety.
- OBJREF (o) = mono_object_new_checked (domain, newobj_class, error);
+ MonoObject* o = mono_object_new_checked (domain, newobj_class, error);
+ sp [0].data.o = o; // return value
+ sp [1].data.o = o; // first parameter
+
mono_error_cleanup (error); // FIXME: do not swallow the error
error_init_reuse (error);
EXCEPTION_CHECKPOINT;
- sp [0].data.o = o; // return value
- sp [1].data.o = o; // first parameter
#ifndef DISABLE_REMOTING
if (mono_object_is_transparent_proxy (o)) {
MonoMethod *remoting_invoke_method = mono_marshal_get_remoting_invoke_with_check (cmethod->method, error);
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_BOX) {
- mono_interp_box (frame, ip, sp);
+ mono_interp_box (frame, ip, sp, tmp_handle);
ip += 3;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_BOX_VT) {
- vt_sp -= mono_interp_box_vt (frame, ip, sp);
+ vt_sp -= mono_interp_box_vt (frame, ip, sp, tmp_handle);
ip += 4;
MINT_IN_BREAK;
}
context->stack_pointer = (guchar*)frame->stack;
DEBUG_LEAVE ();
+
+ HANDLE_FUNCTION_RETURN ();
}
static void