authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-03-28 15:59:28-07:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-05-11 02:17:11-07:00
log350ad90ceec37cb3f152b666377f7f619981a60e
treebbff3fca1130fb2f13461c26e9f57c7796f47b0c
parentcbf62bd6dc1f020df1177b3c6bcf11ed945ac83b

riscv: totally rewrite how we do loads and stores

this commit is a little too large to document fully, however the main gist of it this - finish the `genInlineMemcpy` implement - rename `setValue` to `genCopy` as I agree with jacob that it's a better name - add in `genVarDbgInfo` for a better gdb experience - follow the x86_64's method for genCall, as the procedure is very similar for us - add `airSliceLen` as it's trivial - change up the `airAddWithOverflow implementation a bit - make sure to not spill of the elem_ty is 0 size - correctly follow the RISC-V calling convention and spill the used calle saved registers in the prologue and restore them in the epilogue - add `address`, `deref`, and `offset` helper functions for MCValue. I must say I love these, they make the code very readable and super verbose :) - fix a `register_manager.zig` issue where when using the last register in the set, the value would overflow at comptime. was happening because we were adding to `max_id` before subtracting from it.

7 files changed, 747 insertions(+), 310 deletions(-)

lib/std/builtin.zig+8-8
......@@ -775,14 +775,14 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
775775 }
776776
777777 if (builtin.zig_backend == .stage2_riscv64) {
778 asm volatile ("ecall"
779 :
780 : [number] "{a7}" (64),
781 [arg1] "{a0}" (1),
782 [arg2] "{a1}" (@intFromPtr(msg.ptr)),
783 [arg3] "{a2}" (msg.len),
784 : "rcx", "r11", "memory"
785 );
778 // asm volatile ("ecall"
779 // :
780 // : [number] "{a7}" (64),
781 // [arg1] "{a0}" (1),
782 // [arg2] "{a1}" (@intFromPtr(msg.ptr)),
783 // [arg3] "{a2}" (msg.len),
784 // : "rcx", "r11", "memory"
785 // );
786786 std.posix.exit(127);
787787 }
788788
src/arch/riscv64/CodeGen.zig+618-230
......@@ -38,9 +38,16 @@ const callee_preserved_regs = abi.callee_preserved_regs;
3838const gp = abi.RegisterClass.gp;
3939/// Function Args
4040const fa = abi.RegisterClass.fa;
41/// Temporary Use
42const tp = abi.RegisterClass.tp;
4143
4244const InnerError = CodeGenError || error{OutOfRegisters};
4345
46const RegisterView = enum(u1) {
47 caller,
48 callee,
49};
50
4451gpa: Allocator,
4552air: Air,
4653liveness: Liveness,
......@@ -82,8 +89,8 @@ branch_stack: *std.ArrayList(Branch),
8289
8390// Key is the block instruction
8491blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
85
8692register_manager: RegisterManager = .{},
93
8794/// Maps offset to what is stored there.
8895stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
8996
......@@ -99,6 +106,7 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
99106const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
100107
101108const SymbolOffset = struct { sym: u32, off: i32 = 0 };
109const RegisterOffset = struct { reg: Register, off: i32 = 0 };
102110
103111const MCValue = union(enum) {
104112 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
......@@ -119,6 +127,8 @@ const MCValue = union(enum) {
119127 load_symbol: SymbolOffset,
120128 /// The value is in a target-specific register.
121129 register: Register,
130 /// The value is split across two registers
131 register_pair: [2]Register,
122132 /// The value is in memory at a hard-coded address.
123133 /// If the type is a pointer, it means the pointer address is at this memory location.
124134 memory: u64,
......@@ -127,10 +137,15 @@ const MCValue = union(enum) {
127137 stack_offset: u32,
128138 /// The value is a pointer to one of the stack variables (payload is stack offset).
129139 ptr_stack_offset: u32,
140 air_ref: Air.Inst.Ref,
141 /// The value is in memory at a constant offset from the address in a register.
142 indirect: RegisterOffset,
143 /// The value is a constant offset from the value in a register.
144 register_offset: RegisterOffset,
130145
131146 fn isMemory(mcv: MCValue) bool {
132147 return switch (mcv) {
133 .memory, .stack_offset => true,
148 .memory, .indirect, .load_frame => true,
134149 else => false,
135150 };
136151 }
......@@ -151,15 +166,85 @@ const MCValue = union(enum) {
151166 .immediate,
152167 .memory,
153168 .ptr_stack_offset,
169 .indirect,
154170 .undef,
155171 .load_symbol,
172 .air_ref,
156173 => false,
157174
158175 .register,
176 .register_pair,
177 .register_offset,
159178 .stack_offset,
160179 => true,
161180 };
162181 }
182
183 fn address(mcv: MCValue) MCValue {
184 return switch (mcv) {
185 .none,
186 .unreach,
187 .dead,
188 .immediate,
189 .ptr_stack_offset,
190 .register_offset,
191 .undef,
192 .air_ref,
193 => unreachable, // not in memory
194
195 .memory => |addr| .{ .immediate = addr },
196 .stack_offset => |off| .{ .ptr_stack_offset = off },
197 .indirect => |reg_off| switch (reg_off.off) {
198 0 => .{ .register = reg_off.reg },
199 else => .{ .register_offset = reg_off },
200 },
201 };
202 }
203
204 fn deref(mcv: MCValue) MCValue {
205 return switch (mcv) {
206 .none,
207 .unreach,
208 .dead,
209 .memory,
210 .indirect,
211 .undef,
212 .air_ref,
213 .stack_offset,
214 .register_pair,
215 .load_symbol,
216 => unreachable, // not a pointer
217
218 .immediate => |addr| .{ .memory = addr },
219 .ptr_stack_offset => |off| .{ .stack_offset = off },
220 .register => |reg| .{ .indirect = .{ .reg = reg } },
221 .register_offset => |reg_off| .{ .indirect = reg_off },
222 };
223 }
224
225 fn offset(mcv: MCValue, off: i32) MCValue {
226 return switch (mcv) {
227 .none,
228 .unreach,
229 .dead,
230 .undef,
231 .air_ref,
232 => unreachable, // not valid
233 .register_pair,
234 .memory,
235 .indirect,
236 .stack_offset,
237 .load_symbol,
238 => switch (off) {
239 0 => mcv,
240 else => unreachable, // not offsettable
241 },
242 .immediate => |imm| .{ .immediate = @bitCast(@as(i64, @bitCast(imm)) +% off) },
243 .register => |reg| .{ .register_offset = .{ .reg = reg, .off = off } },
244 .register_offset => |reg_off| .{ .register_offset = .{ .reg = reg_off.reg, .off = reg_off.off + off } },
245 .ptr_stack_offset => |stack_off| .{ .ptr_stack_offset = @intCast(@as(i64, @intCast(stack_off)) +% off) },
246 };
247 }
163248};
164249
165250const Branch = struct {
......@@ -211,6 +296,11 @@ const BigTomb = struct {
211296
212297const Self = @This();
213298
299const CallView = enum(u1) {
300 callee,
301 caller,
302};
303
214304pub fn generate(
215305 lf: *link.File,
216306 src_loc: Module.SrcLoc,
......@@ -261,7 +351,7 @@ pub fn generate(
261351 defer function.blocks.deinit(gpa);
262352 defer function.exitlude_jump_relocs.deinit(gpa);
263353
264 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
354 var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) {
265355 error.CodegenFail => return Result{ .fail = function.err_msg.? },
266356 error.OutOfRegisters => return Result{
267357 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
......@@ -284,6 +374,14 @@ pub fn generate(
284374 else => |e| return e,
285375 };
286376
377 // Create list of registers to save in the prologue.
378 var save_reg_list = Mir.RegisterList{};
379 for (callee_preserved_regs) |reg| {
380 if (function.register_manager.isRegAllocated(reg)) {
381 save_reg_list.push(&callee_preserved_regs, reg);
382 }
383 }
384
287385 var mir = Mir{
288386 .instructions = function.mir_instructions.toOwnedSlice(),
289387 .extra = try function.mir_extra.toOwnedSlice(gpa),
......@@ -300,8 +398,10 @@ pub fn generate(
300398 .prev_di_pc = 0,
301399 .prev_di_line = func.lbrace_line,
302400 .prev_di_column = func.lbrace_column,
303 .stack_size = @max(32, function.max_end_stack),
304401 .code_offset_mapping = .{},
402 // need to at least decrease the sp by -8
403 .stack_size = @max(8, mem.alignForward(u32, function.max_end_stack, 16)),
404 .save_reg_list = save_reg_list,
305405 };
306406 defer emit.deinit();
307407
......@@ -629,6 +729,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
629729 }
630730}
631731
732fn feed(self: *Self, bt: *Liveness.BigTomb, operand: Air.Inst.Ref) !void {
733 if (bt.feed()) if (operand.toIndex()) |inst| self.processDeath(inst);
734}
735
632736/// Asserts there is already capacity to insert into top branch inst_table.
633737fn processDeath(self: *Self, inst: Air.Inst.Index) void {
634738 // When editing this function, note that the logic must synchronize with `reuseOperand`.
......@@ -639,7 +743,7 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
639743 .register => |reg| {
640744 self.register_manager.freeReg(reg);
641745 },
642 else => {}, // TODO process stack allocation death
746 else => {}, // TODO process stack allocation death by freeing it to be reused later
643747 }
644748}
645749
......@@ -650,17 +754,11 @@ fn finishAirBookkeeping(self: *Self) void {
650754 }
651755}
652756
653fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
654 var tomb_bits = self.liveness.getTombBits(inst);
655 for (operands) |op| {
656 const dies = @as(u1, @truncate(tomb_bits)) != 0;
657 tomb_bits >>= 1;
658 if (!dies) continue;
659 const op_index = op.toIndex() orelse continue;
660 self.processDeath(op_index);
661 }
662 const is_used = @as(u1, @truncate(tomb_bits)) == 0;
663 if (is_used) {
757fn finishAirResult(self: *Self, inst: Air.Inst.Index, result: MCValue) void {
758 if (self.liveness.isUnused(inst)) switch (result) {
759 .none, .dead, .unreach => {},
760 else => unreachable, // Why didn't the result die?
761 } else {
664762 log.debug("%{d} => {}", .{ inst, result });
665763 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
666764 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
......@@ -682,6 +780,22 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
682780 self.finishAirBookkeeping();
683781}
684782
783fn finishAir(
784 self: *Self,
785 inst: Air.Inst.Index,
786 result: MCValue,
787 operands: [Liveness.bpi - 1]Air.Inst.Ref,
788) !void {
789 var tomb_bits = self.liveness.getTombBits(inst);
790 for (operands) |op| {
791 const dies = @as(u1, @truncate(tomb_bits)) != 0;
792 tomb_bits >>= 1;
793 if (!dies) continue;
794 self.processDeath(op.toIndexAllowNone() orelse continue);
795 }
796 self.finishAirResult(inst, result);
797}
798
685799fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
686800 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
687801 try table.ensureUnusedCapacity(self.gpa, additional_count);
......@@ -716,6 +830,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
716830fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
717831 const mod = self.bin_file.comp.module.?;
718832 const elem_ty = self.typeOfIndex(inst);
833
719834 const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse {
720835 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
721836 };
......@@ -728,12 +843,12 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
728843 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
729844 if (abi_size <= ptr_bytes) {
730845 if (self.register_manager.tryAllocReg(inst, gp)) |reg| {
731 return MCValue{ .register = reg };
846 return .{ .register = reg };
732847 }
733848 }
734849 }
735850 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
736 return MCValue{ .stack_offset = stack_offset };
851 return .{ .stack_offset = stack_offset };
737852}
738853
739854/// Allocates a register from the general purpose set and returns the Register and the Lock.
......@@ -746,6 +861,12 @@ fn allocReg(self: *Self) !struct { Register, RegisterLock } {
746861}
747862
748863pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
864 const mod = self.bin_file.comp.module.?;
865 const elem_ty = self.typeOfIndex(inst);
866
867 // there isn't anything to spill
868 if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) return;
869
749870 const stack_mcv = try self.allocRegOrMem(inst, false);
750871 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
751872 const reg_mcv = self.getResolvedInstValue(inst);
......@@ -759,7 +880,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
759880/// allocated. A second call to `copyToTmpRegister` may return the same register.
760881/// This can have a side effect of spilling instructions to the stack to free up a register.
761882fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
762 const reg = try self.register_manager.allocReg(null, gp);
883 const reg = try self.register_manager.allocReg(null, tp);
763884 try self.genSetReg(ty, reg, mcv);
764885 return reg;
765886}
......@@ -830,7 +951,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
830951 math.divCeil(u32, src_storage_bits, 64) catch unreachable and
831952 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: {
832953 const dst_mcv = try self.allocRegOrMem(inst, true);
833 try self.setValue(min_ty, dst_mcv, src_mcv);
954 try self.genCopy(min_ty, dst_mcv, src_mcv);
834955 break :dst dst_mcv;
835956 };
836957
......@@ -1261,43 +1382,48 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
12611382
12621383 if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) {
12631384 if (int_info.signedness == .unsigned) {
1264 const overflow_offset = tuple_ty.structFieldOffset(1, mod) + offset;
1265
1266 const max_val = std.math.pow(u16, 2, int_info.bits) - 1;
1267
1268 const overflow_reg, const overflow_lock = try self.allocReg();
1269 defer self.register_manager.unlockReg(overflow_lock);
1385 switch (int_info.bits) {
1386 1...8 => {
1387 const max_val = std.math.pow(u16, 2, int_info.bits) - 1;
12701388
1271 const add_reg, const add_lock = blk: {
1272 if (add_result_mcv == .register) break :blk .{ add_result_mcv.register, null };
1273
1274 const add_reg, const add_lock = try self.allocReg();
1275 try self.genSetReg(lhs_ty, add_reg, add_result_mcv);
1276 break :blk .{ add_reg, add_lock };
1277 };
1278 defer if (add_lock) |lock| self.register_manager.unlockReg(lock);
1279
1280 _ = try self.addInst(.{
1281 .tag = .andi,
1282 .data = .{ .i_type = .{
1283 .rd = overflow_reg,
1284 .rs1 = add_reg,
1285 .imm12 = @intCast(max_val),
1286 } },
1287 });
1389 const overflow_reg, const overflow_lock = try self.allocReg();
1390 defer self.register_manager.unlockReg(overflow_lock);
12881391
1289 const overflow_mcv = try self.binOp(
1290 .cmp_neq,
1291 null,
1292 .{ .register = overflow_reg },
1293 .{ .register = add_reg },
1294 lhs_ty,
1295 lhs_ty,
1296 );
1392 const add_reg, const add_lock = blk: {
1393 if (add_result_mcv == .register) break :blk .{ add_result_mcv.register, null };
12971394
1298 try self.genSetStack(Type.u1, @intCast(overflow_offset), overflow_mcv);
1395 const add_reg, const add_lock = try self.allocReg();
1396 try self.genSetReg(lhs_ty, add_reg, add_result_mcv);
1397 break :blk .{ add_reg, add_lock };
1398 };
1399 defer if (add_lock) |lock| self.register_manager.unlockReg(lock);
1400
1401 _ = try self.addInst(.{
1402 .tag = .andi,
1403 .data = .{ .i_type = .{
1404 .rd = overflow_reg,
1405 .rs1 = add_reg,
1406 .imm12 = @intCast(max_val),
1407 } },
1408 });
1409
1410 const overflow_mcv = try self.binOp(
1411 .cmp_neq,
1412 null,
1413 .{ .register = overflow_reg },
1414 .{ .register = add_reg },
1415 lhs_ty,
1416 lhs_ty,
1417 );
1418
1419 const overflow_offset = tuple_ty.structFieldOffset(1, mod) + offset;
1420 try self.genSetStack(Type.u1, @intCast(overflow_offset), overflow_mcv);
1421
1422 break :result result_mcv;
1423 },
12991424
1300 break :result result_mcv;
1425 else => return self.fail("TODO: addWithOverflow check for size {d}", .{int_info.bits}),
1426 }
13011427 } else {
13021428 return self.fail("TODO: airAddWithOverFlow calculate carry for signed addition", .{});
13031429 }
......@@ -1367,6 +1493,7 @@ fn airShl(self: *Self, inst: Air.Inst.Index) !void {
13671493 const rhs = try self.resolveInst(bin_op.rhs);
13681494 const lhs_ty = self.typeOf(bin_op.lhs);
13691495 const rhs_ty = self.typeOf(bin_op.rhs);
1496
13701497 break :result try self.binOp(.shl, inst, lhs, rhs, lhs_ty, rhs_ty);
13711498 };
13721499 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
......@@ -1506,7 +1633,19 @@ fn slicePtr(self: *Self, mcv: MCValue) !MCValue {
15061633
15071634fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
15081635 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1509 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSliceLen for {}", .{self.target.cpu.arch});
1636 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1637 const ptr_bits = 64;
1638 const ptr_bytes = @divExact(ptr_bits, 8);
1639 const mcv = try self.resolveInst(ty_op.operand);
1640 switch (mcv) {
1641 .dead, .unreach, .none => unreachable,
1642 .register => unreachable, // a slice doesn't fit in one register
1643 .stack_offset => |off| {
1644 break :result MCValue{ .stack_offset = off + ptr_bytes };
1645 },
1646 else => return self.fail("TODO airSliceLen for {}", .{mcv}),
1647 }
1648 };
15101649 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
15111650}
15121651
......@@ -1598,10 +1737,60 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {
15981737
15991738fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
16001739 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1601 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});
1740 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1741 const operand = try self.resolveInst(ty_op.operand);
1742 const operand_ty = self.typeOf(ty_op.operand);
1743
1744 const dest_reg = try self.register_manager.allocReg(inst, gp);
1745
1746 const source_reg, const source_lock = blk: {
1747 if (operand == .register) break :blk .{ operand.register, null };
1748
1749 const source_reg, const source_lock = try self.allocReg();
1750 try self.genSetReg(operand_ty, source_reg, operand);
1751 break :blk .{ source_reg, source_lock };
1752 };
1753 defer if (source_lock) |lock| self.register_manager.unlockReg(lock);
1754
1755 // TODO: the B extension for RISCV should have the ctz instruction, and we should use it.
1756
1757 try self.ctz(source_reg, dest_reg, operand_ty);
1758
1759 break :result .{ .register = dest_reg };
1760 };
16021761 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
16031762}
16041763
1764fn ctz(self: *Self, src: Register, dst: Register, ty: Type) !void {
1765 const mod = self.bin_file.comp.module.?;
1766 const length = (ty.abiSize(mod) * 8) - 1;
1767
1768 const count_reg, const count_lock = try self.allocReg();
1769 defer self.register_manager.unlockReg(count_lock);
1770
1771 const len_reg, const len_lock = try self.allocReg();
1772 defer self.register_manager.unlockReg(len_lock);
1773
1774 try self.genSetReg(Type.usize, count_reg, .{ .immediate = 0 });
1775 try self.genSetReg(Type.usize, len_reg, .{ .immediate = length });
1776
1777 _ = try self.addInst(.{
1778 .tag = .beq,
1779 .data = .{
1780 .b_type = .{
1781 .rs1 = count_reg,
1782 .rs2 = len_reg,
1783 .inst = @intCast(self.mir_instructions.len + 0),
1784 },
1785 },
1786 });
1787
1788 _ = src;
1789 _ = dst;
1790
1791 return self.fail("TODO: finish ctz", .{});
1792}
1793
16051794fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
16061795 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
16071796 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
......@@ -1750,12 +1939,12 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
17501939 const elem_ty = self.typeOfIndex(inst);
17511940 const result: MCValue = result: {
17521941 if (!elem_ty.hasRuntimeBits(mod))
1753 break :result MCValue.none;
1942 break :result .none;
17541943
17551944 const ptr = try self.resolveInst(ty_op.operand);
17561945 const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(mod);
17571946 if (self.liveness.isUnused(inst) and !is_volatile)
1758 break :result MCValue.dead;
1947 break :result .dead;
17591948
17601949 const dst_mcv: MCValue = blk: {
17611950 if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
......@@ -1771,27 +1960,38 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
17711960 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
17721961}
17731962
1774fn load(self: *Self, dst_mcv: MCValue, src_ptr: MCValue, ptr_ty: Type) InnerError!void {
1963fn load(self: *Self, dst_mcv: MCValue, ptr_mcv: MCValue, ptr_ty: Type) InnerError!void {
17751964 const mod = self.bin_file.comp.module.?;
1776 const elem_ty = ptr_ty.childType(mod);
1965 const dst_ty = ptr_ty.childType(mod);
17771966
1778 switch (src_ptr) {
1779 .none => unreachable,
1780 .undef => unreachable,
1781 .unreach => unreachable,
1782 .dead => unreachable,
1783 .immediate => |imm| try self.setValue(elem_ty, dst_mcv, .{ .memory = imm }),
1784 .ptr_stack_offset => |off| try self.setValue(elem_ty, dst_mcv, .{ .stack_offset = off }),
1967 log.debug("loading {}:{} into {}", .{ ptr_mcv, ptr_ty.fmt(mod), dst_mcv });
17851968
1786 .stack_offset,
1969 switch (ptr_mcv) {
1970 .none,
1971 .undef,
1972 .unreach,
1973 .dead,
1974 .register_pair,
1975 => unreachable, // not a valid pointer
1976
1977 .immediate,
17871978 .register,
1979 .register_offset,
1980 .ptr_stack_offset,
1981 => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()),
1982
17881983 .memory,
1789 => try self.setValue(elem_ty, dst_mcv, src_ptr),
1984 .indirect,
1985 .load_symbol,
1986 .stack_offset,
1987 => {
1988 const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv);
1989 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
1990 defer self.register_manager.unlockReg(addr_lock);
17901991
1791 .load_symbol => {
1792 const reg = try self.copyToTmpRegister(ptr_ty, src_ptr);
1793 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
1992 try self.genCopy(dst_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } });
17941993 },
1994 .air_ref => |ptr_ref| try self.load(dst_mcv, try self.resolveInst(ptr_ref), ptr_ty),
17951995 }
17961996}
17971997
......@@ -1817,7 +2017,12 @@ fn store(self: *Self, pointer: MCValue, value: MCValue, ptr_ty: Type, value_ty:
18172017 const mod = self.bin_file.comp.module.?;
18182018 const value_abi_size = value_ty.abiSize(mod);
18192019
1820 log.debug("storing {s}", .{@tagName(pointer)});
2020 log.debug("storing {}:{} in {}:{}", .{ value, value_ty.fmt(mod), pointer, ptr_ty.fmt(mod) });
2021
2022 if (value_ty.isSlice(mod)) {
2023 // cheat a bit by loading in two parts
2024
2025 }
18212026
18222027 switch (pointer) {
18232028 .none => unreachable,
......@@ -1976,7 +2181,6 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
19762181}
19772182
19782183fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1979 const mod = self.bin_file.comp.module.?;
19802184 var arg_index = self.arg_index;
19812185
19822186 // we skip over args that have no bits
......@@ -1986,21 +2190,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
19862190 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
19872191 const src_mcv = self.args[arg_index];
19882192
1989 // we want to move every arg onto the stack.
1990 // while it might no tbe the best solution right now, it simplifies
1991 // the spilling of args with multiple arg levels.
19922193 const dst_mcv = switch (src_mcv) {
19932194 .register => |src_reg| dst: {
1994 // TODO: get the true type of the arg, and fit the spill to size.
1995 const arg_size = Type.usize.abiSize(mod);
1996 const arg_align = Type.usize.abiAlignment(mod);
1997 const offset = try self.allocMem(inst, @intCast(arg_size), arg_align);
1998 try self.genSetStack(Type.usize, offset, .{ .register = src_reg });
1999
2000 // can go on to be reused in next function call
2001 self.register_manager.freeReg(src_reg);
2002
2003 break :dst .{ .stack_offset = offset };
2195 try self.register_manager.getReg(src_reg, null);
2196 break :dst src_mcv;
20042197 },
20052198 else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}),
20062199 };
......@@ -2044,87 +2237,122 @@ fn airFence(self: *Self) !void {
20442237}
20452238
20462239fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
2047 const mod = self.bin_file.comp.module.?;
20482240 if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{});
20492241 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
2050 const fn_ty = self.typeOf(pl_op.operand);
20512242 const callee = pl_op.operand;
20522243 const extra = self.air.extraData(Air.Call, pl_op.payload);
2053 const args: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]);
2244 const arg_refs: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]);
2245
2246 const expected_num_args = 8;
2247 const ExpectedContents = extern struct {
2248 vals: [expected_num_args][@sizeOf(MCValue)]u8 align(@alignOf(MCValue)),
2249 };
2250 var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) =
2251 std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa);
2252 const allocator = stack.get();
2253
2254 const arg_tys = try allocator.alloc(Type, arg_refs.len);
2255 defer allocator.free(arg_tys);
2256 for (arg_tys, arg_refs) |*arg_ty, arg_ref| arg_ty.* = self.typeOf(arg_ref);
2257
2258 const arg_vals = try allocator.alloc(MCValue, arg_refs.len);
2259 defer allocator.free(arg_vals);
2260 for (arg_vals, arg_refs) |*arg_val, arg_ref| arg_val.* = .{ .air_ref = arg_ref };
2261
2262 const call_ret = try self.genCall(.{ .air = callee }, arg_tys, arg_vals);
2263
2264 var bt = self.liveness.iterateBigTomb(inst);
2265 try self.feed(&bt, pl_op.operand);
2266 for (arg_refs) |arg_ref| try self.feed(&bt, arg_ref);
2267
2268 const result = if (self.liveness.isUnused(inst)) .unreach else call_ret;
2269 return self.finishAirResult(inst, result);
2270}
2271
2272fn genCall(
2273 self: *Self,
2274 info: union(enum) {
2275 air: Air.Inst.Ref,
2276 lib: struct {
2277 return_type: InternPool.Index,
2278 param_types: []const InternPool.Index,
2279 lib: ?[]const u8 = null,
2280 callee: []const u8,
2281 },
2282 },
2283 arg_tys: []const Type,
2284 args: []const MCValue,
2285) !MCValue {
2286 const mod = self.bin_file.comp.module.?;
2287
2288 const fn_ty = switch (info) {
2289 .air => |callee| fn_info: {
2290 const callee_ty = self.typeOf(callee);
2291 break :fn_info switch (callee_ty.zigTypeTag(mod)) {
2292 .Fn => callee_ty,
2293 .Pointer => callee_ty.childType(mod),
2294 else => unreachable,
2295 };
2296 },
2297 .lib => |lib| try mod.funcType(.{
2298 .param_types = lib.param_types,
2299 .return_type = lib.return_type,
2300 .cc = .C,
2301 }),
2302 };
20542303
2055 var info = try self.resolveCallingConventionValues(fn_ty);
2056 defer info.deinit(self);
2304 var call_info = try self.resolveCallingConventionValues(fn_ty, .caller);
2305 defer call_info.deinit(self);
2306
2307 for (call_info.args, 0..) |mc_arg, arg_i| try self.genCopy(arg_tys[arg_i], mc_arg, args[arg_i]);
20572308
20582309 // Due to incremental compilation, how function calls are generated depends
20592310 // on linking.
2060 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
2061 for (info.args, 0..) |mc_arg, arg_i| {
2062 const arg = args[arg_i];
2063 const arg_ty = self.typeOf(arg);
2064 const arg_mcv = try self.resolveInst(args[arg_i]);
2065 try self.setValue(arg_ty, mc_arg, arg_mcv);
2066 }
2067
2068 if (try self.air.value(callee, mod)) |func_value| {
2069 switch (mod.intern_pool.indexToKey(func_value.ip_index)) {
2311 switch (info) {
2312 .air => |callee| if (try self.air.value(callee, mod)) |func_value| {
2313 const func_key = mod.intern_pool.indexToKey(func_value.ip_index);
2314 switch (switch (func_key) {
2315 else => func_key,
2316 .ptr => |ptr| switch (ptr.addr) {
2317 .decl => |decl| mod.intern_pool.indexToKey(mod.declPtr(decl).val.toIntern()),
2318 else => func_key,
2319 },
2320 }) {
20702321 .func => |func| {
2071 const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
2072 const sym = elf_file.symbol(sym_index);
2073 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
2074 const got_addr = sym.zigGotAddress(elf_file);
2075 try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr });
2076 _ = try self.addInst(.{
2077 .tag = .jalr,
2078 .data = .{ .i_type = .{
2079 .rd = .ra,
2080 .rs1 = .ra,
2081 .imm12 = 0,
2082 } },
2083 });
2322 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
2323 const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
2324 const sym = elf_file.symbol(sym_index);
2325 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
2326 const got_addr = sym.zigGotAddress(elf_file);
2327 try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr });
2328 _ = try self.addInst(.{
2329 .tag = .jalr,
2330 .data = .{ .i_type = .{
2331 .rd = .ra,
2332 .rs1 = .ra,
2333 .imm12 = 0,
2334 } },
2335 });
2336 } else if (self.bin_file.cast(link.File.Coff)) |_| {
2337 return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch});
2338 } else if (self.bin_file.cast(link.File.MachO)) |_| {
2339 unreachable; // unsupported architecture for MachO
2340 } else if (self.bin_file.cast(link.File.Plan9)) |_| {
2341 return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch});
2342 } else unreachable;
20842343 },
20852344 .extern_func => {
2086 return self.fail("TODO implement calling extern functions", .{});
2087 },
2088 else => {
2089 return self.fail("TODO implement calling bitcasted functions", .{});
2345 return self.fail("TODO: extern func calls", .{});
20902346 },
2347 else => return self.fail("TODO implement calling bitcasted functions", .{}),
20912348 }
20922349 } else {
2093 return self.fail("TODO implement calling runtime-known function pointer", .{});
2094 }
2095 } else if (self.bin_file.cast(link.File.Coff)) |_| {
2096 return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch});
2097 } else if (self.bin_file.cast(link.File.MachO)) |_| {
2098 unreachable; // unsupported architecture for MachO
2099 } else if (self.bin_file.cast(link.File.Plan9)) |_| {
2100 return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch});
2101 } else unreachable;
2102
2103 const result: MCValue = result: {
2104 switch (info.return_value) {
2105 .register => |reg| {
2106 if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) {
2107 // Save function return value in a callee saved register
2108 break :result try self.copyToNewRegister(inst, info.return_value);
2109 }
2110 },
2111 else => {},
2112 }
2113 break :result info.return_value;
2114 };
2115
2116 if (args.len <= Liveness.bpi - 2) {
2117 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
2118 buf[0] = callee;
2119 @memcpy(buf[1..][0..args.len], args);
2120 return self.finishAir(inst, result, buf);
2121 }
2122 var bt = try self.iterateBigTomb(inst, 1 + args.len);
2123 bt.feed(callee);
2124 for (args) |arg| {
2125 bt.feed(arg);
2350 return self.fail("TODO: call function pointers", .{});
2351 },
2352 .lib => return self.fail("TODO: lib func calls", .{}),
21262353 }
2127 return bt.finishAir(result);
2354
2355 return call_info.return_value;
21282356}
21292357
21302358fn airRet(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
......@@ -2151,7 +2379,7 @@ fn ret(self: *Self, mcv: MCValue) !void {
21512379 const mod = self.bin_file.comp.module.?;
21522380
21532381 const ret_ty = self.fn_type.fnReturnType(mod);
2154 try self.setValue(ret_ty, self.ret_mcv, mcv);
2382 try self.genCopy(ret_ty, self.ret_mcv, mcv);
21552383
21562384 _ = try self.addInst(.{
21572385 .tag = .psuedo_epilogue,
......@@ -2183,6 +2411,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index) !void {
21832411 const ty = self.typeOf(bin_op.lhs);
21842412 const mod = self.bin_file.comp.module.?;
21852413 assert(ty.eql(self.typeOf(bin_op.rhs), mod));
2414
21862415 if (ty.zigTypeTag(mod) == .ErrorSet)
21872416 return self.fail("TODO implement cmp for errors", .{});
21882417
......@@ -2233,11 +2462,54 @@ fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
22332462
22342463fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
22352464 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
2236 const name = self.air.nullTerminatedString(pl_op.payload);
22372465 const operand = pl_op.operand;
2238 // TODO emit debug info for this variable
2239 _ = name;
2240 return self.finishAir(inst, .dead, .{ operand, .none, .none });
2466 const ty = self.typeOf(operand);
2467 const mcv = try self.resolveInst(operand);
2468
2469 const name = self.air.nullTerminatedString(pl_op.payload);
2470
2471 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
2472 try self.genVarDbgInfo(tag, ty, mcv, name);
2473
2474 return self.finishAir(inst, .unreach, .{ operand, .none, .none });
2475}
2476
2477fn genVarDbgInfo(
2478 self: Self,
2479 tag: Air.Inst.Tag,
2480 ty: Type,
2481 mcv: MCValue,
2482 name: [:0]const u8,
2483) !void {
2484 const mod = self.bin_file.comp.module.?;
2485 const is_ptr = switch (tag) {
2486 .dbg_var_ptr => true,
2487 .dbg_var_val => false,
2488 else => unreachable,
2489 };
2490
2491 switch (self.debug_output) {
2492 .dwarf => |dw| {
2493 const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (mcv) {
2494 .register => |reg| .{ .register = reg.dwarfLocOp() },
2495 .memory => |address| .{ .memory = address },
2496 .load_symbol => |sym_off| loc: {
2497 assert(sym_off.off == 0);
2498 break :loc .{ .linker_load = .{ .type = .direct, .sym_index = sym_off.sym } };
2499 },
2500 .immediate => |x| .{ .immediate = x },
2501 .undef => .undef,
2502 .none => .none,
2503 else => blk: {
2504 log.debug("TODO generate debug info for {}", .{mcv});
2505 break :blk .nop;
2506 },
2507 };
2508 try dw.genVarDbgInfo(name, ty, mod.funcOwnerDeclIndex(self.func_index), is_ptr, loc);
2509 },
2510 .plan9 => {},
2511 .none => {},
2512 }
22412513}
22422514
22432515fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
......@@ -2348,7 +2620,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
23482620 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });
23492621 // TODO make sure the destination stack offset / register does not already have something
23502622 // going on there.
2351 try self.setValue(self.typeOfIndex(else_key), canon_mcv, else_value);
2623 try self.genCopy(self.typeOfIndex(else_key), canon_mcv, else_value);
23522624 // TODO track the new register / stack allocation
23532625 }
23542626 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
......@@ -2375,7 +2647,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
23752647 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });
23762648 // TODO make sure the destination stack offset / register does not already have something
23772649 // going on there.
2378 try self.setValue(self.typeOfIndex(then_key), parent_mcv, then_value);
2650 try self.genCopy(self.typeOfIndex(then_key), parent_mcv, then_value);
23792651 // TODO track the new register / stack allocation
23802652 }
23812653
......@@ -2638,7 +2910,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
26382910 if (block_mcv == .none) {
26392911 block_data.mcv = operand_mcv;
26402912 } else {
2641 try self.setValue(self.typeOfIndex(block), block_mcv, operand_mcv);
2913 try self.genCopy(self.typeOfIndex(block), block_mcv, operand_mcv);
26422914 }
26432915 }
26442916 return self.brVoid(block);
......@@ -2783,29 +3055,45 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT
27833055}
27843056
27853057/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
2786fn setValue(self: *Self, ty: Type, dst_val: MCValue, src_val: MCValue) !void {
3058fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
27873059 // There isn't anything to store
2788 if (dst_val == .none) return;
3060 if (dst_mcv == .none) return;
27893061
2790 if (!dst_val.isMutable()) {
3062 if (!dst_mcv.isMutable()) {
27913063 // panic so we can see the trace
2792 return std.debug.panic("tried to setValue immutable: {s}", .{@tagName(dst_val)});
3064 return std.debug.panic("tried to genCopy immutable: {s}", .{@tagName(dst_mcv)});
27933065 }
27943066
2795 switch (dst_val) {
2796 .register => |reg| return self.genSetReg(ty, reg, src_val),
2797 .stack_offset => |off| return self.genSetStack(ty, off, src_val),
2798 .memory => |addr| return self.genSetMem(ty, addr, src_val),
2799 else => return self.fail("TODO: setValue {s}", .{@tagName(dst_val)}),
3067 switch (dst_mcv) {
3068 .register => |reg| return self.genSetReg(ty, reg, src_mcv),
3069 .register_pair => |pair| return self.genSetRegPair(ty, pair, src_mcv),
3070 .register_offset => |dst_reg_off| try self.genSetReg(ty, dst_reg_off.reg, switch (src_mcv) {
3071 .none,
3072 .unreach,
3073 .dead,
3074 .undef,
3075 => unreachable,
3076 .immediate,
3077 .register,
3078 .register_offset,
3079 => src_mcv.offset(-dst_reg_off.off),
3080 else => .{ .register_offset = .{
3081 .reg = try self.copyToTmpRegister(ty, src_mcv),
3082 .off = -dst_reg_off.off,
3083 } },
3084 }),
3085 .stack_offset => |off| return self.genSetStack(ty, off, src_mcv),
3086 .memory => |addr| return self.genSetMem(ty, addr, src_mcv),
3087 else => return self.fail("TODO: genCopy {s} with {s}", .{ @tagName(dst_mcv), @tagName(src_mcv) }),
28003088 }
28013089}
28023090
2803/// Sets the value of `src_val` into stack memory at `stack_offset`.
2804fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) InnerError!void {
3091/// Sets the value of `src_mcv` into stack memory at `stack_offset`.
3092fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_mcv: MCValue) InnerError!void {
28053093 const mod = self.bin_file.comp.module.?;
28063094 const abi_size: u32 = @intCast(ty.abiSize(mod));
28073095
2808 switch (src_val) {
3096 switch (src_mcv) {
28093097 .none => return,
28103098 .dead => unreachable,
28113099 .undef => {
......@@ -2820,7 +3108,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
28203108 const reg, const reg_lock = try self.allocReg();
28213109 defer self.register_manager.unlockReg(reg_lock);
28223110
2823 try self.genSetReg(ty, reg, src_val);
3111 try self.genSetReg(ty, reg, src_mcv);
28243112
28253113 return self.genSetStack(ty, stack_offset, .{ .register = reg });
28263114 },
......@@ -2839,24 +3127,24 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
28393127 .tag = tag,
28403128 .data = .{ .i_type = .{
28413129 .rd = reg,
2842 .rs1 = .s0,
3130 .rs1 = .sp,
28433131 .imm12 = math.cast(i12, stack_offset) orelse {
28443132 return self.fail("TODO: genSetStack bigger stack values", .{});
28453133 },
28463134 } },
28473135 });
28483136 },
2849 else => return self.fail("TODO: genSetStack for size={d}", .{abi_size}),
3137 else => unreachable, // register can hold a max of 8 bytes
28503138 }
28513139 },
28523140 .stack_offset, .load_symbol => {
2853 switch (src_val) {
3141 switch (src_mcv) {
28543142 .stack_offset => |off| if (off == stack_offset) return,
28553143 else => {},
28563144 }
28573145
28583146 if (abi_size <= 8) {
2859 const reg = try self.copyToTmpRegister(ty, src_val);
3147 const reg = try self.copyToTmpRegister(ty, src_mcv);
28603148 return self.genSetStack(ty, stack_offset, .{ .register = reg });
28613149 }
28623150
......@@ -2875,7 +3163,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
28753163 const count_reg = regs[3];
28763164 const tmp_reg = regs[4];
28773165
2878 switch (src_val) {
3166 switch (src_mcv) {
28793167 .stack_offset => |offset| {
28803168 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = offset });
28813169 },
......@@ -2894,14 +3182,14 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
28943182 .tag = .load_symbol,
28953183 .data = .{
28963184 .payload = try self.addExtra(Mir.LoadSymbolPayload{
2897 .register = @intFromEnum(src_reg),
3185 .register = src_reg.id(),
28983186 .atom_index = atom_index,
28993187 .sym_index = sym_off.sym,
29003188 }),
29013189 },
29023190 });
29033191 },
2904 else => return self.fail("TODO: genSetStack unreachable {s}", .{@tagName(src_val)}),
3192 else => return self.fail("TODO: genSetStack unreachable {s}", .{@tagName(src_mcv)}),
29053193 }
29063194
29073195 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset });
......@@ -2910,16 +3198,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
29103198 // memcpy(src, dst, len)
29113199 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
29123200 },
2913 else => return self.fail("TODO: genSetStack {s}", .{@tagName(src_val)}),
3201 .air_ref => |ref| try self.genSetStack(ty, stack_offset, try self.resolveInst(ref)),
3202 else => return self.fail("TODO: genSetStack {s}", .{@tagName(src_mcv)}),
29143203 }
29153204}
29163205
2917fn genSetMem(self: *Self, ty: Type, addr: u64, src_val: MCValue) InnerError!void {
3206fn genSetMem(self: *Self, ty: Type, addr: u64, src_mcv: MCValue) InnerError!void {
29183207 const mod = self.bin_file.comp.module.?;
29193208 const abi_size: u32 = @intCast(ty.abiSize(mod));
29203209 _ = abi_size;
29213210 _ = addr;
2922 _ = src_val;
3211 _ = src_mcv;
29233212
29243213 return self.fail("TODO: genSetMem", .{});
29253214}
......@@ -2932,51 +3221,101 @@ fn genInlineMemcpy(
29323221 count: Register,
29333222 tmp: Register,
29343223) !void {
2935 _ = src;
2936 _ = dst;
3224 try self.genSetReg(Type.usize, count, .{ .register = len });
3225
3226 // lb tmp, 0(src)
3227 const first_inst = try self.addInst(.{
3228 .tag = .lb,
3229 .data = .{
3230 .i_type = .{
3231 .rd = tmp,
3232 .rs1 = src,
3233 .imm12 = 0,
3234 },
3235 },
3236 });
29373237
2938 // store 0 in the count
2939 try self.genSetReg(Type.usize, count, .{ .immediate = 0 });
3238 // sb tmp, 0(dst)
3239 _ = try self.addInst(.{
3240 .tag = .sb,
3241 .data = .{
3242 .i_type = .{
3243 .rd = tmp,
3244 .rs1 = dst,
3245 .imm12 = 0,
3246 },
3247 },
3248 });
29403249
2941 // compare count to length
2942 const compare_inst = try self.addInst(.{
2943 .tag = .cmp_eq,
2944 .data = .{ .r_type = .{
2945 .rd = tmp,
2946 .rs1 = count,
2947 .rs2 = len,
2948 } },
3250 // dec count by 1
3251 _ = try self.addInst(.{
3252 .tag = .addi,
3253 .data = .{
3254 .i_type = .{
3255 .rd = count,
3256 .rs1 = count,
3257 .imm12 = -1,
3258 },
3259 },
29493260 });
29503261
2951 // end if true
3262 // branch if count is 0
29523263 _ = try self.addInst(.{
2953 .tag = .bne,
3264 .tag = .beq,
29543265 .data = .{
29553266 .b_type = .{
2956 .inst = @intCast(self.mir_instructions.len + 0), // points after the last inst
2957 .rs1 = .zero,
2958 .rs2 = tmp,
3267 .inst = @intCast(self.mir_instructions.len + 4), // points after the last inst
3268 .rs1 = count,
3269 .rs2 = .zero,
3270 },
3271 },
3272 });
3273
3274 // increment the pointers
3275 _ = try self.addInst(.{
3276 .tag = .addi,
3277 .data = .{
3278 .i_type = .{
3279 .rd = src,
3280 .rs1 = src,
3281 .imm12 = 1,
3282 },
3283 },
3284 });
3285
3286 _ = try self.addInst(.{
3287 .tag = .addi,
3288 .data = .{
3289 .i_type = .{
3290 .rd = dst,
3291 .rs1 = dst,
3292 .imm12 = 1,
29593293 },
29603294 },
29613295 });
2962 _ = compare_inst;
29633296
2964 return self.fail("TODO: finish genInlineMemcpy", .{});
3297 // jump back to start of loop
3298 _ = try self.addInst(.{
3299 .tag = .j,
3300 .data = .{
3301 .inst = first_inst,
3302 },
3303 });
29653304}
29663305
2967/// Sets the value of `src_val` into `reg`. Assumes you have a lock on it.
2968fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!void {
3306/// Sets the value of `src_mcv` into `reg`. Assumes you have a lock on it.
3307fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!void {
29693308 const mod = self.bin_file.comp.module.?;
29703309 const abi_size: u32 = @intCast(ty.abiSize(mod));
29713310
2972 switch (src_val) {
3311 switch (src_mcv) {
29733312 .dead => unreachable,
29743313 .ptr_stack_offset => |off| {
29753314 _ = try self.addInst(.{
29763315 .tag = .addi,
29773316 .data = .{ .i_type = .{
29783317 .rd = reg,
2979 .rs1 = .s0,
3318 .rs1 = .sp,
29803319 .imm12 = math.cast(i12, off) orelse {
29813320 return self.fail("TODO: bigger stack sizes", .{});
29823321 },
......@@ -3006,7 +3345,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!
30063345 const carry: i32 = if (lo12 < 0) 1 else 0;
30073346 const hi20: i20 = @truncate((x >> 12) +% carry);
30083347
3009 // TODO: add test case for 32-bit immediate
30103348 _ = try self.addInst(.{
30113349 .tag = .lui,
30123350 .data = .{ .u_type = .{
......@@ -3069,6 +3407,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!
30693407 } },
30703408 });
30713409 },
3410 .register_pair => |pair| try self.genSetReg(ty, reg, .{ .register = pair[0] }),
30723411 .memory => |addr| {
30733412 try self.genSetReg(ty, reg, .{ .immediate = addr });
30743413
......@@ -3080,8 +3419,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!
30803419 .imm12 = 0,
30813420 } },
30823421 });
3083
3084 // LOAD imm=[i12 offset = 0], rs1
30853422 },
30863423 .stack_offset => |off| {
30873424 const tag: Mir.Inst.Tag = switch (abi_size) {
......@@ -3096,7 +3433,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!
30963433 .tag = tag,
30973434 .data = .{ .i_type = .{
30983435 .rd = reg,
3099 .rs1 = .s0,
3436 .rs1 = .sp,
31003437 .imm12 = math.cast(i12, off) orelse {
31013438 return self.fail("TODO: genSetReg support larger stack sizes", .{});
31023439 },
......@@ -3120,13 +3457,55 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!
31203457 .tag = .load_symbol,
31213458 .data = .{
31223459 .payload = try self.addExtra(Mir.LoadSymbolPayload{
3123 .register = @intFromEnum(reg),
3460 .register = reg.id(),
31243461 .atom_index = atom_index,
31253462 .sym_index = sym_off.sym,
31263463 }),
31273464 },
31283465 });
31293466 },
3467 .air_ref => |ref| try self.genSetReg(ty, reg, try self.resolveInst(ref)),
3468 .indirect => |reg_off| {
3469 const tag: Mir.Inst.Tag = switch (abi_size) {
3470 1 => .lb,
3471 2 => .lh,
3472 4 => .lw,
3473 8 => .ld,
3474 else => return self.fail("TODO: genSetReg for size {d}", .{abi_size}),
3475 };
3476
3477 _ = try self.addInst(.{
3478 .tag = tag,
3479 .data = .{
3480 .i_type = .{
3481 .rd = reg,
3482 .rs1 = reg_off.reg,
3483 .imm12 = @intCast(reg_off.off),
3484 },
3485 },
3486 });
3487 },
3488 else => return self.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}),
3489 }
3490}
3491
3492fn genSetRegPair(self: *Self, ty: Type, pair: [2]Register, src_mcv: MCValue) InnerError!void {
3493 const mod = self.bin_file.comp.module.?;
3494 const abi_size: u32 = @intCast(ty.abiSize(mod));
3495
3496 assert(abi_size > 8 and abi_size <= 16); // must fit only fit into two registers
3497
3498 switch (src_mcv) {
3499 .air_ref => |ref| return self.genSetRegPair(ty, pair, try self.resolveInst(ref)),
3500 .load_symbol => |sym_off| {
3501 _ = sym_off;
3502 // return self.fail("TODO: genSetRegPair load_symbol", .{});
3503 // commented out just for testing.
3504
3505 // plan here is to load the address into a temporary register and
3506 // copy into the pair.
3507 },
3508 else => return self.fail("TODO: genSetRegPair {s}", .{@tagName(src_mcv)}),
31303509 }
31313510}
31323511
......@@ -3138,7 +3517,7 @@ fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void {
31383517
31393518 const dst_mcv = try self.allocRegOrMem(inst, true);
31403519 const dst_ty = self.typeOfIndex(inst);
3141 try self.setValue(dst_ty, dst_mcv, src_mcv);
3520 try self.genCopy(dst_ty, dst_mcv, src_mcv);
31423521 break :result dst_mcv;
31433522 };
31443523 return self.finishAir(inst, result, .{ un_op, .none, .none });
......@@ -3157,7 +3536,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
31573536 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
31583537
31593538 const dest = try self.allocRegOrMem(inst, true);
3160 try self.setValue(self.typeOfIndex(inst), dest, operand);
3539 try self.genCopy(self.typeOfIndex(inst), dest, operand);
31613540 break :result dest;
31623541 };
31633542 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
......@@ -3385,8 +3764,12 @@ const CallMCValues = struct {
33853764};
33863765
33873766/// Caller must call `CallMCValues.deinit`.
3388fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
3767fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: CallView) !CallMCValues {
33893768 const mod = self.bin_file.comp.module.?;
3769 const ip = &mod.intern_pool;
3770
3771 _ = role;
3772
33903773 const fn_info = mod.typeToFunc(fn_ty).?;
33913774 const cc = fn_info.cc;
33923775 var result: CallMCValues = .{
......@@ -3413,26 +3796,31 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
34133796 return self.fail("TODO: support more than 8 function args", .{});
34143797 }
34153798
3416 const locks = try self.gpa.alloc(RegisterLock, result.args.len);
3417 defer self.gpa.free(locks);
3799 var fa_reg_i: u32 = 0;
34183800
3419 for (0..result.args.len) |i| {
3420 const arg_reg = try self.register_manager.allocReg(null, fa);
3421 const lock = self.register_manager.lockRegAssumeUnused(arg_reg);
3422 locks[i] = lock;
3423 result.args[i] = .{ .register = arg_reg };
3424 }
3801 // spill the needed argument registers
3802 for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| {
3803 const param_ty = Type.fromInterned(ty);
3804 const param_size = param_ty.abiSize(mod);
34253805
3426 // we can just free the locks now, as this should be the only place where the fa
3427 // arg set is used.
3428 for (locks) |lock| {
3429 self.register_manager.unlockReg(lock);
3806 switch (param_size) {
3807 1...8 => {
3808 const arg_reg: Register = abi.function_arg_regs[fa_reg_i];
3809 fa_reg_i += 1;
3810 try self.register_manager.getReg(arg_reg, null);
3811 result_arg.* = .{ .register = arg_reg };
3812 },
3813 9...16 => {
3814 const arg_regs: [2]Register = abi.function_arg_regs[fa_reg_i..][0..2].*;
3815 fa_reg_i += 2;
3816 for (arg_regs) |reg| try self.register_manager.getReg(reg, null);
3817 result_arg.* = .{ .register_pair = arg_regs };
3818 },
3819 else => return self.fail("TODO: support args of size {}", .{param_size}),
3820 }
34303821 }
34313822
3432 // stack_offset = num s registers spilled + local var space
3433 // TODO: spill used s registers here
3434
3435 result.stack_byte_count = 0;
3823 result.stack_byte_count = self.max_end_stack;
34363824 result.stack_align = .@"16";
34373825 },
34383826 else => return self.fail("TODO implement function parameters for {} on riscv64", .{cc}),
src/arch/riscv64/Emit.zig+56-48
......@@ -1,19 +1,6 @@
11//! This file contains the functionality for lowering RISCV64 MIR into
22//! machine code
33
4const Emit = @This();
5const std = @import("std");
6const math = std.math;
7const Mir = @import("Mir.zig");
8const bits = @import("bits.zig");
9const link = @import("../../link.zig");
10const Module = @import("../../Module.zig");
11const ErrorMsg = Module.ErrorMsg;
12const assert = std.debug.assert;
13const Instruction = bits.Instruction;
14const Register = bits.Register;
15const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
16
174mir: Mir,
185bin_file: *link.File,
196debug_output: DebugInfoOutput,
......@@ -22,12 +9,17 @@ err_msg: ?*ErrorMsg = null,
229src_loc: Module.SrcLoc,
2310code: *std.ArrayList(u8),
2411
12/// List of registers to save in the prologue.
13save_reg_list: Mir.RegisterList,
14
2515prev_di_line: u32,
2616prev_di_column: u32,
2717/// Relative to the beginning of `code`.
2818prev_di_pc: usize,
19
2920/// Function's stack size. Used for backpatching.
3021stack_size: u32,
22
3123/// For backward branches: stores the code offset of the target
3224/// instruction
3325///
......@@ -212,7 +204,7 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
212204 // rs1 != rs2
213205
214206 try emit.writeInstruction(Instruction.xor(rd, rs1, rs2));
215 try emit.writeInstruction(Instruction.sltu(rd, .x0, rd)); // snez
207 try emit.writeInstruction(Instruction.sltu(rd, .zero, rd)); // snez
216208 },
217209 .cmp_lt => {
218210 // rd = 1 if rs1 < rs2
......@@ -368,17 +360,20 @@ fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void {
368360 return emit.fail("TODO: mirPsuedo support larger stack sizes", .{});
369361 };
370362
371 // Decrement sp by num s registers + local var space
363 // Decrement sp by (num s registers * 8) + local var space
372364 try emit.writeInstruction(Instruction.addi(.sp, .sp, -stack_size));
373365
374366 // Spill ra
375 try emit.writeInstruction(Instruction.sd(.ra, stack_size - 8, .sp));
376
377 // Spill s0
378 try emit.writeInstruction(Instruction.sd(.s0, stack_size - 16, .sp));
379
380 // Setup s0
381 try emit.writeInstruction(Instruction.addi(.s0, .sp, stack_size));
367 try emit.writeInstruction(Instruction.sd(.ra, 0, .sp));
368
369 // Spill callee saved registers.
370 var s_reg_iter = emit.save_reg_list.iterator(.{});
371 var i: i12 = 8;
372 while (s_reg_iter.next()) |reg_i| {
373 const reg = abi.callee_preserved_regs[reg_i];
374 try emit.writeInstruction(Instruction.sd(reg, i, .sp));
375 i += 8;
376 }
382377 },
383378 .psuedo_epilogue => {
384379 const stack_size: i12 = math.cast(i12, emit.stack_size) orelse {
......@@ -386,10 +381,16 @@ fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void {
386381 };
387382
388383 // Restore ra
389 try emit.writeInstruction(Instruction.ld(.ra, stack_size - 8, .sp));
390
391 // Restore s0
392 try emit.writeInstruction(Instruction.ld(.s0, stack_size - 16, .sp));
384 try emit.writeInstruction(Instruction.ld(.ra, 0, .sp));
385
386 // Restore spilled callee saved registers
387 var s_reg_iter = emit.save_reg_list.iterator(.{});
388 var i: i12 = 8;
389 while (s_reg_iter.next()) |reg_i| {
390 const reg = abi.callee_preserved_regs[reg_i];
391 try emit.writeInstruction(Instruction.ld(reg, i, .sp));
392 i += 8;
393 }
393394
394395 // Increment sp back to previous value
395396 try emit.writeInstruction(Instruction.addi(.sp, .sp, stack_size));
......@@ -408,8 +409,11 @@ fn mirRR(emit: *Emit, inst: Mir.Inst.Index) !void {
408409 const tag = emit.mir.instructions.items(.tag)[inst];
409410 const rr = emit.mir.instructions.items(.data)[inst].rr;
410411
412 const rd = rr.rd;
413 const rs = rr.rs;
414
411415 switch (tag) {
412 .mv => try emit.writeInstruction(Instruction.addi(rr.rd, rr.rs, 0)),
416 .mv => try emit.writeInstruction(Instruction.addi(rd, rs, 0)),
413417 else => unreachable,
414418 }
415419}
......@@ -435,7 +439,6 @@ fn mirNop(emit: *Emit, inst: Mir.Inst.Index) !void {
435439}
436440
437441fn mirLoadSymbol(emit: *Emit, inst: Mir.Inst.Index) !void {
438 // const tag = emit.mir.instructions.items(.tag)[inst];
439442 const payload = emit.mir.instructions.items(.data)[inst].payload;
440443 const data = emit.mir.extraData(Mir.LoadSymbolPayload, payload).data;
441444 const reg = @as(Register, @enumFromInt(data.register));
......@@ -523,20 +526,19 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
523526 .dbg_prologue_end,
524527 => 0,
525528
526 .psuedo_prologue,
527 => 16,
528
529 .psuedo_epilogue,
530 .abs,
531 => 12,
532
533529 .cmp_eq,
534530 .cmp_neq,
535531 .cmp_imm_eq,
536532 .cmp_gte,
537533 .load_symbol,
534 .abs,
538535 => 8,
539536
537 .psuedo_epilogue, .psuedo_prologue => size: {
538 const count = emit.save_reg_list.count() * 4;
539 break :size count + 8;
540 },
541
540542 else => 4,
541543 };
542544}
......@@ -547,25 +549,17 @@ fn lowerMir(emit: *Emit) !void {
547549 const mir_tags = emit.mir.instructions.items(.tag);
548550 const mir_datas = emit.mir.instructions.items(.data);
549551
552 const proglogue_size: u32 = @intCast(emit.save_reg_list.size());
553 emit.stack_size += proglogue_size;
554
550555 for (mir_tags, 0..) |tag, index| {
551556 const inst: u32 = @intCast(index);
552557
553558 if (isStore(tag) or isLoad(tag)) {
554559 const data = mir_datas[inst].i_type;
555 // TODO: probably create a psuedo instruction for s0 loads/stores instead of this.
556 if (data.rs1 == .s0) {
560 if (data.rs1 == .sp) {
557561 const offset = mir_datas[inst].i_type.imm12;
558
559 // sp + 32 (aka s0)
560 // ra -- previous ra spilled
561 // s0 -- previous s0 spilled
562 // --- this is -16(s0)
563
564 // TODO: this "+ 8" is completely arbiratary as the largest possible store
565 // we don't want to actually use it. instead we need to calculate the difference
566 // between the first and second stack store and use it instead.
567
568 mir_datas[inst].i_type.imm12 = -(16 + offset + 8);
562 mir_datas[inst].i_type.imm12 = offset + @as(i12, @intCast(proglogue_size)) + 8;
569563 }
570564 }
571565
......@@ -584,3 +578,17 @@ fn lowerMir(emit: *Emit) !void {
584578 current_code_offset += emit.instructionSize(inst);
585579 }
586580}
581
582const Emit = @This();
583const std = @import("std");
584const math = std.math;
585const Mir = @import("Mir.zig");
586const bits = @import("bits.zig");
587const abi = @import("abi.zig");
588const link = @import("../../link.zig");
589const Module = @import("../../Module.zig");
590const ErrorMsg = Module.ErrorMsg;
591const assert = std.debug.assert;
592const Instruction = bits.Instruction;
593const Register = bits.Register;
594const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
src/arch/riscv64/Mir.zig+47-18
......@@ -6,14 +6,6 @@
66//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
77//! so that, for example, the smaller encodings of jump instructions can be used.
88
9const Mir = @This();
10const std = @import("std");
11const builtin = @import("builtin");
12const assert = std.debug.assert;
13
14const bits = @import("bits.zig");
15const Register = bits.Register;
16
179instructions: std.MultiArrayList(Inst).Slice,
1810/// The meaning of this data is determined by `Inst.Tag` value.
1911extra: []const u32,
......@@ -58,7 +50,7 @@ pub const Inst = struct {
5850 /// Jumps. Uses `inst` payload.
5951 j,
6052
61 /// Immediate and, uses i_type payload
53 /// Immediate AND, uses i_type payload
6254 andi,
6355
6456 // NOTE: Maybe create a special data for compares that includes the ops
......@@ -219,15 +211,6 @@ pub const Inst = struct {
219211 },
220212 };
221213
222 const CompareOp = enum {
223 eq,
224 neq,
225 gt,
226 gte,
227 lt,
228 lte,
229 };
230
231214 // Make sure we don't accidentally make instructions bigger than expected.
232215 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
233216 // comptime {
......@@ -268,3 +251,49 @@ pub const LoadSymbolPayload = struct {
268251 atom_index: u32,
269252 sym_index: u32,
270253};
254
255/// Used in conjunction with payload to transfer a list of used registers in a compact manner.
256pub const RegisterList = struct {
257 bitset: BitSet = BitSet.initEmpty(),
258
259 const BitSet = IntegerBitSet(32);
260 const Self = @This();
261
262 fn getIndexForReg(registers: []const Register, reg: Register) BitSet.MaskInt {
263 for (registers, 0..) |cpreg, i| {
264 if (reg.id() == cpreg.id()) return @intCast(i);
265 }
266 unreachable; // register not in input register list!
267 }
268
269 pub fn push(self: *Self, registers: []const Register, reg: Register) void {
270 const index = getIndexForReg(registers, reg);
271 self.bitset.set(index);
272 }
273
274 pub fn isSet(self: Self, registers: []const Register, reg: Register) bool {
275 const index = getIndexForReg(registers, reg);
276 return self.bitset.isSet(index);
277 }
278
279 pub fn iterator(self: Self, comptime options: std.bit_set.IteratorOptions) BitSet.Iterator(options) {
280 return self.bitset.iterator(options);
281 }
282
283 pub fn count(self: Self) u32 {
284 return @intCast(self.bitset.count());
285 }
286
287 pub fn size(self: Self) u32 {
288 return @intCast(self.bitset.count() * 8);
289 }
290};
291
292const Mir = @This();
293const std = @import("std");
294const builtin = @import("builtin");
295const assert = std.debug.assert;
296
297const bits = @import("bits.zig");
298const Register = bits.Register;
299const IntegerBitSet = std.bit_set.IntegerBitSet;
src/arch/riscv64/abi.zig+15-3
......@@ -92,15 +92,18 @@ pub fn classifyType(ty: Type, mod: *Module) Class {
9292}
9393
9494pub const callee_preserved_regs = [_]Register{
95 // NOTE: we use s0 as a psuedo stack pointer, so it's not included.
96 .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
95 .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
9796};
9897
9998pub const function_arg_regs = [_]Register{
10099 .a0, .a1, .a2, .a3, .a4, .a5, .a6, .a7,
101100};
102101
103const allocatable_registers = callee_preserved_regs ++ function_arg_regs;
102pub const temporary_regs = [_]Register{
103 .t0, .t1, .t2, .t3, .t4, .t5, .t6,
104};
105
106const allocatable_registers = callee_preserved_regs ++ function_arg_regs ++ temporary_regs;
104107pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);
105108
106109// Register classes
......@@ -123,4 +126,13 @@ pub const RegisterClass = struct {
123126 }, true);
124127 break :blk set;
125128 };
129
130 pub const tp: RegisterBitSet = blk: {
131 var set = RegisterBitSet.initEmpty();
132 set.setRangeValue(.{
133 .start = callee_preserved_regs.len + function_arg_regs.len,
134 .end = callee_preserved_regs.len + function_arg_regs.len + temporary_regs.len,
135 }, true);
136 break :blk set;
137 };
126138};
src/arch/riscv64/bits.zig+2-2
......@@ -404,14 +404,14 @@ pub const Register = enum(u6) {
404404 t3, t4, t5, t6, // caller saved
405405 // zig fmt: on
406406
407 /// Returns the unique 4-bit ID of this register which is used in
407 /// Returns the unique 5-bit ID of this register which is used in
408408 /// the machine code
409409 pub fn id(self: Register) u5 {
410410 return @as(u5, @truncate(@intFromEnum(self)));
411411 }
412412
413413 pub fn dwarfLocOp(reg: Register) u8 {
414 return @as(u8, reg.id()) + DW.OP.reg0;
414 return @as(u8, reg.id());
415415 }
416416};
417417
src/register_manager.zig+1-1
......@@ -102,7 +102,7 @@ pub fn RegisterManager(
102102 }
103103
104104 const OptionalIndex = std.math.IntFittingRange(0, set.len);
105 comptime var map = [1]OptionalIndex{set.len} ** (max_id + 1 - min_id);
105 comptime var map = [1]OptionalIndex{set.len} ** (max_id - min_id + 1);
106106 inline for (set, 0..) |elem, elem_index| map[comptime elem.id() - min_id] = elem_index;
107107
108108 const id_index = reg.id() -% min_id;