diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 0ffdb655ef..b3fb6b56da 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -163,8 +163,12 @@ const MCValue = union(enum) { immediate: u64, /// The value doesn't exist in memory yet. load_symbol: SymbolOffset, + /// A TLV value. + load_tlv: u32, /// The address of the memory location not-yet-allocated by the linker. lea_symbol: SymbolOffset, + /// The address of a TLV value. + lea_tlv: u32, /// The value is in a target-specific register. register: Register, /// The value is split across two registers @@ -221,6 +225,7 @@ const MCValue = union(enum) { .lea_frame, .undef, .lea_symbol, + .lea_tlv, .air_ref, .reserved_frame, => false, @@ -230,6 +235,7 @@ const MCValue = union(enum) { .register_offset, .load_frame, .load_symbol, + .load_tlv, .indirect, => true, }; @@ -248,10 +254,12 @@ const MCValue = union(enum) { .undef, .air_ref, .lea_symbol, + .lea_tlv, .reserved_frame, => unreachable, // not in memory .load_symbol => |sym_off| .{ .lea_symbol = sym_off }, + .load_tlv => |sym| .{ .lea_tlv = sym }, .memory => |addr| .{ .immediate = addr }, .load_frame => |off| .{ .lea_frame = off }, .indirect => |reg_off| switch (reg_off.off) { @@ -270,17 +278,19 @@ const MCValue = union(enum) { .indirect, .undef, .air_ref, - .load_frame, .register_pair, + .load_frame, .load_symbol, + .load_tlv, .reserved_frame, => unreachable, // not a pointer .immediate => |addr| .{ .memory = addr }, - .lea_frame => |off| .{ .load_frame = off }, .register => |reg| .{ .indirect = .{ .reg = reg } }, .register_offset => |reg_off| .{ .indirect = reg_off }, + .lea_frame => |off| .{ .load_frame = off }, .lea_symbol => |sym_off| .{ .load_symbol = sym_off }, + .lea_tlv => |sym| .{ .load_tlv = sym }, }; } @@ -298,6 +308,8 @@ const MCValue = union(enum) { .indirect, .load_symbol, .lea_symbol, + .lea_tlv, + .load_tlv, => switch (off) { 0 => mcv, else => unreachable, @@ -355,6 +367,8 @@ const InstTracking = struct { .memory, .load_frame, .lea_frame, + .load_tlv, + .lea_tlv, .load_symbol, .lea_symbol, => result, @@ -410,6 +424,8 @@ const InstTracking = struct { .lea_frame, .load_symbol, .lea_symbol, + .load_tlv, + .lea_tlv, => inst_tracking.long, .dead, .register, @@ -438,6 +454,8 @@ const InstTracking = struct { .lea_frame, .load_symbol, .lea_symbol, + .load_tlv, + .lea_tlv, => assert(std.meta.eql(inst_tracking.long, target.long)), .load_frame, .reserved_frame, @@ -3510,17 +3528,19 @@ fn airWrapOptional(func: *Func, inst: Air.Inst.Index) !void { defer if (pl_lock) |lock| func.register_manager.unlockReg(lock); const opt_mcv = try func.allocRegOrMem(opt_ty, inst, true); - try func.genCopy(pl_ty, opt_mcv, pl_mcv); if (!same_repr) { const pl_abi_size: i32 = @intCast(pl_ty.abiSize(pt)); switch (opt_mcv) { - .load_frame => |frame_addr| try func.genSetMem( - .{ .frame = frame_addr.index }, - frame_addr.off + pl_abi_size, - Type.u8, - .{ .immediate = 1 }, - ), + .load_frame => |frame_addr| { + try func.genCopy(pl_ty, opt_mcv, pl_mcv); + try func.genSetMem( + .{ .frame = frame_addr.index }, + frame_addr.off + pl_abi_size, + Type.u8, + .{ .immediate = 1 }, + ); + }, .register => |opt_reg| { try func.genBinOp( @@ -3531,6 +3551,7 @@ fn airWrapOptional(func: *Func, inst: Air.Inst.Index) !void { Type.u64, opt_reg, ); + try func.genCopy(pl_ty, opt_mcv, pl_mcv); }, else => unreachable, } @@ -4457,12 +4478,14 @@ fn load(func: *Func, dst_mcv: MCValue, ptr_mcv: MCValue, ptr_ty: Type) InnerErro .register_offset, .lea_frame, .lea_symbol, + .lea_tlv, => try func.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()), .memory, .indirect, .load_symbol, .load_frame, + .load_tlv, => { const addr_reg = try func.copyToTmpRegister(ptr_ty, ptr_mcv); const addr_lock = func.register_manager.lockRegAssumeUnused(addr_reg); @@ -4509,12 +4532,14 @@ fn store(func: *Func, ptr_mcv: MCValue, src_mcv: MCValue, ptr_ty: Type) !void { .register_offset, .lea_symbol, .lea_frame, + .lea_tlv, => try func.genCopy(src_ty, ptr_mcv.deref(), src_mcv), .memory, .indirect, .load_symbol, .load_frame, + .load_tlv, => { const addr_reg = try func.copyToTmpRegister(ptr_ty, ptr_mcv); const addr_lock = func.register_manager.lockRegAssumeUnused(addr_reg); @@ -5989,7 +6014,14 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { try func.register_manager.getReg(reg, null); try func.genSetReg(ty, reg, input_mcv); break :arg .{ .register = reg }; - } else return func.fail("invalid constraint: '{s}'", .{constraint}); + } else if (mem.eql(u8, constraint, "r")) arg: { + switch (input_mcv) { + .register => break :arg input_mcv, + else => {}, + } + const temp_reg = try func.copyToTmpRegister(ty, input_mcv); + break :arg .{ .register = temp_reg }; + } else return func.fail("invalid input constraint: '{s}'", .{constraint}); if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| { _ = func.register_manager.lockReg(reg); }; @@ -6071,6 +6103,10 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { assert(sym_off.off == 0); break :blk .{ .sym = sym_off }; } else return func.fail("invalid modifier: '{s}'", .{modifier}), + .register => |reg| if (modifier.len == 0) + .{ .reg = reg } + else + return func.fail("invalid modified '{s}'", .{modifier}), else => return func.fail("invalid constraint: '{s}'", .{op_str}), }; } else return func.fail("invalid operand: '{s}'", .{op_str}); @@ -6253,6 +6289,13 @@ fn genCopy(func: *Func, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { ty, src_mcv, ), + .load_tlv => { + const addr_reg, const addr_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(addr_lock); + + try func.genSetReg(ty, addr_reg, dst_mcv.address()); + try func.genCopy(ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv); + }, .memory => return func.fail("TODO: genCopy memory", .{}), .register_pair => |dst_regs| { const src_info: ?struct { addr_reg: Register, addr_lock: ?RegisterLock } = switch (src_mcv) { @@ -6774,6 +6817,25 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! try func.genSetReg(ty, addr_reg, src_mcv.address()); try func.genSetReg(ty, reg, .{ .indirect = .{ .reg = addr_reg } }); }, + .lea_tlv => |sym| { + const atom_index = try func.owner.getSymbolIndex(func); + + _ = try func.addInst(.{ + .tag = .pseudo_load_tlv, + .data = .{ .reloc = .{ + .register = reg, + .atom_index = atom_index, + .sym_index = sym, + } }, + }); + }, + .load_tlv => { + const addr_reg, const addr_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(addr_lock); + + try func.genSetReg(ty, addr_reg, src_mcv.address()); + try func.genSetReg(ty, reg, .{ .indirect = .{ .reg = addr_reg } }); + }, .air_ref => |ref| try func.genSetReg(ty, reg, try func.resolveInst(ref)), else => return func.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}), } @@ -6793,7 +6855,6 @@ fn genSetMem( const dst_ptr_mcv: MCValue = switch (base) { .reg => |base_reg| .{ .register_offset = .{ .reg = base_reg, .off = disp } }, .frame => |base_frame_index| .{ .lea_frame = .{ .index = base_frame_index, .off = disp } }, - .reloc => |base_symbol| .{ .lea_symbol = .{ .sym = base_symbol.sym_index, .off = disp } }, }; switch (src_mcv) { .none, @@ -6940,6 +7001,7 @@ fn genSetMem( return func.genSetMem(base, disp, ty, .{ .register = reg }); }, .air_ref => |src_ref| try func.genSetMem(base, disp, ty, try func.resolveInst(src_ref)), + else => return func.fail("TODO: genSetMem {s}", .{@tagName(src_mcv)}), } } @@ -7761,9 +7823,10 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue { .none => .none, .undef => unreachable, .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } }, + .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, .immediate => |imm| .{ .immediate = imm }, .memory => |addr| .{ .memory = addr }, - .load_got, .load_direct, .load_tlv => { + .load_got, .load_direct => { return func.fail("TODO: genTypedValue {s}", .{@tagName(mcv)}); }, }, diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index 3fd5c405f8..6c04988e78 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -77,6 +77,31 @@ pub fn emitMir(emit: *Emit) Error!void { .r_addend = 0, }); }, + .load_tlv_reloc => |symbol| { + const elf_file = emit.bin_file.cast(link.File.Elf).?; + + const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; + + const R_RISCV = std.elf.R_RISCV; + + try atom_ptr.addReloc(elf_file, .{ + .r_offset = start_offset, + .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20), + .r_addend = 0, + }); + + try atom_ptr.addReloc(elf_file, .{ + .r_offset = start_offset + 4, + .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD), + .r_addend = 0, + }); + + try atom_ptr.addReloc(elf_file, .{ + .r_offset = start_offset + 8, + .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I), + .r_addend = 0, + }); + }, .call_extern_fn_reloc => |symbol| { const elf_file = emit.bin_file.cast(link.File.Elf).?; const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; diff --git a/src/arch/riscv64/Lower.zig b/src/arch/riscv64/Lower.zig index 370731c0ae..fc8202adea 100644 --- a/src/arch/riscv64/Lower.zig +++ b/src/arch/riscv64/Lower.zig @@ -34,6 +34,8 @@ pub const Reloc = struct { /// Relocs the lowered_inst_index and the next instruction. load_symbol_reloc: bits.Symbol, + /// Relocs the lowered_inst_index and the next two instructions. + load_tlv_reloc: bits.Symbol, /// Relocs the lowered_inst_index and the next instruction. call_extern_fn_reloc: bits.Symbol, }; @@ -252,6 +254,34 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index, options: struct { }); }, + .pseudo_load_tlv => { + const payload = inst.data.reloc; + const dst_reg = payload.register; + assert(dst_reg.class() == .int); + + try lower.emit(.lui, &.{ + .{ .reg = dst_reg }, + .{ .imm = lower.reloc(.{ + .load_tlv_reloc = .{ + .atom_index = payload.atom_index, + .sym_index = payload.sym_index, + }, + }) }, + }); + + try lower.emit(.add, &.{ + .{ .reg = dst_reg }, + .{ .reg = dst_reg }, + .{ .reg = .tp }, + }); + + try lower.emit(.addi, &.{ + .{ .reg = dst_reg }, + .{ .reg = dst_reg }, + .{ .imm = Immediate.s(0) }, + }); + }, + .pseudo_lea_rm => { const rm = inst.data.rm; assert(rm.r.class() == .int); diff --git a/src/arch/riscv64/bits.zig b/src/arch/riscv64/bits.zig index 5928449828..45fdd22bc0 100644 --- a/src/arch/riscv64/bits.zig +++ b/src/arch/riscv64/bits.zig @@ -15,7 +15,6 @@ pub const Memory = struct { pub const Base = union(enum) { reg: Register, frame: FrameIndex, - reloc: Symbol, }; pub const Mod = struct { @@ -82,7 +81,6 @@ pub const Memory = struct { .disp = base_loc.disp + offset, }; }, - .reloc => unreachable, } } }; diff --git a/src/arch/riscv64/encoding.zig b/src/arch/riscv64/encoding.zig index 15263aba1d..3ff497a1ea 100644 --- a/src/arch/riscv64/encoding.zig +++ b/src/arch/riscv64/encoding.zig @@ -432,6 +432,7 @@ pub const Lir = struct { .pseudo_j, .pseudo_dead, .pseudo_load_symbol, + .pseudo_load_tlv, .pseudo_mv, .pseudo_restore_regs, .pseudo_spill_regs, diff --git a/src/arch/riscv64/mnem.zig b/src/arch/riscv64/mnem.zig index e37dee74b9..121312e941 100644 --- a/src/arch/riscv64/mnem.zig +++ b/src/arch/riscv64/mnem.zig @@ -234,6 +234,8 @@ pub const Mnemonic = enum(u16) { pseudo_dead, /// Loads the address of a value that hasn't yet been allocated in memory. pseudo_load_symbol, + /// Loads the address of a TLV. + pseudo_load_tlv, /// Moves the value of rs1 to rd. pseudo_mv,