summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/s2wasm.h56
-rw-r--r--src/wasm-linker.cpp13
-rw-r--r--test/dot_s/export_malloc_free.s29
-rw-r--r--test/dot_s/export_malloc_free.wast16
-rw-r--r--test/dot_s/invoke_wrapper.s67
-rw-r--r--test/dot_s/invoke_wrapper.wast95
-rw-r--r--test/dot_s/text_before_type.s2
7 files changed, 269 insertions, 9 deletions
diff --git a/src/s2wasm.h b/src/s2wasm.h
index b17ed0702..3a7cf31db 100644
--- a/src/s2wasm.h
+++ b/src/s2wasm.h
@@ -554,7 +554,7 @@ class S2WasmBuilder {
void parseFuncType() {
auto decl = make_unique<FunctionType>();
- Name name = getCommaSeparated();
+ Name rawName = getCommaSeparated();
skipComma();
if(match("void")) {
decl->result = none;
@@ -562,9 +562,11 @@ class S2WasmBuilder {
decl->result = getType();
}
while (*s && skipComma()) decl->params.push_back(getType());
- decl->name = "FUNCSIG$" + getSig(decl.get());
+ std::string sig = getSig(decl.get());
+ decl->name = "FUNCSIG$" + sig;
FunctionType *ty = wasm->checkFunctionType(decl->name);
+ Name name = fixEmExceptionInvoke(rawName, sig);
if (!ty) {
// The wasm module takes ownership of the FunctionType if we insert it.
// Otherwise it's already in the module and ours is freed.
@@ -876,14 +878,9 @@ class S2WasmBuilder {
} else {
// non-indirect call
Name assign = getAssign();
- Name target = linkerObj->resolveAlias(cleanFunction(getCommaSeparated()), LinkerObject::Relocation::kFunction);
-
+ Name rawTarget = cleanFunction(getCommaSeparated());
Call* curr = allocator->alloc<Call>();
- curr->target = target;
curr->type = type;
- if (!linkerObj->isFunctionImplemented(target)) {
- linkerObj->addUndefinedFunctionCall(curr);
- }
skipWhitespace();
if (*s == ',') {
skipComma();
@@ -893,6 +890,13 @@ class S2WasmBuilder {
curr->operands.push_back(inputs[i]);
}
}
+ Name target = linkerObj->resolveAlias(
+ fixEmExceptionInvoke(rawTarget, curr->type, curr->operands),
+ LinkerObject::Relocation::kFunction);
+ curr->target = target;
+ if (!linkerObj->isFunctionImplemented(target)) {
+ linkerObj->addUndefinedFunctionCall(curr);
+ }
setOutput(curr, assign);
}
};
@@ -1342,6 +1346,42 @@ class S2WasmBuilder {
}
}
+ // Converts invoke wrapper names generated by LLVM backend to real invoke
+ // wrapper names that are expected by JavaScript glue code.
+ // This is required to support wasm exception handling (asm.js style).
+ //
+ // LLVM backend lowers
+ // invoke @func(arg1, arg2) to label %invoke.cont unwind label %lpad
+ // into
+ // ... (some code)
+ // call @invoke_SIG(func, arg1, arg2)
+ // ... (some code)
+ // SIG is a mangled string generated based on the LLVM IR-level function
+ // signature. In LLVM IR, types are not lowered yet, so this mangling scheme
+ // simply takes LLVM's string representtion of parameter types and concatenate
+ // them with '_'. For example, the name of an invoke wrapper for function
+ // void foo(struct mystruct*, int) will be
+ // "__invoke_void_%struct.mystruct*_int".
+ // This function converts the names of invoke wrappers based on their lowered
+ // argument types and a return type. In the example above, the resulting new
+ // wrapper name becomes "invoke_vii".
+ template<typename ListType>
+ Name fixEmExceptionInvoke(const Name &name, WasmType result,
+ const ListType &operands) {
+ return fixEmExceptionInvoke(name, getSig(result, operands));
+ }
+
+ Name fixEmExceptionInvoke(const Name &name, const std::string &sig) {
+ std::string nameStr = name.c_str();
+ if (nameStr.front() == '"' && nameStr.back() == '"') {
+ nameStr = nameStr.substr(1, nameStr.size() - 2);
+ }
+ if (nameStr.find("__invoke_") != 0) {
+ return name;
+ }
+ std::string sigWoOrigFunc = sig.front() + sig.substr(2, sig.size() - 2);
+ return Name("invoke_" + sigWoOrigFunc);
+ }
};
diff --git a/src/wasm-linker.cpp b/src/wasm-linker.cpp
index 45615fd59..d8bae7558 100644
--- a/src/wasm-linker.cpp
+++ b/src/wasm-linker.cpp
@@ -210,6 +210,19 @@ void Linker::layout() {
auto* func = out.wasm.getFunction(name);
func->type = ensureFunctionType(getSig(func), &out.wasm)->name;
}
+
+ // Export malloc and free whenever availble. JavsScript version of malloc has
+ // some issues and it cannot be called once _sbrk() is called.
+ // TODO This should get the list of exported functions from emcc.py - it has
+ // EXPORTED_FUNCTION metadata to keep track of this. Get the list of exported
+ // functions using a command-line argument from emcc.py and export all of
+ // them.
+ if (out.symbolInfo.implementedFunctions.count("malloc")) {
+ exportFunction("malloc", true);
+ }
+ if (out.symbolInfo.implementedFunctions.count("free")) {
+ exportFunction("free", true);
+ }
}
bool Linker::linkObject(S2WasmBuilder& builder) {
diff --git a/test/dot_s/export_malloc_free.s b/test/dot_s/export_malloc_free.s
new file mode 100644
index 000000000..0158811a4
--- /dev/null
+++ b/test/dot_s/export_malloc_free.s
@@ -0,0 +1,29 @@
+ .text
+ .file "export_malloc_free.bc"
+ .hidden main
+ .globl main
+ .type main,@function
+main:
+ .result i32
+ i32.const $push0=, 0
+ .endfunc
+.Lfunc_end1:
+ .size main, .Lfunc_end1-main
+
+ .weak malloc
+ .type malloc,@function
+malloc:
+ .param i32
+ .result i32
+ i32.const $push0=, 0
+ .endfunc
+.Lfunc_end20:
+ .size malloc, .Lfunc_end20-malloc
+
+ .weak free
+ .type free,@function
+free:
+ .param i32
+ .endfunc
+.Lfunc_end21:
+ .size free, .Lfunc_end21-free
diff --git a/test/dot_s/export_malloc_free.wast b/test/dot_s/export_malloc_free.wast
new file mode 100644
index 000000000..e3c2f3d6f
--- /dev/null
+++ b/test/dot_s/export_malloc_free.wast
@@ -0,0 +1,16 @@
+(module
+ (memory 1)
+ (export "memory" memory)
+ (export "main" $main)
+ (export "malloc" $malloc)
+ (export "free" $free)
+ (func $main (result i32)
+ (i32.const 0)
+ )
+ (func $malloc (param $0 i32) (result i32)
+ (i32.const 0)
+ )
+ (func $free (param $0 i32)
+ )
+)
+;; METADATA: { "asmConsts": {},"staticBump": 12, "initializers": [] }
diff --git a/test/dot_s/invoke_wrapper.s b/test/dot_s/invoke_wrapper.s
new file mode 100644
index 000000000..6de2f8b4c
--- /dev/null
+++ b/test/dot_s/invoke_wrapper.s
@@ -0,0 +1,67 @@
+ .text
+ .file "invoke_wrapper.bc"
+ .type _Z5func1v,@function
+_Z5func1v:
+ .endfunc
+.Lfunc_end0:
+ .size _Z5func1v, .Lfunc_end0-_Z5func1v
+
+ .type _Z5func2iii,@function
+_Z5func2iii:
+ .param i32, i32, i32
+ .result i32
+ i32.const $push0=, 3
+ .endfunc
+.Lfunc_end1:
+ .size _Z5func2iii, .Lfunc_end1-_Z5func2iii
+
+ .type _Z5func3fd,@function
+_Z5func3fd:
+ .param f32, f64
+ .result f32
+ f32.const $push0=, 0x1p0
+ .endfunc
+.Lfunc_end2:
+ .size _Z5func3fd, .Lfunc_end2-_Z5func3fd
+
+ .type _Z5func4P8mystructS_,@function
+_Z5func4P8mystructS_:
+ .param i32, i32
+ .result i32
+ i32.const $push0=, 0
+ .endfunc
+.Lfunc_end3:
+ .size _Z5func4P8mystructS_, .Lfunc_end3-_Z5func4P8mystructS_
+
+ .hidden main
+ .globl main
+ .type main,@function
+main:
+ .result i32
+ .local i32, i32, i32, i32
+ i32.const $push1=, _Z5func1v@FUNCTION
+ call __invoke_void@FUNCTION, $pop1
+ i32.const $push5=, _Z5func2iii@FUNCTION
+ i32.const $push4=, 1
+ i32.const $push3=, 2
+ i32.const $push2=, 3
+ i32.call $drop=, __invoke_i32_i32_i32_i32@FUNCTION, $pop5, $pop4, $pop3, $pop2
+ i32.const $push9=, _Z5func3fd@FUNCTION
+ f32.const $push8=, 0x1.8p0
+ f64.const $push7=, 0x1.b333333333333p1
+ f32.call $drop=, __invoke_float_float_double@FUNCTION, $pop9, $pop8, $pop7
+ i32.const $push21=, _Z5func4P8mystructS_@FUNCTION
+ i32.const $push37=, 32
+ i32.add $push38=, $1, $pop37
+ i32.const $push39=, 4
+ i32.add $push40=, $1, $pop39
+ i32.call $drop=, "__invoke_%struct.mystruct*_%struct.mystruct*_%struct.mystruct*"@FUNCTION, $pop21, $pop38, $pop40
+ i32.const $push23=, 0
+ .endfunc
+.Lfunc_end4:
+ .size main, .Lfunc_end4-main
+
+ .functype __invoke_void, void, i32
+ .functype __invoke_i32_i32_i32_i32, i32, i32, i32, i32, i32
+ .functype __invoke_float_float_double, f32, i32, f32, f64
+ .functype __invoke_%struct.mystruct*_%struct.mystruct*_%struct.mystruct*, i32, i32, i32, i32
diff --git a/test/dot_s/invoke_wrapper.wast b/test/dot_s/invoke_wrapper.wast
new file mode 100644
index 000000000..6aa7a4ce6
--- /dev/null
+++ b/test/dot_s/invoke_wrapper.wast
@@ -0,0 +1,95 @@
+(module
+ (memory 1)
+ (export "memory" memory)
+ (type $FUNCSIG$vi (func (param i32)))
+ (type $FUNCSIG$iiiii (func (param i32 i32 i32 i32) (result i32)))
+ (type $FUNCSIG$fifd (func (param i32 f32 f64) (result f32)))
+ (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32)))
+ (type $FUNCSIG$v (func))
+ (type $FUNCSIG$ffd (func (param f32 f64) (result f32)))
+ (type $FUNCSIG$iii (func (param i32 i32) (result i32)))
+ (import $invoke_ffd "env" "invoke_ffd" (param i32 f32 f64) (result f32))
+ (import $invoke_iii "env" "invoke_iii" (param i32 i32 i32) (result i32))
+ (import $invoke_iiii "env" "invoke_iiii" (param i32 i32 i32 i32) (result i32))
+ (import $invoke_v "env" "invoke_v" (param i32))
+ (export "main" $main)
+ (export "dynCall_v" $dynCall_v)
+ (export "dynCall_iiii" $dynCall_iiii)
+ (export "dynCall_ffd" $dynCall_ffd)
+ (export "dynCall_iii" $dynCall_iii)
+ (table $__wasm_nullptr $_Z5func1v $_Z5func2iii $_Z5func3fd $_Z5func4P8mystructS_)
+ (func $_Z5func1v (type $FUNCSIG$v)
+ )
+ (func $_Z5func2iii (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
+ (i32.const 3)
+ )
+ (func $_Z5func3fd (type $FUNCSIG$ffd) (param $0 f32) (param $1 f64) (result f32)
+ (f32.const 1)
+ )
+ (func $_Z5func4P8mystructS_ (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32)
+ (i32.const 0)
+ )
+ (func $main (result i32)
+ (local $0 i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local $3 i32)
+ (call_import $invoke_v
+ (i32.const 1)
+ )
+ (call_import $invoke_iiii
+ (i32.const 2)
+ (i32.const 1)
+ (i32.const 2)
+ (i32.const 3)
+ )
+ (call_import $invoke_ffd
+ (i32.const 3)
+ (f32.const 1.5)
+ (f64.const 3.4)
+ )
+ (call_import $invoke_iii
+ (i32.const 4)
+ (i32.add
+ (get_local $1)
+ (i32.const 32)
+ )
+ (i32.add
+ (get_local $1)
+ (i32.const 4)
+ )
+ )
+ (i32.const 0)
+ )
+ (func $__wasm_nullptr (type $FUNCSIG$v)
+ (unreachable)
+ )
+ (func $dynCall_v (param $fptr i32)
+ (call_indirect $FUNCSIG$v
+ (get_local $fptr)
+ )
+ )
+ (func $dynCall_iiii (param $fptr i32) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
+ (call_indirect $FUNCSIG$iiii
+ (get_local $fptr)
+ (get_local $0)
+ (get_local $1)
+ (get_local $2)
+ )
+ )
+ (func $dynCall_ffd (param $fptr i32) (param $0 f32) (param $1 f64) (result f32)
+ (call_indirect $FUNCSIG$ffd
+ (get_local $fptr)
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ (func $dynCall_iii (param $fptr i32) (param $0 i32) (param $1 i32) (result i32)
+ (call_indirect $FUNCSIG$iii
+ (get_local $fptr)
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+)
+;; METADATA: { "asmConsts": {},"staticBump": 12, "initializers": [] }
diff --git a/test/dot_s/text_before_type.s b/test/dot_s/text_before_type.s
index 7c341078a..c76ecfbed 100644
--- a/test/dot_s/text_before_type.s
+++ b/test/dot_s/text_before_type.s
@@ -1,5 +1,5 @@
.text
- .file "/tmp/tmpGckQku/foo.bc"
+ .file "text_before_type.bc"
.hidden main
.globl main
.type main,@function