summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/passes/CodeFolding.cpp9
-rw-r--r--src/passes/OptimizeInstructions.cpp29
-rw-r--r--src/passes/RemoveUnusedModuleElements.cpp1
-rw-r--r--src/tools/translate-to-fuzz.h88
4 files changed, 113 insertions, 14 deletions
diff --git a/src/passes/CodeFolding.cpp b/src/passes/CodeFolding.cpp
index ae2f81283..047eee452 100644
--- a/src/passes/CodeFolding.cpp
+++ b/src/passes/CodeFolding.cpp
@@ -226,13 +226,17 @@ struct CodeFolding : public WalkerPass<ControlFlowWalker<CodeFolding>> {
// optimize returns at the end, so we can benefit from a fallthrough if there is a value TODO: separate passes for them?
optimizeTerminatingTails(returnTails);
// TODO add fallthrough for returns
- // TODO optimzier returns not in blocks, a big return value can be worth it
+ // TODO optimize returns not in blocks, a big return value can be worth it
// clean up
breakTails.clear();
unreachableTails.clear();
returnTails.clear();
unoptimizables.clear();
modifieds.clear();
+ // if we did any work, types may need to be propagated
+ if (anotherPass) {
+ ReFinalize().walkFunctionInModule(func, getModule());
+ }
}
}
@@ -371,8 +375,7 @@ private:
if (!tail.isFallthrough()) {
tail.block->list.push_back(last);
}
- // the blocks lose their endings, so any values are gone, and the blocks
- // are now either none or unreachable
+ // the block type may change if we removed final values
tail.block->finalize();
}
// since we managed a merge, then it might open up more opportunities later
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 1f2f07110..ad667f888 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -622,16 +622,33 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
std::swap(iff->ifTrue, iff->ifFalse);
}
}
- if (ExpressionAnalyzer::equal(iff->ifTrue, iff->ifFalse)) {
+ if (iff->condition->type != unreachable && ExpressionAnalyzer::equal(iff->ifTrue, iff->ifFalse)) {
// sides are identical, fold
- if (!EffectAnalyzer(getPassOptions(), iff->condition).hasSideEffects()) {
+ // if we can replace the if with one arm, and no side effects in the condition, do that
+ auto needCondition = EffectAnalyzer(getPassOptions(), iff->condition).hasSideEffects();
+ auto typeIsIdentical = iff->ifTrue->type == iff->type;
+ if (typeIsIdentical && !needCondition) {
return iff->ifTrue;
} else {
Builder builder(*getModule());
- return builder.makeSequence(
- builder.makeDrop(iff->condition),
- iff->ifTrue
- );
+ if (typeIsIdentical) {
+ return builder.makeSequence(
+ builder.makeDrop(iff->condition),
+ iff->ifTrue
+ );
+ } else {
+ // the types diff. as the condition is reachable, that means the if must be
+ // concrete while the arm is not
+ assert(isConcreteWasmType(iff->type) && iff->ifTrue->type == unreachable);
+ // emit a block with a forced type
+ auto* ret = builder.makeBlock();
+ if (needCondition) {
+ ret->list.push_back(builder.makeDrop(iff->condition));
+ }
+ ret->list.push_back(iff->ifTrue);
+ ret->finalize(iff->type);
+ return ret;
+ }
}
}
}
diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp
index 259c5b942..d9f6a7978 100644
--- a/src/passes/RemoveUnusedModuleElements.cpp
+++ b/src/passes/RemoveUnusedModuleElements.cpp
@@ -185,6 +185,7 @@ struct RemoveUnusedModuleElements : public Pass {
std::unordered_map<std::string, FunctionType*> canonicals;
std::unordered_set<FunctionType*> needed;
auto canonicalize = [&](Name name) {
+ if (!name.is()) return name;
FunctionType* type = module->getFunctionType(name);
auto sig = getSig(type);
auto iter = canonicals.find(sig);
diff --git a/src/tools/translate-to-fuzz.h b/src/tools/translate-to-fuzz.h
index 74a457013..0d2eb47f3 100644
--- a/src/tools/translate-to-fuzz.h
+++ b/src/tools/translate-to-fuzz.h
@@ -19,7 +19,15 @@
// This is helpful for fuzzing.
//
+/*
+memory too
+high chance for set at start of loop
+ high chance of get of a set local in the scope of that scope
+ high chance of a tee in that case => loop var
+*/
+
#include <wasm-builder.h>
+#include <ast/literal-utils.h>
namespace wasm {
@@ -132,6 +140,7 @@ private:
void build() {
setupMemory();
+ setupTable();
// keep adding functions until we run out of input
while (!finishedInput) {
addFunction();
@@ -142,6 +151,7 @@ private:
if (DE_NAN) {
addDeNanSupport();
}
+ finalizeTable();
}
void setupMemory() {
@@ -150,6 +160,16 @@ private:
wasm.memory.initial = wasm.memory.max = 1;
}
+ void setupTable() {
+ wasm.table.exists = true;
+ wasm.table.segments.emplace_back(builder.makeConst(Literal(int32_t(0))));
+ }
+
+ void finalizeTable() {
+ wasm.table.initial = wasm.table.segments[0].data.size();
+ wasm.table.max = oneIn(2) ? Address(Table::kMaxSize) : wasm.table.initial;
+ }
+
const Name HANG_LIMIT_GLOBAL = "hangLimit";
void addHangLimitSupport() {
@@ -282,12 +302,17 @@ private:
// export some, but not all (to allow inlining etc.). make sure to
// export at least one, though, to keep each testcase interesting
if (num == 0 || oneIn(2)) {
+ func->type = ensureFunctionType(getSig(func), &wasm)->name;
auto* export_ = new Export;
export_->name = func->name;
export_->value = func->name;
export_->kind = ExternalKind::Function;
wasm.addExport(export_);
}
+ // add some to the table
+ while (oneIn(3)) {
+ wasm.table.segments[0].data.push_back(func->name);
+ }
// cleanup
typeLocals.clear();
}
@@ -651,7 +676,41 @@ private:
}
Expression* makeCallIndirect(WasmType type) {
- return make(type); // TODO
+ auto& data = wasm.table.segments[0].data;
+ if (data.empty()) return make(type);
+ // look for a call target with the right type
+ Index start = upTo(data.size());
+ Index i = start;
+ Function* func;
+ while (1) {
+ // TODO: handle unreachable
+ func = wasm.getFunction(data[i]);
+ if (func->result == type) {
+ break;
+ }
+ i++;
+ if (i == data.size()) i = 0;
+ if (i == start) return make(type);
+ }
+ // with high probability, make sure the type is valid otherwise, most are
+ // going to trap
+ Expression* target;
+ if (!oneIn(10)) {
+ target = builder.makeConst(Literal(int32_t(i)));
+ } else {
+ target = make(i32);
+ }
+ std::vector<Expression*> args;
+ for (auto type : func->params) {
+ args.push_back(make(type));
+ }
+ func->type = ensureFunctionType(getSig(func), &wasm)->name;
+ return builder.makeCallIndirect(
+ func->type,
+ target,
+ args,
+ func->result
+ );
}
Expression* makeGetLocal(WasmType type) {
@@ -775,7 +834,7 @@ private:
Expression* makeConst(WasmType type) {
Literal value;
- switch (upTo(3)) {
+ switch (upTo(4)) {
case 0: {
// totally random, entire range
switch (type) {
@@ -789,12 +848,14 @@ private:
}
case 1: {
// small range
- int32_t small;
- switch (upTo(4)) {
+ int64_t small;
+ switch (upTo(6)) {
case 0: small = int8_t(get()); break;
case 1: small = uint8_t(get()); break;
case 2: small = int16_t(get16()); break;
case 3: small = uint16_t(get16()); break;
+ case 4: small = int32_t(get32()); break;
+ case 5: small = uint32_t(get32()); break;
default: WASM_UNREACHABLE();
}
switch (type) {
@@ -840,8 +901,26 @@ private:
std::numeric_limits<uint64_t>::max())); break;
default: WASM_UNREACHABLE();
}
+ // tweak around special values
+ if (oneIn(3)) {
+ value = value.add(LiteralUtils::makeLiteralFromInt32(upTo(3) - 1, type));
+ }
break;
}
+ case 3: {
+ // powers of 2
+ switch (type) {
+ case i32: value = Literal(int32_t(1) << upTo(32)); break;
+ case i64: value = Literal(int64_t(1) << upTo(64)); break;
+ case f32: value = Literal(float(int64_t(1) << upTo(64))); break;
+ case f64: value = Literal(double(int64_t(1) << upTo(64))); break;
+ default: WASM_UNREACHABLE();
+ }
+ // maybe negative
+ if (oneIn(2)) {
+ value = value.mul(LiteralUtils::makeLiteralFromInt32(-1, type));
+ }
+ }
}
auto* ret = wasm.allocator.alloc<Const>();
ret->value = value;
@@ -1069,7 +1148,6 @@ private:
// pick from a vector
template<typename T>
const T& vectorPick(const std::vector<T>& vec) {
- // TODO: get32?
assert(!vec.empty());
auto index = upTo(vec.size());
return vec[index];