summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/literal.cpp2
-rw-r--r--src/wasm/wasm-binary.cpp6
-rw-r--r--src/wasm/wasm-type.cpp217
-rw-r--r--src/wasm/wasm-validator.cpp47
-rw-r--r--src/wasm/wasm.cpp12
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;