Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/coreclr/vm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,8 @@ elseif(CLR_CMAKE_TARGET_ARCH_WASM)
${ARCH_SOURCES_DIR}/calldescrworkerwasm.cpp
${ARCH_SOURCES_DIR}/profiler.cpp
${ARCH_SOURCES_DIR}/helpers.cpp
${ARCH_SOURCES_DIR}/callhelpers.cpp
${ARCH_SOURCES_DIR}/callhelpers-interp-to-managed.cpp
${ARCH_SOURCES_DIR}/callhelpers-reverse.cpp
exceptionhandling.cpp
gcinfodecoder.cpp
)
Expand Down

Large diffs are not rendered by default.

55 changes: 55 additions & 0 deletions src/coreclr/vm/wasm/callhelpers-reverse.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
//

#include <callhelpers.hpp>

// Define reverse thunks here

// Entry point for interpreted method execution from unmanaged code
class MethodDesc;

// WASM-TODO: The method lookup would ideally be fully qualified assembly and then methodDef token.
// The current approach has limitations with overloaded methods.
extern "C" void LookupMethodByName(const char* fullQualifiedTypeName, const char* methodName, MethodDesc** ppMD);
extern "C" void ExecuteInterpretedMethodFromUnmanaged(MethodDesc* pMD, int8_t* args, size_t argSize, int8_t* ret);

static MethodDesc* MD_System_Private_CoreLib_System_Threading_ThreadPool_BackgroundJobHandler_Void_RetVoid = nullptr;
static void Call_System_Private_CoreLib_System_Threading_ThreadPool_BackgroundJobHandler()
{
// Lazy lookup of MethodDesc for the function export scenario.
if (!MD_System_Private_CoreLib_System_Threading_ThreadPool_BackgroundJobHandler_Void_RetVoid)
{
LookupMethodByName("System.Threading.ThreadPool, System.Private.CoreLib", "BackgroundJobHandler", &MD_System_Private_CoreLib_System_Threading_ThreadPool_BackgroundJobHandler_Void_RetVoid);
}
ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Threading_ThreadPool_BackgroundJobHandler_Void_RetVoid, nullptr, 0, nullptr);
}

extern "C" void SystemJS_ExecuteBackgroundJobCallback()
{
Call_System_Private_CoreLib_System_Threading_ThreadPool_BackgroundJobHandler();
}

static MethodDesc* MD_System_Private_CoreLib_System_Threading_TimerQueue_TimerHandler_Void_RetVoid = nullptr;
static void Call_System_Private_CoreLib_System_Threading_TimerQueue_TimerHandler()
{
// Lazy lookup of MethodDesc for the function export scenario.
if (!MD_System_Private_CoreLib_System_Threading_TimerQueue_TimerHandler_Void_RetVoid)
{
LookupMethodByName("System.Threading.TimerQueue, System.Private.CoreLib", "TimerHandler", &MD_System_Private_CoreLib_System_Threading_TimerQueue_TimerHandler_Void_RetVoid);
}
ExecuteInterpretedMethodFromUnmanaged(MD_System_Private_CoreLib_System_Threading_TimerQueue_TimerHandler_Void_RetVoid, nullptr, 0, nullptr);
}

extern "C" void SystemJS_ExecuteTimerCallback()
{
Call_System_Private_CoreLib_System_Threading_TimerQueue_TimerHandler();
}

extern const ReverseThunkMapEntry g_ReverseThunks[] =
{
{ 100678287, { &MD_System_Private_CoreLib_System_Threading_ThreadPool_BackgroundJobHandler_Void_RetVoid, (void*)&Call_System_Private_CoreLib_System_Threading_ThreadPool_BackgroundJobHandler } },
{ 100678363, { &MD_System_Private_CoreLib_System_Threading_TimerQueue_TimerHandler_Void_RetVoid, (void*)&Call_System_Private_CoreLib_System_Threading_TimerQueue_TimerHandler } },
};

const size_t g_ReverseThunksCount = sizeof(g_ReverseThunks) / sizeof(g_ReverseThunks[0]);
32 changes: 32 additions & 0 deletions src/tasks/WasmAppBuilder/WasmAppBuilder.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
<Compile Include="$(RepoRoot)src\libraries\System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs" />
</ItemGroup>

<ItemGroup Condition="'$(RuntimeFlavor)' == 'CoreCLR'">
<Compile Remove="mono/*.cs" />
</ItemGroup>

<ItemGroup Condition="'$(RuntimeFlavor)' == 'mono'">
<Compile Remove="coreclr/*.cs" />
</ItemGroup>

<ItemGroup>
<Compile Include="..\Common\Utils.cs" />
<Compile Include="..\Common\LogAsErrorException.cs" />
Expand Down Expand Up @@ -50,4 +58,28 @@
</ItemGroup>
</Target>

<UsingTask TaskName="ManagedToNativeGenerator" AssemblyFile="$(OutputPath)$(NetCoreAppToolCurrent)\WasmAppBuilder.dll" />

<Target Name="RunGenerator" DependsOnTargets="Build" Condition="'$(AssembliesScanPath)' != ''">
<PropertyGroup>
<GenTestOutput>$(OutputPath)generator-test-output</GenTestOutput>
</PropertyGroup>

<ItemGroup>
<WasmPInvokeAssembly Include="$(AssembliesScanPath)**/*.dll" />
<WasmPInvokeModule Include="libSystem.Native" />
<WasmPInvokeModule Include="libSystem.IO.Compression.Native" />
<WasmPInvokeModule Include="libSystem.Globalization.Native" />
<WasmPInvokeModule Include="libz" />
</ItemGroup>

<Message Importance="high" Text="Running ManagedToNativeGenerator on @(WasmPInvokeAssembly)" />
<ManagedToNativeGenerator
Assemblies="@(WasmPInvokeAssembly)"
PInvokeModules="@(WasmPInvokeModule)"
PInvokeOutputPath="$(GeneratorOutputPath)todo-pinvoke-helpers.cpp"
InterpToNativeOutputPath="$(GeneratorOutputPath)callhelpers-interp-to-managed.cpp">
<Output TaskParameter="FileWrites" ItemName="FileWrites" />
</ManagedToNativeGenerator>
</Target>
</Project>
135 changes: 135 additions & 0 deletions src/tasks/WasmAppBuilder/coreclr/InterpToNativeGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// 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.Linq;
using System.Text;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.Diagnostics.CodeAnalysis;
using WasmAppBuilder;

using JoinedString;
//
// This class generates the icall_trampoline_dispatch () function used by the interpreter to call native code on WASM.
// It should be kept in sync with mono_wasm_interp_to_native_trampoline () in the runtime.
//

#nullable enable

internal sealed class InterpToNativeGenerator
{
private LogAdapter Log { get; set; }

public InterpToNativeGenerator(LogAdapter log) => Log = log;

public void Generate(IEnumerable<string> cookies, string outputPath)
{
using TempFileName tmpFileName = new();
using (var w = File.CreateText(tmpFileName.Path))
{
Emit(w, cookies);
}

if (Utils.CopyIfDifferent(tmpFileName.Path, outputPath, useHash: false))
Log.LogMessage(MessageImportance.Low, $"Generating managed2native table to '{outputPath}'.");
else
Log.LogMessage(MessageImportance.Low, $"Managed2native table in {outputPath} is unchanged.");
}

private static string SignatureToArguments(string signature)
{
if (signature.Length <= 1)
return "void";

return string.Join(", ", signature.Skip(1).Select(static c => SignatureMapper.CharToNativeType(c)));
}

private static string CallFuncName(IEnumerable<char> args, string result)
{
var paramTypes = args.Any() ? args.Join("_", (p, i) => SignatureMapper.CharToNameType(p)).ToString() : "Void";

return $"CallFunc_{paramTypes}_Ret{result}";
}

private static void Emit(StreamWriter w, IEnumerable<string> cookies)
{
// Use OrderBy because Order() is not available on .NET Framework
var signatures = cookies.OrderBy(c => c).Distinct().ToArray();
Array.Sort(signatures, StringComparer.Ordinal);


static IEnumerable<char> Args(string signature)
{
for (int i = 1; i < signature.Length; ++i)
yield return signature[i];
}

static (bool isVoid, string nativeType) Result(string signature)
=> new(SignatureMapper.IsVoidSignature(signature), SignatureMapper.CharToNativeType(signature[0]));

w.Write(
"""
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
//
//
// GENERATED FILE, DON'T EDIT
// Generated by coreclr InterpToNativeGenerator
//
#include <callhelpers.hpp>
// Arguments are passed on the stack with each argument aligned to INTERP_STACK_SLOT_SIZE.
#define ARG_ADDR(i) (pArgs + (i * INTERP_STACK_SLOT_SIZE))
#define ARG_IND(i) ((int32_t)((int32_t*)ARG_ADDR(i)))
#define ARG_I32(i) (*(int32_t*)ARG_ADDR(i))
#define ARG_I64(i) (*(int64_t*)ARG_ADDR(i))
#define ARG_F32(i) (*(float*)ARG_ADDR(i))
#define ARG_F64(i) (*(double*)ARG_ADDR(i))
namespace
{
""");

foreach (var signature in signatures)
{
try
{
var result = Result(signature);
var args = Args(signature);
var portabilityAssert = signature[0] == 'n' ? "PORTABILITY_ASSERT(\"Indirect struct return is not yet implemented.\");\n " : "";
w.Write(
$$"""
void {{CallFuncName(args, SignatureMapper.CharToNameType(signature[0]))}} (PCODE pcode, int8_t *pArgs, int8_t *pRet)
{
{{result.nativeType}} (*fptr)({{args.Join(", ", (p, i) => SignatureMapper.CharToNativeType(p))}}) = ({{result.nativeType}} (*)({{args.Join(", ", (p, i) => SignatureMapper.CharToNativeType(p))}}))pcode;
{{portabilityAssert}}{{(result.isVoid ? "" : "*" + "((" + result.nativeType + "*)pRet) = ")}}(*fptr)({{args.Join(", ", (p, i) => $"ARG_{SignatureMapper.CharToNameType(p)}({i})")}});
}
""");
}
catch (InvalidSignatureCharException e)
{
throw new LogAsErrorException($"Element '{e.Char}' of signature '{signature}' can't be handled by managed2native generator");
}
}

w.Write(
$$"""
}
const StringToWasmSigThunk g_wasmThunks[] = {
{{signatures.Join($",{w.NewLine}", signature =>
$" {{ \"{signature}\", (void*)&{CallFuncName(Args(signature), SignatureMapper.CharToNameType(signature[0]))} }}")}}
};
const size_t g_wasmThunksCount = sizeof(g_wasmThunks) / sizeof(g_wasmThunks[0]);
""");
}
}
Loading
Loading