diff options
-rw-r--r-- | src/s2wasm.h | 56 | ||||
-rw-r--r-- | src/wasm-linker.cpp | 13 | ||||
-rw-r--r-- | test/dot_s/export_malloc_free.s | 29 | ||||
-rw-r--r-- | test/dot_s/export_malloc_free.wast | 16 | ||||
-rw-r--r-- | test/dot_s/invoke_wrapper.s | 67 | ||||
-rw-r--r-- | test/dot_s/invoke_wrapper.wast | 95 | ||||
-rw-r--r-- | test/dot_s/text_before_type.s | 2 |
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 |