Browse Source

[wasm] Linker changes. (#12433)

* [wasm] Add support for v8 style options to the packager, i.e. --option/--no-option.

Add a --linker option to turn on linking in interpreter mode.

* [wasm] Add a print-table.py helper script to print out the function table of a wasm module.

* [runtime] Add a --print-icall-table option to dump the icall table as json. Usable for linking the icall table.

* [runtime] Pass the method to the icall table lookup function.
Zoltan Varga 7 years ago
parent
commit
4d57a5786a

+ 1 - 1
mono/metadata/icall-table.c

@@ -223,7 +223,7 @@ find_class_icalls (const char *name)
 }
 }
 
 
 static gpointer
 static gpointer
-icall_table_lookup (char *classname, char *methodname, char *sigstart, gboolean *uses_handles)
+icall_table_lookup (MonoMethod *method, char *classname, char *methodname, char *sigstart, gboolean *uses_handles)
 {
 {
 	const IcallTypeDesc *imap = NULL;
 	const IcallTypeDesc *imap = NULL;
 	gpointer res;
 	gpointer res;

+ 2 - 2
mono/metadata/icall-table.h

@@ -11,11 +11,11 @@
 #include <mono/utils/mono-publib.h>
 #include <mono/utils/mono-publib.h>
 #include "marshal.h"
 #include "marshal.h"
 
 
-#define MONO_ICALL_TABLE_CALLBACKS_VERSION 1
+#define MONO_ICALL_TABLE_CALLBACKS_VERSION 2
 
 
 typedef struct {
 typedef struct {
 	int version;
 	int version;
-	gpointer (*lookup) (char *classname, char *methodname, char *sigstart, gboolean *uses_handles);
+	gpointer (*lookup) (MonoMethod *method, char *classname, char *methodname, char *sigstart, gboolean *uses_handles);
 	const char* (*lookup_icall_symbol) (gpointer func);
 	const char* (*lookup_icall_symbol) (gpointer func);
 } MonoIcallTableCallbacks;
 } MonoIcallTableCallbacks;
 
 

+ 6 - 7
mono/metadata/icall.c

@@ -8439,6 +8439,10 @@ mono_lookup_internal_call_full (MonoMethod *method, gboolean warn_on_missing, mo
 	sigstart [siglen + 2] = 0;
 	sigstart [siglen + 2] = 0;
 	g_free (tmpsig);
 	g_free (tmpsig);
 
 
+	/* mono_marshal_get_native_wrapper () depends on this */
+	if (method->klass == mono_defaults.string_class && !strcmp (method->name, ".ctor"))
+		return (gpointer)ves_icall_System_String_ctor_RedirectToCreateString;
+
 	mono_icall_lock ();
 	mono_icall_lock ();
 
 
 	res = g_hash_table_lookup (icall_hash, mname);
 	res = g_hash_table_lookup (icall_hash, mname);
@@ -8473,18 +8477,13 @@ mono_lookup_internal_call_full (MonoMethod *method, gboolean warn_on_missing, mo
 		return res;
 		return res;
 	}
 	}
 
 
-
 	if (!icall_table.lookup) {
 	if (!icall_table.lookup) {
 		mono_icall_unlock ();
 		mono_icall_unlock ();
 		g_free (classname);
 		g_free (classname);
 		/* Fail only when the result is actually used */
 		/* Fail only when the result is actually used */
-		/* mono_marshal_get_native_wrapper () depends on this */
-		if (method->klass == mono_defaults.string_class && !strcmp (method->name, ".ctor"))
-			return (gpointer)ves_icall_System_String_ctor_RedirectToCreateString;
-		else
-			return (gpointer)no_icall_table;
+		return (gpointer)no_icall_table;
 	} else {
 	} else {
-		res = icall_table.lookup (classname, sigstart - mlen, sigstart, uses_handles);
+		res = icall_table.lookup (method, classname, sigstart - mlen, sigstart, uses_handles);
 		g_free (classname);
 		g_free (classname);
 
 
 		mono_icall_unlock ();
 		mono_icall_unlock ();

+ 29 - 0
mono/mini/driver.c

@@ -1923,6 +1923,27 @@ mono_regression_test_step (int verbose_level, const char *image, const char *met
 
 
 	return mono_exec_regression_internal (verbose_level, 1, images, FALSE) == 0;
 	return mono_exec_regression_internal (verbose_level, 1, images, FALSE) == 0;
 }
 }
+
+#ifdef ENABLE_ICALL_SYMBOL_MAP
+/* Print the icall table as JSON */
+static void
+print_icall_table (void)
+{
+	// We emit some dummy values to make the code simpler
+
+	printf ("[\n{ \"klass\": \"\", \"icalls\": [");
+#define NOHANDLES(inner) inner
+#define HANDLES(id, name, func, ...)	printf ("\t,{ \"name\": \"%s\", \"func\": \"%s_raw\", \"handles\": true }\n", name, #func);
+#define HANDLES_REUSE_WRAPPER		HANDLES
+#define MONO_HANDLE_REGISTER_ICALL(...) /* nothing  */
+#define ICALL_TYPE(id,name,first) printf ("]},\n { \"klass\":\"%s\", \"icalls\": [{} ", name);
+#define ICALL(id,name,func) printf ("\t,{ \"name\": \"%s\", \"func\": \"%s\", \"handles\": false }\n", name, #func);
+#include <mono/metadata/icall-def.h>
+
+	printf ("]}\n]\n");
+}
+#endif
+
 /**
 /**
  * mono_main:
  * mono_main:
  * \param argc number of arguments in the argv array
  * \param argc number of arguments in the argv array
@@ -2305,6 +2326,14 @@ mono_main (int argc, char* argv[])
 			mono_enable_interp (NULL);
 			mono_enable_interp (NULL);
 		} else if (strncmp (argv [i], "--interp=", 9) == 0) {
 		} else if (strncmp (argv [i], "--interp=", 9) == 0) {
 			mono_enable_interp (argv [i] + 9);
 			mono_enable_interp (argv [i] + 9);
+		} else if (strcmp (argv [i], "--print-icall-table") == 0) {
+#ifdef ENABLE_ICALL_SYMBOL_MAP
+			print_icall_table ();
+			exit (0);
+#else
+			fprintf (stderr, "--print-icall-table requires a runtime configured with the --enable-icall-symbol-map option.\n");
+			exit (1);
+#endif
 		} else if (strncmp (argv [i], "--assembly-loader=", strlen("--assembly-loader=")) == 0) {
 		} else if (strncmp (argv [i], "--assembly-loader=", strlen("--assembly-loader=")) == 0) {
 			gchar *arg = argv [i] + strlen ("--assembly-loader=");
 			gchar *arg = argv [i] + strlen ("--assembly-loader=");
 			if (strcmp (arg, "strict") == 0)
 			if (strcmp (arg, "strict") == 0)

+ 5 - 5
sdks/wasm/Makefile

@@ -154,10 +154,11 @@ packager.exe: packager.cs Mono.Cecil.dll $(OPTIONS_CS)
 
 
 TEST_ASSEMBLIES = $(WASM_BCL_DIR)/nunitlite.dll $(WASM_BCL_DIR)/tests/wasm_corlib_test.dll $(WASM_BCL_DIR)/tests/wasm_System_test.dll $(WASM_BCL_DIR)/tests/wasm_System.Core_test.dll
 TEST_ASSEMBLIES = $(WASM_BCL_DIR)/nunitlite.dll $(WASM_BCL_DIR)/tests/wasm_corlib_test.dll $(WASM_BCL_DIR)/tests/wasm_System_test.dll $(WASM_BCL_DIR)/tests/wasm_System.Core_test.dll
 
 
-.stamp-build-test-suite: $(DRIVER_CONF)/.stamp-build packager.exe $(WASM_FRAMEWORK)/.stamp-framework binding_tests.dll mini_tests.dll main.exe runtime-tests.js
-	mono --debug packager.exe --template=runtime-tests.js --appdir=bin/test-suite --builddir=obj/test-suite binding_tests.dll $(WASM_FRAMEWORK)/WebAssembly.Net.Http.dll mini_tests.dll main.exe $(TEST_ASSEMBLIES)
+bin/test-suite/mono.js: $(DRIVER_CONF)/.stamp-build packager.exe $(WASM_FRAMEWORK)/.stamp-framework binding_tests.dll mini_tests.dll main.exe runtime-tests.js
+	mono --debug packager.exe --template=runtime-tests.js --appdir=bin/test-suite --builddir=obj/test-suite binding_tests.dll $(WASM_FRAMEWORK)/WebAssembly.Net.Http.dll mini_tests.dll main.exe $(TEST_ASSEMBLIES) --linker
 	ninja -v -C obj/test-suite
 	ninja -v -C obj/test-suite
-	touch $@
+
+build-test-suite: bin/test-suite/mono.js
 
 
 $(BROWSER_TEST)/.stamp-browser-test-suite: $(DRIVER_CONF)/.stamp-build packager.exe $(WASM_FRAMEWORK)/.stamp-framework $(BROWSER_TEST_SOURCES)
 $(BROWSER_TEST)/.stamp-browser-test-suite: $(DRIVER_CONF)/.stamp-build packager.exe $(WASM_FRAMEWORK)/.stamp-framework $(BROWSER_TEST_SOURCES)
 	$(CSC) $(CSC_FLAGS) /target:library -out:$(BROWSER_TEST)/HttpTestSuite.dll $(BCL_DEPS) $(WASM_FRAMEWORK_DEPS) $(BROWSER_TEST)/HttpTestSuite.cs 
 	$(CSC) $(CSC_FLAGS) /target:library -out:$(BROWSER_TEST)/HttpTestSuite.dll $(BCL_DEPS) $(WASM_FRAMEWORK_DEPS) $(BROWSER_TEST)/HttpTestSuite.cs 
@@ -174,6 +175,7 @@ aot-sample: packager.exe mini_tests.dll main.exe runtime.js bug.exe
 do-aot-sample: aot-sample
 do-aot-sample: aot-sample
 	(cd bin/aot-sample && $(SM) runtime.js --run bug.exe)
 	(cd bin/aot-sample && $(SM) runtime.js --run bug.exe)
 
 
+
 build-aot-mini: packager.exe mini_tests.dll main.exe runtime.js
 build-aot-mini: packager.exe mini_tests.dll main.exe runtime.js
 	mono packager.exe --emscripten-sdkdir=$(EMSCRIPTEN_SDKDIR) --mono-sdkdir=$(TOP)/sdks/out -appdir=bin/aot-mini --nobinding --builddir=obj/aot-mini --aot --template=runtime-tests.js mini_tests.dll
 	mono packager.exe --emscripten-sdkdir=$(EMSCRIPTEN_SDKDIR) --mono-sdkdir=$(TOP)/sdks/out -appdir=bin/aot-mini --nobinding --builddir=obj/aot-mini --aot --template=runtime-tests.js mini_tests.dll
 	ninja -v -C obj/aot-mini
 	ninja -v -C obj/aot-mini
@@ -217,8 +219,6 @@ run-aot-all: build-aot-all
 
 
 build-debug-sample: .stamp-build-debug-sample
 build-debug-sample: .stamp-build-debug-sample
 
 
-build-test-suite: .stamp-build-test-suite
-
 build-managed: build-debug-sample build-test-suite
 build-managed: build-debug-sample build-test-suite
 
 
 build-dbg-proxy:
 build-dbg-proxy:

+ 91 - 5
sdks/wasm/packager.cs

@@ -6,6 +6,51 @@ using Mono.Cecil;
 using Mono.Options;
 using Mono.Options;
 using Mono.Cecil.Cil;
 using Mono.Cecil.Cil;
 
 
+//
+// Google V8 style options:
+// - bool: --foo/--no-foo
+//
+
+enum FlagType {
+	BoolFlag,
+}
+
+// 'Option' is already used by Mono.Options
+class Flag {
+	public Flag (string name, string desc, FlagType type) {
+		Name = name;
+		FlagType = type;
+		Description = desc;
+	}
+
+	public string Name {
+		get; set;
+	}
+
+	public FlagType FlagType {
+		get; set;
+	}
+
+	public string Description {
+		get; set;
+	}
+}
+
+class BoolFlag : Flag {
+	public BoolFlag (string name, string description, bool def_value, Action<bool> action) : base (name, description, FlagType.BoolFlag) {
+		Setter = action;
+		DefaultValue = def_value;
+	}
+
+	public Action<bool> Setter {
+		get; set;
+	}
+
+	public bool DefaultValue {
+		get; set;
+	}
+}
+
 class Driver {
 class Driver {
 	static bool enable_debug, enable_linker;
 	static bool enable_debug, enable_linker;
 	static string app_prefix, framework_prefix, bcl_prefix, bcl_tools_prefix, bcl_facades_prefix, out_prefix;
 	static string app_prefix, framework_prefix, bcl_prefix, bcl_tools_prefix, bcl_facades_prefix, out_prefix;
@@ -44,13 +89,21 @@ class Driver {
 		None,
 		None,
 	}
 	}
 
 
+	void AddFlag (OptionSet options, Flag flag) {
+		if (flag is BoolFlag) {
+			options.Add (flag.Name, s => (flag as BoolFlag).Setter (true));
+			options.Add ("no-" + flag.Name, s => (flag as BoolFlag).Setter (false));
+		}
+		option_list.Add (flag);
+	}
+
+	static List<Flag> option_list = new List<Flag> ();
+
 	static void Usage () {
 	static void Usage () {
 		Console.WriteLine ("Usage: packager.exe <options> <assemblies>");
 		Console.WriteLine ("Usage: packager.exe <options> <assemblies>");
 		Console.WriteLine ("Valid options:");
 		Console.WriteLine ("Valid options:");
 		Console.WriteLine ("\t--help          Show this help message");
 		Console.WriteLine ("\t--help          Show this help message");
-		Console.WriteLine ("\t--debug         Enable Debugging (default false)");
 		Console.WriteLine ("\t--debugrt       Use the debug runtime (default release) - this has nothing to do with C# debugging");
 		Console.WriteLine ("\t--debugrt       Use the debug runtime (default release) - this has nothing to do with C# debugging");
-		Console.WriteLine ("\t--nobinding     Disable binding engine (default include engine)");
 		Console.WriteLine ("\t--aot           Enable AOT mode");
 		Console.WriteLine ("\t--aot           Enable AOT mode");
 		Console.WriteLine ("\t--aot-interp    Enable AOT+INTERP mode");
 		Console.WriteLine ("\t--aot-interp    Enable AOT+INTERP mode");
 		Console.WriteLine ("\t--prefix=x      Set the input assembly prefix to 'x' (default to the current directory)");
 		Console.WriteLine ("\t--prefix=x      Set the input assembly prefix to 'x' (default to the current directory)");
@@ -68,6 +121,17 @@ class Driver {
 		Console.WriteLine ("\t--aot-assemblies=x List of assemblies to AOT in AOT+INTERP mode.");
 		Console.WriteLine ("\t--aot-assemblies=x List of assemblies to AOT in AOT+INTERP mode.");
 
 
 		Console.WriteLine ("foo.dll         Include foo.dll as one of the root assemblies");
 		Console.WriteLine ("foo.dll         Include foo.dll as one of the root assemblies");
+		Console.WriteLine ();
+
+		Console.WriteLine ("Additional options (--option/--no-option):");
+		foreach (var flag in option_list) {
+			if (flag is BoolFlag) {
+				Console.WriteLine ("  --" + flag.Name + " (" + flag.Description + ")");
+				Console.WriteLine ("        type: bool  default: " + ((flag as BoolFlag).DefaultValue ? "true" : "false"));
+			}
+		}
+
+
 	}
 	}
 
 
 	static void Debug (string s) {
 	static void Debug (string s) {
@@ -235,6 +299,13 @@ class Driver {
 		AotInterp = 3
 		AotInterp = 3
 	}
 	}
 
 
+	class WasmOptions {
+		public bool Debug;
+		public bool DebugRuntime;
+		public bool AddBinding;
+		public bool Linker;
+	}
+
 	int Run (string[] args) {
 	int Run (string[] args) {
 		var add_binding = true;
 		var add_binding = true;
 		var root_assemblies = new List<string> ();
 		var root_assemblies = new List<string> ();
@@ -259,10 +330,15 @@ class Driver {
 		var copyType = CopyType.Default;
 		var copyType = CopyType.Default;
 		var ee_mode = ExecMode.Interp;
 		var ee_mode = ExecMode.Interp;
 
 
+		var opts = new WasmOptions () {
+				AddBinding = true,
+				Debug = false,
+				DebugRuntime = false,
+				Linker = false,
+			};
+
 		var p = new OptionSet () {
 		var p = new OptionSet () {
-				{ "debug", s => enable_debug = true },
-				{ "nobinding", s => add_binding = false },
-				{ "debugrt", s => use_release_runtime = false },
+				{ "nobinding", s => opts.AddBinding = false },
 				{ "out=", s => out_prefix = s },
 				{ "out=", s => out_prefix = s },
 				{ "appdir=", s => out_prefix = s },
 				{ "appdir=", s => out_prefix = s },
 				{ "builddir=", s => builddir = s },
 				{ "builddir=", s => builddir = s },
@@ -282,6 +358,11 @@ class Driver {
 				{ "help", s => print_usage = true },
 				{ "help", s => print_usage = true },
 					};
 					};
 
 
+		AddFlag (p, new BoolFlag ("debug", "enable c# debugging", opts.Debug, b => opts.Debug = b));
+		AddFlag (p, new BoolFlag ("debugrt", "enable debug runtime", opts.DebugRuntime, b => opts.DebugRuntime = b));
+		AddFlag (p, new BoolFlag ("linker", "enable the linker", opts.Linker, b => opts.Linker = b));
+		AddFlag (p, new BoolFlag ("binding", "enable the binding engine", opts.AddBinding, b => opts.AddBinding = b));
+
 		var new_args = p.Parse (args).ToArray ();
 		var new_args = p.Parse (args).ToArray ();
 		foreach (var a in new_args) {
 		foreach (var a in new_args) {
 			root_assemblies.Add (a);
 			root_assemblies.Add (a);
@@ -298,6 +379,11 @@ class Driver {
 			return 1;
 			return 1;
 		}
 		}
 
 
+		enable_debug = opts.Debug;
+		enable_linker = opts.Linker;
+		add_binding = opts.AddBinding;
+		use_release_runtime = !opts.DebugRuntime;
+
 		if (ee_mode == ExecMode.Aot || ee_mode == ExecMode.AotInterp)
 		if (ee_mode == ExecMode.Aot || ee_mode == ExecMode.AotInterp)
 			enable_aot = true;
 			enable_aot = true;
 
 

+ 24 - 0
sdks/wasm/print-table.py

@@ -0,0 +1,24 @@
+#!/usr/bin/python
+
+#
+# print-table.py: Print the function table for a webassembly .wast file
+#
+
+import sys
+
+prefix=" (elem (get_global $tableBase) "
+
+if len(sys.argv) < 2:
+    print "Usage: python print-table.py <path to mono.wast>"
+    sys.exit (1)
+
+f = open (sys.argv [1])
+table_line = None
+for line in f:
+     if prefix in line:
+         table_line = line[len(prefix):]
+         break
+     
+for (index, v) in enumerate (table_line.split (" ")):
+    print "" + str(index) + ": " + v
+    index += 1