vretaddr = TRUE;
ret_type = LLVMVoidType ();
break;
+ case LLVMArgWasmVtypeAsScalar:
+ g_assert (cinfo->ret.esize);
+ ret_type = LLVMIntType (cinfo->ret.esize * 8);
+ break;
default:
break;
}
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);
char *name;
pindex = ainfo->pindex;
+ LLVMValueRef arg = LLVMGetParam (ctx->lmethod, pindex);
switch (ainfo->storage) {
case LLVMArgVtypeInReg:
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);
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]);
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 */
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;
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;
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;
ArgValuetypeAddrOnStack,
ArgGsharedVTOnStack,
ArgValuetypeAddrInIReg,
+ ArgVtypeAsScalar,
ArgInvalid,
} ArgStorage;
typedef struct {
ArgStorage storage : 8;
+ MonoType *type;
} ArgInfo;
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)
{
/* 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;
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;
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;
}
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;
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;