zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob 5d5d4e1c (58870B) - Raw


      1 const std = @import("std");
      2 const Allocator = std.mem.Allocator;
      3 const ArrayList = std.ArrayList;
      4 const assert = std.debug.assert;
      5 const testing = std.testing;
      6 const leb = std.leb;
      7 const mem = std.mem;
      8 const wasm = std.wasm;
      9 
     10 const Module = @import("../Module.zig");
     11 const Decl = Module.Decl;
     12 const Type = @import("../type.zig").Type;
     13 const Value = @import("../value.zig").Value;
     14 const Compilation = @import("../Compilation.zig");
     15 const LazySrcLoc = Module.LazySrcLoc;
     16 const link = @import("../link.zig");
     17 const TypedValue = @import("../TypedValue.zig");
     18 const Air = @import("../Air.zig");
     19 const Liveness = @import("../Liveness.zig");
     20 
     21 /// Wasm Value, created when generating an instruction
     22 const WValue = union(enum) {
     23     /// May be referenced but is unused
     24     none: void,
     25     /// Index of the local variable
     26     local: u32,
     27     /// Holds a memoized typed value
     28     constant: TypedValue,
     29     /// Offset position in the list of bytecode instructions
     30     code_offset: usize,
     31     /// Used for variables that create multiple locals on the stack when allocated
     32     /// such as structs and optionals.
     33     multi_value: struct {
     34         /// The index of the first local variable
     35         index: u32,
     36         /// The count of local variables this `WValue` consists of.
     37         /// i.e. an ErrorUnion has a 'count' of 2.
     38         count: u32,
     39     },
     40 };
     41 
     42 /// Wasm ops, but without input/output/signedness information
     43 /// Used for `buildOpcode`
     44 const Op = enum {
     45     @"unreachable",
     46     nop,
     47     block,
     48     loop,
     49     @"if",
     50     @"else",
     51     end,
     52     br,
     53     br_if,
     54     br_table,
     55     @"return",
     56     call,
     57     call_indirect,
     58     drop,
     59     select,
     60     local_get,
     61     local_set,
     62     local_tee,
     63     global_get,
     64     global_set,
     65     load,
     66     store,
     67     memory_size,
     68     memory_grow,
     69     @"const",
     70     eqz,
     71     eq,
     72     ne,
     73     lt,
     74     gt,
     75     le,
     76     ge,
     77     clz,
     78     ctz,
     79     popcnt,
     80     add,
     81     sub,
     82     mul,
     83     div,
     84     rem,
     85     @"and",
     86     @"or",
     87     xor,
     88     shl,
     89     shr,
     90     rotl,
     91     rotr,
     92     abs,
     93     neg,
     94     ceil,
     95     floor,
     96     trunc,
     97     nearest,
     98     sqrt,
     99     min,
    100     max,
    101     copysign,
    102     wrap,
    103     convert,
    104     demote,
    105     promote,
    106     reinterpret,
    107     extend,
    108 };
    109 
    110 /// Contains the settings needed to create an `Opcode` using `buildOpcode`.
    111 ///
    112 /// The fields correspond to the opcode name. Here is an example
    113 ///          i32_trunc_f32_s
    114 ///          ^   ^     ^   ^
    115 ///          |   |     |   |
    116 ///   valtype1   |     |   |
    117 ///     = .i32   |     |   |
    118 ///              |     |   |
    119 ///             op     |   |
    120 ///       = .trunc     |   |
    121 ///                    |   |
    122 ///             valtype2   |
    123 ///               = .f32   |
    124 ///                        |
    125 ///                width   |
    126 ///               = null   |
    127 ///                        |
    128 ///                   signed
    129 ///                   = true
    130 ///
    131 /// There can be missing fields, here are some more examples:
    132 ///   i64_load8_u
    133 ///     --> .{ .valtype1 = .i64, .op = .load, .width = 8, signed = false }
    134 ///   i32_mul
    135 ///     --> .{ .valtype1 = .i32, .op = .trunc }
    136 ///   nop
    137 ///     --> .{ .op = .nop }
    138 const OpcodeBuildArguments = struct {
    139     /// First valtype in the opcode (usually represents the type of the output)
    140     valtype1: ?wasm.Valtype = null,
    141     /// The operation (e.g. call, unreachable, div, min, sqrt, etc.)
    142     op: Op,
    143     /// Width of the operation (e.g. 8 for i32_load8_s, 16 for i64_extend16_i32_s)
    144     width: ?u8 = null,
    145     /// Second valtype in the opcode name (usually represents the type of the input)
    146     valtype2: ?wasm.Valtype = null,
    147     /// Signedness of the op
    148     signedness: ?std.builtin.Signedness = null,
    149 };
    150 
    151 /// Helper function that builds an Opcode given the arguments needed
    152 fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
    153     switch (args.op) {
    154         .@"unreachable" => return .@"unreachable",
    155         .nop => return .nop,
    156         .block => return .block,
    157         .loop => return .loop,
    158         .@"if" => return .@"if",
    159         .@"else" => return .@"else",
    160         .end => return .end,
    161         .br => return .br,
    162         .br_if => return .br_if,
    163         .br_table => return .br_table,
    164         .@"return" => return .@"return",
    165         .call => return .call,
    166         .call_indirect => return .call_indirect,
    167         .drop => return .drop,
    168         .select => return .select,
    169         .local_get => return .local_get,
    170         .local_set => return .local_set,
    171         .local_tee => return .local_tee,
    172         .global_get => return .global_get,
    173         .global_set => return .global_set,
    174 
    175         .load => if (args.width) |width| switch (width) {
    176             8 => switch (args.valtype1.?) {
    177                 .i32 => if (args.signedness.? == .signed) return .i32_load8_s else return .i32_load8_u,
    178                 .i64 => if (args.signedness.? == .signed) return .i64_load8_s else return .i64_load8_u,
    179                 .f32, .f64 => unreachable,
    180             },
    181             16 => switch (args.valtype1.?) {
    182                 .i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u,
    183                 .i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u,
    184                 .f32, .f64 => unreachable,
    185             },
    186             32 => switch (args.valtype1.?) {
    187                 .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,
    188                 .i32, .f32, .f64 => unreachable,
    189             },
    190             else => unreachable,
    191         } else switch (args.valtype1.?) {
    192             .i32 => return .i32_load,
    193             .i64 => return .i64_load,
    194             .f32 => return .f32_load,
    195             .f64 => return .f64_load,
    196         },
    197         .store => if (args.width) |width| {
    198             switch (width) {
    199                 8 => switch (args.valtype1.?) {
    200                     .i32 => return .i32_store8,
    201                     .i64 => return .i64_store8,
    202                     .f32, .f64 => unreachable,
    203                 },
    204                 16 => switch (args.valtype1.?) {
    205                     .i32 => return .i32_store16,
    206                     .i64 => return .i64_store16,
    207                     .f32, .f64 => unreachable,
    208                 },
    209                 32 => switch (args.valtype1.?) {
    210                     .i64 => return .i64_store32,
    211                     .i32, .f32, .f64 => unreachable,
    212                 },
    213                 else => unreachable,
    214             }
    215         } else {
    216             switch (args.valtype1.?) {
    217                 .i32 => return .i32_store,
    218                 .i64 => return .i64_store,
    219                 .f32 => return .f32_store,
    220                 .f64 => return .f64_store,
    221             }
    222         },
    223 
    224         .memory_size => return .memory_size,
    225         .memory_grow => return .memory_grow,
    226 
    227         .@"const" => switch (args.valtype1.?) {
    228             .i32 => return .i32_const,
    229             .i64 => return .i64_const,
    230             .f32 => return .f32_const,
    231             .f64 => return .f64_const,
    232         },
    233 
    234         .eqz => switch (args.valtype1.?) {
    235             .i32 => return .i32_eqz,
    236             .i64 => return .i64_eqz,
    237             .f32, .f64 => unreachable,
    238         },
    239         .eq => switch (args.valtype1.?) {
    240             .i32 => return .i32_eq,
    241             .i64 => return .i64_eq,
    242             .f32 => return .f32_eq,
    243             .f64 => return .f64_eq,
    244         },
    245         .ne => switch (args.valtype1.?) {
    246             .i32 => return .i32_ne,
    247             .i64 => return .i64_ne,
    248             .f32 => return .f32_ne,
    249             .f64 => return .f64_ne,
    250         },
    251 
    252         .lt => switch (args.valtype1.?) {
    253             .i32 => if (args.signedness.? == .signed) return .i32_lt_s else return .i32_lt_u,
    254             .i64 => if (args.signedness.? == .signed) return .i64_lt_s else return .i64_lt_u,
    255             .f32 => return .f32_lt,
    256             .f64 => return .f64_lt,
    257         },
    258         .gt => switch (args.valtype1.?) {
    259             .i32 => if (args.signedness.? == .signed) return .i32_gt_s else return .i32_gt_u,
    260             .i64 => if (args.signedness.? == .signed) return .i64_gt_s else return .i64_gt_u,
    261             .f32 => return .f32_gt,
    262             .f64 => return .f64_gt,
    263         },
    264         .le => switch (args.valtype1.?) {
    265             .i32 => if (args.signedness.? == .signed) return .i32_le_s else return .i32_le_u,
    266             .i64 => if (args.signedness.? == .signed) return .i64_le_s else return .i64_le_u,
    267             .f32 => return .f32_le,
    268             .f64 => return .f64_le,
    269         },
    270         .ge => switch (args.valtype1.?) {
    271             .i32 => if (args.signedness.? == .signed) return .i32_ge_s else return .i32_ge_u,
    272             .i64 => if (args.signedness.? == .signed) return .i64_ge_s else return .i64_ge_u,
    273             .f32 => return .f32_ge,
    274             .f64 => return .f64_ge,
    275         },
    276 
    277         .clz => switch (args.valtype1.?) {
    278             .i32 => return .i32_clz,
    279             .i64 => return .i64_clz,
    280             .f32, .f64 => unreachable,
    281         },
    282         .ctz => switch (args.valtype1.?) {
    283             .i32 => return .i32_ctz,
    284             .i64 => return .i64_ctz,
    285             .f32, .f64 => unreachable,
    286         },
    287         .popcnt => switch (args.valtype1.?) {
    288             .i32 => return .i32_popcnt,
    289             .i64 => return .i64_popcnt,
    290             .f32, .f64 => unreachable,
    291         },
    292 
    293         .add => switch (args.valtype1.?) {
    294             .i32 => return .i32_add,
    295             .i64 => return .i64_add,
    296             .f32 => return .f32_add,
    297             .f64 => return .f64_add,
    298         },
    299         .sub => switch (args.valtype1.?) {
    300             .i32 => return .i32_sub,
    301             .i64 => return .i64_sub,
    302             .f32 => return .f32_sub,
    303             .f64 => return .f64_sub,
    304         },
    305         .mul => switch (args.valtype1.?) {
    306             .i32 => return .i32_mul,
    307             .i64 => return .i64_mul,
    308             .f32 => return .f32_mul,
    309             .f64 => return .f64_mul,
    310         },
    311 
    312         .div => switch (args.valtype1.?) {
    313             .i32 => if (args.signedness.? == .signed) return .i32_div_s else return .i32_div_u,
    314             .i64 => if (args.signedness.? == .signed) return .i64_div_s else return .i64_div_u,
    315             .f32 => return .f32_div,
    316             .f64 => return .f64_div,
    317         },
    318         .rem => switch (args.valtype1.?) {
    319             .i32 => if (args.signedness.? == .signed) return .i32_rem_s else return .i32_rem_u,
    320             .i64 => if (args.signedness.? == .signed) return .i64_rem_s else return .i64_rem_u,
    321             .f32, .f64 => unreachable,
    322         },
    323 
    324         .@"and" => switch (args.valtype1.?) {
    325             .i32 => return .i32_and,
    326             .i64 => return .i64_and,
    327             .f32, .f64 => unreachable,
    328         },
    329         .@"or" => switch (args.valtype1.?) {
    330             .i32 => return .i32_or,
    331             .i64 => return .i64_or,
    332             .f32, .f64 => unreachable,
    333         },
    334         .xor => switch (args.valtype1.?) {
    335             .i32 => return .i32_xor,
    336             .i64 => return .i64_xor,
    337             .f32, .f64 => unreachable,
    338         },
    339 
    340         .shl => switch (args.valtype1.?) {
    341             .i32 => return .i32_shl,
    342             .i64 => return .i64_shl,
    343             .f32, .f64 => unreachable,
    344         },
    345         .shr => switch (args.valtype1.?) {
    346             .i32 => if (args.signedness.? == .signed) return .i32_shr_s else return .i32_shr_u,
    347             .i64 => if (args.signedness.? == .signed) return .i64_shr_s else return .i64_shr_u,
    348             .f32, .f64 => unreachable,
    349         },
    350         .rotl => switch (args.valtype1.?) {
    351             .i32 => return .i32_rotl,
    352             .i64 => return .i64_rotl,
    353             .f32, .f64 => unreachable,
    354         },
    355         .rotr => switch (args.valtype1.?) {
    356             .i32 => return .i32_rotr,
    357             .i64 => return .i64_rotr,
    358             .f32, .f64 => unreachable,
    359         },
    360 
    361         .abs => switch (args.valtype1.?) {
    362             .i32, .i64 => unreachable,
    363             .f32 => return .f32_abs,
    364             .f64 => return .f64_abs,
    365         },
    366         .neg => switch (args.valtype1.?) {
    367             .i32, .i64 => unreachable,
    368             .f32 => return .f32_neg,
    369             .f64 => return .f64_neg,
    370         },
    371         .ceil => switch (args.valtype1.?) {
    372             .i32, .i64 => unreachable,
    373             .f32 => return .f32_ceil,
    374             .f64 => return .f64_ceil,
    375         },
    376         .floor => switch (args.valtype1.?) {
    377             .i32, .i64 => unreachable,
    378             .f32 => return .f32_floor,
    379             .f64 => return .f64_floor,
    380         },
    381         .trunc => switch (args.valtype1.?) {
    382             .i32 => switch (args.valtype2.?) {
    383                 .i32 => unreachable,
    384                 .i64 => unreachable,
    385                 .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u,
    386                 .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u,
    387             },
    388             .i64 => unreachable,
    389             .f32 => return .f32_trunc,
    390             .f64 => return .f64_trunc,
    391         },
    392         .nearest => switch (args.valtype1.?) {
    393             .i32, .i64 => unreachable,
    394             .f32 => return .f32_nearest,
    395             .f64 => return .f64_nearest,
    396         },
    397         .sqrt => switch (args.valtype1.?) {
    398             .i32, .i64 => unreachable,
    399             .f32 => return .f32_sqrt,
    400             .f64 => return .f64_sqrt,
    401         },
    402         .min => switch (args.valtype1.?) {
    403             .i32, .i64 => unreachable,
    404             .f32 => return .f32_min,
    405             .f64 => return .f64_min,
    406         },
    407         .max => switch (args.valtype1.?) {
    408             .i32, .i64 => unreachable,
    409             .f32 => return .f32_max,
    410             .f64 => return .f64_max,
    411         },
    412         .copysign => switch (args.valtype1.?) {
    413             .i32, .i64 => unreachable,
    414             .f32 => return .f32_copysign,
    415             .f64 => return .f64_copysign,
    416         },
    417 
    418         .wrap => switch (args.valtype1.?) {
    419             .i32 => switch (args.valtype2.?) {
    420                 .i32 => unreachable,
    421                 .i64 => return .i32_wrap_i64,
    422                 .f32, .f64 => unreachable,
    423             },
    424             .i64, .f32, .f64 => unreachable,
    425         },
    426         .convert => switch (args.valtype1.?) {
    427             .i32, .i64 => unreachable,
    428             .f32 => switch (args.valtype2.?) {
    429                 .i32 => if (args.signedness.? == .signed) return .f32_convert_i32_s else return .f32_convert_i32_u,
    430                 .i64 => if (args.signedness.? == .signed) return .f32_convert_i64_s else return .f32_convert_i64_u,
    431                 .f32, .f64 => unreachable,
    432             },
    433             .f64 => switch (args.valtype2.?) {
    434                 .i32 => if (args.signedness.? == .signed) return .f64_convert_i32_s else return .f64_convert_i32_u,
    435                 .i64 => if (args.signedness.? == .signed) return .f64_convert_i64_s else return .f64_convert_i64_u,
    436                 .f32, .f64 => unreachable,
    437             },
    438         },
    439         .demote => if (args.valtype1.? == .f32 and args.valtype2.? == .f64) return .f32_demote_f64 else unreachable,
    440         .promote => if (args.valtype1.? == .f64 and args.valtype2.? == .f32) return .f64_promote_f32 else unreachable,
    441         .reinterpret => switch (args.valtype1.?) {
    442             .i32 => if (args.valtype2.? == .f32) return .i32_reinterpret_f32 else unreachable,
    443             .i64 => if (args.valtype2.? == .f64) return .i64_reinterpret_f64 else unreachable,
    444             .f32 => if (args.valtype2.? == .i32) return .f32_reinterpret_i32 else unreachable,
    445             .f64 => if (args.valtype2.? == .i64) return .f64_reinterpret_i64 else unreachable,
    446         },
    447         .extend => switch (args.valtype1.?) {
    448             .i32 => switch (args.width.?) {
    449                 8 => if (args.signedness.? == .signed) return .i32_extend8_s else unreachable,
    450                 16 => if (args.signedness.? == .signed) return .i32_extend16_s else unreachable,
    451                 else => unreachable,
    452             },
    453             .i64 => switch (args.width.?) {
    454                 8 => if (args.signedness.? == .signed) return .i64_extend8_s else unreachable,
    455                 16 => if (args.signedness.? == .signed) return .i64_extend16_s else unreachable,
    456                 32 => if (args.signedness.? == .signed) return .i64_extend32_s else unreachable,
    457                 else => unreachable,
    458             },
    459             .f32, .f64 => unreachable,
    460         },
    461     }
    462 }
    463 
    464 test "Wasm - buildOpcode" {
    465     // Make sure buildOpcode is referenced, and test some examples
    466     const i32_const = buildOpcode(.{ .op = .@"const", .valtype1 = .i32 });
    467     const end = buildOpcode(.{ .op = .end });
    468     const local_get = buildOpcode(.{ .op = .local_get });
    469     const i64_extend32_s = buildOpcode(.{ .op = .extend, .valtype1 = .i64, .width = 32, .signedness = .signed });
    470     const f64_reinterpret_i64 = buildOpcode(.{ .op = .reinterpret, .valtype1 = .f64, .valtype2 = .i64 });
    471 
    472     try testing.expectEqual(@as(wasm.Opcode, .i32_const), i32_const);
    473     try testing.expectEqual(@as(wasm.Opcode, .end), end);
    474     try testing.expectEqual(@as(wasm.Opcode, .local_get), local_get);
    475     try testing.expectEqual(@as(wasm.Opcode, .i64_extend32_s), i64_extend32_s);
    476     try testing.expectEqual(@as(wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64);
    477 }
    478 
    479 pub const Result = union(enum) {
    480     /// The codegen bytes have been appended to `Context.code`
    481     appended: void,
    482     /// The data is managed externally and are part of the `Result`
    483     externally_managed: []const u8,
    484 };
    485 
    486 /// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
    487 pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Index, WValue);
    488 
    489 /// Code represents the `Code` section of wasm that
    490 /// belongs to a function
    491 pub const Context = struct {
    492     /// Reference to the function declaration the code
    493     /// section belongs to
    494     decl: *Decl,
    495     air: Air,
    496     liveness: Liveness,
    497     gpa: *mem.Allocator,
    498     /// Table to save `WValue`'s generated by an `Air.Inst`
    499     values: ValueTable,
    500     /// Mapping from Air.Inst.Index to block ids
    501     blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{},
    502     /// `bytes` contains the wasm bytecode belonging to the 'code' section.
    503     code: ArrayList(u8),
    504     /// Contains the generated function type bytecode for the current function
    505     /// found in `decl`
    506     func_type_data: ArrayList(u8),
    507     /// The index the next local generated will have
    508     /// NOTE: arguments share the index with locals therefore the first variable
    509     /// will have the index that comes after the last argument's index
    510     local_index: u32 = 0,
    511     /// If codegen fails, an error messages will be allocated and saved in `err_msg`
    512     err_msg: *Module.ErrorMsg,
    513     /// Current block depth. Used to calculate the relative difference between a break
    514     /// and block
    515     block_depth: u32 = 0,
    516     /// List of all locals' types generated throughout this declaration
    517     /// used to emit locals count at start of 'code' section.
    518     locals: std.ArrayListUnmanaged(u8),
    519     /// The Target we're emitting (used to call intInfo)
    520     target: std.Target,
    521     /// Table with the global error set. Consists of every error found in
    522     /// the compiled code. Each error name maps to a `Module.ErrorInt` which is emitted
    523     /// during codegen to determine the error value.
    524     global_error_set: std.StringHashMapUnmanaged(Module.ErrorInt),
    525 
    526     const InnerError = error{
    527         OutOfMemory,
    528         CodegenFail,
    529         /// Can occur when dereferencing a pointer that points to a `Decl` of which the analysis has failed
    530         AnalysisFail,
    531     };
    532 
    533     pub fn deinit(self: *Context) void {
    534         self.values.deinit(self.gpa);
    535         self.blocks.deinit(self.gpa);
    536         self.locals.deinit(self.gpa);
    537         self.* = undefined;
    538     }
    539 
    540     /// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig
    541     fn fail(self: *Context, comptime fmt: []const u8, args: anytype) InnerError {
    542         const src: LazySrcLoc = .{ .node_offset = 0 };
    543         const src_loc = src.toSrcLocWithDecl(self.decl);
    544         self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args);
    545         return error.CodegenFail;
    546     }
    547 
    548     /// Resolves the `WValue` for the given instruction `inst`
    549     /// When the given instruction has a `Value`, it returns a constant instead
    550     fn resolveInst(self: Context, ref: Air.Inst.Ref) WValue {
    551         const inst_index = Air.refToIndex(ref) orelse {
    552             const tv = Air.Inst.Ref.typed_value_map[@enumToInt(ref)];
    553             if (!tv.ty.hasCodeGenBits()) {
    554                 return WValue.none;
    555             }
    556             return WValue{ .constant = tv };
    557         };
    558 
    559         const inst_type = self.air.typeOfIndex(inst_index);
    560         if (!inst_type.hasCodeGenBits()) return .none;
    561 
    562         if (self.air.instructions.items(.tag)[inst_index] == .constant) {
    563             const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
    564             return WValue{ .constant = .{ .ty = inst_type, .val = self.air.values[ty_pl.payload] } };
    565         }
    566 
    567         return self.values.get(inst_index).?; // Instruction does not dominate all uses!
    568     }
    569 
    570     /// Using a given `Type`, returns the corresponding wasm Valtype
    571     fn typeToValtype(self: *Context, ty: Type) InnerError!wasm.Valtype {
    572         return switch (ty.zigTypeTag()) {
    573             .Float => blk: {
    574                 const bits = ty.floatBits(self.target);
    575                 if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32;
    576                 if (bits == 64) break :blk wasm.Valtype.f64;
    577                 return self.fail("Float bit size not supported by wasm: '{d}'", .{bits});
    578             },
    579             .Int => blk: {
    580                 const info = ty.intInfo(self.target);
    581                 if (info.bits <= 32) break :blk wasm.Valtype.i32;
    582                 if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64;
    583                 return self.fail("Integer bit size not supported by wasm: '{d}'", .{info.bits});
    584             },
    585             .Enum => switch (ty.tag()) {
    586                 .enum_simple => wasm.Valtype.i32,
    587                 else => self.typeToValtype(ty.cast(Type.Payload.EnumFull).?.data.tag_ty),
    588             },
    589             .Bool,
    590             .Pointer,
    591             .ErrorSet,
    592             => wasm.Valtype.i32,
    593             .Struct, .ErrorUnion => unreachable, // Multi typed, must be handled individually.
    594             else => self.fail("TODO - Wasm valtype for type '{s}'", .{ty.zigTypeTag()}),
    595         };
    596     }
    597 
    598     /// Using a given `Type`, returns the byte representation of its wasm value type
    599     fn genValtype(self: *Context, ty: Type) InnerError!u8 {
    600         return wasm.valtype(try self.typeToValtype(ty));
    601     }
    602 
    603     /// Using a given `Type`, returns the corresponding wasm value type
    604     /// Differently from `genValtype` this also allows `void` to create a block
    605     /// with no return type
    606     fn genBlockType(self: *Context, ty: Type) InnerError!u8 {
    607         return switch (ty.tag()) {
    608             .void, .noreturn => wasm.block_empty,
    609             else => self.genValtype(ty),
    610         };
    611     }
    612 
    613     /// Writes the bytecode depending on the given `WValue` in `val`
    614     fn emitWValue(self: *Context, val: WValue) InnerError!void {
    615         const writer = self.code.writer();
    616         switch (val) {
    617             .multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually
    618             .none, .code_offset => {}, // no-op
    619             .local => |idx| {
    620                 try writer.writeByte(wasm.opcode(.local_get));
    621                 try leb.writeULEB128(writer, idx);
    622             },
    623             .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack
    624         }
    625     }
    626 
    627     /// Creates one or multiple locals for a given `Type`.
    628     /// Returns a corresponding `Wvalue` that can either be of tag
    629     /// local or multi_value
    630     fn allocLocal(self: *Context, ty: Type) InnerError!WValue {
    631         const initial_index = self.local_index;
    632         switch (ty.zigTypeTag()) {
    633             .Struct => {
    634                 // for each struct field, generate a local
    635                 const struct_data: *Module.Struct = ty.castTag(.@"struct").?.data;
    636                 const fields_len = @intCast(u32, struct_data.fields.count());
    637                 try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len);
    638                 for (struct_data.fields.values()) |*value| {
    639                     const val_type = try self.genValtype(value.ty);
    640                     self.locals.appendAssumeCapacity(val_type);
    641                     self.local_index += 1;
    642                 }
    643                 return WValue{ .multi_value = .{
    644                     .index = initial_index,
    645                     .count = fields_len,
    646                 } };
    647             },
    648             .ErrorUnion => {
    649                 const payload_type = ty.errorUnionPayload();
    650                 const val_type = try self.genValtype(payload_type);
    651 
    652                 // we emit the error value as the first local, and the payload as the following.
    653                 // The first local is also used to find the index of the error and payload.
    654                 //
    655                 // TODO: Add support where the payload is a type that contains multiple locals such as a struct.
    656                 try self.locals.ensureCapacity(self.gpa, self.locals.items.len + 2);
    657                 self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // error values are always i32
    658                 self.locals.appendAssumeCapacity(val_type);
    659                 self.local_index += 2;
    660 
    661                 return WValue{ .multi_value = .{
    662                     .index = initial_index,
    663                     .count = 2,
    664                 } };
    665             },
    666             else => {
    667                 const valtype = try self.genValtype(ty);
    668                 try self.locals.append(self.gpa, valtype);
    669                 self.local_index += 1;
    670                 return WValue{ .local = initial_index };
    671             },
    672         }
    673     }
    674 
    675     fn genFunctype(self: *Context) InnerError!void {
    676         assert(self.decl.has_tv);
    677         const ty = self.decl.ty;
    678         const writer = self.func_type_data.writer();
    679 
    680         try writer.writeByte(wasm.function_type);
    681 
    682         // param types
    683         try leb.writeULEB128(writer, @intCast(u32, ty.fnParamLen()));
    684         if (ty.fnParamLen() != 0) {
    685             const params = try self.gpa.alloc(Type, ty.fnParamLen());
    686             defer self.gpa.free(params);
    687             ty.fnParamTypes(params);
    688             for (params) |param_type| {
    689                 // Can we maybe get the source index of each param?
    690                 const val_type = try self.genValtype(param_type);
    691                 try writer.writeByte(val_type);
    692             }
    693         }
    694 
    695         // return type
    696         const return_type = ty.fnReturnType();
    697         switch (return_type.zigTypeTag()) {
    698             .Void, .NoReturn => try leb.writeULEB128(writer, @as(u32, 0)),
    699             .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}),
    700             .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}),
    701             .ErrorUnion => {
    702                 const val_type = try self.genValtype(return_type.errorUnionPayload());
    703 
    704                 // write down the amount of return values
    705                 try leb.writeULEB128(writer, @as(u32, 2));
    706                 try writer.writeByte(wasm.valtype(.i32)); // error code is always an i32 integer.
    707                 try writer.writeByte(val_type);
    708             },
    709             else => {
    710                 try leb.writeULEB128(writer, @as(u32, 1));
    711                 // Can we maybe get the source index of the return type?
    712                 const val_type = try self.genValtype(return_type);
    713                 try writer.writeByte(val_type);
    714             },
    715         }
    716     }
    717 
    718     pub fn genFunc(self: *Context) InnerError!Result {
    719         try self.genFunctype();
    720         // TODO: check for and handle death of instructions
    721 
    722         // Reserve space to write the size after generating the code as well as space for locals count
    723         try self.code.resize(10);
    724 
    725         try self.genBody(self.air.getMainBody());
    726 
    727         // finally, write our local types at the 'offset' position
    728         {
    729             leb.writeUnsignedFixed(5, self.code.items[5..10], @intCast(u32, self.locals.items.len));
    730 
    731             // offset into 'code' section where we will put our locals types
    732             var local_offset: usize = 10;
    733 
    734             // emit the actual locals amount
    735             for (self.locals.items) |local| {
    736                 var buf: [6]u8 = undefined;
    737                 leb.writeUnsignedFixed(5, buf[0..5], @as(u32, 1));
    738                 buf[5] = local;
    739                 try self.code.insertSlice(local_offset, &buf);
    740                 local_offset += 6;
    741             }
    742         }
    743 
    744         const writer = self.code.writer();
    745         try writer.writeByte(wasm.opcode(.end));
    746 
    747         // Fill in the size of the generated code to the reserved space at the
    748         // beginning of the buffer.
    749         const size = self.code.items.len - 5 + self.decl.fn_link.wasm.idx_refs.items.len * 5;
    750         leb.writeUnsignedFixed(5, self.code.items[0..5], @intCast(u32, size));
    751 
    752         // codegen data has been appended to `code`
    753         return Result.appended;
    754     }
    755 
    756     /// Generates the wasm bytecode for the declaration belonging to `Context`
    757     pub fn gen(self: *Context, typed_value: TypedValue) InnerError!Result {
    758         switch (typed_value.ty.zigTypeTag()) {
    759             .Fn => {
    760                 try self.genFunctype();
    761                 if (typed_value.val.castTag(.extern_fn)) |_| return Result.appended; // don't need code body for extern functions
    762                 return self.fail("TODO implement wasm codegen for function pointers", .{});
    763             },
    764             .Array => {
    765                 if (typed_value.val.castTag(.bytes)) |payload| {
    766                     if (typed_value.ty.sentinel()) |sentinel| {
    767                         try self.code.appendSlice(payload.data);
    768 
    769                         switch (try self.gen(.{
    770                             .ty = typed_value.ty.elemType(),
    771                             .val = sentinel,
    772                         })) {
    773                             .appended => return Result.appended,
    774                             .externally_managed => |data| {
    775                                 try self.code.appendSlice(data);
    776                                 return Result.appended;
    777                             },
    778                         }
    779                     }
    780                     return Result{ .externally_managed = payload.data };
    781                 } else return self.fail("TODO implement gen for more kinds of arrays", .{});
    782             },
    783             .Int => {
    784                 const info = typed_value.ty.intInfo(self.target);
    785                 if (info.bits == 8 and info.signedness == .unsigned) {
    786                     const int_byte = typed_value.val.toUnsignedInt();
    787                     try self.code.append(@intCast(u8, int_byte));
    788                     return Result.appended;
    789                 }
    790                 return self.fail("TODO: Implement codegen for int type: '{}'", .{typed_value.ty});
    791             },
    792             else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
    793         }
    794     }
    795 
    796     fn genInst(self: *Context, inst: Air.Inst.Index) !WValue {
    797         const air_tags = self.air.instructions.items(.tag);
    798         return switch (air_tags[inst]) {
    799             .add => self.airBinOp(inst, .add),
    800             .sub => self.airBinOp(inst, .sub),
    801             .mul => self.airBinOp(inst, .mul),
    802             .div => self.airBinOp(inst, .div),
    803             .bit_and => self.airBinOp(inst, .@"and"),
    804             .bit_or => self.airBinOp(inst, .@"or"),
    805             .bool_and => self.airBinOp(inst, .@"and"),
    806             .bool_or => self.airBinOp(inst, .@"or"),
    807             .xor => self.airBinOp(inst, .xor),
    808 
    809             .cmp_eq => self.airCmp(inst, .eq),
    810             .cmp_gte => self.airCmp(inst, .gte),
    811             .cmp_gt => self.airCmp(inst, .gt),
    812             .cmp_lte => self.airCmp(inst, .lte),
    813             .cmp_lt => self.airCmp(inst, .lt),
    814             .cmp_neq => self.airCmp(inst, .neq),
    815 
    816             .alloc => self.airAlloc(inst),
    817             .arg => self.airArg(inst),
    818             .bitcast => self.airBitcast(inst),
    819             .block => self.airBlock(inst),
    820             .breakpoint => self.airBreakpoint(inst),
    821             .br => self.airBr(inst),
    822             .call => self.airCall(inst),
    823             .cond_br => self.airCondBr(inst),
    824             .constant => unreachable,
    825             .dbg_stmt => WValue.none,
    826             .is_err => self.airIsErr(inst, .i32_ne),
    827             .is_non_err => self.airIsErr(inst, .i32_eq),
    828             .load => self.airLoad(inst),
    829             .loop => self.airLoop(inst),
    830             .not => self.airNot(inst),
    831             .ret => self.airRet(inst),
    832             .store => self.airStore(inst),
    833             .struct_field_ptr => self.airStructFieldPtr(inst),
    834             .switch_br => self.airSwitchBr(inst),
    835             .unreach => self.airUnreachable(inst),
    836             .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst),
    837             .wrap_errunion_payload => self.airWrapErrUnionPayload(inst),
    838             else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
    839         };
    840     }
    841 
    842     fn genBody(self: *Context, body: []const Air.Inst.Index) InnerError!void {
    843         for (body) |inst| {
    844             const result = try self.genInst(inst);
    845             try self.values.putNoClobber(self.gpa, inst, result);
    846         }
    847     }
    848 
    849     fn airRet(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
    850         const un_op = self.air.instructions.items(.data)[inst].un_op;
    851         const operand = self.resolveInst(un_op);
    852         try self.emitWValue(operand);
    853         try self.code.append(wasm.opcode(.@"return"));
    854         return .none;
    855     }
    856 
    857     fn airCall(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
    858         const pl_op = self.air.instructions.items(.data)[inst].pl_op;
    859         const extra = self.air.extraData(Air.Call, pl_op.payload);
    860         const args = self.air.extra[extra.end..][0..extra.data.args_len];
    861 
    862         const target: *Decl = blk: {
    863             const func_val = self.air.value(pl_op.operand).?;
    864 
    865             if (func_val.castTag(.function)) |func| {
    866                 break :blk func.data.owner_decl;
    867             } else if (func_val.castTag(.extern_fn)) |ext_fn| {
    868                 break :blk ext_fn.data;
    869             }
    870             return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()});
    871         };
    872 
    873         for (args) |arg| {
    874             const arg_val = self.resolveInst(@intToEnum(Air.Inst.Ref, arg));
    875             try self.emitWValue(arg_val);
    876         }
    877 
    878         try self.code.append(wasm.opcode(.call));
    879 
    880         // The function index immediate argument will be filled in using this data
    881         // in link.Wasm.flush().
    882         try self.decl.fn_link.wasm.idx_refs.append(self.gpa, .{
    883             .offset = @intCast(u32, self.code.items.len),
    884             .decl = target,
    885         });
    886 
    887         return .none;
    888     }
    889 
    890     fn airAlloc(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
    891         const elem_type = self.air.typeOfIndex(inst).elemType();
    892         return self.allocLocal(elem_type);
    893     }
    894 
    895     fn airStore(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
    896         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    897         const writer = self.code.writer();
    898 
    899         const lhs = self.resolveInst(bin_op.lhs);
    900         const rhs = self.resolveInst(bin_op.rhs);
    901 
    902         switch (lhs) {
    903             .multi_value => |multi_value| switch (rhs) {
    904                 // When assigning a value to a multi_value such as a struct,
    905                 // we simply assign the local_index to the rhs one.
    906                 // This allows us to update struct fields without having to individually
    907                 // set each local as each field's index will be calculated off the struct's base index
    908                 .multi_value => self.values.put(self.gpa, Air.refToIndex(bin_op.lhs).?, rhs) catch unreachable, // Instruction does not dominate all uses!
    909                 .constant, .none => {
    910                     // emit all values onto the stack if constant
    911                     try self.emitWValue(rhs);
    912 
    913                     // for each local, pop the stack value into the local
    914                     // As the last element is on top of the stack, we must populate the locals
    915                     // in reverse.
    916                     var i: u32 = multi_value.count;
    917                     while (i > 0) : (i -= 1) {
    918                         try writer.writeByte(wasm.opcode(.local_set));
    919                         try leb.writeULEB128(writer, multi_value.index + i - 1);
    920                     }
    921                 },
    922                 else => unreachable,
    923             },
    924             .local => |local| {
    925                 try self.emitWValue(rhs);
    926                 try writer.writeByte(wasm.opcode(.local_set));
    927                 try leb.writeULEB128(writer, local);
    928             },
    929             else => unreachable,
    930         }
    931         return .none;
    932     }
    933 
    934     fn airLoad(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
    935         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
    936         return self.resolveInst(ty_op.operand);
    937     }
    938 
    939     fn airArg(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
    940         _ = inst;
    941         // arguments share the index with locals
    942         defer self.local_index += 1;
    943         return WValue{ .local = self.local_index };
    944     }
    945 
    946     fn airBinOp(self: *Context, inst: Air.Inst.Index, op: Op) InnerError!WValue {
    947         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
    948         const lhs = self.resolveInst(bin_op.lhs);
    949         const rhs = self.resolveInst(bin_op.rhs);
    950 
    951         // it's possible for both lhs and/or rhs to return an offset as well,
    952         // in which case we return the first offset occurance we find.
    953         const offset = blk: {
    954             if (lhs == .code_offset) break :blk lhs.code_offset;
    955             if (rhs == .code_offset) break :blk rhs.code_offset;
    956             break :blk self.code.items.len;
    957         };
    958 
    959         try self.emitWValue(lhs);
    960         try self.emitWValue(rhs);
    961 
    962         const bin_ty = self.air.typeOf(bin_op.lhs);
    963         const opcode: wasm.Opcode = buildOpcode(.{
    964             .op = op,
    965             .valtype1 = try self.typeToValtype(bin_ty),
    966             .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
    967         });
    968         try self.code.append(wasm.opcode(opcode));
    969         return WValue{ .code_offset = offset };
    970     }
    971 
    972     fn emitConstant(self: *Context, value: Value, ty: Type) InnerError!void {
    973         const writer = self.code.writer();
    974         switch (ty.zigTypeTag()) {
    975             .Int => {
    976                 // write opcode
    977                 const opcode: wasm.Opcode = buildOpcode(.{
    978                     .op = .@"const",
    979                     .valtype1 = try self.typeToValtype(ty),
    980                 });
    981                 try writer.writeByte(wasm.opcode(opcode));
    982                 // write constant
    983                 switch (ty.intInfo(self.target).signedness) {
    984                     .signed => try leb.writeILEB128(writer, value.toSignedInt()),
    985                     .unsigned => try leb.writeILEB128(writer, value.toUnsignedInt()),
    986                 }
    987             },
    988             .Bool => {
    989                 // write opcode
    990                 try writer.writeByte(wasm.opcode(.i32_const));
    991                 // write constant
    992                 try leb.writeILEB128(writer, value.toSignedInt());
    993             },
    994             .Float => {
    995                 // write opcode
    996                 const opcode: wasm.Opcode = buildOpcode(.{
    997                     .op = .@"const",
    998                     .valtype1 = try self.typeToValtype(ty),
    999                 });
   1000                 try writer.writeByte(wasm.opcode(opcode));
   1001                 // write constant
   1002                 switch (ty.floatBits(self.target)) {
   1003                     0...32 => try writer.writeIntLittle(u32, @bitCast(u32, value.toFloat(f32))),
   1004                     64 => try writer.writeIntLittle(u64, @bitCast(u64, value.toFloat(f64))),
   1005                     else => |bits| return self.fail("Wasm TODO: emitConstant for float with {d} bits", .{bits}),
   1006                 }
   1007             },
   1008             .Pointer => {
   1009                 if (value.castTag(.decl_ref)) |payload| {
   1010                     const decl = payload.data;
   1011 
   1012                     // offset into the offset table within the 'data' section
   1013                     const ptr_width = self.target.cpu.arch.ptrBitWidth() / 8;
   1014                     try writer.writeByte(wasm.opcode(.i32_const));
   1015                     try leb.writeULEB128(writer, decl.link.wasm.offset_index * ptr_width);
   1016 
   1017                     // memory instruction followed by their memarg immediate
   1018                     // memarg ::== x:u32, y:u32 => {align x, offset y}
   1019                     try writer.writeByte(wasm.opcode(.i32_load));
   1020                     try leb.writeULEB128(writer, @as(u32, 0));
   1021                     try leb.writeULEB128(writer, @as(u32, 0));
   1022                 } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{value.tag()});
   1023             },
   1024             .Void => {},
   1025             .Enum => {
   1026                 if (value.castTag(.enum_field_index)) |field_index| {
   1027                     switch (ty.tag()) {
   1028                         .enum_simple => {
   1029                             try writer.writeByte(wasm.opcode(.i32_const));
   1030                             try leb.writeULEB128(writer, field_index.data);
   1031                         },
   1032                         .enum_full, .enum_nonexhaustive => {
   1033                             const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
   1034                             if (enum_full.values.count() != 0) {
   1035                                 const tag_val = enum_full.values.keys()[field_index.data];
   1036                                 try self.emitConstant(tag_val, enum_full.tag_ty);
   1037                             } else {
   1038                                 try writer.writeByte(wasm.opcode(.i32_const));
   1039                                 try leb.writeULEB128(writer, field_index.data);
   1040                             }
   1041                         },
   1042                         else => unreachable,
   1043                     }
   1044                 } else {
   1045                     var int_tag_buffer: Type.Payload.Bits = undefined;
   1046                     const int_tag_ty = ty.intTagType(&int_tag_buffer);
   1047                     try self.emitConstant(value, int_tag_ty);
   1048                 }
   1049             },
   1050             .ErrorSet => {
   1051                 const error_index = self.global_error_set.get(value.getError().?).?;
   1052                 try writer.writeByte(wasm.opcode(.i32_const));
   1053                 try leb.writeULEB128(writer, error_index);
   1054             },
   1055             .ErrorUnion => {
   1056                 const data = value.castTag(.error_union).?.data;
   1057                 const error_type = ty.errorUnionSet();
   1058                 const payload_type = ty.errorUnionPayload();
   1059                 if (value.getError()) |_| {
   1060                     // write the error value
   1061                     try self.emitConstant(data, error_type);
   1062 
   1063                     // no payload, so write a '0' const
   1064                     const opcode: wasm.Opcode = buildOpcode(.{
   1065                         .op = .@"const",
   1066                         .valtype1 = try self.typeToValtype(payload_type),
   1067                     });
   1068                     try writer.writeByte(wasm.opcode(opcode));
   1069                     try leb.writeULEB128(writer, @as(u32, 0));
   1070                 } else {
   1071                     // no error, so write a '0' const
   1072                     try writer.writeByte(wasm.opcode(.i32_const));
   1073                     try leb.writeULEB128(writer, @as(u32, 0));
   1074                     // after the error code, we emit the payload
   1075                     try self.emitConstant(data, payload_type);
   1076                 }
   1077             },
   1078             else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}),
   1079         }
   1080     }
   1081 
   1082     fn airBlock(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
   1083         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1084         const block_ty = try self.genBlockType(self.air.getRefType(ty_pl.ty));
   1085         const extra = self.air.extraData(Air.Block, ty_pl.payload);
   1086         const body = self.air.extra[extra.end..][0..extra.data.body_len];
   1087 
   1088         try self.startBlock(.block, block_ty, null);
   1089         // Here we set the current block idx, so breaks know the depth to jump
   1090         // to when breaking out.
   1091         try self.blocks.putNoClobber(self.gpa, inst, self.block_depth);
   1092         try self.genBody(body);
   1093         try self.endBlock();
   1094 
   1095         return .none;
   1096     }
   1097 
   1098     /// appends a new wasm block to the code section and increases the `block_depth` by 1
   1099     fn startBlock(self: *Context, block_type: wasm.Opcode, valtype: u8, with_offset: ?usize) !void {
   1100         self.block_depth += 1;
   1101         if (with_offset) |offset| {
   1102             try self.code.insert(offset, wasm.opcode(block_type));
   1103             try self.code.insert(offset + 1, valtype);
   1104         } else {
   1105             try self.code.append(wasm.opcode(block_type));
   1106             try self.code.append(valtype);
   1107         }
   1108     }
   1109 
   1110     /// Ends the current wasm block and decreases the `block_depth` by 1
   1111     fn endBlock(self: *Context) !void {
   1112         try self.code.append(wasm.opcode(.end));
   1113         self.block_depth -= 1;
   1114     }
   1115 
   1116     fn airLoop(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
   1117         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1118         const loop = self.air.extraData(Air.Block, ty_pl.payload);
   1119         const body = self.air.extra[loop.end..][0..loop.data.body_len];
   1120 
   1121         // result type of loop is always 'noreturn', meaning we can always
   1122         // emit the wasm type 'block_empty'.
   1123         try self.startBlock(.loop, wasm.block_empty, null);
   1124         try self.genBody(body);
   1125 
   1126         // breaking to the index of a loop block will continue the loop instead
   1127         try self.code.append(wasm.opcode(.br));
   1128         try leb.writeULEB128(self.code.writer(), @as(u32, 0));
   1129 
   1130         try self.endBlock();
   1131 
   1132         return .none;
   1133     }
   1134 
   1135     fn airCondBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
   1136         const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   1137         const condition = self.resolveInst(pl_op.operand);
   1138         const extra = self.air.extraData(Air.CondBr, pl_op.payload);
   1139         const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
   1140         const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
   1141         const writer = self.code.writer();
   1142 
   1143         // TODO: Handle death instructions for then and else body
   1144 
   1145         // insert blocks at the position of `offset` so
   1146         // the condition can jump to it
   1147         const offset = switch (condition) {
   1148             .code_offset => |offset| offset,
   1149             else => blk: {
   1150                 const offset = self.code.items.len;
   1151                 try self.emitWValue(condition);
   1152                 break :blk offset;
   1153             },
   1154         };
   1155 
   1156         // result type is always noreturn, so use `block_empty` as type.
   1157         try self.startBlock(.block, wasm.block_empty, offset);
   1158 
   1159         // we inserted the block in front of the condition
   1160         // so now check if condition matches. If not, break outside this block
   1161         // and continue with the then codepath
   1162         try writer.writeByte(wasm.opcode(.br_if));
   1163         try leb.writeULEB128(writer, @as(u32, 0));
   1164 
   1165         try self.genBody(else_body);
   1166         try self.endBlock();
   1167 
   1168         // Outer block that matches the condition
   1169         try self.genBody(then_body);
   1170 
   1171         return .none;
   1172     }
   1173 
   1174     fn airCmp(self: *Context, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue {
   1175         // save offset, so potential conditions can insert blocks in front of
   1176         // the comparison that we can later jump back to
   1177         const offset = self.code.items.len;
   1178 
   1179         const data: Air.Inst.Data = self.air.instructions.items(.data)[inst];
   1180         const lhs = self.resolveInst(data.bin_op.lhs);
   1181         const rhs = self.resolveInst(data.bin_op.rhs);
   1182         const lhs_ty = self.air.typeOf(data.bin_op.lhs);
   1183 
   1184         try self.emitWValue(lhs);
   1185         try self.emitWValue(rhs);
   1186 
   1187         const signedness: std.builtin.Signedness = blk: {
   1188             // by default we tell the operand type is unsigned (i.e. bools and enum values)
   1189             if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned;
   1190 
   1191             // incase of an actual integer, we emit the correct signedness
   1192             break :blk lhs_ty.intInfo(self.target).signedness;
   1193         };
   1194         const opcode: wasm.Opcode = buildOpcode(.{
   1195             .valtype1 = try self.typeToValtype(lhs_ty),
   1196             .op = switch (op) {
   1197                 .lt => .lt,
   1198                 .lte => .le,
   1199                 .eq => .eq,
   1200                 .neq => .ne,
   1201                 .gte => .ge,
   1202                 .gt => .gt,
   1203             },
   1204             .signedness = signedness,
   1205         });
   1206         try self.code.append(wasm.opcode(opcode));
   1207         return WValue{ .code_offset = offset };
   1208     }
   1209 
   1210     fn airBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
   1211         const br = self.air.instructions.items(.data)[inst].br;
   1212 
   1213         // if operand has codegen bits we should break with a value
   1214         if (self.air.typeOf(br.operand).hasCodeGenBits()) {
   1215             try self.emitWValue(self.resolveInst(br.operand));
   1216         }
   1217 
   1218         // We map every block to its block index.
   1219         // We then determine how far we have to jump to it by substracting it from current block depth
   1220         const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?;
   1221         const writer = self.code.writer();
   1222         try writer.writeByte(wasm.opcode(.br));
   1223         try leb.writeULEB128(writer, idx);
   1224 
   1225         return .none;
   1226     }
   1227 
   1228     fn airNot(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
   1229         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1230         const offset = self.code.items.len;
   1231 
   1232         const operand = self.resolveInst(ty_op.operand);
   1233         try self.emitWValue(operand);
   1234 
   1235         // wasm does not have booleans nor the `not` instruction, therefore compare with 0
   1236         // to create the same logic
   1237         const writer = self.code.writer();
   1238         try writer.writeByte(wasm.opcode(.i32_const));
   1239         try leb.writeILEB128(writer, @as(i32, 0));
   1240 
   1241         try writer.writeByte(wasm.opcode(.i32_eq));
   1242 
   1243         return WValue{ .code_offset = offset };
   1244     }
   1245 
   1246     fn airBreakpoint(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
   1247         _ = self;
   1248         _ = inst;
   1249         // unsupported by wasm itself. Can be implemented once we support DWARF
   1250         // for wasm
   1251         return .none;
   1252     }
   1253 
   1254     fn airUnreachable(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
   1255         _ = inst;
   1256         try self.code.append(wasm.opcode(.@"unreachable"));
   1257         return .none;
   1258     }
   1259 
   1260     fn airBitcast(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
   1261         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1262         return self.resolveInst(ty_op.operand);
   1263     }
   1264 
   1265     fn airStructFieldPtr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
   1266         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   1267         const extra = self.air.extraData(Air.StructField, ty_pl.payload);
   1268         const struct_ptr = self.resolveInst(extra.data.struct_ptr);
   1269 
   1270         return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, extra.data.field_index) };
   1271     }
   1272 
   1273     fn airSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
   1274         // result type is always 'noreturn'
   1275         const blocktype = wasm.block_empty;
   1276         const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   1277         const target = self.resolveInst(pl_op.operand);
   1278         const target_ty = self.air.typeOf(pl_op.operand);
   1279         const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
   1280         var extra_index: usize = switch_br.end;
   1281         var case_i: u32 = 0;
   1282 
   1283         // a map that maps each value with its index and body
   1284         var map = std.AutoArrayHashMap(i32, struct {
   1285             index: u32,
   1286             body: []const Air.Inst.Index,
   1287             value: Value,
   1288         }).init(self.gpa);
   1289         defer map.deinit();
   1290 
   1291         var lowest: i32 = 0;
   1292         var highest: i32 = 0;
   1293         while (case_i < switch_br.data.cases_len) : (case_i += 1) {
   1294             const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
   1295             const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
   1296             const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
   1297             extra_index = case.end + items.len + case_body.len;
   1298 
   1299             for (items) |ref| {
   1300                 const item_val = self.air.value(ref).?;
   1301                 const int_val: i32 = blk: {
   1302                     if (target_ty.intInfo(self.target).signedness == .signed) {
   1303                         // safe to truncate the values as we only use them when
   1304                         // the target's bits is 32 or lower.
   1305                         break :blk @truncate(i32, item_val.toSignedInt());
   1306                     }
   1307 
   1308                     break :blk @intCast(i32, @truncate(u31, item_val.toUnsignedInt()) - 1);
   1309                 };
   1310                 if (int_val < lowest) {
   1311                     lowest = int_val;
   1312                 }
   1313                 if (int_val > highest) {
   1314                     highest = int_val;
   1315                 }
   1316                 try map.put(int_val, .{ .index = case_i, .body = case_body, .value = item_val });
   1317             }
   1318 
   1319             try self.startBlock(.block, blocktype, null);
   1320         }
   1321 
   1322         // When the highest and lowest values are seperated by '50',
   1323         // we define it as sparse and use an if/else-chain, rather than a jump table.
   1324         // When the target is an integer size larger than u32, we have no way to use the value
   1325         // as an index, therefore we also use an if/else-chain for those cases.
   1326         // TODO: Benchmark this to find a proper value, LLVM seems to draw the line at '40~45'.
   1327         const is_sparse = target_ty.intInfo(self.target).bits > 32 or highest - lowest > 50;
   1328 
   1329         const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
   1330         const has_else_body = else_body.len != 0;
   1331         if (has_else_body) {
   1332             try self.startBlock(.block, blocktype, null);
   1333         }
   1334 
   1335         if (!is_sparse) {
   1336             // Generate the jump table 'br_table' when the prongs are not sparse.
   1337             // The value 'target' represents the index into the table.
   1338             // Each index in the table represents a label to the branch
   1339             // to jump to.
   1340             try self.startBlock(.block, blocktype, null);
   1341             try self.emitWValue(target);
   1342             if (lowest < 0) {
   1343                 // since br_table works using indexes, starting from '0', we must ensure all values
   1344                 // we put inside, are atleast 0.
   1345                 try self.code.append(wasm.opcode(.i32_const));
   1346                 try leb.writeILEB128(self.code.writer(), lowest * -1);
   1347                 try self.code.append(wasm.opcode(.i32_add));
   1348             }
   1349             try self.code.append(wasm.opcode(.br_table));
   1350             const depth = highest - lowest + @boolToInt(has_else_body);
   1351             try leb.writeILEB128(self.code.writer(), depth);
   1352             while (lowest <= highest) : (lowest += 1) {
   1353                 const idx = if (map.get(lowest)) |value| blk: {
   1354                     break :blk value.index;
   1355                 } else if (has_else_body) case_i else unreachable;
   1356                 try leb.writeULEB128(self.code.writer(), idx);
   1357             } else if (has_else_body) {
   1358                 try leb.writeULEB128(self.code.writer(), @as(u32, case_i)); // default branch
   1359             }
   1360             try self.endBlock();
   1361         }
   1362 
   1363         const signedness: std.builtin.Signedness = blk: {
   1364             // by default we tell the operand type is unsigned (i.e. bools and enum values)
   1365             if (target_ty.zigTypeTag() != .Int) break :blk .unsigned;
   1366 
   1367             // incase of an actual integer, we emit the correct signedness
   1368             break :blk target_ty.intInfo(self.target).signedness;
   1369         };
   1370 
   1371         for (map.values()) |val| {
   1372             // when sparse, we use if/else-chain, so emit conditional checks
   1373             if (is_sparse) {
   1374                 try self.emitWValue(target);
   1375                 try self.emitConstant(val.value, target_ty);
   1376                 const opcode = buildOpcode(.{
   1377                     .valtype1 = try self.typeToValtype(target_ty),
   1378                     .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition.
   1379                     .signedness = signedness,
   1380                 });
   1381                 try self.code.append(wasm.opcode(opcode));
   1382                 try self.code.append(wasm.opcode(.br_if));
   1383                 try leb.writeULEB128(self.code.writer(), @as(u32, 0));
   1384             }
   1385             try self.genBody(val.body);
   1386             try self.endBlock();
   1387         }
   1388 
   1389         if (has_else_body) {
   1390             try self.genBody(else_body);
   1391             try self.endBlock();
   1392         }
   1393 
   1394         return .none;
   1395     }
   1396 
   1397     fn airIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
   1398         const un_op = self.air.instructions.items(.data)[inst].un_op;
   1399         const operand = self.resolveInst(un_op);
   1400         const offset = self.code.items.len;
   1401         const writer = self.code.writer();
   1402 
   1403         // load the error value which is positioned at multi_value's index
   1404         try self.emitWValue(.{ .local = operand.multi_value.index });
   1405         // Compare the error value with '0'
   1406         try writer.writeByte(wasm.opcode(.i32_const));
   1407         try leb.writeILEB128(writer, @as(i32, 0));
   1408 
   1409         try writer.writeByte(@enumToInt(opcode));
   1410 
   1411         return WValue{ .code_offset = offset };
   1412     }
   1413 
   1414     fn airUnwrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
   1415         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1416         const operand = self.resolveInst(ty_op.operand);
   1417         // The index of multi_value contains the error code. To get the initial index of the payload we get
   1418         // the following index. Next, convert it to a `WValue.local`
   1419         //
   1420         // TODO: Check if payload is a type that requires a multi_value as well and emit that instead. i.e. a struct.
   1421         return WValue{ .local = operand.multi_value.index + 1 };
   1422     }
   1423 
   1424     fn airWrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
   1425         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   1426         return self.resolveInst(ty_op.operand);
   1427     }
   1428 };