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 {...@@ -1311,18 +1311,15 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1311 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1311 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1312 const result = if (self.liveness.isUnused(inst)) .dead else result: {1312 const result = if (self.liveness.isUnused(inst)) .dead else result: {
1313 const ty = self.air.typeOf(bin_op.lhs);1313 const ty = self.air.typeOf(bin_op.lhs);
13141314 const abi_size = ty.abiSize(self.target.*);
1315 switch (ty.zigTypeTag()) {1315 switch (ty.zigTypeTag()) {
1316 .Vector => return self.fail("TODO implement add_with_overflow for Vector type", .{}),1316 .Vector => return self.fail("TODO implement add_with_overflow for Vector type", .{}),
1317 .Int => {1317 .Int => {
1318 const int_info = ty.intInfo(self.target.*);1318 if (abi_size > 8) {
1319
1320 if (int_info.bits > 64) {
1321 return self.fail("TODO implement add_with_overflow for Ints larger than 64bits", .{});1319 return self.fail("TODO implement add_with_overflow for Ints larger than 64bits", .{});
1322 }1320 }
13231321
1324 try self.spillCompareFlagsIfOccupied();1322 try self.spillCompareFlagsIfOccupied();
1325 self.compare_flags_inst = inst;
13261323
1327 const lhs = try self.resolveInst(bin_op.lhs);1324 const lhs = try self.resolveInst(bin_op.lhs);
1328 const rhs = try self.resolveInst(bin_op.rhs);1325 const rhs = try self.resolveInst(bin_op.rhs);
...@@ -1333,11 +1330,30 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1333,11 +1330,30 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1333 else => unreachable,1330 else => unreachable,
1334 };1331 };
1335 const partial = try self.genBinOp(base_tag, null, lhs, rhs, ty, ty);1332 const partial = try self.genBinOp(base_tag, null, lhs, rhs, ty, ty);
1336 const result: MCValue = switch (int_info.signedness) {1333
1337 .signed => .{ .register_overflow_signed = partial.register },1334 const int_info = ty.intInfo(self.target.*);
1338 .unsigned => .{ .register_overflow_unsigned = partial.register },1335
1339 };1336 if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) {
1340 break :result result;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 else => unreachable,1358 else => unreachable,
1343 }1359 }
...@@ -1346,6 +1362,75 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1346,6 +1362,75 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1346 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1362 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1347}1363}
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
1349fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1434fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1350 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1435 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1351 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;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,17 +1440,18 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1355 }1440 }
13561441
1357 const ty = self.air.typeOf(bin_op.lhs);1442 const ty = self.air.typeOf(bin_op.lhs);
1443 const abi_size = ty.abiSize(self.target.*);
1358 const result: MCValue = result: {1444 const result: MCValue = result: {
1359 switch (ty.zigTypeTag()) {1445 switch (ty.zigTypeTag()) {
1360 .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}),1446 .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}),
1361 .Int => {1447 .Int => {
1362 const int_info = ty.intInfo(self.target.*);1448 if (abi_size > 8) {
1363
1364 if (int_info.bits > 64) {
1365 return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{});1449 return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{});
1366 }1450 }
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) {
1369 try self.spillCompareFlagsIfOccupied();1455 try self.spillCompareFlagsIfOccupied();
1370 self.compare_flags_inst = inst;1456 self.compare_flags_inst = inst;
13711457
...@@ -1428,71 +1514,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -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);
14331517
1434 const tuple_ty = self.air.typeOfIndex(inst);1518 const tuple_ty = self.air.typeOfIndex(inst);
1435 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*));1519 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*));
1436 const tuple_align = tuple_ty.abiAlignment(self.target.*);1520 const tuple_align = tuple_ty.abiAlignment(self.target.*);
1437 const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*));1521 const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*));
1438
1439 const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align));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 );
14911523
1492 try self.genSetStack(ty, stack_offset, .{ .register = scratch_reg }, .{});1524 try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, dst_reg);
1493 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{
1494 .register = overflow_reg.to8(),
1495 }, .{});
14961525
1497 break :result MCValue{ .stack_offset = stack_offset };1526 break :result MCValue{ .stack_offset = stack_offset };
1498 },1527 },
test/behavior/math.zig-1
...@@ -640,7 +640,6 @@ test "@addWithOverflow" {...@@ -640,7 +640,6 @@ test "@addWithOverflow" {
640640
641test "small int addition" {641test "small int addition" {
642 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO642 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
643 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
644 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO643 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
645 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO644 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
646645