mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 15:25:52 +00:00
Bug 1286517: Implement Global section in WebAssembly; r=luke
MozReview-Commit-ID: aD6rBQ3e57 --HG-- extra : rebase_source : 6987461ccfee07a5f494745fcb02cbac825def95
This commit is contained in:
parent
8839c5fde7
commit
3bb9e21ec3
@ -111,7 +111,6 @@ class AsmJSGlobal
|
||||
Which which_;
|
||||
union {
|
||||
struct {
|
||||
uint32_t globalDataOffset_;
|
||||
VarInitKind initKind_;
|
||||
union {
|
||||
ValType importType_;
|
||||
@ -150,10 +149,6 @@ class AsmJSGlobal
|
||||
Which which() const {
|
||||
return pod.which_;
|
||||
}
|
||||
uint32_t varGlobalDataOffset() const {
|
||||
MOZ_ASSERT(pod.which_ == Variable);
|
||||
return pod.u.var.globalDataOffset_;
|
||||
}
|
||||
VarInitKind varInitKind() const {
|
||||
MOZ_ASSERT(pod.which_ == Variable);
|
||||
return pod.u.var.initKind_;
|
||||
@ -1447,7 +1442,6 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
}
|
||||
unsigned varOrConstIndex() const {
|
||||
MOZ_ASSERT(which_ == Variable || which_ == ConstantImport);
|
||||
MOZ_ASSERT(u.varOrConst.index_ != -1u);
|
||||
return u.varOrConst.index_;
|
||||
}
|
||||
bool isConst() const {
|
||||
@ -1851,7 +1845,7 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
MOZ_ASSERT(type == Type::canonicalize(Type::lit(lit)));
|
||||
|
||||
uint32_t index;
|
||||
if (!mg_.allocateGlobal(type.canonicalToValType(), isConst, &index))
|
||||
if (!mg_.addGlobal(type.canonicalToValType(), isConst, &index))
|
||||
return false;
|
||||
|
||||
Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
|
||||
@ -1868,7 +1862,6 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
AsmJSGlobal g(AsmJSGlobal::Variable, nullptr);
|
||||
g.pod.u.var.initKind_ = AsmJSGlobal::InitConstant;
|
||||
g.pod.u.var.u.val_ = lit.value();
|
||||
g.pod.u.var.globalDataOffset_ = mg_.global(index).globalDataOffset;
|
||||
return asmJSMetadata_->asmJSGlobals.append(Move(g));
|
||||
}
|
||||
bool addGlobalVarImport(PropertyName* var, PropertyName* field, Type type, bool isConst) {
|
||||
@ -1880,7 +1873,7 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
|
||||
uint32_t index;
|
||||
ValType valType = type.canonicalToValType();
|
||||
if (!mg_.allocateGlobal(valType, isConst, &index))
|
||||
if (!mg_.addGlobal(valType, isConst, &index))
|
||||
return false;
|
||||
|
||||
Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
|
||||
@ -1895,7 +1888,6 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
AsmJSGlobal g(AsmJSGlobal::Variable, Move(fieldChars));
|
||||
g.pod.u.var.initKind_ = AsmJSGlobal::InitImport;
|
||||
g.pod.u.var.u.importType_ = valType;
|
||||
g.pod.u.var.globalDataOffset_ = mg_.global(index).globalDataOffset;
|
||||
return asmJSMetadata_->asmJSGlobals.append(Move(g));
|
||||
}
|
||||
bool addArrayView(PropertyName* var, Scalar::Type vt, PropertyName* maybeField) {
|
||||
@ -7817,25 +7809,9 @@ CheckBuffer(JSContext* cx, const AsmJSMetadata& metadata, HandleValue bufferVal,
|
||||
}
|
||||
|
||||
static bool
|
||||
TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata& metadata,
|
||||
MutableHandleWasmInstanceObject instanceObj, MutableHandleObject exportObj)
|
||||
GetImports(JSContext* cx, const AsmJSMetadata& metadata, HandleValue globalVal,
|
||||
HandleValue importVal, MutableHandle<FunctionVector> funcImports, ValVector* valImports)
|
||||
{
|
||||
HandleValue globalVal = args.get(0);
|
||||
HandleValue importVal = args.get(1);
|
||||
HandleValue bufferVal = args.get(2);
|
||||
|
||||
RootedArrayBufferObjectMaybeShared buffer(cx);
|
||||
RootedWasmMemoryObject memory(cx);
|
||||
if (module.metadata().usesMemory()) {
|
||||
if (!CheckBuffer(cx, metadata, bufferVal, &buffer))
|
||||
return false;
|
||||
|
||||
memory = WasmMemoryObject::create(cx, buffer, nullptr);
|
||||
if (!memory)
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector<Val> valImports(cx);
|
||||
Rooted<FunctionVector> ffis(cx, FunctionVector(cx));
|
||||
if (!ffis.resize(metadata.numFFIs))
|
||||
return false;
|
||||
@ -7843,12 +7819,10 @@ TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata
|
||||
for (const AsmJSGlobal& global : metadata.asmJSGlobals) {
|
||||
switch (global.which()) {
|
||||
case AsmJSGlobal::Variable: {
|
||||
// We don't have any global data into which to write the imported
|
||||
// values until after instantiation, so save them in a Vector.
|
||||
Val val;
|
||||
if (!ValidateGlobalVariable(cx, global, importVal, &val))
|
||||
return false;
|
||||
if (!valImports.append(val))
|
||||
if (!valImports->append(val))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@ -7884,14 +7858,40 @@ TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata
|
||||
}
|
||||
}
|
||||
|
||||
Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
|
||||
for (const AsmJSImport& import : metadata.asmJSImports) {
|
||||
if (!funcs.append(ffis[import.ffiIndex()]))
|
||||
if (!funcImports.append(ffis[import.ffiIndex()]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata& metadata,
|
||||
MutableHandleWasmInstanceObject instanceObj, MutableHandleObject exportObj)
|
||||
{
|
||||
HandleValue globalVal = args.get(0);
|
||||
HandleValue importVal = args.get(1);
|
||||
HandleValue bufferVal = args.get(2);
|
||||
|
||||
RootedArrayBufferObjectMaybeShared buffer(cx);
|
||||
RootedWasmMemoryObject memory(cx);
|
||||
if (module.metadata().usesMemory()) {
|
||||
if (!CheckBuffer(cx, metadata, bufferVal, &buffer))
|
||||
return false;
|
||||
|
||||
memory = WasmMemoryObject::create(cx, buffer, nullptr);
|
||||
if (!memory)
|
||||
return false;
|
||||
}
|
||||
|
||||
ValVector valImports;
|
||||
Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
|
||||
if (!GetImports(cx, metadata, globalVal, importVal, &funcs, &valImports))
|
||||
return false;
|
||||
|
||||
RootedWasmTableObject table(cx);
|
||||
if (!module.instantiate(cx, funcs, table, memory, nullptr, instanceObj))
|
||||
if (!module.instantiate(cx, funcs, table, memory, valImports, nullptr, instanceObj))
|
||||
return false;
|
||||
|
||||
RootedValue exportObjVal(cx);
|
||||
@ -7900,16 +7900,6 @@ TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata
|
||||
|
||||
MOZ_RELEASE_ASSERT(exportObjVal.isObject());
|
||||
exportObj.set(&exportObjVal.toObject());
|
||||
|
||||
// Now write the imported values into global data.
|
||||
uint8_t* globalData = instanceObj->instance().codeSegment().globalData();
|
||||
uint32_t valIndex = 0;
|
||||
for (const AsmJSGlobal& global : metadata.asmJSGlobals) {
|
||||
if (global.which() == AsmJSGlobal::Variable)
|
||||
valImports[valIndex++].writePayload(globalData + global.varGlobalDataOffset());
|
||||
}
|
||||
MOZ_ASSERT(valIndex == valImports.length());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5468,28 +5468,49 @@ BaseCompiler::emitGetGlobal()
|
||||
|
||||
const GlobalDesc& global = mg_.globals[id];
|
||||
|
||||
switch (global.type) {
|
||||
if (global.isConstant()) {
|
||||
Val value = global.constantValue();
|
||||
switch (value.type()) {
|
||||
case ValType::I32:
|
||||
pushI32(value.i32());
|
||||
break;
|
||||
case ValType::I64:
|
||||
pushI64(value.i64());
|
||||
break;
|
||||
case ValType::F32:
|
||||
pushF32(value.f32());
|
||||
break;
|
||||
case ValType::F64:
|
||||
pushF64(value.f64());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Global constant type");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (global.type()) {
|
||||
case ValType::I32: {
|
||||
RegI32 rv = needI32();
|
||||
loadGlobalVarI32(global.globalDataOffset, rv);
|
||||
loadGlobalVarI32(global.offset(), rv);
|
||||
pushI32(rv);
|
||||
break;
|
||||
}
|
||||
case ValType::I64: {
|
||||
RegI64 rv = needI64();
|
||||
loadGlobalVarI64(global.globalDataOffset, rv);
|
||||
loadGlobalVarI64(global.offset(), rv);
|
||||
pushI64(rv);
|
||||
break;
|
||||
}
|
||||
case ValType::F32: {
|
||||
RegF32 rv = needF32();
|
||||
loadGlobalVarF32(global.globalDataOffset, rv);
|
||||
loadGlobalVarF32(global.offset(), rv);
|
||||
pushF32(rv);
|
||||
break;
|
||||
}
|
||||
case ValType::F64: {
|
||||
RegF64 rv = needF64();
|
||||
loadGlobalVarF64(global.globalDataOffset, rv);
|
||||
loadGlobalVarF64(global.offset(), rv);
|
||||
pushF64(rv);
|
||||
break;
|
||||
}
|
||||
@ -5513,28 +5534,28 @@ BaseCompiler::emitSetGlobal()
|
||||
|
||||
const GlobalDesc& global = mg_.globals[id];
|
||||
|
||||
switch (global.type) {
|
||||
switch (global.type()) {
|
||||
case ValType::I32: {
|
||||
RegI32 rv = popI32();
|
||||
storeGlobalVarI32(global.globalDataOffset, rv);
|
||||
storeGlobalVarI32(global.offset(), rv);
|
||||
pushI32(rv);
|
||||
break;
|
||||
}
|
||||
case ValType::I64: {
|
||||
RegI64 rv = popI64();
|
||||
storeGlobalVarI64(global.globalDataOffset, rv);
|
||||
storeGlobalVarI64(global.offset(), rv);
|
||||
pushI64(rv);
|
||||
break;
|
||||
}
|
||||
case ValType::F32: {
|
||||
RegF32 rv = popF32();
|
||||
storeGlobalVarF32(global.globalDataOffset, rv);
|
||||
storeGlobalVarF32(global.offset(), rv);
|
||||
pushF32(rv);
|
||||
break;
|
||||
}
|
||||
case ValType::F64: {
|
||||
RegF64 rv = popF64();
|
||||
storeGlobalVarF64(global.globalDataOffset, rv);
|
||||
storeGlobalVarF64(global.offset(), rv);
|
||||
pushF64(rv);
|
||||
break;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ static const uint32_t MagicNumber = 0x6d736100; // "\0asm"
|
||||
static const uint32_t EncodingVersion = 0x0b;
|
||||
|
||||
static const char TypeSectionId[] = "type";
|
||||
static const char GlobalSectionId[] = "global";
|
||||
static const char ImportSectionId[] = "import";
|
||||
static const char FunctionSectionId[] = "function";
|
||||
static const char TableSectionId[] = "table";
|
||||
@ -70,7 +71,8 @@ enum class DefinitionKind
|
||||
{
|
||||
Function = 0x00,
|
||||
Table = 0x01,
|
||||
Memory = 0x02
|
||||
Memory = 0x02,
|
||||
Global = 0x03
|
||||
};
|
||||
|
||||
enum class ResizableFlags
|
||||
@ -80,6 +82,12 @@ enum class ResizableFlags
|
||||
AllowedMask = 0x3
|
||||
};
|
||||
|
||||
enum class GlobalFlags
|
||||
{
|
||||
IsMutable = 0x1,
|
||||
AllowedMask = 0x1
|
||||
};
|
||||
|
||||
enum class Expr
|
||||
{
|
||||
// Control flow operators
|
||||
|
@ -1211,7 +1211,7 @@ ExprIter<Policy>::readGetGlobal(const GlobalDescVector& globals, uint32_t* id)
|
||||
if (Validate && validateId >= globals.length())
|
||||
return fail("get_global index out of range");
|
||||
|
||||
if (!push(ToExprType(globals[validateId].type)))
|
||||
if (!push(ToExprType(globals[validateId].type())))
|
||||
return false;
|
||||
|
||||
if (Output)
|
||||
@ -1233,7 +1233,10 @@ ExprIter<Policy>::readSetGlobal(const GlobalDescVector& globals, uint32_t* id, V
|
||||
if (Validate && validateId >= globals.length())
|
||||
return fail("set_global index out of range");
|
||||
|
||||
if (!topWithType(ToExprType(globals[validateId].type), value))
|
||||
if (Validate && !globals[validateId].isMutable())
|
||||
return fail("can't write an immutable global");
|
||||
|
||||
if (!topWithType(ToExprType(globals[validateId].type()), value))
|
||||
return false;
|
||||
|
||||
if (Output)
|
||||
|
@ -417,6 +417,7 @@ Metadata::serializedSize() const
|
||||
SerializedVectorSize(funcImports) +
|
||||
SerializedVectorSize(funcExports) +
|
||||
SerializedVectorSize(sigIds) +
|
||||
SerializedPodVectorSize(globals) +
|
||||
SerializedPodVectorSize(tables) +
|
||||
SerializedPodVectorSize(memoryAccesses) +
|
||||
SerializedPodVectorSize(boundsChecks) +
|
||||
@ -435,6 +436,7 @@ Metadata::serialize(uint8_t* cursor) const
|
||||
cursor = SerializeVector(cursor, funcImports);
|
||||
cursor = SerializeVector(cursor, funcExports);
|
||||
cursor = SerializeVector(cursor, sigIds);
|
||||
cursor = SerializePodVector(cursor, globals);
|
||||
cursor = SerializePodVector(cursor, tables);
|
||||
cursor = SerializePodVector(cursor, memoryAccesses);
|
||||
cursor = SerializePodVector(cursor, boundsChecks);
|
||||
@ -454,6 +456,7 @@ Metadata::deserialize(const uint8_t* cursor)
|
||||
(cursor = DeserializeVector(cursor, &funcImports)) &&
|
||||
(cursor = DeserializeVector(cursor, &funcExports)) &&
|
||||
(cursor = DeserializeVector(cursor, &sigIds)) &&
|
||||
(cursor = DeserializePodVector(cursor, &globals)) &&
|
||||
(cursor = DeserializePodVector(cursor, &tables)) &&
|
||||
(cursor = DeserializePodVector(cursor, &memoryAccesses)) &&
|
||||
(cursor = DeserializePodVector(cursor, &boundsChecks)) &&
|
||||
@ -472,6 +475,7 @@ Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
|
||||
return SizeOfVectorExcludingThis(funcImports, mallocSizeOf) +
|
||||
SizeOfVectorExcludingThis(funcExports, mallocSizeOf) +
|
||||
SizeOfVectorExcludingThis(sigIds, mallocSizeOf) +
|
||||
globals.sizeOfExcludingThis(mallocSizeOf) +
|
||||
tables.sizeOfExcludingThis(mallocSizeOf) +
|
||||
memoryAccesses.sizeOfExcludingThis(mallocSizeOf) +
|
||||
boundsChecks.sizeOfExcludingThis(mallocSizeOf) +
|
||||
|
@ -424,7 +424,6 @@ typedef Vector<char16_t, 64> TwoByteName;
|
||||
// Metadata is built incrementally by ModuleGenerator and then shared immutably
|
||||
// between modules.
|
||||
|
||||
|
||||
class MetadataCacheablePod
|
||||
{
|
||||
static const uint32_t NO_START_FUNCTION = UINT32_MAX;
|
||||
@ -467,6 +466,7 @@ struct Metadata : ShareableBase<Metadata>, MetadataCacheablePod
|
||||
FuncImportVector funcImports;
|
||||
FuncExportVector funcExports;
|
||||
SigWithIdVector sigIds;
|
||||
GlobalDescVector globals;
|
||||
TableDescVector tables;
|
||||
MemoryAccessVector memoryAccesses;
|
||||
BoundsCheckVector boundsChecks;
|
||||
|
@ -236,6 +236,10 @@ DecodeExpr(FunctionDecoder& f)
|
||||
return f.iter().readGetLocal(f.locals(), nullptr);
|
||||
case Expr::SetLocal:
|
||||
return f.iter().readSetLocal(f.locals(), nullptr, nullptr);
|
||||
case Expr::GetGlobal:
|
||||
return f.iter().readGetGlobal(f.mg().globals(), nullptr);
|
||||
case Expr::SetGlobal:
|
||||
return f.iter().readSetGlobal(f.mg().globals(), nullptr, nullptr);
|
||||
case Expr::Select:
|
||||
return f.iter().readSelect(nullptr, nullptr, nullptr, nullptr);
|
||||
case Expr::Block:
|
||||
@ -746,6 +750,48 @@ DecodeResizableTable(Decoder& d, ModuleGeneratorData* init)
|
||||
return init->tables.append(table);
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable)
|
||||
{
|
||||
if (!d.readValType(type))
|
||||
return Fail(d, "bad global type");
|
||||
|
||||
if (*type == ValType::I64 && !IsI64Implemented())
|
||||
return Fail(d, "int64 NYI");
|
||||
|
||||
uint32_t flags;
|
||||
if (!d.readVarU32(&flags))
|
||||
return Fail(d, "expected flags");
|
||||
|
||||
if (flags & ~uint32_t(GlobalFlags::AllowedMask))
|
||||
return Fail(d, "unexpected bits set in flags");
|
||||
|
||||
*isMutable = flags & uint32_t(GlobalFlags::IsMutable);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GlobalIsJSCompatible(Decoder& d, ValType type, bool isMutable)
|
||||
{
|
||||
switch (type) {
|
||||
case ValType::I32:
|
||||
case ValType::F32:
|
||||
case ValType::F64:
|
||||
break;
|
||||
case ValType::I64:
|
||||
if (!JitOptions.wasmTestMode)
|
||||
return Fail(d, "can't import/export an Int64 global to JS");
|
||||
break;
|
||||
default:
|
||||
return Fail(d, "unexpected variable type in global import/export");
|
||||
}
|
||||
|
||||
if (isMutable)
|
||||
return Fail(d, "can't import/export mutable globals in the MVP");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeImport(Decoder& d, bool newFormat, ModuleGeneratorData* init, ImportVector* imports)
|
||||
{
|
||||
@ -810,6 +856,17 @@ DecodeImport(Decoder& d, bool newFormat, ModuleGeneratorData* init, ImportVector
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case DefinitionKind::Global: {
|
||||
ValType type;
|
||||
bool isMutable;
|
||||
if (!DecodeGlobalType(d, &type, &isMutable))
|
||||
return false;
|
||||
if (!GlobalIsJSCompatible(d, type, isMutable))
|
||||
return false;
|
||||
if (!init->globals.append(GlobalDesc(type, isMutable, init->globals.length())))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return Fail(d, "unsupported import kind");
|
||||
}
|
||||
@ -946,6 +1003,105 @@ DecodeMemorySection(Decoder& d, bool newFormat, ModuleGeneratorData* init, bool*
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected,
|
||||
InitExpr* init)
|
||||
{
|
||||
Expr expr;
|
||||
if (!d.readExpr(&expr))
|
||||
return Fail(d, "failed to read initializer type");
|
||||
|
||||
switch (expr) {
|
||||
case Expr::I32Const: {
|
||||
int32_t i32;
|
||||
if (!d.readVarS32(&i32))
|
||||
return Fail(d, "failed to read initializer i32 expression");
|
||||
*init = InitExpr(Val(uint32_t(i32)));
|
||||
break;
|
||||
}
|
||||
case Expr::I64Const: {
|
||||
int64_t i64;
|
||||
if (!d.readVarS64(&i64))
|
||||
return Fail(d, "failed to read initializer i64 expression");
|
||||
*init = InitExpr(Val(uint64_t(i64)));
|
||||
break;
|
||||
}
|
||||
case Expr::F32Const: {
|
||||
float f32;
|
||||
if (!d.readFixedF32(&f32))
|
||||
return Fail(d, "failed to read initializer f32 expression");
|
||||
*init = InitExpr(Val(f32));
|
||||
break;
|
||||
}
|
||||
case Expr::F64Const: {
|
||||
double f64;
|
||||
if (!d.readFixedF64(&f64))
|
||||
return Fail(d, "failed to read initializer f64 expression");
|
||||
*init = InitExpr(Val(f64));
|
||||
break;
|
||||
}
|
||||
case Expr::GetGlobal: {
|
||||
uint32_t i;
|
||||
if (!d.readVarU32(&i))
|
||||
return Fail(d, "failed to read get_global index in initializer expression");
|
||||
if (i >= globals.length())
|
||||
return Fail(d, "global index out of range in initializer expression");
|
||||
if (!globals[i].isImport() || globals[i].isMutable())
|
||||
return Fail(d, "initializer expression must reference a global immutable import");
|
||||
*init = InitExpr(i, globals[i].type());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return Fail(d, "unexpected initializer expression");
|
||||
}
|
||||
}
|
||||
|
||||
if (expected != init->type())
|
||||
return Fail(d, "type mismatch: initializer type and expected type don't match");
|
||||
|
||||
Expr end;
|
||||
if (!d.readExpr(&end) || end != Expr::End)
|
||||
return Fail(d, "failed to read end of initializer expression");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeGlobalSection(Decoder& d, ModuleGeneratorData* init)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(GlobalSectionId, §ionStart, §ionSize))
|
||||
return Fail(d, "failed to start section");
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
||||
uint32_t numGlobals;
|
||||
if (!d.readVarU32(&numGlobals))
|
||||
return Fail(d, "expected number of globals");
|
||||
|
||||
if (numGlobals > MaxGlobals)
|
||||
return Fail(d, "too many globals");
|
||||
|
||||
for (uint32_t i = 0; i < numGlobals; 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))
|
||||
return Fail(d, "globals section byte size mismatch");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef HashSet<const char*, CStringHasher, SystemAllocPolicy> CStringSet;
|
||||
|
||||
static UniqueChars
|
||||
@ -1032,6 +1188,20 @@ DecodeExport(Decoder& d, bool newFormat, ModuleGenerator& mg, CStringSet* dupSet
|
||||
|
||||
return mg.addMemoryExport(Move(fieldName));
|
||||
}
|
||||
case DefinitionKind::Global: {
|
||||
uint32_t globalIndex;
|
||||
if (!d.readVarU32(&globalIndex))
|
||||
return Fail(d, "expected global index");
|
||||
|
||||
if (globalIndex >= mg.globals().length())
|
||||
return Fail(d, "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 Fail(d, "unexpected export kind");
|
||||
}
|
||||
@ -1459,6 +1629,9 @@ wasm::Compile(const ShareableBytes& bytecode, CompileArgs&& args, UniqueChars* e
|
||||
if (!DecodeMemorySection(d, newFormat, init.get(), &memoryExported))
|
||||
return nullptr;
|
||||
|
||||
if (!DecodeGlobalSection(d, init.get()))
|
||||
return nullptr;
|
||||
|
||||
ModuleGenerator mg(Move(imports));
|
||||
if (!mg.init(Move(init), Move(args)))
|
||||
return nullptr;
|
||||
|
@ -172,6 +172,13 @@ ModuleGenerator::init(UniqueModuleGeneratorData shared, CompileArgs&& args,
|
||||
sig.id = SigIdDesc::immediate(sig);
|
||||
}
|
||||
}
|
||||
|
||||
for (GlobalDesc& global : shared_->globals) {
|
||||
if (global.isConstant())
|
||||
continue;
|
||||
if (!allocateGlobal(&global))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(shared_->sigs.length() == MaxSigs);
|
||||
MOZ_ASSERT(shared_->tables.length() == MaxTables);
|
||||
@ -590,11 +597,11 @@ ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* g
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::allocateGlobal(ValType type, bool isConst, uint32_t* index)
|
||||
ModuleGenerator::allocateGlobal(GlobalDesc* global)
|
||||
{
|
||||
MOZ_ASSERT(!startedFuncDefs_);
|
||||
unsigned width = 0;
|
||||
switch (type) {
|
||||
switch (global->type()) {
|
||||
case ValType::I32:
|
||||
case ValType::F32:
|
||||
width = 4;
|
||||
@ -621,8 +628,22 @@ ModuleGenerator::allocateGlobal(ValType type, bool isConst, uint32_t* index)
|
||||
if (!allocateGlobalBytes(width, width, &offset))
|
||||
return false;
|
||||
|
||||
global->setOffset(offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::addGlobal(ValType type, bool isConst, uint32_t* index)
|
||||
{
|
||||
MOZ_ASSERT(isAsmJS());
|
||||
MOZ_ASSERT(!startedFuncDefs_);
|
||||
|
||||
*index = shared_->globals.length();
|
||||
return shared_->globals.append(GlobalDesc(type, offset, isConst));
|
||||
GlobalDesc global(type, !isConst, *index);
|
||||
if (!allocateGlobal(&global))
|
||||
return false;
|
||||
|
||||
return shared_->globals.append(global);
|
||||
}
|
||||
|
||||
void
|
||||
@ -713,7 +734,7 @@ ModuleGenerator::funcImport(uint32_t funcImportIndex) const
|
||||
bool
|
||||
ModuleGenerator::addFuncExport(UniqueChars fieldName, uint32_t funcIndex)
|
||||
{
|
||||
return exports_.emplaceBack(Move(fieldName), funcIndex) &&
|
||||
return exports_.emplaceBack(Move(fieldName), funcIndex, DefinitionKind::Function) &&
|
||||
exportedFuncs_.put(funcIndex);
|
||||
}
|
||||
|
||||
@ -731,6 +752,12 @@ ModuleGenerator::addMemoryExport(UniqueChars fieldName)
|
||||
return exports_.emplaceBack(Move(fieldName), DefinitionKind::Memory);
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::addGlobalExport(UniqueChars fieldName, uint32_t globalIndex)
|
||||
{
|
||||
return exports_.emplaceBack(Move(fieldName), globalIndex, DefinitionKind::Global);
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::setStartFunction(uint32_t funcIndex)
|
||||
{
|
||||
@ -970,6 +997,7 @@ ModuleGenerator::finish(const ShareableBytes& bytecode)
|
||||
metadata_->minMemoryLength = shared_->minMemoryLength;
|
||||
metadata_->maxMemoryLength = shared_->maxMemoryLength;
|
||||
metadata_->tables = Move(shared_->tables);
|
||||
metadata_->globals = Move(shared_->globals);
|
||||
|
||||
// These Vectors can get large and the excess capacity can be significant,
|
||||
// so realloc them down to size.
|
||||
|
@ -136,6 +136,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
||||
MOZ_MUST_USE bool finishLinkData(Bytes& code);
|
||||
MOZ_MUST_USE bool addFuncImport(const Sig& sig, uint32_t globalDataOffset);
|
||||
MOZ_MUST_USE bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
|
||||
MOZ_MUST_USE bool allocateGlobal(GlobalDesc* global);
|
||||
|
||||
public:
|
||||
explicit ModuleGenerator(ImportVector&& imports);
|
||||
@ -165,8 +166,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
||||
const SigWithId& funcSig(uint32_t funcIndex) const;
|
||||
|
||||
// Globals:
|
||||
MOZ_MUST_USE bool allocateGlobal(ValType type, bool isConst, uint32_t* index);
|
||||
const GlobalDesc& global(unsigned index) const { return shared_->globals[index]; }
|
||||
const GlobalDescVector& globals() const { return shared_->globals; }
|
||||
|
||||
// Imports:
|
||||
uint32_t numFuncImports() const;
|
||||
@ -176,6 +176,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
||||
MOZ_MUST_USE bool addFuncExport(UniqueChars fieldName, uint32_t funcIndex);
|
||||
MOZ_MUST_USE bool addTableExport(UniqueChars fieldName);
|
||||
MOZ_MUST_USE bool addMemoryExport(UniqueChars fieldName);
|
||||
MOZ_MUST_USE bool addGlobalExport(UniqueChars fieldName, uint32_t globalIndex);
|
||||
|
||||
// Function definitions:
|
||||
MOZ_MUST_USE bool startFuncDefs();
|
||||
@ -201,6 +202,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
||||
MOZ_MUST_USE bool initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncIndices);
|
||||
void initMemoryUsage(MemoryUsage memoryUsage);
|
||||
void bumpMinMemoryLength(uint32_t newMinMemoryLength);
|
||||
MOZ_MUST_USE bool addGlobal(ValType type, bool isConst, uint32_t* index);
|
||||
|
||||
// Finish compilation, provided the list of imports and source bytecode.
|
||||
// Both these Vectors may be empty (viz., b/c asm.js does different things
|
||||
|
@ -203,51 +203,6 @@ Instance::toggleProfiling(JSContext* cx)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ReadI64Object(JSContext* cx, HandleValue v, int64_t* i64)
|
||||
{
|
||||
if (!v.isObject()) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
|
||||
"i64 JS value must be an object");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject obj(cx, &v.toObject());
|
||||
|
||||
int32_t* i32 = (int32_t*)i64;
|
||||
|
||||
RootedValue val(cx);
|
||||
if (!JS_GetProperty(cx, obj, "low", &val))
|
||||
return false;
|
||||
if (!ToInt32(cx, val, &i32[0]))
|
||||
return false;
|
||||
|
||||
if (!JS_GetProperty(cx, obj, "high", &val))
|
||||
return false;
|
||||
if (!ToInt32(cx, val, &i32[1]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
CreateI64Object(JSContext* cx, int64_t i64)
|
||||
{
|
||||
RootedObject result(cx, JS_NewPlainObject(cx));
|
||||
if (!result)
|
||||
return nullptr;
|
||||
|
||||
RootedValue val(cx, Int32Value(uint32_t(i64)));
|
||||
if (!JS_DefineProperty(cx, result, "low", val, JSPROP_ENUMERATE))
|
||||
return nullptr;
|
||||
|
||||
val = Int32Value(uint32_t(i64 >> 32));
|
||||
if (!JS_DefineProperty(cx, result, "high", val, JSPROP_ENUMERATE))
|
||||
return nullptr;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
Instance::callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
|
||||
MutableHandleValue rval)
|
||||
@ -423,7 +378,8 @@ Instance::Instance(JSContext* cx,
|
||||
const ShareableBytes* maybeBytecode,
|
||||
HandleWasmMemoryObject memory,
|
||||
SharedTableVector&& tables,
|
||||
Handle<FunctionVector> funcImports)
|
||||
Handle<FunctionVector> funcImports,
|
||||
const ValVector& globalImports)
|
||||
: codeSegment_(Move(codeSegment)),
|
||||
metadata_(&metadata),
|
||||
maybeBytecode_(maybeBytecode),
|
||||
@ -442,6 +398,40 @@ Instance::Instance(JSContext* cx,
|
||||
exit.baselineScript = nullptr;
|
||||
}
|
||||
|
||||
uint8_t* globalData = codeSegment_->globalData();
|
||||
|
||||
for (size_t i = 0; i < metadata.globals.length(); i++) {
|
||||
const GlobalDesc& global = metadata.globals[i];
|
||||
if (global.isConstant())
|
||||
continue;
|
||||
|
||||
uint8_t* globalAddr = globalData + global.offset();
|
||||
switch (global.kind()) {
|
||||
case GlobalKind::Import: {
|
||||
globalImports[global.importIndex()].writePayload(globalAddr);
|
||||
break;
|
||||
}
|
||||
case GlobalKind::Variable: {
|
||||
const InitExpr& init = global.initExpr();
|
||||
switch (init.kind()) {
|
||||
case InitExpr::Kind::Constant: {
|
||||
init.val().writePayload(globalAddr);
|
||||
break;
|
||||
}
|
||||
case InitExpr::Kind::GetGlobal: {
|
||||
const GlobalDesc& imported = metadata.globals[init.globalIndex()];
|
||||
globalImports[imported.importIndex()].writePayload(globalAddr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GlobalKind::Constant: {
|
||||
MOZ_CRASH("skipped at the top");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (memory)
|
||||
*addressOfMemoryBase() = memory->buffer().dataPointerEither().unwrap();
|
||||
|
||||
|
@ -89,7 +89,8 @@ class Instance
|
||||
const ShareableBytes* maybeBytecode,
|
||||
HandleWasmMemoryObject memory,
|
||||
SharedTableVector&& tables,
|
||||
Handle<FunctionVector> funcImports);
|
||||
Handle<FunctionVector> funcImports,
|
||||
const ValVector& globalImports);
|
||||
~Instance();
|
||||
bool init(JSContext* cx);
|
||||
void trace(JSTracer* trc);
|
||||
|
@ -1848,8 +1848,46 @@ EmitGetGlobal(FunctionCompiler& f)
|
||||
return false;
|
||||
|
||||
const GlobalDesc& global = f.mg().globals[id];
|
||||
f.iter().setResult(f.loadGlobalVar(global.globalDataOffset, global.isConst,
|
||||
ToMIRType(global.type)));
|
||||
if (!global.isConstant()) {
|
||||
f.iter().setResult(f.loadGlobalVar(global.offset(), !global.isMutable(),
|
||||
ToMIRType(global.type())));
|
||||
return true;
|
||||
}
|
||||
|
||||
Val value = global.constantValue();
|
||||
MIRType mirType = ToMIRType(value.type());
|
||||
|
||||
MDefinition* result;
|
||||
switch (value.type()) {
|
||||
case ValType::I32:
|
||||
result = f.constant(Int32Value(value.i32()), mirType);
|
||||
break;
|
||||
case ValType::I64:
|
||||
result = f.constant(value.i64());
|
||||
break;
|
||||
case ValType::F32:
|
||||
result = f.constant(Float32Value(value.f32()), mirType);
|
||||
break;
|
||||
case ValType::F64:
|
||||
result = f.constant(DoubleValue(value.f64()), mirType);
|
||||
break;
|
||||
case ValType::I8x16:
|
||||
result = f.constant(SimdConstant::CreateX16(value.i8x16()), mirType);
|
||||
break;
|
||||
case ValType::I16x8:
|
||||
result = f.constant(SimdConstant::CreateX8(value.i16x8()), mirType);
|
||||
break;
|
||||
case ValType::I32x4:
|
||||
result = f.constant(SimdConstant::CreateX4(value.i32x4()), mirType);
|
||||
break;
|
||||
case ValType::F32x4:
|
||||
result = f.constant(SimdConstant::CreateX4(value.f32x4()), mirType);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected type in EmitGetGlobal");
|
||||
}
|
||||
|
||||
f.iter().setResult(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1862,7 +1900,9 @@ EmitSetGlobal(FunctionCompiler& f)
|
||||
return false;
|
||||
|
||||
const GlobalDesc& global = f.mg().globals[id];
|
||||
f.storeGlobalVar(global.globalDataOffset, value);
|
||||
MOZ_ASSERT(global.isMutable());
|
||||
|
||||
f.storeGlobalVar(global.offset(), value);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -22,11 +22,14 @@
|
||||
#include "asmjs/WasmInstance.h"
|
||||
#include "asmjs/WasmModule.h"
|
||||
|
||||
#include "jit/JitOptions.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
using namespace js::wasm;
|
||||
|
||||
bool
|
||||
@ -89,15 +92,21 @@ GetProperty(JSContext* cx, HandleObject obj, const char* chars, MutableHandleVal
|
||||
|
||||
static bool
|
||||
GetImports(JSContext* cx,
|
||||
const Module& module,
|
||||
HandleObject importObj,
|
||||
const ImportVector& imports,
|
||||
MutableHandle<FunctionVector> funcImports,
|
||||
MutableHandleWasmTableObject tableImport,
|
||||
MutableHandleWasmMemoryObject memoryImport)
|
||||
MutableHandleWasmMemoryObject memoryImport,
|
||||
ValVector* globalImports)
|
||||
{
|
||||
const ImportVector& imports = module.imports();
|
||||
if (!imports.empty() && !importObj)
|
||||
return Throw(cx, "no import object given");
|
||||
|
||||
const Metadata& metadata = module.metadata();
|
||||
|
||||
uint32_t globalIndex = 0;
|
||||
const GlobalDescVector& globals = metadata.globals;
|
||||
for (const Import& import : imports) {
|
||||
RootedValue v(cx);
|
||||
if (!GetProperty(cx, importObj, import.module.get(), &v))
|
||||
@ -135,9 +144,53 @@ GetImports(JSContext* cx,
|
||||
MOZ_ASSERT(!memoryImport);
|
||||
memoryImport.set(&v.toObject().as<WasmMemoryObject>());
|
||||
break;
|
||||
|
||||
case DefinitionKind::Global:
|
||||
Val val;
|
||||
const GlobalDesc& global = globals[globalIndex++];
|
||||
MOZ_ASSERT(global.importIndex() == globalIndex - 1);
|
||||
MOZ_ASSERT(!global.isMutable());
|
||||
switch (global.type()) {
|
||||
case ValType::I32: {
|
||||
int32_t i32;
|
||||
if (!ToInt32(cx, v, &i32))
|
||||
return false;
|
||||
val = Val(uint32_t(i32));
|
||||
break;
|
||||
}
|
||||
case ValType::I64: {
|
||||
MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in JS");
|
||||
int64_t i64;
|
||||
if (!ReadI64Object(cx, v, &i64))
|
||||
return false;
|
||||
val = Val(uint64_t(i64));
|
||||
break;
|
||||
}
|
||||
case ValType::F32: {
|
||||
double d;
|
||||
if (!ToNumber(cx, v, &d))
|
||||
return false;
|
||||
val = Val(float(d));
|
||||
break;
|
||||
}
|
||||
case ValType::F64: {
|
||||
double d;
|
||||
if (!ToNumber(cx, v, &d))
|
||||
return false;
|
||||
val = Val(d);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
MOZ_CRASH("unexpected import value type");
|
||||
}
|
||||
}
|
||||
if (!globalImports->append(val))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(globalIndex == globals.length() || !globals[globalIndex].isImport());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -184,10 +237,11 @@ wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code, HandleObject importObj
|
||||
Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
|
||||
RootedWasmTableObject table(cx);
|
||||
RootedWasmMemoryObject memory(cx);
|
||||
if (!GetImports(cx, importObj, module->imports(), &funcs, &table, &memory))
|
||||
ValVector globals;
|
||||
if (!GetImports(cx, *module, importObj, &funcs, &table, &memory, &globals))
|
||||
return false;
|
||||
|
||||
return module->instantiate(cx, funcs, table, memory, nullptr, instanceObj);
|
||||
return module->instantiate(cx, funcs, table, memory, globals, nullptr, instanceObj);
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -511,12 +565,13 @@ WasmInstanceObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||
Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
|
||||
RootedWasmTableObject table(cx);
|
||||
RootedWasmMemoryObject memory(cx);
|
||||
if (!GetImports(cx, importObj, module.imports(), &funcs, &table, &memory))
|
||||
ValVector globals;
|
||||
if (!GetImports(cx, module, importObj, &funcs, &table, &memory, &globals))
|
||||
return false;
|
||||
|
||||
RootedObject instanceProto(cx, &cx->global()->getPrototype(JSProto_WasmInstance).toObject());
|
||||
RootedWasmInstanceObject instanceObj(cx);
|
||||
if (!module.instantiate(cx, funcs, table, memory, instanceProto, &instanceObj))
|
||||
if (!module.instantiate(cx, funcs, table, memory, globals, instanceProto, &instanceObj))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*instanceObj);
|
||||
|
@ -21,15 +21,62 @@
|
||||
#include "asmjs/WasmInstance.h"
|
||||
#include "asmjs/WasmJS.h"
|
||||
#include "asmjs/WasmSerialize.h"
|
||||
#include "jit/JitOptions.h"
|
||||
|
||||
#include "vm/ArrayBufferObject-inl.h"
|
||||
#include "vm/Debugger-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
using namespace js::wasm;
|
||||
|
||||
const char wasm::InstanceExportField[] = "exports";
|
||||
|
||||
JSObject*
|
||||
js::wasm::CreateI64Object(JSContext* cx, int64_t i64)
|
||||
{
|
||||
RootedObject result(cx, JS_NewPlainObject(cx));
|
||||
if (!result)
|
||||
return nullptr;
|
||||
|
||||
RootedValue val(cx, Int32Value(uint32_t(i64)));
|
||||
if (!JS_DefineProperty(cx, result, "low", val, JSPROP_ENUMERATE))
|
||||
return nullptr;
|
||||
|
||||
val = Int32Value(uint32_t(i64 >> 32));
|
||||
if (!JS_DefineProperty(cx, result, "high", val, JSPROP_ENUMERATE))
|
||||
return nullptr;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
js::wasm::ReadI64Object(JSContext* cx, HandleValue v, int64_t* i64)
|
||||
{
|
||||
if (!v.isObject()) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
|
||||
"i64 JS value must be an object");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject obj(cx, &v.toObject());
|
||||
|
||||
int32_t* i32 = (int32_t*)i64;
|
||||
|
||||
RootedValue val(cx);
|
||||
if (!JS_GetProperty(cx, obj, "low", &val))
|
||||
return false;
|
||||
if (!ToInt32(cx, val, &i32[0]))
|
||||
return false;
|
||||
|
||||
if (!JS_GetProperty(cx, obj, "high", &val))
|
||||
return false;
|
||||
if (!ToInt32(cx, val, &i32[1]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
// On MIPS, CodeLabels are instruction immediates so InternalLinks only
|
||||
// patch instruction immediates.
|
||||
@ -158,25 +205,32 @@ Import::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
|
||||
func.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
Export::Export(UniqueChars fieldName, uint32_t funcIndex)
|
||||
Export::Export(UniqueChars fieldName, uint32_t index, DefinitionKind kind)
|
||||
: fieldName_(Move(fieldName))
|
||||
{
|
||||
pod.kind_ = DefinitionKind::Function;
|
||||
pod.funcIndex_ = funcIndex;
|
||||
pod.kind_ = kind;
|
||||
pod.index_ = index;
|
||||
}
|
||||
|
||||
Export::Export(UniqueChars fieldName, DefinitionKind kind)
|
||||
: fieldName_(Move(fieldName))
|
||||
{
|
||||
pod.kind_ = kind;
|
||||
pod.funcIndex_ = 0;
|
||||
pod.index_ = 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Export::funcIndex() const
|
||||
{
|
||||
MOZ_ASSERT(pod.kind_ == DefinitionKind::Function);
|
||||
return pod.funcIndex_;
|
||||
return pod.index_;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Export::globalIndex() const
|
||||
{
|
||||
MOZ_ASSERT(pod.kind_ == DefinitionKind::Global);
|
||||
return pod.index_;
|
||||
}
|
||||
|
||||
size_t
|
||||
@ -468,11 +522,54 @@ Module::instantiateTable(JSContext* cx, HandleWasmTableObject tableImport,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ExportGlobalValue(JSContext* cx, const GlobalDescVector& globals, uint32_t globalIndex,
|
||||
const ValVector& globalImports, MutableHandleValue jsval)
|
||||
{
|
||||
const GlobalDesc& global = globals[globalIndex];
|
||||
|
||||
// Imports are located upfront in the globals array.
|
||||
Val val;
|
||||
switch (global.kind()) {
|
||||
case GlobalKind::Import: val = globalImports[globalIndex]; break;
|
||||
case GlobalKind::Variable: MOZ_CRASH("mutable variables can't be exported");
|
||||
case GlobalKind::Constant: val = global.constantValue(); break;
|
||||
}
|
||||
|
||||
switch (global.type()) {
|
||||
case ValType::I32: {
|
||||
jsval.set(Int32Value(val.i32()));
|
||||
return true;
|
||||
}
|
||||
case ValType::I64: {
|
||||
MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in asm.js/wasm");
|
||||
RootedObject obj(cx, CreateI64Object(cx, val.i64()));
|
||||
if (!obj)
|
||||
return false;
|
||||
jsval.set(ObjectValue(*obj));
|
||||
return true;
|
||||
}
|
||||
case ValType::F32: {
|
||||
jsval.set(DoubleValue(double(val.f32())));
|
||||
return true;
|
||||
}
|
||||
case ValType::F64: {
|
||||
jsval.set(DoubleValue(val.f64()));
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
MOZ_CRASH("unexpected type when creating global exports");
|
||||
}
|
||||
|
||||
static bool
|
||||
CreateExportObject(JSContext* cx,
|
||||
HandleWasmInstanceObject instanceObj,
|
||||
MutableHandleWasmTableObject tableObj,
|
||||
HandleWasmMemoryObject memoryObj,
|
||||
const ValVector& globalImports,
|
||||
const ExportVector& exports,
|
||||
MutableHandleObject exportObj)
|
||||
{
|
||||
@ -523,6 +620,11 @@ CreateExportObject(JSContext* cx,
|
||||
val = ObjectValue(memoryObj->buffer());
|
||||
break;
|
||||
}
|
||||
case DefinitionKind::Global: {
|
||||
if (!ExportGlobalValue(cx, metadata.globals, exp.globalIndex(), globalImports, &val))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE))
|
||||
@ -537,6 +639,7 @@ Module::instantiate(JSContext* cx,
|
||||
Handle<FunctionVector> funcImports,
|
||||
HandleWasmTableObject tableImport,
|
||||
HandleWasmMemoryObject memoryImport,
|
||||
const ValVector& globalImports,
|
||||
HandleObject instanceProto,
|
||||
MutableHandleWasmInstanceObject instanceObj) const
|
||||
{
|
||||
@ -580,7 +683,8 @@ Module::instantiate(JSContext* cx,
|
||||
maybeBytecode,
|
||||
memory,
|
||||
Move(tables),
|
||||
funcImports);
|
||||
funcImports,
|
||||
globalImports);
|
||||
if (!instance)
|
||||
return false;
|
||||
|
||||
@ -594,7 +698,7 @@ Module::instantiate(JSContext* cx,
|
||||
|
||||
RootedObject exportObj(cx);
|
||||
RootedWasmTableObject table(cx, tableImport);
|
||||
if (!CreateExportObject(cx, instanceObj, &table, memory, exports_, &exportObj))
|
||||
if (!CreateExportObject(cx, instanceObj, &table, memory, globalImports, exports_, &exportObj))
|
||||
return false;
|
||||
|
||||
JSAtom* atom = Atomize(cx, InstanceExportField, strlen(InstanceExportField));
|
||||
|
@ -26,6 +26,18 @@
|
||||
namespace js {
|
||||
namespace wasm {
|
||||
|
||||
// Creates a JS object containing two fields (low: low 32 bits; high: high 32
|
||||
// bits) of a given Int64 value. For testing purposes only.
|
||||
|
||||
JSObject*
|
||||
CreateI64Object(JSContext* cx, int64_t i64);
|
||||
|
||||
// Reads an int64 from a JS object with the same shape as described in the
|
||||
// comment above. For testing purposes only.
|
||||
|
||||
bool
|
||||
ReadI64Object(JSContext* cx, HandleValue v, int64_t* i64);
|
||||
|
||||
// LinkData contains all the metadata necessary to patch all the locations
|
||||
// that depend on the absolute address of a CodeSegment.
|
||||
//
|
||||
@ -113,18 +125,19 @@ class Export
|
||||
CacheableChars fieldName_;
|
||||
struct CacheablePod {
|
||||
DefinitionKind kind_;
|
||||
uint32_t funcIndex_;
|
||||
uint32_t index_;
|
||||
} pod;
|
||||
|
||||
public:
|
||||
Export() = default;
|
||||
explicit Export(UniqueChars fieldName, uint32_t funcIndex);
|
||||
explicit Export(UniqueChars fieldName, uint32_t index, DefinitionKind kind);
|
||||
explicit Export(UniqueChars fieldName, DefinitionKind kind);
|
||||
|
||||
const char* fieldName() const { return fieldName_.get(); }
|
||||
|
||||
DefinitionKind kind() const { return pod.kind_; }
|
||||
uint32_t funcIndex() const;
|
||||
uint32_t globalIndex() const;
|
||||
|
||||
WASM_DECLARE_SERIALIZABLE(Export)
|
||||
};
|
||||
@ -219,6 +232,7 @@ class Module : public RefCounted<Module>
|
||||
Handle<FunctionVector> funcImports,
|
||||
HandleWasmTableObject tableImport,
|
||||
HandleWasmMemoryObject memoryImport,
|
||||
const ValVector& globalImports,
|
||||
HandleObject instanceProto,
|
||||
MutableHandleWasmInstanceObject instanceObj) const;
|
||||
|
||||
|
@ -37,7 +37,7 @@ using namespace js::jit;
|
||||
using namespace js::wasm;
|
||||
|
||||
void
|
||||
Val::writePayload(uint8_t* dst)
|
||||
Val::writePayload(uint8_t* dst) const
|
||||
{
|
||||
switch (type_) {
|
||||
case ValType::I32:
|
||||
|
@ -361,9 +361,11 @@ class Val
|
||||
return u.f32x4_;
|
||||
}
|
||||
|
||||
void writePayload(uint8_t* dst);
|
||||
void writePayload(uint8_t* dst) const;
|
||||
};
|
||||
|
||||
typedef Vector<Val, 0, SystemAllocPolicy> ValVector;
|
||||
|
||||
// The Sig class represents a WebAssembly function signature which takes a list
|
||||
// of value types and returns an expression type. The engine uses two in-memory
|
||||
// representations of the argument Vector's memory (when elements do not fit
|
||||
@ -413,6 +415,142 @@ struct SigHashPolicy
|
||||
static bool match(const Sig* lhs, Lookup rhs) { return *lhs == rhs; }
|
||||
};
|
||||
|
||||
// An InitExpr describes a deferred initializer expression, used to initialize
|
||||
// a global or a table element offset. Such expressions are created during
|
||||
// decoding and actually executed on module instantiation.
|
||||
|
||||
class InitExpr
|
||||
{
|
||||
public:
|
||||
enum class Kind {
|
||||
Constant,
|
||||
GetGlobal
|
||||
};
|
||||
|
||||
private:
|
||||
Kind kind_;
|
||||
union {
|
||||
Val val_;
|
||||
struct {
|
||||
uint32_t index_;
|
||||
ValType type_;
|
||||
} global;
|
||||
} u;
|
||||
|
||||
public:
|
||||
InitExpr() = default;
|
||||
|
||||
explicit InitExpr(Val val) : kind_(Kind::Constant) {
|
||||
u.val_ = val;
|
||||
}
|
||||
|
||||
explicit InitExpr(uint32_t globalIndex, ValType type) : kind_(Kind::GetGlobal) {
|
||||
u.global.index_ = globalIndex;
|
||||
u.global.type_ = type;
|
||||
}
|
||||
|
||||
Kind kind() const { return kind_; }
|
||||
|
||||
bool isVal() const { return kind() == Kind::Constant; }
|
||||
Val val() const { MOZ_ASSERT(isVal()); return u.val_; }
|
||||
|
||||
uint32_t globalIndex() const { MOZ_ASSERT(kind() == Kind::GetGlobal); return u.global.index_; }
|
||||
|
||||
ValType type() const {
|
||||
switch (kind()) {
|
||||
case Kind::Constant: return u.val_.type();
|
||||
case Kind::GetGlobal: return u.global.type_;
|
||||
}
|
||||
MOZ_CRASH("unexpected initExpr type");
|
||||
}
|
||||
};
|
||||
|
||||
// A GlobalDesc describes a single global variable. Currently, asm.js and wasm
|
||||
// exposes mutable and immutable private globals, but can't import nor export
|
||||
// mutable globals.
|
||||
|
||||
enum class GlobalKind
|
||||
{
|
||||
Import,
|
||||
Constant,
|
||||
Variable
|
||||
};
|
||||
|
||||
class GlobalDesc
|
||||
{
|
||||
union {
|
||||
struct {
|
||||
union {
|
||||
InitExpr initial_;
|
||||
struct {
|
||||
ValType type_;
|
||||
uint32_t index_;
|
||||
} import;
|
||||
} val;
|
||||
unsigned offset_;
|
||||
bool isMutable_;
|
||||
} var;
|
||||
Val cst_;
|
||||
} u;
|
||||
GlobalKind kind_;
|
||||
|
||||
public:
|
||||
GlobalDesc() = default;
|
||||
|
||||
explicit GlobalDesc(InitExpr initial, bool isMutable)
|
||||
: kind_((isMutable || !initial.isVal()) ? GlobalKind::Variable : GlobalKind::Constant)
|
||||
{
|
||||
if (isVariable()) {
|
||||
u.var.val.initial_ = initial;
|
||||
u.var.isMutable_ = isMutable;
|
||||
u.var.offset_ = UINT32_MAX;
|
||||
} else {
|
||||
u.cst_ = initial.val();
|
||||
}
|
||||
}
|
||||
|
||||
explicit GlobalDesc(ValType type, bool isMutable, uint32_t importIndex)
|
||||
: kind_(GlobalKind::Import)
|
||||
{
|
||||
u.var.val.import.type_ = type;
|
||||
u.var.val.import.index_ = importIndex;
|
||||
u.var.isMutable_ = isMutable;
|
||||
u.var.offset_ = UINT32_MAX;
|
||||
}
|
||||
|
||||
void setOffset(unsigned offset) {
|
||||
MOZ_ASSERT(!isConstant());
|
||||
MOZ_ASSERT(u.var.offset_ == UINT32_MAX);
|
||||
u.var.offset_ = offset;
|
||||
}
|
||||
unsigned offset() const {
|
||||
MOZ_ASSERT(!isConstant());
|
||||
MOZ_ASSERT(u.var.offset_ != UINT32_MAX);
|
||||
return u.var.offset_;
|
||||
}
|
||||
|
||||
GlobalKind kind() const { return kind_; }
|
||||
bool isVariable() const { return kind_ == GlobalKind::Variable; }
|
||||
bool isConstant() const { return kind_ == GlobalKind::Constant; }
|
||||
bool isImport() const { return kind_ == GlobalKind::Import; }
|
||||
|
||||
bool isMutable() const { return !isConstant() && u.var.isMutable_; }
|
||||
Val constantValue() const { MOZ_ASSERT(isConstant()); return u.cst_; }
|
||||
const InitExpr& initExpr() const { MOZ_ASSERT(isVariable()); return u.var.val.initial_; }
|
||||
uint32_t importIndex() const { MOZ_ASSERT(isImport()); return u.var.val.import.index_; }
|
||||
|
||||
ValType type() const {
|
||||
switch (kind_) {
|
||||
case GlobalKind::Import: return u.var.val.import.type_;
|
||||
case GlobalKind::Variable: return u.var.val.initial_.type();
|
||||
case GlobalKind::Constant: return u.cst_.type();
|
||||
}
|
||||
MOZ_CRASH("unexpected global kind");
|
||||
}
|
||||
};
|
||||
|
||||
typedef Vector<GlobalDesc, 0, SystemAllocPolicy> GlobalDescVector;
|
||||
|
||||
// SigIdDesc describes a signature id that can be used by call_indirect and
|
||||
// table-entry prologues to structurally compare whether the caller and callee's
|
||||
// signatures *structurally* match. To handle the general case, a Sig is
|
||||
@ -468,21 +606,6 @@ struct SigWithId : Sig
|
||||
typedef Vector<SigWithId, 0, SystemAllocPolicy> SigWithIdVector;
|
||||
typedef Vector<const SigWithId*, 0, SystemAllocPolicy> SigWithIdPtrVector;
|
||||
|
||||
// A GlobalDesc describes a single global variable. Currently, globals are only
|
||||
// exposed through asm.js.
|
||||
|
||||
struct GlobalDesc
|
||||
{
|
||||
ValType type;
|
||||
unsigned globalDataOffset;
|
||||
bool isConst;
|
||||
GlobalDesc(ValType type, unsigned offset, bool isConst)
|
||||
: type(type), globalDataOffset(offset), isConst(isConst)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef Vector<GlobalDesc, 0, SystemAllocPolicy> GlobalDescVector;
|
||||
|
||||
// The (,Profiling,Func)Offsets classes are used to record the offsets of
|
||||
// different key points in a CodeRange during compilation.
|
||||
|
||||
@ -940,6 +1063,7 @@ static const unsigned InitialGlobalDataBytes = NaN32GlobalDataOffset + sizeo
|
||||
|
||||
static const unsigned MaxSigs = 4 * 1024;
|
||||
static const unsigned MaxFuncs = 512 * 1024;
|
||||
static const unsigned MaxGlobals = 4 * 1024;
|
||||
static const unsigned MaxLocals = 64 * 1024;
|
||||
static const unsigned MaxImports = 64 * 1024;
|
||||
static const unsigned MaxExports = 64 * 1024;
|
||||
|
Loading…
Reference in New Issue
Block a user