Skip to content

Commit

Permalink
[mono][wasm] Fix the passing/returning of small vtypes. (#62299)
Browse files Browse the repository at this point in the history
According to the ABI, they need to be returned by value.
  • Loading branch information
vargaz authored Dec 3, 2021
1 parent a083a21 commit 915ee6d
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 8 deletions.
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 @@ -3825,6 +3833,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 @@ -3883,6 +3892,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 @@ -4416,6 +4435,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 @@ -4565,6 +4588,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 @@ -5691,7 +5719,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 @@ -5748,6 +5777,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 @@ -12163,6 +12196,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

0 comments on commit 915ee6d

Please sign in to comment.