Bug 1248860 - Baldr: change function bodies to an expr list from expr (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Wed, 17 Feb 2016 09:30:53 -0600
changeset 331620 6bb9aa94a48eb0a7d69d9b07d8bf10400861a6a9
parent 331619 c4d7e7b3172fc806afe55879c19f1e31894cd546
child 331621 266e1a6642b10ac593587674119db65f6aeb0da5
push id11020
push userjolesen@mozilla.com
push dateWed, 17 Feb 2016 18:16:38 +0000
reviewersbbouvier
bugs1248860
milestone47.0a1
Bug 1248860 - Baldr: change function bodies to an expr list from expr (r=bbouvier) MozReview-Commit-ID: B8HbPqtcSFR
js/src/asmjs/AsmJS.cpp
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmIonCompile.cpp
js/src/asmjs/WasmText.cpp
js/src/jit-test/tests/wasm/basic.js
--- 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)))');