wasm_printer/wasm/
mod.rs

1use byteorder::{ByteOrder, LittleEndian};
2use core_wasm_ast as ast;
3use log::warn;
4use std::io::Write;
5
6type BoxError = Box<dyn std::error::Error + Sync + Send>;
7
8pub fn print(module: &ast::Module) -> Result<Vec<u8>, BoxError> {
9    let mut buffer = vec![];
10
11    write_header(&mut buffer)?;
12
13    for section in module.sections.lock().unwrap().iter() {
14        write_section(&mut buffer, &section.value)?;
15    }
16
17    Ok(buffer)
18}
19
20pub fn write_header(buffer: &mut Vec<u8>) -> Result<(), BoxError> {
21    buffer.write(b"\0asm")?;
22    buffer.write(&1u32.to_le_bytes())?;
23    Ok(())
24}
25
26macro_rules! write_section {
27    ($b:expr, $o:expr, $id:expr, $write_fn:expr) => {
28        $b.push($id); // section id
29
30        let before_offset = $b.len();
31        $b.push(0x0); // section bytes
32
33        $write_fn($b, &$o)?;
34
35        let after_offset = $b.len();
36
37        // section fixup
38        let section_len = after_offset - before_offset - 1;
39
40        // section length - fixup
41        write_unsigned_leb128_at_offset($b, before_offset, section_len);
42    };
43}
44
45pub fn write_section(buffer: &mut Vec<u8>, section: &ast::Section) -> Result<(), BoxError> {
46    match section {
47        ast::Section::Import((_size, content)) => {
48            write_section!(buffer, content.lock().unwrap(), 2, write_section_import);
49            Ok(())
50        }
51        ast::Section::Table((_size, content)) => {
52            write_section!(buffer, content.lock().unwrap(), 4, write_section_table);
53            Ok(())
54        }
55        ast::Section::Memory((_size, content)) => {
56            write_section!(buffer, content, 5, write_section_memory);
57            Ok(())
58        }
59        ast::Section::Code((_size, content)) => {
60            write_section!(buffer, content.lock().unwrap(), 10, write_section_code);
61            Ok(())
62        }
63        ast::Section::Type((_size, content)) => {
64            write_section!(buffer, content.lock().unwrap(), 1, write_section_type);
65            Ok(())
66        }
67        ast::Section::Element((_size, content)) => {
68            write_section!(buffer, content.lock().unwrap(), 9, write_section_element);
69            Ok(())
70        }
71        ast::Section::Custom((_size, content)) => {
72            write_section!(buffer, content.lock().unwrap(), 0, write_section_custom);
73            Ok(())
74        }
75        ast::Section::Unknown((id, size, content)) => {
76            buffer.push(*id);
77            write_unsigned_leb128(buffer, *size as u64);
78            buffer.write(content)?;
79            Ok(())
80        }
81        ast::Section::Data((_size, content)) => {
82            write_section!(buffer, content.lock().unwrap(), 11, write_section_data);
83            Ok(())
84        }
85        ast::Section::Func((_size, content)) => {
86            write_section!(buffer, content.lock().unwrap(), 3, write_section_func);
87            Ok(())
88        }
89        ast::Section::Export((_size, content)) => {
90            write_section!(buffer, content.lock().unwrap(), 7, write_section_export);
91            Ok(())
92        }
93        ast::Section::Global((_size, content)) => {
94            write_section!(buffer, content.lock().unwrap(), 6, write_section_global);
95            Ok(())
96        }
97    }
98}
99
100fn write_section_import(buffer: &mut Vec<u8>, content: &Vec<ast::Import>) -> Result<(), BoxError> {
101    write_vec_len(buffer, content); // vec length
102
103    for import in content {
104        write_utf8(buffer, &import.module);
105        write_utf8(buffer, &import.name);
106        match &import.import_type {
107            ast::ImportType::Func(funcidx) => {
108                buffer.push(0x0);
109                write_unsigned_leb128(buffer, *funcidx as u64);
110            }
111
112            ast::ImportType::Table(table) => {
113                buffer.push(0x1);
114                write_table(buffer, table)?;
115            }
116
117            ast::ImportType::Memory(mem) => {
118                buffer.push(0x2);
119                write_memory(buffer, mem)?;
120            }
121
122            ast::ImportType::Global(globaltype) => {
123                buffer.push(0x3);
124
125                write_value_type(buffer, &globaltype.valtype);
126                if globaltype.mutable {
127                    buffer.push(0x01);
128                } else {
129                    buffer.push(0x00);
130                }
131            }
132        }
133    }
134
135    Ok(())
136}
137
138fn write_section_table(buffer: &mut Vec<u8>, content: &Vec<ast::Table>) -> Result<(), BoxError> {
139    write_vec_len(buffer, content); // vec length
140
141    for table in content {
142        write_table(buffer, table)?;
143    }
144
145    Ok(())
146}
147
148fn write_table(buffer: &mut Vec<u8>, table: &ast::Table) -> Result<(), BoxError> {
149    write_reftype(buffer, &table.reftype);
150    write_limits(buffer, &table.limits);
151    Ok(())
152}
153
154fn write_limits(buffer: &mut Vec<u8>, limits: &ast::Limits) {
155    if let Some(max) = limits.max {
156        buffer.push(0x01);
157        write_unsigned_leb128(buffer, limits.min as u64);
158        write_unsigned_leb128(buffer, max as u64);
159    } else {
160        buffer.push(0x00);
161        write_unsigned_leb128(buffer, limits.min as u64);
162    }
163}
164
165fn write_reftype(buffer: &mut Vec<u8>, typeref: &ast::Reftype) {
166    let b = match typeref {
167        ast::Reftype::Func => 0x70,
168        ast::Reftype::Extern => 0x6F,
169    };
170    buffer.push(b);
171}
172
173pub(crate) fn write_utf8(buffer: &mut Vec<u8>, v: &str) {
174    let bytes = v.as_bytes().to_vec();
175    write_vec_len(buffer, &bytes);
176    buffer.write_all(&bytes).unwrap();
177}
178
179fn write_section_memory(buffer: &mut Vec<u8>, content: &Vec<ast::Memory>) -> Result<(), BoxError> {
180    write_vec_len(buffer, content); // vec length
181
182    for mem in content {
183        write_memory(buffer, mem)?;
184    }
185
186    Ok(())
187}
188
189fn write_memory(buffer: &mut Vec<u8>, mem: &ast::Memory) -> Result<(), BoxError> {
190    if let Some(max) = mem.max {
191        buffer.push(0x1);
192        write_unsigned_leb128(buffer, mem.min.value as u64);
193        write_unsigned_leb128(buffer, max as u64);
194    } else {
195        buffer.push(0x0);
196        write_unsigned_leb128(buffer, mem.min.value as u64);
197    }
198
199    Ok(())
200}
201
202fn write_section_global(buffer: &mut Vec<u8>, content: &Vec<ast::Global>) -> Result<(), BoxError> {
203    write_vec_len(buffer, &content); // vec length
204
205    for global in content {
206        write_value_type(buffer, &global.global_type.valtype);
207        if global.global_type.mutable {
208            buffer.push(0x01);
209        } else {
210            buffer.push(0x00);
211        }
212
213        write_code_expr(buffer, &global.expr.value);
214    }
215
216    Ok(())
217}
218
219fn write_section_export(buffer: &mut Vec<u8>, content: &Vec<ast::Export>) -> Result<(), BoxError> {
220    write_vec_len(buffer, &content); // vec length
221
222    for export in content {
223        write_utf8(buffer, &export.name);
224        match &export.descr {
225            ast::ExportDescr::Func(idx) => {
226                buffer.push(0x00);
227                write_unsigned_leb128(buffer, *idx.lock().unwrap() as u64);
228            }
229            ast::ExportDescr::Table(idx) => {
230                buffer.push(0x01);
231                write_unsigned_leb128(buffer, *idx.lock().unwrap() as u64);
232            }
233            ast::ExportDescr::Mem(idx) => {
234                buffer.push(0x02);
235                write_unsigned_leb128(buffer, *idx.lock().unwrap() as u64);
236            }
237            ast::ExportDescr::Global(idx) => {
238                buffer.push(0x03);
239                write_unsigned_leb128(buffer, *idx.lock().unwrap() as u64);
240            }
241        };
242    }
243
244    Ok(())
245}
246
247fn write_section_func(buffer: &mut Vec<u8>, content: &Vec<u32>) -> Result<(), BoxError> {
248    write_vec_len(buffer, &content); // vec length
249
250    for idx in content {
251        write_unsigned_leb128(buffer, *idx as u64);
252    }
253
254    Ok(())
255}
256
257fn write_section_data(
258    buffer: &mut Vec<u8>,
259    content: &Vec<ast::DataSegment>,
260) -> Result<(), BoxError> {
261    write_vec_len(buffer, &content); // vec length
262
263    for data_segment in content {
264        if data_segment.mode == ast::DataSegmentMode::Active {
265            write_unsigned_leb128(buffer, 0);
266            if let Some(offset) = &data_segment.offset {
267                write_code_expr(buffer, &offset.value);
268            }
269        } else {
270            write_unsigned_leb128(buffer, 1);
271        }
272
273        write_vec_len(buffer, &data_segment.bytes); // vec length
274        buffer.write(&data_segment.bytes)?;
275    }
276
277    Ok(())
278}
279
280fn write_section_type(buffer: &mut Vec<u8>, content: &Vec<ast::Type>) -> Result<(), BoxError> {
281    write_vec_len(buffer, &content); // vec length
282
283    for t in content {
284        buffer.push(0x60);
285
286        write_vec_len(buffer, &t.params); // vec length
287        for param in &t.params {
288            write_value_type(buffer, param);
289        }
290
291        write_vec_len(buffer, &t.results); // vec length
292        for result in &t.results {
293            write_value_type(buffer, result);
294        }
295    }
296    Ok(())
297}
298
299fn write_section_custom(
300    buffer: &mut Vec<u8>,
301    content: &ast::CustomSection,
302) -> Result<(), BoxError> {
303    match content {
304        ast::CustomSection::Unknown(name, bytes) => {
305            write_utf8(buffer, &name);
306            buffer.extend_from_slice(&bytes);
307        }
308        ast::CustomSection::Name(content) => {
309            write_utf8(buffer, "name");
310            write_section_custom_name(buffer, &content)?
311        }
312
313        ast::CustomSection::BuildId(id) => {
314            write_utf8(buffer, "build_id");
315            write_section_custom_build_id(buffer, id)?
316        }
317
318        ast::CustomSection::CoredumpCore(content) => {
319            write_utf8(buffer, "core");
320            wasm_coredump_encoder::encode_coredump_process(buffer, content)?;
321        }
322
323        ast::CustomSection::CoredumpCoreStack(content) => {
324            write_utf8(buffer, "corestack");
325            wasm_coredump_encoder::encode_coredump_stack(buffer, content)?;
326        }
327    }
328
329    Ok(())
330}
331
332fn write_section_custom_build_id(buffer: &mut Vec<u8>, id: &[u8]) -> Result<(), BoxError> {
333    write_unsigned_leb128(buffer, id.len() as u64);
334    buffer.extend_from_slice(id);
335    Ok(())
336}
337
338pub fn write_section_custom_name(
339    buffer: &mut Vec<u8>,
340    content: &ast::DebugNames,
341) -> Result<(), BoxError> {
342    if let Some(_module_name) = &content.module {
343        warn!("Module Name not implemented yet")
344    }
345
346    if let Some(func_names) = &content.func_names {
347        buffer.push(1);
348
349        let mut subsection = vec![];
350        {
351            let func_names = func_names.lock().unwrap();
352
353            write_unsigned_leb128(&mut subsection, func_names.len() as u64);
354
355            for funcidx in 0..func_names.len() {
356                if let Some(name) = func_names.get(&(funcidx as u32)) {
357                    write_unsigned_leb128(&mut subsection, funcidx as u64);
358                    write_utf8(&mut subsection, name);
359                }
360            }
361        }
362
363        write_unsigned_leb128(buffer, subsection.len() as u64);
364        buffer.extend_from_slice(&subsection)
365    }
366
367    // Global names
368    {
369        buffer.push(7);
370
371        let mut subsection = vec![];
372        {
373            let global_names = content.global_names.lock().unwrap();
374
375            write_unsigned_leb128(&mut subsection, global_names.len() as u64);
376
377            for idx in 0..global_names.len() {
378                if let Some(name) = global_names.get(&(idx as u32)) {
379                    write_unsigned_leb128(&mut subsection, idx as u64);
380                    write_utf8(&mut subsection, name);
381                }
382            }
383        }
384
385        write_unsigned_leb128(buffer, subsection.len() as u64);
386        buffer.extend_from_slice(&subsection)
387    }
388
389    Ok(())
390}
391
392fn write_section_element(
393    buffer: &mut Vec<u8>,
394    content: &Vec<ast::Element>,
395) -> Result<(), BoxError> {
396    write_vec_len(buffer, &content); // vec length
397
398    for t in content {
399        match t {
400            ast::Element::FuncActive(expr, vec) => {
401                buffer.push(0);
402                write_code_expr(buffer, &expr.value);
403                write_vec_len(buffer, &vec.lock().unwrap());
404                for funcidx in vec.lock().unwrap().iter() {
405                    write_unsigned_leb128(buffer, *funcidx as u64);
406                }
407            }
408        }
409    }
410    Ok(())
411}
412
413fn write_section_code(
414    buffer: &mut Vec<u8>,
415    content: &ast::Value<Vec<ast::Code>>,
416) -> Result<(), BoxError> {
417    write_vec_len(buffer, &content.value); // vec length
418
419    for func in &content.value {
420        let before_offset = buffer.len();
421        buffer.push(0x0); // func size, going to be fixed.
422
423        write_code_local(buffer, &func.locals);
424        write_code_expr(buffer, &func.body.lock().unwrap().value);
425
426        // func size fixup
427        {
428            let after_offset = buffer.len();
429            let func_len = after_offset - before_offset - 1;
430
431            write_unsigned_leb128_at_offset(buffer, before_offset, func_len);
432        }
433    }
434    Ok(())
435}
436
437fn write_vec_len<T>(buffer: &mut Vec<u8>, vec: &Vec<T>) {
438    write_unsigned_leb128(buffer, vec.len() as u64);
439}
440
441fn write_code_local(buffer: &mut Vec<u8>, locals: &Vec<ast::CodeLocal>) {
442    write_vec_len(buffer, locals); // vec length
443
444    for local in locals {
445        write_unsigned_leb128(buffer, local.count as u64);
446        write_value_type(buffer, &local.value_type);
447    }
448}
449
450fn write_value_type(buffer: &mut Vec<u8>, value_type: &ast::ValueType) {
451    use ast::NumType::*;
452    use ast::ValueType::*;
453    let b: u8 = match value_type {
454        NumType(I32) => 0x7F,
455        NumType(I64) => 0x7E,
456        NumType(F32) => 0x7D,
457        NumType(F64) => 0x7C,
458    };
459    buffer.push(b);
460}
461
462fn write_code_expr(buffer: &mut Vec<u8>, expr: &Vec<ast::Value<ast::Instr>>) {
463    for instr in expr {
464        let id = instr.value.clone();
465
466        macro_rules! write_instr {
467            ($byte:expr, $instr:ident) => {
468                if matches!(id, ast::Instr::$instr) {
469                    buffer.push($byte);
470                    continue;
471                }
472            };
473            ($byte:expr, $instr:ident(u8)) => {
474                if let ast::Instr::$instr(imm0) = id {
475                    buffer.push($byte);
476                    buffer.push(imm0);
477                    continue;
478                }
479            };
480
481            ($byte:expr, $instr:ident(f32)) => {
482                if let ast::Instr::$instr(imm0) = id {
483                    buffer.push($byte);
484                    write_float_f32(buffer, imm0);
485                    continue;
486                }
487            };
488
489            ($byte:expr, $instr:ident(f64)) => {
490                if let ast::Instr::$instr(imm0) = id {
491                    buffer.push($byte);
492                    write_float_f64(buffer, imm0);
493                    continue;
494                }
495            };
496
497            ($byte:expr, $instr:ident(i32)) => {
498                if let ast::Instr::$instr(imm0) = id {
499                    buffer.push($byte);
500                    write_signed_leb128(buffer, imm0 as i64);
501                    continue;
502                }
503            };
504
505            ($byte:expr, $instr:ident(i64)) => {
506                if let ast::Instr::$instr(imm0) = id {
507                    buffer.push($byte);
508                    write_signed_leb128(buffer, imm0);
509                    continue;
510                }
511            };
512
513            ($byte:expr, $instr:ident(u32)) => {
514                if let ast::Instr::$instr(imm0) = id {
515                    buffer.push($byte);
516                    write_unsigned_leb128(buffer, imm0 as u64);
517                    continue;
518                }
519            };
520
521            ($byte:expr, $instr:ident(MutableValue<u32>)) => {
522                if let ast::Instr::$instr(imm0) = id {
523                    buffer.push($byte);
524                    write_unsigned_leb128(buffer, imm0.lock().unwrap().value as u64);
525                    continue;
526                }
527            };
528
529            ($byte:expr, $instr:ident(u32, u32)) => {
530                if let ast::Instr::$instr(imm0, imm1) = id {
531                    buffer.push($byte);
532                    write_unsigned_leb128(buffer, imm0 as u64);
533                    write_unsigned_leb128(buffer, imm1 as u64);
534                    continue;
535                }
536            };
537
538            ($byte:expr, $instr:ident(MutableValue<u32>, u32)) => {
539                if let ast::Instr::$instr(imm0, imm1) = id {
540                    buffer.push($byte);
541                    let imm0 = imm0.lock().unwrap().value;
542                    write_unsigned_leb128(buffer, imm0 as u64);
543                    write_unsigned_leb128(buffer, imm1 as u64);
544                    continue;
545                }
546            };
547
548            ($byte:expr, $instr:ident(Vec<u32>, u32)) => {
549                if let ast::Instr::$instr(imm0, imm1) = id.clone() {
550                    buffer.push($byte);
551                    write_vec_len(buffer, &imm0); // vec lengh
552                    for imm in imm0 {
553                        write_unsigned_leb128(buffer, imm as u64);
554                    }
555                    write_unsigned_leb128(buffer, imm1 as u64);
556                    continue;
557                }
558            };
559        }
560
561        write_instr!(0x00, unreachable);
562        write_instr!(0x01, nop);
563
564        if let ast::Instr::Block(ref block_type, body) = id {
565            buffer.push(0x02);
566            write_blocktype(buffer, block_type);
567            write_code_expr(buffer, &body.lock().unwrap().value);
568            continue;
569        }
570
571        if let ast::Instr::Loop(ref block_type, body) = id {
572            buffer.push(0x03);
573            write_blocktype(buffer, block_type);
574            write_code_expr(buffer, &body.lock().unwrap().value);
575            continue;
576        }
577
578        if let ast::Instr::If(ref block_type, body) = id {
579            // FIXME: support IfElse, If will contain both
580            buffer.push(0x04);
581            write_blocktype(buffer, block_type);
582            write_code_expr(buffer, &body.lock().unwrap().value);
583            continue;
584        }
585
586        write_instr!(0xc, br(u32));
587        write_instr!(0xd, br_if(u32));
588        write_instr!(0x0b, end);
589        write_instr!(0x05, else_end);
590        write_instr!(0x0e, br_table(Vec<u32>, u32));
591        write_instr!(0x0f, Return);
592        write_instr!(0x10, call(MutableValue<u32>));
593        write_instr!(0x11, call_indirect(u32, u32));
594
595        write_instr!(0x1a, drop);
596        write_instr!(0x1b, select);
597        // write_instr!(0x1c, select);
598
599        write_instr!(0x20, local_get(u32));
600        write_instr!(0x21, local_set(u32));
601        write_instr!(0x22, local_tee(u32));
602        write_instr!(0x23, global_get(u32));
603        write_instr!(0x24, global_set(u32));
604        write_instr!(0x25, table_get(u32));
605        write_instr!(0x26, table_set(u32));
606
607        write_instr!(0x28, i32_load(MutableValue<u32>, u32));
608        write_instr!(0x29, i64_load(MutableValue<u32>, u32));
609        write_instr!(0x2a, f32_load(MutableValue<u32>, u32));
610        write_instr!(0x2b, f64_load(MutableValue<u32>, u32));
611        write_instr!(0x2c, i32_load8_s(MutableValue<u32>, u32));
612        write_instr!(0x2d, i32_load8_u(MutableValue<u32>, u32));
613        write_instr!(0x2e, i32_load16_s(MutableValue<u32>, u32));
614        write_instr!(0x2f, i32_load16_u(MutableValue<u32>, u32));
615        write_instr!(0x30, i64_load8_s(MutableValue<u32>, u32));
616        write_instr!(0x31, i64_load8_u(MutableValue<u32>, u32));
617        write_instr!(0x32, i64_load16_s(MutableValue<u32>, u32));
618        write_instr!(0x33, i64_load16_u(MutableValue<u32>, u32));
619        write_instr!(0x34, i64_load32_s(MutableValue<u32>, u32));
620        write_instr!(0x35, i64_load32_u(MutableValue<u32>, u32));
621
622        write_instr!(0x36, i32_store(MutableValue<u32>, u32));
623        write_instr!(0x37, i64_store(MutableValue<u32>, u32));
624        write_instr!(0x38, f32_store(MutableValue<u32>, u32));
625        write_instr!(0x39, f64_store(MutableValue<u32>, u32));
626        write_instr!(0x3a, i32_store8(MutableValue<u32>, u32));
627        write_instr!(0x3b, i32_store16(MutableValue<u32>, u32));
628        write_instr!(0x3c, i64_store8(MutableValue<u32>, u32));
629        write_instr!(0x3d, i64_store16(MutableValue<u32>, u32));
630        write_instr!(0x3e, i64_store32(MutableValue<u32>, u32));
631
632        write_instr!(0x3f, memory_size(u8));
633        write_instr!(0x40, memory_grow(u8));
634
635        write_instr!(0x41, i32_const(i32));
636        write_instr!(0x42, i64_const(i64));
637        write_instr!(0x43, f32_const(f32));
638        write_instr!(0x44, f64_const(f64));
639
640        write_instr!(0x45, i32_eqz);
641        write_instr!(0x46, i32_eq);
642        write_instr!(0x47, i32_ne);
643        write_instr!(0x48, i32_lt_s);
644        write_instr!(0x49, i32_lt_u);
645        write_instr!(0x4a, i32_gt_s);
646        write_instr!(0x4b, i32_gt_u);
647        write_instr!(0x4c, i32_le_s);
648        write_instr!(0x4d, i32_le_u);
649        write_instr!(0x4e, i32_ge_s);
650        write_instr!(0x4f, i32_ge_u);
651
652        write_instr!(0x50, i64_eqz);
653        write_instr!(0x51, i64_eq);
654        write_instr!(0x52, i64_ne);
655        write_instr!(0x53, i64_lt_s);
656        write_instr!(0x54, i64_lt_u);
657        write_instr!(0x55, i64_gt_s);
658        write_instr!(0x56, i64_gt_u);
659        write_instr!(0x57, i64_le_s);
660        write_instr!(0x58, i64_le_u);
661        write_instr!(0x59, i64_ge_s);
662        write_instr!(0x5a, i64_ge_u);
663
664        write_instr!(0x5b, f32_eq);
665        write_instr!(0x5c, f32_ne);
666        write_instr!(0x5d, f32_lt);
667        write_instr!(0x5e, f32_gt);
668        write_instr!(0x5f, f32_le);
669        write_instr!(0x60, f32_ge);
670
671        write_instr!(0x61, f64_eq);
672        write_instr!(0x62, f64_ne);
673        write_instr!(0x63, f64_lt);
674        write_instr!(0x64, f64_gt);
675        write_instr!(0x65, f64_le);
676        write_instr!(0x66, f64_ge);
677
678        write_instr!(0x67, i32_clz);
679        write_instr!(0x68, i32_ctz);
680        write_instr!(0x69, i32_popcnt);
681        write_instr!(0x6a, i32_add);
682        write_instr!(0x6b, i32_sub);
683        write_instr!(0x6c, i32_mul);
684        write_instr!(0x6d, i32_div_s);
685        write_instr!(0x6e, i32_div_u);
686        write_instr!(0x6f, i32_rem_s);
687        write_instr!(0x70, i32_rem_u);
688        write_instr!(0x71, i32_and);
689        write_instr!(0x72, i32_or);
690        write_instr!(0x73, i32_xor);
691        write_instr!(0x74, i32_shl);
692        write_instr!(0x75, i32_shr_s);
693        write_instr!(0x76, i32_shr_u);
694        write_instr!(0x77, i32_rotl);
695        write_instr!(0x78, i32_rotr);
696
697        write_instr!(0x79, i64_clz);
698        write_instr!(0x7a, i64_ctz);
699        write_instr!(0x7b, i64_popcnt);
700        write_instr!(0x7c, i64_add);
701        write_instr!(0x7d, i64_sub);
702        write_instr!(0x7e, i64_mul);
703        write_instr!(0x7f, i64_div_s);
704        write_instr!(0x80, i64_div_u);
705        write_instr!(0x81, i64_rem_s);
706        write_instr!(0x82, i64_rem_u);
707        write_instr!(0x83, i64_and);
708        write_instr!(0x84, i64_or);
709        write_instr!(0x85, i64_xor);
710        write_instr!(0x86, i64_shl);
711        write_instr!(0x87, i64_shr_s);
712        write_instr!(0x88, i64_shr_u);
713        write_instr!(0x89, i64_rotl);
714        write_instr!(0x8a, i64_rotr);
715
716        write_instr!(0x8b, f32_abs);
717        write_instr!(0x8c, f32_neg);
718        write_instr!(0x8d, f32_ceil);
719        write_instr!(0x8e, f32_floor);
720        write_instr!(0x8f, f32_trunc);
721        write_instr!(0x90, f32_nearest);
722        write_instr!(0x91, f32_sqrt);
723        write_instr!(0x92, f32_add);
724        write_instr!(0x93, f32_sub);
725        write_instr!(0x94, f32_mul);
726        write_instr!(0x95, f32_div);
727        write_instr!(0x96, f32_min);
728        write_instr!(0x97, f32_max);
729        write_instr!(0x98, f32_copysign);
730
731        write_instr!(0x99, f64_abs);
732        write_instr!(0x9a, f64_neg);
733        write_instr!(0x9b, f64_ceil);
734        write_instr!(0x9c, f64_floor);
735        write_instr!(0x9d, f64_trunc);
736        write_instr!(0x9e, f64_nearest);
737        write_instr!(0x9f, f64_sqrt);
738        write_instr!(0xa0, f64_add);
739        write_instr!(0xa1, f64_sub);
740        write_instr!(0xa2, f64_mul);
741        write_instr!(0xa3, f64_div);
742        write_instr!(0xa4, f64_min);
743        write_instr!(0xa5, f64_max);
744        write_instr!(0xa6, f64_copysign);
745
746        write_instr!(0xa7, i32_wrap_i64);
747        write_instr!(0xa8, i32_trunc_f32_s);
748        write_instr!(0xa9, i32_trunc_f32_u);
749        write_instr!(0xaa, i32_trunc_f64_s);
750        write_instr!(0xab, i32_trunc_f64_u);
751        write_instr!(0xac, i64_extend_i32_s);
752        write_instr!(0xad, i64_extend_i32_u);
753        write_instr!(0xae, i64_trunc_f32_s);
754        write_instr!(0xaf, i64_trunc_f32_u);
755        write_instr!(0xb0, i64_trunc_f64_s);
756        write_instr!(0xb1, i64_trunc_f64_u);
757        write_instr!(0xb2, f32_convert_i32_s);
758        write_instr!(0xb3, f32_convert_i32_u);
759        write_instr!(0xb4, f32_convert_i64_s);
760        write_instr!(0xb5, f32_convert_i64_u);
761        write_instr!(0xb6, f32_demote_f64);
762        write_instr!(0xb7, f64_convert_i32_s);
763        write_instr!(0xb8, f64_convert_i32_u);
764        write_instr!(0xb9, f64_convert_i64_s);
765        write_instr!(0xba, f64_convert_i64_u);
766        write_instr!(0xbb, f64_promote_f32);
767
768        write_instr!(0xbc, i32_reinterpret_f32);
769        write_instr!(0xbd, i64_reinterpret_f64);
770        write_instr!(0xbe, f32_reinterpret_i32);
771        write_instr!(0xbf, f64_reinterpret_i64);
772
773        write_instr!(0xc0, i32_extend8_s);
774        write_instr!(0xc1, i32_extend16_s);
775        write_instr!(0xc2, i64_extend8_s);
776        write_instr!(0xc3, i64_extend16_s);
777        write_instr!(0xc4, i64_extend32_s);
778
779        // https://webassembly.github.io/spec/core/binary/instructions.html#numeric-instructions
780        // saturating truncation instructions
781        if matches!(id, ast::Instr::i32_trunc_sat_f32_s) {
782            buffer.push(0xfc);
783            buffer.push(0);
784            continue;
785        }
786        if matches!(id, ast::Instr::i32_trunc_sat_f32_u) {
787            buffer.push(0xfc);
788            buffer.push(1);
789            continue;
790        }
791        if matches!(id, ast::Instr::i32_trunc_sat_f64_s) {
792            buffer.push(0xfc);
793            buffer.push(2);
794            continue;
795        }
796        if matches!(id, ast::Instr::i32_trunc_sat_f64_u) {
797            buffer.push(0xfc);
798            buffer.push(3);
799            continue;
800        }
801        if matches!(id, ast::Instr::i64_trunc_sat_f32_s) {
802            buffer.push(0xfc);
803            buffer.push(4);
804            continue;
805        }
806        if matches!(id, ast::Instr::i64_trunc_sat_f32_u) {
807            buffer.push(0xfc);
808            buffer.push(5);
809            continue;
810        }
811        if matches!(id, ast::Instr::i64_trunc_sat_f64_s) {
812            buffer.push(0xfc);
813            buffer.push(6);
814            continue;
815        }
816        if matches!(id, ast::Instr::i64_trunc_sat_f64_u) {
817            buffer.push(0xfc);
818            buffer.push(7);
819            continue;
820        }
821
822        if let ast::Instr::memory_copy(imm0, imm1) = id {
823            buffer.push(0xfc);
824            buffer.push(10);
825            buffer.push(imm0);
826            buffer.push(imm1);
827            continue;
828        }
829        if let ast::Instr::memory_fill(imm0) = id {
830            buffer.push(0xfc);
831            buffer.push(11);
832            buffer.push(imm0);
833            continue;
834        }
835
836        unimplemented!("unknown instruction: {:#?}", id);
837    }
838}
839
840fn write_unsigned_leb128_at_offset(bytes: &mut Vec<u8>, offset: usize, n: usize) {
841    // remove placeholder
842    bytes.remove(offset);
843
844    let mut buffer = vec![];
845
846    leb128::write::unsigned(&mut buffer, n as u64).expect("could not write LEB128");
847
848    let mut i = 0;
849    for byte in buffer {
850        bytes.insert(offset + i, byte);
851        i += 1;
852    }
853}
854
855pub(crate) fn write_unsigned_leb128(buffer: &mut Vec<u8>, n: u64) {
856    leb128::write::unsigned(buffer, n).expect("could not write LEB128");
857}
858
859fn write_signed_leb128(buffer: &mut Vec<u8>, n: i64) {
860    leb128::write::signed(buffer, n).expect("could not write LEB128");
861}
862
863fn write_float_f64(buffer: &mut Vec<u8>, n: f64) {
864    let mut b = [0; 8];
865    LittleEndian::write_f64(&mut b, n);
866    buffer.extend(b.iter())
867}
868
869fn write_float_f32(buffer: &mut Vec<u8>, n: f32) {
870    let mut b = [0; 4];
871    LittleEndian::write_f32(&mut b, n);
872    buffer.extend(b.iter())
873}
874
875fn write_blocktype(buffer: &mut Vec<u8>, block_type: &ast::BlockType) {
876    match block_type {
877        ast::BlockType::Empty => {
878            buffer.push(0x40);
879        }
880        ast::BlockType::ValueType(valtype) => {
881            write_value_type(buffer, valtype);
882        }
883        ast::BlockType::Typeidx(valtype) => {
884            write_signed_leb128(buffer, *valtype as i64);
885        }
886    }
887}