summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rwxr-xr-xbuild.sh2
-rwxr-xr-xcheck.py17
-rw-r--r--src/parsing.h2
-rw-r--r--src/s2wasm-main.cpp43
-rw-r--r--src/s2wasm.h218
-rw-r--r--src/wasm-s-parser.h1
-rw-r--r--test/dot_s/basics.s64
-rw-r--r--test/dot_s/minimal.s12
-rw-r--r--test/dot_s/minimal.wast12
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
diff --git a/build.sh b/build.sh
index bbf7ab3c8..3ce42fcb9 100755
--- a/build.sh
+++ b/build.sh
@@ -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
diff --git a/check.py b/check.py
index 5f745b15c..65d0549e9 100755
--- a/check.py
+++ b/check.py
@@ -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)
+ )
+ )
+ )
+ )
+)