diff --git a/src/wasm-builder.h b/src/wasm-builder.h index fdb489c33ac..85195addb5b 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 namespace wasm { @@ -189,15 +190,7 @@ class Builder { bool>; template = true> - Block* makeBlock(const T& items) { - auto* ret = wasm.allocator.alloc(); - ret->list.set(items); - ret->finalize(); - return ret; - } - - template = true> - Block* makeBlock(const T& items, Type type) { + Block* makeBlock(const T& items, std::optional type = std::nullopt) { auto* ret = wasm.allocator.alloc(); ret->list.set(items); ret->finalize(type); @@ -205,38 +198,29 @@ class Builder { } template = true> - Block* makeBlock(Name name, const T& items, Type type) { + Block* makeBlock(Name name, + const T& items, + std::optional type = std::nullopt) { auto* ret = wasm.allocator.alloc(); ret->name = name; ret->list.set(items); ret->finalize(type); return ret; } - Block* makeBlock(std::initializer_list&& items) { - return makeBlock(items); - } - Block* makeBlock(std::initializer_list&& items, Type type) { + Block* makeBlock(std::initializer_list&& items, + std::optional type = std::nullopt) { return makeBlock(items, type); } - Block* - makeBlock(Name name, std::initializer_list&& items, Type type) { + Block* makeBlock(Name name, + std::initializer_list&& items, + std::optional type = std::nullopt) { return makeBlock(name, items, type); } If* makeIf(Expression* condition, Expression* ifTrue, - Expression* ifFalse = nullptr) { - auto* ret = wasm.allocator.alloc(); - 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 = std::nullopt) { auto* ret = wasm.allocator.alloc(); ret->condition = condition; ret->ifTrue = ifTrue; @@ -244,14 +228,9 @@ class Builder { ret->finalize(type); return ret; } - Loop* makeLoop(Name name, Expression* body) { - auto* ret = wasm.allocator.alloc(); - 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 = std::nullopt) { auto* ret = wasm.allocator.alloc(); ret->name = name; ret->body = body; @@ -792,77 +771,47 @@ class Builder { const std::vector& catchTags, const std::vector& catchBodies, Name delegateTarget, - Type type, - bool hasType) { // differentiate whether a type was passed in + std::optional type = std::nullopt) { auto* ret = wasm.allocator.alloc(); 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& catchTags, - const std::vector& catchBodies) { - return makeTry( - Name(), body, catchTags, catchBodies, Name(), Type::none, false); - } + // TODO delete? Try* makeTry(Expression* body, const std::vector& catchTags, const std::vector& catchBodies, - Type type) { - return makeTry(Name(), body, catchTags, catchBodies, Name(), type, true); - } - Try* makeTry(Name name, - Expression* body, - const std::vector& catchTags, - const std::vector& catchBodies) { - return makeTry( - name, body, catchTags, catchBodies, Name(), Type::none, false); + std::optional type = std::nullopt) { + return makeTry(Name(), body, catchTags, catchBodies, Name(), type); } Try* makeTry(Name name, Expression* body, const std::vector& catchTags, const std::vector& 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); + std::optional type = std::nullopt) { + return makeTry(name, body, catchTags, catchBodies, Name(), type); } - Try* makeTry(Expression* body, Name delegateTarget, Type type) { - return makeTry(Name(), body, {}, {}, delegateTarget, type, true); - } - 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 = std::nullopt) { + return makeTry(Name(), body, {}, {}, delegateTarget, type); } - TryTable* makeTryTable(Expression* body, - const std::vector& catchTags, - const std::vector& catchDests, - const std::vector& catchRefs) { - auto* ret = wasm.allocator.alloc(); - 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 = std::nullopt) { + return makeTry(name, body, {}, {}, delegateTarget, type); } TryTable* makeTryTable(Expression* body, const std::vector& catchTags, const std::vector& catchDests, const std::vector& catchRefs, - Type type) { + std::optional type = std::nullopt) { auto* ret = wasm.allocator.alloc(); ret->body = body; ret->catchTags.set(catchTags); @@ -1336,7 +1285,9 @@ class Builder { // ensure a node is a block, if it isn't already, and optionally append to the // block - Block* blockify(Expression* any, Expression* append = nullptr) { + Block* blockify(Expression* any, + Expression* append = nullptr, + std::optional type = std::nullopt) { Block* block = nullptr; if (any) { block = any->dynCast(); @@ -1346,21 +1297,25 @@ class Builder { } if (append) { block->list.push_back(append); - block->finalize(); + block->finalize(type); } return block; } template - Block* blockify(Expression* any, Expression* append, Ts... args) { + Block* blockify(Expression* any, + Expression* append, + Ts... args) { return blockify(blockify(any, append), args...); } // 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 = std::nullopt) { Block* block = nullptr; if (any) { block = any->dynCast(); @@ -1371,21 +1326,16 @@ class Builder { 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 = 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 576a0d0f19d..8d1587d4223 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -828,23 +828,21 @@ class Block : public SpecificExpression { 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_ = std::nullopt, + Breakability breakability = Unknown); }; class If : public SpecificExpression { @@ -856,14 +854,12 @@ class If : public SpecificExpression { 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_ = std::nullopt); }; class Loop : public SpecificExpression { @@ -874,14 +870,12 @@ class Loop : public SpecificExpression { 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_ = std::nullopt); }; class Break : public SpecificExpression { @@ -1472,8 +1466,7 @@ class Try : public SpecificExpression { } bool isCatch() const { return !catchBodies.empty(); } bool isDelegate() const { return delegateTarget.is(); } - void finalize(); - void finalize(Type type_); + void finalize(std::optional type_ = std::nullopt); }; // 'try_table' from the new EH proposal @@ -1497,8 +1490,8 @@ class TryTable : public SpecificExpression { // 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_ = 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 6589ca06984..6665a268218 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,85 +172,82 @@ handleUnreachable(Block* block, } } -void Block::finalize() { - 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. - type = list.back()->type; - if (!name.is()) { - // Nothing branches here, so this is easy. - handleUnreachable(this, NoBreak); - return; - } +void Block::finalize(std::optional type_, Breakability breakability) { + if (type_) { + type = *type_; + if (type == Type::none && list.size() > 0) { + handleUnreachable(this, breakability); + } - // The default type is according to the value that flows out. - BranchUtils::BranchSeeker seeker(this->name); - 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. - seeker.types.insert(type); - type = Type::getLeastUpperBound(seeker.types); } else { - // There are no branches, so this block may be unreachable. - handleUnreachable(this, NoBreak); - } -} - -void Block::finalize(Type type_) { - type = type_; - if (type == Type::none && list.size() > 0) { - handleUnreachable(this); - } -} + 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. + type = list.back()->type; + if (!name.is()) { + // Nothing branches here, so this is easy. + handleUnreachable(this, NoBreak); + return; + } -void Block::finalize(Type type_, Breakability breakability) { - type = type_; - if (type == Type::none && list.size() > 0) { - handleUnreachable(this, breakability); + // The default type is according to the value that flows out. + BranchUtils::BranchSeeker seeker(this->name); + 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. + seeker.types.insert(type); + type = Type::getLeastUpperBound(seeker.types); + } else { + // There are no branches, so this block may be unreachable. + handleUnreachable(this, NoBreak); + } } } -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_) { + if (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() { - type = ifFalse ? Type::getLeastUpperBound(ifTrue->type, ifFalse->type) - : Type::none; - // if the arms return a value, leave it even if the condition - // is unreachable, we still mark ourselves as having that type, e.g. - // (if (result i32) - // (unreachable) - // (i32.const 10) - // (i32.const 20) - // ) - // otherwise, if the condition is unreachable, so is the if - if (type == Type::none && condition->type == Type::unreachable) { - type = Type::unreachable; + } else { + type = ifFalse ? Type::getLeastUpperBound(ifTrue->type, ifFalse->type) + : Type::none; + // if the arms return a value, leave it even if the condition + // is unreachable, we still mark ourselves as having that type, e.g. + // (if (result i32) + // (unreachable) + // (i32.const 10) + // (i32.const 20) + // ) + // otherwise, if the condition is unreachable, so is the if + if (type == Type::none && condition->type == Type::unreachable) { + type = Type::unreachable; + } } } -void Loop::finalize(Type type_) { - type = type_; - if (type == Type::none && body->type == Type::unreachable) { - type = Type::unreachable; +void Loop::finalize(std::optional 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 +869,26 @@ 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 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_) { + 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 { + // 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 types{body->type}; + types.reserve(catchBodies.size()); + for (auto catchBody : catchBodies) { + types.insert(catchBody->type); + } + type = Type::getLeastUpperBound(types); } } @@ -928,15 +924,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_, Module* wasm) { + if (type_) { + type = *type_; + if (type == Type::none && body->type == Type::unreachable) { + type = Type::unreachable; + } + } else { + type = body->type; } populateTryTableSentTypes(this, wasm); }