diff options
Diffstat (limited to 'src/tools')
-rw-r--r-- | src/tools/fuzzing.h | 55 | ||||
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 14 | ||||
-rw-r--r-- | src/tools/wasm-metadce.cpp | 22 | ||||
-rw-r--r-- | src/tools/wasm-reduce.cpp | 157 | ||||
-rw-r--r-- | src/tools/wasm-shell.cpp | 20 |
5 files changed, 138 insertions, 130 deletions
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 1cc3635dc..288fdc23c 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -195,7 +195,7 @@ public: if (allowMemory) { setupMemory(); } - setupTable(); + setupTables(); setupGlobals(); if (wasm.features.hasExceptionHandling()) { setupEvents(); @@ -424,16 +424,23 @@ private: } // TODO(reference-types): allow the fuzzer to create multiple tables - void setupTable() { + void setupTables() { if (wasm.tables.size() > 0) { auto& table = wasm.tables[0]; table->initial = table->max = 0; - table->segments.emplace_back(builder.makeConst(int32_t(0))); + + auto segment = std::make_unique<ElementSegment>( + table->name, builder.makeConst(int32_t(0))); + segment->setName(Name::fromInt(0), false); + wasm.addElementSegment(std::move(segment)); } else { auto table = builder.makeTable( Names::getValidTableName(wasm, "fuzzing_table"), 0, 0); table->hasExplicitName = true; - table->segments.emplace_back(builder.makeConst(int32_t(0))); + auto segment = std::make_unique<ElementSegment>( + table->name, builder.makeConst(int32_t(0))); + segment->setName(Name::fromInt(0), false); + wasm.addElementSegment(std::move(segment)); wasm.addTable(std::move(table)); } } @@ -532,22 +539,23 @@ private: void finalizeTable() { for (auto& table : wasm.tables) { - for (auto& segment : table->segments) { - // If the offset is a global that was imported (which is ok) but no - // longer is (not ok) we need to change that. - if (auto* offset = segment.offset->dynCast<GlobalGet>()) { - if (!wasm.getGlobal(offset->name)->imported()) { - // TODO: the segments must not overlap... - segment.offset = - builder.makeConst(Literal::makeFromInt32(0, Type::i32)); + ModuleUtils::iterTableSegments( + wasm, table->name, [&](ElementSegment* segment) { + // If the offset is a global that was imported (which is ok) but no + // longer is (not ok) we need to change that. + if (auto* offset = segment->offset->dynCast<GlobalGet>()) { + if (!wasm.getGlobal(offset->name)->imported()) { + // TODO: the segments must not overlap... + segment->offset = + builder.makeConst(Literal::makeFromInt32(0, Type::i32)); + } } - } - Address maxOffset = segment.data.size(); - if (auto* offset = segment.offset->dynCast<Const>()) { - maxOffset = maxOffset + offset->value.getInteger(); - } - table->initial = std::max(table->initial, maxOffset); - } + Address maxOffset = segment->data.size(); + if (auto* offset = segment->offset->dynCast<Const>()) { + maxOffset = maxOffset + offset->value.getInteger(); + } + table->initial = std::max(table->initial, maxOffset); + }); table->max = oneIn(2) ? Address(Table::kUnlimitedSize) : table->initial; // Avoid an imported table (which the fuzz harness would need to handle). table->module = table->base = Name(); @@ -713,9 +721,11 @@ private: export_->kind = ExternalKind::Function; wasm.addExport(export_); } - // add some to the table + // add some to an elem segment while (oneIn(3) && !finishedInput) { - wasm.tables[0]->segments[0].data.push_back(func->name); + auto& randomElem = + wasm.elementSegments[upTo(wasm.elementSegments.size())]; + randomElem->data.push_back(func->name); } numAddedFunctions++; return func; @@ -1435,7 +1445,8 @@ private: } Expression* makeCallIndirect(Type type) { - auto& data = wasm.tables[0]->segments[0].data; + auto& randomElem = wasm.elementSegments[upTo(wasm.elementSegments.size())]; + auto& data = randomElem->data; if (data.empty()) { return make(type); } diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 9e3915073..f39d8a34e 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -231,23 +231,27 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { // we assume the table is not modified (hmm) // look through the segments, try to find the function - for (auto& segment : table->segments) { + for (auto& segment : wasm->elementSegments) { + if (segment->table != tableName) { + continue; + } + Index start; // look for the index in this segment. if it has a constant offset, we // look in the proper range. if it instead gets a global, we rely on the // fact that when not dynamically linking then the table is loaded at // offset 0. - if (auto* c = segment.offset->dynCast<Const>()) { + if (auto* c = segment->offset->dynCast<Const>()) { start = c->value.getInteger(); - } else if (segment.offset->is<GlobalGet>()) { + } else if (segment->offset->is<GlobalGet>()) { start = 0; } else { // wasm spec only allows const and global.get there WASM_UNREACHABLE("invalid expr type"); } - auto end = start + segment.data.size(); + auto end = start + segment->data.size(); if (start <= index && index < end) { - auto name = segment.data[index - start]; + auto name = segment->data[index - start]; // if this is one of our functions, we can call it; if it was imported, // fail auto* func = wasm->getFunction(name); diff --git a/src/tools/wasm-metadce.cpp b/src/tools/wasm-metadce.cpp index 46b66c986..71ea2f693 100644 --- a/src/tools/wasm-metadce.cpp +++ b/src/tools/wasm-metadce.cpp @@ -213,20 +213,18 @@ struct MetaDCEGraph { // we can't remove segments, so root what they need InitScanner rooter(this, Name()); rooter.setModule(&wasm); - for (auto& table : wasm.tables) { - for (auto& segment : table->segments) { - // TODO: currently, all functions in the table are roots, but we - // should add an option to refine that - for (auto& name : segment.data) { - if (!wasm.getFunction(name)->imported()) { - roots.insert(functionToDCENode[name]); - } else { - roots.insert(importIdToDCENode[getFunctionImportId(name)]); - } + ModuleUtils::iterActiveElementSegments(wasm, [&](ElementSegment* segment) { + // TODO: currently, all functions in the table are roots, but we + // should add an option to refine that + for (auto& name : segment->data) { + if (!wasm.getFunction(name)->imported()) { + roots.insert(functionToDCENode[name]); + } else { + roots.insert(importIdToDCENode[getFunctionImportId(name)]); } - rooter.walk(segment.offset); } - } + rooter.walk(segment->offset); + }); for (auto& segment : wasm.memory.segments) { if (!segment.isPassive) { rooter.walk(segment.offset); diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp index 4bb4b6f9a..5cd58301e 100644 --- a/src/tools/wasm-reduce.cpp +++ b/src/tools/wasm-reduce.cpp @@ -741,47 +741,39 @@ struct Reducer // TODO: bisection on segment shrinking? - void visitTable(Table* curr) { - std::cerr << "| try to simplify table\n"; - Name first; - for (auto& segment : curr->segments) { - for (auto item : segment.data) { - first = item; - break; - } - if (!first.isNull()) { - break; - } - } - visitSegmented(curr, first, 100); - } - void visitMemory(Memory* curr) { std::cerr << "| try to simplify memory\n"; - visitSegmented(curr, 0, 2); + + // try to reduce to first function. first, shrink segment elements. + // while we are shrinking successfully, keep going exponentially. + bool shrank = false; + for (auto& segment : curr->segments) { + shrank = shrinkByReduction(&segment, 2); + } + // the "opposite" of shrinking: copy a 'zero' element + for (auto& segment : curr->segments) { + reduceByZeroing(&segment, 0, 2, shrank); + } } - template<typename T, typename U> - void visitSegmented(T* curr, U zero, size_t bonus) { + template<typename T> bool shrinkByReduction(T* segment, size_t bonus) { // try to reduce to first function. first, shrink segment elements. // while we are shrinking successfully, keep going exponentially. bool justShrank = false; bool shrank = false; - for (auto& segment : curr->segments) { - auto& data = segment.data; - // when we succeed, try to shrink by more and more, similar to bisection - size_t skip = 1; - for (size_t i = 0; i < data.size() && !data.empty(); i++) { - if (!justShrank && !shouldTryToReduce(bonus)) { - continue; - } + + auto& data = segment->data; + // when we succeed, try to shrink by more and more, similar to bisection + size_t skip = 1; + for (size_t i = 0; i < data.size() && !data.empty(); i++) { + if (justShrank || shouldTryToReduce(bonus)) { auto save = data; for (size_t j = 0; j < skip; j++) { if (!data.empty()) { data.pop_back(); } } - auto justShrank = writeAndTestReduction(); + justShrank = writeAndTestReduction(); if (justShrank) { std::cerr << "| shrank segment (skip: " << skip << ")\n"; shrank = true; @@ -793,37 +785,67 @@ struct Reducer } } } - // the "opposite" of shrinking: copy a 'zero' element - for (auto& segment : curr->segments) { - if (segment.data.empty()) { + + return shrank; + } + + template<typename T, typename U> + void reduceByZeroing(T* segment, U zero, size_t bonus, bool shrank) { + if (segment->data.empty()) { + return; + } + for (auto& item : segment->data) { + if (!shouldTryToReduce(bonus)) { continue; } - for (auto& item : segment.data) { - if (!shouldTryToReduce(bonus)) { - continue; - } - if (item == zero) { - continue; - } - auto save = item; - item = zero; - if (writeAndTestReduction()) { - std::cerr << "| zeroed segment\n"; - noteReduction(); - } else { - item = save; - } - if (shrank) { - // zeroing is fairly inefficient. if we are managing to shrink - // (which we do exponentially), just zero one per segment at most - break; - } + if (item == zero) { + continue; + } + auto save = item; + item = zero; + if (writeAndTestReduction()) { + std::cerr << "| zeroed elem segment\n"; + noteReduction(); + } else { + item = save; + } + if (shrank) { + // zeroing is fairly inefficient. if we are managing to shrink + // (which we do exponentially), just zero one per segment at most + break; } } } + void shrinkElementSegments(Module* module) { + std::cerr << "| try to simplify elem segments\n"; + Name first; + auto it = + std::find_if_not(module->elementSegments.begin(), + module->elementSegments.end(), + [&](auto& segment) { return segment->data.empty(); }); + + if (it != module->elementSegments.end()) { + first = it->get()->data[0]; + } + + // try to reduce to first function. first, shrink segment elements. + // while we are shrinking successfully, keep going exponentially. + bool shrank = false; + for (auto& segment : module->elementSegments) { + shrank = shrinkByReduction(segment.get(), 100); + } + // the "opposite" of shrinking: copy a 'zero' element + for (auto& segment : module->elementSegments) { + reduceByZeroing(segment.get(), first, 100, shrank); + } + } + void visitModule(Module* curr) { assert(curr == module.get()); + + shrinkElementSegments(curr); + // try to remove functions std::cerr << "| try to remove functions\n"; std::vector<Name> functionNames; @@ -898,12 +920,11 @@ struct Reducer } // If we are left with a single function that is not exported or used in // a table, that is useful as then we can change the return type. - bool allTablesEmpty = std::all_of( - module->tables.begin(), module->tables.end(), [&](auto& table) { - return std::all_of(table->segments.begin(), - table->segments.end(), - [&](auto& segment) { return segment.data.empty(); }); - }); + bool allTablesEmpty = + std::all_of(module->elementSegments.begin(), + module->elementSegments.end(), + [&](auto& segment) { return segment->data.empty(); }); + if (module->functions.size() == 1 && module->exports.empty() && allTablesEmpty) { auto* func = module->functions[0].get(); @@ -964,30 +985,6 @@ struct Reducer exportsToRemove.push_back(curr->name); } } - void visitTable(Table* curr) { - Name other; - for (auto& segment : curr->segments) { - for (auto name : segment.data) { - if (!names.count(name)) { - other = name; - break; - } - } - if (!other.isNull()) { - break; - } - } - if (other.isNull()) { - return; // we failed to find a replacement - } - for (auto& segment : curr->segments) { - for (auto& name : segment.data) { - if (names.count(name)) { - name = other; - } - } - } - } void doWalkModule(Module* module) { PostWalker<FunctionReferenceRemover>::doWalkModule(module); for (auto name : exportsToRemove) { diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp index 09aaa023e..efa2c0ca4 100644 --- a/src/tools/wasm-shell.cpp +++ b/src/tools/wasm-shell.cpp @@ -166,20 +166,18 @@ run_asserts(Name moduleName, reportUnknownImport(import); } }); - ModuleUtils::iterDefinedTables(wasm, [&](Table* table) { - for (auto& segment : table->segments) { - for (auto name : segment.data) { - // spec tests consider it illegal to use spectest.print in a table - if (auto* import = wasm.getFunction(name)) { - if (import->imported() && import->module == SPECTEST && - import->base.startsWith(PRINT)) { - std::cerr << "cannot put spectest.print in table\n"; - invalid = true; - } + for (auto& segment : wasm.elementSegments) { + for (auto name : segment->data) { + // spec tests consider it illegal to use spectest.print in a table + if (auto* import = wasm.getFunction(name)) { + if (import->imported() && import->module == SPECTEST && + import->base.startsWith(PRINT)) { + std::cerr << "cannot put spectest.print in table\n"; + invalid = true; } } } - }); + } if (wasm.memory.imported()) { reportUnknownImport(&wasm.memory); } |