Commit 2438f61f1c

Luuk de Gram <luuk@degram.dev>
2021-07-16 22:43:06
Refactor entire wasm-backend to use new AIR memory layout
1 parent 424f260
Changed files (2)
src
codegen
link
src/codegen/wasm.zig
@@ -483,8 +483,8 @@ pub const Result = union(enum) {
     externally_managed: []const u8,
 };
 
-/// Hashmap to store generated `WValue` for each `Inst`
-pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Index, WValue);
+/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
+pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Ref, WValue);
 
 /// Code represents the `Code` section of wasm that
 /// belongs to a function
@@ -495,7 +495,7 @@ pub const Context = struct {
     air: Air,
     liveness: Liveness,
     gpa: *mem.Allocator,
-    /// Table to save `WValue`'s generated by an `Inst`
+    /// Table to save `WValue`'s generated by an `Air.Inst`
     values: ValueTable,
     /// Mapping from Air.Inst.Index to block ids
     blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{},
@@ -547,14 +547,15 @@ pub const Context = struct {
 
     /// Resolves the `WValue` for the given instruction `inst`
     /// When the given instruction has a `Value`, it returns a constant instead
-    fn resolveInst(self: Context, inst: Air.Inst) Index {
-        if (!inst.ty.hasCodeGenBits()) return .none;
+    fn resolveInst(self: Context, ref: Air.Inst.Ref) WValue {
+        const ref_type = self.air.getRefType(ref);
+        if (ref_type.hasCodeGenBits()) return .none;
 
-        if (inst.value()) |_| {
-            return WValue{ .constant = inst };
+        if (self.air.instructions.items(.tag)[@enumToInt(ref)] == .constant) {
+            return WValue{ .constant = @enumToInt(ref) };
         }
 
-        return self.values.get(inst).?; // Instruction does not dominate all uses!
+        return self.values.get(ref).?; // Instruction does not dominate all uses!
     }
 
     /// Using a given `Type`, returns the corresponding wasm Valtype
@@ -610,7 +611,12 @@ pub const Context = struct {
                 try writer.writeByte(wasm.opcode(.local_get));
                 try leb.writeULEB128(writer, idx);
             },
-            .constant => |inst| try self.emitConstant(inst.value().?, inst.ty), // creates a new constant onto the stack
+            .constant => |index| {
+                const ty_pl = self.air.instructions.items(.data)[index].ty_pl;
+                const value = self.air.values[ty_pl.payload];
+                // create a new constant onto the stack
+                try self.emitConstant(value, self.air.getRefType(ty_pl.ty));
+            },
         }
     }
 
@@ -626,10 +632,7 @@ pub const Context = struct {
                 const fields_len = @intCast(u32, struct_data.fields.count());
                 try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len);
                 for (struct_data.fields.values()) |*value| {
-                    const val_type = try self.genValtype(
-                        .{ .node_offset = struct_data.node_offset },
-                        value.ty,
-                    );
+                    const val_type = try self.genValtype(value.ty);
                     self.locals.appendAssumeCapacity(val_type);
                     self.local_index += 1;
                 }
@@ -640,7 +643,7 @@ pub const Context = struct {
             },
             .ErrorUnion => {
                 const payload_type = ty.errorUnionChild();
-                const val_type = try self.genValtype(.{ .node_offset = 0 }, payload_type);
+                const val_type = try self.genValtype(payload_type);
 
                 // we emit the error value as the first local, and the payload as the following.
                 // The first local is also used to find the index of the error and payload.
@@ -657,7 +660,7 @@ pub const Context = struct {
                 } };
             },
             else => {
-                const valtype = try self.genValtype(.{ .node_offset = 0 }, ty);
+                const valtype = try self.genValtype(ty);
                 try self.locals.append(self.gpa, valtype);
                 self.local_index += 1;
                 return WValue{ .local = initial_index };
@@ -708,8 +711,7 @@ pub const Context = struct {
         }
     }
 
-    pub fn genFunc(self: *Context, func: *Module.Fn) InnerError!Result {
-        _ = func;
+    pub fn genFunc(self: *Context) InnerError!Result {
         try self.genFunctype();
         // TODO: check for and handle death of instructions
 
@@ -790,44 +792,43 @@ pub const Context = struct {
     fn genInst(self: *Context, inst: Air.Inst.Index) !WValue {
         const air_tags = self.air.instructions.items(.tag);
         return switch (air_tags[inst]) {
-            // .add => self.genBinOp(inst.castTag(.add).?, .add),
-            // .alloc => self.genAlloc(inst.castTag(.alloc).?),
-            // .arg => self.genArg(inst.castTag(.arg).?),
-            // .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"),
-            // .bitcast => self.genBitcast(inst.castTag(.bitcast).?),
-            // .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"),
-            // .block => self.genBlock(inst.castTag(.block).?),
-            // .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"),
-            // .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"),
-            // .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),
-            // .br => self.genBr(inst.castTag(.br).?),
-            // .call => self.genCall(inst.castTag(.call).?),
-            // .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
-            // .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte),
-            // .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt),
-            // .cmp_lte => self.genCmp(inst.castTag(.cmp_lte).?, .lte),
-            // .cmp_lt => self.genCmp(inst.castTag(.cmp_lt).?, .lt),
-            // .cmp_neq => self.genCmp(inst.castTag(.cmp_neq).?, .neq),
-            // .condbr => self.genCondBr(inst.castTag(.condbr).?),
-            // .constant => unreachable,
-            // .dbg_stmt => WValue.none,
-            // .div => self.genBinOp(inst.castTag(.div).?, .div),
-            // .is_err => self.genIsErr(inst.castTag(.is_err).?, .i32_ne),
-            // .is_non_err => self.genIsErr(inst.castTag(.is_non_err).?, .i32_eq),
-            // .load => self.genLoad(inst.castTag(.load).?),
-            // .loop => self.genLoop(inst.castTag(.loop).?),
-            // .mul => self.genBinOp(inst.castTag(.mul).?, .mul),
-            // .not => self.genNot(inst.castTag(.not).?),
-            // .ret => self.genRet(inst.castTag(.ret).?),
-            // .retvoid => WValue.none,
-            // .store => self.genStore(inst.castTag(.store).?),
-            // .struct_field_ptr => self.genStructFieldPtr(inst.castTag(.struct_field_ptr).?),
-            // .sub => self.genBinOp(inst.castTag(.sub).?, .sub),
-            // .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?),
-            // .unreach => self.genUnreachable(inst.castTag(.unreach).?),
-            // .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst.castTag(.unwrap_errunion_payload).?),
-            // .wrap_errunion_payload => self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?),
-            // .xor => self.genBinOp(inst.castTag(.xor).?, .xor),
+            .add => self.genBinOp(inst, .add),
+            .alloc => self.genAlloc(inst),
+            .arg => self.genArg(inst),
+            .bit_and => self.genBinOp(inst, .@"and"),
+            .bitcast => self.genBitcast(inst),
+            .bit_or => self.genBinOp(inst, .@"or"),
+            .block => self.genBlock(inst),
+            .bool_and => self.genBinOp(inst, .@"and"),
+            .bool_or => self.genBinOp(inst, .@"or"),
+            .breakpoint => self.genBreakpoint(inst),
+            .br => self.genBr(inst),
+            .call => self.genCall(inst),
+            .cmp_eq => self.genCmp(inst, .eq),
+            .cmp_gte => self.genCmp(inst, .gte),
+            .cmp_gt => self.genCmp(inst, .gt),
+            .cmp_lte => self.genCmp(inst, .lte),
+            .cmp_lt => self.genCmp(inst, .lt),
+            .cmp_neq => self.genCmp(inst, .neq),
+            .cond_br => self.genCondBr(inst),
+            .constant => unreachable,
+            .dbg_stmt => WValue.none,
+            .div => self.genBinOp(inst, .div),
+            .is_err => self.genIsErr(inst, .i32_ne),
+            .is_non_err => self.genIsErr(inst, .i32_eq),
+            .load => self.genLoad(inst),
+            .loop => self.genLoop(inst),
+            .mul => self.genBinOp(inst, .mul),
+            .not => self.genNot(inst),
+            .ret => self.genRet(inst),
+            .store => self.genStore(inst),
+            .struct_field_ptr => self.genStructFieldPtr(inst),
+            .sub => self.genBinOp(inst, .sub),
+            .switch_br => self.genSwitchBr(inst),
+            .unreach => self.genUnreachable(inst),
+            .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst),
+            .wrap_errunion_payload => self.genWrapErrUnionPayload(inst),
+            .xor => self.genBinOp(inst, .xor),
             else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
         };
     }
@@ -835,22 +836,27 @@ pub const Context = struct {
     fn genBody(self: *Context, body: []const Air.Inst.Index) InnerError!void {
         for (body) |inst| {
             const result = try self.genInst(inst);
-            try self.values.putNoClobber(self.gpa, inst, result);
+            try self.values.putNoClobber(self.gpa, @intToEnum(Air.Inst.Ref, inst), result);
         }
     }
 
     fn genRet(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
-        // TODO: Implement tail calls
-        const operand = self.resolveInst(inst.operand);
+        const un_op = self.air.instructions.items(.data)[inst].un_op;
+        const operand = self.resolveInst(un_op);
         try self.emitWValue(operand);
         try self.code.append(wasm.opcode(.@"return"));
         return .none;
     }
 
     fn genCall(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
-        const func_val = inst.func.value().?;
+        const pl_op = self.air.instructions.items(.data)[inst].pl_op;
+        const extra = self.air.extraData(Air.Call, pl_op.payload);
+        const args = self.air.extra[extra.end..][0..extra.data.args_len];
 
         const target: *Decl = blk: {
+            const ty_pl = self.air.instructions.items(.data)[@enumToInt(pl_op.operand)].ty_pl;
+            const func_val = self.air.values[ty_pl.payload];
+
             if (func_val.castTag(.function)) |func| {
                 break :blk func.data.owner_decl;
             } else if (func_val.castTag(.extern_fn)) |ext_fn| {
@@ -859,8 +865,8 @@ pub const Context = struct {
             return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()});
         };
 
-        for (inst.args) |arg| {
-            const arg_val = self.resolveInst(arg);
+        for (args) |arg| {
+            const arg_val = self.resolveInst(@intToEnum(Air.Inst.Ref, arg));
             try self.emitWValue(arg_val);
         }
 
@@ -877,15 +883,16 @@ pub const Context = struct {
     }
 
     fn genAlloc(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
-        const elem_type = inst.base.ty.elemType();
+        const elem_type = self.air.getType(inst).elemType();
         return self.allocLocal(elem_type);
     }
 
     fn genStore(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
+        const bin_op = self.air.instructions.items(.data)[inst].bin_op;
         const writer = self.code.writer();
 
-        const lhs = self.resolveInst(inst.lhs);
-        const rhs = self.resolveInst(inst.rhs);
+        const lhs = self.resolveInst(bin_op.lhs);
+        const rhs = self.resolveInst(bin_op.rhs);
 
         switch (lhs) {
             .multi_value => |multi_value| switch (rhs) {
@@ -893,7 +900,7 @@ pub const Context = struct {
                 // we simply assign the local_index to the rhs one.
                 // This allows us to update struct fields without having to individually
                 // set each local as each field's index will be calculated off the struct's base index
-                .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses!
+                .multi_value => self.values.put(self.gpa, bin_op.lhs, rhs) catch unreachable, // Instruction does not dominate all uses!
                 .constant, .none => {
                     // emit all values onto the stack if constant
                     try self.emitWValue(rhs);
@@ -920,7 +927,8 @@ pub const Context = struct {
     }
 
     fn genLoad(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
-        return self.resolveInst(inst.operand);
+        const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+        return self.resolveInst(ty_op.operand);
     }
 
     fn genArg(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
@@ -931,8 +939,9 @@ pub const Context = struct {
     }
 
     fn genBinOp(self: *Context, inst: Air.Inst.Index, op: Op) InnerError!WValue {
-        const lhs = self.resolveInst(inst.lhs);
-        const rhs = self.resolveInst(inst.rhs);
+        const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+        const lhs = self.resolveInst(bin_op.lhs);
+        const rhs = self.resolveInst(bin_op.rhs);
 
         // it's possible for both lhs and/or rhs to return an offset as well,
         // in which case we return the first offset occurance we find.
@@ -945,10 +954,11 @@ pub const Context = struct {
         try self.emitWValue(lhs);
         try self.emitWValue(rhs);
 
+        const bin_ty = self.air.getRefType(bin_op.lhs);
         const opcode: wasm.Opcode = buildOpcode(.{
             .op = op,
-            .valtype1 = try self.typeToValtype(inst.base.ty),
-            .signedness = if (inst.base.ty.isSignedInt()) .signed else .unsigned,
+            .valtype1 = try self.typeToValtype(bin_ty),
+            .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
         });
         try self.code.append(wasm.opcode(opcode));
         return WValue{ .code_offset = offset };
@@ -1064,14 +1074,17 @@ pub const Context = struct {
         }
     }
 
-    fn genBlock(self: *Context, block: Air.Inst.Index) InnerError!WValue {
-        const block_ty = try self.genBlockType(block.base.ty);
+    fn genBlock(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
+        const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+        const block_ty = try self.genBlockType(self.air.getRefType(ty_pl.ty));
+        const extra = self.air.extraData(Air.Block, ty_pl.payload);
+        const body = self.air.extra[extra.end..][0..extra.data.body_len];
 
         try self.startBlock(.block, block_ty, null);
         // Here we set the current block idx, so breaks know the depth to jump
         // to when breaking out.
-        try self.blocks.putNoClobber(self.gpa, block, self.block_depth);
-        try self.genBody(block.body);
+        try self.blocks.putNoClobber(self.gpa, inst, self.block_depth);
+        try self.genBody(body);
         try self.endBlock();
 
         return .none;
@@ -1095,11 +1108,15 @@ pub const Context = struct {
         self.block_depth -= 1;
     }
 
-    fn genLoop(self: *Context, loop: Air.Inst.Index) InnerError!WValue {
-        const loop_ty = try self.genBlockType(loop.base.ty);
+    fn genLoop(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
+        const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+        const loop = self.air.extraData(Air.Block, ty_pl.payload);
+        const body = self.air.extra[loop.end..][0..loop.data.body_len];
 
-        try self.startBlock(.loop, loop_ty, null);
-        try self.genBody(loop.body);
+        // result type of loop is always 'noreturn', meaning we can always
+        // emit the wasm type 'block_empty'.
+        try self.startBlock(.loop, wasm.block_empty, null);
+        try self.genBody(body);
 
         // breaking to the index of a loop block will continue the loop instead
         try self.code.append(wasm.opcode(.br));
@@ -1110,8 +1127,12 @@ pub const Context = struct {
         return .none;
     }
 
-    fn genCondBr(self: *Context, condbr: Air.Inst.Index) InnerError!WValue {
-        const condition = self.resolveInst(condbr.condition);
+    fn genCondBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
+        const pl_op = self.air.instructions.items(.data)[inst].pl_op;
+        const condition = self.resolveInst(pl_op.operand);
+        const extra = self.air.extraData(Air.CondBr, pl_op.payload);
+        const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
+        const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
         const writer = self.code.writer();
 
         // TODO: Handle death instructions for then and else body
@@ -1126,8 +1147,9 @@ pub const Context = struct {
                 break :blk offset;
             },
         };
-        const block_ty = try self.genBlockType(condbr.base.ty);
-        try self.startBlock(.block, block_ty, offset);
+
+        // result type is always noreturn, so use `block_empty` as type.
+        try self.startBlock(.block, wasm.block_empty, offset);
 
         // we inserted the block in front of the condition
         // so now check if condition matches. If not, break outside this block
@@ -1135,11 +1157,11 @@ pub const Context = struct {
         try writer.writeByte(wasm.opcode(.br_if));
         try leb.writeULEB128(writer, @as(u32, 0));
 
-        try self.genBody(condbr.else_body);
+        try self.genBody(else_body);
         try self.endBlock();
 
         // Outer block that matches the condition
-        try self.genBody(condbr.then_body);
+        try self.genBody(then_body);
 
         return .none;
     }
@@ -1149,21 +1171,23 @@ pub const Context = struct {
         // the comparison that we can later jump back to
         const offset = self.code.items.len;
 
-        const lhs = self.resolveInst(inst.lhs);
-        const rhs = self.resolveInst(inst.rhs);
+        const data: Air.Inst.Data = self.air.instructions.items(.data)[inst];
+        const lhs = self.resolveInst(data.bin_op.lhs);
+        const rhs = self.resolveInst(data.bin_op.rhs);
+        const lhs_ty = self.air.getRefType(data.bin_op.lhs);
 
         try self.emitWValue(lhs);
         try self.emitWValue(rhs);
 
         const signedness: std.builtin.Signedness = blk: {
             // by default we tell the operand type is unsigned (i.e. bools and enum values)
-            if (inst.lhs.ty.zigTypeTag() != .Int) break :blk .unsigned;
+            if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned;
 
             // incase of an actual integer, we emit the correct signedness
-            break :blk inst.lhs.ty.intInfo(self.target).signedness;
+            break :blk lhs_ty.intInfo(self.target).signedness;
         };
         const opcode: wasm.Opcode = buildOpcode(.{
-            .valtype1 = try self.typeToValtype(inst.lhs.ty),
+            .valtype1 = try self.typeToValtype(lhs_ty),
             .op = switch (op) {
                 .lt => .lt,
                 .lte => .le,
@@ -1178,16 +1202,17 @@ pub const Context = struct {
         return WValue{ .code_offset = offset };
     }
 
-    fn genBr(self: *Context, br: Air.Inst.Index) InnerError!WValue {
+    fn genBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
+        const br = self.air.instructions.items(.data)[inst].br;
+
         // if operand has codegen bits we should break with a value
-        if (br.operand.ty.hasCodeGenBits()) {
-            const operand = self.resolveInst(br.operand);
-            try self.emitWValue(operand);
+        if (self.air.getRefType(br.operand).hasCodeGenBits()) {
+            try self.emitWValue(self.resolveInst(br.operand));
         }
 
         // We map every block to its block index.
         // We then determine how far we have to jump to it by substracting it from current block depth
-        const idx: u32 = self.block_depth - self.blocks.get(br.block).?;
+        const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?;
         const writer = self.code.writer();
         try writer.writeByte(wasm.opcode(.br));
         try leb.writeULEB128(writer, idx);
@@ -1195,10 +1220,11 @@ pub const Context = struct {
         return .none;
     }
 
-    fn genNot(self: *Context, not: Air.Inst.Index) InnerError!WValue {
+    fn genNot(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
+        const ty_op = self.air.instructions.items(.data)[inst].ty_op;
         const offset = self.code.items.len;
 
-        const operand = self.resolveInst(not.operand);
+        const operand = self.resolveInst(ty_op.operand);
         try self.emitWValue(operand);
 
         // wasm does not have booleans nor the `not` instruction, therefore compare with 0
@@ -1212,35 +1238,44 @@ pub const Context = struct {
         return WValue{ .code_offset = offset };
     }
 
-    fn genBreakpoint(self: *Context, breakpoint: Air.Inst.Index) InnerError!WValue {
+    fn genBreakpoint(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
         _ = self;
-        _ = breakpoint;
+        _ = inst;
         // unsupported by wasm itself. Can be implemented once we support DWARF
         // for wasm
         return .none;
     }
 
-    fn genUnreachable(self: *Context, unreach: Air.Inst.Index) InnerError!WValue {
-        _ = unreach;
+    fn genUnreachable(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
+        _ = inst;
         try self.code.append(wasm.opcode(.@"unreachable"));
         return .none;
     }
 
-    fn genBitcast(self: *Context, bitcast: Air.Inst.Index) InnerError!WValue {
-        return self.resolveInst(bitcast.operand);
+    fn genBitcast(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
+        const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+        return self.resolveInst(ty_op.operand);
     }
 
     fn genStructFieldPtr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
-        const struct_ptr = self.resolveInst(inst.struct_ptr);
+        const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+        const extra = self.air.extraData(Air.StructField, ty_pl.payload);
+        const struct_ptr = self.resolveInst(extra.data.struct_ptr);
 
-        return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, inst.field_index) };
+        return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, extra.data.field_index) };
     }
 
     fn genSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
-        const target = self.resolveInst(inst.target);
-        const target_ty = inst.target.ty;
-        const valtype = try self.typeToValtype(.{ .node_offset = 0 }, target_ty);
-        const blocktype = try self.genBlockType(inst.base.ty);
+        const pl_op = self.air.instructions.items(.data)[inst].pl_op;
+        const extra = self.air.extraData(Air.SwitchBr, pl_op.payload);
+        const cases = self.air.extra[extra.end..][0..extra.data.cases_len];
+        const else_body = self.air.extra[extra.end + cases.len ..][0..extra.data.else_body_len];
+
+        const target = self.resolveInst(pl_op.operand);
+        const target_ty = self.air.getRefType(pl_op.operand);
+        const valtype = try self.typeToValtype(target_ty);
+        // result type is always 'noreturn'
+        const blocktype = wasm.block_empty;
 
         const signedness: std.builtin.Signedness = blk: {
             // by default we tell the operand type is unsigned (i.e. bools and enum values)
@@ -1249,11 +1284,18 @@ pub const Context = struct {
             // incase of an actual integer, we emit the correct signedness
             break :blk target_ty.intInfo(self.target).signedness;
         };
-        for (inst.cases) |case| {
+        for (cases) |case_idx| {
+            const case = self.air.extraData(Air.SwitchBr.Case, case_idx);
+            const case_body = self.air.extra[case.end..][0..case.data.body_len];
+
             // create a block for each case, when the condition does not match we break out of it
             try self.startBlock(.block, blocktype, null);
             try self.emitWValue(target);
-            try self.emitConstant(.{ .node_offset = 0 }, case.item, target_ty);
+
+            // cases must represent a constant of which its type is in the `typed_value_map`
+            // Therefore we can simply retrieve it.
+            const ty_val = Air.Inst.Ref.typed_value_map[@enumToInt(case.data.item)];
+            try self.emitConstant(ty_val.val, target_ty);
             const opcode = buildOpcode(.{
                 .valtype1 = valtype,
                 .op = .ne, // not equal because we jump out the block if it does not match the condition
@@ -1264,7 +1306,7 @@ pub const Context = struct {
             try leb.writeULEB128(self.code.writer(), @as(u32, 0));
 
             // emit our block code
-            try self.genBody(case.body);
+            try self.genBody(case_body);
 
             // end the block we created earlier
             try self.endBlock();
@@ -1272,13 +1314,14 @@ pub const Context = struct {
 
         // finally, emit the else case if it exists. Here we will not have to
         // check for a condition, so also no need to emit a block.
-        try self.genBody(inst.else_body);
+        try self.genBody(else_body);
 
         return .none;
     }
 
     fn genIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
-        const operand = self.resolveInst(inst.operand);
+        const un_op = self.air.instructions.items(.data)[inst].un_op;
+        const operand = self.resolveInst(un_op);
         const offset = self.code.items.len;
         const writer = self.code.writer();
 
@@ -1294,7 +1337,8 @@ pub const Context = struct {
     }
 
     fn genUnwrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
-        const operand = self.resolveInst(inst.operand);
+        const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+        const operand = self.resolveInst(ty_op.operand);
         // The index of multi_value contains the error code. To get the initial index of the payload we get
         // the following index. Next, convert it to a `WValue.local`
         //
@@ -1303,6 +1347,7 @@ pub const Context = struct {
     }
 
     fn genWrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
-        return self.resolveInst(inst.operand);
+        const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+        return self.resolveInst(ty_op.operand);
     }
 };
src/link/Wasm.zig
@@ -220,7 +220,7 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
     defer context.deinit();
 
     // generate the 'code' section for the function declaration
-    const result = context.genFunc(func) catch |err| switch (err) {
+    const result = context.genFunc() catch |err| switch (err) {
         error.CodegenFail => {
             decl.analysis = .codegen_failure;
             try module.failed_decls.put(module.gpa, decl, context.err_msg);