Skip to content

[mono][wasm] Fix the passing/returning of small vtypes. #62299

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion src/mono/mono/mini/mini-llvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1568,6 +1568,10 @@ sig_to_llvm_sig_full (EmitContext *ctx, MonoMethodSignature *sig, LLVMCallInfo *
vretaddr = TRUE;
ret_type = LLVMVoidType ();
break;
case LLVMArgWasmVtypeAsScalar:
g_assert (cinfo->ret.esize);
ret_type = LLVMIntType (cinfo->ret.esize * 8);
break;
default:
break;
}
Expand Down Expand Up @@ -1685,6 +1689,10 @@ sig_to_llvm_sig_full (EmitContext *ctx, MonoMethodSignature *sig, LLVMCallInfo *
case LLVMArgVtypeAsScalar:
g_assert_not_reached ();
break;
case LLVMArgWasmVtypeAsScalar:
g_assert (ainfo->esize);
param_types [pindex ++] = LLVMIntType (ainfo->esize * 8);
break;
case LLVMArgGsharedvtFixed:
case LLVMArgGsharedvtFixedVtype:
param_types [pindex ++] = LLVMPointerType (type_to_llvm_arg_type (ctx, ainfo->type), 0);
Expand Down Expand Up @@ -3820,6 +3828,7 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
char *name;

pindex = ainfo->pindex;
LLVMValueRef arg = LLVMGetParam (ctx->lmethod, pindex);

switch (ainfo->storage) {
case LLVMArgVtypeInReg:
Expand Down Expand Up @@ -3878,6 +3887,16 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
case LLVMArgVtypeAsScalar:
g_assert_not_reached ();
break;
case LLVMArgWasmVtypeAsScalar: {
MonoType *t = mini_get_underlying_type (ainfo->type);

/* The argument is received as a scalar */
ctx->addresses [reg] = build_alloca (ctx, t);

LLVMValueRef dest = convert (ctx, ctx->addresses [reg], LLVMPointerType (LLVMIntType (ainfo->esize * 8), 0));
LLVMBuildStore (ctx->builder, arg, dest);
break;
}
case LLVMArgGsharedvtFixed: {
/* These are non-gsharedvt arguments passed by ref, the rest of the IR treats them as scalars */
LLVMValueRef arg = LLVMGetParam (ctx->lmethod, pindex);
Expand Down Expand Up @@ -4411,6 +4430,10 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
case LLVMArgVtypeAsScalar:
g_assert_not_reached ();
break;
case LLVMArgWasmVtypeAsScalar:
g_assert (addresses [reg]);
args [pindex] = LLVMBuildLoad (ctx->builder, convert (ctx, addresses [reg], LLVMPointerType (LLVMIntType (ainfo->esize * 8), 0)), "");
break;
case LLVMArgGsharedvtFixed:
case LLVMArgGsharedvtFixedVtype:
g_assert (addresses [reg]);
Expand Down Expand Up @@ -4560,6 +4583,11 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
case LLVMArgGsharedvtFixedVtype:
values [ins->dreg] = LLVMBuildLoad (builder, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (type_to_llvm_type (ctx, sig->ret), 0), FALSE), "");
break;
case LLVMArgWasmVtypeAsScalar:
if (!addresses [call->inst.dreg])
addresses [call->inst.dreg] = build_alloca (ctx, sig->ret);
LLVMBuildStore (builder, lcall, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (LLVMTypeOf (lcall), 0), FALSE));
break;
default:
if (sig->ret->type != MONO_TYPE_VOID)
/* If the method returns an unsigned value, need to zext it */
Expand Down Expand Up @@ -5686,7 +5714,8 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
switch (linfo->ret.storage) {
case LLVMArgNormal:
case LLVMArgVtypeInReg:
case LLVMArgVtypeAsScalar: {
case LLVMArgVtypeAsScalar:
case LLVMArgWasmVtypeAsScalar: {
LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method)));
LLVMValueRef retval = LLVMGetUndef (ret_type);
gboolean src_in_reg = FALSE;
Expand Down Expand Up @@ -5743,6 +5772,10 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
retval = LLVMBuildLoad (builder, LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), ""), "");
}
break;
case LLVMArgWasmVtypeAsScalar:
g_assert (addresses [ins->sreg1]);
retval = LLVMBuildLoad (builder, LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), ""), "");
break;
}
LLVMBuildRet (builder, retval);
break;
Expand Down Expand Up @@ -12158,6 +12191,7 @@ mono_llvm_emit_call (MonoCompile *cfg, MonoCallInst *call)
case LLVMArgGsharedvtVariable:
case LLVMArgGsharedvtFixed:
case LLVMArgGsharedvtFixedVtype:
case LLVMArgWasmVtypeAsScalar:
MONO_INST_NEW (cfg, ins, OP_LLVM_OUTARG_VT);
ins->dreg = mono_alloc_ireg (cfg);
ins->sreg1 = in->dreg;
Expand Down
64 changes: 58 additions & 6 deletions src/mono/mono/mini/mini-wasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ typedef enum {
ArgValuetypeAddrOnStack,
ArgGsharedVTOnStack,
ArgValuetypeAddrInIReg,
ArgVtypeAsScalar,
ArgInvalid,
} ArgStorage;

typedef struct {
ArgStorage storage : 8;
MonoType *type;
} ArgInfo;

struct CallInfo {
Expand All @@ -38,6 +40,45 @@ struct CallInfo {
ArgInfo args [1];
};

/* Return whenever TYPE represents a vtype with only one scalar member */
static gboolean
is_scalar_vtype (MonoType *type)
{
MonoClass *klass;
MonoClassField *field;
gpointer iter;

if (!MONO_TYPE_ISSTRUCT (type))
return FALSE;
klass = mono_class_from_mono_type_internal (type);
mono_class_init_internal (klass);

int size = mono_class_value_size (klass, NULL);
if (size == 0 || size >= 8)
return FALSE;

iter = NULL;
int nfields = 0;
field = NULL;
while ((field = mono_class_get_fields_internal (klass, &iter))) {
if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
continue;
nfields ++;
if (nfields > 1)
return FALSE;
if (MONO_TYPE_ISSTRUCT (field->type)) {
if (!is_scalar_vtype (field->type))
return FALSE;
} else if (!((MONO_TYPE_IS_PRIMITIVE (field->type) || MONO_TYPE_IS_REFERENCE (field->type)))) {
return FALSE;
}
}

return TRUE;
}

// WASM ABI: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md

static ArgStorage
get_storage (MonoType *type, gboolean is_return)
{
Expand Down Expand Up @@ -75,14 +116,14 @@ get_storage (MonoType *type, gboolean is_return)
/* fall through */
case MONO_TYPE_VALUETYPE:
case MONO_TYPE_TYPEDBYREF: {
if (is_scalar_vtype (type))
return ArgVtypeAsScalar;
return is_return ? ArgValuetypeAddrInIReg : ArgValuetypeAddrOnStack;
break;
}
case MONO_TYPE_VAR:
case MONO_TYPE_MVAR:
g_assert (mini_is_gsharedvt_type (type));
return ArgGsharedVTOnStack;
break;
case MONO_TYPE_VOID:
g_assert (is_return);
break;
Expand All @@ -107,7 +148,8 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
cinfo->gsharedvt = mini_is_gsharedvt_variable_signature (sig);

/* return value */
cinfo->ret.storage = get_storage (mini_get_underlying_type (sig->ret), TRUE);
cinfo->ret.type = mini_get_underlying_type (sig->ret);
cinfo->ret.storage = get_storage (cinfo->ret.type, TRUE);

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

int i;
for (i = 0; i < sig->param_count; ++i)
cinfo->args [i + sig->hasthis].storage = get_storage (mini_get_underlying_type (sig->params [i]), FALSE);
for (i = 0; i < sig->param_count; ++i) {
cinfo->args [i + sig->hasthis].type = mini_get_underlying_type (sig->params [i]);
cinfo->args [i + sig->hasthis].storage = get_storage (cinfo->args [i + sig->hasthis].type, FALSE);
}

return cinfo;
}
Expand Down Expand Up @@ -304,7 +348,10 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)

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

if (mini_type_is_vtype (sig->ret)) {
if (cinfo->ret.storage == ArgVtypeAsScalar) {
linfo->ret.storage = LLVMArgWasmVtypeAsScalar;
linfo->ret.esize = mono_class_value_size (mono_class_from_mono_type_internal (cinfo->ret.type), NULL);
} else if (mini_type_is_vtype (sig->ret)) {
/* Vtype returned using a hidden argument */
linfo->ret.storage = LLVMArgVtypeRetAddr;
// linfo->vret_arg_index = cinfo->vret_arg_index;
Expand All @@ -326,6 +373,11 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
case ArgGsharedVTOnStack:
linfo->args [i].storage = LLVMArgGsharedvtVariable;
break;
case ArgVtypeAsScalar:
linfo->args [i].storage = LLVMArgWasmVtypeAsScalar;
linfo->args [i].type = ainfo->type;
linfo->args [i].esize = mono_class_value_size (mono_class_from_mono_type_internal (ainfo->type), NULL);
break;
case ArgValuetypeAddrInIReg:
g_error ("this is only valid for sig->ret");
break;
Expand Down
8 changes: 7 additions & 1 deletion src/mono/mono/mini/mini.h
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,13 @@ typedef enum {
/* Vtype returned as an int */
LLVMArgVtypeAsScalar,
/* Address to local vtype passed as argument (using register or stack). */
LLVMArgVtypeAddr
LLVMArgVtypeAddr,
/*
* On WASM, a one element vtype is passed/returned as a scalar with the same
* type as the element.
* esize is the size of the value.
*/
LLVMArgWasmVtypeAsScalar
} LLVMArgStorage;

typedef struct {
Expand Down