kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

encode.c (15183B)


      1 #include "wasm/wasm.h"
      2 #include "wasm/wasm_insn_table.h"
      3 
      4 static void write_byte(KitWriter* w, uint8_t b) { w->write(w, &b, 1); }
      5 
      6 static void write_uleb(KitWriter* w, uint64_t v) {
      7   do {
      8     uint8_t b = (uint8_t)(v & 0x7fu);
      9     v >>= 7u;
     10     if (v) b |= 0x80u;
     11     write_byte(w, b);
     12   } while (v);
     13 }
     14 
     15 static void write_sleb(KitWriter* w, int64_t v) {
     16   int more = 1;
     17   while (more) {
     18     uint8_t b = (uint8_t)(v & 0x7f);
     19     int sign = b & 0x40;
     20     v >>= 7;
     21     if ((v == 0 && !sign) || (v == -1 && sign))
     22       more = 0;
     23     else
     24       b |= 0x80u;
     25     write_byte(w, b);
     26   }
     27 }
     28 
     29 static void write_f32(KitWriter* w, double value) {
     30   float f = (float)value;
     31   uint32_t bits;
     32   memcpy(&bits, &f, sizeof bits);
     33   for (uint32_t i = 0; i < 4u; ++i) write_byte(w, (uint8_t)(bits >> (i * 8u)));
     34 }
     35 
     36 static void write_f64(KitWriter* w, double value) {
     37   uint64_t bits;
     38   memcpy(&bits, &value, sizeof bits);
     39   for (uint32_t i = 0; i < 8u; ++i) write_byte(w, (uint8_t)(bits >> (i * 8u)));
     40 }
     41 
     42 static void write_name(KitWriter* w, const char* name) {
     43   KitSlice s = kit_slice_cstr(name);
     44   write_uleb(w, s.len);
     45   if (s.len) w->write(w, s.s, s.len);
     46 }
     47 
     48 static void encode_section(KitHeap* h, KitWriter* out, uint8_t id,
     49                            void (*fn)(KitWriter*, const WasmModule*),
     50                            const WasmModule* m) {
     51   KitWriter* tmp = NULL;
     52   size_t len;
     53   const uint8_t* bytes;
     54   if (kit_writer_mem(h, &tmp) != KIT_OK) return;
     55   fn(tmp, m);
     56   bytes = kit_writer_mem_bytes(tmp, &len);
     57   write_byte(out, id);
     58   write_uleb(out, len);
     59   out->write(out, bytes, len);
     60   kit_writer_close(tmp);
     61 }
     62 
     63 static void enc_type(KitWriter* w, const WasmModule* m) {
     64   uint32_t i, j;
     65   write_uleb(w, m->ntypes);
     66   for (i = 0; i < m->ntypes; ++i) {
     67     const WasmFuncType* f = &m->types[i];
     68     write_byte(w, 0x60);
     69     write_uleb(w, f->nparams);
     70     for (j = 0; j < f->nparams; ++j) write_byte(w, (uint8_t)f->params[j]);
     71     write_uleb(w, f->nresults);
     72     for (j = 0; j < f->nresults; ++j) write_byte(w, (uint8_t)f->results[j]);
     73   }
     74 }
     75 
     76 static void write_memory_limits(KitWriter* w, const WasmMemory* mem) {
     77   uint32_t flags = (mem->has_max ? 1u : 0u) | (mem->shared ? 2u : 0u) |
     78                    (mem->is64 ? 4u : 0u);
     79   write_uleb(w, flags);
     80   write_uleb(w, mem->min_pages);
     81   if (mem->has_max) write_uleb(w, mem->max_pages);
     82 }
     83 
     84 static void write_limits(KitWriter* w, uint32_t min, uint32_t max,
     85                          int has_max) {
     86   write_uleb(w, has_max ? 1 : 0);
     87   write_uleb(w, min);
     88   if (has_max) write_uleb(w, max);
     89 }
     90 
     91 static void enc_import(KitWriter* w, const WasmModule* m) {
     92   uint32_t i, n = 0;
     93   for (i = 0; i < m->nfuncs; ++i)
     94     if (m->funcs[i].is_import) n++;
     95   for (i = 0; i < m->ntables; ++i)
     96     if (m->tables[i].is_import) n++;
     97   for (i = 0; i < m->nmemories; ++i)
     98     if (m->memories[i].is_import) n++;
     99   for (i = 0; i < m->nglobals; ++i)
    100     if (m->globals[i].is_import) n++;
    101   write_uleb(w, n);
    102   for (i = 0; i < m->nfuncs; ++i) {
    103     const WasmFunc* f = &m->funcs[i];
    104     if (!f->is_import) continue;
    105     write_name(w, f->import_module);
    106     write_name(w, f->import_name);
    107     write_byte(w, 0);
    108     write_uleb(w, f->typeidx);
    109   }
    110   for (i = 0; i < m->ntables; ++i) {
    111     const WasmTable* t = &m->tables[i];
    112     if (!t->is_import) continue;
    113     write_name(w, t->import_module);
    114     write_name(w, t->import_name);
    115     write_byte(w, 1);
    116     write_byte(w, (uint8_t)t->elem_type);
    117     write_limits(w, t->min, t->max, t->has_max);
    118   }
    119   for (i = 0; i < m->nmemories; ++i) {
    120     const WasmMemory* mem = &m->memories[i];
    121     if (!mem->is_import) continue;
    122     write_name(w, mem->import_module);
    123     write_name(w, mem->import_name);
    124     write_byte(w, 2);
    125     write_memory_limits(w, mem);
    126   }
    127   for (i = 0; i < m->nglobals; ++i) {
    128     const WasmGlobal* g = &m->globals[i];
    129     if (!g->is_import) continue;
    130     write_name(w, g->import_module);
    131     write_name(w, g->import_name);
    132     write_byte(w, 3);
    133     write_byte(w, (uint8_t)g->type);
    134     write_byte(w, g->mutable_);
    135   }
    136 }
    137 
    138 static int wasm_module_has_imports(const WasmModule* m) {
    139   uint32_t i;
    140   for (i = 0; i < m->nfuncs; ++i)
    141     if (m->funcs[i].is_import) return 1;
    142   for (i = 0; i < m->ntables; ++i)
    143     if (m->tables[i].is_import) return 1;
    144   for (i = 0; i < m->nmemories; ++i)
    145     if (m->memories[i].is_import) return 1;
    146   for (i = 0; i < m->nglobals; ++i)
    147     if (m->globals[i].is_import) return 1;
    148   return 0;
    149 }
    150 
    151 static void enc_func(KitWriter* w, const WasmModule* m) {
    152   uint32_t i;
    153   uint32_t n = 0;
    154   for (i = 0; i < m->nfuncs; ++i)
    155     if (!m->funcs[i].is_import) n++;
    156   write_uleb(w, n);
    157   for (i = 0; i < m->nfuncs; ++i)
    158     if (!m->funcs[i].is_import) write_uleb(w, m->funcs[i].typeidx);
    159 }
    160 
    161 static void enc_export(KitWriter* w, const WasmModule* m) {
    162   uint32_t i;
    163   write_uleb(w, m->nexports);
    164   for (i = 0; i < m->nexports; ++i) {
    165     write_name(w, m->exports[i].name);
    166     write_byte(w, m->exports[i].kind);
    167     write_uleb(w, m->exports[i].index);
    168   }
    169 }
    170 
    171 static void enc_memory(KitWriter* w, const WasmModule* m) {
    172   uint32_t i, n = 0;
    173   for (i = 0; i < m->nmemories; ++i)
    174     if (!m->memories[i].is_import) n++;
    175   write_uleb(w, n);
    176   for (i = 0; i < m->nmemories; ++i)
    177     if (!m->memories[i].is_import) write_memory_limits(w, &m->memories[i]);
    178 }
    179 
    180 static uint8_t wasm_opcode(uint8_t kind);
    181 
    182 static void enc_table(KitWriter* w, const WasmModule* m) {
    183   uint32_t i, n = 0;
    184   for (i = 0; i < m->ntables; ++i)
    185     if (!m->tables[i].is_import) n++;
    186   write_uleb(w, n);
    187   for (i = 0; i < m->ntables; ++i) {
    188     const WasmTable* t = &m->tables[i];
    189     if (t->is_import) continue;
    190     write_byte(w, (uint8_t)t->elem_type);
    191     write_limits(w, t->min, t->max, t->has_max);
    192   }
    193 }
    194 
    195 static void enc_global(KitWriter* w, const WasmModule* m) {
    196   uint32_t i, n = 0;
    197   for (i = 0; i < m->nglobals; ++i)
    198     if (!m->globals[i].is_import) n++;
    199   write_uleb(w, n);
    200   for (i = 0; i < m->nglobals; ++i) {
    201     const WasmGlobal* g = &m->globals[i];
    202     if (g->is_import) continue;
    203     write_byte(w, (uint8_t)g->type);
    204     write_byte(w, g->mutable_);
    205     write_byte(w, wasm_opcode(g->init.kind));
    206     if (g->init.kind == WASM_INSN_I32_CONST ||
    207         g->init.kind == WASM_INSN_I64_CONST)
    208       write_sleb(w, g->init.imm);
    209     else if (g->init.kind == WASM_INSN_F32_CONST)
    210       write_f32(w, g->init.fp);
    211     else if (g->init.kind == WASM_INSN_F64_CONST)
    212       write_f64(w, g->init.fp);
    213     write_byte(w, 0x0b);
    214   }
    215 }
    216 
    217 /* Single-byte opcode for `kind` (0 if prefixed or unknown), read off the one
    218  * WASM_INSN_TABLE map. */
    219 static uint8_t wasm_opcode(uint8_t kind) {
    220   const WasmInsnInfo* info = wasm_insn_info((WasmInsnKind)kind);
    221   if (!info || info->prefix != WASM_PREFIX_NONE) return 0;
    222   return info->byte;
    223 }
    224 
    225 /* 0xfe-prefixed (threads/atomics) sub-opcode for `kind`, or UINT32_MAX. */
    226 static uint32_t wasm_threads_subopcode(uint8_t kind) {
    227   const WasmInsnInfo* info = wasm_insn_info((WasmInsnKind)kind);
    228   if (!info || info->prefix != WASM_PREFIX_FE) return UINT32_MAX;
    229   return info->byte;
    230 }
    231 
    232 /* 0xfc-prefixed sub-opcode (non-trapping FTOI + bulk memory/table) for `kind`,
    233  * or UINT32_MAX. */
    234 static uint32_t wasm_misc_subopcode(uint8_t kind) {
    235   const WasmInsnInfo* info = wasm_insn_info((WasmInsnKind)kind);
    236   if (!info || info->prefix != WASM_PREFIX_FC) return UINT32_MAX;
    237   return info->byte;
    238 }
    239 
    240 static void enc_code(KitWriter* w, const WasmModule* m) {
    241   uint32_t i, j;
    242   uint32_t n = 0;
    243   for (i = 0; i < m->nfuncs; ++i)
    244     if (!m->funcs[i].is_import) n++;
    245   write_uleb(w, n);
    246   for (i = 0; i < m->nfuncs; ++i) {
    247     KitWriter* body = NULL;
    248     size_t len;
    249     const uint8_t* bytes;
    250     if (kit_writer_mem(m->heap, &body) != KIT_OK) continue;
    251     if (m->funcs[i].is_import) {
    252       kit_writer_close(body);
    253       continue;
    254     }
    255     if (m->funcs[i].nlocals) {
    256       uint32_t group_count = 0;
    257       WasmValType prev = 0;
    258       for (j = 0; j < m->funcs[i].nlocals; ++j) {
    259         if (j == 0 || m->funcs[i].locals[j] != prev) {
    260           group_count++;
    261           prev = m->funcs[i].locals[j];
    262         }
    263       }
    264       write_uleb(body, group_count);
    265       for (j = 0; j < m->funcs[i].nlocals;) {
    266         uint32_t k = j + 1u;
    267         while (k < m->funcs[i].nlocals &&
    268                m->funcs[i].locals[k] == m->funcs[i].locals[j])
    269           k++;
    270         write_uleb(body, k - j);
    271         write_byte(body, (uint8_t)m->funcs[i].locals[j]);
    272         j = k;
    273       }
    274     } else {
    275       write_uleb(body, 0);
    276     }
    277     for (j = 0; j < m->funcs[i].ninsns; ++j) {
    278       WasmInsn in = m->funcs[i].insns[j];
    279       uint8_t op = wasm_opcode(in.kind);
    280       uint32_t threads_op = wasm_threads_subopcode(in.kind);
    281       uint32_t misc_op = wasm_misc_subopcode(in.kind);
    282       if (threads_op != UINT32_MAX) {
    283         write_byte(body, 0xfe);
    284         write_uleb(body, threads_op);
    285       } else if (misc_op != UINT32_MAX) {
    286         write_byte(body, 0xfc);
    287         write_uleb(body, misc_op);
    288       } else {
    289         write_byte(body, op);
    290       }
    291       if (in.kind == WASM_INSN_MEMORY_INIT) {
    292         write_uleb(body, (uint64_t)in.imm);
    293         write_uleb(body, in.memidx);
    294       } else if (in.kind == WASM_INSN_DATA_DROP) {
    295         write_uleb(body, (uint64_t)in.imm);
    296       } else if (in.kind == WASM_INSN_MEMORY_COPY) {
    297         write_uleb(body, in.memidx);
    298         write_uleb(body, in.aux_idx);
    299       } else if (in.kind == WASM_INSN_MEMORY_FILL) {
    300         write_uleb(body, in.memidx);
    301       } else if (in.kind == WASM_INSN_TABLE_INIT) {
    302         write_uleb(body, (uint64_t)in.imm);
    303         write_uleb(body, in.aux_idx);
    304       } else if (in.kind == WASM_INSN_ELEM_DROP) {
    305         write_uleb(body, (uint64_t)in.imm);
    306       } else if (in.kind == WASM_INSN_TABLE_COPY) {
    307         write_uleb(body, (uint64_t)in.imm);
    308         write_uleb(body, in.aux_idx);
    309       } else if (in.kind == WASM_INSN_TABLE_GROW ||
    310                  in.kind == WASM_INSN_TABLE_SIZE ||
    311                  in.kind == WASM_INSN_TABLE_FILL) {
    312         write_uleb(body, (uint64_t)in.imm);
    313       } else if (in.kind == WASM_INSN_ATOMIC_FENCE) {
    314         write_byte(body, 0);
    315       } else if (in.kind == WASM_INSN_BLOCK || in.kind == WASM_INSN_LOOP ||
    316                  in.kind == WASM_INSN_IF)
    317         write_byte(body, 0x40);
    318       else if (in.kind == WASM_INSN_I32_CONST || in.kind == WASM_INSN_I64_CONST)
    319         write_sleb(body, in.imm);
    320       else if (in.kind == WASM_INSN_F32_CONST)
    321         write_f32(body, in.fp);
    322       else if (in.kind == WASM_INSN_F64_CONST)
    323         write_f64(body, in.fp);
    324       else if (in.kind == WASM_INSN_LOCAL_GET ||
    325                in.kind == WASM_INSN_LOCAL_SET ||
    326                in.kind == WASM_INSN_LOCAL_TEE ||
    327                in.kind == WASM_INSN_GLOBAL_GET ||
    328                in.kind == WASM_INSN_GLOBAL_SET || in.kind == WASM_INSN_CALL ||
    329                in.kind == WASM_INSN_RETURN_CALL ||
    330                in.kind == WASM_INSN_CALL_REF ||
    331                in.kind == WASM_INSN_RETURN_CALL_REF ||
    332                in.kind == WASM_INSN_REF_NULL || in.kind == WASM_INSN_REF_FUNC ||
    333                in.kind == WASM_INSN_BR || in.kind == WASM_INSN_BR_IF)
    334         write_uleb(body, (uint64_t)in.imm);
    335       else if (in.kind == WASM_INSN_CALL_INDIRECT ||
    336                in.kind == WASM_INSN_RETURN_CALL_INDIRECT) {
    337         write_uleb(body, (uint64_t)in.imm);
    338         write_uleb(body, in.align);
    339       } else if (in.kind == WASM_INSN_BR_TABLE) {
    340         write_uleb(body, in.ntargets ? in.ntargets - 1u : 0u);
    341         for (uint32_t k = 0; k < in.ntargets; ++k)
    342           write_uleb(body, in.targets[k]);
    343       } else if (wasm_insn_is_mem(in.kind)) {
    344         write_uleb(body, in.memidx ? in.align + 64u : in.align);
    345         if (in.memidx) write_uleb(body, in.memidx);
    346         write_uleb(body, in.offset64);
    347       } else if (in.kind == WASM_INSN_MEMORY_SIZE ||
    348                  in.kind == WASM_INSN_MEMORY_GROW) {
    349         write_uleb(body, in.memidx);
    350       }
    351     }
    352     write_byte(body, 0x0b);
    353     bytes = kit_writer_mem_bytes(body, &len);
    354     write_uleb(w, len);
    355     w->write(w, bytes, len);
    356     kit_writer_close(body);
    357   }
    358 }
    359 
    360 static void enc_data(KitWriter* w, const WasmModule* m) {
    361   uint32_t i;
    362   write_uleb(w, m->ndata);
    363   for (i = 0; i < m->ndata; ++i) {
    364     const WasmDataSegment* d = &m->data[i];
    365     int is64 = 0;
    366     if (d->mode == WASM_SEG_PASSIVE) {
    367       write_uleb(w, 1u);
    368     } else {
    369       if (d->memidx < m->nmemories) is64 = m->memories[d->memidx].is64;
    370       if (d->memidx == 0u) {
    371         write_uleb(w, 0u);
    372       } else {
    373         write_uleb(w, 2u);
    374         write_uleb(w, d->memidx);
    375       }
    376       write_byte(w, is64 ? 0x42 : 0x41);
    377       write_sleb(w, d->offset);
    378       write_byte(w, 0x0b);
    379     }
    380     write_uleb(w, d->nbytes);
    381     if (d->nbytes) w->write(w, d->bytes, (size_t)d->nbytes);
    382   }
    383 }
    384 
    385 static void enc_elem(KitWriter* w, const WasmModule* m) {
    386   uint32_t i, j;
    387   write_uleb(w, m->nelems);
    388   for (i = 0; i < m->nelems; ++i) {
    389     const WasmElemSegment* e = &m->elems[i];
    390     if (e->mode == WASM_SEG_PASSIVE) {
    391       write_uleb(w, 1u);
    392       write_byte(w, 0x00); /* elemkind: funcref */
    393     } else if (e->mode == WASM_SEG_DECLARATIVE) {
    394       write_uleb(w, 3u);
    395       write_byte(w, 0x00);
    396     } else if (e->tableidx != 0) {
    397       write_uleb(w, 2u);
    398       write_uleb(w, e->tableidx);
    399       write_byte(w, 0x41);
    400       write_sleb(w, e->offset);
    401       write_byte(w, 0x0b);
    402       write_byte(w, 0x00);
    403     } else {
    404       write_uleb(w, 0u);
    405       write_byte(w, 0x41);
    406       write_sleb(w, e->offset);
    407       write_byte(w, 0x0b);
    408     }
    409     write_uleb(w, e->nfuncs);
    410     for (j = 0; j < e->nfuncs; ++j) write_uleb(w, e->funcs[j]);
    411   }
    412 }
    413 
    414 static void enc_start(KitWriter* w, const WasmModule* m) {
    415   write_uleb(w, m->start_func);
    416 }
    417 
    418 static void encode_custom(KitHeap* h, KitWriter* out, const WasmModule* m,
    419                           const WasmCustom* cs) {
    420   KitWriter* tmp = NULL;
    421   size_t len;
    422   const uint8_t* bytes;
    423   if (kit_writer_mem(h, &tmp) != KIT_OK) return;
    424   (void)m;
    425   write_name(tmp, cs->name);
    426   if (cs->len) tmp->write(tmp, cs->data, cs->len);
    427   bytes = kit_writer_mem_bytes(tmp, &len);
    428   write_byte(out, 0);
    429   write_uleb(out, len);
    430   out->write(out, bytes, len);
    431   kit_writer_close(tmp);
    432 }
    433 
    434 void wasm_encode(KitCompiler* c, const WasmModule* m, KitWriter* out) {
    435   static const uint8_t magic[] = {0x00, 0x61, 0x73, 0x6d,
    436                                   0x01, 0x00, 0x00, 0x00};
    437   KitHeap* h = kit_compiler_context(c)->heap;
    438   out->write(out, magic, sizeof magic);
    439   for (uint32_t i = 0; i < m->ncustoms; ++i)
    440     encode_custom(h, out, m, &m->customs[i]);
    441   encode_section(h, out, 1, enc_type, m);
    442   if (wasm_module_has_imports(m)) encode_section(h, out, 2, enc_import, m);
    443   encode_section(h, out, 3, enc_func, m);
    444   if (m->ntables) encode_section(h, out, 4, enc_table, m);
    445   {
    446     int has_defined_memory = 0;
    447     for (uint32_t i = 0; i < m->nmemories; ++i)
    448       if (!m->memories[i].is_import) has_defined_memory = 1;
    449     if (has_defined_memory) encode_section(h, out, 5, enc_memory, m);
    450   }
    451   if (m->nglobals) encode_section(h, out, 6, enc_global, m);
    452   encode_section(h, out, 7, enc_export, m);
    453   if (m->has_start) encode_section(h, out, 8, enc_start, m);
    454   if (m->nelems) encode_section(h, out, 9, enc_elem, m);
    455   encode_section(h, out, 10, enc_code, m);
    456   if (m->ndata) encode_section(h, out, 11, enc_data, m);
    457 }