diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index 50977b2ac22..cdddcbb401e 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -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 diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index b30a7bb62ae..f903fcfa3a7 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -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; } diff --git a/js/src/asmjs/WasmBinary.h b/js/src/asmjs/WasmBinary.h index 2fe9d78afdd..4c9a5fd9475 100644 --- a/js/src/asmjs/WasmBinary.h +++ b/js/src/asmjs/WasmBinary.h @@ -206,8 +206,6 @@ enum class Expr : uint16_t InterruptCheckHead, InterruptCheckLoop, - DebugCheckPoint, - I32Min, I32Max, diff --git a/js/src/asmjs/WasmGenerator.cpp b/js/src/asmjs/WasmGenerator.cpp index 62c5e03e9f1..505a9899bb0 100644 --- a/js/src/asmjs/WasmGenerator.cpp +++ b/js/src/asmjs/WasmGenerator.cpp @@ -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; diff --git a/js/src/asmjs/WasmGenerator.h b/js/src/asmjs/WasmGenerator.h index d1d248fce25..508982ccf4b 100644 --- a/js/src/asmjs/WasmGenerator.h +++ b/js/src/asmjs/WasmGenerator.h @@ -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_; } diff --git a/js/src/asmjs/WasmIonCompile.cpp b/js/src/asmjs/WasmIonCompile.cpp index 6d461605e1f..eb0a00ee296 100644 --- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -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); diff --git a/js/src/asmjs/WasmIonCompile.h b/js/src/asmjs/WasmIonCompile.h index 5bbbd4b2766..b3d49b2c518 100644 --- a/js/src/asmjs/WasmIonCompile.h +++ b/js/src/asmjs/WasmIonCompile.h @@ -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), diff --git a/js/src/asmjs/WasmText.cpp b/js/src/asmjs/WasmText.cpp index af65f6335e4..d6c7cc9ea7f 100644 --- a/js/src/asmjs/WasmText.cpp +++ b/js/src/asmjs/WasmText.cpp @@ -41,6 +41,8 @@ static const unsigned AST_LIFO_DEFAULT_CHUNK_SIZE = 4096; /*****************************************************************************/ // wasm AST +class WasmAstExpr; + template using WasmAstVector = mozilla::Vector>; @@ -48,6 +50,7 @@ template using WasmAstHashMap = HashMap>; typedef WasmAstVector WasmAstValTypeVector; +typedef WasmAstVector 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()); case WasmAstKind::Const: return EncodeConst(e, expr.as()); case WasmAstKind::GetLocal: diff --git a/js/src/jit-test/tests/wasm/basic.js b/js/src/jit-test/tests/wasm/basic.js index fb915b045b2..c6e83923ca8 100644 --- a/js/src/jit-test/tests/wasm/basic.js +++ b/js/src/jit-test/tests/wasm/basic.js @@ -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")); +