authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-03-17 20:07:08-04:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-21 08:49:54+01:00
log29e6aedc95766f960d7dbc92c862a82bf40faafd
treeb8102c7086d888ea390f715d1c480de173d04352
parentdff4bbfd2426ce4943972782d2bcffc89b3fd26d

x86_64: implement min and max as commutative binary ops


4 files changed, 147 insertions(+), 108 deletions(-)

src/arch/x86_64/CodeGen.zig+105-91
......@@ -402,8 +402,8 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
402402fn asmSetccRegister(self: *Self, reg: Register, cc: bits.Condition) !void {
403403 _ = try self.addInst(.{
404404 .tag = .setcc,
405 .ops = .r_c,
406 .data = .{ .r_c = .{
405 .ops = .r_cc,
406 .data = .{ .r_cc = .{
407407 .r1 = reg,
408408 .cc = cc,
409409 } },
......@@ -413,8 +413,8 @@ fn asmSetccRegister(self: *Self, reg: Register, cc: bits.Condition) !void {
413413fn asmCmovccRegisterRegister(self: *Self, reg1: Register, reg2: Register, cc: bits.Condition) !void {
414414 _ = try self.addInst(.{
415415 .tag = .cmovcc,
416 .ops = .rr_c,
417 .data = .{ .rr_c = .{
416 .ops = .rr_cc,
417 .data = .{ .rr_cc = .{
418418 .r1 = reg1,
419419 .r2 = reg2,
420420 .cc = cc,
......@@ -422,6 +422,26 @@ fn asmCmovccRegisterRegister(self: *Self, reg1: Register, reg2: Register, cc: bi
422422 });
423423}
424424
425fn asmCmovccRegisterMemory(self: *Self, reg: Register, m: Memory, cc: bits.Condition) !void {
426 _ = try self.addInst(.{
427 .tag = .cmovcc,
428 .ops = switch (m) {
429 .sib => .rm_sib_cc,
430 .rip => .rm_rip_cc,
431 else => unreachable,
432 },
433 .data = .{ .rx_cc = .{
434 .r1 = reg,
435 .cc = cc,
436 .payload = switch (m) {
437 .sib => try self.addExtra(Mir.MemorySib.encode(m)),
438 .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
439 else => unreachable,
440 },
441 } },
442 });
443}
444
425445fn asmJmpReloc(self: *Self, target: Mir.Inst.Index) !Mir.Inst.Index {
426446 return self.addInst(.{
427447 .tag = .jmp_reloc,
......@@ -793,18 +813,20 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
793813
794814 switch (air_tags[inst]) {
795815 // zig fmt: off
796 .add => try self.airBinOp(inst, .add),
797 .addwrap => try self.airBinOp(inst, .addwrap),
798 .sub => try self.airBinOp(inst, .sub),
799 .subwrap => try self.airBinOp(inst, .subwrap),
800 .bool_and => try self.airBinOp(inst, .bool_and),
801 .bool_or => try self.airBinOp(inst, .bool_or),
802 .bit_and => try self.airBinOp(inst, .bit_and),
803 .bit_or => try self.airBinOp(inst, .bit_or),
804 .xor => try self.airBinOp(inst, .xor),
805
806 .ptr_add => try self.airPtrArithmetic(inst, .ptr_add),
807 .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub),
816 .add,
817 .addwrap,
818 .sub,
819 .subwrap,
820 .bool_and,
821 .bool_or,
822 .bit_and,
823 .bit_or,
824 .xor,
825 .min,
826 .max,
827 => |tag| try self.airBinOp(inst, tag),
828
829 .ptr_add, .ptr_sub => |tag| try self.airPtrArithmetic(inst, tag),
808830
809831 .shr, .shr_exact => try self.airShlShrBinOp(inst),
810832 .shl, .shl_exact => try self.airShlShrBinOp(inst),
......@@ -818,8 +840,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
818840 .sub_sat => try self.airSubSat(inst),
819841 .mul_sat => try self.airMulSat(inst),
820842 .shl_sat => try self.airShlSat(inst),
821 .min => try self.airMin(inst),
822 .max => try self.airMax(inst),
823843 .slice => try self.airSlice(inst),
824844
825845 .sqrt,
......@@ -1466,61 +1486,6 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
14661486 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
14671487}
14681488
1469fn airMin(self: *Self, inst: Air.Inst.Index) !void {
1470 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1471 if (self.liveness.isUnused(inst)) {
1472 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1473 }
1474
1475 const ty = self.air.typeOfIndex(inst);
1476 if (ty.zigTypeTag() != .Int) {
1477 return self.fail("TODO implement min for type {}", .{ty.fmtDebug()});
1478 }
1479 const signedness = ty.intInfo(self.target.*).signedness;
1480 const result: MCValue = result: {
1481 // TODO improve by checking if any operand can be reused.
1482 // TODO audit register allocation
1483 const lhs = try self.resolveInst(bin_op.lhs);
1484 const lhs_lock: ?RegisterLock = switch (lhs) {
1485 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1486 else => null,
1487 };
1488 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
1489
1490 const lhs_reg = try self.copyToTmpRegister(ty, lhs);
1491 const lhs_reg_lock = self.register_manager.lockRegAssumeUnused(lhs_reg);
1492 defer self.register_manager.unlockReg(lhs_reg_lock);
1493
1494 const rhs_mcv = try self.limitImmediateType(bin_op.rhs, i32);
1495 const rhs_lock: ?RegisterLock = switch (rhs_mcv) {
1496 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1497 else => null,
1498 };
1499 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
1500
1501 try self.genBinOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv);
1502
1503 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, rhs_mcv);
1504 const cc: Condition = switch (signedness) {
1505 .unsigned => .b,
1506 .signed => .l,
1507 };
1508 try self.asmCmovccRegisterRegister(dst_mcv.register, lhs_reg, cc);
1509
1510 break :result dst_mcv;
1511 };
1512 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1513}
1514
1515fn airMax(self: *Self, inst: Air.Inst.Index) !void {
1516 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1517 const result: MCValue = if (self.liveness.isUnused(inst))
1518 .dead
1519 else
1520 return self.fail("TODO implement max for {}", .{self.target.cpu.arch});
1521 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1522}
1523
15241489fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
15251490 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
15261491 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
......@@ -1545,11 +1510,10 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
15451510fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
15461511 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
15471512
1548 if (self.liveness.isUnused(inst)) {
1549 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1550 }
1551
1552 const result = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
1513 const result = if (self.liveness.isUnused(inst))
1514 .dead
1515 else
1516 try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
15531517 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
15541518}
15551519
......@@ -1557,11 +1521,10 @@ fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void
15571521 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
15581522 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
15591523
1560 if (self.liveness.isUnused(inst)) {
1561 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1562 }
1563
1564 const result = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
1524 const result = if (self.liveness.isUnused(inst))
1525 .dead
1526 else
1527 try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
15651528 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
15661529}
15671530
......@@ -3486,10 +3449,10 @@ fn genBinOp(
34863449 const lhs_ty = self.air.typeOf(lhs_air);
34873450 const rhs_ty = self.air.typeOf(rhs_air);
34883451 if (lhs_ty.zigTypeTag() == .Vector) {
3489 return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()});
3452 return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmt(self.bin_file.options.module.?)});
34903453 }
34913454 if (lhs_ty.abiSize(self.target.*) > 8) {
3492 return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()});
3455 return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmt(self.bin_file.options.module.?)});
34933456 }
34943457
34953458 const is_commutative: bool = switch (tag) {
......@@ -3500,6 +3463,8 @@ fn genBinOp(
35003463 .bool_and,
35013464 .bit_and,
35023465 .xor,
3466 .min,
3467 .max,
35033468 => true,
35043469
35053470 else => false,
......@@ -3520,10 +3485,10 @@ fn genBinOp(
35203485 var flipped: bool = false;
35213486 const dst_mcv: MCValue = blk: {
35223487 if (maybe_inst) |inst| {
3523 if (self.reuseOperand(inst, lhs_air, 0, lhs) and lhs.isRegister()) {
3488 if (lhs.isRegister() and self.reuseOperand(inst, lhs_air, 0, lhs)) {
35243489 break :blk lhs;
35253490 }
3526 if (is_commutative and self.reuseOperand(inst, rhs_air, 1, rhs) and rhs.isRegister()) {
3491 if (rhs.isRegister() and is_commutative and self.reuseOperand(inst, rhs_air, 1, rhs)) {
35273492 flipped = true;
35283493 break :blk rhs;
35293494 }
......@@ -3580,6 +3545,58 @@ fn genBinOp(
35803545
35813546 .xor => try self.genBinOpMir(.xor, lhs_ty, dst_mcv, src_mcv),
35823547
3548 .min,
3549 .max,
3550 => {
3551 if (!lhs_ty.isAbiInt() or !rhs_ty.isAbiInt()) {
3552 return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) });
3553 }
3554
3555 const mat_src_mcv = switch (src_mcv) {
3556 .immediate => MCValue{ .register = try self.copyToTmpRegister(rhs_ty, src_mcv) },
3557 else => src_mcv,
3558 };
3559 const mat_mcv_lock = switch (mat_src_mcv) {
3560 .register => |reg| self.register_manager.lockReg(reg),
3561 else => null,
3562 };
3563 defer if (mat_mcv_lock) |lock| self.register_manager.unlockReg(lock);
3564
3565 try self.genBinOpMir(.cmp, lhs_ty, dst_mcv, mat_src_mcv);
3566
3567 const int_info = lhs_ty.intInfo(self.target.*);
3568 const cc: Condition = switch (int_info.signedness) {
3569 .unsigned => switch (tag) {
3570 .min => .a,
3571 .max => .b,
3572 else => unreachable,
3573 },
3574 .signed => switch (tag) {
3575 .min => .g,
3576 .max => .l,
3577 else => unreachable,
3578 },
3579 };
3580
3581 const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*));
3582 switch (dst_mcv) {
3583 .register => |dst_reg| switch (mat_src_mcv) {
3584 .register => |src_reg| try self.asmCmovccRegisterRegister(
3585 registerAlias(dst_reg, abi_size),
3586 registerAlias(src_reg, abi_size),
3587 cc,
3588 ),
3589 .stack_offset => |off| try self.asmCmovccRegisterMemory(
3590 registerAlias(dst_reg, abi_size),
3591 Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }),
3592 cc,
3593 ),
3594 else => unreachable,
3595 },
3596 else => unreachable,
3597 }
3598 },
3599
35833600 else => unreachable,
35843601 }
35853602 return dst_mcv;
......@@ -3660,13 +3677,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
36603677 Immediate.s(small),
36613678 );
36623679 } else {
3663 const tmp_reg = try self.register_manager.allocReg(null, gp);
3664 const tmp_alias = registerAlias(tmp_reg, abi_size);
3665 try self.asmRegisterImmediate(.mov, tmp_alias, Immediate.u(imm));
36663680 try self.asmRegisterRegister(
36673681 mir_tag,
36683682 registerAlias(dst_reg, abi_size),
3669 tmp_alias,
3683 registerAlias(try self.copyToTmpRegister(dst_ty, src_mcv), abi_size),
36703684 );
36713685 }
36723686 },
src/arch/x86_64/Emit.zig+23-5
......@@ -374,23 +374,41 @@ fn mnemonicFromConditionCode(comptime basename: []const u8, cc: bits.Condition)
374374fn mirCmovcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
375375 const ops = emit.mir.instructions.items(.ops)[inst];
376376 switch (ops) {
377 .rr_c => {
378 const data = emit.mir.instructions.items(.data)[inst].rr_c;
377 .rr_cc => {
378 const data = emit.mir.instructions.items(.data)[inst].rr_cc;
379379 const mnemonic = mnemonicFromConditionCode("cmov", data.cc);
380380 return emit.encode(mnemonic, .{
381381 .op1 = .{ .reg = data.r1 },
382382 .op2 = .{ .reg = data.r2 },
383383 });
384384 },
385 else => unreachable, // TODO
385 .rm_sib_cc => {
386 const data = emit.mir.instructions.items(.data)[inst].rx_cc;
387 const extra = emit.mir.extraData(Mir.MemorySib, data.payload).data;
388 const mnemonic = mnemonicFromConditionCode("cmov", data.cc);
389 return emit.encode(mnemonic, .{
390 .op1 = .{ .reg = data.r1 },
391 .op2 = .{ .mem = Mir.MemorySib.decode(extra) },
392 });
393 },
394 .rm_rip_cc => {
395 const data = emit.mir.instructions.items(.data)[inst].rx_cc;
396 const extra = emit.mir.extraData(Mir.MemoryRip, data.payload).data;
397 const mnemonic = mnemonicFromConditionCode("cmov", data.cc);
398 return emit.encode(mnemonic, .{
399 .op1 = .{ .reg = data.r1 },
400 .op2 = .{ .mem = Mir.MemoryRip.decode(extra) },
401 });
402 },
403 else => unreachable,
386404 }
387405}
388406
389407fn mirSetcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
390408 const ops = emit.mir.instructions.items(.ops)[inst];
391409 switch (ops) {
392 .r_c => {
393 const data = emit.mir.instructions.items(.data)[inst].r_c;
410 .r_cc => {
411 const data = emit.mir.instructions.items(.data)[inst].r_cc;
394412 const mnemonic = mnemonicFromConditionCode("set", data.cc);
395413 return emit.encode(mnemonic, .{
396414 .op1 = .{ .reg = data.r1 },
src/arch/x86_64/Mir.zig+16-8
......@@ -186,10 +186,10 @@ pub const Inst = struct {
186186 rri_u,
187187 /// Register with condition code (CC).
188188 /// Uses `r_c` payload.
189 r_c,
189 r_cc,
190190 /// Register, register with condition code (CC).
191191 /// Uses `rr_c` payload.
192 rr_c,
192 rr_cc,
193193 /// Register, immediate (sign-extended) operands.
194194 /// Uses `ri` payload.
195195 ri_s,
......@@ -214,6 +214,12 @@ pub const Inst = struct {
214214 /// Register, memory (RIP) operands.
215215 /// Uses `rx` payload.
216216 rm_rip,
217 /// Register, memory (SIB) operands with condition code (CC).
218 /// Uses `rx_cc` payload.
219 rm_sib_cc,
220 /// Register, memory (RIP) operands with condition code (CC).
221 /// Uses `rx_cc` payload.
222 rm_rip_cc,
217223 /// Single memory (SIB) operand.
218224 /// Uses `payload` with extra data of type `MemorySib`.
219225 m_sib,
......@@ -250,10 +256,6 @@ pub const Inst = struct {
250256 /// References another Mir instruction directly with condition code (CC).
251257 /// Uses `inst_cc` payload.
252258 inst_cc,
253 /// Uses `payload` payload with data of type `MemoryConditionCode`.
254 m_cc,
255 /// Uses `rx` payload with extra data of type `MemoryConditionCode`.
256 rm_cc,
257259 /// Uses `reloc` payload.
258260 reloc,
259261 /// Linker relocation - GOT indirection.
......@@ -296,12 +298,12 @@ pub const Inst = struct {
296298 imm: u32,
297299 },
298300 /// Register with condition code (CC).
299 r_c: struct {
301 r_cc: struct {
300302 r1: Register,
301303 cc: bits.Condition,
302304 },
303305 /// Register, register with condition code (CC).
304 rr_c: struct {
306 rr_cc: struct {
305307 r1: Register,
306308 r2: Register,
307309 cc: bits.Condition,
......@@ -316,6 +318,12 @@ pub const Inst = struct {
316318 r1: Register,
317319 payload: u32,
318320 },
321 /// Register with condition code (CC), followed by custom payload found in extra.
322 rx_cc: struct {
323 r1: Register,
324 cc: bits.Condition,
325 payload: u32,
326 },
319327 /// Custom payload followed by an immediate.
320328 xi: struct {
321329 payload: u32,
src/arch/x86_64/bits.zig+3-4
......@@ -97,8 +97,7 @@ pub const Condition = enum(u5) {
9797 };
9898 }
9999
100 /// Returns the condition which is true iff the given condition is
101 /// false (if such a condition exists)
100 /// Returns the condition which is true iff the given condition is false
102101 pub fn negate(cond: Condition) Condition {
103102 return switch (cond) {
104103 .a => .na,
......@@ -127,8 +126,8 @@ pub const Condition = enum(u5) {
127126 .nz => .z,
128127 .o => .no,
129128 .p => .np,
130 .pe => unreachable,
131 .po => unreachable,
129 .pe => .po,
130 .po => .pe,
132131 .s => .ns,
133132 .z => .nz,
134133 };