Bug 1316625 - Baldr: tidy up WasmBinary*.h (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Thu, 10 Nov 2016 14:54:39 -0600
changeset 322019 1b0a99027126df1d04c20ffc6a2338d62f205f7e
parent 322018 b06d68ee9270643f5295a15dd34a920ac79c9ace
child 322020 b10dbdd46d12c4f0d98a6e4c6a6a64adab8dfe22
push id83746
push userlwagner@mozilla.com
push dateThu, 10 Nov 2016 20:55:07 +0000
treeherdermozilla-inbound@1b0a99027126 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1316625
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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