diff --git a/src/passes/Directize.cpp b/src/passes/Directize.cpp index 38f5df3e34f..c0cd9e221ab 100644 --- a/src/passes/Directize.cpp +++ b/src/passes/Directize.cpp @@ -31,6 +31,7 @@ #include "call-utils.h" #include "ir/drop.h" +#include "ir/eh-utils.h" #include "ir/find_all.h" #include "ir/table-utils.h" #include "ir/utils.h" @@ -52,6 +53,8 @@ struct FunctionDirectizer : public WalkerPass> { FunctionDirectizer(const TableUtils::TableInfoMap& tables) : tables(tables) {} + bool optimized = false; + void visitCallIndirect(CallIndirect* curr) { auto& table = tables.at(curr->table); if (!table.canOptimizeByEntry()) { @@ -62,6 +65,7 @@ struct FunctionDirectizer : public WalkerPass> { std::vector operands(curr->operands.begin(), curr->operands.end()); makeDirectCall(operands, curr->target, table, curr); + optimized = true; return; } @@ -74,6 +78,7 @@ struct FunctionDirectizer : public WalkerPass> { *getFunction(), *getModule())) { replaceCurrent(calls); + optimized = true; // Note that types may have changed, as the utility here can add locals // which require fixups if they are non-nullable, for example. changedTypes = true; @@ -81,8 +86,17 @@ struct FunctionDirectizer : public WalkerPass> { } } + bool hasTry = false; + + void visitTry(Try* curr) { hasTry = true; } + void doWalkFunction(Function* func) { WalkerPass>::doWalkFunction(func); + + if (optimized && hasTry) { + EHUtils::handleBlockNestedPops(func, *getModule()); + } + if (changedTypes) { ReFinalize().walkFunctionInModule(func, getModule()); } diff --git a/test/lit/passes/directize_all-features.wast b/test/lit/passes/directize_all-features.wast index 6957cf6d158..03ff576245d 100644 --- a/test/lit/passes/directize_all-features.wast +++ b/test/lit/passes/directize_all-features.wast @@ -1867,3 +1867,89 @@ ) ) ) + +(module + ;; CHECK: (type $0 (func (param i32))) + + ;; CHECK: (type $ii (func (param i32) (result i32))) + ;; IMMUT: (type $0 (func (param i32))) + + ;; IMMUT: (type $ii (func (param i32) (result i32))) + (type $ii (func (param i32) (result i32))) + + ;; CHECK: (table $0 10 10 funcref) + + ;; CHECK: (elem $0 (i32.const 0) $0) + + ;; CHECK: (tag $e (type $0) (param i32)) + ;; IMMUT: (table $0 10 10 funcref) + + ;; IMMUT: (elem $0 (i32.const 0) $0) + + ;; IMMUT: (tag $e (type $0) (param i32)) + (tag $e (param i32)) + + (table 10 10 funcref) + + (elem (i32.const 0) $0) + + ;; CHECK: (func $0 (type $ii) (param $0 i32) (result i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $e + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; IMMUT: (func $0 (type $ii) (param $0 i32) (result i32) + ;; IMMUT-NEXT: (local $1 i32) + ;; IMMUT-NEXT: (try + ;; IMMUT-NEXT: (do + ;; IMMUT-NEXT: ) + ;; IMMUT-NEXT: (catch $e + ;; IMMUT-NEXT: (local.set $1 + ;; IMMUT-NEXT: (pop i32) + ;; IMMUT-NEXT: ) + ;; IMMUT-NEXT: (drop + ;; IMMUT-NEXT: (block + ;; IMMUT-NEXT: (drop + ;; IMMUT-NEXT: (local.get $1) + ;; IMMUT-NEXT: ) + ;; IMMUT-NEXT: (unreachable) + ;; IMMUT-NEXT: ) + ;; IMMUT-NEXT: ) + ;; IMMUT-NEXT: ) + ;; IMMUT-NEXT: ) + ;; IMMUT-NEXT: (i32.const 42) + ;; IMMUT-NEXT: ) + (func $0 (param i32) (result i32) + (try + (do) + (catch $e + ;; This is out of bounds (100 is far too large), so this will trap. We emit + ;; a block with the pop, and must handle that properly. + (drop + (call_indirect (type $ii) + (pop i32) + (i32.const 100) + ) + ) + ) + ) + (i32.const 42) + ) +) +