outlining eval pass
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index bd1dd85..25750bb 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -45,6 +45,7 @@
   GlobalTypeOptimization.cpp
   GUFA.cpp
   hash-stringify-walker.cpp
+  outlining.cpp
   Heap2Local.cpp
   I64ToI32Lowering.cpp
   Inlining.cpp
diff --git a/src/passes/hash-stringify-walker.cpp b/src/passes/hash-stringify-walker.cpp
index abcd071..fc60727 100644
--- a/src/passes/hash-stringify-walker.cpp
+++ b/src/passes/hash-stringify-walker.cpp
@@ -70,6 +70,25 @@
   }
 }
 
+std::vector<SuffixTree::RepeatedSubstring>
+StringifyProcessor::repeatSubstrings(const std::vector<uint32_t>& hashString) {
+  SuffixTree st(hashString);
+  std::vector<SuffixTree::RepeatedSubstring> substrings =
+    std::vector(st.begin(), st.end());
+  std::sort(
+    substrings.begin(),
+    substrings.end(),
+    [](SuffixTree::RepeatedSubstring a, SuffixTree::RepeatedSubstring b) {
+      size_t aWeight = a.Length * a.StartIndices.size();
+      size_t bWeight = b.Length * b.StartIndices.size();
+      if (aWeight == bWeight) {
+        return a.StartIndices[0] < b.StartIndices[0];
+      }
+      return aWeight > bWeight;
+    });
+  return substrings;
+}
+
 // Deduplicate substrings by iterating through the list of substrings, keeping
 // only those whose list of end indices is disjoint from the set of end indices
 // for all substrings kept so far. Substrings that are contained within other
@@ -112,7 +131,7 @@
 
 std::vector<SuffixTree::RepeatedSubstring> StringifyProcessor::filter(
   const std::vector<SuffixTree::RepeatedSubstring>& substrings,
-  const std::vector<Expression*> exprs,
+  const std::vector<Expression*>& exprs,
   std::function<bool(const Expression*)> condition) {
 
   struct FilterStringifyWalker : public StringifyWalker<FilterStringifyWalker> {
@@ -168,7 +187,7 @@
 
 std::vector<SuffixTree::RepeatedSubstring> StringifyProcessor::filterLocalSets(
   const std::vector<SuffixTree::RepeatedSubstring>& substrings,
-  const std::vector<Expression*> exprs) {
+  const std::vector<Expression*>& exprs) {
   return StringifyProcessor::filter(
     substrings, exprs, [](const Expression* curr) {
       return curr->is<LocalSet>();
@@ -177,10 +196,11 @@
 
 std::vector<SuffixTree::RepeatedSubstring> StringifyProcessor::filterBranches(
   const std::vector<SuffixTree::RepeatedSubstring>& substrings,
-  const std::vector<Expression*> exprs) {
+  const std::vector<Expression*>& exprs) {
   return StringifyProcessor::filter(
     substrings, exprs, [](const Expression* curr) {
       return Properties::isBranch(curr) || curr->is<Return>();
     });
 }
+
 } // namespace wasm
diff --git a/src/passes/outlining.cpp b/src/passes/outlining.cpp
new file mode 100644
index 0000000..30433e4
--- /dev/null
+++ b/src/passes/outlining.cpp
@@ -0,0 +1,42 @@
+#include "ir/utils.h"
+#include "pass.h"
+#include "passes/stringify-walker.h"
+#include "support/suffix_tree.h"
+#include "wasm.h"
+
+namespace wasm {
+
+struct OutliningEval : public Pass {
+  void run(Module* module) {
+    HashStringifyWalker stringify = HashStringifyWalker();
+    stringify.walkModule(module);
+    auto substrings =
+      StringifyProcessor::repeatSubstrings(stringify.hashString);
+    auto result = StringifyProcessor::dedupe(substrings);
+    result = StringifyProcessor::filterBranches(result, stringify.exprs);
+    result = StringifyProcessor::filterLocalSets(result, stringify.exprs);
+    printStats(substrings, result, &stringify);
+  }
+
+  void printStats(std::vector<SuffixTree::RepeatedSubstring> substrings,
+                  std::vector<SuffixTree::RepeatedSubstring> result,
+                  HashStringifyWalker* stringify) {
+    std::cout << substrings.size() << " substrings found, ";
+    std::cout << "reduced to " << result.size() << std::endl;
+    for (auto rs : result) {
+      size_t startIndex = rs.StartIndices[0];
+      std::cout << rs.StartIndices.size() << "x, ~"
+                << rs.StartIndices.size() * rs.Length * Measurer::BytesPerExpr
+                << " size:";
+      for (size_t i = startIndex; i < startIndex + rs.Length; i++) {
+        std::cout << stringify->hashString[i] << " ("
+                  << ShallowExpression{stringify->exprs[i]} << "), ";
+      }
+      std::cout << std::endl;
+    }
+  }
+};
+
+Pass* createOutliningEvalPass() { return new OutliningEval(); }
+
+} // namespace wasm
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index 35085c2..804b0fc 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -311,6 +311,9 @@
                createOptimizeInstructionsPass);
   registerPass(
     "optimize-stack-ir", "optimize Stack IR", createOptimizeStackIRPass);
+  registerPass("outlining-eval",
+               "evaluate outlining feasibility",
+               createOutliningEvalPass);
   registerPass("pick-load-signs",
                "pick load signs based on their uses",
                createPickLoadSignsPass);
diff --git a/src/passes/passes.h b/src/passes/passes.h
index 2bace5b..c94d398 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -99,6 +99,7 @@
 Pass* createOptimizeCastsPass();
 Pass* createOptimizeForJSPass();
 Pass* createOptimizeStackIRPass();
+Pass* createOutliningEvalPass();
 Pass* createPickLoadSignsPass();
 Pass* createModAsyncifyAlwaysOnlyUnwindPass();
 Pass* createModAsyncifyNeverUnwindPass();
diff --git a/src/passes/stringify-walker.h b/src/passes/stringify-walker.h
index 6095eb7..d2e5134 100644
--- a/src/passes/stringify-walker.h
+++ b/src/passes/stringify-walker.h
@@ -234,20 +234,22 @@
 // Functions that filter vectors of SuffixTree::RepeatedSubstring
 struct StringifyProcessor {
   static std::vector<SuffixTree::RepeatedSubstring>
+  repeatSubstrings(const std::vector<uint32_t>& hashString);
+  static std::vector<SuffixTree::RepeatedSubstring>
   dedupe(const std::vector<SuffixTree::RepeatedSubstring>& substrings);
   // Filter is the general purpose function backing subsequent filter functions.
   // It can be used directly, but generally prefer a wrapper function
   // to encapsulate your condition and make it available for tests
   static std::vector<SuffixTree::RepeatedSubstring>
   filter(const std::vector<SuffixTree::RepeatedSubstring>& substrings,
-         const std::vector<Expression*> exprs,
+         const std::vector<Expression*>& exprs,
          std::function<bool(const Expression*)> condition);
   static std::vector<SuffixTree::RepeatedSubstring>
   filterLocalSets(const std::vector<SuffixTree::RepeatedSubstring>& substrings,
-                  const std::vector<Expression*> exprs);
+                  const std::vector<Expression*>& exprs);
   static std::vector<SuffixTree::RepeatedSubstring>
   filterBranches(const std::vector<SuffixTree::RepeatedSubstring>& substrings,
-                 const std::vector<Expression*> exprs);
+                 const std::vector<Expression*>& exprs);
 };
 
 } // namespace wasm
diff --git a/test/gtest/stringify.cpp b/test/gtest/stringify.cpp
index 8e854ee..59b7db6 100644
--- a/test/gtest/stringify.cpp
+++ b/test/gtest/stringify.cpp
@@ -250,29 +250,11 @@
             }));
 }
 
-std::vector<SuffixTree::RepeatedSubstring>
-repeatSubstrings(std::vector<uint32_t> hashString) {
-  SuffixTree st(hashString);
-  std::vector<SuffixTree::RepeatedSubstring> substrings(st.begin(), st.end());
-  std::sort(
-    substrings.begin(),
-    substrings.end(),
-    [](SuffixTree::RepeatedSubstring a, SuffixTree::RepeatedSubstring b) {
-      size_t aWeight = a.Length * a.StartIndices.size();
-      size_t bWeight = b.Length * b.StartIndices.size();
-      if (aWeight == bWeight) {
-        return a.StartIndices[0] < b.StartIndices[0];
-      }
-      return aWeight > bWeight;
-    });
-  return substrings;
-}
-
 TEST_F(StringifyTest, Substrings) {
   Module wasm;
   parseWast(wasm, dupModuleText);
   auto hashString = hashStringifyModule(&wasm);
-  auto substrings = repeatSubstrings(hashString);
+  auto substrings = StringifyProcessor::repeatSubstrings(hashString);
 
   EXPECT_EQ(
     substrings,
@@ -294,7 +276,7 @@
   parseWast(wasm, dupModuleText);
   auto hashString = hashStringifyModule(&wasm);
   std::vector<SuffixTree::RepeatedSubstring> substrings =
-    repeatSubstrings(hashString);
+    StringifyProcessor::repeatSubstrings(hashString);
   auto result = StringifyProcessor::dedupe(substrings);
 
   EXPECT_EQ(
@@ -332,10 +314,9 @@
   parseWast(wasm, localSetModuleText);
   HashStringifyWalker stringify = HashStringifyWalker();
   stringify.walkModule(&wasm);
-  auto substrings = repeatSubstrings(stringify.hashString);
-  auto result = StringifyProcessor::dedupe(substrings);
-
-  result = StringifyProcessor::filterLocalSets(substrings, stringify.exprs);
+  auto substrings = StringifyProcessor::repeatSubstrings(stringify.hashString);
+  auto result =
+    StringifyProcessor::filterLocalSets(substrings, stringify.exprs);
 
   EXPECT_EQ(
     result,
@@ -375,8 +356,7 @@
   parseWast(wasm, branchesModuleText);
   HashStringifyWalker stringify = HashStringifyWalker();
   stringify.walkModule(&wasm);
-
-  auto substrings = repeatSubstrings(stringify.hashString);
+  auto substrings = StringifyProcessor::repeatSubstrings(stringify.hashString);
   auto result = StringifyProcessor::filterBranches(substrings, stringify.exprs);
 
   EXPECT_EQ(