diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 8c57083312..15d405eed9 100644 --- a/lib/std/builtin.zig +++ b/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); } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 3a7ae9dbfb..cc5a731c28 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/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; }, - else => return self.fail("TODO airSliceLen for {}", .{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 {}", .{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)}), - } -} + .addr_symbol => |sym_off| { + assert(sym_off.off == 0); -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)); + const atom_index = try self.symbolIndex(); - assert(abi_size > 8 and abi_size <= 16); // must fit only fit into two registers - - 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. - - // 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)}), } } diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index 6a0b5a0559..8579f33b38 100644 --- a/src/arch/riscv64/Emit.zig +++ b/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; diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 6ba0930232..1b8c8bb3d3 100644 --- a/src/arch/riscv64/Mir.zig +++ b/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 diff --git a/src/arch/riscv64/abi.zig b/src/arch/riscv64/abi.zig index 198ff437f8..e586d297ae 100644 --- a/src/arch/riscv64/abi.zig +++ b/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, }; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 04a5af8bb0..5b42a701ff 100644 --- a/src/codegen/llvm.zig +++ b/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 diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a1e23945ee..8a3192f93e 100644 --- a/src/link/Elf.zig +++ b/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) { diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 90056cc4c5..239186ffaa 100644 --- a/src/link/Elf/Atom.zig +++ b/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), + }, } }