| ... | ... | @@ -1311,18 +1311,15 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1311 | 1311 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1312 | 1312 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1313 | 1313 | const ty = self.air.typeOf(bin_op.lhs); |
| 1314 | | |
| 1314 | const abi_size = ty.abiSize(self.target.*); |
| 1315 | 1315 | switch (ty.zigTypeTag()) { |
| 1316 | 1316 | .Vector => return self.fail("TODO implement add_with_overflow for Vector type", .{}), |
| 1317 | 1317 | .Int => { |
| 1318 | | const int_info = ty.intInfo(self.target.*); |
| 1319 | | |
| 1320 | | if (int_info.bits > 64) { |
| 1318 | if (abi_size > 8) { |
| 1321 | 1319 | return self.fail("TODO implement add_with_overflow for Ints larger than 64bits", .{}); |
| 1322 | 1320 | } |
| 1323 | 1321 | |
| 1324 | 1322 | try self.spillCompareFlagsIfOccupied(); |
| 1325 | | self.compare_flags_inst = inst; |
| 1326 | 1323 | |
| 1327 | 1324 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1328 | 1325 | const rhs = try self.resolveInst(bin_op.rhs); |
| ... | ... | @@ -1333,11 +1330,30 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1333 | 1330 | else => unreachable, |
| 1334 | 1331 | }; |
| 1335 | 1332 | const partial = try self.genBinOp(base_tag, null, lhs, rhs, ty, ty); |
| 1336 | | const result: MCValue = switch (int_info.signedness) { |
| 1337 | | .signed => .{ .register_overflow_signed = partial.register }, |
| 1338 | | .unsigned => .{ .register_overflow_unsigned = partial.register }, |
| 1339 | | }; |
| 1340 | | break :result result; |
| 1333 | |
| 1334 | const int_info = ty.intInfo(self.target.*); |
| 1335 | |
| 1336 | if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { |
| 1337 | self.compare_flags_inst = inst; |
| 1338 | |
| 1339 | const result: MCValue = switch (int_info.signedness) { |
| 1340 | .signed => .{ .register_overflow_signed = partial.register }, |
| 1341 | .unsigned => .{ .register_overflow_unsigned = partial.register }, |
| 1342 | }; |
| 1343 | break :result result; |
| 1344 | } |
| 1345 | |
| 1346 | self.compare_flags_inst = null; |
| 1347 | |
| 1348 | const tuple_ty = self.air.typeOfIndex(inst); |
| 1349 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); |
| 1350 | const tuple_align = tuple_ty.abiAlignment(self.target.*); |
| 1351 | const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); |
| 1352 | const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); |
| 1353 | |
| 1354 | try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, partial.register); |
| 1355 | |
| 1356 | break :result MCValue{ .stack_offset = stack_offset }; |
| 1341 | 1357 | }, |
| 1342 | 1358 | else => unreachable, |
| 1343 | 1359 | } |
| ... | ... | @@ -1346,6 +1362,75 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1346 | 1362 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1347 | 1363 | } |
| 1348 | 1364 | |
| 1365 | fn genSetStackTruncatedOverflowCompare( |
| 1366 | self: *Self, |
| 1367 | ty: Type, |
| 1368 | stack_offset: i32, |
| 1369 | overflow_bit_offset: i32, |
| 1370 | reg: Register, |
| 1371 | ) !void { |
| 1372 | const reg_lock = self.register_manager.lockReg(reg); |
| 1373 | defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 1374 | |
| 1375 | const int_info = ty.intInfo(self.target.*); |
| 1376 | const extended_ty = switch (int_info.signedness) { |
| 1377 | .signed => Type.isize, |
| 1378 | .unsigned => ty, |
| 1379 | }; |
| 1380 | |
| 1381 | const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }); |
| 1382 | const temp_regs_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs); |
| 1383 | defer for (temp_regs_locks) |rreg| { |
| 1384 | self.register_manager.unlockReg(rreg); |
| 1385 | }; |
| 1386 | |
| 1387 | const overflow_reg = temp_regs[0]; |
| 1388 | const flags: u2 = switch (int_info.signedness) { |
| 1389 | .signed => 0b00, |
| 1390 | .unsigned => 0b10, |
| 1391 | }; |
| 1392 | _ = try self.addInst(.{ |
| 1393 | .tag = .cond_set_byte_overflow, |
| 1394 | .ops = (Mir.Ops{ |
| 1395 | .reg1 = overflow_reg.to8(), |
| 1396 | .flags = flags, |
| 1397 | }).encode(), |
| 1398 | .data = undefined, |
| 1399 | }); |
| 1400 | |
| 1401 | const scratch_reg = temp_regs[1]; |
| 1402 | try self.genSetReg(extended_ty, scratch_reg, .{ .register = reg }); |
| 1403 | try self.truncateRegister(ty, scratch_reg); |
| 1404 | try self.genBinOpMir( |
| 1405 | .cmp, |
| 1406 | extended_ty, |
| 1407 | .{ .register = reg }, |
| 1408 | .{ .register = scratch_reg }, |
| 1409 | ); |
| 1410 | |
| 1411 | const eq_reg = temp_regs[2]; |
| 1412 | _ = try self.addInst(.{ |
| 1413 | .tag = .cond_set_byte_eq_ne, |
| 1414 | .ops = (Mir.Ops{ |
| 1415 | .reg1 = eq_reg.to8(), |
| 1416 | .flags = 0b00, |
| 1417 | }).encode(), |
| 1418 | .data = undefined, |
| 1419 | }); |
| 1420 | |
| 1421 | try self.genBinOpMir( |
| 1422 | .@"or", |
| 1423 | Type.u8, |
| 1424 | .{ .register = overflow_reg }, |
| 1425 | .{ .register = eq_reg }, |
| 1426 | ); |
| 1427 | |
| 1428 | try self.genSetStack(ty, stack_offset, .{ .register = scratch_reg }, .{}); |
| 1429 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ |
| 1430 | .register = overflow_reg.to8(), |
| 1431 | }, .{}); |
| 1432 | } |
| 1433 | |
| 1349 | 1434 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1350 | 1435 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1351 | 1436 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| ... | ... | @@ -1355,17 +1440,18 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1355 | 1440 | } |
| 1356 | 1441 | |
| 1357 | 1442 | const ty = self.air.typeOf(bin_op.lhs); |
| 1443 | const abi_size = ty.abiSize(self.target.*); |
| 1358 | 1444 | const result: MCValue = result: { |
| 1359 | 1445 | switch (ty.zigTypeTag()) { |
| 1360 | 1446 | .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}), |
| 1361 | 1447 | .Int => { |
| 1362 | | const int_info = ty.intInfo(self.target.*); |
| 1363 | | |
| 1364 | | if (int_info.bits > 64) { |
| 1448 | if (abi_size > 8) { |
| 1365 | 1449 | return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{}); |
| 1366 | 1450 | } |
| 1367 | 1451 | |
| 1368 | | if (math.isPowerOfTwo(int_info.bits)) { |
| 1452 | const int_info = ty.intInfo(self.target.*); |
| 1453 | |
| 1454 | if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { |
| 1369 | 1455 | try self.spillCompareFlagsIfOccupied(); |
| 1370 | 1456 | self.compare_flags_inst = inst; |
| 1371 | 1457 | |
| ... | ... | @@ -1428,71 +1514,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1428 | 1514 | }, |
| 1429 | 1515 | } |
| 1430 | 1516 | }; |
| 1431 | | const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); |
| 1432 | | defer self.register_manager.unlockReg(dst_reg_lock); |
| 1433 | 1517 | |
| 1434 | 1518 | const tuple_ty = self.air.typeOfIndex(inst); |
| 1435 | 1519 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); |
| 1436 | 1520 | const tuple_align = tuple_ty.abiAlignment(self.target.*); |
| 1437 | 1521 | const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); |
| 1438 | | |
| 1439 | 1522 | const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); |
| 1440 | | const extended_ty = switch (int_info.signedness) { |
| 1441 | | .signed => Type.isize, |
| 1442 | | .unsigned => ty, |
| 1443 | | }; |
| 1444 | | |
| 1445 | | const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }); |
| 1446 | | const temp_regs_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs); |
| 1447 | | defer for (temp_regs_locks) |reg| { |
| 1448 | | self.register_manager.unlockReg(reg); |
| 1449 | | }; |
| 1450 | | |
| 1451 | | const overflow_reg = temp_regs[0]; |
| 1452 | | const flags: u2 = switch (int_info.signedness) { |
| 1453 | | .signed => 0b00, |
| 1454 | | .unsigned => 0b10, |
| 1455 | | }; |
| 1456 | | _ = try self.addInst(.{ |
| 1457 | | .tag = .cond_set_byte_overflow, |
| 1458 | | .ops = (Mir.Ops{ |
| 1459 | | .reg1 = overflow_reg.to8(), |
| 1460 | | .flags = flags, |
| 1461 | | }).encode(), |
| 1462 | | .data = undefined, |
| 1463 | | }); |
| 1464 | | |
| 1465 | | const scratch_reg = temp_regs[1]; |
| 1466 | | try self.genSetReg(extended_ty, scratch_reg, .{ .register = dst_reg }); |
| 1467 | | try self.truncateRegister(ty, scratch_reg); |
| 1468 | | try self.genBinOpMir( |
| 1469 | | .cmp, |
| 1470 | | extended_ty, |
| 1471 | | .{ .register = dst_reg }, |
| 1472 | | .{ .register = scratch_reg }, |
| 1473 | | ); |
| 1474 | | |
| 1475 | | const eq_reg = temp_regs[2]; |
| 1476 | | _ = try self.addInst(.{ |
| 1477 | | .tag = .cond_set_byte_eq_ne, |
| 1478 | | .ops = (Mir.Ops{ |
| 1479 | | .reg1 = eq_reg.to8(), |
| 1480 | | .flags = 0b00, |
| 1481 | | }).encode(), |
| 1482 | | .data = undefined, |
| 1483 | | }); |
| 1484 | | |
| 1485 | | try self.genBinOpMir( |
| 1486 | | .@"or", |
| 1487 | | Type.u8, |
| 1488 | | .{ .register = overflow_reg }, |
| 1489 | | .{ .register = eq_reg }, |
| 1490 | | ); |
| 1491 | 1523 | |
| 1492 | | try self.genSetStack(ty, stack_offset, .{ .register = scratch_reg }, .{}); |
| 1493 | | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ |
| 1494 | | .register = overflow_reg.to8(), |
| 1495 | | }, .{}); |
| 1524 | try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, dst_reg); |
| 1496 | 1525 | |
| 1497 | 1526 | break :result MCValue{ .stack_offset = stack_offset }; |
| 1498 | 1527 | }, |