summaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/asm2wasm.cpp271
-rw-r--r--src/tools/execution-results.h17
-rw-r--r--src/tools/fuzzing.h1554
-rw-r--r--src/tools/js-wrapper.h35
-rw-r--r--src/tools/optimization-options.h230
-rw-r--r--src/tools/spec-wrapper.h29
-rw-r--r--src/tools/tool-options.h117
-rw-r--r--src/tools/tool-utils.h1
-rw-r--r--src/tools/wasm-as.cpp115
-rw-r--r--src/tools/wasm-ctor-eval.cpp207
-rw-r--r--src/tools/wasm-dis.cpp54
-rw-r--r--src/tools/wasm-emscripten-finalize.cpp150
-rw-r--r--src/tools/wasm-metadce.cpp268
-rw-r--r--src/tools/wasm-opt.cpp225
-rw-r--r--src/tools/wasm-reduce.cpp562
-rw-r--r--src/tools/wasm-shell.cpp125
-rw-r--r--src/tools/wasm2js.cpp321
17 files changed, 2669 insertions, 1612 deletions
diff --git a/src/tools/asm2wasm.cpp b/src/tools/asm2wasm.cpp
index 547bf4ab1..843106ffd 100644
--- a/src/tools/asm2wasm.cpp
+++ b/src/tools/asm2wasm.cpp
@@ -21,21 +21,21 @@
#include <exception>
#include "ir/trapping.h"
+#include "optimization-options.h"
#include "support/colors.h"
#include "support/command-line.h"
#include "support/file.h"
#include "wasm-builder.h"
-#include "wasm-printing.h"
#include "wasm-io.h"
+#include "wasm-printing.h"
#include "wasm-validator.h"
-#include "optimization-options.h"
#include "asm2wasm.h"
using namespace cashew;
using namespace wasm;
-int main(int argc, const char *argv[]) {
+int main(int argc, const char* argv[]) {
bool legalizeJavaScriptFFI = true;
TrapMode trapMode = TrapMode::JS;
bool wasmOnly = false;
@@ -44,81 +44,138 @@ int main(int argc, const char *argv[]) {
std::string symbolMap;
bool emitBinary = true;
- OptimizationOptions options("asm2wasm", "Translate asm.js files to .wast files");
+ OptimizationOptions options("asm2wasm",
+ "Translate asm.js files to .wast files");
options
- .add("--output", "-o", "Output file (stdout if not specified)",
- Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- o->extra["output"] = argument;
- Colors::disable();
- })
- .add("--mapped-globals", "-n", "Mapped globals", Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- std::cerr << "warning: the --mapped-globals/-m option is deprecated (a mapped globals file is no longer needed as we use wasm globals)" << std::endl;
- })
- .add("--mem-init", "-t", "Import a memory initialization file into the output module", Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- o->extra["mem init"] = argument;
- })
- .add("--mem-base", "-mb", "Set the location to write the memory initialization (--mem-init) file (GLOBAL_BASE in emscripten). If not provided, the __memory_base global import is used.", Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- o->extra["mem base"] = argument;
- })
- .add("--mem-max", "-mm", "Set the maximum size of memory in the wasm module (in bytes). -1 means no limit. Without this, TOTAL_MEMORY is used (as it is used for the initial value), or if memory growth is enabled, no limit is set. This overrides both of those.", Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- o->extra["mem max"] = argument;
- })
- .add("--total-memory", "-m", "Total memory size", Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- o->extra["total memory"] = argument;
- })
- .add("--table-max", "-tM", "Set the maximum size of the table. Without this, it is set depending on how many functions are in the module. -1 means no limit", Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- o->extra["table max"] = argument;
- })
- .add("--no-opts", "-n", "Disable optimization passes (deprecated)", Options::Arguments::Zero,
- [](Options *o, const std::string& ) {
- std::cerr << "--no-opts is deprecated (use -O0, etc.)\n";
- })
- .add("--trap-mode", "",
- "Strategy for handling potentially trapping instructions. Valid "
- "values are \"allow\", \"js\", and \"clamp\"",
- Options::Arguments::One,
- [&trapMode](Options *o, const std::string& argument) {
- try {
- trapMode = trapModeFromString(argument);
- } catch (std::invalid_argument& e) {
- std::cerr << "Error: " << e.what() << "\n";
- exit(EXIT_FAILURE);
- }
- })
- .add("--wasm-only", "-w", "Input is in WebAssembly-only format, and not actually valid asm.js", Options::Arguments::Zero,
- [&wasmOnly](Options *o, const std::string& ) {
- wasmOnly = true;
- })
- .add("--no-legalize-javascript-ffi", "-nj", "Do not fully legalize (i64->i32, f32->f64) the imports and exports for interfacing with JS", Options::Arguments::Zero,
- [&legalizeJavaScriptFFI](Options *o, const std::string& ) {
- legalizeJavaScriptFFI = false;
- })
- .add("--debuginfo", "-g", "Emit names section in wasm binary (or full debuginfo in wast)",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { options.passOptions.debugInfo = true; })
- .add("--source-map", "-sm", "Emit source map (if using binary output) to the specified file",
- Options::Arguments::One,
- [&sourceMapFilename](Options *o, const std::string& argument) { sourceMapFilename = argument; })
- .add("--source-map-url", "-su", "Use specified string as source map URL",
- Options::Arguments::One,
- [&sourceMapUrl](Options *o, const std::string& argument) { sourceMapUrl = argument; })
- .add("--symbolmap", "-s", "Emit a symbol map (indexes => names)",
- Options::Arguments::One,
- [&](Options *o, const std::string& argument) { symbolMap = argument; })
- .add("--emit-text", "-S", "Emit text instead of binary for the output file",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& argument) { emitBinary = false; })
- .add_positional("INFILE", Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- o->extra["infile"] = argument;
- });
+ .add("--output",
+ "-o",
+ "Output file (stdout if not specified)",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["output"] = argument;
+ Colors::disable();
+ })
+ .add(
+ "--mapped-globals",
+ "-n",
+ "Mapped globals",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ std::cerr
+ << "warning: the --mapped-globals/-m option is deprecated (a mapped "
+ "globals file is no longer needed as we use wasm globals)"
+ << std::endl;
+ })
+ .add("--mem-init",
+ "-t",
+ "Import a memory initialization file into the output module",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["mem init"] = argument;
+ })
+ .add("--mem-base",
+ "-mb",
+ "Set the location to write the memory initialization (--mem-init) "
+ "file (GLOBAL_BASE in emscripten). If not provided, the __memory_base "
+ "global import is used.",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["mem base"] = argument;
+ })
+ .add("--mem-max",
+ "-mm",
+ "Set the maximum size of memory in the wasm module (in bytes). -1 "
+ "means no limit. Without this, TOTAL_MEMORY is used (as it is used "
+ "for the initial value), or if memory growth is enabled, no limit is "
+ "set. This overrides both of those.",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["mem max"] = argument;
+ })
+ .add("--total-memory",
+ "-m",
+ "Total memory size",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["total memory"] = argument;
+ })
+ .add("--table-max",
+ "-tM",
+ "Set the maximum size of the table. Without this, it is set depending "
+ "on how many functions are in the module. -1 means no limit",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["table max"] = argument;
+ })
+ .add("--no-opts",
+ "-n",
+ "Disable optimization passes (deprecated)",
+ Options::Arguments::Zero,
+ [](Options* o, const std::string&) {
+ std::cerr << "--no-opts is deprecated (use -O0, etc.)\n";
+ })
+ .add("--trap-mode",
+ "",
+ "Strategy for handling potentially trapping instructions. Valid "
+ "values are \"allow\", \"js\", and \"clamp\"",
+ Options::Arguments::One,
+ [&trapMode](Options* o, const std::string& argument) {
+ try {
+ trapMode = trapModeFromString(argument);
+ } catch (std::invalid_argument& e) {
+ std::cerr << "Error: " << e.what() << "\n";
+ exit(EXIT_FAILURE);
+ }
+ })
+ .add("--wasm-only",
+ "-w",
+ "Input is in WebAssembly-only format, and not actually valid asm.js",
+ Options::Arguments::Zero,
+ [&wasmOnly](Options* o, const std::string&) { wasmOnly = true; })
+ .add("--no-legalize-javascript-ffi",
+ "-nj",
+ "Do not fully legalize (i64->i32, f32->f64) the imports and exports "
+ "for interfacing with JS",
+ Options::Arguments::Zero,
+ [&legalizeJavaScriptFFI](Options* o, const std::string&) {
+ legalizeJavaScriptFFI = false;
+ })
+ .add("--debuginfo",
+ "-g",
+ "Emit names section in wasm binary (or full debuginfo in wast)",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) {
+ options.passOptions.debugInfo = true;
+ })
+ .add("--source-map",
+ "-sm",
+ "Emit source map (if using binary output) to the specified file",
+ Options::Arguments::One,
+ [&sourceMapFilename](Options* o, const std::string& argument) {
+ sourceMapFilename = argument;
+ })
+ .add("--source-map-url",
+ "-su",
+ "Use specified string as source map URL",
+ Options::Arguments::One,
+ [&sourceMapUrl](Options* o, const std::string& argument) {
+ sourceMapUrl = argument;
+ })
+ .add("--symbolmap",
+ "-s",
+ "Emit a symbol map (indexes => names)",
+ Options::Arguments::One,
+ [&](Options* o, const std::string& argument) { symbolMap = argument; })
+ .add("--emit-text",
+ "-S",
+ "Emit text instead of binary for the output file",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { emitBinary = false; })
+ .add_positional("INFILE",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["infile"] = argument;
+ });
options.parse(argc, argv);
// finalize arguments
@@ -129,32 +186,39 @@ int main(int argc, const char *argv[]) {
if (options.runningDefaultOptimizationPasses()) {
if (options.passes.size() > 1) {
- Fatal() << "asm2wasm can only run default optimization passes (-O, -Ox, etc.), and not specific additional passes";
+ Fatal() << "asm2wasm can only run default optimization passes (-O, -Ox, "
+ "etc.), and not specific additional passes";
}
}
- const auto &tm_it = options.extra.find("total memory");
- size_t totalMemory =
- tm_it == options.extra.end() ? 16 * 1024 * 1024 : atoll(tm_it->second.c_str());
+ const auto& tm_it = options.extra.find("total memory");
+ size_t totalMemory = tm_it == options.extra.end()
+ ? 16 * 1024 * 1024
+ : atoll(tm_it->second.c_str());
if (totalMemory & ~Memory::kPageMask) {
- std::cerr << "Error: total memory size " << totalMemory <<
- " is not a multiple of the 64k wasm page size\n";
+ std::cerr << "Error: total memory size " << totalMemory
+ << " is not a multiple of the 64k wasm page size\n";
exit(EXIT_FAILURE);
}
Asm2WasmPreProcessor pre;
// wasm binaries can contain a names section, but not full debug info --
// debug info is disabled if a map file is not specified with wasm binary
- pre.debugInfo = options.passOptions.debugInfo && (!emitBinary || sourceMapFilename.size());
- auto input(
- read_file<std::vector<char>>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release));
- char *start = pre.process(input.data());
+ pre.debugInfo =
+ options.passOptions.debugInfo && (!emitBinary || sourceMapFilename.size());
+ auto input(read_file<std::vector<char>>(options.extra["infile"],
+ Flags::Text,
+ options.debug ? Flags::Debug
+ : Flags::Release));
+ char* start = pre.process(input.data());
- if (options.debug) std::cerr << "parsing..." << std::endl;
+ if (options.debug)
+ std::cerr << "parsing..." << std::endl;
cashew::Parser<Ref, DotZeroValueBuilder> builder;
Ref asmjs = builder.parseToplevel(start);
- if (options.debug) std::cerr << "wasming..." << std::endl;
+ if (options.debug)
+ std::cerr << "wasming..." << std::endl;
Module wasm;
// set up memory
@@ -162,17 +226,19 @@ int main(int argc, const char *argv[]) {
// import mem init file, if provided (do this before compiling the module,
// since the optimizer should see the memory segments)
- const auto &memInit = options.extra.find("mem init");
+ const auto& memInit = options.extra.find("mem init");
if (memInit != options.extra.end()) {
auto filename = memInit->second.c_str();
- auto data(read_file<std::vector<char>>(filename, Flags::Binary, options.debug ? Flags::Debug : Flags::Release));
+ auto data(read_file<std::vector<char>>(
+ filename, Flags::Binary, options.debug ? Flags::Debug : Flags::Release));
// create the memory segment
Expression* init;
- const auto &memBase = options.extra.find("mem base");
+ const auto& memBase = options.extra.find("mem base");
if (memBase == options.extra.end()) {
init = Builder(wasm).makeGetGlobal(MEMORY_BASE, i32);
} else {
- init = Builder(wasm).makeConst(Literal(int32_t(atoi(memBase->second.c_str()))));
+ init = Builder(wasm).makeConst(
+ Literal(int32_t(atoi(memBase->second.c_str()))));
}
wasm.memory.segments.emplace_back(init, data);
}
@@ -181,7 +247,14 @@ int main(int argc, const char *argv[]) {
options.applyFeatures(wasm);
// compile the code
- Asm2WasmBuilder asm2wasm(wasm, pre, options.debug, trapMode, options.passOptions, legalizeJavaScriptFFI, options.runningDefaultOptimizationPasses(), wasmOnly);
+ Asm2WasmBuilder asm2wasm(wasm,
+ pre,
+ options.debug,
+ trapMode,
+ options.passOptions,
+ legalizeJavaScriptFFI,
+ options.runningDefaultOptimizationPasses(),
+ wasmOnly);
asm2wasm.processAsm(asmjs);
// finalize the imported mem init
@@ -194,7 +267,7 @@ int main(int argc, const char *argv[]) {
}
// Set the max memory size, if requested
- const auto &memMax = options.extra.find("mem max");
+ const auto& memMax = options.extra.find("mem max");
if (memMax != options.extra.end()) {
uint64_t max = strtoull(memMax->second.c_str(), nullptr, 10);
if (max != uint64_t(-1)) {
@@ -204,7 +277,7 @@ int main(int argc, const char *argv[]) {
}
}
// Set the table sizes, if requested
- const auto &tableMax = options.extra.find("table max");
+ const auto& tableMax = options.extra.find("table max");
if (tableMax != options.extra.end()) {
int max = atoi(tableMax->second.c_str());
if (max >= 0) {
@@ -221,7 +294,8 @@ int main(int argc, const char *argv[]) {
}
}
- if (options.debug) std::cerr << "emitting..." << std::endl;
+ if (options.debug)
+ std::cerr << "emitting..." << std::endl;
ModuleWriter writer;
writer.setDebug(options.debug);
writer.setDebugInfo(options.passOptions.debugInfo);
@@ -233,5 +307,6 @@ int main(int argc, const char *argv[]) {
}
writer.write(wasm, options.extra["output"]);
- if (options.debug) std::cerr << "done." << std::endl;
+ if (options.debug)
+ std::cerr << "done." << std::endl;
}
diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h
index 554b4eb6c..2a42b941b 100644
--- a/src/tools/execution-results.h
+++ b/src/tools/execution-results.h
@@ -18,9 +18,9 @@
// Shared execution result checking code
//
-#include "wasm.h"
-#include "shell-interface.h"
#include "ir/import-utils.h"
+#include "shell-interface.h"
+#include "wasm.h"
namespace wasm {
@@ -59,9 +59,11 @@ struct ExecutionResults {
LoggingExternalInterface interface(loggings);
try {
ModuleInstance instance(wasm, &interface);
- // execute all exported methods (that are therefore preserved through opts)
+ // execute all exported methods (that are therefore preserved through
+ // opts)
for (auto& exp : wasm.exports) {
- if (exp->kind != ExternalKind::Function) continue;
+ if (exp->kind != ExternalKind::Function)
+ continue;
std::cout << "[fuzz-exec] calling " << exp->name << "\n";
auto* func = wasm.getFunction(exp->value);
if (func->result != none) {
@@ -69,7 +71,8 @@ struct ExecutionResults {
results[exp->name] = run(func, wasm, instance);
// ignore the result if we hit an unreachable and returned no value
if (isConcreteType(results[exp->name].type)) {
- std::cout << "[fuzz-exec] note result: " << exp->name << " => " << results[exp->name] << '\n';
+ std::cout << "[fuzz-exec] note result: " << exp->name << " => "
+ << results[exp->name] << '\n';
}
} else {
// no result, run it anyhow (it might modify memory etc.)
@@ -111,9 +114,7 @@ struct ExecutionResults {
return true;
}
- bool operator!=(ExecutionResults& other) {
- return !((*this) == other);
- }
+ bool operator!=(ExecutionResults& other) { return !((*this) == other); }
Literal run(Function* func, Module& wasm) {
LoggingExternalInterface interface(loggings);
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index b7056f964..f5b548ec6 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -25,12 +25,14 @@ high chance for set at start of loop
high chance of a tee in that case => loop var
*/
-#include <wasm-builder.h>
+#include "ir/memory-utils.h"
#include <ir/find_all.h>
#include <ir/literal-utils.h>
#include <ir/manipulation.h>
-#include "ir/memory-utils.h"
#include <ir/utils.h>
+#include <support/file.h>
+#include <tools/optimization-options.h>
+#include <wasm-builder.h>
namespace wasm {
@@ -38,32 +40,35 @@ namespace wasm {
// evaluation, avoiding UB
struct ThreeArgs {
- Expression *a;
- Expression *b;
- Expression *c;
+ Expression* a;
+ Expression* b;
+ Expression* c;
};
struct UnaryArgs {
UnaryOp a;
- Expression *b;
+ Expression* b;
};
struct BinaryArgs {
BinaryOp a;
- Expression *b;
- Expression *c;
+ Expression* b;
+ Expression* c;
};
// main reader
class TranslateToFuzzReader {
public:
- TranslateToFuzzReader(Module& wasm, std::string& filename) : wasm(wasm), builder(wasm) {
- auto input(read_file<std::vector<char>>(filename, Flags::Binary, Flags::Release));
+ TranslateToFuzzReader(Module& wasm, std::string& filename)
+ : wasm(wasm), builder(wasm) {
+ auto input(
+ read_file<std::vector<char>>(filename, Flags::Binary, Flags::Release));
readData(input);
}
- TranslateToFuzzReader(Module& wasm, std::vector<char> input) : wasm(wasm), builder(wasm) {
+ TranslateToFuzzReader(Module& wasm, std::vector<char> input)
+ : wasm(wasm), builder(wasm) {
readData(input);
}
@@ -80,38 +85,91 @@ public:
options.passOptions.shrinkLevel = upTo(4);
break;
}
- case 5: options.passes.push_back("coalesce-locals"); break;
- case 6: options.passes.push_back("code-pushing"); break;
- case 7: options.passes.push_back("code-folding"); break;
- case 8: options.passes.push_back("dce"); break;
- case 9: options.passes.push_back("duplicate-function-elimination"); break;
- case 10: options.passes.push_back("flatten"); break;
- case 11: options.passes.push_back("inlining"); break;
- case 12: options.passes.push_back("inlining-optimizing"); break;
- case 13: options.passes.push_back("local-cse"); break;
- case 14: options.passes.push_back("memory-packing"); break;
- case 15: options.passes.push_back("merge-blocks"); break;
- case 16: options.passes.push_back("optimize-instructions"); break;
- case 17: options.passes.push_back("pick-load-signs"); break;
- case 18: options.passes.push_back("precompute"); break;
- case 19: options.passes.push_back("precompute-propagate"); break;
- case 20: options.passes.push_back("remove-unused-brs"); break;
- case 21: options.passes.push_back("remove-unused-module-elements"); break;
- case 22: options.passes.push_back("remove-unused-names"); break;
- case 23: options.passes.push_back("reorder-functions"); break;
- case 24: options.passes.push_back("reorder-locals"); break;
+ case 5:
+ options.passes.push_back("coalesce-locals");
+ break;
+ case 6:
+ options.passes.push_back("code-pushing");
+ break;
+ case 7:
+ options.passes.push_back("code-folding");
+ break;
+ case 8:
+ options.passes.push_back("dce");
+ break;
+ case 9:
+ options.passes.push_back("duplicate-function-elimination");
+ break;
+ case 10:
+ options.passes.push_back("flatten");
+ break;
+ case 11:
+ options.passes.push_back("inlining");
+ break;
+ case 12:
+ options.passes.push_back("inlining-optimizing");
+ break;
+ case 13:
+ options.passes.push_back("local-cse");
+ break;
+ case 14:
+ options.passes.push_back("memory-packing");
+ break;
+ case 15:
+ options.passes.push_back("merge-blocks");
+ break;
+ case 16:
+ options.passes.push_back("optimize-instructions");
+ break;
+ case 17:
+ options.passes.push_back("pick-load-signs");
+ break;
+ case 18:
+ options.passes.push_back("precompute");
+ break;
+ case 19:
+ options.passes.push_back("precompute-propagate");
+ break;
+ case 20:
+ options.passes.push_back("remove-unused-brs");
+ break;
+ case 21:
+ options.passes.push_back("remove-unused-module-elements");
+ break;
+ case 22:
+ options.passes.push_back("remove-unused-names");
+ break;
+ case 23:
+ options.passes.push_back("reorder-functions");
+ break;
+ case 24:
+ options.passes.push_back("reorder-locals");
+ break;
case 25: {
options.passes.push_back("flatten");
options.passes.push_back("rereloop");
break;
}
- case 26: options.passes.push_back("simplify-locals"); break;
- case 27: options.passes.push_back("simplify-locals-notee"); break;
- case 28: options.passes.push_back("simplify-locals-nostructure"); break;
- case 29: options.passes.push_back("simplify-locals-notee-nostructure"); break;
- case 30: options.passes.push_back("ssa"); break;
- case 31: options.passes.push_back("vacuum"); break;
- default: WASM_UNREACHABLE();
+ case 26:
+ options.passes.push_back("simplify-locals");
+ break;
+ case 27:
+ options.passes.push_back("simplify-locals-notee");
+ break;
+ case 28:
+ options.passes.push_back("simplify-locals-nostructure");
+ break;
+ case 29:
+ options.passes.push_back("simplify-locals-notee-nostructure");
+ break;
+ case 30:
+ options.passes.push_back("ssa");
+ break;
+ case 31:
+ options.passes.push_back("vacuum");
+ break;
+ default:
+ WASM_UNREACHABLE();
}
}
if (oneIn(2)) {
@@ -124,13 +182,9 @@ public:
std::cout << "shrink level: " << options.passOptions.shrinkLevel << '\n';
}
- void setAllowNaNs(bool allowNaNs_) {
- allowNaNs = allowNaNs_;
- }
+ void setAllowNaNs(bool allowNaNs_) { allowNaNs = allowNaNs_; }
- void setAllowMemory(bool allowMemory_) {
- allowMemory = allowMemory_;
- }
+ void setAllowMemory(bool allowMemory_) { allowMemory = allowMemory_; }
void build() {
if (allowMemory) {
@@ -157,8 +211,10 @@ private:
Module& wasm;
Builder builder;
std::vector<char> bytes; // the input bytes
- size_t pos; // the position in the input
- bool finishedInput; // whether we already cycled through all the input (if so, we should try to finish things off)
+ size_t pos; // the position in the input
+ // whether we already cycled through all the input (if so, we should try to
+ // finish things off)
+ bool finishedInput;
// The maximum amount of params to each function.
static const int MAX_PARAMS = 10;
@@ -176,8 +232,8 @@ private:
static const int BLOCK_FACTOR = 5;
// the memory that we use, a small portion so that we have a good chance of
- // looking at writes (we also look outside of this region with small probability)
- // this should be a power of 2
+ // looking at writes (we also look outside of this region with small
+ // probability) this should be a power of 2
static const int USABLE_MEMORY = 16;
// the number of runtime iterations (function calls, loop backbranches) we
@@ -239,13 +295,9 @@ private:
return temp | uint64_t(get32());
}
- float getFloat() {
- return Literal(get32()).reinterpretf32();
- }
+ float getFloat() { return Literal(get32()).reinterpretf32(); }
- double getDouble() {
- return Literal(get64()).reinterpretf64();
- }
+ double getDouble() { return Literal(get64()).reinterpretf64(); }
void setupMemory() {
// Add memory itself
@@ -274,10 +326,12 @@ private:
auto num = upTo(USABLE_MEMORY * 2);
for (size_t i = 0; i < num; i++) {
auto value = upTo(512);
- wasm.memory.segments[0].data.push_back(value >= 256 ? 0 : (value & 0xff));
+ wasm.memory.segments[0].data.push_back(value >= 256 ? 0
+ : (value & 0xff));
}
}
- // Add memory hasher helper (for the hash, see hash.h). The function looks like:
+ // Add memory hasher helper (for the hash, see hash.h). The function looks
+ // like:
// function hashMemory() {
// hash = 5381;
// hash = ((hash << 5) + hash) ^ mem[0];
@@ -287,31 +341,28 @@ private:
// }
std::vector<Expression*> contents;
contents.push_back(
- builder.makeSetLocal(0, builder.makeConst(Literal(uint32_t(5381))))
- );
+ builder.makeSetLocal(0, builder.makeConst(Literal(uint32_t(5381)))));
for (Index i = 0; i < USABLE_MEMORY; i++) {
- contents.push_back(
- builder.makeSetLocal(0,
- builder.makeBinary(XorInt32,
- builder.makeBinary(AddInt32,
- builder.makeBinary(ShlInt32,
- builder.makeGetLocal(0, i32),
- builder.makeConst(Literal(uint32_t(5)))
- ),
- builder.makeGetLocal(0, i32)
- ),
- builder.makeLoad(1, false, i, 1, builder.makeConst(Literal(uint32_t(0))), i32)
- )
- )
- );
- }
- contents.push_back(
- builder.makeGetLocal(0, i32)
- );
+ contents.push_back(builder.makeSetLocal(
+ 0,
+ builder.makeBinary(
+ XorInt32,
+ builder.makeBinary(
+ AddInt32,
+ builder.makeBinary(ShlInt32,
+ builder.makeGetLocal(0, i32),
+ builder.makeConst(Literal(uint32_t(5)))),
+ builder.makeGetLocal(0, i32)),
+ builder.makeLoad(
+ 1, false, i, 1, builder.makeConst(Literal(uint32_t(0))), i32))));
+ }
+ contents.push_back(builder.makeGetLocal(0, i32));
auto* body = builder.makeBlock(contents);
- auto* hasher = wasm.addFunction(builder.makeFunction("hashMemory", std::vector<Type>{}, i32, { i32 }, body));
+ auto* hasher = wasm.addFunction(builder.makeFunction(
+ "hashMemory", std::vector<Type>{}, i32, {i32}, body));
hasher->type = ensureFunctionType(getSig(hasher), &wasm)->name;
- wasm.addExport(builder.makeExport(hasher->name, hasher->name, ExternalKind::Function));
+ wasm.addExport(
+ builder.makeExport(hasher->name, hasher->name, ExternalKind::Function));
}
void setupTable() {
@@ -323,15 +374,14 @@ private:
void setupGlobals() {
size_t index = 0;
- for (auto type : { i32, i64, f32, f64 }) {
+ for (auto type : {i32, i64, f32, f64}) {
auto num = upTo(3);
for (size_t i = 0; i < num; i++) {
- auto* glob = builder.makeGlobal(
- std::string("global$") + std::to_string(index++),
- type,
- makeConst(type),
- Builder::Mutable
- );
+ auto* glob =
+ builder.makeGlobal(std::string("global$") + std::to_string(index++),
+ type,
+ makeConst(type),
+ Builder::Mutable);
wasm.addGlobal(glob);
globalsByType[type].push_back(glob->name);
}
@@ -340,26 +390,25 @@ private:
void finalizeTable() {
wasm.table.initial = wasm.table.segments[0].data.size();
- wasm.table.max = oneIn(2) ? Address(Table::kUnlimitedSize) : wasm.table.initial;
+ wasm.table.max =
+ oneIn(2) ? Address(Table::kUnlimitedSize) : wasm.table.initial;
}
const Name HANG_LIMIT_GLOBAL = "hangLimit";
void addHangLimitSupport() {
- auto* glob = builder.makeGlobal(
- HANG_LIMIT_GLOBAL,
- i32,
- builder.makeConst(Literal(int32_t(HANG_LIMIT))),
- Builder::Mutable
- );
+ auto* glob =
+ builder.makeGlobal(HANG_LIMIT_GLOBAL,
+ i32,
+ builder.makeConst(Literal(int32_t(HANG_LIMIT))),
+ Builder::Mutable);
wasm.addGlobal(glob);
auto* func = new Function;
func->name = "hangLimitInitializer";
func->result = none;
- func->body = builder.makeSetGlobal(glob->name,
- builder.makeConst(Literal(int32_t(HANG_LIMIT)))
- );
+ func->body = builder.makeSetGlobal(
+ glob->name, builder.makeConst(Literal(int32_t(HANG_LIMIT))));
wasm.addFunction(func);
auto* export_ = new Export;
@@ -370,7 +419,7 @@ private:
}
void addImportLoggingSupport() {
- for (auto type : { i32, i64, f32, f64 }) {
+ for (auto type : {i32, i64, f32, f64}) {
auto* func = new Function;
Name name = std::string("log-") + printType(type);
func->name = name;
@@ -386,21 +435,14 @@ private:
Expression* makeHangLimitCheck() {
return builder.makeSequence(
builder.makeIf(
- builder.makeUnary(
- UnaryOp::EqZInt32,
- builder.makeGetGlobal(HANG_LIMIT_GLOBAL, i32)
- ),
- makeTrivial(unreachable)
- ),
+ builder.makeUnary(UnaryOp::EqZInt32,
+ builder.makeGetGlobal(HANG_LIMIT_GLOBAL, i32)),
+ makeTrivial(unreachable)),
builder.makeSetGlobal(
HANG_LIMIT_GLOBAL,
- builder.makeBinary(
- BinaryOp::SubInt32,
- builder.makeGetGlobal(HANG_LIMIT_GLOBAL, i32),
- builder.makeConst(Literal(int32_t(1)))
- )
- )
- );
+ builder.makeBinary(BinaryOp::SubInt32,
+ builder.makeGetGlobal(HANG_LIMIT_GLOBAL, i32),
+ builder.makeConst(Literal(int32_t(1))))));
}
void addDeNanSupport() {
@@ -411,13 +453,9 @@ private:
func->result = type;
func->body = builder.makeIf(
builder.makeBinary(
- op,
- builder.makeGetLocal(0, type),
- builder.makeGetLocal(0, type)
- ),
+ op, builder.makeGetLocal(0, type), builder.makeGetLocal(0, type)),
builder.makeGetLocal(0, type),
- builder.makeConst(literal)
- );
+ builder.makeConst(literal));
wasm.addFunction(func);
};
add("deNan32", f32, Literal(float(0)), EqFloat32);
@@ -425,11 +463,12 @@ private:
}
Expression* makeDeNanOp(Expression* expr) {
- if (allowNaNs) return expr;
+ if (allowNaNs)
+ return expr;
if (expr->type == f32) {
- return builder.makeCall("deNan32", { expr }, f32);
+ return builder.makeCall("deNan32", {expr}, f32);
} else if (expr->type == f64) {
- return builder.makeCall("deNan64", { expr }, f64);
+ return builder.makeCall("deNan64", {expr}, f64);
}
return expr; // unreachable etc. is fine
}
@@ -444,7 +483,8 @@ private:
// which we try to minimize the risk of
std::vector<Expression*> hangStack;
- std::map<Type, std::vector<Index>> typeLocals; // type => list of locals with that type
+ std::map<Type, std::vector<Index>>
+ typeLocals; // type => list of locals with that type
Function* addFunction() {
LOGGING_PERCENT = upToSquared(100);
@@ -518,23 +558,19 @@ private:
// loop limit
FindAll<Loop> loops(func->body);
for (auto* loop : loops.list) {
- loop->body = builder.makeSequence(
- makeHangLimitCheck(),
- loop->body
- );
+ loop->body = builder.makeSequence(makeHangLimitCheck(), loop->body);
}
// recursion limit
- func->body = builder.makeSequence(
- makeHangLimitCheck(),
- func->body
- );
+ func->body = builder.makeSequence(makeHangLimitCheck(), func->body);
}
void recombine(Function* func) {
// Don't always do this.
- if (oneIn(2)) return;
+ if (oneIn(2))
+ return;
// First, scan and group all expressions by type.
- struct Scanner : public PostWalker<Scanner, UnifiedExpressionVisitor<Scanner>> {
+ struct Scanner
+ : public PostWalker<Scanner, UnifiedExpressionVisitor<Scanner>> {
// A map of all expressions, categorized by type.
std::map<Type, std::vector<Expression*>> exprsByType;
@@ -544,10 +580,11 @@ private:
};
Scanner scanner;
scanner.walk(func->body);
- // Potentially trim the list of possible picks, so replacements are more likely
- // to collide.
+ // Potentially trim the list of possible picks, so replacements are more
+ // likely to collide.
for (auto& pair : scanner.exprsByType) {
- if (oneIn(2)) continue;
+ if (oneIn(2))
+ continue;
auto& list = pair.second;
std::vector<Expression*> trimmed;
size_t num = upToSquared(list.size());
@@ -568,19 +605,22 @@ private:
// Second, with some probability replace an item with another item having
// the same type. (This is not always valid due to nesting of labels, but
// we'll fix that up later.)
- struct Modder : public PostWalker<Modder, UnifiedExpressionVisitor<Modder>> {
+ struct Modder
+ : public PostWalker<Modder, UnifiedExpressionVisitor<Modder>> {
Module& wasm;
Scanner& scanner;
TranslateToFuzzReader& parent;
- Modder(Module& wasm, Scanner& scanner, TranslateToFuzzReader& parent) : wasm(wasm), scanner(scanner), parent(parent) {}
+ Modder(Module& wasm, Scanner& scanner, TranslateToFuzzReader& parent)
+ : wasm(wasm), scanner(scanner), parent(parent) {}
void visitExpression(Expression* curr) {
if (parent.oneIn(10)) {
// Replace it!
auto& candidates = scanner.exprsByType[curr->type];
assert(!candidates.empty()); // this expression itself must be there
- replaceCurrent(ExpressionManipulator::copy(parent.vectorPick(candidates), wasm));
+ replaceCurrent(
+ ExpressionManipulator::copy(parent.vectorPick(candidates), wasm));
}
}
};
@@ -590,12 +630,15 @@ private:
void mutate(Function* func) {
// Don't always do this.
- if (oneIn(2)) return;
- struct Modder : public PostWalker<Modder, UnifiedExpressionVisitor<Modder>> {
+ if (oneIn(2))
+ return;
+ struct Modder
+ : public PostWalker<Modder, UnifiedExpressionVisitor<Modder>> {
Module& wasm;
TranslateToFuzzReader& parent;
- Modder(Module& wasm, TranslateToFuzzReader& parent) : wasm(wasm), parent(parent) {}
+ Modder(Module& wasm, TranslateToFuzzReader& parent)
+ : wasm(wasm), parent(parent) {}
void visitExpression(Expression* curr) {
if (parent.oneIn(10)) {
@@ -617,7 +660,8 @@ private:
Module& wasm;
TranslateToFuzzReader& parent;
- Fixer(Module& wasm, TranslateToFuzzReader& parent) : wasm(wasm), parent(parent) {}
+ Fixer(Module& wasm, TranslateToFuzzReader& parent)
+ : wasm(wasm), parent(parent) {}
// Track seen names to find duplication, which is invalid.
std::set<Name> seen;
@@ -644,14 +688,13 @@ private:
void visitSwitch(Switch* curr) {
for (auto name : curr->targets) {
- if (replaceIfInvalid(name)) return;
+ if (replaceIfInvalid(name))
+ return;
}
replaceIfInvalid(curr->default_);
}
- void visitBreak(Break* curr) {
- replaceIfInvalid(curr->name);
- }
+ void visitBreak(Break* curr) { replaceIfInvalid(curr->name); }
bool replaceIfInvalid(Name target) {
if (!hasBreakTarget(target)) {
@@ -662,24 +705,26 @@ private:
return false;
}
- void replace() {
- replaceCurrent(parent.makeTrivial(getCurrent()->type));
- }
+ void replace() { replaceCurrent(parent.makeTrivial(getCurrent()->type)); }
bool hasBreakTarget(Name name) {
- if (controlFlowStack.empty()) return false;
+ if (controlFlowStack.empty())
+ return false;
Index i = controlFlowStack.size() - 1;
while (1) {
auto* curr = controlFlowStack[i];
if (Block* block = curr->template dynCast<Block>()) {
- if (name == block->name) return true;
+ if (name == block->name)
+ return true;
} else if (Loop* loop = curr->template dynCast<Loop>()) {
- if (name == loop->name) return true;
+ if (name == loop->name)
+ return true;
} else {
// an if, ignorable
assert(curr->template is<If>());
}
- if (i == 0) return false;
+ if (i == 0)
+ return false;
i--;
}
}
@@ -709,7 +754,8 @@ private:
invocations.push_back(makeMemoryHashLogging());
}
}
- if (invocations.empty()) return;
+ if (invocations.empty())
+ return;
auto* invoker = new Function;
invoker->name = func->name.str + std::string("_invoker");
invoker->result = none;
@@ -733,8 +779,7 @@ private:
Expression* make(Type type) {
// when we should stop, emit something small (but not necessarily trivial)
- if (finishedInput ||
- nesting >= 5 * NESTING_LIMIT || // hard limit
+ if (finishedInput || nesting >= 5 * NESTING_LIMIT || // hard limit
(nesting >= NESTING_LIMIT && !oneIn(3))) {
if (isConcreteType(type)) {
if (oneIn(2)) {
@@ -759,9 +804,15 @@ private:
case i64:
case f32:
case f64:
- case v128: ret = _makeConcrete(type); break;
- case none: ret = _makenone(); break;
- case unreachable: ret = _makeunreachable(); break;
+ case v128:
+ ret = _makeConcrete(type);
+ break;
+ case none:
+ ret = _makenone();
+ break;
+ case unreachable:
+ ret = _makeunreachable();
+ break;
}
assert(ret->type == type); // we should create the right type of thing
nesting--;
@@ -770,31 +821,38 @@ private:
Expression* _makeConcrete(Type type) {
auto choice = upTo(100);
- if (choice < 10) return makeConst(type);
- if (choice < 30) return makeSetLocal(type);
- if (choice < 50) return makeGetLocal(type);
- if (choice < 60) return makeBlock(type);
- if (choice < 70) return makeIf(type);
- if (choice < 80) return makeLoop(type);
- if (choice < 90) return makeBreak(type);
+ if (choice < 10)
+ return makeConst(type);
+ if (choice < 30)
+ return makeSetLocal(type);
+ if (choice < 50)
+ return makeGetLocal(type);
+ if (choice < 60)
+ return makeBlock(type);
+ if (choice < 70)
+ return makeIf(type);
+ if (choice < 80)
+ return makeLoop(type);
+ if (choice < 90)
+ return makeBreak(type);
using Self = TranslateToFuzzReader;
auto options = FeatureOptions<Expression* (Self::*)(Type)>()
- .add(FeatureSet::MVP,
- &Self::makeBlock,
- &Self::makeIf,
- &Self::makeLoop,
- &Self::makeBreak,
- &Self::makeCall,
- &Self::makeCallIndirect,
- &Self::makeGetLocal,
- &Self::makeSetLocal,
- &Self::makeLoad,
- &Self::makeConst,
- &Self::makeUnary,
- &Self::makeBinary,
- &Self::makeSelect,
- &Self::makeGetGlobal)
- .add(FeatureSet::SIMD, &Self::makeSIMD);
+ .add(FeatureSet::MVP,
+ &Self::makeBlock,
+ &Self::makeIf,
+ &Self::makeLoop,
+ &Self::makeBreak,
+ &Self::makeCall,
+ &Self::makeCallIndirect,
+ &Self::makeGetLocal,
+ &Self::makeSetLocal,
+ &Self::makeLoad,
+ &Self::makeConst,
+ &Self::makeUnary,
+ &Self::makeBinary,
+ &Self::makeSelect,
+ &Self::makeGetGlobal)
+ .add(FeatureSet::SIMD, &Self::makeSIMD);
if (type == i32 || type == i64) {
options.add(FeatureSet::Atomics, &Self::makeAtomic);
}
@@ -811,46 +869,66 @@ private:
}
}
choice = upTo(100);
- if (choice < 50) return makeSetLocal(none);
- if (choice < 60) return makeBlock(none);
- if (choice < 70) return makeIf(none);
- if (choice < 80) return makeLoop(none);
- if (choice < 90) return makeBreak(none);
+ if (choice < 50)
+ return makeSetLocal(none);
+ if (choice < 60)
+ return makeBlock(none);
+ if (choice < 70)
+ return makeIf(none);
+ if (choice < 80)
+ return makeLoop(none);
+ if (choice < 90)
+ return makeBreak(none);
using Self = TranslateToFuzzReader;
auto options = FeatureOptions<Expression* (Self::*)(Type)>()
- .add(FeatureSet::MVP,
- &Self::makeBlock,
- &Self::makeIf,
- &Self::makeLoop,
- &Self::makeBreak,
- &Self::makeCall,
- &Self::makeCallIndirect,
- &Self::makeSetLocal,
- &Self::makeStore,
- &Self::makeDrop,
- &Self::makeNop,
- &Self::makeSetGlobal)
- .add(FeatureSet::BulkMemory, &Self::makeBulkMemory);
+ .add(FeatureSet::MVP,
+ &Self::makeBlock,
+ &Self::makeIf,
+ &Self::makeLoop,
+ &Self::makeBreak,
+ &Self::makeCall,
+ &Self::makeCallIndirect,
+ &Self::makeSetLocal,
+ &Self::makeStore,
+ &Self::makeDrop,
+ &Self::makeNop,
+ &Self::makeSetGlobal)
+ .add(FeatureSet::BulkMemory, &Self::makeBulkMemory);
return (this->*pick(options))(none);
}
Expression* _makeunreachable() {
switch (upTo(15)) {
- case 0: return makeBlock(unreachable);
- case 1: return makeIf(unreachable);
- case 2: return makeLoop(unreachable);
- case 3: return makeBreak(unreachable);
- case 4: return makeCall(unreachable);
- case 5: return makeCallIndirect(unreachable);
- case 6: return makeSetLocal(unreachable);
- case 7: return makeStore(unreachable);
- case 8: return makeUnary(unreachable);
- case 9: return makeBinary(unreachable);
- case 10: return makeSelect(unreachable);
- case 11: return makeSwitch(unreachable);
- case 12: return makeDrop(unreachable);
- case 13: return makeReturn(unreachable);
- case 14: return makeUnreachable(unreachable);
+ case 0:
+ return makeBlock(unreachable);
+ case 1:
+ return makeIf(unreachable);
+ case 2:
+ return makeLoop(unreachable);
+ case 3:
+ return makeBreak(unreachable);
+ case 4:
+ return makeCall(unreachable);
+ case 5:
+ return makeCallIndirect(unreachable);
+ case 6:
+ return makeSetLocal(unreachable);
+ case 7:
+ return makeStore(unreachable);
+ case 8:
+ return makeUnary(unreachable);
+ case 9:
+ return makeBinary(unreachable);
+ case 10:
+ return makeSelect(unreachable);
+ case 11:
+ return makeSwitch(unreachable);
+ case 12:
+ return makeDrop(unreachable);
+ case 13:
+ return makeReturn(unreachable);
+ case 14:
+ return makeUnreachable(unreachable);
}
WASM_UNREACHABLE();
}
@@ -932,7 +1010,8 @@ private:
// ensure a branch back. also optionally create some loop vars
std::vector<Expression*> list;
list.push_back(makeMaybeBlock(none)); // primary contents
- list.push_back(builder.makeBreak(ret->name, nullptr, makeCondition())); // possible branch back
+ // possible branch back
+ list.push_back(builder.makeBreak(ret->name, nullptr, makeCondition()));
list.push_back(make(type)); // final element, so we have the right type
ret->body = builder.makeBlock(list);
}
@@ -970,13 +1049,15 @@ private:
Expression* makeIf(Type type) {
auto* condition = makeCondition();
hangStack.push_back(nullptr);
- auto* ret = buildIf({ condition, makeMaybeBlock(type), makeMaybeBlock(type) });
+ auto* ret =
+ buildIf({condition, makeMaybeBlock(type), makeMaybeBlock(type)});
hangStack.pop_back();
return ret;
}
Expression* makeBreak(Type type) {
- if (breakableStack.empty()) return makeTrivial(type);
+ if (breakableStack.empty())
+ return makeTrivial(type);
Expression* condition = nullptr;
if (type != unreachable) {
hangStack.push_back(nullptr);
@@ -1029,15 +1110,18 @@ private:
}
switch (conditions) {
case 0: {
- if (!oneIn(4)) continue;
+ if (!oneIn(4))
+ continue;
break;
}
case 1: {
- if (!oneIn(2)) continue;
+ if (!oneIn(2))
+ continue;
break;
}
default: {
- if (oneIn(conditions + 1)) continue;
+ if (oneIn(conditions + 1))
+ continue;
}
}
return builder.makeBreak(name);
@@ -1058,7 +1142,8 @@ private:
if (!wasm.functions.empty() && !oneIn(wasm.functions.size())) {
target = vectorPick(wasm.functions).get();
}
- if (target->result != type) continue;
+ if (target->result != type)
+ continue;
// we found one!
std::vector<Expression*> args;
for (auto argType : target->params) {
@@ -1072,7 +1157,8 @@ private:
Expression* makeCallIndirect(Type type) {
auto& data = wasm.table.segments[0].data;
- if (data.empty()) return make(type);
+ if (data.empty())
+ return make(type);
// look for a call target with the right type
Index start = upTo(data.size());
Index i = start;
@@ -1084,8 +1170,10 @@ private:
break;
}
i++;
- if (i == data.size()) i = 0;
- if (i == start) return make(type);
+ 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
@@ -1100,17 +1188,13 @@ private:
args.push_back(make(type));
}
func->type = ensureFunctionType(getSig(func), &wasm)->name;
- return builder.makeCallIndirect(
- func->type,
- target,
- args,
- func->result
- );
+ return builder.makeCallIndirect(func->type, target, args, func->result);
}
Expression* makeGetLocal(Type type) {
auto& locals = typeLocals[type];
- if (locals.empty()) return makeConst(type);
+ if (locals.empty())
+ return makeConst(type);
return builder.makeGetLocal(vectorPick(locals), type);
}
@@ -1123,7 +1207,8 @@ private:
valueType = getConcreteType();
}
auto& locals = typeLocals[valueType];
- if (locals.empty()) return makeTrivial(type);
+ if (locals.empty())
+ return makeTrivial(type);
auto* value = make(valueType);
if (tee) {
return builder.makeTeeLocal(vectorPick(locals), value);
@@ -1134,7 +1219,8 @@ private:
Expression* makeGetGlobal(Type type) {
auto& globals = globalsByType[type];
- if (globals.empty()) return makeConst(type);
+ if (globals.empty())
+ return makeConst(type);
return builder.makeGetGlobal(vectorPick(globals), type);
}
@@ -1142,7 +1228,8 @@ private:
assert(type == none);
type = getConcreteType();
auto& globals = globalsByType[type];
- if (globals.empty()) return makeTrivial(none);
+ if (globals.empty())
+ return makeTrivial(none);
auto* value = make(type);
return builder.makeSetGlobal(vectorPick(globals), value);
}
@@ -1153,10 +1240,8 @@ private:
// range. otherwise, most pointers are going to be out of range and
// most memory ops will just trap
if (!oneIn(10)) {
- ret = builder.makeBinary(AndInt32,
- ret,
- builder.makeConst(Literal(int32_t(USABLE_MEMORY - 1)))
- );
+ ret = builder.makeBinary(
+ AndInt32, ret, builder.makeConst(Literal(int32_t(USABLE_MEMORY - 1))));
}
return ret;
}
@@ -1168,19 +1253,29 @@ private:
case i32: {
bool signed_ = get() & 1;
switch (upTo(3)) {
- case 0: return builder.makeLoad(1, signed_, offset, 1, ptr, type);
- case 1: return builder.makeLoad(2, signed_, offset, pick(1, 2), ptr, type);
- case 2: return builder.makeLoad(4, signed_, offset, pick(1, 2, 4), ptr, type);
+ case 0:
+ return builder.makeLoad(1, signed_, offset, 1, ptr, type);
+ case 1:
+ return builder.makeLoad(2, signed_, offset, pick(1, 2), ptr, type);
+ case 2:
+ return builder.makeLoad(
+ 4, signed_, offset, pick(1, 2, 4), ptr, type);
}
WASM_UNREACHABLE();
}
case i64: {
bool signed_ = get() & 1;
switch (upTo(4)) {
- case 0: return builder.makeLoad(1, signed_, offset, 1, ptr, type);
- case 1: return builder.makeLoad(2, signed_, offset, pick(1, 2), ptr, type);
- case 2: return builder.makeLoad(4, signed_, offset, pick(1, 2, 4), ptr, type);
- case 3: return builder.makeLoad(8, signed_, offset, pick(1, 2, 4, 8), ptr, type);
+ case 0:
+ return builder.makeLoad(1, signed_, offset, 1, ptr, type);
+ case 1:
+ return builder.makeLoad(2, signed_, offset, pick(1, 2), ptr, type);
+ case 2:
+ return builder.makeLoad(
+ 4, signed_, offset, pick(1, 2, 4), ptr, type);
+ case 3:
+ return builder.makeLoad(
+ 8, signed_, offset, pick(1, 2, 4, 8), ptr, type);
}
WASM_UNREACHABLE();
}
@@ -1194,19 +1289,24 @@ private:
if (!wasm.features.hasSIMD()) {
return makeTrivial(type);
}
- return builder.makeLoad(16, false, offset, pick(1, 2, 4, 8, 16), ptr, type);
+ return builder.makeLoad(
+ 16, false, offset, pick(1, 2, 4, 8, 16), ptr, type);
}
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
WASM_UNREACHABLE();
}
Expression* makeLoad(Type type) {
- if (!allowMemory) return makeTrivial(type);
+ if (!allowMemory)
+ return makeTrivial(type);
auto* ret = makeNonAtomicLoad(type);
- if (type != i32 && type != i64) return ret;
- if (!wasm.features.hasAtomics() || oneIn(2)) return ret;
+ if (type != i32 && type != i64)
+ return ret;
+ if (!wasm.features.hasAtomics() || oneIn(2))
+ return ret;
// make it atomic
auto* load = ret->cast<Load>();
wasm.memory.shared = true;
@@ -1221,11 +1321,19 @@ private:
// make a normal store, then make it unreachable
auto* ret = makeNonAtomicStore(getConcreteType());
auto* store = ret->dynCast<Store>();
- if (!store) return ret;
+ if (!store)
+ return ret;
switch (upTo(3)) {
- case 0: store->ptr = make(unreachable); break;
- case 1: store->value = make(unreachable); break;
- case 2: store->ptr = make(unreachable); store->value = make(unreachable); break;
+ case 0:
+ store->ptr = make(unreachable);
+ break;
+ case 1:
+ store->value = make(unreachable);
+ break;
+ case 2:
+ store->ptr = make(unreachable);
+ store->value = make(unreachable);
+ break;
}
store->finalize();
return store;
@@ -1241,18 +1349,28 @@ private:
switch (type) {
case i32: {
switch (upTo(3)) {
- case 0: return builder.makeStore(1, offset, 1, ptr, value, type);
- case 1: return builder.makeStore(2, offset, pick(1, 2), ptr, value, type);
- case 2: return builder.makeStore(4, offset, pick(1, 2, 4), ptr, value, type);
+ case 0:
+ return builder.makeStore(1, offset, 1, ptr, value, type);
+ case 1:
+ return builder.makeStore(2, offset, pick(1, 2), ptr, value, type);
+ case 2:
+ return builder.makeStore(
+ 4, offset, pick(1, 2, 4), ptr, value, type);
}
WASM_UNREACHABLE();
}
case i64: {
switch (upTo(4)) {
- case 0: return builder.makeStore(1, offset, 1, ptr, value, type);
- case 1: return builder.makeStore(2, offset, pick(1, 2), ptr, value, type);
- case 2: return builder.makeStore(4, offset, pick(1, 2, 4), ptr, value, type);
- case 3: return builder.makeStore(8, offset, pick(1, 2, 4, 8), ptr, value, type);
+ case 0:
+ return builder.makeStore(1, offset, 1, ptr, value, type);
+ case 1:
+ return builder.makeStore(2, offset, pick(1, 2), ptr, value, type);
+ case 2:
+ return builder.makeStore(
+ 4, offset, pick(1, 2, 4), ptr, value, type);
+ case 3:
+ return builder.makeStore(
+ 8, offset, pick(1, 2, 4, 8), ptr, value, type);
}
WASM_UNREACHABLE();
}
@@ -1266,21 +1384,27 @@ private:
if (!wasm.features.hasSIMD()) {
return makeTrivial(type);
}
- return builder.makeStore(16, offset, pick(1, 2, 4, 8, 16), ptr, value, type);
+ return builder.makeStore(
+ 16, offset, pick(1, 2, 4, 8, 16), ptr, value, type);
}
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
WASM_UNREACHABLE();
}
Expression* makeStore(Type type) {
- if (!allowMemory) return makeTrivial(type);
+ if (!allowMemory)
+ return makeTrivial(type);
auto* ret = makeNonAtomicStore(type);
auto* store = ret->dynCast<Store>();
- if (!store) return ret;
- if (store->value->type != i32 && store->value->type != i64) return store;
- if (!wasm.features.hasAtomics() || oneIn(2)) return store;
+ if (!store)
+ return ret;
+ if (store->value->type != i32 && store->value->type != i64)
+ return store;
+ if (!wasm.features.hasAtomics() || oneIn(2))
+ return store;
// make it atomic
wasm.memory.shared = true;
store->isAtomic = true;
@@ -1292,25 +1416,50 @@ private:
if (type == v128) {
// generate each lane individually for random lane interpretation
switch (upTo(6)) {
- case 0: return Literal(
- std::array<Literal, 16>{{
- makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32),
- makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32),
- makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32),
- makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32)
- }}
- );
- case 1: return Literal(
- std::array<Literal, 8>{{
- makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32),
- makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32)
- }}
- );
- case 2: return Literal(std::array<Literal, 4>{{makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32)}});
- case 3: return Literal(std::array<Literal, 2>{{makeLiteral(i64), makeLiteral(i64)}});
- case 4: return Literal(std::array<Literal, 4>{{makeLiteral(f32), makeLiteral(f32), makeLiteral(f32), makeLiteral(f32)}});
- case 5: return Literal(std::array<Literal, 2>{{makeLiteral(f64), makeLiteral(f64)}});
- default: WASM_UNREACHABLE();
+ case 0:
+ return Literal(std::array<Literal, 16>{{makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32)}});
+ case 1:
+ return Literal(std::array<Literal, 8>{{makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32)}});
+ case 2:
+ return Literal(std::array<Literal, 4>{{makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32),
+ makeLiteral(i32)}});
+ case 3:
+ return Literal(
+ std::array<Literal, 2>{{makeLiteral(i64), makeLiteral(i64)}});
+ case 4:
+ return Literal(std::array<Literal, 4>{{makeLiteral(f32),
+ makeLiteral(f32),
+ makeLiteral(f32),
+ makeLiteral(f32)}});
+ case 5:
+ return Literal(
+ std::array<Literal, 2>{{makeLiteral(f64), makeLiteral(f64)}});
+ default:
+ WASM_UNREACHABLE();
}
}
@@ -1318,13 +1467,18 @@ private:
case 0: {
// totally random, entire range
switch (type) {
- case i32: return Literal(get32());
- case i64: return Literal(get64());
- case f32: return Literal(getFloat());
- case f64: return Literal(getDouble());
+ case i32:
+ return Literal(get32());
+ case i64:
+ return Literal(get64());
+ case f32:
+ return Literal(getFloat());
+ case f64:
+ return Literal(getDouble());
case v128:
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
break;
}
@@ -1332,22 +1486,40 @@ private:
// small range
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();
+ 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) {
- case i32: return Literal(int32_t(small));
- case i64: return Literal(int64_t(small));
- case f32: return Literal(float(small));
- case f64: return Literal(double(small));
+ case i32:
+ return Literal(int32_t(small));
+ case i64:
+ return Literal(int64_t(small));
+ case f32:
+ return Literal(float(small));
+ case f64:
+ return Literal(double(small));
case v128:
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
break;
}
@@ -1355,38 +1527,63 @@ private:
// special values
Literal value;
switch (type) {
- case i32: value = Literal(pick<int32_t>(0,
- std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max(),
- std::numeric_limits<int16_t>::min(), std::numeric_limits<int16_t>::max(),
- std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max(),
- std::numeric_limits<uint8_t>::max(),
- std::numeric_limits<uint16_t>::max(),
- std::numeric_limits<uint32_t>::max())); break;
- case i64: value = Literal(pick<int64_t>(0,
- std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max(),
- std::numeric_limits<int16_t>::min(), std::numeric_limits<int16_t>::max(),
- std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max(),
- std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max(),
- std::numeric_limits<uint8_t>::max(),
- std::numeric_limits<uint16_t>::max(),
- std::numeric_limits<uint32_t>::max(),
- std::numeric_limits<uint64_t>::max())); break;
- case f32: value = Literal(pick<float>(0,
- std::numeric_limits<float>::min(), std::numeric_limits<float>::max(),
- std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max(),
- std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max(),
- std::numeric_limits<uint32_t>::max(),
- std::numeric_limits<uint64_t>::max())); break;
- case f64: value = Literal(pick<double>(0,
- std::numeric_limits<float>::min(), std::numeric_limits<float>::max(),
- std::numeric_limits<double>::min(), std::numeric_limits<double>::max(),
- std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max(),
- std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max(),
- std::numeric_limits<uint32_t>::max(),
- std::numeric_limits<uint64_t>::max())); break;
+ case i32:
+ value =
+ Literal(pick<int32_t>(0,
+ std::numeric_limits<int8_t>::min(),
+ std::numeric_limits<int8_t>::max(),
+ std::numeric_limits<int16_t>::min(),
+ std::numeric_limits<int16_t>::max(),
+ std::numeric_limits<int32_t>::min(),
+ std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<uint8_t>::max(),
+ std::numeric_limits<uint16_t>::max(),
+ std::numeric_limits<uint32_t>::max()));
+ break;
+ case i64:
+ value =
+ Literal(pick<int64_t>(0,
+ std::numeric_limits<int8_t>::min(),
+ std::numeric_limits<int8_t>::max(),
+ std::numeric_limits<int16_t>::min(),
+ std::numeric_limits<int16_t>::max(),
+ std::numeric_limits<int32_t>::min(),
+ std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int64_t>::min(),
+ std::numeric_limits<int64_t>::max(),
+ std::numeric_limits<uint8_t>::max(),
+ std::numeric_limits<uint16_t>::max(),
+ std::numeric_limits<uint32_t>::max(),
+ std::numeric_limits<uint64_t>::max()));
+ break;
+ case f32:
+ value = Literal(pick<float>(0,
+ std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max(),
+ std::numeric_limits<int32_t>::min(),
+ std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int64_t>::min(),
+ std::numeric_limits<int64_t>::max(),
+ std::numeric_limits<uint32_t>::max(),
+ std::numeric_limits<uint64_t>::max()));
+ break;
+ case f64:
+ value = Literal(pick<double>(0,
+ std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max(),
+ std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::max(),
+ std::numeric_limits<int32_t>::min(),
+ std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int64_t>::min(),
+ std::numeric_limits<int64_t>::max(),
+ std::numeric_limits<uint32_t>::max(),
+ std::numeric_limits<uint64_t>::max()));
+ break;
case v128:
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
// tweak around special values
if (oneIn(3)) { // +- 1
@@ -1401,13 +1598,22 @@ private:
// powers of 2
Literal value;
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;
+ 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;
case v128:
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
// maybe negative
if (oneIn(2)) {
@@ -1452,105 +1658,178 @@ private:
case i32: {
auto op = pick(
FeatureOptions<UnaryOp>()
- .add(FeatureSet::MVP, EqZInt32, ClzInt32, CtzInt32, PopcntInt32)
- .add(FeatureSet::Atomics, ExtendS8Int32, ExtendS16Int32)
- );
- return buildUnary({ op, make(i32) });
+ .add(FeatureSet::MVP, EqZInt32, ClzInt32, CtzInt32, PopcntInt32)
+ .add(FeatureSet::Atomics, ExtendS8Int32, ExtendS16Int32));
+ return buildUnary({op, make(i32)});
}
- case i64: return buildUnary({ pick(EqZInt64, WrapInt64), make(i64) });
+ case i64:
+ return buildUnary({pick(EqZInt64, WrapInt64), make(i64)});
case f32: {
- auto op = pick(
- FeatureOptions<UnaryOp>()
- .add(FeatureSet::MVP, TruncSFloat32ToInt32, TruncUFloat32ToInt32, ReinterpretFloat32)
- .add(FeatureSet::TruncSat, TruncSatSFloat32ToInt32, TruncSatUFloat32ToInt32)
- );
- return buildUnary({ op, make(f32) });
+ auto op = pick(FeatureOptions<UnaryOp>()
+ .add(FeatureSet::MVP,
+ TruncSFloat32ToInt32,
+ TruncUFloat32ToInt32,
+ ReinterpretFloat32)
+ .add(FeatureSet::TruncSat,
+ TruncSatSFloat32ToInt32,
+ TruncSatUFloat32ToInt32));
+ return buildUnary({op, make(f32)});
}
case f64: {
- auto op = pick(
- FeatureOptions<UnaryOp>()
- .add(FeatureSet::MVP, TruncSFloat64ToInt32, TruncUFloat64ToInt32)
- .add(FeatureSet::TruncSat, TruncSatSFloat64ToInt32, TruncSatUFloat64ToInt32)
- );
- return buildUnary({ op, make(f64) });
+ auto op = pick(FeatureOptions<UnaryOp>()
+ .add(FeatureSet::MVP,
+ TruncSFloat64ToInt32,
+ TruncUFloat64ToInt32)
+ .add(FeatureSet::TruncSat,
+ TruncSatSFloat64ToInt32,
+ TruncSatUFloat64ToInt32));
+ return buildUnary({op, make(f64)});
}
case v128: {
assert(wasm.features.hasSIMD());
- return buildUnary({ pick(AnyTrueVecI8x16, AllTrueVecI8x16, AnyTrueVecI16x8, AllTrueVecI16x8,
- AnyTrueVecI32x4, AllTrueVecI32x4, AnyTrueVecI64x2, AllTrueVecI64x2),
- make(v128) });
+ return buildUnary({pick(AnyTrueVecI8x16,
+ AllTrueVecI8x16,
+ AnyTrueVecI16x8,
+ AllTrueVecI16x8,
+ AnyTrueVecI32x4,
+ AllTrueVecI32x4,
+ AnyTrueVecI64x2,
+ AllTrueVecI64x2),
+ make(v128)});
}
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
WASM_UNREACHABLE();
}
case i64: {
switch (upTo(4)) {
case 0: {
- auto op = pick(
- FeatureOptions<UnaryOp>()
- .add(FeatureSet::MVP, ClzInt64, CtzInt64, PopcntInt64)
- .add(FeatureSet::Atomics, ExtendS8Int64, ExtendS16Int64, ExtendS32Int64)
- );
- return buildUnary({ op, make(i64) });
+ auto op =
+ pick(FeatureOptions<UnaryOp>()
+ .add(FeatureSet::MVP, ClzInt64, CtzInt64, PopcntInt64)
+ .add(FeatureSet::Atomics,
+ ExtendS8Int64,
+ ExtendS16Int64,
+ ExtendS32Int64));
+ return buildUnary({op, make(i64)});
}
- case 1: return buildUnary({ pick(ExtendSInt32, ExtendUInt32), make(i32) });
+ case 1:
+ return buildUnary({pick(ExtendSInt32, ExtendUInt32), make(i32)});
case 2: {
- auto op = pick(
- FeatureOptions<UnaryOp>()
- .add(FeatureSet::MVP, TruncSFloat32ToInt64, TruncUFloat32ToInt64)
- .add(FeatureSet::TruncSat, TruncSatSFloat32ToInt64, TruncSatUFloat32ToInt64)
- );
- return buildUnary({ op, make(f32) });
+ auto op = pick(FeatureOptions<UnaryOp>()
+ .add(FeatureSet::MVP,
+ TruncSFloat32ToInt64,
+ TruncUFloat32ToInt64)
+ .add(FeatureSet::TruncSat,
+ TruncSatSFloat32ToInt64,
+ TruncSatUFloat32ToInt64));
+ return buildUnary({op, make(f32)});
}
case 3: {
- auto op = pick(
- FeatureOptions<UnaryOp>()
- .add(FeatureSet::MVP, TruncSFloat64ToInt64, TruncUFloat64ToInt64, ReinterpretFloat64)
- .add(FeatureSet::TruncSat, TruncSatSFloat64ToInt64, TruncSatUFloat64ToInt64)
- );
- return buildUnary({ op, make(f64) });
+ auto op = pick(FeatureOptions<UnaryOp>()
+ .add(FeatureSet::MVP,
+ TruncSFloat64ToInt64,
+ TruncUFloat64ToInt64,
+ ReinterpretFloat64)
+ .add(FeatureSet::TruncSat,
+ TruncSatSFloat64ToInt64,
+ TruncSatUFloat64ToInt64));
+ return buildUnary({op, make(f64)});
}
}
WASM_UNREACHABLE();
}
case f32: {
switch (upTo(4)) {
- case 0: return makeDeNanOp(buildUnary({ pick(NegFloat32, AbsFloat32, CeilFloat32, FloorFloat32, TruncFloat32, NearestFloat32, SqrtFloat32), make(f32) }));
- case 1: return makeDeNanOp(buildUnary({ pick(ConvertUInt32ToFloat32, ConvertSInt32ToFloat32, ReinterpretInt32), make(i32) }));
- case 2: return makeDeNanOp(buildUnary({ pick(ConvertUInt64ToFloat32, ConvertSInt64ToFloat32), make(i64) }));
- case 3: return makeDeNanOp(buildUnary({ DemoteFloat64, make(f64) }));
+ case 0:
+ return makeDeNanOp(buildUnary({pick(NegFloat32,
+ AbsFloat32,
+ CeilFloat32,
+ FloorFloat32,
+ TruncFloat32,
+ NearestFloat32,
+ SqrtFloat32),
+ make(f32)}));
+ case 1:
+ return makeDeNanOp(buildUnary({pick(ConvertUInt32ToFloat32,
+ ConvertSInt32ToFloat32,
+ ReinterpretInt32),
+ make(i32)}));
+ case 2:
+ return makeDeNanOp(
+ buildUnary({pick(ConvertUInt64ToFloat32, ConvertSInt64ToFloat32),
+ make(i64)}));
+ case 3:
+ return makeDeNanOp(buildUnary({DemoteFloat64, make(f64)}));
}
WASM_UNREACHABLE();
}
case f64: {
switch (upTo(4)) {
- case 0: return makeDeNanOp(buildUnary({ pick(NegFloat64, AbsFloat64, CeilFloat64, FloorFloat64, TruncFloat64, NearestFloat64, SqrtFloat64), make(f64) }));
- case 1: return makeDeNanOp(buildUnary({ pick(ConvertUInt32ToFloat64, ConvertSInt32ToFloat64), make(i32) }));
- case 2: return makeDeNanOp(buildUnary({ pick(ConvertUInt64ToFloat64, ConvertSInt64ToFloat64, ReinterpretInt64), make(i64) }));
- case 3: return makeDeNanOp(buildUnary({ PromoteFloat32, make(f32) }));
+ case 0:
+ return makeDeNanOp(buildUnary({pick(NegFloat64,
+ AbsFloat64,
+ CeilFloat64,
+ FloorFloat64,
+ TruncFloat64,
+ NearestFloat64,
+ SqrtFloat64),
+ make(f64)}));
+ case 1:
+ return makeDeNanOp(
+ buildUnary({pick(ConvertUInt32ToFloat64, ConvertSInt32ToFloat64),
+ make(i32)}));
+ case 2:
+ return makeDeNanOp(buildUnary({pick(ConvertUInt64ToFloat64,
+ ConvertSInt64ToFloat64,
+ ReinterpretInt64),
+ make(i64)}));
+ case 3:
+ return makeDeNanOp(buildUnary({PromoteFloat32, make(f32)}));
}
WASM_UNREACHABLE();
}
case v128: {
assert(wasm.features.hasSIMD());
switch (upTo(5)) {
- case 0: return buildUnary({ pick(SplatVecI8x16, SplatVecI16x8, SplatVecI32x4), make(i32) });
- case 1: return buildUnary({ SplatVecI64x2, make(i64) });
- case 2: return buildUnary({ SplatVecF32x4, make(f32) });
- case 3: return buildUnary({ SplatVecF64x2, make(f64) });
- case 4: return buildUnary({
- pick(NotVec128, NegVecI8x16, NegVecI16x8, NegVecI32x4, NegVecI64x2,
- AbsVecF32x4, NegVecF32x4, SqrtVecF32x4, AbsVecF64x2, NegVecF64x2, SqrtVecF64x2,
- TruncSatSVecF32x4ToVecI32x4, TruncSatUVecF32x4ToVecI32x4, TruncSatSVecF64x2ToVecI64x2, TruncSatUVecF64x2ToVecI64x2,
- ConvertSVecI32x4ToVecF32x4, ConvertUVecI32x4ToVecF32x4, ConvertSVecI64x2ToVecF64x2, ConvertUVecI64x2ToVecF64x2),
- make(v128) });
+ case 0:
+ return buildUnary(
+ {pick(SplatVecI8x16, SplatVecI16x8, SplatVecI32x4), make(i32)});
+ case 1:
+ return buildUnary({SplatVecI64x2, make(i64)});
+ case 2:
+ return buildUnary({SplatVecF32x4, make(f32)});
+ case 3:
+ return buildUnary({SplatVecF64x2, make(f64)});
+ case 4:
+ return buildUnary({pick(NotVec128,
+ NegVecI8x16,
+ NegVecI16x8,
+ NegVecI32x4,
+ NegVecI64x2,
+ AbsVecF32x4,
+ NegVecF32x4,
+ SqrtVecF32x4,
+ AbsVecF64x2,
+ NegVecF64x2,
+ SqrtVecF64x2,
+ TruncSatSVecF32x4ToVecI32x4,
+ TruncSatUVecF32x4ToVecI32x4,
+ TruncSatSVecF64x2ToVecI64x2,
+ TruncSatUVecF64x2ToVecI64x2,
+ ConvertSVecI32x4ToVecF32x4,
+ ConvertUVecI32x4ToVecF32x4,
+ ConvertSVecI64x2ToVecF64x2,
+ ConvertUVecI64x2ToVecF64x2),
+ make(v128)});
}
WASM_UNREACHABLE();
}
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
WASM_UNREACHABLE();
}
@@ -1562,7 +1841,8 @@ private:
Expression* makeBinary(Type type) {
if (type == unreachable) {
if (auto* binary = makeBinary(getConcreteType())->dynCast<Binary>()) {
- return makeDeNanOp(buildBinary({ binary->op, make(unreachable), make(unreachable) }));
+ return makeDeNanOp(
+ buildBinary({binary->op, make(unreachable), make(unreachable)}));
}
// give up
return makeTrivial(type);
@@ -1570,37 +1850,193 @@ private:
switch (type) {
case i32: {
switch (upTo(4)) {
- case 0: return buildBinary({ pick(AddInt32, SubInt32, MulInt32, DivSInt32, DivUInt32, RemSInt32, RemUInt32, AndInt32, OrInt32, XorInt32, ShlInt32, ShrUInt32, ShrSInt32, RotLInt32, RotRInt32, EqInt32, NeInt32, LtSInt32, LtUInt32, LeSInt32, LeUInt32, GtSInt32, GtUInt32, GeSInt32, GeUInt32), make(i32), make(i32) });
- case 1: return buildBinary({ pick(EqInt64, NeInt64, LtSInt64, LtUInt64, LeSInt64, LeUInt64, GtSInt64, GtUInt64, GeSInt64, GeUInt64), make(i64), make(i64) });
- case 2: return buildBinary({ pick(EqFloat32, NeFloat32, LtFloat32, LeFloat32, GtFloat32, GeFloat32), make(f32), make(f32) });
- case 3: return buildBinary({ pick(EqFloat64, NeFloat64, LtFloat64, LeFloat64, GtFloat64, GeFloat64), make(f64), make(f64) });
+ case 0:
+ return buildBinary({pick(AddInt32,
+ SubInt32,
+ MulInt32,
+ DivSInt32,
+ DivUInt32,
+ RemSInt32,
+ RemUInt32,
+ AndInt32,
+ OrInt32,
+ XorInt32,
+ ShlInt32,
+ ShrUInt32,
+ ShrSInt32,
+ RotLInt32,
+ RotRInt32,
+ EqInt32,
+ NeInt32,
+ LtSInt32,
+ LtUInt32,
+ LeSInt32,
+ LeUInt32,
+ GtSInt32,
+ GtUInt32,
+ GeSInt32,
+ GeUInt32),
+ make(i32),
+ make(i32)});
+ case 1:
+ return buildBinary({pick(EqInt64,
+ NeInt64,
+ LtSInt64,
+ LtUInt64,
+ LeSInt64,
+ LeUInt64,
+ GtSInt64,
+ GtUInt64,
+ GeSInt64,
+ GeUInt64),
+ make(i64),
+ make(i64)});
+ case 2:
+ return buildBinary({pick(EqFloat32,
+ NeFloat32,
+ LtFloat32,
+ LeFloat32,
+ GtFloat32,
+ GeFloat32),
+ make(f32),
+ make(f32)});
+ case 3:
+ return buildBinary({pick(EqFloat64,
+ NeFloat64,
+ LtFloat64,
+ LeFloat64,
+ GtFloat64,
+ GeFloat64),
+ make(f64),
+ make(f64)});
}
WASM_UNREACHABLE();
}
case i64: {
- return buildBinary({ pick(AddInt64, SubInt64, MulInt64, DivSInt64, DivUInt64, RemSInt64, RemUInt64, AndInt64, OrInt64, XorInt64, ShlInt64, ShrUInt64, ShrSInt64, RotLInt64, RotRInt64), make(i64), make(i64) });
+ return buildBinary({pick(AddInt64,
+ SubInt64,
+ MulInt64,
+ DivSInt64,
+ DivUInt64,
+ RemSInt64,
+ RemUInt64,
+ AndInt64,
+ OrInt64,
+ XorInt64,
+ ShlInt64,
+ ShrUInt64,
+ ShrSInt64,
+ RotLInt64,
+ RotRInt64),
+ make(i64),
+ make(i64)});
}
case f32: {
- return makeDeNanOp(buildBinary({ pick(AddFloat32, SubFloat32, MulFloat32, DivFloat32, CopySignFloat32, MinFloat32, MaxFloat32), make(f32), make(f32) }));
+ return makeDeNanOp(buildBinary({pick(AddFloat32,
+ SubFloat32,
+ MulFloat32,
+ DivFloat32,
+ CopySignFloat32,
+ MinFloat32,
+ MaxFloat32),
+ make(f32),
+ make(f32)}));
}
case f64: {
- return makeDeNanOp(buildBinary({ pick(AddFloat64, SubFloat64, MulFloat64, DivFloat64, CopySignFloat64, MinFloat64, MaxFloat64), make(f64), make(f64) }));
+ return makeDeNanOp(buildBinary({pick(AddFloat64,
+ SubFloat64,
+ MulFloat64,
+ DivFloat64,
+ CopySignFloat64,
+ MinFloat64,
+ MaxFloat64),
+ make(f64),
+ make(f64)}));
}
case v128: {
assert(wasm.features.hasSIMD());
- return buildBinary({
- pick(EqVecI8x16, NeVecI8x16, LtSVecI8x16, LtUVecI8x16, GtSVecI8x16, GtUVecI8x16, LeSVecI8x16, LeUVecI8x16, GeSVecI8x16, GeUVecI8x16,
- EqVecI16x8, NeVecI16x8, LtSVecI16x8, LtUVecI16x8, GtSVecI16x8, GtUVecI16x8, LeSVecI16x8, LeUVecI16x8, GeSVecI16x8, GeUVecI16x8,
- EqVecI32x4, NeVecI32x4, LtSVecI32x4, LtUVecI32x4, GtSVecI32x4, GtUVecI32x4, LeSVecI32x4, LeUVecI32x4, GeSVecI32x4, GeUVecI32x4,
- EqVecF32x4, NeVecF32x4, LtVecF32x4, GtVecF32x4, LeVecF32x4, GeVecF32x4, EqVecF64x2, NeVecF64x2, LtVecF64x2, GtVecF64x2, LeVecF64x2, GeVecF64x2,
- AndVec128, OrVec128, XorVec128, AddVecI8x16, AddSatSVecI8x16, AddSatUVecI8x16, SubVecI8x16, SubSatSVecI8x16, SubSatUVecI8x16, MulVecI8x16,
- AddVecI16x8, AddSatSVecI16x8, AddSatUVecI16x8, SubVecI16x8, SubSatSVecI16x8, SubSatUVecI16x8, MulVecI16x8, AddVecI32x4, SubVecI32x4, MulVecI32x4,
- AddVecI64x2, SubVecI64x2, AddVecF32x4, SubVecF32x4, MulVecF32x4, DivVecF32x4, MinVecF32x4, MaxVecF32x4,
- AddVecF64x2, SubVecF64x2, MulVecF64x2, DivVecF64x2, MinVecF64x2, MaxVecF64x2),
- make(v128), make(v128) });
+ return buildBinary({pick(EqVecI8x16,
+ NeVecI8x16,
+ LtSVecI8x16,
+ LtUVecI8x16,
+ GtSVecI8x16,
+ GtUVecI8x16,
+ LeSVecI8x16,
+ LeUVecI8x16,
+ GeSVecI8x16,
+ GeUVecI8x16,
+ EqVecI16x8,
+ NeVecI16x8,
+ LtSVecI16x8,
+ LtUVecI16x8,
+ GtSVecI16x8,
+ GtUVecI16x8,
+ LeSVecI16x8,
+ LeUVecI16x8,
+ GeSVecI16x8,
+ GeUVecI16x8,
+ EqVecI32x4,
+ NeVecI32x4,
+ LtSVecI32x4,
+ LtUVecI32x4,
+ GtSVecI32x4,
+ GtUVecI32x4,
+ LeSVecI32x4,
+ LeUVecI32x4,
+ GeSVecI32x4,
+ GeUVecI32x4,
+ EqVecF32x4,
+ NeVecF32x4,
+ LtVecF32x4,
+ GtVecF32x4,
+ LeVecF32x4,
+ GeVecF32x4,
+ EqVecF64x2,
+ NeVecF64x2,
+ LtVecF64x2,
+ GtVecF64x2,
+ LeVecF64x2,
+ GeVecF64x2,
+ AndVec128,
+ OrVec128,
+ XorVec128,
+ AddVecI8x16,
+ AddSatSVecI8x16,
+ AddSatUVecI8x16,
+ SubVecI8x16,
+ SubSatSVecI8x16,
+ SubSatUVecI8x16,
+ MulVecI8x16,
+ AddVecI16x8,
+ AddSatSVecI16x8,
+ AddSatUVecI16x8,
+ SubVecI16x8,
+ SubSatSVecI16x8,
+ SubSatUVecI16x8,
+ MulVecI16x8,
+ AddVecI32x4,
+ SubVecI32x4,
+ MulVecI32x4,
+ AddVecI64x2,
+ SubVecI64x2,
+ AddVecF32x4,
+ SubVecF32x4,
+ MulVecF32x4,
+ DivVecF32x4,
+ MinVecF32x4,
+ MaxVecF32x4,
+ AddVecF64x2,
+ SubVecF64x2,
+ MulVecF64x2,
+ DivVecF64x2,
+ MinVecF64x2,
+ MaxVecF64x2),
+ make(v128),
+ make(v128)});
}
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
WASM_UNREACHABLE();
}
@@ -1610,12 +2046,13 @@ private:
}
Expression* makeSelect(Type type) {
- return makeDeNanOp(buildSelect({ make(i32), make(type), make(type) }));
+ return makeDeNanOp(buildSelect({make(i32), make(type), make(type)}));
}
Expression* makeSwitch(Type type) {
assert(type == unreachable);
- if (breakableStack.empty()) return make(type);
+ if (breakableStack.empty())
+ return make(type);
// we need to find proper targets to break to; try a bunch
int tries = TRIES;
std::vector<Name> names;
@@ -1639,16 +2076,19 @@ private:
}
auto default_ = names.back();
names.pop_back();
- auto temp1 = make(i32), temp2 = isConcreteType(valueType) ? make(valueType) : nullptr;
+ auto temp1 = make(i32),
+ temp2 = isConcreteType(valueType) ? make(valueType) : nullptr;
return builder.makeSwitch(names, default_, temp1, temp2);
}
Expression* makeDrop(Type type) {
- return builder.makeDrop(make(type == unreachable ? type : getConcreteType()));
+ return builder.makeDrop(
+ make(type == unreachable ? type : getConcreteType()));
}
Expression* makeReturn(Type type) {
- return builder.makeReturn(isConcreteType(func->result) ? make(func->result) : nullptr);
+ return builder.makeReturn(isConcreteType(func->result) ? make(func->result)
+ : nullptr);
}
Expression* makeNop(Type type) {
@@ -1663,7 +2103,8 @@ private:
Expression* makeAtomic(Type type) {
assert(wasm.features.hasAtomics());
- if (!allowMemory) return makeTrivial(type);
+ if (!allowMemory)
+ return makeTrivial(type);
wasm.memory.shared = true;
if (type == i32 && oneIn(2)) {
if (ATOMIC_WAITS && oneIn(2)) {
@@ -1671,7 +2112,8 @@ private:
auto expectedType = pick(i32, i64);
auto* expected = make(expectedType);
auto* timeout = make(i64);
- return builder.makeAtomicWait(ptr, expected, timeout, expectedType, logify(get()));
+ return builder.makeAtomicWait(
+ ptr, expected, timeout, expectedType, logify(get()));
} else {
auto* ptr = makePointer();
auto* count = make(i32);
@@ -1682,35 +2124,62 @@ private:
switch (type) {
case i32: {
switch (upTo(3)) {
- case 0: bytes = 1; break;
- case 1: bytes = pick(1, 2); break;
- case 2: bytes = pick(1, 2, 4); break;
- default: WASM_UNREACHABLE();
+ case 0:
+ bytes = 1;
+ break;
+ case 1:
+ bytes = pick(1, 2);
+ break;
+ case 2:
+ bytes = pick(1, 2, 4);
+ break;
+ default:
+ WASM_UNREACHABLE();
}
break;
}
case i64: {
switch (upTo(4)) {
- case 0: bytes = 1; break;
- case 1: bytes = pick(1, 2); break;
- case 2: bytes = pick(1, 2, 4); break;
- case 3: bytes = pick(1, 2, 4, 8); break;
- default: WASM_UNREACHABLE();
+ case 0:
+ bytes = 1;
+ break;
+ case 1:
+ bytes = pick(1, 2);
+ break;
+ case 2:
+ bytes = pick(1, 2, 4);
+ break;
+ case 3:
+ bytes = pick(1, 2, 4, 8);
+ break;
+ default:
+ WASM_UNREACHABLE();
}
break;
}
- default: WASM_UNREACHABLE();
+ default:
+ WASM_UNREACHABLE();
}
auto offset = logify(get());
auto* ptr = makePointer();
if (oneIn(2)) {
auto* value = make(type);
- return builder.makeAtomicRMW(pick(AtomicRMWOp::Add, AtomicRMWOp::Sub, AtomicRMWOp::And, AtomicRMWOp::Or, AtomicRMWOp::Xor, AtomicRMWOp::Xchg),
- bytes, offset, ptr, value, type);
+ return builder.makeAtomicRMW(pick(AtomicRMWOp::Add,
+ AtomicRMWOp::Sub,
+ AtomicRMWOp::And,
+ AtomicRMWOp::Or,
+ AtomicRMWOp::Xor,
+ AtomicRMWOp::Xchg),
+ bytes,
+ offset,
+ ptr,
+ value,
+ type);
} else {
auto* expected = make(type);
auto* replacement = make(type);
- return builder.makeAtomicCmpxchg(bytes, offset, ptr, expected, replacement, type);
+ return builder.makeAtomicCmpxchg(
+ bytes, offset, ptr, expected, replacement, type);
}
}
@@ -1720,12 +2189,18 @@ private:
return makeSIMDExtract(type);
}
switch (upTo(6)) {
- case 0: return makeUnary(v128);
- case 1: return makeBinary(v128);
- case 2: return makeSIMDReplace();
- case 3: return makeSIMDShuffle();
- case 4: return makeSIMDBitselect();
- case 5: return makeSIMDShift();
+ case 0:
+ return makeUnary(v128);
+ case 1:
+ return makeBinary(v128);
+ case 2:
+ return makeSIMDReplace();
+ case 3:
+ return makeSIMDShuffle();
+ case 4:
+ return makeSIMDBitselect();
+ case 5:
+ return makeSIMDShift();
}
WASM_UNREACHABLE();
}
@@ -1733,43 +2208,87 @@ private:
Expression* makeSIMDExtract(Type type) {
auto op = static_cast<SIMDExtractOp>(0);
switch (type) {
- case i32: op = pick(ExtractLaneSVecI8x16, ExtractLaneUVecI8x16, ExtractLaneSVecI16x8, ExtractLaneUVecI16x8, ExtractLaneVecI32x4); break;
- case i64: op = ExtractLaneVecI64x2; break;
- case f32: op = ExtractLaneVecF32x4; break;
- case f64: op = ExtractLaneVecF64x2; break;
+ case i32:
+ op = pick(ExtractLaneSVecI8x16,
+ ExtractLaneUVecI8x16,
+ ExtractLaneSVecI16x8,
+ ExtractLaneUVecI16x8,
+ ExtractLaneVecI32x4);
+ break;
+ case i64:
+ op = ExtractLaneVecI64x2;
+ break;
+ case f32:
+ op = ExtractLaneVecF32x4;
+ break;
+ case f64:
+ op = ExtractLaneVecF64x2;
+ break;
case v128:
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
Expression* vec = make(v128);
uint8_t index = 0;
switch (op) {
case ExtractLaneSVecI8x16:
- case ExtractLaneUVecI8x16: index = upTo(16); break;
+ case ExtractLaneUVecI8x16:
+ index = upTo(16);
+ break;
case ExtractLaneSVecI16x8:
- case ExtractLaneUVecI16x8: index = upTo(8); break;
+ case ExtractLaneUVecI16x8:
+ index = upTo(8);
+ break;
case ExtractLaneVecI32x4:
- case ExtractLaneVecF32x4: index = upTo(4); break;
+ case ExtractLaneVecF32x4:
+ index = upTo(4);
+ break;
case ExtractLaneVecI64x2:
- case ExtractLaneVecF64x2: index = upTo(2); break;
+ case ExtractLaneVecF64x2:
+ index = upTo(2);
+ break;
}
return builder.makeSIMDExtract(op, vec, index);
}
Expression* makeSIMDReplace() {
- SIMDReplaceOp op = pick(ReplaceLaneVecI8x16, ReplaceLaneVecI16x8, ReplaceLaneVecI32x4,
- ReplaceLaneVecI64x2, ReplaceLaneVecF32x4, ReplaceLaneVecF64x2);
+ SIMDReplaceOp op = pick(ReplaceLaneVecI8x16,
+ ReplaceLaneVecI16x8,
+ ReplaceLaneVecI32x4,
+ ReplaceLaneVecI64x2,
+ ReplaceLaneVecF32x4,
+ ReplaceLaneVecF64x2);
Expression* vec = make(v128);
uint8_t index;
Type lane_t;
switch (op) {
- case ReplaceLaneVecI8x16: index = upTo(16); lane_t = i32; break;
- case ReplaceLaneVecI16x8: index = upTo(8); lane_t = i32; break;
- case ReplaceLaneVecI32x4: index = upTo(4); lane_t = i32; break;
- case ReplaceLaneVecI64x2: index = upTo(2); lane_t = i64; break;
- case ReplaceLaneVecF32x4: index = upTo(4); lane_t = f32; break;
- case ReplaceLaneVecF64x2: index = upTo(2); lane_t = f64; break;
- default: WASM_UNREACHABLE();
+ case ReplaceLaneVecI8x16:
+ index = upTo(16);
+ lane_t = i32;
+ break;
+ case ReplaceLaneVecI16x8:
+ index = upTo(8);
+ lane_t = i32;
+ break;
+ case ReplaceLaneVecI32x4:
+ index = upTo(4);
+ lane_t = i32;
+ break;
+ case ReplaceLaneVecI64x2:
+ index = upTo(2);
+ lane_t = i64;
+ break;
+ case ReplaceLaneVecF32x4:
+ index = upTo(4);
+ lane_t = f32;
+ break;
+ case ReplaceLaneVecF64x2:
+ index = upTo(2);
+ lane_t = f64;
+ break;
+ default:
+ WASM_UNREACHABLE();
}
Expression* value = make(lane_t);
return builder.makeSIMDReplace(op, vec, index, value);
@@ -1793,28 +2312,44 @@ private:
}
Expression* makeSIMDShift() {
- SIMDShiftOp op = pick(ShlVecI8x16, ShrSVecI8x16, ShrUVecI8x16, ShlVecI16x8, ShrSVecI16x8, ShrUVecI16x8,
- ShlVecI32x4, ShrSVecI32x4, ShrUVecI32x4, ShlVecI64x2, ShrSVecI64x2, ShrUVecI64x2);
+ SIMDShiftOp op = pick(ShlVecI8x16,
+ ShrSVecI8x16,
+ ShrUVecI8x16,
+ ShlVecI16x8,
+ ShrSVecI16x8,
+ ShrUVecI16x8,
+ ShlVecI32x4,
+ ShrSVecI32x4,
+ ShrUVecI32x4,
+ ShlVecI64x2,
+ ShrSVecI64x2,
+ ShrUVecI64x2);
Expression* vec = make(v128);
Expression* shift = make(i32);
return builder.makeSIMDShift(op, vec, shift);
}
Expression* makeBulkMemory(Type type) {
- if (!allowMemory) return makeTrivial(type);
+ if (!allowMemory)
+ return makeTrivial(type);
assert(wasm.features.hasBulkMemory());
assert(type == none);
switch (upTo(4)) {
- case 0: return makeMemoryInit();
- case 1: return makeDataDrop();
- case 2: return makeMemoryCopy();
- case 3: return makeMemoryFill();
+ case 0:
+ return makeMemoryInit();
+ case 1:
+ return makeDataDrop();
+ case 2:
+ return makeMemoryCopy();
+ case 3:
+ return makeMemoryFill();
}
WASM_UNREACHABLE();
}
Expression* makeMemoryInit() {
- if (!allowMemory) return makeTrivial(none);
+ if (!allowMemory)
+ return makeTrivial(none);
uint32_t segment = upTo(wasm.memory.segments.size());
size_t totalSize = wasm.memory.segments[segment].data.size();
size_t offsetVal = upTo(totalSize);
@@ -1826,12 +2361,14 @@ private:
}
Expression* makeDataDrop() {
- if (!allowMemory) return makeTrivial(none);
+ if (!allowMemory)
+ return makeTrivial(none);
return builder.makeDataDrop(upTo(wasm.memory.segments.size()));
}
Expression* makeMemoryCopy() {
- if (!allowMemory) return makeTrivial(none);
+ if (!allowMemory)
+ return makeTrivial(none);
Expression* dest = makePointer();
Expression* source = makePointer();
Expression* size = make(i32);
@@ -1839,7 +2376,8 @@ private:
}
Expression* makeMemoryFill() {
- if (!allowMemory) return makeTrivial(none);
+ if (!allowMemory)
+ return makeTrivial(none);
Expression* dest = makePointer();
Expression* value = makePointer();
Expression* size = make(i32);
@@ -1850,32 +2388,33 @@ private:
Expression* makeLogging() {
auto type = pick(i32, i64, f32, f64);
- return builder.makeCall(std::string("log-") + printType(type), { make(type) }, none);
+ return builder.makeCall(
+ std::string("log-") + printType(type), {make(type)}, none);
}
Expression* makeMemoryHashLogging() {
auto* hash = builder.makeCall(std::string("hashMemory"), {}, i32);
- return builder.makeCall(std::string("log-i32"), { hash }, none);
+ return builder.makeCall(std::string("log-i32"), {hash}, none);
}
// special getters
Type getType() {
return pick(FeatureOptions<Type>()
- .add(FeatureSet::MVP, i32, i64, f32, f64, none, unreachable)
- .add(FeatureSet::SIMD, v128));
+ .add(FeatureSet::MVP, i32, i64, f32, f64, none, unreachable)
+ .add(FeatureSet::SIMD, v128));
}
Type getReachableType() {
return pick(FeatureOptions<Type>()
- .add(FeatureSet::MVP, i32, i64, f32, f64, none)
- .add(FeatureSet::SIMD, v128));
+ .add(FeatureSet::MVP, i32, i64, f32, f64, none)
+ .add(FeatureSet::SIMD, v128));
}
Type getConcreteType() {
return pick(FeatureOptions<Type>()
- .add(FeatureSet::MVP, i32, i64, f32, f64)
- .add(FeatureSet::SIMD, v128));
+ .add(FeatureSet::MVP, i32, i64, f32, f64)
+ .add(FeatureSet::SIMD, v128));
}
// statistical distributions
@@ -1889,7 +2428,8 @@ private:
// this isn't a perfectly uniform distribution, but it's fast
// and reasonable
Index upTo(Index x) {
- if (x == 0) return 0;
+ if (x == 0)
+ return 0;
Index raw;
if (x <= 255) {
raw = get();
@@ -1904,9 +2444,7 @@ private:
return ret;
}
- bool oneIn(Index x) {
- return upTo(x) == 0;
- }
+ bool oneIn(Index x) { return upTo(x) == 0; }
bool onceEvery(Index x) {
static int counter = 0;
@@ -1916,69 +2454,60 @@ private:
// apply upTo twice, generating a skewed distribution towards
// low values
- Index upToSquared(Index x) {
- return upTo(upTo(x));
- }
+ Index upToSquared(Index x) { return upTo(upTo(x)); }
// pick from a vector
- template<typename T>
- const T& vectorPick(const std::vector<T>& vec) {
+ template<typename T> const T& vectorPick(const std::vector<T>& vec) {
assert(!vec.empty());
auto index = upTo(vec.size());
return vec[index];
}
// pick from a fixed list
- template<typename T, typename... Args>
- T pick(T first, Args... args) {
+ template<typename T, typename... Args> T pick(T first, Args... args) {
auto num = sizeof...(Args) + 1;
auto temp = upTo(num);
return pickGivenNum<T>(temp, first, args...);
}
- template<typename T>
- T pickGivenNum(size_t num, T first) {
+ template<typename T> T pickGivenNum(size_t num, T first) {
assert(num == 0);
return first;
}
- // Trick to avoid a bug in GCC 7.x.
- // Upstream bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82800
- #define GCC_VERSION (__GNUC__ * 10000 \
- + __GNUC_MINOR__ * 100 \
- + __GNUC_PATCHLEVEL__)
- #if GCC_VERSION > 70000 && GCC_VERSION < 70300
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
- #endif
+// Trick to avoid a bug in GCC 7.x.
+// Upstream bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82800
+#define GCC_VERSION \
+ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#if GCC_VERSION > 70000 && GCC_VERSION < 70300
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
template<typename T, typename... Args>
T pickGivenNum(size_t num, T first, Args... args) {
- if (num == 0) return first;
+ if (num == 0)
+ return first;
return pickGivenNum<T>(num - 1, args...);
}
- #if GCC_VERSION > 70000 && GCC_VERSION < 70300
- #pragma GCC diagnostic pop
- #endif
+#if GCC_VERSION > 70000 && GCC_VERSION < 70300
+#pragma GCC diagnostic pop
+#endif
- template<typename T>
- struct FeatureOptions {
- template<typename ...Ts>
+ template<typename T> struct FeatureOptions {
+ template<typename... Ts>
FeatureOptions<T>& add(FeatureSet::Feature feature, T option, Ts... rest) {
options[feature].push_back(option);
return add(feature, rest...);
}
- FeatureOptions<T>& add(FeatureSet::Feature feature) {
- return *this;
- }
+ FeatureOptions<T>& add(FeatureSet::Feature feature) { return *this; }
std::map<FeatureSet::Feature, std::vector<T>> options;
};
- template<typename T>
- const T pick(FeatureOptions<T>& picker) {
+ template<typename T> const T pick(FeatureOptions<T>& picker) {
std::vector<T> matches;
for (const auto& item : picker.options) {
if (wasm.features.has(item.first)) {
@@ -2012,6 +2541,7 @@ private:
} // namespace wasm
-// XXX Switch class has a condition?! is it real? should the node type be the value type if it exists?!
+// XXX Switch class has a condition?! is it real? should the node type be the
+// value type if it exists?!
// TODO copy an existing function and replace just one node in it
diff --git a/src/tools/js-wrapper.h b/src/tools/js-wrapper.h
index 433d6fc5a..e39a015d6 100644
--- a/src/tools/js-wrapper.h
+++ b/src/tools/js-wrapper.h
@@ -28,7 +28,8 @@ static std::string generateJSWrapper(Module& wasm) {
"}\n"
"var tempRet0;\n"
"var binary;\n"
- "if (typeof process === 'object' && typeof require === 'function' /* node.js detection */) {\n"
+ "if (typeof process === 'object' && typeof require === 'function' /* "
+ "node.js detection */) {\n"
" var args = process.argv.slice(2);\n"
" binary = require('fs').readFileSync(args[0]);\n"
" if (!binary.buffer) binary = new Uint8Array(binary);\n"
@@ -59,12 +60,21 @@ static std::string generateJSWrapper(Module& wasm) {
" }\n"
" return ret;\n"
"}\n"
- "var instance = new WebAssembly.Instance(new WebAssembly.Module(binary), {\n"
+ "var instance = new WebAssembly.Instance(new "
+ "WebAssembly.Module(binary), {\n"
" 'fuzzing-support': {\n"
- " 'log-i32': function(x) { console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') + ']') },\n"
- " 'log-i64': function(x, y) { console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') + ' ' + literal(y, 'i32') + ']') },\n" // legalization: two i32s
- " 'log-f32': function(x) { console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') + ']') },\n" // legalization: an f64
- " 'log-f64': function(x) { console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') + ']') },\n"
+ " 'log-i32': function(x) { "
+ "console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') "
+ "+ ']') },\n"
+ " 'log-i64': function(x, y) { "
+ "console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') "
+ "+ ' ' + literal(y, 'i32') + ']') },\n" // legalization: two i32s
+ " 'log-f32': function(x) { "
+ "console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') "
+ "+ ']') },\n" // legalization: an f64
+ " 'log-f64': function(x) { "
+ "console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') "
+ "+ ']') },\n"
" },\n"
" 'env': {\n"
" 'setTempRet0': function(x) { tempRet0 = x },\n"
@@ -73,12 +83,16 @@ static std::string generateJSWrapper(Module& wasm) {
"});\n";
for (auto& exp : wasm.exports) {
auto* func = wasm.getFunctionOrNull(exp->value);
- if (!func) continue; // something exported other than a function
- ret += "if (instance.exports.hangLimitInitializer) instance.exports.hangLimitInitializer();\n";
+ if (!func)
+ continue; // something exported other than a function
+ ret += "if (instance.exports.hangLimitInitializer) "
+ "instance.exports.hangLimitInitializer();\n";
ret += "try {\n";
- ret += std::string(" console.log('[fuzz-exec] calling $") + exp->name.str + "');\n";
+ ret += std::string(" console.log('[fuzz-exec] calling $") + exp->name.str +
+ "');\n";
if (func->result != none) {
- ret += std::string(" console.log('[fuzz-exec] note result: $") + exp->name.str + " => ' + literal(";
+ ret += std::string(" console.log('[fuzz-exec] note result: $") +
+ exp->name.str + " => ' + literal(";
} else {
ret += " ";
}
@@ -110,4 +124,3 @@ static std::string generateJSWrapper(Module& wasm) {
}
} // namespace wasm
-
diff --git a/src/tools/optimization-options.h b/src/tools/optimization-options.h
index cf7c612ea..6fa9aac36 100644
--- a/src/tools/optimization-options.h
+++ b/src/tools/optimization-options.h
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#ifndef wasm_tools_optimization_options_h
+#define wasm_tools_optimization_options_h
+
#include "tool-options.h"
//
@@ -27,101 +30,137 @@ struct OptimizationOptions : public ToolOptions {
std::vector<std::string> passes;
- OptimizationOptions(const std::string& command, const std::string& description) : ToolOptions(command, description) {
- (*this).add("", "-O", "execute default optimization passes",
- Options::Arguments::Zero,
- [this](Options*, const std::string&) {
- passOptions.setDefaultOptimizationOptions();
- passes.push_back(DEFAULT_OPT_PASSES);
- })
- .add("", "-O0", "execute no optimization passes",
- Options::Arguments::Zero,
- [this](Options*, const std::string&) {
- passOptions.optimizeLevel = 0;
- passOptions.shrinkLevel = 0;
- })
- .add("", "-O1", "execute -O1 optimization passes (quick&useful opts, useful for iteration builds)",
- Options::Arguments::Zero,
- [this](Options*, const std::string&) {
- passOptions.optimizeLevel = 1;
- passOptions.shrinkLevel = 0;
- passes.push_back(DEFAULT_OPT_PASSES);
- })
- .add("", "-O2", "execute -O2 optimization passes (most opts, generally gets most perf)",
- Options::Arguments::Zero,
- [this](Options*, const std::string&) {
- passOptions.optimizeLevel = 2;
- passOptions.shrinkLevel = 0;
- passes.push_back(DEFAULT_OPT_PASSES);
- })
- .add("", "-O3", "execute -O3 optimization passes (spends potentially a lot of time optimizing)",
- Options::Arguments::Zero,
- [this](Options*, const std::string&) {
- passOptions.optimizeLevel = 3;
- passOptions.shrinkLevel = 0;
- passes.push_back(DEFAULT_OPT_PASSES);
- })
- .add("", "-O4", "execute -O4 optimization passes (also flatten the IR, which can take a lot more time and memory, but is useful on more nested / complex / less-optimized input)",
- Options::Arguments::Zero,
- [this](Options*, const std::string&) {
- passOptions.optimizeLevel = 4;
- passOptions.shrinkLevel = 0;
- passes.push_back(DEFAULT_OPT_PASSES);
- })
- .add("", "-Os", "execute default optimization passes, focusing on code size",
- Options::Arguments::Zero,
- [this](Options*, const std::string&) {
- passOptions.optimizeLevel = 2;
- passOptions.shrinkLevel = 1;
- passes.push_back(DEFAULT_OPT_PASSES);
- })
- .add("", "-Oz", "execute default optimization passes, super-focusing on code size",
- Options::Arguments::Zero,
- [this](Options*, const std::string&) {
- passOptions.optimizeLevel = 2;
- passOptions.shrinkLevel = 2;
- passes.push_back(DEFAULT_OPT_PASSES);
- })
- .add("--optimize-level", "-ol", "How much to focus on optimizing code",
- Options::Arguments::One,
- [this](Options* o, const std::string& argument) {
- passOptions.optimizeLevel = atoi(argument.c_str());
- })
- .add("--shrink-level", "-s", "How much to focus on shrinking code size",
- Options::Arguments::One,
- [this](Options* o, const std::string& argument) {
- passOptions.shrinkLevel = atoi(argument.c_str());
- })
- .add("--ignore-implicit-traps", "-iit", "Optimize under the helpful assumption that no surprising traps occur (from load, div/mod, etc.)",
- Options::Arguments::Zero,
- [this](Options*, const std::string&) {
- passOptions.ignoreImplicitTraps = true;
- })
- .add("--low-memory-unused", "-lmu", "Optimize under the helpful assumption that the low 1K of memory is not used by the application",
- Options::Arguments::Zero,
- [this](Options*, const std::string&) {
- passOptions.lowMemoryUnused = true;
- })
- .add("--pass-arg", "-pa", "An argument passed along to optimization passes being run. Must be in the form KEY:VALUE",
- Options::Arguments::N,
- [this](Options*, const std::string& argument) {
- auto colon = argument.find(':');
- if (colon == std::string::npos) {
- Fatal() << "--pass-arg value must be in the form of KEY:VALUE";
- }
- auto key = argument.substr(0, colon);
- auto value = argument.substr(colon + 1);
- passOptions.arguments[key] = value;
- });
+ OptimizationOptions(const std::string& command,
+ const std::string& description)
+ : ToolOptions(command, description) {
+ (*this)
+ .add("",
+ "-O",
+ "execute default optimization passes",
+ Options::Arguments::Zero,
+ [this](Options*, const std::string&) {
+ passOptions.setDefaultOptimizationOptions();
+ passes.push_back(DEFAULT_OPT_PASSES);
+ })
+ .add("",
+ "-O0",
+ "execute no optimization passes",
+ Options::Arguments::Zero,
+ [this](Options*, const std::string&) {
+ passOptions.optimizeLevel = 0;
+ passOptions.shrinkLevel = 0;
+ })
+ .add("",
+ "-O1",
+ "execute -O1 optimization passes (quick&useful opts, useful for "
+ "iteration builds)",
+ Options::Arguments::Zero,
+ [this](Options*, const std::string&) {
+ passOptions.optimizeLevel = 1;
+ passOptions.shrinkLevel = 0;
+ passes.push_back(DEFAULT_OPT_PASSES);
+ })
+ .add(
+ "",
+ "-O2",
+ "execute -O2 optimization passes (most opts, generally gets most perf)",
+ Options::Arguments::Zero,
+ [this](Options*, const std::string&) {
+ passOptions.optimizeLevel = 2;
+ passOptions.shrinkLevel = 0;
+ passes.push_back(DEFAULT_OPT_PASSES);
+ })
+ .add("",
+ "-O3",
+ "execute -O3 optimization passes (spends potentially a lot of time "
+ "optimizing)",
+ Options::Arguments::Zero,
+ [this](Options*, const std::string&) {
+ passOptions.optimizeLevel = 3;
+ passOptions.shrinkLevel = 0;
+ passes.push_back(DEFAULT_OPT_PASSES);
+ })
+ .add("",
+ "-O4",
+ "execute -O4 optimization passes (also flatten the IR, which can "
+ "take a lot more time and memory, but is useful on more nested / "
+ "complex / less-optimized input)",
+ Options::Arguments::Zero,
+ [this](Options*, const std::string&) {
+ passOptions.optimizeLevel = 4;
+ passOptions.shrinkLevel = 0;
+ passes.push_back(DEFAULT_OPT_PASSES);
+ })
+ .add("",
+ "-Os",
+ "execute default optimization passes, focusing on code size",
+ Options::Arguments::Zero,
+ [this](Options*, const std::string&) {
+ passOptions.optimizeLevel = 2;
+ passOptions.shrinkLevel = 1;
+ passes.push_back(DEFAULT_OPT_PASSES);
+ })
+ .add("",
+ "-Oz",
+ "execute default optimization passes, super-focusing on code size",
+ Options::Arguments::Zero,
+ [this](Options*, const std::string&) {
+ passOptions.optimizeLevel = 2;
+ passOptions.shrinkLevel = 2;
+ passes.push_back(DEFAULT_OPT_PASSES);
+ })
+ .add("--optimize-level",
+ "-ol",
+ "How much to focus on optimizing code",
+ Options::Arguments::One,
+ [this](Options* o, const std::string& argument) {
+ passOptions.optimizeLevel = atoi(argument.c_str());
+ })
+ .add("--shrink-level",
+ "-s",
+ "How much to focus on shrinking code size",
+ Options::Arguments::One,
+ [this](Options* o, const std::string& argument) {
+ passOptions.shrinkLevel = atoi(argument.c_str());
+ })
+ .add("--ignore-implicit-traps",
+ "-iit",
+ "Optimize under the helpful assumption that no surprising traps "
+ "occur (from load, div/mod, etc.)",
+ Options::Arguments::Zero,
+ [this](Options*, const std::string&) {
+ passOptions.ignoreImplicitTraps = true;
+ })
+ .add("--low-memory-unused",
+ "-lmu",
+ "Optimize under the helpful assumption that the low 1K of memory is "
+ "not used by the application",
+ Options::Arguments::Zero,
+ [this](Options*, const std::string&) {
+ passOptions.lowMemoryUnused = true;
+ })
+ .add("--pass-arg",
+ "-pa",
+ "An argument passed along to optimization passes being run. Must be "
+ "in the form KEY:VALUE",
+ Options::Arguments::N,
+ [this](Options*, const std::string& argument) {
+ auto colon = argument.find(':');
+ if (colon == std::string::npos) {
+ Fatal() << "--pass-arg value must be in the form of KEY:VALUE";
+ }
+ auto key = argument.substr(0, colon);
+ auto value = argument.substr(colon + 1);
+ passOptions.arguments[key] = value;
+ });
// add passes in registry
for (const auto& p : PassRegistry::get()->getRegisteredNames()) {
(*this).add(
- std::string("--") + p, "", PassRegistry::get()->getPassDescription(p),
+ std::string("--") + p,
+ "",
+ PassRegistry::get()->getPassDescription(p),
Options::Arguments::Zero,
- [this, p](Options*, const std::string&) {
- passes.push_back(p);
- }
- );
+ [this, p](Options*, const std::string&) { passes.push_back(p); });
}
}
@@ -134,13 +173,12 @@ struct OptimizationOptions : public ToolOptions {
return false;
}
- bool runningPasses() {
- return passes.size() > 0;
- }
+ bool runningPasses() { return passes.size() > 0; }
void runPasses(Module& wasm) {
PassRunner passRunner(&wasm, passOptions);
- if (debug) passRunner.setDebug(true);
+ if (debug)
+ passRunner.setDebug(true);
for (auto& pass : passes) {
if (pass == DEFAULT_OPT_PASSES) {
passRunner.addDefaultOptimizationPasses();
@@ -153,3 +191,5 @@ struct OptimizationOptions : public ToolOptions {
};
} // namespace wasm
+
+#endif
diff --git a/src/tools/spec-wrapper.h b/src/tools/spec-wrapper.h
index 77db8a0f4..516ce17a9 100644
--- a/src/tools/spec-wrapper.h
+++ b/src/tools/spec-wrapper.h
@@ -25,18 +25,31 @@ static std::string generateSpecWrapper(Module& wasm) {
std::string ret;
for (auto& exp : wasm.exports) {
auto* func = wasm.getFunctionOrNull(exp->value);
- if (!func) continue; // something exported other than a function
- ret += std::string("(invoke \"hangLimitInitializer\") (invoke \"") + exp->name.str + "\" ";
+ if (!func)
+ continue; // something exported other than a function
+ ret += std::string("(invoke \"hangLimitInitializer\") (invoke \"") +
+ exp->name.str + "\" ";
for (Type param : func->params) {
// zeros in arguments TODO more?
switch (param) {
- case i32: ret += "(i32.const 0)"; break;
- case i64: ret += "(i64.const 0)"; break;
- case f32: ret += "(f32.const 0)"; break;
- case f64: ret += "(f64.const 0)"; break;
- case v128: ret += "(v128.const i32x4 0 0 0 0)"; break;
+ case i32:
+ ret += "(i32.const 0)";
+ break;
+ case i64:
+ ret += "(i64.const 0)";
+ break;
+ case f32:
+ ret += "(f32.const 0)";
+ break;
+ case f64:
+ ret += "(f64.const 0)";
+ break;
+ case v128:
+ ret += "(v128.const i32x4 0 0 0 0)";
+ break;
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
ret += " ";
}
diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h
index 02572d72e..1f13063c5 100644
--- a/src/tools/tool-options.h
+++ b/src/tools/tool-options.h
@@ -14,9 +14,12 @@
* limitations under the License.
*/
+#ifndef wasm_tools_tool_options_h
+#define wasm_tools_tool_options_h
+
#include "ir/module-utils.h"
-#include "support/command-line.h"
#include "pass.h"
+#include "support/command-line.h"
//
// Shared optimization options for commandline tools
@@ -28,64 +31,74 @@ struct ToolOptions : public Options {
PassOptions passOptions;
ToolOptions(const std::string& command, const std::string& description)
- : Options(command, description) {
+ : Options(command, description) {
(*this)
- .add("--mvp-features", "-mvp", "Disable all non-MVP features",
- Arguments::Zero,
- [this](Options*, const std::string&) {
- hasFeatureOptions = true;
- enabledFeatures.makeMVP();
- disabledFeatures.setAll();
- })
- .add("--all-features", "-all", "Enable all features",
- Arguments::Zero,
- [this](Options*, const std::string&) {
- hasFeatureOptions = true;
- enabledFeatures.setAll();
- disabledFeatures.makeMVP();
- })
- .add("--detect-features", "",
- "Use features from the target features section, or MVP (default)",
- Arguments::Zero,
- [this](Options*, const std::string&) {
- hasFeatureOptions = true;
- detectFeatures = true;
- enabledFeatures.makeMVP();
- disabledFeatures.makeMVP();
- });
+ .add("--mvp-features",
+ "-mvp",
+ "Disable all non-MVP features",
+ Arguments::Zero,
+ [this](Options*, const std::string&) {
+ hasFeatureOptions = true;
+ enabledFeatures.makeMVP();
+ disabledFeatures.setAll();
+ })
+ .add("--all-features",
+ "-all",
+ "Enable all features",
+ Arguments::Zero,
+ [this](Options*, const std::string&) {
+ hasFeatureOptions = true;
+ enabledFeatures.setAll();
+ disabledFeatures.makeMVP();
+ })
+ .add("--detect-features",
+ "",
+ "Use features from the target features section, or MVP (default)",
+ Arguments::Zero,
+ [this](Options*, const std::string&) {
+ hasFeatureOptions = true;
+ detectFeatures = true;
+ enabledFeatures.makeMVP();
+ disabledFeatures.makeMVP();
+ });
(*this)
- .addFeature(FeatureSet::SignExt, "sign extension operations")
- .addFeature(FeatureSet::Atomics, "atomic operations")
- .addFeature(FeatureSet::MutableGlobals, "mutable globals")
- .addFeature(FeatureSet::TruncSat, "nontrapping float-to-int operations")
- .addFeature(FeatureSet::SIMD, "SIMD operations and types")
- .addFeature(FeatureSet::BulkMemory, "bulk memory operations")
- .add("--no-validation", "-n",
- "Disables validation, assumes inputs are correct",
- Options::Arguments::Zero,
- [this](Options* o, const std::string& argument) {
- passOptions.validate = false;
- });
+ .addFeature(FeatureSet::SignExt, "sign extension operations")
+ .addFeature(FeatureSet::Atomics, "atomic operations")
+ .addFeature(FeatureSet::MutableGlobals, "mutable globals")
+ .addFeature(FeatureSet::TruncSat, "nontrapping float-to-int operations")
+ .addFeature(FeatureSet::SIMD, "SIMD operations and types")
+ .addFeature(FeatureSet::BulkMemory, "bulk memory operations")
+ .add("--no-validation",
+ "-n",
+ "Disables validation, assumes inputs are correct",
+ Options::Arguments::Zero,
+ [this](Options* o, const std::string& argument) {
+ passOptions.validate = false;
+ });
}
ToolOptions& addFeature(FeatureSet::Feature feature,
const std::string& description) {
(*this)
- .add(std::string("--enable-") + FeatureSet::toString(feature), "",
- std::string("Enable ") + description, Arguments::Zero,
- [=](Options*, const std::string&) {
- hasFeatureOptions = true;
- enabledFeatures.set(feature, true);
- disabledFeatures.set(feature, false);
- })
+ .add(std::string("--enable-") + FeatureSet::toString(feature),
+ "",
+ std::string("Enable ") + description,
+ Arguments::Zero,
+ [=](Options*, const std::string&) {
+ hasFeatureOptions = true;
+ enabledFeatures.set(feature, true);
+ disabledFeatures.set(feature, false);
+ })
- .add(std::string("--disable-") + FeatureSet::toString(feature), "",
- std::string("Disable ") + description, Arguments::Zero,
- [=](Options*, const std::string&) {
- hasFeatureOptions = true;
- enabledFeatures.set(feature, false);
- disabledFeatures.set(feature, true);
- });
+ .add(std::string("--disable-") + FeatureSet::toString(feature),
+ "",
+ std::string("Disable ") + description,
+ Arguments::Zero,
+ [=](Options*, const std::string&) {
+ hasFeatureOptions = true;
+ enabledFeatures.set(feature, false);
+ disabledFeatures.set(feature, true);
+ });
return *this;
}
@@ -113,3 +126,5 @@ private:
};
} // namespace wasm
+
+#endif
diff --git a/src/tools/tool-utils.h b/src/tools/tool-utils.h
index a897e01f0..9e010ae3a 100644
--- a/src/tools/tool-utils.h
+++ b/src/tools/tool-utils.h
@@ -34,4 +34,3 @@ inline std::string removeSpecificSuffix(std::string str, std::string suffix) {
}
} // namespace wasm
-
diff --git a/src/tools/wasm-as.cpp b/src/tools/wasm-as.cpp
index 20e51e31f..253a93b3b 100644
--- a/src/tools/wasm-as.cpp
+++ b/src/tools/wasm-as.cpp
@@ -30,61 +30,87 @@
using namespace cashew;
using namespace wasm;
-int main(int argc, const char *argv[]) {
+int main(int argc, const char* argv[]) {
bool debugInfo = false;
std::string symbolMap;
std::string sourceMapFilename;
std::string sourceMapUrl;
- ToolOptions options("wasm-as", "Assemble a .wast (WebAssembly text format) into a .wasm (WebAssembly binary format)");
+ ToolOptions options("wasm-as",
+ "Assemble a .wast (WebAssembly text format) into a .wasm "
+ "(WebAssembly binary format)");
options.extra["validate"] = "wasm";
options
- .add("--output", "-o", "Output file (stdout if not specified)",
- Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- o->extra["output"] = argument;
- Colors::disable();
- })
- .add("--validate", "-v", "Control validation of the output module",
- Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- if (argument != "web" && argument != "none" && argument != "wasm") {
- std::cerr << "Valid arguments for --validate flag are 'wasm', 'web', and 'none'.\n";
- exit(1);
- }
- o->extra["validate"] = argument;
- })
- .add("--debuginfo", "-g", "Emit names section and debug info",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { debugInfo = true; })
- .add("--source-map", "-sm", "Emit source map to the specified file",
- Options::Arguments::One,
- [&sourceMapFilename](Options *o, const std::string& argument) { sourceMapFilename = argument; })
- .add("--source-map-url", "-su", "Use specified string as source map URL",
- Options::Arguments::One,
- [&sourceMapUrl](Options *o, const std::string& argument) { sourceMapUrl = argument; })
- .add("--symbolmap", "-s", "Emit a symbol map (indexes => names)",
- Options::Arguments::One,
- [&](Options *o, const std::string& argument) { symbolMap = argument; })
- .add_positional("INFILE", Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- o->extra["infile"] = argument;
- });
+ .add("--output",
+ "-o",
+ "Output file (stdout if not specified)",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["output"] = argument;
+ Colors::disable();
+ })
+ .add("--validate",
+ "-v",
+ "Control validation of the output module",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ if (argument != "web" && argument != "none" && argument != "wasm") {
+ std::cerr << "Valid arguments for --validate flag are 'wasm', "
+ "'web', and 'none'.\n";
+ exit(1);
+ }
+ o->extra["validate"] = argument;
+ })
+ .add("--debuginfo",
+ "-g",
+ "Emit names section and debug info",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) { debugInfo = true; })
+ .add("--source-map",
+ "-sm",
+ "Emit source map to the specified file",
+ Options::Arguments::One,
+ [&sourceMapFilename](Options* o, const std::string& argument) {
+ sourceMapFilename = argument;
+ })
+ .add("--source-map-url",
+ "-su",
+ "Use specified string as source map URL",
+ Options::Arguments::One,
+ [&sourceMapUrl](Options* o, const std::string& argument) {
+ sourceMapUrl = argument;
+ })
+ .add("--symbolmap",
+ "-s",
+ "Emit a symbol map (indexes => names)",
+ Options::Arguments::One,
+ [&](Options* o, const std::string& argument) { symbolMap = argument; })
+ .add_positional("INFILE",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["infile"] = argument;
+ });
options.parse(argc, argv);
// default output is infile with changed suffix
if (options.extra.find("output") == options.extra.end()) {
- options.extra["output"] = removeSpecificSuffix(options.extra["infile"], ".wast") + ".wasm";
+ options.extra["output"] =
+ removeSpecificSuffix(options.extra["infile"], ".wast") + ".wasm";
}
- auto input(read_file<std::string>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release));
+ auto input(
+ read_file<std::string>(options.extra["infile"],
+ Flags::Text,
+ options.debug ? Flags::Debug : Flags::Release));
Module wasm;
try {
- if (options.debug) std::cerr << "s-parsing..." << std::endl;
+ if (options.debug)
+ std::cerr << "s-parsing..." << std::endl;
SExpressionParser parser(const_cast<char*>(input.c_str()));
Element& root = *parser.root;
- if (options.debug) std::cerr << "w-parsing..." << std::endl;
+ if (options.debug)
+ std::cerr << "w-parsing..." << std::endl;
SExpressionWasmBuilder builder(wasm, *root[0]);
} catch (ParseException& p) {
p.dump(std::cerr);
@@ -94,15 +120,19 @@ int main(int argc, const char *argv[]) {
options.applyFeatures(wasm);
if (options.extra["validate"] != "none") {
- if (options.debug) std::cerr << "Validating..." << std::endl;
- if (!wasm::WasmValidator().validate(wasm,
- WasmValidator::Globally | (options.extra["validate"] == "web" ? WasmValidator::Web : 0))) {
+ if (options.debug)
+ std::cerr << "Validating..." << std::endl;
+ if (!wasm::WasmValidator().validate(
+ wasm,
+ WasmValidator::Globally |
+ (options.extra["validate"] == "web" ? WasmValidator::Web : 0))) {
WasmPrinter::printModule(&wasm);
Fatal() << "Error: input module is not valid.\n";
}
}
- if (options.debug) std::cerr << "writing..." << std::endl;
+ if (options.debug)
+ std::cerr << "writing..." << std::endl;
ModuleWriter writer;
writer.setBinary(true);
writer.setDebugInfo(debugInfo);
@@ -115,5 +145,6 @@ int main(int argc, const char *argv[]) {
}
writer.write(wasm, options.extra["output"]);
- if (options.debug) std::cerr << "Done." << std::endl;
+ if (options.debug)
+ std::cerr << "Done." << std::endl;
}
diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp
index 420ec06ad..2050b3ebc 100644
--- a/src/tools/wasm-ctor-eval.cpp
+++ b/src/tools/wasm-ctor-eval.cpp
@@ -24,19 +24,19 @@
#include <memory>
+#include "ir/global-utils.h"
+#include "ir/import-utils.h"
+#include "ir/literal-utils.h"
+#include "ir/memory-utils.h"
+#include "ir/module-utils.h"
#include "pass.h"
-#include "support/file.h"
#include "support/colors.h"
+#include "support/file.h"
#include "tool-options.h"
-#include "wasm-io.h"
-#include "wasm-interpreter.h"
#include "wasm-builder.h"
+#include "wasm-interpreter.h"
+#include "wasm-io.h"
#include "wasm-validator.h"
-#include "ir/memory-utils.h"
-#include "ir/global-utils.h"
-#include "ir/import-utils.h"
-#include "ir/literal-utils.h"
-#include "ir/module-utils.h"
using namespace wasm;
@@ -57,13 +57,9 @@ class EvallingGlobalManager {
bool sealed = false;
public:
- void addDangerous(Name name) {
- dangerousGlobals.insert(name);
- }
+ void addDangerous(Name name) { dangerousGlobals.insert(name); }
- void seal() {
- sealed = true;
- }
+ void seal() { sealed = true; }
// for equality purposes, we just care about the globals
// and whether they have changed
@@ -78,9 +74,13 @@ public:
if (dangerousGlobals.count(name) > 0) {
std::string extra;
if (name == "___dso_handle") {
- extra = "\nrecommendation: build with -s NO_EXIT_RUNTIME=1 so that calls to atexit that use ___dso_handle are not emitted";
+ extra = "\nrecommendation: build with -s NO_EXIT_RUNTIME=1 so that "
+ "calls to atexit that use ___dso_handle are not emitted";
}
- throw FailToEvalException(std::string("tried to access a dangerous (import-initialized) global: ") + name.str + extra);
+ throw FailToEvalException(
+ std::string(
+ "tried to access a dangerous (import-initialized) global: ") +
+ name.str + extra);
}
return globals[name];
}
@@ -91,14 +91,14 @@ public:
bool found;
Iterator() : found(false) {}
- Iterator(Name name, Literal value) : first(name), second(value), found(true) {}
+ Iterator(Name name, Literal value)
+ : first(name), second(value), found(true) {}
bool operator==(const Iterator& other) {
- return first == other.first && second == other.second && found == other.found;
- }
- bool operator!=(const Iterator& other) {
- return !(*this == other);
+ return first == other.first && second == other.second &&
+ found == other.found;
}
+ bool operator!=(const Iterator& other) { return !(*this == other); }
};
Iterator find(Name name) {
@@ -108,9 +108,7 @@ public:
return Iterator(name, globals[name]);
}
- Iterator end() {
- return Iterator();
- }
+ Iterator end() { return Iterator(); }
};
// Use a ridiculously large stack size.
@@ -125,25 +123,28 @@ static Index STACK_START = 1024 * 1024 * 1024 + STACK_SIZE;
static Index STACK_LOWER_LIMIT = STACK_START - STACK_SIZE;
static Index STACK_UPPER_LIMIT = STACK_START + STACK_SIZE;
-class EvallingModuleInstance : public ModuleInstanceBase<EvallingGlobalManager, EvallingModuleInstance> {
+class EvallingModuleInstance
+ : public ModuleInstanceBase<EvallingGlobalManager, EvallingModuleInstance> {
public:
- EvallingModuleInstance(Module& wasm, ExternalInterface* externalInterface) : ModuleInstanceBase(wasm, externalInterface) {
- // if any global in the module has a non-const constructor, it is using a global import,
- // which we don't have, and is illegal to use
+ EvallingModuleInstance(Module& wasm, ExternalInterface* externalInterface)
+ : ModuleInstanceBase(wasm, externalInterface) {
+ // if any global in the module has a non-const constructor, it is using a
+ // global import, which we don't have, and is illegal to use
ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) {
if (!global->init->is<Const>()) {
// some constants are ok to use
if (auto* get = global->init->dynCast<GetGlobal>()) {
auto name = get->name;
auto* import = wasm.getGlobal(name);
- if (import->module == Name(ENV) && (
- import->base == STACKTOP || // stack constants are special, we handle them
- import->base == STACK_MAX
- )) {
+ if (import->module == Name(ENV) &&
+ (import->base ==
+ STACKTOP || // stack constants are special, we handle them
+ import->base == STACK_MAX)) {
return; // this is fine
}
}
- // this global is dangerously initialized by an import, so if it is used, we must fail
+ // this global is dangerously initialized by an import, so if it is
+ // used, we must fail
globals.addDangerous(global->name);
}
});
@@ -177,13 +178,15 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
ImportInfo imports(wasm_);
if (auto* stackTop = imports.getImportedGlobal(ENV, STACKTOP)) {
globals[stackTop->name] = Literal(int32_t(STACK_START));
- if (auto* stackTop = GlobalUtils::getGlobalInitializedToImport(wasm_, ENV, STACKTOP)) {
+ if (auto* stackTop =
+ GlobalUtils::getGlobalInitializedToImport(wasm_, ENV, STACKTOP)) {
globals[stackTop->name] = Literal(int32_t(STACK_START));
}
}
if (auto* stackMax = imports.getImportedGlobal(ENV, STACK_MAX)) {
globals[stackMax->name] = Literal(int32_t(STACK_START));
- if (auto* stackMax = GlobalUtils::getGlobalInitializedToImport(wasm_, ENV, STACK_MAX)) {
+ if (auto* stackMax =
+ GlobalUtils::getGlobalInitializedToImport(wasm_, ENV, STACK_MAX)) {
globals[stackMax->name] = Literal(int32_t(STACK_START));
}
}
@@ -203,19 +206,26 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
Literal callImport(Function* import, LiteralList& arguments) override {
std::string extra;
if (import->module == ENV && import->base == "___cxa_atexit") {
- extra = "\nrecommendation: build with -s NO_EXIT_RUNTIME=1 so that calls to atexit are not emitted";
+ extra = "\nrecommendation: build with -s NO_EXIT_RUNTIME=1 so that calls "
+ "to atexit are not emitted";
}
- throw FailToEvalException(std::string("call import: ") + import->module.str + "." + import->base.str + extra);
+ throw FailToEvalException(std::string("call import: ") +
+ import->module.str + "." + import->base.str +
+ extra);
}
- Literal callTable(Index index, LiteralList& arguments, Type result, EvallingModuleInstance& instance) override {
+ Literal callTable(Index index,
+ LiteralList& arguments,
+ Type result,
+ EvallingModuleInstance& instance) override {
// we assume the table is not modified (hmm)
// look through the segments, try to find the function
for (auto& segment : wasm->table.segments) {
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.
+ // 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>()) {
start = c->value.getInteger();
} else if (segment.offset->is<GetGlobal>()) {
@@ -226,16 +236,20 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
auto end = start + segment.data.size();
if (start <= index && index < end) {
auto name = segment.data[index - start];
- // if this is one of our functions, we can call it; if it was imported, fail
+ // if this is one of our functions, we can call it; if it was imported,
+ // fail
auto* func = wasm->getFunction(name);
if (!func->imported()) {
return instance.callFunctionInternal(name, arguments);
} else {
- throw FailToEvalException(std::string("callTable on imported function: ") + name.str);
+ throw FailToEvalException(
+ std::string("callTable on imported function: ") + name.str);
}
}
}
- throw FailToEvalException(std::string("callTable on index not found in static segments: ") + std::to_string(index));
+ throw FailToEvalException(
+ std::string("callTable on index not found in static segments: ") +
+ std::to_string(index));
}
int8_t load8s(Address addr) override { return doLoad<int8_t>(addr); }
@@ -247,13 +261,21 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
int64_t load64s(Address addr) override { return doLoad<int64_t>(addr); }
uint64_t load64u(Address addr) override { return doLoad<uint64_t>(addr); }
- void store8(Address addr, int8_t value) override { doStore<int8_t>(addr, value); }
- void store16(Address addr, int16_t value) override { doStore<int16_t>(addr, value); }
- void store32(Address addr, int32_t value) override { doStore<int32_t>(addr, value); }
- void store64(Address addr, int64_t value) override { doStore<int64_t>(addr, value); }
+ void store8(Address addr, int8_t value) override {
+ doStore<int8_t>(addr, value);
+ }
+ void store16(Address addr, int16_t value) override {
+ doStore<int16_t>(addr, value);
+ }
+ void store32(Address addr, int32_t value) override {
+ doStore<int32_t>(addr, value);
+ }
+ void store64(Address addr, int64_t value) override {
+ doStore<int64_t>(addr, value);
+ }
// called during initialization, but we don't keep track of a table
- void tableStore(Address addr, Name value) override { }
+ void tableStore(Address addr, Name value) override {}
void growMemory(Address /*oldSize*/, Address newSize) override {
throw FailToEvalException("grow memory");
@@ -266,8 +288,7 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
private:
// TODO: handle unaligned too, see shell-interface
- template<typename T>
- T* getMemory(Address address) {
+ template<typename T> T* getMemory(Address address) {
// if memory is on the stack, use the stack
if (address >= STACK_LOWER_LIMIT) {
if (address >= STACK_UPPER_LIMIT) {
@@ -283,14 +304,11 @@ private:
std::vector<char> temp;
Builder builder(*wasm);
wasm->memory.segments.push_back(
- Memory::Segment(
- builder.makeConst(Literal(int32_t(0))),
- temp
- )
- );
+ Memory::Segment(builder.makeConst(Literal(int32_t(0))), temp));
}
// memory should already have been flattened
- assert(wasm->memory.segments[0].offset->cast<Const>()->value.getInteger() == 0);
+ assert(wasm->memory.segments[0].offset->cast<Const>()->value.getInteger() ==
+ 0);
auto max = address + sizeof(T);
auto& data = wasm->memory.segments[0].data;
if (max > data.size()) {
@@ -299,14 +317,12 @@ private:
return (T*)(&data[address]);
}
- template<typename T>
- void doStore(Address address, T value) {
+ template<typename T> void doStore(Address address, T value) {
// do a memcpy to avoid undefined behavior if unaligned
memcpy(getMemory<T>(address), &value, sizeof(T));
}
- template<typename T>
- T doLoad(Address address) {
+ template<typename T> T doLoad(Address address) {
// do a memcpy to avoid undefined behavior if unaligned
T ret;
memcpy(&ret, getMemory<T>(address), sizeof(T));
@@ -337,7 +353,7 @@ void evalCtors(Module& wasm, std::vector<std::string> ctors) {
// snapshot globals (note that STACKTOP might be modified, but should
// be returned, so that works out)
auto globalsBefore = instance.globals;
- Export *ex = wasm.getExportOrNull(ctor);
+ Export* ex = wasm.getExportOrNull(ctor);
if (!ex) {
Fatal() << "export not found: " << ctor;
}
@@ -366,7 +382,8 @@ void evalCtors(Module& wasm, std::vector<std::string> ctors) {
}
} catch (FailToEvalException& fail) {
// that's it, we failed to even create the instance
- std::cerr << " ...stopping since could not create module instance: " << fail.why << "\n";
+ std::cerr << " ...stopping since could not create module instance: "
+ << fail.why << "\n";
return;
}
}
@@ -382,37 +399,50 @@ int main(int argc, const char* argv[]) {
bool debugInfo = false;
std::string ctorsString;
- ToolOptions options("wasm-ctor-eval", "Execute C++ global constructors ahead of time");
+ ToolOptions options("wasm-ctor-eval",
+ "Execute C++ global constructors ahead of time");
options
- .add("--output", "-o", "Output file (stdout if not specified)",
- Options::Arguments::One,
- [](Options* o, const std::string& argument) {
- o->extra["output"] = argument;
- Colors::disable();
- })
- .add("--emit-text", "-S", "Emit text instead of binary for the output file",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& argument) { emitBinary = false; })
- .add("--debuginfo", "-g", "Emit names section and debug info",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { debugInfo = true; })
- .add("--ctors", "-c", "Comma-separated list of global constructor functions to evaluate",
- Options::Arguments::One,
- [&](Options* o, const std::string& argument) {
- ctorsString = argument;
- })
- .add_positional("INFILE", Options::Arguments::One,
- [](Options* o, const std::string& argument) {
- o->extra["infile"] = argument;
- });
+ .add("--output",
+ "-o",
+ "Output file (stdout if not specified)",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["output"] = argument;
+ Colors::disable();
+ })
+ .add("--emit-text",
+ "-S",
+ "Emit text instead of binary for the output file",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { emitBinary = false; })
+ .add("--debuginfo",
+ "-g",
+ "Emit names section and debug info",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) { debugInfo = true; })
+ .add(
+ "--ctors",
+ "-c",
+ "Comma-separated list of global constructor functions to evaluate",
+ Options::Arguments::One,
+ [&](Options* o, const std::string& argument) { ctorsString = argument; })
+ .add_positional("INFILE",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["infile"] = argument;
+ });
options.parse(argc, argv);
- auto input(read_file<std::string>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release));
+ auto input(
+ read_file<std::string>(options.extra["infile"],
+ Flags::Text,
+ options.debug ? Flags::Debug : Flags::Release));
Module wasm;
{
- if (options.debug) std::cerr << "reading...\n";
+ if (options.debug)
+ std::cerr << "reading...\n";
ModuleReader reader;
reader.setDebug(options.debug);
@@ -453,7 +483,8 @@ int main(int argc, const char* argv[]) {
}
if (options.extra.count("output") > 0) {
- if (options.debug) std::cerr << "writing..." << std::endl;
+ if (options.debug)
+ std::cerr << "writing..." << std::endl;
ModuleWriter writer;
writer.setDebug(options.debug);
writer.setBinary(emitBinary);
diff --git a/src/tools/wasm-dis.cpp b/src/tools/wasm-dis.cpp
index 3ff6819a5..5b9cb9804 100644
--- a/src/tools/wasm-dis.cpp
+++ b/src/tools/wasm-dis.cpp
@@ -27,25 +27,37 @@
using namespace cashew;
using namespace wasm;
-int main(int argc, const char *argv[]) {
+int main(int argc, const char* argv[]) {
std::string sourceMapFilename;
- Options options("wasm-dis", "Un-assemble a .wasm (WebAssembly binary format) into a .wast (WebAssembly text format)");
- options.add("--output", "-o", "Output file (stdout if not specified)",
- Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- o->extra["output"] = argument;
- Colors::disable();
- })
- .add("--source-map", "-sm", "Consume source map from the specified file to add location information",
- Options::Arguments::One,
- [&sourceMapFilename](Options *o, const std::string& argument) { sourceMapFilename = argument; })
- .add_positional("INFILE", Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- o->extra["infile"] = argument;
- });
+ Options options("wasm-dis",
+ "Un-assemble a .wasm (WebAssembly binary format) into a "
+ ".wast (WebAssembly text format)");
+ options
+ .add("--output",
+ "-o",
+ "Output file (stdout if not specified)",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["output"] = argument;
+ Colors::disable();
+ })
+ .add(
+ "--source-map",
+ "-sm",
+ "Consume source map from the specified file to add location information",
+ Options::Arguments::One,
+ [&sourceMapFilename](Options* o, const std::string& argument) {
+ sourceMapFilename = argument;
+ })
+ .add_positional("INFILE",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["infile"] = argument;
+ });
options.parse(argc, argv);
- if (options.debug) std::cerr << "parsing binary..." << std::endl;
+ if (options.debug)
+ std::cerr << "parsing binary..." << std::endl;
Module wasm;
try {
ModuleReader().readBinary(options.extra["infile"], wasm, sourceMapFilename);
@@ -59,10 +71,14 @@ int main(int argc, const char *argv[]) {
Fatal() << "error in parsing wasm source mapping";
}
- if (options.debug) std::cerr << "Printing..." << std::endl;
- Output output(options.extra["output"], Flags::Text, options.debug ? Flags::Debug : Flags::Release);
+ if (options.debug)
+ std::cerr << "Printing..." << std::endl;
+ Output output(options.extra["output"],
+ Flags::Text,
+ options.debug ? Flags::Debug : Flags::Release);
WasmPrinter::printModule(&wasm, output.getStream());
output << '\n';
- if (options.debug) std::cerr << "Done." << std::endl;
+ if (options.debug)
+ std::cerr << "Done." << std::endl;
}
diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp
index 46fb0533b..a23ba1c5a 100644
--- a/src/tools/wasm-emscripten-finalize.cpp
+++ b/src/tools/wasm-emscripten-finalize.cpp
@@ -21,6 +21,7 @@
#include <exception>
+#include "abi/js.h"
#include "ir/trapping.h"
#include "support/colors.h"
#include "support/file.h"
@@ -30,12 +31,11 @@
#include "wasm-io.h"
#include "wasm-printing.h"
#include "wasm-validator.h"
-#include "abi/js.h"
using namespace cashew;
using namespace wasm;
-int main(int argc, const char *argv[]) {
+int main(int argc, const char* argv[]) {
const uint64_t INVALID_BASE = -1;
std::string infile;
@@ -53,60 +53,86 @@ int main(int argc, const char *argv[]) {
ToolOptions options("wasm-emscripten-finalize",
"Performs Emscripten-specific transforms on .wasm files");
options
- .add("--output", "-o", "Output file",
- Options::Arguments::One,
- [&outfile](Options*, const std::string& argument) {
- outfile = argument;
- Colors::disable();
- })
- .add("--debuginfo", "-g",
- "Emit names section in wasm binary (or full debuginfo in wast)",
- Options::Arguments::Zero,
- [&debugInfo](Options *, const std::string &) {
- debugInfo = true;
- })
- .add("--emit-text", "-S", "Emit text instead of binary for the output file",
- Options::Arguments::Zero,
- [&emitBinary](Options*, const std::string& ) {
- emitBinary = false;
- })
- .add("--global-base", "", "The address at which static globals were placed",
- Options::Arguments::One,
- [&globalBase](Options*, const std::string&argument ) {
- globalBase = std::stoull(argument);
- })
- .add("--initial-stack-pointer", "", "The initial location of the stack pointer",
- Options::Arguments::One,
- [&initialStackPointer](Options*, const std::string&argument ) {
- initialStackPointer = std::stoull(argument);
- })
- .add("--side-module", "", "Input is an emscripten side module",
- Options::Arguments::Zero,
- [&isSideModule](Options *o, const std::string& argument) {
- isSideModule = true;
- })
- .add("--input-source-map", "-ism", "Consume source map from the specified file",
- Options::Arguments::One,
- [&inputSourceMapFilename](Options *o, const std::string& argument) { inputSourceMapFilename = argument; })
- .add("--no-legalize-javascript-ffi", "-nj", "Do not fully legalize (i64->i32, "
- "f32->f64) the imports and exports for interfacing with JS",
- Options::Arguments::Zero,
- [&legalizeJavaScriptFFI](Options *o, const std::string& ) {
- legalizeJavaScriptFFI = false;
- })
- .add("--output-source-map", "-osm", "Emit source map to the specified file",
- Options::Arguments::One,
- [&outputSourceMapFilename](Options *o, const std::string& argument) { outputSourceMapFilename = argument; })
- .add("--output-source-map-url", "-osu", "Emit specified string as source map URL",
- Options::Arguments::One,
- [&outputSourceMapUrl](Options *o, const std::string& argument) { outputSourceMapUrl = argument; })
- .add("--separate-data-segments", "", "Separate data segments to a file",
- Options::Arguments::One,
- [&dataSegmentFile](Options *o, const std::string& argument) { dataSegmentFile = argument;})
- .add_positional("INFILE", Options::Arguments::One,
- [&infile](Options *o, const std::string& argument) {
- infile = argument;
- });
+ .add("--output",
+ "-o",
+ "Output file",
+ Options::Arguments::One,
+ [&outfile](Options*, const std::string& argument) {
+ outfile = argument;
+ Colors::disable();
+ })
+ .add("--debuginfo",
+ "-g",
+ "Emit names section in wasm binary (or full debuginfo in wast)",
+ Options::Arguments::Zero,
+ [&debugInfo](Options*, const std::string&) { debugInfo = true; })
+ .add("--emit-text",
+ "-S",
+ "Emit text instead of binary for the output file",
+ Options::Arguments::Zero,
+ [&emitBinary](Options*, const std::string&) { emitBinary = false; })
+ .add("--global-base",
+ "",
+ "The address at which static globals were placed",
+ Options::Arguments::One,
+ [&globalBase](Options*, const std::string& argument) {
+ globalBase = std::stoull(argument);
+ })
+ .add("--initial-stack-pointer",
+ "",
+ "The initial location of the stack pointer",
+ Options::Arguments::One,
+ [&initialStackPointer](Options*, const std::string& argument) {
+ initialStackPointer = std::stoull(argument);
+ })
+ .add("--side-module",
+ "",
+ "Input is an emscripten side module",
+ Options::Arguments::Zero,
+ [&isSideModule](Options* o, const std::string& argument) {
+ isSideModule = true;
+ })
+ .add("--input-source-map",
+ "-ism",
+ "Consume source map from the specified file",
+ Options::Arguments::One,
+ [&inputSourceMapFilename](Options* o, const std::string& argument) {
+ inputSourceMapFilename = argument;
+ })
+ .add("--no-legalize-javascript-ffi",
+ "-nj",
+ "Do not fully legalize (i64->i32, "
+ "f32->f64) the imports and exports for interfacing with JS",
+ Options::Arguments::Zero,
+ [&legalizeJavaScriptFFI](Options* o, const std::string&) {
+ legalizeJavaScriptFFI = false;
+ })
+ .add("--output-source-map",
+ "-osm",
+ "Emit source map to the specified file",
+ Options::Arguments::One,
+ [&outputSourceMapFilename](Options* o, const std::string& argument) {
+ outputSourceMapFilename = argument;
+ })
+ .add("--output-source-map-url",
+ "-osu",
+ "Emit specified string as source map URL",
+ Options::Arguments::One,
+ [&outputSourceMapUrl](Options* o, const std::string& argument) {
+ outputSourceMapUrl = argument;
+ })
+ .add("--separate-data-segments",
+ "",
+ "Separate data segments to a file",
+ Options::Arguments::One,
+ [&dataSegmentFile](Options* o, const std::string& argument) {
+ dataSegmentFile = argument;
+ })
+ .add_positional("INFILE",
+ Options::Arguments::One,
+ [&infile](Options* o, const std::string& argument) {
+ infile = argument;
+ });
options.parse(argc, argv);
if (infile == "") {
@@ -167,10 +193,12 @@ int main(int argc, const char *argv[]) {
std::vector<Name> initializerFunctions;
if (wasm.table.imported()) {
- if (wasm.table.base != "table") wasm.table.base = Name("table");
+ if (wasm.table.base != "table")
+ wasm.table.base = Name("table");
}
if (wasm.memory.imported()) {
- if (wasm.table.base != "memory") wasm.memory.base = Name("memory");
+ if (wasm.table.base != "memory")
+ wasm.memory.base = Name("memory");
}
wasm.updateMaps();
@@ -204,13 +232,13 @@ int main(int argc, const char *argv[]) {
passRunner.setDebugInfo(debugInfo);
passRunner.add(ABI::getLegalizationPass(
legalizeJavaScriptFFI ? ABI::LegalizationLevel::Full
- : ABI::LegalizationLevel::Minimal
- ));
+ : ABI::LegalizationLevel::Minimal));
passRunner.run();
}
// Substantial changes to the wasm are done, enough to create the metadata.
- std::string metadata = generator.generateEmscriptenMetadata(dataSize, initializerFunctions);
+ std::string metadata =
+ generator.generateEmscriptenMetadata(dataSize, initializerFunctions);
// Finally, separate out data segments if relevant (they may have been needed
// for metadata).
diff --git a/src/tools/wasm-metadce.cpp b/src/tools/wasm-metadce.cpp
index adb623ea0..a6f5bb012 100644
--- a/src/tools/wasm-metadce.cpp
+++ b/src/tools/wasm-metadce.cpp
@@ -26,14 +26,14 @@
#include <memory>
+#include "ir/module-utils.h"
#include "pass.h"
+#include "support/colors.h"
#include "support/command-line.h"
#include "support/file.h"
#include "support/json.h"
-#include "support/colors.h"
-#include "wasm-io.h"
#include "wasm-builder.h"
-#include "ir/module-utils.h"
+#include "wasm-io.h"
using namespace wasm;
@@ -51,9 +51,10 @@ struct MetaDCEGraph {
std::unordered_map<Name, DCENode> nodes;
std::unordered_set<Name> roots;
- std::unordered_map<Name, Name> exportToDCENode; // export exported name => DCE name
+ // export exported name => DCE name
+ std::unordered_map<Name, Name> exportToDCENode;
std::unordered_map<Name, Name> functionToDCENode; // function name => DCE name
- std::unordered_map<Name, Name> globalToDCENode; // global name => DCE name
+ std::unordered_map<Name, Name> globalToDCENode; // global name => DCE name
std::unordered_map<Name, Name> DCENodeToExport; // reverse maps
std::unordered_map<Name, Name> DCENodeToFunction;
@@ -79,18 +80,20 @@ struct MetaDCEGraph {
return getImportId(imp->module, imp->base);
}
- std::unordered_map<Name, Name> importIdToDCENode; // import module.base => DCE name
+ // import module.base => DCE name
+ std::unordered_map<Name, Name> importIdToDCENode;
Module& wasm;
MetaDCEGraph(Module& wasm) : wasm(wasm) {}
- // populate the graph with info from the wasm, integrating with potentially-existing
- // nodes for imports and exports that the graph may already contain.
+ // populate the graph with info from the wasm, integrating with
+ // potentially-existing nodes for imports and exports that the graph may
+ // already contain.
void scanWebAssembly() {
// Add an entry for everything we might need ahead of time, so parallel work
- // does not alter parent state, just adds to things pointed by it, independently
- // (each thread will add for one function, etc.)
+ // does not alter parent state, just adds to things pointed by it,
+ // independently (each thread will add for one function, etc.)
ModuleUtils::iterDefinedFunctions(wasm, [&](Function* func) {
auto dceName = getName("func", func->name.str);
DCENodeToFunction[dceName] = func->name;
@@ -103,7 +106,8 @@ struct MetaDCEGraph {
globalToDCENode[global->name] = dceName;
nodes[dceName] = DCENode(dceName);
});
- // only process function and global imports - the table and memory are always there
+ // only process function and global imports - the table and memory are
+ // always there
ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) {
auto id = getImportId(import->module, import->base);
if (importIdToDCENode.find(id) == importIdToDCENode.end()) {
@@ -131,13 +135,15 @@ struct MetaDCEGraph {
if (!wasm.getFunction(exp->value)->imported()) {
node.reaches.push_back(functionToDCENode[exp->value]);
} else {
- node.reaches.push_back(importIdToDCENode[getFunctionImportId(exp->value)]);
+ node.reaches.push_back(
+ importIdToDCENode[getFunctionImportId(exp->value)]);
}
} else if (exp->kind == ExternalKind::Global) {
if (!wasm.getGlobal(exp->value)->imported()) {
node.reaches.push_back(globalToDCENode[exp->value]);
} else {
- node.reaches.push_back(importIdToDCENode[getGlobalImportId(exp->value)]);
+ node.reaches.push_back(
+ importIdToDCENode[getGlobalImportId(exp->value)]);
}
}
}
@@ -145,14 +151,11 @@ struct MetaDCEGraph {
// if we provide a parent DCE name, that is who can reach what we see
// if none is provided, then it is something we must root
struct InitScanner : public PostWalker<InitScanner> {
- InitScanner(MetaDCEGraph* parent, Name parentDceName) : parent(parent), parentDceName(parentDceName) {}
+ InitScanner(MetaDCEGraph* parent, Name parentDceName)
+ : parent(parent), parentDceName(parentDceName) {}
- void visitGetGlobal(GetGlobal* curr) {
- handleGlobal(curr->name);
- }
- void visitSetGlobal(SetGlobal* curr) {
- handleGlobal(curr->name);
- }
+ void visitGetGlobal(GetGlobal* curr) { handleGlobal(curr->name); }
+ void visitSetGlobal(SetGlobal* curr) { handleGlobal(curr->name); }
private:
MetaDCEGraph* parent;
@@ -206,34 +209,29 @@ struct MetaDCEGraph {
Scanner(MetaDCEGraph* parent) : parent(parent) {}
- Scanner* create() override {
- return new Scanner(parent);
- }
+ Scanner* create() override { return new Scanner(parent); }
void visitCall(Call* curr) {
if (!getModule()->getFunction(curr->target)->imported()) {
- parent->nodes[parent->functionToDCENode[getFunction()->name]].reaches.push_back(
- parent->functionToDCENode[curr->target]
- );
+ parent->nodes[parent->functionToDCENode[getFunction()->name]]
+ .reaches.push_back(parent->functionToDCENode[curr->target]);
} else {
assert(parent->functionToDCENode.count(getFunction()->name) > 0);
- parent->nodes[parent->functionToDCENode[getFunction()->name]].reaches.push_back(
- parent->importIdToDCENode[parent->getFunctionImportId(curr->target)]
- );
+ parent->nodes[parent->functionToDCENode[getFunction()->name]]
+ .reaches.push_back(
+ parent
+ ->importIdToDCENode[parent->getFunctionImportId(curr->target)]);
}
}
- void visitGetGlobal(GetGlobal* curr) {
- handleGlobal(curr->name);
- }
- void visitSetGlobal(SetGlobal* curr) {
- handleGlobal(curr->name);
- }
+ void visitGetGlobal(GetGlobal* curr) { handleGlobal(curr->name); }
+ void visitSetGlobal(SetGlobal* curr) { handleGlobal(curr->name); }
private:
MetaDCEGraph* parent;
void handleGlobal(Name name) {
- if (!getFunction()) return; // non-function stuff (initializers) are handled separately
+ if (!getFunction())
+ return; // non-function stuff (initializers) are handled separately
Name dceName;
if (!getModule()->getGlobal(name)->imported()) {
// its a global
@@ -242,7 +240,8 @@ struct MetaDCEGraph {
// it's an import.
dceName = parent->importIdToDCENode[parent->getGlobalImportId(name)];
}
- parent->nodes[parent->functionToDCENode[getFunction()->name]].reaches.push_back(dceName);
+ parent->nodes[parent->functionToDCENode[getFunction()->name]]
+ .reaches.push_back(dceName);
}
};
@@ -256,7 +255,8 @@ private:
// gets a unique name for the graph
Name getName(std::string prefix1, std::string prefix2) {
while (1) {
- auto curr = Name(prefix1 + '$' + prefix2 + '$' + std::to_string(nameIndex++));
+ auto curr =
+ Name(prefix1 + '$' + prefix2 + '$' + std::to_string(nameIndex++));
if (nodes.find(curr) == nodes.end()) {
return curr;
}
@@ -305,7 +305,8 @@ public:
// Now they are gone, standard optimization passes can do the rest!
PassRunner passRunner(&wasm);
passRunner.add("remove-unused-module-elements");
- passRunner.add("reorder-functions"); // removing functions may alter the optimum order, as # of calls can change
+ // removing functions may alter the optimum order, as # of calls can change
+ passRunner.add("reorder-functions");
passRunner.run();
}
@@ -344,7 +345,8 @@ public:
std::cout << " is import " << importMap[name] << '\n';
}
if (DCENodeToExport.find(name) != DCENodeToExport.end()) {
- std::cout << " is export " << DCENodeToExport[name].str << ", " << wasm.getExport(DCENodeToExport[name])->value << '\n';
+ std::cout << " is export " << DCENodeToExport[name].str << ", "
+ << wasm.getExport(DCENodeToExport[name])->value << '\n';
}
if (DCENodeToFunction.find(name) != DCENodeToFunction.end()) {
std::cout << " is function " << DCENodeToFunction[name] << '\n';
@@ -372,86 +374,99 @@ int main(int argc, const char* argv[]) {
std::string graphFile;
bool dump = false;
- Options options("wasm-metadce", "This tool performs dead code elimination (DCE) on a larger space "
- "that the wasm module is just a part of. For example, if you have "
- "JS and wasm that are connected, this can DCE the combined graph. "
- "By doing so, it is able to eliminate wasm module exports, which "
- "otherwise regular optimizations cannot.\n\n"
- "This tool receives a representation of the reachability graph "
- "that the wasm module resides in, which contains abstract nodes "
- "and connections showing what they reach. Some of those nodes "
- "can represent the wasm module's imports and exports. The tool "
- "then completes the graph by adding the internal parts of the "
- "module, and does DCE on the entire thing.\n\n"
- "This tool will output a wasm module with dead code eliminated, "
- "and metadata describing the things in the rest of the graph "
- "that can be eliminated as well.\n\n"
- "The graph description file should represent the graph in the following "
- "JSON-like notation (note, this is not true JSON, things like "
- "comments, escaping, single-quotes, etc. are not supported):\n\n"
- " [\n"
- " {\n"
- " \"name\": \"entity1\",\n"
- " \"reaches\": [\"entity2, \"entity3\"],\n"
- " \"root\": true\n"
- " },\n"
- " {\n"
- " \"name\": \"entity2\",\n"
- " \"reaches\": [\"entity1, \"entity4\"]\n"
- " },\n"
- " {\n"
- " \"name\": \"entity3\",\n"
- " \"reaches\": [\"entity1\"],\n"
- " \"export\": \"export1\"\n"
- " },\n"
- " {\n"
- " \"name\": \"entity4\",\n"
- " \"import\": [\"module\", \"import1\"]\n"
- " },\n"
- " ]\n\n"
- "Each entity has a name and an optional list of the other "
- "entities it reaches. It can also be marked as a root, "
- "export (with the export string), or import (with the "
- "module and import strings). DCE then computes what is "
- "reachable from the roots.");
+ Options options(
+ "wasm-metadce",
+ "This tool performs dead code elimination (DCE) on a larger space "
+ "that the wasm module is just a part of. For example, if you have "
+ "JS and wasm that are connected, this can DCE the combined graph. "
+ "By doing so, it is able to eliminate wasm module exports, which "
+ "otherwise regular optimizations cannot.\n\n"
+ "This tool receives a representation of the reachability graph "
+ "that the wasm module resides in, which contains abstract nodes "
+ "and connections showing what they reach. Some of those nodes "
+ "can represent the wasm module's imports and exports. The tool "
+ "then completes the graph by adding the internal parts of the "
+ "module, and does DCE on the entire thing.\n\n"
+ "This tool will output a wasm module with dead code eliminated, "
+ "and metadata describing the things in the rest of the graph "
+ "that can be eliminated as well.\n\n"
+ "The graph description file should represent the graph in the following "
+ "JSON-like notation (note, this is not true JSON, things like "
+ "comments, escaping, single-quotes, etc. are not supported):\n\n"
+ " [\n"
+ " {\n"
+ " \"name\": \"entity1\",\n"
+ " \"reaches\": [\"entity2, \"entity3\"],\n"
+ " \"root\": true\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"entity2\",\n"
+ " \"reaches\": [\"entity1, \"entity4\"]\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"entity3\",\n"
+ " \"reaches\": [\"entity1\"],\n"
+ " \"export\": \"export1\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"entity4\",\n"
+ " \"import\": [\"module\", \"import1\"]\n"
+ " },\n"
+ " ]\n\n"
+ "Each entity has a name and an optional list of the other "
+ "entities it reaches. It can also be marked as a root, "
+ "export (with the export string), or import (with the "
+ "module and import strings). DCE then computes what is "
+ "reachable from the roots.");
options
- .add("--output", "-o", "Output file (stdout if not specified)",
- Options::Arguments::One,
- [](Options* o, const std::string& argument) {
- o->extra["output"] = argument;
- Colors::disable();
- })
- .add("--emit-text", "-S", "Emit text instead of binary for the output file",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& argument) { emitBinary = false; })
- .add("--debuginfo", "-g", "Emit names section and debug info",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { debugInfo = true; })
- .add("--graph-file", "-f", "Filename of the graph description file",
- Options::Arguments::One,
- [&](Options* o, const std::string& argument) {
- graphFile = argument;
- })
- .add("--dump", "-d", "Dump the combined graph file (useful for debugging)",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { dump = true; })
- .add_positional("INFILE", Options::Arguments::One,
- [](Options* o, const std::string& argument) {
- o->extra["infile"] = argument;
- });
+ .add("--output",
+ "-o",
+ "Output file (stdout if not specified)",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["output"] = argument;
+ Colors::disable();
+ })
+ .add("--emit-text",
+ "-S",
+ "Emit text instead of binary for the output file",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { emitBinary = false; })
+ .add("--debuginfo",
+ "-g",
+ "Emit names section and debug info",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) { debugInfo = true; })
+ .add("--graph-file",
+ "-f",
+ "Filename of the graph description file",
+ Options::Arguments::One,
+ [&](Options* o, const std::string& argument) { graphFile = argument; })
+ .add("--dump",
+ "-d",
+ "Dump the combined graph file (useful for debugging)",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) { dump = true; })
+ .add_positional("INFILE",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["infile"] = argument;
+ });
options.parse(argc, argv);
if (graphFile.size() == 0) {
Fatal() << "no graph file provided.";
}
- auto input(read_file<std::string>(options.extra["infile"], Flags::Text, Flags::Release));
+ auto input(read_file<std::string>(
+ options.extra["infile"], Flags::Text, Flags::Release));
Module wasm;
{
- if (options.debug) std::cerr << "reading...\n";
+ if (options.debug)
+ std::cerr << "reading...\n";
ModuleReader reader;
reader.setDebug(options.debug);
@@ -463,32 +478,36 @@ int main(int argc, const char* argv[]) {
}
}
- auto graphInput(read_file<std::string>(graphFile, Flags::Text, Flags::Release));
+ auto graphInput(
+ read_file<std::string>(graphFile, Flags::Text, Flags::Release));
auto* copy = strdup(graphInput.c_str());
json::Value outside;
outside.parse(copy);
// parse the JSON into our graph, doing all the JSON parsing here, leaving
// the abstract computation for the class itself
- const json::IString NAME("name"),
- REACHES("reaches"),
- ROOT("root"),
- EXPORT("export"),
- IMPORT("import");
+ const json::IString NAME("name");
+ const json::IString REACHES("reaches");
+ const json::IString ROOT("root");
+ const json::IString EXPORT("export");
+ const json::IString IMPORT("import");
MetaDCEGraph graph(wasm);
if (!outside.isArray()) {
- Fatal() << "input graph must be a JSON array of nodes. see --help for the form";
+ Fatal()
+ << "input graph must be a JSON array of nodes. see --help for the form";
}
auto size = outside.size();
for (size_t i = 0; i < size; i++) {
json::Ref ref = outside[i];
if (!ref->isObject()) {
- Fatal() << "nodes in input graph must be JSON objects. see --help for the form";
+ Fatal()
+ << "nodes in input graph must be JSON objects. see --help for the form";
}
if (!ref->has(NAME)) {
- Fatal() << "nodes in input graph must have a name. see --help for the form";
+ Fatal()
+ << "nodes in input graph must have a name. see --help for the form";
}
DCENode node(ref[NAME]->getIString());
if (ref->has(REACHES)) {
@@ -500,7 +519,8 @@ int main(int argc, const char* argv[]) {
for (size_t j = 0; j < size; j++) {
json::Ref name = reaches[j];
if (!name->isString()) {
- Fatal() << "node.reaches items must be strings. see --help for the form";
+ Fatal()
+ << "node.reaches items must be strings. see --help for the form";
}
node.reaches.push_back(name->getIString());
}
@@ -508,22 +528,26 @@ int main(int argc, const char* argv[]) {
if (ref->has(ROOT)) {
json::Ref root = ref[ROOT];
if (!root->isBool() || !root->getBool()) {
- Fatal() << "node.root, if it exists, must be true. see --help for the form";
+ Fatal()
+ << "node.root, if it exists, must be true. see --help for the form";
}
graph.roots.insert(node.name);
}
if (ref->has(EXPORT)) {
json::Ref exp = ref[EXPORT];
if (!exp->isString()) {
- Fatal() << "node.export, if it exists, must be a string. see --help for the form";
+ Fatal() << "node.export, if it exists, must be a string. see --help "
+ "for the form";
}
graph.exportToDCENode[exp->getIString()] = node.name;
graph.DCENodeToExport[node.name] = exp->getIString();
}
if (ref->has(IMPORT)) {
json::Ref imp = ref[IMPORT];
- if (!imp->isArray() || imp->size() != 2 || !imp[0]->isString() || !imp[1]->isString()) {
- Fatal() << "node.import, if it exists, must be an array of two strings. see --help for the form";
+ if (!imp->isArray() || imp->size() != 2 || !imp[0]->isString() ||
+ !imp[1]->isString()) {
+ Fatal() << "node.import, if it exists, must be an array of two "
+ "strings. see --help for the form";
}
auto id = graph.getImportId(imp[0]->getIString(), imp[1]->getIString());
graph.importIdToDCENode[id] = node.name;
diff --git a/src/tools/wasm-opt.cpp b/src/tools/wasm-opt.cpp
index 519713298..e9f369d22 100644
--- a/src/tools/wasm-opt.cpp
+++ b/src/tools/wasm-opt.cpp
@@ -21,21 +21,21 @@
#include <memory>
+#include "execution-results.h"
+#include "fuzzing.h"
+#include "js-wrapper.h"
+#include "optimization-options.h"
#include "pass.h"
+#include "shell-interface.h"
+#include "spec-wrapper.h"
#include "support/command-line.h"
#include "support/file.h"
+#include "wasm-binary.h"
+#include "wasm-interpreter.h"
+#include "wasm-io.h"
#include "wasm-printing.h"
#include "wasm-s-parser.h"
#include "wasm-validator.h"
-#include "wasm-io.h"
-#include "wasm-interpreter.h"
-#include "wasm-binary.h"
-#include "shell-interface.h"
-#include "optimization-options.h"
-#include "execution-results.h"
-#include "fuzzing.h"
-#include "js-wrapper.h"
-#include "spec-wrapper.h"
using namespace wasm;
@@ -45,7 +45,7 @@ std::string runCommand(std::string command) {
std::string output;
const int MAX_BUFFER = 1024;
char buffer[MAX_BUFFER];
- FILE *stream = popen(command.c_str(), "r");
+ FILE* stream = popen(command.c_str(), "r");
while (fgets(buffer, MAX_BUFFER, stream) != NULL) {
output.append(buffer);
}
@@ -81,69 +81,130 @@ int main(int argc, const char* argv[]) {
OptimizationOptions options("wasm-opt", "Read, write, and optimize files");
options
- .add("--output", "-o", "Output file (stdout if not specified)",
- Options::Arguments::One,
- [](Options* o, const std::string& argument) {
- o->extra["output"] = argument;
- Colors::disable();
- })
- .add("--emit-text", "-S", "Emit text instead of binary for the output file",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& argument) { emitBinary = false; })
- .add("--debuginfo", "-g", "Emit names section and debug info",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { debugInfo = true; })
- .add("--converge", "-c", "Run passes to convergence, continuing while binary size decreases",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { converge = true; })
- .add("--fuzz-exec-before", "-feh", "Execute functions before optimization, helping fuzzing find bugs",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { fuzzExecBefore = true; })
- .add("--fuzz-exec", "-fe", "Execute functions before and after optimization, helping fuzzing find bugs",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { fuzzExecBefore = fuzzExecAfter = true; })
- .add("--fuzz-binary", "-fb", "Convert to binary and back after optimizations and before fuzz-exec, helping fuzzing find binary format bugs",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { fuzzBinary = true; })
- .add("--extra-fuzz-command", "-efc", "An extra command to run on the output before and after optimizing. The output is compared between the two, and an error occurs if they are not equal",
- Options::Arguments::One,
- [&](Options *o, const std::string& arguments) { extraFuzzCommand = arguments; })
- .add("--translate-to-fuzz", "-ttf", "Translate the input into a valid wasm module *somehow*, useful for fuzzing",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { translateToFuzz = true; })
- .add("--fuzz-passes", "-fp", "Pick a random set of passes to run, useful for fuzzing. this depends on translate-to-fuzz (it picks the passes from the input)",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { fuzzPasses = true; })
- .add("--no-fuzz-nans", "", "don't emit NaNs when fuzzing, and remove them at runtime as well (helps avoid nondeterminism between VMs)",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { fuzzNaNs = false; })
- .add("--no-fuzz-memory", "", "don't emit memory ops when fuzzing",
- Options::Arguments::Zero,
- [&](Options *o, const std::string& arguments) { fuzzMemory = false; })
- .add("--emit-js-wrapper", "-ejw", "Emit a JavaScript wrapper file that can run the wasm with some test values, useful for fuzzing",
- Options::Arguments::One,
- [&](Options *o, const std::string& arguments) { emitJSWrapper = arguments; })
- .add("--emit-spec-wrapper", "-esw", "Emit a wasm spec interpreter wrapper file that can run the wasm with some test values, useful for fuzzing",
- Options::Arguments::One,
- [&](Options *o, const std::string& arguments) { emitSpecWrapper = arguments; })
- .add("--input-source-map", "-ism", "Consume source map from the specified file",
- Options::Arguments::One,
- [&inputSourceMapFilename](Options *o, const std::string& argument) { inputSourceMapFilename = argument; })
- .add("--output-source-map", "-osm", "Emit source map to the specified file",
- Options::Arguments::One,
- [&outputSourceMapFilename](Options *o, const std::string& argument) { outputSourceMapFilename = argument; })
- .add("--output-source-map-url", "-osu", "Emit specified string as source map URL",
- Options::Arguments::One,
- [&outputSourceMapUrl](Options *o, const std::string& argument) { outputSourceMapUrl = argument; })
- .add_positional("INFILE", Options::Arguments::One,
- [](Options* o, const std::string& argument) {
- o->extra["infile"] = argument;
- });
+ .add("--output",
+ "-o",
+ "Output file (stdout if not specified)",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["output"] = argument;
+ Colors::disable();
+ })
+ .add("--emit-text",
+ "-S",
+ "Emit text instead of binary for the output file",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { emitBinary = false; })
+ .add("--debuginfo",
+ "-g",
+ "Emit names section and debug info",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) { debugInfo = true; })
+ .add("--converge",
+ "-c",
+ "Run passes to convergence, continuing while binary size decreases",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) { converge = true; })
+ .add(
+ "--fuzz-exec-before",
+ "-feh",
+ "Execute functions before optimization, helping fuzzing find bugs",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) { fuzzExecBefore = true; })
+ .add("--fuzz-exec",
+ "-fe",
+ "Execute functions before and after optimization, helping fuzzing "
+ "find bugs",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) {
+ fuzzExecBefore = fuzzExecAfter = true;
+ })
+ .add("--fuzz-binary",
+ "-fb",
+ "Convert to binary and back after optimizations and before fuzz-exec, "
+ "helping fuzzing find binary format bugs",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) { fuzzBinary = true; })
+ .add("--extra-fuzz-command",
+ "-efc",
+ "An extra command to run on the output before and after optimizing. "
+ "The output is compared between the two, and an error occurs if they "
+ "are not equal",
+ Options::Arguments::One,
+ [&](Options* o, const std::string& arguments) {
+ extraFuzzCommand = arguments;
+ })
+ .add(
+ "--translate-to-fuzz",
+ "-ttf",
+ "Translate the input into a valid wasm module *somehow*, useful for "
+ "fuzzing",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) { translateToFuzz = true; })
+ .add("--fuzz-passes",
+ "-fp",
+ "Pick a random set of passes to run, useful for fuzzing. this depends "
+ "on translate-to-fuzz (it picks the passes from the input)",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) { fuzzPasses = true; })
+ .add("--no-fuzz-nans",
+ "",
+ "don't emit NaNs when fuzzing, and remove them at runtime as well "
+ "(helps avoid nondeterminism between VMs)",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) { fuzzNaNs = false; })
+ .add("--no-fuzz-memory",
+ "",
+ "don't emit memory ops when fuzzing",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) { fuzzMemory = false; })
+ .add("--emit-js-wrapper",
+ "-ejw",
+ "Emit a JavaScript wrapper file that can run the wasm with some test "
+ "values, useful for fuzzing",
+ Options::Arguments::One,
+ [&](Options* o, const std::string& arguments) {
+ emitJSWrapper = arguments;
+ })
+ .add("--emit-spec-wrapper",
+ "-esw",
+ "Emit a wasm spec interpreter wrapper file that can run the wasm with "
+ "some test values, useful for fuzzing",
+ Options::Arguments::One,
+ [&](Options* o, const std::string& arguments) {
+ emitSpecWrapper = arguments;
+ })
+ .add("--input-source-map",
+ "-ism",
+ "Consume source map from the specified file",
+ Options::Arguments::One,
+ [&inputSourceMapFilename](Options* o, const std::string& argument) {
+ inputSourceMapFilename = argument;
+ })
+ .add("--output-source-map",
+ "-osm",
+ "Emit source map to the specified file",
+ Options::Arguments::One,
+ [&outputSourceMapFilename](Options* o, const std::string& argument) {
+ outputSourceMapFilename = argument;
+ })
+ .add("--output-source-map-url",
+ "-osu",
+ "Emit specified string as source map URL",
+ Options::Arguments::One,
+ [&outputSourceMapUrl](Options* o, const std::string& argument) {
+ outputSourceMapUrl = argument;
+ })
+ .add_positional("INFILE",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["infile"] = argument;
+ });
options.parse(argc, argv);
Module wasm;
- if (options.debug) std::cerr << "reading...\n";
+ if (options.debug)
+ std::cerr << "reading...\n";
if (!translateToFuzz) {
ModuleReader reader;
@@ -159,7 +220,8 @@ int main(int argc, const char* argv[]) {
std::cerr << '\n';
Fatal() << "error in parsing wasm source map";
} catch (std::bad_alloc&) {
- Fatal() << "error in building module, std::bad_alloc (possibly invalid request for silly amounts of memory)";
+ Fatal() << "error in building module, std::bad_alloc (possibly invalid "
+ "request for silly amounts of memory)";
}
options.applyFeatures(wasm);
@@ -218,7 +280,9 @@ int main(int argc, const char* argv[]) {
std::string firstOutput;
if (extraFuzzCommand.size() > 0 && options.extra.count("output") > 0) {
- if (options.debug) std::cerr << "writing binary before opts, for extra fuzz command..." << std::endl;
+ if (options.debug)
+ std::cerr << "writing binary before opts, for extra fuzz command..."
+ << std::endl;
ModuleWriter writer;
writer.setDebug(options.debug);
writer.setBinary(emitBinary);
@@ -252,12 +316,12 @@ int main(int argc, const char* argv[]) {
}
if (options.runningPasses()) {
- if (options.debug) std::cerr << "running passes...\n";
+ if (options.debug)
+ std::cerr << "running passes...\n";
auto runPasses = [&]() {
options.runPasses(*curr);
if (options.passOptions.validate) {
- bool valid =
- WasmValidator().validate(*curr);
+ bool valid = WasmValidator().validate(*curr);
if (!valid) {
WasmPrinter::printModule(&*curr);
}
@@ -276,10 +340,13 @@ int main(int argc, const char* argv[]) {
};
auto lastSize = getSize();
while (1) {
- if (options.debug) std::cerr << "running iteration for convergence (" << lastSize << ")...\n";
+ if (options.debug)
+ std::cerr << "running iteration for convergence (" << lastSize
+ << ")...\n";
runPasses();
auto currSize = getSize();
- if (currSize >= lastSize) break;
+ if (currSize >= lastSize)
+ break;
lastSize = currSize;
}
}
@@ -292,7 +359,8 @@ int main(int argc, const char* argv[]) {
if (options.extra.count("output") == 0) {
std::cerr << "(no output file specified, not emitting output)\n";
} else {
- if (options.debug) std::cerr << "writing..." << std::endl;
+ if (options.debug)
+ std::cerr << "writing..." << std::endl;
ModuleWriter writer;
writer.setDebug(options.debug);
writer.setBinary(emitBinary);
@@ -305,7 +373,8 @@ int main(int argc, const char* argv[]) {
if (extraFuzzCommand.size() > 0) {
auto secondOutput = runCommand(extraFuzzCommand);
- std::cout << "[extra-fuzz-command second output:]\n" << firstOutput << '\n';
+ std::cout << "[extra-fuzz-command second output:]\n"
+ << firstOutput << '\n';
if (firstOutput != secondOutput) {
std::cerr << "extra fuzz command output differs\n";
abort();
diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp
index e064abbcb..87f64c1ae 100644
--- a/src/tools/wasm-reduce.cpp
+++ b/src/tools/wasm-reduce.cpp
@@ -23,22 +23,22 @@
// much more debuggable manner).
//
-#include <memory>
#include <cstdio>
#include <cstdlib>
+#include <memory>
+#include "ir/branch-utils.h"
+#include "ir/iteration.h"
+#include "ir/literal-utils.h"
+#include "ir/properties.h"
#include "pass.h"
-#include "support/command-line.h"
#include "support/colors.h"
+#include "support/command-line.h"
#include "support/file.h"
#include "support/path.h"
#include "support/timing.h"
-#include "wasm-io.h"
#include "wasm-builder.h"
-#include "ir/branch-utils.h"
-#include "ir/iteration.h"
-#include "ir/literal-utils.h"
-#include "ir/properties.h"
+#include "wasm-io.h"
#include "wasm-validator.h"
#ifdef _WIN32
#ifndef NOMINMAX
@@ -50,18 +50,18 @@ std::string GetLastErrorStdStr() {
DWORD error = GetLastError();
if (error) {
LPVOID lpMsgBuf;
- DWORD bufLen = FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- error,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR) &lpMsgBuf,
- 0, NULL );
+ DWORD bufLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&lpMsgBuf,
+ 0,
+ NULL);
if (bufLen) {
LPCSTR lpMsgStr = (LPCSTR)lpMsgBuf;
- std::string result(lpMsgStr, lpMsgStr+bufLen);
+ std::string result(lpMsgStr, lpMsgStr + bufLen);
LocalFree(lpMsgBuf);
return result;
}
@@ -80,9 +80,7 @@ struct ProgramResult {
double time;
ProgramResult() = default;
- ProgramResult(std::string command) {
- getFromExecution(command);
- }
+ ProgramResult(std::string command) { getFromExecution(command); }
#ifdef _WIN32
void getFromExecution(std::string command) {
@@ -100,9 +98,9 @@ struct ProgramResult {
// Create a pipe for the child process's STDOUT.
!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &saAttr, 0) ||
// Ensure the read handle to the pipe for STDOUT is not inherited.
- !SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)
- ) {
- Fatal() << "CreatePipe \"" << command << "\" failed: " << GetLastErrorStdStr() << ".\n";
+ !SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
+ Fatal() << "CreatePipe \"" << command
+ << "\" failed: " << GetLastErrorStdStr() << ".\n";
}
STARTUPINFO si;
@@ -116,18 +114,19 @@ struct ProgramResult {
ZeroMemory(&pi, sizeof(pi));
// Start the child process.
- if (!CreateProcess(NULL, // No module name (use command line)
- (LPSTR)command.c_str(),// Command line
- NULL, // Process handle not inheritable
- NULL, // Thread handle not inheritable
- TRUE, // Set handle inheritance to TRUE
- 0, // No creation flags
- NULL, // Use parent's environment block
- NULL, // Use parent's starting directory
- &si, // Pointer to STARTUPINFO structure
- &pi ) // Pointer to PROCESS_INFORMATION structure
+ if (!CreateProcess(NULL, // No module name (use command line)
+ (LPSTR)command.c_str(), // Command line
+ NULL, // Process handle not inheritable
+ NULL, // Thread handle not inheritable
+ TRUE, // Set handle inheritance to TRUE
+ 0, // No creation flags
+ NULL, // Use parent's environment block
+ NULL, // Use parent's starting directory
+ &si, // Pointer to STARTUPINFO structure
+ &pi) // Pointer to PROCESS_INFORMATION structure
) {
- Fatal() << "CreateProcess \"" << command << "\" failed: " << GetLastErrorStdStr() << ".\n";
+ Fatal() << "CreateProcess \"" << command
+ << "\" failed: " << GetLastErrorStdStr() << ".\n";
}
// Wait until child process exits.
@@ -156,8 +155,10 @@ struct ProgramResult {
PeekNamedPipe(hChildStd_OUT_Rd, NULL, 0, NULL, &dwTotal, NULL);
while (dwTotalRead < dwTotal) {
- bSuccess = ReadFile(hChildStd_OUT_Rd, chBuf, BUFSIZE - 1, &dwRead, NULL);
- if (!bSuccess || dwRead == 0) break;
+ bSuccess =
+ ReadFile(hChildStd_OUT_Rd, chBuf, BUFSIZE - 1, &dwRead, NULL);
+ if (!bSuccess || dwRead == 0)
+ break;
chBuf[dwRead] = 0;
dwTotalRead += dwRead;
output.append(chBuf);
@@ -166,7 +167,7 @@ struct ProgramResult {
timer.stop();
time = timer.getTotal();
}
-#else // POSIX
+#else // POSIX
// runs the command and notes the output
// TODO: also stderr, not just stdout?
void getFromExecution(std::string command) {
@@ -174,10 +175,15 @@ struct ProgramResult {
timer.start();
// do this using just core stdio.h and stdlib.h, for portability
// sadly this requires two invokes
- code = system(("timeout " + std::to_string(timeout) + "s " + command + " > /dev/null 2> /dev/null").c_str());
+ code = system(("timeout " + std::to_string(timeout) + "s " + command +
+ " > /dev/null 2> /dev/null")
+ .c_str());
const int MAX_BUFFER = 1024;
char buffer[MAX_BUFFER];
- FILE *stream = popen(("timeout " + std::to_string(timeout) + "s " + command + " 2> /dev/null").c_str(), "r");
+ FILE* stream = popen(
+ ("timeout " + std::to_string(timeout) + "s " + command + " 2> /dev/null")
+ .c_str(),
+ "r");
while (fgets(buffer, MAX_BUFFER, stream) != NULL) {
output.append(buffer);
}
@@ -190,16 +196,13 @@ struct ProgramResult {
bool operator==(ProgramResult& other) {
return code == other.code && output == other.output;
}
- bool operator!=(ProgramResult& other) {
- return !(*this == other);
- }
+ bool operator!=(ProgramResult& other) { return !(*this == other); }
- bool failed() {
- return code != 0;
- }
+ bool failed() { return code != 0; }
void dump(std::ostream& o) {
- o << "[ProgramResult] code: " << code << " stdout: \n" << output << "[====]\nin " << time << " seconds\n[/ProgramResult]\n";
+ o << "[ProgramResult] code: " << code << " stdout: \n"
+ << output << "[====]\nin " << time << " seconds\n[/ProgramResult]\n";
}
};
@@ -210,7 +213,7 @@ inline std::ostream& operator<<(std::ostream& o, ProgramResult& result) {
return o;
}
-}
+} // namespace std
ProgramResult expected;
@@ -219,14 +222,22 @@ ProgramResult expected;
// case we may try again but much later.
static std::unordered_set<Name> functionsWeTriedToRemove;
-struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<Reducer>>> {
+struct Reducer
+ : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<Reducer>>> {
std::string command, test, working;
bool binary, deNan, verbose, debugInfo;
// test is the file we write to that the command will operate on
// working is the current temporary state, the reduction so far
- Reducer(std::string command, std::string test, std::string working, bool binary, bool deNan, bool verbose, bool debugInfo) :
- command(command), test(test), working(working), binary(binary), deNan(deNan), verbose(verbose), debugInfo(debugInfo) {}
+ Reducer(std::string command,
+ std::string test,
+ std::string working,
+ bool binary,
+ bool deNan,
+ bool verbose,
+ bool debugInfo)
+ : command(command), test(test), working(working), binary(binary),
+ deNan(deNan), verbose(verbose), debugInfo(debugInfo) {}
// runs passes in order to reduce, until we can't reduce any more
// the criterion here is wasm binary size
@@ -261,28 +272,32 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
"--reorder-locals",
"--simplify-locals --vacuum",
"--strip",
- "--vacuum"
- };
+ "--vacuum"};
auto oldSize = file_size(working);
bool more = true;
while (more) {
- //std::cerr << "| starting passes loop iteration\n";
+ // std::cerr << "| starting passes loop iteration\n";
more = false;
- // try both combining with a generic shrink (so minor pass overhead is compensated for), and without
+ // try both combining with a generic shrink (so minor pass overhead is
+ // compensated for), and without
for (auto pass : passes) {
std::string currCommand = Path::getBinaryenBinaryTool("wasm-opt") + " ";
// TODO(tlively): -all should be replaced with an option to use the
// existing feature set, once implemented.
currCommand += working + " -all -o " + test + " " + pass;
- if (debugInfo) currCommand += " -g ";
- if (verbose) std::cerr << "| trying pass command: " << currCommand << "\n";
+ if (debugInfo)
+ currCommand += " -g ";
+ if (verbose)
+ std::cerr << "| trying pass command: " << currCommand << "\n";
if (!ProgramResult(currCommand).failed()) {
auto newSize = file_size(test);
if (newSize < oldSize) {
// the pass didn't fail, and the size looks smaller, so promising
// see if it is still has the property we are preserving
if (ProgramResult(command) == expected) {
- std::cerr << "| command \"" << currCommand << "\" succeeded, reduced size to " << newSize << ", and preserved the property\n";
+ std::cerr << "| command \"" << currCommand
+ << "\" succeeded, reduced size to " << newSize
+ << ", and preserved the property\n";
copy_file(test, working);
more = true;
oldSize = newSize;
@@ -291,7 +306,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
}
}
}
- if (verbose) std::cerr << "| done with passes for now\n";
+ if (verbose)
+ std::cerr << "| done with passes for now\n";
}
// does one pass of slow and destructive reduction. returns whether it
@@ -311,7 +327,9 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
// size should be as expected, and output should be as expected
ProgramResult result;
if (!writeAndTestReduction(result)) {
- std::cerr << "\n|! WARNING: writing before destructive reduction fails, very unlikely reduction can work\n" << result << '\n';
+ std::cerr << "\n|! WARNING: writing before destructive reduction fails, "
+ "very unlikely reduction can work\n"
+ << result << '\n';
}
// destroy!
walkModule(getModule());
@@ -381,15 +399,18 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
return false;
}
auto* curr = getCurrent();
- //std::cerr << "try " << curr << " => " << with << '\n';
- if (curr->type != with->type) return false;
- if (!shouldTryToReduce()) return false;
+ // std::cerr << "try " << curr << " => " << with << '\n';
+ if (curr->type != with->type)
+ return false;
+ if (!shouldTryToReduce())
+ return false;
replaceCurrent(with);
if (!writeAndTestReduction()) {
replaceCurrent(curr);
return false;
}
- std::cerr << "| tryToReplaceCurrent succeeded (in " << getLocation() << ")\n";
+ std::cerr << "| tryToReplaceCurrent succeeded (in " << getLocation()
+ << ")\n";
noteReduction();
return true;
}
@@ -404,38 +425,45 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
if (!isOkReplacement(with)) {
return false;
}
- if (child->type != with->type) return false;
- if (!shouldTryToReduce()) return false;
+ if (child->type != with->type)
+ return false;
+ if (!shouldTryToReduce())
+ return false;
auto* before = child;
child = with;
if (!writeAndTestReduction()) {
child = before;
return false;
}
- std::cerr << "| tryToReplaceChild succeeded (in " << getLocation() << ")\n";
- //std::cerr << "| " << before << " => " << with << '\n';
+ std::cerr << "| tryToReplaceChild succeeded (in " << getLocation()
+ << ")\n";
+ // std::cerr << "| " << before << " => " << with << '\n';
noteReduction();
return true;
}
std::string getLocation() {
- if (getFunction()) return getFunction()->name.str;
+ if (getFunction())
+ return getFunction()->name.str;
return "(non-function context)";
}
- // visitors. in each we try to remove code in a destructive and nontrivial way.
- // "nontrivial" means something that optimization passes can't achieve, since we
- // don't need to duplicate work that they do
+ // visitors. in each we try to remove code in a destructive and nontrivial
+ // way. "nontrivial" means something that optimization passes can't achieve,
+ // since we don't need to duplicate work that they do
void visitExpression(Expression* curr) {
// type-based reductions
if (curr->type == none) {
- if (tryToReduceCurrentToNop()) return;
+ if (tryToReduceCurrentToNop())
+ return;
} else if (isConcreteType(curr->type)) {
- if (tryToReduceCurrentToConst()) return;
+ if (tryToReduceCurrentToConst())
+ return;
} else {
assert(curr->type == unreachable);
- if (tryToReduceCurrentToUnreachable()) return;
+ if (tryToReduceCurrentToUnreachable())
+ return;
}
// specific reductions
if (auto* iff = curr->dynCast<If>()) {
@@ -472,11 +500,14 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
}
}
} else if (auto* block = curr->dynCast<Block>()) {
- if (!shouldTryToReduce()) return;
+ if (!shouldTryToReduce())
+ return;
// replace a singleton
auto& list = block->list;
- if (list.size() == 1 && !BranchUtils::BranchSeeker::hasNamed(block, block->name)) {
- if (tryToReplaceCurrent(block->list[0])) return;
+ if (list.size() == 1 &&
+ !BranchUtils::BranchSeeker::hasNamed(block, block->name)) {
+ if (tryToReplaceCurrent(block->list[0]))
+ return;
}
// try to get rid of nops
Index i = 0;
@@ -504,7 +535,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
}
return; // nothing more to do
} else if (auto* loop = curr->dynCast<Loop>()) {
- if (shouldTryToReduce() && !BranchUtils::BranchSeeker::hasNamed(loop, loop->name)) {
+ if (shouldTryToReduce() &&
+ !BranchUtils::BranchSeeker::hasNamed(loop, loop->name)) {
tryToReplaceCurrent(loop->body);
}
return; // nothing more to do
@@ -512,73 +544,116 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
// Finally, try to replace with a child.
for (auto* child : ChildIterator(curr)) {
if (isConcreteType(child->type) && curr->type == none) {
- if (tryToReplaceCurrent(builder->makeDrop(child))) return;
+ if (tryToReplaceCurrent(builder->makeDrop(child)))
+ return;
} else {
- if (tryToReplaceCurrent(child)) return;
+ if (tryToReplaceCurrent(child))
+ return;
}
}
// If that didn't work, try to replace with a child + a unary conversion
if (isConcreteType(curr->type) &&
!curr->is<Unary>()) { // but not if it's already unary
for (auto* child : ChildIterator(curr)) {
- if (child->type == curr->type) continue; // already tried
- if (!isConcreteType(child->type)) continue; // no conversion
+ if (child->type == curr->type)
+ continue; // already tried
+ if (!isConcreteType(child->type))
+ continue; // no conversion
Expression* fixed = nullptr;
switch (curr->type) {
case i32: {
switch (child->type) {
- case i32: WASM_UNREACHABLE();
- case i64: fixed = builder->makeUnary(WrapInt64, child); break;
- case f32: fixed = builder->makeUnary(TruncSFloat32ToInt32, child); break;
- case f64: fixed = builder->makeUnary(TruncSFloat64ToInt32, child); break;
- case v128: continue; // v128 not implemented yet
+ case i32:
+ WASM_UNREACHABLE();
+ case i64:
+ fixed = builder->makeUnary(WrapInt64, child);
+ break;
+ case f32:
+ fixed = builder->makeUnary(TruncSFloat32ToInt32, child);
+ break;
+ case f64:
+ fixed = builder->makeUnary(TruncSFloat64ToInt32, child);
+ break;
+ case v128:
+ continue; // v128 not implemented yet
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
break;
}
case i64: {
switch (child->type) {
- case i32: fixed = builder->makeUnary(ExtendSInt32, child); break;
- case i64: WASM_UNREACHABLE();
- case f32: fixed = builder->makeUnary(TruncSFloat32ToInt64, child); break;
- case f64: fixed = builder->makeUnary(TruncSFloat64ToInt64, child); break;
- case v128: continue; // v128 not implemented yet
+ case i32:
+ fixed = builder->makeUnary(ExtendSInt32, child);
+ break;
+ case i64:
+ WASM_UNREACHABLE();
+ case f32:
+ fixed = builder->makeUnary(TruncSFloat32ToInt64, child);
+ break;
+ case f64:
+ fixed = builder->makeUnary(TruncSFloat64ToInt64, child);
+ break;
+ case v128:
+ continue; // v128 not implemented yet
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
break;
}
case f32: {
switch (child->type) {
- case i32: fixed = builder->makeUnary(ConvertSInt32ToFloat32, child); break;
- case i64: fixed = builder->makeUnary(ConvertSInt64ToFloat32, child); break;
- case f32: WASM_UNREACHABLE();
- case f64: fixed = builder->makeUnary(DemoteFloat64, child); break;
- case v128: continue; // v128 not implemented yet
+ case i32:
+ fixed = builder->makeUnary(ConvertSInt32ToFloat32, child);
+ break;
+ case i64:
+ fixed = builder->makeUnary(ConvertSInt64ToFloat32, child);
+ break;
+ case f32:
+ WASM_UNREACHABLE();
+ case f64:
+ fixed = builder->makeUnary(DemoteFloat64, child);
+ break;
+ case v128:
+ continue; // v128 not implemented yet
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
break;
}
case f64: {
switch (child->type) {
- case i32: fixed = builder->makeUnary(ConvertSInt32ToFloat64, child); break;
- case i64: fixed = builder->makeUnary(ConvertSInt64ToFloat64, child); break;
- case f32: fixed = builder->makeUnary(PromoteFloat32, child); break;
- case f64: WASM_UNREACHABLE();
- case v128: continue; // v128 not implemented yet
+ case i32:
+ fixed = builder->makeUnary(ConvertSInt32ToFloat64, child);
+ break;
+ case i64:
+ fixed = builder->makeUnary(ConvertSInt64ToFloat64, child);
+ break;
+ case f32:
+ fixed = builder->makeUnary(PromoteFloat32, child);
+ break;
+ case f64:
+ WASM_UNREACHABLE();
+ case v128:
+ continue; // v128 not implemented yet
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
break;
}
- case v128: continue; // v128 not implemented yet
+ case v128:
+ continue; // v128 not implemented yet
case none:
- case unreachable: WASM_UNREACHABLE();
+ case unreachable:
+ WASM_UNREACHABLE();
}
assert(fixed->type == curr->type);
- if (tryToReplaceCurrent(fixed)) return;
+ if (tryToReplaceCurrent(fixed))
+ return;
}
}
}
@@ -609,7 +684,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
first = item;
break;
}
- if (!first.isNull()) break;
+ if (!first.isNull())
+ break;
}
visitSegmented(curr, first, 100);
}
@@ -627,12 +703,15 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
bool shrank = false;
for (auto& segment : curr->segments) {
auto& data = segment.data;
- size_t skip = 1; // when we succeed, try to shrink by more and more, similar to bisection
+ // 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;
+ if (!justShrank && !shouldTryToReduce(bonus))
+ continue;
auto save = data;
for (size_t j = 0; j < skip; j++) {
- if (!data.empty()) data.pop_back();
+ if (!data.empty())
+ data.pop_back();
}
auto justShrank = writeAndTestReduction();
if (justShrank) {
@@ -648,10 +727,13 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
}
// the "opposite" of shrinking: copy a 'zero' element
for (auto& segment : curr->segments) {
- if (segment.data.empty()) continue;
+ if (segment.data.empty())
+ continue;
for (auto& item : segment.data) {
- if (!shouldTryToReduce(bonus)) continue;
- if (item == zero) continue;
+ if (!shouldTryToReduce(bonus))
+ continue;
+ if (item == zero)
+ continue;
auto save = item;
item = zero;
if (writeAndTestReduction()) {
@@ -678,23 +760,27 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
functionNames.push_back(func->name);
}
size_t skip = 1;
- // If we just removed some functions in the previous iteration, keep trying to remove more
- // as this is one of the most efficient ways to reduce.
+ // If we just removed some functions in the previous iteration, keep trying
+ // to remove more as this is one of the most efficient ways to reduce.
bool justRemoved = false;
for (size_t i = 0; i < functionNames.size(); i++) {
if (!justRemoved &&
functionsWeTriedToRemove.count(functionNames[i]) == 1 &&
- !shouldTryToReduce(std::max((factor / 100) + 1, 1000))) continue;
+ !shouldTryToReduce(std::max((factor / 100) + 1, 1000)))
+ continue;
std::vector<Name> names;
- for (size_t j = 0; names.size() < skip && i + j < functionNames.size(); j++) {
+ for (size_t j = 0; names.size() < skip && i + j < functionNames.size();
+ j++) {
auto name = functionNames[i + j];
if (module->getFunctionOrNull(name)) {
names.push_back(name);
functionsWeTriedToRemove.insert(name);
}
}
- if (names.size() == 0) continue;
- std::cout << "| try to remove " << names.size() << " functions (skip: " << skip << ")\n";
+ if (names.size() == 0)
+ continue;
+ std::cout << "| try to remove " << names.size()
+ << " functions (skip: " << skip << ")\n";
justRemoved = tryToRemoveFunctions(names);
if (justRemoved) {
noteReduction(names.size());
@@ -712,9 +798,11 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
}
skip = 1;
for (size_t i = 0; i < exports.size(); i++) {
- if (!shouldTryToReduce(std::max((factor / 100) + 1, 1000))) continue;
+ if (!shouldTryToReduce(std::max((factor / 100) + 1, 1000)))
+ continue;
std::vector<Export> currExports;
- for (size_t j = 0; currExports.size() < skip && i + j < exports.size(); j++) {
+ for (size_t j = 0; currExports.size() < skip && i + j < exports.size();
+ j++) {
auto exp = exports[i + j];
if (module->getExportOrNull(exp.name)) {
currExports.push_back(exp);
@@ -736,7 +824,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
}
// 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.
- if (module->functions.size() == 1 && module->exports.empty() && module->table.segments.empty()) {
+ if (module->functions.size() == 1 && module->exports.empty() &&
+ module->table.segments.empty()) {
auto* func = module->functions[0].get();
// We can't remove something that might have breaks to it.
if (!func->imported() && !Properties::isNamedControlFlow(func->body)) {
@@ -773,7 +862,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
}
// remove all references to them
- struct FunctionReferenceRemover : public PostWalker<FunctionReferenceRemover> {
+ struct FunctionReferenceRemover
+ : public PostWalker<FunctionReferenceRemover> {
std::unordered_set<Name> names;
std::vector<Name> exportsToRemove;
@@ -801,9 +891,11 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
break;
}
}
- if (!other.isNull()) break;
+ if (!other.isNull())
+ break;
}
- if (other.isNull()) return; // we failed to find a replacement
+ if (other.isNull())
+ return; // we failed to find a replacement
for (auto& segment : curr->segments) {
for (auto& name : segment.data) {
if (names.count(name)) {
@@ -822,7 +914,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
FunctionReferenceRemover referenceRemover(names);
referenceRemover.walkModule(module.get());
- if (WasmValidator().validate(*module, WasmValidator::Globally | WasmValidator::Quiet) &&
+ if (WasmValidator().validate(
+ *module, WasmValidator::Globally | WasmValidator::Quiet) &&
writeAndTestReduction()) {
std::cerr << "| removed " << names.size() << " functions\n";
return true;
@@ -836,8 +929,10 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
// try to replace condition with always true and always false
void handleCondition(Expression*& condition) {
- if (!condition) return;
- if (condition->is<Const>()) return;
+ if (!condition)
+ return;
+ if (condition->is<Const>())
+ return;
auto* c = builder->makeConst(Literal(int32_t(0)));
if (!tryToReplaceChild(condition, c)) {
c->value = Literal(int32_t(1));
@@ -847,7 +942,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
bool tryToReduceCurrentToNop() {
auto* curr = getCurrent();
- if (curr->is<Nop>()) return false;
+ if (curr->is<Nop>())
+ return false;
// try to replace with a trivial value
Nop nop;
if (tryToReplaceCurrent(&nop)) {
@@ -860,10 +956,12 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
// try to replace a concrete value with a trivial constant
bool tryToReduceCurrentToConst() {
auto* curr = getCurrent();
- if (curr->is<Const>()) return false;
+ if (curr->is<Const>())
+ return false;
// try to replace with a trivial value
Const* c = builder->makeConst(Literal(int32_t(0)));
- if (tryToReplaceCurrent(c)) return true;
+ if (tryToReplaceCurrent(c))
+ return true;
c->value = Literal::makeFromInt32(1, curr->type);
c->type = curr->type;
return tryToReplaceCurrent(c);
@@ -871,7 +969,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
bool tryToReduceCurrentToUnreachable() {
auto* curr = getCurrent();
- if (curr->is<Unreachable>()) return false;
+ if (curr->is<Unreachable>())
+ return false;
// try to replace with a trivial value
Unreachable un;
if (tryToReplaceCurrent(&un)) {
@@ -889,76 +988,87 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
int main(int argc, const char* argv[]) {
std::string input, test, working, command;
- bool binary = true,
- deNan = false,
- verbose = false,
- debugInfo = false,
+ bool binary = true, deNan = false, verbose = false, debugInfo = false,
force = false;
- Options options("wasm-reduce", "Reduce a wasm file to a smaller one that has the same behavior on a given command");
+ Options options("wasm-reduce",
+ "Reduce a wasm file to a smaller one that has the same "
+ "behavior on a given command");
options
- .add("--command", "-cmd", "The command to run on the test, that we want to reduce while keeping the command's output identical. "
- "We look at the command's return code and stdout here (TODO: stderr), "
- "and we reduce while keeping those unchanged.",
- Options::Arguments::One,
- [&](Options* o, const std::string& argument) {
- command = argument;
- })
- .add("--test", "-t", "Test file (this will be written to to test, the given command should read it when we call it)",
- Options::Arguments::One,
- [&](Options* o, const std::string& argument) {
- test = argument;
- })
- .add("--working", "-w", "Working file (this will contain the current good state while doing temporary computations, "
- "and will contain the final best result at the end)",
- Options::Arguments::One,
- [&](Options* o, const std::string& argument) {
- working = argument;
- })
- .add("--binaries", "-b", "binaryen binaries location (bin/ directory)",
- Options::Arguments::One,
- [&](Options* o, const std::string& argument) {
- // Add separator just in case
- Path::setBinaryenBinDir(argument + Path::getPathSeparator());
- })
- .add("--text", "-S", "Emit intermediate files as text, instead of binary (also make sure the test and working files have a .wat or .wast suffix)",
- Options::Arguments::Zero,
- [&](Options* o, const std::string& argument) {
- binary = false;
- })
- .add("--denan", "", "Avoid nans when reducing",
- Options::Arguments::Zero,
- [&](Options* o, const std::string& argument) {
- deNan = true;
- })
- .add("--verbose", "-v", "Verbose output mode",
- Options::Arguments::Zero,
- [&](Options* o, const std::string& argument) {
- verbose = true;
- })
- .add("--debugInfo", "-g", "Keep debug info in binaries",
- Options::Arguments::Zero,
- [&](Options* o, const std::string& argument) {
- debugInfo = true;
- })
- .add("--force", "-f", "Force the reduction attempt, ignoring problems that imply it is unlikely to succeed",
- Options::Arguments::Zero,
- [&](Options* o, const std::string& argument) {
- force = true;
- })
- .add("--timeout", "-to", "A timeout to apply to each execution of the command, in seconds (default: 2)",
- Options::Arguments::One,
- [&](Options* o, const std::string& argument) {
- timeout = atoi(argument.c_str());
- std::cout << "|applying timeout: " << timeout << "\n";
- })
- .add_positional("INFILE", Options::Arguments::One,
- [&](Options* o, const std::string& argument) {
- input = argument;
- });
+ .add("--command",
+ "-cmd",
+ "The command to run on the test, that we want to reduce while keeping "
+ "the command's output identical. "
+ "We look at the command's return code and stdout here (TODO: stderr), "
+ "and we reduce while keeping those unchanged.",
+ Options::Arguments::One,
+ [&](Options* o, const std::string& argument) { command = argument; })
+ .add("--test",
+ "-t",
+ "Test file (this will be written to to test, the given command should "
+ "read it when we call it)",
+ Options::Arguments::One,
+ [&](Options* o, const std::string& argument) { test = argument; })
+ .add("--working",
+ "-w",
+ "Working file (this will contain the current good state while doing "
+ "temporary computations, "
+ "and will contain the final best result at the end)",
+ Options::Arguments::One,
+ [&](Options* o, const std::string& argument) { working = argument; })
+ .add("--binaries",
+ "-b",
+ "binaryen binaries location (bin/ directory)",
+ Options::Arguments::One,
+ [&](Options* o, const std::string& argument) {
+ // Add separator just in case
+ Path::setBinaryenBinDir(argument + Path::getPathSeparator());
+ })
+ .add("--text",
+ "-S",
+ "Emit intermediate files as text, instead of binary (also make sure "
+ "the test and working files have a .wat or .wast suffix)",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { binary = false; })
+ .add("--denan",
+ "",
+ "Avoid nans when reducing",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { deNan = true; })
+ .add("--verbose",
+ "-v",
+ "Verbose output mode",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { verbose = true; })
+ .add("--debugInfo",
+ "-g",
+ "Keep debug info in binaries",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { debugInfo = true; })
+ .add("--force",
+ "-f",
+ "Force the reduction attempt, ignoring problems that imply it is "
+ "unlikely to succeed",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { force = true; })
+ .add("--timeout",
+ "-to",
+ "A timeout to apply to each execution of the command, in seconds "
+ "(default: 2)",
+ Options::Arguments::One,
+ [&](Options* o, const std::string& argument) {
+ timeout = atoi(argument.c_str());
+ std::cout << "|applying timeout: " << timeout << "\n";
+ })
+ .add_positional(
+ "INFILE",
+ Options::Arguments::One,
+ [&](Options* o, const std::string& argument) { input = argument; });
options.parse(argc, argv);
- if (test.size() == 0) Fatal() << "test file not provided\n";
- if (working.size() == 0) Fatal() << "working file not provided\n";
+ if (test.size() == 0)
+ Fatal() << "test file not provided\n";
+ if (working.size() == 0)
+ Fatal() << "working file not provided\n";
if (!binary) {
Colors::disable();
@@ -979,15 +1089,20 @@ int main(int argc, const char* argv[]) {
auto stopIfNotForced = [&](std::string message, ProgramResult& result) {
std::cerr << "|! " << message << '\n' << result << '\n';
if (!force) {
- Fatal() << "|! stopping, as it is very unlikely reduction can succeed (use -f to ignore this check)";
+ Fatal() << "|! stopping, as it is very unlikely reduction can succeed "
+ "(use -f to ignore this check)";
}
};
if (expected.time + 1 >= timeout) {
- stopIfNotForced("execution time is dangerously close to the timeout - you should probably increase the timeout", expected);
+ stopIfNotForced("execution time is dangerously close to the timeout - you "
+ "should probably increase the timeout",
+ expected);
}
- std::cerr << "|checking that command has different behavior on invalid binary (this verifies that the test file is used by the command)\n";
+ std::cerr
+ << "|checking that command has different behavior on invalid binary (this "
+ "verifies that the test file is used by the command)\n";
{
{
std::ofstream dst(test, std::ios::binary);
@@ -995,24 +1110,31 @@ int main(int argc, const char* argv[]) {
}
ProgramResult result(command);
if (result == expected) {
- stopIfNotForced("running command on an invalid module should give different results", result);
+ stopIfNotForced(
+ "running command on an invalid module should give different results",
+ result);
}
}
- std::cerr << "|checking that command has expected behavior on canonicalized (read-written) binary\n";
+ std::cerr << "|checking that command has expected behavior on canonicalized "
+ "(read-written) binary\n";
{
// read and write it
// TODO(tlively): -all should be replaced with an option to use the existing
// feature set, once implemented.
- auto cmd = Path::getBinaryenBinaryTool("wasm-opt") + " " + input + " -all -o " + test;
- if (!binary) cmd += " -S";
+ auto cmd = Path::getBinaryenBinaryTool("wasm-opt") + " " + input +
+ " -all -o " + test;
+ if (!binary)
+ cmd += " -S";
ProgramResult readWrite(cmd);
if (readWrite.failed()) {
stopIfNotForced("failed to read and write the binary", readWrite);
} else {
ProgramResult result(command);
if (result != expected) {
- stopIfNotForced("running command on the canonicalized module should give the same results", result);
+ stopIfNotForced("running command on the canonicalized module should "
+ "give the same results",
+ result);
}
}
}
@@ -1044,13 +1166,15 @@ int main(int argc, const char* argv[]) {
std::cerr << "| after pass reduction: " << newSize << "\n";
// always stop after a pass reduction attempt, for final cleanup
- if (stopping) break;
+ if (stopping)
+ break;
// check if the full cycle (destructive/passes) has helped or not
if (lastPostPassesSize && newSize >= lastPostPassesSize) {
std::cerr << "| progress has stopped, skipping to the end\n";
if (factor == 1) {
- // this is after doing work with factor 1, so after the remaining work, stop
+ // this is after doing work with factor 1, so after the remaining work,
+ // stop
stopping = true;
} else {
// just try to remove all we can and finish up
@@ -1059,9 +1183,10 @@ int main(int argc, const char* argv[]) {
}
lastPostPassesSize = newSize;
- // if destructive reductions lead to useful proportionate pass reductions, keep
- // going at the same factor, as pass reductions are far faster
- std::cerr << "| pass progress: " << passProgress << ", last destructive: " << lastDestructiveReductions << '\n';
+ // if destructive reductions lead to useful proportionate pass reductions,
+ // keep going at the same factor, as pass reductions are far faster
+ std::cerr << "| pass progress: " << passProgress
+ << ", last destructive: " << lastDestructiveReductions << '\n';
if (passProgress >= 4 * lastDestructiveReductions) {
// don't change
std::cerr << "| progress is good, do not quickly decrease factor\n";
@@ -1083,16 +1208,19 @@ int main(int argc, const char* argv[]) {
while (1) {
std::cerr << "| reduce destructively... (factor: " << factor << ")\n";
lastDestructiveReductions = reducer.reduceDestructively(factor);
- if (lastDestructiveReductions > 0) break;
+ if (lastDestructiveReductions > 0)
+ break;
// we failed to reduce destructively
if (factor == 1) {
stopping = true;
break;
}
- factor = std::max(1, factor / 4); // quickly now, try to find *something* we can reduce
+ factor = std::max(
+ 1, factor / 4); // quickly now, try to find *something* we can reduce
}
- std::cerr << "| destructive reduction led to size: " << file_size(working) << '\n';
+ std::cerr << "| destructive reduction led to size: " << file_size(working)
+ << '\n';
}
std::cerr << "|finished, final size: " << file_size(working) << "\n";
copy_file(working, test); // just to avoid confusion
diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp
index 0c95b39ea..6141b1a37 100644
--- a/src/tools/wasm-shell.cpp
+++ b/src/tools/wasm-shell.cpp
@@ -35,13 +35,13 @@
using namespace cashew;
using namespace wasm;
-Name ASSERT_RETURN("assert_return"),
- ASSERT_TRAP("assert_trap"),
- ASSERT_INVALID("assert_invalid"),
- ASSERT_MALFORMED("assert_malformed"),
- ASSERT_UNLINKABLE("assert_unlinkable"),
- INVOKE("invoke"),
- GET("get");
+Name ASSERT_RETURN("assert_return");
+Name ASSERT_TRAP("assert_trap");
+Name ASSERT_INVALID("assert_invalid");
+Name ASSERT_MALFORMED("assert_malformed");
+Name ASSERT_UNLINKABLE("assert_unlinkable");
+Name INVOKE("invoke");
+Name GET("get");
// Modules named in the file
@@ -60,7 +60,10 @@ struct Operation {
Name name;
LiteralList arguments;
- Operation(Element& element, ModuleInstance* instanceInit, SExpressionWasmBuilder& builder) : instance(instanceInit) {
+ Operation(Element& element,
+ ModuleInstance* instanceInit,
+ SExpressionWasmBuilder& builder)
+ : instance(instanceInit) {
operation = element[0]->str();
Index i = 1;
if (element.size() >= 3 && element[2]->isStr()) {
@@ -87,14 +90,19 @@ struct Operation {
}
};
-static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm,
+static void run_asserts(Name moduleName,
+ size_t* i,
+ bool* checked,
+ Module* wasm,
Element* root,
SExpressionWasmBuilder* builder,
Name entry) {
ModuleInstance* instance = nullptr;
if (wasm) {
- auto tempInterface = wasm::make_unique<ShellExternalInterface>(); // prefix make_unique to work around visual studio bugs
- auto tempInstance = wasm::make_unique<ModuleInstance>(*wasm, tempInterface.get());
+ // prefix make_unique to work around visual studio bugs
+ auto tempInterface = wasm::make_unique<ShellExternalInterface>();
+ auto tempInstance =
+ wasm::make_unique<ModuleInstance>(*wasm, tempInterface.get());
interfaces[moduleName].swap(tempInterface);
instances[moduleName].swap(tempInstance);
instance = instances[moduleName].get();
@@ -117,7 +125,8 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm,
while (*i < root->size()) {
Element& curr = *(*root)[*i];
IString id = curr[0]->str();
- if (id == MODULE) break;
+ if (id == MODULE)
+ break;
*checked = true;
Colors::red(std::cerr);
std::cerr << *i << '/' << (root->size() - 1);
@@ -128,15 +137,15 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm,
Colors::green(std::cerr);
std::cerr << " [line: " << curr.line << "]\n";
Colors::normal(std::cerr);
- if (id == ASSERT_INVALID || id == ASSERT_MALFORMED || id == ASSERT_UNLINKABLE) {
+ if (id == ASSERT_INVALID || id == ASSERT_MALFORMED ||
+ id == ASSERT_UNLINKABLE) {
// a module invalidity test
Module wasm;
bool invalid = false;
std::unique_ptr<SExpressionWasmBuilder> builder;
try {
builder = std::unique_ptr<SExpressionWasmBuilder>(
- new SExpressionWasmBuilder(wasm, *curr[1])
- );
+ new SExpressionWasmBuilder(wasm, *curr[1]));
} catch (const ParseException&) {
invalid = true;
}
@@ -147,7 +156,8 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm,
if (!invalid && id == ASSERT_UNLINKABLE) {
// validate "instantiating" the mdoule
auto reportUnknownImport = [&](Importable* import) {
- std::cerr << "unknown import: " << import->module << '.' << import->base << '\n';
+ std::cerr << "unknown import: " << import->module << '.'
+ << import->base << '\n';
invalid = true;
};
ModuleUtils::iterImportedGlobals(wasm, reportUnknownImport);
@@ -168,7 +178,8 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm,
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 == PRINT) {
+ if (import->imported() && import->module == SPECTEST &&
+ import->base == PRINT) {
std::cerr << "cannot put spectest.print in table\n";
invalid = true;
}
@@ -201,10 +212,8 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm,
if (id == ASSERT_RETURN) {
assert(!trapped);
if (curr.size() >= 3) {
- Literal expected = builder
- ->parseExpression(*curr[2])
- ->dynCast<Const>()
- ->value;
+ Literal expected =
+ builder->parseExpression(*curr[2])->dynCast<Const>()->value;
std::cerr << "seen " << result << ", expected " << expected << '\n';
if (expected != result) {
std::cout << "unexpected, should be identical\n";
@@ -219,7 +228,8 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm,
}
}
}
- if (id == ASSERT_TRAP) assert(trapped);
+ if (id == ASSERT_TRAP)
+ assert(trapped);
}
*i += 1;
}
@@ -235,37 +245,44 @@ int main(int argc, const char* argv[]) {
Options options("wasm-shell", "Execute .wast files");
options
- .add(
- "--entry", "-e", "Call the entry point after parsing the module",
- Options::Arguments::One,
- [&entry](Options*, const std::string& argument) { entry = argument; })
- .add(
- "--skip", "-s", "Skip input on certain lines (comma-separated-list)",
- Options::Arguments::One,
- [&skipped](Options*, const std::string& argument) {
- size_t i = 0;
- while (i < argument.size()) {
- auto ending = argument.find(',', i);
- if (ending == std::string::npos) {
- ending = argument.size();
- }
- auto sub = argument.substr(i, ending - i);
- skipped.insert(atoi(sub.c_str()));
- i = ending + 1;
- }
- })
- .add_positional("INFILE", Options::Arguments::One,
- [](Options* o, const std::string& argument) {
- o->extra["infile"] = argument;
- });
+ .add("--entry",
+ "-e",
+ "Call the entry point after parsing the module",
+ Options::Arguments::One,
+ [&entry](Options*, const std::string& argument) { entry = argument; })
+ .add("--skip",
+ "-s",
+ "Skip input on certain lines (comma-separated-list)",
+ Options::Arguments::One,
+ [&skipped](Options*, const std::string& argument) {
+ size_t i = 0;
+ while (i < argument.size()) {
+ auto ending = argument.find(',', i);
+ if (ending == std::string::npos) {
+ ending = argument.size();
+ }
+ auto sub = argument.substr(i, ending - i);
+ skipped.insert(atoi(sub.c_str()));
+ i = ending + 1;
+ }
+ })
+ .add_positional("INFILE",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["infile"] = argument;
+ });
options.parse(argc, argv);
- auto input(read_file<std::vector<char>>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release));
+ auto input(read_file<std::vector<char>>(options.extra["infile"],
+ Flags::Text,
+ options.debug ? Flags::Debug
+ : Flags::Release));
bool checked = false;
try {
- if (options.debug) std::cerr << "parsing text to s-expressions...\n";
+ if (options.debug)
+ std::cerr << "parsing text to s-expressions...\n";
SExpressionParser parser(input.data());
Element& root = *parser.root;
@@ -282,13 +299,15 @@ int main(int argc, const char* argv[]) {
}
IString id = curr[0]->str();
if (id == MODULE) {
- if (options.debug) std::cerr << "parsing s-expressions to wasm...\n";
+ if (options.debug)
+ std::cerr << "parsing s-expressions to wasm...\n";
Colors::green(std::cerr);
std::cerr << "BUILDING MODULE [line: " << curr.line << "]\n";
Colors::normal(std::cerr);
auto module = wasm::make_unique<Module>();
Name moduleName;
- auto builder = wasm::make_unique<SExpressionWasmBuilder>(*module, *root[i], &moduleName);
+ auto builder = wasm::make_unique<SExpressionWasmBuilder>(
+ *module, *root[i], &moduleName);
builders[moduleName].swap(builder);
modules[moduleName].swap(module);
i++;
@@ -298,7 +317,13 @@ int main(int argc, const char* argv[]) {
WasmPrinter::printModule(modules[moduleName].get());
}
assert(valid);
- run_asserts(moduleName, &i, &checked, modules[moduleName].get(), &root, builders[moduleName].get(), entry);
+ run_asserts(moduleName,
+ &i,
+ &checked,
+ modules[moduleName].get(),
+ &root,
+ builders[moduleName].get(),
+ entry);
} else {
run_asserts(Name(), &i, &checked, nullptr, &root, nullptr, entry);
}
diff --git a/src/tools/wasm2js.cpp b/src/tools/wasm2js.cpp
index 84aa9f024..3d5f484da 100644
--- a/src/tools/wasm2js.cpp
+++ b/src/tools/wasm2js.cpp
@@ -18,13 +18,14 @@
// wasm2js console tool
//
+#include "wasm2js.h"
+#include "optimization-options.h"
+#include "pass.h"
#include "support/colors.h"
#include "support/command-line.h"
#include "support/file.h"
#include "wasm-s-parser.h"
#include "wasm2js.h"
-#include "optimization-options.h"
-#include "pass.h"
using namespace cashew;
using namespace wasm;
@@ -34,8 +35,8 @@ using namespace wasm;
namespace {
static void optimizeWasm(Module& wasm, PassOptions options) {
- // Perform various optimizations that will be good for JS, but would not be great
- // for wasm in general
+ // Perform various optimizations that will be good for JS, but would not be
+ // great for wasm in general
struct OptimizeForJS : public WalkerPass<PostWalker<OptimizeForJS>> {
bool isFunctionParallel() override { return true; }
@@ -60,14 +61,12 @@ static void optimizeWasm(Module& wasm, PassOptions options) {
runner.run();
}
-template<typename T>
-static void printJS(Ref ast, T& output) {
+template<typename T> static void printJS(Ref ast, T& output) {
JSPrinter jser(true, true, ast);
jser.printAst();
output << jser.buffer << std::endl;
}
-
// Traversals
struct TraverseInfo {
@@ -102,7 +101,9 @@ private:
};
// Traverse, calling visit after the children
-static void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function<void (Ref)> visitPost) {
+static void traversePrePost(Ref node,
+ std::function<void(Ref)> visitPre,
+ std::function<void(Ref)> visitPost) {
std::vector<TraverseInfo> stack;
stack.push_back(TraverseInfo(node));
while (!stack.empty()) {
@@ -123,7 +124,7 @@ static void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::f
}
}
-static void traversePost(Ref node, std::function<void (Ref)> visit) {
+static void traversePost(Ref node, std::function<void(Ref)> visit) {
traversePrePost(node, [](Ref node) {}, visit);
}
@@ -131,24 +132,28 @@ static void optimizeJS(Ref ast) {
// helpers
auto isOrZero = [](Ref node) {
- return node->isArray() && !node->empty() && node[0] == BINARY && node[1] == OR && node[3]->isNumber() && node[3]->getNumber() == 0;
+ return node->isArray() && !node->empty() && node[0] == BINARY &&
+ node[1] == OR && node[3]->isNumber() && node[3]->getNumber() == 0;
};
auto isPlus = [](Ref node) {
- return node->isArray() && !node->empty() && node[0] == UNARY_PREFIX && node[1] == PLUS;
+ return node->isArray() && !node->empty() && node[0] == UNARY_PREFIX &&
+ node[1] == PLUS;
};
auto isBitwise = [](Ref node) {
if (node->isArray() && !node->empty() && node[0] == BINARY) {
auto op = node[1];
- return op == OR || op == AND || op == XOR || op == RSHIFT || op == TRSHIFT || op == LSHIFT;
+ return op == OR || op == AND || op == XOR || op == RSHIFT ||
+ op == TRSHIFT || op == LSHIFT;
}
return false;
};
// x >> 0 => x | 0
traversePost(ast, [](Ref node) {
- if (node->isArray() && !node->empty() && node[0] == BINARY && node[1] == RSHIFT && node[3]->isNumber()) {
+ if (node->isArray() && !node->empty() && node[0] == BINARY &&
+ node[1] == RSHIFT && node[3]->isNumber()) {
if (node[3]->getNumber() == 0) {
node[1]->setString(OR);
}
@@ -190,60 +195,69 @@ static void optimizeJS(Ref ast) {
// XXX IString invalid("__wasm2js$INVALID_LABEL__");
std::vector<Ref> breakCapturers;
std::vector<Ref> continueCapturers;
- std::unordered_map<IString, Ref> labelToValue; // maps the label to the loop/etc.
+ std::unordered_map<IString, Ref>
+ labelToValue; // maps the label to the loop/etc.
std::unordered_set<Value*> labelled; // all things with a label on them.
Value INVALID;
- traversePrePost(ast, [&](Ref node) {
- if (node->isArray() && !node->empty()) {
- if (node[0] == LABEL) {
- auto label = node[1]->getIString();
- labelToValue[label] = node[2];
- labelled.insert(node[2].get());
- } else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) {
- breakCapturers.push_back(node);
- continueCapturers.push_back(node);
- } else if (node[0] == cashew::BLOCK) {
- if (labelled.count(node.get())) {
- // Cannot break to a block without the label.
- breakCapturers.push_back(Ref(&INVALID));
+ traversePrePost(
+ ast,
+ [&](Ref node) {
+ if (node->isArray() && !node->empty()) {
+ if (node[0] == LABEL) {
+ auto label = node[1]->getIString();
+ labelToValue[label] = node[2];
+ labelled.insert(node[2].get());
+ } else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) {
+ breakCapturers.push_back(node);
+ continueCapturers.push_back(node);
+ } else if (node[0] == cashew::BLOCK) {
+ if (labelled.count(node.get())) {
+ // Cannot break to a block without the label.
+ breakCapturers.push_back(Ref(&INVALID));
+ }
+ } else if (node[0] == SWITCH) {
+ breakCapturers.push_back(node);
}
- } else if (node[0] == SWITCH) {
- breakCapturers.push_back(node);
}
- }
- }, [&](Ref node) {
- if (node->isArray() && !node->empty()) {
- if (node[0] == LABEL) {
- auto label = node[1]->getIString();
- labelToValue.erase(label);
- labelled.erase(node[2].get());
- } else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) {
- breakCapturers.pop_back();
- continueCapturers.pop_back();
- } else if (node[0] == cashew::BLOCK) {
- if (labelled.count(node.get())) {
- breakCapturers.pop_back();
- }
- } else if (node[0] == SWITCH) {
- breakCapturers.pop_back();
- } else if (node[0] == BREAK || node[0] == CONTINUE) {
- if (!node[1]->isNull()) {
+ },
+ [&](Ref node) {
+ if (node->isArray() && !node->empty()) {
+ if (node[0] == LABEL) {
auto label = node[1]->getIString();
- assert(labelToValue.count(label));
- auto& capturers = node[0] == BREAK ? breakCapturers : continueCapturers;
- assert(!capturers.empty());
- if (capturers.back() == labelToValue[label]) {
- // Success, the break/continue goes exactly where we would if we
- // didn't have the label!
- node[1]->setNull();
+ labelToValue.erase(label);
+ labelled.erase(node[2].get());
+ } else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) {
+ breakCapturers.pop_back();
+ continueCapturers.pop_back();
+ } else if (node[0] == cashew::BLOCK) {
+ if (labelled.count(node.get())) {
+ breakCapturers.pop_back();
+ }
+ } else if (node[0] == SWITCH) {
+ breakCapturers.pop_back();
+ } else if (node[0] == BREAK || node[0] == CONTINUE) {
+ if (!node[1]->isNull()) {
+ auto label = node[1]->getIString();
+ assert(labelToValue.count(label));
+ auto& capturers =
+ node[0] == BREAK ? breakCapturers : continueCapturers;
+ assert(!capturers.empty());
+ if (capturers.back() == labelToValue[label]) {
+ // Success, the break/continue goes exactly where we would if we
+ // didn't have the label!
+ node[1]->setNull();
+ }
}
}
}
- }
- });
+ });
}
-static void emitWasm(Module& wasm, Output& output, Wasm2JSBuilder::Flags flags, PassOptions options, Name name) {
+static void emitWasm(Module& wasm,
+ Output& output,
+ Wasm2JSBuilder::Flags flags,
+ PassOptions options,
+ Name name) {
if (options.optimizeLevel > 0) {
optimizeWasm(wasm, options);
}
@@ -264,7 +278,9 @@ public:
SExpressionWasmBuilder& sexpBuilder,
Output& out,
Wasm2JSBuilder::Flags flags,
- PassOptions options) : root(root), sexpBuilder(sexpBuilder), out(out), flags(flags), options(options) {}
+ PassOptions options)
+ : root(root), sexpBuilder(sexpBuilder), out(out), flags(flags),
+ options(options) {}
void emit();
@@ -310,11 +326,9 @@ Ref AssertionEmitter::emitAssertReturnFunc(Builder& wasmBuilder,
Expression* actual = sexpBuilder.parseExpression(e[1]);
Expression* body = nullptr;
if (e.size() == 2) {
- if (actual->type == none) {
- body = wasmBuilder.blockify(
- actual,
- wasmBuilder.makeConst(Literal(uint32_t(1)))
- );
+ if (actual->type == none) {
+ body = wasmBuilder.blockify(actual,
+ wasmBuilder.makeConst(Literal(uint32_t(1))));
} else {
body = actual;
}
@@ -330,9 +344,10 @@ Ref AssertionEmitter::emitAssertReturnFunc(Builder& wasmBuilder,
case i64:
body = wasmBuilder.makeCall(
"i64Equal",
- {actual, wasmBuilder.makeCall(WASM_FETCH_HIGH_BITS, {}, i32), expected},
- i32
- );
+ {actual,
+ wasmBuilder.makeCall(WASM_FETCH_HIGH_BITS, {}, i32),
+ expected},
+ i32);
break;
case f32: {
@@ -353,14 +368,11 @@ Ref AssertionEmitter::emitAssertReturnFunc(Builder& wasmBuilder,
assert(false && "Unexpected number of parameters in assert_return");
}
std::unique_ptr<Function> testFunc(
- wasmBuilder.makeFunction(
- testFuncName,
- std::vector<NameType>{},
- body->type,
- std::vector<NameType>{},
- body
- )
- );
+ wasmBuilder.makeFunction(testFuncName,
+ std::vector<NameType>{},
+ body->type,
+ std::vector<NameType>{},
+ body));
Ref jsFunc = processFunction(testFunc.get());
fixCalls(jsFunc, asmModule);
emitFunction(jsFunc);
@@ -374,14 +386,11 @@ Ref AssertionEmitter::emitAssertReturnNanFunc(Builder& wasmBuilder,
Expression* actual = sexpBuilder.parseExpression(e[1]);
Expression* body = wasmBuilder.makeCall("isNaN", {actual}, i32);
std::unique_ptr<Function> testFunc(
- wasmBuilder.makeFunction(
- testFuncName,
- std::vector<NameType>{},
- body->type,
- std::vector<NameType>{},
- body
- )
- );
+ wasmBuilder.makeFunction(testFuncName,
+ std::vector<NameType>{},
+ body->type,
+ std::vector<NameType>{},
+ body));
Ref jsFunc = processFunction(testFunc.get());
fixCalls(jsFunc, asmModule);
emitFunction(jsFunc);
@@ -399,8 +408,7 @@ Ref AssertionEmitter::emitAssertTrapFunc(Builder& wasmBuilder,
std::vector<NameType>{},
expr->type,
std::vector<NameType>{},
- expr)
- );
+ expr));
IString expectedErr = e[2]->str();
Ref innerFunc = processFunction(exprFunc.get());
fixCalls(innerFunc, asmModule);
@@ -411,33 +419,25 @@ Ref AssertionEmitter::emitAssertTrapFunc(Builder& wasmBuilder,
Ref catchBlock = ValueBuilder::makeBlock();
ValueBuilder::appendToBlock(
catchBlock,
- ValueBuilder::makeReturn(
- ValueBuilder::makeCall(
- ValueBuilder::makeDot(
- ValueBuilder::makeName(IString("e")),
- ValueBuilder::makeName(IString("message")),
- ValueBuilder::makeName(IString("includes"))
- ),
- ValueBuilder::makeString(expectedErr)
- )
- )
- );
+ ValueBuilder::makeReturn(ValueBuilder::makeCall(
+ ValueBuilder::makeDot(ValueBuilder::makeName(IString("e")),
+ ValueBuilder::makeName(IString("message")),
+ ValueBuilder::makeName(IString("includes"))),
+ ValueBuilder::makeString(expectedErr))));
outerFunc[3]->push_back(ValueBuilder::makeTry(
- tryBlock,
- ValueBuilder::makeName((IString("e"))),
- catchBlock));
+ tryBlock, ValueBuilder::makeName((IString("e"))), catchBlock));
outerFunc[3]->push_back(ValueBuilder::makeReturn(ValueBuilder::makeInt(0)));
emitFunction(outerFunc);
return outerFunc;
}
bool AssertionEmitter::isAssertHandled(Element& e) {
- return e.isList() && e.size() >= 2 && e[0]->isStr()
- && (e[0]->str() == Name("assert_return") ||
+ return e.isList() && e.size() >= 2 && e[0]->isStr() &&
+ (e[0]->str() == Name("assert_return") ||
e[0]->str() == Name("assert_return_nan") ||
- (flags.pedantic && e[0]->str() == Name("assert_trap")))
- && e[1]->isList() && e[1]->size() >= 2 && (*e[1])[0]->isStr()
- && (*e[1])[0]->str() == Name("invoke");
+ (flags.pedantic && e[0]->str() == Name("assert_trap"))) &&
+ e[1]->isList() && e[1]->size() >= 2 && (*e[1])[0]->isStr() &&
+ (*e[1])[0]->str() == Name("invoke");
}
void AssertionEmitter::fixCalls(Ref asmjs, Name asmModule) {
@@ -446,7 +446,8 @@ void AssertionEmitter::fixCalls(Ref asmjs, Name asmModule) {
for (Ref& r : arr) {
fixCalls(r, asmModule);
}
- if (arr.size() > 0 && arr[0]->isString() && arr[0]->getIString() == cashew::CALL) {
+ if (arr.size() > 0 && arr[0]->isString() &&
+ arr[0]->getIString() == cashew::CALL) {
assert(arr.size() >= 2);
if (arr[1]->getIString() == "f32Equal" ||
arr[1]->getIString() == "f64Equal" ||
@@ -457,7 +458,7 @@ void AssertionEmitter::fixCalls(Ref asmjs, Name asmModule) {
arr[1]->setString("Math.fround");
} else {
Ref fixed = ValueBuilder::makeDot(ValueBuilder::makeName(asmModule),
- arr[1]->getIString());
+ arr[1]->getIString());
arr[1]->setArray(fixed->getArray());
}
}
@@ -522,7 +523,8 @@ void AssertionEmitter::emit() {
Name asmModule = std::string("ret") + ASM_FUNC.str;
for (size_t i = 0; i < root.size(); ++i) {
Element& e = *root[i];
- if (e.isList() && e.size() >= 1 && e[0]->isStr() && e[0]->str() == Name("module")) {
+ if (e.isList() && e.size() >= 1 && e[0]->isStr() &&
+ e[0]->str() == Name("module")) {
std::stringstream funcNameS;
funcNameS << ASM_FUNC.c_str() << i;
std::stringstream moduleNameS;
@@ -555,10 +557,7 @@ void AssertionEmitter::emit() {
emitAssertTrapFunc(wasmBuilder, e, testFuncName, asmModule);
}
- out << "if (!"
- << testFuncName.str
- << "()) throw 'assertion failed: "
- << e
+ out << "if (!" << testFuncName.str << "()) throw 'assertion failed: " << e
<< "';\n";
}
}
@@ -567,38 +566,48 @@ void AssertionEmitter::emit() {
// Main
-int main(int argc, const char *argv[]) {
+int main(int argc, const char* argv[]) {
Wasm2JSBuilder::Flags flags;
- OptimizationOptions options("wasm2js", "Transform .wasm/.wast files to asm.js");
+ OptimizationOptions options("wasm2js",
+ "Transform .wasm/.wast files to asm.js");
options
- .add("--output", "-o", "Output file (stdout if not specified)",
- Options::Arguments::One,
- [](Options* o, const std::string& argument) {
- o->extra["output"] = argument;
- Colors::disable();
- })
- .add("--allow-asserts", "", "Allow compilation of .wast testing asserts",
- Options::Arguments::Zero,
- [&](Options* o, const std::string& argument) {
- flags.allowAsserts = true;
- o->extra["asserts"] = "1";
- })
- .add("--pedantic", "", "Emulate WebAssembly trapping behavior",
- Options::Arguments::Zero,
- [&](Options* o, const std::string& argument) {
- flags.pedantic = true;
- })
- .add("--emscripten", "", "Emulate the glue in emscripten-compatible form (and not ES6 module form)",
- Options::Arguments::Zero,
- [&](Options* o, const std::string& argument) {
- flags.emscripten = true;
- })
- .add_positional("INFILE", Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- o->extra["infile"] = argument;
- });
+ .add("--output",
+ "-o",
+ "Output file (stdout if not specified)",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["output"] = argument;
+ Colors::disable();
+ })
+ .add("--allow-asserts",
+ "",
+ "Allow compilation of .wast testing asserts",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) {
+ flags.allowAsserts = true;
+ o->extra["asserts"] = "1";
+ })
+ .add(
+ "--pedantic",
+ "",
+ "Emulate WebAssembly trapping behavior",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { flags.pedantic = true; })
+ .add(
+ "--emscripten",
+ "",
+ "Emulate the glue in emscripten-compatible form (and not ES6 module "
+ "form)",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { flags.emscripten = true; })
+ .add_positional("INFILE",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["infile"] = argument;
+ });
options.parse(argc, argv);
- if (options.debug) flags.debug = true;
+ if (options.debug)
+ flags.debug = true;
Element* root = nullptr;
Module wasm;
@@ -606,11 +615,11 @@ int main(int argc, const char *argv[]) {
std::unique_ptr<SExpressionParser> sexprParser;
std::unique_ptr<SExpressionWasmBuilder> sexprBuilder;
- auto &input = options.extra["infile"];
+ auto& input = options.extra["infile"];
std::string suffix(".wasm");
bool binaryInput =
- input.size() >= suffix.size() &&
- input.compare(input.size() - suffix.size(), suffix.size(), suffix) == 0;
+ input.size() >= suffix.size() &&
+ input.compare(input.size() - suffix.size(), suffix.size(), suffix) == 0;
try {
// If the input filename ends in `.wasm`, then parse it in binary form,
@@ -627,20 +636,25 @@ int main(int argc, const char *argv[]) {
reader.read(input, wasm, "");
options.applyFeatures(wasm);
} else {
- auto input(
- read_file<std::vector<char>>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release));
- if (options.debug) std::cerr << "s-parsing..." << std::endl;
+ auto input(read_file<std::vector<char>>(options.extra["infile"],
+ Flags::Text,
+ options.debug ? Flags::Debug
+ : Flags::Release));
+ if (options.debug)
+ std::cerr << "s-parsing..." << std::endl;
sexprParser = make_unique<SExpressionParser>(input.data());
root = sexprParser->root;
- if (options.debug) std::cerr << "w-parsing..." << std::endl;
+ if (options.debug)
+ std::cerr << "w-parsing..." << std::endl;
sexprBuilder = make_unique<SExpressionWasmBuilder>(wasm, *(*root)[0]);
}
} catch (ParseException& p) {
p.dump(std::cerr);
Fatal() << "error in parsing input";
} catch (std::bad_alloc&) {
- Fatal() << "error in building module, std::bad_alloc (possibly invalid request for silly amounts of memory)";
+ Fatal() << "error in building module, std::bad_alloc (possibly invalid "
+ "request for silly amounts of memory)";
}
if (options.passOptions.validate) {
@@ -650,13 +664,18 @@ int main(int argc, const char *argv[]) {
}
}
- if (options.debug) std::cerr << "j-printing..." << std::endl;
- Output output(options.extra["output"], Flags::Text, options.debug ? Flags::Debug : Flags::Release);
+ if (options.debug)
+ std::cerr << "j-printing..." << std::endl;
+ Output output(options.extra["output"],
+ Flags::Text,
+ options.debug ? Flags::Debug : Flags::Release);
if (!binaryInput && options.extra["asserts"] == "1") {
- AssertionEmitter(*root, *sexprBuilder, output, flags, options.passOptions).emit();
+ AssertionEmitter(*root, *sexprBuilder, output, flags, options.passOptions)
+ .emit();
} else {
emitWasm(wasm, output, flags, options.passOptions, "asmFunc");
}
- if (options.debug) std::cerr << "done." << std::endl;
+ if (options.debug)
+ std::cerr << "done." << std::endl;
}