diff options
author | Alon Zakai <alonzakai@gmail.com> | 2016-10-06 17:37:50 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-10-06 17:37:50 -0700 |
commit | caf0a3db20bbc03d2261b2c5a112bc0eddd3ca73 (patch) | |
tree | e5474640cc73b60257d4011e39051d5792e260bb /src | |
parent | eb958269b8f3c5dd98bb24f99e9f1d5deceaa032 (diff) | |
download | binaryen-caf0a3db20bbc03d2261b2c5a112bc0eddd3ca73.tar.gz binaryen-caf0a3db20bbc03d2261b2c5a112bc0eddd3ca73.tar.bz2 binaryen-caf0a3db20bbc03d2261b2c5a112bc0eddd3ca73.zip |
Require unique names in binaryen IR (#746)
Diffstat (limited to 'src')
-rw-r--r-- | src/asm2wasm.h | 57 | ||||
-rw-r--r-- | src/ast_utils.h | 5 | ||||
-rw-r--r-- | src/cfg/Relooper.cpp | 6 | ||||
-rw-r--r-- | src/parsing.h | 85 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 66 | ||||
-rw-r--r-- | src/wasm-validator.h | 13 |
6 files changed, 167 insertions, 65 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h index d9618d8fd..acf973a69 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -30,6 +30,7 @@ #include "asm_v_wasm.h" #include "passes/passes.h" #include "pass.h" +#include "parsing.h" #include "ast_utils.h" #include "wasm-builder.h" #include "wasm-emscripten.h" @@ -1106,17 +1107,14 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { Ref params = ast[2]; Ref body = ast[3]; - unsigned nextId = 0; - auto getNextId = [&nextId](std::string prefix) { - return IString((prefix + '$' + std::to_string(nextId++)).c_str(), false); - }; + UniqueNameMapper nameMapper; // given an asm.js label, returns the wasm label for breaks or continues auto getBreakLabelName = [](IString label) { - return IString((std::string("label$break$") + label.str).c_str(), false); + return Name(std::string("label$break$") + label.str); }; auto getContinueLabelName = [](IString label) { - return IString((std::string("label$continue$") + label.str).c_str(), false); + return Name(std::string("label$continue$") + label.str); }; IStringSet functionVariables; // params or vars @@ -1705,13 +1703,14 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } else if (what == BLOCK) { Name name; if (parentLabel.is()) { - name = getBreakLabelName(parentLabel); + name = nameMapper.pushLabelName(getBreakLabelName(parentLabel)); parentLabel = IString(); breakStack.push_back(name); } auto ret = processStatements(ast[1], 0); if (name.is()) { breakStack.pop_back(); + nameMapper.popLabelName(name); Block* block = ret->dynCast<Block>(); if (block && block->name.isNull()) { block->name = name; @@ -1727,12 +1726,12 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } else if (what == BREAK) { auto ret = allocator.alloc<Break>(); assert(breakStack.size() > 0); - ret->name = !!ast[1] ? getBreakLabelName(ast[1]->getIString()) : breakStack.back(); + ret->name = !!ast[1] ? nameMapper.sourceToUnique(getBreakLabelName(ast[1]->getIString())) : breakStack.back(); return ret; } else if (what == CONTINUE) { auto ret = allocator.alloc<Break>(); assert(continueStack.size() > 0); - ret->name = !!ast[1] ? getContinueLabelName(ast[1]->getIString()) : continueStack.back(); + ret->name = !!ast[1] ? nameMapper.sourceToUnique(getContinueLabelName(ast[1]->getIString())) : continueStack.back(); return ret; } else if (what == WHILE) { bool forever = ast[1][0] == NUM && ast[1][1]->getInteger() == 1; @@ -1743,9 +1742,11 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { in = getContinueLabelName(parentLabel); parentLabel = IString(); } else { - out = getNextId("while-out"); - in = getNextId("while-in"); + out = "while-out"; + in = "while-in"; } + out = nameMapper.pushLabelName(out); + in = nameMapper.pushLabelName(in); ret->name = in; breakStack.push_back(out); continueStack.push_back(in); @@ -1774,6 +1775,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { ret->finalize(); continueStack.pop_back(); breakStack.pop_back(); + nameMapper.popLabelName(in); + nameMapper.popLabelName(out); return ret; } else if (what == DO) { if (ast[1][0] == NUM && ast[1][1]->getNumber() == 0) { @@ -1783,14 +1786,17 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { stop = getBreakLabelName(parentLabel); parentLabel = IString(); } else { - stop = getNextId("do-once"); + stop = "do-once"; } - IString more = getNextId("unlikely-continue"); + stop = nameMapper.pushLabelName(stop); + Name more = nameMapper.pushLabelName("unlikely-continue"); breakStack.push_back(stop); continueStack.push_back(more); auto child = process(ast[2]); continueStack.pop_back(); breakStack.pop_back(); + nameMapper.popLabelName(more); + nameMapper.popLabelName(stop); // if we never continued, we don't need a loop BreakSeeker breakSeeker(more); breakSeeker.walk(child); @@ -1819,15 +1825,19 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { in = getContinueLabelName(parentLabel); parentLabel = IString(); } else { - out = getNextId("do-out"); - in = getNextId("do-in"); + out = "do-out"; + in = "do-in"; } + out = nameMapper.pushLabelName(out); + in = nameMapper.pushLabelName(in); loop->name = in; breakStack.push_back(out); continueStack.push_back(in); loop->body = process(ast[2]); continueStack.pop_back(); breakStack.pop_back(); + nameMapper.popLabelName(in); + nameMapper.popLabelName(out); Break *continuer = allocator.alloc<Break>(); continuer->name = in; continuer->condition = process(ast[1]); @@ -1847,9 +1857,11 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { in = getContinueLabelName(parentLabel); parentLabel = IString(); } else { - out = getNextId("for-out"); - in = getNextId("for-in"); + out = "for-out"; + in = "for-in"; } + out = nameMapper.pushLabelName(out); + in = nameMapper.pushLabelName(in); ret->name = in; breakStack.push_back(out); continueStack.push_back(in); @@ -1873,6 +1885,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { ret->finalize(); continueStack.pop_back(); breakStack.pop_back(); + nameMapper.popLabelName(in); + nameMapper.popLabelName(out); Block *outer = allocator.alloc<Block>(); // add an outer block for the init as well outer->list.push_back(process(finit)); @@ -1957,8 +1971,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { name = getBreakLabelName(parentLabel); parentLabel = IString(); } else { - name = getNextId("switch"); + name = "switch"; } + name = nameMapper.pushLabelName(name); breakStack.push_back(name); auto br = allocator.alloc<Switch>(); @@ -2009,14 +2024,14 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { auto case_ = processStatements(body, 0); Name name; if (condition->isNull()) { - name = br->default_ = getNextId("switch-default"); + name = br->default_ = nameMapper.pushLabelName("switch-default"); } else { auto index = getLiteral(condition).getInteger(); assert(index >= min); index -= min; assert(index >= 0); uint64_t index_s = index; - name = getNextId("switch-case"); + name = nameMapper.pushLabelName("switch-case"); if (br->targets.size() <= index_s) { br->targets.resize(index_s + 1); } @@ -2028,6 +2043,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { next->list.push_back(case_); next->finalize(); top = next; + nameMapper.popLabelName(name); } // the outermost block can be branched to to exit the whole switch @@ -2042,6 +2058,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } breakStack.pop_back(); + nameMapper.popLabelName(name); return top; } diff --git a/src/ast_utils.h b/src/ast_utils.h index 8124d3a79..861a09aaf 100644 --- a/src/ast_utils.h +++ b/src/ast_utils.h @@ -25,8 +25,11 @@ namespace wasm { +// Finds if there are breaks targeting a name. Note that since names are +// unique in our IR, we just need to look for the name, and do not need +// to analyze scoping. struct BreakSeeker : public PostWalker<BreakSeeker, Visitor<BreakSeeker>> { - Name target; // look for this one XXX looking by name may fall prey to duplicate names + Name target; Index found; WasmType valueType; diff --git a/src/cfg/Relooper.cpp b/src/cfg/Relooper.cpp index 8a5337ed0..d412c7b31 100644 --- a/src/cfg/Relooper.cpp +++ b/src/cfg/Relooper.cpp @@ -24,6 +24,7 @@ #include <string> #include "ast_utils.h" +#include "parsing.h" namespace CFG { @@ -939,7 +940,10 @@ void Relooper::Calculate(Block *Entry) { wasm::Expression* Relooper::Render(RelooperBuilder& Builder) { assert(Root); - return Root->Render(Builder, false); + auto* ret = Root->Render(Builder, false); + // we may use the same name for more than one block in HandleFollowupMultiples + wasm::UniqueNameMapper::uniquify(ret); + return ret; } #ifdef RELOOPER_DEBUG diff --git a/src/parsing.h b/src/parsing.h index c5341c40b..1485dc6eb 100644 --- a/src/parsing.h +++ b/src/parsing.h @@ -191,6 +191,91 @@ struct ParseException { } }; +// Helper for parsers that may not have unique label names. This transforms +// the names into unique ones, as required by Binaryen IR. +struct UniqueNameMapper { + std::vector<Name> labelStack; + std::map<Name, std::vector<Name>> labelMappings; // name in source => stack of uniquified names + std::map<Name, Name> reverseLabelMapping; // uniquified name => name in source + + Index otherIndex = 0; + + Name getPrefixedName(Name prefix) { + if (reverseLabelMapping.find(prefix) == reverseLabelMapping.end()) return prefix; + // make sure to return a unique name not already on the stack + while (1) { + Name ret = Name(prefix.str + std::to_string(otherIndex++)); + if (reverseLabelMapping.find(ret) == reverseLabelMapping.end()) return ret; + } + } + + // receives a source name. generates a unique name, pushes it, and returns it + Name pushLabelName(Name sName) { + Name name = getPrefixedName(sName); + labelStack.push_back(name); + labelMappings[sName].push_back(name); + reverseLabelMapping[name] = sName; + return name; + } + + void popLabelName(Name name) { + assert(labelStack.back() == name); + labelStack.pop_back(); + labelMappings[reverseLabelMapping[name]].pop_back(); + } + + Name sourceToUnique(Name sName) { + return labelMappings.at(sName).back(); + } + + Name uniqueToSource(Name name) { + return reverseLabelMapping.at(name); + } + + void clear() { + labelStack.clear(); + labelMappings.clear(); + reverseLabelMapping.clear(); + } + + // Given an expression, ensures all names are unique + static void uniquify(Expression* curr) { + struct Walker : public ControlFlowWalker<Walker, Visitor<Walker>> { + UniqueNameMapper mapper; + + static void doPreVisitControlFlow(Walker* self, Expression** currp) { + auto* curr = *currp; + if (auto* block = curr->dynCast<Block>()) { + if (block->name.is()) block->name = self->mapper.pushLabelName(block->name); + } else if (auto* loop = curr->dynCast<Loop>()) { + if (loop->name.is()) loop->name = self->mapper.pushLabelName(loop->name); + } + } + static void doPostVisitControlFlow(Walker* self, Expression** currp) { + auto* curr = *currp; + if (auto* block = curr->dynCast<Block>()) { + if (block->name.is()) self->mapper.popLabelName(block->name); + } else if (auto* loop = curr->dynCast<Loop>()) { + if (loop->name.is()) self->mapper.popLabelName(loop->name); + } + } + + void visitBreak(Break *curr) { + curr->name = mapper.sourceToUnique(curr->name); + } + void visitSwitch(Switch *curr) { + for (auto& target : curr->targets) { + target = mapper.sourceToUnique(target); + } + curr->default_ = mapper.sourceToUnique(curr->default_); + } + }; + + Walker walker; + walker.walk(curr); + } +}; + } // namespace wasm #endif // wasm_parsing_h diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 72949eb2b..7f5afe8f7 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -428,16 +428,9 @@ private: std::map<Name, WasmType> currLocalTypes; size_t localIndex; // params and vars size_t otherIndex; - std::vector<Name> labelStack; bool brokeToAutoBlock; - Name getPrefixedName(std::string prefix) { - // make sure to return a unique name not already on the stack - while (1) { - Name ret = IString((prefix + std::to_string(otherIndex++)).c_str(), false); - if (std::find(labelStack.begin(), labelStack.end(), ret) == labelStack.end()) return ret; - } - } + UniqueNameMapper nameMapper; Name getFunctionName(Element& s) { if (s.dollared()) { @@ -642,7 +635,7 @@ private: wasm.addImport(im.release()); assert(!currFunction); currLocalTypes.clear(); - labelStack.clear(); + nameMapper.clear(); return; } assert(!preParseImport); @@ -662,7 +655,7 @@ private: currFunction->type = type; wasm.addFunction(currFunction.release()); currLocalTypes.clear(); - labelStack.clear(); + nameMapper.clear(); } WasmType stringToWasmType(IString str, bool allowError=false, bool prefix=false) { @@ -1085,18 +1078,18 @@ private: stack.emplace_back(sp, curr); auto& s = *sp; size_t i = 1; + Name sName; if (i < s.size() && s[i]->isStr()) { // could be a name or a type if (s[i]->dollared() || stringToWasmType(s[i]->str(), true /* allowError */) == none) { - curr->name = s[i]->str(); - i++; + sName = s[i++]->str(); } else { - curr->name = getPrefixedName("block"); + sName = "block"; } } else { - curr->name = getPrefixedName("block"); + sName = "block"; } - labelStack.push_back(curr->name); + curr->name = nameMapper.pushLabelName(sName); if (i >= s.size()) break; // empty block if (s[i]->isStr()) { // block signature @@ -1133,8 +1126,7 @@ private: curr->list.push_back(parseExpression(s[i])); } } - assert(labelStack.back() == curr->name); - labelStack.pop_back(); + nameMapper.popLabelName(curr->name); curr->finalize(curr->type); } return stack[0].second; @@ -1240,14 +1232,14 @@ private: Expression* makeIf(Element& s) { auto ret = allocator.alloc<If>(); Index i = 1; - Name label; + Name sName; if (s[i]->dollared()) { // the if is labeled - label = s[i++]->str(); + sName = s[i++]->str(); } else { - label = getPrefixedName("if"); + sName = "if"; } - labelStack.push_back(label); + auto label = nameMapper.pushLabelName(sName); WasmType type = none; if (s[i]->isStr()) { type = stringToWasmType(s[i++]->str()); @@ -1258,7 +1250,7 @@ private: ret->ifFalse = parseExpression(*s[i++]); } ret->finalize(type); - labelStack.pop_back(); + nameMapper.popLabelName(label); // create a break target if we must if (BreakSeeker::has(ret, label)) { auto* block = allocator.alloc<Block>(); @@ -1288,33 +1280,21 @@ private: Expression* makeLoop(Element& s) { auto ret = allocator.alloc<Loop>(); size_t i = 1; - Name out; - if (s.size() > i + 1 && s[i]->dollared() && s[i + 1]->dollared()) { // out can only be named if both are - out = s[i]->str(); - i++; - } + Name sName; if (s.size() > i && s[i]->dollared()) { - ret->name = s[i]->str(); - i++; + sName = s[i++]->str(); } else { - ret->name = getPrefixedName("loop-in"); + sName = "loop-in"; } + ret->name = nameMapper.pushLabelName(sName); ret->type = none; if (i < s.size() && s[i]->isStr()) { // block signature ret->type = stringToWasmType(s[i++]->str()); } - labelStack.push_back(ret->name); ret->body = makeMaybeBlock(s, i, ret->type); - labelStack.pop_back(); + nameMapper.popLabelName(ret->name); ret->finalize(ret->type); - if (out.is()) { - auto* block = allocator.alloc<Block>(); - block->name = out; - block->list.push_back(ret); - block->finalize(); - return block; - } return ret; } @@ -1367,17 +1347,17 @@ private: } Name getLabel(Element& s) { if (s.dollared()) { - return s.str(); + return nameMapper.sourceToUnique(s.str()); } else { // offset, break to nth outside label uint64_t offset = std::stoll(s.c_str(), nullptr, 0); - if (offset > labelStack.size()) throw ParseException("invalid label", s.line, s.col); - if (offset == labelStack.size()) { + if (offset > nameMapper.labelStack.size()) throw ParseException("invalid label", s.line, s.col); + if (offset == nameMapper.labelStack.size()) { // a break to the function's scope. this means we need an automatic block, with a name brokeToAutoBlock = true; return FAKE_RETURN; } - return labelStack[labelStack.size() - 1 - offset]; + return nameMapper.labelStack[nameMapper.labelStack.size() - 1 - offset]; } } Expression* makeBreak(Element& s) { diff --git a/src/wasm-validator.h b/src/wasm-validator.h index f17ed16d5..cb226a924 100644 --- a/src/wasm-validator.h +++ b/src/wasm-validator.h @@ -37,6 +37,8 @@ #ifndef wasm_wasm_validator_h #define wasm_wasm_validator_h +#include <set> + #include "wasm.h" #include "wasm-printing.h" @@ -61,6 +63,14 @@ struct WasmValidator : public PostWalker<WasmValidator, Visitor<WasmValidator>> WasmType returnType = unreachable; // type used in returns + std::set<Name> labelNames; // Binaryen IR requires that label names must be unique - IR generators must ensure that + + void noteLabelName(Name name) { + if (!name.is()) return; + shouldBeTrue(labelNames.find(name) == labelNames.end(), name, "names in Binaren IR must be unique - IR generators must ensure that"); + labelNames.insert(name); + } + public: bool validate(Module& module, bool validateWeb_ = false, bool validateGlobally_ = true) { validateWeb = validateWeb_; @@ -82,6 +92,7 @@ public: void visitBlock(Block *curr) { // if we are break'ed to, then the value must be right for us if (curr->name.is()) { + noteLabelName(curr->name); if (breakInfos.count(curr) > 0) { auto& info = breakInfos[curr]; if (isConcreteWasmType(curr->type)) { @@ -128,6 +139,7 @@ public: void visitLoop(Loop *curr) { if (curr->name.is()) { + noteLabelName(curr->name); breakTargets[curr->name].pop_back(); if (breakInfos.count(curr) > 0) { auto& info = breakInfos[curr]; @@ -405,6 +417,7 @@ public: } } returnType = unreachable; + labelNames.clear(); } bool isConstant(Expression* curr) { |