authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-18 17:36:03+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-19 14:24:03+01:00
logbd396d7e076c09d162f679c7d26ba883dbe5d7cf
tree824be689e26571cc078bf7d1d6833fd44208b42f
parent2c13a4b87e1d9ae5c5c1117bc03b5fb67825ca34

x64: add unsigned div and move logic into a helper fn


3 files changed, 119 insertions(+), 78 deletions(-)

src/arch/x86_64/CodeGen.zig+111-74
...@@ -1252,37 +1252,54 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1252,37 +1252,54 @@ 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
1255fn airDiv(self: *Self, inst: Air.Inst.Index) !void {1255/// Perform signed and unsigned integer division.
1256 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1256/// TODO it might be wise to split some functionality into integer and floating-point
1257 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1257/// specialised functions.
1258 const dst_ty = self.air.typeOfIndex(inst);1258/// Supports AIR tag:
1259 const tag = self.air.instructions.items(.tag)[inst];1259/// .div_exact, .div_trunc, .div_floor, .mod, .rem
1260 switch (tag) {1260fn genDivOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
1261 .div_exact => {},1261 const dst_ty = self.air.typeOfIndex(inst);
1262 .div_trunc, .div_floor, .div_float => return self.fail("TODO implement {}", .{tag}),1262 const tag = self.air.instructions.items(.tag)[inst];
1263 else => unreachable,
1264 }
12651263
1266 if (dst_ty.zigTypeTag() != .Int) {1264 switch (tag) {
1267 return self.fail("TODO implement {} for operands of type {}", .{ tag, dst_ty.zigTypeTag() });1265 .div_exact, .div_trunc, .div_floor, .mod, .rem => {},
1268 }1266 .div_float => return self.fail("TODO implement genDivOp for {}", .{tag}),
1267 else => unreachable,
1268 }
1269
1270 if (dst_ty.zigTypeTag() != .Int) {
1271 return self.fail("TODO implement {} for operands of type {}", .{ tag, dst_ty.zigTypeTag() });
1272 }
1273 if (dst_ty.abiSize(self.target.*) > 8) {
1274 return self.fail("TODO implement {} for ABI size larger than 8", .{tag});
1275 }
12691276
1270 const signedness = dst_ty.intInfo(self.target.*).signedness;1277 const signedness = dst_ty.intInfo(self.target.*).signedness;
1271 const ty = if (signedness == .signed) Type.isize else dst_ty;1278 const tmp_ty = switch (signedness) {
1272 const abi_size = @intCast(u32, ty.abiSize(self.target.*));1279 .signed => Type.isize,
1280 .unsigned => dst_ty,
1281 };
1282 const abi_size = @intCast(u32, tmp_ty.abiSize(self.target.*));
12731283
1274 const lhs = try self.resolveInst(bin_op.lhs);1284 const lhs = try self.resolveInst(op_lhs);
1275 blk: {1285 blk: {
1276 switch (lhs) {1286 switch (lhs) {
1277 .register => |reg| {1287 .register => |reg| {
1278 if (reg.to64() == .rax) break :blk;1288 if (reg.to64() == .rax) break :blk;
1279 },1289 },
1280 else => {},1290 else => {},
1281 }
1282 try self.register_manager.getReg(.rax, inst); // track inst -> rax in register manager
1283 try self.genSetReg(ty, .rax, lhs);
1284 }1291 }
1285 if (signedness == .signed) {1292 try self.register_manager.getReg(.rax, inst); // track inst -> rax in register manager
1293 try self.genSetReg(tmp_ty, .rax, lhs);
1294 }
1295
1296 try self.register_manager.getReg(.rdx, null);
1297 self.register_manager.freezeRegs(&.{ .rax, .rdx });
1298 defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
1299
1300 // Prep rdx for the op
1301 switch (signedness) {
1302 .signed => {
1286 _ = try self.addInst(.{1303 _ = try self.addInst(.{
1287 .tag = .cwd,1304 .tag = .cwd,
1288 .ops = (Mir.Ops{1305 .ops = (Mir.Ops{
...@@ -1290,56 +1307,76 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void {...@@ -1290,56 +1307,76 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1290 }).encode(),1307 }).encode(),
1291 .data = undefined,1308 .data = undefined,
1292 });1309 });
1293 }1310 },
1294 const dst_mcv = MCValue{ .register = registerAlias(.rax, abi_size) };1311 .unsigned => {
12951312 _ = try self.addInst(.{
1296 try self.register_manager.getReg(.rdx, null);1313 .tag = .xor,
1297 self.register_manager.freezeRegs(&.{ .rax, .rdx });1314 .ops = (Mir.Ops{
1298 defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });1315 .reg1 = .rdx,
12991316 .reg2 = .rdx,
1300 const rhs = try self.resolveInst(bin_op.rhs);1317 }).encode(),
1301 const divisor = blk: {1318 .data = undefined,
1302 switch (rhs) {1319 });
1303 .register, .stack_offset => break :blk rhs,1320 },
1304 else => {1321 }
1305 const reg = try self.copyToTmpRegister(ty, rhs);
1306 break :blk MCValue{ .register = reg };
1307 },
1308 }
1309 };
13101322
1311 switch (divisor) {1323 const rhs = try self.resolveInst(op_rhs);
1312 .register => |reg| {1324 const divisor = blk: {
1313 _ = try self.addInst(.{1325 switch (rhs) {
1314 .tag = .idiv,1326 .register, .stack_offset => break :blk rhs,
1315 .ops = (Mir.Ops{1327 else => {
1316 .reg1 = registerAlias(reg, abi_size),1328 const reg = try self.copyToTmpRegister(tmp_ty, rhs);
1317 }).encode(),1329 break :blk MCValue{ .register = reg };
1318 .data = undefined,
1319 });
1320 },
1321 .stack_offset => |off| {
1322 const flags: u2 = switch (abi_size) {
1323 1 => 0b00,
1324 2 => 0b01,
1325 4 => 0b10,
1326 8 => 0b11,
1327 else => unreachable,
1328 };
1329 _ = try self.addInst(.{
1330 .tag = .idiv,
1331 .ops = (Mir.Ops{
1332 .reg2 = .rbp,
1333 .flags = flags,
1334 }).encode(),
1335 .data = .{ .imm = @bitCast(u32, -off) },
1336 });
1337 },1330 },
1338 else => unreachable,
1339 }1331 }
1332 };
1333 const op_tag: Mir.Inst.Tag = switch (signedness) {
1334 .signed => .idiv,
1335 .unsigned => .div,
1336 };
13401337
1341 break :result dst_mcv;1338 switch (divisor) {
1339 .register => |reg| {
1340 _ = try self.addInst(.{
1341 .tag = op_tag,
1342 .ops = (Mir.Ops{
1343 .reg1 = registerAlias(reg, abi_size),
1344 }).encode(),
1345 .data = undefined,
1346 });
1347 },
1348 .stack_offset => |off| {
1349 _ = try self.addInst(.{
1350 .tag = op_tag,
1351 .ops = (Mir.Ops{
1352 .reg2 = .rbp,
1353 .flags = switch (abi_size) {
1354 1 => 0b00,
1355 2 => 0b01,
1356 4 => 0b10,
1357 8 => 0b11,
1358 else => unreachable,
1359 },
1360 }).encode(),
1361 .data = .{ .imm = @bitCast(u32, -off) },
1362 });
1363 },
1364 else => unreachable,
1365 }
1366
1367 return switch (tag) {
1368 .mod, .div_exact, .div_trunc, .div_floor => MCValue{ .register = .rax },
1369 .rem => MCValue{ .register = .rdx },
1370 else => unreachable,
1342 };1371 };
1372}
1373
1374fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1375 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1376 const result: MCValue = if (self.liveness.isUnused(inst))
1377 .dead
1378 else
1379 try self.genDivOp(inst, bin_op.lhs, bin_op.rhs);
1343 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1380 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1344}1381}
13451382
...@@ -1348,7 +1385,7 @@ fn airRem(self: *Self, inst: Air.Inst.Index) !void {...@@ -1348,7 +1385,7 @@ fn airRem(self: *Self, inst: Air.Inst.Index) !void {
1348 const result: MCValue = if (self.liveness.isUnused(inst))1385 const result: MCValue = if (self.liveness.isUnused(inst))
1349 .dead1386 .dead
1350 else1387 else
1351 return self.fail("TODO implement rem for {}", .{self.target.cpu.arch});1388 try self.genDivOp(inst, bin_op.lhs, bin_op.rhs);
1352 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1389 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1353}1390}
13541391
...@@ -1357,7 +1394,7 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void {...@@ -1357,7 +1394,7 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void {
1357 const result: MCValue = if (self.liveness.isUnused(inst))1394 const result: MCValue = if (self.liveness.isUnused(inst))
1358 .dead1395 .dead
1359 else1396 else
1360 return self.fail("TODO implement mod for {}", .{self.target.cpu.arch});1397 try self.genDivOp(inst, bin_op.lhs, bin_op.rhs);
1361 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1398 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1362}1399}
13631400
src/arch/x86_64/Emit.zig+7-4
...@@ -138,8 +138,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void {...@@ -138,8 +138,9 @@ 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.mirIMulIDiv(.imul, inst),141 .imul => try emit.mirMulDiv(.imul, inst),
142 .idiv => try emit.mirIMulIDiv(.idiv, inst),142 .idiv => try emit.mirMulDiv(.idiv, inst),
143 .div => try emit.mirMulDiv(.div, inst),
143 .imul_complex => try emit.mirIMulComplex(inst),144 .imul_complex => try emit.mirIMulComplex(inst),
144145
145 .cwd => try emit.mirCwd(inst),146 .cwd => try emit.mirCwd(inst),
...@@ -687,7 +688,7 @@ fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {...@@ -687,7 +688,7 @@ fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
687 }688 }
688}689}
689690
690fn mirIMulIDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {691fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
691 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);692 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
692 if (ops.reg1 != .none) {693 if (ops.reg1 != .none) {
693 assert(ops.reg2 == .none);694 assert(ops.reg2 == .none);
...@@ -1085,6 +1086,7 @@ const Tag = enum {...@@ -1085,6 +1086,7 @@ const Tag = enum {
1085 nop,1086 nop,
1086 imul,1087 imul,
1087 idiv,1088 idiv,
1089 div,
1088 syscall,1090 syscall,
1089 ret_near,1091 ret_near,
1090 ret_far,1092 ret_far,
...@@ -1319,7 +1321,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {...@@ -1319,7 +1321,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
1319 .setnl, .setge => OpCode.twoByte(0x0f, 0x9d),1321 .setnl, .setge => OpCode.twoByte(0x0f, 0x9d),
1320 .setle, .setng => OpCode.twoByte(0x0f, 0x9e),1322 .setle, .setng => OpCode.twoByte(0x0f, 0x9e),
1321 .setnle, .setg => OpCode.twoByte(0x0f, 0x9f),1323 .setnle, .setg => OpCode.twoByte(0x0f, 0x9f),
1322 .idiv, .imul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),1324 .idiv, .div, .imul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),
1323 else => null,1325 else => null,
1324 },1326 },
1325 .o => return switch (tag) {1327 .o => return switch (tag) {
...@@ -1455,6 +1457,7 @@ inline fn getModRmExt(tag: Tag) ?u3 {...@@ -1455,6 +1457,7 @@ inline fn getModRmExt(tag: Tag) ?u3 {
1455 .sar => 0x7,1457 .sar => 0x7,
1456 .imul => 0x5,1458 .imul => 0x5,
1457 .idiv => 0x7,1459 .idiv => 0x7,
1460 .div => 0x6,
1458 else => null,1461 else => null,
1459 };1462 };
1460}1463}
src/arch/x86_64/Mir.zig+1
...@@ -227,6 +227,7 @@ pub const Inst = struct {...@@ -227,6 +227,7 @@ pub const Inst = struct {
227 /// 0b11 qword ptr [reg2 + imm32]227 /// 0b11 qword ptr [reg2 + imm32]
228 imul,228 imul,
229 idiv,229 idiv,
230 div,
230231
231 /// ops flags: form:232 /// ops flags: form:
232 /// 0b00 AX <- AL233 /// 0b00 AX <- AL