summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/wasm-builder.h134
-rw-r--r--src/wasm.h63
-rw-r--r--src/wasm/wasm.cpp121
-rw-r--r--test/example/stack-utils.cpp2
4 files changed, 125 insertions, 195 deletions
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index fdb489c33..dd5b498be 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -20,6 +20,7 @@
#include "ir/manipulation.h"
#include "parsing.h"
#include "wasm.h"
+#include <optional>
namespace wasm {
@@ -189,15 +190,7 @@ public:
bool>;
template<typename T, bool_if_not_expr_t<T> = true>
- Block* makeBlock(const T& items) {
- auto* ret = wasm.allocator.alloc<Block>();
- ret->list.set(items);
- ret->finalize();
- return ret;
- }
-
- template<typename T, bool_if_not_expr_t<T> = true>
- Block* makeBlock(const T& items, Type type) {
+ Block* makeBlock(const T& items, std::optional<Type> type = std::nullopt) {
auto* ret = wasm.allocator.alloc<Block>();
ret->list.set(items);
ret->finalize(type);
@@ -205,38 +198,29 @@ public:
}
template<typename T, bool_if_not_expr_t<T> = true>
- Block* makeBlock(Name name, const T& items, Type type) {
+ Block* makeBlock(Name name,
+ const T& items,
+ std::optional<Type> type = std::nullopt) {
auto* ret = wasm.allocator.alloc<Block>();
ret->name = name;
ret->list.set(items);
ret->finalize(type);
return ret;
}
- Block* makeBlock(std::initializer_list<Expression*>&& items) {
- return makeBlock(items);
- }
- Block* makeBlock(std::initializer_list<Expression*>&& items, Type type) {
+ Block* makeBlock(std::initializer_list<Expression*>&& items,
+ std::optional<Type> type = std::nullopt) {
return makeBlock(items, type);
}
- Block*
- makeBlock(Name name, std::initializer_list<Expression*>&& items, Type type) {
+ Block* makeBlock(Name name,
+ std::initializer_list<Expression*>&& items,
+ std::optional<Type> type = std::nullopt) {
return makeBlock(name, items, type);
}
If* makeIf(Expression* condition,
Expression* ifTrue,
- Expression* ifFalse = nullptr) {
- auto* ret = wasm.allocator.alloc<If>();
- ret->condition = condition;
- ret->ifTrue = ifTrue;
- ret->ifFalse = ifFalse;
- ret->finalize();
- return ret;
- }
- If* makeIf(Expression* condition,
- Expression* ifTrue,
- Expression* ifFalse,
- Type type) {
+ Expression* ifFalse = nullptr,
+ std::optional<Type> type = std::nullopt) {
auto* ret = wasm.allocator.alloc<If>();
ret->condition = condition;
ret->ifTrue = ifTrue;
@@ -244,14 +228,9 @@ public:
ret->finalize(type);
return ret;
}
- Loop* makeLoop(Name name, Expression* body) {
- auto* ret = wasm.allocator.alloc<Loop>();
- ret->name = name;
- ret->body = body;
- ret->finalize();
- return ret;
- }
- Loop* makeLoop(Name name, Expression* body, Type type) {
+ Loop* makeLoop(Name name,
+ Expression* body,
+ std::optional<Type> type = std::nullopt) {
auto* ret = wasm.allocator.alloc<Loop>();
ret->name = name;
ret->body = body;
@@ -792,77 +771,47 @@ private:
const std::vector<Name>& catchTags,
const std::vector<Expression*>& catchBodies,
Name delegateTarget,
- Type type,
- bool hasType) { // differentiate whether a type was passed in
+ std::optional<Type> type = std::nullopt) {
auto* ret = wasm.allocator.alloc<Try>();
ret->name = name;
ret->body = body;
ret->catchTags.set(catchTags);
ret->catchBodies.set(catchBodies);
- if (hasType) {
- ret->finalize(type);
- } else {
- ret->finalize();
- }
+ ret->finalize(type);
return ret;
}
public:
- Try* makeTry(Expression* body,
- const std::vector<Name>& catchTags,
- const std::vector<Expression*>& catchBodies) {
- return makeTry(
- Name(), body, catchTags, catchBodies, Name(), Type::none, false);
- }
+ // TODO delete?
Try* makeTry(Expression* body,
const std::vector<Name>& catchTags,
const std::vector<Expression*>& catchBodies,
- Type type) {
- return makeTry(Name(), body, catchTags, catchBodies, Name(), type, true);
- }
- Try* makeTry(Name name,
- Expression* body,
- const std::vector<Name>& catchTags,
- const std::vector<Expression*>& catchBodies) {
- return makeTry(
- name, body, catchTags, catchBodies, Name(), Type::none, false);
+ std::optional<Type> type = std::nullopt) {
+ return makeTry(Name(), body, catchTags, catchBodies, Name(), type);
}
Try* makeTry(Name name,
Expression* body,
const std::vector<Name>& catchTags,
const std::vector<Expression*>& catchBodies,
- Type type) {
- return makeTry(name, body, catchTags, catchBodies, Name(), type, true);
- }
- Try* makeTry(Expression* body, Name delegateTarget) {
- return makeTry(Name(), body, {}, {}, delegateTarget, Type::none, false);
- }
- Try* makeTry(Expression* body, Name delegateTarget, Type type) {
- return makeTry(Name(), body, {}, {}, delegateTarget, type, true);
+ std::optional<Type> type = std::nullopt) {
+ return makeTry(name, body, catchTags, catchBodies, Name(), type);
}
- Try* makeTry(Name name, Expression* body, Name delegateTarget) {
- return makeTry(name, body, {}, {}, delegateTarget, Type::none, false);
- }
- Try* makeTry(Name name, Expression* body, Name delegateTarget, Type type) {
- return makeTry(name, body, {}, {}, delegateTarget, type, true);
+ Try* makeTry(Expression* body,
+ Name delegateTarget,
+ std::optional<Type> type = std::nullopt) {
+ return makeTry(Name(), body, {}, {}, delegateTarget, type);
}
- TryTable* makeTryTable(Expression* body,
- const std::vector<Name>& catchTags,
- const std::vector<Name>& catchDests,
- const std::vector<bool>& catchRefs) {
- auto* ret = wasm.allocator.alloc<TryTable>();
- ret->body = body;
- ret->catchTags.set(catchTags);
- ret->catchDests.set(catchDests);
- ret->catchRefs.set(catchRefs);
- ret->finalize(&wasm);
- return ret;
+ Try* makeTry(Name name,
+ Expression* body,
+ Name delegateTarget,
+ std::optional<Type> type = std::nullopt) {
+ return makeTry(name, body, {}, {}, delegateTarget, type);
}
TryTable* makeTryTable(Expression* body,
const std::vector<Name>& catchTags,
const std::vector<Name>& catchDests,
const std::vector<bool>& catchRefs,
- Type type) {
+ std::optional<Type> type = std::nullopt) {
auto* ret = wasm.allocator.alloc<TryTable>();
ret->body = body;
ret->catchTags.set(catchTags);
@@ -1359,8 +1308,10 @@ public:
// ensure a node is a block, if it isn't already, and optionally append to the
// block this variant sets a name for the block, so it will not reuse a block
// already named
- Block*
- blockifyWithName(Expression* any, Name name, Expression* append = nullptr) {
+ Block* blockifyWithName(Expression* any,
+ Name name,
+ Expression* append = nullptr,
+ std::optional<Type> type = std::nullopt) {
Block* block = nullptr;
if (any) {
block = any->dynCast<Block>();
@@ -1371,21 +1322,16 @@ public:
block->name = name;
if (append) {
block->list.push_back(append);
- block->finalize();
+ block->finalize(type);
}
return block;
}
// a helper for the common pattern of a sequence of two expressions. Similar
// to blockify, but does *not* reuse a block if the first is one.
- Block* makeSequence(Expression* left, Expression* right) {
- auto* block = makeBlock(left);
- block->list.push_back(right);
- block->finalize();
- return block;
- }
-
- Block* makeSequence(Expression* left, Expression* right, Type type) {
+ Block* makeSequence(Expression* left,
+ Expression* right,
+ std::optional<Type> type = std::nullopt) {
auto* block = makeBlock(left);
block->list.push_back(right);
block->finalize(type);
diff --git a/src/wasm.h b/src/wasm.h
index 576a0d0f1..8d1587d42 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -828,23 +828,21 @@ public:
Name name;
ExpressionList list;
- // set the type purely based on its contents. this scans the block, so it is
- // not fast.
- void finalize();
-
- // set the type given you know its type, which is the case when parsing
- // s-expression or binary, as explicit types are given. the only additional
- // work this does is to set the type to unreachable in the cases that is
- // needed (which may require scanning the block)
- void finalize(Type type_);
-
enum Breakability { Unknown, HasBreak, NoBreak };
- // set the type given you know its type, and you know if there is a break to
- // this block. this avoids the need to scan the contents of the block in the
- // case that it might be unreachable, so it is recommended if you already know
- // the type and breakability anyhow.
- void finalize(Type type_, Breakability breakability);
+ // If type_ is not given, set the type purely based on its contents. this
+ // scans the block, so it is not fast.
+ // If type_ is given, set the type given you know its type, which is the case
+ // when parsing s-expression or binary, as explicit types are given. the only
+ // additional work this does is to set the type to unreachable in the cases
+ // that is needed (which may require scanning the block)
+ //
+ // If breakability is given, you know if there is a break to this block. this
+ // avoids the need to scan the contents of the block in the case that it might
+ // be unreachable, so it is recommended if you already know the type and
+ // breakability anyhow.
+ void finalize(std::optional<Type> type_ = std::nullopt,
+ Breakability breakability = Unknown);
};
class If : public SpecificExpression<Expression::IfId> {
@@ -856,14 +854,12 @@ public:
Expression* ifTrue;
Expression* ifFalse;
- // set the type given you know its type, which is the case when parsing
- // s-expression or binary, as explicit types are given. the only additional
- // work this does is to set the type to unreachable in the cases that is
- // needed.
- void finalize(Type type_);
-
- // set the type purely based on its contents.
- void finalize();
+ // If type_ is not given, set the type purely based on its contents.
+ // If type_ is given, set the type given you know its type, which is the case
+ // when parsing s-expression or binary, as explicit types are given. the only
+ // additional work this does is to set the type to unreachable in the cases
+ // that is needed.
+ void finalize(std::optional<Type> type_ = std::nullopt);
};
class Loop : public SpecificExpression<Expression::LoopId> {
@@ -874,14 +870,12 @@ public:
Name name;
Expression* body;
- // set the type given you know its type, which is the case when parsing
- // s-expression or binary, as explicit types are given. the only additional
- // work this does is to set the type to unreachable in the cases that is
- // needed.
- void finalize(Type type_);
-
- // set the type purely based on its contents.
- void finalize();
+ // If type_ is not given, set the type purely based on its contents.
+ // If type_ is given, set the type given you know its type, which is the case
+ // when parsing s-expression or binary, as explicit types are given. the only
+ // additional work this does is to set the type to unreachable in the cases
+ // that is needed.
+ void finalize(std::optional<Type> type_ = std::nullopt);
};
class Break : public SpecificExpression<Expression::BreakId> {
@@ -1472,8 +1466,7 @@ public:
}
bool isCatch() const { return !catchBodies.empty(); }
bool isDelegate() const { return delegateTarget.is(); }
- void finalize();
- void finalize(Type type_);
+ void finalize(std::optional<Type> type_ = std::nullopt);
};
// 'try_table' from the new EH proposal
@@ -1497,8 +1490,8 @@ public:
// When 'Module*' parameter is given, we cache catch tags' types into
// 'sentTypes' array, so that the types can be accessed in other analyses
// without accessing the module.
- void finalize(Module* wasm = nullptr);
- void finalize(Type type_, Module* wasm = nullptr);
+ void finalize(std::optional<Type> type_ = std::nullopt,
+ Module* wasm = nullptr);
// Caches tags' types in the catch clauses in order not to query the module
// every time we query the sent types
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 6589ca069..ad44acda4 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -141,9 +141,7 @@ Literals getLiteralsFromConstExpression(Expression* curr) {
// a block is unreachable if one of its elements is unreachable,
// and there are no branches to it
-static void
-handleUnreachable(Block* block,
- Block::Breakability breakability = Block::Unknown) {
+static void handleUnreachable(Block* block, Block::Breakability breakability) {
if (block->type == Type::unreachable) {
return; // nothing to do
}
@@ -174,13 +172,21 @@ handleUnreachable(Block* block,
}
}
-void Block::finalize() {
+void Block::finalize(std::optional<Type> type_, Breakability breakability) {
+ if (type_) {
+ type = *type_;
+ if (type == Type::none && list.size() > 0) {
+ handleUnreachable(this, breakability);
+ }
+ return;
+ }
+
if (list.size() == 0) {
type = Type::none;
return;
}
- // The default type is what is at the end. Next we need to see if breaks and/
- // or unreachability change that.
+ // The default type is what is at the end. Next we need to see if breaks
+ // and/ or unreachability change that.
type = list.back()->type;
if (!name.is()) {
// Nothing branches here, so this is easy.
@@ -193,10 +199,7 @@ void Block::finalize() {
Expression* temp = this;
seeker.walk(temp);
if (seeker.found) {
- // Calculate the supertype of the branch types and the flowed-out type. If
- // there is no supertype among the available types, assume the current type
- // is already correct. TODO: calculate proper LUBs to compute a new correct
- // type in this situation.
+ // Calculate the LUB of the branch types and the flowed-out type.
seeker.types.insert(type);
type = Type::getLeastUpperBound(seeker.types);
} else {
@@ -205,30 +208,17 @@ void Block::finalize() {
}
}
-void Block::finalize(Type type_) {
- type = type_;
- if (type == Type::none && list.size() > 0) {
- handleUnreachable(this);
- }
-}
-
-void Block::finalize(Type type_, Breakability breakability) {
- type = type_;
- if (type == Type::none && list.size() > 0) {
- handleUnreachable(this, breakability);
- }
-}
-
-void If::finalize(Type type_) {
- type = type_;
- if (type == Type::none && (condition->type == Type::unreachable ||
- (ifFalse && ifTrue->type == Type::unreachable &&
- ifFalse->type == Type::unreachable))) {
- type = Type::unreachable;
+void If::finalize(std::optional<Type> type_) {
+ if (type_) {
+ type = *type_;
+ if (type == Type::none && (condition->type == Type::unreachable ||
+ (ifFalse && ifTrue->type == Type::unreachable &&
+ ifFalse->type == Type::unreachable))) {
+ type = Type::unreachable;
+ }
+ return;
}
-}
-void If::finalize() {
type = ifFalse ? Type::getLeastUpperBound(ifTrue->type, ifFalse->type)
: Type::none;
// if the arms return a value, leave it even if the condition
@@ -244,15 +234,17 @@ void If::finalize() {
}
}
-void Loop::finalize(Type type_) {
- type = type_;
- if (type == Type::none && body->type == Type::unreachable) {
- type = Type::unreachable;
+void Loop::finalize(std::optional<Type> type_) {
+ if (type_) {
+ type = *type_;
+ if (type == Type::none && body->type == Type::unreachable) {
+ type = Type::unreachable;
+ }
+ } else {
+ type = body->type;
}
}
-void Loop::finalize() { type = body->type; }
-
void Break::finalize() {
if (condition) {
if (condition->type == Type::unreachable) {
@@ -874,25 +866,25 @@ void TableCopy::finalize() {
}
}
-void Try::finalize() {
- // If none of the component bodies' type is a supertype of the others, assume
- // the current type is already correct. TODO: Calculate a proper LUB.
- std::unordered_set<Type> types{body->type};
- types.reserve(catchBodies.size());
- for (auto catchBody : catchBodies) {
- types.insert(catchBody->type);
- }
- type = Type::getLeastUpperBound(types);
-}
+void Try::finalize(std::optional<Type> type_) {
+ if (type_) {
+ type = *type_;
+ bool allUnreachable = body->type == Type::unreachable;
+ for (auto catchBody : catchBodies) {
+ allUnreachable &= catchBody->type == Type::unreachable;
+ }
+ if (type == Type::none && allUnreachable) {
+ type = Type::unreachable;
+ }
-void Try::finalize(Type type_) {
- type = type_;
- bool allUnreachable = body->type == Type::unreachable;
- for (auto catchBody : catchBodies) {
- allUnreachable &= catchBody->type == Type::unreachable;
- }
- if (type == Type::none && allUnreachable) {
- type = Type::unreachable;
+ } else {
+ // Calculate the LUB of catch bodies' types.
+ std::unordered_set<Type> types{body->type};
+ types.reserve(catchBodies.size());
+ for (auto catchBody : catchBodies) {
+ types.insert(catchBody->type);
+ }
+ type = Type::getLeastUpperBound(types);
}
}
@@ -928,15 +920,14 @@ static void populateTryTableSentTypes(TryTable* curr, Module* wasm) {
}
}
-void TryTable::finalize(Module* wasm) {
- type = body->type;
- populateTryTableSentTypes(this, wasm);
-}
-
-void TryTable::finalize(Type type_, Module* wasm) {
- type = type_;
- if (type == Type::none && body->type == Type::unreachable) {
- type = Type::unreachable;
+void TryTable::finalize(std::optional<Type> type_, Module* wasm) {
+ if (type_) {
+ type = *type_;
+ if (type == Type::none && body->type == Type::unreachable) {
+ type = Type::unreachable;
+ }
+ } else {
+ type = body->type;
}
populateTryTableSentTypes(this, wasm);
}
diff --git a/test/example/stack-utils.cpp b/test/example/stack-utils.cpp
index f8824e5cb..e393e13e3 100644
--- a/test/example/stack-utils.cpp
+++ b/test/example/stack-utils.cpp
@@ -24,7 +24,7 @@ void test_remove_nops() {
builder.makeNop(),
builder.makeNop(),
},
- {Type::i32, Type::i64});
+ Type{Type::i32, Type::i64});
std::cout << *block << '\n';
StackUtils::removeNops(block);
std::cout << *block << '\n';