Commit 424f260f85

Luuk de Gram <luuk@degram.dev>
2021-07-16 14:48:51
Fix wasm-related compile errors:
- Update `fail()` to not require a `srcLoc`. This brings it in line with other backends, and we were always passing 'node_offset = 0', anyway. - Fix unused local due to change of architecture wrt function/decl generation. - Replace all old instructions to indexes within the function signatures.
1 parent 8082660
Changed files (2)
src
codegen
link
src/codegen/wasm.zig
@@ -25,7 +25,7 @@ const WValue = union(enum) {
     /// Index of the local variable
     local: u32,
     /// Instruction holding a constant `Value`
-    constant: *Inst,
+    constant: Air.Inst.Index,
     /// Offset position in the list of bytecode instructions
     code_offset: usize,
     /// Used for variables that create multiple locals on the stack when allocated
@@ -484,7 +484,7 @@ pub const Result = union(enum) {
 };
 
 /// Hashmap to store generated `WValue` for each `Inst`
-pub const ValueTable = std.AutoHashMapUnmanaged(*Inst, WValue);
+pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Index, WValue);
 
 /// Code represents the `Code` section of wasm that
 /// belongs to a function
@@ -497,8 +497,8 @@ pub const Context = struct {
     gpa: *mem.Allocator,
     /// Table to save `WValue`'s generated by an `Inst`
     values: ValueTable,
-    /// Mapping from *Inst.Block to block ids
-    blocks: std.AutoArrayHashMapUnmanaged(*Inst.Block, u32) = .{},
+    /// Mapping from Air.Inst.Index to block ids
+    blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{},
     /// `bytes` contains the wasm bytecode belonging to the 'code' section.
     code: ArrayList(u8),
     /// Contains the generated function type bytecode for the current function
@@ -538,7 +538,8 @@ pub const Context = struct {
     }
 
     /// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig
-    fn fail(self: *Context, src: LazySrcLoc, comptime fmt: []const u8, args: anytype) InnerError {
+    fn fail(self: *Context, comptime fmt: []const u8, args: anytype) InnerError {
+        const src: LazySrcLoc = .{ .node_offset = 0 };
         const src_loc = src.toSrcLocWithDecl(self.decl);
         self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args);
         return error.CodegenFail;
@@ -546,7 +547,7 @@ 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: *Inst) WValue {
+    fn resolveInst(self: Context, inst: Air.Inst) Index {
         if (!inst.ty.hasCodeGenBits()) return .none;
 
         if (inst.value()) |_| {
@@ -557,48 +558,45 @@ pub const Context = struct {
     }
 
     /// Using a given `Type`, returns the corresponding wasm Valtype
-    fn typeToValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!wasm.Valtype {
+    fn typeToValtype(self: *Context, ty: Type) InnerError!wasm.Valtype {
         return switch (ty.zigTypeTag()) {
             .Float => blk: {
                 const bits = ty.floatBits(self.target);
                 if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32;
                 if (bits == 64) break :blk wasm.Valtype.f64;
-                return self.fail(src, "Float bit size not supported by wasm: '{d}'", .{bits});
+                return self.fail("Float bit size not supported by wasm: '{d}'", .{bits});
             },
             .Int => blk: {
                 const info = ty.intInfo(self.target);
                 if (info.bits <= 32) break :blk wasm.Valtype.i32;
                 if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64;
-                return self.fail(src, "Integer bit size not supported by wasm: '{d}'", .{info.bits});
+                return self.fail("Integer bit size not supported by wasm: '{d}'", .{info.bits});
             },
             .Enum => switch (ty.tag()) {
                 .enum_simple => wasm.Valtype.i32,
-                else => self.typeToValtype(
-                    src,
-                    ty.cast(Type.Payload.EnumFull).?.data.tag_ty,
-                ),
+                else => self.typeToValtype(ty.cast(Type.Payload.EnumFull).?.data.tag_ty),
             },
             .Bool,
             .Pointer,
             .ErrorSet,
             => wasm.Valtype.i32,
             .Struct, .ErrorUnion => unreachable, // Multi typed, must be handled individually.
-            else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.zigTypeTag()}),
+            else => self.fail("TODO - Wasm valtype for type '{s}'", .{ty.zigTypeTag()}),
         };
     }
 
     /// Using a given `Type`, returns the byte representation of its wasm value type
-    fn genValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 {
-        return wasm.valtype(try self.typeToValtype(src, ty));
+    fn genValtype(self: *Context, ty: Type) InnerError!u8 {
+        return wasm.valtype(try self.typeToValtype(ty));
     }
 
     /// Using a given `Type`, returns the corresponding wasm value type
     /// Differently from `genValtype` this also allows `void` to create a block
     /// with no return type
-    fn genBlockType(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 {
+    fn genBlockType(self: *Context, ty: Type) InnerError!u8 {
         return switch (ty.tag()) {
             .void, .noreturn => wasm.block_empty,
-            else => self.genValtype(src, ty),
+            else => self.genValtype(ty),
         };
     }
 
@@ -612,7 +610,7 @@ pub const Context = struct {
                 try writer.writeByte(wasm.opcode(.local_get));
                 try leb.writeULEB128(writer, idx);
             },
-            .constant => |inst| try self.emitConstant(inst.src, inst.value().?, inst.ty), // creates a new constant onto the stack
+            .constant => |inst| try self.emitConstant(inst.value().?, inst.ty), // creates a new constant onto the stack
         }
     }
 
@@ -682,7 +680,7 @@ pub const Context = struct {
             ty.fnParamTypes(params);
             for (params) |param_type| {
                 // Can we maybe get the source index of each param?
-                const val_type = try self.genValtype(.{ .node_offset = 0 }, param_type);
+                const val_type = try self.genValtype(param_type);
                 try writer.writeByte(val_type);
             }
         }
@@ -691,13 +689,10 @@ pub const Context = struct {
         const return_type = ty.fnReturnType();
         switch (return_type.zigTypeTag()) {
             .Void, .NoReturn => try leb.writeULEB128(writer, @as(u32, 0)),
-            .Struct => return self.fail(.{ .node_offset = 0 }, "TODO: Implement struct as return type for wasm", .{}),
-            .Optional => return self.fail(.{ .node_offset = 0 }, "TODO: Implement optionals as return type for wasm", .{}),
+            .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}),
+            .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}),
             .ErrorUnion => {
-                const val_type = try self.genValtype(
-                    .{ .node_offset = 0 },
-                    return_type.errorUnionChild(),
-                );
+                const val_type = try self.genValtype(return_type.errorUnionChild());
 
                 // write down the amount of return values
                 try leb.writeULEB128(writer, @as(u32, 2));
@@ -707,22 +702,21 @@ pub const Context = struct {
             else => {
                 try leb.writeULEB128(writer, @as(u32, 1));
                 // Can we maybe get the source index of the return type?
-                const val_type = try self.genValtype(.{ .node_offset = 0 }, return_type);
+                const val_type = try self.genValtype(return_type);
                 try writer.writeByte(val_type);
             },
         }
     }
 
     pub fn genFunc(self: *Context, func: *Module.Fn) InnerError!Result {
+        _ = func;
         try self.genFunctype();
-
-        // Write instructions
         // TODO: check for and handle death of instructions
 
         // Reserve space to write the size after generating the code as well as space for locals count
         try self.code.resize(10);
 
-        try self.genBody(func.body);
+        try self.genBody(self.air.getMainBody());
 
         // finally, write our local types at the 'offset' position
         {
@@ -753,7 +747,7 @@ pub const Context = struct {
         return Result.appended;
     }
 
-    /// Generates the wasm bytecode for the function declaration belonging to `Context`
+    /// Generates the wasm bytecode for the declaration belonging to `Context`
     pub fn gen(self: *Context, typed_value: TypedValue) InnerError!Result {
         switch (typed_value.ty.zigTypeTag()) {
             .Fn => {
@@ -793,58 +787,59 @@ pub const Context = struct {
         }
     }
 
-    fn genInst(self: *Context, inst: *Inst) InnerError!WValue {
-        return switch (inst.tag) {
-            .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),
-            else => self.fail(.{ .node_offset = 0 }, "TODO: Implement wasm inst: {s}", .{inst.tag}),
+    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),
+            else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
         };
     }
 
-    fn genBody(self: *Context, body: ir.Body) InnerError!void {
-        for (body.instructions) |inst| {
+    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);
         }
     }
 
-    fn genRet(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
+    fn genRet(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
         // TODO: Implement tail calls
         const operand = self.resolveInst(inst.operand);
         try self.emitWValue(operand);
@@ -852,7 +847,7 @@ pub const Context = struct {
         return .none;
     }
 
-    fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue {
+    fn genCall(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
         const func_val = inst.func.value().?;
 
         const target: *Decl = blk: {
@@ -861,7 +856,7 @@ pub const Context = struct {
             } else if (func_val.castTag(.extern_fn)) |ext_fn| {
                 break :blk ext_fn.data;
             }
-            return self.fail(inst.base.src, "Expected a function, but instead found type '{s}'", .{func_val.tag()});
+            return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()});
         };
 
         for (inst.args) |arg| {
@@ -881,12 +876,12 @@ pub const Context = struct {
         return .none;
     }
 
-    fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue {
+    fn genAlloc(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
         const elem_type = inst.base.ty.elemType();
         return self.allocLocal(elem_type);
     }
 
-    fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue {
+    fn genStore(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
         const writer = self.code.writer();
 
         const lhs = self.resolveInst(inst.lhs);
@@ -924,18 +919,18 @@ pub const Context = struct {
         return .none;
     }
 
-    fn genLoad(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
+    fn genLoad(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
         return self.resolveInst(inst.operand);
     }
 
-    fn genArg(self: *Context, inst: *Inst.Arg) InnerError!WValue {
+    fn genArg(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
         _ = inst;
         // arguments share the index with locals
         defer self.local_index += 1;
         return WValue{ .local = self.local_index };
     }
 
-    fn genBinOp(self: *Context, inst: *Inst.BinOp, op: Op) InnerError!WValue {
+    fn genBinOp(self: *Context, inst: Air.Inst.Index, op: Op) InnerError!WValue {
         const lhs = self.resolveInst(inst.lhs);
         const rhs = self.resolveInst(inst.rhs);
 
@@ -952,21 +947,21 @@ pub const Context = struct {
 
         const opcode: wasm.Opcode = buildOpcode(.{
             .op = op,
-            .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty),
+            .valtype1 = try self.typeToValtype(inst.base.ty),
             .signedness = if (inst.base.ty.isSignedInt()) .signed else .unsigned,
         });
         try self.code.append(wasm.opcode(opcode));
         return WValue{ .code_offset = offset };
     }
 
-    fn emitConstant(self: *Context, src: LazySrcLoc, value: Value, ty: Type) InnerError!void {
+    fn emitConstant(self: *Context, value: Value, ty: Type) InnerError!void {
         const writer = self.code.writer();
         switch (ty.zigTypeTag()) {
             .Int => {
                 // write opcode
                 const opcode: wasm.Opcode = buildOpcode(.{
                     .op = .@"const",
-                    .valtype1 = try self.typeToValtype(src, ty),
+                    .valtype1 = try self.typeToValtype(ty),
                 });
                 try writer.writeByte(wasm.opcode(opcode));
                 // write constant
@@ -985,14 +980,14 @@ pub const Context = struct {
                 // write opcode
                 const opcode: wasm.Opcode = buildOpcode(.{
                     .op = .@"const",
-                    .valtype1 = try self.typeToValtype(src, ty),
+                    .valtype1 = try self.typeToValtype(ty),
                 });
                 try writer.writeByte(wasm.opcode(opcode));
                 // write constant
                 switch (ty.floatBits(self.target)) {
                     0...32 => try writer.writeIntLittle(u32, @bitCast(u32, value.toFloat(f32))),
                     64 => try writer.writeIntLittle(u64, @bitCast(u64, value.toFloat(f64))),
-                    else => |bits| return self.fail(src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}),
+                    else => |bits| return self.fail("Wasm TODO: emitConstant for float with {d} bits", .{bits}),
                 }
             },
             .Pointer => {
@@ -1009,7 +1004,7 @@ pub const Context = struct {
                     try writer.writeByte(wasm.opcode(.i32_load));
                     try leb.writeULEB128(writer, @as(u32, 0));
                     try leb.writeULEB128(writer, @as(u32, 0));
-                } else return self.fail(src, "Wasm TODO: emitConstant for other const pointer tag {s}", .{value.tag()});
+                } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{value.tag()});
             },
             .Void => {},
             .Enum => {
@@ -1023,7 +1018,7 @@ pub const Context = struct {
                             const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
                             if (enum_full.values.count() != 0) {
                                 const tag_val = enum_full.values.keys()[field_index.data];
-                                try self.emitConstant(src, tag_val, enum_full.tag_ty);
+                                try self.emitConstant(tag_val, enum_full.tag_ty);
                             } else {
                                 try writer.writeByte(wasm.opcode(.i32_const));
                                 try leb.writeULEB128(writer, field_index.data);
@@ -1034,7 +1029,7 @@ pub const Context = struct {
                 } else {
                     var int_tag_buffer: Type.Payload.Bits = undefined;
                     const int_tag_ty = ty.intTagType(&int_tag_buffer);
-                    try self.emitConstant(src, value, int_tag_ty);
+                    try self.emitConstant(value, int_tag_ty);
                 }
             },
             .ErrorSet => {
@@ -1048,12 +1043,12 @@ pub const Context = struct {
                 const payload_type = ty.errorUnionChild();
                 if (value.getError()) |_| {
                     // write the error value
-                    try self.emitConstant(src, data, error_type);
+                    try self.emitConstant(data, error_type);
 
                     // no payload, so write a '0' const
                     const opcode: wasm.Opcode = buildOpcode(.{
                         .op = .@"const",
-                        .valtype1 = try self.typeToValtype(src, payload_type),
+                        .valtype1 = try self.typeToValtype(payload_type),
                     });
                     try writer.writeByte(wasm.opcode(opcode));
                     try leb.writeULEB128(writer, @as(u32, 0));
@@ -1062,15 +1057,15 @@ pub const Context = struct {
                     try writer.writeByte(wasm.opcode(.i32_const));
                     try leb.writeULEB128(writer, @as(u32, 0));
                     // after the error code, we emit the payload
-                    try self.emitConstant(src, data, payload_type);
+                    try self.emitConstant(data, payload_type);
                 }
             },
-            else => |zig_type| return self.fail(src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}),
+            else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}),
         }
     }
 
-    fn genBlock(self: *Context, block: *Inst.Block) InnerError!WValue {
-        const block_ty = try self.genBlockType(block.base.src, block.base.ty);
+    fn genBlock(self: *Context, block: Air.Inst.Index) InnerError!WValue {
+        const block_ty = try self.genBlockType(block.base.ty);
 
         try self.startBlock(.block, block_ty, null);
         // Here we set the current block idx, so breaks know the depth to jump
@@ -1100,8 +1095,8 @@ pub const Context = struct {
         self.block_depth -= 1;
     }
 
-    fn genLoop(self: *Context, loop: *Inst.Loop) InnerError!WValue {
-        const loop_ty = try self.genBlockType(loop.base.src, loop.base.ty);
+    fn genLoop(self: *Context, loop: Air.Inst.Index) InnerError!WValue {
+        const loop_ty = try self.genBlockType(loop.base.ty);
 
         try self.startBlock(.loop, loop_ty, null);
         try self.genBody(loop.body);
@@ -1115,7 +1110,7 @@ pub const Context = struct {
         return .none;
     }
 
-    fn genCondBr(self: *Context, condbr: *Inst.CondBr) InnerError!WValue {
+    fn genCondBr(self: *Context, condbr: Air.Inst.Index) InnerError!WValue {
         const condition = self.resolveInst(condbr.condition);
         const writer = self.code.writer();
 
@@ -1131,7 +1126,7 @@ pub const Context = struct {
                 break :blk offset;
             },
         };
-        const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty);
+        const block_ty = try self.genBlockType(condbr.base.ty);
         try self.startBlock(.block, block_ty, offset);
 
         // we inserted the block in front of the condition
@@ -1149,7 +1144,7 @@ pub const Context = struct {
         return .none;
     }
 
-    fn genCmp(self: *Context, inst: *Inst.BinOp, op: std.math.CompareOperator) InnerError!WValue {
+    fn genCmp(self: *Context, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue {
         // save offset, so potential conditions can insert blocks in front of
         // the comparison that we can later jump back to
         const offset = self.code.items.len;
@@ -1168,7 +1163,7 @@ pub const Context = struct {
             break :blk inst.lhs.ty.intInfo(self.target).signedness;
         };
         const opcode: wasm.Opcode = buildOpcode(.{
-            .valtype1 = try self.typeToValtype(inst.base.src, inst.lhs.ty),
+            .valtype1 = try self.typeToValtype(inst.lhs.ty),
             .op = switch (op) {
                 .lt => .lt,
                 .lte => .le,
@@ -1183,7 +1178,7 @@ pub const Context = struct {
         return WValue{ .code_offset = offset };
     }
 
-    fn genBr(self: *Context, br: *Inst.Br) InnerError!WValue {
+    fn genBr(self: *Context, br: Air.Inst.Index) InnerError!WValue {
         // if operand has codegen bits we should break with a value
         if (br.operand.ty.hasCodeGenBits()) {
             const operand = self.resolveInst(br.operand);
@@ -1200,7 +1195,7 @@ pub const Context = struct {
         return .none;
     }
 
-    fn genNot(self: *Context, not: *Inst.UnOp) InnerError!WValue {
+    fn genNot(self: *Context, not: Air.Inst.Index) InnerError!WValue {
         const offset = self.code.items.len;
 
         const operand = self.resolveInst(not.operand);
@@ -1217,7 +1212,7 @@ pub const Context = struct {
         return WValue{ .code_offset = offset };
     }
 
-    fn genBreakpoint(self: *Context, breakpoint: *Inst.NoOp) InnerError!WValue {
+    fn genBreakpoint(self: *Context, breakpoint: Air.Inst.Index) InnerError!WValue {
         _ = self;
         _ = breakpoint;
         // unsupported by wasm itself. Can be implemented once we support DWARF
@@ -1225,27 +1220,27 @@ pub const Context = struct {
         return .none;
     }
 
-    fn genUnreachable(self: *Context, unreach: *Inst.NoOp) InnerError!WValue {
+    fn genUnreachable(self: *Context, unreach: Air.Inst.Index) InnerError!WValue {
         _ = unreach;
         try self.code.append(wasm.opcode(.@"unreachable"));
         return .none;
     }
 
-    fn genBitcast(self: *Context, bitcast: *Inst.UnOp) InnerError!WValue {
+    fn genBitcast(self: *Context, bitcast: Air.Inst.Index) InnerError!WValue {
         return self.resolveInst(bitcast.operand);
     }
 
-    fn genStructFieldPtr(self: *Context, inst: *Inst.StructFieldPtr) InnerError!WValue {
+    fn genStructFieldPtr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
         const struct_ptr = self.resolveInst(inst.struct_ptr);
 
         return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, inst.field_index) };
     }
 
-    fn genSwitchBr(self: *Context, inst: *Inst.SwitchBr) InnerError!WValue {
+    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.src, inst.base.ty);
+        const blocktype = try self.genBlockType(inst.base.ty);
 
         const signedness: std.builtin.Signedness = blk: {
             // by default we tell the operand type is unsigned (i.e. bools and enum values)
@@ -1282,7 +1277,7 @@ pub const Context = struct {
         return .none;
     }
 
-    fn genIsErr(self: *Context, inst: *Inst.UnOp, opcode: wasm.Opcode) InnerError!WValue {
+    fn genIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
         const operand = self.resolveInst(inst.operand);
         const offset = self.code.items.len;
         const writer = self.code.writer();
@@ -1298,7 +1293,7 @@ pub const Context = struct {
         return WValue{ .code_offset = offset };
     }
 
-    fn genUnwrapErrUnionPayload(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
+    fn genUnwrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
         const operand = self.resolveInst(inst.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`
@@ -1307,7 +1302,7 @@ pub const Context = struct {
         return WValue{ .local = operand.multi_value.index + 1 };
     }
 
-    fn genWrapErrUnionPayload(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
+    fn genWrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
         return self.resolveInst(inst.operand);
     }
 };
src/link/Wasm.zig
@@ -228,7 +228,7 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
         },
         else => |e| return e,
     };
-    return self.finishUpdateDecl(decl, result);
+    return self.finishUpdateDecl(decl, result, &context);
 }
 
 // Generate code for the Decl, storing it in memory to be later written to
@@ -270,18 +270,21 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
         },
         else => |e| return e,
     };
-    return self.finishUpdateDecl(decl, result);
+
+    return self.finishUpdateDecl(decl, result, &context);
 }
 
-fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, result: codegen.Result) !void {
-    const code: []const u8 = switch (result) {
-        .appended => @as([]const u8, context.code.items),
-        .externally_managed => |payload| payload,
-    };
+fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, result: codegen.Result, context: *codegen.Context) !void {
+    const fn_data: *FnData = &decl.fn_link.wasm;
 
     fn_data.code = context.code.toUnmanaged();
     fn_data.functype = context.func_type_data.toUnmanaged();
 
+    const code: []const u8 = switch (result) {
+        .appended => @as([]const u8, fn_data.code.items),
+        .externally_managed => |payload| payload,
+    };
+
     const block = &decl.link.wasm;
     if (decl.ty.zigTypeTag() == .Fn) {
         // as locals are patched afterwards, the offsets of funcidx's are off,