@@ -193,6 +193,8 @@ typedef struct {
193
193
char * method_name ;
194
194
GHashTable * jit_callees ;
195
195
LLVMValueRef long_bb_break_var ;
196
+ int * gc_var_indexes ;
197
+ LLVMValueRef gc_pin_area ;
196
198
} EmitContext ;
197
199
198
200
typedef struct {
@@ -3732,6 +3734,18 @@ emit_unbox_tramp (EmitContext *ctx, const char *method_name, LLVMTypeRef method_
3732
3734
LLVMDisposeBuilder (builder );
3733
3735
}
3734
3736
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
+
3735
3749
/*
3736
3750
* emit_entry_bb:
3737
3751
*
@@ -3752,6 +3766,27 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
3752
3766
3753
3767
ctx -> alloca_builder = create_builder (ctx );
3754
3768
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
+
3755
3790
/*
3756
3791
* Handle indirect/volatile variables by allocating memory for them
3757
3792
* using 'alloca', and storing their address in a temporary.
@@ -3763,13 +3798,6 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
3763
3798
if ((var -> opcode == OP_GSHAREDVT_LOCAL || var -> opcode == OP_GSHAREDVT_ARG_REGOFFSET ))
3764
3799
continue ;
3765
3800
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
-
3773
3801
if (var -> flags & (MONO_INST_VOLATILE |MONO_INST_INDIRECT ) || (mini_type_is_vtype (var -> inst_vtype ) && !MONO_CLASS_IS_SIMD (ctx -> cfg , var -> klass ))) {
3774
3802
vtype = type_to_llvm_type (ctx , var -> inst_vtype );
3775
3803
if (!ctx_ok (ctx ))
@@ -3966,6 +3994,19 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
3966
3994
}
3967
3995
}
3968
3996
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
+
3969
4010
/* Initialize the method if needed */
3970
4011
if (cfg -> compile_aot ) {
3971
4012
/* Emit a location for the initialization code */
@@ -10998,9 +11039,15 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
10998
11039
values [ins -> dreg ] = convert (ctx , values [ins -> dreg ], ctx -> vreg_types [ins -> dreg ]);
10999
11040
}
11000
11041
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
+ }
11004
11051
}
11005
11052
11006
11053
if (!ctx_ok (ctx ))
@@ -11152,6 +11199,7 @@ free_ctx (EmitContext *ctx)
11152
11199
g_free (ctx -> vreg_cli_types );
11153
11200
g_free (ctx -> is_dead );
11154
11201
g_free (ctx -> unreachable );
11202
+ g_free (ctx -> gc_var_indexes );
11155
11203
g_ptr_array_free (ctx -> phi_values , TRUE);
11156
11204
g_free (ctx -> bblocks );
11157
11205
g_hash_table_destroy (ctx -> region_to_handler );
@@ -12015,12 +12063,12 @@ emit_method_inner (EmitContext *ctx)
12015
12063
g_free (name );
12016
12064
}
12017
12065
12018
- /*
12066
+ #if 0
12019
12067
int err = LLVMVerifyFunction (ctx -> lmethod , LLVMPrintMessageAction );
12020
12068
if (err != 0 )
12021
12069
LLVMDumpValue (ctx -> lmethod );
12022
12070
g_assert (err == 0 );
12023
- */
12071
+ #endif
12024
12072
} else {
12025
12073
//LLVMVerifyFunction (method, 0);
12026
12074
llvm_jit_finalize_method (ctx );
0 commit comments