| ... | @@ -586,23 +586,23 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -586,23 +586,23 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 586 | | 586 | |
| 587 | switch (air_tags[inst]) { | 587 | switch (air_tags[inst]) { |
| 588 | // zig fmt: off | 588 | // zig fmt: off |
| 589 | .add => try self.airAdd(inst), | 589 | .add => try self.airBinOp(inst), |
| 590 | .addwrap => try self.airAdd(inst), | 590 | .addwrap => try self.airBinOp(inst), |
| 591 | .add_sat => try self.airAddSat(inst), | 591 | .add_sat => try self.airAddSat(inst), |
| 592 | .sub => try self.airSub(inst), | 592 | .sub => try self.airBinOp(inst), |
| 593 | .subwrap => try self.airSub(inst), | 593 | .subwrap => try self.airBinOp(inst), |
| 594 | .sub_sat => try self.airSubSat(inst), | 594 | .sub_sat => try self.airSubSat(inst), |
| 595 | .mul => try self.airMul(inst), | 595 | .mul => try self.airMulDivBinOp(inst), |
| 596 | .mulwrap => try self.airMul(inst), | 596 | .mulwrap => try self.airMulDivBinOp(inst), |
| 597 | .mul_sat => try self.airMulSat(inst), | 597 | .mul_sat => try self.airMulSat(inst), |
| 598 | .rem => try self.airRem(inst), | 598 | .rem => try self.airMulDivBinOp(inst), |
| 599 | .mod => try self.airMod(inst), | 599 | .mod => try self.airMulDivBinOp(inst), |
| 600 | .shl, .shl_exact => try self.airShl(inst), | 600 | .shl, .shl_exact => try self.airShlShrBinOp(inst), |
| 601 | .shl_sat => try self.airShlSat(inst), | 601 | .shl_sat => try self.airShlSat(inst), |
| 602 | .min => try self.airMin(inst), | 602 | .min => try self.airMin(inst), |
| 603 | .max => try self.airMax(inst), | 603 | .max => try self.airMax(inst), |
| 604 | .ptr_add => try self.airPtrAdd(inst), | 604 | .ptr_add => try self.airBinOp(inst), |
| 605 | .ptr_sub => try self.airPtrSub(inst), | 605 | .ptr_sub => try self.airBinOp(inst), |
| 606 | .slice => try self.airSlice(inst), | 606 | .slice => try self.airSlice(inst), |
| 607 | | 607 | |
| 608 | .sqrt, | 608 | .sqrt, |
| ... | @@ -621,12 +621,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -621,12 +621,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 621 | .trunc_float, | 621 | .trunc_float, |
| 622 | => try self.airUnaryMath(inst), | 622 | => try self.airUnaryMath(inst), |
| 623 | | 623 | |
| 624 | .add_with_overflow => try self.airAddWithOverflow(inst), | 624 | .add_with_overflow => try self.airAddSubShlWithOverflow(inst), |
| 625 | .sub_with_overflow => try self.airSubWithOverflow(inst), | 625 | .sub_with_overflow => try self.airAddSubShlWithOverflow(inst), |
| 626 | .mul_with_overflow => try self.airMulWithOverflow(inst), | 626 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 627 | .shl_with_overflow => try self.airShlWithOverflow(inst), | 627 | .shl_with_overflow => try self.airAddSubShlWithOverflow(inst), |
| 628 | | 628 | |
| 629 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), | 629 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airMulDivBinOp(inst), |
| 630 | | 630 | |
| 631 | .cmp_lt => try self.airCmp(inst, .lt), | 631 | .cmp_lt => try self.airCmp(inst, .lt), |
| 632 | .cmp_lte => try self.airCmp(inst, .lte), | 632 | .cmp_lte => try self.airCmp(inst, .lte), |
| ... | @@ -638,12 +638,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -638,12 +638,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 638 | .cmp_vector => try self.airCmpVector(inst), | 638 | .cmp_vector => try self.airCmpVector(inst), |
| 639 | .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), | 639 | .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), |
| 640 | | 640 | |
| 641 | .bool_and => try self.airBoolOp(inst), | 641 | .bool_and => try self.airBinOp(inst), |
| 642 | .bool_or => try self.airBoolOp(inst), | 642 | .bool_or => try self.airBinOp(inst), |
| 643 | .bit_and => try self.airBitAnd(inst), | 643 | .bit_and => try self.airBinOp(inst), |
| 644 | .bit_or => try self.airBitOr(inst), | 644 | .bit_or => try self.airBinOp(inst), |
| 645 | .xor => try self.airXor(inst), | 645 | .xor => try self.airBinOp(inst), |
| 646 | .shr, .shr_exact => try self.airShr(inst), | 646 | .shr, .shr_exact => try self.airShlShrBinOp(inst), |
| 647 | | 647 | |
| 648 | .alloc => try self.airAlloc(inst), | 648 | .alloc => try self.airAlloc(inst), |
| 649 | .ret_ptr => try self.airRetPtr(inst), | 649 | .ret_ptr => try self.airRetPtr(inst), |
| ... | @@ -959,6 +959,12 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { | ... | @@ -959,6 +959,12 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 959 | } | 959 | } |
| 960 | } | 960 | } |
| 961 | | 961 | |
| | 962 | pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [count]Register) !void { |
| | 963 | for (registers) |reg| { |
| | 964 | try self.register_manager.getReg(reg, null); |
| | 965 | } |
| | 966 | } |
| | 967 | |
| 962 | /// Copies a value to a register without tracking the register. The register is not considered | 968 | /// Copies a value to a register without tracking the register. The register is not considered |
| 963 | /// allocated. A second call to `copyToTmpRegister` may return the same register. | 969 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 964 | /// This can have a side effect of spilling instructions to the stack to free up a register. | 970 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| ... | @@ -1089,8 +1095,15 @@ fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1089,8 +1095,15 @@ fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 1089 | | 1095 | |
| 1090 | fn airNot(self: *Self, inst: Air.Inst.Index) !void { | 1096 | fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1091 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1097 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1092 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 1098 | |
| 1093 | const operand = try self.resolveInst(ty_op.operand); | 1099 | if (self.liveness.isUnused(inst)) { |
| | 1100 | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); |
| | 1101 | } |
| | 1102 | |
| | 1103 | const operand_ty = self.air.typeOf(ty_op.operand); |
| | 1104 | const operand = try self.resolveInst(ty_op.operand); |
| | 1105 | |
| | 1106 | const result: MCValue = result: { |
| 1094 | switch (operand) { | 1107 | switch (operand) { |
| 1095 | .dead => unreachable, | 1108 | .dead => unreachable, |
| 1096 | .unreach => unreachable, | 1109 | .unreach => unreachable, |
| ... | @@ -1122,7 +1135,29 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1122,7 +1135,29 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1122 | }, | 1135 | }, |
| 1123 | else => {}, | 1136 | else => {}, |
| 1124 | } | 1137 | } |
| 1125 | break :result try self.genBinMathOp(inst, ty_op.operand, .bool_true); | 1138 | |
| | 1139 | const operand_lock: ?RegisterLock = switch (operand) { |
| | 1140 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| | 1141 | else => null, |
| | 1142 | }; |
| | 1143 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); |
| | 1144 | |
| | 1145 | const dst_mcv: MCValue = blk: { |
| | 1146 | if (self.reuseOperand(inst, ty_op.operand, 0, operand) and operand.isRegister()) { |
| | 1147 | break :blk operand; |
| | 1148 | } |
| | 1149 | break :blk try self.copyToRegisterWithInstTracking(inst, operand_ty, operand); |
| | 1150 | }; |
| | 1151 | const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { |
| | 1152 | .register => |reg| self.register_manager.lockReg(reg), |
| | 1153 | else => null, |
| | 1154 | }; |
| | 1155 | defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); |
| | 1156 | |
| | 1157 | const mask = ~@as(u64, 0); |
| | 1158 | try self.genBinOpMir(.xor, operand_ty, dst_mcv, .{ .immediate = mask }); |
| | 1159 | |
| | 1160 | break :result dst_mcv; |
| 1126 | }; | 1161 | }; |
| 1127 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1162 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1128 | } | 1163 | } |
| ... | @@ -1159,7 +1194,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1159,7 +1194,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { |
| 1159 | }; | 1194 | }; |
| 1160 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); | 1195 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 1161 | | 1196 | |
| 1162 | try self.genBinMathOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv); | 1197 | try self.genBinOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv); |
| 1163 | | 1198 | |
| 1164 | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, rhs_mcv); | 1199 | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, rhs_mcv); |
| 1165 | _ = try self.addInst(.{ | 1200 | _ = try self.addInst(.{ |
| ... | @@ -1185,75 +1220,6 @@ fn airMax(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1185,75 +1220,6 @@ fn airMax(self: *Self, inst: Air.Inst.Index) !void { |
| 1185 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1220 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1186 | } | 1221 | } |
| 1187 | | 1222 | |
| 1188 | fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { | | |
| 1189 | const dst_ty = self.air.typeOfIndex(inst); | | |
| 1190 | const elem_size = dst_ty.elemType2().abiSize(self.target.*); | | |
| 1191 | const ptr = try self.resolveInst(op_lhs); | | |
| 1192 | const offset = try self.resolveInst(op_rhs); | | |
| 1193 | const offset_ty = self.air.typeOf(op_rhs); | | |
| 1194 | | | |
| 1195 | const offset_lock: ?RegisterLock = switch (offset) { | | |
| 1196 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | | |
| 1197 | else => null, | | |
| 1198 | }; | | |
| 1199 | defer if (offset_lock) |lock| self.register_manager.unlockReg(lock); | | |
| 1200 | | | |
| 1201 | const dst_mcv: MCValue = blk: { | | |
| 1202 | if (self.reuseOperand(inst, op_lhs, 0, ptr)) { | | |
| 1203 | if (ptr.isMemory() or ptr.isRegister()) break :blk ptr; | | |
| 1204 | } | | |
| 1205 | break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, ptr) }; | | |
| 1206 | }; | | |
| 1207 | | | |
| 1208 | const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { | | |
| 1209 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | | |
| 1210 | else => null, | | |
| 1211 | }; | | |
| 1212 | defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); | | |
| 1213 | | | |
| 1214 | const offset_mcv: MCValue = blk: { | | |
| 1215 | if (self.reuseOperand(inst, op_rhs, 1, offset)) { | | |
| 1216 | if (offset.isRegister()) break :blk offset; | | |
| 1217 | } | | |
| 1218 | break :blk MCValue{ .register = try self.copyToTmpRegister(offset_ty, offset) }; | | |
| 1219 | }; | | |
| 1220 | | | |
| 1221 | const offset_mcv_lock: ?RegisterLock = switch (offset_mcv) { | | |
| 1222 | .register => |reg| self.register_manager.lockReg(reg), | | |
| 1223 | else => null, | | |
| 1224 | }; | | |
| 1225 | defer if (offset_mcv_lock) |lock| self.register_manager.unlockReg(lock); | | |
| 1226 | | | |
| 1227 | try self.genIntMulComplexOpMir(offset_ty, offset_mcv, .{ .immediate = elem_size }); | | |
| 1228 | | | |
| 1229 | const tag = self.air.instructions.items(.tag)[inst]; | | |
| 1230 | switch (tag) { | | |
| 1231 | .ptr_add => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, offset_mcv), | | |
| 1232 | .ptr_sub => try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, offset_mcv), | | |
| 1233 | else => unreachable, | | |
| 1234 | } | | |
| 1235 | | | |
| 1236 | return dst_mcv; | | |
| 1237 | } | | |
| 1238 | | | |
| 1239 | fn airPtrAdd(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1240 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1241 | const result = if (self.liveness.isUnused(inst)) | | |
| 1242 | .dead | | |
| 1243 | else | | |
| 1244 | try self.genPtrBinMathOp(inst, bin_op.lhs, bin_op.rhs); | | |
| 1245 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1246 | } | | |
| 1247 | | | |
| 1248 | fn airPtrSub(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1249 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1250 | const result = if (self.liveness.isUnused(inst)) | | |
| 1251 | .dead | | |
| 1252 | else | | |
| 1253 | try self.genPtrBinMathOp(inst, bin_op.lhs, bin_op.rhs); | | |
| 1254 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1255 | } | | |
| 1256 | | | |
| 1257 | fn airSlice(self: *Self, inst: Air.Inst.Index) !void { | 1223 | fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1258 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 1224 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1259 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; | 1225 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| ... | @@ -1275,84 +1241,50 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1275,84 +1241,50 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1275 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1241 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1276 | } | 1242 | } |
| 1277 | | 1243 | |
| 1278 | fn airAdd(self: *Self, inst: Air.Inst.Index) !void { | 1244 | fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1279 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1245 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1280 | const result: MCValue = if (self.liveness.isUnused(inst)) | | |
| 1281 | .dead | | |
| 1282 | else | | |
| 1283 | try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); | | |
| 1284 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1285 | } | | |
| 1286 | | 1246 | |
| 1287 | fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void { | 1247 | if (self.liveness.isUnused(inst)) { |
| 1288 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1248 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1289 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1249 | } |
| 1290 | .dead | 1250 | |
| 1291 | else | 1251 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1292 | try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); | 1252 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1293 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1253 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1294 | } | 1254 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| | 1255 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| | 1256 | |
| | 1257 | const result = try self.genBinOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); |
| 1295 | | 1258 | |
| 1296 | fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1297 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1298 | const result: MCValue = if (self.liveness.isUnused(inst)) | | |
| 1299 | .dead | | |
| 1300 | else | | |
| 1301 | return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}); | | |
| 1302 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1259 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1303 | } | 1260 | } |
| 1304 | | 1261 | |
| 1305 | /// Result is always a register. | 1262 | fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1306 | fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { | 1263 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1307 | const dst_ty = self.air.typeOf(op_lhs); | | |
| 1308 | | 1264 | |
| 1309 | const lhs = try self.resolveInst(op_lhs); | 1265 | if (self.liveness.isUnused(inst)) { |
| 1310 | const lhs_lock: ?RegisterLock = switch (lhs) { | 1266 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1311 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | 1267 | } |
| 1312 | else => null, | | |
| 1313 | }; | | |
| 1314 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); | | |
| 1315 | | 1268 | |
| 1316 | const rhs = try self.resolveInst(op_rhs); | 1269 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1317 | const rhs_lock: ?RegisterLock = switch (rhs) { | 1270 | const ty = self.air.typeOfIndex(inst); |
| 1318 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | | |
| 1319 | else => null, | | |
| 1320 | }; | | |
| 1321 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); | | |
| 1322 | | 1271 | |
| 1323 | const dst_mcv: MCValue = blk: { | 1272 | try self.spillRegisters(2, .{ .rax, .rdx }); |
| 1324 | if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { | | |
| 1325 | break :blk lhs; | | |
| 1326 | } | | |
| 1327 | break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); | | |
| 1328 | }; | | |
| 1329 | const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { | | |
| 1330 | .register => |reg| self.register_manager.lockReg(reg), | | |
| 1331 | else => null, | | |
| 1332 | }; | | |
| 1333 | defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); | | |
| 1334 | | 1273 | |
| 1335 | const rhs_mcv: MCValue = blk: { | 1274 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1336 | if (rhs.isMemory() or rhs.isRegister()) break :blk rhs; | 1275 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1337 | break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) }; | | |
| 1338 | }; | | |
| 1339 | const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) { | | |
| 1340 | .register => |reg| self.register_manager.lockReg(reg), | | |
| 1341 | else => null, | | |
| 1342 | }; | | |
| 1343 | defer if (rhs_mcv_lock) |lock| self.register_manager.unlockReg(lock); | | |
| 1344 | | 1276 | |
| 1345 | try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, rhs_mcv); | 1277 | const result = try self.genMulDivBinOp(tag, inst, ty, lhs, rhs); |
| 1346 | | 1278 | |
| 1347 | return dst_mcv; | 1279 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1348 | } | 1280 | } |
| 1349 | | 1281 | |
| 1350 | fn airSub(self: *Self, inst: Air.Inst.Index) !void { | 1282 | fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1351 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1283 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1352 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1284 | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 1353 | .dead | 1285 | .dead |
| 1354 | else | 1286 | else |
| 1355 | try self.genSubOp(inst, bin_op.lhs, bin_op.rhs); | 1287 | return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}); |
| 1356 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1288 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1357 | } | 1289 | } |
| 1358 | | 1290 | |
| ... | @@ -1365,36 +1297,6 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1365,36 +1297,6 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1365 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1297 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1366 | } | 1298 | } |
| 1367 | | 1299 | |
| 1368 | fn airMul(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1369 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1370 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | | |
| 1371 | const ty = self.air.typeOfIndex(inst); | | |
| 1372 | | | |
| 1373 | if (ty.zigTypeTag() != .Int) { | | |
| 1374 | return self.fail("TODO implement 'mul' for operands of dst type {}", .{ty.zigTypeTag()}); | | |
| 1375 | } | | |
| 1376 | | | |
| 1377 | // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. | | |
| 1378 | try self.register_manager.getReg(.rax, inst); | | |
| 1379 | try self.register_manager.getReg(.rdx, null); | | |
| 1380 | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); | | |
| 1381 | defer for (reg_locks) |reg| { | | |
| 1382 | self.register_manager.unlockReg(reg); | | |
| 1383 | }; | | |
| 1384 | | | |
| 1385 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 1386 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 1387 | | | |
| 1388 | const signedness = ty.intInfo(self.target.*).signedness; | | |
| 1389 | try self.genIntMulDivOpMir(switch (signedness) { | | |
| 1390 | .signed => .imul, | | |
| 1391 | .unsigned => .mul, | | |
| 1392 | }, ty, signedness, lhs, rhs); | | |
| 1393 | break :result MCValue{ .register = .rax }; | | |
| 1394 | }; | | |
| 1395 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1396 | } | | |
| 1397 | | | |
| 1398 | fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { | 1300 | fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1399 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1301 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1400 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1302 | const result: MCValue = if (self.liveness.isUnused(inst)) |
| ... | @@ -1404,62 +1306,62 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1404,62 +1306,62 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1404 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1306 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1405 | } | 1307 | } |
| 1406 | | 1308 | |
| 1407 | fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 1309 | fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1408 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 1310 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| | 1311 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1409 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; | 1312 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1410 | const result = if (self.liveness.isUnused(inst)) .dead else result: { | 1313 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1411 | const ty = self.air.typeOf(bin_op.lhs); | 1314 | const ty = self.air.typeOf(bin_op.lhs); |
| 1412 | | 1315 | const abi_size = ty.abiSize(self.target.*); |
| 1413 | switch (ty.zigTypeTag()) { | 1316 | switch (ty.zigTypeTag()) { |
| 1414 | .Vector => return self.fail("TODO implement add_with_overflow for Vector type", .{}), | 1317 | .Vector => return self.fail("TODO implement add/sub/shl with overflow for Vector type", .{}), |
| 1415 | .Int => { | 1318 | .Int => { |
| 1416 | const int_info = ty.intInfo(self.target.*); | 1319 | if (abi_size > 8) { |
| 1417 | | 1320 | return self.fail("TODO implement add/sub/shl with overflow for Ints larger than 64bits", .{}); |
| 1418 | if (int_info.bits > 64) { | | |
| 1419 | return self.fail("TODO implement add_with_overflow for Ints larger than 64bits", .{}); | | |
| 1420 | } | 1321 | } |
| 1421 | | 1322 | |
| 1422 | try self.spillCompareFlagsIfOccupied(); | 1323 | try self.spillCompareFlagsIfOccupied(); |
| 1423 | self.compare_flags_inst = inst; | | |
| 1424 | | 1324 | |
| 1425 | const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); | 1325 | if (tag == .shl_with_overflow) { |
| 1426 | const result: MCValue = switch (int_info.signedness) { | 1326 | try self.spillRegisters(1, .{.rcx}); |
| 1427 | .signed => .{ .register_overflow_signed = partial.register }, | 1327 | } |
| 1428 | .unsigned => .{ .register_overflow_unsigned = partial.register }, | | |
| 1429 | }; | | |
| 1430 | break :result result; | | |
| 1431 | }, | | |
| 1432 | else => unreachable, | | |
| 1433 | } | | |
| 1434 | }; | | |
| 1435 | | 1328 | |
| 1436 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1329 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1437 | } | 1330 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1438 | | 1331 | |
| 1439 | fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 1332 | const partial: MCValue = switch (tag) { |
| 1440 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 1333 | .add_with_overflow => try self.genBinOp(.add, null, lhs, rhs, ty, ty), |
| 1441 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; | 1334 | .sub_with_overflow => try self.genBinOp(.sub, null, lhs, rhs, ty, ty), |
| 1442 | const result = if (self.liveness.isUnused(inst)) .dead else result: { | 1335 | .shl_with_overflow => blk: { |
| 1443 | const ty = self.air.typeOf(bin_op.lhs); | 1336 | const shift_ty = self.air.typeOf(bin_op.rhs); |
| | 1337 | break :blk try self.genShiftBinOp(.shl, null, lhs, rhs, ty, shift_ty); |
| | 1338 | }, |
| | 1339 | else => unreachable, |
| | 1340 | }; |
| 1444 | | 1341 | |
| 1445 | switch (ty.zigTypeTag()) { | | |
| 1446 | .Vector => return self.fail("TODO implement sub_with_overflow for Vector type", .{}), | | |
| 1447 | .Int => { | | |
| 1448 | const int_info = ty.intInfo(self.target.*); | 1342 | const int_info = ty.intInfo(self.target.*); |
| 1449 | | 1343 | |
| 1450 | if (int_info.bits > 64) { | 1344 | if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { |
| 1451 | return self.fail("TODO implement sub_with_overflow for Ints larger than 64bits", .{}); | 1345 | self.compare_flags_inst = inst; |
| | 1346 | |
| | 1347 | const result: MCValue = switch (int_info.signedness) { |
| | 1348 | .signed => .{ .register_overflow_signed = partial.register }, |
| | 1349 | .unsigned => .{ .register_overflow_unsigned = partial.register }, |
| | 1350 | }; |
| | 1351 | break :result result; |
| 1452 | } | 1352 | } |
| 1453 | | 1353 | |
| 1454 | try self.spillCompareFlagsIfOccupied(); | 1354 | self.compare_flags_inst = null; |
| 1455 | self.compare_flags_inst = inst; | | |
| 1456 | | 1355 | |
| 1457 | const partial = try self.genSubOp(inst, bin_op.lhs, bin_op.rhs); | 1356 | const tuple_ty = self.air.typeOfIndex(inst); |
| 1458 | const result: MCValue = switch (int_info.signedness) { | 1357 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); |
| 1459 | .signed => .{ .register_overflow_signed = partial.register }, | 1358 | const tuple_align = tuple_ty.abiAlignment(self.target.*); |
| 1460 | .unsigned => .{ .register_overflow_unsigned = partial.register }, | 1359 | const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); |
| 1461 | }; | 1360 | const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); |
| 1462 | break :result result; | 1361 | |
| | 1362 | try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, partial.register); |
| | 1363 | |
| | 1364 | break :result MCValue{ .stack_offset = stack_offset }; |
| 1463 | }, | 1365 | }, |
| 1464 | else => unreachable, | 1366 | else => unreachable, |
| 1465 | } | 1367 | } |
| ... | @@ -1468,6 +1370,75 @@ fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1468,6 +1370,75 @@ fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1468 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1370 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1469 | } | 1371 | } |
| 1470 | | 1372 | |
| | 1373 | fn genSetStackTruncatedOverflowCompare( |
| | 1374 | self: *Self, |
| | 1375 | ty: Type, |
| | 1376 | stack_offset: i32, |
| | 1377 | overflow_bit_offset: i32, |
| | 1378 | reg: Register, |
| | 1379 | ) !void { |
| | 1380 | const reg_lock = self.register_manager.lockReg(reg); |
| | 1381 | defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); |
| | 1382 | |
| | 1383 | const int_info = ty.intInfo(self.target.*); |
| | 1384 | const extended_ty = switch (int_info.signedness) { |
| | 1385 | .signed => Type.isize, |
| | 1386 | .unsigned => ty, |
| | 1387 | }; |
| | 1388 | |
| | 1389 | const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }); |
| | 1390 | const temp_regs_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs); |
| | 1391 | defer for (temp_regs_locks) |rreg| { |
| | 1392 | self.register_manager.unlockReg(rreg); |
| | 1393 | }; |
| | 1394 | |
| | 1395 | const overflow_reg = temp_regs[0]; |
| | 1396 | const flags: u2 = switch (int_info.signedness) { |
| | 1397 | .signed => 0b00, |
| | 1398 | .unsigned => 0b10, |
| | 1399 | }; |
| | 1400 | _ = try self.addInst(.{ |
| | 1401 | .tag = .cond_set_byte_overflow, |
| | 1402 | .ops = (Mir.Ops{ |
| | 1403 | .reg1 = overflow_reg.to8(), |
| | 1404 | .flags = flags, |
| | 1405 | }).encode(), |
| | 1406 | .data = undefined, |
| | 1407 | }); |
| | 1408 | |
| | 1409 | const scratch_reg = temp_regs[1]; |
| | 1410 | try self.genSetReg(extended_ty, scratch_reg, .{ .register = reg }); |
| | 1411 | try self.truncateRegister(ty, scratch_reg); |
| | 1412 | try self.genBinOpMir( |
| | 1413 | .cmp, |
| | 1414 | extended_ty, |
| | 1415 | .{ .register = reg }, |
| | 1416 | .{ .register = scratch_reg }, |
| | 1417 | ); |
| | 1418 | |
| | 1419 | const eq_reg = temp_regs[2]; |
| | 1420 | _ = try self.addInst(.{ |
| | 1421 | .tag = .cond_set_byte_eq_ne, |
| | 1422 | .ops = (Mir.Ops{ |
| | 1423 | .reg1 = eq_reg.to8(), |
| | 1424 | .flags = 0b00, |
| | 1425 | }).encode(), |
| | 1426 | .data = undefined, |
| | 1427 | }); |
| | 1428 | |
| | 1429 | try self.genBinOpMir( |
| | 1430 | .@"or", |
| | 1431 | Type.u8, |
| | 1432 | .{ .register = overflow_reg }, |
| | 1433 | .{ .register = eq_reg }, |
| | 1434 | ); |
| | 1435 | |
| | 1436 | try self.genSetStack(ty, stack_offset, .{ .register = scratch_reg }, .{}); |
| | 1437 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ |
| | 1438 | .register = overflow_reg.to8(), |
| | 1439 | }, .{}); |
| | 1440 | } |
| | 1441 | |
| 1471 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 1442 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1472 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 1443 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1473 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; | 1444 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| ... | @@ -1477,41 +1448,31 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1477,41 +1448,31 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1477 | } | 1448 | } |
| 1478 | | 1449 | |
| 1479 | const ty = self.air.typeOf(bin_op.lhs); | 1450 | const ty = self.air.typeOf(bin_op.lhs); |
| | 1451 | const abi_size = ty.abiSize(self.target.*); |
| 1480 | const result: MCValue = result: { | 1452 | const result: MCValue = result: { |
| 1481 | switch (ty.zigTypeTag()) { | 1453 | switch (ty.zigTypeTag()) { |
| 1482 | .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}), | 1454 | .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}), |
| 1483 | .Int => { | 1455 | .Int => { |
| 1484 | const int_info = ty.intInfo(self.target.*); | 1456 | if (abi_size > 8) { |
| 1485 | | | |
| 1486 | if (int_info.bits > 64) { | | |
| 1487 | return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{}); | 1457 | return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{}); |
| 1488 | } | 1458 | } |
| 1489 | | 1459 | |
| 1490 | if (math.isPowerOfTwo(int_info.bits)) { | 1460 | const int_info = ty.intInfo(self.target.*); |
| | 1461 | |
| | 1462 | if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { |
| 1491 | try self.spillCompareFlagsIfOccupied(); | 1463 | try self.spillCompareFlagsIfOccupied(); |
| 1492 | self.compare_flags_inst = inst; | 1464 | self.compare_flags_inst = inst; |
| 1493 | | 1465 | |
| 1494 | // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. | 1466 | try self.spillRegisters(2, .{ .rax, .rdx }); |
| 1495 | try self.register_manager.getReg(.rax, inst); | | |
| 1496 | try self.register_manager.getReg(.rdx, null); | | |
| 1497 | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); | | |
| 1498 | defer for (reg_locks) |reg| { | | |
| 1499 | self.register_manager.unlockReg(reg); | | |
| 1500 | }; | | |
| 1501 | | 1467 | |
| 1502 | const lhs = try self.resolveInst(bin_op.lhs); | 1468 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1503 | const rhs = try self.resolveInst(bin_op.rhs); | 1469 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1504 | | 1470 | |
| 1505 | try self.genIntMulDivOpMir(switch (int_info.signedness) { | 1471 | const partial = try self.genMulDivBinOp(.mul, null, ty, lhs, rhs); |
| 1506 | .signed => .imul, | 1472 | break :result switch (int_info.signedness) { |
| 1507 | .unsigned => .mul, | 1473 | .signed => MCValue{ .register_overflow_signed = partial.register }, |
| 1508 | }, ty, int_info.signedness, lhs, rhs); | 1474 | .unsigned => MCValue{ .register_overflow_unsigned = partial.register }, |
| 1509 | | | |
| 1510 | const result: MCValue = switch (int_info.signedness) { | | |
| 1511 | .signed => .{ .register_overflow_signed = .rax }, | | |
| 1512 | .unsigned => .{ .register_overflow_unsigned = .rax }, | | |
| 1513 | }; | 1475 | }; |
| 1514 | break :result result; | | |
| 1515 | } | 1476 | } |
| 1516 | | 1477 | |
| 1517 | try self.spillCompareFlagsIfOccupied(); | 1478 | try self.spillCompareFlagsIfOccupied(); |
| ... | @@ -1551,88 +1512,24 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1551,88 +1512,24 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1551 | break :dst_reg dst_reg; | 1512 | break :dst_reg dst_reg; |
| 1552 | }, | 1513 | }, |
| 1553 | .unsigned => { | 1514 | .unsigned => { |
| 1554 | // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. | 1515 | try self.spillRegisters(2, .{ .rax, .rdx }); |
| 1555 | try self.register_manager.getReg(.rax, null); | | |
| 1556 | try self.register_manager.getReg(.rdx, null); | | |
| 1557 | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); | | |
| 1558 | defer for (reg_locks) |reg| { | | |
| 1559 | self.register_manager.unlockReg(reg); | | |
| 1560 | }; | | |
| 1561 | | 1516 | |
| 1562 | const lhs = try self.resolveInst(bin_op.lhs); | 1517 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1563 | const rhs = try self.resolveInst(bin_op.rhs); | 1518 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1564 | | 1519 | |
| 1565 | try self.genIntMulDivOpMir(.mul, ty, .unsigned, lhs, rhs); | 1520 | const dst_mcv = try self.genMulDivBinOp(.mul, null, ty, lhs, rhs); |
| 1566 | | 1521 | break :dst_reg dst_mcv.register; |
| 1567 | break :dst_reg registerAlias(.rax, @intCast(u32, ty.abiSize(self.target.*))); | | |
| 1568 | }, | 1522 | }, |
| 1569 | } | 1523 | } |
| 1570 | }; | 1524 | }; |
| 1571 | const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); | | |
| 1572 | defer self.register_manager.unlockReg(dst_reg_lock); | | |
| 1573 | | 1525 | |
| 1574 | const tuple_ty = self.air.typeOfIndex(inst); | 1526 | const tuple_ty = self.air.typeOfIndex(inst); |
| 1575 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); | 1527 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); |
| 1576 | const tuple_align = tuple_ty.abiAlignment(self.target.*); | 1528 | const tuple_align = tuple_ty.abiAlignment(self.target.*); |
| 1577 | const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); | 1529 | const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); |
| 1578 | | | |
| 1579 | const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); | 1530 | const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); |
| 1580 | const extended_ty = switch (int_info.signedness) { | | |
| 1581 | .signed => Type.isize, | | |
| 1582 | .unsigned => ty, | | |
| 1583 | }; | | |
| 1584 | | | |
| 1585 | const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }); | | |
| 1586 | const temp_regs_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs); | | |
| 1587 | defer for (temp_regs_locks) |reg| { | | |
| 1588 | self.register_manager.unlockReg(reg); | | |
| 1589 | }; | | |
| 1590 | | | |
| 1591 | const overflow_reg = temp_regs[0]; | | |
| 1592 | const flags: u2 = switch (int_info.signedness) { | | |
| 1593 | .signed => 0b00, | | |
| 1594 | .unsigned => 0b10, | | |
| 1595 | }; | | |
| 1596 | _ = try self.addInst(.{ | | |
| 1597 | .tag = .cond_set_byte_overflow, | | |
| 1598 | .ops = (Mir.Ops{ | | |
| 1599 | .reg1 = overflow_reg.to8(), | | |
| 1600 | .flags = flags, | | |
| 1601 | }).encode(), | | |
| 1602 | .data = undefined, | | |
| 1603 | }); | | |
| 1604 | | | |
| 1605 | const scratch_reg = temp_regs[1]; | | |
| 1606 | try self.genSetReg(extended_ty, scratch_reg, .{ .register = dst_reg }); | | |
| 1607 | try self.truncateRegister(ty, scratch_reg); | | |
| 1608 | try self.genBinMathOpMir( | | |
| 1609 | .cmp, | | |
| 1610 | extended_ty, | | |
| 1611 | .{ .register = dst_reg }, | | |
| 1612 | .{ .register = scratch_reg }, | | |
| 1613 | ); | | |
| 1614 | | | |
| 1615 | const eq_reg = temp_regs[2]; | | |
| 1616 | _ = try self.addInst(.{ | | |
| 1617 | .tag = .cond_set_byte_eq_ne, | | |
| 1618 | .ops = (Mir.Ops{ | | |
| 1619 | .reg1 = eq_reg.to8(), | | |
| 1620 | .flags = 0b00, | | |
| 1621 | }).encode(), | | |
| 1622 | .data = undefined, | | |
| 1623 | }); | | |
| 1624 | | 1531 | |
| 1625 | try self.genBinMathOpMir( | 1532 | try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, dst_reg); |
| 1626 | .@"or", | | |
| 1627 | Type.u8, | | |
| 1628 | .{ .register = overflow_reg }, | | |
| 1629 | .{ .register = eq_reg }, | | |
| 1630 | ); | | |
| 1631 | | | |
| 1632 | try self.genSetStack(ty, stack_offset, .{ .register = scratch_reg }, .{}); | | |
| 1633 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ | | |
| 1634 | .register = overflow_reg.to8(), | | |
| 1635 | }, .{}); | | |
| 1636 | | 1533 | |
| 1637 | break :result MCValue{ .stack_offset = stack_offset }; | 1534 | break :result MCValue{ .stack_offset = stack_offset }; |
| 1638 | }, | 1535 | }, |
| ... | @@ -1643,11 +1540,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1643,11 +1540,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1643 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1540 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1644 | } | 1541 | } |
| 1645 | | 1542 | |
| 1646 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1647 | _ = inst; | | |
| 1648 | return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch}); | | |
| 1649 | } | | |
| 1650 | | | |
| 1651 | /// Generates signed or unsigned integer multiplication/division. | 1543 | /// Generates signed or unsigned integer multiplication/division. |
| 1652 | /// Clobbers .rax and .rdx registers. | 1544 | /// Clobbers .rax and .rdx registers. |
| 1653 | /// Quotient is saved in .rax and remainder in .rdx. | 1545 | /// Quotient is saved in .rax and remainder in .rdx. |
| ... | @@ -1664,7 +1556,13 @@ fn genIntMulDivOpMir( | ... | @@ -1664,7 +1556,13 @@ fn genIntMulDivOpMir( |
| 1664 | return self.fail("TODO implement genIntMulDivOpMir for ABI size larger than 8", .{}); | 1556 | return self.fail("TODO implement genIntMulDivOpMir for ABI size larger than 8", .{}); |
| 1665 | } | 1557 | } |
| 1666 | | 1558 | |
| 1667 | try self.genSetReg(ty, .rax, lhs); | 1559 | lhs: { |
| | 1560 | switch (lhs) { |
| | 1561 | .register => |reg| if (reg.to64() == .rax) break :lhs, |
| | 1562 | else => {}, |
| | 1563 | } |
| | 1564 | try self.genSetReg(ty, .rax, lhs); |
| | 1565 | } |
| 1668 | | 1566 | |
| 1669 | switch (signedness) { | 1567 | switch (signedness) { |
| 1670 | .signed => { | 1568 | .signed => { |
| ... | @@ -1727,6 +1625,7 @@ fn genIntMulDivOpMir( | ... | @@ -1727,6 +1625,7 @@ fn genIntMulDivOpMir( |
| 1727 | } | 1625 | } |
| 1728 | } | 1626 | } |
| 1729 | | 1627 | |
| | 1628 | /// Always returns a register. |
| 1730 | /// Clobbers .rax and .rdx registers. | 1629 | /// Clobbers .rax and .rdx registers. |
| 1731 | fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCValue { | 1630 | fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCValue { |
| 1732 | const signedness = ty.intInfo(self.target.*).signedness; | 1631 | const signedness = ty.intInfo(self.target.*).signedness; |
| ... | @@ -1761,301 +1660,56 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa | ... | @@ -1761,301 +1660,56 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa |
| 1761 | .tag = .sar, | 1660 | .tag = .sar, |
| 1762 | .ops = (Mir.Ops{ | 1661 | .ops = (Mir.Ops{ |
| 1763 | .reg1 = divisor.to64(), | 1662 | .reg1 = divisor.to64(), |
| 1764 | .flags = 0b10, | 1663 | .flags = 0b10, |
| 1765 | }).encode(), | 1664 | }).encode(), |
| 1766 | .data = .{ .imm = 63 }, | 1665 | .data = .{ .imm = 63 }, |
| 1767 | }); | 1666 | }); |
| 1768 | _ = try self.addInst(.{ | | |
| 1769 | .tag = .@"test", | | |
| 1770 | .ops = (Mir.Ops{ | | |
| 1771 | .reg1 = .rdx, | | |
| 1772 | .reg2 = .rdx, | | |
| 1773 | }).encode(), | | |
| 1774 | .data = undefined, | | |
| 1775 | }); | | |
| 1776 | _ = try self.addInst(.{ | | |
| 1777 | .tag = .cond_mov_eq, | | |
| 1778 | .ops = (Mir.Ops{ | | |
| 1779 | .reg1 = divisor.to64(), | | |
| 1780 | .reg2 = .rdx, | | |
| 1781 | }).encode(), | | |
| 1782 | .data = undefined, | | |
| 1783 | }); | | |
| 1784 | try self.genBinMathOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); | | |
| 1785 | return MCValue{ .register = divisor }; | | |
| 1786 | } | | |
| 1787 | | | |
| 1788 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1789 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1790 | | | |
| 1791 | if (self.liveness.isUnused(inst)) { | | |
| 1792 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1793 | } | | |
| 1794 | | | |
| 1795 | const tag = self.air.instructions.items(.tag)[inst]; | | |
| 1796 | const ty = self.air.typeOfIndex(inst); | | |
| 1797 | | | |
| 1798 | if (ty.zigTypeTag() != .Int) { | | |
| 1799 | return self.fail("TODO implement {} for operands of dst type {}", .{ tag, ty.zigTypeTag() }); | | |
| 1800 | } | | |
| 1801 | | | |
| 1802 | if (tag == .div_float) { | | |
| 1803 | return self.fail("TODO implement {}", .{tag}); | | |
| 1804 | } | | |
| 1805 | | | |
| 1806 | const signedness = ty.intInfo(self.target.*).signedness; | | |
| 1807 | | | |
| 1808 | // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. | | |
| 1809 | const track_rax: ?Air.Inst.Index = blk: { | | |
| 1810 | if (signedness == .unsigned) break :blk inst; | | |
| 1811 | switch (tag) { | | |
| 1812 | .div_exact, .div_trunc => break :blk inst, | | |
| 1813 | else => break :blk null, | | |
| 1814 | } | | |
| 1815 | }; | | |
| 1816 | try self.register_manager.getReg(.rax, track_rax); | | |
| 1817 | try self.register_manager.getReg(.rdx, null); | | |
| 1818 | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); | | |
| 1819 | defer for (reg_locks) |reg| { | | |
| 1820 | self.register_manager.unlockReg(reg); | | |
| 1821 | }; | | |
| 1822 | | | |
| 1823 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 1824 | const lhs_lock: ?RegisterLock = switch (lhs) { | | |
| 1825 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | | |
| 1826 | else => null, | | |
| 1827 | }; | | |
| 1828 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); | | |
| 1829 | | | |
| 1830 | const rhs: MCValue = blk: { | | |
| 1831 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 1832 | if (signedness == .signed) { | | |
| 1833 | switch (tag) { | | |
| 1834 | .div_floor => { | | |
| 1835 | const rhs_lock: ?RegisterLock = switch (rhs) { | | |
| 1836 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | | |
| 1837 | else => null, | | |
| 1838 | }; | | |
| 1839 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); | | |
| 1840 | | | |
| 1841 | break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs); | | |
| 1842 | }, | | |
| 1843 | else => {}, | | |
| 1844 | } | | |
| 1845 | } | | |
| 1846 | break :blk rhs; | | |
| 1847 | }; | | |
| 1848 | const rhs_lock: ?RegisterLock = switch (rhs) { | | |
| 1849 | .register => |reg| self.register_manager.lockReg(reg), | | |
| 1850 | else => null, | | |
| 1851 | }; | | |
| 1852 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); | | |
| 1853 | | | |
| 1854 | const result: MCValue = result: { | | |
| 1855 | if (signedness == .unsigned) { | | |
| 1856 | try self.genIntMulDivOpMir(.div, ty, signedness, lhs, rhs); | | |
| 1857 | break :result MCValue{ .register = .rax }; | | |
| 1858 | } | | |
| 1859 | | | |
| 1860 | switch (tag) { | | |
| 1861 | .div_exact, .div_trunc => { | | |
| 1862 | try self.genIntMulDivOpMir(switch (signedness) { | | |
| 1863 | .signed => .idiv, | | |
| 1864 | .unsigned => .div, | | |
| 1865 | }, ty, signedness, lhs, rhs); | | |
| 1866 | break :result MCValue{ .register = .rax }; | | |
| 1867 | }, | | |
| 1868 | .div_floor => { | | |
| 1869 | break :result try self.genInlineIntDivFloor(ty, lhs, rhs); | | |
| 1870 | }, | | |
| 1871 | else => unreachable, | | |
| 1872 | } | | |
| 1873 | }; | | |
| 1874 | | | |
| 1875 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1876 | } | | |
| 1877 | | | |
| 1878 | fn airRem(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1879 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1880 | | | |
| 1881 | if (self.liveness.isUnused(inst)) { | | |
| 1882 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1883 | } | | |
| 1884 | const ty = self.air.typeOfIndex(inst); | | |
| 1885 | if (ty.zigTypeTag() != .Int) { | | |
| 1886 | return self.fail("TODO implement .rem for operands of dst type {}", .{ty.zigTypeTag()}); | | |
| 1887 | } | | |
| 1888 | // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. | | |
| 1889 | try self.register_manager.getReg(.rax, null); | | |
| 1890 | try self.register_manager.getReg(.rdx, inst); | | |
| 1891 | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); | | |
| 1892 | defer for (reg_locks) |reg| { | | |
| 1893 | self.register_manager.unlockReg(reg); | | |
| 1894 | }; | | |
| 1895 | | | |
| 1896 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 1897 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 1898 | | | |
| 1899 | const signedness = ty.intInfo(self.target.*).signedness; | | |
| 1900 | try self.genIntMulDivOpMir(switch (signedness) { | | |
| 1901 | .signed => .idiv, | | |
| 1902 | .unsigned => .div, | | |
| 1903 | }, ty, signedness, lhs, rhs); | | |
| 1904 | | | |
| 1905 | const result: MCValue = .{ .register = .rdx }; | | |
| 1906 | | | |
| 1907 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1908 | } | | |
| 1909 | | | |
| 1910 | fn airMod(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1911 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1912 | | | |
| 1913 | if (self.liveness.isUnused(inst)) { | | |
| 1914 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1915 | } | | |
| 1916 | | | |
| 1917 | const ty = self.air.typeOfIndex(inst); | | |
| 1918 | if (ty.zigTypeTag() != .Int) { | | |
| 1919 | return self.fail("TODO implement .mod for operands of dst type {}", .{ty.zigTypeTag()}); | | |
| 1920 | } | | |
| 1921 | const signedness = ty.intInfo(self.target.*).signedness; | | |
| 1922 | | | |
| 1923 | // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. | | |
| 1924 | try self.register_manager.getReg(.rax, null); | | |
| 1925 | try self.register_manager.getReg(.rdx, if (signedness == .unsigned) inst else null); | | |
| 1926 | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); | | |
| 1927 | defer for (reg_locks) |reg| { | | |
| 1928 | self.register_manager.unlockReg(reg); | | |
| 1929 | }; | | |
| 1930 | | | |
| 1931 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 1932 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 1933 | | | |
| 1934 | const result: MCValue = result: { | | |
| 1935 | switch (signedness) { | | |
| 1936 | .unsigned => { | | |
| 1937 | try self.genIntMulDivOpMir(switch (signedness) { | | |
| 1938 | .signed => .idiv, | | |
| 1939 | .unsigned => .div, | | |
| 1940 | }, ty, signedness, lhs, rhs); | | |
| 1941 | break :result MCValue{ .register = .rdx }; | | |
| 1942 | }, | | |
| 1943 | .signed => { | | |
| 1944 | const div_floor = try self.genInlineIntDivFloor(ty, lhs, rhs); | | |
| 1945 | try self.genIntMulComplexOpMir(ty, div_floor, rhs); | | |
| 1946 | | | |
| 1947 | const result = try self.copyToRegisterWithInstTracking(inst, ty, lhs); | | |
| 1948 | try self.genBinMathOpMir(.sub, ty, result, div_floor); | | |
| 1949 | | | |
| 1950 | break :result result; | | |
| 1951 | }, | | |
| 1952 | } | | |
| 1953 | }; | | |
| 1954 | | | |
| 1955 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1956 | } | | |
| 1957 | | | |
| 1958 | fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1959 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1960 | const result: MCValue = if (self.liveness.isUnused(inst)) | | |
| 1961 | .dead | | |
| 1962 | else | | |
| 1963 | try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); | | |
| 1964 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1965 | } | | |
| 1966 | | | |
| 1967 | fn airBitOr(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1968 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1969 | const result: MCValue = if (self.liveness.isUnused(inst)) | | |
| 1970 | .dead | | |
| 1971 | else | | |
| 1972 | try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); | | |
| 1973 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1974 | } | | |
| 1975 | | | |
| 1976 | fn airXor(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1977 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1978 | const result: MCValue = if (self.liveness.isUnused(inst)) | | |
| 1979 | .dead | | |
| 1980 | else | | |
| 1981 | try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); | | |
| 1982 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1983 | } | | |
| 1984 | | | |
| 1985 | fn airShl(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1986 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1987 | if (self.liveness.isUnused(inst)) { | | |
| 1988 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1989 | } | | |
| 1990 | | | |
| 1991 | const ty = self.air.typeOfIndex(inst); | | |
| 1992 | const tag = self.air.instructions.items(.tag)[inst]; | | |
| 1993 | switch (tag) { | | |
| 1994 | .shl_exact => return self.fail("TODO implement {} for type {}", .{ tag, ty.fmtDebug() }), | | |
| 1995 | .shl => {}, | | |
| 1996 | else => unreachable, | | |
| 1997 | } | | |
| 1998 | | | |
| 1999 | if (ty.zigTypeTag() != .Int) { | | |
| 2000 | return self.fail("TODO implement .shl for type {}", .{ty.fmtDebug()}); | | |
| 2001 | } | | |
| 2002 | if (ty.abiSize(self.target.*) > 8) { | | |
| 2003 | return self.fail("TODO implement .shl for integers larger than 8 bytes", .{}); | | |
| 2004 | } | | |
| 2005 | | | |
| 2006 | // TODO look into reusing the operands | | |
| 2007 | // TODO audit register allocation mechanics | | |
| 2008 | const shift = try self.resolveInst(bin_op.rhs); | | |
| 2009 | const shift_ty = self.air.typeOf(bin_op.rhs); | | |
| 2010 | | | |
| 2011 | blk: { | | |
| 2012 | switch (shift) { | | |
| 2013 | .register => |reg| { | | |
| 2014 | if (reg.to64() == .rcx) break :blk; | | |
| 2015 | }, | | |
| 2016 | else => {}, | | |
| 2017 | } | | |
| 2018 | try self.register_manager.getReg(.rcx, null); | | |
| 2019 | try self.genSetReg(shift_ty, .rcx, shift); | | |
| 2020 | } | | |
| 2021 | const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); | | |
| 2022 | defer self.register_manager.unlockReg(rcx_lock); | | |
| 2023 | | | |
| 2024 | const value = try self.resolveInst(bin_op.lhs); | | |
| 2025 | const value_lock: ?RegisterLock = switch (value) { | | |
| 2026 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | | |
| 2027 | else => null, | | |
| 2028 | }; | | |
| 2029 | defer if (value_lock) |lock| self.register_manager.unlockReg(lock); | | |
| 2030 | | | |
| 2031 | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, value); | | |
| 2032 | _ = try self.addInst(.{ | 1667 | _ = try self.addInst(.{ |
| 2033 | .tag = .sal, | 1668 | .tag = .@"test", |
| 2034 | .ops = (Mir.Ops{ | 1669 | .ops = (Mir.Ops{ |
| 2035 | .reg1 = dst_mcv.register, | 1670 | .reg1 = .rdx, |
| 2036 | .flags = 0b01, | 1671 | .reg2 = .rdx, |
| 2037 | }).encode(), | 1672 | }).encode(), |
| 2038 | .data = undefined, | 1673 | .data = undefined, |
| 2039 | }); | 1674 | }); |
| 2040 | | 1675 | _ = try self.addInst(.{ |
| 2041 | return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); | 1676 | .tag = .cond_mov_eq, |
| | 1677 | .ops = (Mir.Ops{ |
| | 1678 | .reg1 = divisor.to64(), |
| | 1679 | .reg2 = .rdx, |
| | 1680 | }).encode(), |
| | 1681 | .data = undefined, |
| | 1682 | }); |
| | 1683 | try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); |
| | 1684 | return MCValue{ .register = divisor }; |
| 2042 | } | 1685 | } |
| 2043 | | 1686 | |
| 2044 | fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { | 1687 | fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 2045 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1688 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2046 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1689 | |
| 2047 | .dead | 1690 | if (self.liveness.isUnused(inst)) { |
| 2048 | else | 1691 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2049 | return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); | 1692 | } |
| | 1693 | |
| | 1694 | try self.spillRegisters(1, .{.rcx}); |
| | 1695 | |
| | 1696 | const tag = self.air.instructions.items(.tag)[inst]; |
| | 1697 | const lhs = try self.resolveInst(bin_op.lhs); |
| | 1698 | const rhs = try self.resolveInst(bin_op.rhs); |
| | 1699 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| | 1700 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| | 1701 | |
| | 1702 | const result = try self.genShiftBinOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 1703 | |
| 2050 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1704 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2051 | } | 1705 | } |
| 2052 | | 1706 | |
| 2053 | fn airShr(self: *Self, inst: Air.Inst.Index) !void { | 1707 | fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { |
| 2054 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1708 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2055 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1709 | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 2056 | .dead | 1710 | .dead |
| 2057 | else | 1711 | else |
| 2058 | return self.fail("TODO implement shr for {}", .{self.target.cpu.arch}); | 1712 | return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); |
| 2059 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1713 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2060 | } | 1714 | } |
| 2061 | | 1715 | |
| ... | @@ -2086,7 +1740,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2086,7 +1740,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 2086 | // TODO reuse the operand | 1740 | // TODO reuse the operand |
| 2087 | const result = try self.copyToRegisterWithInstTracking(inst, optional_ty, operand); | 1741 | const result = try self.copyToRegisterWithInstTracking(inst, optional_ty, operand); |
| 2088 | const shift = @intCast(u8, offset * @sizeOf(usize)); | 1742 | const shift = @intCast(u8, offset * @sizeOf(usize)); |
| 2089 | try self.shiftRegister(result.register, @intCast(u8, shift)); | 1743 | try self.genShiftBinOpMir(.shr, optional_ty, result.register, .{ .immediate = @intCast(u8, shift) }); |
| 2090 | break :result result; | 1744 | break :result result; |
| 2091 | }, | 1745 | }, |
| 2092 | else => return self.fail("TODO implement optional_payload when operand is {}", .{operand}), | 1746 | else => return self.fail("TODO implement optional_payload when operand is {}", .{operand}), |
| ... | @@ -2173,7 +1827,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2173,7 +1827,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 2173 | // TODO reuse operand | 1827 | // TODO reuse operand |
| 2174 | const shift = @intCast(u6, err_abi_size * @sizeOf(usize)); | 1828 | const shift = @intCast(u6, err_abi_size * @sizeOf(usize)); |
| 2175 | const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); | 1829 | const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); |
| 2176 | try self.shiftRegister(result.register.to64(), shift); | 1830 | try self.genShiftBinOpMir(.shr, Type.usize, result.register, .{ .immediate = shift }); |
| 2177 | break :result MCValue{ | 1831 | break :result MCValue{ |
| 2178 | .register = registerAlias(result.register, @intCast(u32, payload_ty.abiSize(self.target.*))), | 1832 | .register = registerAlias(result.register, @intCast(u32, payload_ty.abiSize(self.target.*))), |
| 2179 | }; | 1833 | }; |
| ... | @@ -2405,7 +2059,7 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { | ... | @@ -2405,7 +2059,7 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { |
| 2405 | } | 2059 | } |
| 2406 | // TODO we could allocate register here, but need to expect addr register and potentially | 2060 | // TODO we could allocate register here, but need to expect addr register and potentially |
| 2407 | // offset register. | 2061 | // offset register. |
| 2408 | try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg }, .{ | 2062 | try self.genBinOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg }, .{ |
| 2409 | .register = offset_reg, | 2063 | .register = offset_reg, |
| 2410 | }); | 2064 | }); |
| 2411 | return MCValue{ .register = addr_reg.to64() }; | 2065 | return MCValue{ .register = addr_reg.to64() }; |
| ... | @@ -2508,7 +2162,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2508,7 +2162,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2508 | // TODO we could allocate register here, but need to expect addr register and potentially | 2162 | // TODO we could allocate register here, but need to expect addr register and potentially |
| 2509 | // offset register. | 2163 | // offset register. |
| 2510 | const dst_mcv = try self.allocRegOrMem(inst, false); | 2164 | const dst_mcv = try self.allocRegOrMem(inst, false); |
| 2511 | try self.genBinMathOpMir(.add, Type.usize, .{ .register = addr_reg }, .{ .register = offset_reg }); | 2165 | try self.genBinOpMir(.add, Type.usize, .{ .register = addr_reg }, .{ .register = offset_reg }); |
| 2512 | try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty); | 2166 | try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty); |
| 2513 | | 2167 | |
| 2514 | return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); | 2168 | return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); |
| ... | @@ -2548,7 +2202,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2548,7 +2202,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2548 | defer self.register_manager.unlockReg(offset_reg_lock); | 2202 | defer self.register_manager.unlockReg(offset_reg_lock); |
| 2549 | | 2203 | |
| 2550 | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); | 2204 | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); |
| 2551 | try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); | 2205 | try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); |
| 2552 | | 2206 | |
| 2553 | const result: MCValue = result: { | 2207 | const result: MCValue = result: { |
| 2554 | if (elem_abi_size > 8) { | 2208 | if (elem_abi_size > 8) { |
| ... | @@ -2602,7 +2256,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2602,7 +2256,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2602 | defer self.register_manager.unlockReg(offset_reg_lock); | 2256 | defer self.register_manager.unlockReg(offset_reg_lock); |
| 2603 | | 2257 | |
| 2604 | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); | 2258 | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); |
| 2605 | try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); | 2259 | try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); |
| 2606 | | 2260 | |
| 2607 | return self.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none }); | 2261 | return self.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none }); |
| 2608 | } | 2262 | } |
| ... | @@ -2635,7 +2289,7 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2635,7 +2289,7 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { |
| 2635 | const adjusted_ptr: MCValue = if (layout.payload_size > 0 and layout.tag_align < layout.payload_align) blk: { | 2289 | const adjusted_ptr: MCValue = if (layout.payload_size > 0 and layout.tag_align < layout.payload_align) blk: { |
| 2636 | // TODO reusing the operand | 2290 | // TODO reusing the operand |
| 2637 | const reg = try self.copyToTmpRegister(ptr_ty, ptr); | 2291 | const reg = try self.copyToTmpRegister(ptr_ty, ptr); |
| 2638 | try self.genBinMathOpMir(.add, ptr_ty, .{ .register = reg }, .{ .immediate = layout.payload_size }); | 2292 | try self.genBinOpMir(.add, ptr_ty, .{ .register = reg }, .{ .immediate = layout.payload_size }); |
| 2639 | break :blk MCValue{ .register = reg }; | 2293 | break :blk MCValue{ .register = reg }; |
| 2640 | } else ptr; | 2294 | } else ptr; |
| 2641 | | 2295 | |
| ... | @@ -2685,7 +2339,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2685,7 +2339,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { |
| 2685 | else | 2339 | else |
| 2686 | 0; | 2340 | 0; |
| 2687 | const result = try self.copyToRegisterWithInstTracking(inst, union_ty, operand); | 2341 | const result = try self.copyToRegisterWithInstTracking(inst, union_ty, operand); |
| 2688 | try self.shiftRegister(result.register.to64(), shift); | 2342 | try self.genShiftBinOpMir(.shr, Type.usize, result.register, .{ .immediate = shift }); |
| 2689 | break :blk MCValue{ | 2343 | break :blk MCValue{ |
| 2690 | .register = registerAlias(result.register, @intCast(u32, layout.tag_size)), | 2344 | .register = registerAlias(result.register, @intCast(u32, layout.tag_size)), |
| 2691 | }; | 2345 | }; |
| ... | @@ -3202,7 +2856,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde | ... | @@ -3202,7 +2856,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 3202 | defer self.register_manager.unlockReg(offset_reg_lock); | 2856 | defer self.register_manager.unlockReg(offset_reg_lock); |
| 3203 | | 2857 | |
| 3204 | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, mcv); | 2858 | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, mcv); |
| 3205 | try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); | 2859 | try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); |
| 3206 | break :result dst_mcv; | 2860 | break :result dst_mcv; |
| 3207 | }, | 2861 | }, |
| 3208 | .ptr_stack_offset => |off| { | 2862 | .ptr_stack_offset => |off| { |
| ... | @@ -3232,7 +2886,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde | ... | @@ -3232,7 +2886,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 3232 | const result_reg_lock = self.register_manager.lockReg(result_reg); | 2886 | const result_reg_lock = self.register_manager.lockReg(result_reg); |
| 3233 | defer if (result_reg_lock) |lock| self.register_manager.unlockReg(lock); | 2887 | defer if (result_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 3234 | | 2888 | |
| 3235 | try self.genBinMathOpMir(.add, ptr_ty, .{ .register = result_reg }, .{ .register = offset_reg }); | 2889 | try self.genBinOpMir(.add, ptr_ty, .{ .register = result_reg }, .{ .register = offset_reg }); |
| 3236 | break :result MCValue{ .register = result_reg }; | 2890 | break :result MCValue{ .register = result_reg }; |
| 3237 | }, | 2891 | }, |
| 3238 | else => return self.fail("TODO implement codegen struct_field_ptr for {}", .{mcv}), | 2892 | else => return self.fail("TODO implement codegen struct_field_ptr for {}", .{mcv}), |
| ... | @@ -3284,7 +2938,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3284,7 +2938,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3284 | | 2938 | |
| 3285 | // Shift by struct_field_offset. | 2939 | // Shift by struct_field_offset. |
| 3286 | const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); | 2940 | const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); |
| 3287 | try self.shiftRegister(dst_mcv.register, shift); | 2941 | try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv.register, .{ .immediate = shift }); |
| 3288 | | 2942 | |
| 3289 | // Mask with reg.size() - struct_field_size | 2943 | // Mask with reg.size() - struct_field_size |
| 3290 | const max_reg_bit_width = Register.rax.size(); | 2944 | const max_reg_bit_width = Register.rax.size(); |
| ... | @@ -3292,7 +2946,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3292,7 +2946,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3292 | const mask = (~@as(u64, 0)) >> mask_shift; | 2946 | const mask = (~@as(u64, 0)) >> mask_shift; |
| 3293 | | 2947 | |
| 3294 | const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); | 2948 | const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); |
| 3295 | try self.genBinMathOpMir(.@"and", Type.usize, dst_mcv, .{ .register = tmp_reg }); | 2949 | try self.genBinOpMir(.@"and", Type.usize, dst_mcv, .{ .register = tmp_reg }); |
| 3296 | | 2950 | |
| 3297 | const signedness: std.builtin.Signedness = blk: { | 2951 | const signedness: std.builtin.Signedness = blk: { |
| 3298 | if (struct_field_ty.zigTypeTag() != .Int) break :blk .unsigned; | 2952 | if (struct_field_ty.zigTypeTag() != .Int) break :blk .unsigned; |
| ... | @@ -3360,18 +3014,331 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3360,18 +3014,331 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3360 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 3014 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3361 | } | 3015 | } |
| 3362 | | 3016 | |
| | 3017 | /// Clobbers .rcx for non-immediate shift value. |
| | 3018 | fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shift: MCValue) !void { |
| | 3019 | assert(reg.to64() != .rcx); |
| | 3020 | |
| | 3021 | switch (tag) { |
| | 3022 | .sal, .sar, .shl, .shr => {}, |
| | 3023 | else => unreachable, |
| | 3024 | } |
| | 3025 | |
| | 3026 | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); |
| | 3027 | blk: { |
| | 3028 | switch (shift) { |
| | 3029 | .immediate => |imm| switch (imm) { |
| | 3030 | 0 => return, |
| | 3031 | 1 => { |
| | 3032 | _ = try self.addInst(.{ |
| | 3033 | .tag = tag, |
| | 3034 | .ops = (Mir.Ops{ |
| | 3035 | .reg1 = registerAlias(reg, abi_size), |
| | 3036 | .flags = 0b00, |
| | 3037 | }).encode(), |
| | 3038 | .data = undefined, |
| | 3039 | }); |
| | 3040 | return; |
| | 3041 | }, |
| | 3042 | else => { |
| | 3043 | _ = try self.addInst(.{ |
| | 3044 | .tag = tag, |
| | 3045 | .ops = (Mir.Ops{ |
| | 3046 | .reg1 = registerAlias(reg, abi_size), |
| | 3047 | .flags = 0b10, |
| | 3048 | }).encode(), |
| | 3049 | .data = .{ .imm = @intCast(u8, imm) }, |
| | 3050 | }); |
| | 3051 | return; |
| | 3052 | }, |
| | 3053 | }, |
| | 3054 | .register => |shift_reg| { |
| | 3055 | if (shift_reg == .rcx) break :blk; |
| | 3056 | }, |
| | 3057 | else => {}, |
| | 3058 | } |
| | 3059 | assert(self.register_manager.isRegFree(.rcx)); |
| | 3060 | try self.register_manager.getReg(.rcx, null); |
| | 3061 | try self.genSetReg(Type.u8, .rcx, shift); |
| | 3062 | } |
| | 3063 | |
| | 3064 | _ = try self.addInst(.{ |
| | 3065 | .tag = tag, |
| | 3066 | .ops = (Mir.Ops{ |
| | 3067 | .reg1 = registerAlias(reg, abi_size), |
| | 3068 | .flags = 0b01, |
| | 3069 | }).encode(), |
| | 3070 | .data = undefined, |
| | 3071 | }); |
| | 3072 | } |
| | 3073 | |
| | 3074 | /// Result is always a register. |
| | 3075 | /// Clobbers .rcx for non-immediate rhs, therefore care is needed to spill .rcx upfront. |
| | 3076 | /// Asserts .rcx is free. |
| | 3077 | fn genShiftBinOp( |
| | 3078 | self: *Self, |
| | 3079 | tag: Air.Inst.Tag, |
| | 3080 | maybe_inst: ?Air.Inst.Index, |
| | 3081 | lhs: MCValue, |
| | 3082 | rhs: MCValue, |
| | 3083 | lhs_ty: Type, |
| | 3084 | rhs_ty: Type, |
| | 3085 | ) !MCValue { |
| | 3086 | if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { |
| | 3087 | return self.fail("TODO implement genShiftBinOp for {}", .{lhs_ty.fmtDebug()}); |
| | 3088 | } |
| | 3089 | if (lhs_ty.abiSize(self.target.*) > 8) { |
| | 3090 | return self.fail("TODO implement genShiftBinOp for {}", .{lhs_ty.fmtDebug()}); |
| | 3091 | } |
| | 3092 | |
| | 3093 | assert(rhs_ty.abiSize(self.target.*) == 1); |
| | 3094 | |
| | 3095 | const lhs_lock: ?RegisterLock = switch (lhs) { |
| | 3096 | .register => |reg| self.register_manager.lockReg(reg), |
| | 3097 | else => null, |
| | 3098 | }; |
| | 3099 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| | 3100 | |
| | 3101 | const rhs_lock: ?RegisterLock = switch (rhs) { |
| | 3102 | .register => |reg| self.register_manager.lockReg(reg), |
| | 3103 | else => null, |
| | 3104 | }; |
| | 3105 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| | 3106 | |
| | 3107 | assert(self.register_manager.isRegFree(.rcx)); |
| | 3108 | try self.register_manager.getReg(.rcx, null); |
| | 3109 | const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); |
| | 3110 | defer self.register_manager.unlockReg(rcx_lock); |
| | 3111 | |
| | 3112 | const dst: MCValue = blk: { |
| | 3113 | if (maybe_inst) |inst| { |
| | 3114 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 3115 | // TODO dst can also be a memory location |
| | 3116 | if (self.reuseOperand(inst, bin_op.lhs, 0, lhs) and lhs.isRegister()) { |
| | 3117 | break :blk lhs; |
| | 3118 | } |
| | 3119 | break :blk try self.copyToRegisterWithInstTracking(inst, lhs_ty, lhs); |
| | 3120 | } |
| | 3121 | break :blk MCValue{ .register = try self.copyToTmpRegister(lhs_ty, lhs) }; |
| | 3122 | }; |
| | 3123 | |
| | 3124 | const signedness = lhs_ty.intInfo(self.target.*).signedness; |
| | 3125 | switch (tag) { |
| | 3126 | .shl => try self.genShiftBinOpMir(switch (signedness) { |
| | 3127 | .signed => .sal, |
| | 3128 | .unsigned => .shl, |
| | 3129 | }, lhs_ty, dst.register, rhs), |
| | 3130 | |
| | 3131 | .shl_exact => try self.genShiftBinOpMir(.shl, lhs_ty, dst.register, rhs), |
| | 3132 | |
| | 3133 | .shr, |
| | 3134 | .shr_exact, |
| | 3135 | => try self.genShiftBinOpMir(switch (signedness) { |
| | 3136 | .signed => .sar, |
| | 3137 | .unsigned => .shr, |
| | 3138 | }, lhs_ty, dst.register, rhs), |
| | 3139 | |
| | 3140 | else => unreachable, |
| | 3141 | } |
| | 3142 | |
| | 3143 | return dst; |
| | 3144 | } |
| | 3145 | |
| | 3146 | /// Result is always a register. |
| | 3147 | /// Clobbers .rax and .rdx therefore care is needed to spill .rax and .rdx upfront. |
| | 3148 | /// Asserts .rax and .rdx are free. |
| | 3149 | fn genMulDivBinOp( |
| | 3150 | self: *Self, |
| | 3151 | tag: Air.Inst.Tag, |
| | 3152 | maybe_inst: ?Air.Inst.Index, |
| | 3153 | ty: Type, |
| | 3154 | lhs: MCValue, |
| | 3155 | rhs: MCValue, |
| | 3156 | ) !MCValue { |
| | 3157 | if (ty.zigTypeTag() == .Vector or ty.zigTypeTag() == .Float) { |
| | 3158 | return self.fail("TODO implement genBinOp for {}", .{ty.fmtDebug()}); |
| | 3159 | } |
| | 3160 | if (ty.abiSize(self.target.*) > 8) { |
| | 3161 | return self.fail("TODO implement genBinOp for {}", .{ty.fmtDebug()}); |
| | 3162 | } |
| | 3163 | if (tag == .div_float) { |
| | 3164 | return self.fail("TODO implement genMulDivBinOp for div_float", .{}); |
| | 3165 | } |
| | 3166 | |
| | 3167 | assert(self.register_manager.isRegFree(.rax)); |
| | 3168 | assert(self.register_manager.isRegFree(.rdx)); |
| | 3169 | |
| | 3170 | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); |
| | 3171 | defer for (reg_locks) |reg| { |
| | 3172 | self.register_manager.unlockReg(reg); |
| | 3173 | }; |
| | 3174 | |
| | 3175 | const int_info = ty.intInfo(self.target.*); |
| | 3176 | const signedness = int_info.signedness; |
| | 3177 | |
| | 3178 | switch (tag) { |
| | 3179 | .mul, |
| | 3180 | .mulwrap, |
| | 3181 | .rem, |
| | 3182 | .div_trunc, |
| | 3183 | .div_exact, |
| | 3184 | => { |
| | 3185 | const track_inst_rax: ?Air.Inst.Index = switch (tag) { |
| | 3186 | .mul, .mulwrap, .div_exact, .div_trunc => maybe_inst, |
| | 3187 | else => null, |
| | 3188 | }; |
| | 3189 | const track_inst_rdx: ?Air.Inst.Index = switch (tag) { |
| | 3190 | .rem => maybe_inst, |
| | 3191 | else => null, |
| | 3192 | }; |
| | 3193 | try self.register_manager.getReg(.rax, track_inst_rax); |
| | 3194 | try self.register_manager.getReg(.rdx, track_inst_rdx); |
| | 3195 | |
| | 3196 | const mir_tag: Mir.Inst.Tag = switch (signedness) { |
| | 3197 | .signed => switch (tag) { |
| | 3198 | .mul, .mulwrap => Mir.Inst.Tag.imul, |
| | 3199 | .div_trunc, .div_exact, .rem => Mir.Inst.Tag.idiv, |
| | 3200 | else => unreachable, |
| | 3201 | }, |
| | 3202 | .unsigned => switch (tag) { |
| | 3203 | .mul, .mulwrap => Mir.Inst.Tag.mul, |
| | 3204 | .div_trunc, .div_exact, .rem => Mir.Inst.Tag.div, |
| | 3205 | else => unreachable, |
| | 3206 | }, |
| | 3207 | }; |
| | 3208 | |
| | 3209 | try self.genIntMulDivOpMir(mir_tag, ty, .signed, lhs, rhs); |
| | 3210 | |
| | 3211 | switch (signedness) { |
| | 3212 | .signed => switch (tag) { |
| | 3213 | .mul, .mulwrap, .div_trunc, .div_exact => return MCValue{ .register = .rax }, |
| | 3214 | .rem => return MCValue{ .register = .rdx }, |
| | 3215 | else => unreachable, |
| | 3216 | }, |
| | 3217 | .unsigned => switch (tag) { |
| | 3218 | .mul, .mulwrap, .div_trunc, .div_exact => return MCValue{ |
| | 3219 | .register = registerAlias(.rax, @intCast(u32, ty.abiSize(self.target.*))), |
| | 3220 | }, |
| | 3221 | .rem => return MCValue{ |
| | 3222 | .register = registerAlias(.rdx, @intCast(u32, ty.abiSize(self.target.*))), |
| | 3223 | }, |
| | 3224 | else => unreachable, |
| | 3225 | }, |
| | 3226 | } |
| | 3227 | }, |
| | 3228 | |
| | 3229 | .mod => { |
| | 3230 | try self.register_manager.getReg(.rax, null); |
| | 3231 | try self.register_manager.getReg(.rdx, if (signedness == .unsigned) maybe_inst else null); |
| | 3232 | |
| | 3233 | switch (signedness) { |
| | 3234 | .signed => { |
| | 3235 | const div_floor = try self.genInlineIntDivFloor(ty, lhs, rhs); |
| | 3236 | try self.genIntMulComplexOpMir(ty, div_floor, rhs); |
| | 3237 | const div_floor_lock = self.register_manager.lockReg(div_floor.register); |
| | 3238 | defer if (div_floor_lock) |lock| self.register_manager.unlockReg(lock); |
| | 3239 | |
| | 3240 | const result: MCValue = if (maybe_inst) |inst| |
| | 3241 | try self.copyToRegisterWithInstTracking(inst, ty, lhs) |
| | 3242 | else |
| | 3243 | MCValue{ .register = try self.copyToTmpRegister(ty, lhs) }; |
| | 3244 | try self.genBinOpMir(.sub, ty, result, div_floor); |
| | 3245 | |
| | 3246 | return result; |
| | 3247 | }, |
| | 3248 | .unsigned => { |
| | 3249 | try self.genIntMulDivOpMir(.div, ty, .unsigned, lhs, rhs); |
| | 3250 | return MCValue{ .register = registerAlias(.rdx, @intCast(u32, ty.abiSize(self.target.*))) }; |
| | 3251 | }, |
| | 3252 | } |
| | 3253 | }, |
| | 3254 | |
| | 3255 | .div_floor => { |
| | 3256 | try self.register_manager.getReg(.rax, if (signedness == .unsigned) maybe_inst else null); |
| | 3257 | try self.register_manager.getReg(.rdx, null); |
| | 3258 | |
| | 3259 | const lhs_lock: ?RegisterLock = switch (lhs) { |
| | 3260 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| | 3261 | else => null, |
| | 3262 | }; |
| | 3263 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| | 3264 | |
| | 3265 | const actual_rhs: MCValue = blk: { |
| | 3266 | switch (signedness) { |
| | 3267 | .signed => { |
| | 3268 | const rhs_lock: ?RegisterLock = switch (rhs) { |
| | 3269 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| | 3270 | else => null, |
| | 3271 | }; |
| | 3272 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| | 3273 | |
| | 3274 | if (maybe_inst) |inst| { |
| | 3275 | break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs); |
| | 3276 | } |
| | 3277 | break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) }; |
| | 3278 | }, |
| | 3279 | .unsigned => break :blk rhs, |
| | 3280 | } |
| | 3281 | }; |
| | 3282 | const rhs_lock: ?RegisterLock = switch (actual_rhs) { |
| | 3283 | .register => |reg| self.register_manager.lockReg(reg), |
| | 3284 | else => null, |
| | 3285 | }; |
| | 3286 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| | 3287 | |
| | 3288 | const result: MCValue = result: { |
| | 3289 | switch (signedness) { |
| | 3290 | .signed => break :result try self.genInlineIntDivFloor(ty, lhs, actual_rhs), |
| | 3291 | .unsigned => { |
| | 3292 | try self.genIntMulDivOpMir(.div, ty, .unsigned, lhs, actual_rhs); |
| | 3293 | break :result MCValue{ |
| | 3294 | .register = registerAlias(.rax, @intCast(u32, ty.abiSize(self.target.*))), |
| | 3295 | }; |
| | 3296 | }, |
| | 3297 | } |
| | 3298 | }; |
| | 3299 | return result; |
| | 3300 | }, |
| | 3301 | |
| | 3302 | else => unreachable, |
| | 3303 | } |
| | 3304 | } |
| | 3305 | |
| 3363 | /// Result is always a register. | 3306 | /// Result is always a register. |
| 3364 | fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { | 3307 | fn genBinOp( |
| 3365 | const dst_ty = self.air.typeOf(op_lhs); | 3308 | self: *Self, |
| | 3309 | tag: Air.Inst.Tag, |
| | 3310 | maybe_inst: ?Air.Inst.Index, |
| | 3311 | lhs: MCValue, |
| | 3312 | rhs: MCValue, |
| | 3313 | lhs_ty: Type, |
| | 3314 | rhs_ty: Type, |
| | 3315 | ) !MCValue { |
| | 3316 | if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { |
| | 3317 | return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); |
| | 3318 | } |
| | 3319 | if (lhs_ty.abiSize(self.target.*) > 8) { |
| | 3320 | return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); |
| | 3321 | } |
| | 3322 | |
| | 3323 | const is_commutative: bool = switch (tag) { |
| | 3324 | .add, |
| | 3325 | .addwrap, |
| | 3326 | .bool_or, |
| | 3327 | .bit_or, |
| | 3328 | .bool_and, |
| | 3329 | .bit_and, |
| | 3330 | .xor, |
| | 3331 | => true, |
| | 3332 | |
| | 3333 | else => false, |
| | 3334 | }; |
| 3366 | | 3335 | |
| 3367 | const lhs = try self.resolveInst(op_lhs); | | |
| 3368 | const lhs_lock: ?RegisterLock = switch (lhs) { | 3336 | const lhs_lock: ?RegisterLock = switch (lhs) { |
| 3369 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | 3337 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 3370 | else => null, | 3338 | else => null, |
| 3371 | }; | 3339 | }; |
| 3372 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); | 3340 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 3373 | | 3341 | |
| 3374 | const rhs = try self.resolveInst(op_rhs); | | |
| 3375 | const rhs_lock: ?RegisterLock = switch (rhs) { | 3342 | const rhs_lock: ?RegisterLock = switch (rhs) { |
| 3376 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | 3343 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 3377 | else => null, | 3344 | else => null, |
| ... | @@ -3380,14 +3347,18 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: | ... | @@ -3380,14 +3347,18 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: |
| 3380 | | 3347 | |
| 3381 | var flipped: bool = false; | 3348 | var flipped: bool = false; |
| 3382 | const dst_mcv: MCValue = blk: { | 3349 | const dst_mcv: MCValue = blk: { |
| 3383 | if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { | 3350 | if (maybe_inst) |inst| { |
| 3384 | break :blk lhs; | 3351 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 3385 | } | 3352 | if (self.reuseOperand(inst, bin_op.lhs, 0, lhs) and lhs.isRegister()) { |
| 3386 | if (self.reuseOperand(inst, op_rhs, 1, rhs) and rhs.isRegister()) { | 3353 | break :blk lhs; |
| 3387 | flipped = true; | 3354 | } |
| 3388 | break :blk rhs; | 3355 | if (is_commutative and self.reuseOperand(inst, bin_op.rhs, 1, rhs) and rhs.isRegister()) { |
| | 3356 | flipped = true; |
| | 3357 | break :blk rhs; |
| | 3358 | } |
| | 3359 | break :blk try self.copyToRegisterWithInstTracking(inst, lhs_ty, lhs); |
| 3389 | } | 3360 | } |
| 3390 | break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); | 3361 | break :blk MCValue{ .register = try self.copyToTmpRegister(lhs_ty, lhs) }; |
| 3391 | }; | 3362 | }; |
| 3392 | const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { | 3363 | const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { |
| 3393 | .register => |reg| self.register_manager.lockReg(reg), | 3364 | .register => |reg| self.register_manager.lockReg(reg), |
| ... | @@ -3398,7 +3369,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: | ... | @@ -3398,7 +3369,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: |
| 3398 | const src_mcv: MCValue = blk: { | 3369 | const src_mcv: MCValue = blk: { |
| 3399 | const mcv = if (flipped) lhs else rhs; | 3370 | const mcv = if (flipped) lhs else rhs; |
| 3400 | if (mcv.isRegister() or mcv.isMemory()) break :blk mcv; | 3371 | if (mcv.isRegister() or mcv.isMemory()) break :blk mcv; |
| 3401 | break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, mcv) }; | 3372 | break :blk MCValue{ .register = try self.copyToTmpRegister(rhs_ty, mcv) }; |
| 3402 | }; | 3373 | }; |
| 3403 | const src_mcv_lock: ?RegisterLock = switch (src_mcv) { | 3374 | const src_mcv_lock: ?RegisterLock = switch (src_mcv) { |
| 3404 | .register => |reg| self.register_manager.lockReg(reg), | 3375 | .register => |reg| self.register_manager.lockReg(reg), |
| ... | @@ -3406,18 +3377,44 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: | ... | @@ -3406,18 +3377,44 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: |
| 3406 | }; | 3377 | }; |
| 3407 | defer if (src_mcv_lock) |lock| self.register_manager.unlockReg(lock); | 3378 | defer if (src_mcv_lock) |lock| self.register_manager.unlockReg(lock); |
| 3408 | | 3379 | |
| 3409 | const tag = self.air.instructions.items(.tag)[inst]; | | |
| 3410 | switch (tag) { | 3380 | switch (tag) { |
| 3411 | .add, .addwrap, .add_with_overflow => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv), | 3381 | .add, |
| 3412 | .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, dst_mcv, src_mcv), | 3382 | .addwrap, |
| 3413 | .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, src_mcv), | 3383 | => try self.genBinOpMir(.add, lhs_ty, dst_mcv, src_mcv), |
| 3414 | .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, dst_mcv, src_mcv), | 3384 | |
| | 3385 | .sub, |
| | 3386 | .subwrap, |
| | 3387 | => try self.genBinOpMir(.sub, lhs_ty, dst_mcv, src_mcv), |
| | 3388 | |
| | 3389 | .ptr_add, |
| | 3390 | .ptr_sub, |
| | 3391 | => { |
| | 3392 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| | 3393 | .ptr_add => .add, |
| | 3394 | .ptr_sub => .sub, |
| | 3395 | else => unreachable, |
| | 3396 | }; |
| | 3397 | const elem_size = lhs_ty.elemType2().abiSize(self.target.*); |
| | 3398 | try self.genIntMulComplexOpMir(rhs_ty, src_mcv, .{ .immediate = elem_size }); |
| | 3399 | try self.genBinOpMir(mir_tag, lhs_ty, dst_mcv, src_mcv); |
| | 3400 | }, |
| | 3401 | |
| | 3402 | .bool_or, |
| | 3403 | .bit_or, |
| | 3404 | => try self.genBinOpMir(.@"or", lhs_ty, dst_mcv, src_mcv), |
| | 3405 | |
| | 3406 | .bool_and, |
| | 3407 | .bit_and, |
| | 3408 | => try self.genBinOpMir(.@"and", lhs_ty, dst_mcv, src_mcv), |
| | 3409 | |
| | 3410 | .xor => try self.genBinOpMir(.xor, lhs_ty, dst_mcv, src_mcv), |
| | 3411 | |
| 3415 | else => unreachable, | 3412 | else => unreachable, |
| 3416 | } | 3413 | } |
| 3417 | return dst_mcv; | 3414 | return dst_mcv; |
| 3418 | } | 3415 | } |
| 3419 | | 3416 | |
| 3420 | fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { | 3417 | fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { |
| 3421 | const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); | 3418 | const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); |
| 3422 | switch (dst_mcv) { | 3419 | switch (dst_mcv) { |
| 3423 | .none => unreachable, | 3420 | .none => unreachable, |
| ... | @@ -3439,7 +3436,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC | ... | @@ -3439,7 +3436,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC |
| 3439 | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); | 3436 | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 3440 | | 3437 | |
| 3441 | const reg = try self.copyToTmpRegister(dst_ty, src_mcv); | 3438 | const reg = try self.copyToTmpRegister(dst_ty, src_mcv); |
| 3442 | return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); | 3439 | return self.genBinOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); |
| 3443 | }, | 3440 | }, |
| 3444 | .register => |src_reg| { | 3441 | .register => |src_reg| { |
| 3445 | _ = try self.addInst(.{ | 3442 | _ = try self.addInst(.{ |
| ... | @@ -3471,7 +3468,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC | ... | @@ -3471,7 +3468,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC |
| 3471 | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); | 3468 | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 3472 | | 3469 | |
| 3473 | const reg = try self.copyToTmpRegister(dst_ty, src_mcv); | 3470 | const reg = try self.copyToTmpRegister(dst_ty, src_mcv); |
| 3474 | return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); | 3471 | return self.genBinOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); |
| 3475 | }, | 3472 | }, |
| 3476 | .stack_offset => |off| { | 3473 | .stack_offset => |off| { |
| 3477 | if (off > math.maxInt(i32)) { | 3474 | if (off > math.maxInt(i32)) { |
| ... | @@ -3572,7 +3569,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC | ... | @@ -3572,7 +3569,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC |
| 3572 | | 3569 | |
| 3573 | /// Performs multi-operand integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv. | 3570 | /// Performs multi-operand integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv. |
| 3574 | /// Does not support byte-size operands. | 3571 | /// Does not support byte-size operands. |
| 3575 | fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { | 3572 | fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError!void { |
| 3576 | const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); | 3573 | const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); |
| 3577 | switch (dst_mcv) { | 3574 | switch (dst_mcv) { |
| 3578 | .none => unreachable, | 3575 | .none => unreachable, |
| ... | @@ -3668,11 +3665,17 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M | ... | @@ -3668,11 +3665,17 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M |
| 3668 | .data = undefined, | 3665 | .data = undefined, |
| 3669 | }); | 3666 | }); |
| 3670 | // copy dst_reg back out | 3667 | // copy dst_reg back out |
| 3671 | return self.genSetStack(dst_ty, off, MCValue{ .register = dst_reg }, .{}); | 3668 | return self.genSetStack(dst_ty, off, .{ .register = dst_reg }, .{}); |
| 3672 | }, | 3669 | }, |
| 3673 | .immediate => |imm| { | 3670 | .immediate => { |
| 3674 | _ = imm; | 3671 | // copy dst to a register |
| 3675 | return self.fail("TODO implement x86 multiply source immediate", .{}); | 3672 | const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); |
| | 3673 | const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); |
| | 3674 | defer self.register_manager.unlockReg(dst_reg_lock); |
| | 3675 | |
| | 3676 | try self.genIntMulComplexOpMir(dst_ty, .{ .register = dst_reg }, src_mcv); |
| | 3677 | |
| | 3678 | return self.genSetStack(dst_ty, off, .{ .register = dst_reg }, .{}); |
| 3676 | }, | 3679 | }, |
| 3677 | .memory, .stack_offset => { | 3680 | .memory, .stack_offset => { |
| 3678 | return self.fail("TODO implement x86 multiply source memory", .{}); | 3681 | return self.fail("TODO implement x86 multiply source memory", .{}); |
| ... | @@ -4148,7 +4151,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | ... | @@ -4148,7 +4151,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 4148 | // This instruction supports only signed 32-bit immediates at most. | 4151 | // This instruction supports only signed 32-bit immediates at most. |
| 4149 | const src_mcv = try self.limitImmediateType(bin_op.rhs, i32); | 4152 | const src_mcv = try self.limitImmediateType(bin_op.rhs, i32); |
| 4150 | | 4153 | |
| 4151 | try self.genBinMathOpMir(.cmp, ty, dst_mcv, src_mcv); | 4154 | try self.genBinOpMir(.cmp, ty, dst_mcv, src_mcv); |
| 4152 | break :result switch (signedness) { | 4155 | break :result switch (signedness) { |
| 4153 | .signed => MCValue{ .compare_flags_signed = op }, | 4156 | .signed => MCValue{ .compare_flags_signed = op }, |
| 4154 | .unsigned => MCValue{ .compare_flags_unsigned = op }, | 4157 | .unsigned => MCValue{ .compare_flags_unsigned = op }, |
| ... | @@ -4541,7 +4544,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValu | ... | @@ -4541,7 +4544,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValu |
| 4541 | break :blk if (payload_ty.hasRuntimeBits()) Type.bool else ty; | 4544 | break :blk if (payload_ty.hasRuntimeBits()) Type.bool else ty; |
| 4542 | } else ty; | 4545 | } else ty; |
| 4543 | | 4546 | |
| 4544 | try self.genBinMathOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 }); | 4547 | try self.genBinOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 }); |
| 4545 | | 4548 | |
| 4546 | return MCValue{ .compare_flags_unsigned = .eq }; | 4549 | return MCValue{ .compare_flags_unsigned = .eq }; |
| 4547 | } | 4550 | } |
| ... | @@ -4564,13 +4567,13 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue | ... | @@ -4564,13 +4567,13 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue |
| 4564 | | 4567 | |
| 4565 | if (!payload_type.hasRuntimeBits()) { | 4568 | if (!payload_type.hasRuntimeBits()) { |
| 4566 | if (err_type.abiSize(self.target.*) <= 8) { | 4569 | if (err_type.abiSize(self.target.*) <= 8) { |
| 4567 | try self.genBinMathOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); | 4570 | try self.genBinOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); |
| 4568 | return MCValue{ .compare_flags_unsigned = .gt }; | 4571 | return MCValue{ .compare_flags_unsigned = .gt }; |
| 4569 | } else { | 4572 | } else { |
| 4570 | return self.fail("TODO isErr for errors with size larger than register size", .{}); | 4573 | return self.fail("TODO isErr for errors with size larger than register size", .{}); |
| 4571 | } | 4574 | } |
| 4572 | } else { | 4575 | } else { |
| 4573 | try self.genBinMathOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); | 4576 | try self.genBinOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); |
| 4574 | return MCValue{ .compare_flags_unsigned = .gt }; | 4577 | return MCValue{ .compare_flags_unsigned = .gt }; |
| 4575 | } | 4578 | } |
| 4576 | } | 4579 | } |
| ... | @@ -4988,21 +4991,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4988,21 +4991,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4988 | return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); | 4991 | return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); |
| 4989 | } | 4992 | } |
| 4990 | | 4993 | |
| 4991 | fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void { | | |
| 4992 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 4993 | const air_tags = self.air.instructions.items(.tag); | | |
| 4994 | const result: MCValue = if (self.liveness.isUnused(inst)) | | |
| 4995 | .dead | | |
| 4996 | else switch (air_tags[inst]) { | | |
| 4997 | // lhs AND rhs | | |
| 4998 | .bool_and => try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs), | | |
| 4999 | // lhs OR rhs | | |
| 5000 | .bool_or => try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs), | | |
| 5001 | else => unreachable, // Not a boolean operation | | |
| 5002 | }; | | |
| 5003 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 5004 | } | | |
| 5005 | | | |
| 5006 | fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { | 4994 | fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 5007 | const block_data = self.blocks.getPtr(block).?; | 4995 | const block_data = self.blocks.getPtr(block).?; |
| 5008 | | 4996 | |
| ... | @@ -5473,14 +5461,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl | ... | @@ -5473,14 +5461,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 5473 | }); | 5461 | }); |
| 5474 | | 5462 | |
| 5475 | if (nearest_power_of_two > 1) { | 5463 | if (nearest_power_of_two > 1) { |
| 5476 | _ = try self.addInst(.{ | 5464 | try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ .immediate = nearest_power_of_two * 8 }); |
| 5477 | .tag = .shr, | | |
| 5478 | .ops = (Mir.Ops{ | | |
| 5479 | .reg1 = tmp_reg, | | |
| 5480 | .flags = 0b10, | | |
| 5481 | }).encode(), | | |
| 5482 | .data = .{ .imm = nearest_power_of_two * 8 }, | | |
| 5483 | }); | | |
| 5484 | } | 5465 | } |
| 5485 | | 5466 | |
| 5486 | remainder -= nearest_power_of_two; | 5467 | remainder -= nearest_power_of_two; |
| ... | @@ -5542,6 +5523,7 @@ fn genInlineMemcpy( | ... | @@ -5542,6 +5523,7 @@ fn genInlineMemcpy( |
| 5542 | len: MCValue, | 5523 | len: MCValue, |
| 5543 | opts: InlineMemcpyOpts, | 5524 | opts: InlineMemcpyOpts, |
| 5544 | ) InnerError!void { | 5525 | ) InnerError!void { |
| | 5526 | // TODO preserve contents of .rax and .rcx if not free, and then restore |
| 5545 | try self.register_manager.getReg(.rax, null); | 5527 | try self.register_manager.getReg(.rax, null); |
| 5546 | try self.register_manager.getReg(.rcx, null); | 5528 | try self.register_manager.getReg(.rcx, null); |
| 5547 | | 5529 | |
| ... | @@ -5739,6 +5721,7 @@ fn genInlineMemset( | ... | @@ -5739,6 +5721,7 @@ fn genInlineMemset( |
| 5739 | len: MCValue, | 5721 | len: MCValue, |
| 5740 | opts: InlineMemcpyOpts, | 5722 | opts: InlineMemcpyOpts, |
| 5741 | ) InnerError!void { | 5723 | ) InnerError!void { |
| | 5724 | // TODO preserve contents of .rax and then restore |
| 5742 | try self.register_manager.getReg(.rax, null); | 5725 | try self.register_manager.getReg(.rax, null); |
| 5743 | const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); | 5726 | const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); |
| 5744 | defer self.register_manager.unlockReg(rax_lock); | 5727 | defer self.register_manager.unlockReg(rax_lock); |
| ... | @@ -5779,7 +5762,7 @@ fn genInlineMemset( | ... | @@ -5779,7 +5762,7 @@ fn genInlineMemset( |
| 5779 | defer self.register_manager.unlockReg(addr_reg_lock); | 5762 | defer self.register_manager.unlockReg(addr_reg_lock); |
| 5780 | | 5763 | |
| 5781 | try self.genSetReg(Type.usize, .rax, len); | 5764 | try self.genSetReg(Type.usize, .rax, len); |
| 5782 | try self.genBinMathOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 }); | 5765 | try self.genBinOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 }); |
| 5783 | | 5766 | |
| 5784 | // loop: | 5767 | // loop: |
| 5785 | // cmp rax, -1 | 5768 | // cmp rax, -1 |
| ... | @@ -6847,28 +6830,6 @@ fn registerAlias(reg: Register, size_bytes: u32) Register { | ... | @@ -6847,28 +6830,6 @@ fn registerAlias(reg: Register, size_bytes: u32) Register { |
| 6847 | } | 6830 | } |
| 6848 | } | 6831 | } |
| 6849 | | 6832 | |
| 6850 | fn shiftRegister(self: *Self, reg: Register, shift: u8) !void { | | |
| 6851 | if (shift == 0) return; | | |
| 6852 | if (shift == 1) { | | |
| 6853 | _ = try self.addInst(.{ | | |
| 6854 | .tag = .shr, | | |
| 6855 | .ops = (Mir.Ops{ | | |
| 6856 | .reg1 = reg, | | |
| 6857 | }).encode(), | | |
| 6858 | .data = undefined, | | |
| 6859 | }); | | |
| 6860 | } else { | | |
| 6861 | _ = try self.addInst(.{ | | |
| 6862 | .tag = .shr, | | |
| 6863 | .ops = (Mir.Ops{ | | |
| 6864 | .reg1 = reg, | | |
| 6865 | .flags = 0b10, | | |
| 6866 | }).encode(), | | |
| 6867 | .data = .{ .imm = shift }, | | |
| 6868 | }); | | |
| 6869 | } | | |
| 6870 | } | | |
| 6871 | | | |
| 6872 | /// Truncates the value in the register in place. | 6833 | /// Truncates the value in the register in place. |
| 6873 | /// Clobbers any remaining bits. | 6834 | /// Clobbers any remaining bits. |
| 6874 | fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { | 6835 | fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { |
| ... | @@ -6877,31 +6838,17 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { | ... | @@ -6877,31 +6838,17 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { |
| 6877 | switch (int_info.signedness) { | 6838 | switch (int_info.signedness) { |
| 6878 | .signed => { | 6839 | .signed => { |
| 6879 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); | 6840 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); |
| 6880 | _ = try self.addInst(.{ | 6841 | try self.genShiftBinOpMir(.sal, Type.isize, reg, .{ .immediate = shift }); |
| 6881 | .tag = .sal, | 6842 | try self.genShiftBinOpMir(.sar, Type.isize, reg, .{ .immediate = shift }); |
| 6882 | .ops = (Mir.Ops{ | | |
| 6883 | .reg1 = reg.to64(), | | |
| 6884 | .flags = 0b10, | | |
| 6885 | }).encode(), | | |
| 6886 | .data = .{ .imm = shift }, | | |
| 6887 | }); | | |
| 6888 | _ = try self.addInst(.{ | | |
| 6889 | .tag = .sar, | | |
| 6890 | .ops = (Mir.Ops{ | | |
| 6891 | .reg1 = reg.to64(), | | |
| 6892 | .flags = 0b10, | | |
| 6893 | }).encode(), | | |
| 6894 | .data = .{ .imm = shift }, | | |
| 6895 | }); | | |
| 6896 | }, | 6843 | }, |
| 6897 | .unsigned => { | 6844 | .unsigned => { |
| 6898 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); | 6845 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); |
| 6899 | const mask = (~@as(u64, 0)) >> shift; | 6846 | const mask = (~@as(u64, 0)) >> shift; |
| 6900 | if (int_info.bits <= 32) { | 6847 | if (int_info.bits <= 32) { |
| 6901 | try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask }); | 6848 | try self.genBinOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask }); |
| 6902 | } else { | 6849 | } else { |
| 6903 | const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); | 6850 | const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); |
| 6904 | try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .register = tmp_reg }); | 6851 | try self.genBinOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .register = tmp_reg }); |
| 6905 | } | 6852 | } |
| 6906 | }, | 6853 | }, |
| 6907 | } | 6854 | } |