Description
The wasm ABI https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md#function-signatures appears to imply that the following cases will all become scalars and be passed by-value instead of by-reference:
- A struct only containing one F32 (A)
- A struct only containing one F64 (B)
- A struct only containing one I32 (C)
- A struct only containing one I64 (D)
The current PInvoke generator doesn't support any of this (I'm working on fixing it in #94446) so you may need to use my PR in order to test.
At present we only seem to implement case A & case C reliably. By updating mini_wasm_is_scalar_vtype
in mini-wasm.c
we can make case B work* in the AOT compiler but if we also implement case D by allowing I8/U8 types through, we get assertion failures in the AOT compiler:
Assertion at /home/kate/Projects/dotnet-runtime-wasm/src/mono/mono/mini/mini-llvm.c:6052, condition `addresses [ins->sreg1]' not met
* When I say 'work' above I mean 'compile'; the compiled code still does not work right in either the interpreter or AOT:
[wasm test-browser] info: &arg=4ff270 (ulonglong)arg=40091eb851eb851f arg.value.value=3.140000
[wasm test-browser] info: resF=3.14
[wasm test-browser] fail: MONO_WASM: RuntimeError: null function or function signature mismatch
[wasm test-browser] at http://127.0.0.1:38685/_framework/dotnet.native.wasm:wasm-function[96]:0x18168
[wasm test-browser] at http://127.0.0.1:38685/_framework/dotnet.native.wasm:wasm-function[89]:0x17cdb
[wasm test-browser] at http://127.0.0.1:38685/_framework/dotnet.native.wasm:wasm-function[12890]:0x1a2a2a8
[wasm test-browser] at http://127.0.0.1:38685/_framework/dotnet.native.wasm:wasm-function[18247]:0x2ff814a
[wasm test-browser] at http://127.0.0.1:38685/_framework/dotnet.native.wasm:wasm-function[18500]:0x3017cb7
[wasm test-browser] at http://127.0.0.1:38685/_framework/dotnet.native.wasm:wasm-function[18169]:0x2ff5165
[wasm test-browser] at http://127.0.0.1:38685/_framework/dotnet.native.wasm:wasm-function[18159]:0x2fe75f3
[wasm test-browser] at http://127.0.0.1:38685/_framework/dotnet.native.wasm:wasm-function[18202]:0x2ff6272
[wasm test-browser] at http://127.0.0.1:38685/_framework/dotnet.native.wasm:wasm-function[22479]:0x30e192a
[wasm test-browser] at http://127.0.0.1:38685/_framework/dotnet.native.wasm:wasm-function[21430]:0x30ad1ae
In the interpreter, the code will run but either gets garbage or 0 instead of the struct instance:
[wasm test-browser] info: &arg=5d9c68 (ulonglong)arg=40091eb851eb851f arg.value.value=3.140000
[wasm test-browser] info: resF=3.14
[wasm test-browser] info: &arg=5d9988 (ulonglong)arg=0 arg.value.value=0.000000
[wasm test-browser] info: res=0
I think this is because the interpreter has its own logic for identifying scalar vtypes, but when I attempted to fix it that caused all sorts of problems.
Sample code I used for testing:
#include <stdio.h>
typedef struct {
float value;
} TRes;
TRes accept_double_struct_and_return_float_struct (
struct { struct { double value; } value; } arg
) {
printf (
"&arg=%x (ulonglong)arg=%llx arg.value.value=%lf\n",
(unsigned int)&arg, *(unsigned long long*)&arg, (double)arg.value.value
);
TRes result = { arg.value.value };
return result;
}
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
public struct SingleFloatStruct {
public float Value;
}
public struct SingleDoubleStruct {
public struct Nested1 {
// This field is private on purpose to ensure we treat visibility correctly
double Value;
}
public Nested1 Value;
}
public class Test
{
public static unsafe int Main(string[] argv)
{
var resF = direct(3.14);
Console.WriteLine(""resF="" + resF);
SingleDoubleStruct sds = default;
Unsafe.As<SingleDoubleStruct, double>(ref sds) = 3.14; // I tested using pointers instead of Unsafe.As and that also doesn't work
var res = indirect(sds);
Console.WriteLine(""res="" + res.Value);
return (int)res.Value;
}
[DllImport(""wasm-abi"", EntryPoint=""accept_double_struct_and_return_float_struct"")]
public static extern SingleFloatStruct indirect(SingleDoubleStruct arg);
[DllImport(""wasm-abi"", EntryPoint=""accept_double_struct_and_return_float_struct"")]
public static extern float direct(double arg);
}
cc @vargaz