| ... | ... | @@ -736,6 +736,15 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 736 | 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 | 748 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 740 | 749 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 741 | 750 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |
| ... | ... | @@ -983,67 +992,36 @@ fn binOpRegister( |
| 983 | 992 | lhs_ty: Type, |
| 984 | 993 | rhs_ty: Type, |
| 985 | 994 | ) !MCValue { |
| 986 | | const lhs_is_register = lhs == .register; |
| 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); |
| 995 | _ = maybe_inst; |
| 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); |
| 1010 | | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1004 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 1011 | 1005 | |
| 1012 | | const rhs_reg = if (rhs_is_register) rhs.register else blk: { |
| 1013 | | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| 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 }); |
| 1006 | const rhs_reg, const rhs_lock = blk: { |
| 1007 | if (rhs == .register) break :blk .{ rhs.register, null }; |
| 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); |
| 1025 | | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1013 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 1026 | 1014 | |
| 1027 | | const dest_reg = if (maybe_inst) |inst| blk: { |
| 1028 | | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 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); |
| 1015 | const dest_reg, const dest_lock = try self.allocReg(); |
| 1016 | defer self.register_manager.unlockReg(dest_lock); |
| 1041 | 1017 | |
| 1042 | 1018 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1043 | 1019 | .add => .add, |
| 1044 | 1020 | .sub => .sub, |
| 1045 | 1021 | .cmp_eq => .cmp_eq, |
| 1046 | 1022 | .cmp_gt => .cmp_gt, |
| 1023 | .cmp_gte => .cmp_gte, |
| 1024 | .cmp_lt => .cmp_lt, |
| 1047 | 1025 | .shl => .sllw, |
| 1048 | 1026 | .shr => .srlw, |
| 1049 | 1027 | else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}), |
| ... | ... | @@ -1080,48 +1058,28 @@ fn binOpImm( |
| 1080 | 1058 | rhs_ty: Type, |
| 1081 | 1059 | ) !MCValue { |
| 1082 | 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) |
| 1087 | | self.register_manager.lockReg(lhs.register) |
| 1088 | | else |
| 1089 | | null; |
| 1090 | | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1065 | const lhs_reg, const lhs_lock = blk: { |
| 1066 | if (lhs == .register) break :blk .{ lhs.register, null }; |
| 1091 | 1067 | |
| 1092 | | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1093 | | |
| 1094 | | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 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; |
| 1068 | const lhs_reg, const lhs_lock = try self.allocReg(); |
| 1069 | try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1070 | break :blk .{ lhs_reg, lhs_lock }; |
| 1105 | 1071 | }; |
| 1106 | | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); |
| 1107 | | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1072 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 1108 | 1073 | |
| 1109 | | const dest_reg = if (maybe_inst) |inst| blk: { |
| 1110 | | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 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); |
| 1074 | const dest_reg, const dest_lock = try self.allocReg(); |
| 1075 | defer self.register_manager.unlockReg(dest_lock); |
| 1120 | 1076 | |
| 1121 | 1077 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1122 | 1078 | .shl => .slli, |
| 1123 | 1079 | .shr => .srli, |
| 1124 | 1080 | .cmp_gte => .cmp_imm_gte, |
| 1081 | .cmp_eq => .cmp_imm_eq, |
| 1082 | .add => .addi, |
| 1125 | 1083 | else => return self.fail("TODO: binOpImm {s}", .{@tagName(tag)}), |
| 1126 | 1084 | }; |
| 1127 | 1085 | |
| ... | ... | @@ -1129,6 +1087,8 @@ fn binOpImm( |
| 1129 | 1087 | switch (mir_tag) { |
| 1130 | 1088 | .slli, |
| 1131 | 1089 | .srli, |
| 1090 | .addi, |
| 1091 | .cmp_imm_eq, |
| 1132 | 1092 | => { |
| 1133 | 1093 | _ = try self.addInst(.{ |
| 1134 | 1094 | .tag = mir_tag, |
| ... | ... | @@ -1156,8 +1116,6 @@ fn binOpImm( |
| 1156 | 1116 | else => unreachable, |
| 1157 | 1117 | } |
| 1158 | 1118 | |
| 1159 | | // generate the struct for overflow checks |
| 1160 | | |
| 1161 | 1119 | return MCValue{ .register = dest_reg }; |
| 1162 | 1120 | } |
| 1163 | 1121 | |
| ... | ... | @@ -1216,6 +1174,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1216 | 1174 | } |
| 1217 | 1175 | |
| 1218 | 1176 | fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1177 | const mod = self.bin_file.comp.module.?; |
| 1219 | 1178 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 1220 | 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 | 1184 | const lhs_ty = self.typeOf(extra.lhs); |
| 1226 | 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 | 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 | 1729 | .dead => unreachable, |
| 1750 | 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 | 1741 | .register => |reg| { |
| 1753 | 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 | 2154 | const cond_reg_lock = self.register_manager.lockRegAssumeUnused(cond_reg); |
| 2166 | 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 | 2158 | const reloc = try self.condBr(cond_ty, cond, cond_reg); |
| 2170 | 2159 | |
| 2171 | 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 | 2290 | try self.genSetReg(cond_ty, cond_reg, condition); |
| 2302 | 2291 | |
| 2303 | 2292 | return try self.addInst(.{ |
| 2304 | | .tag = .bne, |
| 2293 | .tag = .beq, |
| 2305 | 2294 | .data = .{ |
| 2306 | 2295 | .b_type = .{ |
| 2307 | 2296 | .rs1 = cond_reg, |
| ... | ... | @@ -2729,8 +2718,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner |
| 2729 | 2718 | => { |
| 2730 | 2719 | // TODO: remove this lock in favor of a copyToTmpRegister when we load 64 bit immediates with |
| 2731 | 2720 | // a register allocation. |
| 2732 | | const reg = try self.register_manager.allocReg(null, gp); |
| 2733 | | const reg_lock = self.register_manager.lockRegAssumeUnused(reg); |
| 2721 | const reg, const reg_lock = try self.allocReg(); |
| 2734 | 2722 | defer self.register_manager.unlockReg(reg_lock); |
| 2735 | 2723 | |
| 2736 | 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 | 2927 | // TODO: use a more advanced myriad seq to do this without a reg. |
| 2940 | 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); |
| 2943 | | const temp_lock = self.register_manager.lockRegAssumeUnused(temp); |
| 2930 | const temp, const temp_lock = try self.allocReg(); |
| 2944 | 2931 | defer self.register_manager.unlockReg(temp_lock); |
| 2945 | 2932 | |
| 2946 | 2933 | const lo32: i32 = @truncate(x); |