Bug 1248860 - Baldr: change function bodies to an expr list from expr (r=bbouvier)
MozReview-Commit-ID: B8HbPqtcSFR
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -3402,17 +3402,17 @@ static bool
SetLocal(FunctionValidator& f, NumLit lit)
{
return f.encoder().writeExpr(Expr::SetLocal) &&
f.encoder().writeVarU32(f.numLocals()) &&
f.writeLit(lit);
}
static bool
-CheckVariable(FunctionValidator& f, ParseNode* var)
+CheckVariable(FunctionValidator& f, ParseNode* var, uint32_t* numStmts)
{
if (!IsDefinition(var))
return f.fail(var, "local variable names must not restate argument names");
PropertyName* name = var->name();
if (!CheckIdentifier(f.m(), var, name))
return false;
@@ -3423,30 +3423,33 @@ CheckVariable(FunctionValidator& f, Pars
NumLit lit;
if (!IsLiteralOrConst(f, initNode, &lit))
return f.failName(var, "var '%s' initializer must be literal or const literal", name);
if (!lit.valid())
return f.failName(var, "var '%s' initializer out of range", name);
- if (!lit.isZeroBits() && !SetLocal(f, lit))
- return false;
+ if (!lit.isZeroBits()) {
+ ++*numStmts;
+ if (!SetLocal(f, lit))
+ return false;
+ }
return f.addLocal(var, name, lit.type());
}
static bool
-CheckVariables(FunctionValidator& f, ParseNode** stmtIter)
+CheckVariables(FunctionValidator& f, ParseNode** stmtIter, uint32_t* numStmts)
{
ParseNode* stmt = *stmtIter;
for (; stmt && stmt->isKind(PNK_VAR); stmt = NextNonEmptyStatement(stmt)) {
for (ParseNode* var = VarListHead(stmt); var; var = NextNode(var)) {
- if (!CheckVariable(f, var))
+ if (!CheckVariable(f, var, numStmts))
return false;
}
}
*stmtIter = stmt;
return true;
}
@@ -6594,30 +6597,38 @@ CheckFunction(ModuleValidator& m)
if (!CheckProcessingDirectives(m, &stmtIter))
return false;
ValTypeVector args;
if (!CheckArguments(f, &stmtIter, &args))
return false;
- if (!CheckVariables(f, &stmtIter))
+ uint32_t numStmts = 0;
+
+ size_t numStmtsAt;
+ if (!f.encoder().writePatchableVarU32(&numStmtsAt))
+ return false;
+
+ if (!CheckVariables(f, &stmtIter, &numStmts))
return false;
ParseNode* lastNonEmptyStmt = nullptr;
- for (; stmtIter; stmtIter = NextNode(stmtIter)) {
+ for (; stmtIter; stmtIter = NextNonEmptyStatement(stmtIter)) {
+ numStmts++;
+ lastNonEmptyStmt = stmtIter;
if (!CheckStatement(f, stmtIter))
return false;
- if (!IsEmptyStatement(stmtIter))
- lastNonEmptyStmt = stmtIter;
}
if (!CheckFinalReturn(f, lastNonEmptyStmt))
return false;
+ f.encoder().patchVarU32(numStmtsAt, numStmts);
+
ModuleValidator::Func* func = nullptr;
if (!CheckFunctionSignature(m, fn, Sig(Move(args), f.returnedType()), FunctionName(fn), &func))
return false;
if (func->defined())
return m.failName(fn, "function '%s' already defined", FunctionName(fn));
func->define(fn);
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -622,18 +622,33 @@ DecodeExpr(FunctionDecoder& f, ExprType
static bool
DecodeFuncBody(JSContext* cx, Decoder& d, ModuleGenerator& mg, FunctionGenerator& fg,
uint32_t funcIndex)
{
const uint8_t* bodyBegin = d.currentPosition();
FunctionDecoder f(cx, d, mg, fg, funcIndex);
- if (!DecodeExpr(f, f.ret()))
- return false;
+
+ uint32_t numExprs;
+ if (!d.readVarU32(&numExprs))
+ return Fail(cx, d, "expected number of function body expressions");
+
+ if (numExprs) {
+ for (size_t i = 0; i < numExprs - 1; i++) {
+ if (!DecodeExpr(f, ExprType::Void))
+ return false;
+ }
+
+ if (!DecodeExpr(f, f.ret()))
+ return false;
+ } else {
+ if (!CheckType(f, ExprType::Void, f.ret()))
+ return false;
+ }
const uint8_t* bodyEnd = d.currentPosition();
uintptr_t bodyLength = bodyEnd - bodyBegin;
if (!fg.bytecode().resize(bodyLength))
return false;
memcpy(fg.bytecode().begin(), bodyBegin, bodyLength);
return true;
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -3016,27 +3016,27 @@ wasm::IonCompileFunction(IonCompileTask*
// Build MIR graph
{
FunctionCompiler f(task->mg(), func, mir, results);
if (!f.init())
return false;
MDefinition* last = nullptr;
- if (f.mg().isAsmJS()) {
- while (!f.done()) {
- if (!EmitExprStmt(f, &last))
+ if (uint32_t numExprs = f.readVarU32()) {
+ for (uint32_t i = 0; i < numExprs - 1; i++) {
+ if (!EmitExpr(f, ExprType::Void, &last))
return false;
}
- } else {
+
if (!EmitExpr(f, f.sig().ret(), &last))
return false;
- MOZ_ASSERT(f.done());
}
+ MOZ_ASSERT(f.done());
MOZ_ASSERT(IsVoid(f.sig().ret()) || f.inDeadCode() || last);
if (IsVoid(f.sig().ret()))
f.returnVoid();
else
f.returnExpr(last);
f.checkPostconditions();
--- a/js/src/asmjs/WasmText.cpp
+++ b/js/src/asmjs/WasmText.cpp
@@ -333,27 +333,27 @@ class WasmAstStore : public WasmAstExpr
const WasmAstLoadStoreAddress& address() const { return address_; }
WasmAstExpr& value() const { return *value_; }
};
class WasmAstFunc : public WasmAstNode
{
const uint32_t sigIndex_;
WasmAstValTypeVector varTypes_;
- WasmAstExpr* const maybeBody_;
+ WasmAstExprVector body_;
public:
- WasmAstFunc(uint32_t sigIndex, WasmAstValTypeVector&& varTypes, WasmAstExpr* maybeBody)
+ WasmAstFunc(uint32_t sigIndex, WasmAstValTypeVector&& varTypes, WasmAstExprVector&& body)
: sigIndex_(sigIndex),
varTypes_(Move(varTypes)),
- maybeBody_(maybeBody)
+ body_(Move(body))
{}
uint32_t sigIndex() const { return sigIndex_; }
const WasmAstValTypeVector& varTypes() const { return varTypes_; }
- WasmAstExpr* maybeBody() const { return maybeBody_; }
+ const WasmAstExprVector& body() const { return body_; }
};
class WasmAstImport : public WasmAstNode
{
TwoByteChars module_;
TwoByteChars func_;
uint32_t sigIndex_;
@@ -2417,50 +2417,50 @@ ParseFunc(WasmParseContext& c, WasmAstMo
sigIndex = sigToken.index();
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
} else {
c.ts.unget(openParen);
}
}
- WasmAstExpr* maybeBody = nullptr;
-
- while (c.ts.getIf(WasmToken::OpenParen) && !maybeBody) {
+ WasmAstExprVector body(c.lifo);
+
+ while (c.ts.getIf(WasmToken::OpenParen)) {
WasmToken token = c.ts.get();
switch (token.kind()) {
case WasmToken::Local:
if (!ParseValueType(c, &vars))
return nullptr;
break;
case WasmToken::Param:
if (!ParseValueType(c, &args))
return nullptr;
break;
case WasmToken::Result:
if (!ParseResult(c, &result))
return nullptr;
break;
default:
c.ts.unget(token);
- maybeBody = ParseExprInsideParens(c);
- if (!maybeBody)
+ WasmAstExpr* expr = ParseExprInsideParens(c);
+ if (!expr || !body.append(expr))
return nullptr;
break;
}
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
}
if (sigIndex == BadSigIndex) {
if (!module->declare(WasmAstSig(Move(args), result), &sigIndex))
return nullptr;
}
- return new(c.lifo) WasmAstFunc(sigIndex, Move(vars), maybeBody);
+ return new(c.lifo) WasmAstFunc(sigIndex, Move(vars), Move(body));
}
static bool
ParseFuncType(WasmParseContext& c, WasmAstSig* sig)
{
WasmAstValTypeVector args(c.lifo);
ExprType result = ExprType::Void;
@@ -3118,21 +3118,21 @@ EncodeFunctionSection(Encoder& e, WasmAs
if (!e.writeVarU32(func.varTypes().length()))
return false;
for (ValType type : func.varTypes()) {
if (!e.writeValType(type))
return false;
}
- if (func.maybeBody()) {
- if (!EncodeExpr(e, *func.maybeBody()))
- return false;
- } else {
- if (!e.writeExpr(Expr::Nop))
+ if (!e.writeVarU32(func.body().length()))
+ return false;
+
+ for (WasmAstExpr* expr : func.body()) {
+ if (!EncodeExpr(e, *expr))
return false;
}
e.finishSection(offset);
return true;
}
--- a/js/src/jit-test/tests/wasm/basic.js
+++ b/js/src/jit-test/tests/wasm/basic.js
@@ -233,16 +233,18 @@ assertErrorMessage(() => wasmEvalText('(
assertEq(wasmEvalText('(module (func (block (block))) (export "" 0))')(), undefined);
assertEq(wasmEvalText('(module (func (result i32) (block (i32.const 42))) (export "" 0))')(), 42);
assertEq(wasmEvalText('(module (func (result i32) (block (block (i32.const 42)))) (export "" 0))')(), 42);
assertErrorMessage(() => wasmEvalText('(module (func (result f32) (block (i32.const 0))))'), TypeError, mismatchError("i32", "f32"));
assertEq(wasmEvalText('(module (func (result i32) (block (i32.const 13) (block (i32.const 42)))) (export "" 0))')(), 42);
assertErrorMessage(() => wasmEvalText('(module (func (result f32) (param f32) (block (get_local 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f32"));
+assertEq(wasmEvalText('(module (func (result i32) (local i32) (set_local 0 (i32.const 42)) (get_local 0)) (export "" 0))')(), 42);
+
// ----------------------------------------------------------------------------
// calls
assertThrowsInstanceOf(() => wasmEvalText('(module (func (nop)) (func (call 0 (i32.const 0))))'), TypeError);
assertThrowsInstanceOf(() => wasmEvalText('(module (func (param i32) (nop)) (func (call 0)))'), TypeError);
assertThrowsInstanceOf(() => wasmEvalText('(module (func (param f32) (nop)) (func (call 0 (i32.const 0))))'), TypeError);
assertErrorMessage(() => wasmEvalText('(module (func (nop)) (func (call 3)))'), TypeError, /callee index out of range/);
wasmEvalText('(module (func (nop)) (func (call 0)))');