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...@@ -775,14 +775,14 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
775 }775 }
776776
777 if (builtin.zig_backend == .stage2_riscv64) {777 if (builtin.zig_backend == .stage2_riscv64) {
778 asm volatile ("ecall"778 // asm volatile ("ecall"
779 :779 // :
780 : [number] "{a7}" (64),780 // : [number] "{a7}" (64),
781 [arg1] "{a0}" (1),781 // [arg1] "{a0}" (1),
782 [arg2] "{a1}" (@intFromPtr(msg.ptr)),782 // [arg2] "{a1}" (@intFromPtr(msg.ptr)),
783 [arg3] "{a2}" (msg.len),783 // [arg3] "{a2}" (msg.len),
784 : "rcx", "r11", "memory"784 // : "rcx", "r11", "memory"
785 );785 // );
786 std.posix.exit(127);786 std.posix.exit(127);
787 }787 }
788788
src/arch/riscv64/CodeGen.zig+618-230
...@@ -38,9 +38,16 @@ const callee_preserved_regs = abi.callee_preserved_regs;...@@ -38,9 +38,16 @@ const callee_preserved_regs = abi.callee_preserved_regs;
38const gp = abi.RegisterClass.gp;38const gp = abi.RegisterClass.gp;
39/// Function Args39/// Function Args
40const fa = abi.RegisterClass.fa;40const fa = abi.RegisterClass.fa;
41/// Temporary Use
42const tp = abi.RegisterClass.tp;
4143
42const InnerError = CodeGenError || error{OutOfRegisters};44const InnerError = CodeGenError || error{OutOfRegisters};
4345
46const RegisterView = enum(u1) {
47 caller,
48 callee,
49};
50
44gpa: Allocator,51gpa: Allocator,
45air: Air,52air: Air,
46liveness: Liveness,53liveness: Liveness,
...@@ -82,8 +89,8 @@ branch_stack: *std.ArrayList(Branch),...@@ -82,8 +89,8 @@ branch_stack: *std.ArrayList(Branch),
8289
83// Key is the block instruction90// Key is the block instruction
84blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},91blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
85
86register_manager: RegisterManager = .{},92register_manager: RegisterManager = .{},
93
87/// Maps offset to what is stored there.94/// Maps offset to what is stored there.
88stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},95stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
8996
...@@ -99,6 +106,7 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,...@@ -99,6 +106,7 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
99const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};106const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
100107
101const SymbolOffset = struct { sym: u32, off: i32 = 0 };108const SymbolOffset = struct { sym: u32, off: i32 = 0 };
109const RegisterOffset = struct { reg: Register, off: i32 = 0 };
102110
103const MCValue = union(enum) {111const MCValue = union(enum) {
104 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.112 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
...@@ -119,6 +127,8 @@ const MCValue = union(enum) {...@@ -119,6 +127,8 @@ const MCValue = union(enum) {
119 load_symbol: SymbolOffset,127 load_symbol: SymbolOffset,
120 /// The value is in a target-specific register.128 /// The value is in a target-specific register.
121 register: Register,129 register: Register,
130 /// The value is split across two registers
131 register_pair: [2]Register,
122 /// The value is in memory at a hard-coded address.132 /// The value is in memory at a hard-coded address.
123 /// If the type is a pointer, it means the pointer address is at this memory location.133 /// If the type is a pointer, it means the pointer address is at this memory location.
124 memory: u64,134 memory: u64,
...@@ -127,10 +137,15 @@ const MCValue = union(enum) {...@@ -127,10 +137,15 @@ const MCValue = union(enum) {
127 stack_offset: u32,137 stack_offset: u32,
128 /// The value is a pointer to one of the stack variables (payload is stack offset).138 /// The value is a pointer to one of the stack variables (payload is stack offset).
129 ptr_stack_offset: u32,139 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
131 fn isMemory(mcv: MCValue) bool {146 fn isMemory(mcv: MCValue) bool {
132 return switch (mcv) {147 return switch (mcv) {
133 .memory, .stack_offset => true,148 .memory, .indirect, .load_frame => true,
134 else => false,149 else => false,
135 };150 };
136 }151 }
...@@ -151,15 +166,85 @@ const MCValue = union(enum) {...@@ -151,15 +166,85 @@ const MCValue = union(enum) {
151 .immediate,166 .immediate,
152 .memory,167 .memory,
153 .ptr_stack_offset,168 .ptr_stack_offset,
169 .indirect,
154 .undef,170 .undef,
155 .load_symbol,171 .load_symbol,
172 .air_ref,
156 => false,173 => false,
157174
158 .register,175 .register,
176 .register_pair,
177 .register_offset,
159 .stack_offset,178 .stack_offset,
160 => true,179 => true,
161 };180 };
162 }181 }
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 }
163};248};
164249
165const Branch = struct {250const Branch = struct {
...@@ -211,6 +296,11 @@ const BigTomb = struct {...@@ -211,6 +296,11 @@ const BigTomb = struct {
211296
212const Self = @This();297const Self = @This();
213298
299const CallView = enum(u1) {
300 callee,
301 caller,
302};
303
214pub fn generate(304pub fn generate(
215 lf: *link.File,305 lf: *link.File,
216 src_loc: Module.SrcLoc,306 src_loc: Module.SrcLoc,
...@@ -261,7 +351,7 @@ pub fn generate(...@@ -261,7 +351,7 @@ pub fn generate(
261 defer function.blocks.deinit(gpa);351 defer function.blocks.deinit(gpa);
262 defer function.exitlude_jump_relocs.deinit(gpa);352 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) {
265 error.CodegenFail => return Result{ .fail = function.err_msg.? },355 error.CodegenFail => return Result{ .fail = function.err_msg.? },
266 error.OutOfRegisters => return Result{356 error.OutOfRegisters => return Result{
267 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),357 .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(...@@ -284,6 +374,14 @@ pub fn generate(
284 else => |e| return e,374 else => |e| return e,
285 };375 };
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
287 var mir = Mir{385 var mir = Mir{
288 .instructions = function.mir_instructions.toOwnedSlice(),386 .instructions = function.mir_instructions.toOwnedSlice(),
289 .extra = try function.mir_extra.toOwnedSlice(gpa),387 .extra = try function.mir_extra.toOwnedSlice(gpa),
...@@ -300,8 +398,10 @@ pub fn generate(...@@ -300,8 +398,10 @@ pub fn generate(
300 .prev_di_pc = 0,398 .prev_di_pc = 0,
301 .prev_di_line = func.lbrace_line,399 .prev_di_line = func.lbrace_line,
302 .prev_di_column = func.lbrace_column,400 .prev_di_column = func.lbrace_column,
303 .stack_size = @max(32, function.max_end_stack),
304 .code_offset_mapping = .{},401 .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,
305 };405 };
306 defer emit.deinit();406 defer emit.deinit();
307407
...@@ -629,6 +729,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -629,6 +729,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
629 }729 }
630}730}
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
632/// Asserts there is already capacity to insert into top branch inst_table.736/// Asserts there is already capacity to insert into top branch inst_table.
633fn processDeath(self: *Self, inst: Air.Inst.Index) void {737fn processDeath(self: *Self, inst: Air.Inst.Index) void {
634 // When editing this function, note that the logic must synchronize with `reuseOperand`.738 // 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 {...@@ -639,7 +743,7 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
639 .register => |reg| {743 .register => |reg| {
640 self.register_manager.freeReg(reg);744 self.register_manager.freeReg(reg);
641 },745 },
642 else => {}, // TODO process stack allocation death746 else => {}, // TODO process stack allocation death by freeing it to be reused later
643 }747 }
644}748}
645749
...@@ -650,17 +754,11 @@ fn finishAirBookkeeping(self: *Self) void {...@@ -650,17 +754,11 @@ fn finishAirBookkeeping(self: *Self) void {
650 }754 }
651}755}
652756
653fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {757fn finishAirResult(self: *Self, inst: Air.Inst.Index, result: MCValue) void {
654 var tomb_bits = self.liveness.getTombBits(inst);758 if (self.liveness.isUnused(inst)) switch (result) {
655 for (operands) |op| {759 .none, .dead, .unreach => {},
656 const dies = @as(u1, @truncate(tomb_bits)) != 0;760 else => unreachable, // Why didn't the result die?
657 tomb_bits >>= 1;761 } else {
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) {
664 log.debug("%{d} => {}", .{ inst, result });762 log.debug("%{d} => {}", .{ inst, result });
665 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];763 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
666 branch.inst_table.putAssumeCapacityNoClobber(inst, result);764 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
...@@ -682,6 +780,22 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live...@@ -682,6 +780,22 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
682 self.finishAirBookkeeping();780 self.finishAirBookkeeping();
683}781}
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
685fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {799fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
686 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;800 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
687 try table.ensureUnusedCapacity(self.gpa, additional_count);801 try table.ensureUnusedCapacity(self.gpa, additional_count);
...@@ -716,6 +830,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {...@@ -716,6 +830,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
716fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {830fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
717 const mod = self.bin_file.comp.module.?;831 const mod = self.bin_file.comp.module.?;
718 const elem_ty = self.typeOfIndex(inst);832 const elem_ty = self.typeOfIndex(inst);
833
719 const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse {834 const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse {
720 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});835 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
721 };836 };
...@@ -728,12 +843,12 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {...@@ -728,12 +843,12 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
728 const ptr_bytes: u64 = @divExact(ptr_bits, 8);843 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
729 if (abi_size <= ptr_bytes) {844 if (abi_size <= ptr_bytes) {
730 if (self.register_manager.tryAllocReg(inst, gp)) |reg| {845 if (self.register_manager.tryAllocReg(inst, gp)) |reg| {
731 return MCValue{ .register = reg };846 return .{ .register = reg };
732 }847 }
733 }848 }
734 }849 }
735 const stack_offset = try self.allocMem(inst, abi_size, abi_align);850 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
736 return MCValue{ .stack_offset = stack_offset };851 return .{ .stack_offset = stack_offset };
737}852}
738853
739/// Allocates a register from the general purpose set and returns the Register and the Lock.854/// 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 } {...@@ -746,6 +861,12 @@ fn allocReg(self: *Self) !struct { Register, RegisterLock } {
746}861}
747862
748pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {863pub 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
749 const stack_mcv = try self.allocRegOrMem(inst, false);870 const stack_mcv = try self.allocRegOrMem(inst, false);
750 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });871 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
751 const reg_mcv = self.getResolvedInstValue(inst);872 const reg_mcv = self.getResolvedInstValue(inst);
...@@ -759,7 +880,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void...@@ -759,7 +880,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
759/// allocated. A second call to `copyToTmpRegister` may return the same register.880/// allocated. A second call to `copyToTmpRegister` may return the same register.
760/// This can have a side effect of spilling instructions to the stack to free up a register.881/// This can have a side effect of spilling instructions to the stack to free up a register.
761fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {882fn 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);
763 try self.genSetReg(ty, reg, mcv);884 try self.genSetReg(ty, reg, mcv);
764 return reg;885 return reg;
765}886}
...@@ -830,7 +951,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {...@@ -830,7 +951,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
830 math.divCeil(u32, src_storage_bits, 64) catch unreachable and951 math.divCeil(u32, src_storage_bits, 64) catch unreachable and
831 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: {952 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: {
832 const dst_mcv = try self.allocRegOrMem(inst, true);953 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);
834 break :dst dst_mcv;955 break :dst dst_mcv;
835 };956 };
836957
...@@ -1261,43 +1382,48 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1261,43 +1382,48 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
12611382
1262 if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) {1383 if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) {
1263 if (int_info.signedness == .unsigned) {1384 if (int_info.signedness == .unsigned) {
1264 const overflow_offset = tuple_ty.structFieldOffset(1, mod) + offset;1385 switch (int_info.bits) {
12651386 1...8 => {
1266 const max_val = std.math.pow(u16, 2, int_info.bits) - 1;1387 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);
12701388
1271 const add_reg, const add_lock = blk: {1389 const overflow_reg, const overflow_lock = try self.allocReg();
1272 if (add_result_mcv == .register) break :blk .{ add_result_mcv.register, null };1390 defer self.register_manager.unlockReg(overflow_lock);
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 });
12881391
1289 const overflow_mcv = try self.binOp(1392 const add_reg, const add_lock = blk: {
1290 .cmp_neq,1393 if (add_result_mcv == .register) break :blk .{ add_result_mcv.register, null };
1291 null,
1292 .{ .register = overflow_reg },
1293 .{ .register = add_reg },
1294 lhs_ty,
1295 lhs_ty,
1296 );
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 }
1301 } else {1427 } else {
1302 return self.fail("TODO: airAddWithOverFlow calculate carry for signed addition", .{});1428 return self.fail("TODO: airAddWithOverFlow calculate carry for signed addition", .{});
1303 }1429 }
...@@ -1367,6 +1493,7 @@ fn airShl(self: *Self, inst: Air.Inst.Index) !void {...@@ -1367,6 +1493,7 @@ fn airShl(self: *Self, inst: Air.Inst.Index) !void {
1367 const rhs = try self.resolveInst(bin_op.rhs);1493 const rhs = try self.resolveInst(bin_op.rhs);
1368 const lhs_ty = self.typeOf(bin_op.lhs);1494 const lhs_ty = self.typeOf(bin_op.lhs);
1369 const rhs_ty = self.typeOf(bin_op.rhs);1495 const rhs_ty = self.typeOf(bin_op.rhs);
1496
1370 break :result try self.binOp(.shl, inst, lhs, rhs, lhs_ty, rhs_ty);1497 break :result try self.binOp(.shl, inst, lhs, rhs, lhs_ty, rhs_ty);
1371 };1498 };
1372 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1499 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
...@@ -1506,7 +1633,19 @@ fn slicePtr(self: *Self, mcv: MCValue) !MCValue {...@@ -1506,7 +1633,19 @@ fn slicePtr(self: *Self, mcv: MCValue) !MCValue {
15061633
1507fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {1634fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
1508 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1635 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 };
1510 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1649 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1511}1650}
15121651
...@@ -1598,10 +1737,60 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {...@@ -1598,10 +1737,60 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void {
15981737
1599fn airCtz(self: *Self, inst: Air.Inst.Index) !void {1738fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
1600 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1739 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 };
1602 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1761 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1603}1762}
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
1605fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {1794fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
1606 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;1795 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
1607 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});1796 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 {...@@ -1750,12 +1939,12 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
1750 const elem_ty = self.typeOfIndex(inst);1939 const elem_ty = self.typeOfIndex(inst);
1751 const result: MCValue = result: {1940 const result: MCValue = result: {
1752 if (!elem_ty.hasRuntimeBits(mod))1941 if (!elem_ty.hasRuntimeBits(mod))
1753 break :result MCValue.none;1942 break :result .none;
17541943
1755 const ptr = try self.resolveInst(ty_op.operand);1944 const ptr = try self.resolveInst(ty_op.operand);
1756 const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(mod);1945 const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(mod);
1757 if (self.liveness.isUnused(inst) and !is_volatile)1946 if (self.liveness.isUnused(inst) and !is_volatile)
1758 break :result MCValue.dead;1947 break :result .dead;
17591948
1760 const dst_mcv: MCValue = blk: {1949 const dst_mcv: MCValue = blk: {
1761 if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {1950 if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
...@@ -1771,27 +1960,38 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -1771,27 +1960,38 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
1771 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1960 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1772}1961}
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 {
1775 const mod = self.bin_file.comp.module.?;1964 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) {1967 log.debug("loading {}:{} into {}", .{ ptr_mcv, ptr_ty.fmt(mod), dst_mcv });
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 }),
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,
1787 .register,1978 .register,
1979 .register_offset,
1980 .ptr_stack_offset,
1981 => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()),
1982
1788 .memory,1983 .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 => {1992 try self.genCopy(dst_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } });
1792 const reg = try self.copyToTmpRegister(ptr_ty, src_ptr);
1793 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
1794 },1993 },
1994 .air_ref => |ptr_ref| try self.load(dst_mcv, try self.resolveInst(ptr_ref), ptr_ty),
1795 }1995 }
1796}1996}
17971997
...@@ -1817,7 +2017,12 @@ fn store(self: *Self, pointer: MCValue, value: MCValue, ptr_ty: Type, value_ty:...@@ -1817,7 +2017,12 @@ fn store(self: *Self, pointer: MCValue, value: MCValue, ptr_ty: Type, value_ty:
1817 const mod = self.bin_file.comp.module.?;2017 const mod = self.bin_file.comp.module.?;
1818 const value_abi_size = value_ty.abiSize(mod);2018 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
1822 switch (pointer) {2027 switch (pointer) {
1823 .none => unreachable,2028 .none => unreachable,
...@@ -1976,7 +2181,6 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {...@@ -1976,7 +2181,6 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
1976}2181}
19772182
1978fn airArg(self: *Self, inst: Air.Inst.Index) !void {2183fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1979 const mod = self.bin_file.comp.module.?;
1980 var arg_index = self.arg_index;2184 var arg_index = self.arg_index;
19812185
1982 // we skip over args that have no bits2186 // we skip over args that have no bits
...@@ -1986,21 +2190,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -1986,21 +2190,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1986 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {2190 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
1987 const src_mcv = self.args[arg_index];2191 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.
1992 const dst_mcv = switch (src_mcv) {2193 const dst_mcv = switch (src_mcv) {
1993 .register => |src_reg| dst: {2194 .register => |src_reg| dst: {
1994 // TODO: get the true type of the arg, and fit the spill to size.2195 try self.register_manager.getReg(src_reg, null);
1995 const arg_size = Type.usize.abiSize(mod);2196 break :dst src_mcv;
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 };
2004 },2197 },
2005 else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}),2198 else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}),
2006 };2199 };
...@@ -2044,87 +2237,122 @@ fn airFence(self: *Self) !void {...@@ -2044,87 +2237,122 @@ fn airFence(self: *Self) !void {
2044}2237}
20452238
2046fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {2239fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
2047 const mod = self.bin_file.comp.module.?;
2048 if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{});2240 if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{});
2049 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;2241 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
2050 const fn_ty = self.typeOf(pl_op.operand);
2051 const callee = pl_op.operand;2242 const callee = pl_op.operand;
2052 const extra = self.air.extraData(Air.Call, pl_op.payload);2243 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);2304 var call_info = try self.resolveCallingConventionValues(fn_ty, .caller);
2056 defer info.deinit(self);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
2058 // Due to incremental compilation, how function calls are generated depends2309 // Due to incremental compilation, how function calls are generated depends
2059 // on linking.2310 // on linking.
2060 if (self.bin_file.cast(link.File.Elf)) |elf_file| {2311 switch (info) {
2061 for (info.args, 0..) |mc_arg, arg_i| {2312 .air => |callee| if (try self.air.value(callee, mod)) |func_value| {
2062 const arg = args[arg_i];2313 const func_key = mod.intern_pool.indexToKey(func_value.ip_index);
2063 const arg_ty = self.typeOf(arg);2314 switch (switch (func_key) {
2064 const arg_mcv = try self.resolveInst(args[arg_i]);2315 else => func_key,
2065 try self.setValue(arg_ty, mc_arg, arg_mcv);2316 .ptr => |ptr| switch (ptr.addr) {
2066 }2317 .decl => |decl| mod.intern_pool.indexToKey(mod.declPtr(decl).val.toIntern()),
20672318 else => func_key,
2068 if (try self.air.value(callee, mod)) |func_value| {2319 },
2069 switch (mod.intern_pool.indexToKey(func_value.ip_index)) {2320 }) {
2070 .func => |func| {2321 .func => |func| {
2071 const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);2322 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
2072 const sym = elf_file.symbol(sym_index);2323 const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
2073 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);2324 const sym = elf_file.symbol(sym_index);
2074 const got_addr = sym.zigGotAddress(elf_file);2325 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
2075 try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr });2326 const got_addr = sym.zigGotAddress(elf_file);
2076 _ = try self.addInst(.{2327 try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr });
2077 .tag = .jalr,2328 _ = try self.addInst(.{
2078 .data = .{ .i_type = .{2329 .tag = .jalr,
2079 .rd = .ra,2330 .data = .{ .i_type = .{
2080 .rs1 = .ra,2331 .rd = .ra,
2081 .imm12 = 0,2332 .rs1 = .ra,
2082 } },2333 .imm12 = 0,
2083 });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;
2084 },2343 },
2085 .extern_func => {2344 .extern_func => {
2086 return self.fail("TODO implement calling extern functions", .{});2345 return self.fail("TODO: extern func calls", .{});
2087 },
2088 else => {
2089 return self.fail("TODO implement calling bitcasted functions", .{});
2090 },2346 },
2347 else => return self.fail("TODO implement calling bitcasted functions", .{}),
2091 }2348 }
2092 } else {2349 } else {
2093 return self.fail("TODO implement calling runtime-known function pointer", .{});2350 return self.fail("TODO: call function pointers", .{});
2094 }2351 },
2095 } else if (self.bin_file.cast(link.File.Coff)) |_| {2352 .lib => return self.fail("TODO: lib func calls", .{}),
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);
2126 }2353 }
2127 return bt.finishAir(result);2354
2355 return call_info.return_value;
2128}2356}
21292357
2130fn airRet(self: *Self, inst: Air.Inst.Index, safety: bool) !void {2358fn airRet(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
...@@ -2151,7 +2379,7 @@ fn ret(self: *Self, mcv: MCValue) !void {...@@ -2151,7 +2379,7 @@ fn ret(self: *Self, mcv: MCValue) !void {
2151 const mod = self.bin_file.comp.module.?;2379 const mod = self.bin_file.comp.module.?;
21522380
2153 const ret_ty = self.fn_type.fnReturnType(mod);2381 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
2156 _ = try self.addInst(.{2384 _ = try self.addInst(.{
2157 .tag = .psuedo_epilogue,2385 .tag = .psuedo_epilogue,
...@@ -2183,6 +2411,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index) !void {...@@ -2183,6 +2411,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index) !void {
2183 const ty = self.typeOf(bin_op.lhs);2411 const ty = self.typeOf(bin_op.lhs);
2184 const mod = self.bin_file.comp.module.?;2412 const mod = self.bin_file.comp.module.?;
2185 assert(ty.eql(self.typeOf(bin_op.rhs), mod));2413 assert(ty.eql(self.typeOf(bin_op.rhs), mod));
2414
2186 if (ty.zigTypeTag(mod) == .ErrorSet)2415 if (ty.zigTypeTag(mod) == .ErrorSet)
2187 return self.fail("TODO implement cmp for errors", .{});2416 return self.fail("TODO implement cmp for errors", .{});
21882417
...@@ -2233,11 +2462,54 @@ fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {...@@ -2233,11 +2462,54 @@ fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
22332462
2234fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {2463fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
2235 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;2464 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
2236 const name = self.air.nullTerminatedString(pl_op.payload);
2237 const operand = pl_op.operand;2465 const operand = pl_op.operand;
2238 // TODO emit debug info for this variable2466 const ty = self.typeOf(operand);
2239 _ = name;2467 const mcv = try self.resolveInst(operand);
2240 return self.finishAir(inst, .dead, .{ operand, .none, .none });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 }
2241}2513}
22422514
2243fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {2515fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
...@@ -2348,7 +2620,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2348,7 +2620,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
2348 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });2620 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });
2349 // TODO make sure the destination stack offset / register does not already have something2621 // TODO make sure the destination stack offset / register does not already have something
2350 // going on there.2622 // 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);
2352 // TODO track the new register / stack allocation2624 // TODO track the new register / stack allocation
2353 }2625 }
2354 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());2626 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 {...@@ -2375,7 +2647,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
2375 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });2647 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });
2376 // TODO make sure the destination stack offset / register does not already have something2648 // TODO make sure the destination stack offset / register does not already have something
2377 // going on there.2649 // 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);
2379 // TODO track the new register / stack allocation2651 // TODO track the new register / stack allocation
2380 }2652 }
23812653
...@@ -2638,7 +2910,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {...@@ -2638,7 +2910,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
2638 if (block_mcv == .none) {2910 if (block_mcv == .none) {
2639 block_data.mcv = operand_mcv;2911 block_data.mcv = operand_mcv;
2640 } else {2912 } else {
2641 try self.setValue(self.typeOfIndex(block), block_mcv, operand_mcv);2913 try self.genCopy(self.typeOfIndex(block), block_mcv, operand_mcv);
2642 }2914 }
2643 }2915 }
2644 return self.brVoid(block);2916 return self.brVoid(block);
...@@ -2783,29 +3055,45 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT...@@ -2783,29 +3055,45 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT
2783}3055}
27843056
2785/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.3057/// 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 {
2787 // There isn't anything to store3059 // 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()) {
2791 // panic so we can see the trace3063 // 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)});
2793 }3065 }
27943066
2795 switch (dst_val) {3067 switch (dst_mcv) {
2796 .register => |reg| return self.genSetReg(ty, reg, src_val),3068 .register => |reg| return self.genSetReg(ty, reg, src_mcv),
2797 .stack_offset => |off| return self.genSetStack(ty, off, src_val),3069 .register_pair => |pair| return self.genSetRegPair(ty, pair, src_mcv),
2798 .memory => |addr| return self.genSetMem(ty, addr, src_val),3070 .register_offset => |dst_reg_off| try self.genSetReg(ty, dst_reg_off.reg, switch (src_mcv) {
2799 else => return self.fail("TODO: setValue {s}", .{@tagName(dst_val)}),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) }),
2800 }3088 }
2801}3089}
28023090
2803/// Sets the value of `src_val` into stack memory at `stack_offset`.3091/// Sets the value of `src_mcv` into stack memory at `stack_offset`.
2804fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) InnerError!void {3092fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_mcv: MCValue) InnerError!void {
2805 const mod = self.bin_file.comp.module.?;3093 const mod = self.bin_file.comp.module.?;
2806 const abi_size: u32 = @intCast(ty.abiSize(mod));3094 const abi_size: u32 = @intCast(ty.abiSize(mod));
28073095
2808 switch (src_val) {3096 switch (src_mcv) {
2809 .none => return,3097 .none => return,
2810 .dead => unreachable,3098 .dead => unreachable,
2811 .undef => {3099 .undef => {
...@@ -2820,7 +3108,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner...@@ -2820,7 +3108,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
2820 const reg, const reg_lock = try self.allocReg();3108 const reg, const reg_lock = try self.allocReg();
2821 defer self.register_manager.unlockReg(reg_lock);3109 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
2825 return self.genSetStack(ty, stack_offset, .{ .register = reg });3113 return self.genSetStack(ty, stack_offset, .{ .register = reg });
2826 },3114 },
...@@ -2839,24 +3127,24 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner...@@ -2839,24 +3127,24 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
2839 .tag = tag,3127 .tag = tag,
2840 .data = .{ .i_type = .{3128 .data = .{ .i_type = .{
2841 .rd = reg,3129 .rd = reg,
2842 .rs1 = .s0,3130 .rs1 = .sp,
2843 .imm12 = math.cast(i12, stack_offset) orelse {3131 .imm12 = math.cast(i12, stack_offset) orelse {
2844 return self.fail("TODO: genSetStack bigger stack values", .{});3132 return self.fail("TODO: genSetStack bigger stack values", .{});
2845 },3133 },
2846 } },3134 } },
2847 });3135 });
2848 },3136 },
2849 else => return self.fail("TODO: genSetStack for size={d}", .{abi_size}),3137 else => unreachable, // register can hold a max of 8 bytes
2850 }3138 }
2851 },3139 },
2852 .stack_offset, .load_symbol => {3140 .stack_offset, .load_symbol => {
2853 switch (src_val) {3141 switch (src_mcv) {
2854 .stack_offset => |off| if (off == stack_offset) return,3142 .stack_offset => |off| if (off == stack_offset) return,
2855 else => {},3143 else => {},
2856 }3144 }
28573145
2858 if (abi_size <= 8) {3146 if (abi_size <= 8) {
2859 const reg = try self.copyToTmpRegister(ty, src_val);3147 const reg = try self.copyToTmpRegister(ty, src_mcv);
2860 return self.genSetStack(ty, stack_offset, .{ .register = reg });3148 return self.genSetStack(ty, stack_offset, .{ .register = reg });
2861 }3149 }
28623150
...@@ -2875,7 +3163,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner...@@ -2875,7 +3163,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
2875 const count_reg = regs[3];3163 const count_reg = regs[3];
2876 const tmp_reg = regs[4];3164 const tmp_reg = regs[4];
28773165
2878 switch (src_val) {3166 switch (src_mcv) {
2879 .stack_offset => |offset| {3167 .stack_offset => |offset| {
2880 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = offset });3168 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = offset });
2881 },3169 },
...@@ -2894,14 +3182,14 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner...@@ -2894,14 +3182,14 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
2894 .tag = .load_symbol,3182 .tag = .load_symbol,
2895 .data = .{3183 .data = .{
2896 .payload = try self.addExtra(Mir.LoadSymbolPayload{3184 .payload = try self.addExtra(Mir.LoadSymbolPayload{
2897 .register = @intFromEnum(src_reg),3185 .register = src_reg.id(),
2898 .atom_index = atom_index,3186 .atom_index = atom_index,
2899 .sym_index = sym_off.sym,3187 .sym_index = sym_off.sym,
2900 }),3188 }),
2901 },3189 },
2902 });3190 });
2903 },3191 },
2904 else => return self.fail("TODO: genSetStack unreachable {s}", .{@tagName(src_val)}),3192 else => return self.fail("TODO: genSetStack unreachable {s}", .{@tagName(src_mcv)}),
2905 }3193 }
29063194
2907 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset });3195 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...@@ -2910,16 +3198,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
2910 // memcpy(src, dst, len)3198 // memcpy(src, dst, len)
2911 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);3199 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
2912 },3200 },
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)}),
2914 }3203 }
2915}3204}
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 {
2918 const mod = self.bin_file.comp.module.?;3207 const mod = self.bin_file.comp.module.?;
2919 const abi_size: u32 = @intCast(ty.abiSize(mod));3208 const abi_size: u32 = @intCast(ty.abiSize(mod));
2920 _ = abi_size;3209 _ = abi_size;
2921 _ = addr;3210 _ = addr;
2922 _ = src_val;3211 _ = src_mcv;
29233212
2924 return self.fail("TODO: genSetMem", .{});3213 return self.fail("TODO: genSetMem", .{});
2925}3214}
...@@ -2932,51 +3221,101 @@ fn genInlineMemcpy(...@@ -2932,51 +3221,101 @@ fn genInlineMemcpy(
2932 count: Register,3221 count: Register,
2933 tmp: Register,3222 tmp: Register,
2934) !void {3223) !void {
2935 _ = src;3224 try self.genSetReg(Type.usize, count, .{ .register = len });
2936 _ = dst;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 count3238 // sb tmp, 0(dst)
2939 try self.genSetReg(Type.usize, count, .{ .immediate = 0 });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 length3250 // dec count by 1
2942 const compare_inst = try self.addInst(.{3251 _ = try self.addInst(.{
2943 .tag = .cmp_eq,3252 .tag = .addi,
2944 .data = .{ .r_type = .{3253 .data = .{
2945 .rd = tmp,3254 .i_type = .{
2946 .rs1 = count,3255 .rd = count,
2947 .rs2 = len,3256 .rs1 = count,
2948 } },3257 .imm12 = -1,
3258 },
3259 },
2949 });3260 });
29503261
2951 // end if true3262 // branch if count is 0
2952 _ = try self.addInst(.{3263 _ = try self.addInst(.{
2953 .tag = .bne,3264 .tag = .beq,
2954 .data = .{3265 .data = .{
2955 .b_type = .{3266 .b_type = .{
2956 .inst = @intCast(self.mir_instructions.len + 0), // points after the last inst3267 .inst = @intCast(self.mir_instructions.len + 4), // points after the last inst
2957 .rs1 = .zero,3268 .rs1 = count,
2958 .rs2 = tmp,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,
2959 },3293 },
2960 },3294 },
2961 });3295 });
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 });
2965}3304}
29663305
2967/// Sets the value of `src_val` into `reg`. Assumes you have a lock on it.3306/// Sets the value of `src_mcv` into `reg`. Assumes you have a lock on it.
2968fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!void {3307fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!void {
2969 const mod = self.bin_file.comp.module.?;3308 const mod = self.bin_file.comp.module.?;
2970 const abi_size: u32 = @intCast(ty.abiSize(mod));3309 const abi_size: u32 = @intCast(ty.abiSize(mod));
29713310
2972 switch (src_val) {3311 switch (src_mcv) {
2973 .dead => unreachable,3312 .dead => unreachable,
2974 .ptr_stack_offset => |off| {3313 .ptr_stack_offset => |off| {
2975 _ = try self.addInst(.{3314 _ = try self.addInst(.{
2976 .tag = .addi,3315 .tag = .addi,
2977 .data = .{ .i_type = .{3316 .data = .{ .i_type = .{
2978 .rd = reg,3317 .rd = reg,
2979 .rs1 = .s0,3318 .rs1 = .sp,
2980 .imm12 = math.cast(i12, off) orelse {3319 .imm12 = math.cast(i12, off) orelse {
2981 return self.fail("TODO: bigger stack sizes", .{});3320 return self.fail("TODO: bigger stack sizes", .{});
2982 },3321 },
...@@ -3006,7 +3345,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!...@@ -3006,7 +3345,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!
3006 const carry: i32 = if (lo12 < 0) 1 else 0;3345 const carry: i32 = if (lo12 < 0) 1 else 0;
3007 const hi20: i20 = @truncate((x >> 12) +% carry);3346 const hi20: i20 = @truncate((x >> 12) +% carry);
30083347
3009 // TODO: add test case for 32-bit immediate
3010 _ = try self.addInst(.{3348 _ = try self.addInst(.{
3011 .tag = .lui,3349 .tag = .lui,
3012 .data = .{ .u_type = .{3350 .data = .{ .u_type = .{
...@@ -3069,6 +3407,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!...@@ -3069,6 +3407,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!
3069 } },3407 } },
3070 });3408 });
3071 },3409 },
3410 .register_pair => |pair| try self.genSetReg(ty, reg, .{ .register = pair[0] }),
3072 .memory => |addr| {3411 .memory => |addr| {
3073 try self.genSetReg(ty, reg, .{ .immediate = addr });3412 try self.genSetReg(ty, reg, .{ .immediate = addr });
30743413
...@@ -3080,8 +3419,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!...@@ -3080,8 +3419,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!
3080 .imm12 = 0,3419 .imm12 = 0,
3081 } },3420 } },
3082 });3421 });
3083
3084 // LOAD imm=[i12 offset = 0], rs1
3085 },3422 },
3086 .stack_offset => |off| {3423 .stack_offset => |off| {
3087 const tag: Mir.Inst.Tag = switch (abi_size) {3424 const tag: Mir.Inst.Tag = switch (abi_size) {
...@@ -3096,7 +3433,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!...@@ -3096,7 +3433,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!
3096 .tag = tag,3433 .tag = tag,
3097 .data = .{ .i_type = .{3434 .data = .{ .i_type = .{
3098 .rd = reg,3435 .rd = reg,
3099 .rs1 = .s0,3436 .rs1 = .sp,
3100 .imm12 = math.cast(i12, off) orelse {3437 .imm12 = math.cast(i12, off) orelse {
3101 return self.fail("TODO: genSetReg support larger stack sizes", .{});3438 return self.fail("TODO: genSetReg support larger stack sizes", .{});
3102 },3439 },
...@@ -3120,13 +3457,55 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!...@@ -3120,13 +3457,55 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!
3120 .tag = .load_symbol,3457 .tag = .load_symbol,
3121 .data = .{3458 .data = .{
3122 .payload = try self.addExtra(Mir.LoadSymbolPayload{3459 .payload = try self.addExtra(Mir.LoadSymbolPayload{
3123 .register = @intFromEnum(reg),3460 .register = reg.id(),
3124 .atom_index = atom_index,3461 .atom_index = atom_index,
3125 .sym_index = sym_off.sym,3462 .sym_index = sym_off.sym,
3126 }),3463 }),
3127 },3464 },
3128 });3465 });
3129 },3466 },
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)}),
3130 }3509 }
3131}3510}
31323511
...@@ -3138,7 +3517,7 @@ fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3138,7 +3517,7 @@ fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void {
31383517
3139 const dst_mcv = try self.allocRegOrMem(inst, true);3518 const dst_mcv = try self.allocRegOrMem(inst, true);
3140 const dst_ty = self.typeOfIndex(inst);3519 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);
3142 break :result dst_mcv;3521 break :result dst_mcv;
3143 };3522 };
3144 return self.finishAir(inst, result, .{ un_op, .none, .none });3523 return self.finishAir(inst, result, .{ un_op, .none, .none });
...@@ -3157,7 +3536,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {...@@ -3157,7 +3536,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
3157 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);3536 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
31583537
3159 const dest = try self.allocRegOrMem(inst, true);3538 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);
3161 break :result dest;3540 break :result dest;
3162 };3541 };
3163 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3542 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
...@@ -3385,8 +3764,12 @@ const CallMCValues = struct {...@@ -3385,8 +3764,12 @@ const CallMCValues = struct {
3385};3764};
33863765
3387/// Caller must call `CallMCValues.deinit`.3766/// Caller must call `CallMCValues.deinit`.
3388fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {3767fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: CallView) !CallMCValues {
3389 const mod = self.bin_file.comp.module.?;3768 const mod = self.bin_file.comp.module.?;
3769 const ip = &mod.intern_pool;
3770
3771 _ = role;
3772
3390 const fn_info = mod.typeToFunc(fn_ty).?;3773 const fn_info = mod.typeToFunc(fn_ty).?;
3391 const cc = fn_info.cc;3774 const cc = fn_info.cc;
3392 var result: CallMCValues = .{3775 var result: CallMCValues = .{
...@@ -3413,26 +3796,31 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -3413,26 +3796,31 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
3413 return self.fail("TODO: support more than 8 function args", .{});3796 return self.fail("TODO: support more than 8 function args", .{});
3414 }3797 }
34153798
3416 const locks = try self.gpa.alloc(RegisterLock, result.args.len);3799 var fa_reg_i: u32 = 0;
3417 defer self.gpa.free(locks);
34183800
3419 for (0..result.args.len) |i| {3801 // spill the needed argument registers
3420 const arg_reg = try self.register_manager.allocReg(null, fa);3802 for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| {
3421 const lock = self.register_manager.lockRegAssumeUnused(arg_reg);3803 const param_ty = Type.fromInterned(ty);
3422 locks[i] = lock;3804 const param_size = param_ty.abiSize(mod);
3423 result.args[i] = .{ .register = arg_reg };
3424 }
34253805
3426 // we can just free the locks now, as this should be the only place where the fa3806 switch (param_size) {
3427 // arg set is used.3807 1...8 => {
3428 for (locks) |lock| {3808 const arg_reg: Register = abi.function_arg_regs[fa_reg_i];
3429 self.register_manager.unlockReg(lock);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 }
3430 }3821 }
34313822
3432 // stack_offset = num s registers spilled + local var space3823 result.stack_byte_count = self.max_end_stack;
3433 // TODO: spill used s registers here
3434
3435 result.stack_byte_count = 0;
3436 result.stack_align = .@"16";3824 result.stack_align = .@"16";
3437 },3825 },
3438 else => return self.fail("TODO implement function parameters for {} on riscv64", .{cc}),3826 else => return self.fail("TODO implement function parameters for {} on riscv64", .{cc}),
src/arch/riscv64/Emit.zig+56-48
...@@ -1,19 +1,6 @@...@@ -1,19 +1,6 @@
1//! This file contains the functionality for lowering RISCV64 MIR into1//! This file contains the functionality for lowering RISCV64 MIR into
2//! machine code2//! 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
17mir: Mir,4mir: Mir,
18bin_file: *link.File,5bin_file: *link.File,
19debug_output: DebugInfoOutput,6debug_output: DebugInfoOutput,
...@@ -22,12 +9,17 @@ err_msg: ?*ErrorMsg = null,...@@ -22,12 +9,17 @@ err_msg: ?*ErrorMsg = null,
22src_loc: Module.SrcLoc,9src_loc: Module.SrcLoc,
23code: *std.ArrayList(u8),10code: *std.ArrayList(u8),
2411
12/// List of registers to save in the prologue.
13save_reg_list: Mir.RegisterList,
14
25prev_di_line: u32,15prev_di_line: u32,
26prev_di_column: u32,16prev_di_column: u32,
27/// Relative to the beginning of `code`.17/// Relative to the beginning of `code`.
28prev_di_pc: usize,18prev_di_pc: usize,
19
29/// Function's stack size. Used for backpatching.20/// Function's stack size. Used for backpatching.
30stack_size: u32,21stack_size: u32,
22
31/// For backward branches: stores the code offset of the target23/// For backward branches: stores the code offset of the target
32/// instruction24/// instruction
33///25///
...@@ -212,7 +204,7 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -212,7 +204,7 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
212 // rs1 != rs2204 // rs1 != rs2
213205
214 try emit.writeInstruction(Instruction.xor(rd, rs1, rs2));206 try emit.writeInstruction(Instruction.xor(rd, rs1, rs2));
215 try emit.writeInstruction(Instruction.sltu(rd, .x0, rd)); // snez207 try emit.writeInstruction(Instruction.sltu(rd, .zero, rd)); // snez
216 },208 },
217 .cmp_lt => {209 .cmp_lt => {
218 // rd = 1 if rs1 < rs2210 // rd = 1 if rs1 < rs2
...@@ -368,17 +360,20 @@ fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -368,17 +360,20 @@ fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void {
368 return emit.fail("TODO: mirPsuedo support larger stack sizes", .{});360 return emit.fail("TODO: mirPsuedo support larger stack sizes", .{});
369 };361 };
370362
371 // Decrement sp by num s registers + local var space363 // Decrement sp by (num s registers * 8) + local var space
372 try emit.writeInstruction(Instruction.addi(.sp, .sp, -stack_size));364 try emit.writeInstruction(Instruction.addi(.sp, .sp, -stack_size));
373365
374 // Spill ra366 // Spill ra
375 try emit.writeInstruction(Instruction.sd(.ra, stack_size - 8, .sp));367 try emit.writeInstruction(Instruction.sd(.ra, 0, .sp));
376368
377 // Spill s0369 // Spill callee saved registers.
378 try emit.writeInstruction(Instruction.sd(.s0, stack_size - 16, .sp));370 var s_reg_iter = emit.save_reg_list.iterator(.{});
379371 var i: i12 = 8;
380 // Setup s0372 while (s_reg_iter.next()) |reg_i| {
381 try emit.writeInstruction(Instruction.addi(.s0, .sp, stack_size));373 const reg = abi.callee_preserved_regs[reg_i];
374 try emit.writeInstruction(Instruction.sd(reg, i, .sp));
375 i += 8;
376 }
382 },377 },
383 .psuedo_epilogue => {378 .psuedo_epilogue => {
384 const stack_size: i12 = math.cast(i12, emit.stack_size) orelse {379 const stack_size: i12 = math.cast(i12, emit.stack_size) orelse {
...@@ -386,10 +381,16 @@ fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -386,10 +381,16 @@ fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void {
386 };381 };
387382
388 // Restore ra383 // Restore ra
389 try emit.writeInstruction(Instruction.ld(.ra, stack_size - 8, .sp));384 try emit.writeInstruction(Instruction.ld(.ra, 0, .sp));
390385
391 // Restore s0386 // Restore spilled callee saved registers
392 try emit.writeInstruction(Instruction.ld(.s0, stack_size - 16, .sp));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
394 // Increment sp back to previous value395 // Increment sp back to previous value
395 try emit.writeInstruction(Instruction.addi(.sp, .sp, stack_size));396 try emit.writeInstruction(Instruction.addi(.sp, .sp, stack_size));
...@@ -408,8 +409,11 @@ fn mirRR(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -408,8 +409,11 @@ fn mirRR(emit: *Emit, inst: Mir.Inst.Index) !void {
408 const tag = emit.mir.instructions.items(.tag)[inst];409 const tag = emit.mir.instructions.items(.tag)[inst];
409 const rr = emit.mir.instructions.items(.data)[inst].rr;410 const rr = emit.mir.instructions.items(.data)[inst].rr;
410411
412 const rd = rr.rd;
413 const rs = rr.rs;
414
411 switch (tag) {415 switch (tag) {
412 .mv => try emit.writeInstruction(Instruction.addi(rr.rd, rr.rs, 0)),416 .mv => try emit.writeInstruction(Instruction.addi(rd, rs, 0)),
413 else => unreachable,417 else => unreachable,
414 }418 }
415}419}
...@@ -435,7 +439,6 @@ fn mirNop(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -435,7 +439,6 @@ fn mirNop(emit: *Emit, inst: Mir.Inst.Index) !void {
435}439}
436440
437fn mirLoadSymbol(emit: *Emit, inst: Mir.Inst.Index) !void {441fn mirLoadSymbol(emit: *Emit, inst: Mir.Inst.Index) !void {
438 // const tag = emit.mir.instructions.items(.tag)[inst];
439 const payload = emit.mir.instructions.items(.data)[inst].payload;442 const payload = emit.mir.instructions.items(.data)[inst].payload;
440 const data = emit.mir.extraData(Mir.LoadSymbolPayload, payload).data;443 const data = emit.mir.extraData(Mir.LoadSymbolPayload, payload).data;
441 const reg = @as(Register, @enumFromInt(data.register));444 const reg = @as(Register, @enumFromInt(data.register));
...@@ -523,20 +526,19 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {...@@ -523,20 +526,19 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
523 .dbg_prologue_end,526 .dbg_prologue_end,
524 => 0,527 => 0,
525528
526 .psuedo_prologue,
527 => 16,
528
529 .psuedo_epilogue,
530 .abs,
531 => 12,
532
533 .cmp_eq,529 .cmp_eq,
534 .cmp_neq,530 .cmp_neq,
535 .cmp_imm_eq,531 .cmp_imm_eq,
536 .cmp_gte,532 .cmp_gte,
537 .load_symbol,533 .load_symbol,
534 .abs,
538 => 8,535 => 8,
539536
537 .psuedo_epilogue, .psuedo_prologue => size: {
538 const count = emit.save_reg_list.count() * 4;
539 break :size count + 8;
540 },
541
540 else => 4,542 else => 4,
541 };543 };
542}544}
...@@ -547,25 +549,17 @@ fn lowerMir(emit: *Emit) !void {...@@ -547,25 +549,17 @@ fn lowerMir(emit: *Emit) !void {
547 const mir_tags = emit.mir.instructions.items(.tag);549 const mir_tags = emit.mir.instructions.items(.tag);
548 const mir_datas = emit.mir.instructions.items(.data);550 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
550 for (mir_tags, 0..) |tag, index| {555 for (mir_tags, 0..) |tag, index| {
551 const inst: u32 = @intCast(index);556 const inst: u32 = @intCast(index);
552557
553 if (isStore(tag) or isLoad(tag)) {558 if (isStore(tag) or isLoad(tag)) {
554 const data = mir_datas[inst].i_type;559 const data = mir_datas[inst].i_type;
555 // TODO: probably create a psuedo instruction for s0 loads/stores instead of this.560 if (data.rs1 == .sp) {
556 if (data.rs1 == .s0) {
557 const offset = mir_datas[inst].i_type.imm12;561 const offset = mir_datas[inst].i_type.imm12;
558562 mir_datas[inst].i_type.imm12 = offset + @as(i12, @intCast(proglogue_size)) + 8;
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);
569 }563 }
570 }564 }
571565
...@@ -584,3 +578,17 @@ fn lowerMir(emit: *Emit) !void {...@@ -584,3 +578,17 @@ fn lowerMir(emit: *Emit) !void {
584 current_code_offset += emit.instructionSize(inst);578 current_code_offset += emit.instructionSize(inst);
585 }579 }
586}580}
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 @@...@@ -6,14 +6,6 @@
6//! The main purpose of MIR is to postpone the assignment of offsets until Isel,6//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
7//! so that, for example, the smaller encodings of jump instructions can be used.7//! 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
17instructions: std.MultiArrayList(Inst).Slice,9instructions: std.MultiArrayList(Inst).Slice,
18/// The meaning of this data is determined by `Inst.Tag` value.10/// The meaning of this data is determined by `Inst.Tag` value.
19extra: []const u32,11extra: []const u32,
...@@ -58,7 +50,7 @@ pub const Inst = struct {...@@ -58,7 +50,7 @@ pub const Inst = struct {
58 /// Jumps. Uses `inst` payload.50 /// Jumps. Uses `inst` payload.
59 j,51 j,
6052
61 /// Immediate and, uses i_type payload53 /// Immediate AND, uses i_type payload
62 andi,54 andi,
6355
64 // NOTE: Maybe create a special data for compares that includes the ops56 // NOTE: Maybe create a special data for compares that includes the ops
...@@ -219,15 +211,6 @@ pub const Inst = struct {...@@ -219,15 +211,6 @@ pub const Inst = struct {
219 },211 },
220 };212 };
221213
222 const CompareOp = enum {
223 eq,
224 neq,
225 gt,
226 gte,
227 lt,
228 lte,
229 };
230
231 // Make sure we don't accidentally make instructions bigger than expected.214 // Make sure we don't accidentally make instructions bigger than expected.
232 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.215 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
233 // comptime {216 // comptime {
...@@ -268,3 +251,49 @@ pub const LoadSymbolPayload = struct {...@@ -268,3 +251,49 @@ pub const LoadSymbolPayload = struct {
268 atom_index: u32,251 atom_index: u32,
269 sym_index: u32,252 sym_index: u32,
270};253};
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 {...@@ -92,15 +92,18 @@ pub fn classifyType(ty: Type, mod: *Module) Class {
92}92}
9393
94pub const callee_preserved_regs = [_]Register{94pub const callee_preserved_regs = [_]Register{
95 // NOTE: we use s0 as a psuedo stack pointer, so it's not included.95 .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
96 .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
97};96};
9897
99pub const function_arg_regs = [_]Register{98pub const function_arg_regs = [_]Register{
100 .a0, .a1, .a2, .a3, .a4, .a5, .a6, .a7,99 .a0, .a1, .a2, .a3, .a4, .a5, .a6, .a7,
101};100};
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;
104pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);107pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);
105108
106// Register classes109// Register classes
...@@ -123,4 +126,13 @@ pub const RegisterClass = struct {...@@ -123,4 +126,13 @@ pub const RegisterClass = struct {
123 }, true);126 }, true);
124 break :blk set;127 break :blk set;
125 };128 };
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 };
126};138};
src/arch/riscv64/bits.zig+2-2
...@@ -404,14 +404,14 @@ pub const Register = enum(u6) {...@@ -404,14 +404,14 @@ pub const Register = enum(u6) {
404 t3, t4, t5, t6, // caller saved404 t3, t4, t5, t6, // caller saved
405 // zig fmt: on405 // zig fmt: on
406406
407 /// Returns the unique 4-bit ID of this register which is used in407 /// Returns the unique 5-bit ID of this register which is used in
408 /// the machine code408 /// the machine code
409 pub fn id(self: Register) u5 {409 pub fn id(self: Register) u5 {
410 return @as(u5, @truncate(@intFromEnum(self)));410 return @as(u5, @truncate(@intFromEnum(self)));
411 }411 }
412412
413 pub fn dwarfLocOp(reg: Register) u8 {413 pub fn dwarfLocOp(reg: Register) u8 {
414 return @as(u8, reg.id()) + DW.OP.reg0;414 return @as(u8, reg.id());
415 }415 }
416};416};
417417
src/register_manager.zig+1-1
...@@ -102,7 +102,7 @@ pub fn RegisterManager(...@@ -102,7 +102,7 @@ pub fn RegisterManager(
102 }102 }
103103
104 const OptionalIndex = std.math.IntFittingRange(0, set.len);104 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);
106 inline for (set, 0..) |elem, elem_index| map[comptime elem.id() - min_id] = elem_index;106 inline for (set, 0..) |elem, elem_index| map[comptime elem.id() - min_id] = elem_index;
107107
108 const id_index = reg.id() -% min_id;108 const id_index = reg.id() -% min_id;