Bug 1260696: Update wasm text format spewing; r=luke
authorBenjamin Bouvier <benj@benj.me>
Wed, 30 Mar 2016 13:23:21 +0200
changeset 291246 bf8dc00cc76fad3ef265e391d84b41d6b3e86abc
parent 291245 c79a34d5b3a3d8edce927f6a8a0d57cc7565a9dd
child 291247 ef04d22a4de586d67193e7011bd37aaecdf90164
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1260696
milestone48.0a1
Bug 1260696: Update wasm text format spewing; r=luke MozReview-Commit-ID: 8Yu19SYPphg
js/src/asmjs/WasmBinaryToText.cpp
js/src/asmjs/WasmTextToBinary.cpp
js/src/builtin/TestingFunctions.cpp
--- a/js/src/asmjs/WasmBinaryToText.cpp
+++ b/js/src/asmjs/WasmBinaryToText.cpp
@@ -27,32 +27,36 @@
 #include "asmjs/WasmTypes.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/StringBuffer.h"
 
 using namespace js;
 using namespace js::wasm;
 
 using mozilla::CheckedInt;
+using mozilla::IsInfinite;
 using mozilla::IsNaN;
+using mozilla::IsNegativeZero;
 
 struct WasmRenderContext
 {
     JSContext* cx;
     Decoder& d;
     StringBuffer& buffer;
     uint32_t indent;
 
     DeclaredSigVector signatures;
     Uint32Vector funcSigs;
+    Uint32Vector funcLocals;
     Uint32Vector importSigs;
-    bool has_return;
+
+    uint32_t currentFuncIndex;
 
     WasmRenderContext(JSContext* cx, Decoder& d, StringBuffer& buffer)
-      : cx(cx), d(d), buffer(buffer), indent(0)
+      : cx(cx), d(d), buffer(buffer), indent(0), currentFuncIndex(0)
     {}
 };
 
 /*****************************************************************************/
 // utilities
 
 static bool
 RenderFail(WasmRenderContext& c, const char* str)
@@ -76,18 +80,55 @@ RenderIndent(WasmRenderContext& c)
 
 static bool
 RenderInt32(WasmRenderContext& c, int32_t num)
 {
     return NumberValueToStringBuffer(c.cx, Int32Value(num), c.buffer);
 }
 
 static bool
+RenderInt64(WasmRenderContext& c, int64_t num)
+{
+    if (num < 0 && !c.buffer.append("-"))
+        return false;
+    if (!num)
+        return c.buffer.append("0");
+
+    int64_t abs = mozilla::Abs(num);
+    int64_t n = abs;
+    uint64_t pow = 1;
+    while (n) {
+        pow *= 10;
+        n /= 10;
+    }
+    pow /= 10;
+
+    n = abs;
+    while (pow) {
+        if (!c.buffer.append("0123456789"[n / pow]))
+            return false;
+        n -= (n / pow) * pow;
+        pow /= 10;
+    }
+
+    return true;
+}
+
+static bool
 RenderDouble(WasmRenderContext& c, double num)
 {
+    if (IsNegativeZero(num))
+        return c.buffer.append("-0");
+    if (IsNaN(num))
+        return c.buffer.append("nan");
+    if (IsInfinite(num)) {
+        if (num > 0)
+            return c.buffer.append("infinity");
+        return c.buffer.append("-infinity");
+    }
     return NumberValueToStringBuffer(c.cx, DoubleValue(num), c.buffer);
 }
 
 static bool
 RenderString(WasmRenderContext& c, const uint8_t* s, size_t length)
 {
     for (size_t i = 0; i < length; i++) {
         uint8_t byte = s[i];
@@ -186,16 +227,19 @@ RenderCall(WasmRenderContext& c)
     if (!c.d.readVarU32(&funcIndex))
         return RenderFail(c, "unable to read import index");
 
     if (!c.buffer.append("(call $func$"))
         return false;
     if (!RenderInt32(c, funcIndex))
         return false;
 
+    if (funcIndex >= c.funcSigs.length())
+        return RenderFail(c, "callee index out of range");
+
     uint32_t sigIndex = c.funcSigs[funcIndex];
     if (!RenderCallWithSig(c, sigIndex))
         return false;
     if (!c.buffer.append(")"))
         return false;
     return true;
 }
 
@@ -206,16 +250,19 @@ RenderCallImport(WasmRenderContext& c)
     if (!c.d.readVarU32(&importIndex))
         return RenderFail(c, "unable to read import index");
 
     if (!c.buffer.append("(call_import $import$"))
         return false;
     if (!RenderInt32(c, importIndex))
         return false;
 
+    if (importIndex >= c.importSigs.length())
+        return RenderFail(c, "import index out of range");
+
     uint32_t sigIndex = c.importSigs[importIndex];
     if (!RenderCallWithSig(c, sigIndex))
         return false;
     if (!c.buffer.append(")"))
         return false;
 
     return true;
 }
@@ -268,24 +315,21 @@ RenderConstI32(WasmRenderContext& c)
 }
 
 static bool
 RenderConstI64(WasmRenderContext& c)
 {
     if (!c.buffer.append("(i64.const "))
         return false;
 
-    uint64_t u64;
-    if (!c.d.readVarU64(&u64))
+    int64_t i64;
+    if (!c.d.readVarS64(&i64))
         return RenderFail(c, "unable to read i64.const immediate");
 
-    if (u64 != (uint64_t)(double)u64)
-        return RenderFail(c, "unable to render i64.const immediate");
-
-    if (!RenderDouble(c, u64))
+    if (!RenderInt64(c, i64))
         return false;
 
     if (!c.buffer.append(")"))
         return false;
     return true;
 }
 
 static bool
@@ -338,32 +382,38 @@ RenderConstF64(WasmRenderContext& c)
 
 static bool
 RenderGetLocal(WasmRenderContext& c)
 {
     uint32_t localIndex;
     if (!c.d.readVarU32(&localIndex))
         return RenderFail(c, "unable to read get_local index");
 
+    if (localIndex >= c.funcLocals[c.currentFuncIndex])
+        return RenderFail(c, "get_local index out of range");
+
     if (!c.buffer.append("(get_local $var$"))
         return false;
     if (!RenderInt32(c, localIndex))
         return false;
     if (!c.buffer.append(")"))
         return false;
     return true;
 }
 
 static bool
 RenderSetLocal(WasmRenderContext& c)
 {
     uint32_t localIndex;
     if (!c.d.readVarU32(&localIndex))
         return RenderFail(c, "unable to read set_local index");
 
+    if (localIndex >= c.funcLocals[c.currentFuncIndex])
+        return RenderFail(c, "set_local index out of range");
+
     if (!c.buffer.append("(set_local $var$"))
         return false;
     if (!RenderInt32(c, localIndex))
         return false;
     if (!c.buffer.append(" "))
         return false;
 
     if (!RenderExpr(c))
@@ -429,16 +479,17 @@ RenderLoop(WasmRenderContext& c)
 static bool
 RenderUnaryOperator(WasmRenderContext& c, Expr expr, ValType argType)
 {
     if (!c.buffer.append("("))
       return false;
 
     bool success = false;
     switch (expr) {
+      case Expr::I32Eqz:     success = c.buffer.append("i32.eqz"); break;
       case Expr::I32Clz:     success = c.buffer.append("i32.clz"); break;
       case Expr::I32Ctz:     success = c.buffer.append("i32.ctz"); break;
       case Expr::I32Popcnt:  success = c.buffer.append("i32.popcnt"); break;
       case Expr::I64Clz:     success = c.buffer.append("i64.clz"); break;
       case Expr::I64Ctz:     success = c.buffer.append("i64.ctz"); break;
       case Expr::I64Popcnt:  success = c.buffer.append("i64.popcnt"); break;
       case Expr::F32Abs:     success = c.buffer.append("f32.abs"); break;
       case Expr::F32Neg:     success = c.buffer.append("f32.neg"); break;
@@ -509,17 +560,17 @@ RenderBinaryOperator(WasmRenderContext& 
       case Expr::F32Min:      success = c.buffer.append("f32.min"); break;
       case Expr::F32Max:      success = c.buffer.append("f32.max"); break;
       case Expr::F32CopySign: success = c.buffer.append("f32.copysign"); break;
       case Expr::F64Add:      success = c.buffer.append("f64.add"); break;
       case Expr::F64Sub:      success = c.buffer.append("f64.sub"); break;
       case Expr::F64Mul:      success = c.buffer.append("f64.mul"); break;
       case Expr::F64Div:      success = c.buffer.append("f64.div"); break;
       case Expr::F64Min:      success = c.buffer.append("f64.min"); break;
-      case Expr::F64Max:      success = c.buffer.append("f64.mix"); break;
+      case Expr::F64Max:      success = c.buffer.append("f64.max"); break;
       default: return false;
     }
     if (!success)
         return false;
     if (!c.buffer.append(" "))
         return false;
     if (!RenderExpr(c))
         return false;
@@ -593,17 +644,17 @@ RenderComparisonOperator(WasmRenderConte
 static bool
 RenderConversionOperator(WasmRenderContext& c, Expr expr, ValType to, ValType argType)
 {
     if (!c.buffer.append("("))
       return false;
 
     bool success = false;
     switch (expr) {
-      case Expr::I32WrapI64:        success = c.buffer.append("i32.warp/i64"); break;
+      case Expr::I32WrapI64:        success = c.buffer.append("i32.wrap/i64"); break;
       case Expr::I32TruncSF32:      success = c.buffer.append("i32.trunc_s/f32"); break;
       case Expr::I32TruncUF32:      success = c.buffer.append("i32.trunc_u/f32"); break;
       case Expr::I32ReinterpretF32: success = c.buffer.append("i32.reinterpret/f32"); break;
       case Expr::I32TruncSF64:      success = c.buffer.append("i32.trunc_s/f64"); break;
       case Expr::I32TruncUF64:      success = c.buffer.append("i32.trunc_u/f64"); break;
       case Expr::I64ExtendSI32:     success = c.buffer.append("i64.extend_s/i32"); break;
       case Expr::I64ExtendUI32:     success = c.buffer.append("i64.extend_u/i32"); break;
       case Expr::I64TruncSF32:      success = c.buffer.append("i64.trunc_s/f32"); break;
@@ -684,16 +735,22 @@ RenderLoadStoreAddress(WasmRenderContext
 
     if (offset != 0) {
       if (!c.buffer.append(" offset="))
           return false;
       if (!RenderInt32(c, offset))
           return false;
     }
 
+    uint32_t alignLog2 = flags;
+    if (!c.buffer.append(" align="))
+        return false;
+    if (!RenderInt32(c, 1 << alignLog2))
+        return false;
+
     if (!c.buffer.append(" "))
         return false;
 
     if (!RenderExpr(c))
         return false;
 
     return true;
 }
@@ -789,44 +846,41 @@ RenderStore(WasmRenderContext& c, Expr e
         return false;
 
     return true;
 }
 
 static bool
 RenderBranch(WasmRenderContext& c, Expr expr)
 {
+    MOZ_ASSERT(expr == Expr::BrIf || expr == Expr::Br);
+
     uint32_t relativeDepth;
     if (!c.d.readVarU32(&relativeDepth))
         return RenderFail(c, "expected relative depth");
 
-    Expr value;
-    if (!c.d.readExpr(&value))
-        return RenderFail(c, "expected branch value");
-    if (value != Expr::Nop)
-        return RenderFail(c, "NYI: branch values");
+    if (expr == Expr::BrIf ? !c.buffer.append("(br_if ") : !c.buffer.append("(br "))
+        return false;
+
+    if (!RenderInt32(c, relativeDepth))
+        return false;
+
+    if (!c.buffer.append(" "))
+        return false;
+
+    if (!RenderExpr(c))
+        return false;
 
     if (expr == Expr::BrIf) {
-        if (!c.buffer.append("(br_if "))
-          return false;
-
-        if (!RenderInt32(c, relativeDepth))
-            return false;
-
         if (!c.buffer.append(" "))
             return false;
-
         if (!RenderExpr(c))
             return false;
-    } else {
-        if (!c.buffer.append("(br "))
-            return false;
-        if (!RenderInt32(c, relativeDepth))
-            return false;
     }
+
     if (!c.buffer.append(")"))
         return false;
     return true;
 }
 
 static bool
 RenderBrTable(WasmRenderContext& c)
 {
@@ -869,29 +923,53 @@ RenderBrTable(WasmRenderContext& c)
 }
 
 static bool
 RenderReturn(WasmRenderContext& c)
 {
     if (!c.buffer.append("(return"))
         return false;
 
-    if (c.has_return) {
+    if (c.signatures[c.funcSigs[c.currentFuncIndex]].ret() != ExprType::Void) {
         if (!c.buffer.append(" "))
             return false;
         if (!RenderExpr(c))
             return false;
     }
 
     if (!c.buffer.append(")"))
         return false;
     return true;
 }
 
 static bool
+RenderSelect(WasmRenderContext& c)
+{
+    if (!c.buffer.append("(select "))
+        return false;
+
+    if (!RenderExpr(c))
+        return false;
+
+    if (!c.buffer.append(" "))
+        return false;
+
+    if (!RenderExpr(c))
+        return false;
+
+    if (!c.buffer.append(" "))
+        return false;
+
+    if (!RenderExpr(c))
+        return false;
+
+    return c.buffer.append(")");
+}
+
+static bool
 RenderExpr(WasmRenderContext& c)
 {
     Expr expr;
     if (!c.d.readExpr(&expr))
         return RenderFail(c, "unable to read expression");
 
     switch (expr) {
       case Expr::Nop:
@@ -922,16 +1000,17 @@ RenderExpr(WasmRenderContext& c)
         return RenderLoop(c);
       case Expr::If:
         return RenderIfElse(c, false);
       case Expr::IfElse:
         return RenderIfElse(c, true);
       case Expr::I32Clz:
       case Expr::I32Ctz:
       case Expr::I32Popcnt:
+      case Expr::I32Eqz:
         return RenderUnaryOperator(c, expr, ValType::I32);
       case Expr::I64Clz:
       case Expr::I64Ctz:
       case Expr::I64Popcnt:
         return RenderFail(c, "NYI: i64") &&
             RenderUnaryOperator(c, expr, ValType::I64);
       case Expr::F32Abs:
       case Expr::F32Neg:
@@ -1058,28 +1137,26 @@ RenderExpr(WasmRenderContext& c)
         return RenderFail(c, "NYI: i64");
       case Expr::F32ConvertSI32:
       case Expr::F32ConvertUI32:
         return RenderConversionOperator(c, expr, ValType::F32, ValType::I32);
       case Expr::F32ReinterpretI32:
         return RenderFail(c, "NYI: reinterpret");
       case Expr::F32ConvertSI64:
       case Expr::F32ConvertUI64:
-        return RenderFail(c, "NYI: i64") &&
-               RenderConversionOperator(c, expr, ValType::F32, ValType::I64);
+        return RenderConversionOperator(c, expr, ValType::F32, ValType::I64);
       case Expr::F32DemoteF64:
         return RenderConversionOperator(c, expr, ValType::F32, ValType::I64);
       case Expr::F64ConvertSI32:
       case Expr::F64ConvertUI32:
         return RenderConversionOperator(c, expr, ValType::F64, ValType::I32);
       case Expr::F64ConvertSI64:
       case Expr::F64ConvertUI64:
       case Expr::F64ReinterpretI64:
-        return RenderFail(c, "NYI: i64") &&
-               RenderConversionOperator(c, expr, ValType::F64, ValType::I64);
+        return RenderConversionOperator(c, expr, ValType::F64, ValType::I64);
       case Expr::F64PromoteF32:
         return RenderConversionOperator(c, expr, ValType::F64, ValType::F32);
       case Expr::I32Load8S:
       case Expr::I32Load8U:
       case Expr::I32Load16S:
       case Expr::I32Load16U:
       case Expr::I32Load:
         return RenderLoad(c, expr, ValType::I32);
@@ -1115,16 +1192,18 @@ RenderExpr(WasmRenderContext& c)
       case Expr::Br:
         return RenderBranch(c, expr);
       case Expr::BrIf:
         return RenderBranch(c, expr);
       case Expr::BrTable:
         return RenderBrTable(c);
       case Expr::Return:
         return RenderReturn(c);
+      case Expr::Select:
+        return RenderSelect(c);
       default:
         // Note: it's important not to remove this default since readExpr()
         // can return Expr values for which there is no enumerator.
         break;
     }
 
     return RenderFail(c, "bad expression code");
 }
@@ -1269,27 +1348,31 @@ RenderFunctionTable(WasmRenderContext& c
         return RenderFail(c, "failed to start section");
     if (sectionStart == Decoder::NotStarted)
         return true;
 
     uint32_t numTableElems;
     if (!c.d.readVarU32(&numTableElems))
         return RenderFail(c, "expected number of table elems");
 
-    Uint32Vector elems;
-    if (!elems.resize(numTableElems))
+    if (!c.buffer.append("(table "))
         return false;
 
     for (uint32_t i = 0; i < numTableElems; i++) {
         uint32_t funcIndex;
         if (!c.d.readVarU32(&funcIndex))
             return RenderFail(c, "expected table element");
+        if (!RenderInt32(c, funcIndex))
+            return false;
+        if (!c.buffer.append(" "))
+            return false;
+    }
 
-        elems[i] = funcIndex;
-    }
+    if (!c.buffer.append(")"))
+        return false;
 
     if (!c.d.finishSection(sectionStart))
         return RenderFail(c, "table section byte size mismatch");
 
     return true;
 }
 
 static bool
@@ -1393,16 +1476,19 @@ RenderMemory(WasmRenderContext& c, uint3
 
     uint8_t exported;
     if (!c.d.readFixedU8(&exported))
         return RenderFail(c, "expected exported byte");
 
     if (!c.d.finishSection(sectionStart))
         return RenderFail(c, "memory section byte size mismatch");
 
+    if (exported && !c.buffer.append("(export \"memory\" memory)"))
+        return false;
+
     return true;
 }
 
 static bool
 RenderExportName(WasmRenderContext& c)
 {
     Bytes fieldBytes;
     if (!c.d.readBytes(&fieldBytes)) {
@@ -1418,16 +1504,19 @@ RenderExportName(WasmRenderContext& c)
 
 static bool
 RenderFunctionExport(WasmRenderContext& c)
 {
     uint32_t funcIndex;
     if (!c.d.readVarU32(&funcIndex))
         return RenderFail(c, "expected export internal index");
 
+    if (funcIndex >= c.funcSigs.length())
+        return RenderFail(c, "export function index out of range");
+
     if (!RenderIndent(c))
         return false;
     if (!c.buffer.append("(export \""))
         return false;
     if (!RenderExportName(c))
         return false;
     if (!c.buffer.append("\" "))
         return false;
@@ -1502,16 +1591,21 @@ RenderFunctionBody(WasmRenderContext& c,
                 return false;
             if (!c.buffer.append(") "))
                 return false;
         }
         if (!c.buffer.append("\n"))
             return false;
     }
 
+    if (funcIndex >= c.funcLocals.length() && !c.funcLocals.resize(funcIndex + 1))
+        return false;
+
+    c.funcLocals[funcIndex] = localsNum + paramsNum;
+
     while (c.d.currentPosition() < bodyEnd) {
       if (!RenderFullLine(c))
           return false;
     }
 
     if (c.d.currentPosition() != bodyEnd)
         return RenderFail(c, "function body length mismatch");
 
@@ -1548,17 +1642,17 @@ RenderFunctionBodies(WasmRenderContext& 
         if (!RenderInt32(c, funcIndex))
             return false;
 
         if (!RenderSignature(c, sig, true))
             return false;
         if (!c.buffer.append("\n"))
             return false;
 
-        c.has_return = sig.ret() != ExprType::Void;
+        c.currentFuncIndex = funcIndex;
 
         c.indent++;
         if (!RenderFunctionBody(c, funcIndex, sig.args().length()))
             return false;
         c.indent--;
         if (!RenderIndent(c))
             return false;
         if (!c.buffer.append(")\n"))
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -3322,16 +3322,19 @@ ResolveBlock(Resolver& r, WasmAstBlock& 
 }
 
 static bool
 ResolveBranch(Resolver& r, WasmAstBranch& br)
 {
     if (!r.resolveBranchTarget(br.target()))
         return false;
 
+    if (br.maybeValue() && !ResolveExpr(r, *br.maybeValue()))
+        return false;
+
     if (br.expr() == Expr::BrIf) {
         if (!ResolveExpr(r, br.cond()))
             return false;
     }
 
     return true;
 }
 
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -573,18 +573,20 @@ WasmBinaryToText(JSContext* cx, unsigned
     if (code->bufferUnshared()->hasInlineData()) {
         if (!copy.append(bytes, length))
             return false;
         bytes = copy.begin();
     }
 
     StringBuffer buffer(cx);
     if (!wasm::BinaryToText(cx, bytes, length, buffer)) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL,
-                             "print error");
+        if (!cx->isExceptionPending()) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_DECODE_FAIL,
+                                 "print error");
+        }
         return false;
     }
 
     JSString* result = buffer.finishString();
     if (!result)
         return false;
 
     args.rval().setString(result);