Commit 3bf008a3d0

David Rubin <daviru007@icloud.com>
2024-03-29 14:48:41
riscv: implement slices
1 parent 350ad90
Changed files (8)
lib/std/builtin.zig
@@ -775,14 +775,14 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
     }
 
     if (builtin.zig_backend == .stage2_riscv64) {
-        // asm volatile ("ecall"
-        //     :
-        //     : [number] "{a7}" (64),
-        //       [arg1] "{a0}" (1),
-        //       [arg2] "{a1}" (@intFromPtr(msg.ptr)),
-        //       [arg3] "{a2}" (msg.len),
-        //     : "rcx", "r11", "memory"
-        // );
+        asm volatile ("ecall"
+            :
+            : [number] "{a7}" (64),
+              [arg1] "{a0}" (1),
+              [arg2] "{a1}" (@intFromPtr(msg.ptr)),
+              [arg3] "{a2}" (msg.len),
+            : "rcx", "r11", "memory"
+        );
         std.posix.exit(127);
     }
 
src/arch/riscv64/abi.zig
@@ -5,7 +5,7 @@ const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
 const Type = @import("../../type.zig").Type;
 const Module = @import("../../Module.zig");
 
-pub const Class = enum { memory, byval, integer, double_integer, fields };
+pub const Class = enum { memory, byval, integer, double_integer, fields, none };
 
 pub fn classifyType(ty: Type, mod: *Module) Class {
     const target = mod.getTarget();
@@ -91,6 +91,37 @@ pub fn classifyType(ty: Type, mod: *Module) Class {
     }
 }
 
+/// There are a maximum of 8 possible return slots. Returned values are in
+/// the beginning of the array; unused slots are filled with .none.
+pub fn classifySystemV(ty: Type, mod: *Module) [8]Class {
+    const memory_class = [_]Class{
+        .memory, .none, .none, .none,
+        .none,   .none, .none, .none,
+    };
+    var result = [1]Class{.none} ** 8;
+    switch (ty.zigTypeTag(mod)) {
+        .Pointer => switch (ty.ptrSize(mod)) {
+            .Slice => {
+                result[0] = .integer;
+                result[1] = .integer;
+                return result;
+            },
+            else => {
+                result[0] = .integer;
+                return result;
+            },
+        },
+        .Optional => {
+            if (ty.isPtrLikeOptional(mod)) {
+                result[0] = .integer;
+                return result;
+            }
+            return memory_class;
+        },
+        else => return result,
+    }
+}
+
 pub const callee_preserved_regs = [_]Register{
     .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
 };
src/arch/riscv64/CodeGen.zig
@@ -122,9 +122,10 @@ const MCValue = union(enum) {
     /// A pointer-sized integer that fits in a register.
     /// If the type is a pointer, this is the pointer address in virtual address space.
     immediate: u64,
-    /// The value is in memory at an address not-yet-allocated by the linker.
-    /// This traditionally corresponds to a relocation emitted in a relocatable object file.
+    /// The value doesn't exist in memory yet.
     load_symbol: SymbolOffset,
+    /// The address of the memory location not-yet-allocated by the linker.
+    addr_symbol: SymbolOffset,
     /// The value is in a target-specific register.
     register: Register,
     /// The value is split across two registers
@@ -169,6 +170,7 @@ const MCValue = union(enum) {
             .indirect,
             .undef,
             .load_symbol,
+            .addr_symbol,
             .air_ref,
             => false,
 
@@ -188,10 +190,14 @@ const MCValue = union(enum) {
             .immediate,
             .ptr_stack_offset,
             .register_offset,
+            .register_pair,
+            .register,
             .undef,
             .air_ref,
+            .addr_symbol,
             => unreachable, // not in memory
 
+            .load_symbol => |sym_off| .{ .addr_symbol = sym_off },
             .memory => |addr| .{ .immediate = addr },
             .stack_offset => |off| .{ .ptr_stack_offset = off },
             .indirect => |reg_off| switch (reg_off.off) {
@@ -219,6 +225,7 @@ const MCValue = union(enum) {
             .ptr_stack_offset => |off| .{ .stack_offset = off },
             .register => |reg| .{ .indirect = .{ .reg = reg } },
             .register_offset => |reg_off| .{ .indirect = reg_off },
+            .addr_symbol => |sym_off| .{ .load_symbol = sym_off },
         };
     }
 
@@ -235,6 +242,7 @@ const MCValue = union(enum) {
             .indirect,
             .stack_offset,
             .load_symbol,
+            .addr_symbol,
             => switch (off) {
                 0 => mcv,
                 else => unreachable, // not offsettable
@@ -801,6 +809,43 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
     try table.ensureUnusedCapacity(self.gpa, additional_count);
 }
 
+fn splitType(self: *Self, ty: Type) ![2]Type {
+    const mod = self.bin_file.comp.module.?;
+    const classes = mem.sliceTo(&abi.classifySystemV(ty, mod), .none);
+    var parts: [2]Type = undefined;
+    if (classes.len == 2) for (&parts, classes, 0..) |*part, class, part_i| {
+        part.* = switch (class) {
+            .integer => switch (part_i) {
+                0 => Type.u64,
+                1 => part: {
+                    const elem_size = ty.abiAlignment(mod).minStrict(.@"8").toByteUnitsOptional().?;
+                    const elem_ty = try mod.intType(.unsigned, @intCast(elem_size * 8));
+                    break :part switch (@divExact(ty.abiSize(mod) - 8, elem_size)) {
+                        1 => elem_ty,
+                        else => |len| try mod.arrayType(.{ .len = len, .child = elem_ty.toIntern() }),
+                    };
+                },
+                else => unreachable,
+            },
+            else => break,
+        };
+    } else if (parts[0].abiSize(mod) + parts[1].abiSize(mod) == ty.abiSize(mod)) return parts;
+    return std.debug.panic("TODO implement splitType for {}", .{ty.fmt(mod)});
+}
+
+fn symbolIndex(self: *Self) !u32 {
+    const mod = self.bin_file.comp.module.?;
+    const decl_index = mod.funcOwnerDeclIndex(self.func_index);
+    return switch (self.bin_file.tag) {
+        .elf => blk: {
+            const elf_file = self.bin_file.cast(link.File.Elf).?;
+            const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
+            break :blk atom_index;
+        },
+        else => return self.fail("TODO genSetReg load_symbol for {s}", .{@tagName(self.bin_file.tag)}),
+    };
+}
+
 fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: Alignment) !u32 {
     self.stack_align = self.stack_align.max(abi_align);
     // TODO find a free slot instead of always appending
@@ -1610,40 +1655,41 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
 
 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
-    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
-        const mcv = try self.resolveInst(ty_op.operand);
-        break :result try self.slicePtr(mcv);
+    const result = result: {
+        const src_mcv = try self.resolveInst(ty_op.operand);
+        if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result src_mcv;
+
+        const dst_mcv = try self.allocRegOrMem(inst, true);
+        const dst_ty = self.typeOfIndex(inst);
+        try self.genCopy(dst_ty, dst_mcv, src_mcv);
+        break :result dst_mcv;
     };
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
-fn slicePtr(self: *Self, mcv: MCValue) !MCValue {
-    switch (mcv) {
-        .dead, .unreach, .none => unreachable,
-        .register => unreachable, // a slice doesn't fit in one register
-        .stack_offset => |off| {
-            return MCValue{ .stack_offset = off };
-        },
-        .memory => |addr| {
-            return MCValue{ .memory = addr };
-        },
-        else => return self.fail("TODO slicePtr {s}", .{@tagName(mcv)}),
-    }
-}
-
 fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
-        const ptr_bits = 64;
-        const ptr_bytes = @divExact(ptr_bits, 8);
-        const mcv = try self.resolveInst(ty_op.operand);
-        switch (mcv) {
-            .dead, .unreach, .none => unreachable,
-            .register => unreachable, // a slice doesn't fit in one register
+        const src_mcv = try self.resolveInst(ty_op.operand);
+        switch (src_mcv) {
             .stack_offset => |off| {
-                break :result MCValue{ .stack_offset = off + ptr_bytes };
+                const len_mcv: MCValue = .{ .stack_offset = off + 8 };
+                if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result len_mcv;
+
+                const dst_mcv = try self.allocRegOrMem(inst, true);
+                try self.genCopy(Type.usize, dst_mcv, len_mcv);
+                break :result dst_mcv;
+            },
+            .register_pair => |pair| {
+                const len_mcv: MCValue = .{ .register = pair[1] };
+
+                if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result len_mcv;
+
+                const dst_mcv = try self.allocRegOrMem(inst, true);
+                try self.genCopy(Type.usize, dst_mcv, len_mcv);
+                break :result dst_mcv;
             },
-            else => return self.fail("TODO airSliceLen for {}", .{mcv}),
+            else => return self.fail("TODO airSliceLen for {}", .{src_mcv}),
         }
     };
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
@@ -1978,6 +2024,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_mcv: MCValue, ptr_ty: Type) InnerErro
         .register,
         .register_offset,
         .ptr_stack_offset,
+        .addr_symbol,
         => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()),
 
         .memory,
@@ -2019,11 +2066,6 @@ fn store(self: *Self, pointer: MCValue, value: MCValue, ptr_ty: Type, value_ty:
 
     log.debug("storing {}:{} in {}:{}", .{ value, value_ty.fmt(mod), pointer, ptr_ty.fmt(mod) });
 
-    if (value_ty.isSlice(mod)) {
-        // cheat a bit by loading in two parts
-
-    }
-
     switch (pointer) {
         .none => unreachable,
         .undef => unreachable,
@@ -2192,7 +2234,11 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
 
         const dst_mcv = switch (src_mcv) {
             .register => |src_reg| dst: {
-                try self.register_manager.getReg(src_reg, null);
+                self.register_manager.getRegAssumeFree(src_reg, null);
+                break :dst src_mcv;
+            },
+            .register_pair => |pair| dst: {
+                for (pair) |reg| self.register_manager.getRegAssumeFree(reg, null);
                 break :dst src_mcv;
             },
             else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}),
@@ -3056,6 +3102,8 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT
 
 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
 fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
+    const mod = self.bin_file.comp.module.?;
+
     // There isn't anything to store
     if (dst_mcv == .none) return;
 
@@ -3066,7 +3114,6 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
 
     switch (dst_mcv) {
         .register => |reg| return self.genSetReg(ty, reg, src_mcv),
-        .register_pair => |pair| return self.genSetRegPair(ty, pair, src_mcv),
         .register_offset => |dst_reg_off| try self.genSetReg(ty, dst_reg_off.reg, switch (src_mcv) {
             .none,
             .unreach,
@@ -3084,7 +3131,47 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
         }),
         .stack_offset => |off| return self.genSetStack(ty, off, src_mcv),
         .memory => |addr| return self.genSetMem(ty, addr, src_mcv),
-        else => return self.fail("TODO: genCopy {s} with {s}", .{ @tagName(dst_mcv), @tagName(src_mcv) }),
+        .register_pair => |dst_regs| {
+            const src_info: ?struct { addr_reg: Register, addr_lock: RegisterLock } = switch (src_mcv) {
+                .register_pair, .memory, .indirect, .stack_offset => null,
+                .load_symbol => src: {
+                    const src_addr_reg, const src_addr_lock = try self.allocReg();
+                    errdefer self.register_manager.unlockReg(src_addr_lock);
+
+                    try self.genSetReg(Type.usize, src_addr_reg, src_mcv.address());
+                    break :src .{ .addr_reg = src_addr_reg, .addr_lock = src_addr_lock };
+                },
+                .air_ref => |src_ref| return self.genCopy(
+                    ty,
+                    dst_mcv,
+                    try self.resolveInst(src_ref),
+                ),
+                else => return self.fail("TODO implement genCopy for {s} of {}", .{
+                    @tagName(src_mcv), ty.fmt(mod),
+                }),
+            };
+            defer if (src_info) |info| self.register_manager.unlockReg(info.addr_lock);
+
+            switch (ty.zigTypeTag(mod)) {
+                .Optional => return,
+                else => {},
+            }
+
+            var part_disp: i32 = 0;
+            for (dst_regs, try self.splitType(ty), 0..) |dst_reg, dst_ty, part_i| {
+                try self.genSetReg(dst_ty, dst_reg, switch (src_mcv) {
+                    .register_pair => |src_regs| .{ .register = src_regs[part_i] },
+                    .memory, .indirect, .stack_offset => src_mcv.address().offset(part_disp).deref(),
+                    .load_symbol => .{ .indirect = .{
+                        .reg = src_info.?.addr_reg,
+                        .off = part_disp,
+                    } },
+                    else => unreachable,
+                });
+                part_disp += @intCast(dst_ty.abiSize(mod));
+            }
+        },
+        else => return std.debug.panic("TODO: genCopy {s} with {s}", .{ @tagName(dst_mcv), @tagName(src_mcv) }),
     }
 }
 
@@ -3168,14 +3255,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_mcv: MCValue) Inner
                     try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = offset });
                 },
                 .load_symbol => |sym_off| {
-                    const atom_index = atom: {
-                        const decl_index = mod.funcOwnerDeclIndex(self.func_index);
-
-                        if (self.bin_file.cast(link.File.Elf)) |elf_file| {
-                            const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
-                            break :atom atom_index;
-                        } else return self.fail("TODO genSetStack for {s}", .{@tagName(self.bin_file.tag)});
-                    };
+                    const atom_index = try self.symbolIndex();
 
                     // setup the src pointer
                     _ = try self.addInst(.{
@@ -3443,16 +3523,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
         .load_symbol => |sym_off| {
             assert(sym_off.off == 0);
 
-            const decl_index = mod.funcOwnerDeclIndex(self.func_index);
+            const atom_index = try self.symbolIndex();
 
-            const atom_index = switch (self.bin_file.tag) {
-                .elf => blk: {
-                    const elf_file = self.bin_file.cast(link.File.Elf).?;
-                    const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
-                    break :blk atom_index;
-                },
-                else => return self.fail("TODO genSetReg load_symbol for {s}", .{@tagName(self.bin_file.tag)}),
-            };
             _ = try self.addInst(.{
                 .tag = .load_symbol,
                 .data = .{
@@ -3485,27 +3557,23 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
                 },
             });
         },
-        else => return self.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}),
-    }
-}
-
-fn genSetRegPair(self: *Self, ty: Type, pair: [2]Register, src_mcv: MCValue) InnerError!void {
-    const mod = self.bin_file.comp.module.?;
-    const abi_size: u32 = @intCast(ty.abiSize(mod));
-
-    assert(abi_size > 8 and abi_size <= 16); // must fit only fit into two registers
+        .addr_symbol => |sym_off| {
+            assert(sym_off.off == 0);
 
-    switch (src_mcv) {
-        .air_ref => |ref| return self.genSetRegPair(ty, pair, try self.resolveInst(ref)),
-        .load_symbol => |sym_off| {
-            _ = sym_off;
-            // return self.fail("TODO: genSetRegPair load_symbol", .{});
-            // commented out just for testing.
+            const atom_index = try self.symbolIndex();
 
-            // plan here is to load the address into a temporary register and
-            // copy into the pair.
+            _ = try self.addInst(.{
+                .tag = .load_symbol,
+                .data = .{
+                    .payload = try self.addExtra(Mir.LoadSymbolPayload{
+                        .register = reg.id(),
+                        .atom_index = atom_index,
+                        .sym_index = sym_off.sym,
+                    }),
+                },
+            });
         },
-        else => return self.fail("TODO: genSetRegPair {s}", .{@tagName(src_mcv)}),
+        else => return self.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}),
     }
 }
 
src/arch/riscv64/Emit.zig
@@ -398,7 +398,7 @@ fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void {
 
         .j => {
             const offset = @as(i64, @intCast(emit.code_offset_mapping.get(data.inst).?)) - @as(i64, @intCast(emit.code.items.len));
-            try emit.writeInstruction(Instruction.jal(.s0, @intCast(offset)));
+            try emit.writeInstruction(Instruction.jal(.zero, @intCast(offset)));
         },
 
         else => unreachable,
@@ -443,27 +443,40 @@ fn mirLoadSymbol(emit: *Emit, inst: Mir.Inst.Index) !void {
     const data = emit.mir.extraData(Mir.LoadSymbolPayload, payload).data;
     const reg = @as(Register, @enumFromInt(data.register));
 
-    const end_offset = @as(u32, @intCast(emit.code.items.len));
+    const start_offset = @as(u32, @intCast(emit.code.items.len));
     try emit.writeInstruction(Instruction.lui(reg, 0));
-    try emit.writeInstruction(Instruction.lw(reg, 0, reg));
 
     switch (emit.bin_file.tag) {
         .elf => {
             const elf_file = emit.bin_file.cast(link.File.Elf).?;
             const atom_ptr = elf_file.symbol(data.atom_index).atom(elf_file).?;
+            const sym_index = elf_file.zigObjectPtr().?.symbol(data.sym_index);
+            const sym = elf_file.symbol(sym_index);
 
-            const hi_r_type = @intFromEnum(std.elf.R_RISCV.HI20);
+            var hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20);
+            var lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I);
+
+            if (sym.flags.needs_zig_got) {
+                _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
+
+                hi_r_type = Elf.R_ZIG_GOT_HI20;
+                lo_r_type = Elf.R_ZIG_GOT_LO12;
+
+                // we need to deref once if we are getting from zig_got, as itll
+                // reloc an address of the address in the got.
+                try emit.writeInstruction(Instruction.ld(reg, 0, reg));
+            } else {
+                try emit.writeInstruction(Instruction.addi(reg, reg, 0));
+            }
 
             try atom_ptr.addReloc(elf_file, .{
-                .r_offset = end_offset,
+                .r_offset = start_offset,
                 .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | hi_r_type,
                 .r_addend = 0,
             });
 
-            const lo_r_type = @intFromEnum(std.elf.R_RISCV.LO12_I);
-
             try atom_ptr.addReloc(elf_file, .{
-                .r_offset = end_offset + 4,
+                .r_offset = start_offset + 4,
                 .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | lo_r_type,
                 .r_addend = 0,
             });
@@ -587,6 +600,7 @@ const bits = @import("bits.zig");
 const abi = @import("abi.zig");
 const link = @import("../../link.zig");
 const Module = @import("../../Module.zig");
+const Elf = @import("../../link/Elf.zig");
 const ErrorMsg = Module.ErrorMsg;
 const assert = std.debug.assert;
 const Instruction = bits.Instruction;
src/arch/riscv64/Mir.zig
@@ -118,7 +118,9 @@ pub const Inst = struct {
         /// function epilogue
         psuedo_epilogue,
 
-        // TODO: add description
+        /// Loads the address of a value that hasn't yet been allocated in memory.
+        ///
+        /// uses the Mir.LoadSymbolPayload payload.
         load_symbol,
 
         // TODO: add description
src/codegen/llvm.zig
@@ -11132,6 +11132,7 @@ fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!Bu
                             }
                             return o.builder.structType(.normal, types[0..types_len]);
                         },
+                        .none => unreachable,
                     }
                 },
                 // TODO investigate C ABI for other architectures
@@ -11389,6 +11390,7 @@ const ParamTypeIterator = struct {
                             it.llvm_index += it.types_len - 1;
                             return .multiple_llvm_types;
                         },
+                        .none => unreachable,
                     }
                 },
                 // TODO investigate C ABI for other architectures
src/link/Elf/Atom.zig
@@ -2025,7 +2025,15 @@ const riscv = struct {
             .SUB32,
             => {},
 
-            else => try atom.reportUnhandledRelocError(rel, elf_file),
+            else => |x| switch (@intFromEnum(x)) {
+                Elf.R_ZIG_GOT_HI20,
+                Elf.R_ZIG_GOT_LO12,
+                => {
+                    assert(symbol.flags.has_zig_got);
+                },
+
+                else => try atom.reportUnhandledRelocError(rel, elf_file),
+            },
         }
     }
 
@@ -2046,7 +2054,6 @@ const riscv = struct {
         const P, const A, const S, const GOT, const G, const TP, const DTP, const ZIG_GOT = args;
         _ = TP;
         _ = DTP;
-        _ = ZIG_GOT;
 
         switch (r_type) {
             .NONE => unreachable,
@@ -2136,7 +2143,22 @@ const riscv = struct {
                 }
             },
 
-            else => try atom.reportUnhandledRelocError(rel, elf_file),
+            else => |x| switch (@intFromEnum(x)) {
+                // Zig custom relocations
+                Elf.R_ZIG_GOT_HI20 => {
+                    assert(target.flags.has_zig_got);
+                    const disp: u32 = @bitCast(math.cast(i32, G + ZIG_GOT + A) orelse return error.Overflow);
+                    riscv_util.writeInstU(code[r_offset..][0..4], disp);
+                },
+
+                Elf.R_ZIG_GOT_LO12 => {
+                    assert(target.flags.has_zig_got);
+                    const value: u32 = @bitCast(math.cast(i32, G + ZIG_GOT + A) orelse return error.Overflow);
+                    riscv_util.writeInstI(code[r_offset..][0..4], value);
+                },
+
+                else => try atom.reportUnhandledRelocError(rel, elf_file),
+            },
         }
     }
 
src/link/Elf.zig
@@ -6409,6 +6409,8 @@ const RelaSectionTable = std.AutoArrayHashMapUnmanaged(u32, RelaSection);
 // TODO: add comptime check we don't clobber any reloc for any ISA
 pub const R_ZIG_GOT32: u32 = 0xff00;
 pub const R_ZIG_GOTPCREL: u32 = 0xff01;
+pub const R_ZIG_GOT_HI20: u32 = 0xff02;
+pub const R_ZIG_GOT_LO12: u32 = 0xff03;
 
 fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 {
     return switch (cpu_arch) {