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 {...@@ -1252,30 +1252,244 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1252 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});1252 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
1253}1253}
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
1255fn airDiv(self: *Self, inst: Air.Inst.Index) !void {1404fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1256 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1405 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1257 const result: MCValue = if (self.liveness.isUnused(inst))1406 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1258 .dead1407 const tag = self.air.instructions.items(.tag)[inst];
1259 else1408 const ty = self.air.typeOfIndex(inst);
1260 return self.fail("TODO implement div for {}", .{self.target.cpu.arch});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 };
1261 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1442 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1262}1443}
12631444
1264fn airRem(self: *Self, inst: Air.Inst.Index) !void {1445fn airRem(self: *Self, inst: Air.Inst.Index) !void {
1265 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1446 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1266 const result: MCValue = if (self.liveness.isUnused(inst))1447 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1267 .dead1448 const ty = self.air.typeOfIndex(inst);
1268 else1449 if (ty.zigTypeTag() != .Int) {
1269 return self.fail("TODO implement rem for {}", .{self.target.cpu.arch});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 };
1270 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1461 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1271}1462}
12721463
1273fn airMod(self: *Self, inst: Air.Inst.Index) !void {1464fn airMod(self: *Self, inst: Air.Inst.Index) !void {
1274 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1465 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1275 const result: MCValue = if (self.liveness.isUnused(inst))1466 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1276 .dead1467 const ty = self.air.typeOfIndex(inst);
1277 else1468 if (ty.zigTypeTag() != .Int) {
1278 return self.fail("TODO implement mod for {}", .{self.target.cpu.arch});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 };
1279 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1493 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1280}1494}
12811495
...@@ -4126,7 +4340,7 @@ fn genInlineMemset(...@@ -4126,7 +4340,7 @@ fn genInlineMemset(
4126}4340}
41274341
4128fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {4342fn 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.*));
4130 switch (mcv) {4344 switch (mcv) {
4131 .dead => unreachable,4345 .dead => unreachable,
4132 .ptr_stack_offset => |off| {4346 .ptr_stack_offset => |off| {
...@@ -4136,7 +4350,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4136,7 +4350,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4136 _ = try self.addInst(.{4350 _ = try self.addInst(.{
4137 .tag = .lea,4351 .tag = .lea,
4138 .ops = (Mir.Ops{4352 .ops = (Mir.Ops{
4139 .reg1 = registerAlias(reg, @intCast(u32, abi_size)),4353 .reg1 = registerAlias(reg, abi_size),
4140 .reg2 = .rbp,4354 .reg2 = .rbp,
4141 }).encode(),4355 }).encode(),
4142 .data = .{ .imm = @bitCast(u32, -off) },4356 .data = .{ .imm = @bitCast(u32, -off) },
...@@ -4202,7 +4416,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4202,7 +4416,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4202 _ = try self.addInst(.{4416 _ = try self.addInst(.{
4203 .tag = .mov,4417 .tag = .mov,
4204 .ops = (Mir.Ops{4418 .ops = (Mir.Ops{
4205 .reg1 = registerAlias(reg, @intCast(u32, abi_size)),4419 .reg1 = registerAlias(reg, abi_size),
4206 }).encode(),4420 }).encode(),
4207 .data = .{ .imm = @truncate(u32, x) },4421 .data = .{ .imm = @truncate(u32, x) },
4208 });4422 });
...@@ -4249,7 +4463,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4249,7 +4463,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4249 .tag = .mov_sign_extend,4463 .tag = .mov_sign_extend,
4250 .ops = (Mir.Ops{4464 .ops = (Mir.Ops{
4251 .reg1 = reg.to64(),4465 .reg1 = reg.to64(),
4252 .reg2 = src_reg,4466 .reg2 = registerAlias(src_reg, abi_size),
4253 }).encode(),4467 }).encode(),
4254 .data = undefined,4468 .data = undefined,
4255 });4469 });
...@@ -4260,7 +4474,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4260,7 +4474,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4260 .tag = .mov_zero_extend,4474 .tag = .mov_zero_extend,
4261 .ops = (Mir.Ops{4475 .ops = (Mir.Ops{
4262 .reg1 = reg.to64(),4476 .reg1 = reg.to64(),
4263 .reg2 = src_reg,4477 .reg2 = registerAlias(src_reg, abi_size),
4264 }).encode(),4478 }).encode(),
4265 .data = undefined,4479 .data = undefined,
4266 });4480 });
...@@ -4272,8 +4486,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4272,8 +4486,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4272 _ = try self.addInst(.{4486 _ = try self.addInst(.{
4273 .tag = .mov,4487 .tag = .mov,
4274 .ops = (Mir.Ops{4488 .ops = (Mir.Ops{
4275 .reg1 = registerAlias(reg, @divExact(src_reg.size(), 8)),4489 .reg1 = registerAlias(reg, abi_size),
4276 .reg2 = src_reg,4490 .reg2 = registerAlias(src_reg, abi_size),
4277 }).encode(),4491 }).encode(),
4278 .data = undefined,4492 .data = undefined,
4279 });4493 });
...@@ -4399,7 +4613,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4399,7 +4613,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4399 _ = try self.addInst(.{4613 _ = try self.addInst(.{
4400 .tag = .mov,4614 .tag = .mov,
4401 .ops = (Mir.Ops{4615 .ops = (Mir.Ops{
4402 .reg1 = registerAlias(reg, @intCast(u32, abi_size)),4616 .reg1 = registerAlias(reg, abi_size),
4403 .reg2 = .rbp,4617 .reg2 = .rbp,
4404 .flags = 0b01,4618 .flags = 0b01,
4405 }).encode(),4619 }).encode(),
src/arch/x86_64/Emit.zig+87-2
...@@ -138,8 +138,13 @@ pub fn lowerMir(emit: *Emit) InnerError!void {...@@ -138,8 +138,13 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
138 .shr => try emit.mirShift(.shr, inst),138 .shr => try emit.mirShift(.shr, inst),
139 .sar => try emit.mirShift(.sar, inst),139 .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),
141 .imul_complex => try emit.mirIMulComplex(inst),144 .imul_complex => try emit.mirIMulComplex(inst),
142145
146 .cwd => try emit.mirCwd(inst),
147
143 .push => try emit.mirPushPop(.push, inst),148 .push => try emit.mirPushPop(.push, inst),
144 .pop => try emit.mirPushPop(.pop, inst),149 .pop => try emit.mirPushPop(.pop, inst),
145150
...@@ -156,6 +161,8 @@ pub fn lowerMir(emit: *Emit) InnerError!void {...@@ -156,6 +161,8 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
156 .cond_set_byte_eq_ne,161 .cond_set_byte_eq_ne,
157 => try emit.mirCondSetByte(tag, inst),162 => try emit.mirCondSetByte(tag, inst),
158163
164 .cond_mov_eq => try emit.mirCondMov(.cmove, inst),
165
159 .ret => try emit.mirRet(inst),166 .ret => try emit.mirRet(inst),
160167
161 .syscall => try emit.mirSyscall(),168 .syscall => try emit.mirSyscall(),
...@@ -368,6 +375,24 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne...@@ -368,6 +375,24 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne
368 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code);375 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code);
369}376}
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
371fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {396fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
372 const tag = emit.mir.instructions.items(.tag)[inst];397 const tag = emit.mir.instructions.items(.tag)[inst];
373 assert(tag == .@"test");398 assert(tag == .@"test");
...@@ -386,7 +411,7 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -386,7 +411,7 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
386 return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code);411 return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code);
387 }412 }
388 // TEST r/m64, r64413 // 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);
390 },415 },
391 else => return emit.fail("TODO more TEST alternatives", .{}),416 else => return emit.fail("TODO more TEST alternatives", .{}),
392 }417 }
...@@ -683,6 +708,27 @@ fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {...@@ -683,6 +708,27 @@ fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
683 }708 }
684}709}
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
686fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {732fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
687 const tag = emit.mir.instructions.items(.tag)[inst];733 const tag = emit.mir.instructions.items(.tag)[inst];
688 assert(tag == .imul_complex);734 assert(tag == .imul_complex);
...@@ -714,6 +760,17 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -714,6 +760,17 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
714 }760 }
715}761}
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
717fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {774fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
718 const tag = emit.mir.instructions.items(.tag)[inst];775 const tag = emit.mir.instructions.items(.tag)[inst];
719 assert(tag == .lea);776 assert(tag == .lea);
...@@ -1048,6 +1105,8 @@ const Tag = enum {...@@ -1048,6 +1105,8 @@ const Tag = enum {
1048 brk,1105 brk,
1049 nop,1106 nop,
1050 imul,1107 imul,
1108 idiv,
1109 div,
1051 syscall,1110 syscall,
1052 ret_near,1111 ret_near,
1053 ret_far,1112 ret_far,
...@@ -1115,6 +1174,12 @@ const Tag = enum {...@@ -1115,6 +1174,12 @@ const Tag = enum {
1115 sal,1174 sal,
1116 shr,1175 shr,
1117 sar,1176 sar,
1177 cbw,
1178 cwd,
1179 cdq,
1180 cqo,
1181 cmove,
1182 cmovz,
11181183
1119 fn isSetCC(tag: Tag) bool {1184 fn isSetCC(tag: Tag) bool {
1120 return switch (tag) {1185 return switch (tag) {
...@@ -1234,6 +1299,8 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {...@@ -1234,6 +1299,8 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
1234 .brk => OpCode.oneByte(0xcc),1299 .brk => OpCode.oneByte(0xcc),
1235 .nop => OpCode.oneByte(0x90),1300 .nop => OpCode.oneByte(0x90),
1236 .syscall => OpCode.twoByte(0x0f, 0x05),1301 .syscall => OpCode.twoByte(0x0f, 0x05),
1302 .cbw => OpCode.oneByte(0x98),
1303 .cwd, .cdq, .cqo => OpCode.oneByte(0x99),
1237 else => null,1304 else => null,
1238 },1305 },
1239 .d => return switch (tag) {1306 .d => return switch (tag) {
...@@ -1276,6 +1343,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {...@@ -1276,6 +1343,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
1276 .setnl, .setge => OpCode.twoByte(0x0f, 0x9d),1343 .setnl, .setge => OpCode.twoByte(0x0f, 0x9d),
1277 .setle, .setng => OpCode.twoByte(0x0f, 0x9e),1344 .setle, .setng => OpCode.twoByte(0x0f, 0x9e),
1278 .setnle, .setg => OpCode.twoByte(0x0f, 0x9f),1345 .setnle, .setg => OpCode.twoByte(0x0f, 0x9f),
1346 .idiv, .div, .imul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),
1279 else => null,1347 else => null,
1280 },1348 },
1281 .o => return switch (tag) {1349 .o => return switch (tag) {
...@@ -1319,6 +1387,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {...@@ -1319,6 +1387,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
1319 .sbb => OpCode.oneByte(if (is_one_byte) 0x18 else 0x19),1387 .sbb => OpCode.oneByte(if (is_one_byte) 0x18 else 0x19),
1320 .cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39),1388 .cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39),
1321 .mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89),1389 .mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89),
1390 .@"test" => OpCode.oneByte(if (is_one_byte) 0x84 else 0x85),
1322 else => null,1391 else => null,
1323 },1392 },
1324 .rm => return switch (tag) {1393 .rm => return switch (tag) {
...@@ -1336,6 +1405,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {...@@ -1336,6 +1405,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
1336 .movzx => OpCode.twoByte(0x0f, if (is_one_byte) 0xb6 else 0xb7),1405 .movzx => OpCode.twoByte(0x0f, if (is_one_byte) 0xb6 else 0xb7),
1337 .lea => OpCode.oneByte(if (is_one_byte) 0x8c else 0x8d),1406 .lea => OpCode.oneByte(if (is_one_byte) 0x8c else 0x8d),
1338 .imul => OpCode.twoByte(0x0f, 0xaf),1407 .imul => OpCode.twoByte(0x0f, 0xaf),
1408 .cmove, .cmovz => OpCode.twoByte(0x0f, 0x44),
1339 else => null,1409 else => null,
1340 },1410 },
1341 .oi => return switch (tag) {1411 .oi => return switch (tag) {
...@@ -1409,6 +1479,9 @@ inline fn getModRmExt(tag: Tag) ?u3 {...@@ -1409,6 +1479,9 @@ inline fn getModRmExt(tag: Tag) ?u3 {
1409 => 0x4,1479 => 0x4,
1410 .shr => 0x5,1480 .shr => 0x5,
1411 .sar => 0x7,1481 .sar => 0x7,
1482 .imul => 0x5,
1483 .idiv => 0x7,
1484 .div => 0x6,
1412 else => null,1485 else => null,
1413 };1486 };
1414}1487}
...@@ -1565,7 +1638,15 @@ const RegisterOrMemory = union(enum) {...@@ -1565,7 +1638,15 @@ const RegisterOrMemory = union(enum) {
15651638
1566fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void {1639fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void {
1567 const opc = getOpCode(tag, .zo, false).?;1640 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 }
1569 opc.encode(encoder);1650 opc.encode(encoder);
1570}1651}
15711652
...@@ -2204,6 +2285,10 @@ test "lower M encoding" {...@@ -2204,6 +2285,10 @@ test "lower M encoding" {
2204 try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", emit.lowered(), "jmp qword ptr [ds:0x10]");2285 try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", emit.lowered(), "jmp qword ptr [ds:0x10]");
2205 try lowerToMEnc(.seta, RegisterOrMemory.reg(.r11b), emit.code());2286 try lowerToMEnc(.seta, RegisterOrMemory.reg(.r11b), emit.code());
2206 try expectEqualHexStrings("\x41\x0F\x97\xC3", emit.lowered(), "seta r11b");2287 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");
2207}2292}
22082293
2209test "lower M1 and MC encodings" {2294test "lower M1 and MC encodings" {
src/arch/x86_64/Mir.zig+20-2
...@@ -220,10 +220,21 @@ pub const Inst = struct {...@@ -220,10 +220,21 @@ pub const Inst = struct {
220 sar_mem_index_imm,220 sar_mem_index_imm,
221221
222 /// ops flags: form:222 /// ops flags: form:
223 /// 0bX0 reg1223 /// 0b00 reg1
224 /// 0bX1 [reg1 + imm32]224 /// 0b00 byte ptr [reg2 + imm32]
225 /// 0b01 word ptr [reg2 + imm32]
226 /// 0b10 dword ptr [reg2 + imm32]
227 /// 0b11 qword ptr [reg2 + imm32]
225 imul,228 imul,
226 idiv,229 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
228 /// ops flags: form:239 /// ops flags: form:
229 /// 0b00 reg1, reg2240 /// 0b00 reg1, reg2
...@@ -275,6 +286,13 @@ pub const Inst = struct {...@@ -275,6 +286,13 @@ pub const Inst = struct {
275 cond_jmp_eq_ne,286 cond_jmp_eq_ne,
276 cond_set_byte_eq_ne,287 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
278 /// ops flags: form:296 /// ops flags: form:
279 /// 0b00 reg1297 /// 0b00 reg1
280 /// 0b01 [reg1 + imm32]298 /// 0b01 [reg1 + imm32]
test/behavior.zig+1
...@@ -50,6 +50,7 @@ test {...@@ -50,6 +50,7 @@ test {
50 _ = @import("behavior/tuple.zig");50 _ = @import("behavior/tuple.zig");
51 _ = @import("behavior/type.zig");51 _ = @import("behavior/type.zig");
52 _ = @import("behavior/var_args.zig");52 _ = @import("behavior/var_args.zig");
53 _ = @import("behavior/int_div.zig");
5354
54 // tests that don't pass for stage155 // tests that don't pass for stage1
55 if (builtin.zig_backend != .stage1) {56 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}