diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rwxr-xr-x | build.sh | 2 | ||||
-rwxr-xr-x | check.py | 17 | ||||
-rw-r--r-- | src/parsing.h | 2 | ||||
-rw-r--r-- | src/s2wasm-main.cpp | 43 | ||||
-rw-r--r-- | src/s2wasm.h | 218 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 1 | ||||
-rw-r--r-- | test/dot_s/basics.s | 64 | ||||
-rw-r--r-- | test/dot_s/minimal.s | 12 | ||||
-rw-r--r-- | test/dot_s/minimal.wast | 12 |
10 files changed, 371 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore index 36e53a12a..1f4afbbf0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ bin/binaryen-shell bin/asm2wasm bin/wasm2asm +bin/s2wasm bin/wasm.js *~ *.diff @@ -4,6 +4,8 @@ echo "building asm2wasm" g++ -O2 -std=c++11 src/asm2wasm-main.cpp src/emscripten-optimizer/parser.cpp src/emscripten-optimizer/simple_ast.cpp src/emscripten-optimizer/optimizer-shared.cpp -o bin/asm2wasm echo "building wasm2asm" g++ -O2 -std=c++11 src/wasm2asm-main.cpp src/emscripten-optimizer/parser.cpp src/emscripten-optimizer/simple_ast.cpp src/emscripten-optimizer/optimizer-shared.cpp -o bin/wasm2asm +echo "building s2wasm" +g++ -O2 -std=c++11 src/s2wasm-main.cpp -o bin/s2wasm echo "building interpreter/js" em++ -std=c++11 src/wasm-js.cpp src/emscripten-optimizer/parser.cpp src/emscripten-optimizer/simple_ast.cpp src/emscripten-optimizer/optimizer-shared.cpp -o bin/wasm.js -s MODULARIZE=1 -s 'EXPORT_NAME="WasmJS"' --memory-init-file 0 -Oz -s ALLOW_MEMORY_GROWTH=1 -profiling -s DEMANGLE_SUPPORT=1 #-DWASM_JS_DEBUG -DWASM_INTERPRETER_DEBUG=2 cat src/js/post.js >> bin/wasm.js @@ -212,6 +212,23 @@ for t in spec_tests: if actual != expected: fail(actual, expected) +print '\n[ checking .s testcases... ]\n' + +for s in ['minimal.s']: + print '..', s + wasm = s.replace('.s', '.wast') + actual, err = subprocess.Popen([os.path.join('bin', 's2wasm'), os.path.join('test', 'dot_s', s)], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + assert err == '', 'bad err:' + err + + # verify output + expected_file = os.path.join('test', 'dot_s', wasm) + if not os.path.exists(expected_file): + print actual + raise Exception('output ' + expected_file + ' does not exist') + expected = open(expected_file).read() + if actual != expected: + fail(actual, expected) + print '\n[ checking example testcases... ]\n' subprocess.check_call(['g++', '-std=c++11', os.path.join('test', 'example', 'find_div0s.cpp'), '-Isrc', '-g']) diff --git a/src/parsing.h b/src/parsing.h index 1a207a3ab..59e8fc5ae 100644 --- a/src/parsing.h +++ b/src/parsing.h @@ -1,4 +1,6 @@ +#include <sstream> + #include "wasm.h" #include "shared-constants.h" #include "mixed_arena.h" diff --git a/src/s2wasm-main.cpp b/src/s2wasm-main.cpp new file mode 100644 index 000000000..011e4d164 --- /dev/null +++ b/src/s2wasm-main.cpp @@ -0,0 +1,43 @@ +// +// wasm2asm console tool +// + +#include "s2wasm.h" + +using namespace cashew; +using namespace wasm; + +namespace wasm { +int debug = 0; +} + +int main(int argc, char **argv) { + debug = getenv("WASM2ASM_DEBUG") ? getenv("WASM2ASM_DEBUG")[0] - '0' : 0; + + char *infile = argv[1]; + + if (debug) std::cerr << "loading '" << infile << "'...\n"; + FILE *f = fopen(argv[1], "r"); + assert(f); + fseek(f, 0, SEEK_END); + int size = ftell(f); + char *input = new char[size+1]; + rewind(f); + int num = fread(input, 1, size, f); + // On Windows, ftell() gives the byte position (\r\n counts as two bytes), but when + // reading, fread() returns the number of characters read (\r\n is read as one char \n, and counted as one), + // so return value of fread can be less than size reported by ftell, and that is normal. + assert((num > 0 || size == 0) && num <= size); + fclose(f); + input[num] = 0; + + if (debug) std::cerr << "parsing and wasming...\n"; + AllocatingModule wasm; + S2WasmBuilder s2wasm(wasm, input); + + if (debug) std::cerr << "printing...\n"; + std::cout << wasm; + + if (debug) std::cerr << "done.\n"; +} + diff --git a/src/s2wasm.h b/src/s2wasm.h new file mode 100644 index 000000000..e6782a284 --- /dev/null +++ b/src/s2wasm.h @@ -0,0 +1,218 @@ + +// +// .s to WebAssembly translator. +// + +#include "wasm.h" +#include "parsing.h" + +namespace wasm { + +extern int debug; // wasm::debug is set in main(), typically from an env var + +// +// S2WasmBuilder - parses a .s file into WebAssembly +// + +class S2WasmBuilder { + AllocatingModule& wasm; + MixedArena& allocator; + char *s; + +public: + S2WasmBuilder(AllocatingModule& wasm, char *s) : wasm(wasm), allocator(wasm.allocator), s(s) { + process(); + } + +private: + // utilities + + void skipWhitespace() { + while (1) { + while (*s && isspace(*s)) s++; + if (*s != '#') break; + while (*s != '\n') s++; + } + } + + void findComma() { + while (*s && *s != ',') s++; + s++; + skipWhitespace(); + } + + // match and skip the pattern, if matched + bool match(const char *pattern) { + size_t size = strlen(pattern); + if (strncmp(s, pattern, size) == 0) { + s += size; + skipWhitespace(); + return true; + } + return false; + } + + void mustMatch(const char *pattern) { + bool matched = match(pattern); + assert(matched); + } + + #define abort_on(why) { \ + printf("%s : %20s\n", why, s); \ + abort(); \ + } + + void dump(const char *text) { + std::cerr << text << "\n==========\n" << s << "\n==========\n"; + } + + Name getStr() { + std::string str; + while (*s && !isspace(*s)) { + str += *s; + s++; + } + return cashew::IString(str.c_str(), false); + } + + WasmType getType() { + if (match("i32")) return i32; + if (match("i64")) return i64; + if (match("f32")) return f32; + if (match("f64")) return f64; + abort_on("getType"); + } + + // state + + typedef std::pair<Const*, Name> Addressing; + std::vector<Addressing> addressings; + + // processors + + void process() { + while (*s) { + skipWhitespace(); + if (!*s) break; + if (*s != '.') break; + s++; + if (match("text")) parseText(); + else if (match("data")) parseData(); + else abort_on(s); + } + } + + void parseText() { + while (*s) { + skipWhitespace(); + if (!*s) break; + if (*s != '.') break; + s++; + if (match("file")) parseFile(); + else if (match("globl")) parseGlobl(); + else abort_on(s); + } + } + + void parseFile() { + assert(*s == '"'); + s++; + std::string filename; + while (*s != '"') { + filename += *s; + s++; + } + s++; + // TODO: use the filename? + } + + void parseGlobl() { + unsigned nextId = 0; + auto getNextId = [&nextId]() { + return cashew::IString(('$' + std::to_string(nextId++)).c_str(), false); + }; + + Name name = getStr(); + skipWhitespace(); + mustMatch(".type"); + mustMatch(name.str); + mustMatch(",@function"); + mustMatch(name.str); + mustMatch(":"); + auto func = allocator.alloc<Function>(); + // params and result + while (1) { + if (match(".param")) { + while (1) { + func->params.emplace_back(getNextId(), getType()); + skipWhitespace(); + if (!match(",")) break; + } + } else if (match(".result")) { + func->result = getType(); + } else break; + } + // parse body + auto currBlock = allocator.alloc<Block>(); + func->body = currBlock; + std::vector<Expression*> stack; + auto push = [&](Expression* curr) { + stack.push_back(curr); + }; + auto pop = [&]() { + Expression* ret = stack.back(); + stack.pop_back(); + return ret; + }; + while (1) { + skipWhitespace(); + if (match("i32.")) { + if (match("const")) { + mustMatch("$push"); + findComma(); + if (*s == '.') { + // global address + auto curr = allocator.alloc<Const>(); + curr->type = i32; + addressings.emplace_back(curr, getStr()); + push(curr); + } else { + // constant + push(parseConst(getStr(), i32, allocator)); + } + } + } else if (match("return")) { + Block *temp; + if (!(func->body && (temp = func->body->dyn_cast<Block>()) && temp->name == FAKE_RETURN)) { + Expression* old = func->body; + temp = allocator.alloc<Block>(); + temp->name = FAKE_RETURN; + if (old) temp->list.push_back(old); + func->body = temp; + } + auto curr = allocator.alloc<Break>(); + curr->name = FAKE_RETURN; + if (*s == '$') { + getStr(); + curr->value = pop(); + } + currBlock->list.push_back(curr); + } else if (match("func_end0:")) { + mustMatch(".size"); + mustMatch("main,"); + mustMatch("func_end0-main"); + wasm.addFunction(func); + return; // the function is done + } else { + break; + } + } + } + + void parseData() { + abort(); + } +}; + +} // namespace wasm + diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index c84c93dc9..1600e41f5 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -5,7 +5,6 @@ // #include <cmath> -#include <sstream> #include "wasm.h" #include "mixed_arena.h" diff --git a/test/dot_s/basics.s b/test/dot_s/basics.s new file mode 100644 index 000000000..2fc5adc8c --- /dev/null +++ b/test/dot_s/basics.s @@ -0,0 +1,64 @@ + .text + .file "/tmp/tmplu1mMq/a.out.bc" + .globl main + .type main,@function +main: # @main + .param i32, i32 + .result i32 +# BB#0: + i32.const $push0=, .str + call $discard=, puts, $pop0 + i32.const $push1=, 31 + i32.shr_s $push2=, $0, $pop1 + i32.const $push3=, 30 + i32.shr_u $push4=, $pop2, $pop3 + i32.add $push5=, $0, $pop4 + i32.const $push6=, -4 + i32.and $push7=, $pop5, $pop6 + i32.sub $push8=, $0, $pop7 + i32.const $push9=, 1 + i32.ne $push10=, $pop8, $pop9 + block BB0_5 + block BB0_4 + br_if $pop10, BB0_4 +BB0_1: # %.preheader + # =>This Inner Loop Header: Depth=1 + loop BB0_4 + i32.const $push12=, 10 + i32.gt_s $push13=, $0, $pop12 + i32.add $0=, $pop13, $0 + i32.const $push14=, 5 + i32.rem_s $push15=, $0, $pop14 + i32.const $push16=, 3 + i32.ne $push17=, $pop15, $pop16 + block BB0_3 + br_if $pop17, BB0_3 +# BB#2: # in Loop: Header=BB0_1 Depth=1 + i32.const $push18=, 111 + i32.rem_s $push19=, $0, $pop18 + i32.add $0=, $pop19, $0 +BB0_3: # in Loop: Header=BB0_1 Depth=1 + i32.const $push20=, 7 + i32.rem_s $push21=, $0, $pop20 + i32.const $push22=, 0 + i32.eq $push23=, $pop21, $pop22 + br_if $pop23, BB0_5 + br BB0_1 +BB0_4: + i32.const $push11=, -12 + i32.add $0=, $0, $pop11 +BB0_5: # %.loopexit + return $0 +func_end0: + .size main, func_end0-main + + .type .str,@object # @.str + .data +.str: + .asciz "hello, world!\n" + .size .str, 15 + + + .imports + .import puts "" puts (param i32) (result i32) + diff --git a/test/dot_s/minimal.s b/test/dot_s/minimal.s new file mode 100644 index 000000000..c329a6eec --- /dev/null +++ b/test/dot_s/minimal.s @@ -0,0 +1,12 @@ + .text + .file "/tmp/tmpAEEklZ/a.out.bc" + .globl main + .type main,@function +main: # @main + .result i32 +# BB#0: + i32.const $push0=, 5 + return $pop0 +func_end0: + .size main, func_end0-main + diff --git a/test/dot_s/minimal.wast b/test/dot_s/minimal.wast new file mode 100644 index 000000000..83a12abe3 --- /dev/null +++ b/test/dot_s/minimal.wast @@ -0,0 +1,12 @@ +(module + (memory 0 4294967295) + (func $0 (result i32) + (block $fake_return_waka123 + (block + (br $fake_return_waka123 + (i32.const 5) + ) + ) + ) + ) +) |