Skip to content

Commit 30226bf

Browse files
authored
[mono][wasm] Rework the handling of GC references in AOTed code. (#59352)
Previously, variables holding GC refs were marked volatile so they were loaded/stored to the C stack on every access. Since the stack is conservatively scanned, all the objects pointed to by it are pinned, so there is no need to load them on every access. Instead of marking them as volatile, allocate a 'gc pinning' area on the stack, and store ref variables to it after they are assigned. This improves code size and performance.
1 parent f697235 commit 30226bf

File tree

3 files changed

+66
-17
lines changed

3 files changed

+66
-17
lines changed

src/mono/mono/mini/ir-emit.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,7 @@ alloc_ireg_ref (MonoCompile *cfg)
6161
mono_mark_vreg_as_ref (cfg, vreg);
6262

6363
#ifdef TARGET_WASM
64-
/*
65-
* For GC stack scanning to work, have to spill all reference variables to the stack.
66-
*/
67-
MonoInst *ins = mono_compile_create_var_for_vreg (cfg, m_class_get_byval_arg (mono_get_object_class ()), OP_LOCAL, vreg);
68-
ins->flags |= MONO_INST_VOLATILE;
64+
mono_mark_vreg_as_ref (cfg, vreg);
6965
#endif
7066

7167
return vreg;

src/mono/mono/mini/mini-llvm.c

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ typedef struct {
193193
char *method_name;
194194
GHashTable *jit_callees;
195195
LLVMValueRef long_bb_break_var;
196+
int *gc_var_indexes;
197+
LLVMValueRef gc_pin_area;
196198
} EmitContext;
197199

198200
typedef struct {
@@ -3732,6 +3734,18 @@ emit_unbox_tramp (EmitContext *ctx, const char *method_name, LLVMTypeRef method_
37323734
LLVMDisposeBuilder (builder);
37333735
}
37343736

3737+
#ifdef TARGET_WASM
3738+
static void
3739+
emit_gc_pin (EmitContext *ctx, LLVMBuilderRef builder, int vreg)
3740+
{
3741+
LLVMValueRef index0 = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
3742+
LLVMValueRef index1 = LLVMConstInt (LLVMInt32Type (), ctx->gc_var_indexes [vreg] - 1, FALSE);
3743+
LLVMValueRef indexes [] = { index0, index1 };
3744+
LLVMValueRef addr = LLVMBuildGEP (builder, ctx->gc_pin_area, indexes, 2, "");
3745+
mono_llvm_build_store (builder, convert (ctx, ctx->values [vreg], IntPtrType ()), addr, TRUE, LLVM_BARRIER_NONE);
3746+
}
3747+
#endif
3748+
37353749
/*
37363750
* emit_entry_bb:
37373751
*
@@ -3752,6 +3766,27 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
37523766

37533767
ctx->alloca_builder = create_builder (ctx);
37543768

3769+
#ifdef TARGET_WASM
3770+
/*
3771+
* For GC stack scanning to work, allocate an area on the stack and store
3772+
* every ref vreg into it after its written. Because the stack is scanned
3773+
* conservatively, the objects will be pinned, so the vregs can directly
3774+
* reference the objects, there is no need to load them from the stack
3775+
* on every access.
3776+
*/
3777+
ctx->gc_var_indexes = g_new0 (int, cfg->next_vreg);
3778+
int ngc_vars = 0;
3779+
for (i = 0; i < cfg->next_vreg; ++i) {
3780+
if (vreg_is_ref (cfg, i)) {
3781+
ctx->gc_var_indexes [i] = ngc_vars + 1;
3782+
ngc_vars ++;
3783+
}
3784+
}
3785+
3786+
// FIXME: Count only live vregs
3787+
ctx->gc_pin_area = build_alloca_llvm_type_name (ctx, LLVMArrayType (IntPtrType (), ngc_vars), 0, "gc_pin");
3788+
#endif
3789+
37553790
/*
37563791
* Handle indirect/volatile variables by allocating memory for them
37573792
* using 'alloca', and storing their address in a temporary.
@@ -3763,13 +3798,6 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
37633798
if ((var->opcode == OP_GSHAREDVT_LOCAL || var->opcode == OP_GSHAREDVT_ARG_REGOFFSET))
37643799
continue;
37653800

3766-
#ifdef TARGET_WASM
3767-
// For GC stack scanning to work, have to spill all reference variables to the stack
3768-
// Some ref variables have type intptr
3769-
if (ctx->has_safepoints && (MONO_TYPE_IS_REFERENCE (var->inst_vtype) || var->inst_vtype->type == MONO_TYPE_I) && var != ctx->cfg->rgctx_var)
3770-
var->flags |= MONO_INST_INDIRECT;
3771-
#endif
3772-
37733801
if (var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (mini_type_is_vtype (var->inst_vtype) && !MONO_CLASS_IS_SIMD (ctx->cfg, var->klass))) {
37743802
vtype = type_to_llvm_type (ctx, var->inst_vtype);
37753803
if (!ctx_ok (ctx))
@@ -3966,6 +3994,19 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
39663994
}
39673995
}
39683996

3997+
#ifdef TARGET_WASM
3998+
/*
3999+
* Store ref arguments to the pin area.
4000+
* FIXME: This might not be needed, since the caller already does it ?
4001+
*/
4002+
for (i = 0; i < cfg->num_varinfo; ++i) {
4003+
MonoInst *var = cfg->varinfo [i];
4004+
4005+
if (var->opcode == OP_ARG && vreg_is_ref (cfg, var->dreg) && ctx->values [var->dreg])
4006+
emit_gc_pin (ctx, builder, var->dreg);
4007+
}
4008+
#endif
4009+
39694010
/* Initialize the method if needed */
39704011
if (cfg->compile_aot) {
39714012
/* Emit a location for the initialization code */
@@ -10998,9 +11039,15 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
1099811039
values [ins->dreg] = convert (ctx, values [ins->dreg], ctx->vreg_types [ins->dreg]);
1099911040
}
1100011041

11001-
/* Add stores for volatile variables */
11002-
if (!skip_volatile_store && spec [MONO_INST_DEST] != ' ' && spec [MONO_INST_DEST] != 'v' && !MONO_IS_STORE_MEMBASE (ins))
11003-
emit_volatile_store (ctx, ins->dreg);
11042+
/* Add stores for volatile/ref variables */
11043+
if (spec [MONO_INST_DEST] != ' ' && spec [MONO_INST_DEST] != 'v' && !MONO_IS_STORE_MEMBASE (ins)) {
11044+
if (!skip_volatile_store)
11045+
emit_volatile_store (ctx, ins->dreg);
11046+
#ifdef TARGET_WASM
11047+
if (vreg_is_ref (cfg, ins->dreg) && ctx->values [ins->dreg])
11048+
emit_gc_pin (ctx, builder, ins->dreg);
11049+
#endif
11050+
}
1100411051
}
1100511052

1100611053
if (!ctx_ok (ctx))
@@ -11152,6 +11199,7 @@ free_ctx (EmitContext *ctx)
1115211199
g_free (ctx->vreg_cli_types);
1115311200
g_free (ctx->is_dead);
1115411201
g_free (ctx->unreachable);
11202+
g_free (ctx->gc_var_indexes);
1115511203
g_ptr_array_free (ctx->phi_values, TRUE);
1115611204
g_free (ctx->bblocks);
1115711205
g_hash_table_destroy (ctx->region_to_handler);
@@ -12015,12 +12063,12 @@ emit_method_inner (EmitContext *ctx)
1201512063
g_free (name);
1201612064
}
1201712065

12018-
/*
12066+
#if 0
1201912067
int err = LLVMVerifyFunction (ctx->lmethod, LLVMPrintMessageAction);
1202012068
if (err != 0)
1202112069
LLVMDumpValue (ctx->lmethod);
1202212070
g_assert (err == 0);
12023-
*/
12071+
#endif
1202412072
} else {
1202512073
//LLVMVerifyFunction (method, 0);
1202612074
llvm_jit_finalize_method (ctx);

src/mono/mono/mini/mini.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,11 @@ mono_compile_create_var_for_vreg (MonoCompile *cfg, MonoType *type, int opcode,
669669
}
670670
}
671671
}
672+
673+
#ifdef TARGET_WASM
674+
if (mini_type_is_reference (type))
675+
mono_mark_vreg_as_ref (cfg, vreg);
676+
#endif
672677

673678
cfg->varinfo [num] = inst;
674679

0 commit comments

Comments
 (0)