Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/mcs
diff options
context:
space:
mode:
authorZoltan Varga <vargaz@gmail.com>2020-07-08 16:47:32 +0300
committerGitHub <noreply@github.com>2020-07-08 16:47:32 +0300
commitf047b0b29904f595ce1fbd7a24022bd753f545dc (patch)
tree0a10c2b0b04e74a5344460c60832ca9c501c5426 /mcs
parentdc7448f15f67a8e4b94ab402cdfa2f02a090771e (diff)
[wasm] Implement support for pinvoke callbacks in the interpeter. (#20076)
* [wasm] Refactor the wasm tuner. Move the pinvoke table generation code into a new PInvokeTableGenerator.cs file, to be similar to the corresponding code in WasmAppBuilder in dotnet/runtime. NFC. * [wasm] Implement support for pinvoke callbacks in the interpeter. * Modify the tuner to generate a table of trampoline functions which have the same signature as the pinvoke callback methods. * These functions transfer control to the interpreter. * Modify the interpreter/wasm runtime to look up the functions corresponding to the pinvoke callback methods. * [csproj] Update project files * Fix license. * Remove leftover debug code. Co-authored-by: monojenkins <jo.shields+jenkins@xamarin.com>
Diffstat (limited to 'mcs')
-rw-r--r--mcs/tools/wasm-tuner/PInvokeTableGenerator.cs298
-rw-r--r--mcs/tools/wasm-tuner/tuner.cs118
-rw-r--r--mcs/tools/wasm-tuner/wasm-tuner.csproj1
-rw-r--r--mcs/tools/wasm-tuner/wasm-tuner.exe.sources1
4 files changed, 316 insertions, 102 deletions
diff --git a/mcs/tools/wasm-tuner/PInvokeTableGenerator.cs b/mcs/tools/wasm-tuner/PInvokeTableGenerator.cs
new file mode 100644
index 00000000000..4ef3865f947
--- /dev/null
+++ b/mcs/tools/wasm-tuner/PInvokeTableGenerator.cs
@@ -0,0 +1,298 @@
+// 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.Json;
+using System.Collections.Generic;
+using Mono.Cecil;
+
+public class PInvokeTableGenerator
+{
+ public PInvokeTableGenerator () {
+ }
+
+ List<AssemblyDefinition> assemblies;
+ Dictionary<string, string> modules;
+ List<Pinvoke> pinvokes;
+ List<PinvokeCallback> callbacks;
+
+ public void Run (List<AssemblyDefinition> assemblies, Dictionary<string, string> modules) {
+ this.assemblies = assemblies;
+ this.modules = modules;
+
+ CollectPInvokes (assemblies, out pinvokes, out callbacks);
+
+ Console.WriteLine ("// GENERATED FILE, DO NOT MODIFY");
+ Console.WriteLine ();
+
+ GenPInvokeTable ();
+ GenNativeToInterp ();
+ }
+
+ void GenPInvokeTable () {
+ var decls = new Dictionary<string, Pinvoke> ();
+ foreach (var pinvoke in pinvokes) {
+ if (modules.ContainsKey (pinvoke.Module)) {
+ pinvoke.CDecl = GenPinvokeDecl (pinvoke);
+ if (decls.TryGetValue (pinvoke.EntryPoint, out Pinvoke prev_pinvoke)) {
+ if (pinvoke.CDecl != prev_pinvoke.CDecl) {
+ Console.Error.WriteLine ($"Warning: PInvoke method '{pinvoke.EntryPoint}' has incompatible declarations.");
+ pinvoke.TableEntryPoint = "mono_wasm_pinvoke_vararg_stub";
+ prev_pinvoke.TableEntryPoint = "mono_wasm_pinvoke_vararg_stub";
+ continue;
+ }
+ }
+ decls [pinvoke.EntryPoint] = pinvoke;
+ Console.WriteLine (pinvoke.CDecl);
+ }
+ }
+
+ foreach (var module in modules.Keys) {
+ string symbol = module.Replace (".", "_") + "_imports";
+ Console.WriteLine ("static PinvokeImport " + symbol + " [] = {");
+ foreach (var pinvoke in pinvokes) {
+ if (pinvoke.Module == module)
+ Console.WriteLine ("{\"" + pinvoke.EntryPoint + "\", " + pinvoke.TableEntryPoint + "},");
+ }
+ Console.WriteLine ("{NULL, NULL}");
+ Console.WriteLine ("};");
+ }
+ Console.Write ("static void *pinvoke_tables[] = { ");
+ foreach (var module in modules.Keys) {
+ string symbol = module.Replace (".", "_") + "_imports";
+ Console.Write (symbol + ",");
+ }
+ Console.WriteLine ("};");
+ Console.Write ("static char *pinvoke_names[] = { ");
+ foreach (var module in modules.Keys) {
+ Console.Write ("\"" + module + "\"" + ",");
+ }
+ Console.WriteLine ("};");
+ }
+
+ void GenNativeToInterp () {
+ // Generate native->interp entry functions
+ // These are called by native code, so they need to obtain
+ // the interp entry function/arg from a global array
+ // They also need to have a signature matching what the
+ // native code expects, which is the native signature
+ // of the delegate invoke in the [MonoPInvokeCallback]
+ // attribute.
+ // Only blittable parameter/return types are supposed.
+ int cb_index = 0;
+
+ // Arguments to interp entry functions in the runtime
+ Console.WriteLine ("InterpFtnDesc wasm_native_to_interp_ftndescs[" + callbacks.Count + "];");
+
+ foreach (var cb in callbacks) {
+ var method = cb.Method;
+
+ if (!IsBlittable (method.ReturnType))
+ Error ("The return type of pinvoke callback method '" + method.FullName + "' needs to be blittable.");
+ foreach (var p in method.Parameters) {
+ if (!IsBlittable (p.ParameterType))
+ Error ("Parameter types of pinvoke callback method '" + method.FullName + "' needs to be blittable.");
+ }
+ }
+
+ foreach (var cb in callbacks) {
+ var sb = new StringBuilder ();
+ var method = cb.Method;
+
+ // The signature of the interp entry function
+ // This is a gsharedvt_in signature
+ sb.Append ("typedef void ");
+ sb.Append (" (*WasmInterpEntrySig_" + cb_index + ") (");
+ int pindex = 0;
+ if (method.ReturnType.Name != "Void") {
+ sb.Append ("int");
+ pindex ++;
+ }
+ foreach (var p in method.Parameters) {
+ if (pindex > 0)
+ sb.Append (",");
+ sb.Append ("int");
+ pindex ++;
+ }
+ if (pindex > 0)
+ sb.Append (",");
+ // Extra arg
+ sb.Append ("int");
+ sb.Append (");\n");
+
+ bool is_void = method.ReturnType.Name == "Void";
+
+ string module_symbol = method.DeclaringType.Module.Assembly.Name.Name.Replace (".", "_");
+ var token = method.MetadataToken.ToUInt32 ();
+ string entry_name = $"wasm_native_to_interp_{module_symbol}_{token}";
+ cb.EntryName = entry_name;
+ sb.Append (WasmTuner.MapType (method.ReturnType));
+ sb.Append ($" {entry_name} (");
+ pindex = 0;
+ foreach (var p in method.Parameters) {
+ if (pindex > 0)
+ sb.Append (",");
+ sb.Append (WasmTuner.MapType (method.Parameters [pindex].ParameterType));
+ sb.Append (" arg" + pindex);
+ pindex ++;
+ }
+ sb.Append (") { \n");
+ if (!is_void)
+ sb.Append (WasmTuner.MapType (method.ReturnType) + " res;\n");
+ sb.Append ("((WasmInterpEntrySig_" + cb_index + ")wasm_native_to_interp_ftndescs [" + cb_index + "].func) (");
+ pindex = 0;
+ if (!is_void) {
+ sb.Append ("&res");
+ pindex ++;
+ }
+ int aindex = 0;
+ foreach (var p in method.Parameters) {
+ if (pindex > 0)
+ sb.Append (", ");
+ sb.Append ("&arg" + aindex);
+ pindex ++;
+ aindex ++;
+ }
+ if (pindex > 0)
+ sb.Append (", ");
+ sb.Append ($"wasm_native_to_interp_ftndescs [{cb_index}].arg");
+ sb.Append (");\n");
+ if (!is_void)
+ sb.Append ("return res;\n");
+ sb.Append ("}");
+ Console.WriteLine (sb);
+ cb_index ++;
+ }
+
+ // Array of function pointers
+ Console.Write ("static void *wasm_native_to_interp_funcs[] = { ");
+ foreach (var cb in callbacks) {
+ Console.Write (cb.EntryName + ",");
+ }
+ Console.WriteLine ("};");
+
+ // Lookup table from method->interp entry
+ // The key is a string of the form <assembly name>_<method token>
+ // FIXME: Use a better encoding
+ Console.Write ("static const char *wasm_native_to_interp_map[] = { ");
+ foreach (var cb in callbacks) {
+ var method = cb.Method;
+ string module_symbol = method.DeclaringType.Module.Assembly.Name.Name.Replace (".", "_");
+ var token = method.MetadataToken.ToUInt32 ();
+ Console.WriteLine ($"\"{module_symbol}_{token}\",");
+ }
+ Console.WriteLine ("};");
+ }
+
+ static bool IsBlittable (TypeReference type) {
+ switch (type.MetadataType) {
+ case MetadataType.Char:
+ case MetadataType.Boolean:
+ case MetadataType.Byte:
+ case MetadataType.SByte:
+ case MetadataType.Int16:
+ case MetadataType.UInt16:
+ case MetadataType.Int32:
+ case MetadataType.UInt32:
+ case MetadataType.Int64:
+ case MetadataType.UInt64:
+ case MetadataType.IntPtr:
+ case MetadataType.UIntPtr:
+ case MetadataType.Single:
+ case MetadataType.Double:
+ case MetadataType.Pointer:
+ case MetadataType.ByReference:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static void Error (string msg) {
+ Console.Error.WriteLine (msg);
+ Environment.Exit (1);
+ }
+
+ public static void CollectPInvokes (List<AssemblyDefinition> assemblies, out List<Pinvoke> pinvokes, out List<PinvokeCallback> callbacks) {
+ pinvokes = new List<Pinvoke> ();
+ callbacks = new List<PinvokeCallback> ();
+ foreach (var assembly in assemblies) {
+ foreach (var type in assembly.MainModule.Types) {
+ ProcessTypeForPinvoke (pinvokes, callbacks, type);
+ foreach (var nested in type.NestedTypes)
+ ProcessTypeForPinvoke (pinvokes, callbacks, nested);
+ }
+ }
+ }
+
+ static void ProcessTypeForPinvoke (List<Pinvoke> pinvokes, List<PinvokeCallback> callbacks, TypeDefinition type) {
+ foreach (var method in type.Methods) {
+ var info = method.PInvokeInfo;
+ if (info != null) {
+ pinvokes.Add (new Pinvoke (info.EntryPoint, info.Module.Name, method));
+ }
+
+ foreach (var attr in method.CustomAttributes) {
+ // The type has no defined namespace
+ if (attr.Constructor.DeclaringType.Name == "MonoPInvokeCallbackAttribute") {
+ if (!method.IsStatic)
+ Error ($"Method '{method.FullName}' decored with [MonoPInvokeCallback] needs to be static.");
+ var callback_type = attr.ConstructorArguments [0];
+ if (callback_type.Type.Name != "Type" || callback_type.Value == null)
+ Error ("[MonoPInvokeCallback] attribute has invalid format.");
+ var tref = (TypeReference)callback_type.Value;
+ callbacks.Add (new PinvokeCallback (method, tref));
+ } else if (attr.Constructor.DeclaringType.Name == "UnmanagedCallersOnlyAttribute") {
+ callbacks.Add (new PinvokeCallback (method, null));
+ }
+ }
+ }
+ }
+
+ static string GenPinvokeDecl (Pinvoke pinvoke) {
+ var sb = new StringBuilder ();
+ var method = pinvoke.Method;
+ sb.Append (WasmTuner.MapType (method.ReturnType));
+ sb.Append ($" {pinvoke.EntryPoint} (");
+ int pindex = 0;
+ foreach (var p in method.Parameters) {
+ if (pindex > 0)
+ sb.Append (",");
+ sb.Append (WasmTuner.MapType (method.Parameters [pindex].ParameterType));
+ pindex ++;
+ }
+ sb.Append (");");
+ return sb.ToString ();
+ }
+}
+
+public class Pinvoke
+{
+ public Pinvoke (string entry_point, string module, MethodReference method) {
+ EntryPoint = entry_point;
+ TableEntryPoint = entry_point;
+ Module = module;
+ Method = method;
+ }
+
+ public string EntryPoint;
+ public string TableEntryPoint;
+ public string Module;
+ public MethodReference Method;
+ public string CDecl;
+}
+
+public class PinvokeCallback
+{
+ public PinvokeCallback (MethodReference method, TypeReference callback_type) {
+ Method = method;
+ CallbackType = callback_type;
+ }
+
+ public MethodReference Method;
+ public TypeReference CallbackType;
+ public string EntryName;
+}
diff --git a/mcs/tools/wasm-tuner/tuner.cs b/mcs/tools/wasm-tuner/tuner.cs
index 3004f35cd66..d6768e7ebbc 100644
--- a/mcs/tools/wasm-tuner/tuner.cs
+++ b/mcs/tools/wasm-tuner/tuner.cs
@@ -40,22 +40,6 @@ class IcallClass {
public Dictionary<string, Icall> Icalls;
}
-class Pinvoke
-{
- public Pinvoke (string entry_point, string module, MethodReference method) {
- EntryPoint = entry_point;
- TableEntryPoint = entry_point;
- Module = module;
- Method = method;
- }
-
- public string EntryPoint;
- public string TableEntryPoint;
- public string Module;
- public MethodReference Method;
- public string CDecl;
-}
-
public class WasmTuner
{
public static int Main (String[] args) {
@@ -66,8 +50,6 @@ public class WasmTuner
Dictionary<string, IcallClass> runtime_icalls;
- List<Pinvoke> pinvokes;
-
// Read the icall table generated by mono --print-icall-table
void ReadTable (string filename) {
JsonValue json;
@@ -128,7 +110,7 @@ public class WasmTuner
}
}
- static string MapType (TypeReference t) {
+ public static string MapType (TypeReference t) {
if (t.Name == "Void")
return "void";
else if (t.Name == "Double")
@@ -143,91 +125,25 @@ public class WasmTuner
return "int";
}
- static string GenPinvokeDecl (Pinvoke pinvoke) {
- var sb = new StringBuilder ();
- var method = pinvoke.Method;
- sb.Append (MapType (method.ReturnType));
- sb.Append ($" {pinvoke.EntryPoint} (");
- int pindex = 0;
- foreach (var p in method.Parameters) {
- if (pindex > 0)
- sb.Append (",");
- sb.Append (MapType (method.Parameters [pindex].ParameterType));
- pindex ++;
- }
- sb.Append (");");
- return sb.ToString ();
- }
-
int GenPinvokeTable (String[] args) {
var modules = new Dictionary<string, string> ();
foreach (var module in args [1].Split (','))
modules [module] = module;
args = args.Skip (2).ToArray ();
- pinvokes = new List<Pinvoke> ();
- foreach (var fname in args) {
- var a = AssemblyDefinition.ReadAssembly (fname);
+ var assemblies = new List<AssemblyDefinition> ();
+ foreach (var fname in args)
+ assemblies.Add (AssemblyDefinition.ReadAssembly (fname));
- foreach (var type in a.MainModule.Types) {
- ProcessTypeForPinvoke (pinvokes, type);
- foreach (var nested in type.NestedTypes)
- ProcessTypeForPinvoke (pinvokes, nested);
- }
- }
-
- Console.WriteLine ("// GENERATED FILE, DO NOT MODIFY");
- Console.WriteLine ();
-
- var decls = new Dictionary<string, Pinvoke> ();
- foreach (var pinvoke in pinvokes) {
- if (modules.ContainsKey (pinvoke.Module)) {
- pinvoke.CDecl = GenPinvokeDecl (pinvoke);
- if (decls.TryGetValue (pinvoke.EntryPoint, out Pinvoke prev_pinvoke)) {
- if (pinvoke.CDecl != prev_pinvoke.CDecl) {
- Console.Error.WriteLine ($"Warning: PInvoke method '{pinvoke.EntryPoint}' has incompatible declarations.");
- pinvoke.TableEntryPoint = "mono_wasm_pinvoke_vararg_stub";
- prev_pinvoke.TableEntryPoint = "mono_wasm_pinvoke_vararg_stub";
- continue;
- }
- }
- decls [pinvoke.EntryPoint] = pinvoke;
- Console.WriteLine (pinvoke.CDecl);
- }
- }
-
- foreach (var module in modules.Keys) {
- string symbol = module.Replace (".", "_") + "_imports";
- Console.WriteLine ("static PinvokeImport " + symbol + " [] = {");
- foreach (var pinvoke in pinvokes) {
- if (pinvoke.Module == module)
- Console.WriteLine ("{\"" + pinvoke.EntryPoint + "\", " + pinvoke.TableEntryPoint + "},");
- }
- Console.WriteLine ("{NULL, NULL}");
- Console.WriteLine ("};");
- }
- Console.Write ("static void *pinvoke_tables[] = { ");
- foreach (var module in modules.Keys) {
- string symbol = module.Replace (".", "_") + "_imports";
- Console.Write (symbol + ",");
- }
- Console.WriteLine ("};");
- Console.Write ("static char *pinvoke_names[] = { ");
- foreach (var module in modules.Keys) {
- Console.Write ("\"" + module + "\"" + ",");
- }
- Console.WriteLine ("};");
+ var generator = new PInvokeTableGenerator ();
+ generator.Run (assemblies, modules);
return 0;
}
- void ProcessTypeForPinvoke (List<Pinvoke> pinvokes, TypeDefinition type) {
- foreach (var method in type.Methods) {
- var info = method.PInvokeInfo;
- if (info == null)
- continue;
- pinvokes.Add (new Pinvoke (info.EntryPoint, info.Module.Name, method));
- }
+ void Error (string msg) {
+ Console.Error.WriteLine (msg);
+ Environment.Exit (1);
}
static string GenIcallDecl (Icall icall) {
@@ -454,16 +370,14 @@ public class WasmTuner
string outfileName = args [1];
args = args.Skip (2).ToArray ();
- pinvokes = new List<Pinvoke> ();
- foreach (var fname in args) {
- var a = AssemblyDefinition.ReadAssembly (fname);
+ var assemblies = new List<AssemblyDefinition> ();
+ foreach (var fname in args)
+ assemblies.Add (AssemblyDefinition.ReadAssembly (fname));
- foreach (var type in a.MainModule.Types) {
- ProcessTypeForPinvoke (pinvokes, type);
- foreach (var nested in type.NestedTypes)
- ProcessTypeForPinvoke (pinvokes, nested);
- }
- }
+ List<Pinvoke> pinvokes;
+ List<PinvokeCallback> callbacks;
+
+ PInvokeTableGenerator.CollectPInvokes (assemblies, out pinvokes, out callbacks);
var gen = new InterpToNativeGenerator ();
foreach (var pinvoke in pinvokes)
diff --git a/mcs/tools/wasm-tuner/wasm-tuner.csproj b/mcs/tools/wasm-tuner/wasm-tuner.csproj
index d61ae501f27..ec8d1a52d83 100644
--- a/mcs/tools/wasm-tuner/wasm-tuner.csproj
+++ b/mcs/tools/wasm-tuner/wasm-tuner.csproj
@@ -54,6 +54,7 @@
<Compile Include="..\..\build\common\SR.cs" />
<Compile Include="..\..\class\System.Json\corefx\SR.cs" />
<Compile Include="InterpToNativeGenerator.cs" />
+ <Compile Include="PInvokeTableGenerator.cs" />
<Compile Include="tuner.cs" />
<ProjectReference Include="$(SolutionDir)\msvc\scripts\genconsts.csproj">
<Name>genconsts</Name>
diff --git a/mcs/tools/wasm-tuner/wasm-tuner.exe.sources b/mcs/tools/wasm-tuner/wasm-tuner.exe.sources
index dd547f12c6d..c89615d45af 100644
--- a/mcs/tools/wasm-tuner/wasm-tuner.exe.sources
+++ b/mcs/tools/wasm-tuner/wasm-tuner.exe.sources
@@ -1,5 +1,6 @@
tuner.cs
InterpToNativeGenerator.cs
+PInvokeTableGenerator.cs
../../build/common/Consts.cs
../../build/common/SR.cs
../../class/System.Json/corefx/SR.cs