skip to content
==> src/instr.c <==
1/* Decoding one instruction: opcode, sub-opcode, immediates. */
2#include "wasm/instr.h"
3
4#include <string.h>
5
6static void memarg(wasm_reader *r, wasm_instr *in) {
7    in->align = wasm_rd_u32(r);
8    in->mem = 0;
9    /* Multi-memory: bit 6 of the alignment says a memory index follows. */
10    if (in->align & 0x40) {
11        in->align &= ~0x40u;
12        in->mem = wasm_rd_u32(r);
13    }
14    in->moffset = wasm_rd_u64(r);
15}
16
17static void blocktype(wasm_reader *r, wasm_instr *in) {
18    size_t at = wasm_rd_offset(r);
19    int64_t bt = wasm_rd_s33(r);
20    in->blocktype = bt;
21    in->bt_val = 0;
22    if (bt >= 0 || bt == WASM_BT_EMPTY) return;
23    /* A negative one-byte s33 is a value type: 0x7f is -1, 0x6f is -17. */
24    if (bt < -64) {
25        wasm_rd_fail_at(r, at, "invalid block type");
26        return;
27    }
28    wasm_valtype t = (wasm_valtype)(bt + 0x80);
29    if (!wasm_is_valtype(t)) {
30        wasm_rd_fail_at(r, at, "invalid value type");
31        return;
32    }
33    in->bt_val = t;
34}
35
36static wasm_valtype reftype(wasm_reader *r) {
37    size_t at = wasm_rd_offset(r);
38    uint8_t t = wasm_rd_u8(r);
39    if (!r->failed && t != WASM_FUNCREF && t != WASM_EXTERNREF) wasm_rd_fail_at(r, at, "malformed reference type");
40    return t;
41}
42
43bool wasm_rd_instr(wasm_reader *r, wasm_instr *in) {
44    memset(in, 0, sizeof *in);
45    in->offset = wasm_rd_offset(r);
46    uint32_t op = wasm_rd_u8(r);
47    if (r->failed) return false;
48    if (op == WASM_PREFIX_FC || op == WASM_PREFIX_SIMD || op == WASM_PREFIX_ATOMIC) {
49        uint32_t sub = wasm_rd_u32(r);
50        if (r->failed) return false;
51        if (sub > 0xffff) return wasm_rd_fail_at(r, in->offset, "unknown 0x%02x subopcode: 0x%x", op, sub);
52        op = op << 16 | sub;
53    }
54    in->op = op;
55    if (!wasm_op_info(op, &in->info)) {
56        if (op > 0xff) return wasm_rd_fail_at(r, in->offset, "unknown 0x%02x subopcode: 0x%x", op >> 16, op & 0xffff);
57        return wasm_rd_fail_at(r, in->offset, "illegal opcode: 0x%x", op);
58    }
59    switch (in->info.imm) {
60    case WASM_IMM_NONE: break;
61    case WASM_IMM_BLOCK: blocktype(r, in); break;
62    case WASM_IMM_LABEL:
63    case WASM_IMM_FUNC:
64    case WASM_IMM_LOCAL:
65    case WASM_IMM_GLOBAL:
66    case WASM_IMM_TABLE:
67    case WASM_IMM_DATA:
68    case WASM_IMM_ELEM: in->a = wasm_rd_u32(r); break;
69    case WASM_IMM_MEMIDX: in->mem = wasm_rd_u32(r); break;
70    case WASM_IMM_BR_TABLE: {
71        in->ntargets = wasm_rd_count(r, 1, "br_table");
72        in->targets = r->p;
73        for (uint32_t i = 0; i < in->ntargets && !r->failed; i++) (void)wasm_rd_u32(r);
74        in->b = wasm_rd_u32(r); /* the default */
75        break;
76    }
77    case WASM_IMM_CALL_INDIRECT:
78        in->a = wasm_rd_u32(r); /* type */
79        in->b = wasm_rd_u32(r); /* table */
80        break;
81    case WASM_IMM_MEMARG: memarg(r, in); break;
82    case WASM_IMM_MEMARG_LANE:
83        memarg(r, in);
84        in->lane = wasm_rd_u8(r);
85        break;
86    case WASM_IMM_I32: in->i = wasm_rd_s32(r); break;
87    case WASM_IMM_I64: in->i = wasm_rd_s64(r); break;
88    case WASM_IMM_F32: in->f32 = wasm_rd_le32(r); break;
89    case WASM_IMM_F64: in->f64 = wasm_rd_le64(r); break;
90    case WASM_IMM_SELECT_T: {
91        size_t at = wasm_rd_offset(r);
92        in->nselect = wasm_rd_count(r, 1, "select types");
93        for (uint32_t i = 0; i < in->nselect && !r->failed; i++) {
94            size_t tat = wasm_rd_offset(r);
95            uint8_t t = wasm_rd_u8(r);
96            if (!r->failed && !wasm_is_valtype(t)) return wasm_rd_fail_at(r, tat, "invalid value type");
97            if (i == 0) in->select_type = t;
98        }
99        if (!r->failed && in->nselect != 1) return wasm_rd_fail_at(r, at, "invalid result arity");
100        break;
101    }
102    case WASM_IMM_REFTYPE: in->reftype = reftype(r); break;
103    case WASM_IMM_MEMORY_INIT:
104        in->a = wasm_rd_u32(r); /* data */
105        in->mem = wasm_rd_u32(r);
106        break;
107    case WASM_IMM_MEMORY_COPY:
108        in->mem = wasm_rd_u32(r); /* destination */
109        in->b = wasm_rd_u32(r);   /* source */
110        break;
111    case WASM_IMM_TABLE_INIT:
112        in->a = wasm_rd_u32(r); /* element */
113        in->b = wasm_rd_u32(r); /* table */
114        break;
115    case WASM_IMM_TABLE_COPY:
116        in->a = wasm_rd_u32(r); /* destination */
117        in->b = wasm_rd_u32(r); /* source */
118        break;
119    case WASM_IMM_V128:
120    case WASM_IMM_SHUFFLE: in->v128 = wasm_rd_bytes(r, 16); break;
121    case WASM_IMM_LANE: in->lane = wasm_rd_u8(r); break;
122    case WASM_IMM_ZERO_BYTE: {
123        size_t at = wasm_rd_offset(r);
124        if (wasm_rd_u8(r) != 0 && !r->failed) return wasm_rd_fail_at(r, at, "nonzero byte after atomic.fence");
125        break;
126    }
127    }
128    return !r->failed;
129}
130
131void wasm_instr_targets(const wasm_instr *in, uint32_t *out) {
132    const uint8_t *p = in->targets;
133    for (uint32_t i = 0; i < in->ntargets; i++) {
134        uint32_t v = 0;
135        /* Already checked by wasm_rd_instr: these can't fail; bound by 5 bytes each. */
136        p += wasm_leb_u32(p, p + 5, &v);
137        out[i] = v;
138    }
139    out[in->ntargets] = in->b;
140}