summaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/fuzzing.h55
-rw-r--r--src/tools/wasm-ctor-eval.cpp14
-rw-r--r--src/tools/wasm-metadce.cpp22
-rw-r--r--src/tools/wasm-reduce.cpp157
-rw-r--r--src/tools/wasm-shell.cpp20
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);
}