authorgravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-05-16 23:00:49+07:00
committergravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-05-16 23:17:11+07:00
log0c8ce9ed9d916599e9ec518ecced941bf0b37eff
tree0df093fca109c5301c71dd5536d50d28841ed3ff
parentae2d6b7eea9c2dff7cfe6d38f5713d2f8cfed4e6

stage2: sparc64: Implement airCmp


2 files changed, 227 insertions(+), 6 deletions(-)

src/arch/sparc64/CodeGen.zig+223-6
......@@ -521,12 +521,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
521521
522522 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
523523
524 .cmp_lt => @panic("TODO try self.airCmp(inst, .lt)"),
525 .cmp_lte => @panic("TODO try self.airCmp(inst, .lte)"),
526 .cmp_eq => @panic("TODO try self.airCmp(inst, .eq)"),
527 .cmp_gte => @panic("TODO try self.airCmp(inst, .gte)"),
528 .cmp_gt => @panic("TODO try self.airCmp(inst, .gt)"),
529 .cmp_neq => @panic("TODO try self.airCmp(inst, .neq)"),
524 .cmp_lt => try self.airCmp(inst, .lt),
525 .cmp_lte => try self.airCmp(inst, .lte),
526 .cmp_eq => try self.airCmp(inst, .eq),
527 .cmp_gte => try self.airCmp(inst, .gte),
528 .cmp_gt => try self.airCmp(inst, .gt),
529 .cmp_neq => try self.airCmp(inst, .neq),
530530 .cmp_vector => @panic("TODO try self.airCmpVector(inst)"),
531531 .cmp_lt_errors_len => @panic("TODO try self.airCmpLtErrorsLen(inst)"),
532532
......@@ -996,6 +996,54 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
996996 @panic("TODO handle return value with BigTomb");
997997}
998998
999fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
1000 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1001 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1002 const lhs = try self.resolveInst(bin_op.lhs);
1003 const rhs = try self.resolveInst(bin_op.rhs);
1004 const lhs_ty = self.air.typeOf(bin_op.lhs);
1005
1006 var int_buffer: Type.Payload.Bits = undefined;
1007 const int_ty = switch (lhs_ty.zigTypeTag()) {
1008 .Vector => unreachable, // Should be handled by cmp_vector?
1009 .Enum => lhs_ty.intTagType(&int_buffer),
1010 .Int => lhs_ty,
1011 .Bool => Type.initTag(.u1),
1012 .Pointer => Type.usize,
1013 .ErrorSet => Type.initTag(.u16),
1014 .Optional => blk: {
1015 var opt_buffer: Type.Payload.ElemType = undefined;
1016 const payload_ty = lhs_ty.optionalChild(&opt_buffer);
1017 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
1018 break :blk Type.initTag(.u1);
1019 } else if (lhs_ty.isPtrLikeOptional()) {
1020 break :blk Type.usize;
1021 } else {
1022 return self.fail("TODO SPARCv9 cmp non-pointer optionals", .{});
1023 }
1024 },
1025 .Float => return self.fail("TODO SPARCv9 cmp floats", .{}),
1026 else => unreachable,
1027 };
1028
1029 const int_info = int_ty.intInfo(self.target.*);
1030 if (int_info.bits <= 64) {
1031 _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty);
1032
1033 try self.spillCompareFlagsIfOccupied();
1034 self.compare_flags_inst = inst;
1035
1036 break :result switch (int_info.signedness) {
1037 .signed => MCValue{ .compare_flags_signed = op },
1038 .unsigned => MCValue{ .compare_flags_unsigned = op },
1039 };
1040 } else {
1041 return self.fail("TODO SPARCv9 cmp for ints > 64 bits", .{});
1042 }
1043 };
1044 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1045}
1046
9991047fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
10001048 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
10011049 const cond = try self.resolveInst(pl_op.operand);
......@@ -1427,6 +1475,149 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
14271475 return MCValue{ .stack_offset = stack_offset };
14281476}
14291477
1478/// For all your binary operation needs, this function will generate
1479/// the corresponding Mir instruction(s). Returns the location of the
1480/// result.
1481///
1482/// If the binary operation itself happens to be an Air instruction,
1483/// pass the corresponding index in the inst parameter. That helps
1484/// this function do stuff like reusing operands.
1485///
1486/// This function does not do any lowering to Mir itself, but instead
1487/// looks at the lhs and rhs and determines which kind of lowering
1488/// would be best suitable and then delegates the lowering to other
1489/// functions.
1490fn binOp(
1491 self: *Self,
1492 tag: Air.Inst.Tag,
1493 maybe_inst: ?Air.Inst.Index,
1494 lhs: MCValue,
1495 rhs: MCValue,
1496 lhs_ty: Type,
1497 rhs_ty: Type,
1498) InnerError!MCValue {
1499 const mod = self.bin_file.options.module.?;
1500 switch (tag) {
1501 .cmp_eq => {
1502 switch (lhs_ty.zigTypeTag()) {
1503 .Float => return self.fail("TODO binary operations on floats", .{}),
1504 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1505 .Int => {
1506 assert(lhs_ty.eql(rhs_ty, mod));
1507 const int_info = lhs_ty.intInfo(self.target.*);
1508 if (int_info.bits <= 64) {
1509 // TODO optimize for small (i13) values by putting them inside immediates
1510
1511 const mir_tag: Mir.Inst.Tag = switch (tag) {
1512 .cmp_eq => .subcc,
1513 else => unreachable,
1514 };
1515
1516 return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1517 } else {
1518 return self.fail("TODO binary operations on int with bits > 64", .{});
1519 }
1520 },
1521 else => unreachable,
1522 }
1523 },
1524
1525 else => return self.fail("TODO implement {} binOp for SPARCv9", .{tag}),
1526 }
1527}
1528
1529/// Don't call this function directly. Use binOp instead.
1530///
1531/// Calling this function signals an intention to generate a Mir
1532/// instruction of the form
1533///
1534/// op dest, lhs, rhs
1535///
1536/// Asserts that generating an instruction of that form is possible.
1537fn binOpRegister(
1538 self: *Self,
1539 mir_tag: Mir.Inst.Tag,
1540 maybe_inst: ?Air.Inst.Index,
1541 lhs: MCValue,
1542 rhs: MCValue,
1543 lhs_ty: Type,
1544 rhs_ty: Type,
1545) !MCValue {
1546 const lhs_is_register = lhs == .register;
1547 const rhs_is_register = rhs == .register;
1548
1549 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
1550 if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
1551
1552 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1553
1554 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
1555 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1556 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1557 break :inst Air.refToIndex(bin_op.lhs).?;
1558 } else null;
1559
1560 const reg = try self.register_manager.allocReg(track_inst);
1561 self.register_manager.freezeRegs(&.{reg});
1562
1563 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
1564
1565 break :blk reg;
1566 };
1567 defer self.register_manager.unfreezeRegs(&.{lhs_reg});
1568
1569 const rhs_reg = if (rhs_is_register) rhs.register else blk: {
1570 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1571 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1572 break :inst Air.refToIndex(bin_op.rhs).?;
1573 } else null;
1574
1575 const reg = try self.register_manager.allocReg(track_inst);
1576 self.register_manager.freezeRegs(&.{reg});
1577
1578 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
1579
1580 break :blk reg;
1581 };
1582 defer self.register_manager.unfreezeRegs(&.{rhs_reg});
1583
1584 const dest_reg = switch (mir_tag) {
1585 else => if (maybe_inst) |inst| blk: {
1586 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1587
1588 if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
1589 break :blk lhs_reg;
1590 } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
1591 break :blk rhs_reg;
1592 } else {
1593 break :blk try self.register_manager.allocReg(inst);
1594 }
1595 } else blk: {
1596 break :blk try self.register_manager.allocReg(null);
1597 },
1598 };
1599
1600 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
1601 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
1602
1603 const mir_data: Mir.Inst.Data = switch (mir_tag) {
1604 .subcc => .{ .arithmetic_3op = .{
1605 .is_imm = false,
1606 .rd = dest_reg,
1607 .rs1 = lhs_reg,
1608 .rs2_or_imm = .{ .rs2 = rhs_reg },
1609 } },
1610 else => unreachable,
1611 };
1612
1613 _ = try self.addInst(.{
1614 .tag = mir_tag,
1615 .data = mir_data,
1616 });
1617
1618 return MCValue{ .register = dest_reg };
1619}
1620
14301621fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
14311622 const block_data = self.blocks.getPtr(block).?;
14321623
......@@ -2146,6 +2337,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
21462337 .register => |reg| {
21472338 self.register_manager.freeReg(reg);
21482339 },
2340 .compare_flags_signed, .compare_flags_unsigned => {
2341 self.compare_flags_inst = null;
2342 },
21492343 else => {}, // TODO process stack allocation death
21502344 }
21512345}
......@@ -2338,6 +2532,29 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
23382532 }
23392533}
23402534
2535/// Save the current instruction stored in the compare flags if
2536/// occupied
2537fn spillCompareFlagsIfOccupied(self: *Self) !void {
2538 if (self.compare_flags_inst) |inst_to_save| {
2539 const mcv = self.getResolvedInstValue(inst_to_save);
2540 switch (mcv) {
2541 .compare_flags_signed,
2542 .compare_flags_unsigned,
2543 => {},
2544 else => unreachable, // mcv doesn't occupy the compare flags
2545 }
2546
2547 const new_mcv = try self.allocRegOrMem(inst_to_save, true);
2548 try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);
2549 log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv });
2550
2551 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2552 try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);
2553
2554 self.compare_flags_inst = null;
2555 }
2556}
2557
23412558pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
23422559 const stack_mcv = try self.allocRegOrMem(inst, false);
23432560 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
src/arch/sparc64/Mir.zig+4
......@@ -115,6 +115,10 @@ pub const Inst = struct {
115115 /// A.61 Trap on Integer Condition Codes (Tcc)
116116 /// This uses the trap field.
117117 tcc,
118
119 // TODO add synthetic instructions
120 // TODO add cmp synthetic instruction to avoid wasting a register when
121 // comparing with subcc
118122 };
119123
120124 /// The position of an MIR instruction within the `Mir` instructions array.