Skip to content

Commit b01f2bb

Browse files
authored
Optimize wasm-reduce (#1359)
1 parent eb7ec32 commit b01f2bb

File tree

2 files changed

+115
-46
lines changed

2 files changed

+115
-46
lines changed

src/tools/wasm-reduce.cpp

Lines changed: 99 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -171,28 +171,34 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
171171
// from the start
172172
size_t reduceDestructively(int factor_) {
173173
factor = factor_;
174-
Module wasm;
175-
ModuleReader reader;
176-
reader.read(working, wasm);
177174
// prepare
175+
loadWorking();
178176
reduced = 0;
179-
builder = make_unique<Builder>(wasm);
180177
funcsSeen = 0;
181178
// before we do any changes, it should be valid to write out the module:
182179
// size should be as expected, and output should be as expected
183-
setModule(&wasm);
184180
if (!writeAndTestReduction()) {
185181
std::cerr << "\n|! WARNING: writing before destructive reduction fails, very unlikely reduction can work\n\n";
186182
}
187183
// destroy!
188-
walkModule(&wasm);
184+
walkModule(getModule());
189185
return reduced;
190186
}
191187

188+
void loadWorking() {
189+
module = make_unique<Module>();
190+
Module wasm;
191+
ModuleReader reader;
192+
reader.read(working, *module);
193+
builder = make_unique<Builder>(*module);
194+
setModule(module.get());
195+
}
196+
192197
// destructive reduction state
193198

194199
size_t reduced;
195200
Expression* beforeReduction;
201+
std::unique_ptr<Module> module;
196202
std::unique_ptr<Builder> builder;
197203
Index funcsSeen;
198204
int factor;
@@ -240,8 +246,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
240246
return true;
241247
}
242248

243-
void noteReduction() {
244-
reduced++;
249+
void noteReduction(size_t amount = 1) {
250+
reduced += amount;
245251
copy_file(test, working);
246252
}
247253

@@ -393,38 +399,99 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
393399
}
394400

395401
void visitModule(Module* curr) {
402+
assert(curr == module.get());
403+
// try to remove functions
404+
std::cerr << "| try to remove functions\n";
405+
std::vector<Name> functionNames;
406+
for (auto& func : module->functions) {
407+
functionNames.push_back(func->name);
408+
}
409+
size_t skip = 1;
410+
for (size_t i = 0; i < functionNames.size(); i++) {
411+
if (!shouldTryToReduce(std::max((factor / 100) + 1, 10000))) continue;
412+
std::vector<Name> names;
413+
for (size_t j = 0; names.size() < skip && i + j < functionNames.size(); j++) {
414+
auto name = functionNames[i + j];
415+
if (module->getFunctionOrNull(name)) {
416+
names.push_back(name);
417+
}
418+
}
419+
if (names.size() == 0) continue;
420+
if (tryToRemoveFunctions(names)) {
421+
noteReduction(names.size());
422+
skip = std::min(size_t(factor), 2 * skip);
423+
} else {
424+
skip = std::max(skip / 2, size_t(1)); // or 1?
425+
}
426+
std::cout << "| (new skip: " << skip << ")\n";
427+
}
396428
// try to remove exports
397-
std::cerr << "| try to remove exports\n";
429+
std::cerr << "| try to remove exports (with factor " << factor << ")\n";
398430
std::vector<Export> exports;
399-
for (auto& exp : curr->exports) {
400-
if (!shouldTryToReduce(10000)) continue;
431+
for (auto& exp : module->exports) {
401432
exports.push_back(*exp);
402433
}
403434
for (auto& exp : exports) {
404-
curr->removeExport(exp.name);
405-
if (!writeAndTestReduction()) {
406-
curr->addExport(new Export(exp));
435+
module->removeExport(exp.name);
436+
ProgramResult result;
437+
if (!writeAndTestReduction(result)) {
438+
module->addExport(new Export(exp));
407439
} else {
408440
std::cerr << "| removed export " << exp.name << '\n';
409441
noteReduction();
410442
}
411443
}
412-
// try to remove functions
413-
std::cerr << "| try to remove functions\n";
414-
std::vector<Function> functions;
415-
for (auto& func : curr->functions) {
416-
if (!shouldTryToReduce(10000)) continue;
417-
functions.push_back(*func);
444+
}
445+
446+
bool tryToRemoveFunctions(std::vector<Name> names) {
447+
for (auto name : names) {
448+
module->removeFunction(name);
418449
}
419-
for (auto& func : functions) {
420-
curr->removeFunction(func.name);
421-
if (WasmValidator().validate(*curr, Feature::MVP, WasmValidator::Globally | WasmValidator::Quiet) &&
422-
writeAndTestReduction()) {
423-
std::cerr << "| removed function " << func.name << '\n';
424-
noteReduction();
425-
} else {
426-
curr->addFunction(new Function(func));
450+
451+
// remove all references to them
452+
struct FunctionReferenceRemover : public PostWalker<FunctionReferenceRemover> {
453+
std::unordered_set<Name> names;
454+
FunctionReferenceRemover(std::vector<Name>& vec) {
455+
for (auto name : vec) {
456+
names.insert(name);
457+
}
458+
}
459+
void visitCall(Call* curr) {
460+
if (names.count(curr->target)) {
461+
replaceCurrent(Builder(*getModule()).replaceWithIdenticalType(curr));
462+
}
463+
}
464+
void visitTable(Table* curr) {
465+
Name other;
466+
for (auto& segment : curr->segments) {
467+
for (auto name : segment.data) {
468+
if (!names.count(name)) {
469+
other = name;
470+
break;
471+
}
472+
}
473+
if (!other.isNull()) break;
474+
}
475+
if (other.isNull()) return; // we failed to find a replacement
476+
for (auto& segment : curr->segments) {
477+
for (auto& name : segment.data) {
478+
if (names.count(name)) {
479+
name = other;
480+
}
481+
}
482+
}
427483
}
484+
};
485+
FunctionReferenceRemover referenceRemover(names);
486+
referenceRemover.walkModule(module.get());
487+
488+
if (WasmValidator().validate(*module, Feature::MVP, WasmValidator::Globally | WasmValidator::Quiet) &&
489+
writeAndTestReduction()) {
490+
std::cerr << "| removed " << names.size() << " functions\n";
491+
return true;
492+
} else {
493+
loadWorking(); // restore it from orbit
494+
return false;
428495
}
429496
}
430497

@@ -545,6 +612,7 @@ int main(int argc, const char* argv[]) {
545612
if (test.size() == 0) Fatal() << "test file not provided\n";
546613
if (working.size() == 0) Fatal() << "working file not provided\n";
547614

615+
std::cerr << "|wasm-reduce\n";
548616
std::cerr << "|input: " << input << '\n';
549617
std::cerr << "|test: " << test << '\n';
550618
std::cerr << "|working: " << working << '\n';
@@ -589,11 +657,12 @@ int main(int argc, const char* argv[]) {
589657
}
590658

591659
copy_file(input, working);
592-
std::cerr << "|input size: " << file_size(working) << "\n";
660+
auto workingSize = file_size(working);
661+
std::cerr << "|input size: " << workingSize << "\n";
593662

594663
std::cerr << "|starting reduction!\n";
595664

596-
int factor = 4096;
665+
int factor = workingSize * 2;
597666
size_t lastDestructiveReductions = 0;
598667
size_t lastPostPassesSize = 0;
599668

test/reduce/memory_table.wast.txt

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,18 @@
22
(type $0 (func (result i32)))
33
(type $1 (func))
44
(table 481 481 anyfunc)
5-
(elem (i32.const 0) $1 $1 $1 $3)
5+
(elem (i32.const 0) $0 $0 $0 $2)
66
(memory $0 256 256)
7-
(export "f1" (func $2))
8-
(export "f2" (func $3))
9-
(export "f4" (func $0))
7+
(export "f1" (func $1))
8+
(export "f2" (func $2))
9+
(export "f4" (func $3))
1010
(func $0 (; 0 ;) (type $0) (result i32)
11-
(i32.add
12-
(call_indirect (type $0)
13-
(i32.const 3)
14-
)
15-
(call_indirect (type $0)
16-
(i32.const 0)
17-
)
18-
)
19-
)
20-
(func $1 (; 1 ;) (type $0) (result i32)
2111
(i32.const 1234)
2212
)
23-
(func $2 (; 2 ;) (type $1)
13+
(func $1 (; 1 ;) (type $1)
2414
(nop)
2515
)
26-
(func $3 (; 3 ;) (type $0) (result i32)
16+
(func $2 (; 2 ;) (type $0) (result i32)
2717
(i32.store
2818
(i32.const 0)
2919
(i32.const 65530)
@@ -32,5 +22,15 @@
3222
(i32.const 0)
3323
)
3424
)
25+
(func $3 (; 3 ;) (type $0) (result i32)
26+
(i32.add
27+
(call_indirect (type $0)
28+
(i32.const 3)
29+
)
30+
(call_indirect (type $0)
31+
(i32.const 0)
32+
)
33+
)
34+
)
3535
)
3636

0 commit comments

Comments
 (0)