Commit d19b77d63f

David Rubin <daviru007@icloud.com>
2024-04-14 04:30:10
riscv: back to hello world panics
1 parent cc204e2
lib/std/builtin.zig
@@ -775,7 +775,15 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
     }
 
     if (builtin.zig_backend == .stage2_riscv64) {
-        unreachable;
+        asm volatile ("ecall"
+            :
+            : [number] "{a7}" (64),
+              [arg1] "{a0}" (1),
+              [arg2] "{a1}" (@intFromPtr(msg.ptr)),
+              [arg3] "{a2}" (msg.len),
+            : "memory"
+        );
+        std.posix.exit(127);
     }
 
     switch (builtin.os.tag) {
src/arch/riscv64/bits.zig
@@ -65,17 +65,25 @@ pub const Memory = struct {
 
     /// Asserts `mem` can be represented as a `FrameLoc`.
     pub fn toFrameLoc(mem: Memory, mir: Mir) Mir.FrameLoc {
+        const offset: i32 = switch (mem.mod) {
+            .off => |off| @intCast(off),
+            .rm => |rm| rm.disp,
+        };
+
         switch (mem.base) {
             .reg => |reg| {
                 return .{
                     .base = reg,
-                    .disp = switch (mem.mod) {
-                        .off => unreachable, // TODO: toFrameLoc disp.off
-                        .rm => |rm| rm.disp,
-                    },
+                    .disp = offset,
+                };
+            },
+            .frame => |index| {
+                const base_loc = mir.frame_locs.get(@intFromEnum(index));
+                return .{
+                    .base = base_loc.base,
+                    .disp = base_loc.disp + offset,
                 };
             },
-            .frame => |index| return mir.frame_locs.get(@intFromEnum(index)),
             .reloc => unreachable,
         }
     }
src/arch/riscv64/CodeGen.zig
@@ -1513,7 +1513,7 @@ fn splitType(self: *Self, ty: Type) ![2]Type {
                 },
                 else => unreachable,
             },
-            else => break,
+            else => return self.fail("TODO: splitType class {}", .{class}),
         };
     } else if (parts[0].abiSize(zcu) + parts[1].abiSize(zcu) == ty.abiSize(zcu)) return parts;
     return self.fail("TODO implement splitType for {}", .{ty.fmt(zcu)});
@@ -3434,6 +3434,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
     const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
         const src_mcv = self.args[arg_index];
 
+        const arg_ty = self.typeOfIndex(inst);
+
         const dst_mcv = switch (src_mcv) {
             .register => dst: {
                 const frame = try self.allocFrameIndex(FrameAlloc.init(.{
@@ -3441,9 +3443,16 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
                     .alignment = Type.usize.abiAlignment(zcu),
                 }));
                 const dst_mcv: MCValue = .{ .load_frame = .{ .index = frame } };
-
                 try self.genCopy(Type.usize, dst_mcv, src_mcv);
-
+                break :dst dst_mcv;
+            },
+            .register_pair => dst: {
+                const frame = try self.allocFrameIndex(FrameAlloc.init(.{
+                    .size = Type.usize.abiSize(zcu) * 2,
+                    .alignment = Type.usize.abiAlignment(zcu),
+                }));
+                const dst_mcv: MCValue = .{ .load_frame = .{ .index = frame } };
+                try self.genCopy(arg_ty, dst_mcv, src_mcv);
                 break :dst dst_mcv;
             },
             .load_frame => src_mcv,
@@ -4506,6 +4515,17 @@ fn genSetStack(
                 else => unreachable, // register can hold a max of 8 bytes
             }
         },
+        .register_pair => |pair| {
+            var part_disp: i32 = frame.off;
+            for (try self.splitType(ty), pair) |src_ty, src_reg| {
+                try self.genSetStack(
+                    src_ty,
+                    .{ .index = frame.index, .off = part_disp },
+                    .{ .register = src_reg },
+                );
+                part_disp += @intCast(src_ty.abiSize(zcu));
+            }
+        },
         .load_frame,
         .indirect,
         .load_symbol,
@@ -4564,8 +4584,8 @@ fn genInlineMemcpy(
         .ops = .rri,
         .data = .{
             .i_type = .{
-                .rd = tmp,
-                .rs1 = dst,
+                .rd = dst,
+                .rs1 = tmp,
                 .imm12 = Immediate.s(0),
             },
         },
src/arch/riscv64/Emit.zig
@@ -41,7 +41,35 @@ pub fn emitMir(emit: *Emit) Error!void {
                     .offset = 0,
                     .enc = std.meta.activeTag(lowered_inst.encoding.data),
                 }),
-                else => |x| return emit.fail("TODO: emitMir {s}", .{@tagName(x)}),
+                .load_symbol_reloc => |symbol| {
+                    if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| {
+                        const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
+                        const sym_index = elf_file.zigObjectPtr().?.symbol(symbol.sym_index);
+                        const sym = elf_file.symbol(sym_index);
+
+                        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;
+                        }
+
+                        try atom_ptr.addReloc(elf_file, .{
+                            .r_offset = start_offset,
+                            .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type,
+                            .r_addend = 0,
+                        });
+
+                        try atom_ptr.addReloc(elf_file, .{
+                            .r_offset = start_offset + 4,
+                            .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type,
+                            .r_addend = 0,
+                        });
+                    } else return emit.fail("TODO: load_symbol_reloc non-ELF", .{});
+                },
             };
         }
         std.debug.assert(lowered_relocs.len == 0);
@@ -120,6 +148,7 @@ fn fixupRelocs(emit: *Emit) Error!void {
 
         switch (reloc.enc) {
             .J => riscv_util.writeInstJ(code, @bitCast(disp)),
+            .B => riscv_util.writeInstB(code, @bitCast(disp)),
             else => return emit.fail("tried to reloc encoding type {s}", .{@tagName(reloc.enc)}),
         }
     }
@@ -161,3 +190,4 @@ const Lower = @import("Lower.zig");
 const Mir = @import("Mir.zig");
 const riscv_util = @import("../../link/riscv.zig");
 const Encoding = @import("Encoding.zig");
+const Elf = @import("../../link/Elf.zig");
src/arch/riscv64/encoder.zig
@@ -1,6 +1,6 @@
 pub const Instruction = struct {
     encoding: Encoding,
-    ops: [4]Operand = .{.none} ** 4,
+    ops: [3]Operand = .{.none} ** 3,
 
     pub const Operand = union(enum) {
         none,
@@ -11,7 +11,7 @@ pub const Instruction = struct {
 
     pub fn new(mnemonic: Encoding.Mnemonic, ops: []const Operand) !Instruction {
         const encoding = (try Encoding.findByMnemonic(mnemonic, ops)) orelse {
-            log.err("no encoding found for:  {s} {s} {s} {s} {s}", .{
+            std.log.err("no encoding found for:  {s} {s} {s} {s} {s}", .{
                 @tagName(mnemonic),
                 @tagName(if (ops.len > 0) ops[0] else .none),
                 @tagName(if (ops.len > 1) ops[1] else .none),
@@ -21,7 +21,7 @@ pub const Instruction = struct {
             return error.InvalidInstruction;
         };
 
-        var result_ops: [4]Operand = .{.none} ** 4;
+        var result_ops: [3]Operand = .{.none} ** 3;
         @memcpy(result_ops[0..ops.len], ops);
 
         return .{
src/arch/riscv64/Encoding.zig
@@ -29,6 +29,9 @@ pub const Mnemonic = enum {
     // J Type
     jal,
 
+    // B Type
+    beq,
+
     // System
     ecall,
     ebreak,
@@ -58,7 +61,9 @@ pub const Mnemonic = enum {
             .sh     => .{ .opcode = 0b0100011, .funct3 = 0b001, .funct7 = null      },
             .sb     => .{ .opcode = 0b0100011, .funct3 = 0b000, .funct7 = null      },
 
-            .jal    => .{ .opcode = 0b1101111, .funct3 = null,  .funct7 = null     },
+            .jal    => .{ .opcode = 0b1101111, .funct3 = null,  .funct7 = null      },
+
+            .beq    => .{ .opcode = 0b1100011, .funct3 = 0b000, .funct7 = null      },
 
             .ecall  => .{ .opcode = 0b1110011, .funct3 = 0b000, .funct7 = null      },
             .ebreak => .{ .opcode = 0b1110011, .funct3 = 0b000, .funct7 = null      },
@@ -107,6 +112,9 @@ pub const InstEnc = enum {
             .jal,
             => .J,
 
+            .beq,
+            => .B,
+
             .ecall,
             .ebreak,
             .unimp,
@@ -114,15 +122,17 @@ pub const InstEnc = enum {
         };
     }
 
-    pub fn opsList(enc: InstEnc) [4]std.meta.FieldEnum(Operand) {
+    pub fn opsList(enc: InstEnc) [3]std.meta.FieldEnum(Operand) {
         return switch (enc) {
-            .R => .{ .reg, .reg, .reg, .none },
-            .I => .{ .reg, .reg, .imm, .none },
-            .S => .{ .reg, .reg, .imm, .none },
-            .B => .{ .imm, .reg, .reg, .imm },
-            .U => .{ .reg, .imm, .none, .none },
-            .J => .{ .reg, .imm, .none, .none },
-            .system => .{ .none, .none, .none, .none },
+            // zig fmt: off
+            .R =>      .{ .reg,  .reg,  .reg,  },
+            .I =>      .{ .reg,  .reg,  .imm,  },
+            .S =>      .{ .reg,  .reg,  .imm,  },
+            .B =>      .{ .reg,  .reg,  .imm,  },
+            .U =>      .{ .reg,  .imm,  .none, },
+            .J =>      .{ .reg,  .imm,  .none, },
+            .system => .{ .none, .none, .none, },
+            // zig fmt: on
         };
     }
 };
@@ -292,6 +302,26 @@ pub const Data = union(InstEnc) {
                     },
                 };
             },
+            .B => {
+                assert(ops.len == 3);
+
+                const umm = ops[2].imm.asBits(u13);
+                assert(umm % 4 == 0); // misaligned branch target
+
+                return .{
+                    .B = .{
+                        .rs1 = ops[0].reg.id(),
+                        .rs2 = ops[1].reg.id(),
+                        .imm1_4 = @truncate(umm >> 1),
+                        .imm5_10 = @truncate(umm >> 5),
+                        .imm11 = @truncate(umm >> 11),
+                        .imm12 = @truncate(umm >> 12),
+
+                        .opcode = enc.opcode,
+                        .funct3 = enc.funct3.?,
+                    },
+                };
+            },
 
             else => std.debug.panic("TODO: construct {s}", .{@tagName(inst_enc)}),
         }
src/arch/riscv64/Lower.zig
@@ -31,7 +31,9 @@ pub const Reloc = struct {
 
     const Target = union(enum) {
         inst: Mir.Inst.Index,
-        linker_reloc: bits.Symbol,
+
+        /// Relocs the lowered_inst_index and the next one.
+        load_symbol_reloc: bits.Symbol,
     };
 };
 
@@ -59,6 +61,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
             .pseudo_dbg_prologue_end,
             .pseudo_dead,
             => {},
+
             .pseudo_load_rm, .pseudo_store_rm => {
                 const rm = inst.data.rm;
 
@@ -106,6 +109,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
                     .{ .imm = Immediate.s(0) },
                 });
             },
+
             .pseudo_ret => {
                 try lower.emit(.jalr, &.{
                     .{ .reg = .zero },
@@ -113,6 +117,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
                     .{ .imm = Immediate.s(0) },
                 });
             },
+
             .pseudo_j => {
                 try lower.emit(.jal, &.{
                     .{ .reg = .zero },
@@ -123,7 +128,38 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
             .pseudo_spill_regs => try lower.pushPopRegList(true, inst.data.reg_list),
             .pseudo_restore_regs => try lower.pushPopRegList(false, inst.data.reg_list),
 
-            else => return lower.fail("TODO: psuedo {s}", .{@tagName(inst.ops)}),
+            .pseudo_load_symbol => {
+                const payload = inst.data.payload;
+                const data = lower.mir.extraData(Mir.LoadSymbolPayload, payload).data;
+
+                try lower.emit(.lui, &.{
+                    .{ .reg = @enumFromInt(data.register) },
+                    .{ .imm = lower.reloc(.{ .load_symbol_reloc = .{
+                        .atom_index = data.atom_index,
+                        .sym_index = data.sym_index,
+                    } }) },
+                });
+
+                // the above reloc implies this one
+                try lower.emit(.addi, &.{
+                    .{ .reg = @enumFromInt(data.register) },
+                    .{ .reg = @enumFromInt(data.register) },
+                    .{ .imm = Immediate.s(0) },
+                });
+            },
+
+            .pseudo_lea_rm => {
+                const rm = inst.data.rm;
+                const frame = rm.m.toFrameLoc(lower.mir);
+
+                try lower.emit(.addi, &.{
+                    .{ .reg = rm.r },
+                    .{ .reg = frame.base },
+                    .{ .imm = Immediate.s(frame.disp) },
+                });
+            },
+
+            else => return lower.fail("TODO Lower: psuedo {s}", .{@tagName(inst.ops)}),
         },
     }
 
@@ -135,7 +171,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
 
 fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
     const mnemonic = std.meta.stringToEnum(Encoding.Mnemonic, @tagName(inst.tag)) orelse {
-        return lower.fail("generic inst name {s}-{s} doesn't match with a mnemonic", .{
+        return lower.fail("generic inst name '{s}' with op {s} doesn't match with a mnemonic", .{
             @tagName(inst.tag),
             @tagName(inst.ops),
         });
@@ -151,6 +187,11 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
             .{ .reg = inst.data.i_type.rs1 },
             .{ .imm = inst.data.i_type.imm12 },
         },
+        .rr_inst => &.{
+            .{ .reg = inst.data.b_type.rs1 },
+            .{ .reg = inst.data.b_type.rs2 },
+            .{ .imm = lower.reloc(.{ .inst = inst.data.b_type.inst }) },
+        },
         else => return lower.fail("TODO: generic lower ops {s}", .{@tagName(inst.ops)}),
     });
 }
src/link/riscv.zig
@@ -73,6 +73,20 @@ pub fn writeInstJ(code: *[4]u8, value: u32) void {
     mem.writeInt(u32, code, data.toU32(), .little);
 }
 
+pub fn writeInstB(code: *[4]u8, value: u32) void {
+    var data = Encoding.Data{
+        .B = mem.bytesToValue(std.meta.TagPayload(
+            Encoding.Data,
+            Encoding.Data.B,
+        ), code),
+    };
+    data.B.imm1_4 = bitSlice(value, 4, 1);
+    data.B.imm5_10 = bitSlice(value, 10, 5);
+    data.B.imm11 = bitSlice(value, 11, 11);
+    data.B.imm12 = bitSlice(value, 12, 12);
+    mem.writeInt(u32, code, data.toU32(), .little);
+}
+
 fn bitSlice(
     value: anytype,
     comptime high: comptime_int,
src/target.zig
@@ -526,7 +526,7 @@ pub fn backendSupportsFeature(
     feature: Feature,
 ) bool {
     return switch (feature) {
-        .panic_fn => ofmt == .c or use_llvm or cpu_arch == .x86_64,
+        .panic_fn => ofmt == .c or use_llvm or cpu_arch == .x86_64 or cpu_arch == .riscv64,
         .panic_unwrap_error => ofmt == .c or use_llvm,
         .safety_check_formatted => ofmt == .c or use_llvm,
         .error_return_trace => use_llvm,