Beispiel #1
0
static bool
DecodeConst(FunctionDecoder& f, ExprType expected)
{
    if (!f.d().readVarU32())
        return f.fail("unable to read i32.const immediate");

    return CheckType(f, ExprType::I32, expected);
}
Beispiel #2
0
static bool
DecodeExpr(FunctionDecoder& f, ExprType expected)
{
    Expr expr;
    if (!f.d().readExpr(&expr))
        return f.fail("unable to read expression");

    switch (expr) {
      case Expr::Nop:
        return CheckType(f, ExprType::Void, expected);
      case Expr::Call:
        return DecodeCall(f, expected);
      case Expr::CallImport:
        return DecodeCallImport(f, expected);
      case Expr::I32Const:
        return DecodeConst(f, expected);
      case Expr::GetLocal:
        return DecodeGetLocal(f, expected);
      case Expr::SetLocal:
        return DecodeSetLocal(f, expected);
      case Expr::Block:
        return DecodeBlock(f, expected);
      case Expr::I32Add:
      case Expr::I32Sub:
      case Expr::I32Mul:
      case Expr::I32DivS:
      case Expr::I32DivU:
      case Expr::I32RemS:
      case Expr::I32RemU:
      case Expr::I32And:
      case Expr::I32Or:
      case Expr::I32Xor:
      case Expr::I32Shl:
      case Expr::I32ShrS:
      case Expr::I32ShrU:
      case Expr::F32Add:
      case Expr::F32Sub:
      case Expr::F32Mul:
      case Expr::F32Div:
      case Expr::F32Min:
      case Expr::F32Max:
      case Expr::F32CopySign:
      case Expr::F64Add:
      case Expr::F64Sub:
      case Expr::F64Mul:
      case Expr::F64Div:
      case Expr::F64Min:
      case Expr::F64Max:
      case Expr::F64CopySign:
        return DecodeBinaryOperator(f, expected);
      default:
        break;
    }

    return f.fail("bad expression code");
}
Beispiel #3
0
static bool
DecodeConstI64(FunctionDecoder& f, ExprType* type)
{
    int64_t _;
    if (!f.d().readVarS64(&_))
        return f.fail("unable to read i64.const immediate");

    *type = ExprType::I64;
    return true;
}
Beispiel #4
0
static bool
DecodeConstI32(FunctionDecoder& f, ExprType* type)
{
    int32_t _;
    if (!f.d().readVarS32(&_))
        return f.fail("unable to read i32.const immediate");

    *type = ExprType::I32;
    return true;
}
Beispiel #5
0
static bool
DecodeLoadStoreAddress(FunctionDecoder &f)
{
    uint32_t offset, align;
    return DecodeExpr(f, ExprType::I32) &&
           f.d().readVarU32(&offset) &&
           f.d().readVarU32(&align) &&
           mozilla::IsPowerOfTwo(align) &&
           (offset == 0 || f.fail("NYI: address offsets")) &&
           f.fail("NYI: wasm loads and stores");
}
Beispiel #6
0
static bool
DecodeCallImport(FunctionDecoder& f, ExprType* type)
{
    uint32_t importIndex;
    if (!f.d().readVarU32(&importIndex))
        return f.fail("unable to read import index");

    if (importIndex >= f.mg().numImports())
        return f.fail("import index out of range");

    return DecodeCallWithSig(f, *f.mg().import(importIndex).sig, type);
}
Beispiel #7
0
static bool
DecodeCall(FunctionDecoder& f, ExprType* type)
{
    uint32_t funcIndex;
    if (!f.d().readVarU32(&funcIndex))
        return f.fail("unable to read import index");

    if (funcIndex >= f.mg().numFuncSigs())
        return f.fail("callee index out of range");

    return DecodeCallWithSig(f, f.mg().funcSig(funcIndex), type);
}
Beispiel #8
0
static bool
DecodeGetLocal(FunctionDecoder& f, ExprType expected)
{
    uint32_t localIndex;
    if (!f.d().readVarU32(&localIndex))
        return f.fail("unable to read get_local index");

    if (localIndex >= f.fg().locals().length())
        return f.fail("get_local index out of range");

    return CheckType(f, ToExprType(f.fg().locals()[localIndex]), expected);
}
Beispiel #9
0
static bool
DecodeGetLocal(FunctionDecoder& f, ExprType* type)
{
    uint32_t localIndex;
    if (!f.d().readVarU32(&localIndex))
        return f.fail("unable to read get_local index");

    if (localIndex >= f.locals().length())
        return f.fail("get_local index out of range");

    *type = ToExprType(f.locals()[localIndex]);
    return true;
}
Beispiel #10
0
static bool
DecodeBlock(FunctionDecoder& f, bool isLoop, ExprType* type)
{
    if (!f.pushBlock())
        return f.fail("nesting overflow");

    if (isLoop) {
        if (!f.pushBlock())
            return f.fail("nesting overflow");
    }

    uint32_t numExprs;
    if (!f.d().readVarU32(&numExprs))
        return f.fail("unable to read block's number of expressions");

    ExprType exprType = ExprType::Void;

    for (uint32_t i = 0; i < numExprs; i++) {
        if (!DecodeExpr(f, &exprType))
            return false;
    }

    if (isLoop)
        f.popBlock();

    ExprType branchType = f.popBlock();
    *type = Unify(branchType, exprType);
    return true;
}
Beispiel #11
0
static bool
DecodeConstF64(FunctionDecoder& f, ExprType expected)
{
    double value;
    if (!f.d().readFixedF64(&value))
        return f.fail("unable to read f64.const immediate");
    if (IsNaN(value)) {
        const double jsNaN = JS::GenericNaN();
        if (memcmp(&value, &jsNaN, sizeof(value)) != 0)
            return f.fail("NYI: NaN literals with custom payloads");
    }

    return CheckType(f, ExprType::F64, expected);
}
Beispiel #12
0
static bool
DecodeCallIndirect(FunctionDecoder& f, ExprType expected)
{
    uint32_t sigIndex;
    if (!f.d().readVarU32(&sigIndex))
        return f.fail("unable to read indirect call signature index");

    if (sigIndex >= f.mg().numSigs())
        return f.fail("signature index out of range");

    if (!DecodeExpr(f, ExprType::I32))
        return false;

    return DecodeCallWithSig(f, f.mg().sig(sigIndex), expected);
}
Beispiel #13
0
static bool
DecodeReturn(FunctionDecoder& f, ExprType* type)
{
    if (f.sig().ret() != ExprType::Void) {
        ExprType actual;
        if (!DecodeExpr(f, &actual))
            return false;

        if (!CheckType(f, actual, f.sig().ret()))
            return false;
    }

    *type = AnyType;
    return true;
}
Beispiel #14
0
static bool
DecodeConstF32(FunctionDecoder& f, ExprType* type)
{
    float value;
    if (!f.d().readFixedF32(&value))
        return f.fail("unable to read f32.const immediate");

    if (IsNaN(value)) {
        const float jsNaN = (float)JS::GenericNaN();
        if (memcmp(&value, &jsNaN, sizeof(value)) != 0)
            return f.fail("NYI: NaN literals with custom payloads");
    }

    *type = ExprType::F32;
    return true;
}
Beispiel #15
0
static bool
DecodeSetLocal(FunctionDecoder& f, ExprType expected)
{
    uint32_t localIndex;
    if (!f.d().readVarU32(&localIndex))
        return f.fail("unable to read set_local index");

    if (localIndex >= f.fg().locals().length())
        return f.fail("set_local index out of range");

    ExprType localType = ToExprType(f.fg().locals()[localIndex]);

    if (!DecodeExpr(f, localType))
        return false;

    return CheckType(f, localType, expected);
}
Beispiel #16
0
static bool
DecodeBranch(FunctionDecoder& f, Expr expr, ExprType* type)
{
    MOZ_ASSERT(expr == Expr::Br || expr == Expr::BrIf);

    uint32_t relativeDepth;
    if (!f.d().readVarU32(&relativeDepth))
        return f.fail("expected relative depth");

    if (!f.branchWithType(relativeDepth, ExprType::Void))
        return f.fail("branch depth exceeds current nesting level");

    Expr value;
    if (!f.d().readExpr(&value))
        return f.fail("expected branch value");

    if (value != Expr::Nop)
        return f.fail("NYI: branch values");

    if (expr == Expr::BrIf) {
        ExprType actual;
        if (!DecodeExpr(f, &actual))
            return false;

        if (!CheckType(f, actual, ValType::I32))
            return false;

        *type = ExprType::Void;
    } else {
        *type = AnyType;
    }

    return true;
}
Beispiel #17
0
static bool
DecodeCallImport(FunctionDecoder& f, ExprType expected)
{
    uint32_t importIndex;
    if (!f.d().readU32(&importIndex))
        return f.fail("unable to read import index");

    if (importIndex >= f.mg().numImports())
        return f.fail("import index out of range");

    const DeclaredSig& sig = *f.mg().import(importIndex).sig;

    for (ValType argType : sig.args()) {
        if (!DecodeExpr(f, ToExprType(argType)))
            return false;
    }

    return CheckType(f, sig.ret(), expected);
}
Beispiel #18
0
static bool
DecodeCall(FunctionDecoder& f, ExprType expected)
{
    uint32_t funcIndex;
    if (!f.d().readU32(&funcIndex))
        return f.fail("unable to read import index");

    if (funcIndex >= f.mg().numFuncSigs())
        return f.fail("callee index out of range");

    const DeclaredSig& sig = f.mg().funcSig(funcIndex);

    for (ValType argType : sig.args()) {
        if (!DecodeExpr(f, ToExprType(argType)))
            return false;
    }

    return CheckType(f, sig.ret(), expected);
}
Beispiel #19
0
static bool
DecodeBlock(FunctionDecoder& f, ExprType expected)
{
    uint32_t numExprs;
    if (!f.d().readVarU32(&numExprs))
        return f.fail("unable to read block's number of expressions");

    if (numExprs) {
        for (uint32_t i = 0; i < numExprs - 1; i++) {
            if (!DecodeExpr(f, ExprType::Void))
                return false;
        }
        if (!DecodeExpr(f, expected))
            return false;
    } else {
        if (!CheckType(f, ExprType::Void, expected))
            return false;
    }

    return true;
}
Beispiel #20
0
static bool
DecodeLoadStoreAddress(FunctionDecoder &f, unsigned width)
{
    uint32_t flags;
    if (!f.d().readVarU32(&flags))
        return f.fail("expected memory access flags");

    uint32_t alignLog2 = flags;
    if (alignLog2 >= 32 || (1u << alignLog2) > width)
        return f.fail("greater than natural alignment");

    uint32_t offset;
    if (!f.d().readVarU32(&offset))
        return f.fail("expected memory access offset");

    ExprType baseType;
    if (!DecodeExpr(f, &baseType))
        return false;

    return CheckType(f, baseType, ExprType::I32);
}
Beispiel #21
0
static bool
CheckType(FunctionDecoder& f, ExprType actual, ExprType expected)
{
    if (actual == expected || expected == ExprType::Void)
        return true;

    UniqueChars error(JS_smprintf("type mismatch: expression has type %s but expected %s",
                                  ToCString(actual), ToCString(expected)));
    if (!error)
        return false;

    return f.fail(error.get());
}
Beispiel #22
0
static bool
DecodeBrTable(FunctionDecoder& f, ExprType* type)
{
    uint32_t tableLength;
    if (!f.d().readVarU32(&tableLength))
        return false;

    if (tableLength > MaxBrTableElems)
        return f.fail("too many br_table entries");

    for (uint32_t i = 0; i < tableLength; i++) {
        uint32_t depth;
        if (!f.d().readFixedU32(&depth))
            return f.fail("missing br_table entry");

        if (!f.branchWithType(depth, ExprType::Void))
            return f.fail("branch depth exceeds current nesting level");
    }

    uint32_t defaultDepth;
    if (!f.d().readFixedU32(&defaultDepth))
        return f.fail("expected default relative depth");

    if (!f.branchWithType(defaultDepth, ExprType::Void))
        return f.fail("branch depth exceeds current nesting level");

    ExprType actual;
    if (!DecodeExpr(f, &actual))
        return false;

    if (!CheckType(f, actual, ExprType::I32))
        return false;

    *type = AnyType;
    return true;
}
Beispiel #23
0
static bool
DecodeExpr(FunctionDecoder& f, ExprType expected)
{
    Expr expr;
    if (!f.d().readExpr(&expr))
        return f.fail("unable to read expression");

    switch (expr) {
      case Expr::Nop:
        return CheckType(f, ExprType::Void, expected);
      case Expr::Call:
        return DecodeCall(f, expected);
      case Expr::CallImport:
        return DecodeCallImport(f, expected);
      case Expr::CallIndirect:
        return DecodeCallIndirect(f, expected);
      case Expr::I32Const:
        return DecodeConstI32(f, expected);
      case Expr::I64Const:
        return f.fail("NYI: i64") &&
               DecodeConstI64(f, expected);
      case Expr::F32Const:
        return DecodeConstF32(f, expected);
      case Expr::F64Const:
        return DecodeConstF64(f, expected);
      case Expr::GetLocal:
        return DecodeGetLocal(f, expected);
      case Expr::SetLocal:
        return DecodeSetLocal(f, expected);
      case Expr::Block:
        return DecodeBlock(f, expected);
      case Expr::If:
        return DecodeIfElse(f, /* hasElse */ false, expected);
      case Expr::IfElse:
        return DecodeIfElse(f, /* hasElse */ true, expected);
      case Expr::I32Clz:
        return DecodeUnaryOperator(f, expected, ExprType::I32);
      case Expr::I32Ctz:
        return f.fail("NYI: ctz");
      case Expr::I32Popcnt:
        return f.fail("NYI: popcnt");
      case Expr::I64Clz:
      case Expr::I64Ctz:
      case Expr::I64Popcnt:
        return f.fail("NYI: i64") &&
               DecodeUnaryOperator(f, expected, ExprType::I64);
      case Expr::F32Abs:
      case Expr::F32Neg:
      case Expr::F32Ceil:
      case Expr::F32Floor:
      case Expr::F32Sqrt:
        return DecodeUnaryOperator(f, expected, ExprType::F32);
      case Expr::F32Trunc:
        return f.fail("NYI: trunc");
      case Expr::F32Nearest:
        return f.fail("NYI: nearest");
      case Expr::F64Abs:
      case Expr::F64Neg:
      case Expr::F64Ceil:
      case Expr::F64Floor:
      case Expr::F64Sqrt:
        return DecodeUnaryOperator(f, expected, ExprType::F64);
      case Expr::F64Trunc:
        return f.fail("NYI: trunc");
      case Expr::F64Nearest:
        return f.fail("NYI: nearest");
      case Expr::I32Add:
      case Expr::I32Sub:
      case Expr::I32Mul:
      case Expr::I32DivS:
      case Expr::I32DivU:
      case Expr::I32RemS:
      case Expr::I32RemU:
      case Expr::I32And:
      case Expr::I32Or:
      case Expr::I32Xor:
      case Expr::I32Shl:
      case Expr::I32ShrS:
      case Expr::I32ShrU:
        return DecodeBinaryOperator(f, expected, ExprType::I32);
      case Expr::I64Add:
      case Expr::I64Sub:
      case Expr::I64Mul:
      case Expr::I64DivS:
      case Expr::I64DivU:
      case Expr::I64RemS:
      case Expr::I64RemU:
      case Expr::I64And:
      case Expr::I64Or:
      case Expr::I64Xor:
      case Expr::I64Shl:
      case Expr::I64ShrS:
      case Expr::I64ShrU:
        return f.fail("NYI: i64") &&
               DecodeBinaryOperator(f, expected, ExprType::I64);
      case Expr::F32Add:
      case Expr::F32Sub:
      case Expr::F32Mul:
      case Expr::F32Div:
      case Expr::F32Min:
      case Expr::F32Max:
        return DecodeBinaryOperator(f, expected, ExprType::F32);
      case Expr::F32CopySign:
        return f.fail("NYI: copysign");
      case Expr::F64Add:
      case Expr::F64Sub:
      case Expr::F64Mul:
      case Expr::F64Div:
      case Expr::F64Min:
      case Expr::F64Max:
        return DecodeBinaryOperator(f, expected, ExprType::F64);
      case Expr::F64CopySign:
        return f.fail("NYI: copysign");
      case Expr::I32Eq:
      case Expr::I32Ne:
      case Expr::I32LtS:
      case Expr::I32LtU:
      case Expr::I32LeS:
      case Expr::I32LeU:
      case Expr::I32GtS:
      case Expr::I32GtU:
      case Expr::I32GeS:
      case Expr::I32GeU:
        return DecodeComparisonOperator(f, expected, ExprType::I32);
      case Expr::I64Eq:
      case Expr::I64Ne:
      case Expr::I64LtS:
      case Expr::I64LtU:
      case Expr::I64LeS:
      case Expr::I64LeU:
      case Expr::I64GtS:
      case Expr::I64GtU:
      case Expr::I64GeS:
      case Expr::I64GeU:
        return f.fail("NYI: i64") &&
               DecodeComparisonOperator(f, expected, ExprType::I64);
      case Expr::F32Eq:
      case Expr::F32Ne:
      case Expr::F32Lt:
      case Expr::F32Le:
      case Expr::F32Gt:
      case Expr::F32Ge:
        return DecodeComparisonOperator(f, expected, ExprType::F32);
      case Expr::F64Eq:
      case Expr::F64Ne:
      case Expr::F64Lt:
      case Expr::F64Le:
      case Expr::F64Gt:
      case Expr::F64Ge:
        return DecodeComparisonOperator(f, expected, ExprType::F64);
      case Expr::I32WrapI64:
        return f.fail("NYI: i64") &&
               DecodeConversionOperator(f, expected, ExprType::I32, ExprType::I64);
      case Expr::I32TruncSF32:
      case Expr::I32TruncUF32:
        return DecodeConversionOperator(f, expected, ExprType::I32, ExprType::F32);
      case Expr::I32ReinterpretF32:
        return f.fail("NYI: reinterpret");
      case Expr::I32TruncSF64:
      case Expr::I32TruncUF64:
        return DecodeConversionOperator(f, expected, ExprType::I32, ExprType::F64);
      case Expr::I64ExtendSI32:
      case Expr::I64ExtendUI32:
        return f.fail("NYI: i64") &&
               DecodeConversionOperator(f, expected, ExprType::I64, ExprType::I32);
      case Expr::I64TruncSF32:
      case Expr::I64TruncUF32:
        return f.fail("NYI: i64") &&
               DecodeConversionOperator(f, expected, ExprType::I64, ExprType::F32);
      case Expr::I64TruncSF64:
      case Expr::I64TruncUF64:
      case Expr::I64ReinterpretF64:
        return f.fail("NYI: i64") &&
               DecodeConversionOperator(f, expected, ExprType::I64, ExprType::F64);
      case Expr::F32ConvertSI32:
      case Expr::F32ConvertUI32:
        return DecodeConversionOperator(f, expected, ExprType::F32, ExprType::I32);
      case Expr::F32ReinterpretI32:
        return f.fail("NYI: reinterpret");
      case Expr::F32ConvertSI64:
      case Expr::F32ConvertUI64:
        return f.fail("NYI: i64") &&
               DecodeConversionOperator(f, expected, ExprType::F32, ExprType::I64);
      case Expr::F32DemoteF64:
        return DecodeConversionOperator(f, expected, ExprType::F32, ExprType::F64);
      case Expr::F64ConvertSI32:
      case Expr::F64ConvertUI32:
        return DecodeConversionOperator(f, expected, ExprType::F64, ExprType::I32);
      case Expr::F64ConvertSI64:
      case Expr::F64ConvertUI64:
      case Expr::F64ReinterpretI64:
        return f.fail("NYI: i64") &&
               DecodeConversionOperator(f, expected, ExprType::F64, ExprType::I64);
      case Expr::F64PromoteF32:
        return DecodeConversionOperator(f, expected, ExprType::F64, ExprType::F32);
      case Expr::I32LoadMem:
      case Expr::I32LoadMem8S:
      case Expr::I32LoadMem8U:
      case Expr::I32LoadMem16S:
      case Expr::I32LoadMem16U:
        return DecodeLoad(f, expected, ExprType::I32);
      case Expr::I64LoadMem:
      case Expr::I64LoadMem8S:
      case Expr::I64LoadMem8U:
      case Expr::I64LoadMem16S:
      case Expr::I64LoadMem16U:
      case Expr::I64LoadMem32S:
      case Expr::I64LoadMem32U:
        return DecodeLoad(f, expected, ExprType::I64);
      case Expr::F32LoadMem:
        return DecodeLoad(f, expected, ExprType::F32);
      case Expr::F64LoadMem:
        return DecodeLoad(f, expected, ExprType::F64);
      case Expr::I32StoreMem:
      case Expr::I32StoreMem8:
      case Expr::I32StoreMem16:
        return DecodeStore(f, expected, ExprType::I32);
      case Expr::I64StoreMem:
      case Expr::I64StoreMem8:
      case Expr::I64StoreMem16:
      case Expr::I64StoreMem32:
        return f.fail("NYI: i64") &&
               DecodeStore(f, expected, ExprType::I64);
      case Expr::F32StoreMem:
        return DecodeStore(f, expected, ExprType::F32);
      case Expr::F64StoreMem:
        return DecodeStore(f, expected, ExprType::F64);
      default:
        break;
    }

    return f.fail("bad expression code");
}
Beispiel #24
0
static bool
DecodeExpr(FunctionDecoder& f, ExprType* type)
{
    Expr expr;
    if (!f.d().readExpr(&expr))
        return f.fail("unable to read expression");

    switch (expr) {
      case Expr::Nop:
        return DecodeNop(f, type);
      case Expr::Call:
        return DecodeCall(f, type);
      case Expr::CallImport:
        return DecodeCallImport(f, type);
      case Expr::CallIndirect:
        return DecodeCallIndirect(f, type);
      case Expr::I32Const:
        return DecodeConstI32(f, type);
      case Expr::I64Const:
        return DecodeConstI64(f, type);
      case Expr::F32Const:
        return DecodeConstF32(f, type);
      case Expr::F64Const:
        return DecodeConstF64(f, type);
      case Expr::GetLocal:
        return DecodeGetLocal(f, type);
      case Expr::SetLocal:
        return DecodeSetLocal(f, type);
      case Expr::Block:
        return DecodeBlock(f, /* isLoop */ false, type);
      case Expr::Loop:
        return DecodeBlock(f, /* isLoop */ true, type);
      case Expr::If:
        return DecodeIfElse(f, /* hasElse */ false, type);
      case Expr::IfElse:
        return DecodeIfElse(f, /* hasElse */ true, type);
      case Expr::I32Clz:
      case Expr::I32Ctz:
      case Expr::I32Popcnt:
        return DecodeUnaryOperator(f, ValType::I32, type);
      case Expr::I64Clz:
      case Expr::I64Ctz:
      case Expr::I64Popcnt:
        return f.fail("NYI: i64") &&
               DecodeUnaryOperator(f, ValType::I64, type);
      case Expr::F32Abs:
      case Expr::F32Neg:
      case Expr::F32Ceil:
      case Expr::F32Floor:
      case Expr::F32Sqrt:
        return DecodeUnaryOperator(f, ValType::F32, type);
      case Expr::F32Trunc:
        return f.fail("NYI: trunc");
      case Expr::F32Nearest:
        return f.fail("NYI: nearest");
      case Expr::F64Abs:
      case Expr::F64Neg:
      case Expr::F64Ceil:
      case Expr::F64Floor:
      case Expr::F64Sqrt:
        return DecodeUnaryOperator(f, ValType::F64, type);
      case Expr::F64Trunc:
        return f.fail("NYI: trunc");
      case Expr::F64Nearest:
        return f.fail("NYI: nearest");
      case Expr::I32Add:
      case Expr::I32Sub:
      case Expr::I32Mul:
      case Expr::I32DivS:
      case Expr::I32DivU:
      case Expr::I32RemS:
      case Expr::I32RemU:
      case Expr::I32And:
      case Expr::I32Or:
      case Expr::I32Xor:
      case Expr::I32Shl:
      case Expr::I32ShrS:
      case Expr::I32ShrU:
        return DecodeBinaryOperator(f, ValType::I32, type);
      case Expr::I64Add:
      case Expr::I64Sub:
      case Expr::I64Mul:
      case Expr::I64DivS:
      case Expr::I64DivU:
      case Expr::I64RemS:
      case Expr::I64RemU:
      case Expr::I64And:
      case Expr::I64Or:
      case Expr::I64Xor:
      case Expr::I64Shl:
      case Expr::I64ShrS:
      case Expr::I64ShrU:
        return DecodeBinaryOperator(f, ValType::I64, type);
      case Expr::F32Add:
      case Expr::F32Sub:
      case Expr::F32Mul:
      case Expr::F32Div:
      case Expr::F32Min:
      case Expr::F32Max:
        return DecodeBinaryOperator(f, ValType::F32, type);
      case Expr::F32CopySign:
        return f.fail("NYI: copysign");
      case Expr::F64Add:
      case Expr::F64Sub:
      case Expr::F64Mul:
      case Expr::F64Div:
      case Expr::F64Min:
      case Expr::F64Max:
        return DecodeBinaryOperator(f, ValType::F64, type);
      case Expr::F64CopySign:
        return f.fail("NYI: copysign");
      case Expr::I32Eq:
      case Expr::I32Ne:
      case Expr::I32LtS:
      case Expr::I32LtU:
      case Expr::I32LeS:
      case Expr::I32LeU:
      case Expr::I32GtS:
      case Expr::I32GtU:
      case Expr::I32GeS:
      case Expr::I32GeU:
        return DecodeComparisonOperator(f, ValType::I32, type);
      case Expr::I64Eq:
      case Expr::I64Ne:
      case Expr::I64LtS:
      case Expr::I64LtU:
      case Expr::I64LeS:
      case Expr::I64LeU:
      case Expr::I64GtS:
      case Expr::I64GtU:
      case Expr::I64GeS:
      case Expr::I64GeU:
        return DecodeComparisonOperator(f, ValType::I64, type);
      case Expr::F32Eq:
      case Expr::F32Ne:
      case Expr::F32Lt:
      case Expr::F32Le:
      case Expr::F32Gt:
      case Expr::F32Ge:
        return DecodeComparisonOperator(f, ValType::F32, type);
      case Expr::F64Eq:
      case Expr::F64Ne:
      case Expr::F64Lt:
      case Expr::F64Le:
      case Expr::F64Gt:
      case Expr::F64Ge:
        return DecodeComparisonOperator(f, ValType::F64, type);
      case Expr::I32WrapI64:
        return DecodeConversionOperator(f, ValType::I32, ValType::I64, type);
      case Expr::I32TruncSF32:
      case Expr::I32TruncUF32:
        return DecodeConversionOperator(f, ValType::I32, ValType::F32, type);
      case Expr::I32ReinterpretF32:
        return f.fail("NYI: reinterpret");
      case Expr::I32TruncSF64:
      case Expr::I32TruncUF64:
        return DecodeConversionOperator(f, ValType::I32, ValType::F64, type);
      case Expr::I64ExtendSI32:
      case Expr::I64ExtendUI32:
        return DecodeConversionOperator(f, ValType::I64, ValType::I32, type);
      case Expr::I64TruncSF32:
      case Expr::I64TruncUF32:
        return DecodeConversionOperator(f, ValType::I64, ValType::F32, type);
      case Expr::I64TruncSF64:
      case Expr::I64TruncUF64:
        return DecodeConversionOperator(f, ValType::I64, ValType::F64, type);
      case Expr::I64ReinterpretF64:
        return f.fail("NYI: i64");
      case Expr::F32ConvertSI32:
      case Expr::F32ConvertUI32:
        return DecodeConversionOperator(f, ValType::F32, ValType::I32, type);
      case Expr::F32ReinterpretI32:
        return f.fail("NYI: reinterpret");
      case Expr::F32ConvertSI64:
      case Expr::F32ConvertUI64:
        return f.fail("NYI: i64") &&
               DecodeConversionOperator(f, ValType::F32, ValType::I64, type);
      case Expr::F32DemoteF64:
        return DecodeConversionOperator(f, ValType::F32, ValType::F64, type);
      case Expr::F64ConvertSI32:
      case Expr::F64ConvertUI32:
        return DecodeConversionOperator(f, ValType::F64, ValType::I32, type);
      case Expr::F64ConvertSI64:
      case Expr::F64ConvertUI64:
      case Expr::F64ReinterpretI64:
        return f.fail("NYI: i64") &&
               DecodeConversionOperator(f, ValType::F64, ValType::I64, type);
      case Expr::F64PromoteF32:
        return DecodeConversionOperator(f, ValType::F64, ValType::F32, type);
      case Expr::I32Load8S:
      case Expr::I32Load8U:
        return DecodeLoad(f, 1, ValType::I32, type);
      case Expr::I32Load16S:
      case Expr::I32Load16U:
        return DecodeLoad(f, 2, ValType::I32, type);
      case Expr::I32Load:
        return DecodeLoad(f, 4, ValType::I32, type);
      case Expr::I64Load:
      case Expr::I64Load8S:
      case Expr::I64Load8U:
      case Expr::I64Load16S:
      case Expr::I64Load16U:
      case Expr::I64Load32S:
      case Expr::I64Load32U:
        return f.fail("NYI: i64") &&
               DecodeLoad(f, 0, ValType::I64, type);
      case Expr::F32Load:
        return DecodeLoad(f, 4, ValType::F32, type);
      case Expr::F64Load:
        return DecodeLoad(f, 8, ValType::F64, type);
      case Expr::I32Store8:
        return DecodeStore(f, 1, ValType::I32, type);
      case Expr::I32Store16:
        return DecodeStore(f, 2, ValType::I32, type);
      case Expr::I32Store:
        return DecodeStore(f, 4, ValType::I32, type);
      case Expr::I64Store:
      case Expr::I64Store8:
      case Expr::I64Store16:
      case Expr::I64Store32:
        return f.fail("NYI: i64") &&
               DecodeStore(f, 0, ValType::I64, type);
      case Expr::F32Store:
        return DecodeStore(f, 4, ValType::F32, type);
      case Expr::F64Store:
        return DecodeStore(f, 8, ValType::F64, type);
      case Expr::Br:
        return DecodeBranch(f, expr, type);
      case Expr::BrIf:
        return DecodeBranch(f, expr, type);
      case Expr::BrTable:
        return DecodeBrTable(f, type);
      case Expr::Return:
        return DecodeReturn(f, type);
      default:
        // Note: it's important not to remove this default since readExpr()
        // can return Expr values for which there is no enumerator.
        break;
    }

    return f.fail("bad expression code");
}