Bug 1242949: Implement Block in WebAssembly; r=luke

This commit is contained in:
Benjamin Bouvier 2016-01-27 10:51:03 +01:00
parent 7e50fbe47b
commit afd85313d2
9 changed files with 133 additions and 36 deletions

View File

@ -2768,14 +2768,6 @@ class MOZ_STACK_CLASS FunctionValidator
return writeOp(SimdToExpr(simdType, op));
}
MOZ_WARN_UNUSED_RESULT
bool writeDebugCheckPoint() {
#ifdef DEBUG
return writeOp(Expr::DebugCheckPoint);
#endif
return true;
}
MOZ_WARN_UNUSED_RESULT
bool writeU8(uint8_t u8) {
return encoder().writeU8(u8);
@ -5704,7 +5696,7 @@ CheckComma(FunctionValidator& f, ParseNode* comma, Type* type)
MOZ_ASSERT(comma->isKind(PNK_COMMA));
ParseNode* operands = ListHead(comma);
if (!f.writeOp(Expr::Block) || !f.writeU32(ListLength(comma)))
if (!f.writeOp(Expr::Block) || !f.writeVarU32(ListLength(comma)))
return false;
ParseNode* pn = operands;
@ -5713,8 +5705,7 @@ CheckComma(FunctionValidator& f, ParseNode* comma, Type* type)
return false;
}
return CheckExpr(f, pn, type) &&
f.writeDebugCheckPoint();
return CheckExpr(f, pn, type);
}
static bool
@ -6245,7 +6236,7 @@ CheckFor(FunctionValidator& f, ParseNode* forStmt)
if (maybeInc && !CheckAsExprStatement(f, maybeInc))
return false;
return f.writeDebugCheckPoint();
return true;
}
static bool
@ -6561,7 +6552,7 @@ CheckStatementList(FunctionValidator& f, ParseNode* stmtList)
{
MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
if (!f.writeOp(Expr::Block) || !f.writeU32(ListLength(stmtList)))
if (!f.writeOp(Expr::Block) || !f.writeVarU32(ListLength(stmtList)))
return false;
for (ParseNode* stmt = ListHead(stmtList); stmt; stmt = NextNode(stmt)) {
@ -6569,7 +6560,7 @@ CheckStatementList(FunctionValidator& f, ParseNode* stmtList)
return false;
}
return f.writeDebugCheckPoint();
return true;
}
static bool

View File

@ -201,6 +201,28 @@ DecodeSetLocal(FunctionDecoder& f, ExprType expected)
return CheckType(f, localType, expected);
}
static bool
DecodeBlock(FunctionDecoder& f, ExprType expected)
{
uint32_t numExprs;
if (!f.d().readVarU32(&numExprs))
return f.fail("unable to read block's number of expressions");
if (numExprs) {
for (uint32_t i = 0; i < numExprs - 1; i++) {
if (!DecodeExpr(f, ExprType::Void))
return false;
}
if (!DecodeExpr(f, expected))
return false;
} else {
if (!CheckType(f, ExprType::Void, expected))
return false;
}
return true;
}
static bool
DecodeExpr(FunctionDecoder& f, ExprType expected)
{
@ -217,6 +239,8 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
return DecodeGetLocal(f, expected);
case Expr::SetLocal:
return DecodeSetLocal(f, expected);
case Expr::Block:
return DecodeBlock(f, expected);
default:
break;
}

View File

@ -206,8 +206,6 @@ enum class Expr : uint16_t
InterruptCheckHead,
InterruptCheckLoop,
DebugCheckPoint,
I32Min,
I32Max,

View File

@ -267,7 +267,7 @@ ModuleGenerator::allocateGlobalVar(ValType type, bool isConst, uint32_t* index)
void
ModuleGenerator::initSig(uint32_t sigIndex, Sig&& sig)
{
MOZ_ASSERT(module_->kind == ModuleKind::AsmJS);
MOZ_ASSERT(isAsmJS());
MOZ_ASSERT(sigIndex == numSigs_);
numSigs_++;
@ -285,7 +285,7 @@ ModuleGenerator::sig(uint32_t index) const
bool
ModuleGenerator::initFuncSig(uint32_t funcIndex, uint32_t sigIndex)
{
MOZ_ASSERT(module_->kind == ModuleKind::AsmJS);
MOZ_ASSERT(isAsmJS());
MOZ_ASSERT(funcIndex == module_->numFuncs);
MOZ_ASSERT(!shared_->funcSigs[funcIndex]);
@ -304,7 +304,7 @@ ModuleGenerator::funcSig(uint32_t funcIndex) const
bool
ModuleGenerator::initImport(uint32_t importIndex, uint32_t sigIndex, uint32_t globalDataOffset)
{
MOZ_ASSERT(module_->kind == ModuleKind::AsmJS);
MOZ_ASSERT(isAsmJS());
MOZ_ASSERT(importIndex == module_->imports.length());
if (!addImport(sig(sigIndex), globalDataOffset))
return false;

View File

@ -166,6 +166,7 @@ class MOZ_STACK_CLASS ModuleGenerator
bool init(UniqueModuleGeneratorData shared, ModuleKind = ModuleKind::Wasm);
bool isAsmJS() const { return module_->kind == ModuleKind::AsmJS; }
CompileArgs args() const { return module_->compileArgs; }
jit::MacroAssembler& masm() { return masm_; }
const Uint32Vector& funcEntryOffsets() const { return funcEntryOffsets_; }

View File

@ -1195,10 +1195,6 @@ class FunctionCompiler
*column = sc.column;
}
void assertDebugCheckPoint() {
MOZ_ASSERT(readOpcode() == Expr::DebugCheckPoint);
}
bool done() const { return decoder_.done(); }
/*************************************************************************/
@ -2470,8 +2466,6 @@ EmitFor(FunctionCompiler& f, Expr expr, const LabelVector* maybeLabels)
return false;
}
f.assertDebugCheckPoint();
return f.closeLoop(loopEntry, afterLoop);
}
@ -2635,15 +2629,16 @@ EmitRet(FunctionCompiler& f)
static bool
EmitBlock(FunctionCompiler& f, ExprType type, MDefinition** def)
{
size_t numStmt = f.readU32();
for (size_t i = 1; i < numStmt; i++) {
// Fine to clobber def, we only want the last use.
if (!EmitExprStmt(f, def))
uint32_t numStmts = f.readVarU32();
if (numStmts) {
for (uint32_t i = 0; i < numStmts - 1; i++) {
// Fine to clobber def, we only want the last use.
if (!EmitExprStmt(f, def))
return false;
}
if (!EmitExpr(f, type, def))
return false;
}
if (numStmt && !EmitExpr(f, type, def))
return false;
f.assertDebugCheckPoint();
return true;
}
@ -2967,7 +2962,6 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may
case Expr::I64StoreMem32:
case Expr::I64StoreMem:
MOZ_CRASH("NYI");
case Expr::DebugCheckPoint:
case Expr::Unreachable:
break;
case Expr::Limit:
@ -3012,7 +3006,7 @@ wasm::IonCompileFunction(IonCompileTask* task)
return false;
}
if (IsVoid(f.sig().ret()))
if (IsVoid(f.sig().ret()) || !last)
f.returnVoid();
else
f.returnExpr(last);

View File

@ -77,7 +77,8 @@ class IonCompileTask
IonCompileTask& operator=(const IonCompileTask&) = delete;
public:
IonCompileTask(JSRuntime* rt, CompileArgs args, ModuleGeneratorThreadView& mg, size_t defaultChunkSize)
IonCompileTask(JSRuntime* rt, CompileArgs args, ModuleGeneratorThreadView& mg,
size_t defaultChunkSize)
: runtime_(rt),
args_(args),
mg_(mg),

View File

@ -41,6 +41,8 @@ static const unsigned AST_LIFO_DEFAULT_CHUNK_SIZE = 4096;
/*****************************************************************************/
// wasm AST
class WasmAstExpr;
template <class T>
using WasmAstVector = mozilla::Vector<T, 0, LifoAllocPolicy<Fallible>>;
@ -48,6 +50,7 @@ template <class K, class V, class HP>
using WasmAstHashMap = HashMap<K, V, HP, LifoAllocPolicy<Fallible>>;
typedef WasmAstVector<ValType> WasmAstValTypeVector;
typedef WasmAstVector<WasmAstExpr*> WasmAstExprVector;
struct WasmAstBase
{
@ -88,6 +91,7 @@ class WasmAstSig : public WasmAstBase
enum class WasmAstKind
{
Block,
Const,
Export,
Func,
@ -178,6 +182,20 @@ class WasmAstSetLocal : public WasmAstExpr
}
};
class WasmAstBlock : public WasmAstExpr
{
WasmAstExprVector exprs_;
public:
static const WasmAstKind Kind = WasmAstKind::Block;
WasmAstBlock(WasmAstExprVector&& exprs)
: WasmAstExpr(Kind),
exprs_(Move(exprs))
{}
const WasmAstExprVector& exprs() const { return exprs_; }
};
class WasmAstFunc : public WasmAstNode
{
const uint32_t sigIndex_;
@ -285,6 +303,7 @@ class WasmToken
public:
enum Kind
{
Block,
CloseParen,
Const,
EndOfFile,
@ -457,6 +476,11 @@ class WasmTokenStream
} while (*cur_++ != '"');
return WasmToken(WasmToken::Text, begin, cur_);
case 'b':
if (consume(end_, MOZ_UTF16("lock")))
return WasmToken(WasmToken::Block, begin, cur_);
break;
case '$':
while (cur_ != end_ && IsNameAfterDollar(*cur_))
cur_++;
@ -571,7 +595,7 @@ class WasmTokenStream
{}
void generateError(WasmToken token, UniqueChars* error) {
unsigned column = token.begin() - lineStart_ + 1;
error->reset(JS_smprintf("parsing wasm text at at %u:%u", line_, column));
error->reset(JS_smprintf("parsing wasm text at %u:%u", line_, column));
}
WasmToken peek() {
@ -679,6 +703,27 @@ ParseSetLocal(WasmParseContext& c)
return new(c.lifo) WasmAstSetLocal(localIndex.integer(), *value);
}
static WasmAstBlock*
ParseBlock(WasmParseContext& c)
{
WasmToken numExprs;
if (!c.ts.match(WasmToken::Integer, &numExprs, c.error))
return nullptr;
WasmAstExprVector exprs(c.lifo);
if (!exprs.reserve(numExprs.integer()))
return nullptr;
for (uint32_t i = 0; i < numExprs.integer(); i++) {
WasmAstExpr* value = ParseExpr(c);
if (!value)
return nullptr;
exprs.infallibleAppend(value);
}
return new(c.lifo) WasmAstBlock(Move(exprs));
}
static WasmAstExpr*
ParseExprInsideParens(WasmParseContext& c)
{
@ -687,6 +732,8 @@ ParseExprInsideParens(WasmParseContext& c)
switch (expr.kind()) {
case WasmToken::Nop:
return new(c.lifo) WasmAstNop;
case WasmToken::Block:
return ParseBlock(c);
case WasmToken::Const:
return ParseConst(c, expr);
case WasmToken::GetLocal:
@ -850,6 +897,24 @@ TextToAst(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
static bool
EncodeExpr(Encoder& e, WasmAstExpr& expr);
static bool
EncodeBlock(Encoder& e, WasmAstBlock& b)
{
if (!e.writeExpr(Expr::Block))
return false;
size_t numExprs = b.exprs().length();
if (!e.writeVarU32(numExprs))
return false;
for (size_t i = 0; i < numExprs; i++) {
if (!EncodeExpr(e, *b.exprs()[i]))
return false;
}
return true;
}
static bool
EncodeConst(Encoder& e, WasmAstConst& c)
{
@ -884,6 +949,8 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr)
switch (expr.kind()) {
case WasmAstKind::Nop:
return e.writeExpr(Expr::Nop);
case WasmAstKind::Block:
return EncodeBlock(e, expr.as<WasmAstBlock>());
case WasmAstKind::Const:
return EncodeConst(e, expr.as<WasmAstConst>());
case WasmAstKind::GetLocal:

View File

@ -126,3 +126,24 @@ assertEq(wasmEvalText('(module (func (result i32) (local i32) (set_local 0 (i32.
assertEq(wasmEvalText('(module (func (result i32) (local i32) (set_local 0 (get_local 0))) (export "" 0))')(), 0);
assertErrorMessage(() => wasmEvalText('(module (func (local i64)))'), Error, /NYI/);
// ----------------------------------------------------------------------------
// blocks
assertThrowsInstanceOf(() => wasmEvalText('(module (block))'), Error);
assertThrowsInstanceOf(() => wasmEvalText('(module (block -1 (i32.const 42)))'), Error);
assertEq(wasmEvalText('(module (func (block 0)) (export "" 0))')(), undefined);
assertErrorMessage(() => wasmEvalText('(module (func (result i32) (block 0)))'), Error, mismatchError("void", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (result i32) (block 1 (block 0))))'), Error, mismatchError("void", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (local i32) (set_local 0 (block 0))))'), Error, mismatchError("void", "i32"));
assertThrowsInstanceOf(() => wasmEvalText('(module (block 1))'), Error);
assertEq(wasmEvalText('(module (func (block 1 (block 0))) (export "" 0))')(), undefined);
assertEq(wasmEvalText('(module (func (result i32) (block 1 (i32.const 42))) (export "" 0))')(), 42);
assertEq(wasmEvalText('(module (func (result i32) (block 1 (block 1 (i32.const 42)))) (export "" 0))')(), 42);
assertErrorMessage(() => wasmEvalText('(module (func (result f32) (block 1 (i32.const 0))))'), Error, mismatchError("i32", "f32"));
assertEq(wasmEvalText('(module (func (result i32) (block 2 (i32.const 13) (block 1 (i32.const 42)))) (export "" 0))')(), 42);
assertErrorMessage(() => wasmEvalText('(module (func (result f32) (param f32) (block 2 (get_local 0) (i32.const 0))))'), Error, mismatchError("i32", "f32"));