From 6a48ce3e0f5778b419a201edb6159ea9d3574887 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Thu, 22 Sep 2016 16:29:45 -0700 Subject: [PATCH 1/9] Signatures for blocks, loops, and ifs --- src/wasm-binary.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 3047d43b74f..d7a0c580ab7 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -898,6 +898,7 @@ class WasmBinaryWriter : public Visitor { void visitBlock(Block *curr) { if (debug) std::cerr << "zz node: Block" << std::endl; o << int8_t(BinaryConsts::Block); + o << binaryWasmType(curr->type != unreachable ? curr->type : none); breakStack.push_back(curr->name); size_t i = 0; for (auto* child : curr->list) { @@ -924,6 +925,7 @@ class WasmBinaryWriter : public Visitor { if (debug) std::cerr << "zz node: If" << std::endl; recurse(curr->condition); o << int8_t(BinaryConsts::If); + o << binaryWasmType(curr->type != unreachable ? curr->type : none); breakStack.push_back(IMPOSSIBLE_CONTINUE); // the binary format requires this; we have a block if we need one; TODO: optimize recursePossibleBlockContents(curr->ifTrue); // TODO: emit block contents directly, if possible breakStack.pop_back(); @@ -938,6 +940,7 @@ class WasmBinaryWriter : public Visitor { void visitLoop(Loop *curr) { if (debug) std::cerr << "zz node: Loop" << std::endl; o << int8_t(BinaryConsts::Loop); + o << binaryWasmType(curr->type != unreachable ? curr->type : none); breakStack.push_back(curr->name); recursePossibleBlockContents(curr->body); breakStack.pop_back(); @@ -1908,6 +1911,7 @@ class WasmBinaryBuilder { // a common pattern that can be very highly nested. std::vector stack; while (1) { + curr->type = getWasmType(); curr->name = getNextLabel(); breakStack.push_back(curr->name); stack.push_back(curr); @@ -1971,6 +1975,7 @@ class WasmBinaryBuilder { void visitIf(If *curr) { if (debug) std::cerr << "zz node: If" << std::endl; + curr->type = getWasmType(); curr->condition = popExpression(); curr->ifTrue = getBlock(); if (lastSeparator == BinaryConsts::Else) { @@ -1981,6 +1986,7 @@ class WasmBinaryBuilder { } void visitLoop(Loop *curr) { if (debug) std::cerr << "zz node: Loop" << std::endl; + curr->type = getWasmType(); curr->name = getNextLabel(); breakStack.push_back(curr->name); curr->body = getMaybeBlock(); From 2dc70bcedae7a8aed1ee7469696858479a0c0d3c Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Fri, 23 Sep 2016 17:44:24 -0700 Subject: [PATCH 2/9] remove arities from breaks, fix switch encoding, fix nop/unreachable opcodes, name section --- src/wasm-binary.h | 71 ++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/src/wasm-binary.h b/src/wasm-binary.h index d7a0c580ab7..333b5ce7b65 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -257,7 +257,7 @@ enum ElementType { }; namespace UserSections { -extern const char* Names; +extern const char* Name; } enum ASTNodes { @@ -424,7 +424,7 @@ enum ASTNodes { GetGlobal = 0x1a, SetGlobal = 0x1b, - Nop = 0x00, + Unreachable = 0x00, Block = 0x01, Loop = 0x02, If = 0x03, @@ -434,7 +434,7 @@ enum ASTNodes { BrIf = 0x07, TableSwitch = 0x08, Return = 0x09, - Unreachable = 0x0a, + Nop = 0x0a, Drop = 0x0b, End = 0x0f }; @@ -825,7 +825,7 @@ class WasmBinaryWriter : public Visitor { if (wasm->functions.size() == 0) return; if (debug) std::cerr << "== writeNames" << std::endl; auto start = startSection(BinaryConsts::Section::User); - writeInlineString("names"); + writeInlineString(BinaryConsts::UserSections::Name); o << U32LEB(wasm->functions.size()); for (auto& curr : wasm->functions) { writeInlineString(curr->name.str); @@ -893,13 +893,15 @@ class WasmBinaryWriter : public Visitor { if (debug) std::cerr << "zz recurse from " << depth-- << " at " << o.size() << std::endl; } - std::vector breakStack; + struct BreakTarget { Name name; int arity; }; + std::vector breakStack; void visitBlock(Block *curr) { if (debug) std::cerr << "zz node: Block" << std::endl; o << int8_t(BinaryConsts::Block); + int arity = curr->type != unreachable && curr->type != none; o << binaryWasmType(curr->type != unreachable ? curr->type : none); - breakStack.push_back(curr->name); + breakStack.push_back({curr->name, arity}); size_t i = 0; for (auto* child : curr->list) { if (debug) std::cerr << " " << size_t(curr) << "\n zz Block element " << i++ << std::endl; @@ -926,12 +928,12 @@ class WasmBinaryWriter : public Visitor { recurse(curr->condition); o << int8_t(BinaryConsts::If); o << binaryWasmType(curr->type != unreachable ? curr->type : none); - breakStack.push_back(IMPOSSIBLE_CONTINUE); // the binary format requires this; we have a block if we need one; TODO: optimize + breakStack.push_back({IMPOSSIBLE_CONTINUE, 0}); // the binary format requires this; we have a block if we need one; TODO: optimize recursePossibleBlockContents(curr->ifTrue); // TODO: emit block contents directly, if possible breakStack.pop_back(); if (curr->ifFalse) { o << int8_t(BinaryConsts::Else); - breakStack.push_back(IMPOSSIBLE_CONTINUE); // TODO ditto + breakStack.push_back({IMPOSSIBLE_CONTINUE, 0}); // TODO ditto recursePossibleBlockContents(curr->ifFalse); breakStack.pop_back(); } @@ -941,7 +943,8 @@ class WasmBinaryWriter : public Visitor { if (debug) std::cerr << "zz node: Loop" << std::endl; o << int8_t(BinaryConsts::Loop); o << binaryWasmType(curr->type != unreachable ? curr->type : none); - breakStack.push_back(curr->name); + int arity = curr->type != unreachable && curr->type != none; + breakStack.push_back({curr->name, arity}); recursePossibleBlockContents(curr->body); breakStack.pop_back(); o << int8_t(BinaryConsts::End); @@ -949,7 +952,7 @@ class WasmBinaryWriter : public Visitor { int32_t getBreakIndex(Name name) { // -1 if not found for (int i = breakStack.size() - 1; i >= 0; i--) { - if (breakStack[i] == name) { + if (breakStack[i].name == name) { return breakStack.size() - 1 - i; } } @@ -964,7 +967,7 @@ class WasmBinaryWriter : public Visitor { } if (curr->condition) recurse(curr->condition); o << int8_t(curr->condition ? BinaryConsts::BrIf : BinaryConsts::Br) - << U32LEB(curr->value ? 1 : 0) << U32LEB(getBreakIndex(curr->name)); + << U32LEB(getBreakIndex(curr->name)); } void visitSwitch(Switch *curr) { if (debug) std::cerr << "zz node: Switch" << std::endl; @@ -972,11 +975,11 @@ class WasmBinaryWriter : public Visitor { recurse(curr->value); } recurse(curr->condition); - o << int8_t(BinaryConsts::TableSwitch) << U32LEB(curr->value ? 1 : 0) << U32LEB(curr->targets.size()); + o << int8_t(BinaryConsts::TableSwitch) << U32LEB(curr->targets.size()); for (auto target : curr->targets) { - o << uint32_t(getBreakIndex(target)); + o << U32LEB(getBreakIndex(target)); } - o << uint32_t(getBreakIndex(curr->default_)); + o << U32LEB(getBreakIndex(curr->default_)); } void visitCall(Call *curr) { if (debug) std::cerr << "zz node: Call" << std::endl; @@ -1343,7 +1346,7 @@ class WasmBinaryBuilder { bool readUserSection() { Name sectionName = getInlineString(); - if (sectionName.equals(BinaryConsts::UserSections::Names)) { + if (sectionName.equals(BinaryConsts::UserSections::Name)) { readNames(); return true; } @@ -1723,7 +1726,8 @@ class WasmBinaryBuilder { } } - std::vector breakStack; + struct BreakTarget { Name name; int arity;}; + std::vector breakStack; std::vector expressionStack; @@ -1913,7 +1917,7 @@ class WasmBinaryBuilder { while (1) { curr->type = getWasmType(); curr->name = getNextLabel(); - breakStack.push_back(curr->name); + breakStack.push_back({curr->name, curr->type != none}); stack.push_back(curr); if (getInt8() == BinaryConsts::Block) { // a recursion @@ -1964,9 +1968,9 @@ class WasmBinaryBuilder { return block; } - Expression* getBlock() { + Expression* getBlock(WasmType ty) { Name label = getNextLabel(); - breakStack.push_back(label); + breakStack.push_back({label, ty != none && ty != unreachable}); auto* block = Builder(wasm).blockify(getMaybeBlock()); breakStack.pop_back(); block->cast()->name = label; @@ -1977,9 +1981,9 @@ class WasmBinaryBuilder { if (debug) std::cerr << "zz node: If" << std::endl; curr->type = getWasmType(); curr->condition = popExpression(); - curr->ifTrue = getBlock(); + curr->ifTrue = getBlock(curr->type); if (lastSeparator == BinaryConsts::Else) { - curr->ifFalse = getBlock(); + curr->ifFalse = getBlock(curr->type); curr->finalize(); } assert(lastSeparator == BinaryConsts::End); @@ -1988,37 +1992,40 @@ class WasmBinaryBuilder { if (debug) std::cerr << "zz node: Loop" << std::endl; curr->type = getWasmType(); curr->name = getNextLabel(); - breakStack.push_back(curr->name); + breakStack.push_back({curr->name, 0}); curr->body = getMaybeBlock(); breakStack.pop_back(); curr->finalize(); } - Name getBreakName(int32_t offset) { + BreakTarget getBreakTarget(int32_t offset) { + if (debug) std::cerr << "getBreakTarget "<name = getBreakName(getU32LEB()); + BreakTarget target = getBreakTarget(getU32LEB()); + curr->name = target.name; if (code == BinaryConsts::BrIf) curr->condition = popExpression(); - if (arity == 1) curr->value = popExpression(); + if (target.arity) curr->value = popExpression(); curr->finalize(); } void visitSwitch(Switch *curr) { if (debug) std::cerr << "zz node: Switch" << std::endl; - auto arity = getU32LEB(); - assert(arity == 0 || arity == 1); curr->condition = popExpression(); - if (arity == 1) curr->value = popExpression(); + auto numTargets = getU32LEB(); + if (debug) std::cerr << "targets: "<< numTargets<targets.push_back(getBreakName(getInt32())); + curr->targets.push_back(getBreakTarget(getU32LEB()).name); } - curr->default_ = getBreakName(getInt32()); + auto defaultTarget = getBreakTarget(getU32LEB()); + curr->default_ = defaultTarget.name; + if (debug) std::cerr << "default: "<< curr->default_<value = popExpression(); } template From c86677918a3705e5ab92f03032ec1d0949f89302 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Fri, 23 Sep 2016 18:19:37 -0700 Subject: [PATCH 3/9] forgot to add the rest of the files --- src/passes/Print.cpp | 3 +++ src/wasm-s-parser.h | 1 + src/wasm.cpp | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 363147b9868..69dc447a8da 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -162,6 +162,9 @@ struct PrintSExpression : public Visitor { if (curr->name.is()) { o << ' ' << curr->name; } + if (isConcreteWasmType(curr->type)) { + o << ' ' << printWasmType(curr->type); + } incIndent(); auto block = curr->body->dynCast(); if (!full && block && block->name.isNull()) { diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index bf2ab773963..c1c19e1b00a 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -652,6 +652,7 @@ class SExpressionWasmBuilder { } if (autoBlock) { autoBlock->finalize(); + autoBlock->type = result; } if (!currFunction) { makeFunction(); diff --git a/src/wasm.cpp b/src/wasm.cpp index f43f4be1e1a..b5a4314de54 100644 --- a/src/wasm.cpp +++ b/src/wasm.cpp @@ -27,7 +27,7 @@ Name WASM("wasm"), namespace BinaryConsts { namespace UserSections { -const char* Names = "names"; +const char* Name = "name"; } } From f8f0658a794f09ac25cf2603e7d1b720cea8b500 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Tue, 27 Sep 2016 11:14:00 -0700 Subject: [PATCH 4/9] update tests to use block/loop signatures --- test/passes/nm.txt | 2 +- test/passes/simplify-locals.txt | 2 +- test/unit.wast | 2 +- test/unit.wast.fromBinary | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/passes/nm.txt b/test/passes/nm.txt index 1b12c5cffaf..27b12bfdae5 100644 --- a/test/passes/nm.txt +++ b/test/passes/nm.txt @@ -9,7 +9,7 @@ ) (func $b (type $0) (drop - (loop $loop-in1 + (loop $loop-in1 i32 (nop) (i32.const 1000) ) diff --git a/test/passes/simplify-locals.txt b/test/passes/simplify-locals.txt index f1205488772..15cd591405f 100644 --- a/test/passes/simplify-locals.txt +++ b/test/passes/simplify-locals.txt @@ -323,7 +323,7 @@ (i32.const 1337) ) (drop - (loop $loop-in5 + (loop $loop-in5 i32 (drop (get_local $a) ) diff --git a/test/unit.wast b/test/unit.wast index fab6705d24c..2ba0f66126a 100644 --- a/test/unit.wast +++ b/test/unit.wast @@ -429,7 +429,7 @@ (i32.const 0) ) (func $loop-roundtrip (type $7) (param $0 f64) (result f64) - (loop $loop-in1 + (loop $loop-in1 f64 (drop (get_local $0) ) diff --git a/test/unit.wast.fromBinary b/test/unit.wast.fromBinary index 1080d5d2069..8516d17757a 100644 --- a/test/unit.wast.fromBinary +++ b/test/unit.wast.fromBinary @@ -439,7 +439,7 @@ ) ) (func $loop-roundtrip (type $7) (param $var$0 f64) (result f64) - (loop $label$0 + (loop $label$0 f64 (drop (get_local $var$0) ) From 19343525b6882bf927168c37f17eaa3619d05081 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Tue, 27 Sep 2016 11:21:42 -0700 Subject: [PATCH 5/9] remove autoblock change from wasm-s-parser --- src/wasm-s-parser.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index c1c19e1b00a..bf2ab773963 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -652,7 +652,6 @@ class SExpressionWasmBuilder { } if (autoBlock) { autoBlock->finalize(); - autoBlock->type = result; } if (!currFunction) { makeFunction(); From 43b8bd7cc17ba719e38f4606ea080f5b321e2a70 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Tue, 27 Sep 2016 11:52:49 -0700 Subject: [PATCH 6/9] Update new test from master --- test/unit.wast.fromBinary.noDebugInfo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit.wast.fromBinary.noDebugInfo b/test/unit.wast.fromBinary.noDebugInfo index 4d957850010..d6980b9bedb 100644 --- a/test/unit.wast.fromBinary.noDebugInfo +++ b/test/unit.wast.fromBinary.noDebugInfo @@ -439,7 +439,7 @@ ) ) (func $20 (type $7) (param $var$0 f64) (result f64) - (loop $label$0 + (loop $label$0 f64 (drop (get_local $var$0) ) From 6b0ec0068904196bff5d5c14b8ac56ff1aecc0df Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Tue, 27 Sep 2016 15:46:29 -0700 Subject: [PATCH 7/9] Update c-api tests for loop signatures --- test/example/c-api-kitchen-sink.txt | 8 ++++---- test/example/c-api-kitchen-sink.txt.txt | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index b669821405d..8b4de4402da 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -415,12 +415,12 @@ BinaryenFloat64: 4 ) ) (drop - (loop $in + (loop $in i32 (i32.const 0) ) ) (drop - (loop + (loop i32 (i32.const 0) ) ) @@ -1958,12 +1958,12 @@ int main() { ) ) (drop - (loop $in + (loop $in i32 (i32.const 0) ) ) (drop - (loop + (loop i32 (i32.const 0) ) ) diff --git a/test/example/c-api-kitchen-sink.txt.txt b/test/example/c-api-kitchen-sink.txt.txt index b42c1ec344a..4d166cc2e01 100644 --- a/test/example/c-api-kitchen-sink.txt.txt +++ b/test/example/c-api-kitchen-sink.txt.txt @@ -410,12 +410,12 @@ ) ) (drop - (loop $in + (loop $in i32 (i32.const 0) ) ) (drop - (loop + (loop i32 (i32.const 0) ) ) From 72df460f0ebb38d4791fd7d1690e606f99955484 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Tue, 27 Sep 2016 17:40:46 -0700 Subject: [PATCH 8/9] Use arity finder for encoding unreachable block arities --- src/wasm-binary.h | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 333b5ce7b65..a0096476f24 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -451,6 +451,20 @@ enum TypeForms { } // namespace BinaryConsts + +struct ArityChecker : public PostWalker> { + std::unordered_map arities; + + ArityChecker(Expression* function) { + walk(function); + } + + void visitBreak(Break* curr) { + // Assume the module has already beeen type-checked, and that all breaks have matching arity. + if (curr->value) arities[curr->name] = true; + } +}; + inline int8_t binaryWasmType(WasmType type) { switch (type) { case none: return 0; @@ -468,6 +482,8 @@ class WasmBinaryWriter : public Visitor { bool debug; bool debugInfo = true; + std::unordered_map brTargetArities; + MixedArena allocator; void prepare() { @@ -701,6 +717,10 @@ class WasmBinaryWriter : public Visitor { if (numLocalsByType[i64]) o << U32LEB(numLocalsByType[i64]) << binaryWasmType(i64); if (numLocalsByType[f32]) o << U32LEB(numLocalsByType[f32]) << binaryWasmType(f32); if (numLocalsByType[f64]) o << U32LEB(numLocalsByType[f64]) << binaryWasmType(f64); + + ArityChecker ar(function->body); + brTargetArities = std::move(ar.arities); + writeExpression(function->body); size_t size = o.size() - start; assert(size <= std::numeric_limits::max()); @@ -899,8 +919,18 @@ class WasmBinaryWriter : public Visitor { void visitBlock(Block *curr) { if (debug) std::cerr << "zz node: Block" << std::endl; o << int8_t(BinaryConsts::Block); - int arity = curr->type != unreachable && curr->type != none; - o << binaryWasmType(curr->type != unreachable ? curr->type : none); + + int arity = curr->type != none && curr->type != unreachable; + if (brTargetArities.count(curr->name)) { + if (curr->type == unreachable) { + arity = brTargetArities[curr->name]; + } else { + assert((curr->type != none) == brTargetArities[curr->name]); + } + } + // For blocks with type unreachable but whose breaks have arity 1, encode i32 as their + // signature so that the decoder knows to pop a value for the breaks' values. + o << binaryWasmType(curr->type != unreachable ? curr->type : arity ? i32 : none); breakStack.push_back({curr->name, arity}); size_t i = 0; for (auto* child : curr->list) { From 3e52a5edfe2610de510aacced0e6a7d157c6fcda Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Tue, 27 Sep 2016 17:45:03 -0700 Subject: [PATCH 9/9] No need for writer's breakStack to have arities --- src/wasm-binary.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/wasm-binary.h b/src/wasm-binary.h index a0096476f24..4a32360a4a5 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -913,8 +913,7 @@ class WasmBinaryWriter : public Visitor { if (debug) std::cerr << "zz recurse from " << depth-- << " at " << o.size() << std::endl; } - struct BreakTarget { Name name; int arity; }; - std::vector breakStack; + std::vector breakStack; void visitBlock(Block *curr) { if (debug) std::cerr << "zz node: Block" << std::endl; @@ -931,7 +930,7 @@ class WasmBinaryWriter : public Visitor { // For blocks with type unreachable but whose breaks have arity 1, encode i32 as their // signature so that the decoder knows to pop a value for the breaks' values. o << binaryWasmType(curr->type != unreachable ? curr->type : arity ? i32 : none); - breakStack.push_back({curr->name, arity}); + breakStack.push_back(curr->name); size_t i = 0; for (auto* child : curr->list) { if (debug) std::cerr << " " << size_t(curr) << "\n zz Block element " << i++ << std::endl; @@ -958,12 +957,12 @@ class WasmBinaryWriter : public Visitor { recurse(curr->condition); o << int8_t(BinaryConsts::If); o << binaryWasmType(curr->type != unreachable ? curr->type : none); - breakStack.push_back({IMPOSSIBLE_CONTINUE, 0}); // the binary format requires this; we have a block if we need one; TODO: optimize + breakStack.push_back(IMPOSSIBLE_CONTINUE); // the binary format requires this; we have a block if we need one; TODO: optimize recursePossibleBlockContents(curr->ifTrue); // TODO: emit block contents directly, if possible breakStack.pop_back(); if (curr->ifFalse) { o << int8_t(BinaryConsts::Else); - breakStack.push_back({IMPOSSIBLE_CONTINUE, 0}); // TODO ditto + breakStack.push_back(IMPOSSIBLE_CONTINUE); // TODO ditto recursePossibleBlockContents(curr->ifFalse); breakStack.pop_back(); } @@ -973,8 +972,7 @@ class WasmBinaryWriter : public Visitor { if (debug) std::cerr << "zz node: Loop" << std::endl; o << int8_t(BinaryConsts::Loop); o << binaryWasmType(curr->type != unreachable ? curr->type : none); - int arity = curr->type != unreachable && curr->type != none; - breakStack.push_back({curr->name, arity}); + breakStack.push_back(curr->name); recursePossibleBlockContents(curr->body); breakStack.pop_back(); o << int8_t(BinaryConsts::End); @@ -982,7 +980,7 @@ class WasmBinaryWriter : public Visitor { int32_t getBreakIndex(Name name) { // -1 if not found for (int i = breakStack.size() - 1; i >= 0; i--) { - if (breakStack[i].name == name) { + if (breakStack[i] == name) { return breakStack.size() - 1 - i; } }