diff options
Diffstat (limited to 'src/ir')
-rw-r--r-- | src/ir/ReFinalize.cpp | 7 | ||||
-rw-r--r-- | src/ir/effects.h | 26 | ||||
-rw-r--r-- | src/ir/linear-execution.h | 6 | ||||
-rw-r--r-- | src/ir/possible-contents.cpp | 34 | ||||
-rw-r--r-- | src/ir/possible-contents.h | 16 |
5 files changed, 86 insertions, 3 deletions
diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 0f78d37b7..c32d6efbf 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -128,7 +128,12 @@ void ReFinalize::visitTableFill(TableFill* curr) { curr->finalize(); } void ReFinalize::visitTableCopy(TableCopy* curr) { curr->finalize(); } void ReFinalize::visitTableInit(TableInit* curr) { curr->finalize(); } void ReFinalize::visitTry(Try* curr) { curr->finalize(); } -void ReFinalize::visitTryTable(TryTable* curr) { curr->finalize(); } +void ReFinalize::visitTryTable(TryTable* curr) { + curr->finalize(); + for (size_t i = 0; i < curr->catchDests.size(); i++) { + updateBreakValueType(curr->catchDests[i], curr->sentTypes[i]); + } +} void ReFinalize::visitThrow(Throw* curr) { curr->finalize(); } void ReFinalize::visitRethrow(Rethrow* curr) { curr->finalize(); } void ReFinalize::visitThrowRef(ThrowRef* curr) { curr->finalize(); } diff --git a/src/ir/effects.h b/src/ir/effects.h index fee8b3441..716624d64 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -431,6 +431,14 @@ private: self->pushTask(doStartTry, currp); return; } + if (auto* tryTable = curr->dynCast<TryTable>()) { + // We need to increment try depth before starting. + self->pushTask(doEndTryTable, currp); + self->pushTask(doVisitTryTable, currp); + self->pushTask(scan, &tryTable->body); + self->pushTask(doStartTryTable, currp); + return; + } PostWalker<InternalAnalyzer, OverriddenVisitor<InternalAnalyzer>>::scan( self, currp); } @@ -472,6 +480,24 @@ private: self->parent.catchDepth--; } + static void doStartTryTable(InternalAnalyzer* self, Expression** currp) { + auto* curr = (*currp)->cast<TryTable>(); + // We only count 'try_table's with a 'catch_all' because instructions + // within a 'try_table' without a 'catch_all' can still throw outside of + // the try. + if (curr->hasCatchAll()) { + self->parent.tryDepth++; + } + } + + static void doEndTryTable(InternalAnalyzer* self, Expression** currp) { + auto* curr = (*currp)->cast<TryTable>(); + if (curr->hasCatchAll()) { + assert(self->parent.tryDepth > 0 && "try depth cannot be negative"); + self->parent.tryDepth--; + } + } + void visitBlock(Block* curr) { if (curr->name.is()) { parent.breakTargets.erase(curr->name); // these were internal breaks diff --git a/src/ir/linear-execution.h b/src/ir/linear-execution.h index c6593bd64..e8b1923aa 100644 --- a/src/ir/linear-execution.h +++ b/src/ir/linear-execution.h @@ -171,6 +171,12 @@ struct LinearExecutionWalker : public PostWalker<SubType, VisitorType> { self->pushTask(SubType::scan, &curr->cast<Try>()->body); break; } + case Expression::Id::TryTableId: { + self->pushTask(SubType::doVisitTryTable, currp); + self->pushTask(SubType::doNoteNonLinear, currp); + self->pushTask(SubType::scan, &curr->cast<TryTable>()->body); + break; + } case Expression::Id::ThrowId: { self->pushTask(SubType::doVisitThrow, currp); self->pushTask(SubType::doNoteNonLinear, currp); diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index e7454c7c6..e5e6cf659 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -1135,8 +1135,38 @@ struct InfoCollector } } void visitTryTable(TryTable* curr) { - // TODO: optimize when possible - addRoot(curr); + receiveChildValue(curr->body, curr); + + // Connect caught tags with their branch targets, and materialize non-null + // exnref values. + auto numTags = curr->catchTags.size(); + for (Index tagIndex = 0; tagIndex < numTags; tagIndex++) { + auto tag = curr->catchTags[tagIndex]; + auto target = curr->catchDests[tagIndex]; + + Index exnrefIndex = 0; + if (tag.is()) { + auto params = getModule()->getTag(tag)->sig.params; + + for (Index i = 0; i < params.size(); i++) { + if (isRelevant(params[i])) { + info.links.push_back( + {TagLocation{tag, i}, + BreakTargetLocation{getFunction(), target, i}}); + } + } + + exnrefIndex = params.size(); + } + + if (curr->catchRefs[tagIndex]) { + auto location = CaughtExnRefLocation{}; + addRoot(location, + PossibleContents::fromType(Type(HeapType::exn, NonNullable))); + info.links.push_back( + {location, BreakTargetLocation{getFunction(), target, exnrefIndex}}); + } + } } void visitThrow(Throw* curr) { auto& operands = curr->operands; diff --git a/src/ir/possible-contents.h b/src/ir/possible-contents.h index 5ec4f758f..7b88483cf 100644 --- a/src/ir/possible-contents.h +++ b/src/ir/possible-contents.h @@ -473,6 +473,15 @@ struct TagLocation { } }; +// The location of an exnref materialized by a catch_ref or catch_all_ref clause +// of a try_table. No data is stored here. exnrefs contain a tag and a payload +// at run-time, as well as potential metadata such as stack traces, but we don't +// track that. So this is the same as NullLocation in a way: we just need *a* +// source of contents for places that receive an exnref. +struct CaughtExnRefLocation { + bool operator==(const CaughtExnRefLocation& other) const { return true; } +}; + // A null value. This is used as the location of the default value of a var in a // function, a null written to a struct field in struct.new_with_default, etc. struct NullLocation { @@ -520,6 +529,7 @@ using Location = std::variant<ExpressionLocation, SignatureResultLocation, DataLocation, TagLocation, + CaughtExnRefLocation, NullLocation, ConeReadLocation>; @@ -608,6 +618,12 @@ template<> struct hash<wasm::TagLocation> { } }; +template<> struct hash<wasm::CaughtExnRefLocation> { + size_t operator()(const wasm::CaughtExnRefLocation& loc) const { + return std::hash<const char*>()("caught-exnref-location"); + } +}; + template<> struct hash<wasm::NullLocation> { size_t operator()(const wasm::NullLocation& loc) const { return std::hash<wasm::Type>{}(loc.type); |