Skip to content

Commit 915ee6d

Browse files
authored
[mono][wasm] Fix the passing/returning of small vtypes. (#62299)
According to the ABI, they need to be returned by value.
1 parent a083a21 commit 915ee6d

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);
@@ -3825,6 +3833,7 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
38253833
char *name;
38263834

38273835
pindex = ainfo->pindex;
3836+
LLVMValueRef arg = LLVMGetParam (ctx->lmethod, pindex);
38283837

38293838
switch (ainfo->storage) {
38303839
case LLVMArgVtypeInReg:
@@ -3883,6 +3892,16 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
38833892
case LLVMArgVtypeAsScalar:
38843893
g_assert_not_reached ();
38853894
break;
3895+
case LLVMArgWasmVtypeAsScalar: {
3896+
MonoType *t = mini_get_underlying_type (ainfo->type);
3897+
3898+
/* The argument is received as a scalar */
3899+
ctx->addresses [reg] = build_alloca (ctx, t);
3900+
3901+
LLVMValueRef dest = convert (ctx, ctx->addresses [reg], LLVMPointerType (LLVMIntType (ainfo->esize * 8), 0));
3902+
LLVMBuildStore (ctx->builder, arg, dest);
3903+
break;
3904+
}
38863905
case LLVMArgGsharedvtFixed: {
38873906
/* These are non-gsharedvt arguments passed by ref, the rest of the IR treats them as scalars */
38883907
LLVMValueRef arg = LLVMGetParam (ctx->lmethod, pindex);
@@ -4416,6 +4435,10 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
44164435
case LLVMArgVtypeAsScalar:
44174436
g_assert_not_reached ();
44184437
break;
4438+
case LLVMArgWasmVtypeAsScalar:
4439+
g_assert (addresses [reg]);
4440+
args [pindex] = LLVMBuildLoad (ctx->builder, convert (ctx, addresses [reg], LLVMPointerType (LLVMIntType (ainfo->esize * 8), 0)), "");
4441+
break;
44194442
case LLVMArgGsharedvtFixed:
44204443
case LLVMArgGsharedvtFixedVtype:
44214444
g_assert (addresses [reg]);
@@ -4565,6 +4588,11 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
45654588
case LLVMArgGsharedvtFixedVtype:
45664589
values [ins->dreg] = LLVMBuildLoad (builder, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (type_to_llvm_type (ctx, sig->ret), 0), FALSE), "");
45674590
break;
4591+
case LLVMArgWasmVtypeAsScalar:
4592+
if (!addresses [call->inst.dreg])
4593+
addresses [call->inst.dreg] = build_alloca (ctx, sig->ret);
4594+
LLVMBuildStore (builder, lcall, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (LLVMTypeOf (lcall), 0), FALSE));
4595+
break;
45684596
default:
45694597
if (sig->ret->type != MONO_TYPE_VOID)
45704598
/* If the method returns an unsigned value, need to zext it */
@@ -5691,7 +5719,8 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
56915719
switch (linfo->ret.storage) {
56925720
case LLVMArgNormal:
56935721
case LLVMArgVtypeInReg:
5694-
case LLVMArgVtypeAsScalar: {
5722+
case LLVMArgVtypeAsScalar:
5723+
case LLVMArgWasmVtypeAsScalar: {
56955724
LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method)));
56965725
LLVMValueRef retval = LLVMGetUndef (ret_type);
56975726
gboolean src_in_reg = FALSE;
@@ -5748,6 +5777,10 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
57485777
retval = LLVMBuildLoad (builder, LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), ""), "");
57495778
}
57505779
break;
5780+
case LLVMArgWasmVtypeAsScalar:
5781+
g_assert (addresses [ins->sreg1]);
5782+
retval = LLVMBuildLoad (builder, LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), ""), "");
5783+
break;
57515784
}
57525785
LLVMBuildRet (builder, retval);
57535786
break;
@@ -12163,6 +12196,7 @@ mono_llvm_emit_call (MonoCompile *cfg, MonoCallInst *call)
1216312196
case LLVMArgGsharedvtVariable:
1216412197
case LLVMArgGsharedvtFixed:
1216512198
case LLVMArgGsharedvtFixedVtype:
12199+
case LLVMArgWasmVtypeAsScalar:
1216612200
MONO_INST_NEW (cfg, ins, OP_LLVM_OUTARG_VT);
1216712201
ins->dreg = mono_alloc_ireg (cfg);
1216812202
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)