Commit c746cbc686

Jakub Konka <kubkon@jakubkonka.com>
2023-03-03 18:06:25
codegen: move gen logic for typed values, consts and decl ref to common codegen
1 parent 6be5946
src/arch/aarch64/CodeGen.zig
@@ -41,11 +41,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs;
 const c_abi_int_return_regs = abi.c_abi_int_return_regs;
 const gp = abi.RegisterClass.gp;
 
-const InnerError = error{
-    OutOfMemory,
-    CodegenFail,
-    OutOfRegisters,
-};
+const InnerError = codegen.CodeGenError || error{OutOfRegisters};
 
 gpa: Allocator,
 air: Air,
src/arch/arm/CodeGen.zig
@@ -42,11 +42,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs;
 const c_abi_int_return_regs = abi.c_abi_int_return_regs;
 const gp = abi.RegisterClass.gp;
 
-const InnerError = error{
-    OutOfMemory,
-    CodegenFail,
-    OutOfRegisters,
-};
+const InnerError = codegen.CodeGenError || error{OutOfRegisters};
 
 gpa: Allocator,
 air: Air,
src/arch/riscv64/CodeGen.zig
@@ -21,10 +21,10 @@ const DW = std.dwarf;
 const leb128 = std.leb;
 const log = std.log.scoped(.codegen);
 const build_options = @import("build_options");
+const codegen = @import("../../codegen.zig");
 
-const Result = @import("../../codegen.zig").Result;
-const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
-const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
+const Result = codegen.Result;
+const DebugInfoOutput = codegen.DebugInfoOutput;
 
 const bits = @import("bits.zig");
 const abi = @import("abi.zig");
@@ -35,11 +35,7 @@ const Instruction = abi.Instruction;
 const callee_preserved_regs = abi.callee_preserved_regs;
 const gp = abi.RegisterClass.gp;
 
-const InnerError = error{
-    OutOfMemory,
-    CodegenFail,
-    OutOfRegisters,
-};
+const InnerError = codegen.CodeGenError || error{OutOfRegisters};
 
 gpa: Allocator,
 air: Air,
@@ -225,7 +221,7 @@ pub fn generate(
     liveness: Liveness,
     code: *std.ArrayList(u8),
     debug_output: DebugInfoOutput,
-) GenerateSymbolError!Result {
+) codegen.CodeGenError!Result {
     if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
         @panic("Attempted to compile for architecture that was disabled by build configuration");
     }
src/arch/sparc64/CodeGen.zig
@@ -38,11 +38,7 @@ const gp = abi.RegisterClass.gp;
 
 const Self = @This();
 
-const InnerError = error{
-    OutOfMemory,
-    CodegenFail,
-    OutOfRegisters,
-};
+const InnerError = codegen.CodeGenError || error{OutOfRegisters};
 
 const RegisterView = enum(u1) {
     caller,
src/arch/wasm/CodeGen.zig
@@ -733,8 +733,6 @@ const InnerError = error{
     OutOfMemory,
     /// An error occurred when trying to lower AIR to MIR.
     CodegenFail,
-    /// Can occur when dereferencing a pointer that points to a `Decl` of which the analysis has failed
-    AnalysisFail,
     /// Compiler implementation could not handle a large integer.
     Overflow,
 };
src/arch/x86_64/CodeGen.zig
@@ -40,11 +40,7 @@ const Register = bits.Register;
 const gp = abi.RegisterClass.gp;
 const sse = abi.RegisterClass.sse;
 
-const InnerError = error{
-    OutOfMemory,
-    CodegenFail,
-    OutOfRegisters,
-};
+const InnerError = codegen.CodeGenError || error{OutOfRegisters};
 
 gpa: Allocator,
 air: Air,
@@ -6683,7 +6679,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
 }
 
-pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
+fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
     // First section of indexes correspond to a set number of constant values.
     const ref_int = @enumToInt(inst);
     if (ref_int < Air.Inst.Ref.typed_value_map.len) {
@@ -6752,200 +6748,26 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV
     return mcv;
 }
 
-fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
-    log.debug("lowerDeclRef: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
-    const ptr_bits = self.target.cpu.arch.ptrBitWidth();
-    const ptr_bytes: u64 = @divExact(ptr_bits, 8);
-
-    // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
-    if (tv.ty.zigTypeTag() == .Pointer) blk: {
-        if (tv.ty.castPtrToFn()) |_| break :blk;
-        if (!tv.ty.elemType2().hasRuntimeBits()) {
-            return MCValue.none;
-        }
-    }
-
-    const module = self.bin_file.options.module.?;
-    const decl = module.declPtr(decl_index);
-    module.markDeclAlive(decl);
-
-    if (self.bin_file.cast(link.File.Elf)) |elf_file| {
-        const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
-        const atom = elf_file.getAtom(atom_index);
-        return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
-    } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
-        const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
-        const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
-        return MCValue{ .linker_load = .{
-            .type = .got,
-            .sym_index = sym_index,
-        } };
-    } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
-        const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
-        const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
-        return MCValue{ .linker_load = .{
-            .type = .got,
-            .sym_index = sym_index,
-        } };
-    } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
-        const decl_block_index = try p9.seeDecl(decl_index);
-        const decl_block = p9.getDeclBlock(decl_block_index);
-        const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
-        return MCValue{ .memory = got_addr };
-    } else {
-        return self.fail("TODO codegen non-ELF const Decl pointer", .{});
-    }
-}
-
-fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
-    log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
-    const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| {
-        return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
-    };
-    if (self.bin_file.cast(link.File.Elf)) |elf_file| {
-        return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
-    } else if (self.bin_file.cast(link.File.MachO)) |_| {
-        return MCValue{ .linker_load = .{
-            .type = .direct,
-            .sym_index = local_sym_index,
-        } };
-    } else if (self.bin_file.cast(link.File.Coff)) |_| {
-        return MCValue{ .linker_load = .{
-            .type = .direct,
-            .sym_index = local_sym_index,
-        } };
-    } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
-        const ptr_bits = self.target.cpu.arch.ptrBitWidth();
-        const ptr_bytes: u64 = @divExact(ptr_bits, 8);
-        const got_index = local_sym_index; // the plan9 backend returns the got_index
-        const got_addr = p9.bases.data + got_index * ptr_bytes;
-        return MCValue{ .memory = got_addr };
-    } else {
-        return self.fail("TODO lower unnamed const", .{});
-    }
-}
-
 fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
-    var typed_value = arg_tv;
-    if (typed_value.val.castTag(.runtime_value)) |rt| {
-        typed_value.val = rt.data;
-    }
-    log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });
-    if (typed_value.val.isUndef())
-        return MCValue{ .undef = {} };
-    const ptr_bits = self.target.cpu.arch.ptrBitWidth();
-
-    if (typed_value.val.castTag(.decl_ref)) |payload| {
-        return self.lowerDeclRef(typed_value, payload.data);
-    }
-    if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
-        return self.lowerDeclRef(typed_value, payload.data.decl_index);
-    }
-
-    const target = self.target.*;
-
-    switch (typed_value.ty.zigTypeTag()) {
-        .Void => return MCValue{ .none = {} },
-        .Pointer => switch (typed_value.ty.ptrSize()) {
-            .Slice => {},
-            else => {
-                switch (typed_value.val.tag()) {
-                    .int_u64 => {
-                        return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
-                    },
-                    else => {},
-                }
-            },
+    const mcv: MCValue = switch (try codegen.genTypedValue(
+        self.bin_file,
+        self.src_loc,
+        arg_tv,
+        self.mod_fn.owner_decl,
+    )) {
+        .mcv => |mcv| switch (mcv) {
+            .none => .none,
+            .undef => .undef,
+            .linker_load => |ll| .{ .linker_load = ll },
+            .immediate => |imm| .{ .immediate = imm },
+            .memory => |addr| .{ .memory = addr },
         },
-        .Int => {
-            const info = typed_value.ty.intInfo(self.target.*);
-            if (info.bits <= ptr_bits and info.signedness == .signed) {
-                return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt(target)) };
-            }
-            if (!(info.bits > ptr_bits or info.signedness == .signed)) {
-                return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
-            }
-        },
-        .Bool => {
-            return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
-        },
-        .Optional => {
-            if (typed_value.ty.isPtrLikeOptional()) {
-                if (typed_value.val.isNull())
-                    return MCValue{ .immediate = 0 };
-
-                var buf: Type.Payload.ElemType = undefined;
-                return self.genTypedValue(.{
-                    .ty = typed_value.ty.optionalChild(&buf),
-                    .val = typed_value.val,
-                });
-            } else if (typed_value.ty.abiSize(self.target.*) == 1) {
-                return MCValue{ .immediate = @boolToInt(!typed_value.val.isNull()) };
-            }
-        },
-        .Enum => {
-            if (typed_value.val.castTag(.enum_field_index)) |field_index| {
-                switch (typed_value.ty.tag()) {
-                    .enum_simple => {
-                        return MCValue{ .immediate = field_index.data };
-                    },
-                    .enum_full, .enum_nonexhaustive => {
-                        const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
-                        if (enum_full.values.count() != 0) {
-                            const tag_val = enum_full.values.keys()[field_index.data];
-                            return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
-                        } else {
-                            return MCValue{ .immediate = field_index.data };
-                        }
-                    },
-                    else => unreachable,
-                }
-            } else {
-                var int_tag_buffer: Type.Payload.Bits = undefined;
-                const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
-                return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
-            }
+        .fail => |msg| {
+            self.err_msg = msg;
+            return error.CodegenFail;
         },
-        .ErrorSet => {
-            switch (typed_value.val.tag()) {
-                .@"error" => {
-                    const err_name = typed_value.val.castTag(.@"error").?.data.name;
-                    const module = self.bin_file.options.module.?;
-                    const global_error_set = module.global_error_set;
-                    const error_index = global_error_set.get(err_name).?;
-                    return MCValue{ .immediate = error_index };
-                },
-                else => {
-                    // In this case we are rendering an error union which has a 0 bits payload.
-                    return MCValue{ .immediate = 0 };
-                },
-            }
-        },
-        .ErrorUnion => {
-            const error_type = typed_value.ty.errorUnionSet();
-            const payload_type = typed_value.ty.errorUnionPayload();
-            const is_pl = typed_value.val.errorUnionIsPayload();
-
-            if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
-                // We use the error type directly as the type.
-                const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero);
-                return self.genTypedValue(.{ .ty = error_type, .val = err_val });
-            }
-        },
-
-        .ComptimeInt => unreachable,
-        .ComptimeFloat => unreachable,
-        .Type => unreachable,
-        .EnumLiteral => unreachable,
-        .NoReturn => unreachable,
-        .Undefined => unreachable,
-        .Null => unreachable,
-        .Opaque => unreachable,
-
-        else => {},
-    }
-
-    return self.lowerUnnamedConst(typed_value);
+    };
+    return mcv;
 }
 
 const CallMCValues = struct {
src/link/Coff.zig
@@ -1060,7 +1060,7 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
             decl.analysis = .codegen_failure;
             try mod.failed_decls.put(mod.gpa, decl_index, em);
             log.err("{s}", .{em.msg});
-            return error.AnalysisFail;
+            return error.CodegenFail;
         },
     };
 
src/link/Elf.zig
@@ -2618,7 +2618,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
             decl.analysis = .codegen_failure;
             try mod.failed_decls.put(mod.gpa, decl_index, em);
             log.err("{s}", .{em.msg});
-            return error.AnalysisFail;
+            return error.CodegenFail;
         },
     };
 
src/link/MachO.zig
@@ -2089,7 +2089,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
             decl.analysis = .codegen_failure;
             try module.failed_decls.put(module.gpa, decl_index, em);
             log.err("{s}", .{em.msg});
-            return error.AnalysisFail;
+            return error.CodegenFail;
         },
     };
 
src/link/Plan9.zig
@@ -377,7 +377,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I
             decl.analysis = .codegen_failure;
             try mod.failed_decls.put(mod.gpa, decl_index, em);
             log.err("{s}", .{em.msg});
-            return error.AnalysisFail;
+            return error.CodegenFail;
         },
     };
     // duped_code is freed when the unnamed const is freed
src/link/Wasm.zig
@@ -1255,7 +1255,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
             .fail => |em| {
                 decl.analysis = .codegen_failure;
                 try mod.failed_decls.put(mod.gpa, decl_index, em);
-                return error.AnalysisFail;
+                return error.CodegenFail;
             },
         };
     };
src/codegen.zig
@@ -29,13 +29,14 @@ pub const Result = union(enum) {
     fail: *ErrorMsg,
 };
 
-pub const GenerateSymbolError = error{
+pub const CodeGenError = error{
     OutOfMemory,
     Overflow,
-    /// A Decl that this symbol depends on had a semantic analysis failure.
-    AnalysisFail,
+    CodegenFail,
 };
 
+pub const GenerateSymbolError = CodeGenError;
+
 pub const DebugInfoOutput = union(enum) {
     dwarf: *link.File.Dwarf.DeclState,
     /// the plan9 debuginfo output is a bytecode with 4 opcodes
@@ -63,19 +64,6 @@ pub const DebugInfoOutput = union(enum) {
     none,
 };
 
-/// Helper struct to denote that the value is in memory but requires a linker relocation fixup:
-/// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc)
-/// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc)
-/// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc)
-pub const LinkerLoad = struct {
-    type: enum {
-        got,
-        direct,
-        import,
-    },
-    sym_index: u32,
-};
-
 pub fn generateFunction(
     bin_file: *link.File,
     src_loc: Module.SrcLoc,
@@ -84,7 +72,7 @@ pub fn generateFunction(
     liveness: Liveness,
     code: *std.ArrayList(u8),
     debug_output: DebugInfoOutput,
-) GenerateSymbolError!Result {
+) CodeGenError!Result {
     switch (bin_file.options.target.cpu.arch) {
         .arm,
         .armeb,
@@ -120,7 +108,7 @@ pub fn generateSymbol(
     code: *std.ArrayList(u8),
     debug_output: DebugInfoOutput,
     reloc_info: RelocInfo,
-) GenerateSymbolError!Result {
+) CodeGenError!Result {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -823,7 +811,7 @@ fn lowerDeclRef(
     code: *std.ArrayList(u8),
     debug_output: DebugInfoOutput,
     reloc_info: RelocInfo,
-) GenerateSymbolError!Result {
+) CodeGenError!Result {
     const target = bin_file.options.target;
     const module = bin_file.options.module.?;
     if (typed_value.ty.isSlice()) {
@@ -880,6 +868,287 @@ fn lowerDeclRef(
     return Result.ok;
 }
 
+/// Helper struct to denote that the value is in memory but requires a linker relocation fixup:
+/// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc)
+/// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc)
+/// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc)
+pub const LinkerLoad = struct {
+    type: enum {
+        got,
+        direct,
+        import,
+    },
+    sym_index: u32,
+};
+
+pub const GenResult = union(enum) {
+    mcv: MCValue,
+    fail: *ErrorMsg,
+
+    const MCValue = union(enum) {
+        none,
+        undef,
+        /// The bit-width of the immediate may be smaller than `u64`. For example, on 32-bit targets
+        /// such as ARM, the immediate will never exceed 32-bits.
+        immediate: u64,
+        linker_load: LinkerLoad,
+        /// Direct by-address reference to memory location.
+        memory: u64,
+    };
+
+    fn mcv(val: MCValue) GenResult {
+        return .{ .mcv = val };
+    }
+
+    fn fail(
+        gpa: Allocator,
+        src_loc: Module.SrcLoc,
+        comptime format: []const u8,
+        args: anytype,
+    ) Allocator.Error!GenResult {
+        const msg = try ErrorMsg.create(gpa, src_loc, format, args);
+        return .{ .fail = msg };
+    }
+};
+
+fn genDeclRef(
+    bin_file: *link.File,
+    src_loc: Module.SrcLoc,
+    tv: TypedValue,
+    decl_index: Module.Decl.Index,
+) CodeGenError!GenResult {
+    log.debug("genDeclRef: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
+
+    const target = bin_file.options.target;
+    const ptr_bits = target.cpu.arch.ptrBitWidth();
+    const ptr_bytes: u64 = @divExact(ptr_bits, 8);
+
+    const module = bin_file.options.module.?;
+    const decl = module.declPtr(decl_index);
+
+    if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) {
+        const imm: u64 = switch (ptr_bytes) {
+            1 => 0xaa,
+            2 => 0xaaaa,
+            4 => 0xaaaaaaaa,
+            8 => 0xaaaaaaaaaaaaaaaa,
+            else => unreachable,
+        };
+        return GenResult.mcv(.{ .immediate = imm });
+    }
+
+    // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
+    if (tv.ty.zigTypeTag() == .Pointer) blk: {
+        if (tv.ty.castPtrToFn()) |_| break :blk;
+        if (!tv.ty.elemType2().hasRuntimeBits()) {
+            return GenResult.mcv(.none);
+        }
+    }
+
+    module.markDeclAlive(decl);
+
+    if (bin_file.cast(link.File.Elf)) |elf_file| {
+        const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
+        const atom = elf_file.getAtom(atom_index);
+        return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) });
+    } else if (bin_file.cast(link.File.MachO)) |macho_file| {
+        const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
+        const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
+        return GenResult.mcv(.{ .linker_load = .{
+            .type = .got,
+            .sym_index = sym_index,
+        } });
+    } else if (bin_file.cast(link.File.Coff)) |coff_file| {
+        const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
+        const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
+        return GenResult.mcv(.{ .linker_load = .{
+            .type = .got,
+            .sym_index = sym_index,
+        } });
+    } else if (bin_file.cast(link.File.Plan9)) |p9| {
+        const decl_block_index = try p9.seeDecl(decl_index);
+        const decl_block = p9.getDeclBlock(decl_block_index);
+        const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
+        return GenResult.mcv(.{ .memory = got_addr });
+    } else {
+        return GenResult.fail(bin_file.allocator, src_loc, "TODO genDeclRef for target {}", .{target});
+    }
+}
+
+fn genUnnamedConst(
+    bin_file: *link.File,
+    src_loc: Module.SrcLoc,
+    tv: TypedValue,
+    owner_decl_index: Module.Decl.Index,
+) CodeGenError!GenResult {
+    log.debug("genUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
+
+    const target = bin_file.options.target;
+    const local_sym_index = bin_file.lowerUnnamedConst(tv, owner_decl_index) catch |err| {
+        return GenResult.fail(bin_file.allocator, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)});
+    };
+    if (bin_file.cast(link.File.Elf)) |elf_file| {
+        return GenResult.mcv(.{ .memory = elf_file.getSymbol(local_sym_index).st_value });
+    } else if (bin_file.cast(link.File.MachO)) |_| {
+        return GenResult.mcv(.{ .linker_load = .{
+            .type = .direct,
+            .sym_index = local_sym_index,
+        } });
+    } else if (bin_file.cast(link.File.Coff)) |_| {
+        return GenResult.mcv(.{ .linker_load = .{
+            .type = .direct,
+            .sym_index = local_sym_index,
+        } });
+    } else if (bin_file.cast(link.File.Plan9)) |p9| {
+        const ptr_bits = target.cpu.arch.ptrBitWidth();
+        const ptr_bytes: u64 = @divExact(ptr_bits, 8);
+        const got_index = local_sym_index; // the plan9 backend returns the got_index
+        const got_addr = p9.bases.data + got_index * ptr_bytes;
+        return GenResult.mcv(.{ .memory = got_addr });
+    } else {
+        return GenResult.fail(bin_file.allocator, src_loc, "TODO genUnnamedConst for target {}", .{target});
+    }
+}
+
+pub fn genTypedValue(
+    bin_file: *link.File,
+    src_loc: Module.SrcLoc,
+    arg_tv: TypedValue,
+    owner_decl_index: Module.Decl.Index,
+) CodeGenError!GenResult {
+    var typed_value = arg_tv;
+    if (typed_value.val.castTag(.runtime_value)) |rt| {
+        typed_value.val = rt.data;
+    }
+
+    log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });
+
+    if (typed_value.val.isUndef())
+        return GenResult.mcv(.undef);
+
+    const target = bin_file.options.target;
+    const ptr_bits = target.cpu.arch.ptrBitWidth();
+
+    if (typed_value.val.castTag(.decl_ref)) |payload| {
+        return genDeclRef(bin_file, src_loc, typed_value, payload.data);
+    }
+    if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
+        return genDeclRef(bin_file, src_loc, typed_value, payload.data.decl_index);
+    }
+
+    switch (typed_value.ty.zigTypeTag()) {
+        .Void => return GenResult.mcv(.none),
+        .Pointer => switch (typed_value.ty.ptrSize()) {
+            .Slice => {},
+            else => {
+                switch (typed_value.val.tag()) {
+                    .int_u64 => {
+                        return GenResult.mcv(.{ .immediate = typed_value.val.toUnsignedInt(target) });
+                    },
+                    else => {},
+                }
+            },
+        },
+        .Int => {
+            const info = typed_value.ty.intInfo(target);
+            if (info.bits <= ptr_bits and info.signedness == .signed) {
+                return GenResult.mcv(.{ .immediate = @bitCast(u64, typed_value.val.toSignedInt(target)) });
+            }
+            if (!(info.bits > ptr_bits or info.signedness == .signed)) {
+                return GenResult.mcv(.{ .immediate = typed_value.val.toUnsignedInt(target) });
+            }
+        },
+        .Bool => {
+            return GenResult.mcv(.{ .immediate = @boolToInt(typed_value.val.toBool()) });
+        },
+        .Optional => {
+            if (typed_value.ty.isPtrLikeOptional()) {
+                if (typed_value.val.isNull())
+                    return GenResult.mcv(.{ .immediate = 0 });
+
+                var buf: Type.Payload.ElemType = undefined;
+                return genTypedValue(bin_file, src_loc, .{
+                    .ty = typed_value.ty.optionalChild(&buf),
+                    .val = typed_value.val,
+                }, owner_decl_index);
+            } else if (typed_value.ty.abiSize(target) == 1) {
+                return GenResult.mcv(.{ .immediate = @boolToInt(!typed_value.val.isNull()) });
+            }
+        },
+        .Enum => {
+            if (typed_value.val.castTag(.enum_field_index)) |field_index| {
+                switch (typed_value.ty.tag()) {
+                    .enum_simple => {
+                        return GenResult.mcv(.{ .immediate = field_index.data });
+                    },
+                    .enum_full, .enum_nonexhaustive => {
+                        const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
+                        if (enum_full.values.count() != 0) {
+                            const tag_val = enum_full.values.keys()[field_index.data];
+                            return genTypedValue(bin_file, src_loc, .{
+                                .ty = enum_full.tag_ty,
+                                .val = tag_val,
+                            }, owner_decl_index);
+                        } else {
+                            return GenResult.mcv(.{ .immediate = field_index.data });
+                        }
+                    },
+                    else => unreachable,
+                }
+            } else {
+                var int_tag_buffer: Type.Payload.Bits = undefined;
+                const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
+                return genTypedValue(bin_file, src_loc, .{
+                    .ty = int_tag_ty,
+                    .val = typed_value.val,
+                }, owner_decl_index);
+            }
+        },
+        .ErrorSet => {
+            switch (typed_value.val.tag()) {
+                .@"error" => {
+                    const err_name = typed_value.val.castTag(.@"error").?.data.name;
+                    const module = bin_file.options.module.?;
+                    const global_error_set = module.global_error_set;
+                    const error_index = global_error_set.get(err_name).?;
+                    return GenResult.mcv(.{ .immediate = error_index });
+                },
+                else => {
+                    // In this case we are rendering an error union which has a 0 bits payload.
+                    return GenResult.mcv(.{ .immediate = 0 });
+                },
+            }
+        },
+        .ErrorUnion => {
+            const error_type = typed_value.ty.errorUnionSet();
+            const payload_type = typed_value.ty.errorUnionPayload();
+            const is_pl = typed_value.val.errorUnionIsPayload();
+
+            if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
+                // We use the error type directly as the type.
+                const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero);
+                return genTypedValue(bin_file, src_loc, .{
+                    .ty = error_type,
+                    .val = err_val,
+                }, owner_decl_index);
+            }
+        },
+
+        .ComptimeInt => unreachable,
+        .ComptimeFloat => unreachable,
+        .Type => unreachable,
+        .EnumLiteral => unreachable,
+        .NoReturn => unreachable,
+        .Undefined => unreachable,
+        .Null => unreachable,
+        .Opaque => unreachable,
+
+        else => {},
+    }
+
+    return genUnnamedConst(bin_file, src_loc, typed_value, owner_decl_index);
+}
+
 pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 {
     const payload_align = payload_ty.abiAlignment(target);
     const error_align = Type.anyerror.abiAlignment(target);
src/register_manager.zig
@@ -19,6 +19,9 @@ pub const AllocateRegistersError = error{
     /// Can happen when spilling an instruction in codegen runs out of
     /// memory, so we propagate that error
     OutOfMemory,
+    /// Can happen when spilling an instruction in codegen triggers integer
+    /// overflow, so we propagate that error
+    Overflow,
     /// Can happen when spilling an instruction triggers a codegen
     /// error, so we propagate that error
     CodegenFail,