Bug 1234985 - Odin: hoist read/writeOp into wasm::Decoder/Encoder (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Wed, 20 Jan 2016 10:26:04 -0600
changeset 303027 0f7510fa983c1804792cb4a43d341b6b4e533172
parent 303026 1079f6d91f65f24c401133e6c313c7f405749c82
child 303028 8be707e04f9acfaa018a7ff838c36b74b214927f
push id8978
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 14:05:32 +0000
treeherdermozilla-aurora@b9a803752a2c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1234985
milestone46.0a1
Bug 1234985 - Odin: hoist read/writeOp into wasm::Decoder/Encoder (r=bbouvier)
js/src/asmjs/AsmJS.cpp
js/src/asmjs/WasmBinary.h
js/src/asmjs/WasmIonCompile.cpp
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -2709,18 +2709,17 @@ class MOZ_STACK_CLASS FunctionValidator
     bool noteLineCol(ParseNode* pn) {
         uint32_t line, column;
         m().tokenStream().srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column);
         return fg_.addSourceCoords(encoder().bytecodeOffset(), line, column);
     }
 
     MOZ_WARN_UNUSED_RESULT
     bool writeOp(Expr op) {
-        static_assert(sizeof(Expr) == sizeof(uint8_t), "opcodes must be uint8");
-        return encoder().writeU8(uint8_t(op));
+        return encoder().writeExpr(op);
     }
 
     MOZ_WARN_UNUSED_RESULT
     bool writeDebugCheckPoint() {
 #ifdef DEBUG
         return writeOp(Expr::DebugCheckPoint);
 #endif
         return true;
@@ -2767,35 +2766,34 @@ class MOZ_STACK_CLASS FunctionValidator
             return writeOp(Expr::B32X4Const) && encoder().writeI32X4(lit.simdValue().asInt32x4());
           case NumLit::OutOfRangeInt:
             break;
         }
         MOZ_CRASH("unexpected literal type");
     }
 
     void patchOp(size_t pos, Expr stmt) {
-        static_assert(sizeof(Expr) == sizeof(uint8_t), "opcodes must be uint8");
-        encoder().patchU8(pos, uint8_t(stmt));
+        encoder().patchExpr(pos, stmt);
     }
     void patchU8(size_t pos, uint8_t u8) {
         encoder().patchU8(pos, u8);
     }
     template<class T>
     void patch32(size_t pos, T val) {
         static_assert(sizeof(T) == sizeof(uint32_t), "patch32 is used for 4-bytes long ops");
         encoder().patch32(pos, val);
     }
 
     MOZ_WARN_UNUSED_RESULT
     bool tempU8(size_t* offset) {
         return encoder().writeU8(uint8_t(Expr::Unreachable), offset);
     }
     MOZ_WARN_UNUSED_RESULT
     bool tempOp(size_t* offset) {
-        return tempU8(offset);
+        return encoder().writeExpr(Expr::Unreachable, offset);
     }
     MOZ_WARN_UNUSED_RESULT
     bool temp32(size_t* offset) {
         if (!encoder().writeU8(uint8_t(Expr::Unreachable), offset))
             return false;
         for (size_t i = 1; i < 4; i++) {
             if (!encoder().writeU8(uint8_t(Expr::Unreachable)))
                 return false;
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -306,24 +306,44 @@ typedef UniquePtr<Bytecode> UniqueByteco
 
 // The Encoder class recycles (through its constructor) or creates a new Bytecode (through its
 // init() method). Its Bytecode is released when it's done building the wasm IR in finish().
 class Encoder
 {
     UniqueBytecode bytecode_;
     DebugOnly<bool> done_;
 
-    template<class T>
+    template <class T>
     MOZ_WARN_UNUSED_RESULT
     bool write(T v, size_t* offset) {
         if (offset)
             *offset = bytecode_->length();
         return bytecode_->append(reinterpret_cast<uint8_t*>(&v), sizeof(T));
     }
 
+    template <class T>
+    MOZ_WARN_UNUSED_RESULT
+    bool writeEnum(T v, size_t* offset) {
+        // For now, just write a u8 instead of a variable-length integer.
+        // Variable-length is somewhat annoying at the moment due to the
+        // pre-order encoding and back-patching; let's see if we switch to
+        // post-order first.
+        static_assert(mozilla::IsEnum<T>::value, "is an enum");
+        MOZ_ASSERT(uint64_t(v) < UINT8_MAX);
+        return writeU8(uint8_t(v), offset);
+    }
+
+    template <class T>
+    void patchEnum(size_t pc, T v) {
+        // See writeEnum comment.
+        static_assert(mozilla::IsEnum<T>::value, "is an enum");
+        MOZ_ASSERT(uint64_t(v) < UINT8_MAX);
+        (*bytecode_)[pc] = uint8_t(v);
+    }
+
   public:
     Encoder()
       : bytecode_(nullptr),
         done_(false)
     {}
 
     bool init(UniqueBytecode bytecode = UniqueBytecode()) {
         if (bytecode) {
@@ -353,16 +373,21 @@ class Encoder
                 byte |= 0x80;
             if (!writeU8(byte))
                 return false;
         } while(i != 0);
         return true;
     }
 
     MOZ_WARN_UNUSED_RESULT bool
+    writeExpr(Expr expr, size_t* offset = nullptr) {
+        return writeEnum(expr, offset);
+    }
+
+    MOZ_WARN_UNUSED_RESULT bool
     writeU8(uint8_t i, size_t* offset = nullptr)   { return write<uint8_t>(i, offset); }
     MOZ_WARN_UNUSED_RESULT bool
     writeI32(int32_t i, size_t* offset = nullptr)  { return write<int32_t>(i, offset); }
     MOZ_WARN_UNUSED_RESULT bool
     writeU32(uint32_t i, size_t* offset = nullptr) { return write<uint32_t>(i, offset); }
     MOZ_WARN_UNUSED_RESULT bool
     writeF32(float f, size_t* offset = nullptr)    { return write<float>(f, offset); }
     MOZ_WARN_UNUSED_RESULT bool
@@ -392,56 +417,77 @@ class Encoder
 #ifdef DEBUG
     bool pcIsPatchable(size_t pc, unsigned size) const {
         bool patchable = true;
         for (unsigned i = 0; patchable && i < size; i++)
             patchable &= Expr((*bytecode_)[pc]) == Expr::Unreachable;
         return patchable;
     }
 #endif
-
     void patchU8(size_t pc, uint8_t i) {
         MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint8_t)));
         (*bytecode_)[pc] = i;
     }
-
+    void patchExpr(size_t pc, Expr expr) {
+        MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint8_t)));
+        patchEnum(pc, expr);
+    }
     template<class T>
     void patch32(size_t pc, T i) {
         static_assert(sizeof(T) == sizeof(uint32_t),
                       "patch32 must be used with 32-bits wide types");
         MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint32_t)));
         memcpy(&(*bytecode_)[pc], &i, sizeof(uint32_t));
     }
 };
 
 class Decoder
 {
     const uint8_t* const beg_;
     const uint8_t* const end_;
     const uint8_t* cur_;
 
-    template<class T>
+    template <class T>
     MOZ_WARN_UNUSED_RESULT bool
     read(T* out) {
         if (uintptr_t(end_ - cur_) < sizeof(T))
             return false;
         memcpy((void*)out, cur_, sizeof(T));
         cur_ += sizeof(T);
         return true;
     }
 
-    template<class T>
+    template <class T>
+    MOZ_WARN_UNUSED_RESULT bool
+    readEnum(T* out) {
+        static_assert(mozilla::IsEnum<T>::value, "is an enum");
+        // See Encoder::writeEnum.
+        uint8_t u8;
+        if (!read(&u8))
+            return false;
+        *out = T(u8);
+        return true;
+    }
+
+    template <class T>
     T uncheckedRead() {
         MOZ_ASSERT(uintptr_t(end_ - cur_) >= sizeof(T));
         T ret;
         memcpy(&ret, cur_, sizeof(T));
         cur_ += sizeof(T);
         return ret;
     }
 
+    template <class T>
+    T uncheckedReadEnum() {
+        // See Encoder::writeEnum.
+        static_assert(mozilla::IsEnum<T>::value, "is an enum");
+        return (T)uncheckedReadU8();
+    }
+
   public:
     explicit Decoder(const Bytecode& bytecode)
       : beg_(bytecode.begin()),
         end_(bytecode.end()),
         cur_(bytecode.begin())
     {}
 
     bool done() const {
@@ -494,16 +540,19 @@ class Decoder
             *decoded |= uint32_t(byte & 0x7F) << shift;
             shift += 7;
         } while (shift != 28);
         if (!readU8(&byte) || (byte & 0xF0))
             return false;
         *decoded |= uint32_t(byte) << 28;
         return true;
     }
+    MOZ_WARN_UNUSED_RESULT bool readExpr(Expr* expr) {
+        return readEnum(expr);
+    }
 
     // The infallible unpacking API should be used when we are sure that the
     // bytecode is well-formed.
     uint8_t        uncheckedReadU8 () { return uncheckedRead<uint8_t>(); }
     int32_t        uncheckedReadI32() { return uncheckedRead<int32_t>(); }
     float          uncheckedReadF32() { return uncheckedRead<float>(); }
     uint32_t       uncheckedReadU32() { return uncheckedRead<uint32_t>(); }
     double         uncheckedReadF64() { return uncheckedRead<double>(); }
@@ -534,16 +583,19 @@ class Decoder
             decoded |= uint32_t(byte & 0x7F) << shift;
             shift += 7;
         } while (shift != 28);
         byte = uncheckedReadU8();
         MOZ_ASSERT(!(byte & 0xF0));
         decoded |= uint32_t(byte) << 28;
         return decoded;
     }
+    Expr uncheckedReadExpr() {
+        return uncheckedReadEnum<Expr>();
+    }
 };
 
 // Source coordinates for a call site. As they're read sequentially, we
 // don't need to store the call's bytecode offset, unless we want to
 // check its correctness in debug mode.
 struct SourceCoords {
     DebugOnly<size_t> offset; // after call opcode
     uint32_t line;
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -1175,18 +1175,17 @@ class FunctionCompiler
     uint8_t        readU8()     { return decoder_.uncheckedReadU8(); }
     uint32_t       readU32()    { return decoder_.uncheckedReadU32(); }
     uint32_t       readVarU32() { return decoder_.uncheckedReadVarU32(); }
     int32_t        readI32()    { return decoder_.uncheckedReadI32(); }
     float          readF32()    { return decoder_.uncheckedReadF32(); }
     double         readF64()    { return decoder_.uncheckedReadF64(); }
     SimdConstant   readI32X4()  { return decoder_.uncheckedReadI32X4(); }
     SimdConstant   readF32X4()  { return decoder_.uncheckedReadF32X4(); }
-
-    Expr           readOpcode() { return Expr(readU8()); }
+    Expr           readOpcode() { return decoder_.uncheckedReadExpr(); }
 
     void readCallLineCol(uint32_t* line, uint32_t* column) {
         const SourceCoords& sc = func_.sourceCoords(lastReadCallSite_++);
         decoder_.assertCurrentIs(sc.offset);
         *line = sc.line;
         *column = sc.column;
     }