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 }