From 6dd24f76a95de11118afd72dfcf9650a46ed9f05 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Fri, 3 Nov 2023 07:26:54 -0700 Subject: [PATCH 01/26] Checkpoint Checkpoint Checkpoint Checkpoint Update tests; make fn pointers explicitly blittable on net8 Checkpoint Checkpoint Update test assertions to handle out-of-process line breaking Csproj no longer needed Repair merge damage Blittable structs Fix broken WBT tests Checkpoint wasm ABI Wasm ABI in SignatureMapper Wasm ABI fixes Add test covering most of the weird parts of the wasm ABI (doesn't currently pass) Checkpoint (this is broken) Revert transform.c changes Split test into both AOT and interp variants Checkpoint Checkpoint: scalarized calls seem to work now but the return value causes crashes Update build_args_from_sig to properly support scalarized struct parameters on wasm Fix scalarized struct return values in interp Add missing file --- .../Common/src/Interop/Interop.Calendar.cs | 9 +- src/mono/mono/mini/aot-runtime-wasm.c | 11 + src/mono/mono/mini/interp/interp.c | 32 ++- src/mono/mono/mini/interp/interp.h | 1 + src/mono/mono/mini/mini-wasm.c | 11 +- src/mono/mono/mini/mini-wasm.h | 3 + .../PInvokeTableGeneratorTests.cs | 158 +++++++++++-- .../wasm/testassets/native-libs/wasm-abi.c | 16 ++ .../ConvertDllsToWebCil.cs | 4 +- ...soft.NET.Sdk.WebAssembly.Pack.Tasks.csproj | 1 + src/tasks/WasmAppBuilder/ExternalTaskHost.cs | 42 ++++ .../WasmAppBuilder/IcallTableGenerator.cs | 11 +- .../WasmAppBuilder/InterpToNativeGenerator.cs | 9 +- src/tasks/WasmAppBuilder/LogAdapter.cs | 69 ++++++ .../ManagedToNativeGenerator.cs | 207 +++++++++++++----- src/tasks/WasmAppBuilder/PInvokeCollector.cs | 31 +-- .../WasmAppBuilder/PInvokeTableGenerator.cs | 113 ++++++++-- src/tasks/WasmAppBuilder/SignatureMapper.cs | 23 +- src/tasks/WasmAppBuilder/WasmAppBuilder.cs | 6 +- .../WasmAppBuilder/WasmAppBuilder.csproj | 20 +- src/tasks/WasmAppBuilder/WebcilConverter.cs | 7 +- 21 files changed, 627 insertions(+), 157 deletions(-) create mode 100644 src/mono/wasm/testassets/native-libs/wasm-abi.c create mode 100644 src/tasks/WasmAppBuilder/ExternalTaskHost.cs create mode 100644 src/tasks/WasmAppBuilder/LogAdapter.cs diff --git a/src/libraries/Common/src/Interop/Interop.Calendar.cs b/src/libraries/Common/src/Interop/Interop.Calendar.cs index 622ecc6522044..e421b16147639 100644 --- a/src/libraries/Common/src/Interop/Interop.Calendar.cs +++ b/src/libraries/Common/src/Interop/Interop.Calendar.cs @@ -15,16 +15,9 @@ internal static partial class Globalization [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetCalendarInfo", StringMarshalling = StringMarshalling.Utf16)] internal static unsafe partial ResultCode GetCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType calendarDataType, char* result, int resultCapacity); - internal static unsafe bool EnumCalendarInfo(delegate* unmanaged callback, string localeName, CalendarId calendarId, CalendarDataType calendarDataType, IntPtr context) - { - return EnumCalendarInfo((IntPtr)callback, localeName, calendarId, calendarDataType, context); - } - [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_EnumCalendarInfo", StringMarshalling = StringMarshalling.Utf16)] [return: MarshalAs(UnmanagedType.Bool)] - // We skip the following DllImport because of 'Parsing function pointer types in signatures is not supported.' for some targeted - // platforms (for example, WASM build). - private static unsafe partial bool EnumCalendarInfo(IntPtr callback, string localeName, CalendarId calendarId, CalendarDataType calendarDataType, IntPtr context); + internal static unsafe partial bool EnumCalendarInfo(delegate* unmanaged callback, string localeName, CalendarId calendarId, CalendarDataType calendarDataType, IntPtr context); [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLatestJapaneseEra")] internal static partial int GetLatestJapaneseEra(); diff --git a/src/mono/mono/mini/aot-runtime-wasm.c b/src/mono/mono/mini/aot-runtime-wasm.c index 2ab4ae75301ce..a3dedc0a6b0a6 100644 --- a/src/mono/mono/mini/aot-runtime-wasm.c +++ b/src/mono/mono/mini/aot-runtime-wasm.c @@ -14,6 +14,9 @@ #ifdef HOST_WASM +MonoType * +mini_wasm_get_scalar_vtype (MonoType *type); + static char type_to_c (MonoType *t) { @@ -54,6 +57,14 @@ type_to_c (MonoType *t) goto handle_enum; } + // https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md#function-signatures + // Any struct or union that recursively (including through nested structs, unions, and arrays) + // contains just a single scalar value and is not specified to have greater than natural alignment. + // FIXME: Handle the scenario where there are fields of struct types that contain no members + MonoType *scalar_vtype = mini_wasm_get_scalar_vtype (t); + if (scalar_vtype) + return type_to_c (scalar_vtype); + return 'I'; case MONO_TYPE_GENERICINST: if (m_class_is_valuetype (t->data.klass)) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 1e6903672b475..2fddf262b0a19 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -1334,6 +1334,20 @@ static MonoFuncV mono_native_to_interp_trampoline = NULL; #endif #ifndef MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP +static MonoType* +filter_type_for_build_args_from_sig (MonoType *type) +{ +#ifdef HOST_WASM + // If the parameter type is scalarized according to WASM C ABI, treat it as the + // scalarized type. Otherwise the MONO_TYPE_VALUETYPE case will treat it as int32. + MonoType *scalar = mini_wasm_get_scalar_vtype (type); + if (scalar) + return scalar; +#endif + + return type; +} + static InterpMethodArguments* build_args_from_sig (MonoMethodSignature *sig, InterpFrame *frame) { @@ -1352,7 +1366,9 @@ build_args_from_sig (MonoMethodSignature *sig, InterpFrame *frame) margs->ilen++; for (int i = 0; i < sig->param_count; i++) { - guint32 ptype = m_type_is_byref (sig->params [i]) ? MONO_TYPE_PTR : sig->params [i]->type; + MonoType *param_type = filter_type_for_build_args_from_sig (sig->params [i]); + guint32 ptype = m_type_is_byref (param_type) ? MONO_TYPE_PTR : param_type->type; + switch (ptype) { case MONO_TYPE_BOOLEAN: case MONO_TYPE_CHAR: @@ -1423,7 +1439,7 @@ build_args_from_sig (MonoMethodSignature *sig, InterpFrame *frame) for (int i = 0; i < sig->param_count; i++) { guint32 offset = get_arg_offset (frame->imethod, sig, i); stackval *sp_arg = STACK_ADD_BYTES (frame->stack, offset); - MonoType *type = sig->params [i]; + MonoType *type = filter_type_for_build_args_from_sig (sig->params [i]); guint32 ptype; retry: ptype = m_type_is_byref (type) ? MONO_TYPE_PTR : type->type; @@ -1516,7 +1532,9 @@ build_args_from_sig (MonoMethodSignature *sig, InterpFrame *frame) } } - switch (sig->ret->type) { + margs->ret_type = filter_type_for_build_args_from_sig (sig->ret); + + switch (margs->ret_type->type) { case MONO_TYPE_BOOLEAN: case MONO_TYPE_CHAR: case MONO_TYPE_I1: @@ -1549,7 +1567,7 @@ build_args_from_sig (MonoMethodSignature *sig, InterpFrame *frame) margs->retval = NULL; break; default: - g_error ("build_args_from_sig: ret type not implemented yet: 0x%x\n", sig->ret->type); + g_error ("build_args_from_sig: ret type not implemented yet: 0x%x\n", margs->ret_type->type); } return margs; @@ -1721,8 +1739,8 @@ ves_pinvoke_method ( g_free (ccontext.stack); #else // Only the vt address has been returned, we need to copy the entire content on interp stack - if (!context->has_resume_state && MONO_TYPE_ISSTRUCT (sig->ret)) - stackval_from_data (sig->ret, frame.retval, (char*)frame.retval->data.p, sig->pinvoke && !sig->marshalling_disabled); + if (!context->has_resume_state && MONO_TYPE_ISSTRUCT (margs->ret_type)) + stackval_from_data (margs->ret_type, frame.retval, (char*)frame.retval->data.p, sig->pinvoke && !sig->marshalling_disabled); g_free (margs->iargs); g_free (margs->fargs); @@ -4182,7 +4200,7 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause LOCAL_VAR (call_args_offset, gpointer) = unboxed; } -jit_call: +jit_call: { InterpMethodCodeType code_type = cmethod->code_type; diff --git a/src/mono/mono/mini/interp/interp.h b/src/mono/mono/mini/interp/interp.h index e4dcae36407cf..8fde76b322b7e 100644 --- a/src/mono/mono/mini/interp/interp.h +++ b/src/mono/mono/mini/interp/interp.h @@ -26,6 +26,7 @@ struct _InterpMethodArguments { #ifdef TARGET_WASM // FIXME HOST MonoMethodSignature *sig; #endif + MonoType *ret_type; }; enum { diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c index e0849ba463623..de06365c534d4 100644 --- a/src/mono/mono/mini/mini-wasm.c +++ b/src/mono/mono/mini/mini-wasm.c @@ -743,6 +743,7 @@ mono_wasm_get_debug_level (void) gboolean mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype) { + MonoType *result = NULL; MonoClass *klass; MonoClassField *field; gpointer iter; @@ -751,7 +752,7 @@ mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype) *etype = NULL; if (!MONO_TYPE_ISSTRUCT (type)) - return FALSE; + return NULL; klass = mono_class_from_mono_type_internal (type); mono_class_init_internal (klass); @@ -766,8 +767,12 @@ mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype) if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) continue; nfields ++; - if (nfields > 1) - return FALSE; + if (nfields > 1) { + /* + g_fprintf (stderr, "Struct %s has too many fields to be scalar vtype\n", m_class_get_name (klass)); + */ + return NULL; + } MonoType *t = mini_get_underlying_type (field->type); if (MONO_TYPE_ISSTRUCT (t)) { if (!mini_wasm_is_scalar_vtype (t, etype)) diff --git a/src/mono/mono/mini/mini-wasm.h b/src/mono/mono/mini/mini-wasm.h index 77c7f3a78fa5c..53ffdf053e85b 100644 --- a/src/mono/mono/mini/mini-wasm.h +++ b/src/mono/mono/mini/mini-wasm.h @@ -106,6 +106,9 @@ void mono_wasm_main_thread_schedule_timer (void *timerHandler, int shortestDueTi void mono_wasm_print_stack_trace (void); +MonoType * +mini_wasm_get_scalar_vtype (MonoType *type); + gboolean mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype); diff --git a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs index 179d769d1e662..3c8b2e37818fc 100644 --- a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -50,7 +51,9 @@ public static int Main(string[] args) buildArgs with { ProjectName = $"variadic_{buildArgs.Config}_{id}" }, id); Assert.Matches("warning.*native function.*sum.*varargs", output); - Assert.Matches("warning.*sum_(one|two|three)", output); + Assert.Contains("System.Int32 sum_one(System.Int32)", output); + Assert.Contains("System.Int32 sum_two(System.Int32, System.Int32)", output); + Assert.Contains("System.Int32 sum_three(System.Int32, System.Int32, System.Int32)", output); output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); Assert.Contains("Main running", output); @@ -58,7 +61,7 @@ public static int Main(string[] args) [Theory] [BuildAndRun(host: RunHost.Chrome)] - public void DllImportWithFunctionPointersCompilesWithWarning(BuildArgs buildArgs, RunHost host, string id) + public void DllImportWithFunctionPointersCompilesWithoutWarning(BuildArgs buildArgs, RunHost host, string id) { string code = """ @@ -84,8 +87,8 @@ public static int Main() buildArgs with { ProjectName = $"fnptr_{buildArgs.Config}_{id}" }, id); - Assert.Matches("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); - Assert.Matches("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); + Assert.DoesNotMatch("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); + Assert.DoesNotMatch("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); Assert.Contains("Main running", output); @@ -93,7 +96,7 @@ public static int Main() [Theory] [BuildAndRun(host: RunHost.Chrome)] - public void DllImportWithFunctionPointers_ForVariadicFunction_CompilesWithWarning(BuildArgs buildArgs, RunHost host, string id) + public void DllImportWithFunctionPointers_ForVariadicFunction_CompilesWithoutWarning(BuildArgs buildArgs, RunHost host, string id) { string code = @" using System; @@ -114,8 +117,8 @@ public static int Main() buildArgs with { ProjectName = $"fnptr_variadic_{buildArgs.Config}_{id}" }, id); - Assert.Matches("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); - Assert.Matches("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); + Assert.DoesNotMatch("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); + Assert.DoesNotMatch("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); Assert.Contains("Main running", output); @@ -128,6 +131,7 @@ public void UnmanagedStructAndMethodIn_SameAssembly_WithoutDisableRuntimeMarshal { (_, string output) = SingleProjectForDisabledRuntimeMarshallingTest( withDisabledRuntimeMarshallingAttribute: false, + withAutoLayout: true, expectSuccess: false, buildArgs, id @@ -136,6 +140,22 @@ public void UnmanagedStructAndMethodIn_SameAssembly_WithoutDisableRuntimeMarshal Assert.Matches("error.*Parameter.*types.*pinvoke.*.*blittable", output); } + [Theory] + [BuildAndRun(host: RunHost.None)] + public void UnmanagedStructAndMethodIn_SameAssembly_WithoutDisableRuntimeMarshallingAttribute_WithStructLayout_ConsideredBlittable + (BuildArgs buildArgs, string id) + { + (_, string output) = SingleProjectForDisabledRuntimeMarshallingTest( + withDisabledRuntimeMarshallingAttribute: false, + withAutoLayout: false, + expectSuccess: true, + buildArgs, + id + ); + + Assert.DoesNotMatch("error.*Parameter.*types.*pinvoke.*.*blittable", output); + } + [Theory] [BuildAndRun(host: RunHost.Chrome)] public void UnmanagedStructAndMethodIn_SameAssembly_WithDisableRuntimeMarshallingAttribute_ConsideredBlittable @@ -143,6 +163,7 @@ public void UnmanagedStructAndMethodIn_SameAssembly_WithDisableRuntimeMarshallin { (buildArgs, _) = SingleProjectForDisabledRuntimeMarshallingTest( withDisabledRuntimeMarshallingAttribute: true, + withAutoLayout: true, expectSuccess: true, buildArgs, id @@ -152,8 +173,10 @@ public void UnmanagedStructAndMethodIn_SameAssembly_WithDisableRuntimeMarshallin Assert.Contains("Main running 5", output); } - private (BuildArgs buildArgs ,string output) SingleProjectForDisabledRuntimeMarshallingTest(bool withDisabledRuntimeMarshallingAttribute, bool expectSuccess, BuildArgs buildArgs, string id) - { + private (BuildArgs buildArgs ,string output) SingleProjectForDisabledRuntimeMarshallingTest( + bool withDisabledRuntimeMarshallingAttribute, bool withAutoLayout, + bool expectSuccess, BuildArgs buildArgs, string id + ) { string code = """ using System; @@ -171,8 +194,10 @@ public static int Main() Console.WriteLine("Main running " + x.Value); return 42; } - - public struct S { public int Value; } + """ + + (withAutoLayout ? "\n[StructLayout(LayoutKind.Auto)]\n" : "") + + """ + public struct S { public int Value; public float Value2; } [UnmanagedCallersOnly] public static void M(S myStruct) { } @@ -230,7 +255,7 @@ private void SeparateAssembliesForDisableRuntimeMarshallingTest { string code = (libraryHasAttribute ? "[assembly: System.Runtime.CompilerServices.DisableRuntimeMarshalling]" : "") - + "public struct S { public int Value; }"; + + "public struct __NonBlittableTypeForAutomatedTests__ { } public struct S { public int Value; public __NonBlittableTypeForAutomatedTests__ NonBlittable; }"; var libraryBuildArgs = ExpandBuildArgs( buildArgs with { ProjectName = $"blittable_different_library_{buildArgs.Config}_{id}" }, @@ -256,6 +281,7 @@ private void SeparateAssembliesForDisableRuntimeMarshallingTest using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + """ + (appHasAttribute ? "[assembly: DisableRuntimeMarshalling]" : "") + """ @@ -347,7 +373,7 @@ public static int Main() [Theory] [BuildAndRun(host: RunHost.None)] - public void UnmanagedCallback_WithFunctionPointers_CompilesWithWarnings(BuildArgs buildArgs, string id) + public void UnmanagedCallback_WithFunctionPointers_CompilesWithoutWarnings(BuildArgs buildArgs, string id) { string code = """ @@ -372,7 +398,7 @@ public static int Main() id ); - Assert.Matches("warning\\sWASM0001.*Skipping.*Test::SomeFunction1.*because.*function\\spointer", output); + Assert.DoesNotMatch("warning\\sWASM0001.*Skipping.*Test::SomeFunction1.*because.*function\\spointer", output); } [Theory] @@ -406,7 +432,7 @@ file class Foo ); Assert.DoesNotMatch(".*(warning|error).*>[A-Z0-9]+__Foo", output); - + output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); Assert.Contains("Main running", output); } @@ -692,5 +718,107 @@ void GenerateSourceFiles(string outputPath, int baseArg) return (buildArgs, output); } + + private void EnsureWasmAbiRulesAreFollowed(BuildArgs buildArgs, RunHost host, string id) + { + string programText = @" + 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(""f (d)="" + resF); + + SingleDoubleStruct sds = default; + Unsafe.As(ref sds) = 3.14; + + resF = indirect_arg(sds); + Console.WriteLine(""f (s)="" + resF); + + // FIXME: This crashes + var res = indirect(sds); + Console.WriteLine(""s (s)="" + 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 indirect_arg(SingleDoubleStruct arg); + + [DllImport(""wasm-abi"", EntryPoint=""accept_double_struct_and_return_float_struct"")] + public static extern float direct(double arg); + }"; + + var extraProperties = "true<_WasmDevel>true"; + var extraItems = @""; + /* + extraItems += @" +"; + */ + + buildArgs = ExpandBuildArgs(buildArgs, + extraItems: extraItems, + extraProperties: extraProperties); + + (string libraryDir, string output) = BuildProject(buildArgs, + id: id, + new BuildProjectOptions( + InitProject: () => + { + File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText); + File.Copy(Path.Combine(BuildEnvironment.TestAssetsPath, "native-libs", "wasm-abi.c"), + Path.Combine(_projectDir!, "wasm-abi.c")); + }, + Publish: buildArgs.AOT, + DotnetWasmFromRuntimePack: false)); + + string objDir = Path.Combine(_projectDir!, "obj", buildArgs.Config!, "net9.0", "browser-wasm", "wasm", buildArgs.AOT ? "for-publish" : "for-build"); + Console.WriteLine($"objDir={objDir} buildArgs={buildArgs}"); + + // Verify that the right signature was added for the pinvoke. We can't determine this by examining the m2n file + Assert.Contains("Adding pinvoke signature FD for method 'Test.", output); + + string pinvokeTable = File.ReadAllText(Path.Combine(objDir, "pinvoke-table.h")); + // Verify that the invoke is in the pinvoke table. Under various circumstances we will silently skip it, + // for example if the module isn't found + Assert.Contains("\"accept_double_struct_and_return_float_struct\", accept_double_struct_and_return_float_struct", pinvokeTable); + // Verify the signature of the C function prototype. Wasm ABI specifies that the structs should both decompose into scalars. + Assert.Contains("float accept_double_struct_and_return_float_struct (double);", pinvokeTable); + + var runOutput = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 3, host: host, id: id); + Assert.Contains("f (d)=3.14", runOutput); + Assert.Contains("f (s)=3.14", runOutput); + Assert.Contains("s (s)=3.14", runOutput); + } + + [Theory] + [BuildAndRun(host: RunHost.Chrome, aot: true)] + public void EnsureWasmAbiRulesAreFollowedInAOT(BuildArgs buildArgs, RunHost host, string id) => + EnsureWasmAbiRulesAreFollowed(buildArgs, host, id); + + [Theory] + [BuildAndRun(host: RunHost.Chrome, aot: false)] + public void EnsureWasmAbiRulesAreFollowedInInterpreter(BuildArgs buildArgs, RunHost host, string id) => + EnsureWasmAbiRulesAreFollowed(buildArgs, host, id); } } diff --git a/src/mono/wasm/testassets/native-libs/wasm-abi.c b/src/mono/wasm/testassets/native-libs/wasm-abi.c new file mode 100644 index 0000000000000..5b6d8ccd033de --- /dev/null +++ b/src/mono/wasm/testassets/native-libs/wasm-abi.c @@ -0,0 +1,16 @@ +#include + +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; +} diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs index acbe2214bce42..2d21f3820a558 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs @@ -5,6 +5,7 @@ using System.IO; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using WasmAppBuilder; namespace Microsoft.NET.Sdk.WebAssembly; @@ -69,7 +70,8 @@ public override bool Execute() if (Utils.IsNewerThan(dllFilePath, finalWebcil)) { var tmpWebcil = Path.Combine(tmpDir, webcilFileName); - var webcilWriter = Microsoft.WebAssembly.Build.Tasks.WebcilConverter.FromPortableExecutable(inputPath: dllFilePath, outputPath: tmpWebcil, logger: Log); + var logAdapter = new LogAdapter(Log); + var webcilWriter = Microsoft.WebAssembly.Build.Tasks.WebcilConverter.FromPortableExecutable(inputPath: dllFilePath, outputPath: tmpWebcil, logger: logAdapter); webcilWriter.ConvertToWebcil(); if (!Directory.Exists(candidatePath)) diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks.csproj b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks.csproj index 4d329dff0242f..072900772b149 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks.csproj +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks.csproj @@ -19,6 +19,7 @@ + diff --git a/src/tasks/WasmAppBuilder/ExternalTaskHost.cs b/src/tasks/WasmAppBuilder/ExternalTaskHost.cs new file mode 100644 index 0000000000000..fc2ded591915f --- /dev/null +++ b/src/tasks/WasmAppBuilder/ExternalTaskHost.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Text.Json; +using System.Linq; + +namespace WasmAppBuilder; + +public static class Program { + public static int Main (string[] args) { + if (args.Length != 1) + throw new ArgumentException("Expected 'wasmappbuilder [response file]'"); + if (!File.Exists(args[0])) + throw new FileNotFoundException("Response file not found: " + args[0]); + var rspText = File.ReadAllText(args[0]); + var rsp = JsonSerializer.Deserialize(rspText); + var logAdapter = new LogAdapter(); + + try { + Microsoft.WebAssembly.Build.Tasks.ManagedToNativeGenerator.ExecuteForAssemblies( + logAdapter, + rsp.managedAssemblies, + rsp.runtimeIcallTableFile, + rsp.pInvokeModules, + rsp.pInvokeOutputPath, + rsp.icallOutputPath, + rsp.interpToNativeOutputPath, + rsp.cacheFilePath + ); + } catch (LogAsErrorException e) { + logAdapter.Error(e.Message); + return 2; + } + + if (logAdapter.HasLoggedErrors) + return 1; + else + return 0; + } +} diff --git a/src/tasks/WasmAppBuilder/IcallTableGenerator.cs b/src/tasks/WasmAppBuilder/IcallTableGenerator.cs index 16a29d67f5200..0de857cdcf31d 100644 --- a/src/tasks/WasmAppBuilder/IcallTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/IcallTableGenerator.cs @@ -10,6 +10,7 @@ using System.Reflection; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using WasmAppBuilder; internal sealed class IcallTableGenerator { @@ -19,7 +20,7 @@ internal sealed class IcallTableGenerator private readonly HashSet _signatures = new(); private Dictionary _runtimeIcalls = new Dictionary(); - private TaskLoggingHelper Log { get; set; } + private LogAdapter Log { get; set; } private readonly Func _fixupSymbolName; // @@ -28,7 +29,7 @@ internal sealed class IcallTableGenerator // The runtime icall table should be generated using // mono --print-icall-table // - public IcallTableGenerator(string? runtimeIcallTableFile, Func fixupSymbolName, TaskLoggingHelper log) + public IcallTableGenerator(string? runtimeIcallTableFile, Func fixupSymbolName, LogAdapter log) { Log = log; _fixupSymbolName = fixupSymbolName; @@ -148,7 +149,7 @@ private void ProcessType(Type type) } catch (Exception ex) when (ex is not LogAsErrorException) { - Log.LogWarning(null, "WASM0001", "", "", 0, 0, 0, 0, $"Could not get icall, or callbacks for method '{type.FullName}::{method.Name}' because '{ex.Message}'"); + Log.Warning("WASM0001", $"Could not get icall, or callbacks for method '{type.FullName}::{method.Name}' because '{ex.Message}'"); continue; } @@ -200,7 +201,7 @@ private void ProcessType(Type type) } catch (NotImplementedException nie) { - Log.LogWarning(null, "WASM0001", "", "", 0, 0, 0, 0, $"Failed to generate icall function for method '[{method.DeclaringType!.Assembly.GetName().Name}] {className}::{method.Name}'" + + Log.Warning("WASM0001", $"Failed to generate icall function for method '[{method.DeclaringType!.Assembly.GetName().Name}] {className}::{method.Name}'" + $" because type '{nie.Message}' is not supported for parameter named '{par.Name}'. Ignoring."); return null; } @@ -213,7 +214,7 @@ private void ProcessType(Type type) void AddSignature(Type type, MethodInfo method) { - string? signature = SignatureMapper.MethodToSignature(method); + string? signature = SignatureMapper.MethodToSignature(method, Log); if (signature == null) { throw new LogAsErrorException($"Unsupported parameter type in method '{type.FullName}.{method.Name}'"); diff --git a/src/tasks/WasmAppBuilder/InterpToNativeGenerator.cs b/src/tasks/WasmAppBuilder/InterpToNativeGenerator.cs index 9c23012ab6035..21b23eb764305 100644 --- a/src/tasks/WasmAppBuilder/InterpToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/InterpToNativeGenerator.cs @@ -10,6 +10,7 @@ using Microsoft.Build.Utilities; using Microsoft.Build.Framework; using System.Diagnostics.CodeAnalysis; +using WasmAppBuilder; // // This class generates the icall_trampoline_dispatch () function used by the interpreter to call native code on WASM. @@ -20,9 +21,9 @@ internal sealed class InterpToNativeGenerator { - private TaskLoggingHelper Log { get; set; } + private LogAdapter Log { get; set; } - public InterpToNativeGenerator(TaskLoggingHelper log) => Log = log; + public InterpToNativeGenerator(LogAdapter log) => Log = log; public void Generate(IEnumerable cookies, string outputPath) { @@ -136,8 +137,8 @@ static int { return strcmp (key, *(void**)elem); } - - static void* + + static void* mono_wasm_interp_to_native_callback (char* cookie) { void* p = bsearch (cookie, interp_to_native_signatures, interp_to_native_signatures_count, sizeof (void*), compare_icall_tramp); diff --git a/src/tasks/WasmAppBuilder/LogAdapter.cs b/src/tasks/WasmAppBuilder/LogAdapter.cs new file mode 100644 index 0000000000000..95f005f657f75 --- /dev/null +++ b/src/tasks/WasmAppBuilder/LogAdapter.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using Microsoft.Build.Utilities; +using Microsoft.Build.Framework; + +namespace WasmAppBuilder { + public sealed class LogAdapter { + public bool HasLoggedErrors { + get => Helper?.HasLoggedErrors ?? _HasLoggedErrors; + } + private bool _HasLoggedErrors; + private TaskLoggingHelper? Helper; + private TextWriter? Output, ErrorOutput; + + public LogAdapter (TaskLoggingHelper helper) { + Helper = helper; + Output = null; + ErrorOutput = null; + } + + public LogAdapter () { + Helper = null; + Output = Console.Out; + ErrorOutput = Console.Error; + } + + private static string AutoFormat (string s, object[] o) { + if ((o?.Length ?? 0) > 0) + return string.Format(s!, o!); + else + return s; + } + + public void LogMessage (string s, params object[] o) { + Helper?.LogMessage(s, o); + Output?.WriteLine(AutoFormat(s, o)); + } + + public void LogMessage (MessageImportance mi, string s, params object[] o) { + Helper?.LogMessage(mi, s, o); + Output?.WriteLine(AutoFormat(s, o)); + } + + public void Info (string code, string message, params object[] args) { + Helper?.LogMessage(null, code, null, null, 0, 0, 0, 0, MessageImportance.Low, message, args); + Output?.WriteLine($"info : {code}: {AutoFormat(message, args)}"); + } + + public void Warning (string code, string message, params object[] args) { + Helper?.LogWarning(null, code, null, null, 0, 0, 0, 0, message, args); + ErrorOutput?.WriteLine($"warning : {code}: {AutoFormat(message, args)}"); + } + + public void Error (string message) { + Helper?.LogError(message); + ErrorOutput?.WriteLine($"error : {message}"); + _HasLoggedErrors = true; + } + + public void Error (string code, string message, params object[] args) { + Helper?.LogError(null, code, null, null, 0, 0, 0, 0, message, args); + ErrorOutput?.WriteLine($"error : {code}: {AutoFormat(message, args)}"); + _HasLoggedErrors = true; + } + } +} diff --git a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs index b5bf1f70457ab..789a55e4c6da7 100644 --- a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs @@ -8,8 +8,10 @@ using System.Linq; using System.Reflection; using System.Text; +using System.Text.Json; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using WasmAppBuilder; namespace Microsoft.WebAssembly.Build.Tasks; @@ -37,9 +39,6 @@ public class ManagedToNativeGenerator : Task private static readonly char[] s_charsToReplace = new[] { '.', '-', '+', '<', '>' }; - // Avoid sharing this cache with all the invocations of this task throughout the build - private readonly Dictionary _symbolNameFixups = new(); - public override bool Execute() { if (Assemblies!.Length == 0) @@ -56,7 +55,8 @@ public override bool Execute() try { - ExecuteInternal(); + var logAdapter = new LogAdapter(Log); + ExecuteInternal(logAdapter); return !Log.HasLoggedErrors; } catch (LogAsErrorException e) @@ -66,33 +66,80 @@ public override bool Execute() } } - private void ExecuteInternal() + // dotnet/roslyn src/Compilers/Shared/RuntimeHostInfo.cs + + private const string DotNetHostPathEnvironmentName = "DOTNET_HOST_PATH"; + + /// + /// Get the path to the dotnet executable. In the case the .NET SDK did not provide this information + /// in the environment this tries to find "dotnet" on the PATH. In the case it is not found, + /// this will return simply "dotnet". + /// + internal static string GetDotNetPathOrDefault() { - List managedAssemblies = FilterOutUnmanagedBinaries(Assemblies); - if (ShouldRun(managedAssemblies)) + if (Environment.GetEnvironmentVariable(DotNetHostPathEnvironmentName) is string pathToDotNet) { - var pinvoke = new PInvokeTableGenerator(FixupSymbolName, Log); - var icall = new IcallTableGenerator(RuntimeIcallTableFile, FixupSymbolName, Log); + return pathToDotNet; + } - var resolver = new PathAssemblyResolver(managedAssemblies); - using var mlc = new MetadataLoadContext(resolver, "System.Private.CoreLib"); - foreach (string asmPath in managedAssemblies) + var (fileName, sep) = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform( + System.Runtime.InteropServices.OSPlatform.Windows + ) + ? ("dotnet.exe", ';') + : ("dotnet", ':'); + + var path = Environment.GetEnvironmentVariable("PATH") ?? ""; + foreach (var item in path.Split(sep)) + { + if (string.IsNullOrEmpty(item)) + continue; + + try { - Log.LogMessage(MessageImportance.Low, $"Loading {asmPath} to scan for pinvokes, and icalls"); - Assembly asm = mlc.LoadFromAssemblyPath(asmPath); - pinvoke.ScanAssembly(asm); - icall.ScanAssembly(asm); + var filePath = Path.Combine(item, fileName); + if (File.Exists(filePath)) + { + return filePath; + } } + catch + { + // If we can't read a directory for any reason just skip it + } + } - IEnumerable cookies = Enumerable.Concat( - pinvoke.Generate(PInvokeModules, PInvokeOutputPath), - icall.Generate(IcallOutputPath)); - - var m2n = new InterpToNativeGenerator(Log); - m2n.Generate(cookies, InterpToNativeOutputPath); + return fileName; + } - if (!string.IsNullOrEmpty(CacheFilePath)) - File.WriteAllLines(CacheFilePath, PInvokeModules); + private void ExecuteInternal(LogAdapter logAdapter) + { + List managedAssemblies = FilterOutUnmanagedBinaries(Assemblies); + if (ShouldRun(managedAssemblies)) + { + var rspPath = Path.GetTempFileName(); + var rsp = new ExecuteArguments( + managedAssemblies.Select(Path.GetFullPath).ToArray(), + RuntimeIcallTableFile, + PInvokeModules, + PInvokeOutputPath, + IcallOutputPath, InterpToNativeOutputPath, + CacheFilePath + ); + var rspText = JsonSerializer.Serialize(rsp); + File.WriteAllText(rspPath, rspText); + var dotnetPath = GetDotNetPathOrDefault(); + + (int exitCode, string output) = Utils.TryRunProcess( + Log, + dotnetPath, + $"{GetType().Assembly.Location} {rspPath}", + envVars: null, + workingDir: null, + silent: false, + logStdErrAsMessage: true + ); + if (exitCode != 0) + logAdapter.Error("WASM0066", $"WasmAppBuilder external process failed with exit code {exitCode}!"); } List fileWritesList = new() { PInvokeOutputPath, InterpToNativeOutputPath }; @@ -104,6 +151,85 @@ private void ExecuteInternal() FileWrites = fileWritesList.ToArray(); } + internal readonly record struct ExecuteArguments ( + IList managedAssemblies, + string? runtimeIcallTableFile, + string[] pInvokeModules, + string pInvokeOutputPath, + string? icallOutputPath, + string interpToNativeOutputPath, + string? cacheFilePath + ); + + internal static void ExecuteForAssemblies( + LogAdapter logAdapter, + IList managedAssemblies, + string? runtimeIcallTableFile, + string[] pInvokeModules, + string pInvokeOutputPath, + string? icallOutputPath, + string interpToNativeOutputPath, + string? cacheFilePath + ) { + Dictionary _symbolNameFixups = new(); + + var pinvoke = new PInvokeTableGenerator(FixupSymbolName, logAdapter); + var icall = new IcallTableGenerator(runtimeIcallTableFile, FixupSymbolName, logAdapter); + + var resolver = new PathAssemblyResolver(managedAssemblies); + using var mlc = new MetadataLoadContext(resolver, "System.Private.CoreLib"); + foreach (string asmPath in managedAssemblies) + { + logAdapter.LogMessage(MessageImportance.Low, $"Loading {asmPath} to scan for pinvokes, and icalls"); + Assembly asm = mlc.LoadFromAssemblyPath(asmPath); + pinvoke.ScanAssembly(asm); + icall.ScanAssembly(asm); + } + + IEnumerable cookies = Enumerable.Concat( + pinvoke.Generate(pInvokeModules, pInvokeOutputPath), + icall.Generate(icallOutputPath)); + + var m2n = new InterpToNativeGenerator(logAdapter); + m2n.Generate(cookies, interpToNativeOutputPath); + + if (!string.IsNullOrEmpty(cacheFilePath)) + File.WriteAllLines(cacheFilePath, pInvokeModules); + + string FixupSymbolName(string name) + { + if (_symbolNameFixups.TryGetValue(name, out string? fixedName)) + return fixedName; + + UTF8Encoding utf8 = new(); + byte[] bytes = utf8.GetBytes(name); + StringBuilder sb = new(); + + foreach (byte b in bytes) + { + if ((b >= (byte)'0' && b <= (byte)'9') || + (b >= (byte)'a' && b <= (byte)'z') || + (b >= (byte)'A' && b <= (byte)'Z') || + (b == (byte)'_')) + { + sb.Append((char)b); + } + else if (s_charsToReplace.Contains((char)b)) + { + sb.Append('_'); + } + else + { + sb.Append($"_{b:X}_"); + } + } + + fixedName = sb.ToString(); + _symbolNameFixups[name] = fixedName; + return fixedName; + } + } + private bool ShouldRun(IList managedAssemblies) { if (string.IsNullOrEmpty(CacheFilePath) || !File.Exists(CacheFilePath)) @@ -158,39 +284,6 @@ bool CheckShouldRunBecauseOfOutputFile(string? path, ref DateTime oldestDt) } } - public string FixupSymbolName(string name) - { - if (_symbolNameFixups.TryGetValue(name, out string? fixedName)) - return fixedName; - - UTF8Encoding utf8 = new(); - byte[] bytes = utf8.GetBytes(name); - StringBuilder sb = new(); - - foreach (byte b in bytes) - { - if ((b >= (byte)'0' && b <= (byte)'9') || - (b >= (byte)'a' && b <= (byte)'z') || - (b >= (byte)'A' && b <= (byte)'Z') || - (b == (byte)'_')) - { - sb.Append((char)b); - } - else if (s_charsToReplace.Contains((char)b)) - { - sb.Append('_'); - } - else - { - sb.Append($"_{b:X}_"); - } - } - - fixedName = sb.ToString(); - _symbolNameFixups[name] = fixedName; - return fixedName; - } - private List FilterOutUnmanagedBinaries(string[] assemblies) { List managedAssemblies = new(assemblies.Length); diff --git a/src/tasks/WasmAppBuilder/PInvokeCollector.cs b/src/tasks/WasmAppBuilder/PInvokeCollector.cs index 6c26c3a7be979..03d264c020804 100644 --- a/src/tasks/WasmAppBuilder/PInvokeCollector.cs +++ b/src/tasks/WasmAppBuilder/PInvokeCollector.cs @@ -9,6 +9,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Microsoft.Build.Tasks; +using WasmAppBuilder; #pragma warning disable CA1067 #pragma warning disable CS0649 @@ -56,9 +57,9 @@ public int GetHashCode(PInvoke pinvoke) internal sealed class PInvokeCollector { private readonly Dictionary _assemblyDisableRuntimeMarshallingAttributeCache = new(); - private TaskLoggingHelper Log { get; init; } + private LogAdapter Log { get; init; } - public PInvokeCollector(TaskLoggingHelper log) + public PInvokeCollector(LogAdapter log) { Log = log; } @@ -70,13 +71,12 @@ public void CollectPInvokes(List pinvokes, List callba try { CollectPInvokesForMethod(method); - if (DoesMethodHaveCallbacks(method)) + if (DoesMethodHaveCallbacks(method, Log)) callbacks.Add(new PInvokeCallback(method)); } catch (Exception ex) when (ex is not LogAsErrorException) { - Log.LogWarning(null, "WASM0001", "", "", 0, 0, 0, 0, - $"Could not get pinvoke, or callbacks for method '{type.FullName}::{method.Name}' because '{ex.Message}'"); + Log.Warning("WASM0001", $"Could not get pinvoke, or callbacks for method '{type.FullName}::{method.Name}' because '{ex}'"); } } @@ -86,7 +86,7 @@ public void CollectPInvokes(List pinvokes, List callba if (method != null) { - string? signature = SignatureMapper.MethodToSignature(method!); + string? signature = SignatureMapper.MethodToSignature(method!, Log); if (signature == null) throw new NotSupportedException($"Unsupported parameter type in method '{type.FullName}.{method.Name}'"); @@ -104,7 +104,7 @@ void CollectPInvokesForMethod(MethodInfo method) var entrypoint = (string)dllimport.NamedArguments.First(arg => arg.MemberName == "EntryPoint").TypedValue.Value!; pinvokes.Add(new PInvoke(entrypoint, module, method)); - string? signature = SignatureMapper.MethodToSignature(method); + string? signature = SignatureMapper.MethodToSignature(method, Log); if (signature == null) { throw new NotSupportedException($"Unsupported parameter type in method '{type.FullName}.{method.Name}'"); @@ -115,15 +115,14 @@ void CollectPInvokesForMethod(MethodInfo method) } } - bool DoesMethodHaveCallbacks(MethodInfo method) + bool DoesMethodHaveCallbacks(MethodInfo method, LogAdapter log) { if (!MethodHasCallbackAttributes(method)) return false; if (TryIsMethodGetParametersUnsupported(method, out string? reason)) { - Log.LogWarning(null, "WASM0001", "", "", 0, 0, 0, 0, - $"Skipping callback '{method.DeclaringType!.FullName}::{method.Name}' because '{reason}'."); + Log.Warning("WASM0001", $"Skipping callback '{method.DeclaringType!.FullName}::{method.Name}' because '{reason}'."); return false; } @@ -133,12 +132,12 @@ bool DoesMethodHaveCallbacks(MethodInfo method) // No DisableRuntimeMarshalling attribute, so check if the params/ret-type are // blittable bool isVoid = method.ReturnType.FullName == "System.Void"; - if (!isVoid && !IsBlittable(method.ReturnType)) + if (!isVoid && !IsBlittable(method.ReturnType, log)) Error($"The return type '{method.ReturnType.FullName}' of pinvoke callback method '{method}' needs to be blittable."); foreach (var p in method.GetParameters()) { - if (!IsBlittable(p.ParameterType)) + if (!IsBlittable(p.ParameterType, log)) Error("Parameter types of pinvoke callback method '" + method + "' needs to be blittable."); } @@ -167,13 +166,7 @@ static bool MethodHasCallbackAttributes(MethodInfo method) } } - public static bool IsBlittable(Type type) - { - if (type.IsPrimitive || type.IsByRef || type.IsPointer || type.IsEnum) - return true; - else - return false; - } + public static bool IsBlittable(Type type, LogAdapter log) => PInvokeTableGenerator.IsBlittable(type, log); private static void Error(string msg) => throw new LogAsErrorException(msg); diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index 2665508c9dfa5..7648d4ab81130 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -8,21 +8,23 @@ using System.Linq; using System.Text; using System.Reflection; +using System.Runtime.InteropServices; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using WasmAppBuilder; internal sealed class PInvokeTableGenerator { private readonly Dictionary _assemblyDisableRuntimeMarshallingAttributeCache = new(); - private TaskLoggingHelper Log { get; set; } + private LogAdapter Log { get; set; } private readonly Func _fixupSymbolName; private readonly HashSet signatures = new(); private readonly List pinvokes = new(); private readonly List callbacks = new(); private readonly PInvokeCollector _pinvokeCollector; - public PInvokeTableGenerator(Func fixupSymbolName, TaskLoggingHelper log) + public PInvokeTableGenerator(Func fixupSymbolName, LogAdapter log) { Log = log; _fixupSymbolName = fixupSymbolName; @@ -106,7 +108,7 @@ private void EmitPInvokeTable(StreamWriter w, Dictionary modules string imports = string.Join(Environment.NewLine, candidates.Select( p => $" {p.Method} (in [{p.Method.DeclaringType?.Assembly.GetName().Name}] {p.Method.DeclaringType})")); - Log.LogWarning(null, "WASM0001", "", "", 0, 0, 0, 0, $"Found a native function ({first.EntryPoint}) with varargs in {first.Module}." + + Log.Warning("WASM0001", $"Found a native function ({first.EntryPoint}) with varargs in {first.Module}." + " Calling such functions is not supported, and will fail at runtime." + $" Managed DllImports: {Environment.NewLine}{imports}"); @@ -197,9 +199,40 @@ private string SymbolNameForMethod(MethodInfo method) nameof(Single) => "float", nameof(Int64) => "int64_t", nameof(UInt64) => "uint64_t", - _ => "int" + nameof(Int32) => "int32_t", + nameof(UInt32) => "uint32_t", + nameof(Int16) => "int32_t", + nameof(UInt16) => "uint32_t", + nameof(Char) => "int32_t", + nameof(Boolean) => "int32_t", + nameof(SByte) => "int32_t", + nameof(Byte) => "uint32_t", + nameof(IntPtr) => "void *", + nameof(UIntPtr) => "void *", + _ => PickCTypeNameForUnknownType(t) }; + private static string PickCTypeNameForUnknownType (Type t) { + // Pass objects by-reference (their address by-value) + if (!t.IsValueType) + return "void *"; + // Pass pointers and function pointers by-value + else if (t.IsPointer || IsFunctionPointer(t)) + return "void *"; + else if (t.IsPrimitive) + throw new NotImplementedException("No native type mapping for type " + t); + + // https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md#function-signatures + // Any struct or union that recursively (including through nested structs, unions, and arrays) + // contains just a single scalar value and is not specified to have greater than natural alignment. + // FIXME: Handle the scenario where there are fields of struct types that contain no members + var fields = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (fields.Length == 1) + return MapType(fields[0].FieldType); + else + return "void *"; + } + // FIXME: System.Reflection.MetadataLoadContext can't decode function pointer types // https://github.com/dotnet/runtime/issues/43791 private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotNullWhen(true)] out string? reason) @@ -226,20 +259,12 @@ private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotN { var sb = new StringBuilder(); var method = pinvoke.Method; - if (method.Name == "EnumCalendarInfo") - { - // FIXME: System.Reflection.MetadataLoadContext can't decode function pointer types - // https://github.com/dotnet/runtime/issues/43791 - sb.Append($"int {_fixupSymbolName(pinvoke.EntryPoint)} (int, int, int, int, int);"); - return sb.ToString(); - } if (TryIsMethodGetParametersUnsupported(pinvoke.Method, out string? reason)) { // Don't use method.ToString() or any of it's parameters, or return type // because at least one of those are unsupported, and will throw - Log.LogWarning(null, "WASM0001", "", "", 0, 0, 0, 0, - $"Skipping pinvoke '{pinvoke.Method.DeclaringType!.FullName}::{pinvoke.Method.Name}' because '{reason}'."); + Log.Warning("WASM0001", $"Skipping pinvoke '{pinvoke.Method.DeclaringType!.FullName}::{pinvoke.Method.Name}' because '{reason}'."); pinvoke.Skip = true; return null; @@ -393,12 +418,66 @@ private bool HasAssemblyDisableRuntimeMarshallingAttribute(Assembly assembly) return value; } - private static bool IsBlittable(Type type) + private static readonly Dictionary BlittableCache = new (); + + public static bool IsFunctionPointer(Type type) + { + object? bIsFunctionPointer = type.GetType().GetProperty("IsFunctionPointer")?.GetValue(type); + return (bIsFunctionPointer is bool b) && b; + } + + public static bool IsBlittable(Type type, LogAdapter log) { - if (type.IsPrimitive || type.IsByRef || type.IsPointer || type.IsEnum) + // We maintain a cache of results in order to only produce log messages the first time + // we analyze a given type. Otherwise, each (successful) use of a user-defined type + // in a callback or pinvoke would generate duplicate messages. + lock (BlittableCache) + if (BlittableCache.TryGetValue(type, out bool blittable)) + return blittable; + + bool result = IsBlittableUncached(type, log); + lock (BlittableCache) + BlittableCache[type] = result; + return result; + + static bool IsBlittableUncached (Type type, LogAdapter log) { + if (type.IsPrimitive || type.IsByRef || type.IsPointer || type.IsEnum) + return true; + + if (IsFunctionPointer(type)) + return true; + + if (type.Name == "__NonBlittableTypeForAutomatedTests__") + return false; + + if (!type.IsValueType) { + log.Warning("WASM0060", "Type {0} is not blittable: Not a ValueType", type); + return false; + } + + var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + + if (!type.IsLayoutSequential && (fields.Length > 1)) { + log.Warning("WASM0061", "Type {0} is not blittable: LayoutKind is not Sequential", type); + return false; + } + + foreach (var ft in fields) { + if (!IsBlittable(ft.FieldType, log)) { + log.Warning("WASM0062", "Type {0} is not blittable: Field {1} is not blittable", type, ft.Name); + return false; + } + // HACK: Skip tricky cases + // TODO: Do we need to support InitOnly for readonly structs? The callee could mutate them + if (ft.IsInitOnly || ft.IsLiteral) { + log.Warning("WASM0063", "Type {0} is not blittable: Field {1} is initonly/literal", type, ft.Name); + return false; + } + } + + log.Info("WASM0069", "ValueType {0} is blittable", type); return true; - else - return false; + } } private static void Error(string msg) => throw new LogAsErrorException(msg); diff --git a/src/tasks/WasmAppBuilder/SignatureMapper.cs b/src/tasks/WasmAppBuilder/SignatureMapper.cs index 75b51ddeb47e8..fcb1026793ffb 100644 --- a/src/tasks/WasmAppBuilder/SignatureMapper.cs +++ b/src/tasks/WasmAppBuilder/SignatureMapper.cs @@ -7,10 +7,11 @@ using System.Reflection; using System.Text; using System.Threading.Tasks; +using WasmAppBuilder; internal static class SignatureMapper { - private static char? TypeToChar(Type t) + private static char? TypeToChar(Type t, LogAdapter log) { char? c = t.Name switch { @@ -41,17 +42,27 @@ internal static class SignatureMapper else if (t.IsInterface) c = 'I'; else if (t.IsEnum) - c = TypeToChar(t.GetEnumUnderlyingType()); - else if (t.IsValueType) + c = TypeToChar(t.GetEnumUnderlyingType(), log); + else if (t.IsPointer) + c = 'I'; + else if (PInvokeTableGenerator.IsFunctionPointer(t)) c = 'I'; + else if (t.IsValueType) + { + var fields = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (fields.Length == 1) + return TypeToChar(fields[0].FieldType, log); + else if (PInvokeTableGenerator.IsBlittable(t, log)) + c = 'I'; + } } return c; } - public static string? MethodToSignature(MethodInfo method) + public static string? MethodToSignature(MethodInfo method, LogAdapter log) { - string? result = TypeToChar(method.ReturnType)?.ToString(); + string? result = TypeToChar(method.ReturnType, log)?.ToString(); if (result == null) { return null; @@ -59,7 +70,7 @@ internal static class SignatureMapper foreach (var parameter in method.GetParameters()) { - char? parameterChar = TypeToChar(parameter.ParameterType); + char? parameterChar = TypeToChar(parameter.ParameterType, log); if (parameterChar == null) { return null; diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index 5c7cfbf4dd2ee..e0bea72d42b1a 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -15,6 +15,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Microsoft.NET.Sdk.WebAssembly; +using WasmAppBuilder; namespace Microsoft.WebAssembly.Build.Tasks; @@ -93,6 +94,7 @@ private GlobalizationMode GetGlobalizationMode() protected override bool ExecuteInternal() { var helper = new BootJsonBuilderHelper(Log); + var logAdapter = new LogAdapter(Log); if (!ValidateArguments()) return false; @@ -132,7 +134,7 @@ protected override bool ExecuteInternal() if (UseWebcil) { var tmpWebcil = Path.GetTempFileName(); - var webcilWriter = Microsoft.WebAssembly.Build.Tasks.WebcilConverter.FromPortableExecutable(inputPath: assembly, outputPath: tmpWebcil, logger: Log); + var webcilWriter = Microsoft.WebAssembly.Build.Tasks.WebcilConverter.FromPortableExecutable(inputPath: assembly, outputPath: tmpWebcil, logger: logAdapter); webcilWriter.ConvertToWebcil(); var finalWebcil = Path.Combine(runtimeAssetsPath, Path.ChangeExtension(Path.GetFileName(assembly), Utils.WebcilInWasmExtension)); if (Utils.CopyIfDifferent(tmpWebcil, finalWebcil, useHash: true)) @@ -230,7 +232,7 @@ protected override bool ExecuteInternal() if (UseWebcil) { var tmpWebcil = Path.GetTempFileName(); - var webcilWriter = Microsoft.WebAssembly.Build.Tasks.WebcilConverter.FromPortableExecutable(inputPath: args.fullPath, outputPath: tmpWebcil, logger: Log); + var webcilWriter = Microsoft.WebAssembly.Build.Tasks.WebcilConverter.FromPortableExecutable(inputPath: args.fullPath, outputPath: tmpWebcil, logger: logAdapter); webcilWriter.ConvertToWebcil(); var finalWebcil = Path.Combine(cultureDirectory, Path.ChangeExtension(name, Utils.WebcilInWasmExtension)); if (Utils.CopyIfDifferent(tmpWebcil, finalWebcil, useHash: true)) diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj index 34a5109598613..ba4adbf058463 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj @@ -9,6 +9,7 @@ false true true + exe @@ -17,16 +18,19 @@ + + + + + + - - - @@ -35,14 +39,10 @@ - - - + - - Date: Mon, 20 Nov 2023 19:50:44 -0800 Subject: [PATCH 02/26] Repair merge damage --- src/mono/mono/mini/aot-runtime-wasm.c | 8 ++++---- src/mono/mono/mini/interp/interp.c | 4 ++-- src/mono/mono/mini/mini-wasm.c | 11 +++-------- src/mono/mono/mini/mini-wasm.h | 3 --- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/mono/mono/mini/aot-runtime-wasm.c b/src/mono/mono/mini/aot-runtime-wasm.c index a3dedc0a6b0a6..07e9bf9ba3956 100644 --- a/src/mono/mono/mini/aot-runtime-wasm.c +++ b/src/mono/mono/mini/aot-runtime-wasm.c @@ -14,8 +14,8 @@ #ifdef HOST_WASM -MonoType * -mini_wasm_get_scalar_vtype (MonoType *type); +gboolean +mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype); static char type_to_c (MonoType *t) @@ -61,8 +61,8 @@ type_to_c (MonoType *t) // Any struct or union that recursively (including through nested structs, unions, and arrays) // contains just a single scalar value and is not specified to have greater than natural alignment. // FIXME: Handle the scenario where there are fields of struct types that contain no members - MonoType *scalar_vtype = mini_wasm_get_scalar_vtype (t); - if (scalar_vtype) + MonoType *scalar_vtype; + if (mini_wasm_is_scalar_vtype (t, &scalar_vtype)) return type_to_c (scalar_vtype); return 'I'; diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 2fddf262b0a19..a4e69fdf60343 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -1340,8 +1340,8 @@ filter_type_for_build_args_from_sig (MonoType *type) #ifdef HOST_WASM // If the parameter type is scalarized according to WASM C ABI, treat it as the // scalarized type. Otherwise the MONO_TYPE_VALUETYPE case will treat it as int32. - MonoType *scalar = mini_wasm_get_scalar_vtype (type); - if (scalar) + MonoType *scalar; + if (mini_wasm_is_scalar_vtype (type, &scalar)) return scalar; #endif diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c index de06365c534d4..e0849ba463623 100644 --- a/src/mono/mono/mini/mini-wasm.c +++ b/src/mono/mono/mini/mini-wasm.c @@ -743,7 +743,6 @@ mono_wasm_get_debug_level (void) gboolean mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype) { - MonoType *result = NULL; MonoClass *klass; MonoClassField *field; gpointer iter; @@ -752,7 +751,7 @@ mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype) *etype = NULL; if (!MONO_TYPE_ISSTRUCT (type)) - return NULL; + return FALSE; klass = mono_class_from_mono_type_internal (type); mono_class_init_internal (klass); @@ -767,12 +766,8 @@ mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype) if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) continue; nfields ++; - if (nfields > 1) { - /* - g_fprintf (stderr, "Struct %s has too many fields to be scalar vtype\n", m_class_get_name (klass)); - */ - return NULL; - } + if (nfields > 1) + return FALSE; MonoType *t = mini_get_underlying_type (field->type); if (MONO_TYPE_ISSTRUCT (t)) { if (!mini_wasm_is_scalar_vtype (t, etype)) diff --git a/src/mono/mono/mini/mini-wasm.h b/src/mono/mono/mini/mini-wasm.h index 53ffdf053e85b..77c7f3a78fa5c 100644 --- a/src/mono/mono/mini/mini-wasm.h +++ b/src/mono/mono/mini/mini-wasm.h @@ -106,9 +106,6 @@ void mono_wasm_main_thread_schedule_timer (void *timerHandler, int shortestDueTi void mono_wasm_print_stack_trace (void); -MonoType * -mini_wasm_get_scalar_vtype (MonoType *type); - gboolean mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype); From 13157f377ad5a8b9f03ea18472196ec14a5ef2c3 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Tue, 28 Nov 2023 18:03:54 -0800 Subject: [PATCH 03/26] Run M2NG in-process again --- .../PInvokeTableGeneratorTests.cs | 11 ++-- .../ManagedToNativeGenerator.cs | 52 +++++++++++++------ 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs index 3c8b2e37818fc..7dc49f7e8ace7 100644 --- a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs @@ -769,12 +769,6 @@ public static unsafe int Main(string[] argv) var extraProperties = "true<_WasmDevel>true"; var extraItems = @""; - /* - extraItems += @" -"; - */ buildArgs = ExpandBuildArgs(buildArgs, extraItems: extraItems, @@ -790,13 +784,14 @@ public static unsafe int Main(string[] argv) Path.Combine(_projectDir!, "wasm-abi.c")); }, Publish: buildArgs.AOT, + // Verbosity: "diagnostic", DotnetWasmFromRuntimePack: false)); string objDir = Path.Combine(_projectDir!, "obj", buildArgs.Config!, "net9.0", "browser-wasm", "wasm", buildArgs.AOT ? "for-publish" : "for-build"); - Console.WriteLine($"objDir={objDir} buildArgs={buildArgs}"); // Verify that the right signature was added for the pinvoke. We can't determine this by examining the m2n file - Assert.Contains("Adding pinvoke signature FD for method 'Test.", output); + // FIXME: Not possible in in-process mode for some reason, even with verbosity at "diagnostic" + // Assert.Contains("Adding pinvoke signature FD for method 'Test.", output); string pinvokeTable = File.ReadAllText(Path.Combine(objDir, "pinvoke-table.h")); // Verify that the invoke is in the pinvoke table. Under various circumstances we will silently skip it, diff --git a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs index 789a55e4c6da7..8b7a4bab45393 100644 --- a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs @@ -116,7 +116,6 @@ private void ExecuteInternal(LogAdapter logAdapter) List managedAssemblies = FilterOutUnmanagedBinaries(Assemblies); if (ShouldRun(managedAssemblies)) { - var rspPath = Path.GetTempFileName(); var rsp = new ExecuteArguments( managedAssemblies.Select(Path.GetFullPath).ToArray(), RuntimeIcallTableFile, @@ -125,21 +124,8 @@ private void ExecuteInternal(LogAdapter logAdapter) IcallOutputPath, InterpToNativeOutputPath, CacheFilePath ); - var rspText = JsonSerializer.Serialize(rsp); - File.WriteAllText(rspPath, rspText); - var dotnetPath = GetDotNetPathOrDefault(); - - (int exitCode, string output) = Utils.TryRunProcess( - Log, - dotnetPath, - $"{GetType().Assembly.Location} {rspPath}", - envVars: null, - workingDir: null, - silent: false, - logStdErrAsMessage: true - ); - if (exitCode != 0) - logAdapter.Error("WASM0066", $"WasmAppBuilder external process failed with exit code {exitCode}!"); + + ExecuteInProcess(logAdapter, rsp); } List fileWritesList = new() { PInvokeOutputPath, InterpToNativeOutputPath }; @@ -151,6 +137,40 @@ private void ExecuteInternal(LogAdapter logAdapter) FileWrites = fileWritesList.ToArray(); } + private static void ExecuteInProcess(LogAdapter logAdapter, ExecuteArguments rsp) + { + ExecuteForAssemblies( + logAdapter, + rsp.managedAssemblies, + rsp.runtimeIcallTableFile, + rsp.pInvokeModules, + rsp.pInvokeOutputPath, + rsp.icallOutputPath, + rsp.interpToNativeOutputPath, + rsp.cacheFilePath + ); + } + + private void ExecuteOutOfProcess(LogAdapter logAdapter, ExecuteArguments rsp) + { + var rspPath = Path.GetTempFileName(); + var rspText = JsonSerializer.Serialize(rsp); + File.WriteAllText(rspPath, rspText); + var dotnetPath = GetDotNetPathOrDefault(); + + (int exitCode, string output) = Utils.TryRunProcess( + Log, + dotnetPath, + $"{GetType().Assembly.Location} {rspPath}", + envVars: null, + workingDir: null, + silent: false, + logStdErrAsMessage: true + ); + if (exitCode != 0) + logAdapter.Error("WASM0066", $"WasmAppBuilder external process failed with exit code {exitCode}!"); + } + internal readonly record struct ExecuteArguments ( IList managedAssemblies, string? runtimeIcallTableFile, From 11c3d7a1505fc7afc114b962db6942dc3513bb89 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Wed, 29 Nov 2023 18:34:23 -0800 Subject: [PATCH 04/26] Our current approach to SystemNative_SNPrintF is incorrect on some architectures, so change to an approach that always works --- .../src/Interop/Unix/System.Native/Interop.SNPrintF.cs | 4 ++-- src/native/libs/System.Native/entrypoints.c | 2 ++ src/native/libs/System.Native/pal_string.c | 10 ++++++++++ src/native/libs/System.Native/pal_string.h | 9 +++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SNPrintF.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SNPrintF.cs index fada5626b317f..39695cde2e5fa 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SNPrintF.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SNPrintF.cs @@ -26,7 +26,7 @@ internal static partial class Sys /// success; if the return value is equal to the size then the result may have been truncated. /// On failure, returns a negative value. /// - [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_SNPrintF", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)] + [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_SNPrintF_1S", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)] internal static unsafe partial int SNPrintF(byte* str, int size, string format, string arg1); /// @@ -47,7 +47,7 @@ internal static partial class Sys /// success; if the return value is equal to the size then the result may have been truncated. /// On failure, returns a negative value. /// - [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_SNPrintF", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)] + [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_SNPrintF_1I", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)] internal static unsafe partial int SNPrintF(byte* str, int size, string format, int arg1); } } diff --git a/src/native/libs/System.Native/entrypoints.c b/src/native/libs/System.Native/entrypoints.c index 1a5b7dcae503f..ee842ee2b7364 100644 --- a/src/native/libs/System.Native/entrypoints.c +++ b/src/native/libs/System.Native/entrypoints.c @@ -228,6 +228,8 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_SetDelayedSigChildConsoleConfigurationHandler) DllImportEntry(SystemNative_SetTerminalInvalidationHandler) DllImportEntry(SystemNative_SNPrintF) + DllImportEntry(SystemNative_SNPrintF_1S) + DllImportEntry(SystemNative_SNPrintF_1I) DllImportEntry(SystemNative_Sysctl) DllImportEntry(SystemNative_MapTcpState) DllImportEntry(SystemNative_LowLevelMonitor_Create) diff --git a/src/native/libs/System.Native/pal_string.c b/src/native/libs/System.Native/pal_string.c index f1a7c65ca0e12..0692df97d07c0 100644 --- a/src/native/libs/System.Native/pal_string.c +++ b/src/native/libs/System.Native/pal_string.c @@ -23,3 +23,13 @@ int32_t SystemNative_SNPrintF(char* string, int32_t size, const char* format, .. va_end(arguments); return result; } + +int32_t SystemNative_SNPrintF_1S(char* string, int32_t size, const char* format, char* str) +{ + return SystemNative_SNPrintF(string, size, format, str); +} + +int32_t SystemNative_SNPrintF_1I(char* string, int32_t size, const char* format, int arg) +{ + return SystemNative_SNPrintF(string, size, format, arg); +} diff --git a/src/native/libs/System.Native/pal_string.h b/src/native/libs/System.Native/pal_string.h index 49160dbd94969..ff69055fec09a 100644 --- a/src/native/libs/System.Native/pal_string.h +++ b/src/native/libs/System.Native/pal_string.h @@ -15,3 +15,12 @@ * On failure, returns a negative value. */ PALEXPORT int32_t SystemNative_SNPrintF(char* string, int32_t size, const char* format, ...); + +/** + * Two specialized overloads for use from Interop.Sys, because these two signatures are not equivalent + * on some architectures (like 64-bit WebAssembly) +*/ + +PALEXPORT int32_t SystemNative_SNPrintF_1S(char* string, int32_t size, const char* format, char* str); + +PALEXPORT int32_t SystemNative_SNPrintF_1I(char* string, int32_t size, const char* format, int arg); From 0d413b2839cf2a2e91d3972d3ad7ae980b3b1deb Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Wed, 29 Nov 2023 18:34:40 -0800 Subject: [PATCH 05/26] Support initonly fields in blittable structs in PInvokeTableGenerator etc --- src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index 3ea06356bbcb8..c50ba34c64c3b 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -461,10 +461,11 @@ static bool IsBlittableUncached (Type type, LogAdapter log) { log.Warning("WASM0062", "Type {0} is not blittable: Field {1} is not blittable", type, ft.Name); return false; } - // HACK: Skip tricky cases - // TODO: Do we need to support InitOnly for readonly structs? The callee could mutate them - if (ft.IsInitOnly || ft.IsLiteral) { - log.Warning("WASM0063", "Type {0} is not blittable: Field {1} is initonly/literal", type, ft.Name); + // HACK: Skip literals since they're complicated + // Ideally we would block initonly fields too since the callee could mutate them, but + // we rely on being able to pass types like System.Guid which are readonly + if (ft.IsLiteral) { + log.Warning("WASM0063", "Type {0} is not blittable: Field {1} is literal", type, ft.Name); return false; } } From 51274ed4ff7f32a77ded974e6a853404d321fb2c Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Fri, 1 Dec 2023 16:34:11 -0800 Subject: [PATCH 06/26] Work around our rampant use of warnaserror --- src/tasks/WasmAppBuilder/LogAdapter.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tasks/WasmAppBuilder/LogAdapter.cs b/src/tasks/WasmAppBuilder/LogAdapter.cs index 95f005f657f75..4d13559a3db20 100644 --- a/src/tasks/WasmAppBuilder/LogAdapter.cs +++ b/src/tasks/WasmAppBuilder/LogAdapter.cs @@ -50,8 +50,9 @@ public void Info (string code, string message, params object[] args) { } public void Warning (string code, string message, params object[] args) { - Helper?.LogWarning(null, code, null, null, 0, 0, 0, 0, message, args); - ErrorOutput?.WriteLine($"warning : {code}: {AutoFormat(message, args)}"); + // HACK: We use warnaserror in random places, so we can't use warnings + Helper?.LogMessage(null, code, null, null, 0, 0, 0, 0, MessageImportance.Normal, message, args); + Output?.WriteLine($"info : {code}: {AutoFormat(message, args)}"); } public void Error (string message) { From bf9439d140ef600a72e7a1aeb9181f773f8af244 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Fri, 1 Dec 2023 16:35:32 -0800 Subject: [PATCH 07/26] Revert most csproj changes --- .../WasmAppBuilder/WasmAppBuilder.csproj | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj index ba4adbf058463..68f4523a1fec4 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj @@ -18,19 +18,16 @@ - - - - - - + + + @@ -39,10 +36,14 @@ - - + + + + Date: Fri, 1 Dec 2023 16:38:17 -0800 Subject: [PATCH 08/26] Better solution for warnaserror (probably) --- src/tasks/WasmAppBuilder/LogAdapter.cs | 5 ++--- src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/tasks/WasmAppBuilder/LogAdapter.cs b/src/tasks/WasmAppBuilder/LogAdapter.cs index 4d13559a3db20..95f005f657f75 100644 --- a/src/tasks/WasmAppBuilder/LogAdapter.cs +++ b/src/tasks/WasmAppBuilder/LogAdapter.cs @@ -50,9 +50,8 @@ public void Info (string code, string message, params object[] args) { } public void Warning (string code, string message, params object[] args) { - // HACK: We use warnaserror in random places, so we can't use warnings - Helper?.LogMessage(null, code, null, null, 0, 0, 0, 0, MessageImportance.Normal, message, args); - Output?.WriteLine($"info : {code}: {AutoFormat(message, args)}"); + Helper?.LogWarning(null, code, null, null, 0, 0, 0, 0, message, args); + ErrorOutput?.WriteLine($"warning : {code}: {AutoFormat(message, args)}"); } public void Error (string message) { diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index c50ba34c64c3b..037afc6a9536c 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -445,32 +445,32 @@ static bool IsBlittableUncached (Type type, LogAdapter log) { return false; if (!type.IsValueType) { - log.Warning("WASM0060", "Type {0} is not blittable: Not a ValueType", type); + log.Info("WASM0060", "Type {0} is not blittable: Not a ValueType", type); return false; } var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (!type.IsLayoutSequential && (fields.Length > 1)) { - log.Warning("WASM0061", "Type {0} is not blittable: LayoutKind is not Sequential", type); + log.Info("WASM0061", "Type {0} is not blittable: LayoutKind is not Sequential", type); return false; } foreach (var ft in fields) { if (!IsBlittable(ft.FieldType, log)) { - log.Warning("WASM0062", "Type {0} is not blittable: Field {1} is not blittable", type, ft.Name); + log.Info("WASM0062", "Type {0} is not blittable: Field {1} is not blittable", type, ft.Name); return false; } // HACK: Skip literals since they're complicated // Ideally we would block initonly fields too since the callee could mutate them, but // we rely on being able to pass types like System.Guid which are readonly if (ft.IsLiteral) { - log.Warning("WASM0063", "Type {0} is not blittable: Field {1} is literal", type, ft.Name); + log.Info("WASM0063", "Type {0} is not blittable: Field {1} is literal", type, ft.Name); return false; } } - log.Info("WASM0069", "ValueType {0} is blittable", type); + // log.Info("WASM0069", "ValueType {0} is blittable", type); return true; } } From c25be75de3f7dd15088fba12308f09f2629a6a24 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Fri, 1 Dec 2023 17:33:58 -0800 Subject: [PATCH 09/26] Restore old EnumCalendarInfo since we're in-process again --- src/libraries/Common/src/Interop/Interop.Calendar.cs | 9 ++++++++- src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/src/Interop/Interop.Calendar.cs b/src/libraries/Common/src/Interop/Interop.Calendar.cs index e421b16147639..622ecc6522044 100644 --- a/src/libraries/Common/src/Interop/Interop.Calendar.cs +++ b/src/libraries/Common/src/Interop/Interop.Calendar.cs @@ -15,9 +15,16 @@ internal static partial class Globalization [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetCalendarInfo", StringMarshalling = StringMarshalling.Utf16)] internal static unsafe partial ResultCode GetCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType calendarDataType, char* result, int resultCapacity); + internal static unsafe bool EnumCalendarInfo(delegate* unmanaged callback, string localeName, CalendarId calendarId, CalendarDataType calendarDataType, IntPtr context) + { + return EnumCalendarInfo((IntPtr)callback, localeName, calendarId, calendarDataType, context); + } + [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_EnumCalendarInfo", StringMarshalling = StringMarshalling.Utf16)] [return: MarshalAs(UnmanagedType.Bool)] - internal static unsafe partial bool EnumCalendarInfo(delegate* unmanaged callback, string localeName, CalendarId calendarId, CalendarDataType calendarDataType, IntPtr context); + // We skip the following DllImport because of 'Parsing function pointer types in signatures is not supported.' for some targeted + // platforms (for example, WASM build). + private static unsafe partial bool EnumCalendarInfo(IntPtr callback, string localeName, CalendarId calendarId, CalendarDataType calendarDataType, IntPtr context); [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLatestJapaneseEra")] internal static partial int GetLatestJapaneseEra(); diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index 037afc6a9536c..dca8154de95e6 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -258,6 +258,13 @@ private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotN { var method = pinvoke.Method; + if (method.Name == "EnumCalendarInfo") + { + // FIXME: System.Reflection.MetadataLoadContext can't decode function pointer types + // https://github.com/dotnet/runtime/issues/43791 + return $"int {_fixupSymbolName(pinvoke.EntryPoint)} (int, int, int, int, int);"; + } + if (TryIsMethodGetParametersUnsupported(pinvoke.Method, out string? reason)) { // Don't use method.ToString() or any of it's parameters, or return type From 65275b66b289f40cbcdeefbaae2b3aec978f27c1 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Fri, 1 Dec 2023 18:18:40 -0800 Subject: [PATCH 10/26] Revert fn ptr test changes --- .../PInvokeTableGeneratorTests.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs index 7dc49f7e8ace7..6e8ea85d71026 100644 --- a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs @@ -61,7 +61,7 @@ public static int Main(string[] args) [Theory] [BuildAndRun(host: RunHost.Chrome)] - public void DllImportWithFunctionPointersCompilesWithoutWarning(BuildArgs buildArgs, RunHost host, string id) + public void DllImportWithFunctionPointersCompilesWithWarning(BuildArgs buildArgs, RunHost host, string id) { string code = """ @@ -87,8 +87,8 @@ public static int Main() buildArgs with { ProjectName = $"fnptr_{buildArgs.Config}_{id}" }, id); - Assert.DoesNotMatch("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); - Assert.DoesNotMatch("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); + Assert.Matches("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); + Assert.Matches("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); Assert.Contains("Main running", output); @@ -96,7 +96,7 @@ public static int Main() [Theory] [BuildAndRun(host: RunHost.Chrome)] - public void DllImportWithFunctionPointers_ForVariadicFunction_CompilesWithoutWarning(BuildArgs buildArgs, RunHost host, string id) + public void DllImportWithFunctionPointers_ForVariadicFunction_CompilesWithWarning(BuildArgs buildArgs, RunHost host, string id) { string code = @" using System; @@ -117,8 +117,8 @@ public static int Main() buildArgs with { ProjectName = $"fnptr_variadic_{buildArgs.Config}_{id}" }, id); - Assert.DoesNotMatch("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); - Assert.DoesNotMatch("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); + Assert.Matches("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); + Assert.Matches("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); Assert.Contains("Main running", output); @@ -373,7 +373,7 @@ public static int Main() [Theory] [BuildAndRun(host: RunHost.None)] - public void UnmanagedCallback_WithFunctionPointers_CompilesWithoutWarnings(BuildArgs buildArgs, string id) + public void UnmanagedCallback_WithFunctionPointers_CompilesWithWarnings(BuildArgs buildArgs, string id) { string code = """ @@ -398,7 +398,7 @@ public static int Main() id ); - Assert.DoesNotMatch("warning\\sWASM0001.*Skipping.*Test::SomeFunction1.*because.*function\\spointer", output); + Assert.Matches("warning\\sWASM0001.*Skipping.*Test::SomeFunction1.*because.*function\\spointer", output); } [Theory] From f47f8ea11af3e9741d4ead8683fc476d6c99bf9b Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 4 Dec 2023 13:35:27 -0800 Subject: [PATCH 11/26] Maybe fix blazor WBTs --- src/tasks/WasmAppBuilder/SignatureMapper.cs | 50 +++++++++++++-------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/tasks/WasmAppBuilder/SignatureMapper.cs b/src/tasks/WasmAppBuilder/SignatureMapper.cs index fcb1026793ffb..f3b7f17ad017b 100644 --- a/src/tasks/WasmAppBuilder/SignatureMapper.cs +++ b/src/tasks/WasmAppBuilder/SignatureMapper.cs @@ -13,30 +13,40 @@ internal static class SignatureMapper { private static char? TypeToChar(Type t, LogAdapter log) { - char? c = t.Name switch - { - nameof(String) => 'I', - nameof(Boolean) => 'I', - nameof(Char) => 'I', - nameof(Byte) => 'I', - nameof(Int16) => 'I', - nameof(UInt16) => 'I', - nameof(Int32) => 'I', - nameof(UInt32) => 'I', - nameof(IntPtr) => 'I', - nameof(UIntPtr) => 'I', - nameof(Int64) => 'L', - nameof(UInt64) => 'L', - nameof(Single) => 'F', - nameof(Double) => 'D', - "Void" => 'V', - _ => null - }; + char? c = null; + if (t.Namespace == "System") { + c = t.Name switch + { + nameof(String) => 'I', + nameof(Boolean) => 'I', + nameof(Char) => 'I', + nameof(Byte) => 'I', + nameof(Int16) => 'I', + nameof(UInt16) => 'I', + nameof(Int32) => 'I', + nameof(UInt32) => 'I', + nameof(Int64) => 'L', + nameof(UInt64) => 'L', + nameof(Single) => 'F', + nameof(Double) => 'D', + // FIXME: These will need to be L for wasm64 + nameof(IntPtr) => 'I', + nameof(UIntPtr) => 'I', + "Void" => 'V', + _ => null + }; + } if (c == null) { + // FIXME: Most of these need to be L for wasm64 if (t.IsArray) c = 'I'; + else if (t.IsByRef) + c = 'I'; + else if (typeof(Delegate).IsAssignableFrom(t)) + // FIXME: Should we narrow this to only certain types of delegates? + c = 'I'; else if (t.IsClass) c = 'I'; else if (t.IsInterface) @@ -55,6 +65,8 @@ internal static class SignatureMapper else if (PInvokeTableGenerator.IsBlittable(t, log)) c = 'I'; } + else + log.Warning("WASM0064", $"Unsupported parameter type '{t.Name}'"); } return c; From 835e2788dd194e89ac43af794d213788bd96895b Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 4 Dec 2023 18:19:46 -0800 Subject: [PATCH 12/26] Make LogAdapter Info high priority so it appears in failed build logs --- src/tasks/WasmAppBuilder/LogAdapter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tasks/WasmAppBuilder/LogAdapter.cs b/src/tasks/WasmAppBuilder/LogAdapter.cs index 95f005f657f75..87382a1c72d34 100644 --- a/src/tasks/WasmAppBuilder/LogAdapter.cs +++ b/src/tasks/WasmAppBuilder/LogAdapter.cs @@ -45,7 +45,9 @@ public void LogMessage (MessageImportance mi, string s, params object[] o) { } public void Info (string code, string message, params object[] args) { - Helper?.LogMessage(null, code, null, null, 0, 0, 0, 0, MessageImportance.Low, message, args); + // We use MessageImportance.High to ensure this appears in build output, since + // warnaserror makes warnings hard to use + Helper?.LogMessage(null, code, null, null, 0, 0, 0, 0, MessageImportance.High, message, args); Output?.WriteLine($"info : {code}: {AutoFormat(message, args)}"); } From d8ac1a90162dde49268fa5bccac60def70d03682 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 4 Dec 2023 18:20:03 -0800 Subject: [PATCH 13/26] Hack to make skiasharp build --- src/tasks/WasmAppBuilder/PInvokeCollector.cs | 23 +--------------- .../WasmAppBuilder/PInvokeTableGenerator.cs | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/tasks/WasmAppBuilder/PInvokeCollector.cs b/src/tasks/WasmAppBuilder/PInvokeCollector.cs index b646f4a4d4a03..2aa95be749a4b 100644 --- a/src/tasks/WasmAppBuilder/PInvokeCollector.cs +++ b/src/tasks/WasmAppBuilder/PInvokeCollector.cs @@ -173,28 +173,7 @@ static bool MethodHasCallbackAttributes(MethodInfo method) private static void Error(string msg) => throw new LogAsErrorException(msg); - private static bool HasAttribute(MemberInfo element, params string[] attributeNames) - { - foreach (CustomAttributeData cattr in CustomAttributeData.GetCustomAttributes(element)) - { - try - { - for (int i = 0; i < attributeNames.Length; ++i) - { - if (cattr.AttributeType.FullName == attributeNames [i] || - cattr.AttributeType.Name == attributeNames[i]) - { - return true; - } - } - } - catch - { - // Assembly not found, ignore - } - } - return false; - } + internal static bool HasAttribute(MemberInfo element, params string[] attributeNames) => PInvokeTableGenerator.HasAttribute(element, attributeNames); private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotNullWhen(true)] out string? reason) { diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index dca8154de95e6..9110ee02bbe49 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -448,6 +448,10 @@ static bool IsBlittableUncached (Type type, LogAdapter log) { if (IsFunctionPointer(type)) return true; + // HACK: SkiaSharp has pinvokes that rely on this + if (HasAttribute(type, "System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute")) + return true; + if (type.Name == "__NonBlittableTypeForAutomatedTests__") return false; @@ -482,5 +486,28 @@ static bool IsBlittableUncached (Type type, LogAdapter log) { } } + public static bool HasAttribute(MemberInfo element, params string[] attributeNames) + { + foreach (CustomAttributeData cattr in CustomAttributeData.GetCustomAttributes(element)) + { + try + { + for (int i = 0; i < attributeNames.Length; ++i) + { + if (cattr.AttributeType.FullName == attributeNames [i] || + cattr.AttributeType.Name == attributeNames[i]) + { + return true; + } + } + } + catch + { + // Assembly not found, ignore + } + } + return false; + } + private static void Error(string msg) => throw new LogAsErrorException(msg); } From 3039f553c208ed4f0ab9b27092ac81f1b42693a4 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Wed, 6 Dec 2023 13:10:09 -0800 Subject: [PATCH 14/26] Implement wasm abi struct scalar rules in interp again --- src/mono/mono/mini/interp/interp.c | 34 +++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index fb86596c32f45..29708df4bfefc 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -1345,10 +1345,22 @@ typedef enum { typedef struct { int ilen, flen; - PInvokeArgType ret_type; + MonoType *ret_mono_type; + PInvokeArgType ret_pinvoke_type; PInvokeArgType *arg_types; } BuildArgsFromSigInfo; +static MonoType * +filter_type_for_args_from_sig (MonoType *type) { +#if defined(HOST_WASM) || defined(TARGET_WASM) + MonoType *etype; + if (MONO_TYPE_ISSTRUCT (type) && mini_wasm_is_scalar_vtype (type, &etype)) + // FIXME: Does this need to be recursive? + return etype; +#endif + return type; +} + static BuildArgsFromSigInfo * get_build_args_from_sig_info (MonoMemoryManager *mem_manager, MonoMethodSignature *sig) { @@ -1360,7 +1372,7 @@ get_build_args_from_sig_info (MonoMemoryManager *mem_manager, MonoMethodSignatur g_assert (!sig->hasthis); for (int i = 0; i < sig->param_count; i++) { - MonoType *type = sig->params [i]; + MonoType *type = filter_type_for_args_from_sig (sig->params [i]); guint32 ptype; retry: @@ -1442,7 +1454,9 @@ get_build_args_from_sig_info (MonoMemoryManager *mem_manager, MonoMethodSignatur info->ilen = ilen; info->flen = flen; - switch (sig->ret->type) { + info->ret_mono_type = filter_type_for_args_from_sig (sig->ret); + + switch (info->ret_mono_type->type) { case MONO_TYPE_BOOLEAN: case MONO_TYPE_CHAR: case MONO_TYPE_I1: @@ -1463,17 +1477,17 @@ get_build_args_from_sig_info (MonoMemoryManager *mem_manager, MonoMethodSignatur case MONO_TYPE_U8: case MONO_TYPE_VALUETYPE: case MONO_TYPE_GENERICINST: - info->ret_type = PINVOKE_ARG_INT; + info->ret_pinvoke_type = PINVOKE_ARG_INT; break; case MONO_TYPE_R4: case MONO_TYPE_R8: - info->ret_type = PINVOKE_ARG_R8; + info->ret_pinvoke_type = PINVOKE_ARG_R8; break; case MONO_TYPE_VOID: - info->ret_type = PINVOKE_ARG_NONE; + info->ret_pinvoke_type = PINVOKE_ARG_NONE; break; default: - g_error ("build_args_from_sig: ret type not implemented yet: 0x%x\n", margs->ret_type->type); + g_error ("build_args_from_sig: ret type not implemented yet: 0x%x\n", info->ret_mono_type->type); } return info; @@ -1563,7 +1577,7 @@ build_args_from_sig (InterpMethodArguments *margs, MonoMethodSignature *sig, Bui } } - switch (info->ret_type) { + switch (info->ret_pinvoke_type) { case PINVOKE_ARG_INT: margs->retval = (gpointer*)frame->retval; margs->is_float_ret = 0; @@ -1768,8 +1782,8 @@ ves_pinvoke_method ( g_free (ccontext.stack); #else // Only the vt address has been returned, we need to copy the entire content on interp stack - if (!context->has_resume_state && MONO_TYPE_ISSTRUCT (margs->ret_type)) - stackval_from_data (margs->ret_type, frame.retval, (char*)frame.retval->data.p, sig->pinvoke && !sig->marshalling_disabled); + if (!context->has_resume_state && MONO_TYPE_ISSTRUCT (call_info->ret_mono_type)) + stackval_from_data (call_info->ret_mono_type, frame.retval, (char*)frame.retval->data.p, sig->pinvoke && !sig->marshalling_disabled); if (margs.iargs != margs.iargs_buf) g_free (margs.iargs); From e92b95d6c5c62a85e22e3df8257e08ef29438b45 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Wed, 6 Dec 2023 14:13:50 -0800 Subject: [PATCH 15/26] Add test coverage for i64 pinvokes + add fixme comments --- src/mono/mono/mini/mini-wasm.c | 3 +++ .../PInvokeTableGeneratorTests.cs | 22 ++++++++++++++++++- .../wasm/testassets/native-libs/wasm-abi.c | 13 +++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c index e0849ba463623..69edaac26a8e2 100644 --- a/src/mono/mono/mini/mini-wasm.c +++ b/src/mono/mono/mini/mini-wasm.c @@ -775,6 +775,9 @@ mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype) } else if (!((MONO_TYPE_IS_PRIMITIVE (t) || MONO_TYPE_IS_REFERENCE (t) || MONO_TYPE_IS_POINTER (t)))) { return FALSE; } else if (size == 8 && t->type != MONO_TYPE_R8) { + // FIXME: The MONO_TYPE_R8 restriction here means that WASM scalar structs + // containing I8/U8 fields don't work correctly, but removing the restriction + // causes the AOT compiler to crash return FALSE; } else { if (etype) diff --git a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs index 6e8ea85d71026..6bdecc7ab350e 100644 --- a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs @@ -736,11 +736,25 @@ public struct Nested1 { } public Nested1 Value; } + public struct SingleI64Struct { + public Int64 Value; + } public class Test { public static unsafe int Main(string[] argv) { + var i64_a = 0xFF00FF00FF00FF0L; + var i64_b = ~i64_a; + var resI = direct64(i64_a); + Console.WriteLine(""l (l)="" + resI); + + var sis = new SingleI64Struct { Value = i64_a }; + // FIXME: In the interpreter this performs an _ii invoke incorrectly, + // and in AOT it also doesn't work. See mini-wasm.c mini_wasm_is_scalar_vtype + // var resSI = indirect64(sis); + // Console.WriteLine(""s (s)="" + resSI.Value); + var resF = direct(3.14); Console.WriteLine(""f (d)="" + resF); @@ -750,7 +764,6 @@ public static unsafe int Main(string[] argv) resF = indirect_arg(sds); Console.WriteLine(""f (s)="" + resF); - // FIXME: This crashes var res = indirect(sds); Console.WriteLine(""s (s)="" + res.Value); @@ -765,6 +778,12 @@ public static unsafe int Main(string[] argv) [DllImport(""wasm-abi"", EntryPoint=""accept_double_struct_and_return_float_struct"")] public static extern float direct(double arg); + + [DllImport(""wasm-abi"", EntryPoint=""accept_and_return_i64_struct"")] + public static extern SingleI64Struct indirect64(SingleI64Struct arg); + + [DllImport(""wasm-abi"", EntryPoint=""accept_and_return_i64_struct"")] + public static extern Int64 direct64(Int64 arg); }"; var extraProperties = "true<_WasmDevel>true"; @@ -799,6 +818,7 @@ public static unsafe int Main(string[] argv) Assert.Contains("\"accept_double_struct_and_return_float_struct\", accept_double_struct_and_return_float_struct", pinvokeTable); // Verify the signature of the C function prototype. Wasm ABI specifies that the structs should both decompose into scalars. Assert.Contains("float accept_double_struct_and_return_float_struct (double);", pinvokeTable); + Assert.Contains("int64_t accept_and_return_i64_struct (int64_t);", pinvokeTable); var runOutput = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 3, host: host, id: id); Assert.Contains("f (d)=3.14", runOutput); diff --git a/src/mono/wasm/testassets/native-libs/wasm-abi.c b/src/mono/wasm/testassets/native-libs/wasm-abi.c index 5b6d8ccd033de..0ace2037daf2f 100644 --- a/src/mono/wasm/testassets/native-libs/wasm-abi.c +++ b/src/mono/wasm/testassets/native-libs/wasm-abi.c @@ -14,3 +14,16 @@ TRes accept_double_struct_and_return_float_struct ( TRes result = { arg.value.value }; return result; } + +typedef struct { + long long value; +} TResI64; + +TResI64 accept_and_return_i64_struct (TResI64 arg) { + printf ( + "&arg=%x (ulonglong)arg=%llx\n", + (unsigned int)&arg, *(unsigned long long*)&arg + ); + TResI64 result = { ~arg.value }; + return result; +} From 2964b7329cc95359741e97f375592029c57c5b7e Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Thu, 7 Dec 2023 20:22:59 -0800 Subject: [PATCH 16/26] Fix scalar int64 structs --- src/mono/mono/mini/mini-wasm.c | 8 +++----- .../wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs | 8 ++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c index 69edaac26a8e2..c7a8c3100df10 100644 --- a/src/mono/mono/mini/mini-wasm.c +++ b/src/mono/mono/mini/mini-wasm.c @@ -755,6 +755,9 @@ mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype) klass = mono_class_from_mono_type_internal (type); mono_class_init_internal (klass); + if (m_class_is_simd_type (klass)) + return FALSE; + int size = mono_class_value_size (klass, NULL); if (size == 0 || size > 8) return FALSE; @@ -774,11 +777,6 @@ mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype) return FALSE; } else if (!((MONO_TYPE_IS_PRIMITIVE (t) || MONO_TYPE_IS_REFERENCE (t) || MONO_TYPE_IS_POINTER (t)))) { return FALSE; - } else if (size == 8 && t->type != MONO_TYPE_R8) { - // FIXME: The MONO_TYPE_R8 restriction here means that WASM scalar structs - // containing I8/U8 fields don't work correctly, but removing the restriction - // causes the AOT compiler to crash - return FALSE; } else { if (etype) *etype = t; diff --git a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs index 6bdecc7ab350e..ec1f3615fc0a5 100644 --- a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs @@ -750,10 +750,8 @@ public static unsafe int Main(string[] argv) Console.WriteLine(""l (l)="" + resI); var sis = new SingleI64Struct { Value = i64_a }; - // FIXME: In the interpreter this performs an _ii invoke incorrectly, - // and in AOT it also doesn't work. See mini-wasm.c mini_wasm_is_scalar_vtype - // var resSI = indirect64(sis); - // Console.WriteLine(""s (s)="" + resSI.Value); + var resSI = indirect64(sis); + Console.WriteLine(""s (s)="" + resSI.Value); var resF = direct(3.14); Console.WriteLine(""f (d)="" + resF); @@ -821,6 +819,8 @@ public static unsafe int Main(string[] argv) Assert.Contains("int64_t accept_and_return_i64_struct (int64_t);", pinvokeTable); var runOutput = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 3, host: host, id: id); + Assert.Contains("l (l)=-1148435428713435121", runOutput); + Assert.Contains("s (s)=-1148435428713435121", runOutput); Assert.Contains("f (d)=3.14", runOutput); Assert.Contains("f (s)=3.14", runOutput); Assert.Contains("s (s)=3.14", runOutput); From ce56e6613ab69d81ba33211a518ee4f29a246db9 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Fri, 8 Dec 2023 23:52:04 -0800 Subject: [PATCH 17/26] Code cleanup --- src/tasks/WasmAppBuilder/LogAdapter.cs | 118 ++++++++++-------- .../WasmAppBuilder/PInvokeTableGenerator.cs | 10 +- 2 files changed, 71 insertions(+), 57 deletions(-) diff --git a/src/tasks/WasmAppBuilder/LogAdapter.cs b/src/tasks/WasmAppBuilder/LogAdapter.cs index 87382a1c72d34..2a23fa175cae8 100644 --- a/src/tasks/WasmAppBuilder/LogAdapter.cs +++ b/src/tasks/WasmAppBuilder/LogAdapter.cs @@ -6,66 +6,80 @@ using Microsoft.Build.Utilities; using Microsoft.Build.Framework; -namespace WasmAppBuilder { - public sealed class LogAdapter { - public bool HasLoggedErrors { - get => Helper?.HasLoggedErrors ?? _HasLoggedErrors; - } - private bool _HasLoggedErrors; - private TaskLoggingHelper? Helper; - private TextWriter? Output, ErrorOutput; +#nullable enable - public LogAdapter (TaskLoggingHelper helper) { - Helper = helper; - Output = null; - ErrorOutput = null; - } +namespace WasmAppBuilder; - public LogAdapter () { - Helper = null; - Output = Console.Out; - ErrorOutput = Console.Error; - } +public sealed class LogAdapter +{ + public bool HasLoggedErrors + { + get => _helper?.HasLoggedErrors ?? _hasLoggedErrors; + } + + private bool _hasLoggedErrors; + private TaskLoggingHelper? _helper; + private TextWriter? _output, _errorOutput; + + public LogAdapter(TaskLoggingHelper helper) + { + _helper = helper; + _output = null; + _errorOutput = null; + } + + public LogAdapter() + { + _helper = null; + _output = Console.Out; + _errorOutput = Console.Error; + } - private static string AutoFormat (string s, object[] o) { - if ((o?.Length ?? 0) > 0) - return string.Format(s!, o!); - else - return s; - } + private static string AutoFormat(string s, object[] o) + { + if ((o?.Length ?? 0) > 0) + return string.Format(s!, o!); + else + return s; + } - public void LogMessage (string s, params object[] o) { - Helper?.LogMessage(s, o); - Output?.WriteLine(AutoFormat(s, o)); - } + public void LogMessage(string s, params object[] o) + { + _helper?.LogMessage(s, o); + _output?.WriteLine(AutoFormat(s, o)); + } - public void LogMessage (MessageImportance mi, string s, params object[] o) { - Helper?.LogMessage(mi, s, o); - Output?.WriteLine(AutoFormat(s, o)); - } + public void LogMessage(MessageImportance mi, string s, params object[] o) + { + _helper?.LogMessage(mi, s, o); + _output?.WriteLine(AutoFormat(s, o)); + } - public void Info (string code, string message, params object[] args) { - // We use MessageImportance.High to ensure this appears in build output, since - // warnaserror makes warnings hard to use - Helper?.LogMessage(null, code, null, null, 0, 0, 0, 0, MessageImportance.High, message, args); - Output?.WriteLine($"info : {code}: {AutoFormat(message, args)}"); - } + public void Info(string code, string message, params object[] args) + { + // We use MessageImportance.High to ensure this appears in build output, since + // warnaserror makes warnings hard to use + _helper?.LogMessage(null, code, null, null, 0, 0, 0, 0, MessageImportance.High, message, args); + _output?.WriteLine($"info : {code}: {AutoFormat(message, args)}"); + } - public void Warning (string code, string message, params object[] args) { - Helper?.LogWarning(null, code, null, null, 0, 0, 0, 0, message, args); - ErrorOutput?.WriteLine($"warning : {code}: {AutoFormat(message, args)}"); - } + public void Warning(string code, string message, params object[] args) + { + _helper?.LogWarning(null, code, null, null, 0, 0, 0, 0, message, args); + _errorOutput?.WriteLine($"warning : {code}: {AutoFormat(message, args)}"); + } - public void Error (string message) { - Helper?.LogError(message); - ErrorOutput?.WriteLine($"error : {message}"); - _HasLoggedErrors = true; - } + public void Error(string message) + { + _helper?.LogError(message); + _errorOutput?.WriteLine($"error : {message}"); + _hasLoggedErrors = true; + } - public void Error (string code, string message, params object[] args) { - Helper?.LogError(null, code, null, null, 0, 0, 0, 0, message, args); - ErrorOutput?.WriteLine($"error : {code}: {AutoFormat(message, args)}"); - _HasLoggedErrors = true; - } + public void Error(string code, string message, params object[] args) + { + _helper?.LogError(null, code, null, null, 0, 0, 0, 0, message, args); + _errorOutput?.WriteLine($"error : {code}: {AutoFormat(message, args)}"); + _hasLoggedErrors = true; } } diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index 9110ee02bbe49..89bc33ca95f5c 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -419,7 +419,7 @@ private bool HasAssemblyDisableRuntimeMarshallingAttribute(Assembly assembly) return value; } - private static readonly Dictionary BlittableCache = new (); + private static readonly Dictionary _blittableCache = new (); public static bool IsFunctionPointer(Type type) { @@ -432,13 +432,13 @@ public static bool IsBlittable(Type type, LogAdapter log) // We maintain a cache of results in order to only produce log messages the first time // we analyze a given type. Otherwise, each (successful) use of a user-defined type // in a callback or pinvoke would generate duplicate messages. - lock (BlittableCache) - if (BlittableCache.TryGetValue(type, out bool blittable)) + lock (_blittableCache) + if (_blittableCache.TryGetValue(type, out bool blittable)) return blittable; bool result = IsBlittableUncached(type, log); - lock (BlittableCache) - BlittableCache[type] = result; + lock (_blittableCache) + _blittableCache[type] = result; return result; static bool IsBlittableUncached (Type type, LogAdapter log) { From 037df97669f0ec6a7da1fc678a343b3114f3f278 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Fri, 8 Dec 2023 23:52:45 -0800 Subject: [PATCH 18/26] Formatting --- .../WasmAppBuilder/PInvokeTableGenerator.cs | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index 89bc33ca95f5c..110cd2d8d437e 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -211,7 +211,8 @@ private string CEntryPoint(PInvoke pinvoke) _ => PickCTypeNameForUnknownType(t) }; - private static string PickCTypeNameForUnknownType (Type t) { + private static string PickCTypeNameForUnknownType(Type t) + { // Pass objects by-reference (their address by-value) if (!t.IsValueType) return "void *"; @@ -278,15 +279,14 @@ private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotN return $$""" {{(pinvoke.WasmLinkage ? $"__attribute__((import_module(\"{EscapeLiteral(pinvoke.Module)}\"),import_name(\"{EscapeLiteral(pinvoke.EntryPoint)}\")))" : "")}} - {{(pinvoke.WasmLinkage ? "extern " : "")}}{{MapType(method.ReturnType)}} {{CEntryPoint(pinvoke)}} ({{ - string.Join(", ", method.GetParameters().Select(p => MapType(p.ParameterType))) - }}); + {{(pinvoke.WasmLinkage ? "extern " : "")}}{{MapType(method.ReturnType)}} {{CEntryPoint(pinvoke)}} ({{string.Join(", ", method.GetParameters().Select(p => MapType(p.ParameterType)))}}); """; } private string CEntryPoint(PInvokeCallback export) { - if (export.EntryPoint is not null) { + if (export.EntryPoint is not null) + { return _fixupSymbolName(export.EntryPoint); } @@ -419,7 +419,7 @@ private bool HasAssemblyDisableRuntimeMarshallingAttribute(Assembly assembly) return value; } - private static readonly Dictionary _blittableCache = new (); + private static readonly Dictionary _blittableCache = new(); public static bool IsFunctionPointer(Type type) { @@ -441,7 +441,8 @@ public static bool IsBlittable(Type type, LogAdapter log) _blittableCache[type] = result; return result; - static bool IsBlittableUncached (Type type, LogAdapter log) { + static bool IsBlittableUncached(Type type, LogAdapter log) + { if (type.IsPrimitive || type.IsByRef || type.IsPointer || type.IsEnum) return true; @@ -455,27 +456,32 @@ static bool IsBlittableUncached (Type type, LogAdapter log) { if (type.Name == "__NonBlittableTypeForAutomatedTests__") return false; - if (!type.IsValueType) { + if (!type.IsValueType) + { log.Info("WASM0060", "Type {0} is not blittable: Not a ValueType", type); return false; } var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - if (!type.IsLayoutSequential && (fields.Length > 1)) { + if (!type.IsLayoutSequential && (fields.Length > 1)) + { log.Info("WASM0061", "Type {0} is not blittable: LayoutKind is not Sequential", type); return false; } - foreach (var ft in fields) { - if (!IsBlittable(ft.FieldType, log)) { + foreach (var ft in fields) + { + if (!IsBlittable(ft.FieldType, log)) + { log.Info("WASM0062", "Type {0} is not blittable: Field {1} is not blittable", type, ft.Name); return false; } // HACK: Skip literals since they're complicated // Ideally we would block initonly fields too since the callee could mutate them, but // we rely on being able to pass types like System.Guid which are readonly - if (ft.IsLiteral) { + if (ft.IsLiteral) + { log.Info("WASM0063", "Type {0} is not blittable: Field {1} is literal", type, ft.Name); return false; } @@ -494,7 +500,7 @@ public static bool HasAttribute(MemberInfo element, params string[] attributeNam { for (int i = 0; i < attributeNames.Length; ++i) { - if (cattr.AttributeType.FullName == attributeNames [i] || + if (cattr.AttributeType.FullName == attributeNames[i] || cattr.AttributeType.Name == attributeNames[i]) { return true; From 0dda91a45d96cc9889315a6e06da7e5089f52ada Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Tue, 19 Dec 2023 09:08:04 -0800 Subject: [PATCH 19/26] Checkpoint (reverse out-of-process) --- src/tasks/WasmAppBuilder/ExternalTaskHost.cs | 42 ----- .../ManagedToNativeGenerator.cs | 156 +++--------------- .../WasmAppBuilder/PInvokeTableGenerator.cs | 9 +- .../WasmAppBuilder/WasmAppBuilder.csproj | 1 - 4 files changed, 26 insertions(+), 182 deletions(-) delete mode 100644 src/tasks/WasmAppBuilder/ExternalTaskHost.cs diff --git a/src/tasks/WasmAppBuilder/ExternalTaskHost.cs b/src/tasks/WasmAppBuilder/ExternalTaskHost.cs deleted file mode 100644 index fc2ded591915f..0000000000000 --- a/src/tasks/WasmAppBuilder/ExternalTaskHost.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; -using System.Text.Json; -using System.Linq; - -namespace WasmAppBuilder; - -public static class Program { - public static int Main (string[] args) { - if (args.Length != 1) - throw new ArgumentException("Expected 'wasmappbuilder [response file]'"); - if (!File.Exists(args[0])) - throw new FileNotFoundException("Response file not found: " + args[0]); - var rspText = File.ReadAllText(args[0]); - var rsp = JsonSerializer.Deserialize(rspText); - var logAdapter = new LogAdapter(); - - try { - Microsoft.WebAssembly.Build.Tasks.ManagedToNativeGenerator.ExecuteForAssemblies( - logAdapter, - rsp.managedAssemblies, - rsp.runtimeIcallTableFile, - rsp.pInvokeModules, - rsp.pInvokeOutputPath, - rsp.icallOutputPath, - rsp.interpToNativeOutputPath, - rsp.cacheFilePath - ); - } catch (LogAsErrorException e) { - logAdapter.Error(e.Message); - return 2; - } - - if (logAdapter.HasLoggedErrors) - return 1; - else - return 0; - } -} diff --git a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs index 8b7a4bab45393..2efaa894f6383 100644 --- a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Reflection; using System.Text; -using System.Text.Json; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using WasmAppBuilder; @@ -66,66 +65,34 @@ public override bool Execute() } } - // dotnet/roslyn src/Compilers/Shared/RuntimeHostInfo.cs - - private const string DotNetHostPathEnvironmentName = "DOTNET_HOST_PATH"; - - /// - /// Get the path to the dotnet executable. In the case the .NET SDK did not provide this information - /// in the environment this tries to find "dotnet" on the PATH. In the case it is not found, - /// this will return simply "dotnet". - /// - internal static string GetDotNetPathOrDefault() + private void ExecuteInternal() { - if (Environment.GetEnvironmentVariable(DotNetHostPathEnvironmentName) is string pathToDotNet) - { - return pathToDotNet; - } - - var (fileName, sep) = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform( - System.Runtime.InteropServices.OSPlatform.Windows - ) - ? ("dotnet.exe", ';') - : ("dotnet", ':'); - - var path = Environment.GetEnvironmentVariable("PATH") ?? ""; - foreach (var item in path.Split(sep)) + Dictionary _symbolNameFixups = new(); + List managedAssemblies = FilterOutUnmanagedBinaries(Assemblies); + if (ShouldRun(managedAssemblies)) { - if (string.IsNullOrEmpty(item)) - continue; + var pinvoke = new PInvokeTableGenerator(FixupSymbolName, Log); + var icall = new IcallTableGenerator(RuntimeIcallTableFile, FixupSymbolName, Log); - try + var resolver = new PathAssemblyResolver(managedAssemblies); + using var mlc = new MetadataLoadContext(resolver, "System.Private.CoreLib"); + foreach (string asmPath in managedAssemblies) { - var filePath = Path.Combine(item, fileName); - if (File.Exists(filePath)) - { - return filePath; - } + Log.LogMessage(MessageImportance.Low, $"Loading {asmPath} to scan for pinvokes, and icalls"); + Assembly asm = mlc.LoadFromAssemblyPath(asmPath); + pinvoke.ScanAssembly(asm); + icall.ScanAssembly(asm); } - catch - { - // If we can't read a directory for any reason just skip it - } - } - return fileName; - } + IEnumerable cookies = Enumerable.Concat( + pinvoke.Generate(PInvokeModules, PInvokeOutputPath), + icall.Generate(IcallOutputPath)); - private void ExecuteInternal(LogAdapter logAdapter) - { - List managedAssemblies = FilterOutUnmanagedBinaries(Assemblies); - if (ShouldRun(managedAssemblies)) - { - var rsp = new ExecuteArguments( - managedAssemblies.Select(Path.GetFullPath).ToArray(), - RuntimeIcallTableFile, - PInvokeModules, - PInvokeOutputPath, - IcallOutputPath, InterpToNativeOutputPath, - CacheFilePath - ); - - ExecuteInProcess(logAdapter, rsp); + var m2n = new InterpToNativeGenerator(Log); + m2n.Generate(cookies, InterpToNativeOutputPath); + + if (!string.IsNullOrEmpty(CacheFilePath)) + File.WriteAllLines(CacheFilePath, PInvokeModules); } List fileWritesList = new() { PInvokeOutputPath, InterpToNativeOutputPath }; @@ -135,86 +102,6 @@ private void ExecuteInternal(LogAdapter logAdapter) fileWritesList.Add(CacheFilePath); FileWrites = fileWritesList.ToArray(); - } - - private static void ExecuteInProcess(LogAdapter logAdapter, ExecuteArguments rsp) - { - ExecuteForAssemblies( - logAdapter, - rsp.managedAssemblies, - rsp.runtimeIcallTableFile, - rsp.pInvokeModules, - rsp.pInvokeOutputPath, - rsp.icallOutputPath, - rsp.interpToNativeOutputPath, - rsp.cacheFilePath - ); - } - - private void ExecuteOutOfProcess(LogAdapter logAdapter, ExecuteArguments rsp) - { - var rspPath = Path.GetTempFileName(); - var rspText = JsonSerializer.Serialize(rsp); - File.WriteAllText(rspPath, rspText); - var dotnetPath = GetDotNetPathOrDefault(); - - (int exitCode, string output) = Utils.TryRunProcess( - Log, - dotnetPath, - $"{GetType().Assembly.Location} {rspPath}", - envVars: null, - workingDir: null, - silent: false, - logStdErrAsMessage: true - ); - if (exitCode != 0) - logAdapter.Error("WASM0066", $"WasmAppBuilder external process failed with exit code {exitCode}!"); - } - - internal readonly record struct ExecuteArguments ( - IList managedAssemblies, - string? runtimeIcallTableFile, - string[] pInvokeModules, - string pInvokeOutputPath, - string? icallOutputPath, - string interpToNativeOutputPath, - string? cacheFilePath - ); - - internal static void ExecuteForAssemblies( - LogAdapter logAdapter, - IList managedAssemblies, - string? runtimeIcallTableFile, - string[] pInvokeModules, - string pInvokeOutputPath, - string? icallOutputPath, - string interpToNativeOutputPath, - string? cacheFilePath - ) { - Dictionary _symbolNameFixups = new(); - - var pinvoke = new PInvokeTableGenerator(FixupSymbolName, logAdapter); - var icall = new IcallTableGenerator(runtimeIcallTableFile, FixupSymbolName, logAdapter); - - var resolver = new PathAssemblyResolver(managedAssemblies); - using var mlc = new MetadataLoadContext(resolver, "System.Private.CoreLib"); - foreach (string asmPath in managedAssemblies) - { - logAdapter.LogMessage(MessageImportance.Low, $"Loading {asmPath} to scan for pinvokes, and icalls"); - Assembly asm = mlc.LoadFromAssemblyPath(asmPath); - pinvoke.ScanAssembly(asm); - icall.ScanAssembly(asm); - } - - IEnumerable cookies = Enumerable.Concat( - pinvoke.Generate(pInvokeModules, pInvokeOutputPath), - icall.Generate(icallOutputPath)); - - var m2n = new InterpToNativeGenerator(logAdapter); - m2n.Generate(cookies, interpToNativeOutputPath); - - if (!string.IsNullOrEmpty(cacheFilePath)) - File.WriteAllLines(cacheFilePath, pInvokeModules); string FixupSymbolName(string name) { @@ -248,6 +135,7 @@ string FixupSymbolName(string name) _symbolNameFixups[name] = fixedName; return fixedName; } + } private bool ShouldRun(IList managedAssemblies) diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index 110cd2d8d437e..f808112ea3566 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -458,7 +458,7 @@ static bool IsBlittableUncached(Type type, LogAdapter log) if (!type.IsValueType) { - log.Info("WASM0060", "Type {0} is not blittable: Not a ValueType", type); + log.InfoHigh("WASM0060", "Type {0} is not blittable: Not a ValueType", type); return false; } @@ -466,7 +466,7 @@ static bool IsBlittableUncached(Type type, LogAdapter log) if (!type.IsLayoutSequential && (fields.Length > 1)) { - log.Info("WASM0061", "Type {0} is not blittable: LayoutKind is not Sequential", type); + log.InfoHigh("WASM0061", "Type {0} is not blittable: LayoutKind is not Sequential", type); return false; } @@ -474,7 +474,7 @@ static bool IsBlittableUncached(Type type, LogAdapter log) { if (!IsBlittable(ft.FieldType, log)) { - log.Info("WASM0062", "Type {0} is not blittable: Field {1} is not blittable", type, ft.Name); + log.InfoHigh("WASM0062", "Type {0} is not blittable: Field {1} is not blittable", type, ft.Name); return false; } // HACK: Skip literals since they're complicated @@ -482,12 +482,11 @@ static bool IsBlittableUncached(Type type, LogAdapter log) // we rely on being able to pass types like System.Guid which are readonly if (ft.IsLiteral) { - log.Info("WASM0063", "Type {0} is not blittable: Field {1} is literal", type, ft.Name); + log.InfoHigh("WASM0063", "Type {0} is not blittable: Field {1} is literal", type, ft.Name); return false; } } - // log.Info("WASM0069", "ValueType {0} is blittable", type); return true; } } diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj index 68f4523a1fec4..34a5109598613 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj @@ -9,7 +9,6 @@ false true true - exe From 8f869e2b2b25825ed2e40f69f354f7f916ad1d45 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Tue, 19 Dec 2023 12:06:38 -0800 Subject: [PATCH 20/26] Checkpoint --- src/tasks/WasmAppBuilder/LogAdapter.cs | 2 +- src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tasks/WasmAppBuilder/LogAdapter.cs b/src/tasks/WasmAppBuilder/LogAdapter.cs index 2a23fa175cae8..8a068047e2f4a 100644 --- a/src/tasks/WasmAppBuilder/LogAdapter.cs +++ b/src/tasks/WasmAppBuilder/LogAdapter.cs @@ -55,7 +55,7 @@ public void LogMessage(MessageImportance mi, string s, params object[] o) _output?.WriteLine(AutoFormat(s, o)); } - public void Info(string code, string message, params object[] args) + public void InfoHigh(string code, string message, params object[] args) { // We use MessageImportance.High to ensure this appears in build output, since // warnaserror makes warnings hard to use diff --git a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs index 2efaa894f6383..3acdcf06ba8a6 100644 --- a/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs @@ -65,20 +65,20 @@ public override bool Execute() } } - private void ExecuteInternal() + private void ExecuteInternal(LogAdapter log) { Dictionary _symbolNameFixups = new(); List managedAssemblies = FilterOutUnmanagedBinaries(Assemblies); if (ShouldRun(managedAssemblies)) { - var pinvoke = new PInvokeTableGenerator(FixupSymbolName, Log); - var icall = new IcallTableGenerator(RuntimeIcallTableFile, FixupSymbolName, Log); + var pinvoke = new PInvokeTableGenerator(FixupSymbolName, log); + var icall = new IcallTableGenerator(RuntimeIcallTableFile, FixupSymbolName, log); var resolver = new PathAssemblyResolver(managedAssemblies); using var mlc = new MetadataLoadContext(resolver, "System.Private.CoreLib"); foreach (string asmPath in managedAssemblies) { - Log.LogMessage(MessageImportance.Low, $"Loading {asmPath} to scan for pinvokes, and icalls"); + log.LogMessage(MessageImportance.Low, $"Loading {asmPath} to scan for pinvokes, and icalls"); Assembly asm = mlc.LoadFromAssemblyPath(asmPath); pinvoke.ScanAssembly(asm); icall.ScanAssembly(asm); @@ -88,7 +88,7 @@ private void ExecuteInternal() pinvoke.Generate(PInvokeModules, PInvokeOutputPath), icall.Generate(IcallOutputPath)); - var m2n = new InterpToNativeGenerator(Log); + var m2n = new InterpToNativeGenerator(log); m2n.Generate(cookies, InterpToNativeOutputPath); if (!string.IsNullOrEmpty(CacheFilePath)) From 9abbf32111653efe79c9f7f45c1ef7a806b60f03 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Fri, 5 Jan 2024 18:05:08 -0800 Subject: [PATCH 21/26] Fix Vector64 AOT test failures (calling convention was incorrect when gsharedvt and simd types were involved) --- src/mono/mono/mini/mini-llvm.c | 16 +++++++++++++--- src/mono/mono/mini/mini-wasm.c | 3 --- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index 86825212fa22d..d246d30940a75 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -4167,7 +4167,7 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder) case LLVMArgVtypeAddr: case LLVMArgVtypeByRef: case LLVMArgAsFpArgs: - { + case LLVMArgWasmVtypeAsScalar: { if (mini_class_is_simd (ctx->cfg, mono_class_from_mono_type_internal (ainfo->type))) /* Treat these as normal values */ ctx->values [reg] = LLVMBuildLoad2 (builder, ctx->addresses [reg]->type, ctx->addresses [reg]->value, "simd_vtype"); @@ -4935,6 +4935,8 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, if (!addresses [call->inst.dreg]) addresses [call->inst.dreg] = build_alloca_address (ctx, sig->ret); emit_store (builder, lcall, convert_full (ctx, addresses [call->inst.dreg]->value, pointer_type (LLVMTypeOf (lcall)), FALSE), is_volatile); + load_name = "wasm_vtype_as_scalar"; + should_promote_to_value = TRUE; break; } default: @@ -5387,6 +5389,7 @@ static LLVMValueRef concatenate_vectors (EmitContext *ctx, LLVMValueRef xs, LLVMValueRef ys) { LLVMTypeRef t = LLVMTypeOf (xs); + g_assert (LLVMGetTypeKind (t) == LLVMVectorTypeKind); unsigned int elems = LLVMGetVectorSize (t) * 2; int mask [MAX_VECTOR_ELEMS] = { 0 }; for (guint i = 0; i < elems; ++i) @@ -6141,8 +6144,15 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) } break; case LLVMArgWasmVtypeAsScalar: - g_assert (addresses [ins->sreg1]); - retval = LLVMBuildLoad2 (builder, ret_type, build_ptr_cast (builder, addresses [ins->sreg1]->value, pointer_type (ret_type)), ""); + if (!addresses [ins->sreg1]) { + /* SIMD value */ + g_assert (lhs); + retval = LLVMBuildBitCast (builder, lhs, ret_type, ""); + mono_llvm_dump_value (lhs); + mono_llvm_dump_type (ret_type); + } else { + retval = LLVMBuildLoad2 (builder, ret_type, build_ptr_cast (builder, addresses [ins->sreg1]->value, pointer_type (ret_type)), ""); + } break; } LLVMBuildRet (builder, retval); diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c index c7a8c3100df10..af943e24adece 100644 --- a/src/mono/mono/mini/mini-wasm.c +++ b/src/mono/mono/mini/mini-wasm.c @@ -755,9 +755,6 @@ mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype) klass = mono_class_from_mono_type_internal (type); mono_class_init_internal (klass); - if (m_class_is_simd_type (klass)) - return FALSE; - int size = mono_class_value_size (klass, NULL); if (size == 0 || size > 8) return FALSE; From 8548fbfc444ff87308ee49446c92c60fa602531c Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 8 Jan 2024 15:29:54 -0800 Subject: [PATCH 22/26] Ensure we get MLC 8.0 in WasmAppBuilder --- src/tasks/WasmAppBuilder/WasmAppBuilder.csproj | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj index 0b663a993a57d..83374663528f6 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj @@ -23,11 +23,13 @@ - - - - - + + + + + + + From 36dc34a67134e5ea9652a91af11307b22f18786e Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 8 Jan 2024 19:13:22 -0800 Subject: [PATCH 23/26] Update tests since function pointers work now --- .../Wasm.Build.Tests/PInvokeTableGeneratorTests.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs index ec1f3615fc0a5..f553f10ddeb36 100644 --- a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs @@ -61,7 +61,7 @@ public static int Main(string[] args) [Theory] [BuildAndRun(host: RunHost.Chrome)] - public void DllImportWithFunctionPointersCompilesWithWarning(BuildArgs buildArgs, RunHost host, string id) + public void DllImportWithFunctionPointersCompilesWithoutWarning(BuildArgs buildArgs, RunHost host, string id) { string code = """ @@ -87,8 +87,8 @@ public static int Main() buildArgs with { ProjectName = $"fnptr_{buildArgs.Config}_{id}" }, id); - Assert.Matches("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); - Assert.Matches("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); + Assert.DoesNotMatch("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); + Assert.DoesNotMatch("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); Assert.Contains("Main running", output); @@ -117,8 +117,8 @@ public static int Main() buildArgs with { ProjectName = $"fnptr_variadic_{buildArgs.Config}_{id}" }, id); - Assert.Matches("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); - Assert.Matches("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); + Assert.DoesNotMatch("warning\\sWASM0001.*Could\\snot\\sget\\spinvoke.*Parsing\\sfunction\\spointer\\stypes", output); + Assert.DoesNotMatch("warning\\sWASM0001.*Skipping.*using_sum_one.*because.*function\\spointer", output); output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); Assert.Contains("Main running", output); @@ -398,7 +398,7 @@ public static int Main() id ); - Assert.Matches("warning\\sWASM0001.*Skipping.*Test::SomeFunction1.*because.*function\\spointer", output); + Assert.DoesNotMatch("warning\\sWASM0001.*Skipping.*Test::SomeFunction1.*because.*function\\spointer", output); } [Theory] From 74664630aa6f8aaea79305a21f84d0bbede57b4a Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Wed, 10 Jan 2024 13:02:16 -0800 Subject: [PATCH 24/26] Attempt to resolve prebuilts/source build CI failure by adding (missing?) dependency on MLC Explicitly track our target version of MLC Bump our target versions of SRM and SCI --- eng/Version.Details.xml | 5 +++++ eng/Versions.props | 5 +++-- src/tasks/WasmAppBuilder/WasmAppBuilder.csproj | 9 +++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 11e665e0298d4..01c1b29e467f3 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -317,6 +317,11 @@ 4dffd80c4d77c27e772a0be26e8036af77fbb26e + + https://github.com/dotnet/runtime + 4dffd80c4d77c27e772a0be26e8036af77fbb26e + + https://github.com/dotnet/runtime 4dffd80c4d77c27e772a0be26e8036af77fbb26e diff --git a/eng/Versions.props b/eng/Versions.props index ea5219971d280..d08b99f60aaa3 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -120,19 +120,20 @@ 5.0.0 1.2.0-beta.507 4.5.1 - 7.0.0 + 8.0.0 5.0.0 4.8.5 8.0.0 5.0.0 4.5.5 - 7.0.0 + 8.0.0 6.0.0 5.0.0 5.0.0 5.0.0 7.0.0 9.0.0-alpha.1.23617.5 + 8.0.0 6.0.0 7.0.0 4.5.4 diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj index 83374663528f6..4f362d32fb486 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj @@ -26,10 +26,11 @@ - - - - + + + + From b1e64a0c4df449da73ee652aed563d7375dd7995 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 5 Feb 2024 12:01:54 -0800 Subject: [PATCH 25/26] Address PR feedback --- src/mono/mono/mini/aot-runtime-wasm.c | 3 --- src/mono/mono/mini/interp/interp.c | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/mono/mono/mini/aot-runtime-wasm.c b/src/mono/mono/mini/aot-runtime-wasm.c index 07e9bf9ba3956..cf1ab02392934 100644 --- a/src/mono/mono/mini/aot-runtime-wasm.c +++ b/src/mono/mono/mini/aot-runtime-wasm.c @@ -14,9 +14,6 @@ #ifdef HOST_WASM -gboolean -mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype); - static char type_to_c (MonoType *t) { diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index b230f99beffe2..03a33e90ca233 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -1352,7 +1352,7 @@ typedef struct { static MonoType * filter_type_for_args_from_sig (MonoType *type) { -#if defined(HOST_WASM) || defined(TARGET_WASM) +#if defined(HOST_WASM) MonoType *etype; if (MONO_TYPE_ISSTRUCT (type) && mini_wasm_is_scalar_vtype (type, &etype)) // FIXME: Does this need to be recursive? From 1ba922219780cdea2dc4fee294b2349dedf61c20 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 5 Feb 2024 12:18:50 -0800 Subject: [PATCH 26/26] Repair merge damage Address PR feedback --- eng/Versions.props | 1 - src/mono/mono/mini/mini-llvm.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index e87805da2f69f..9da1700c5202a 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -137,7 +137,6 @@ 5.0.0 7.0.0 9.0.0-alpha.1.24072.1 - 8.0.0 6.0.0 7.0.0 4.5.4 diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index e980321b2c247..1b226dd754946 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -6203,8 +6203,6 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) /* SIMD value */ g_assert (lhs); retval = LLVMBuildBitCast (builder, lhs, ret_type, ""); - mono_llvm_dump_value (lhs); - mono_llvm_dump_type (ret_type); } else { retval = LLVMBuildLoad2 (builder, ret_type, build_ptr_cast (builder, addresses [ins->sreg1]->value, pointer_type (ret_type)), ""); }