authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-19 17:35:58+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-19 17:35:58+01:00
log539bb8a2d7de08a9f7b63f136cb1ec8e5d88568a
tree515c8565f8cb54e40b3addef8293b107f93c26b1
parente86a89d3f0e911b99fa6432425a2799aada6f801
parent59df39e949907183e76ac78d7ace07e96f72e8a8
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10927 from ziglang/x64-idiv-imul

stage2,x64: implement integer division

5 files changed, 435 insertions(+), 24 deletions(-)

src/arch/x86_64/CodeGen.zig+234-20
......@@ -1252,30 +1252,244 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
12521252 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
12531253}
12541254
1255/// Generates signed or unsigned integer division.
1256/// Requires use of .rax and .rdx registers. Spills them if necessary.
1257/// Quotient is saved in .rax and remainder in .rdx.
1258fn genIntDivOpMir(
1259 self: *Self,
1260 ty: Type,
1261 signedness: std.builtin.Signedness,
1262 lhs: MCValue,
1263 rhs: MCValue,
1264) !void {
1265 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
1266 if (abi_size > 8) {
1267 return self.fail("TODO implement genIntDivOpMir for ABI size larger than 8", .{});
1268 }
1269
1270 try self.register_manager.getReg(.rax, null);
1271 try self.register_manager.getReg(.rdx, null);
1272 self.register_manager.freezeRegs(&.{ .rax, .rdx });
1273 defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
1274
1275 const dividend = switch (lhs) {
1276 .register => lhs,
1277 else => blk: {
1278 const reg = try self.copyToTmpRegister(ty, lhs);
1279 break :blk MCValue{ .register = reg };
1280 },
1281 };
1282 try self.genSetReg(ty, .rax, dividend);
1283
1284 self.register_manager.freezeRegs(&.{dividend.register});
1285 defer self.register_manager.unfreezeRegs(&.{dividend.register});
1286
1287 switch (signedness) {
1288 .signed => {
1289 _ = try self.addInst(.{
1290 .tag = .cwd,
1291 .ops = (Mir.Ops{
1292 .flags = 0b11,
1293 }).encode(),
1294 .data = undefined,
1295 });
1296 },
1297 .unsigned => {
1298 _ = try self.addInst(.{
1299 .tag = .xor,
1300 .ops = (Mir.Ops{
1301 .reg1 = .rdx,
1302 .reg2 = .rdx,
1303 }).encode(),
1304 .data = undefined,
1305 });
1306 },
1307 }
1308
1309 const divisor = switch (rhs) {
1310 .register => rhs,
1311 else => blk: {
1312 const reg = try self.copyToTmpRegister(ty, rhs);
1313 break :blk MCValue{ .register = reg };
1314 },
1315 };
1316 const op_tag: Mir.Inst.Tag = switch (signedness) {
1317 .signed => .idiv,
1318 .unsigned => .div,
1319 };
1320
1321 switch (divisor) {
1322 .register => |reg| {
1323 _ = try self.addInst(.{
1324 .tag = op_tag,
1325 .ops = (Mir.Ops{
1326 .reg1 = reg,
1327 }).encode(),
1328 .data = undefined,
1329 });
1330 },
1331 .stack_offset => |off| {
1332 _ = try self.addInst(.{
1333 .tag = op_tag,
1334 .ops = (Mir.Ops{
1335 .reg2 = .rbp,
1336 .flags = switch (abi_size) {
1337 1 => 0b00,
1338 2 => 0b01,
1339 4 => 0b10,
1340 8 => 0b11,
1341 else => unreachable,
1342 },
1343 }).encode(),
1344 .data = .{ .imm = @bitCast(u32, -off) },
1345 });
1346 },
1347 else => unreachable,
1348 }
1349}
1350
1351fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCValue {
1352 const signedness = ty.intInfo(self.target.*).signedness;
1353 const dividend = switch (lhs) {
1354 .register => |reg| reg,
1355 else => try self.copyToTmpRegister(ty, lhs),
1356 };
1357 self.register_manager.freezeRegs(&.{dividend});
1358
1359 const divisor = switch (rhs) {
1360 .register => |reg| reg,
1361 else => try self.copyToTmpRegister(ty, rhs),
1362 };
1363 self.register_manager.freezeRegs(&.{divisor});
1364 defer self.register_manager.unfreezeRegs(&.{ dividend, divisor });
1365
1366 try self.genIntDivOpMir(Type.isize, signedness, .{ .register = dividend }, .{ .register = divisor });
1367
1368 _ = try self.addInst(.{
1369 .tag = .xor,
1370 .ops = (Mir.Ops{
1371 .reg1 = divisor.to64(),
1372 .reg2 = dividend.to64(),
1373 }).encode(),
1374 .data = undefined,
1375 });
1376 _ = try self.addInst(.{
1377 .tag = .sar,
1378 .ops = (Mir.Ops{
1379 .reg1 = divisor.to64(),
1380 .flags = 0b10,
1381 }).encode(),
1382 .data = .{ .imm = 63 },
1383 });
1384 _ = try self.addInst(.{
1385 .tag = .@"test",
1386 .ops = (Mir.Ops{
1387 .reg1 = .rdx,
1388 .reg2 = .rdx,
1389 }).encode(),
1390 .data = undefined,
1391 });
1392 _ = try self.addInst(.{
1393 .tag = .cond_mov_eq,
1394 .ops = (Mir.Ops{
1395 .reg1 = divisor.to64(),
1396 .reg2 = .rdx,
1397 }).encode(),
1398 .data = undefined,
1399 });
1400 try self.genBinMathOpMir(.add, Type.isize, .{ .register = divisor.to64() }, .{ .register = .rax });
1401 return MCValue{ .register = divisor };
1402}
1403
12551404fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
12561405 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1257 const result: MCValue = if (self.liveness.isUnused(inst))
1258 .dead
1259 else
1260 return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
1406 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1407 const tag = self.air.instructions.items(.tag)[inst];
1408 const ty = self.air.typeOfIndex(inst);
1409
1410 if (ty.zigTypeTag() != .Int) {
1411 return self.fail("TODO implement {} for operands of dst type {}", .{ tag, ty.zigTypeTag() });
1412 }
1413
1414 if (tag == .div_float) {
1415 return self.fail("TODO implement {}", .{tag});
1416 }
1417
1418 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
1419 try self.register_manager.getReg(.rax, null);
1420 try self.register_manager.getReg(.rdx, null);
1421
1422 const lhs = try self.resolveInst(bin_op.lhs);
1423 const rhs = try self.resolveInst(bin_op.rhs);
1424
1425 const signedness = ty.intInfo(self.target.*).signedness;
1426 if (signedness == .unsigned) {
1427 try self.genIntDivOpMir(ty, signedness, lhs, rhs);
1428 break :result MCValue{ .register = .rax };
1429 }
1430
1431 switch (tag) {
1432 .div_exact, .div_trunc => {
1433 try self.genIntDivOpMir(ty, signedness, lhs, rhs);
1434 break :result MCValue{ .register = .rax };
1435 },
1436 .div_floor => {
1437 break :result try self.genInlineIntDivFloor(ty, lhs, rhs);
1438 },
1439 else => unreachable,
1440 }
1441 };
12611442 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
12621443}
12631444
12641445fn airRem(self: *Self, inst: Air.Inst.Index) !void {
12651446 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1266 const result: MCValue = if (self.liveness.isUnused(inst))
1267 .dead
1268 else
1269 return self.fail("TODO implement rem for {}", .{self.target.cpu.arch});
1447 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1448 const ty = self.air.typeOfIndex(inst);
1449 if (ty.zigTypeTag() != .Int) {
1450 return self.fail("TODO implement .rem for operands of dst type {}", .{ty.zigTypeTag()});
1451 }
1452 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
1453 try self.register_manager.getReg(.rax, null);
1454 try self.register_manager.getReg(.rdx, null);
1455 const lhs = try self.resolveInst(bin_op.lhs);
1456 const rhs = try self.resolveInst(bin_op.rhs);
1457 const signedness = ty.intInfo(self.target.*).signedness;
1458 try self.genIntDivOpMir(ty, signedness, lhs, rhs);
1459 break :result MCValue{ .register = .rdx };
1460 };
12701461 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
12711462}
12721463
12731464fn airMod(self: *Self, inst: Air.Inst.Index) !void {
12741465 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1275 const result: MCValue = if (self.liveness.isUnused(inst))
1276 .dead
1277 else
1278 return self.fail("TODO implement mod for {}", .{self.target.cpu.arch});
1466 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1467 const ty = self.air.typeOfIndex(inst);
1468 if (ty.zigTypeTag() != .Int) {
1469 return self.fail("TODO implement .mod for operands of dst type {}", .{ty.zigTypeTag()});
1470 }
1471 // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
1472 try self.register_manager.getReg(.rax, null);
1473 try self.register_manager.getReg(.rdx, null);
1474 const lhs = try self.resolveInst(bin_op.lhs);
1475 const rhs = try self.resolveInst(bin_op.rhs);
1476 const signedness = ty.intInfo(self.target.*).signedness;
1477 switch (signedness) {
1478 .unsigned => {
1479 try self.genIntDivOpMir(ty, signedness, lhs, rhs);
1480 break :result MCValue{ .register = .rdx };
1481 },
1482 .signed => {
1483 const div_floor = try self.genInlineIntDivFloor(ty, lhs, rhs);
1484 try self.genIMulOpMir(ty, div_floor, rhs);
1485
1486 const reg = try self.copyToTmpRegister(ty, lhs);
1487 try self.genBinMathOpMir(.sub, ty, .{ .register = reg }, div_floor);
1488
1489 break :result MCValue{ .register = reg };
1490 },
1491 }
1492 };
12791493 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
12801494}
12811495
......@@ -4126,7 +4340,7 @@ fn genInlineMemset(
41264340}
41274341
41284342fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
4129 const abi_size = ty.abiSize(self.target.*);
4343 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
41304344 switch (mcv) {
41314345 .dead => unreachable,
41324346 .ptr_stack_offset => |off| {
......@@ -4136,7 +4350,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
41364350 _ = try self.addInst(.{
41374351 .tag = .lea,
41384352 .ops = (Mir.Ops{
4139 .reg1 = registerAlias(reg, @intCast(u32, abi_size)),
4353 .reg1 = registerAlias(reg, abi_size),
41404354 .reg2 = .rbp,
41414355 }).encode(),
41424356 .data = .{ .imm = @bitCast(u32, -off) },
......@@ -4202,7 +4416,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
42024416 _ = try self.addInst(.{
42034417 .tag = .mov,
42044418 .ops = (Mir.Ops{
4205 .reg1 = registerAlias(reg, @intCast(u32, abi_size)),
4419 .reg1 = registerAlias(reg, abi_size),
42064420 }).encode(),
42074421 .data = .{ .imm = @truncate(u32, x) },
42084422 });
......@@ -4249,7 +4463,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
42494463 .tag = .mov_sign_extend,
42504464 .ops = (Mir.Ops{
42514465 .reg1 = reg.to64(),
4252 .reg2 = src_reg,
4466 .reg2 = registerAlias(src_reg, abi_size),
42534467 }).encode(),
42544468 .data = undefined,
42554469 });
......@@ -4260,7 +4474,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
42604474 .tag = .mov_zero_extend,
42614475 .ops = (Mir.Ops{
42624476 .reg1 = reg.to64(),
4263 .reg2 = src_reg,
4477 .reg2 = registerAlias(src_reg, abi_size),
42644478 }).encode(),
42654479 .data = undefined,
42664480 });
......@@ -4272,8 +4486,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
42724486 _ = try self.addInst(.{
42734487 .tag = .mov,
42744488 .ops = (Mir.Ops{
4275 .reg1 = registerAlias(reg, @divExact(src_reg.size(), 8)),
4276 .reg2 = src_reg,
4489 .reg1 = registerAlias(reg, abi_size),
4490 .reg2 = registerAlias(src_reg, abi_size),
42774491 }).encode(),
42784492 .data = undefined,
42794493 });
......@@ -4399,7 +4613,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
43994613 _ = try self.addInst(.{
44004614 .tag = .mov,
44014615 .ops = (Mir.Ops{
4402 .reg1 = registerAlias(reg, @intCast(u32, abi_size)),
4616 .reg1 = registerAlias(reg, abi_size),
44034617 .reg2 = .rbp,
44044618 .flags = 0b01,
44054619 }).encode(),
src/arch/x86_64/Emit.zig+87-2
......@@ -138,8 +138,13 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
138138 .shr => try emit.mirShift(.shr, inst),
139139 .sar => try emit.mirShift(.sar, inst),
140140
141 .imul => try emit.mirMulDiv(.imul, inst),
142 .idiv => try emit.mirMulDiv(.idiv, inst),
143 .div => try emit.mirMulDiv(.div, inst),
141144 .imul_complex => try emit.mirIMulComplex(inst),
142145
146 .cwd => try emit.mirCwd(inst),
147
143148 .push => try emit.mirPushPop(.push, inst),
144149 .pop => try emit.mirPushPop(.pop, inst),
145150
......@@ -156,6 +161,8 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
156161 .cond_set_byte_eq_ne,
157162 => try emit.mirCondSetByte(tag, inst),
158163
164 .cond_mov_eq => try emit.mirCondMov(.cmove, inst),
165
159166 .ret => try emit.mirRet(inst),
160167
161168 .syscall => try emit.mirSyscall(),
......@@ -368,6 +375,24 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne
368375 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code);
369376}
370377
378fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
379 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
380 if (ops.flags == 0b00) {
381 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
382 }
383 const imm = emit.mir.instructions.items(.data)[inst].imm;
384 const ptr_size: Memory.PtrSize = switch (ops.flags) {
385 0b00 => unreachable,
386 0b01 => .word_ptr,
387 0b10 => .dword_ptr,
388 0b11 => .qword_ptr,
389 };
390 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(ptr_size, .{
391 .disp = imm,
392 .base = ops.reg2,
393 }), emit.code);
394}
395
371396fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
372397 const tag = emit.mir.instructions.items(.tag)[inst];
373398 assert(tag == .@"test");
......@@ -386,7 +411,7 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
386411 return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code);
387412 }
388413 // TEST r/m64, r64
389 return emit.fail("TODO TEST r/m64, r64", .{});
414 return lowerToMrEnc(.@"test", RegisterOrMemory.reg(ops.reg1), ops.reg2, emit.code);
390415 },
391416 else => return emit.fail("TODO more TEST alternatives", .{}),
392417 }
......@@ -683,6 +708,27 @@ fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
683708 }
684709}
685710
711fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
712 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
713 if (ops.reg1 != .none) {
714 assert(ops.reg2 == .none);
715 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
716 }
717 assert(ops.reg1 == .none);
718 assert(ops.reg2 != .none);
719 const imm = emit.mir.instructions.items(.data)[inst].imm;
720 const ptr_size: Memory.PtrSize = switch (ops.flags) {
721 0b00 => .byte_ptr,
722 0b01 => .word_ptr,
723 0b10 => .dword_ptr,
724 0b11 => .qword_ptr,
725 };
726 return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{
727 .disp = imm,
728 .base = ops.reg2,
729 }), emit.code);
730}
731
686732fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
687733 const tag = emit.mir.instructions.items(.tag)[inst];
688734 assert(tag == .imul_complex);
......@@ -714,6 +760,17 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
714760 }
715761}
716762
763fn mirCwd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
764 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
765 const tag: Tag = switch (ops.flags) {
766 0b00 => .cbw,
767 0b01 => .cwd,
768 0b10 => .cdq,
769 0b11 => .cqo,
770 };
771 return lowerToZoEnc(tag, emit.code);
772}
773
717774fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
718775 const tag = emit.mir.instructions.items(.tag)[inst];
719776 assert(tag == .lea);
......@@ -1048,6 +1105,8 @@ const Tag = enum {
10481105 brk,
10491106 nop,
10501107 imul,
1108 idiv,
1109 div,
10511110 syscall,
10521111 ret_near,
10531112 ret_far,
......@@ -1115,6 +1174,12 @@ const Tag = enum {
11151174 sal,
11161175 shr,
11171176 sar,
1177 cbw,
1178 cwd,
1179 cdq,
1180 cqo,
1181 cmove,
1182 cmovz,
11181183
11191184 fn isSetCC(tag: Tag) bool {
11201185 return switch (tag) {
......@@ -1234,6 +1299,8 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
12341299 .brk => OpCode.oneByte(0xcc),
12351300 .nop => OpCode.oneByte(0x90),
12361301 .syscall => OpCode.twoByte(0x0f, 0x05),
1302 .cbw => OpCode.oneByte(0x98),
1303 .cwd, .cdq, .cqo => OpCode.oneByte(0x99),
12371304 else => null,
12381305 },
12391306 .d => return switch (tag) {
......@@ -1276,6 +1343,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
12761343 .setnl, .setge => OpCode.twoByte(0x0f, 0x9d),
12771344 .setle, .setng => OpCode.twoByte(0x0f, 0x9e),
12781345 .setnle, .setg => OpCode.twoByte(0x0f, 0x9f),
1346 .idiv, .div, .imul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),
12791347 else => null,
12801348 },
12811349 .o => return switch (tag) {
......@@ -1319,6 +1387,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
13191387 .sbb => OpCode.oneByte(if (is_one_byte) 0x18 else 0x19),
13201388 .cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39),
13211389 .mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89),
1390 .@"test" => OpCode.oneByte(if (is_one_byte) 0x84 else 0x85),
13221391 else => null,
13231392 },
13241393 .rm => return switch (tag) {
......@@ -1336,6 +1405,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
13361405 .movzx => OpCode.twoByte(0x0f, if (is_one_byte) 0xb6 else 0xb7),
13371406 .lea => OpCode.oneByte(if (is_one_byte) 0x8c else 0x8d),
13381407 .imul => OpCode.twoByte(0x0f, 0xaf),
1408 .cmove, .cmovz => OpCode.twoByte(0x0f, 0x44),
13391409 else => null,
13401410 },
13411411 .oi => return switch (tag) {
......@@ -1409,6 +1479,9 @@ inline fn getModRmExt(tag: Tag) ?u3 {
14091479 => 0x4,
14101480 .shr => 0x5,
14111481 .sar => 0x7,
1482 .imul => 0x5,
1483 .idiv => 0x7,
1484 .div => 0x6,
14121485 else => null,
14131486 };
14141487}
......@@ -1565,7 +1638,15 @@ const RegisterOrMemory = union(enum) {
15651638
15661639fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void {
15671640 const opc = getOpCode(tag, .zo, false).?;
1568 const encoder = try Encoder.init(code, 1);
1641 const encoder = try Encoder.init(code, 2);
1642 switch (tag) {
1643 .cqo => {
1644 encoder.rex(.{
1645 .w = true,
1646 });
1647 },
1648 else => {},
1649 }
15691650 opc.encode(encoder);
15701651}
15711652
......@@ -2204,6 +2285,10 @@ test "lower M encoding" {
22042285 try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", emit.lowered(), "jmp qword ptr [ds:0x10]");
22052286 try lowerToMEnc(.seta, RegisterOrMemory.reg(.r11b), emit.code());
22062287 try expectEqualHexStrings("\x41\x0F\x97\xC3", emit.lowered(), "seta r11b");
2288 try lowerToMEnc(.idiv, RegisterOrMemory.reg(.rax), emit.code());
2289 try expectEqualHexStrings("\x48\xF7\xF8", emit.lowered(), "idiv rax");
2290 try lowerToMEnc(.imul, RegisterOrMemory.reg(.al), emit.code());
2291 try expectEqualHexStrings("\xF6\xE8", emit.lowered(), "imul al");
22072292}
22082293
22092294test "lower M1 and MC encodings" {
src/arch/x86_64/Mir.zig+20-2
......@@ -220,10 +220,21 @@ pub const Inst = struct {
220220 sar_mem_index_imm,
221221
222222 /// ops flags: form:
223 /// 0bX0 reg1
224 /// 0bX1 [reg1 + imm32]
223 /// 0b00 reg1
224 /// 0b00 byte ptr [reg2 + imm32]
225 /// 0b01 word ptr [reg2 + imm32]
226 /// 0b10 dword ptr [reg2 + imm32]
227 /// 0b11 qword ptr [reg2 + imm32]
225228 imul,
226229 idiv,
230 div,
231
232 /// ops flags: form:
233 /// 0b00 AX <- AL
234 /// 0b01 DX:AX <- AX
235 /// 0b10 EDX:EAX <- EAX
236 /// 0b11 RDX:RAX <- RAX
237 cwd,
227238
228239 /// ops flags: form:
229240 /// 0b00 reg1, reg2
......@@ -275,6 +286,13 @@ pub const Inst = struct {
275286 cond_jmp_eq_ne,
276287 cond_set_byte_eq_ne,
277288
289 /// ops flags:
290 /// 0b00 reg1, reg2,
291 /// 0b01 reg1, word ptr [reg2 + imm]
292 /// 0b10 reg1, dword ptr [reg2 + imm]
293 /// 0b11 reg1, qword ptr [reg2 + imm]
294 cond_mov_eq,
295
278296 /// ops flags: form:
279297 /// 0b00 reg1
280298 /// 0b01 [reg1 + imm32]
test/behavior.zig+1
......@@ -50,6 +50,7 @@ test {
5050 _ = @import("behavior/tuple.zig");
5151 _ = @import("behavior/type.zig");
5252 _ = @import("behavior/var_args.zig");
53 _ = @import("behavior/int_div.zig");
5354
5455 // tests that don't pass for stage1
5556 if (builtin.zig_backend != .stage1) {
test/behavior/int_div.zig created+93
......@@ -0,0 +1,93 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4
5test "integer division" {
6 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
8 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
10
11 try testDivision();
12 comptime try testDivision();
13}
14fn testDivision() !void {
15 try expect(div(u32, 13, 3) == 4);
16 try expect(div(u64, 13, 3) == 4);
17 try expect(div(u8, 13, 3) == 4);
18
19 try expect(divExact(u32, 55, 11) == 5);
20 try expect(divExact(i32, -55, 11) == -5);
21 try expect(divExact(i64, -55, 11) == -5);
22 try expect(divExact(i16, -55, 11) == -5);
23
24 try expect(divFloor(i8, 5, 3) == 1);
25 try expect(divFloor(i16, -5, 3) == -2);
26 try expect(divFloor(i64, -0x80000000, -2) == 0x40000000);
27 try expect(divFloor(i32, 0, -0x80000000) == 0);
28 try expect(divFloor(i64, -0x40000001, 0x40000000) == -2);
29 try expect(divFloor(i32, -0x80000000, 1) == -0x80000000);
30 try expect(divFloor(i32, 10, 12) == 0);
31 try expect(divFloor(i32, -14, 12) == -2);
32 try expect(divFloor(i32, -2, 12) == -1);
33
34 try expect(divTrunc(i32, 5, 3) == 1);
35 try expect(divTrunc(i32, -5, 3) == -1);
36 try expect(divTrunc(i32, 9, -10) == 0);
37 try expect(divTrunc(i32, -9, 10) == 0);
38 try expect(divTrunc(i32, 10, 12) == 0);
39 try expect(divTrunc(i32, -14, 12) == -1);
40 try expect(divTrunc(i32, -2, 12) == 0);
41
42 try expect(mod(u32, 10, 12) == 10);
43 try expect(mod(i32, 10, 12) == 10);
44 try expect(mod(i64, -14, 12) == 10);
45 try expect(mod(i16, -2, 12) == 10);
46 try expect(mod(i8, -2, 12) == 10);
47
48 try expect(rem(i32, 10, 12) == 10);
49 try expect(rem(i32, -14, 12) == -2);
50 try expect(rem(i32, -2, 12) == -2);
51
52 comptime {
53 try expect(
54 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600,
55 );
56 try expect(
57 @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600,
58 );
59 try expect(
60 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,
61 );
62 try expect(
63 @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
64 );
65 try expect(
66 @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,
67 );
68 try expect(
69 @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,
70 );
71 try expect(
72 4126227191251978491697987544882340798050766755606969681711 % 10 == 1,
73 );
74 }
75}
76fn div(comptime T: type, a: T, b: T) T {
77 return a / b;
78}
79fn divExact(comptime T: type, a: T, b: T) T {
80 return @divExact(a, b);
81}
82fn divFloor(comptime T: type, a: T, b: T) T {
83 return @divFloor(a, b);
84}
85fn divTrunc(comptime T: type, a: T, b: T) T {
86 return @divTrunc(a, b);
87}
88fn mod(comptime T: type, a: T, b: T) T {
89 return @mod(a, b);
90}
91fn rem(comptime T: type, a: T, b: T) T {
92 return @rem(a, b);
93}