authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-10 20:45:57+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-10 20:45:57+02:00
log2a738599a0180b6f12439d33722a52182b11c21e
tree08aa21fa0e3d12b8a5977b4c0e78c0c3733e75d6
parentf131e41db9f8b1b1bbd678b52ef377821d83bddc

x64: implement missing bits in add_with_overflow and sub_with_overflow


2 files changed, 101 insertions(+), 73 deletions(-)

src/arch/x86_64/CodeGen.zig+101-72
......@@ -1311,18 +1311,15 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
13111311 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
13121312 const result = if (self.liveness.isUnused(inst)) .dead else result: {
13131313 const ty = self.air.typeOf(bin_op.lhs);
1314
1314 const abi_size = ty.abiSize(self.target.*);
13151315 switch (ty.zigTypeTag()) {
13161316 .Vector => return self.fail("TODO implement add_with_overflow for Vector type", .{}),
13171317 .Int => {
1318 const int_info = ty.intInfo(self.target.*);
1319
1320 if (int_info.bits > 64) {
1318 if (abi_size > 8) {
13211319 return self.fail("TODO implement add_with_overflow for Ints larger than 64bits", .{});
13221320 }
13231321
13241322 try self.spillCompareFlagsIfOccupied();
1325 self.compare_flags_inst = inst;
13261323
13271324 const lhs = try self.resolveInst(bin_op.lhs);
13281325 const rhs = try self.resolveInst(bin_op.rhs);
......@@ -1333,11 +1330,30 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
13331330 else => unreachable,
13341331 };
13351332 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 };
13411357 },
13421358 else => unreachable,
13431359 }
......@@ -1346,6 +1362,75 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
13461362 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
13471363}
13481364
1365fn 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
13491434fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
13501435 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
13511436 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 {
13551440 }
13561441
13571442 const ty = self.air.typeOf(bin_op.lhs);
1443 const abi_size = ty.abiSize(self.target.*);
13581444 const result: MCValue = result: {
13591445 switch (ty.zigTypeTag()) {
13601446 .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}),
13611447 .Int => {
1362 const int_info = ty.intInfo(self.target.*);
1363
1364 if (int_info.bits > 64) {
1448 if (abi_size > 8) {
13651449 return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{});
13661450 }
13671451
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) {
13691455 try self.spillCompareFlagsIfOccupied();
13701456 self.compare_flags_inst = inst;
13711457
......@@ -1428,71 +1514,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
14281514 },
14291515 }
14301516 };
1431 const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
1432 defer self.register_manager.unlockReg(dst_reg_lock);
14331517
14341518 const tuple_ty = self.air.typeOfIndex(inst);
14351519 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*));
14361520 const tuple_align = tuple_ty.abiAlignment(self.target.*);
14371521 const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*));
1438
14391522 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 );
14911523
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);
14961525
14971526 break :result MCValue{ .stack_offset = stack_offset };
14981527 },
test/behavior/math.zig-1
......@@ -640,7 +640,6 @@ test "@addWithOverflow" {
640640
641641test "small int addition" {
642642 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
643 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
644643 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
645644 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
646645