Bug 1316625 - Baldr: tidy up WasmBinary*.h (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Thu, 10 Nov 2016 14:54:39 -0600
changeset 352126 1b0a99027126df1d04c20ffc6a2338d62f205f7e
parent 352125 b06d68ee9270643f5295a15dd34a920ac79c9ace
child 352127 b10dbdd46d12c4f0d98a6e4c6a6a64adab8dfe22
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1316625
milestone52.0a1
Bug 1316625 - Baldr: tidy up WasmBinary*.h (r=bbouvier) MozReview-Commit-ID: BUtoQpsKYF5
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jsapi-tests/testWasmLEB128.cpp
js/src/moz.build
js/src/wasm/WasmBaselineCompile.h
js/src/wasm/WasmBinary.cpp
js/src/wasm/WasmBinary.h
js/src/wasm/WasmBinaryConstants.h
js/src/wasm/WasmBinaryFormat.cpp
js/src/wasm/WasmBinaryFormat.h
js/src/wasm/WasmBinaryIterator.h
js/src/wasm/WasmGenerator.h
js/src/wasm/WasmIonCompile.h
js/src/wasm/WasmTextToBinary.h
js/src/wasm/WasmTextUtils.cpp
js/src/wasm/WasmTypes.h
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -12,17 +12,16 @@
 #include "mozilla/MathAlgorithms.h"
 
 #include "jit/arm/Simulator-arm.h"
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/JitFrames.h"
 #include "jit/MacroAssembler.h"
 #include "jit/MoveEmitter.h"
-#include "wasm/WasmBinary.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace jit;
 
 using mozilla::Abs;
 using mozilla::BitwiseCast;
--- a/js/src/jsapi-tests/testWasmLEB128.cpp
+++ b/js/src/jsapi-tests/testWasmLEB128.cpp
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <stdlib.h>
 
 #include "jsapi-tests/tests.h"
 
-#include "wasm/WasmBinary.h"
+#include "wasm/WasmBinaryFormat.h"
 
 static bool WriteValidBytes(js::wasm::Encoder& encoder, bool* passed)
 {
     *passed = false;
     if (!encoder.empty())
         return true;
 
     // These remain the same under LEB128 unsigned encoding
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -355,17 +355,16 @@ UNIFIED_SOURCES += [
     'vm/UbiNodeShortestPaths.cpp',
     'vm/UnboxedObject.cpp',
     'vm/Unicode.cpp',
     'vm/Value.cpp',
     'vm/WeakMapPtr.cpp',
     'vm/Xdr.cpp',
     'wasm/AsmJS.cpp',
     'wasm/WasmBaselineCompile.cpp',
-    'wasm/WasmBinary.cpp',
     'wasm/WasmBinaryFormat.cpp',
     'wasm/WasmBinaryIterator.cpp',
     'wasm/WasmBinaryToAST.cpp',
     'wasm/WasmBinaryToExperimentalText.cpp',
     'wasm/WasmBinaryToText.cpp',
     'wasm/WasmCode.cpp',
     'wasm/WasmCompartment.cpp',
     'wasm/WasmCompile.cpp',
--- a/js/src/wasm/WasmBaselineCompile.h
+++ b/js/src/wasm/WasmBaselineCompile.h
@@ -14,17 +14,16 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef asmjs_wasm_baseline_compile_h
 #define asmjs_wasm_baseline_compile_h
 
-#include "wasm/WasmBinary.h"
 #include "wasm/WasmIonCompile.h"
 
 namespace js {
 namespace wasm {
 
 class FunctionGenerator;
 
 // Return true if BaselineCompileFunction can generate code for the
deleted file mode 100644
--- a/js/src/wasm/WasmBinary.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- *
- * Copyright 2016 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "wasm/WasmBinary.h"
-
-#include <stdarg.h>
-
-#include "jsprf.h"
-#include "wasm/WasmTypes.h"
-
-using namespace js;
-using namespace js::wasm;
-
-bool
-Decoder::fail(const char* msg, ...) {
-    va_list ap;
-    va_start(ap, msg);
-    UniqueChars str(JS_vsmprintf(msg, ap));
-    va_end(ap);
-    if (!str)
-        return false;
-
-    return fail(Move(str));
-}
-
-bool
-Decoder::fail(UniqueChars msg) {
-    MOZ_ASSERT(error_);
-    UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s", currentOffset(), msg.get()));
-    if (!strWithOffset)
-        return false;
-
-    *error_ = Move(strWithOffset);
-    return false;
-}
rename from js/src/wasm/WasmBinary.h
rename to js/src/wasm/WasmBinaryConstants.h
--- a/js/src/wasm/WasmBinary.h
+++ b/js/src/wasm/WasmBinaryConstants.h
@@ -19,75 +19,32 @@
 #ifndef wasm_binary_h
 #define wasm_binary_h
 
 #include "builtin/SIMD.h"
 
 namespace js {
 namespace wasm {
 
-// Because WebAssembly allows one to define the payload of a NaN value,
-// including the signal/quiet bit (highest order bit of payload), another
-// represenation of floating-point values is required: on some platforms (x86
-// without SSE2), passing a floating-point argument to a function call may use
-// the x87 stack, which has the side-effect of clearing the signal/quiet bit.
-// Because the signal/quiet bit must be preserved (by spec), we use the raw
-// punned integer representation of floating points instead, in function calls.
-//
-// When we leave the WebAssembly sandbox back to JS, NaNs are canonicalized, so
-// this isn't observable from JS.
-
-template<class T>
-class Raw
-{
-    typedef typename mozilla::FloatingPoint<T>::Bits Bits;
-    Bits value_;
-
-  public:
-    Raw() : value_(0) {}
-
-    explicit Raw(T value)
-      : value_(mozilla::BitwiseCast<Bits>(value))
-    {}
-
-    template<class U> MOZ_IMPLICIT Raw(U) = delete;
-
-    static Raw fromBits(Bits bits) { Raw r; r.value_ = bits; return r; }
-
-    Bits bits() const { return value_; }
-    T fp() const { return mozilla::BitwiseCast<T>(value_); }
-};
-
-using RawF64 = Raw<double>;
-using RawF32 = Raw<float>;
-
-// Telemetry sample values for the JS_AOT_USAGE key, indicating whether asm.js
-// or WebAssembly is used.
-
-enum class Telemetry {
-    ASMJS = 0,
-    WASM = 1
-};
-
 static const uint32_t MagicNumber        = 0x6d736100; // "\0asm"
 static const uint32_t EncodingVersion    = 0x0d;
 
 enum class SectionId {
-    UserDefined = 0,
-    Type        = 1,
-    Import      = 2,
-    Function    = 3,
-    Table       = 4,
-    Memory      = 5,
-    Global      = 6,
-    Export      = 7,
-    Start       = 8,
-    Elem        = 9,
-    Code        = 10,
-    Data        = 11
+    UserDefined                          = 0,
+    Type                                 = 1,
+    Import                               = 2,
+    Function                             = 3,
+    Table                                = 4,
+    Memory                               = 5,
+    Global                               = 6,
+    Export                               = 7,
+    Start                                = 8,
+    Elem                                 = 9,
+    Code                                 = 10,
+    Data                                 = 11
 };
 
 static const char NameSectionName[] = "name";
 
 enum class TypeCode
 {
     I32                                  = 0x7f,  // SLEB128(-0x01)
     I64                                  = 0x7e,  // SLEB128(-0x02)
@@ -470,650 +427,21 @@ enum class Expr : uint32_t // fix type s
     B32x4Constructor,
     B32x4Const,
 #undef _
 #undef OPCODE
 
     Limit
 };
 
-// The ExprType enum represents the type of a WebAssembly expression or return
-// value and may either be a value type or void. Soon, expression types will be
-// generalized to a list of ValType and this enum will go away, replaced,
-// wherever it is used, by a varU32 + list of ValType.
-
-enum class ExprType : uint32_t // fix type so we can cast from any u8 in decoder
-{
-    Void  = uint8_t(TypeCode::BlockVoid),
-
-    I32   = uint8_t(TypeCode::I32),
-    I64   = uint8_t(TypeCode::I64),
-    F32   = uint8_t(TypeCode::F32),
-    F64   = uint8_t(TypeCode::F64),
-
-    I8x16 = uint8_t(TypeCode::I8x16),
-    I16x8 = uint8_t(TypeCode::I16x8),
-    I32x4 = uint8_t(TypeCode::I32x4),
-    F32x4 = uint8_t(TypeCode::F32x4),
-    B8x16 = uint8_t(TypeCode::B8x16),
-    B16x8 = uint8_t(TypeCode::B16x8),
-    B32x4 = uint8_t(TypeCode::B32x4),
-
-    Limit = uint8_t(TypeCode::Limit)
-};
-
-typedef int8_t I8x16[16];
-typedef int16_t I16x8[8];
-typedef int32_t I32x4[4];
-typedef float F32x4[4];
-typedef Vector<uint8_t, 0, SystemAllocPolicy> Bytes;
-
-// The Encoder class appends bytes to the Bytes object it is given during
-// construction. The client is responsible for the Bytes's lifetime and must
-// keep the Bytes alive as long as the Encoder is used.
-
-class Encoder
-{
-    Bytes& bytes_;
-
-    template <class T>
-    MOZ_MUST_USE bool write(const T& v) {
-        return bytes_.append(reinterpret_cast<const uint8_t*>(&v), sizeof(T));
-    }
-
-    template <typename UInt>
-    MOZ_MUST_USE bool writeVarU(UInt i) {
-        do {
-            uint8_t byte = i & 0x7f;
-            i >>= 7;
-            if (i != 0)
-                byte |= 0x80;
-            if (!bytes_.append(byte))
-                return false;
-        } while (i != 0);
-        return true;
-    }
-
-    template <typename SInt>
-    MOZ_MUST_USE bool writeVarS(SInt i) {
-        bool done;
-        do {
-            uint8_t byte = i & 0x7f;
-            i >>= 7;
-            done = ((i == 0) && !(byte & 0x40)) || ((i == -1) && (byte & 0x40));
-            if (!done)
-                byte |= 0x80;
-            if (!bytes_.append(byte))
-                return false;
-        } while (!done);
-        return true;
-    }
-
-    void patchVarU32(size_t offset, uint32_t patchBits, uint32_t assertBits) {
-        do {
-            uint8_t assertByte = assertBits & 0x7f;
-            uint8_t patchByte = patchBits & 0x7f;
-            assertBits >>= 7;
-            patchBits >>= 7;
-            if (assertBits != 0) {
-                assertByte |= 0x80;
-                patchByte |= 0x80;
-            }
-            MOZ_ASSERT(assertByte == bytes_[offset]);
-            bytes_[offset] = patchByte;
-            offset++;
-        } while(assertBits != 0);
-    }
-
-    void patchFixedU7(size_t offset, uint8_t patchBits, uint8_t assertBits) {
-        MOZ_ASSERT(patchBits <= uint8_t(INT8_MAX));
-        patchFixedU8(offset, patchBits, assertBits);
-    }
-
-    void patchFixedU8(size_t offset, uint8_t patchBits, uint8_t assertBits) {
-        MOZ_ASSERT(bytes_[offset] == assertBits);
-        bytes_[offset] = patchBits;
-    }
-
-    uint32_t varU32ByteLength(size_t offset) const {
-        size_t start = offset;
-        while (bytes_[offset] & 0x80)
-            offset++;
-        return offset - start + 1;
-    }
-
-    static const size_t ExprLimit = 2 * UINT8_MAX - 1;
-
-  public:
-    explicit Encoder(Bytes& bytes)
-      : bytes_(bytes)
-    {
-        MOZ_ASSERT(empty());
-    }
-
-    size_t currentOffset() const { return bytes_.length(); }
-    bool empty() const { return currentOffset() == 0; }
-
-    // Fixed-size encoding operations simply copy the literal bytes (without
-    // attempting to align).
-
-    MOZ_MUST_USE bool writeFixedU7(uint8_t i) {
-        MOZ_ASSERT(i <= uint8_t(INT8_MAX));
-        return writeFixedU8(i);
-    }
-    MOZ_MUST_USE bool writeFixedU8(uint8_t i) {
-        return write<uint8_t>(i);
-    }
-    MOZ_MUST_USE bool writeFixedU32(uint32_t i) {
-        return write<uint32_t>(i);
-    }
-    MOZ_MUST_USE bool writeFixedF32(RawF32 f) {
-        return write<uint32_t>(f.bits());
-    }
-    MOZ_MUST_USE bool writeFixedF64(RawF64 d) {
-        return write<uint64_t>(d.bits());
-    }
-    MOZ_MUST_USE bool writeFixedI8x16(const I8x16& i8x16) {
-        return write<I8x16>(i8x16);
-    }
-    MOZ_MUST_USE bool writeFixedI16x8(const I16x8& i16x8) {
-        return write<I16x8>(i16x8);
-    }
-    MOZ_MUST_USE bool writeFixedI32x4(const I32x4& i32x4) {
-        return write<I32x4>(i32x4);
-    }
-    MOZ_MUST_USE bool writeFixedF32x4(const F32x4& f32x4) {
-        return write<F32x4>(f32x4);
-    }
-
-    // Variable-length encodings that all use LEB128.
-
-    MOZ_MUST_USE bool writeVarU32(uint32_t i) {
-        return writeVarU<uint32_t>(i);
-    }
-    MOZ_MUST_USE bool writeVarS32(int32_t i) {
-        return writeVarS<int32_t>(i);
-    }
-    MOZ_MUST_USE bool writeVarU64(uint64_t i) {
-        return writeVarU<uint64_t>(i);
-    }
-    MOZ_MUST_USE bool writeVarS64(int64_t i) {
-        return writeVarS<int64_t>(i);
-    }
-    MOZ_MUST_USE bool writeValType(ValType type) {
-        static_assert(size_t(TypeCode::Max) <= INT8_MAX, "fits");
-        MOZ_ASSERT(size_t(type) <= size_t(TypeCode::Max));
-        return writeFixedU8(uint8_t(type));
-    }
-    MOZ_MUST_USE bool writeBlockType(ExprType type) {
-        static_assert(size_t(TypeCode::Max) <= INT8_MAX, "fits");
-        MOZ_ASSERT(size_t(type) <= size_t(TypeCode::Max));
-        return writeFixedU8(uint8_t(type));
-    }
-    MOZ_MUST_USE bool writeExpr(Expr expr) {
-        static_assert(size_t(Expr::Limit) <= ExprLimit, "fits");
-        if (size_t(expr) < UINT8_MAX)
-            return writeFixedU8(uint8_t(expr));
-        return writeFixedU8(UINT8_MAX) &&
-               writeFixedU8(size_t(expr) - UINT8_MAX);
-    }
-
-    // Fixed-length encodings that allow back-patching.
-
-    MOZ_MUST_USE bool writePatchableFixedU7(size_t* offset) {
-        *offset = bytes_.length();
-        return writeFixedU8(UINT8_MAX);
-    }
-    void patchFixedU7(size_t offset, uint8_t patchBits) {
-        return patchFixedU7(offset, patchBits, UINT8_MAX);
-    }
-
-    // Variable-length encodings that allow back-patching.
-
-    MOZ_MUST_USE bool writePatchableVarU32(size_t* offset) {
-        *offset = bytes_.length();
-        return writeVarU32(UINT32_MAX);
-    }
-    void patchVarU32(size_t offset, uint32_t patchBits) {
-        return patchVarU32(offset, patchBits, UINT32_MAX);
-    }
-
-    // Byte ranges start with an LEB128 length followed by an arbitrary sequence
-    // of bytes. When used for strings, bytes are to be interpreted as utf8.
-
-    MOZ_MUST_USE bool writeBytes(const void* bytes, uint32_t numBytes) {
-        return writeVarU32(numBytes) &&
-               bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
-    }
-
-    // A "section" is a contiguous range of bytes that stores its own size so
-    // that it may be trivially skipped without examining the contents. Sections
-    // require backpatching since the size of the section is only known at the
-    // end while the size's varU32 must be stored at the beginning. Immediately
-    // after the section length is the string id of the section.
-
-    MOZ_MUST_USE bool startSection(SectionId id, size_t* offset) {
-        MOZ_ASSERT(id != SectionId::UserDefined); // not supported yet
-
-        return writeVarU32(uint32_t(id)) &&
-               writePatchableVarU32(offset);
-    }
-    void finishSection(size_t offset) {
-        return patchVarU32(offset, bytes_.length() - offset - varU32ByteLength(offset));
-    }
-};
-
-// The Decoder class decodes the bytes in the range it is given during
-// construction. The client is responsible for keeping the byte range alive as
-// long as the Decoder is used.
-
-class Decoder
-{
-    const uint8_t* const beg_;
-    const uint8_t* const end_;
-    const uint8_t* cur_;
-    UniqueChars* error_;
-
-    template <class T>
-    MOZ_MUST_USE bool read(T* out) {
-        if (bytesRemain() < sizeof(T))
-            return false;
-        memcpy((void*)out, cur_, sizeof(T));
-        cur_ += sizeof(T);
-        return true;
-    }
-
-    template <class T>
-    T uncheckedRead() {
-        MOZ_ASSERT(bytesRemain() >= sizeof(T));
-        T ret;
-        memcpy(&ret, cur_, sizeof(T));
-        cur_ += sizeof(T);
-        return ret;
-    }
-
-    template <class T>
-    void uncheckedRead(T* ret) {
-        MOZ_ASSERT(bytesRemain() >= sizeof(T));
-        memcpy(ret, cur_, sizeof(T));
-        cur_ += sizeof(T);
-    }
-
-    template <typename UInt>
-    MOZ_MUST_USE bool readVarU(UInt* out) {
-        const unsigned numBits = sizeof(UInt) * CHAR_BIT;
-        const unsigned remainderBits = numBits % 7;
-        const unsigned numBitsInSevens = numBits - remainderBits;
-        UInt u = 0;
-        uint8_t byte;
-        UInt shift = 0;
-        do {
-            if (!readFixedU8(&byte))
-                return false;
-            if (!(byte & 0x80)) {
-                *out = u | UInt(byte) << shift;
-                return true;
-            }
-            u |= UInt(byte & 0x7F) << shift;
-            shift += 7;
-        } while (shift != numBitsInSevens);
-        if (!readFixedU8(&byte) || (byte & (unsigned(-1) << remainderBits)))
-            return false;
-        *out = u | (UInt(byte) << numBitsInSevens);
-        return true;
-    }
-
-    template <typename SInt>
-    MOZ_MUST_USE bool readVarS(SInt* out) {
-        const unsigned numBits = sizeof(SInt) * CHAR_BIT;
-        const unsigned remainderBits = numBits % 7;
-        const unsigned numBitsInSevens = numBits - remainderBits;
-        SInt s = 0;
-        uint8_t byte;
-        unsigned shift = 0;
-        do {
-            if (!readFixedU8(&byte))
-                return false;
-            s |= SInt(byte & 0x7f) << shift;
-            shift += 7;
-            if (!(byte & 0x80)) {
-                if (byte & 0x40)
-                    s |= SInt(-1) << shift;
-                *out = s;
-                return true;
-            }
-        } while (shift < numBitsInSevens);
-        if (!remainderBits || !readFixedU8(&byte) || (byte & 0x80))
-            return false;
-        uint8_t mask = 0x7f & (uint8_t(-1) << remainderBits);
-        if ((byte & mask) != ((byte & (1 << (remainderBits - 1))) ? mask : 0))
-            return false;
-        *out = s | SInt(byte) << shift;
-        return true;
-    }
-
-    static const size_t ExprLimit = 2 * UINT8_MAX - 1;
+// Telemetry sample values for the JS_AOT_USAGE key, indicating whether asm.js
+// or WebAssembly is used.
 
-  public:
-    Decoder(const uint8_t* begin, const uint8_t* end, UniqueChars* error)
-      : beg_(begin),
-        end_(end),
-        cur_(begin),
-        error_(error)
-    {
-        MOZ_ASSERT(begin <= end);
-    }
-    explicit Decoder(const Bytes& bytes, UniqueChars* error = nullptr)
-      : beg_(bytes.begin()),
-        end_(bytes.end()),
-        cur_(bytes.begin()),
-        error_(error)
-    {}
-
-    bool fail(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
-    bool fail(UniqueChars msg);
-    void clearError() {
-        if (error_)
-            error_->reset();
-    }
-
-    bool done() const {
-        MOZ_ASSERT(cur_ <= end_);
-        return cur_ == end_;
-    }
-
-    size_t bytesRemain() const {
-        MOZ_ASSERT(end_ >= cur_);
-        return size_t(end_ - cur_);
-    }
-    const uint8_t* currentPosition() const {
-        return cur_;
-    }
-    size_t currentOffset() const {
-        return cur_ - beg_;
-    }
-    const uint8_t* begin() const {
-        return beg_;
-    }
-
-    // Fixed-size encoding operations simply copy the literal bytes (without
-    // attempting to align).
-
-    MOZ_MUST_USE bool readFixedU8(uint8_t* i) {
-        return read<uint8_t>(i);
-    }
-    MOZ_MUST_USE bool readFixedU32(uint32_t* u) {
-        return read<uint32_t>(u);
-    }
-    MOZ_MUST_USE bool readFixedF32(RawF32* f) {
-        uint32_t u;
-        if (!read<uint32_t>(&u))
-            return false;
-        *f = RawF32::fromBits(u);
-        return true;
-    }
-    MOZ_MUST_USE bool readFixedF64(RawF64* d) {
-        uint64_t u;
-        if (!read<uint64_t>(&u))
-            return false;
-        *d = RawF64::fromBits(u);
-        return true;
-    }
-    MOZ_MUST_USE bool readFixedI8x16(I8x16* i8x16) {
-        return read<I8x16>(i8x16);
-    }
-    MOZ_MUST_USE bool readFixedI16x8(I16x8* i16x8) {
-        return read<I16x8>(i16x8);
-    }
-    MOZ_MUST_USE bool readFixedI32x4(I32x4* i32x4) {
-        return read<I32x4>(i32x4);
-    }
-    MOZ_MUST_USE bool readFixedF32x4(F32x4* f32x4) {
-        return read<F32x4>(f32x4);
-    }
-
-    // Variable-length encodings that all use LEB128.
-
-    MOZ_MUST_USE bool readVarU32(uint32_t* out) {
-        return readVarU<uint32_t>(out);
-    }
-    MOZ_MUST_USE bool readVarS32(int32_t* out) {
-        return readVarS<int32_t>(out);
-    }
-    MOZ_MUST_USE bool readVarU64(uint64_t* out) {
-        return readVarU<uint64_t>(out);
-    }
-    MOZ_MUST_USE bool readVarS64(int64_t* out) {
-        return readVarS<int64_t>(out);
-    }
-    MOZ_MUST_USE bool readValType(ValType* type) {
-        static_assert(uint8_t(TypeCode::Max) <= INT8_MAX, "fits");
-        uint8_t u8;
-        if (!readFixedU8(&u8))
-            return false;
-        *type = (ValType)u8;
-        return true;
-    }
-    MOZ_MUST_USE bool readBlockType(ExprType* type) {
-        static_assert(size_t(TypeCode::Max) <= INT8_MAX, "fits");
-        uint8_t u8;
-        if (!readFixedU8(&u8))
-            return false;
-        *type = (ExprType)u8;
-        return true;
-    }
-    MOZ_MUST_USE bool readExpr(Expr* expr) {
-        static_assert(size_t(Expr::Limit) <= ExprLimit, "fits");
-        uint8_t u8;
-        if (!readFixedU8(&u8))
-            return false;
-        if (u8 != UINT8_MAX) {
-            *expr = Expr(u8);
-            return true;
-        }
-        if (!readFixedU8(&u8))
-            return false;
-        if (u8 == UINT8_MAX)
-            return false;
-        *expr = Expr(uint16_t(u8) + UINT8_MAX);
-        return true;
-    }
-
-    // See writeBytes comment.
-
-    MOZ_MUST_USE bool readBytes(uint32_t numBytes, const uint8_t** bytes = nullptr) {
-        if (bytes)
-            *bytes = cur_;
-        if (bytesRemain() < numBytes)
-            return false;
-        cur_ += numBytes;
-        return true;
-    }
-
-    // See "section" description in Encoder.
-
-    static const uint32_t NotStarted = UINT32_MAX;
-
-    MOZ_MUST_USE bool startSection(SectionId id,
-                                   uint32_t* startOffset,
-                                   uint32_t* size,
-                                   const char* sectionName)
-    {
-        const uint8_t* const before = cur_;
-        const uint8_t* beforeId = before;
-        uint32_t idValue;
-        if (!readVarU32(&idValue))
-            goto backup;
-        while (idValue != uint32_t(id)) {
-            if (idValue != uint32_t(SectionId::UserDefined))
-                goto backup;
-            // Rewind to the section id since skipUserDefinedSection expects it.
-            cur_ = beforeId;
-            if (!skipUserDefinedSection())
-                return false;
-            beforeId = cur_;
-            if (!readVarU32(&idValue))
-                goto backup;
-        }
-        if (!readVarU32(size))
-            goto fail;
-        if (bytesRemain() < *size)
-            goto fail;
-        *startOffset = cur_ - beg_;
-        return true;
-      backup:
-        cur_ = before;
-        *startOffset = NotStarted;
-        return true;
-      fail:
-        return fail("failed to start %s section", sectionName);
-    }
-    MOZ_MUST_USE bool finishSection(uint32_t startOffset, uint32_t size,
-                                    const char* sectionName)
-    {
-        if (size != (cur_ - beg_) - startOffset)
-            return fail("byte size mismatch in %s section", sectionName);
-        return true;
-    }
-
-    // "User sections" do not cause validation errors unless the error is in
-    // the user-defined section header itself.
-
-    MOZ_MUST_USE bool startUserDefinedSection(const char* expectedId,
-                                              size_t expectedIdSize,
-                                              uint32_t* sectionStart,
-                                              uint32_t* sectionSize)
-    {
-        const uint8_t* const before = cur_;
-        while (true) {
-            if (!startSection(SectionId::UserDefined, sectionStart, sectionSize, "user-defined"))
-                return false;
-            if (*sectionStart == NotStarted) {
-                cur_ = before;
-                return true;
-            }
-            uint32_t idSize;
-            if (!readVarU32(&idSize))
-                goto fail;
-            if (idSize > bytesRemain() || currentOffset() + idSize > *sectionStart + *sectionSize)
-                goto fail;
-            if (expectedId && (expectedIdSize != idSize || !!memcmp(cur_, expectedId, idSize))) {
-                finishUserDefinedSection(*sectionStart, *sectionSize);
-                continue;
-            }
-            cur_ += idSize;
-            return true;
-        }
-        MOZ_CRASH("unreachable");
-      fail:
-        return fail("failed to start user-defined section");
-    }
-    template <size_t IdSizeWith0>
-    MOZ_MUST_USE bool startUserDefinedSection(const char (&id)[IdSizeWith0],
-                                              uint32_t* sectionStart,
-                                              uint32_t* sectionSize)
-    {
-        MOZ_ASSERT(id[IdSizeWith0 - 1] == '\0');
-        return startUserDefinedSection(id, IdSizeWith0 - 1, sectionStart, sectionSize);
-    }
-    void finishUserDefinedSection(uint32_t sectionStart, uint32_t sectionSize) {
-        MOZ_ASSERT(cur_ >= beg_);
-        MOZ_ASSERT(cur_ <= end_);
-        cur_ = (beg_ + sectionStart) + sectionSize;
-        MOZ_ASSERT(cur_ <= end_);
-        clearError();
-    }
-    MOZ_MUST_USE bool skipUserDefinedSection() {
-        uint32_t sectionStart, sectionSize;
-        if (!startUserDefinedSection(nullptr, 0, &sectionStart, &sectionSize))
-            return false;
-        if (sectionStart == NotStarted)
-            return fail("expected user-defined section");
-        finishUserDefinedSection(sectionStart, sectionSize);
-        return true;
-    }
-
-    // The infallible "unchecked" decoding functions can be used when we are
-    // sure that the bytes are well-formed (by construction or due to previous
-    // validation).
-
-    uint8_t uncheckedReadFixedU8() {
-        return uncheckedRead<uint8_t>();
-    }
-    uint32_t uncheckedReadFixedU32() {
-        return uncheckedRead<uint32_t>();
-    }
-    RawF32 uncheckedReadFixedF32() {
-        return RawF32::fromBits(uncheckedRead<uint32_t>());
-    }
-    RawF64 uncheckedReadFixedF64() {
-        return RawF64::fromBits(uncheckedRead<uint64_t>());
-    }
-    template <typename UInt>
-    UInt uncheckedReadVarU() {
-        static const unsigned numBits = sizeof(UInt) * CHAR_BIT;
-        static const unsigned remainderBits = numBits % 7;
-        static const unsigned numBitsInSevens = numBits - remainderBits;
-        UInt decoded = 0;
-        uint32_t shift = 0;
-        do {
-            uint8_t byte = *cur_++;
-            if (!(byte & 0x80))
-                return decoded | (UInt(byte) << shift);
-            decoded |= UInt(byte & 0x7f) << shift;
-            shift += 7;
-        } while (shift != numBitsInSevens);
-        uint8_t byte = *cur_++;
-        MOZ_ASSERT(!(byte & 0xf0));
-        return decoded | (UInt(byte) << numBitsInSevens);
-    }
-    uint32_t uncheckedReadVarU32() {
-        return uncheckedReadVarU<uint32_t>();
-    }
-    int32_t uncheckedReadVarS32() {
-        int32_t i32 = 0;
-        MOZ_ALWAYS_TRUE(readVarS32(&i32));
-        return i32;
-    }
-    uint64_t uncheckedReadVarU64() {
-        return uncheckedReadVarU<uint64_t>();
-    }
-    int64_t uncheckedReadVarS64() {
-        int64_t i64 = 0;
-        MOZ_ALWAYS_TRUE(readVarS64(&i64));
-        return i64;
-    }
-    ValType uncheckedReadValType() {
-        return (ValType)uncheckedReadFixedU8();
-    }
-    Expr uncheckedReadExpr() {
-        static_assert(size_t(Expr::Limit) <= ExprLimit, "fits");
-        uint8_t u8 = uncheckedReadFixedU8();
-        return u8 != UINT8_MAX
-               ? Expr(u8)
-               : Expr(uncheckedReadFixedU8() + UINT8_MAX);
-    }
-    void uncheckedReadFixedI8x16(I8x16* i8x16) {
-        struct T { I8x16 v; };
-        T t = uncheckedRead<T>();
-        memcpy(i8x16, &t, sizeof(t));
-    }
-    void uncheckedReadFixedI16x8(I16x8* i16x8) {
-        struct T { I16x8 v; };
-        T t = uncheckedRead<T>();
-        memcpy(i16x8, &t, sizeof(t));
-    }
-    void uncheckedReadFixedI32x4(I32x4* i32x4) {
-        struct T { I32x4 v; };
-        T t = uncheckedRead<T>();
-        memcpy(i32x4, &t, sizeof(t));
-    }
-    void uncheckedReadFixedF32x4(F32x4* f32x4) {
-        struct T { F32x4 v; };
-        T t = uncheckedRead<T>();
-        memcpy(f32x4, &t, sizeof(t));
-    }
+enum class Telemetry
+{
+    ASMJS = 0,
+    WASM = 1
 };
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_binary_h
--- a/js/src/wasm/WasmBinaryFormat.cpp
+++ b/js/src/wasm/WasmBinaryFormat.cpp
@@ -326,8 +326,33 @@ wasm::DecodeUnknownSections(Decoder& d)
 {
     while (!d.done()) {
         if (!d.skipUserDefinedSection())
             return false;
     }
 
     return true;
 }
+
+bool
+Decoder::fail(const char* msg, ...)
+{
+    va_list ap;
+    va_start(ap, msg);
+    UniqueChars str(JS_vsmprintf(msg, ap));
+    va_end(ap);
+    if (!str)
+        return false;
+
+    return fail(Move(str));
+}
+
+bool
+Decoder::fail(UniqueChars msg)
+{
+    MOZ_ASSERT(error_);
+    UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s", currentOffset(), msg.get()));
+    if (!strWithOffset)
+        return false;
+
+    *error_ = Move(strWithOffset);
+    return false;
+}
--- a/js/src/wasm/WasmBinaryFormat.h
+++ b/js/src/wasm/WasmBinaryFormat.h
@@ -14,22 +14,628 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_binary_format_h
 #define wasm_binary_format_h
 
-#include "wasm/WasmBinary.h"
 #include "wasm/WasmTypes.h"
 
 namespace js {
 namespace wasm {
 
+// The Encoder class appends bytes to the Bytes object it is given during
+// construction. The client is responsible for the Bytes's lifetime and must
+// keep the Bytes alive as long as the Encoder is used.
+
+class Encoder
+{
+    Bytes& bytes_;
+
+    template <class T>
+    MOZ_MUST_USE bool write(const T& v) {
+        return bytes_.append(reinterpret_cast<const uint8_t*>(&v), sizeof(T));
+    }
+
+    template <typename UInt>
+    MOZ_MUST_USE bool writeVarU(UInt i) {
+        do {
+            uint8_t byte = i & 0x7f;
+            i >>= 7;
+            if (i != 0)
+                byte |= 0x80;
+            if (!bytes_.append(byte))
+                return false;
+        } while (i != 0);
+        return true;
+    }
+
+    template <typename SInt>
+    MOZ_MUST_USE bool writeVarS(SInt i) {
+        bool done;
+        do {
+            uint8_t byte = i & 0x7f;
+            i >>= 7;
+            done = ((i == 0) && !(byte & 0x40)) || ((i == -1) && (byte & 0x40));
+            if (!done)
+                byte |= 0x80;
+            if (!bytes_.append(byte))
+                return false;
+        } while (!done);
+        return true;
+    }
+
+    void patchVarU32(size_t offset, uint32_t patchBits, uint32_t assertBits) {
+        do {
+            uint8_t assertByte = assertBits & 0x7f;
+            uint8_t patchByte = patchBits & 0x7f;
+            assertBits >>= 7;
+            patchBits >>= 7;
+            if (assertBits != 0) {
+                assertByte |= 0x80;
+                patchByte |= 0x80;
+            }
+            MOZ_ASSERT(assertByte == bytes_[offset]);
+            bytes_[offset] = patchByte;
+            offset++;
+        } while(assertBits != 0);
+    }
+
+    void patchFixedU7(size_t offset, uint8_t patchBits, uint8_t assertBits) {
+        MOZ_ASSERT(patchBits <= uint8_t(INT8_MAX));
+        patchFixedU8(offset, patchBits, assertBits);
+    }
+
+    void patchFixedU8(size_t offset, uint8_t patchBits, uint8_t assertBits) {
+        MOZ_ASSERT(bytes_[offset] == assertBits);
+        bytes_[offset] = patchBits;
+    }
+
+    uint32_t varU32ByteLength(size_t offset) const {
+        size_t start = offset;
+        while (bytes_[offset] & 0x80)
+            offset++;
+        return offset - start + 1;
+    }
+
+    static const size_t ExprLimit = 2 * UINT8_MAX - 1;
+
+  public:
+    explicit Encoder(Bytes& bytes)
+      : bytes_(bytes)
+    {
+        MOZ_ASSERT(empty());
+    }
+
+    size_t currentOffset() const { return bytes_.length(); }
+    bool empty() const { return currentOffset() == 0; }
+
+    // Fixed-size encoding operations simply copy the literal bytes (without
+    // attempting to align).
+
+    MOZ_MUST_USE bool writeFixedU7(uint8_t i) {
+        MOZ_ASSERT(i <= uint8_t(INT8_MAX));
+        return writeFixedU8(i);
+    }
+    MOZ_MUST_USE bool writeFixedU8(uint8_t i) {
+        return write<uint8_t>(i);
+    }
+    MOZ_MUST_USE bool writeFixedU32(uint32_t i) {
+        return write<uint32_t>(i);
+    }
+    MOZ_MUST_USE bool writeFixedF32(RawF32 f) {
+        return write<uint32_t>(f.bits());
+    }
+    MOZ_MUST_USE bool writeFixedF64(RawF64 d) {
+        return write<uint64_t>(d.bits());
+    }
+    MOZ_MUST_USE bool writeFixedI8x16(const I8x16& i8x16) {
+        return write<I8x16>(i8x16);
+    }
+    MOZ_MUST_USE bool writeFixedI16x8(const I16x8& i16x8) {
+        return write<I16x8>(i16x8);
+    }
+    MOZ_MUST_USE bool writeFixedI32x4(const I32x4& i32x4) {
+        return write<I32x4>(i32x4);
+    }
+    MOZ_MUST_USE bool writeFixedF32x4(const F32x4& f32x4) {
+        return write<F32x4>(f32x4);
+    }
+
+    // Variable-length encodings that all use LEB128.
+
+    MOZ_MUST_USE bool writeVarU32(uint32_t i) {
+        return writeVarU<uint32_t>(i);
+    }
+    MOZ_MUST_USE bool writeVarS32(int32_t i) {
+        return writeVarS<int32_t>(i);
+    }
+    MOZ_MUST_USE bool writeVarU64(uint64_t i) {
+        return writeVarU<uint64_t>(i);
+    }
+    MOZ_MUST_USE bool writeVarS64(int64_t i) {
+        return writeVarS<int64_t>(i);
+    }
+    MOZ_MUST_USE bool writeValType(ValType type) {
+        static_assert(size_t(TypeCode::Max) <= INT8_MAX, "fits");
+        MOZ_ASSERT(size_t(type) <= size_t(TypeCode::Max));
+        return writeFixedU8(uint8_t(type));
+    }
+    MOZ_MUST_USE bool writeBlockType(ExprType type) {
+        static_assert(size_t(TypeCode::Max) <= INT8_MAX, "fits");
+        MOZ_ASSERT(size_t(type) <= size_t(TypeCode::Max));
+        return writeFixedU8(uint8_t(type));
+    }
+    MOZ_MUST_USE bool writeExpr(Expr expr) {
+        static_assert(size_t(Expr::Limit) <= ExprLimit, "fits");
+        if (size_t(expr) < UINT8_MAX)
+            return writeFixedU8(uint8_t(expr));
+        return writeFixedU8(UINT8_MAX) &&
+               writeFixedU8(size_t(expr) - UINT8_MAX);
+    }
+
+    // Fixed-length encodings that allow back-patching.
+
+    MOZ_MUST_USE bool writePatchableFixedU7(size_t* offset) {
+        *offset = bytes_.length();
+        return writeFixedU8(UINT8_MAX);
+    }
+    void patchFixedU7(size_t offset, uint8_t patchBits) {
+        return patchFixedU7(offset, patchBits, UINT8_MAX);
+    }
+
+    // Variable-length encodings that allow back-patching.
+
+    MOZ_MUST_USE bool writePatchableVarU32(size_t* offset) {
+        *offset = bytes_.length();
+        return writeVarU32(UINT32_MAX);
+    }
+    void patchVarU32(size_t offset, uint32_t patchBits) {
+        return patchVarU32(offset, patchBits, UINT32_MAX);
+    }
+
+    // Byte ranges start with an LEB128 length followed by an arbitrary sequence
+    // of bytes. When used for strings, bytes are to be interpreted as utf8.
+
+    MOZ_MUST_USE bool writeBytes(const void* bytes, uint32_t numBytes) {
+        return writeVarU32(numBytes) &&
+               bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
+    }
+
+    // A "section" is a contiguous range of bytes that stores its own size so
+    // that it may be trivially skipped without examining the contents. Sections
+    // require backpatching since the size of the section is only known at the
+    // end while the size's varU32 must be stored at the beginning. Immediately
+    // after the section length is the string id of the section.
+
+    MOZ_MUST_USE bool startSection(SectionId id, size_t* offset) {
+        MOZ_ASSERT(id != SectionId::UserDefined); // not supported yet
+
+        return writeVarU32(uint32_t(id)) &&
+               writePatchableVarU32(offset);
+    }
+    void finishSection(size_t offset) {
+        return patchVarU32(offset, bytes_.length() - offset - varU32ByteLength(offset));
+    }
+};
+
+// The Decoder class decodes the bytes in the range it is given during
+// construction. The client is responsible for keeping the byte range alive as
+// long as the Decoder is used.
+
+class Decoder
+{
+    const uint8_t* const beg_;
+    const uint8_t* const end_;
+    const uint8_t* cur_;
+    UniqueChars* error_;
+
+    template <class T>
+    MOZ_MUST_USE bool read(T* out) {
+        if (bytesRemain() < sizeof(T))
+            return false;
+        memcpy((void*)out, cur_, sizeof(T));
+        cur_ += sizeof(T);
+        return true;
+    }
+
+    template <class T>
+    T uncheckedRead() {
+        MOZ_ASSERT(bytesRemain() >= sizeof(T));
+        T ret;
+        memcpy(&ret, cur_, sizeof(T));
+        cur_ += sizeof(T);
+        return ret;
+    }
+
+    template <class T>
+    void uncheckedRead(T* ret) {
+        MOZ_ASSERT(bytesRemain() >= sizeof(T));
+        memcpy(ret, cur_, sizeof(T));
+        cur_ += sizeof(T);
+    }
+
+    template <typename UInt>
+    MOZ_MUST_USE bool readVarU(UInt* out) {
+        const unsigned numBits = sizeof(UInt) * CHAR_BIT;
+        const unsigned remainderBits = numBits % 7;
+        const unsigned numBitsInSevens = numBits - remainderBits;
+        UInt u = 0;
+        uint8_t byte;
+        UInt shift = 0;
+        do {
+            if (!readFixedU8(&byte))
+                return false;
+            if (!(byte & 0x80)) {
+                *out = u | UInt(byte) << shift;
+                return true;
+            }
+            u |= UInt(byte & 0x7F) << shift;
+            shift += 7;
+        } while (shift != numBitsInSevens);
+        if (!readFixedU8(&byte) || (byte & (unsigned(-1) << remainderBits)))
+            return false;
+        *out = u | (UInt(byte) << numBitsInSevens);
+        return true;
+    }
+
+    template <typename SInt>
+    MOZ_MUST_USE bool readVarS(SInt* out) {
+        const unsigned numBits = sizeof(SInt) * CHAR_BIT;
+        const unsigned remainderBits = numBits % 7;
+        const unsigned numBitsInSevens = numBits - remainderBits;
+        SInt s = 0;
+        uint8_t byte;
+        unsigned shift = 0;
+        do {
+            if (!readFixedU8(&byte))
+                return false;
+            s |= SInt(byte & 0x7f) << shift;
+            shift += 7;
+            if (!(byte & 0x80)) {
+                if (byte & 0x40)
+                    s |= SInt(-1) << shift;
+                *out = s;
+                return true;
+            }
+        } while (shift < numBitsInSevens);
+        if (!remainderBits || !readFixedU8(&byte) || (byte & 0x80))
+            return false;
+        uint8_t mask = 0x7f & (uint8_t(-1) << remainderBits);
+        if ((byte & mask) != ((byte & (1 << (remainderBits - 1))) ? mask : 0))
+            return false;
+        *out = s | SInt(byte) << shift;
+        return true;
+    }
+
+    static const size_t ExprLimit = 2 * UINT8_MAX - 1;
+
+  public:
+    Decoder(const uint8_t* begin, const uint8_t* end, UniqueChars* error)
+      : beg_(begin),
+        end_(end),
+        cur_(begin),
+        error_(error)
+    {
+        MOZ_ASSERT(begin <= end);
+    }
+    explicit Decoder(const Bytes& bytes, UniqueChars* error = nullptr)
+      : beg_(bytes.begin()),
+        end_(bytes.end()),
+        cur_(bytes.begin()),
+        error_(error)
+    {}
+
+    bool fail(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
+    bool fail(UniqueChars msg);
+    void clearError() {
+        if (error_)
+            error_->reset();
+    }
+
+    bool done() const {
+        MOZ_ASSERT(cur_ <= end_);
+        return cur_ == end_;
+    }
+
+    size_t bytesRemain() const {
+        MOZ_ASSERT(end_ >= cur_);
+        return size_t(end_ - cur_);
+    }
+    const uint8_t* currentPosition() const {
+        return cur_;
+    }
+    size_t currentOffset() const {
+        return cur_ - beg_;
+    }
+    const uint8_t* begin() const {
+        return beg_;
+    }
+
+    // Fixed-size encoding operations simply copy the literal bytes (without
+    // attempting to align).
+
+    MOZ_MUST_USE bool readFixedU8(uint8_t* i) {
+        return read<uint8_t>(i);
+    }
+    MOZ_MUST_USE bool readFixedU32(uint32_t* u) {
+        return read<uint32_t>(u);
+    }
+    MOZ_MUST_USE bool readFixedF32(RawF32* f) {
+        uint32_t u;
+        if (!read<uint32_t>(&u))
+            return false;
+        *f = RawF32::fromBits(u);
+        return true;
+    }
+    MOZ_MUST_USE bool readFixedF64(RawF64* d) {
+        uint64_t u;
+        if (!read<uint64_t>(&u))
+            return false;
+        *d = RawF64::fromBits(u);
+        return true;
+    }
+    MOZ_MUST_USE bool readFixedI8x16(I8x16* i8x16) {
+        return read<I8x16>(i8x16);
+    }
+    MOZ_MUST_USE bool readFixedI16x8(I16x8* i16x8) {
+        return read<I16x8>(i16x8);
+    }
+    MOZ_MUST_USE bool readFixedI32x4(I32x4* i32x4) {
+        return read<I32x4>(i32x4);
+    }
+    MOZ_MUST_USE bool readFixedF32x4(F32x4* f32x4) {
+        return read<F32x4>(f32x4);
+    }
+
+    // Variable-length encodings that all use LEB128.
+
+    MOZ_MUST_USE bool readVarU32(uint32_t* out) {
+        return readVarU<uint32_t>(out);
+    }
+    MOZ_MUST_USE bool readVarS32(int32_t* out) {
+        return readVarS<int32_t>(out);
+    }
+    MOZ_MUST_USE bool readVarU64(uint64_t* out) {
+        return readVarU<uint64_t>(out);
+    }
+    MOZ_MUST_USE bool readVarS64(int64_t* out) {
+        return readVarS<int64_t>(out);
+    }
+    MOZ_MUST_USE bool readValType(ValType* type) {
+        static_assert(uint8_t(TypeCode::Max) <= INT8_MAX, "fits");
+        uint8_t u8;
+        if (!readFixedU8(&u8))
+            return false;
+        *type = (ValType)u8;
+        return true;
+    }
+    MOZ_MUST_USE bool readBlockType(ExprType* type) {
+        static_assert(size_t(TypeCode::Max) <= INT8_MAX, "fits");
+        uint8_t u8;
+        if (!readFixedU8(&u8))
+            return false;
+        *type = (ExprType)u8;
+        return true;
+    }
+    MOZ_MUST_USE bool readExpr(Expr* expr) {
+        static_assert(size_t(Expr::Limit) <= ExprLimit, "fits");
+        uint8_t u8;
+        if (!readFixedU8(&u8))
+            return false;
+        if (u8 != UINT8_MAX) {
+            *expr = Expr(u8);
+            return true;
+        }
+        if (!readFixedU8(&u8))
+            return false;
+        if (u8 == UINT8_MAX)
+            return false;
+        *expr = Expr(uint16_t(u8) + UINT8_MAX);
+        return true;
+    }
+
+    // See writeBytes comment.
+
+    MOZ_MUST_USE bool readBytes(uint32_t numBytes, const uint8_t** bytes = nullptr) {
+        if (bytes)
+            *bytes = cur_;
+        if (bytesRemain() < numBytes)
+            return false;
+        cur_ += numBytes;
+        return true;
+    }
+
+    // See "section" description in Encoder.
+
+    static const uint32_t NotStarted = UINT32_MAX;
+
+    MOZ_MUST_USE bool startSection(SectionId id,
+                                   uint32_t* startOffset,
+                                   uint32_t* size,
+                                   const char* sectionName)
+    {
+        const uint8_t* const before = cur_;
+        const uint8_t* beforeId = before;
+        uint32_t idValue;
+        if (!readVarU32(&idValue))
+            goto backup;
+        while (idValue != uint32_t(id)) {
+            if (idValue != uint32_t(SectionId::UserDefined))
+                goto backup;
+            // Rewind to the section id since skipUserDefinedSection expects it.
+            cur_ = beforeId;
+            if (!skipUserDefinedSection())
+                return false;
+            beforeId = cur_;
+            if (!readVarU32(&idValue))
+                goto backup;
+        }
+        if (!readVarU32(size))
+            goto fail;
+        if (bytesRemain() < *size)
+            goto fail;
+        *startOffset = cur_ - beg_;
+        return true;
+      backup:
+        cur_ = before;
+        *startOffset = NotStarted;
+        return true;
+      fail:
+        return fail("failed to start %s section", sectionName);
+    }
+    MOZ_MUST_USE bool finishSection(uint32_t startOffset, uint32_t size,
+                                    const char* sectionName)
+    {
+        if (size != (cur_ - beg_) - startOffset)
+            return fail("byte size mismatch in %s section", sectionName);
+        return true;
+    }
+
+    // "User sections" do not cause validation errors unless the error is in
+    // the user-defined section header itself.
+
+    MOZ_MUST_USE bool startUserDefinedSection(const char* expectedId,
+                                              size_t expectedIdSize,
+                                              uint32_t* sectionStart,
+                                              uint32_t* sectionSize)
+    {
+        const uint8_t* const before = cur_;
+        while (true) {
+            if (!startSection(SectionId::UserDefined, sectionStart, sectionSize, "user-defined"))
+                return false;
+            if (*sectionStart == NotStarted) {
+                cur_ = before;
+                return true;
+            }
+            uint32_t idSize;
+            if (!readVarU32(&idSize))
+                goto fail;
+            if (idSize > bytesRemain() || currentOffset() + idSize > *sectionStart + *sectionSize)
+                goto fail;
+            if (expectedId && (expectedIdSize != idSize || !!memcmp(cur_, expectedId, idSize))) {
+                finishUserDefinedSection(*sectionStart, *sectionSize);
+                continue;
+            }
+            cur_ += idSize;
+            return true;
+        }
+        MOZ_CRASH("unreachable");
+      fail:
+        return fail("failed to start user-defined section");
+    }
+    template <size_t IdSizeWith0>
+    MOZ_MUST_USE bool startUserDefinedSection(const char (&id)[IdSizeWith0],
+                                              uint32_t* sectionStart,
+                                              uint32_t* sectionSize)
+    {
+        MOZ_ASSERT(id[IdSizeWith0 - 1] == '\0');
+        return startUserDefinedSection(id, IdSizeWith0 - 1, sectionStart, sectionSize);
+    }
+    void finishUserDefinedSection(uint32_t sectionStart, uint32_t sectionSize) {
+        MOZ_ASSERT(cur_ >= beg_);
+        MOZ_ASSERT(cur_ <= end_);
+        cur_ = (beg_ + sectionStart) + sectionSize;
+        MOZ_ASSERT(cur_ <= end_);
+        clearError();
+    }
+    MOZ_MUST_USE bool skipUserDefinedSection() {
+        uint32_t sectionStart, sectionSize;
+        if (!startUserDefinedSection(nullptr, 0, &sectionStart, &sectionSize))
+            return false;
+        if (sectionStart == NotStarted)
+            return fail("expected user-defined section");
+        finishUserDefinedSection(sectionStart, sectionSize);
+        return true;
+    }
+
+    // The infallible "unchecked" decoding functions can be used when we are
+    // sure that the bytes are well-formed (by construction or due to previous
+    // validation).
+
+    uint8_t uncheckedReadFixedU8() {
+        return uncheckedRead<uint8_t>();
+    }
+    uint32_t uncheckedReadFixedU32() {
+        return uncheckedRead<uint32_t>();
+    }
+    RawF32 uncheckedReadFixedF32() {
+        return RawF32::fromBits(uncheckedRead<uint32_t>());
+    }
+    RawF64 uncheckedReadFixedF64() {
+        return RawF64::fromBits(uncheckedRead<uint64_t>());
+    }
+    template <typename UInt>
+    UInt uncheckedReadVarU() {
+        static const unsigned numBits = sizeof(UInt) * CHAR_BIT;
+        static const unsigned remainderBits = numBits % 7;
+        static const unsigned numBitsInSevens = numBits - remainderBits;
+        UInt decoded = 0;
+        uint32_t shift = 0;
+        do {
+            uint8_t byte = *cur_++;
+            if (!(byte & 0x80))
+                return decoded | (UInt(byte) << shift);
+            decoded |= UInt(byte & 0x7f) << shift;
+            shift += 7;
+        } while (shift != numBitsInSevens);
+        uint8_t byte = *cur_++;
+        MOZ_ASSERT(!(byte & 0xf0));
+        return decoded | (UInt(byte) << numBitsInSevens);
+    }
+    uint32_t uncheckedReadVarU32() {
+        return uncheckedReadVarU<uint32_t>();
+    }
+    int32_t uncheckedReadVarS32() {
+        int32_t i32 = 0;
+        MOZ_ALWAYS_TRUE(readVarS32(&i32));
+        return i32;
+    }
+    uint64_t uncheckedReadVarU64() {
+        return uncheckedReadVarU<uint64_t>();
+    }
+    int64_t uncheckedReadVarS64() {
+        int64_t i64 = 0;
+        MOZ_ALWAYS_TRUE(readVarS64(&i64));
+        return i64;
+    }
+    ValType uncheckedReadValType() {
+        return (ValType)uncheckedReadFixedU8();
+    }
+    Expr uncheckedReadExpr() {
+        static_assert(size_t(Expr::Limit) <= ExprLimit, "fits");
+        uint8_t u8 = uncheckedReadFixedU8();
+        return u8 != UINT8_MAX
+               ? Expr(u8)
+               : Expr(uncheckedReadFixedU8() + UINT8_MAX);
+    }
+    void uncheckedReadFixedI8x16(I8x16* i8x16) {
+        struct T { I8x16 v; };
+        T t = uncheckedRead<T>();
+        memcpy(i8x16, &t, sizeof(t));
+    }
+    void uncheckedReadFixedI16x8(I16x8* i16x8) {
+        struct T { I16x8 v; };
+        T t = uncheckedRead<T>();
+        memcpy(i16x8, &t, sizeof(t));
+    }
+    void uncheckedReadFixedI32x4(I32x4* i32x4) {
+        struct T { I32x4 v; };
+        T t = uncheckedRead<T>();
+        memcpy(i32x4, &t, sizeof(t));
+    }
+    void uncheckedReadFixedF32x4(F32x4* f32x4) {
+        struct T { F32x4 v; };
+        T t = uncheckedRead<T>();
+        memcpy(f32x4, &t, sizeof(t));
+    }
+};
+
 // Reusable macro encoding/decoding functions reused by both the two
 // encoders (AsmJS/WasmTextToBinary) and all the decoders
 // (WasmCompile/WasmIonCompile/WasmBaselineCompile/WasmBinaryToText).
 
 MOZ_MUST_USE bool
 DecodePreamble(Decoder& d);
 
 MOZ_MUST_USE bool
--- a/js/src/wasm/WasmBinaryIterator.h
+++ b/js/src/wasm/WasmBinaryIterator.h
@@ -19,17 +19,17 @@
 #ifndef wasm_binary_iterator_h
 #define wasm_binary_iterator_h
 
 #include "mozilla/Poison.h"
 
 #include "jsprf.h"
 
 #include "jit/AtomicOp.h"
-#include "wasm/WasmTypes.h"
+#include "wasm/WasmBinaryFormat.h"
 
 namespace js {
 namespace wasm {
 
 // The kind of a control-flow stack item.
 enum class LabelKind : uint8_t {
     Block,
     Loop,
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -15,17 +15,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_generator_h
 #define wasm_generator_h
 
 #include "jit/MacroAssembler.h"
-#include "wasm/WasmBinary.h"
 #include "wasm/WasmCompile.h"
 #include "wasm/WasmModule.h"
 
 namespace js {
 namespace wasm {
 
 class FunctionGenerator;
 
--- a/js/src/wasm/WasmIonCompile.h
+++ b/js/src/wasm/WasmIonCompile.h
@@ -15,17 +15,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_ion_compile_h
 #define wasm_ion_compile_h
 
 #include "jit/MacroAssembler.h"
-#include "wasm/WasmBinary.h"
+#include "wasm/WasmTypes.h"
 
 namespace js {
 namespace wasm {
 
 struct ModuleGeneratorData;
 
 typedef Vector<jit::MIRType, 8, SystemAllocPolicy> MIRTypeVector;
 typedef jit::ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
--- a/js/src/wasm/WasmTextToBinary.h
+++ b/js/src/wasm/WasmTextToBinary.h
@@ -14,19 +14,17 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_text_to_binary_h
 #define wasm_text_to_binary_h
 
-#include "js/Utility.h"
-
-#include "wasm/WasmBinary.h"
+#include "wasm/WasmTypes.h"
 
 namespace js {
 namespace wasm {
 
 // Translate the textual representation of a wasm module (given by a
 // null-terminated char16_t array) into serialized bytes. If there is an error
 // other than out-of-memory an error message string will be stored in 'error'.
 
--- a/js/src/wasm/WasmTextUtils.cpp
+++ b/js/src/wasm/WasmTextUtils.cpp
@@ -14,17 +14,17 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmTextUtils.h"
 
 #include "vm/StringBuffer.h"
-#include "wasm/WasmBinary.h"
+#include "wasm/WasmTypes.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::IsNaN;
 
 template<size_t base>
 bool
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -30,17 +30,17 @@
 #include "NamespaceImports.h"
 
 #include "ds/LifoAlloc.h"
 #include "jit/IonTypes.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 #include "js/Vector.h"
 #include "vm/MallocProvider.h"
-#include "wasm/WasmBinary.h"
+#include "wasm/WasmBinaryConstants.h"
 
 namespace js {
 
 class PropertyName;
 namespace jit { struct BaselineScript; }
 
 // This is a widespread header, so lets keep out the core wasm impl types.
 
@@ -77,16 +77,22 @@ using mozilla::Nothing;
 using mozilla::PodZero;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::RefCounted;
 using mozilla::Some;
 using mozilla::Unused;
 
 typedef Vector<uint32_t, 0, SystemAllocPolicy> Uint32Vector;
+typedef Vector<uint8_t, 0, SystemAllocPolicy> Bytes;
+
+typedef int8_t I8x16[16];
+typedef int16_t I16x8[8];
+typedef int32_t I32x4[4];
+typedef float F32x4[4];
 
 class Code;
 class CodeRange;
 class Memory;
 class Module;
 class Instance;
 class Table;
 
@@ -139,36 +145,17 @@ struct ShareableBase : RefCounted<T>
         if (p)
             return 0;
         bool ok = seen->add(p, self);
         (void)ok;  // oh well
         return mallocSizeOf(self) + self->sizeOfExcludingThis(mallocSizeOf);
     }
 };
 
-// ValType/ExprType utilities
-
-static inline bool
-IsVoid(ExprType et)
-{
-    return et == ExprType::Void;
-}
-
-static inline ValType
-NonVoidToValType(ExprType et)
-{
-    MOZ_ASSERT(!IsVoid(et));
-    return ValType(et);
-}
-
-static inline ExprType
-ToExprType(ValType vt)
-{
-    return ExprType(vt);
-}
+// ValType utilities
 
 static inline bool
 IsSimdType(ValType vt)
 {
     switch (vt) {
       case ValType::I8x16:
       case ValType::I16x8:
       case ValType::I32x4:
@@ -238,22 +225,16 @@ SimdBoolType(ValType vt)
       case ValType::B32x4:
         return ValType::B32x4;
      default:
         MOZ_CRASH("Unhandled SIMD type");
     }
 }
 
 static inline bool
-IsSimdType(ExprType et)
-{
-    return IsVoid(et) ? false : IsSimdType(ValType(et));
-}
-
-static inline bool
 IsSimdBoolType(ValType vt)
 {
     return vt == ValType::B8x16 || vt == ValType::B16x8 || vt == ValType::B32x4;
 }
 
 static inline jit::MIRType
 ToMIRType(ValType vt)
 {
@@ -268,16 +249,66 @@ ToMIRType(ValType vt)
       case ValType::F32x4: return jit::MIRType::Float32x4;
       case ValType::B8x16: return jit::MIRType::Bool8x16;
       case ValType::B16x8: return jit::MIRType::Bool16x8;
       case ValType::B32x4: return jit::MIRType::Bool32x4;
     }
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type");
 }
 
+// The ExprType enum represents the type of a WebAssembly expression or return
+// value and may either be a value type or void. Soon, expression types will be
+// generalized to a list of ValType and this enum will go away, replaced,
+// wherever it is used, by a varU32 + list of ValType.
+
+enum class ExprType : uint32_t // fix type so we can cast from any u8 in decoder
+{
+    Void  = uint8_t(TypeCode::BlockVoid),
+
+    I32   = uint8_t(TypeCode::I32),
+    I64   = uint8_t(TypeCode::I64),
+    F32   = uint8_t(TypeCode::F32),
+    F64   = uint8_t(TypeCode::F64),
+
+    I8x16 = uint8_t(TypeCode::I8x16),
+    I16x8 = uint8_t(TypeCode::I16x8),
+    I32x4 = uint8_t(TypeCode::I32x4),
+    F32x4 = uint8_t(TypeCode::F32x4),
+    B8x16 = uint8_t(TypeCode::B8x16),
+    B16x8 = uint8_t(TypeCode::B16x8),
+    B32x4 = uint8_t(TypeCode::B32x4),
+
+    Limit = uint8_t(TypeCode::Limit)
+};
+
+static inline bool
+IsVoid(ExprType et)
+{
+    return et == ExprType::Void;
+}
+
+static inline ValType
+NonVoidToValType(ExprType et)
+{
+    MOZ_ASSERT(!IsVoid(et));
+    return ValType(et);
+}
+
+static inline ExprType
+ToExprType(ValType vt)
+{
+    return ExprType(vt);
+}
+
+static inline bool
+IsSimdType(ExprType et)
+{
+    return IsVoid(et) ? false : IsSimdType(ValType(et));
+}
+
 static inline jit::MIRType
 ToMIRType(ExprType et)
 {
     return IsVoid(et) ? jit::MIRType::None : ToMIRType(ValType(et));
 }
 
 static inline const char*
 ToCString(ExprType type)
@@ -301,16 +332,51 @@ ToCString(ExprType type)
 }
 
 static inline const char*
 ToCString(ValType type)
 {
     return ToCString(ToExprType(type));
 }
 
+// Because WebAssembly allows one to define the payload of a NaN value,
+// including the signal/quiet bit (highest order bit of payload), another
+// represenation of floating-point values is required: on some platforms (x86
+// without SSE2), passing a floating-point argument to a function call may use
+// the x87 stack, which has the side-effect of clearing the signal/quiet bit.
+// Because the signal/quiet bit must be preserved (by spec), we use the raw
+// punned integer representation of floating points instead, in function calls.
+//
+// When we leave the WebAssembly sandbox back to JS, NaNs are canonicalized, so
+// this isn't observable from JS.
+
+template<class T>
+class Raw
+{
+    typedef typename mozilla::FloatingPoint<T>::Bits Bits;
+    Bits value_;
+
+  public:
+    Raw() : value_(0) {}
+
+    explicit Raw(T value)
+      : value_(mozilla::BitwiseCast<Bits>(value))
+    {}
+
+    template<class U> MOZ_IMPLICIT Raw(U) = delete;
+
+    static Raw fromBits(Bits bits) { Raw r; r.value_ = bits; return r; }
+
+    Bits bits() const { return value_; }
+    T fp() const { return mozilla::BitwiseCast<T>(value_); }
+};
+
+using RawF64 = Raw<double>;
+using RawF32 = Raw<float>;
+
 // The Val class represents a single WebAssembly value of a given value type,
 // mostly for the purpose of numeric literals and initializers. A Val does not
 // directly map to a JS value since there is not (currently) a precise
 // representation of i64 values. A Val may contain non-canonical NaNs since,
 // within WebAssembly, floats are not canonicalized. Canonicalization must
 // happen at the JS boundary.
 
 class Val