diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/CodeFolding.cpp | 9 | ||||
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 29 | ||||
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 1 | ||||
-rw-r--r-- | src/tools/translate-to-fuzz.h | 88 |
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]; |