blob: 8b7e4b1b767508f8b599800df3ecb2adda5c9703 [file] [log] [blame] [edit]
/*
* Copyright 2015 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// asm2wasm console tool
//
#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-io.h"
#include "wasm-printing.h"
#include "wasm-validator.h"
#include "asm2wasm.h"
using namespace cashew;
using namespace wasm;
int main(int argc, const char* argv[]) {
bool legalizeJavaScriptFFI = true;
TrapMode trapMode = TrapMode::JS;
bool wasmOnly = false;
std::string sourceMapFilename;
std::string sourceMapUrl;
std::string symbolMap;
bool emitBinary = true;
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::setEnabled(false);
})
.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("--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
if (options.extra["output"].size() == 0) {
// when no output file is specified, we emit text to stdout
emitBinary = false;
}
if (options.runningDefaultOptimizationPasses()) {
if (options.passes.size() > 1) {
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());
if (totalMemory & ~Memory::kPageMask) {
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));
char* start = pre.process(input.data());
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;
}
Module wasm;
// set up memory
wasm.memory.initial = wasm.memory.max = totalMemory / Memory::kPageSize;
// 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");
if (memInit != options.extra.end()) {
auto filename = memInit->second.c_str();
auto data(read_file<std::vector<char>>(filename, Flags::Binary));
// create the memory segment
Expression* init;
const auto& memBase = options.extra.find("mem base");
if (memBase == options.extra.end()) {
init = Builder(wasm).makeGlobalGet(MEMORY_BASE, Type::i32);
} else {
init = Builder(wasm).makeConst(
Literal(int32_t(atoi(memBase->second.c_str()))));
}
wasm.memory.segments.emplace_back(init, data);
}
// set up the module's features, needed by optimization and validation passes
options.applyFeatures(wasm);
// compile the code
Asm2WasmBuilder asm2wasm(wasm,
pre,
options.debug,
trapMode,
options.passOptions,
legalizeJavaScriptFFI,
options.runningDefaultOptimizationPasses(),
wasmOnly);
asm2wasm.processAsm(asmjs);
// Set the max memory size, if requested
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)) {
wasm.memory.max = max / Memory::kPageSize;
} else {
wasm.memory.max = Memory::kUnlimitedSize;
}
}
// Set the table sizes, if requested
const auto& tableMax = options.extra.find("table max");
if (tableMax != options.extra.end()) {
int max = atoi(tableMax->second.c_str());
if (max >= 0) {
wasm.table.max = max;
} else {
wasm.table.max = Table::kUnlimitedSize;
}
}
if (options.passOptions.validate) {
if (!WasmValidator().validate(wasm)) {
WasmPrinter::printModule(&wasm);
Fatal() << "error in validating output";
}
}
if (options.debug) {
std::cerr << "emitting..." << std::endl;
}
ModuleWriter writer;
writer.setDebugInfo(options.passOptions.debugInfo);
writer.setSymbolMap(symbolMap);
writer.setBinary(emitBinary);
if (emitBinary) {
writer.setSourceMapFilename(sourceMapFilename);
writer.setSourceMapUrl(sourceMapUrl);
}
writer.write(wasm, options.extra["output"]);
if (options.debug) {
std::cerr << "done." << std::endl;
}
}