summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2017-09-12 11:28:18 -0700
committerGitHub <noreply@github.com>2017-09-12 11:28:18 -0700
commit6977feb0f371c31f35ab8b1304c97dc3ac155d92 (patch)
tree906215a5d915a49ff8a3e96e86f16ed26bf6c117
parent72f72c8b4a005bbd728d7246f87b9d347c2a775f (diff)
downloadbinaryen-6977feb0f371c31f35ab8b1304c97dc3ac155d92.tar.gz
binaryen-6977feb0f371c31f35ab8b1304c97dc3ac155d92.tar.bz2
binaryen-6977feb0f371c31f35ab8b1304c97dc3ac155d92.zip
Const hoisting (#1176)
A pass that hoists repeating constants to a local, and replaces their uses with a get of that local. This can reduce binary size, but can also *increase* gzip size, so it's mostly for experimentation and not used by default.
-rw-r--r--src/ast/ExpressionAnalyzer.cpp2
-rw-r--r--src/literal.h26
-rw-r--r--src/passes/CMakeLists.txt1
-rw-r--r--src/passes/ConstHoisting.cpp131
-rw-r--r--src/passes/DuplicateFunctionElimination.cpp2
-rw-r--r--src/passes/pass.cpp1
-rw-r--r--src/passes/passes.h1
-rw-r--r--src/support/hash.h5
-rw-r--r--src/wasm-binary.h2
-rw-r--r--src/wasm-builder.h6
-rw-r--r--src/wasm/literal.cpp6
-rw-r--r--test/passes/const-hoisting.txt630
-rw-r--r--test/passes/const-hoisting.wast201
13 files changed, 1005 insertions, 9 deletions
diff --git a/src/ast/ExpressionAnalyzer.cpp b/src/ast/ExpressionAnalyzer.cpp
index b8e2ae34c..8a372c2db 100644
--- a/src/ast/ExpressionAnalyzer.cpp
+++ b/src/ast/ExpressionAnalyzer.cpp
@@ -313,7 +313,7 @@ uint32_t ExpressionAnalyzer::hash(Expression* curr) {
digest = rehash(digest, hash);
};
auto hash64 = [&digest](uint64_t hash) {
- digest = rehash(rehash(digest, hash >> 32), uint32_t(hash));
+ digest = rehash(rehash(digest, uint32_t(hash >> 32)), uint32_t(hash));
};
std::vector<Name> nameStack;
diff --git a/src/literal.h b/src/literal.h
index af87e620d..560895e7a 100644
--- a/src/literal.h
+++ b/src/literal.h
@@ -18,6 +18,8 @@
#define wasm_literal_h
#include <iostream>
+
+#include "support/hash.h"
#include "support/utilities.h"
#include "compiler-support.h"
#include "wasm-type.h"
@@ -69,9 +71,9 @@ private:
float reinterpretf32() const { assert(type == WasmType::i32); return bit_cast<float>(i32); }
double reinterpretf64() const { assert(type == WasmType::i64); return bit_cast<double>(i64); }
- int64_t getInteger();
- double getFloat();
- int64_t getBits();
+ int64_t getInteger() const;
+ double getFloat() const;
+ int64_t getBits() const;
bool operator==(const Literal& other) const;
bool operator!=(const Literal& other) const;
@@ -148,4 +150,22 @@ private:
} // namespace wasm
+namespace std {
+template<> struct hash<wasm::Literal> {
+ size_t operator()(const wasm::Literal& a) const {
+ return wasm::rehash(
+ hash<size_t>()(size_t(a.type)),
+ hash<int64_t>()(a.getBits())
+ );
+ }
+};
+template<> struct less<wasm::Literal> {
+ bool operator()(const wasm::Literal& a, const wasm::Literal& b) const {
+ if (a.type < b.type) return true;
+ if (a.type > b.type) return false;
+ return a.getBits() < b.getBits();
+ }
+};
+}
+
#endif // wasm_literal_h
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index edbf945cf..6870259c4 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -3,6 +3,7 @@ SET(passes_SOURCES
CoalesceLocals.cpp
CodePushing.cpp
CodeFolding.cpp
+ ConstHoisting.cpp
DeadCodeElimination.cpp
DuplicateFunctionElimination.cpp
ExtractFunction.cpp
diff --git a/src/passes/ConstHoisting.cpp b/src/passes/ConstHoisting.cpp
new file mode 100644
index 000000000..bddc0b5c5
--- /dev/null
+++ b/src/passes/ConstHoisting.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Hoists repeated constants to a local. A get_local takes 2 bytes
+// in most cases, and if a const is larger than that, it may be
+// better to store it to a local, then get it from that local.
+//
+// WARNING: this often shrinks code size, but can *increase* gzip
+// size. apparently having the constants in their proper
+// places lets them be compressed better, across
+// functions, etc.
+//
+
+#include <map>
+
+#include <wasm.h>
+#include <pass.h>
+#include <wasm-binary.h>
+#include <wasm-builder.h>
+
+namespace wasm {
+
+// with fewer uses than this, it is never beneficial to hoist
+static const Index MIN_USES = 2;
+
+struct ConstHoisting : public WalkerPass<PostWalker<ConstHoisting>> {
+ bool isFunctionParallel() override { return true; }
+
+ Pass* create() override { return new ConstHoisting; }
+
+ std::map<Literal, std::vector<Expression**>> uses;
+
+ void visitConst(Const* curr) {
+ uses[curr->value].push_back(getCurrentPointer());
+ }
+
+ void visitFunction(Function* curr) {
+ std::vector<Expression*> prelude;
+ for (auto& pair : uses) {
+ auto value = pair.first;
+ auto& vec = pair.second;
+ auto num = vec.size();
+ if (worthHoisting(value, num)) {
+ prelude.push_back(hoist(vec));
+ }
+ }
+ if (!prelude.empty()) {
+ Builder builder(*getModule());
+ // merge-blocks can optimize this into a single block later in most cases
+ curr->body = builder.makeSequence(
+ builder.makeBlock(prelude),
+ curr->body
+ );
+ }
+ }
+
+private:
+ bool worthHoisting(Literal value, Index num) {
+ if (num < MIN_USES) return false;
+ // measure the size of the constant
+ Index size;
+ switch (value.type) {
+ case i32: {
+ size = getWrittenSize(S32LEB(value.geti32()));
+ break;
+ }
+ case i64: {
+ size = getWrittenSize(S64LEB(value.geti64()));
+ break;
+ }
+ case f32:
+ case f64: {
+ size = getWasmTypeSize(value.type);
+ break;
+ }
+ default: WASM_UNREACHABLE();
+ }
+ // compute the benefit, of replacing the uses with
+ // one use + a set and then a get for each use
+ // doing the algebra, the criterion here is when
+ // size > 2(1+num)/(num-1)
+ // or
+ // num > (size+2)/(size-2)
+ auto before = num * size;
+ auto after = size + 2 /* set_local */ + (2 /* get_local */ * num);
+ return after < before;
+ }
+
+ template<typename T>
+ Index getWrittenSize(const T& thing) {
+ BufferWithRandomAccess buffer;
+ buffer << thing;
+ return buffer.size();
+ }
+
+ // replace all the uses with gets, for a local set at the top. returns
+ // the set.
+ Expression* hoist(std::vector<Expression**>& vec) {
+ auto type = (*(vec[0]))->type;
+ Builder builder(*getModule());
+ auto temp = builder.addVar(getFunction(), type);
+ auto* ret = builder.makeSetLocal(
+ temp,
+ *(vec[0])
+ );
+ for (auto item : vec) {
+ *item = builder.makeGetLocal(temp, type);
+ }
+ return ret;
+ }
+};
+
+Pass *createConstHoistingPass() {
+ return new ConstHoisting();
+}
+
+} // namespace wasm
diff --git a/src/passes/DuplicateFunctionElimination.cpp b/src/passes/DuplicateFunctionElimination.cpp
index 5d55c7318..a96bd8fdf 100644
--- a/src/passes/DuplicateFunctionElimination.cpp
+++ b/src/passes/DuplicateFunctionElimination.cpp
@@ -56,7 +56,7 @@ private:
digest = rehash(digest, hash);
}
void hash64(uint64_t hash) {
- digest = rehash(rehash(digest, hash >> 32), uint32_t(hash));
+ digest = rehash(rehash(digest, uint32_t(hash >> 32)), uint32_t(hash));
};
};
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index 728fe2371..4ec454a50 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -68,6 +68,7 @@ void PassRegistry::registerPasses() {
registerPass("coalesce-locals-learning", "reduce # of locals by coalescing and learning", createCoalesceLocalsWithLearningPass);
registerPass("code-pushing", "push code forward, potentially making it not always execute", createCodePushingPass);
registerPass("code-folding", "fold code, merging duplicates", createCodeFoldingPass);
+ registerPass("const-hoisting", "hoist repeated constants to a local", createConstHoistingPass);
registerPass("dce", "removes unreachable code", createDeadCodeEliminationPass);
registerPass("duplicate-function-elimination", "removes duplicate functions", createDuplicateFunctionEliminationPass);
registerPass("extract-function", "leaves just one function (useful for debugging)", createExtractFunctionPass);
diff --git a/src/passes/passes.h b/src/passes/passes.h
index b1a6cb5c6..4e039f4bf 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -26,6 +26,7 @@ Pass *createCoalesceLocalsPass();
Pass *createCoalesceLocalsWithLearningPass();
Pass *createCodeFoldingPass();
Pass *createCodePushingPass();
+Pass *createConstHoistingPass();
Pass *createDeadCodeEliminationPass();
Pass *createDuplicateFunctionEliminationPass();
Pass *createExtractFunctionPass();
diff --git a/src/support/hash.h b/src/support/hash.h
index e2d393d25..5ee3b8a3d 100644
--- a/src/support/hash.h
+++ b/src/support/hash.h
@@ -17,6 +17,7 @@
#ifndef wasm_support_hash_h
#define wasm_support_hash_h
+#include <functional>
#include <stdint.h>
namespace wasm {
@@ -34,6 +35,10 @@ inline uint32_t rehash(uint32_t x, uint32_t y) { // see http://www.cse.yorku.ca/
return hash;
}
+inline size_t rehash(uint64_t x, uint64_t y) { // see boost and https://stackoverflow.com/a/2595226/1176841
+ return x ^ (y + 0x9e3779b9 + (x << 6) + (x >> 2));
+}
+
} // namespace wasm
#endif // wasm_support_hash_h
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 256cea75c..677f7ac62 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -142,7 +142,7 @@ class BufferWithRandomAccess : public std::vector<uint8_t> {
bool debug;
public:
- BufferWithRandomAccess(bool debug) : debug(debug) {}
+ BufferWithRandomAccess(bool debug = false) : debug(debug) {}
BufferWithRandomAccess& operator<<(int8_t x) {
if (debug) std::cerr << "writeInt8: " << (int)(uint8_t)x << " (at " << size() << ")" << std::endl;
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index ad404c337..e451f9e2b 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -82,6 +82,12 @@ public:
ret->name = name;
return ret;
}
+ Block* makeBlock(const std::vector<Expression*>& items) {
+ auto* ret = allocator.alloc<Block>();
+ ret->list.set(items);
+ ret->finalize();
+ return ret;
+ }
If* makeIf(Expression* condition, Expression* ifTrue, Expression* ifFalse = nullptr) {
auto* ret = allocator.alloc<If>();
ret->condition = condition; ret->ifTrue = ifTrue; ret->ifFalse = ifFalse;
diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp
index d6d7848d0..be1363dde 100644
--- a/src/wasm/literal.cpp
+++ b/src/wasm/literal.cpp
@@ -53,7 +53,7 @@ Literal Literal::castToI64() {
return ret;
}
-int64_t Literal::getInteger() {
+int64_t Literal::getInteger() const {
switch (type) {
case WasmType::i32: return i32;
case WasmType::i64: return i64;
@@ -61,7 +61,7 @@ int64_t Literal::getInteger() {
}
}
-double Literal::getFloat() {
+double Literal::getFloat() const {
switch (type) {
case WasmType::f32: return getf32();
case WasmType::f64: return getf64();
@@ -69,7 +69,7 @@ double Literal::getFloat() {
}
}
-int64_t Literal::getBits() {
+int64_t Literal::getBits() const {
switch (type) {
case WasmType::i32: case WasmType::f32: return i32;
case WasmType::i64: case WasmType::f64: return i64;
diff --git a/test/passes/const-hoisting.txt b/test/passes/const-hoisting.txt
new file mode 100644
index 000000000..7023a5d6c
--- /dev/null
+++ b/test/passes/const-hoisting.txt
@@ -0,0 +1,630 @@
+(module
+ (type $0 (func))
+ (memory $0 0)
+ (func $10-of-each (type $0)
+ (local $0 i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local $5 i32)
+ (block
+ (set_local $0
+ (i32.const -1048577)
+ )
+ (set_local $1
+ (i32.const -1048576)
+ )
+ (set_local $2
+ (i32.const -8193)
+ )
+ (set_local $3
+ (i32.const 8192)
+ )
+ (set_local $4
+ (i32.const 1048575)
+ )
+ (set_local $5
+ (i32.const 1048576)
+ )
+ )
+ (block
+ (drop
+ (i32.const 0)
+ )
+ (drop
+ (i32.const 63)
+ )
+ (drop
+ (i32.const 64)
+ )
+ (drop
+ (i32.const 8191)
+ )
+ (drop
+ (get_local $3)
+ )
+ (drop
+ (get_local $4)
+ )
+ (drop
+ (get_local $5)
+ )
+ (drop
+ (i32.const -64)
+ )
+ (drop
+ (i32.const -65)
+ )
+ (drop
+ (i32.const -8192)
+ )
+ (drop
+ (get_local $2)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (i32.const 0)
+ )
+ (drop
+ (i32.const 63)
+ )
+ (drop
+ (i32.const 64)
+ )
+ (drop
+ (i32.const 8191)
+ )
+ (drop
+ (get_local $3)
+ )
+ (drop
+ (get_local $4)
+ )
+ (drop
+ (get_local $5)
+ )
+ (drop
+ (i32.const -64)
+ )
+ (drop
+ (i32.const -65)
+ )
+ (drop
+ (i32.const -8192)
+ )
+ (drop
+ (get_local $2)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (i32.const 0)
+ )
+ (drop
+ (i32.const 63)
+ )
+ (drop
+ (i32.const 64)
+ )
+ (drop
+ (i32.const 8191)
+ )
+ (drop
+ (get_local $3)
+ )
+ (drop
+ (get_local $4)
+ )
+ (drop
+ (get_local $5)
+ )
+ (drop
+ (i32.const -64)
+ )
+ (drop
+ (i32.const -65)
+ )
+ (drop
+ (i32.const -8192)
+ )
+ (drop
+ (get_local $2)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (i32.const 0)
+ )
+ (drop
+ (i32.const 63)
+ )
+ (drop
+ (i32.const 64)
+ )
+ (drop
+ (i32.const 8191)
+ )
+ (drop
+ (get_local $3)
+ )
+ (drop
+ (get_local $4)
+ )
+ (drop
+ (get_local $5)
+ )
+ (drop
+ (i32.const -64)
+ )
+ (drop
+ (i32.const -65)
+ )
+ (drop
+ (i32.const -8192)
+ )
+ (drop
+ (get_local $2)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (i32.const 0)
+ )
+ (drop
+ (i32.const 63)
+ )
+ (drop
+ (i32.const 64)
+ )
+ (drop
+ (i32.const 8191)
+ )
+ (drop
+ (get_local $3)
+ )
+ (drop
+ (get_local $4)
+ )
+ (drop
+ (get_local $5)
+ )
+ (drop
+ (i32.const -64)
+ )
+ (drop
+ (i32.const -65)
+ )
+ (drop
+ (i32.const -8192)
+ )
+ (drop
+ (get_local $2)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (i32.const 0)
+ )
+ (drop
+ (i32.const 63)
+ )
+ (drop
+ (i32.const 64)
+ )
+ (drop
+ (i32.const 8191)
+ )
+ (drop
+ (get_local $3)
+ )
+ (drop
+ (get_local $4)
+ )
+ (drop
+ (get_local $5)
+ )
+ (drop
+ (i32.const -64)
+ )
+ (drop
+ (i32.const -65)
+ )
+ (drop
+ (i32.const -8192)
+ )
+ (drop
+ (get_local $2)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (i32.const 0)
+ )
+ (drop
+ (i32.const 63)
+ )
+ (drop
+ (i32.const 64)
+ )
+ (drop
+ (i32.const 8191)
+ )
+ (drop
+ (get_local $3)
+ )
+ (drop
+ (get_local $4)
+ )
+ (drop
+ (get_local $5)
+ )
+ (drop
+ (i32.const -64)
+ )
+ (drop
+ (i32.const -65)
+ )
+ (drop
+ (i32.const -8192)
+ )
+ (drop
+ (get_local $2)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (i32.const 0)
+ )
+ (drop
+ (i32.const 63)
+ )
+ (drop
+ (i32.const 64)
+ )
+ (drop
+ (i32.const 8191)
+ )
+ (drop
+ (get_local $3)
+ )
+ (drop
+ (get_local $4)
+ )
+ (drop
+ (get_local $5)
+ )
+ (drop
+ (i32.const -64)
+ )
+ (drop
+ (i32.const -65)
+ )
+ (drop
+ (i32.const -8192)
+ )
+ (drop
+ (get_local $2)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (i32.const 0)
+ )
+ (drop
+ (i32.const 63)
+ )
+ (drop
+ (i32.const 64)
+ )
+ (drop
+ (i32.const 8191)
+ )
+ (drop
+ (get_local $3)
+ )
+ (drop
+ (get_local $4)
+ )
+ (drop
+ (get_local $5)
+ )
+ (drop
+ (i32.const -64)
+ )
+ (drop
+ (i32.const -65)
+ )
+ (drop
+ (i32.const -8192)
+ )
+ (drop
+ (get_local $2)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (i32.const 0)
+ )
+ (drop
+ (i32.const 63)
+ )
+ (drop
+ (i32.const 64)
+ )
+ (drop
+ (i32.const 8191)
+ )
+ (drop
+ (get_local $3)
+ )
+ (drop
+ (get_local $4)
+ )
+ (drop
+ (get_local $5)
+ )
+ (drop
+ (i32.const -64)
+ )
+ (drop
+ (i32.const -65)
+ )
+ (drop
+ (i32.const -8192)
+ )
+ (drop
+ (get_local $2)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ )
+ )
+ (func $floats-10-times (type $0)
+ (local $0 f32)
+ (local $1 f64)
+ (block
+ (set_local $0
+ (f32.const 0)
+ )
+ (set_local $1
+ (f64.const 0)
+ )
+ )
+ (block
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $1)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $1)
+ )
+ )
+ )
+ (func $too-few (type $0)
+ (drop
+ (i32.const 8192)
+ )
+ (drop
+ (i32.const 8192)
+ )
+ (drop
+ (i32.const 8192)
+ )
+ (drop
+ (i32.const 8192)
+ )
+ (drop
+ (i32.const 8192)
+ )
+ )
+ (func $just-enough (type $0)
+ (local $0 i32)
+ (block
+ (set_local $0
+ (i32.const 8192)
+ )
+ )
+ (block
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $0)
+ )
+ )
+ )
+ (func $too-few-b (type $0)
+ (drop
+ (i32.const 1048576)
+ )
+ (drop
+ (i32.const 1048576)
+ )
+ (drop
+ (i32.const 1048576)
+ )
+ )
+ (func $enough-b (type $0)
+ (local $0 i32)
+ (block
+ (set_local $0
+ (i32.const 1048576)
+ )
+ )
+ (block
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $0)
+ )
+ )
+ )
+ (func $too-few-c (type $0)
+ (drop
+ (f32.const 0)
+ )
+ (drop
+ (f32.const 0)
+ )
+ (drop
+ (f32.const 0)
+ )
+ )
+ (func $enough-c (type $0)
+ (local $0 f32)
+ (block
+ (set_local $0
+ (f32.const 0)
+ )
+ )
+ (block
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $0)
+ )
+ )
+ )
+ (func $too-few-d (type $0)
+ (drop
+ (f64.const 0)
+ )
+ )
+ (func $enough-d (type $0)
+ (local $0 f64)
+ (block
+ (set_local $0
+ (f64.const 0)
+ )
+ )
+ (block
+ (drop
+ (get_local $0)
+ )
+ (drop
+ (get_local $0)
+ )
+ )
+ )
+)
diff --git a/test/passes/const-hoisting.wast b/test/passes/const-hoisting.wast
new file mode 100644
index 000000000..66f79bc14
--- /dev/null
+++ b/test/passes/const-hoisting.wast
@@ -0,0 +1,201 @@
+(module
+ (func $10-of-each
+ (drop (i32.const 0)) ;; 1 byte
+ (drop (i32.const 63)) ;; 1 byte
+ (drop (i32.const 64)) ;; 2 bytes
+ (drop (i32.const 8191)) ;; 2 bytes
+ (drop (i32.const 8192)) ;; 3 bytes
+ (drop (i32.const 1048575)) ;; 3 bytes
+ (drop (i32.const 1048576)) ;; 4 bytes
+ (drop (i32.const -64)) ;; 1 byte
+ (drop (i32.const -65)) ;; 2 bytes
+ (drop (i32.const -8192)) ;; 2 bytes
+ (drop (i32.const -8193)) ;; 3 bytes
+ (drop (i32.const -1048576)) ;; 3 bytes
+ (drop (i32.const -1048577)) ;; 4 bytes
+ (drop (i32.const 0)) ;; 1 byte
+ (drop (i32.const 63)) ;; 1 byte
+ (drop (i32.const 64)) ;; 2 bytes
+ (drop (i32.const 8191)) ;; 2 bytes
+ (drop (i32.const 8192)) ;; 3 bytes
+ (drop (i32.const 1048575)) ;; 3 bytes
+ (drop (i32.const 1048576)) ;; 4 bytes
+ (drop (i32.const -64)) ;; 1 byte
+ (drop (i32.const -65)) ;; 2 bytes
+ (drop (i32.const -8192)) ;; 2 bytes
+ (drop (i32.const -8193)) ;; 3 bytes
+ (drop (i32.const -1048576)) ;; 3 bytes
+ (drop (i32.const -1048577)) ;; 4 bytes
+ (drop (i32.const 0)) ;; 1 byte
+ (drop (i32.const 63)) ;; 1 byte
+ (drop (i32.const 64)) ;; 2 bytes
+ (drop (i32.const 8191)) ;; 2 bytes
+ (drop (i32.const 8192)) ;; 3 bytes
+ (drop (i32.const 1048575)) ;; 3 bytes
+ (drop (i32.const 1048576)) ;; 4 bytes
+ (drop (i32.const -64)) ;; 1 byte
+ (drop (i32.const -65)) ;; 2 bytes
+ (drop (i32.const -8192)) ;; 2 bytes
+ (drop (i32.const -8193)) ;; 3 bytes
+ (drop (i32.const -1048576)) ;; 3 bytes
+ (drop (i32.const -1048577)) ;; 4 bytes
+ (drop (i32.const 0)) ;; 1 byte
+ (drop (i32.const 63)) ;; 1 byte
+ (drop (i32.const 64)) ;; 2 bytes
+ (drop (i32.const 8191)) ;; 2 bytes
+ (drop (i32.const 8192)) ;; 3 bytes
+ (drop (i32.const 1048575)) ;; 3 bytes
+ (drop (i32.const 1048576)) ;; 4 bytes
+ (drop (i32.const -64)) ;; 1 byte
+ (drop (i32.const -65)) ;; 2 bytes
+ (drop (i32.const -8192)) ;; 2 bytes
+ (drop (i32.const -8193)) ;; 3 bytes
+ (drop (i32.const -1048576)) ;; 3 bytes
+ (drop (i32.const -1048577)) ;; 4 bytes
+ (drop (i32.const 0)) ;; 1 byte
+ (drop (i32.const 63)) ;; 1 byte
+ (drop (i32.const 64)) ;; 2 bytes
+ (drop (i32.const 8191)) ;; 2 bytes
+ (drop (i32.const 8192)) ;; 3 bytes
+ (drop (i32.const 1048575)) ;; 3 bytes
+ (drop (i32.const 1048576)) ;; 4 bytes
+ (drop (i32.const -64)) ;; 1 byte
+ (drop (i32.const -65)) ;; 2 bytes
+ (drop (i32.const -8192)) ;; 2 bytes
+ (drop (i32.const -8193)) ;; 3 bytes
+ (drop (i32.const -1048576)) ;; 3 bytes
+ (drop (i32.const -1048577)) ;; 4 bytes
+ (drop (i32.const 0)) ;; 1 byte
+ (drop (i32.const 63)) ;; 1 byte
+ (drop (i32.const 64)) ;; 2 bytes
+ (drop (i32.const 8191)) ;; 2 bytes
+ (drop (i32.const 8192)) ;; 3 bytes
+ (drop (i32.const 1048575)) ;; 3 bytes
+ (drop (i32.const 1048576)) ;; 4 bytes
+ (drop (i32.const -64)) ;; 1 byte
+ (drop (i32.const -65)) ;; 2 bytes
+ (drop (i32.const -8192)) ;; 2 bytes
+ (drop (i32.const -8193)) ;; 3 bytes
+ (drop (i32.const -1048576)) ;; 3 bytes
+ (drop (i32.const -1048577)) ;; 4 bytes
+ (drop (i32.const 0)) ;; 1 byte
+ (drop (i32.const 63)) ;; 1 byte
+ (drop (i32.const 64)) ;; 2 bytes
+ (drop (i32.const 8191)) ;; 2 bytes
+ (drop (i32.const 8192)) ;; 3 bytes
+ (drop (i32.const 1048575)) ;; 3 bytes
+ (drop (i32.const 1048576)) ;; 4 bytes
+ (drop (i32.const -64)) ;; 1 byte
+ (drop (i32.const -65)) ;; 2 bytes
+ (drop (i32.const -8192)) ;; 2 bytes
+ (drop (i32.const -8193)) ;; 3 bytes
+ (drop (i32.const -1048576)) ;; 3 bytes
+ (drop (i32.const -1048577)) ;; 4 bytes
+ (drop (i32.const 0)) ;; 1 byte
+ (drop (i32.const 63)) ;; 1 byte
+ (drop (i32.const 64)) ;; 2 bytes
+ (drop (i32.const 8191)) ;; 2 bytes
+ (drop (i32.const 8192)) ;; 3 bytes
+ (drop (i32.const 1048575)) ;; 3 bytes
+ (drop (i32.const 1048576)) ;; 4 bytes
+ (drop (i32.const -64)) ;; 1 byte
+ (drop (i32.const -65)) ;; 2 bytes
+ (drop (i32.const -8192)) ;; 2 bytes
+ (drop (i32.const -8193)) ;; 3 bytes
+ (drop (i32.const -1048576)) ;; 3 bytes
+ (drop (i32.const -1048577)) ;; 4 bytes
+ (drop (i32.const 0)) ;; 1 byte
+ (drop (i32.const 63)) ;; 1 byte
+ (drop (i32.const 64)) ;; 2 bytes
+ (drop (i32.const 8191)) ;; 2 bytes
+ (drop (i32.const 8192)) ;; 3 bytes
+ (drop (i32.const 1048575)) ;; 3 bytes
+ (drop (i32.const 1048576)) ;; 4 bytes
+ (drop (i32.const -64)) ;; 1 byte
+ (drop (i32.const -65)) ;; 2 bytes
+ (drop (i32.const -8192)) ;; 2 bytes
+ (drop (i32.const -8193)) ;; 3 bytes
+ (drop (i32.const -1048576)) ;; 3 bytes
+ (drop (i32.const -1048577)) ;; 4 bytes
+ (drop (i32.const 0)) ;; 1 byte
+ (drop (i32.const 63)) ;; 1 byte
+ (drop (i32.const 64)) ;; 2 bytes
+ (drop (i32.const 8191)) ;; 2 bytes
+ (drop (i32.const 8192)) ;; 3 bytes
+ (drop (i32.const 1048575)) ;; 3 bytes
+ (drop (i32.const 1048576)) ;; 4 bytes
+ (drop (i32.const -64)) ;; 1 byte
+ (drop (i32.const -65)) ;; 2 bytes
+ (drop (i32.const -8192)) ;; 2 bytes
+ (drop (i32.const -8193)) ;; 3 bytes
+ (drop (i32.const -1048576)) ;; 3 bytes
+ (drop (i32.const -1048577)) ;; 4 bytes
+ )
+ (func $floats-10-times
+ (drop (f32.const 0)) ;; 4 bytes
+ (drop (f64.const 0)) ;; 8 bytes
+ (drop (f32.const 0)) ;; 4 bytes
+ (drop (f64.const 0)) ;; 8 bytes
+ (drop (f32.const 0)) ;; 4 bytes
+ (drop (f64.const 0)) ;; 8 bytes
+ (drop (f32.const 0)) ;; 4 bytes
+ (drop (f64.const 0)) ;; 8 bytes
+ (drop (f32.const 0)) ;; 4 bytes
+ (drop (f64.const 0)) ;; 8 bytes
+ (drop (f32.const 0)) ;; 4 bytes
+ (drop (f64.const 0)) ;; 8 bytes
+ (drop (f32.const 0)) ;; 4 bytes
+ (drop (f64.const 0)) ;; 8 bytes
+ (drop (f32.const 0)) ;; 4 bytes
+ (drop (f64.const 0)) ;; 8 bytes
+ (drop (f32.const 0)) ;; 4 bytes
+ (drop (f64.const 0)) ;; 8 bytes
+ (drop (f32.const 0)) ;; 4 bytes
+ (drop (f64.const 0)) ;; 8 bytes
+ )
+ (func $too-few
+ (drop (i32.const 8192)) ;; 3 bytes, need 6 appearances
+ (drop (i32.const 8192))
+ (drop (i32.const 8192))
+ (drop (i32.const 8192))
+ (drop (i32.const 8192))
+ )
+ (func $just-enough
+ (drop (i32.const 8192)) ;; 3 bytes, need 6 appearances
+ (drop (i32.const 8192))
+ (drop (i32.const 8192))
+ (drop (i32.const 8192))
+ (drop (i32.const 8192))
+ (drop (i32.const 8192))
+ )
+ (func $too-few-b
+ (drop (i32.const 1048576)) ;; 4 bytes, need 4 appearances
+ (drop (i32.const 1048576))
+ (drop (i32.const 1048576))
+ )
+ (func $enough-b
+ (drop (i32.const 1048576)) ;; 4 bytes, need 4 appearances
+ (drop (i32.const 1048576))
+ (drop (i32.const 1048576))
+ (drop (i32.const 1048576))
+ )
+ (func $too-few-c
+ (drop (f32.const 0)) ;; 4 bytes, need 4 appearances
+ (drop (f32.const 0))
+ (drop (f32.const 0))
+ )
+ (func $enough-c
+ (drop (f32.const 0)) ;; 4 bytes, need 4 appearances
+ (drop (f32.const 0))
+ (drop (f32.const 0))
+ (drop (f32.const 0))
+ )
+ (func $too-few-d
+ (drop (f64.const 0)) ;; 8 bytes, need 2 appearances
+ )
+ (func $enough-d
+ (drop (f64.const 0)) ;; 4 bytes, need 4 appearances
+ (drop (f64.const 0))
+ )
+)
+