diff options
Diffstat (limited to 'src/wasm')
-rw-r--r-- | src/wasm/literal.cpp | 2 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 6 | ||||
-rw-r--r-- | src/wasm/wasm-type.cpp | 217 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 47 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 12 |
5 files changed, 201 insertions, 83 deletions
diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp index 8d8868eb4..41d8d2924 100644 --- a/src/wasm/literal.cpp +++ b/src/wasm/literal.cpp @@ -252,7 +252,7 @@ void Literal::printVec128(std::ostream& o, const std::array<uint8_t, 16>& v) { } std::ostream& operator<<(std::ostream& o, Literal literal) { - prepareMinorColor(o) << printType(literal.type) << ".const "; + prepareMinorColor(o) << literal.type << ".const "; switch (literal.type) { case Type::none: o << "?"; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 8d6721c9c..8f2fe3fe7 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1098,7 +1098,7 @@ Type WasmBinaryBuilder::getType() { Type WasmBinaryBuilder::getConcreteType() { auto type = getType(); - if (!isConcreteType(type)) { + if (!type.isConcrete()) { throw ParseException("non-concrete type when one expected"); } return type; @@ -1860,7 +1860,7 @@ Expression* WasmBinaryBuilder::popNonVoidExpression() { } requireFunctionContext("popping void where we need a new local"); auto type = block->list[0]->type; - if (isConcreteType(type)) { + if (type.isConcrete()) { auto local = builder.addVar(currFunction, type); block->list[0] = builder.makeLocalSet(local, block->list[0]); block->list.push_back(builder.makeLocalGet(local, type)); @@ -2417,7 +2417,7 @@ void WasmBinaryBuilder::pushBlockElements(Block* curr, if (i < end - 1) { // stacky&unreachable code may introduce elements that need to be dropped // in non-final positions - if (isConcreteType(item->type)) { + if (item->type.isConcrete()) { curr->list.back() = Builder(wasm).makeDrop(item); if (consumable == NONE) { // this is the first, and hence consumable value. note the location diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 39383a478..9d0f9b108 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -14,38 +14,193 @@ * limitations under the License. */ +#include <cassert> +#include <shared_mutex> +#include <sstream> +#include <unordered_map> + #include "wasm-type.h" #include "wasm-features.h" #include "compiler-support.h" -#include <cstdlib> + +template<> class std::hash<std::vector<wasm::Type>> { +public: + size_t operator()(const std::vector<wasm::Type>& types) const { + size_t res = 0; + for (auto vt : types) { + res ^= std::hash<uint32_t>{}(vt); + } + return res; + } +}; namespace wasm { -const char* printType(Type type) { +namespace { + +// TODO: switch to std::shared_mutex in C++17 +std::shared_timed_mutex mutex; + +std::vector<std::unique_ptr<std::vector<Type>>> typeLists = [] { + std::vector<std::unique_ptr<std::vector<Type>>> lists; + + auto add = [&](std::initializer_list<Type> types) { + return lists.push_back(std::make_unique<std::vector<Type>>(types)); + }; + + add({}); + add({}); + add({Type::i32}); + add({Type::i64}); + add({Type::f32}); + add({Type::f64}); + add({Type::v128}); + add({Type::anyref}); + add({Type::exnref}); + return lists; +}(); + +std::unordered_map<std::vector<Type>, uint32_t> indices = { + {{}, Type::none}, + {{}, Type::unreachable}, + {{Type::i32}, Type::i32}, + {{Type::i64}, Type::i64}, + {{Type::f32}, Type::f32}, + {{Type::f64}, Type::f64}, + {{Type::v128}, Type::v128}, + {{Type::anyref}, Type::anyref}, + {{Type::exnref}, Type::exnref}, +}; + +} // anonymous namespace + +void Type::init(const std::vector<Type>& types) { +#ifndef NDEBUG + for (Type t : types) { + assert(t.isSingle() && t.isConcrete()); + } +#endif + + auto lookup = [&]() { + auto indexIt = indices.find(types); + if (indexIt != indices.end()) { + id = indexIt->second; + return true; + } else { + return false; + } + }; + + { + // Try to look up previously interned type + std::shared_lock<std::shared_timed_mutex> lock(mutex); + if (lookup()) { + return; + } + } + { + // Add a new type if it hasn't been added concurrently + std::lock_guard<std::shared_timed_mutex> lock(mutex); + if (lookup()) { + return; + } + id = typeLists.size(); + typeLists.push_back(std::make_unique<std::vector<Type>>(types)); + indices[types] = id; + } +} + +Type::Type(std::initializer_list<Type> types) { init(types); } + +Type::Type(const std::vector<Type>& types) { init(types); } + +size_t Type::size() const { return expand().size(); } + +const std::vector<Type>& Type::expand() const { + std::shared_lock<std::shared_timed_mutex> lock(mutex); + assert(id < typeLists.size()); + return *typeLists[id].get(); +} + +namespace { + +std::ostream& +printPrefixedTypes(std::ostream& os, const char* prefix, Type type) { + os << '(' << prefix; + for (auto t : type.expand()) { + os << " " << t; + } + os << ')'; + return os; +} + +template<typename T> std::string genericToString(const T& t) { + std::ostringstream ss; + ss << t; + return ss.str(); +} + +} // anonymous namespace + +std::ostream& operator<<(std::ostream& os, Type type) { switch (type) { case Type::none: - return "none"; + os << "none"; + break; + case Type::unreachable: + os << "unreachable"; + break; case Type::i32: - return "i32"; + os << "i32"; + break; case Type::i64: - return "i64"; + os << "i64"; + break; case Type::f32: - return "f32"; + os << "f32"; + break; case Type::f64: - return "f64"; + os << "f64"; + break; case Type::v128: - return "v128"; + os << "v128"; + break; case Type::anyref: - return "anyref"; + os << "anyref"; + break; case Type::exnref: - return "exnref"; - case Type::unreachable: - return "unreachable"; + os << "exnref"; + break; + default: { + os << '('; + const std::vector<Type>& types = type.expand(); + for (size_t i = 0; i < types.size(); ++i) { + os << types[i]; + if (i < types.size() - 1) { + os << ", "; + } + } + os << ')'; + } } - WASM_UNREACHABLE(); + return os; } +std::ostream& operator<<(std::ostream& os, ParamType param) { + return printPrefixedTypes(os, "param", param.type); +} + +std::ostream& operator<<(std::ostream& os, ResultType param) { + return printPrefixedTypes(os, "result", param.type); +} + +std::string Type::toString() const { return genericToString(*this); } + +std::string ParamType::toString() const { return genericToString(*this); } + +std::string ResultType::toString() const { return genericToString(*this); } + unsigned getTypeSize(Type type) { switch (type) { case Type::i32: @@ -96,42 +251,6 @@ Type getType(unsigned size, bool float_) { WASM_UNREACHABLE(); } -Type getReachableType(Type a, Type b) { return a != unreachable ? a : b; } - -bool isConcreteType(Type type) { return type != none && type != unreachable; } - -bool isIntegerType(Type type) { - switch (type) { - case i32: - case i64: - return true; - default: - return false; - } -} - -bool isFloatType(Type type) { - switch (type) { - case f32: - case f64: - return true; - default: - return false; - } -} - -bool isVectorType(Type type) { return type == v128; } - -bool isReferenceType(Type type) { - switch (type) { - case anyref: - case exnref: - return true; - default: - return false; - } -} - Type reinterpretType(Type type) { switch (type) { case Type::i32: diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 82cd6d6f0..05cb45d25 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -352,7 +352,7 @@ void FunctionValidator::visitBlock(Block* curr) { assert(iter != breakInfos.end()); // we set it ourselves auto& info = iter->second; if (info.hasBeenSet()) { - if (isConcreteType(curr->type)) { + if (curr->type.isConcrete()) { shouldBeTrue(info.arity != 0, curr, "break arities must be > 0 if block has a value"); @@ -363,15 +363,14 @@ void FunctionValidator::visitBlock(Block* curr) { } // none or unreachable means a poison value that we should ignore - if // consumed, it will error - if (isConcreteType(info.type) && isConcreteType(curr->type)) { + if (info.type.isConcrete() && curr->type.isConcrete()) { shouldBeEqual( curr->type, info.type, curr, "block+breaks must have right type if breaks return a value"); } - if (isConcreteType(curr->type) && info.arity && - info.type != unreachable) { + if (curr->type.isConcrete() && info.arity && info.type != unreachable) { shouldBeEqual(curr->type, info.type, curr, @@ -381,7 +380,7 @@ void FunctionValidator::visitBlock(Block* curr) { info.arity != BreakInfo::PoisonArity, curr, "break arities must match"); if (curr->list.size() > 0) { auto last = curr->list.back()->type; - if (isConcreteType(last) && info.type != unreachable) { + if (last.isConcrete() && info.type != unreachable) { shouldBeEqual(last, info.type, curr, @@ -401,7 +400,7 @@ void FunctionValidator::visitBlock(Block* curr) { if (curr->list.size() > 1) { for (Index i = 0; i < curr->list.size() - 1; i++) { if (!shouldBeTrue( - !isConcreteType(curr->list[i]->type), + !curr->list[i]->type.isConcrete(), curr, "non-final block elements returning a value must be drop()ed " "(binaryen's autodrop option might help you)") && @@ -414,13 +413,13 @@ void FunctionValidator::visitBlock(Block* curr) { } if (curr->list.size() > 0) { auto backType = curr->list.back()->type; - if (!isConcreteType(curr->type)) { - shouldBeFalse(isConcreteType(backType), + if (!curr->type.isConcrete()) { + shouldBeFalse(backType.isConcrete(), curr, "if block is not returning a value, final element should " "not flow out a value"); } else { - if (isConcreteType(backType)) { + if (backType.isConcrete()) { shouldBeEqual( curr->type, backType, @@ -435,7 +434,7 @@ void FunctionValidator::visitBlock(Block* curr) { } } } - if (isConcreteType(curr->type)) { + if (curr->type.isConcrete()) { shouldBeTrue( curr->list.size() > 0, curr, "block with a value must not be empty"); } @@ -454,7 +453,7 @@ void FunctionValidator::visitLoop(Loop* curr) { breakInfos.erase(iter); } if (curr->type == none) { - shouldBeFalse(isConcreteType(curr->body->type), + shouldBeFalse(curr->body->type.isConcrete(), curr, "bad body for a loop that has no value"); } @@ -466,7 +465,7 @@ void FunctionValidator::visitIf(If* curr) { curr, "if condition must be valid"); if (!curr->ifFalse) { - shouldBeFalse(isConcreteType(curr->ifTrue->type), + shouldBeFalse(curr->ifTrue->type.isConcrete(), curr, "if without else must not return a value in body"); if (curr->condition->type != unreachable) { @@ -499,7 +498,7 @@ void FunctionValidator::visitIf(If* curr) { "unreachable if-else must have unreachable false"); } } - if (isConcreteType(curr->ifTrue->type)) { + if (curr->ifTrue->type.isConcrete()) { shouldBeEqual(curr->type, curr->ifTrue->type, curr, @@ -509,7 +508,7 @@ void FunctionValidator::visitIf(If* curr) { curr, "other arm must match concrete ifTrue"); } - if (isConcreteType(curr->ifFalse->type)) { + if (curr->ifFalse->type.isConcrete()) { shouldBeEqual(curr->type, curr->ifFalse->type, curr, @@ -705,7 +704,7 @@ void FunctionValidator::visitLocalGet(LocalGet* curr) { shouldBeTrue(curr->index < getFunction()->getNumLocals(), curr, "local.get index must be small enough"); - shouldBeTrue(isConcreteType(curr->type), + shouldBeTrue(curr->type.isConcrete(), curr, "local.get must have a valid type - check what you provided " "when you constructed the node"); @@ -1626,7 +1625,7 @@ void FunctionValidator::visitSelect(Select* curr) { } void FunctionValidator::visitDrop(Drop* curr) { - shouldBeTrue(isConcreteType(curr->value->type) || + shouldBeTrue(curr->value->type.isConcrete() || curr->value->type == unreachable, curr, "can only drop a valid value"); @@ -1678,14 +1677,14 @@ void FunctionValidator::visitTry(Try* curr) { curr->catchBody, "try's type does not match catch's body type"); } - if (isConcreteType(curr->body->type)) { + if (curr->body->type.isConcrete()) { shouldBeEqualOrFirstIsUnreachable( curr->catchBody->type, curr->body->type, curr->catchBody, "try's body type must match catch's body type"); } - if (isConcreteType(curr->catchBody->type)) { + if (curr->catchBody->type.isConcrete()) { shouldBeEqualOrFirstIsUnreachable( curr->body->type, curr->catchBody->type, @@ -1757,11 +1756,11 @@ void FunctionValidator::visitFunction(Function* curr) { FeatureSet typeFeatures = getFeatures(curr->result); for (auto type : curr->params) { typeFeatures |= getFeatures(type); - shouldBeTrue(isConcreteType(type), curr, "params must be concretely typed"); + shouldBeTrue(type.isConcrete(), curr, "params must be concretely typed"); } for (auto type : curr->vars) { typeFeatures |= getFeatures(type); - shouldBeTrue(isConcreteType(type), curr, "vars must be concretely typed"); + shouldBeTrue(type.isConcrete(), curr, "vars must be concretely typed"); } shouldBeTrue(typeFeatures <= getModule()->features, curr, @@ -1895,11 +1894,11 @@ static void validateBinaryenIR(Module& wasm, ValidationInfo& info) { // // The block has an added type, not derived from the ast itself, so it // is ok for it to be either i32 or unreachable. - if (!(isConcreteType(oldType) && newType == unreachable)) { + if (!(oldType.isConcrete() && newType == unreachable)) { std::ostringstream ss; ss << "stale type found in " << scope << " on " << curr - << "\n(marked as " << printType(oldType) << ", should be " - << printType(newType) << ")\n"; + << "\n(marked as " << oldType << ", should be " << newType + << ")\n"; info.fail(ss.str(), curr, getFunction()); } curr->type = oldType; @@ -2121,7 +2120,7 @@ static void validateEvents(Module& module, ValidationInfo& info) { curr->attribute, "Currently only attribute 0 is supported"); for (auto type : curr->params) { - info.shouldBeTrue(isIntegerType(type) || isFloatType(type), + info.shouldBeTrue(type.isInteger() || type.isFloat(), curr->name, "Values in an event should have integer or float type"); } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 72fed6c7b..b137044b2 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -283,7 +283,7 @@ static void handleUnreachable(Block* block, // if we are concrete, stop - even an unreachable child // won't change that (since we have a break with a value, // or the final child flows out a value) - if (isConcreteType(block->type)) { + if (block->type.isConcrete()) { return; } // look for an unreachable child @@ -314,7 +314,7 @@ void Block::finalize() { // (return) // (i32.const 10) // ) - if (isConcreteType(type)) { + if (type.isConcrete()) { return; } // if we are unreachable, we are done @@ -367,9 +367,9 @@ void If::finalize() { if (ifFalse) { if (ifTrue->type == ifFalse->type) { type = ifTrue->type; - } else if (isConcreteType(ifTrue->type) && ifFalse->type == unreachable) { + } else if (ifTrue->type.isConcrete() && ifFalse->type == unreachable) { type = ifTrue->type; - } else if (isConcreteType(ifFalse->type) && ifTrue->type == unreachable) { + } else if (ifFalse->type.isConcrete() && ifTrue->type == unreachable) { type = ifFalse->type; } else { type = none; @@ -895,9 +895,9 @@ void Host::finalize() { void Try::finalize() { if (body->type == catchBody->type) { type = body->type; - } else if (isConcreteType(body->type) && catchBody->type == unreachable) { + } else if (body->type.isConcrete() && catchBody->type == unreachable) { type = body->type; - } else if (isConcreteType(catchBody->type) && body->type == unreachable) { + } else if (catchBody->type.isConcrete() && body->type == unreachable) { type = catchBody->type; } else { type = none; |