summaryrefslogtreecommitdiff
path: root/src/ir
diff options
context:
space:
mode:
Diffstat (limited to 'src/ir')
-rw-r--r--src/ir/ReFinalize.cpp7
-rw-r--r--src/ir/effects.h26
-rw-r--r--src/ir/linear-execution.h6
-rw-r--r--src/ir/possible-contents.cpp34
-rw-r--r--src/ir/possible-contents.h16
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);