| ... | @@ -736,6 +736,15 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { | ... | @@ -736,6 +736,15 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 736 | return MCValue{ .stack_offset = stack_offset }; | 736 | return MCValue{ .stack_offset = stack_offset }; |
| 737 | } | 737 | } |
| 738 | | 738 | |
| | 739 | /// Allocates a register from the general purpose set and returns the Register and the Lock. |
| | 740 | /// |
| | 741 | /// Up to the user to unlock the register later. |
| | 742 | fn allocReg(self: *Self) !struct { Register, RegisterLock } { |
| | 743 | const reg = try self.register_manager.allocReg(null, gp); |
| | 744 | const lock = self.register_manager.lockRegAssumeUnused(reg); |
| | 745 | return .{ reg, lock }; |
| | 746 | } |
| | 747 | |
| 739 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { | 748 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 740 | const stack_mcv = try self.allocRegOrMem(inst, false); | 749 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 741 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); | 750 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |
| ... | @@ -983,67 +992,36 @@ fn binOpRegister( | ... | @@ -983,67 +992,36 @@ fn binOpRegister( |
| 983 | lhs_ty: Type, | 992 | lhs_ty: Type, |
| 984 | rhs_ty: Type, | 993 | rhs_ty: Type, |
| 985 | ) !MCValue { | 994 | ) !MCValue { |
| 986 | const lhs_is_register = lhs == .register; | 995 | _ = maybe_inst; |
| 987 | const rhs_is_register = rhs == .register; | | |
| 988 | | | |
| 989 | const lhs_lock: ?RegisterLock = if (lhs_is_register) | | |
| 990 | self.register_manager.lockReg(lhs.register) | | |
| 991 | else | | |
| 992 | null; | | |
| 993 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); | | |
| 994 | | | |
| 995 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | | |
| 996 | | | |
| 997 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { | | |
| 998 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { | | |
| 999 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 1000 | break :inst bin_op.lhs.toIndex().?; | | |
| 1001 | } else null; | | |
| 1002 | | | |
| 1003 | const reg = try self.register_manager.allocReg(track_inst, gp); | | |
| 1004 | | 996 | |
| 1005 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); | 997 | const lhs_reg, const lhs_lock = blk: { |
| | 998 | if (lhs == .register) break :blk .{ lhs.register, null }; |
| 1006 | | 999 | |
| 1007 | break :blk reg; | 1000 | const lhs_reg, const lhs_lock = try self.allocReg(); |
| | 1001 | try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| | 1002 | break :blk .{ lhs_reg, lhs_lock }; |
| 1008 | }; | 1003 | }; |
| 1009 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); | 1004 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 1010 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); | | |
| 1011 | | 1005 | |
| 1012 | const rhs_reg = if (rhs_is_register) rhs.register else blk: { | 1006 | const rhs_reg, const rhs_lock = blk: { |
| 1013 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { | 1007 | if (rhs == .register) break :blk .{ rhs.register, null }; |
| 1014 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 1015 | break :inst bin_op.rhs.toIndex().?; | | |
| 1016 | } else null; | | |
| 1017 | | | |
| 1018 | const reg = try self.register_manager.allocReg(track_inst, gp); | | |
| 1019 | | | |
| 1020 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); | | |
| 1021 | | 1008 | |
| 1022 | break :blk reg; | 1009 | const rhs_reg, const rhs_lock = try self.allocReg(); |
| | 1010 | try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| | 1011 | break :blk .{ rhs_reg, rhs_lock }; |
| 1023 | }; | 1012 | }; |
| 1024 | const new_rhs_lock = self.register_manager.lockReg(rhs_reg); | 1013 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 1025 | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); | | |
| 1026 | | 1014 | |
| 1027 | const dest_reg = if (maybe_inst) |inst| blk: { | 1015 | const dest_reg, const dest_lock = try self.allocReg(); |
| 1028 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 1016 | defer self.register_manager.unlockReg(dest_lock); |
| 1029 | | | |
| 1030 | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { | | |
| 1031 | break :blk lhs_reg; | | |
| 1032 | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { | | |
| 1033 | break :blk rhs_reg; | | |
| 1034 | } else { | | |
| 1035 | break :blk try self.register_manager.allocReg(inst, gp); | | |
| 1036 | } | | |
| 1037 | } else try self.register_manager.allocReg(null, gp); | | |
| 1038 | | | |
| 1039 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | | |
| 1040 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); | | |
| 1041 | | 1017 | |
| 1042 | const mir_tag: Mir.Inst.Tag = switch (tag) { | 1018 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1043 | .add => .add, | 1019 | .add => .add, |
| 1044 | .sub => .sub, | 1020 | .sub => .sub, |
| 1045 | .cmp_eq => .cmp_eq, | 1021 | .cmp_eq => .cmp_eq, |
| 1046 | .cmp_gt => .cmp_gt, | 1022 | .cmp_gt => .cmp_gt, |
| | 1023 | .cmp_gte => .cmp_gte, |
| | 1024 | .cmp_lt => .cmp_lt, |
| 1047 | .shl => .sllw, | 1025 | .shl => .sllw, |
| 1048 | .shr => .srlw, | 1026 | .shr => .srlw, |
| 1049 | else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}), | 1027 | else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}), |
| ... | @@ -1080,48 +1058,28 @@ fn binOpImm( | ... | @@ -1080,48 +1058,28 @@ fn binOpImm( |
| 1080 | rhs_ty: Type, | 1058 | rhs_ty: Type, |
| 1081 | ) !MCValue { | 1059 | ) !MCValue { |
| 1082 | assert(rhs == .immediate); | 1060 | assert(rhs == .immediate); |
| | 1061 | _ = maybe_inst; |
| 1083 | | 1062 | |
| 1084 | const lhs_is_register = lhs == .register; | 1063 | // TODO: use `maybe_inst` to track instead of forcing a lock. |
| 1085 | | 1064 | |
| 1086 | const lhs_lock: ?RegisterLock = if (lhs_is_register) | 1065 | const lhs_reg, const lhs_lock = blk: { |
| 1087 | self.register_manager.lockReg(lhs.register) | 1066 | if (lhs == .register) break :blk .{ lhs.register, null }; |
| 1088 | else | | |
| 1089 | null; | | |
| 1090 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); | | |
| 1091 | | 1067 | |
| 1092 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 1068 | const lhs_reg, const lhs_lock = try self.allocReg(); |
| 1093 | | 1069 | try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1094 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { | 1070 | break :blk .{ lhs_reg, lhs_lock }; |
| 1095 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { | | |
| 1096 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | | |
| 1097 | break :inst bin_op.lhs.toIndex().?; | | |
| 1098 | } else null; | | |
| 1099 | | | |
| 1100 | const reg = try self.register_manager.allocReg(track_inst, gp); | | |
| 1101 | | | |
| 1102 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); | | |
| 1103 | | | |
| 1104 | break :blk reg; | | |
| 1105 | }; | 1071 | }; |
| 1106 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); | 1072 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 1107 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); | | |
| 1108 | | 1073 | |
| 1109 | const dest_reg = if (maybe_inst) |inst| blk: { | 1074 | const dest_reg, const dest_lock = try self.allocReg(); |
| 1110 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 1075 | defer self.register_manager.unlockReg(dest_lock); |
| 1111 | | | |
| 1112 | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { | | |
| 1113 | break :blk lhs_reg; | | |
| 1114 | } else { | | |
| 1115 | break :blk try self.register_manager.allocReg(inst, gp); | | |
| 1116 | } | | |
| 1117 | } else try self.register_manager.allocReg(null, gp); | | |
| 1118 | | | |
| 1119 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | | |
| 1120 | | 1076 | |
| 1121 | const mir_tag: Mir.Inst.Tag = switch (tag) { | 1077 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1122 | .shl => .slli, | 1078 | .shl => .slli, |
| 1123 | .shr => .srli, | 1079 | .shr => .srli, |
| 1124 | .cmp_gte => .cmp_imm_gte, | 1080 | .cmp_gte => .cmp_imm_gte, |
| | 1081 | .cmp_eq => .cmp_imm_eq, |
| | 1082 | .add => .addi, |
| 1125 | else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}), | 1083 | else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}), |
| 1126 | }; | 1084 | }; |
| 1127 | | 1085 | |
| ... | @@ -1129,6 +1087,8 @@ fn binOpImm( | ... | @@ -1129,6 +1087,8 @@ fn binOpImm( |
| 1129 | switch (mir_tag) { | 1087 | switch (mir_tag) { |
| 1130 | .slli, | 1088 | .slli, |
| 1131 | .srli, | 1089 | .srli, |
| | 1090 | .addi, |
| | 1091 | .cmp_imm_eq, |
| 1132 | => { | 1092 | => { |
| 1133 | _ = try self.addInst(.{ | 1093 | _ = try self.addInst(.{ |
| 1134 | .tag = mir_tag, | 1094 | .tag = mir_tag, |
| ... | @@ -1156,8 +1116,6 @@ fn binOpImm( | ... | @@ -1156,8 +1116,6 @@ fn binOpImm( |
| 1156 | else => unreachable, | 1116 | else => unreachable, |
| 1157 | } | 1117 | } |
| 1158 | | 1118 | |
| 1159 | // generate the struct for overflow checks | | |
| 1160 | | | |
| 1161 | return MCValue{ .register = dest_reg }; | 1119 | return MCValue{ .register = dest_reg }; |
| 1162 | } | 1120 | } |
| 1163 | | 1121 | |
| ... | @@ -1216,6 +1174,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1216,6 +1174,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1216 | } | 1174 | } |
| 1217 | | 1175 | |
| 1218 | fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 1176 | fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| | 1177 | const mod = self.bin_file.comp.module.?; |
| 1219 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 1178 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 1220 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 1179 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1221 | | 1180 | |
| ... | @@ -1225,7 +1184,28 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1225,7 +1184,28 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1225 | const lhs_ty = self.typeOf(extra.lhs); | 1184 | const lhs_ty = self.typeOf(extra.lhs); |
| 1226 | const rhs_ty = self.typeOf(extra.rhs); | 1185 | const rhs_ty = self.typeOf(extra.rhs); |
| 1227 | | 1186 | |
| 1228 | break :result try self.binOp(.add, null, lhs, rhs, lhs_ty, rhs_ty); | 1187 | const partial_mcv = try self.binOp(.add, null, lhs, rhs, lhs_ty, rhs_ty); |
| | 1188 | |
| | 1189 | const tuple_ty = self.typeOfIndex(inst); |
| | 1190 | |
| | 1191 | // TODO: optimization, set this to true. needs the other struct access stuff to support |
| | 1192 | // accessing registers. |
| | 1193 | const result_mcv = try self.allocRegOrMem(inst, false); |
| | 1194 | const offset = result_mcv.stack_offset; |
| | 1195 | |
| | 1196 | const overflow_offset = tuple_ty.structFieldOffset(1, mod) + offset; |
| | 1197 | const result_offset = tuple_ty.structFieldOffset(0, mod) + offset; |
| | 1198 | |
| | 1199 | const overflow_mcv = try self.binOp(.cmp_lt, null, partial_mcv, lhs, lhs_ty, lhs_ty); |
| | 1200 | |
| | 1201 | const overflow_reg, const overflow_lock = try self.allocReg(); |
| | 1202 | defer self.register_manager.unlockReg(overflow_lock); |
| | 1203 | |
| | 1204 | try self.genSetReg(lhs_ty, overflow_reg, overflow_mcv); |
| | 1205 | |
| | 1206 | try self.genSetStack(Type.u1, @intCast(overflow_offset), overflow_mcv); |
| | 1207 | try self.genSetStack(lhs_ty, @intCast(result_offset), partial_mcv); |
| | 1208 | break :result result_mcv; |
| 1229 | }; | 1209 | }; |
| 1230 | | 1210 | |
| 1231 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); | 1211 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| ... | @@ -1749,6 +1729,15 @@ fn store(self: *Self, pointer: MCValue, value: MCValue, ptr_ty: Type, value_ty: | ... | @@ -1749,6 +1729,15 @@ fn store(self: *Self, pointer: MCValue, value: MCValue, ptr_ty: Type, value_ty: |
| 1749 | .dead => unreachable, | 1729 | .dead => unreachable, |
| 1750 | .ptr_stack_offset => |off| try self.genSetStack(value_ty, off, value), | 1730 | .ptr_stack_offset => |off| try self.genSetStack(value_ty, off, value), |
| 1751 | | 1731 | |
| | 1732 | .stack_offset => { |
| | 1733 | const pointer_reg, const lock = try self.allocReg(); |
| | 1734 | defer self.register_manager.unlockReg(lock); |
| | 1735 | |
| | 1736 | try self.genSetReg(ptr_ty, pointer_reg, pointer); |
| | 1737 | |
| | 1738 | return self.store(.{ .register = pointer_reg }, value, ptr_ty, value_ty); |
| | 1739 | }, |
| | 1740 | |
| 1752 | .register => |reg| { | 1741 | .register => |reg| { |
| 1753 | const value_reg = try self.copyToTmpRegister(value_ty, value); | 1742 | const value_reg = try self.copyToTmpRegister(value_ty, value); |
| 1754 | | 1743 | |
| ... | @@ -2165,7 +2154,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2165,7 +2154,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2165 | const cond_reg_lock = self.register_manager.lockRegAssumeUnused(cond_reg); | 2154 | const cond_reg_lock = self.register_manager.lockRegAssumeUnused(cond_reg); |
| 2166 | defer self.register_manager.unlockReg(cond_reg_lock); | 2155 | defer self.register_manager.unlockReg(cond_reg_lock); |
| 2167 | | 2156 | |
| 2168 | // A branch to the false section. Uses bne | 2157 | // A branch to the false section. Uses beq. 1 is the default "true" state. |
| 2169 | const reloc = try self.condBr(cond_ty, cond, cond_reg); | 2158 | const reloc = try self.condBr(cond_ty, cond, cond_reg); |
| 2170 | | 2159 | |
| 2171 | // If the condition dies here in this condbr instruction, process | 2160 | // If the condition dies here in this condbr instruction, process |
| ... | @@ -2301,7 +2290,7 @@ fn condBr(self: *Self, cond_ty: Type, condition: MCValue, cond_reg: Register) !M | ... | @@ -2301,7 +2290,7 @@ fn condBr(self: *Self, cond_ty: Type, condition: MCValue, cond_reg: Register) !M |
| 2301 | try self.genSetReg(cond_ty, cond_reg, condition); | 2290 | try self.genSetReg(cond_ty, cond_reg, condition); |
| 2302 | | 2291 | |
| 2303 | return try self.addInst(.{ | 2292 | return try self.addInst(.{ |
| 2304 | .tag = .bne, | 2293 | .tag = .beq, |
| 2305 | .data = .{ | 2294 | .data = .{ |
| 2306 | .b_type = .{ | 2295 | .b_type = .{ |
| 2307 | .rs1 = cond_reg, | 2296 | .rs1 = cond_reg, |
| ... | @@ -2729,8 +2718,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner | ... | @@ -2729,8 +2718,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner |
| 2729 | => { | 2718 | => { |
| 2730 | // TODO: remove this lock in favor of a copyToTmpRegister when we load 64 bit immediates with | 2719 | // TODO: remove this lock in favor of a copyToTmpRegister when we load 64 bit immediates with |
| 2731 | // a register allocation. | 2720 | // a register allocation. |
| 2732 | const reg = try self.register_manager.allocReg(null, gp); | 2721 | const reg, const reg_lock = try self.allocReg(); |
| 2733 | const reg_lock = self.register_manager.lockRegAssumeUnused(reg); | | |
| 2734 | defer self.register_manager.unlockReg(reg_lock); | 2722 | defer self.register_manager.unlockReg(reg_lock); |
| 2735 | | 2723 | |
| 2736 | try self.genSetReg(ty, reg, src_val); | 2724 | try self.genSetReg(ty, reg, src_val); |
| ... | @@ -2939,8 +2927,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! | ... | @@ -2939,8 +2927,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError! |
| 2939 | // TODO: use a more advanced myriad seq to do this without a reg. | 2927 | // TODO: use a more advanced myriad seq to do this without a reg. |
| 2940 | // see: https://github.com/llvm/llvm-project/blob/081a66ffacfe85a37ff775addafcf3371e967328/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMatInt.cpp#L224 | 2928 | // see: https://github.com/llvm/llvm-project/blob/081a66ffacfe85a37ff775addafcf3371e967328/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMatInt.cpp#L224 |
| 2941 | | 2929 | |
| 2942 | const temp = try self.register_manager.allocReg(null, gp); | 2930 | const temp, const temp_lock = try self.allocReg(); |
| 2943 | const temp_lock = self.register_manager.lockRegAssumeUnused(temp); | | |
| 2944 | defer self.register_manager.unlockReg(temp_lock); | 2931 | defer self.register_manager.unlockReg(temp_lock); |
| 2945 | | 2932 | |
| 2946 | const lo32: i32 = @truncate(x); | 2933 | const lo32: i32 = @truncate(x); |