Mypal/js/src/wasm/WasmCompile.cpp

968 lines
30 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2015 Mozilla Foundation
*
* 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.
*/
#include "wasm/WasmCompile.h"
#include "mozilla/CheckedInt.h"
#include "jsprf.h"
#include "wasm/WasmBinaryFormat.h"
#include "wasm/WasmBinaryIterator.h"
#include "wasm/WasmGenerator.h"
#include "wasm/WasmSignalHandlers.h"
using namespace js;
using namespace js::jit;
using namespace js::wasm;
using mozilla::CheckedInt;
using mozilla::IsNaN;
namespace {
struct ValidatingPolicy : OpIterPolicy
{
// Validation is what we're all about here.
static const bool Validate = true;
};
typedef OpIter<ValidatingPolicy> ValidatingOpIter;
class FunctionDecoder
{
const ModuleGenerator& mg_;
const ValTypeVector& locals_;
ValidatingOpIter iter_;
public:
FunctionDecoder(const ModuleGenerator& mg, const ValTypeVector& locals, Decoder& d)
: mg_(mg), locals_(locals), iter_(d)
{}
const ModuleGenerator& mg() const { return mg_; }
ValidatingOpIter& iter() { return iter_; }
const ValTypeVector& locals() const { return locals_; }
bool checkHasMemory() {
if (!mg().usesMemory())
return iter().fail("can't touch memory without memory");
return true;
}
};
} // end anonymous namespace
static bool
DecodeCallArgs(FunctionDecoder& f, const Sig& sig)
{
const ValTypeVector& args = sig.args();
uint32_t numArgs = args.length();
for (size_t i = 0; i < numArgs; ++i) {
ValType argType = args[i];
if (!f.iter().readCallArg(argType, numArgs, i, nullptr))
return false;
}
return f.iter().readCallArgsEnd(numArgs);
}
static bool
DecodeCallReturn(FunctionDecoder& f, const Sig& sig)
{
return f.iter().readCallReturn(sig.ret());
}
static bool
DecodeCall(FunctionDecoder& f)
{
uint32_t funcIndex;
if (!f.iter().readCall(&funcIndex))
return false;
if (funcIndex >= f.mg().numFuncs())
return f.iter().fail("callee index out of range");
if (!f.iter().inReachableCode())
return true;
const Sig* sig = &f.mg().funcSig(funcIndex);
return DecodeCallArgs(f, *sig) &&
DecodeCallReturn(f, *sig);
}
static bool
DecodeCallIndirect(FunctionDecoder& f)
{
if (!f.mg().numTables())
return f.iter().fail("can't call_indirect without a table");
uint32_t sigIndex;
if (!f.iter().readCallIndirect(&sigIndex, nullptr))
return false;
if (sigIndex >= f.mg().numSigs())
return f.iter().fail("signature index out of range");
if (!f.iter().inReachableCode())
return true;
const Sig& sig = f.mg().sig(sigIndex);
if (!DecodeCallArgs(f, sig))
return false;
return DecodeCallReturn(f, sig);
}
static bool
DecodeBrTable(FunctionDecoder& f)
{
uint32_t tableLength;
ExprType type = ExprType::Limit;
if (!f.iter().readBrTable(&tableLength, &type, nullptr, nullptr))
return false;
uint32_t depth;
for (size_t i = 0, e = tableLength; i < e; ++i) {
if (!f.iter().readBrTableEntry(&type, nullptr, &depth))
return false;
}
// Read the default label.
return f.iter().readBrTableDefault(&type, nullptr, &depth);
}
static bool
DecodeFunctionBodyExprs(FunctionDecoder& f)
{
#define CHECK(c) if (!(c)) return false; break
while (true) {
uint16_t op;
if (!f.iter().readOp(&op))
return false;
switch (op) {
case uint16_t(Op::End):
if (!f.iter().readEnd(nullptr, nullptr, nullptr))
return false;
if (f.iter().controlStackEmpty())
return true;
break;
case uint16_t(Op::Nop):
CHECK(f.iter().readNop());
case uint16_t(Op::Drop):
CHECK(f.iter().readDrop());
case uint16_t(Op::Call):
CHECK(DecodeCall(f));
case uint16_t(Op::CallIndirect):
CHECK(DecodeCallIndirect(f));
case uint16_t(Op::I32Const):
CHECK(f.iter().readI32Const(nullptr));
case uint16_t(Op::I64Const):
CHECK(f.iter().readI64Const(nullptr));
case uint16_t(Op::F32Const):
CHECK(f.iter().readF32Const(nullptr));
case uint16_t(Op::F64Const):
CHECK(f.iter().readF64Const(nullptr));
case uint16_t(Op::GetLocal):
CHECK(f.iter().readGetLocal(f.locals(), nullptr));
case uint16_t(Op::SetLocal):
CHECK(f.iter().readSetLocal(f.locals(), nullptr, nullptr));
case uint16_t(Op::TeeLocal):
CHECK(f.iter().readTeeLocal(f.locals(), nullptr, nullptr));
case uint16_t(Op::GetGlobal):
CHECK(f.iter().readGetGlobal(f.mg().globals(), nullptr));
case uint16_t(Op::SetGlobal):
CHECK(f.iter().readSetGlobal(f.mg().globals(), nullptr, nullptr));
case uint16_t(Op::Select):
CHECK(f.iter().readSelect(nullptr, nullptr, nullptr, nullptr));
case uint16_t(Op::Block):
CHECK(f.iter().readBlock());
case uint16_t(Op::Loop):
CHECK(f.iter().readLoop());
case uint16_t(Op::If):
CHECK(f.iter().readIf(nullptr));
case uint16_t(Op::Else):
CHECK(f.iter().readElse(nullptr, nullptr));
case uint16_t(Op::I32Clz):
case uint16_t(Op::I32Ctz):
case uint16_t(Op::I32Popcnt):
CHECK(f.iter().readUnary(ValType::I32, nullptr));
case uint16_t(Op::I64Clz):
case uint16_t(Op::I64Ctz):
case uint16_t(Op::I64Popcnt):
CHECK(f.iter().readUnary(ValType::I64, nullptr));
case uint16_t(Op::F32Abs):
case uint16_t(Op::F32Neg):
case uint16_t(Op::F32Ceil):
case uint16_t(Op::F32Floor):
case uint16_t(Op::F32Sqrt):
case uint16_t(Op::F32Trunc):
case uint16_t(Op::F32Nearest):
CHECK(f.iter().readUnary(ValType::F32, nullptr));
case uint16_t(Op::F64Abs):
case uint16_t(Op::F64Neg):
case uint16_t(Op::F64Ceil):
case uint16_t(Op::F64Floor):
case uint16_t(Op::F64Sqrt):
case uint16_t(Op::F64Trunc):
case uint16_t(Op::F64Nearest):
CHECK(f.iter().readUnary(ValType::F64, nullptr));
case uint16_t(Op::I32Add):
case uint16_t(Op::I32Sub):
case uint16_t(Op::I32Mul):
case uint16_t(Op::I32DivS):
case uint16_t(Op::I32DivU):
case uint16_t(Op::I32RemS):
case uint16_t(Op::I32RemU):
case uint16_t(Op::I32And):
case uint16_t(Op::I32Or):
case uint16_t(Op::I32Xor):
case uint16_t(Op::I32Shl):
case uint16_t(Op::I32ShrS):
case uint16_t(Op::I32ShrU):
case uint16_t(Op::I32Rotl):
case uint16_t(Op::I32Rotr):
CHECK(f.iter().readBinary(ValType::I32, nullptr, nullptr));
case uint16_t(Op::I64Add):
case uint16_t(Op::I64Sub):
case uint16_t(Op::I64Mul):
case uint16_t(Op::I64DivS):
case uint16_t(Op::I64DivU):
case uint16_t(Op::I64RemS):
case uint16_t(Op::I64RemU):
case uint16_t(Op::I64And):
case uint16_t(Op::I64Or):
case uint16_t(Op::I64Xor):
case uint16_t(Op::I64Shl):
case uint16_t(Op::I64ShrS):
case uint16_t(Op::I64ShrU):
case uint16_t(Op::I64Rotl):
case uint16_t(Op::I64Rotr):
CHECK(f.iter().readBinary(ValType::I64, nullptr, nullptr));
case uint16_t(Op::F32Add):
case uint16_t(Op::F32Sub):
case uint16_t(Op::F32Mul):
case uint16_t(Op::F32Div):
case uint16_t(Op::F32Min):
case uint16_t(Op::F32Max):
case uint16_t(Op::F32CopySign):
CHECK(f.iter().readBinary(ValType::F32, nullptr, nullptr));
case uint16_t(Op::F64Add):
case uint16_t(Op::F64Sub):
case uint16_t(Op::F64Mul):
case uint16_t(Op::F64Div):
case uint16_t(Op::F64Min):
case uint16_t(Op::F64Max):
case uint16_t(Op::F64CopySign):
CHECK(f.iter().readBinary(ValType::F64, nullptr, nullptr));
case uint16_t(Op::I32Eq):
case uint16_t(Op::I32Ne):
case uint16_t(Op::I32LtS):
case uint16_t(Op::I32LtU):
case uint16_t(Op::I32LeS):
case uint16_t(Op::I32LeU):
case uint16_t(Op::I32GtS):
case uint16_t(Op::I32GtU):
case uint16_t(Op::I32GeS):
case uint16_t(Op::I32GeU):
CHECK(f.iter().readComparison(ValType::I32, nullptr, nullptr));
case uint16_t(Op::I64Eq):
case uint16_t(Op::I64Ne):
case uint16_t(Op::I64LtS):
case uint16_t(Op::I64LtU):
case uint16_t(Op::I64LeS):
case uint16_t(Op::I64LeU):
case uint16_t(Op::I64GtS):
case uint16_t(Op::I64GtU):
case uint16_t(Op::I64GeS):
case uint16_t(Op::I64GeU):
CHECK(f.iter().readComparison(ValType::I64, nullptr, nullptr));
case uint16_t(Op::F32Eq):
case uint16_t(Op::F32Ne):
case uint16_t(Op::F32Lt):
case uint16_t(Op::F32Le):
case uint16_t(Op::F32Gt):
case uint16_t(Op::F32Ge):
CHECK(f.iter().readComparison(ValType::F32, nullptr, nullptr));
case uint16_t(Op::F64Eq):
case uint16_t(Op::F64Ne):
case uint16_t(Op::F64Lt):
case uint16_t(Op::F64Le):
case uint16_t(Op::F64Gt):
case uint16_t(Op::F64Ge):
CHECK(f.iter().readComparison(ValType::F64, nullptr, nullptr));
case uint16_t(Op::I32Eqz):
CHECK(f.iter().readConversion(ValType::I32, ValType::I32, nullptr));
case uint16_t(Op::I64Eqz):
case uint16_t(Op::I32WrapI64):
CHECK(f.iter().readConversion(ValType::I64, ValType::I32, nullptr));
case uint16_t(Op::I32TruncSF32):
case uint16_t(Op::I32TruncUF32):
case uint16_t(Op::I32ReinterpretF32):
CHECK(f.iter().readConversion(ValType::F32, ValType::I32, nullptr));
case uint16_t(Op::I32TruncSF64):
case uint16_t(Op::I32TruncUF64):
CHECK(f.iter().readConversion(ValType::F64, ValType::I32, nullptr));
case uint16_t(Op::I64ExtendSI32):
case uint16_t(Op::I64ExtendUI32):
CHECK(f.iter().readConversion(ValType::I32, ValType::I64, nullptr));
case uint16_t(Op::I64TruncSF32):
case uint16_t(Op::I64TruncUF32):
CHECK(f.iter().readConversion(ValType::F32, ValType::I64, nullptr));
case uint16_t(Op::I64TruncSF64):
case uint16_t(Op::I64TruncUF64):
case uint16_t(Op::I64ReinterpretF64):
CHECK(f.iter().readConversion(ValType::F64, ValType::I64, nullptr));
case uint16_t(Op::F32ConvertSI32):
case uint16_t(Op::F32ConvertUI32):
case uint16_t(Op::F32ReinterpretI32):
CHECK(f.iter().readConversion(ValType::I32, ValType::F32, nullptr));
case uint16_t(Op::F32ConvertSI64):
case uint16_t(Op::F32ConvertUI64):
CHECK(f.iter().readConversion(ValType::I64, ValType::F32, nullptr));
case uint16_t(Op::F32DemoteF64):
CHECK(f.iter().readConversion(ValType::F64, ValType::F32, nullptr));
case uint16_t(Op::F64ConvertSI32):
case uint16_t(Op::F64ConvertUI32):
CHECK(f.iter().readConversion(ValType::I32, ValType::F64, nullptr));
case uint16_t(Op::F64ConvertSI64):
case uint16_t(Op::F64ConvertUI64):
case uint16_t(Op::F64ReinterpretI64):
CHECK(f.iter().readConversion(ValType::I64, ValType::F64, nullptr));
case uint16_t(Op::F64PromoteF32):
CHECK(f.iter().readConversion(ValType::F32, ValType::F64, nullptr));
case uint16_t(Op::I32Load8S):
case uint16_t(Op::I32Load8U):
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 1, nullptr));
case uint16_t(Op::I32Load16S):
case uint16_t(Op::I32Load16U):
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 2, nullptr));
case uint16_t(Op::I32Load):
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 4, nullptr));
case uint16_t(Op::I64Load8S):
case uint16_t(Op::I64Load8U):
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 1, nullptr));
case uint16_t(Op::I64Load16S):
case uint16_t(Op::I64Load16U):
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 2, nullptr));
case uint16_t(Op::I64Load32S):
case uint16_t(Op::I64Load32U):
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 4, nullptr));
case uint16_t(Op::I64Load):
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 8, nullptr));
case uint16_t(Op::F32Load):
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::F32, 4, nullptr));
case uint16_t(Op::F64Load):
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::F64, 8, nullptr));
case uint16_t(Op::I32Store8):
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 1, nullptr, nullptr));
case uint16_t(Op::I32Store16):
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 2, nullptr, nullptr));
case uint16_t(Op::I32Store):
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 4, nullptr, nullptr));
case uint16_t(Op::I64Store8):
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 1, nullptr, nullptr));
case uint16_t(Op::I64Store16):
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 2, nullptr, nullptr));
case uint16_t(Op::I64Store32):
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 4, nullptr, nullptr));
case uint16_t(Op::I64Store):
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 8, nullptr, nullptr));
case uint16_t(Op::F32Store):
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F32, 4, nullptr, nullptr));
case uint16_t(Op::F64Store):
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F64, 8, nullptr, nullptr));
case uint16_t(Op::GrowMemory):
CHECK(f.checkHasMemory() && f.iter().readGrowMemory(nullptr));
case uint16_t(Op::CurrentMemory):
CHECK(f.checkHasMemory() && f.iter().readCurrentMemory());
case uint16_t(Op::Br):
CHECK(f.iter().readBr(nullptr, nullptr, nullptr));
case uint16_t(Op::BrIf):
CHECK(f.iter().readBrIf(nullptr, nullptr, nullptr, nullptr));
case uint16_t(Op::BrTable):
CHECK(DecodeBrTable(f));
case uint16_t(Op::Return):
CHECK(f.iter().readReturn(nullptr));
case uint16_t(Op::Unreachable):
CHECK(f.iter().readUnreachable());
default:
return f.iter().unrecognizedOpcode(op);
}
}
MOZ_CRASH("unreachable");
#undef CHECK
}
static bool
DecodeImportSection(Decoder& d, ModuleGeneratorData* init, ImportVector* imports)
{
Maybe<Limits> memory;
Uint32Vector funcSigIndices;
if (!DecodeImportSection(d, init->sigs, &funcSigIndices, &init->globals, &init->tables, &memory,
imports))
return false;
for (uint32_t sigIndex : funcSigIndices) {
if (!init->funcSigs.append(&init->sigs[sigIndex]))
return false;
}
// The global data offsets will be filled in by ModuleGenerator::init.
if (!init->funcImportGlobalDataOffsets.resize(init->funcSigs.length()))
return false;
if (memory) {
init->memoryUsage = MemoryUsage::Unshared;
init->minMemoryLength = memory->initial;
init->maxMemoryLength = memory->maximum;
}
return true;
}
static bool
DecodeFunctionSection(Decoder& d, ModuleGeneratorData* init)
{
Uint32Vector funcSigIndexes;
if (!DecodeFunctionSection(d, init->sigs, init->funcSigs.length(), &funcSigIndexes))
return false;
if (!init->funcSigs.reserve(init->funcSigs.length() + funcSigIndexes.length()))
return false;
for (uint32_t sigIndex : funcSigIndexes)
init->funcSigs.infallibleAppend(&init->sigs[sigIndex]);
return true;
}
static bool
DecodeTableSection(Decoder& d, ModuleGeneratorData* init)
{
uint32_t sectionStart, sectionSize;
if (!d.startSection(SectionId::Table, &sectionStart, &sectionSize, "table"))
return false;
if (sectionStart == Decoder::NotStarted)
return true;
uint32_t numTables;
if (!d.readVarU32(&numTables))
return d.fail("failed to read number of tables");
if (numTables != 1)
return d.fail("the number of tables must be exactly one");
if (!DecodeTableLimits(d, &init->tables))
return false;
if (!d.finishSection(sectionStart, sectionSize, "table"))
return false;
return true;
}
static bool
DecodeMemorySection(Decoder& d, ModuleGeneratorData* init)
{
bool present;
Limits memory;
if (!DecodeMemorySection(d, UsesMemory(init->memoryUsage), &memory, &present))
return false;
if (present) {
init->memoryUsage = MemoryUsage::Unshared;
init->minMemoryLength = memory.initial;
init->maxMemoryLength = memory.maximum;
}
return true;
}
static bool
DecodeGlobalSection(Decoder& d, ModuleGeneratorData* init)
{
uint32_t sectionStart, sectionSize;
if (!d.startSection(SectionId::Global, &sectionStart, &sectionSize, "global"))
return false;
if (sectionStart == Decoder::NotStarted)
return true;
uint32_t numDefs;
if (!d.readVarU32(&numDefs))
return d.fail("expected number of globals");
CheckedInt<uint32_t> numGlobals = init->globals.length();
numGlobals += numDefs;
if (!numGlobals.isValid() || numGlobals.value() > MaxGlobals)
return d.fail("too many globals");
for (uint32_t i = 0; i < numDefs; i++) {
ValType type;
bool isMutable;
if (!DecodeGlobalType(d, &type, &isMutable))
return false;
InitExpr initializer;
if (!DecodeInitializerExpression(d, init->globals, type, &initializer))
return false;
if (!init->globals.append(GlobalDesc(initializer, isMutable)))
return false;
}
if (!d.finishSection(sectionStart, sectionSize, "global"))
return false;
return true;
}
typedef HashSet<const char*, CStringHasher, SystemAllocPolicy> CStringSet;
static UniqueChars
DecodeExportName(Decoder& d, CStringSet* dupSet)
{
UniqueChars exportName = DecodeName(d);
if (!exportName) {
d.fail("expected valid export name");
return nullptr;
}
CStringSet::AddPtr p = dupSet->lookupForAdd(exportName.get());
if (p) {
d.fail("duplicate export");
return nullptr;
}
if (!dupSet->add(p, exportName.get()))
return nullptr;
return Move(exportName);
}
static bool
DecodeExport(Decoder& d, ModuleGenerator& mg, CStringSet* dupSet)
{
UniqueChars fieldName = DecodeExportName(d, dupSet);
if (!fieldName)
return false;
uint32_t exportKind;
if (!d.readVarU32(&exportKind))
return d.fail("failed to read export kind");
switch (DefinitionKind(exportKind)) {
case DefinitionKind::Function: {
uint32_t funcIndex;
if (!d.readVarU32(&funcIndex))
return d.fail("expected export internal index");
if (funcIndex >= mg.numFuncs())
return d.fail("exported function index out of bounds");
return mg.addFuncExport(Move(fieldName), funcIndex);
}
case DefinitionKind::Table: {
uint32_t tableIndex;
if (!d.readVarU32(&tableIndex))
return d.fail("expected table index");
if (tableIndex >= mg.tables().length())
return d.fail("exported table index out of bounds");
return mg.addTableExport(Move(fieldName));
}
case DefinitionKind::Memory: {
uint32_t memoryIndex;
if (!d.readVarU32(&memoryIndex))
return d.fail("expected memory index");
if (memoryIndex > 0 || !mg.usesMemory())
return d.fail("exported memory index out of bounds");
return mg.addMemoryExport(Move(fieldName));
}
case DefinitionKind::Global: {
uint32_t globalIndex;
if (!d.readVarU32(&globalIndex))
return d.fail("expected global index");
if (globalIndex >= mg.globals().length())
return d.fail("exported global index out of bounds");
const GlobalDesc& global = mg.globals()[globalIndex];
if (!GlobalIsJSCompatible(d, global.type(), global.isMutable()))
return false;
return mg.addGlobalExport(Move(fieldName), globalIndex);
}
default:
return d.fail("unexpected export kind");
}
MOZ_CRASH("unreachable");
}
static bool
DecodeExportSection(Decoder& d, ModuleGenerator& mg)
{
uint32_t sectionStart, sectionSize;
if (!d.startSection(SectionId::Export, &sectionStart, &sectionSize, "export"))
return false;
if (sectionStart == Decoder::NotStarted)
return true;
CStringSet dupSet;
if (!dupSet.init())
return false;
uint32_t numExports;
if (!d.readVarU32(&numExports))
return d.fail("failed to read number of exports");
if (numExports > MaxExports)
return d.fail("too many exports");
for (uint32_t i = 0; i < numExports; i++) {
if (!DecodeExport(d, mg, &dupSet))
return false;
}
if (!d.finishSection(sectionStart, sectionSize, "export"))
return false;
return true;
}
static bool
DecodeFunctionBody(Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
{
uint32_t bodySize;
if (!d.readVarU32(&bodySize))
return d.fail("expected number of function body bytes");
if (d.bytesRemain() < bodySize)
return d.fail("function body length too big");
const uint8_t* bodyBegin = d.currentPosition();
const size_t offsetInModule = d.currentOffset();
FunctionGenerator fg;
if (!mg.startFuncDef(offsetInModule, &fg))
return false;
ValTypeVector locals;
const Sig& sig = mg.funcSig(funcIndex);
if (!locals.appendAll(sig.args()))
return false;
if (!DecodeLocalEntries(d, ModuleKind::Wasm, &locals))
return false;
FunctionDecoder f(mg, locals, d);
if (!f.iter().readFunctionStart(sig.ret()))
return false;
if (!DecodeFunctionBodyExprs(f))
return false;
if (!f.iter().readFunctionEnd())
return false;
if (d.currentPosition() != bodyBegin + bodySize)
return d.fail("function body length mismatch");
if (!fg.bytes().resize(bodySize))
return false;
memcpy(fg.bytes().begin(), bodyBegin, bodySize);
return mg.finishFuncDef(funcIndex, &fg);
}
static bool
DecodeStartSection(Decoder& d, ModuleGenerator& mg)
{
uint32_t sectionStart, sectionSize;
if (!d.startSection(SectionId::Start, &sectionStart, &sectionSize, "start"))
return false;
if (sectionStart == Decoder::NotStarted)
return true;
uint32_t funcIndex;
if (!d.readVarU32(&funcIndex))
return d.fail("failed to read start func index");
if (funcIndex >= mg.numFuncs())
return d.fail("unknown start function");
const Sig& sig = mg.funcSig(funcIndex);
if (!IsVoid(sig.ret()))
return d.fail("start function must not return anything");
if (sig.args().length())
return d.fail("start function must be nullary");
if (!mg.setStartFunction(funcIndex))
return false;
if (!d.finishSection(sectionStart, sectionSize, "start"))
return false;
return true;
}
static bool
DecodeCodeSection(Decoder& d, ModuleGenerator& mg)
{
if (!mg.startFuncDefs())
return false;
uint32_t sectionStart, sectionSize;
if (!d.startSection(SectionId::Code, &sectionStart, &sectionSize, "code"))
return false;
if (sectionStart == Decoder::NotStarted) {
if (mg.numFuncDefs() != 0)
return d.fail("expected function bodies");
return mg.finishFuncDefs();
}
uint32_t numFuncDefs;
if (!d.readVarU32(&numFuncDefs))
return d.fail("expected function body count");
if (numFuncDefs != mg.numFuncDefs())
return d.fail("function body count does not match function signature count");
for (uint32_t funcDefIndex = 0; funcDefIndex < numFuncDefs; funcDefIndex++) {
if (!DecodeFunctionBody(d, mg, mg.numFuncImports() + funcDefIndex))
return false;
}
if (!d.finishSection(sectionStart, sectionSize, "code"))
return false;
return mg.finishFuncDefs();
}
static bool
DecodeElemSection(Decoder& d, ModuleGenerator& mg)
{
uint32_t sectionStart, sectionSize;
if (!d.startSection(SectionId::Elem, &sectionStart, &sectionSize, "elem"))
return false;
if (sectionStart == Decoder::NotStarted)
return true;
uint32_t numSegments;
if (!d.readVarU32(&numSegments))
return d.fail("failed to read number of elem segments");
if (numSegments > MaxElemSegments)
return d.fail("too many elem segments");
for (uint32_t i = 0; i < numSegments; i++) {
uint32_t tableIndex;
if (!d.readVarU32(&tableIndex))
return d.fail("expected table index");
MOZ_ASSERT(mg.tables().length() <= 1);
if (tableIndex >= mg.tables().length())
return d.fail("table index out of range");
InitExpr offset;
if (!DecodeInitializerExpression(d, mg.globals(), ValType::I32, &offset))
return false;
uint32_t numElems;
if (!d.readVarU32(&numElems))
return d.fail("expected segment size");
Uint32Vector elemFuncIndices;
if (!elemFuncIndices.resize(numElems))
return false;
for (uint32_t i = 0; i < numElems; i++) {
if (!d.readVarU32(&elemFuncIndices[i]))
return d.fail("failed to read element function index");
if (elemFuncIndices[i] >= mg.numFuncs())
return d.fail("table element out of range");
}
if (!mg.addElemSegment(offset, Move(elemFuncIndices)))
return false;
}
if (!d.finishSection(sectionStart, sectionSize, "elem"))
return false;
return true;
}
static void
MaybeDecodeNameSectionBody(Decoder& d, ModuleGenerator& mg)
{
// For simplicity, ignore all failures, even OOM. Failure will simply result
// in the names section not being included for this module.
uint32_t numFuncNames;
if (!d.readVarU32(&numFuncNames))
return;
if (numFuncNames > MaxFuncs)
return;
NameInBytecodeVector funcNames;
if (!funcNames.resize(numFuncNames))
return;
for (uint32_t i = 0; i < numFuncNames; i++) {
uint32_t numBytes;
if (!d.readVarU32(&numBytes))
return;
NameInBytecode name;
name.offset = d.currentOffset();
name.length = numBytes;
funcNames[i] = name;
if (!d.readBytes(numBytes))
return;
// Skip local names for a function.
uint32_t numLocals;
if (!d.readVarU32(&numLocals))
return;
for (uint32_t j = 0; j < numLocals; j++) {
uint32_t numBytes;
if (!d.readVarU32(&numBytes))
return;
if (!d.readBytes(numBytes))
return;
}
}
mg.setFuncNames(Move(funcNames));
}
static bool
DecodeDataSection(Decoder& d, ModuleGenerator& mg)
{
DataSegmentVector dataSegments;
if (!DecodeDataSection(d, mg.usesMemory(), mg.minMemoryLength(), mg.globals(), &dataSegments))
return false;
mg.setDataSegments(Move(dataSegments));
return true;
}
static bool
DecodeNameSection(Decoder& d, ModuleGenerator& mg)
{
uint32_t sectionStart, sectionSize;
if (!d.startUserDefinedSection(NameSectionName, &sectionStart, &sectionSize))
return false;
if (sectionStart == Decoder::NotStarted)
return true;
// Once started, user-defined sections do not report validation errors.
MaybeDecodeNameSectionBody(d, mg);
d.finishUserDefinedSection(sectionStart, sectionSize);
return true;
}
bool
CompileArgs::initFromContext(ExclusiveContext* cx, ScriptedCaller&& scriptedCaller)
{
alwaysBaseline = cx->options().wasmAlwaysBaseline();
this->scriptedCaller = Move(scriptedCaller);
return assumptions.initBuildIdFromContext(cx);
}
SharedModule
wasm::Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueChars* error)
{
MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
Decoder d(bytecode.begin(), bytecode.end(), error);
auto init = js::MakeUnique<ModuleGeneratorData>();
if (!init)
return nullptr;
if (!DecodePreamble(d))
return nullptr;
if (!DecodeTypeSection(d, &init->sigs))
return nullptr;
ImportVector imports;
if (!::DecodeImportSection(d, init.get(), &imports))
return nullptr;
if (!::DecodeFunctionSection(d, init.get()))
return nullptr;
if (!DecodeTableSection(d, init.get()))
return nullptr;
if (!::DecodeMemorySection(d, init.get()))
return nullptr;
if (!DecodeGlobalSection(d, init.get()))
return nullptr;
ModuleGenerator mg(Move(imports));
if (!mg.init(Move(init), args))
return nullptr;
if (!DecodeExportSection(d, mg))
return nullptr;
if (!DecodeStartSection(d, mg))
return nullptr;
if (!DecodeElemSection(d, mg))
return nullptr;
if (!DecodeCodeSection(d, mg))
return nullptr;
if (!::DecodeDataSection(d, mg))
return nullptr;
if (!DecodeNameSection(d, mg))
return nullptr;
if (!DecodeUnknownSections(d))
return nullptr;
MOZ_ASSERT(!*error, "unreported error in decoding");
return mg.finish(bytecode);
}