authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-11 08:24:03+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-05-11 08:24:03+02:00
log6608fa135321c2caa989a4e0357307874c644b6b
tree90f6bb4308f368205a7bda7872a8486f6e145de3
parentb33c8b0b06dd6a24d89efb278eeb833db93b2f9b
parent3c69810fe6660fa3115d345bb60b3f811ac7c8c0
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11628 from ziglang/x64-shifts


2 files changed, 687 insertions(+), 723 deletions(-)

src/arch/x86_64/CodeGen.zig+648-701
...@@ -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 {
586586
587 switch (air_tags[inst]) {587 switch (air_tags[inst]) {
588 // zig fmt: off588 // 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),
607607
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),
623623
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),
628628
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),
630630
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),
640640
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),
647647
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}
961961
962pub 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 considered968/// 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 {
10891095
1090fn airNot(self: *Self, inst: Air.Inst.Index) !void {1096fn 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);
11611196
1162 try self.genBinMathOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv);1197 try self.genBinOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv);
11631198
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}
11871222
1188fn 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
1239fn 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
1248fn 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
1257fn airSlice(self: *Self, inst: Air.Inst.Index) !void {1223fn 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}
12771243
1278fn airAdd(self: *Self, inst: Air.Inst.Index) !void {1244fn 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}
12861246
1287fn 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 .dead1250
1291 else1251 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);
12951258
1296fn 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}
13041261
1305/// Result is always a register.1262fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {
1306fn 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);
13081264
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);
13151268
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);
13221271
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);
13341273
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);
13441276
1345 try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, rhs_mcv);1277 const result = try self.genMulDivBinOp(tag, inst, ty, lhs, rhs);
13461278
1347 return dst_mcv;1279 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1348}1280}
13491281
1350fn airSub(self: *Self, inst: Air.Inst.Index) !void {1282fn 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 .dead1285 .dead
1354 else1286 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}
13581290
...@@ -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}
13671299
1368fn 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
1398fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {1300fn 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}
14061308
1407fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1309fn 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);
14121315 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) {
14171320 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 }
14211322
1422 try self.spillCompareFlagsIfOccupied();1323 try self.spillCompareFlagsIfOccupied();
1423 self.compare_flags_inst = inst;
14241324
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 };
14351328
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);
14381331
1439fn 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 };
14441341
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.*);
14491343
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 }
14531353
1454 try self.spillCompareFlagsIfOccupied();1354 self.compare_flags_inst = null;
1455 self.compare_flags_inst = inst;
14561355
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}
14701372
1373fn 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
1471fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1442fn 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 }
14781449
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 }
14891459
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;
14931465
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 };
15011467
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);
15041470
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 }
15161477
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 };
15611516
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);
15641519
1565 try self.genIntMulDivOpMir(.mul, ty, .unsigned, lhs, rhs);1520 const dst_mcv = try self.genMulDivBinOp(.mul, null, ty, lhs, rhs);
15661521 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);
15731525
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 });
16241531
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 }, .{});
16361533
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}
16451542
1646fn 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 }
16661558
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 }
16681566
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}
17291627
1628/// Always returns a register.
1730/// Clobbers .rax and .rdx registers.1629/// Clobbers .rax and .rdx registers.
1731fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCValue {1630fn 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
1788fn 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
1878fn 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
1910fn 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
1958fn 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
1967fn 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
1976fn 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
1985fn 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 });
20401675 _ = 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}
20431686
2044fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {1687fn 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 .dead1690 if (self.liveness.isUnused(inst)) {
2048 else1691 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}
20521706
2053fn airShr(self: *Self, inst: Air.Inst.Index) !void {1707fn 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 .dead1710 .dead
2057 else1711 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}
20611715
...@@ -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 operand1740 // 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 operand1827 // 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 potentially2060 // 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 potentially2162 // 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);
25132167
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);
25492203
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 });
25522206
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);
26032257
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 });
26062260
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 operand2290 // 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;
26412295
...@@ -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 else2339 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);
32032857
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);
32342888
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 {
32842938
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 });
32882942
3289 // Mask with reg.size() - struct_field_size2943 // 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;
32932947
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 });
32962950
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}
33623016
3017/// Clobbers .rcx for non-immediate shift value.
3018fn 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.
3077fn 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.
3149fn 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.
3364fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {3307fn 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 };
33663335
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);
33733341
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:
33803347
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);
34083379
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}
34193416
3420fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {3417fn 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);
34403437
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);
34723469
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
35723569
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.
3575fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {3572fn 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 out3667 // 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);
41504153
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;
45434546
4544 try self.genBinMathOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 });4547 try self.genBinOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 });
45454548
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
45644567
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}
49904993
4991fn 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
5006fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {4994fn 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).?;
50084996
...@@ -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 });
54745462
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 }
54855466
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);
55475529
...@@ -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);
57805763
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 });
57835766
5784 // loop:5767 // loop:
5785 // cmp rax, -15768 // 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}
68496832
6850fn 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.
6874fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {6835fn 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 }
test/behavior/math.zig+39-22
...@@ -9,7 +9,6 @@ const mem = std.mem;...@@ -9,7 +9,6 @@ const mem = std.mem;
9const math = std.math;9const math = std.math;
1010
11test "assignment operators" {11test "assignment operators" {
12 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
13 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO12 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO13 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1514
...@@ -363,7 +362,6 @@ fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int...@@ -363,7 +362,6 @@ fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int
363362
364test "binary not" {363test "binary not" {
365 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO364 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
366 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
367365
368 try expect(comptime x: {366 try expect(comptime x: {
369 break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101;367 break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101;
...@@ -570,8 +568,6 @@ test "bit shift a u1" {...@@ -570,8 +568,6 @@ test "bit shift a u1" {
570}568}
571569
572test "truncating shift right" {570test "truncating shift right" {
573 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
574
575 try testShrTrunc(maxInt(u16));571 try testShrTrunc(maxInt(u16));
576 comptime try testShrTrunc(maxInt(u16));572 comptime try testShrTrunc(maxInt(u16));
577}573}
...@@ -642,7 +638,6 @@ test "@addWithOverflow" {...@@ -642,7 +638,6 @@ test "@addWithOverflow" {
642638
643test "small int addition" {639test "small int addition" {
644 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO640 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
645 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
646 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO641 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
647 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO642 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
648643
...@@ -908,21 +903,47 @@ test "@subWithOverflow" {...@@ -908,21 +903,47 @@ test "@subWithOverflow" {
908903
909test "@shlWithOverflow" {904test "@shlWithOverflow" {
910 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO905 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
911 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO906 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
907
908 {
909 var result: u4 = undefined;
910 var a: u4 = 2;
911 var b: u2 = 1;
912 try expect(!@shlWithOverflow(u4, a, b, &result));
913 try expect(result == 4);
914
915 b = 3;
916 try expect(@shlWithOverflow(u4, a, b, &result));
917 try expect(result == 0);
918 }
912919
913 var result: u16 = undefined;920 {
914 try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));921 var result: i9 = undefined;
915 try expect(result == 0b0111111111111000);922 var a: i9 = 127;
916 try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));923 var b: u4 = 1;
917 try expect(result == 0b1011111111111100);924 try expect(!@shlWithOverflow(i9, a, b, &result));
925 try expect(result == 254);
926
927 b = 2;
928 try expect(@shlWithOverflow(i9, a, b, &result));
929 try expect(result == -4);
930 }
918931
919 var a: u16 = 0b0000_0000_0000_0011;932 {
920 var b: u4 = 15;933 var result: u16 = undefined;
921 try expect(@shlWithOverflow(u16, a, b, &result));934 try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));
922 try expect(result == 0b1000_0000_0000_0000);935 try expect(result == 0b0111111111111000);
923 b = 14;936 try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));
924 try expect(!@shlWithOverflow(u16, a, b, &result));937 try expect(result == 0b1011111111111100);
925 try expect(result == 0b1100_0000_0000_0000);938
939 var a: u16 = 0b0000_0000_0000_0011;
940 var b: u4 = 15;
941 try expect(@shlWithOverflow(u16, a, b, &result));
942 try expect(result == 0b1000_0000_0000_0000);
943 b = 14;
944 try expect(!@shlWithOverflow(u16, a, b, &result));
945 try expect(result == 0b1100_0000_0000_0000);
946 }
926}947}
927948
928test "overflow arithmetic with u0 values" {949test "overflow arithmetic with u0 values" {
...@@ -1067,8 +1088,6 @@ fn testShlTrunc(x: u16) !void {...@@ -1067,8 +1088,6 @@ fn testShlTrunc(x: u16) !void {
1067}1088}
10681089
1069test "exact shift left" {1090test "exact shift left" {
1070 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1071
1072 try testShlExact(0b00110101);1091 try testShlExact(0b00110101);
1073 comptime try testShlExact(0b00110101);1092 comptime try testShlExact(0b00110101);
1074}1093}
...@@ -1078,8 +1097,6 @@ fn testShlExact(x: u8) !void {...@@ -1078,8 +1097,6 @@ fn testShlExact(x: u8) !void {
1078}1097}
10791098
1080test "exact shift right" {1099test "exact shift right" {
1081 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1082
1083 try testShrExact(0b10110100);1100 try testShrExact(0b10110100);
1084 comptime try testShrExact(0b10110100);1101 comptime try testShrExact(0b10110100);
1085}1102}