Skip to content

Commit 6fb6d49

Browse files
committed
[mono][wasm] Fix the passing/returning of small vtypes.
According to the ABI, they need to be returned by value.
1 parent 85642f8 commit 6fb6d49

File tree

3 files changed

+100
-8
lines changed

3 files changed

+100
-8
lines changed

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,10 @@ sig_to_llvm_sig_full (EmitContext *ctx, MonoMethodSignature *sig, LLVMCallInfo *
15681568
vretaddr = TRUE;
15691569
ret_type = LLVMVoidType ();
15701570
break;
1571+
case LLVMArgWasmVtypeAsScalar:
1572+
g_assert (cinfo->ret.esize);
1573+
ret_type = LLVMIntType (cinfo->ret.esize * 8);
1574+
break;
15711575
default:
15721576
break;
15731577
}
@@ -1685,6 +1689,10 @@ sig_to_llvm_sig_full (EmitContext *ctx, MonoMethodSignature *sig, LLVMCallInfo *
16851689
case LLVMArgVtypeAsScalar:
16861690
g_assert_not_reached ();
16871691
break;
1692+
case LLVMArgWasmVtypeAsScalar:
1693+
g_assert (ainfo->esize);
1694+
param_types [pindex ++] = LLVMIntType (ainfo->esize * 8);
1695+
break;
16881696
case LLVMArgGsharedvtFixed:
16891697
case LLVMArgGsharedvtFixedVtype:
16901698
param_types [pindex ++] = LLVMPointerType (type_to_llvm_arg_type (ctx, ainfo->type), 0);
@@ -3820,6 +3828,7 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
38203828
char *name;
38213829

38223830
pindex = ainfo->pindex;
3831+
LLVMValueRef arg = LLVMGetParam (ctx->lmethod, pindex);
38233832

38243833
switch (ainfo->storage) {
38253834
case LLVMArgVtypeInReg:
@@ -3878,6 +3887,16 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
38783887
case LLVMArgVtypeAsScalar:
38793888
g_assert_not_reached ();
38803889
break;
3890+
case LLVMArgWasmVtypeAsScalar: {
3891+
MonoType *t = mini_get_underlying_type (ainfo->type);
3892+
3893+
/* The argument is received as a scalar */
3894+
ctx->addresses [reg] = build_alloca (ctx, t);
3895+
3896+
LLVMValueRef dest = convert (ctx, ctx->addresses [reg], LLVMPointerType (LLVMIntType (ainfo->esize * 8), 0));
3897+
LLVMBuildStore (ctx->builder, arg, dest);
3898+
break;
3899+
}
38813900
case LLVMArgGsharedvtFixed: {
38823901
/* These are non-gsharedvt arguments passed by ref, the rest of the IR treats them as scalars */
38833902
LLVMValueRef arg = LLVMGetParam (ctx->lmethod, pindex);
@@ -4411,6 +4430,10 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
44114430
case LLVMArgVtypeAsScalar:
44124431
g_assert_not_reached ();
44134432
break;
4433+
case LLVMArgWasmVtypeAsScalar:
4434+
g_assert (addresses [reg]);
4435+
args [pindex] = LLVMBuildLoad (ctx->builder, convert (ctx, addresses [reg], LLVMPointerType (LLVMIntType (ainfo->esize * 8), 0)), "");
4436+
break;
44144437
case LLVMArgGsharedvtFixed:
44154438
case LLVMArgGsharedvtFixedVtype:
44164439
g_assert (addresses [reg]);
@@ -4560,6 +4583,11 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
45604583
case LLVMArgGsharedvtFixedVtype:
45614584
values [ins->dreg] = LLVMBuildLoad (builder, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (type_to_llvm_type (ctx, sig->ret), 0), FALSE), "");
45624585
break;
4586+
case LLVMArgWasmVtypeAsScalar:
4587+
if (!addresses [call->inst.dreg])
4588+
addresses [call->inst.dreg] = build_alloca (ctx, sig->ret);
4589+
LLVMBuildStore (builder, lcall, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (LLVMTypeOf (lcall), 0), FALSE));
4590+
break;
45634591
default:
45644592
if (sig->ret->type != MONO_TYPE_VOID)
45654593
/* If the method returns an unsigned value, need to zext it */
@@ -5686,7 +5714,8 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
56865714
switch (linfo->ret.storage) {
56875715
case LLVMArgNormal:
56885716
case LLVMArgVtypeInReg:
5689-
case LLVMArgVtypeAsScalar: {
5717+
case LLVMArgVtypeAsScalar:
5718+
case LLVMArgWasmVtypeAsScalar: {
56905719
LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method)));
56915720
LLVMValueRef retval = LLVMGetUndef (ret_type);
56925721
gboolean src_in_reg = FALSE;
@@ -5743,6 +5772,10 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
57435772
retval = LLVMBuildLoad (builder, LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), ""), "");
57445773
}
57455774
break;
5775+
case LLVMArgWasmVtypeAsScalar:
5776+
g_assert (addresses [ins->sreg1]);
5777+
retval = LLVMBuildLoad (builder, LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), ""), "");
5778+
break;
57465779
}
57475780
LLVMBuildRet (builder, retval);
57485781
break;
@@ -12158,6 +12191,7 @@ mono_llvm_emit_call (MonoCompile *cfg, MonoCallInst *call)
1215812191
case LLVMArgGsharedvtVariable:
1215912192
case LLVMArgGsharedvtFixed:
1216012193
case LLVMArgGsharedvtFixedVtype:
12194+
case LLVMArgWasmVtypeAsScalar:
1216112195
MONO_INST_NEW (cfg, ins, OP_LLVM_OUTARG_VT);
1216212196
ins->dreg = mono_alloc_ireg (cfg);
1216312197
ins->sreg1 = in->dreg;

src/mono/mono/mini/mini-wasm.c

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ typedef enum {
2323
ArgValuetypeAddrOnStack,
2424
ArgGsharedVTOnStack,
2525
ArgValuetypeAddrInIReg,
26+
ArgVtypeAsScalar,
2627
ArgInvalid,
2728
} ArgStorage;
2829

2930
typedef struct {
3031
ArgStorage storage : 8;
32+
MonoType *type;
3133
} ArgInfo;
3234

3335
struct CallInfo {
@@ -38,6 +40,45 @@ struct CallInfo {
3840
ArgInfo args [1];
3941
};
4042

43+
/* Return whenever TYPE represents a vtype with only one scalar member */
44+
static gboolean
45+
is_scalar_vtype (MonoType *type)
46+
{
47+
MonoClass *klass;
48+
MonoClassField *field;
49+
gpointer iter;
50+
51+
if (!MONO_TYPE_ISSTRUCT (type))
52+
return FALSE;
53+
klass = mono_class_from_mono_type_internal (type);
54+
mono_class_init_internal (klass);
55+
56+
int size = mono_class_value_size (klass, NULL);
57+
if (size == 0 || size >= 8)
58+
return FALSE;
59+
60+
iter = NULL;
61+
int nfields = 0;
62+
field = NULL;
63+
while ((field = mono_class_get_fields_internal (klass, &iter))) {
64+
if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
65+
continue;
66+
nfields ++;
67+
if (nfields > 1)
68+
return FALSE;
69+
if (MONO_TYPE_ISSTRUCT (field->type)) {
70+
if (!is_scalar_vtype (field->type))
71+
return FALSE;
72+
} else if (!((MONO_TYPE_IS_PRIMITIVE (field->type) || MONO_TYPE_IS_REFERENCE (field->type)))) {
73+
return FALSE;
74+
}
75+
}
76+
77+
return TRUE;
78+
}
79+
80+
// WASM ABI: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md
81+
4182
static ArgStorage
4283
get_storage (MonoType *type, gboolean is_return)
4384
{
@@ -75,14 +116,14 @@ get_storage (MonoType *type, gboolean is_return)
75116
/* fall through */
76117
case MONO_TYPE_VALUETYPE:
77118
case MONO_TYPE_TYPEDBYREF: {
119+
if (is_scalar_vtype (type))
120+
return ArgVtypeAsScalar;
78121
return is_return ? ArgValuetypeAddrInIReg : ArgValuetypeAddrOnStack;
79-
break;
80122
}
81123
case MONO_TYPE_VAR:
82124
case MONO_TYPE_MVAR:
83125
g_assert (mini_is_gsharedvt_type (type));
84126
return ArgGsharedVTOnStack;
85-
break;
86127
case MONO_TYPE_VOID:
87128
g_assert (is_return);
88129
break;
@@ -107,7 +148,8 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
107148
cinfo->gsharedvt = mini_is_gsharedvt_variable_signature (sig);
108149

109150
/* return value */
110-
cinfo->ret.storage = get_storage (mini_get_underlying_type (sig->ret), TRUE);
151+
cinfo->ret.type = mini_get_underlying_type (sig->ret);
152+
cinfo->ret.storage = get_storage (cinfo->ret.type, TRUE);
111153

112154
if (sig->hasthis)
113155
cinfo->args [0].storage = ArgOnStack;
@@ -116,8 +158,10 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
116158
g_assert (sig->call_convention != MONO_CALL_VARARG);
117159

118160
int i;
119-
for (i = 0; i < sig->param_count; ++i)
120-
cinfo->args [i + sig->hasthis].storage = get_storage (mini_get_underlying_type (sig->params [i]), FALSE);
161+
for (i = 0; i < sig->param_count; ++i) {
162+
cinfo->args [i + sig->hasthis].type = mini_get_underlying_type (sig->params [i]);
163+
cinfo->args [i + sig->hasthis].storage = get_storage (cinfo->args [i + sig->hasthis].type, FALSE);
164+
}
121165

122166
return cinfo;
123167
}
@@ -304,7 +348,10 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
304348

305349
linfo = mono_mempool_alloc0 (cfg->mempool, sizeof (LLVMCallInfo) + (sizeof (LLVMArgInfo) * n));
306350

307-
if (mini_type_is_vtype (sig->ret)) {
351+
if (cinfo->ret.storage == ArgVtypeAsScalar) {
352+
linfo->ret.storage = LLVMArgWasmVtypeAsScalar;
353+
linfo->ret.esize = mono_class_value_size (mono_class_from_mono_type_internal (cinfo->ret.type), NULL);
354+
} else if (mini_type_is_vtype (sig->ret)) {
308355
/* Vtype returned using a hidden argument */
309356
linfo->ret.storage = LLVMArgVtypeRetAddr;
310357
// linfo->vret_arg_index = cinfo->vret_arg_index;
@@ -326,6 +373,11 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
326373
case ArgGsharedVTOnStack:
327374
linfo->args [i].storage = LLVMArgGsharedvtVariable;
328375
break;
376+
case ArgVtypeAsScalar:
377+
linfo->args [i].storage = LLVMArgWasmVtypeAsScalar;
378+
linfo->args [i].type = ainfo->type;
379+
linfo->args [i].esize = mono_class_value_size (mono_class_from_mono_type_internal (ainfo->type), NULL);
380+
break;
329381
case ArgValuetypeAddrInIReg:
330382
g_error ("this is only valid for sig->ret");
331383
break;

src/mono/mono/mini/mini.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,13 @@ typedef enum {
667667
/* Vtype returned as an int */
668668
LLVMArgVtypeAsScalar,
669669
/* Address to local vtype passed as argument (using register or stack). */
670-
LLVMArgVtypeAddr
670+
LLVMArgVtypeAddr,
671+
/*
672+
* On WASM, a one element vtype is passed/returned as a scalar with the same
673+
* type as the element.
674+
* esize is the size of the value.
675+
*/
676+
LLVMArgWasmVtypeAsScalar
671677
} LLVMArgStorage;
672678

673679
typedef struct {

0 commit comments

Comments
 (0)