summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/PostEmscripten.cpp81
-rw-r--r--test/passes/post-emscripten.txt89
-rw-r--r--test/passes/post-emscripten.wast71
3 files changed, 238 insertions, 3 deletions
diff --git a/src/passes/PostEmscripten.cpp b/src/passes/PostEmscripten.cpp
index 55ffd6e0c..9d23a299c 100644
--- a/src/passes/PostEmscripten.cpp
+++ b/src/passes/PostEmscripten.cpp
@@ -23,6 +23,8 @@
#include <ir/import-utils.h>
#include <ir/localize.h>
#include <ir/memory-utils.h>
+#include <ir/module-utils.h>
+#include <ir/table-utils.h>
#include <pass.h>
#include <shared-constants.h>
#include <wasm-builder.h>
@@ -32,6 +34,8 @@ namespace wasm {
namespace {
+static bool isInvoke(Name name) { return name.startsWith("invoke_"); }
+
struct OptimizeCalls : public WalkerPass<PostWalker<OptimizeCalls>> {
bool isFunctionParallel() override { return true; }
@@ -106,6 +110,83 @@ struct PostEmscripten : public Pass {
// Optimize calls
OptimizeCalls().run(runner, module);
+
+ // Optimize exceptions
+ optimizeExceptions(runner, module);
+ }
+
+ // Optimize exceptions (and setjmp) by removing unnecessary invoke* calls.
+ // An invoke is a call to JS with a function pointer; JS does a try-catch
+ // and calls the pointer, catching and reporting any error. If we know no
+ // exception will be thrown, we can simply skip the invoke.
+ void optimizeExceptions(PassRunner* runner, Module* module) {
+ // First, check if this code even uses invokes.
+ bool hasInvokes = false;
+ for (auto& imp : module->functions) {
+ if (imp->imported() && imp->module == ENV && isInvoke(imp->base)) {
+ hasInvokes = true;
+ }
+ }
+ if (!hasInvokes) {
+ return;
+ }
+ // Next, see if the Table is flat, which we need in order to see where
+ // invokes go statically. (In dynamic linking, the table is not flat,
+ // and we can't do this.)
+ FlatTable flatTable(module->table);
+ if (!flatTable.valid) {
+ return;
+ }
+ // This code has exceptions. Find functions that definitely cannot throw,
+ // and remove invokes to them.
+ struct Info
+ : public ModuleUtils::CallGraphPropertyAnalysis<Info>::FunctionInfo {
+ bool canThrow = false;
+ };
+ ModuleUtils::CallGraphPropertyAnalysis<Info> analyzer(
+ *module, [&](Function* func, Info& info) {
+ if (func->imported()) {
+ // Assume any import can throw. We may want to reduce this to just
+ // longjmp/cxa_throw/etc.
+ info.canThrow = true;
+ }
+ });
+
+ analyzer.propagateBack([](const Info& info) { return info.canThrow; },
+ [](const Info& info) { return true; },
+ [](Info& info) { info.canThrow = true; });
+
+ // Apply the information.
+ struct OptimizeInvokes : public WalkerPass<PostWalker<OptimizeInvokes>> {
+ bool isFunctionParallel() override { return true; }
+
+ Pass* create() override { return new OptimizeInvokes(map, flatTable); }
+
+ std::map<Function*, Info>& map;
+ FlatTable& flatTable;
+
+ OptimizeInvokes(std::map<Function*, Info>& map, FlatTable& flatTable)
+ : map(map), flatTable(flatTable) {}
+
+ void visitCall(Call* curr) {
+ if (isInvoke(curr->target)) {
+ // The first operand is the function pointer index, which must be
+ // constant if we are to optimize it statically.
+ if (auto* index = curr->operands[0]->dynCast<Const>()) {
+ auto actualTarget = flatTable.names.at(index->value.geti32());
+ if (!map[getModule()->getFunction(actualTarget)].canThrow) {
+ // This invoke cannot throw! Make it a direct call.
+ curr->target = actualTarget;
+ for (Index i = 0; i < curr->operands.size() - 1; i++) {
+ curr->operands[i] = curr->operands[i + 1];
+ }
+ curr->operands.resize(curr->operands.size() - 1);
+ }
+ }
+ }
+ }
+ };
+ OptimizeInvokes(analyzer.map, flatTable).run(runner, module);
}
};
diff --git a/test/passes/post-emscripten.txt b/test/passes/post-emscripten.txt
index c6b53e43c..ee4eb4976 100644
--- a/test/passes/post-emscripten.txt
+++ b/test/passes/post-emscripten.txt
@@ -1,10 +1,15 @@
(module
(type $0 (func (param i32)))
(type $FUNCSIG$ddd (func (param f64 f64) (result f64)))
+ (type $FUNCSIG$viif (func (param i32 i32 f32)))
(type $FUNCSIG$v (func))
+ (type $FUNCSIG$vif (func (param i32 f32)))
(import "global.Math" "pow" (func $Math_pow (param f64 f64) (result f64)))
+ (import "env" "invoke_vif" (func $invoke_vif (param i32 i32 f32)))
(memory $0 256 256)
- (func $pow2 (; 1 ;) (type $FUNCSIG$v)
+ (table $0 7 7 funcref)
+ (elem (i32.const 0) $pow2 $pow.2 $exc $other_safe $other_unsafe $deep_safe $deep_unsafe)
+ (func $pow2 (; 2 ;) (type $FUNCSIG$v)
(local $x f64)
(local $y f64)
(local $2 f64)
@@ -55,7 +60,7 @@
)
)
)
- (func $pow.2 (; 2 ;) (type $FUNCSIG$v)
+ (func $pow.2 (; 3 ;) (type $FUNCSIG$v)
(drop
(f64.sqrt
(f64.const 1)
@@ -68,4 +73,84 @@
)
)
)
+ (func $exc (; 4 ;) (type $FUNCSIG$v)
+ (call $other_safe
+ (i32.const 42)
+ (f32.const 3.141590118408203)
+ )
+ (call $invoke_vif
+ (i32.const 4)
+ (i32.const 55)
+ (f32.const 2.1828181743621826)
+ )
+ (call $deep_safe
+ (i32.const 100)
+ (f32.const 1.1109999418258667)
+ )
+ (call $invoke_vif
+ (i32.const 6)
+ (i32.const 999)
+ (f32.const 1.4140000343322754)
+ )
+ (call $invoke_vif
+ (i32.add
+ (i32.const 1)
+ (i32.const 1)
+ )
+ (i32.const 42)
+ (f32.const 3.141590118408203)
+ )
+ )
+ (func $other_safe (; 5 ;) (type $FUNCSIG$vif) (param $0 i32) (param $1 f32)
+ (nop)
+ )
+ (func $other_unsafe (; 6 ;) (type $FUNCSIG$vif) (param $0 i32) (param $1 f32)
+ (drop
+ (call $Math_pow
+ (f64.const 1)
+ (f64.const 3)
+ )
+ )
+ )
+ (func $deep_safe (; 7 ;) (type $FUNCSIG$vif) (param $0 i32) (param $1 f32)
+ (call $other_safe
+ (unreachable)
+ (unreachable)
+ )
+ )
+ (func $deep_unsafe (; 8 ;) (type $FUNCSIG$vif) (param $0 i32) (param $1 f32)
+ (call $other_unsafe
+ (unreachable)
+ (unreachable)
+ )
+ )
+)
+(module
+ (type $FUNCSIG$v (func))
+ (func $call (; 0 ;) (type $FUNCSIG$v)
+ (call $call)
+ )
+)
+(module
+ (type $0 (func (param i32)))
+ (type $FUNCSIG$ddd (func (param f64 f64) (result f64)))
+ (type $FUNCSIG$viif (func (param i32 i32 f32)))
+ (type $FUNCSIG$v (func))
+ (type $FUNCSIG$vif (func (param i32 f32)))
+ (import "env" "glob" (global $glob i32))
+ (import "global.Math" "pow" (func $Math_pow (param f64 f64) (result f64)))
+ (import "env" "invoke_vif" (func $invoke_vif (param i32 i32 f32)))
+ (memory $0 256 256)
+ (table $0 7 7 funcref)
+ (elem (global.get $glob) $other_safe)
+ (func $exc (; 2 ;) (type $FUNCSIG$v)
+ (call $invoke_vif
+ (i32.const 3)
+ (i32.const 42)
+ (f32.const 3.141590118408203)
+ )
+ )
+ (func $other_safe (; 3 ;) (type $FUNCSIG$vif) (param $0 i32) (param $1 f32)
+ (nop)
+ )
)
diff --git a/test/passes/post-emscripten.wast b/test/passes/post-emscripten.wast
index f3656eebd..fe60858f7 100644
--- a/test/passes/post-emscripten.wast
+++ b/test/passes/post-emscripten.wast
@@ -1,7 +1,10 @@
(module
- (memory 256 256)
(type $0 (func (param i32)))
(import "global.Math" "pow" (func $Math_pow (param f64 f64) (result f64)))
+ (import "env" "invoke_vif" (func $invoke_vif (param i32 i32 f32)))
+ (memory 256 256)
+ (table 7 7 funcref)
+ (elem (i32.const 0) $pow2 $pow.2 $exc $other_safe $other_unsafe $deep_safe $deep_unsafe)
(func $pow2
(local $x f64)
(local $y f64)
@@ -57,4 +60,70 @@
)
)
)
+ (func $exc
+ (call $invoke_vif
+ (i32.const 3) ;; other_safe()
+ (i32.const 42)
+ (f32.const 3.14159)
+ )
+ (call $invoke_vif
+ (i32.const 4) ;; other_unsafe()
+ (i32.const 55)
+ (f32.const 2.18281828)
+ )
+ (call $invoke_vif
+ (i32.const 5) ;; deep_safe()
+ (i32.const 100)
+ (f32.const 1.111)
+ )
+ (call $invoke_vif
+ (i32.const 6) ;; deep_unsafe()
+ (i32.const 999)
+ (f32.const 1.414)
+ )
+ (call $invoke_vif
+ (i32.add (i32.const 1) (i32.const 1)) ;; nonconstant
+ (i32.const 42)
+ (f32.const 3.14159)
+ )
+ )
+ (func $other_safe (param i32) (param f32)
+ )
+ (func $other_unsafe (param i32) (param f32)
+ (drop
+ (call $Math_pow
+ (f64.const 1)
+ (f64.const 3)
+ )
+ )
+ )
+ (func $deep_safe (param i32) (param f32)
+ (call $other_safe (unreachable) (unreachable))
+ )
+ (func $deep_unsafe (param i32) (param f32)
+ (call $other_unsafe (unreachable) (unreachable))
+ )
+)
+(module ;; no invokes
+ (func $call
+ (call $call)
+ )
+)
+(module
+ (type $0 (func (param i32)))
+ (import "global.Math" "pow" (func $Math_pow (param f64 f64) (result f64)))
+ (import "env" "invoke_vif" (func $invoke_vif (param i32 i32 f32)))
+ (import "env" "glob" (global $glob i32)) ;; non-constant table offset
+ (memory 256 256)
+ (table 7 7 funcref)
+ (elem (global.get $glob) $other_safe)
+ (func $exc
+ (call $invoke_vif
+ (i32.const 3) ;; other_safe()
+ (i32.const 42)
+ (f32.const 3.14159)
+ )
+ )
+ (func $other_safe (param i32) (param f32)
+ )
)