summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asm2wasm.h4
-rw-r--r--src/binaryen-c.cpp36
-rw-r--r--src/binaryen-c.h4
-rw-r--r--src/dataflow/graph.h6
-rw-r--r--src/dataflow/utils.h2
-rw-r--r--src/ir/ReFinalize.cpp6
-rw-r--r--src/ir/block-utils.h4
-rw-r--r--src/ir/flat.h6
-rw-r--r--src/ir/load-utils.h2
-rw-r--r--src/ir/type-updating.h10
-rw-r--r--src/ir/utils.h6
-rw-r--r--src/js/binaryen.js-post.js20
-rw-r--r--src/parsing.h2
-rw-r--r--src/passes/Asyncify.cpp2
-rw-r--r--src/passes/CodeFolding.cpp4
-rw-r--r--src/passes/DataFlowOpts.cpp2
-rw-r--r--src/passes/DeadArgumentElimination.cpp2
-rw-r--r--src/passes/Flatten.cpp20
-rw-r--r--src/passes/I64ToI32Lowering.cpp2
-rw-r--r--src/passes/Inlining.cpp4
-rw-r--r--src/passes/LocalCSE.cpp2
-rw-r--r--src/passes/MergeBlocks.cpp10
-rw-r--r--src/passes/OptimizeInstructions.cpp10
-rw-r--r--src/passes/Precompute.cpp8
-rw-r--r--src/passes/Print.cpp77
-rw-r--r--src/passes/RemoveUnusedBrs.cpp16
-rw-r--r--src/passes/SafeHeap.cpp8
-rw-r--r--src/passes/Souperify.cpp10
-rw-r--r--src/passes/StackIR.cpp2
-rw-r--r--src/passes/Vacuum.cpp13
-rw-r--r--src/tools/execution-results.h2
-rw-r--r--src/tools/fuzzing.h28
-rw-r--r--src/tools/js-wrapper.h2
-rw-r--r--src/tools/wasm-reduce.cpp10
-rw-r--r--src/wasm-builder.h6
-rw-r--r--src/wasm-interpreter.h19
-rw-r--r--src/wasm-type.h63
-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
-rw-r--r--src/wasm2js.h2
43 files changed, 451 insertions, 265 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index 3f7206f4d..0255b2847 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -1973,7 +1973,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
ret->op = parseAsmBinaryOp(
ast[1]->getIString(), ast[2], ast[3], ret->left, ret->right);
ret->finalize();
- if (ret->op == BinaryOp::RemSInt32 && isFloatType(ret->type)) {
+ if (ret->op == BinaryOp::RemSInt32 && ret->type.isFloat()) {
// WebAssembly does not have floating-point remainder, we have to emit a
// call to a special import of ours
Call* call = allocator.alloc<Call>();
@@ -2849,7 +2849,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
if (seeker.found == 0) {
auto block = allocator.alloc<Block>();
block->list.push_back(child);
- if (isConcreteType(child->type)) {
+ if (child->type.isConcrete()) {
// ensure a nop at the end, so the block has guaranteed none type
// and no values fall through
block->list.push_back(builder.makeNop());
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index 1cdddfdc8..d33f40cdb 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -273,6 +273,42 @@ BinaryenType BinaryenTypeExnref(void) { return exnref; }
BinaryenType BinaryenTypeUnreachable(void) { return unreachable; }
BinaryenType BinaryenTypeAuto(void) { return uint32_t(-1); }
+BinaryenType BinaryenTypeCreate(BinaryenType* types, uint32_t numTypes) {
+ std::vector<Type> typeVec;
+ typeVec.reserve(numTypes);
+ for (size_t i = 0; i < numTypes; ++i) {
+ typeVec.push_back(Type(types[i]));
+ }
+ Type result(typeVec);
+
+ if (tracing) {
+ std::string array = getTemp();
+ std::cout << " {\n";
+ std::cout << " BinaryenType " << array << "[] = {";
+ for (size_t i = 0; i < numTypes; ++i) {
+ std::cout << uint32_t(types[i]);
+ if (i < numTypes - 1) {
+ std::cout << ", ";
+ }
+ }
+ std::cout << "};\n";
+ std::cout << " BinaryenTypeCreate(" << array << ", " << numTypes
+ << "); // " << uint32_t(result) << "\n";
+ std::cout << " }\n";
+ }
+
+ return uint32_t(result);
+}
+
+uint32_t BinaryenTypeArity(BinaryenType t) { return Type(t).size(); }
+
+void BinaryenTypeExpand(BinaryenType t, BinaryenType* buf) {
+ const std::vector<Type>& types = Type(t).expand();
+ for (size_t i = 0; i < types.size(); ++i) {
+ buf[i] = types[i];
+ }
+}
+
WASM_DEPRECATED BinaryenType BinaryenNone(void) { return none; }
WASM_DEPRECATED BinaryenType BinaryenInt32(void) { return i32; }
WASM_DEPRECATED BinaryenType BinaryenInt64(void) { return i64; }
diff --git a/src/binaryen-c.h b/src/binaryen-c.h
index 6f9878535..726129983 100644
--- a/src/binaryen-c.h
+++ b/src/binaryen-c.h
@@ -95,6 +95,10 @@ BINARYEN_API BinaryenType BinaryenTypeUnreachable(void);
// Not a real type. Used as the last parameter to BinaryenBlock to let
// the API figure out the type instead of providing one.
BINARYEN_API BinaryenType BinaryenTypeAuto(void);
+BINARYEN_API BinaryenType BinaryenTypeCreate(BinaryenType* valueTypes,
+ uint32_t numTypes);
+BINARYEN_API uint32_t BinaryenTypeArity(BinaryenType t);
+BINARYEN_API void BinaryenTypeExpand(BinaryenType t, BinaryenType* buf);
WASM_DEPRECATED BinaryenType BinaryenNone(void);
WASM_DEPRECATED BinaryenType BinaryenInt32(void);
diff --git a/src/dataflow/graph.h b/src/dataflow/graph.h
index 5c01714f5..a243b0fff 100644
--- a/src/dataflow/graph.h
+++ b/src/dataflow/graph.h
@@ -169,7 +169,7 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> {
assert(!node->isBad());
Builder builder(*module);
auto type = node->getWasmType();
- if (!isConcreteType(type)) {
+ if (!type.isConcrete()) {
return &bad;
}
auto* zero = makeZero(type);
@@ -397,7 +397,7 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> {
if (!isRelevantLocal(curr->index) || isInUnreachable()) {
return &bad;
}
- assert(isConcreteType(curr->value->type));
+ assert(curr->value->type.isConcrete());
sets.push_back(curr);
expressionParentMap[curr] = parent;
expressionParentMap[curr->value] = curr;
@@ -603,7 +603,7 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> {
// Helpers.
- bool isRelevantType(wasm::Type type) { return isIntegerType(type); }
+ bool isRelevantType(wasm::Type type) { return type.isInteger(); }
bool isRelevantLocal(Index index) {
return isRelevantType(func->getLocalType(index));
diff --git a/src/dataflow/utils.h b/src/dataflow/utils.h
index 4408c6080..2d32dbeac 100644
--- a/src/dataflow/utils.h
+++ b/src/dataflow/utils.h
@@ -43,7 +43,7 @@ inline std::ostream& dump(Node* node, std::ostream& o, size_t indent = 0) {
o << '[' << node << ' ';
switch (node->type) {
case Node::Type::Var:
- o << "var " << printType(node->wasmType) << ' ' << node;
+ o << "var " << node->wasmType << ' ' << node;
break;
case Node::Type::Expr: {
o << "expr ";
diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp
index b27b49bd4..9ee109f5e 100644
--- a/src/ir/ReFinalize.cpp
+++ b/src/ir/ReFinalize.cpp
@@ -50,12 +50,12 @@ void ReFinalize::visitBlock(Block* curr) {
curr->type = curr->list.back()->type;
// if concrete, it doesn't matter if we have an unreachable child, and we
// don't need to look at breaks
- if (isConcreteType(curr->type)) {
+ if (curr->type.isConcrete()) {
// make sure our branches make sense for us - we may have just made
// ourselves concrete for a value flowing out, while branches did not send a
// value. such branches could not have been actually taken before, that is,
// there were in unreachable code, but we do still need to fix them up here.
- if (!isConcreteType(old)) {
+ if (!old.isConcrete()) {
auto iter = breakValues.find(curr->name);
if (iter != breakValues.end()) {
// there is a break to here
@@ -210,7 +210,7 @@ void ReFinalize::replaceUntaken(Expression* value, Expression* condition) {
// the value is unreachable, and necessary since the type of
// the condition did not have an impact before (the break/switch
// type was unreachable), and might not fit in.
- if (isConcreteType(condition->type)) {
+ if (condition->type.isConcrete()) {
condition = builder.makeDrop(condition);
}
replacement = builder.makeSequence(value, condition);
diff --git a/src/ir/block-utils.h b/src/ir/block-utils.h
index 120178a47..1ed9ee413 100644
--- a/src/ir/block-utils.h
+++ b/src/ir/block-utils.h
@@ -39,7 +39,7 @@ simplifyToContents(Block* block, T* parent, bool allowTypeChange = false) {
auto* singleton = list[0];
auto sideEffects =
EffectAnalyzer(parent->getPassOptions(), singleton).hasSideEffects();
- if (!sideEffects && !isConcreteType(singleton->type)) {
+ if (!sideEffects && !singleton->type.isConcrete()) {
// no side effects, and singleton is not returning a value, so we can
// throw away the block and its contents, basically
return Builder(*parent->getModule()).replaceWithIdenticalType(block);
@@ -50,7 +50,7 @@ simplifyToContents(Block* block, T* parent, bool allowTypeChange = false) {
// inside is unreachable (if both concrete, must match, and since no name
// on block, we can't be branched to, so if singleton is unreachable, so
// is the block)
- assert(isConcreteType(block->type) && singleton->type == unreachable);
+ assert(block->type.isConcrete() && singleton->type == unreachable);
// we could replace with unreachable, but would need to update all
// the parent's types
}
diff --git a/src/ir/flat.h b/src/ir/flat.h
index 749d4532d..dd72e339d 100644
--- a/src/ir/flat.h
+++ b/src/ir/flat.h
@@ -73,10 +73,10 @@ inline void verifyFlatness(Function* func) {
UnifiedExpressionVisitor<VerifyFlatness>> {
void visitExpression(Expression* curr) {
if (isControlFlowStructure(curr)) {
- verify(!isConcreteType(curr->type),
+ verify(!curr->type.isConcrete(),
"control flow structures must not flow values");
} else if (curr->is<LocalSet>()) {
- verify(!isConcreteType(curr->type), "tees are not allowed, only sets");
+ verify(!curr->type.isConcrete(), "tees are not allowed, only sets");
} else {
for (auto* child : ChildIterator(curr)) {
verify(child->is<Const>() || child->is<LocalGet>() ||
@@ -98,7 +98,7 @@ inline void verifyFlatness(Function* func) {
VerifyFlatness verifier;
verifier.walkFunction(func);
verifier.setFunction(func);
- verifier.verify(!isConcreteType(func->body->type),
+ verifier.verify(!func->body->type.isConcrete(),
"function bodies must not flow values");
}
diff --git a/src/ir/load-utils.h b/src/ir/load-utils.h
index c7e9bad99..037f5297b 100644
--- a/src/ir/load-utils.h
+++ b/src/ir/load-utils.h
@@ -31,7 +31,7 @@ inline bool isSignRelevant(Load* load) {
if (load->type == unreachable) {
return false;
}
- return !isFloatType(type) && load->bytes < getTypeSize(type);
+ return !type.isFloat() && load->bytes < getTypeSize(type);
}
// check if a load can be signed (which some opts want to do)
diff --git a/src/ir/type-updating.h b/src/ir/type-updating.h
index 40c17427c..1fa891ec1 100644
--- a/src/ir/type-updating.h
+++ b/src/ir/type-updating.h
@@ -234,7 +234,7 @@ struct TypeUpdater
// but exceptions exist
if (auto* block = curr->dynCast<Block>()) {
// if the block has a fallthrough, it can keep its type
- if (isConcreteType(block->list.back()->type)) {
+ if (block->list.back()->type.isConcrete()) {
return; // did not turn
}
// if the block has breaks, it can keep its type
@@ -265,7 +265,7 @@ struct TypeUpdater
// unreachable, and it does this efficiently, without scanning the full
// contents
void maybeUpdateTypeToUnreachable(Block* curr) {
- if (!isConcreteType(curr->type)) {
+ if (!curr->type.isConcrete()) {
return; // nothing concrete to change to unreachable
}
if (curr->name.is() && blockInfos[curr->name].numBreaks > 0) {
@@ -279,7 +279,7 @@ struct TypeUpdater
if (curr->type == unreachable) {
return; // no change possible
}
- if (!curr->list.empty() && isConcreteType(curr->list.back()->type)) {
+ if (!curr->list.empty() && curr->list.back()->type.isConcrete()) {
// should keep type due to fallthrough, even if has an unreachable child
return;
}
@@ -296,7 +296,7 @@ struct TypeUpdater
// can remove a concrete type and turn the if unreachable when it is
// unreachable
void maybeUpdateTypeToUnreachable(If* curr) {
- if (!isConcreteType(curr->type)) {
+ if (!curr->type.isConcrete()) {
return; // nothing concrete to change to unreachable
}
curr->finalize();
@@ -306,7 +306,7 @@ struct TypeUpdater
}
void maybeUpdateTypeToUnreachable(Try* curr) {
- if (!isConcreteType(curr->type)) {
+ if (!curr->type.isConcrete()) {
return; // nothing concrete to change to unreachable
}
curr->finalize();
diff --git a/src/ir/utils.h b/src/ir/utils.h
index ca8803741..e8c5b78b3 100644
--- a/src/ir/utils.h
+++ b/src/ir/utils.h
@@ -251,7 +251,7 @@ struct AutoDrop : public WalkerPass<ExpressionStackWalker<AutoDrop>> {
bool maybeDrop(Expression*& child) {
bool acted = false;
- if (isConcreteType(child->type)) {
+ if (child->type.isConcrete()) {
expressionStack.push_back(child);
if (!ExpressionAnalyzer::isResultUsed(expressionStack, getFunction()) &&
!ExpressionAnalyzer::isResultDropped(expressionStack)) {
@@ -271,7 +271,7 @@ struct AutoDrop : public WalkerPass<ExpressionStackWalker<AutoDrop>> {
}
for (Index i = 0; i < curr->list.size() - 1; i++) {
auto* child = curr->list[i];
- if (isConcreteType(child->type)) {
+ if (child->type.isConcrete()) {
curr->list[i] = Builder(*getModule()).makeDrop(child);
}
}
@@ -300,7 +300,7 @@ struct AutoDrop : public WalkerPass<ExpressionStackWalker<AutoDrop>> {
void doWalkFunction(Function* curr) {
ReFinalize().walkFunctionInModule(curr, getModule());
walk(curr->body);
- if (curr->result == none && isConcreteType(curr->body->type)) {
+ if (curr->result == none && curr->body->type.isConcrete()) {
curr->body = Builder(*getModule()).makeDrop(curr->body);
}
ReFinalize().walkFunctionInModule(curr, getModule());
diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js
index 37d2432cb..1f2c414e5 100644
--- a/src/js/binaryen.js-post.js
+++ b/src/js/binaryen.js-post.js
@@ -42,6 +42,26 @@ Module['exnref'] = Module['_BinaryenTypeExnref']();
Module['unreachable'] = Module['_BinaryenTypeUnreachable']();
Module['auto'] = /* deprecated */ Module['undefined'] = Module['_BinaryenTypeAuto']();
+Module['createType'] = function(types) {
+ return preserveStack(function() {
+ var array = i32sToStack(types);
+ return Module['_BinaryenTypeCreate'](array, types.length);
+ });
+};
+
+Module['expandType'] = function(ty) {
+ return preserveStack(function() {
+ var numTypes = Module['_BinaryenTypeArity'](ty);
+ var array = stackAlloc(numTypes << 2);
+ Module['_BinaryenTypeExpand'](ty, array);
+ var types = [];
+ for (var i = 0; i < numTypes; i++) {
+ types.push(HEAPU32[(array >>> 2) + i]);
+ }
+ return types;
+ });
+};
+
// Expression ids
Module['InvalidId'] = Module['_BinaryenInvalidId']();
Module['BlockId'] = Module['_BinaryenBlockId']();
diff --git a/src/parsing.h b/src/parsing.h
index 49051800f..2591c4357 100644
--- a/src/parsing.h
+++ b/src/parsing.h
@@ -82,7 +82,7 @@ parseConst(cashew::IString s, Type type, MixedArena& allocator) {
const char* str = s.str;
auto ret = allocator.alloc<Const>();
ret->type = type;
- if (isFloatType(type)) {
+ if (type.isFloat()) {
if (s == _INFINITY) {
switch (type) {
case f32:
diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp
index dd8845577..818d61907 100644
--- a/src/passes/Asyncify.cpp
+++ b/src/passes/Asyncify.cpp
@@ -948,7 +948,7 @@ private:
builder->makeLocalGet(oldState, i32)),
builder->makeUnreachable());
Expression* rep;
- if (isConcreteType(call->type)) {
+ if (call->type.isConcrete()) {
auto temp = builder->addVar(func, call->type);
rep = builder->makeBlock({
builder->makeLocalSet(temp, call),
diff --git a/src/passes/CodeFolding.cpp b/src/passes/CodeFolding.cpp
index d60bc76d1..818bb33b8 100644
--- a/src/passes/CodeFolding.cpp
+++ b/src/passes/CodeFolding.cpp
@@ -139,7 +139,7 @@ struct CodeFolding : public WalkerPass<ControlFlowWalker<CodeFolding>> {
// elements out of it if there is a value being returned)
Block* parent = controlFlowStack.back()->dynCast<Block>();
if (parent && curr == parent->list.back() &&
- !isConcreteType(parent->list.back()->type)) {
+ !parent->list.back()->type.isConcrete()) {
breakTails[curr->name].push_back(Tail(curr, parent));
} else {
unoptimizables.insert(curr->name);
@@ -189,7 +189,7 @@ struct CodeFolding : public WalkerPass<ControlFlowWalker<CodeFolding>> {
return;
}
// we can't optimize a fallthrough value
- if (isConcreteType(curr->list.back()->type)) {
+ if (curr->list.back()->type.isConcrete()) {
return;
}
auto iter = breakTails.find(curr->name);
diff --git a/src/passes/DataFlowOpts.cpp b/src/passes/DataFlowOpts.cpp
index 62d2e5f24..dc8f6ca94 100644
--- a/src/passes/DataFlowOpts.cpp
+++ b/src/passes/DataFlowOpts.cpp
@@ -103,7 +103,7 @@ struct DataFlowOpts : public WalkerPass<PostWalker<DataFlowOpts>> {
assert(!node->isConst());
// If this is a concrete value (not e.g. an eqz of unreachable),
// it can definitely be precomputed into a constant.
- if (isConcreteType(node->expr->type)) {
+ if (node->expr->type.isConcrete()) {
// This can be precomputed.
// TODO not just all-constant inputs? E.g. i32.mul of 0 and X.
optimizeExprToConstant(node);
diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp
index 9ef762f98..8e1da3ac5 100644
--- a/src/passes/DeadArgumentElimination.cpp
+++ b/src/passes/DeadArgumentElimination.cpp
@@ -458,7 +458,7 @@ private:
}
} returnUpdater(func, module);
// Remove any value flowing out.
- if (isConcreteType(func->body->type)) {
+ if (func->body->type.isConcrete()) {
func->body = builder.makeDrop(func->body);
}
// Remove the drops on the calls.
diff --git a/src/passes/Flatten.cpp b/src/passes/Flatten.cpp
index 9caf6cae8..ee7a93b72 100644
--- a/src/passes/Flatten.cpp
+++ b/src/passes/Flatten.cpp
@@ -84,7 +84,7 @@ struct Flatten
block->list.swap(newList);
// remove a block return value
auto type = block->type;
- if (isConcreteType(type)) {
+ if (type.isConcrete()) {
// if there is a temp index for breaking to the block, use that
Index temp;
auto iter = breakTemps.find(block->name);
@@ -94,7 +94,7 @@ struct Flatten
temp = builder.addVar(getFunction(), type);
}
auto*& last = block->list.back();
- if (isConcreteType(last->type)) {
+ if (last->type.isConcrete()) {
last = builder.makeLocalSet(temp, last);
}
block->finalize(none);
@@ -114,12 +114,12 @@ struct Flatten
auto* originalIfFalse = iff->ifFalse;
auto type = iff->type;
Expression* prelude = nullptr;
- if (isConcreteType(type)) {
+ if (type.isConcrete()) {
Index temp = builder.addVar(getFunction(), type);
- if (isConcreteType(iff->ifTrue->type)) {
+ if (iff->ifTrue->type.isConcrete()) {
iff->ifTrue = builder.makeLocalSet(temp, iff->ifTrue);
}
- if (iff->ifFalse && isConcreteType(iff->ifFalse->type)) {
+ if (iff->ifFalse && iff->ifFalse->type.isConcrete()) {
iff->ifFalse = builder.makeLocalSet(temp, iff->ifFalse);
}
// the whole if (+any preludes from the condition) is now a prelude
@@ -143,7 +143,7 @@ struct Flatten
Expression* rep = loop;
auto* originalBody = loop->body;
auto type = loop->type;
- if (isConcreteType(type)) {
+ if (type.isConcrete()) {
Index temp = builder.addVar(getFunction(), type);
loop->body = builder.makeLocalSet(temp, loop->body);
// and we leave just a get of the value
@@ -180,14 +180,14 @@ struct Flatten
} else if (auto* br = curr->dynCast<Break>()) {
if (br->value) {
auto type = br->value->type;
- if (isConcreteType(type)) {
+ if (type.isConcrete()) {
// we are sending a value. use a local instead
Index temp = getTempForBreakTarget(br->name, type);
ourPreludes.push_back(builder.makeLocalSet(temp, br->value));
if (br->condition) {
// the value must also flow out
ourPreludes.push_back(br);
- if (isConcreteType(br->type)) {
+ if (br->type.isConcrete()) {
replaceCurrent(builder.makeLocalGet(temp, type));
} else {
assert(br->type == unreachable);
@@ -205,7 +205,7 @@ struct Flatten
} else if (auto* sw = curr->dynCast<Switch>()) {
if (sw->value) {
auto type = sw->value->type;
- if (isConcreteType(type)) {
+ if (type.isConcrete()) {
// we are sending a value. use a local instead
Index temp = builder.addVar(getFunction(), type);
ourPreludes.push_back(builder.makeLocalSet(temp, sw->value));
@@ -266,7 +266,7 @@ struct Flatten
void visitFunction(Function* curr) {
auto* originalBody = curr->body;
// if the body is a block with a result, turn that into a return
- if (isConcreteType(curr->body->type)) {
+ if (curr->body->type.isConcrete()) {
curr->body = Builder(*getModule()).makeReturn(curr->body);
}
// the body may have preludes
diff --git a/src/passes/I64ToI32Lowering.cpp b/src/passes/I64ToI32Lowering.cpp
index 5d7d5998c..b51858474 100644
--- a/src/passes/I64ToI32Lowering.cpp
+++ b/src/passes/I64ToI32Lowering.cpp
@@ -1504,7 +1504,7 @@ private:
std::vector<Expression*> children;
bool hasUnreachable = false;
for (auto* child : ChildIterator(curr)) {
- if (isConcreteType(child->type)) {
+ if (child->type.isConcrete()) {
child = builder->makeDrop(child);
} else if (child->type == unreachable) {
hasUnreachable = true;
diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp
index 53a57cfeb..10b41abc0 100644
--- a/src/passes/Inlining.cpp
+++ b/src/passes/Inlining.cpp
@@ -190,7 +190,7 @@ struct Updater : public PostWalker<Updater> {
template<typename T> void handleReturnCall(T* curr, Type targetType) {
curr->isReturn = false;
curr->type = targetType;
- if (isConcreteType(targetType)) {
+ if (targetType.isConcrete()) {
replaceCurrent(builder->makeBreak(returnName, curr));
} else {
replaceCurrent(builder->blockify(curr, builder->makeBreak(returnName)));
@@ -226,7 +226,7 @@ doInlining(Module* module, Function* into, const InliningAction& action) {
auto* block = builder.makeBlock();
block->name = Name(std::string("__inlined_func$") + from->name.str);
if (call->isReturn) {
- if (isConcreteType(retType)) {
+ if (retType.isConcrete()) {
*action.callSite = builder.makeReturn(block);
} else {
*action.callSite = builder.makeSequence(block, builder.makeReturn());
diff --git a/src/passes/LocalCSE.cpp b/src/passes/LocalCSE.cpp
index f3e03d95d..afd30c040 100644
--- a/src/passes/LocalCSE.cpp
+++ b/src/passes/LocalCSE.cpp
@@ -208,7 +208,7 @@ struct LocalCSE : public WalkerPass<LinearExecutionWalker<LocalCSE>> {
if (value->is<LocalGet>()) {
return false; // trivial, this is what we optimize to!
}
- if (!isConcreteType(value->type)) {
+ if (!value->type.isConcrete()) {
return false; // don't bother with unreachable etc.
}
if (EffectAnalyzer(getPassOptions(), value).hasSideEffects()) {
diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp
index bba64c381..babbf77ba 100644
--- a/src/passes/MergeBlocks.cpp
+++ b/src/passes/MergeBlocks.cpp
@@ -164,7 +164,7 @@ struct BreakValueDropper : public ControlFlowWalker<BreakValueDropper> {
// (block (drop value) (br_if)) with type none, which does not need a drop
// likewise, unreachable does not need to be dropped, so we just leave drops
// of concrete values
- if (!isConcreteType(curr->value->type)) {
+ if (!curr->value->type.isConcrete()) {
replaceCurrent(curr->value);
}
}
@@ -240,7 +240,7 @@ optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) {
// we can do it!
// reuse the drop, if we still need it
auto* last = childBlock->list.back();
- if (isConcreteType(last->type)) {
+ if (last->type.isConcrete()) {
drop->value = last;
drop->finalize();
childBlock->list.back() = drop;
@@ -281,7 +281,7 @@ optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) {
if (childBlock->name.is()) {
// If it has a concrete value, then breaks may be sending it a value,
// and we'd need to handle that. TODO
- if (isConcreteType(childBlock->type)) {
+ if (childBlock->type.isConcrete()) {
continue;
}
auto childName = childBlock->name;
@@ -311,7 +311,7 @@ optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) {
// value, we would need special handling for that - not worth it,
// probably TODO
// FIXME is this not handled by the drop later down?
- if (keepEnd < childSize && isConcreteType(childList.back()->type)) {
+ if (keepEnd < childSize && childList.back()->type.isConcrete()) {
continue;
}
}
@@ -362,7 +362,7 @@ optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) {
if (!merged.empty()) {
auto* last = merged.back();
for (auto*& item : merged) {
- if (item != last && isConcreteType(item->type)) {
+ if (item != last && item->type.isConcrete()) {
Builder builder(*module);
item = builder.makeDrop(item);
}
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 729a42ada..64c49fc96 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -762,7 +762,7 @@ struct OptimizeInstructions
} else {
// the types diff. as the condition is reachable, that means the
// if must be concrete while the arm is not
- assert(isConcreteType(iff->type) &&
+ assert(iff->type.isConcrete() &&
iff->ifTrue->type == unreachable);
// emit a block with a forced type
auto* ret = builder.makeBlock();
@@ -1298,7 +1298,7 @@ private:
Expression* optimizeWithConstantOnRight(Binary* binary) {
auto type = binary->right->type;
auto* right = binary->right->cast<Const>();
- if (isIntegerType(type)) {
+ if (type.isInteger()) {
// operations on zero
if (right->value == Literal::makeFromInt32(0, type)) {
if (binary->op == Abstract::getBinary(type, Abstract::Shl) ||
@@ -1351,7 +1351,7 @@ private:
}
}
}
- if (isIntegerType(type) || isFloatType(type)) {
+ if (type.isInteger() || type.isFloat()) {
// note that this is correct even on floats with a NaN on the left,
// as a NaN would skip the computation and just return the NaN,
// and that is precisely what we do here. but, the same with -1
@@ -1375,7 +1375,7 @@ private:
Expression* optimizeWithConstantOnLeft(Binary* binary) {
auto type = binary->left->type;
auto* left = binary->left->cast<Const>();
- if (isIntegerType(type)) {
+ if (type.isInteger()) {
// operations on zero
if (left->value == Literal::makeFromInt32(0, type)) {
if ((binary->op == Abstract::getBinary(type, Abstract::Shl) ||
@@ -1397,7 +1397,7 @@ private:
// x + 5 == 7
// =>
// x == 2
- if (isIntegerType(binary->left->type)) {
+ if (binary->left->type.isInteger()) {
if (binary->op == Abstract::getBinary(type, Abstract::Eq) ||
binary->op == Abstract::getBinary(type, Abstract::Ne)) {
if (auto* left = binary->left->dynCast<Binary>()) {
diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp
index 43ecadf7f..57a3ab27f 100644
--- a/src/passes/Precompute.cpp
+++ b/src/passes/Precompute.cpp
@@ -100,7 +100,7 @@ public:
// If we don't need to replace the whole expression, see if there
// is a value flowing through a tee.
if (!replaceExpression) {
- if (isConcreteType(curr->type)) {
+ if (curr->type.isConcrete()) {
assert(curr->isTee());
return visit(curr->value);
}
@@ -183,12 +183,12 @@ struct Precompute
// Until engines implement v128.const and we have SIMD-aware optimizations
// that can break large v128.const instructions into smaller consts and
// splats, do not try to precompute v128 expressions.
- if (isVectorType(curr->type)) {
+ if (curr->type.isVector()) {
return;
}
// try to evaluate this into a const
Flow flow = precomputeExpression(curr);
- if (isVectorType(flow.value.type)) {
+ if (flow.value.type.isVector()) {
return;
}
if (flow.breaking()) {
@@ -248,7 +248,7 @@ struct Precompute
return;
}
// this was precomputed
- if (isConcreteType(flow.value.type)) {
+ if (flow.value.type.isConcrete()) {
replaceCurrent(Builder(*getModule()).makeConst(flow.value));
worked = true;
} else {
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index bfd12dc94..f51bccf27 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -57,9 +57,7 @@ static Name printableLocal(Index index, Function* func) {
// Printing "unreachable" as a instruction prefix type is not valid in wasm text
// format. Print something else to make it pass.
-static Type forceConcrete(Type type) {
- return isConcreteType(type) ? type : i32;
-}
+static Type forceConcrete(Type type) { return type.isConcrete() ? type : i32; }
// Prints the internal contents of an expression: everything but
// the children.
@@ -77,14 +75,14 @@ struct PrintExpressionContents
o << ' ';
printName(curr->name, o);
}
- if (isConcreteType(curr->type)) {
- o << " (result " << printType(curr->type) << ')';
+ if (curr->type.isConcrete()) {
+ o << ' ' << ResultType(curr->type);
}
}
void visitIf(If* curr) {
printMedium(o, "if");
- if (isConcreteType(curr->type)) {
- o << " (result " << printType(curr->type) << ')';
+ if (curr->type.isConcrete()) {
+ o << ' ' << ResultType(curr->type);
}
}
void visitLoop(Loop* curr) {
@@ -92,8 +90,8 @@ struct PrintExpressionContents
if (curr->name.is()) {
o << ' ' << curr->name;
}
- if (isConcreteType(curr->type)) {
- o << " (result " << printType(curr->type) << ')';
+ if (curr->type.isConcrete()) {
+ o << ' ' << ResultType(curr->type);
}
}
void visitBreak(Break* curr) {
@@ -147,7 +145,7 @@ struct PrintExpressionContents
printName(curr->name, o);
}
void visitLoad(Load* curr) {
- prepareColor(o) << printType(forceConcrete(curr->type));
+ prepareColor(o) << forceConcrete(curr->type);
if (curr->isAtomic) {
o << ".atomic";
}
@@ -173,7 +171,7 @@ struct PrintExpressionContents
}
}
void visitStore(Store* curr) {
- prepareColor(o) << printType(forceConcrete(curr->valueType));
+ prepareColor(o) << forceConcrete(curr->valueType);
if (curr->isAtomic) {
o << ".atomic";
}
@@ -198,7 +196,7 @@ struct PrintExpressionContents
}
}
static void printRMWSize(std::ostream& o, Type type, uint8_t bytes) {
- prepareColor(o) << printType(forceConcrete(type)) << ".atomic.rmw";
+ prepareColor(o) << forceConcrete(type) << ".atomic.rmw";
if (type != unreachable && bytes != getTypeSize(type)) {
if (bytes == 1) {
o << '8';
@@ -257,7 +255,7 @@ struct PrintExpressionContents
}
void visitAtomicWait(AtomicWait* curr) {
prepareColor(o);
- o << printType(forceConcrete(curr->expectedType)) << ".atomic.wait";
+ o << forceConcrete(curr->expectedType) << ".atomic.wait";
if (curr->offset) {
o << " offset=" << curr->offset;
}
@@ -1306,8 +1304,8 @@ struct PrintExpressionContents
}
void visitTry(Try* curr) {
printMedium(o, "try");
- if (isConcreteType(curr->type)) {
- o << " (result " << printType(curr->type) << ')';
+ if (curr->type.isConcrete()) {
+ o << ' ' << ResultType(curr->type);
}
}
void visitThrow(Throw* curr) {
@@ -1325,7 +1323,7 @@ struct PrintExpressionContents
void visitUnreachable(Unreachable* curr) { printMinor(o, "unreachable"); }
void visitPush(Push* curr) { prepareColor(o) << "push"; }
void visitPop(Pop* curr) {
- prepareColor(o) << printType(curr->type);
+ prepareColor(o) << curr->type;
o << ".pop";
restoreNormalColor(o);
}
@@ -1413,7 +1411,7 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
void printFullLine(Expression* expression) {
!minify && doIndent(o, indent);
if (full) {
- o << "[" << printType(expression->type) << "] ";
+ o << "[" << expression->type << "] ";
}
visit(expression);
o << maybeNewLine;
@@ -1444,7 +1442,7 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
}
stack.push_back(curr);
if (full) {
- o << "[" << printType(curr->type) << "] ";
+ o << "[" << curr->type << "] ";
}
o << '(';
PrintExpressionContents(currFunction, o).visit(curr);
@@ -1872,17 +1870,11 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
}
if (curr->params.size() > 0) {
o << maybeSpace;
- o << '(';
- printMinor(o, "param");
- for (auto& param : curr->params) {
- o << ' ' << printType(param);
- }
- o << ')';
+ o << ParamType(Type(curr->params));
}
if (curr->result != none) {
o << maybeSpace;
- o << '(';
- printMinor(o, "result ") << printType(curr->result) << ')';
+ o << ResultType(curr->result);
}
o << ")";
}
@@ -1926,9 +1918,9 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
}
void emitGlobalType(Global* curr) {
if (curr->mutable_) {
- o << "(mut " << printType(curr->type) << ')';
+ o << "(mut " << curr->type << ')';
} else {
- o << printType(curr->type);
+ o << curr->type;
}
}
void visitImportedGlobal(Global* curr) {
@@ -2002,20 +1994,19 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
o << maybeSpace;
o << '(';
printMinor(o, "param ") << printableLocal(i, currFunction) << ' '
- << printType(curr->getLocalType(i)) << ')';
+ << curr->getLocalType(i) << ')';
}
}
if (curr->result != none) {
o << maybeSpace;
- o << '(';
- printMinor(o, "result ") << printType(curr->result) << ')';
+ o << ResultType(curr->result);
}
incIndent();
for (size_t i = curr->getVarIndexBase(); i < curr->getNumLocals(); i++) {
doIndent(o, indent);
o << '(';
printMinor(o, "local ") << printableLocal(i, currFunction) << ' '
- << printType(curr->getLocalType(i)) << ')';
+ << curr->getLocalType(i) << ')';
o << maybeNewLine;
}
// Print the body.
@@ -2064,12 +2055,9 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
emitImportHeader(curr);
o << "(event ";
printName(curr->name, o);
- o << maybeSpace << "(attr " << curr->attribute << ')' << maybeSpace << '(';
- printMinor(o, "param");
- for (auto& param : curr->params) {
- o << ' ' << printType(param);
- }
- o << ")))";
+ o << maybeSpace << "(attr " << curr->attribute << ')' << maybeSpace;
+ o << ParamType(Type(curr->params));
+ o << "))";
o << maybeNewLine;
}
void visitDefinedEvent(Event* curr) {
@@ -2077,12 +2065,9 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
o << '(';
printMedium(o, "event ");
printName(curr->name, o);
- o << maybeSpace << "(attr " << curr->attribute << ')' << maybeSpace << '(';
- printMinor(o, "param");
- for (auto& param : curr->params) {
- o << ' ' << printType(param);
- }
- o << "))" << maybeNewLine;
+ o << maybeSpace << "(attr " << curr->attribute << ')' << maybeSpace;
+ o << ParamType(Type(curr->params));
+ o << ")" << maybeNewLine;
}
void printTableHeader(Table* curr) {
o << '(';
@@ -2372,7 +2357,7 @@ std::ostream& WasmPrinter::printExpression(Expression* expression,
print.setMinify(minify);
if (full || isFullForced()) {
print.setFull(true);
- o << "[" << printType(expression->type) << "] ";
+ o << "[" << expression->type << "] ";
}
print.visit(expression);
return o;
@@ -2394,7 +2379,7 @@ WasmPrinter::printStackInst(StackInst* inst, std::ostream& o, Function* func) {
case StackInst::BlockEnd:
case StackInst::IfEnd:
case StackInst::LoopEnd: {
- o << "end (" << printType(inst->type) << ')';
+ o << "end (" << inst->type << ')';
break;
}
case StackInst::IfElse: {
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp
index 7f3a9965d..d8668196d 100644
--- a/src/passes/RemoveUnusedBrs.cpp
+++ b/src/passes/RemoveUnusedBrs.cpp
@@ -435,7 +435,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
// append to the other, if there is no returned value to concern us
// can't be, since in the middle of a block
- assert(!isConcreteType(iff->type));
+ assert(!iff->type.isConcrete());
// ensures the first node is a block, if it isn't already, and merges
// in the second, either as a single element or, if a block, by
@@ -454,7 +454,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
if (!block || block->name.is()) {
block = builder.makeBlock(any);
} else {
- assert(!isConcreteType(block->type));
+ assert(!block->type.isConcrete());
}
auto* other = append->dynCast<Block>();
if (!other) {
@@ -910,8 +910,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
// Convert an if into a select, if possible and beneficial to do so.
Select* selectify(If* iff) {
- if (!iff->ifFalse || !isConcreteType(iff->ifTrue->type) ||
- !isConcreteType(iff->ifFalse->type)) {
+ if (!iff->ifFalse || !iff->ifTrue->type.isConcrete() ||
+ !iff->ifFalse->type.isConcrete()) {
return nullptr;
}
// This is always helpful for code size, but can be a tradeoff with
@@ -972,8 +972,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
bool optimizeSetIfWithBrArm(Expression** currp) {
auto* set = (*currp)->cast<LocalSet>();
auto* iff = set->value->dynCast<If>();
- if (!iff || !isConcreteType(iff->type) ||
- !isConcreteType(iff->condition->type)) {
+ if (!iff || !iff->type.isConcrete() ||
+ !iff->condition->type.isConcrete()) {
return false;
}
auto tryToOptimize =
@@ -1047,8 +1047,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
bool optimizeSetIfWithCopyArm(Expression** currp) {
auto* set = (*currp)->cast<LocalSet>();
auto* iff = set->value->dynCast<If>();
- if (!iff || !isConcreteType(iff->type) ||
- !isConcreteType(iff->condition->type)) {
+ if (!iff || !iff->type.isConcrete() ||
+ !iff->condition->type.isConcrete()) {
return false;
}
Builder builder(*getModule());
diff --git a/src/passes/SafeHeap.cpp b/src/passes/SafeHeap.cpp
index 169c129b4..f29a1cf66 100644
--- a/src/passes/SafeHeap.cpp
+++ b/src/passes/SafeHeap.cpp
@@ -41,7 +41,7 @@ static const Name ALIGNFAULT_IMPORT("alignfault");
static Name getLoadName(Load* curr) {
std::string ret = "SAFE_HEAP_LOAD_";
- ret += printType(curr->type);
+ ret += curr->type.toString();
ret += "_" + std::to_string(curr->bytes) + "_";
if (LoadUtils::isSignRelevant(curr) && !curr->signed_) {
ret += "U_";
@@ -56,7 +56,7 @@ static Name getLoadName(Load* curr) {
static Name getStoreName(Store* curr) {
std::string ret = "SAFE_HEAP_STORE_";
- ret += printType(curr->valueType);
+ ret += curr->valueType.toString();
ret += "_" + std::to_string(curr->bytes) + "_";
if (curr->isAtomic) {
ret += "A";
@@ -170,7 +170,7 @@ struct SafeHeap : public Pass {
bool
isPossibleAtomicOperation(Index align, Index bytes, bool shared, Type type) {
- return align == bytes && shared && isIntegerType(type);
+ return align == bytes && shared && type.isInteger();
}
void addGlobals(Module* module, FeatureSet features) {
@@ -189,7 +189,7 @@ struct SafeHeap : public Pass {
}
for (auto signed_ : {true, false}) {
load.signed_ = signed_;
- if (isFloatType(type) && signed_) {
+ if (type.isFloat() && signed_) {
continue;
}
for (Index align : {1, 2, 4, 8, 16}) {
diff --git a/src/passes/Souperify.cpp b/src/passes/Souperify.cpp
index 199db727e..c5dca51fa 100644
--- a/src/passes/Souperify.cpp
+++ b/src/passes/Souperify.cpp
@@ -254,7 +254,7 @@ struct Trace {
(node != toInfer &&
excludeAsChildren.find(node) != excludeAsChildren.end())) {
auto type = node->getWasmType();
- assert(isConcreteType(type));
+ assert(type.isConcrete());
auto* var = Node::makeVar(type);
replacements[node] = std::unique_ptr<Node>(var);
node = var;
@@ -458,8 +458,7 @@ struct Printer {
assert(node);
switch (node->type) {
case Node::Type::Var: {
- std::cout << "%" << indexing[node] << ":" << printType(node->wasmType)
- << " = var";
+ std::cout << "%" << indexing[node] << ":" << node->wasmType << " = var";
break; // nothing more to add
}
case Node::Type::Expr: {
@@ -496,8 +495,7 @@ struct Printer {
}
case Node::Type::Zext: {
auto* child = node->getValue(0);
- std::cout << "%" << indexing[node] << ':'
- << printType(child->getWasmType());
+ std::cout << "%" << indexing[node] << ':' << child->getWasmType();
std::cout << " = zext ";
printInternal(child);
break;
@@ -523,7 +521,7 @@ struct Printer {
}
void print(Literal value) {
- std::cout << value.getInteger() << ':' << printType(value.type);
+ std::cout << value.getInteger() << ':' << value.type;
}
void printInternal(Node* node) {
diff --git a/src/passes/StackIR.cpp b/src/passes/StackIR.cpp
index 52ea061a3..2434c44f6 100644
--- a/src/passes/StackIR.cpp
+++ b/src/passes/StackIR.cpp
@@ -169,7 +169,7 @@ private:
values.clear();
}
// This is something we should handle, look into it.
- if (isConcreteType(inst->type)) {
+ if (inst->type.isConcrete()) {
bool optimized = false;
if (auto* get = inst->origin->dynCast<LocalGet>()) {
// This is a potential optimization opportunity! See if we
diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp
index 82d904701..d97ce461b 100644
--- a/src/passes/Vacuum.cpp
+++ b/src/passes/Vacuum.cpp
@@ -216,11 +216,11 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
auto* child = list[z];
// The last element may be used.
bool used =
- z == size - 1 && isConcreteType(curr->type) &&
+ z == size - 1 && curr->type.isConcrete() &&
ExpressionAnalyzer::isResultUsed(expressionStack, getFunction());
auto* optimized = optimize(child, used, true);
if (!optimized) {
- if (isConcreteType(child->type)) {
+ if (child->type.isConcrete()) {
// We can't just skip a final concrete element, even if it isn't used.
// Instead, replace it with something that's easy to optimize out (for
// example, code-folding can merge out identical zeros at the end of
@@ -357,7 +357,7 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
// note that the last element may be concrete but not the block, if the
// block has an unreachable element in the middle, making the block
// unreachable despite later elements and in particular the last
- if (isConcreteType(last->type) && block->type == last->type) {
+ if (last->type.isConcrete() && block->type == last->type) {
last = optimize(last, false, false);
if (!last) {
// we may be able to remove this, if there are no brs
@@ -393,16 +393,15 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
// unreachable, as it if is a branch, this can make that branch optimizable
// and more vaccuming possible
auto* iff = curr->value->dynCast<If>();
- if (iff && iff->ifFalse && isConcreteType(iff->type)) {
+ if (iff && iff->ifFalse && iff->type.isConcrete()) {
// reuse the drop in both cases
- if (iff->ifTrue->type == unreachable &&
- isConcreteType(iff->ifFalse->type)) {
+ if (iff->ifTrue->type == unreachable && iff->ifFalse->type.isConcrete()) {
curr->value = iff->ifFalse;
iff->ifFalse = curr;
iff->type = none;
replaceCurrent(iff);
} else if (iff->ifFalse->type == unreachable &&
- isConcreteType(iff->ifTrue->type)) {
+ iff->ifTrue->type.isConcrete()) {
curr->value = iff->ifTrue;
iff->ifTrue = curr;
iff->type = none;
diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h
index 13771a69d..9ef3d2e1e 100644
--- a/src/tools/execution-results.h
+++ b/src/tools/execution-results.h
@@ -71,7 +71,7 @@ struct ExecutionResults {
// this has a result
results[exp->name] = run(func, wasm, instance);
// ignore the result if we hit an unreachable and returned no value
- if (isConcreteType(results[exp->name].type)) {
+ if (results[exp->name].type.isConcrete()) {
std::cout << "[fuzz-exec] note result: " << exp->name << " => "
<< results[exp->name] << '\n';
}
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index 0e5184826..fe4a3955a 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -457,7 +457,7 @@ private:
void addImportLoggingSupport() {
for (auto type : getConcreteTypes()) {
auto* func = new Function;
- Name name = std::string("log-") + printType(type);
+ Name name = std::string("log-") + type.toString();
func->name = name;
func->module = "fuzzing-support";
func->base = name;
@@ -790,7 +790,7 @@ private:
args.push_back(makeConst(type));
}
Expression* invoke = builder.makeCall(func->name, args, func->result);
- if (isConcreteType(func->result)) {
+ if (func->result.isConcrete()) {
invoke = builder.makeDrop(invoke);
}
invocations.push_back(invoke);
@@ -827,7 +827,7 @@ private:
// when we should stop, emit something small (but not necessarily trivial)
if (finishedInput || nesting >= 5 * NESTING_LIMIT || // hard limit
(nesting >= NESTING_LIMIT && !oneIn(3))) {
- if (isConcreteType(type)) {
+ if (type.isConcrete()) {
if (oneIn(2)) {
return makeConst(type);
} else {
@@ -996,7 +996,7 @@ private:
// make something with no chance of infinite recursion
Expression* makeTrivial(Type type) {
- if (isConcreteType(type)) {
+ if (type.isConcrete()) {
if (oneIn(2)) {
return makeLocalGet(type);
} else {
@@ -1007,7 +1007,7 @@ private:
}
assert(type == unreachable);
Expression* ret = nullptr;
- if (isConcreteType(func->result)) {
+ if (func->result.isConcrete()) {
ret = makeTrivial(func->result);
}
return builder.makeReturn(ret);
@@ -1039,13 +1039,13 @@ private:
}
// give a chance to make the final element an unreachable break, instead
// of concrete - a common pattern (branch to the top of a loop etc.)
- if (!finishedInput && isConcreteType(type) && oneIn(2)) {
+ if (!finishedInput && type.isConcrete() && oneIn(2)) {
ret->list.push_back(makeBreak(unreachable));
} else {
ret->list.push_back(make(type));
}
breakableStack.pop_back();
- if (isConcreteType(type)) {
+ if (type.isConcrete()) {
ret->finalize(type);
} else {
ret->finalize();
@@ -1131,7 +1131,7 @@ private:
auto* target = pick(breakableStack);
auto name = getTargetName(target);
auto valueType = getTargetType(target);
- if (isConcreteType(type)) {
+ if (type.isConcrete()) {
// we are flowing out a value
if (valueType != type) {
// we need to break to a proper place
@@ -1485,7 +1485,7 @@ private:
Expression* makeStore(Type type) {
// exnref type cannot be stored in memory
- if (!allowMemory || isReferenceType(type)) {
+ if (!allowMemory || type.isRef()) {
return makeTrivial(type);
}
auto* ret = makeNonAtomicStore(type);
@@ -1982,7 +1982,7 @@ private:
return makeTrivial(type);
}
// There's no binary ops for exnref
- if (isReferenceType(type)) {
+ if (type.isRef()) {
makeTrivial(type);
}
@@ -2238,7 +2238,7 @@ private:
auto default_ = names.back();
names.pop_back();
auto temp1 = make(i32),
- temp2 = isConcreteType(valueType) ? make(valueType) : nullptr;
+ temp2 = valueType.isConcrete() ? make(valueType) : nullptr;
return builder.makeSwitch(names, default_, temp1, temp2);
}
@@ -2248,8 +2248,8 @@ private:
}
Expression* makeReturn(Type type) {
- return builder.makeReturn(isConcreteType(func->result) ? make(func->result)
- : nullptr);
+ return builder.makeReturn(func->result.isConcrete() ? make(func->result)
+ : nullptr);
}
Expression* makeNop(Type type) {
@@ -2607,7 +2607,7 @@ private:
Expression* makeLogging() {
auto type = getConcreteType();
return builder.makeCall(
- std::string("log-") + printType(type), {make(type)}, none);
+ std::string("log-") + type.toString(), {make(type)}, none);
}
Expression* makeMemoryHashLogging() {
diff --git a/src/tools/js-wrapper.h b/src/tools/js-wrapper.h
index 32b46affa..34a823e97 100644
--- a/src/tools/js-wrapper.h
+++ b/src/tools/js-wrapper.h
@@ -113,7 +113,7 @@ static std::string generateJSWrapper(Module& wasm) {
}
ret += ")";
if (func->result != none) {
- ret += ", '" + std::string(printType(func->result)) + "'))";
+ ret += ", '" + func->result.toString() + "'))";
// TODO: getTempRet
}
ret += ";\n";
diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp
index ba9f10264..f1fbebc24 100644
--- a/src/tools/wasm-reduce.cpp
+++ b/src/tools/wasm-reduce.cpp
@@ -466,7 +466,7 @@ struct Reducer
if (tryToReduceCurrentToNop()) {
return;
}
- } else if (isConcreteType(curr->type)) {
+ } else if (curr->type.isConcrete()) {
if (tryToReduceCurrentToConst()) {
return;
}
@@ -556,7 +556,7 @@ struct Reducer
}
// Finally, try to replace with a child.
for (auto* child : ChildIterator(curr)) {
- if (isConcreteType(child->type) && curr->type == none) {
+ if (child->type.isConcrete() && curr->type == none) {
if (tryToReplaceCurrent(builder->makeDrop(child))) {
return;
}
@@ -567,13 +567,13 @@ struct Reducer
}
}
// If that didn't work, try to replace with a child + a unary conversion
- if (isConcreteType(curr->type) &&
+ if (curr->type.isConcrete() &&
!curr->is<Unary>()) { // but not if it's already unary
for (auto* child : ChildIterator(curr)) {
if (child->type == curr->type) {
continue; // already tried
}
- if (!isConcreteType(child->type)) {
+ if (!child->type.isConcrete()) {
continue; // no conversion
}
Expression* fixed = nullptr;
@@ -870,7 +870,7 @@ struct Reducer
auto funcResult = func->result;
auto* funcBody = func->body;
for (auto* child : ChildIterator(func->body)) {
- if (!(isConcreteType(child->type) || child->type == none)) {
+ if (!(child->type.isConcrete() || child->type == none)) {
continue; // not something a function can return
}
// Try to replace the body with the child, fixing up the function
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index d47ec413f..87b1f0306 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -325,7 +325,7 @@ public:
ret->value = value;
ret->valueType = type;
ret->finalize();
- assert(isConcreteType(ret->value->type) ? ret->value->type == type : true);
+ assert(ret->value->type.isConcrete() ? ret->value->type == type : true);
return ret;
}
Store* makeAtomicStore(unsigned bytes,
@@ -596,7 +596,7 @@ public:
static Index addVar(Function* func, Name name, Type type) {
// always ok to add a var, it does not affect other indices
- assert(isConcreteType(type));
+ assert(type.isConcrete());
Index index = func->getNumLocals();
if (name.is()) {
func->localIndices[name] = index;
@@ -701,7 +701,7 @@ public:
// Drop an expression if it has a concrete type
Expression* dropIfConcretelyTyped(Expression* curr) {
- if (!isConcreteType(curr->type)) {
+ if (!curr->type.isConcrete()) {
return curr;
}
return makeDrop(curr);
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index dc2696716..e17b4e939 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -141,11 +141,11 @@ public:
}
auto ret = OverriddenVisitor<SubType, Flow>::visit(curr);
if (!ret.breaking() &&
- (isConcreteType(curr->type) || isConcreteType(ret.value.type))) {
+ (curr->type.isConcrete() || ret.value.type.isConcrete())) {
#if 1 // def WASM_INTERPRETER_DEBUG
if (ret.value.type != curr->type) {
- std::cerr << "expected " << printType(curr->type) << ", seeing "
- << printType(ret.value.type) << " from\n"
+ std::cerr << "expected " << curr->type << ", seeing " << ret.value.type
+ << " from\n"
<< curr << '\n';
}
#endif
@@ -482,10 +482,10 @@ public:
}
Literal right = flow.value;
NOTE_EVAL2(left, right);
- assert(isConcreteType(curr->left->type) ? left.type == curr->left->type
- : true);
- assert(isConcreteType(curr->right->type) ? right.type == curr->right->type
- : true);
+ assert(curr->left->type.isConcrete() ? left.type == curr->left->type
+ : true);
+ assert(curr->right->type.isConcrete() ? right.type == curr->right->type
+ : true);
switch (curr->op) {
case AddInt32:
case AddInt64:
@@ -1451,9 +1451,8 @@ private:
assert(function->isParam(i));
if (function->params[i] != arguments[i].type) {
std::cerr << "Function `" << function->name << "` expects type "
- << printType(function->params[i]) << " for parameter "
- << i << ", got " << printType(arguments[i].type) << "."
- << std::endl;
+ << function->params[i] << " for parameter " << i
+ << ", got " << arguments[i].type << "." << std::endl;
WASM_UNREACHABLE();
}
locals[i] = arguments[i];
diff --git a/src/wasm-type.h b/src/wasm-type.h
index a7c9f645b..371d3216f 100644
--- a/src/wasm-type.h
+++ b/src/wasm-type.h
@@ -18,15 +18,19 @@
#define wasm_wasm_type_h
#include "wasm-features.h"
+#include <ostream>
+#include <vector>
namespace wasm {
class Type {
uint32_t id;
+ void init(const std::vector<Type>&);
public:
enum ValueType : uint32_t {
none,
+ unreachable,
i32,
i64,
f32,
@@ -34,10 +38,7 @@ public:
v128,
anyref,
exnref,
- // none means no type, e.g. a block can have no return type. but unreachable
- // is different, as it can be "ignored" when doing type checking across
- // branches
- unreachable
+ _last_value_type,
};
Type() = default;
@@ -48,21 +49,54 @@ public:
// But converting raw uint32_t is more dangerous, so make it explicit
constexpr explicit Type(uint32_t id) : id(id){};
+ // Construct from lists of elementary types
+ Type(std::initializer_list<Type> types);
+ explicit Type(const std::vector<Type>& types);
+
+ // Accessors
+ size_t size() const;
+ const std::vector<Type>& expand() const;
+
+ // Predicates
+ bool isSingle() const { return id >= i32 && id < _last_value_type; }
+ bool isMulti() const { return id >= _last_value_type; }
+ bool isConcrete() const { return id >= i32; }
+ bool isInteger() const { return id == i32 || id == i64; }
+ bool isFloat() const { return id == f32 || id == f64; }
+ bool isVector() const { return id == v128; };
+ bool isRef() const { return id == anyref || id == exnref; }
+
// (In)equality must be defined for both Type and ValueType because it is
// otherwise ambiguous whether to convert both this and other to int or
// convert other to Type.
- bool operator==(const Type& other) { return id == other.id; }
+ bool operator==(const Type& other) const { return id == other.id; }
+ bool operator==(const ValueType& other) const { return id == other; }
+ bool operator!=(const Type& other) const { return id != other.id; }
+ bool operator!=(const ValueType& other) const { return id != other; }
- bool operator==(const ValueType& other) { return id == other; }
-
- bool operator!=(const Type& other) { return id != other.id; }
+ // Allows for using Types in switch statements
+ constexpr operator uint32_t() const { return id; }
+ std::string toString() const;
+};
- bool operator!=(const ValueType& other) { return id != other; }
+// Wrapper type for formatting types as "(param i32 i64 f32)"
+struct ParamType {
+ Type type;
+ ParamType(Type type) : type(type) {}
+ std::string toString() const;
+};
- // Allows for using Types in switch statements
- constexpr operator int() const { return id; }
+// Wrapper type for formatting types as "(result i32 i64 f32)"
+struct ResultType {
+ Type type;
+ ResultType(Type type) : type(type) {}
+ std::string toString() const;
};
+std::ostream& operator<<(std::ostream& os, Type t);
+std::ostream& operator<<(std::ostream& os, ParamType t);
+std::ostream& operator<<(std::ostream& os, ResultType t);
+
constexpr Type none = Type::none;
constexpr Type i32 = Type::i32;
constexpr Type i64 = Type::i64;
@@ -73,16 +107,9 @@ constexpr Type anyref = Type::anyref;
constexpr Type exnref = Type::exnref;
constexpr Type unreachable = Type::unreachable;
-const char* printType(Type type);
unsigned getTypeSize(Type type);
FeatureSet getFeatures(Type type);
Type getType(unsigned size, bool float_);
-Type getReachableType(Type a, Type b);
-bool isConcreteType(Type type);
-bool isFloatType(Type type);
-bool isIntegerType(Type type);
-bool isVectorType(Type type);
-bool isReferenceType(Type type);
Type reinterpretType(Type type);
} // namespace wasm
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;
diff --git a/src/wasm2js.h b/src/wasm2js.h
index 337ea3806..344416b54 100644
--- a/src/wasm2js.h
+++ b/src/wasm2js.h
@@ -155,7 +155,7 @@ public:
frees[type].pop_back();
} else {
size_t index = temps[type]++;
- ret = IString((std::string("wasm2js_") + printType(type) + "$" +
+ ret = IString((std::string("wasm2js_") + type.toString() + "$" +
std::to_string(index))
.c_str(),
false);