зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1286517: Implement text-to-binary for Globals; r=luke
MozReview-Commit-ID: ajyuriWJK5 --HG-- extra : rebase_source : e9c33a0e729ebc3dda24c18fe5fe3a3db019e30d
This commit is contained in:
Родитель
3bb9e21ec3
Коммит
bcbc873b13
|
@ -195,11 +195,13 @@ enum class AstExprKind
|
|||
ComparisonOperator,
|
||||
Const,
|
||||
ConversionOperator,
|
||||
GetGlobal,
|
||||
GetLocal,
|
||||
If,
|
||||
Load,
|
||||
Nop,
|
||||
Return,
|
||||
SetGlobal,
|
||||
SetLocal,
|
||||
Store,
|
||||
TernaryOperator,
|
||||
|
@ -290,6 +292,41 @@ class AstSetLocal : public AstExpr
|
|||
}
|
||||
};
|
||||
|
||||
class AstGetGlobal : public AstExpr
|
||||
{
|
||||
AstRef global_;
|
||||
|
||||
public:
|
||||
static const AstExprKind Kind = AstExprKind::GetGlobal;
|
||||
explicit AstGetGlobal(AstRef global)
|
||||
: AstExpr(Kind),
|
||||
global_(global)
|
||||
{}
|
||||
AstRef& global() {
|
||||
return global_;
|
||||
}
|
||||
};
|
||||
|
||||
class AstSetGlobal : public AstExpr
|
||||
{
|
||||
AstRef global_;
|
||||
AstExpr& value_;
|
||||
|
||||
public:
|
||||
static const AstExprKind Kind = AstExprKind::SetGlobal;
|
||||
AstSetGlobal(AstRef global, AstExpr& value)
|
||||
: AstExpr(Kind),
|
||||
global_(global),
|
||||
value_(value)
|
||||
{}
|
||||
AstRef& global() {
|
||||
return global_;
|
||||
}
|
||||
AstExpr& value() const {
|
||||
return value_;
|
||||
}
|
||||
};
|
||||
|
||||
class AstBlock : public AstExpr
|
||||
{
|
||||
Expr expr_;
|
||||
|
@ -527,14 +564,42 @@ class AstResizable
|
|||
const Maybe<uint32_t>& maximum() const { return maximum_; }
|
||||
};
|
||||
|
||||
class AstGlobal : public AstNode
|
||||
{
|
||||
AstName name_;
|
||||
uint32_t flags_;
|
||||
ValType type_;
|
||||
Maybe<AstExpr*> init_;
|
||||
|
||||
public:
|
||||
AstGlobal() : flags_(0), type_(ValType::Limit)
|
||||
{}
|
||||
|
||||
explicit AstGlobal(AstName name, ValType type, uint32_t flags,
|
||||
Maybe<AstExpr*> init = Maybe<AstExpr*>())
|
||||
: name_(name), flags_(flags), type_(type), init_(init)
|
||||
{}
|
||||
|
||||
AstName name() const { return name_; }
|
||||
uint32_t flags() const { return flags_; }
|
||||
ValType type() const { return type_; }
|
||||
|
||||
bool hasInit() const { return !!init_; }
|
||||
AstExpr& init() const { MOZ_ASSERT(hasInit()); return **init_; }
|
||||
};
|
||||
|
||||
typedef AstVector<AstGlobal*> AstGlobalVector;
|
||||
|
||||
class AstImport : public AstNode
|
||||
{
|
||||
AstName name_;
|
||||
AstName module_;
|
||||
AstName field_;
|
||||
DefinitionKind kind_;
|
||||
|
||||
AstRef funcSig_;
|
||||
AstResizable resizable_;
|
||||
AstGlobal global_;
|
||||
|
||||
public:
|
||||
AstImport(AstName name, AstName module, AstName field, AstRef funcSig)
|
||||
|
@ -543,30 +608,48 @@ class AstImport : public AstNode
|
|||
AstImport(AstName name, AstName module, AstName field, DefinitionKind kind, AstResizable resizable)
|
||||
: name_(name), module_(module), field_(field), kind_(kind), resizable_(resizable)
|
||||
{}
|
||||
AstImport(AstName name, AstName module, AstName field, AstGlobal global)
|
||||
: name_(name), module_(module), field_(field), kind_(DefinitionKind::Global), global_(global)
|
||||
{}
|
||||
|
||||
AstName name() const { return name_; }
|
||||
AstName module() const { return module_; }
|
||||
AstName field() const { return field_; }
|
||||
|
||||
DefinitionKind kind() const { return kind_; }
|
||||
AstRef& funcSig() { MOZ_ASSERT(kind_ == DefinitionKind::Function); return funcSig_; }
|
||||
AstResizable resizable() const { MOZ_ASSERT(kind_ != DefinitionKind::Function); return resizable_; }
|
||||
AstRef& funcSig() {
|
||||
MOZ_ASSERT(kind_ == DefinitionKind::Function);
|
||||
return funcSig_;
|
||||
}
|
||||
AstResizable resizable() const {
|
||||
MOZ_ASSERT(kind_ == DefinitionKind::Memory || kind_ == DefinitionKind::Table);
|
||||
return resizable_;
|
||||
}
|
||||
const AstGlobal& global() const {
|
||||
MOZ_ASSERT(kind_ == DefinitionKind::Global);
|
||||
return global_;
|
||||
}
|
||||
};
|
||||
|
||||
class AstExport : public AstNode
|
||||
{
|
||||
AstName name_;
|
||||
DefinitionKind kind_;
|
||||
AstRef func_;
|
||||
AstRef ref_;
|
||||
|
||||
public:
|
||||
AstExport(AstName name, AstRef func)
|
||||
: name_(name), kind_(DefinitionKind::Function), func_(func)
|
||||
AstExport(AstName name, DefinitionKind kind, AstRef ref)
|
||||
: name_(name), kind_(kind), ref_(ref)
|
||||
{}
|
||||
explicit AstExport(AstName name, DefinitionKind kind)
|
||||
: name_(name), kind_(kind)
|
||||
{}
|
||||
AstName name() const { return name_; }
|
||||
DefinitionKind kind() const { return kind_; }
|
||||
AstRef& func() { MOZ_ASSERT(kind_ == DefinitionKind::Function); return func_; }
|
||||
AstRef& ref() {
|
||||
MOZ_ASSERT(kind_ == DefinitionKind::Function || kind_ == DefinitionKind::Global);
|
||||
return ref_;
|
||||
}
|
||||
};
|
||||
|
||||
class AstDataSegment : public AstNode
|
||||
|
@ -636,6 +719,7 @@ class AstModule : public AstNode
|
|||
FuncVector funcs_;
|
||||
AstDataSegmentVector dataSegments_;
|
||||
AstElemSegmentVector elemSegments_;
|
||||
AstGlobalVector globals_;
|
||||
|
||||
public:
|
||||
explicit AstModule(LifoAlloc& lifo)
|
||||
|
@ -646,7 +730,8 @@ class AstModule : public AstNode
|
|||
exports_(lifo),
|
||||
funcs_(lifo),
|
||||
dataSegments_(lifo),
|
||||
elemSegments_(lifo)
|
||||
elemSegments_(lifo),
|
||||
globals_(lifo)
|
||||
{}
|
||||
bool init() {
|
||||
return sigMap_.init();
|
||||
|
@ -727,18 +812,24 @@ class AstModule : public AstNode
|
|||
const FuncVector& funcs() const {
|
||||
return funcs_;
|
||||
}
|
||||
const ImportVector& imports() const {
|
||||
return imports_;
|
||||
}
|
||||
bool append(AstImport* imp) {
|
||||
return imports_.append(imp);
|
||||
}
|
||||
const ImportVector& imports() const {
|
||||
return imports_;
|
||||
}
|
||||
bool append(AstExport* exp) {
|
||||
return exports_.append(exp);
|
||||
}
|
||||
const ExportVector& exports() const {
|
||||
return exports_;
|
||||
}
|
||||
bool append(AstGlobal* glob) {
|
||||
return globals_.append(glob);
|
||||
}
|
||||
const AstGlobalVector& globals() const {
|
||||
return globals_;
|
||||
}
|
||||
};
|
||||
|
||||
class AstUnaryOperator final : public AstExpr
|
||||
|
|
|
@ -1391,6 +1391,71 @@ AstDecodeMemorySection(AstDecodeContext& c)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
AstDecodeGlobal(AstDecodeContext& c, uint32_t i, AstGlobal* global)
|
||||
{
|
||||
AstName name;
|
||||
if (!AstDecodeGenerateName(c, AstName(u"global"), i, &name))
|
||||
return false;
|
||||
|
||||
ValType type;
|
||||
if (!c.d.readValType(&type))
|
||||
return AstDecodeFail(c, "bad global type");
|
||||
|
||||
uint32_t flags;
|
||||
if (!c.d.readVarU32(&flags))
|
||||
return AstDecodeFail(c, "expected flags");
|
||||
|
||||
if (flags & ~uint32_t(GlobalFlags::AllowedMask))
|
||||
return AstDecodeFail(c, "unexpected bits set in flags");
|
||||
|
||||
if (!AstDecodeExpr(c))
|
||||
return false;
|
||||
|
||||
AstDecodeStackItem item = c.iter().getResult();
|
||||
MOZ_ASSERT(item.popped == 0);
|
||||
if (!item.expr)
|
||||
return AstDecodeFail(c, "missing initializer expression");
|
||||
|
||||
AstExpr* init = item.expr;
|
||||
|
||||
if (!AstDecodeEnd(c))
|
||||
return AstDecodeFail(c, "missing initializer end");
|
||||
|
||||
item = c.iter().getResult();
|
||||
MOZ_ASSERT(item.terminationKind == AstDecodeTerminationKind::End);
|
||||
|
||||
*global = AstGlobal(name, type, flags, Some(init));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
AstDecodeGlobalSection(AstDecodeContext& c)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!c.d.startSection(GlobalSectionId, §ionStart, §ionSize))
|
||||
return AstDecodeFail(c, "failed to start section");
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
||||
uint32_t numGlobals;
|
||||
if (!c.d.readVarU32(&numGlobals))
|
||||
return AstDecodeFail(c, "expected number of globals");
|
||||
|
||||
for (uint32_t i = 0; i < numGlobals; i++) {
|
||||
auto* global = new(c.lifo) AstGlobal;
|
||||
if (!AstDecodeGlobal(c, i, global))
|
||||
return false;
|
||||
if (!c.module().append(global))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!c.d.finishSection(sectionStart, sectionSize))
|
||||
return AstDecodeFail(c, "globals section byte size mismatch");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
AstDecodeFunctionExport(AstDecodeContext& c, AstExport** export_)
|
||||
{
|
||||
|
@ -1405,7 +1470,8 @@ AstDecodeFunctionExport(AstDecodeContext& c, AstExport** export_)
|
|||
if (!AstDecodeName(c, &fieldName))
|
||||
return AstDecodeFail(c, "expected export name");
|
||||
|
||||
*export_ = new(c.lifo) AstExport(fieldName, AstRef(AstName(), funcIndex));
|
||||
*export_ = new(c.lifo) AstExport(fieldName, DefinitionKind::Function,
|
||||
AstRef(AstName(), funcIndex));
|
||||
if (!*export_)
|
||||
return false;
|
||||
|
||||
|
@ -1647,6 +1713,9 @@ wasm::BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length,
|
|||
if (!AstDecodeMemorySection(c))
|
||||
return false;
|
||||
|
||||
if (!AstDecodeGlobalSection(c))
|
||||
return false;
|
||||
|
||||
if (!AstDecodeExportSection(c))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -1565,9 +1565,9 @@ PrintExport(WasmPrintContext& c, AstExport& export_, const AstModule::FuncVector
|
|||
if (!c.buffer.append("memory"))
|
||||
return false;
|
||||
} else {
|
||||
const AstFunc* func = funcs[export_.func().index()];
|
||||
const AstFunc* func = funcs[export_.ref().index()];
|
||||
if (func->name().empty()) {
|
||||
if (!PrintInt32(c, export_.func().index()))
|
||||
if (!PrintInt32(c, export_.ref().index()))
|
||||
return false;
|
||||
} else {
|
||||
if (!PrintName(c, func->name()))
|
||||
|
|
|
@ -1174,9 +1174,9 @@ RenderExport(WasmRenderContext& c, AstExport& export_, const AstModule::FuncVect
|
|||
if (!c.buffer.append("memory"))
|
||||
return false;
|
||||
} else {
|
||||
const AstFunc* func = funcs[export_.func().index()];
|
||||
const AstFunc* func = funcs[export_.ref().index()];
|
||||
if (func->name().empty()) {
|
||||
if (!RenderInt32(c, export_.func().index()))
|
||||
if (!RenderInt32(c, export_.ref().index()))
|
||||
return false;
|
||||
} else {
|
||||
if (!RenderName(c, func->name()))
|
||||
|
|
|
@ -86,8 +86,11 @@ class WasmToken
|
|||
Export,
|
||||
Float,
|
||||
Func,
|
||||
GetGlobal,
|
||||
GetLocal,
|
||||
Global,
|
||||
If,
|
||||
Immutable,
|
||||
Import,
|
||||
Index,
|
||||
Memory,
|
||||
|
@ -105,6 +108,7 @@ class WasmToken
|
|||
Result,
|
||||
Return,
|
||||
Segment,
|
||||
SetGlobal,
|
||||
SetLocal,
|
||||
SignedInteger,
|
||||
Start,
|
||||
|
@ -978,8 +982,12 @@ WasmTokenStream::next()
|
|||
break;
|
||||
|
||||
case 'g':
|
||||
if (consume(u"get_global"))
|
||||
return WasmToken(WasmToken::GetGlobal, begin, cur_);
|
||||
if (consume(u"get_local"))
|
||||
return WasmToken(WasmToken::GetLocal, begin, cur_);
|
||||
if (consume(u"global"))
|
||||
return WasmToken(WasmToken::Global, begin, cur_);
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
|
@ -1266,6 +1274,8 @@ WasmTokenStream::next()
|
|||
}
|
||||
break;
|
||||
}
|
||||
if (consume(u"immutable"))
|
||||
return WasmToken(WasmToken::Immutable, begin, cur_);
|
||||
if (consume(u"import"))
|
||||
return WasmToken(WasmToken::Import, begin, cur_);
|
||||
if (consume(u"infinity"))
|
||||
|
@ -1317,6 +1327,8 @@ WasmTokenStream::next()
|
|||
case 's':
|
||||
if (consume(u"select"))
|
||||
return WasmToken(WasmToken::TernaryOpcode, Expr::Select, begin, cur_);
|
||||
if (consume(u"set_global"))
|
||||
return WasmToken(WasmToken::SetGlobal, begin, cur_);
|
||||
if (consume(u"set_local"))
|
||||
return WasmToken(WasmToken::SetLocal, begin, cur_);
|
||||
if (consume(u"segment"))
|
||||
|
@ -1841,6 +1853,29 @@ ParseGetLocal(WasmParseContext& c)
|
|||
return new(c.lifo) AstGetLocal(local);
|
||||
}
|
||||
|
||||
static AstGetGlobal*
|
||||
ParseGetGlobal(WasmParseContext& c)
|
||||
{
|
||||
AstRef local;
|
||||
if (!c.ts.matchRef(&local, c.error))
|
||||
return nullptr;
|
||||
return new(c.lifo) AstGetGlobal(local);
|
||||
}
|
||||
|
||||
static AstSetGlobal*
|
||||
ParseSetGlobal(WasmParseContext& c)
|
||||
{
|
||||
AstRef global;
|
||||
if (!c.ts.matchRef(&global, c.error))
|
||||
return nullptr;
|
||||
|
||||
AstExpr* value = ParseExpr(c);
|
||||
if (!value)
|
||||
return nullptr;
|
||||
|
||||
return new(c.lifo) AstSetGlobal(global, *value);
|
||||
}
|
||||
|
||||
static AstSetLocal*
|
||||
ParseSetLocal(WasmParseContext& c)
|
||||
{
|
||||
|
@ -2176,6 +2211,8 @@ ParseExprInsideParens(WasmParseContext& c)
|
|||
return ParseConversionOperator(c, token.expr());
|
||||
case WasmToken::If:
|
||||
return ParseIf(c);
|
||||
case WasmToken::GetGlobal:
|
||||
return ParseGetGlobal(c);
|
||||
case WasmToken::GetLocal:
|
||||
return ParseGetLocal(c);
|
||||
case WasmToken::Load:
|
||||
|
@ -2184,6 +2221,8 @@ ParseExprInsideParens(WasmParseContext& c)
|
|||
return ParseBlock(c, Expr::Loop);
|
||||
case WasmToken::Return:
|
||||
return ParseReturn(c);
|
||||
case WasmToken::SetGlobal:
|
||||
return ParseSetGlobal(c);
|
||||
case WasmToken::SetLocal:
|
||||
return ParseSetLocal(c);
|
||||
case WasmToken::Store:
|
||||
|
@ -2423,6 +2462,20 @@ ParseStartFunc(WasmParseContext& c, WasmToken token, AstModule* module)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ParseGlobalType(WasmParseContext& c, WasmToken* typeToken, uint32_t* flags)
|
||||
{
|
||||
if (!c.ts.match(WasmToken::ValueType, typeToken, c.error))
|
||||
return false;
|
||||
|
||||
// Mutable by default.
|
||||
*flags = 0x1;
|
||||
if (c.ts.getIf(WasmToken::Immutable))
|
||||
*flags = 0x0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static AstImport*
|
||||
ParseImport(WasmParseContext& c, bool newFormat, AstModule* module)
|
||||
{
|
||||
|
@ -2458,6 +2511,16 @@ ParseImport(WasmParseContext& c, bool newFormat, AstModule* module)
|
|||
return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
|
||||
DefinitionKind::Table, table);
|
||||
}
|
||||
if (c.ts.getIf(WasmToken::Global)) {
|
||||
WasmToken typeToken;
|
||||
uint32_t flags = 0;
|
||||
if (!ParseGlobalType(c, &typeToken, &flags))
|
||||
return nullptr;
|
||||
if (!c.ts.match(WasmToken::CloseParen, c.error))
|
||||
return nullptr;
|
||||
return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
|
||||
AstGlobal(AstName(), typeToken.valueType(), flags));
|
||||
}
|
||||
}
|
||||
|
||||
if (c.ts.getIf(WasmToken::Type)) {
|
||||
|
@ -2494,13 +2557,21 @@ ParseExport(WasmParseContext& c)
|
|||
WasmToken exportee = c.ts.get();
|
||||
switch (exportee.kind()) {
|
||||
case WasmToken::Index:
|
||||
return new(c.lifo) AstExport(name.text(), AstRef(AstName(), exportee.index()));
|
||||
return new(c.lifo) AstExport(name.text(), DefinitionKind::Function,
|
||||
AstRef(AstName(), exportee.index()));
|
||||
case WasmToken::Name:
|
||||
return new(c.lifo) AstExport(name.text(), AstRef(exportee.name(), AstNoIndex));
|
||||
return new(c.lifo) AstExport(name.text(), DefinitionKind::Function,
|
||||
AstRef(exportee.name(), AstNoIndex));
|
||||
case WasmToken::Table:
|
||||
return new(c.lifo) AstExport(name.text(), DefinitionKind::Table);
|
||||
case WasmToken::Memory:
|
||||
return new(c.lifo) AstExport(name.text(), DefinitionKind::Memory);
|
||||
case WasmToken::Global: {
|
||||
AstRef ref;
|
||||
if (!c.ts.matchRef(&ref, c.error))
|
||||
return nullptr;
|
||||
return new(c.lifo) AstExport(name.text(), DefinitionKind::Global, ref);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2563,6 +2634,23 @@ ParseElemSegment(WasmParseContext& c)
|
|||
return new(c.lifo) AstElemSegment(offset.index(), Move(elems));
|
||||
}
|
||||
|
||||
static AstGlobal*
|
||||
ParseGlobal(WasmParseContext& c)
|
||||
{
|
||||
AstName name = c.ts.getIfName();
|
||||
|
||||
WasmToken typeToken;
|
||||
uint32_t flags = 0;
|
||||
if (!ParseGlobalType(c, &typeToken, &flags))
|
||||
return nullptr;
|
||||
|
||||
AstExpr* init = ParseExpr(c);
|
||||
if (!init)
|
||||
return nullptr;
|
||||
|
||||
return new(c.lifo) AstGlobal(name, typeToken.valueType(), flags, Some(init));
|
||||
}
|
||||
|
||||
static AstModule*
|
||||
ParseModule(const char16_t* text, bool newFormat, LifoAlloc& lifo, UniqueChars* error)
|
||||
{
|
||||
|
@ -2597,6 +2685,12 @@ ParseModule(const char16_t* text, bool newFormat, LifoAlloc& lifo, UniqueChars*
|
|||
return nullptr;
|
||||
break;
|
||||
}
|
||||
case WasmToken::Global: {
|
||||
AstGlobal* global = ParseGlobal(c);
|
||||
if (!global || !module->append(global))
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
case WasmToken::Data: {
|
||||
AstDataSegment* segment = ParseDataSegment(c);
|
||||
if (!segment || !module->append(segment))
|
||||
|
@ -2658,6 +2752,7 @@ class Resolver
|
|||
{
|
||||
UniqueChars* error_;
|
||||
AstNameMap varMap_;
|
||||
AstNameMap globalMap_;
|
||||
AstNameMap sigMap_;
|
||||
AstNameMap funcMap_;
|
||||
AstNameMap importMap_;
|
||||
|
@ -2703,13 +2798,18 @@ class Resolver
|
|||
explicit Resolver(LifoAlloc& lifo, UniqueChars* error)
|
||||
: error_(error),
|
||||
varMap_(lifo),
|
||||
globalMap_(lifo),
|
||||
sigMap_(lifo),
|
||||
funcMap_(lifo),
|
||||
importMap_(lifo),
|
||||
targetStack_(lifo)
|
||||
{}
|
||||
bool init() {
|
||||
return sigMap_.init() && funcMap_.init() && importMap_.init() && varMap_.init();
|
||||
return sigMap_.init() &&
|
||||
funcMap_.init() &&
|
||||
importMap_.init() &&
|
||||
varMap_.init() &&
|
||||
globalMap_.init();
|
||||
}
|
||||
void beginFunc() {
|
||||
varMap_.clear();
|
||||
|
@ -2727,6 +2827,9 @@ class Resolver
|
|||
bool registerVarName(AstName name, size_t index) {
|
||||
return name.empty() || registerName(varMap_, name, index);
|
||||
}
|
||||
bool registerGlobalName(AstName name, size_t index) {
|
||||
return name.empty() || registerName(globalMap_, name, index);
|
||||
}
|
||||
bool pushTarget(AstName name) {
|
||||
return targetStack_.append(name);
|
||||
}
|
||||
|
@ -2755,6 +2858,11 @@ class Resolver
|
|||
return failResolveLabel("local", ref.name());
|
||||
return true;
|
||||
}
|
||||
bool resolveGlobal(AstRef& ref) {
|
||||
if (!ref.name().empty() && !resolveRef(globalMap_, ref))
|
||||
return failResolveLabel("global", ref.name());
|
||||
return true;
|
||||
}
|
||||
bool resolveBranchTarget(AstRef& ref) {
|
||||
if (ref.name().empty())
|
||||
return true;
|
||||
|
@ -2887,6 +2995,24 @@ ResolveSetLocal(Resolver& r, AstSetLocal& sl)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ResolveGetGlobal(Resolver& r, AstGetGlobal& gl)
|
||||
{
|
||||
return r.resolveGlobal(gl.global());
|
||||
}
|
||||
|
||||
static bool
|
||||
ResolveSetGlobal(Resolver& r, AstSetGlobal& sl)
|
||||
{
|
||||
if (!ResolveExpr(r, sl.value()))
|
||||
return false;
|
||||
|
||||
if (!r.resolveGlobal(sl.global()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ResolveUnaryOperator(Resolver& r, AstUnaryOperator& b)
|
||||
{
|
||||
|
@ -3006,6 +3132,8 @@ ResolveExpr(Resolver& r, AstExpr& expr)
|
|||
return true;
|
||||
case AstExprKind::ConversionOperator:
|
||||
return ResolveConversionOperator(r, expr.as<AstConversionOperator>());
|
||||
case AstExprKind::GetGlobal:
|
||||
return ResolveGetGlobal(r, expr.as<AstGetGlobal>());
|
||||
case AstExprKind::GetLocal:
|
||||
return ResolveGetLocal(r, expr.as<AstGetLocal>());
|
||||
case AstExprKind::If:
|
||||
|
@ -3014,6 +3142,8 @@ ResolveExpr(Resolver& r, AstExpr& expr)
|
|||
return ResolveLoad(r, expr.as<AstLoad>());
|
||||
case AstExprKind::Return:
|
||||
return ResolveReturn(r, expr.as<AstReturn>());
|
||||
case AstExprKind::SetGlobal:
|
||||
return ResolveSetGlobal(r, expr.as<AstSetGlobal>());
|
||||
case AstExprKind::SetLocal:
|
||||
return ResolveSetLocal(r, expr.as<AstSetLocal>());
|
||||
case AstExprKind::Store:
|
||||
|
@ -3033,8 +3163,7 @@ ResolveFunc(Resolver& r, AstFunc& func)
|
|||
{
|
||||
r.beginFunc();
|
||||
|
||||
size_t numVars = func.locals().length();
|
||||
for (size_t i = 0; i < numVars; i++) {
|
||||
for (size_t i = 0; i < func.locals().length(); i++) {
|
||||
if (!r.registerVarName(func.locals()[i], i))
|
||||
return r.fail("duplicate var");
|
||||
}
|
||||
|
@ -3078,29 +3207,50 @@ ResolveModule(LifoAlloc& lifo, AstModule* module, UniqueChars* error)
|
|||
}
|
||||
|
||||
size_t numImports = module->imports().length();
|
||||
size_t lastGlobalIndex = 0;
|
||||
for (size_t i = 0; i < numImports; i++) {
|
||||
AstImport* imp = module->imports()[i];
|
||||
if (!r.registerImportName(imp->name(), i))
|
||||
return r.fail("duplicate import");
|
||||
|
||||
switch (imp->kind()) {
|
||||
case DefinitionKind::Function:
|
||||
if (!r.registerImportName(imp->name(), i))
|
||||
return r.fail("duplicate import");
|
||||
if (!r.resolveSignature(imp->funcSig()))
|
||||
return false;
|
||||
break;
|
||||
case DefinitionKind::Global:
|
||||
if (!r.registerGlobalName(imp->name(), lastGlobalIndex++))
|
||||
return r.fail("duplicate import");
|
||||
break;
|
||||
case DefinitionKind::Memory:
|
||||
case DefinitionKind::Table:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (AstExport* export_ : module->exports()) {
|
||||
if (export_->kind() != DefinitionKind::Function)
|
||||
continue;
|
||||
if (!r.resolveFunction(export_->func()))
|
||||
const AstGlobalVector& globals = module->globals();
|
||||
for (const AstGlobal* global : globals) {
|
||||
if (!r.registerGlobalName(global->name(), lastGlobalIndex++))
|
||||
return r.fail("duplicate import");
|
||||
if (global->hasInit() && !ResolveExpr(r, global->init()))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AstExport* export_ : module->exports()) {
|
||||
switch (export_->kind()) {
|
||||
case DefinitionKind::Function:
|
||||
if (!r.resolveFunction(export_->ref()))
|
||||
return false;
|
||||
break;
|
||||
case DefinitionKind::Global:
|
||||
if (!r.resolveGlobal(export_->ref()))
|
||||
return false;
|
||||
break;
|
||||
case DefinitionKind::Table:
|
||||
case DefinitionKind::Memory:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (AstFunc* func : module->funcs()) {
|
||||
if (!ResolveFunc(r, *func))
|
||||
return false;
|
||||
|
@ -3261,6 +3411,21 @@ EncodeSetLocal(Encoder& e, AstSetLocal& sl)
|
|||
e.writeVarU32(sl.local().index());
|
||||
}
|
||||
|
||||
static bool
|
||||
EncodeGetGlobal(Encoder& e, AstGetGlobal& gg)
|
||||
{
|
||||
return e.writeExpr(Expr::GetGlobal) &&
|
||||
e.writeVarU32(gg.global().index());
|
||||
}
|
||||
|
||||
static bool
|
||||
EncodeSetGlobal(Encoder& e, AstSetGlobal& sg)
|
||||
{
|
||||
return EncodeExpr(e, sg.value()) &&
|
||||
e.writeExpr(Expr::SetGlobal) &&
|
||||
e.writeVarU32(sg.global().index());
|
||||
}
|
||||
|
||||
static bool
|
||||
EncodeUnaryOperator(Encoder& e, AstUnaryOperator& b)
|
||||
{
|
||||
|
@ -3427,6 +3592,8 @@ EncodeExpr(Encoder& e, AstExpr& expr)
|
|||
return EncodeConversionOperator(e, expr.as<AstConversionOperator>());
|
||||
case AstExprKind::GetLocal:
|
||||
return EncodeGetLocal(e, expr.as<AstGetLocal>());
|
||||
case AstExprKind::GetGlobal:
|
||||
return EncodeGetGlobal(e, expr.as<AstGetGlobal>());
|
||||
case AstExprKind::If:
|
||||
return EncodeIf(e, expr.as<AstIf>());
|
||||
case AstExprKind::Load:
|
||||
|
@ -3435,6 +3602,8 @@ EncodeExpr(Encoder& e, AstExpr& expr)
|
|||
return EncodeReturn(e, expr.as<AstReturn>());
|
||||
case AstExprKind::SetLocal:
|
||||
return EncodeSetLocal(e, expr.as<AstSetLocal>());
|
||||
case AstExprKind::SetGlobal:
|
||||
return EncodeSetGlobal(e, expr.as<AstSetGlobal>());
|
||||
case AstExprKind::Store:
|
||||
return EncodeStore(e, expr.as<AstStore>());
|
||||
case AstExprKind::BranchTable:
|
||||
|
@ -3569,6 +3738,13 @@ EncodeImport(Encoder& e, bool newFormat, AstImport& imp)
|
|||
if (!e.writeVarU32(imp.funcSig().index()))
|
||||
return false;
|
||||
break;
|
||||
case DefinitionKind::Global:
|
||||
MOZ_ASSERT(!imp.global().hasInit());
|
||||
if (!e.writeValType(imp.global().type()))
|
||||
return false;
|
||||
if (!e.writeVarU32(imp.global().flags()))
|
||||
return false;
|
||||
break;
|
||||
case DefinitionKind::Table:
|
||||
case DefinitionKind::Memory:
|
||||
if (!EncodeResizable(e, imp.resizable()))
|
||||
|
@ -3640,6 +3816,34 @@ EncodeMemorySection(Encoder& e, bool newFormat, AstModule& module)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EncodeGlobalSection(Encoder& e, AstModule& module)
|
||||
{
|
||||
size_t offset;
|
||||
if (!e.startSection(GlobalSectionId, &offset))
|
||||
return false;
|
||||
|
||||
const AstGlobalVector& globals = module.globals();
|
||||
|
||||
if (!e.writeVarU32(globals.length()))
|
||||
return false;
|
||||
|
||||
for (const AstGlobal* global : globals) {
|
||||
MOZ_ASSERT(global->hasInit());
|
||||
if (!e.writeValType(global->type()))
|
||||
return false;
|
||||
if (!e.writeVarU32(global->flags()))
|
||||
return false;
|
||||
if (!EncodeExpr(e, global->init()))
|
||||
return false;
|
||||
if (!e.writeExpr(Expr::End))
|
||||
return false;
|
||||
}
|
||||
|
||||
e.finishSection(offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EncodeExport(Encoder& e, bool newFormat, AstExport& exp)
|
||||
{
|
||||
|
@ -3647,7 +3851,7 @@ EncodeExport(Encoder& e, bool newFormat, AstExport& exp)
|
|||
if (exp.kind() != DefinitionKind::Function)
|
||||
return true;
|
||||
|
||||
if (!e.writeVarU32(exp.func().index()))
|
||||
if (!e.writeVarU32(exp.ref().index()))
|
||||
return false;
|
||||
|
||||
if (!EncodeBytes(e, exp.name()))
|
||||
|
@ -3664,7 +3868,8 @@ EncodeExport(Encoder& e, bool newFormat, AstExport& exp)
|
|||
|
||||
switch (exp.kind()) {
|
||||
case DefinitionKind::Function:
|
||||
if (!e.writeVarU32(exp.func().index()))
|
||||
case DefinitionKind::Global:
|
||||
if (!e.writeVarU32(exp.ref().index()))
|
||||
return false;
|
||||
break;
|
||||
case DefinitionKind::Table:
|
||||
|
@ -3932,6 +4137,9 @@ EncodeModule(AstModule& module, bool newFormat, Bytes* bytes)
|
|||
if (!EncodeMemorySection(e, newFormat, module))
|
||||
return false;
|
||||
|
||||
if (!EncodeGlobalSection(e, module))
|
||||
return false;
|
||||
|
||||
if (!EncodeExportSection(e, newFormat, module))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -36,8 +36,9 @@ function jsify(wasmVal) {
|
|||
// - if the expected value is in the int32 range, it can be just a number.
|
||||
// - otherwise, an object with the properties "high" and "low".
|
||||
function assertEqI64(observed, expect) {
|
||||
assertEq(typeof observed, 'object');
|
||||
assertEq(typeof expect === 'object' || typeof expect === 'number', true);
|
||||
assertEq(typeof observed, 'object', "observed must be an i64 object");
|
||||
assertEq(typeof expect === 'object' || typeof expect === 'number', true,
|
||||
"expect must be an i64 object or number");
|
||||
|
||||
let {low, high} = observed;
|
||||
if (typeof expect === 'number') {
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
// |jit-test| test-also-wasm-baseline
|
||||
load(libdir + "wasm.js");
|
||||
|
||||
const { Instance, Module } = WebAssembly;
|
||||
const evalText = (txt, imports = {}) => new Instance(new Module(wasmTextToBinary(txt, 'new-format')), imports).exports;
|
||||
|
||||
// Locally-defined globals
|
||||
assertErrorMessage(() => evalText(`(module (global))`), SyntaxError, /parsing/);
|
||||
assertErrorMessage(() => evalText(`(module (global i32))`), SyntaxError, /parsing/);
|
||||
assertErrorMessage(() => evalText(`(module (global i32 immutable))`), SyntaxError, /parsing/);
|
||||
|
||||
// Initializer expressions.
|
||||
assertErrorMessage(() => evalText(`(module (global i32 (f32.const 13.37)))`), TypeError, /type mismatch/);
|
||||
assertErrorMessage(() => evalText(`(module (global f64 (f32.const 13.37)))`), TypeError, /type mismatch/);
|
||||
assertErrorMessage(() => evalText(`(module (global i32 (i32.add (i32.const 13) (i32.const 37))))`), TypeError, /failed to read end/);
|
||||
|
||||
assertErrorMessage(() => evalText(`(module (global i32 (get_global 0)))`), TypeError, /out of range/);
|
||||
assertErrorMessage(() => evalText(`(module (global i32 (get_global 1)) (global i32 immutable (i32.const 1)))`), TypeError, /out of range/);
|
||||
|
||||
// Test a well-defined global section.
|
||||
function testInner(type, initialValue, nextValue, coercion, assertFunc = assertEq)
|
||||
{
|
||||
var module = evalText(`(module
|
||||
(global ${type} (${type}.const ${initialValue}))
|
||||
(global ${type} immutable (${type}.const ${initialValue}))
|
||||
|
||||
(func $get (result ${type}) (get_global 0))
|
||||
(func $set (param ${type}) (set_global 0 (get_local 0)))
|
||||
|
||||
(func $get_cst (result ${type}) (get_global 1))
|
||||
|
||||
(export "get" $get)
|
||||
(export "get_cst" $get_cst)
|
||||
|
||||
(export "set" $set)
|
||||
)`);
|
||||
|
||||
assertFunc(module.get(), coercion(initialValue));
|
||||
assertEq(module.set(coercion(nextValue)), undefined);
|
||||
assertFunc(module.get(), coercion(nextValue));
|
||||
|
||||
assertFunc(module.get_cst(), coercion(initialValue));
|
||||
}
|
||||
|
||||
testInner('i32', 13, 37, x => x|0);
|
||||
testInner('f32', 13.37, 0.1989, Math.fround);
|
||||
testInner('f64', 13.37, 0.1989, x => +x);
|
||||
|
||||
// Semantic errors.
|
||||
assertErrorMessage(() => evalText(`(module (global i32 (i32.const 1337)) (func (set_global 1 (i32.const 0))))`), TypeError, /out of range/);
|
||||
assertErrorMessage(() => evalText(`(module (global i32 immutable (i32.const 1337)) (func (set_global 0 (i32.const 0))))`), TypeError, /can't write an immutable global/);
|
||||
|
||||
// Big module with many variables: test that setting one doesn't overwrite the
|
||||
// other ones.
|
||||
function get_set(i, type) { return `
|
||||
(func $get_${i} (result ${type}) (get_global ${i}))
|
||||
(func $set_${i} (param ${type}) (set_global ${i} (get_local 0)))
|
||||
`
|
||||
}
|
||||
|
||||
var module = evalText(`(module
|
||||
(global i32 (i32.const 42))
|
||||
(global i32 (i32.const 10))
|
||||
(global f32 (f32.const 13.37))
|
||||
(global f64 (f64.const 13.37))
|
||||
(global i32 (i32.const -18))
|
||||
|
||||
${get_set(0, 'i32')}
|
||||
${get_set(1, 'i32')}
|
||||
${get_set(2, 'f32')}
|
||||
${get_set(3, 'f64')}
|
||||
${get_set(4, 'i32')}
|
||||
|
||||
(export "get0" $get_0) (export "set0" $set_0)
|
||||
(export "get1" $get_1) (export "set1" $set_1)
|
||||
(export "get2" $get_2) (export "set2" $set_2)
|
||||
(export "get3" $get_3) (export "set3" $set_3)
|
||||
(export "get4" $get_4) (export "set4" $set_4)
|
||||
)`);
|
||||
|
||||
let values = [42, 10, Math.fround(13.37), 13.37, -18];
|
||||
let nextValues = [13, 37, Math.fround(-17.89), 9.3, -13];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
assertEq(module[`get${i}`](), values[i]);
|
||||
assertEq(module[`set${i}`](nextValues[i]), undefined);
|
||||
assertEq(module[`get${i}`](), nextValues[i]);
|
||||
for (let j = 0; j < 5; j++) {
|
||||
if (i === j)
|
||||
continue;
|
||||
assertEq(module[`get${j}`](), values[j]);
|
||||
}
|
||||
assertEq(module[`set${i}`](values[i]), undefined);
|
||||
assertEq(module[`get${i}`](), values[i]);
|
||||
}
|
||||
|
||||
// Import/export rules.
|
||||
assertErrorMessage(() => evalText(`(module (import "globals" "x" (global i32)))`), TypeError, /can't import.* mutable globals in the MVP/);
|
||||
assertErrorMessage(() => evalText(`(module (global i32 (i32.const 42)) (export "" global 0))`), TypeError, /can't .*export mutable globals in the MVP/);
|
||||
|
||||
// Import/export semantics.
|
||||
module = evalText(`(module
|
||||
(import $g "globals" "x" (global i32 immutable))
|
||||
(func $get (result i32) (get_global $g))
|
||||
(export "getter" $get)
|
||||
(export "value" global 0)
|
||||
)`, { globals: {x: 42} });
|
||||
|
||||
assertEq(module.getter(), 42);
|
||||
assertEq(module.value, 42);
|
||||
|
||||
// Imported globals and locally defined globals use the same index space.
|
||||
module = evalText(`(module
|
||||
(import "globals" "x" (global i32 immutable))
|
||||
(global i32 immutable (i32.const 1337))
|
||||
(export "imported" global 0)
|
||||
(export "defined" global 1)
|
||||
)`, { globals: {x: 42} });
|
||||
|
||||
assertEq(module.imported, 42);
|
||||
assertEq(module.defined, 1337);
|
||||
|
||||
// Initializer expressions can reference an imported immutable global.
|
||||
assertErrorMessage(() => evalText(`(module (global f32 immutable (f32.const 13.37)) (global i32 (get_global 0)))`), TypeError, /must reference a global immutable import/);
|
||||
assertErrorMessage(() => evalText(`(module (global f32 (f32.const 13.37)) (global i32 (get_global 0)))`), TypeError, /must reference a global immutable import/);
|
||||
assertErrorMessage(() => evalText(`(module (global i32 (i32.const 0)) (global i32 (get_global 0)))`), TypeError, /must reference a global immutable import/);
|
||||
|
||||
assertErrorMessage(() => evalText(`(module (import "globals" "a" (global f32 immutable)) (global i32 (get_global 0)))`), TypeError, /type mismatch/);
|
||||
|
||||
function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = assertEq) {
|
||||
var module = evalText(`(module
|
||||
(import "globals" "a" (global ${type} immutable))
|
||||
|
||||
(global ${type} (get_global 0))
|
||||
(global ${type} immutable (get_global 0))
|
||||
|
||||
(func $get0 (result ${type}) (get_global 0))
|
||||
|
||||
(func $get1 (result ${type}) (get_global 1))
|
||||
(func $set1 (param ${type}) (set_global 1 (get_local 0)))
|
||||
|
||||
(func $get_cst (result ${type}) (get_global 2))
|
||||
|
||||
(export "get0" $get0)
|
||||
(export "get1" $get1)
|
||||
(export "get_cst" $get_cst)
|
||||
|
||||
(export "set1" $set1)
|
||||
)`, {
|
||||
globals: {
|
||||
a: coercion(initialValue)
|
||||
}
|
||||
});
|
||||
|
||||
assertFunc(module.get0(), coercion(initialValue));
|
||||
assertFunc(module.get1(), coercion(initialValue));
|
||||
|
||||
assertEq(module.set1(coercion(nextValue)), undefined);
|
||||
assertFunc(module.get1(), coercion(nextValue));
|
||||
assertFunc(module.get0(), coercion(initialValue));
|
||||
|
||||
assertFunc(module.get_cst(), coercion(initialValue));
|
||||
}
|
||||
|
||||
testInitExpr('i32', 13, 37, x => x|0);
|
||||
testInitExpr('f32', 13.37, 0.1989, Math.fround);
|
||||
testInitExpr('f64', 13.37, 0.1989, x => +x);
|
||||
|
||||
// Int64.
|
||||
if (hasI64()) {
|
||||
assertErrorMessage(() => evalText(`(module (import "globals" "x" (global i64 immutable)))`), TypeError, /can't import.* an Int64 global/);
|
||||
assertErrorMessage(() => evalText(`(module (global i64 immutable (i64.const 42)) (export "" global 0))`), TypeError, /can't .*export an Int64 global/);
|
||||
|
||||
setJitCompilerOption('wasm.test-mode', 1);
|
||||
testInner('i64', '0x531642753864975F', '0x123456789abcdef0', createI64, assertEqI64);
|
||||
testInitExpr('i64', '0x531642753864975F', '0x123456789abcdef0', createI64, assertEqI64);
|
||||
|
||||
module = evalText(`(module
|
||||
(import "globals" "x" (global i64 immutable))
|
||||
(global i64 immutable (i64.const 0xFAFADADABABA))
|
||||
(export "imported" global 0)
|
||||
(export "defined" global 1)
|
||||
)`, { globals: {x: createI64('0x1234567887654321')} });
|
||||
|
||||
assertEqI64(module.imported, createI64('0x1234567887654321'));
|
||||
assertEqI64(module.defined, createI64('0xFAFADADABABA'));
|
||||
|
||||
setJitCompilerOption('wasm.test-mode', 0);
|
||||
} else {
|
||||
assertErrorMessage(() => evalText(`(module (global i64 (i64.const 0)))`), TypeError, /NYI/);
|
||||
assertErrorMessage(() => evalText(`(module (import "globals" "x" (global i64 immutable)))`), TypeError, /NYI/);
|
||||
}
|
||||
|
|
@ -267,6 +267,7 @@ assertEq(tbl, e.bar);
|
|||
// Non-existent export errors
|
||||
|
||||
assertErrorMessage(() => new Module(textToBinary('(module (export "a" 0))')), TypeError, /exported function index out of bounds/);
|
||||
assertErrorMessage(() => new Module(textToBinary('(module (export "a" global 0))')), TypeError, /exported global index out of bounds/);
|
||||
assertErrorMessage(() => new Module(textToBinary('(module (export "a" memory))')), TypeError, /exported memory index out of bounds/);
|
||||
assertErrorMessage(() => new Module(textToBinary('(module (export "a" table))')), TypeError, /exported table index out of bounds/);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче