authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-07 21:24:56+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-06-07 21:24:56+02:00
log6c59aa9e029c27c6da03287e907e72ac2c735b7b
treef61ac6cab473baeee042bfd16862620fd4f0a03f
parent523fae420bd569dc79153eb3a9adb176e18a115c
parent97d35a5147bb88c5b9ccf26c62bf303162fbf613
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11806 from koachan/sparc64-codegen

stage2: sparc64: Some more Air lowerings

6 files changed, 644 insertions(+), 61 deletions(-)

src/arch/sparc64/CodeGen.zig+532-60
......@@ -123,6 +123,16 @@ const MCValue = union(enum) {
123123 immediate: u64,
124124 /// The value is in a target-specific register.
125125 register: Register,
126 /// The value is a tuple { wrapped, overflow } where
127 /// wrapped is stored in the register and the overflow bit is
128 /// stored in the C (signed) or V (unsigned) flag of the CCR.
129 ///
130 /// This MCValue is only generated by a add_with_overflow or
131 /// sub_with_overflow instruction operating on 32- or 64-bit values.
132 register_with_overflow: struct {
133 reg: Register,
134 flag: struct { cond: Instruction.ICondition, ccr: Instruction.CCR },
135 },
126136 /// The value is in memory at a hard-coded address.
127137 /// If the type is a pointer, it means the pointer address is at this memory location.
128138 memory: u64,
......@@ -131,12 +141,18 @@ const MCValue = union(enum) {
131141 stack_offset: u32,
132142 /// The value is a pointer to one of the stack variables (payload is stack offset).
133143 ptr_stack_offset: u32,
134 /// The value is in the compare flags assuming an unsigned operation,
135 /// with this operator applied on top of it.
136 compare_flags_unsigned: math.CompareOperator,
137 /// The value is in the compare flags assuming a signed operation,
138 /// with this operator applied on top of it.
139 compare_flags_signed: math.CompareOperator,
144 /// The value is in the specified CCR assuming an unsigned operation,
145 /// with the operator applied on top of it.
146 compare_flags_unsigned: struct {
147 cmp: math.CompareOperator,
148 ccr: Instruction.CCR,
149 },
150 /// The value is in the specified CCR assuming an signed operation,
151 /// with the operator applied on top of it.
152 compare_flags_signed: struct {
153 cmp: math.CompareOperator,
154 ccr: Instruction.CCR,
155 },
140156
141157 fn isMemory(mcv: MCValue) bool {
142158 return switch (mcv) {
......@@ -501,7 +517,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
501517 .shl_sat => @panic("TODO try self.airShlSat(inst)"),
502518 .min => @panic("TODO try self.airMin(inst)"),
503519 .max => @panic("TODO try self.airMax(inst)"),
504 .slice => @panic("TODO try self.airSlice(inst)"),
520 .slice => try self.airSlice(inst),
505521
506522 .sqrt,
507523 .sin,
......@@ -519,8 +535,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
519535 .trunc_float,
520536 => @panic("TODO try self.airUnaryMath(inst)"),
521537
522 .add_with_overflow => @panic("TODO try self.airAddWithOverflow(inst)"),
523 .sub_with_overflow => @panic("TODO try self.airSubWithOverflow(inst)"),
538 .add_with_overflow => try self.airAddSubWithOverflow(inst),
539 .sub_with_overflow => try self.airAddSubWithOverflow(inst),
524540 .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"),
525541 .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"),
526542
......@@ -570,14 +586,14 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
570586 .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"),
571587 .load => try self.airLoad(inst),
572588 .loop => try self.airLoop(inst),
573 .not => @panic("TODO try self.airNot(inst)"),
589 .not => try self.airNot(inst),
574590 .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"),
575591 .ret => try self.airRet(inst),
576592 .ret_load => try self.airRetLoad(inst),
577593 .store => try self.airStore(inst),
578594 .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"),
579 .struct_field_val=> @panic("TODO try self.airStructFieldVal(inst)"),
580 .array_to_slice => @panic("TODO try self.airArrayToSlice(inst)"),
595 .struct_field_val=> try self.airStructFieldVal(inst),
596 .array_to_slice => try self.airArrayToSlice(inst),
581597 .int_to_float => @panic("TODO try self.airIntToFloat(inst)"),
582598 .float_to_int => @panic("TODO try self.airFloatToInt(inst)"),
583599 .cmpxchg_strong => @panic("TODO try self.airCmpxchg(inst)"),
......@@ -647,7 +663,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
647663 .slice_elem_val => try self.airSliceElemVal(inst),
648664 .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"),
649665 .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"),
650 .ptr_elem_ptr => @panic("TODO try self.airPtrElemPtr(inst)"),
666 .ptr_elem_ptr => try self.airPtrElemPtr(inst),
651667
652668 .constant => unreachable, // excluded from function bodies
653669 .const_ty => unreachable, // excluded from function bodies
......@@ -666,13 +682,15 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
666682
667683 .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"),
668684 .wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"),
669 .wrap_errunion_err => @panic("TODO try self.airWrapErrUnionErr(inst)"),
685 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
670686
671687 .wasm_memory_size => unreachable,
672688 .wasm_memory_grow => unreachable,
673689 // zig fmt: on
674690 }
675691
692 assert(!self.register_manager.lockedRegsExist());
693
676694 if (std.debug.runtime_safety) {
677695 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
678696 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] });
......@@ -681,11 +699,112 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
681699 }
682700}
683701
702fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
703 const tag = self.air.instructions.items(.tag)[inst];
704 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
705 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
706 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
707 const lhs = try self.resolveInst(extra.lhs);
708 const rhs = try self.resolveInst(extra.rhs);
709 const lhs_ty = self.air.typeOf(extra.lhs);
710 const rhs_ty = self.air.typeOf(extra.rhs);
711
712 switch (lhs_ty.zigTypeTag()) {
713 .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}),
714 .Int => {
715 const mod = self.bin_file.options.module.?;
716 assert(lhs_ty.eql(rhs_ty, mod));
717 const int_info = lhs_ty.intInfo(self.target.*);
718 switch (int_info.bits) {
719 32, 64 => {
720 // Only say yes if the operation is
721 // commutative, i.e. we can swap both of the
722 // operands
723 const lhs_immediate_ok = switch (tag) {
724 .add_with_overflow => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12),
725 .sub_with_overflow => false,
726 else => unreachable,
727 };
728 const rhs_immediate_ok = switch (tag) {
729 .add_with_overflow,
730 .sub_with_overflow,
731 => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12),
732 else => unreachable,
733 };
734
735 const mir_tag: Mir.Inst.Tag = switch (tag) {
736 .add_with_overflow => .addcc,
737 .sub_with_overflow => .subcc,
738 else => unreachable,
739 };
740
741 try self.spillCompareFlagsIfOccupied();
742 self.compare_flags_inst = inst;
743
744 const dest = blk: {
745 if (rhs_immediate_ok) {
746 break :blk try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, null);
747 } else if (lhs_immediate_ok) {
748 // swap lhs and rhs
749 break :blk try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, null);
750 } else {
751 break :blk try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, null);
752 }
753 };
754
755 const cond = switch (int_info.signedness) {
756 .unsigned => switch (tag) {
757 .add_with_overflow => Instruction.ICondition.cs,
758 .sub_with_overflow => Instruction.ICondition.cc,
759 else => unreachable,
760 },
761 .signed => Instruction.ICondition.vs,
762 };
763
764 const ccr = switch (int_info.bits) {
765 32 => Instruction.CCR.icc,
766 64 => Instruction.CCR.xcc,
767 else => unreachable,
768 };
769
770 break :result MCValue{ .register_with_overflow = .{
771 .reg = dest.register,
772 .flag = .{ .cond = cond, .ccr = ccr },
773 } };
774 },
775 else => return self.fail("TODO overflow operations on other integer sizes", .{}),
776 }
777 },
778 else => unreachable,
779 }
780 };
781 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
782}
783
684784fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
685785 const stack_offset = try self.allocMemPtr(inst);
686786 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
687787}
688788
789fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
790 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
791 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
792 const ptr_ty = self.air.typeOf(ty_op.operand);
793 const ptr = try self.resolveInst(ty_op.operand);
794 const array_ty = ptr_ty.childType();
795 const array_len = @intCast(u32, array_ty.arrayLen());
796
797 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
798 const ptr_bytes = @divExact(ptr_bits, 8);
799
800 const stack_offset = try self.allocMem(inst, ptr_bytes * 2, ptr_bytes * 2);
801 try self.genSetStack(ptr_ty, stack_offset, ptr);
802 try self.genSetStack(Type.initTag(.usize), stack_offset - ptr_bytes, .{ .immediate = array_len });
803 break :result MCValue{ .stack_offset = stack_offset };
804 };
805 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
806}
807
689808fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
690809 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
691810 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
......@@ -896,9 +1015,14 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
8961015 const relocs = &self.blocks.getPtr(inst).?.relocs;
8971016 if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) {
8981017 // If the last Mir instruction is the last relocation (which
899 // would just jump one instruction further), it can be safely
1018 // would just jump two instruction further), it can be safely
9001019 // removed
901 self.mir_instructions.orderedRemove(relocs.pop());
1020 const index = relocs.pop();
1021
1022 // First, remove the delay slot, then remove
1023 // the branch instruction itself.
1024 self.mir_instructions.orderedRemove(index + 1);
1025 self.mir_instructions.orderedRemove(index);
9021026 }
9031027 for (relocs.items) |reloc| {
9041028 try self.performReloc(reloc);
......@@ -945,6 +1069,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
9451069
9461070 var info = try self.resolveCallingConventionValues(fn_ty, .caller);
9471071 defer info.deinit(self);
1072
1073 // CCR is volatile across function calls
1074 // (SCD 2.4.1, page 3P-10)
1075 try self.spillCompareFlagsIfOccupied();
1076
9481077 for (info.args) |mc_arg, arg_i| {
9491078 const arg = args[arg_i];
9501079 const arg_ty = self.air.typeOf(arg);
......@@ -952,13 +1081,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
9521081
9531082 switch (mc_arg) {
9541083 .none => continue,
955 .undef => unreachable,
956 .immediate => unreachable,
957 .unreach => unreachable,
958 .dead => unreachable,
959 .memory => unreachable,
960 .compare_flags_signed => unreachable,
961 .compare_flags_unsigned => unreachable,
9621084 .register => |reg| {
9631085 try self.register_manager.getReg(reg, null);
9641086 try self.genSetReg(arg_ty, reg, arg_mcv);
......@@ -969,6 +1091,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
9691091 .ptr_stack_offset => {
9701092 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
9711093 },
1094 else => unreachable,
9721095 }
9731096 }
9741097
......@@ -1089,8 +1212,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
10891212 self.compare_flags_inst = inst;
10901213
10911214 break :result switch (int_info.signedness) {
1092 .signed => MCValue{ .compare_flags_signed = op },
1093 .unsigned => MCValue{ .compare_flags_unsigned = op },
1215 .signed => MCValue{ .compare_flags_signed = .{ .cmp = op, .ccr = .xcc } },
1216 .unsigned => MCValue{ .compare_flags_unsigned = .{ .cmp = op, .ccr = .xcc } },
10941217 };
10951218 } else {
10961219 return self.fail("TODO SPARCv9 cmp for ints > 64 bits", .{});
......@@ -1116,16 +1239,20 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
11161239 .tag = .bpcc,
11171240 .data = .{
11181241 .branch_predict_int = .{
1119 .ccr = .xcc,
1242 .ccr = switch (cond) {
1243 .compare_flags_signed => |cmp_op| cmp_op.ccr,
1244 .compare_flags_unsigned => |cmp_op| cmp_op.ccr,
1245 else => unreachable,
1246 },
11201247 .cond = switch (cond) {
11211248 .compare_flags_signed => |cmp_op| blk: {
11221249 // Here we map to the opposite condition because the jump is to the false branch.
1123 const condition = Instruction.ICondition.fromCompareOperatorSigned(cmp_op);
1250 const condition = Instruction.ICondition.fromCompareOperatorSigned(cmp_op.cmp);
11241251 break :blk condition.negate();
11251252 },
11261253 .compare_flags_unsigned => |cmp_op| blk: {
11271254 // Here we map to the opposite condition because the jump is to the false branch.
1128 const condition = Instruction.ICondition.fromCompareOperatorUnsigned(cmp_op);
1255 const condition = Instruction.ICondition.fromCompareOperatorUnsigned(cmp_op.cmp);
11291256 break :blk condition.negate();
11301257 },
11311258 else => unreachable,
......@@ -1402,6 +1529,133 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
14021529 return self.finishAirBookkeeping();
14031530}
14041531
1532fn airNot(self: *Self, inst: Air.Inst.Index) !void {
1533 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1534 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1535 const operand = try self.resolveInst(ty_op.operand);
1536 const operand_ty = self.air.typeOf(ty_op.operand);
1537 switch (operand) {
1538 .dead => unreachable,
1539 .unreach => unreachable,
1540 .compare_flags_unsigned => |op| {
1541 const r = MCValue{
1542 .compare_flags_unsigned = .{
1543 .cmp = switch (op.cmp) {
1544 .gte => .lt,
1545 .gt => .lte,
1546 .neq => .eq,
1547 .lt => .gte,
1548 .lte => .gt,
1549 .eq => .neq,
1550 },
1551 .ccr = op.ccr,
1552 },
1553 };
1554 break :result r;
1555 },
1556 .compare_flags_signed => |op| {
1557 const r = MCValue{
1558 .compare_flags_signed = .{
1559 .cmp = switch (op.cmp) {
1560 .gte => .lt,
1561 .gt => .lte,
1562 .neq => .eq,
1563 .lt => .gte,
1564 .lte => .gt,
1565 .eq => .neq,
1566 },
1567 .ccr = op.ccr,
1568 },
1569 };
1570 break :result r;
1571 },
1572 else => {
1573 switch (operand_ty.zigTypeTag()) {
1574 .Bool => {
1575 // TODO convert this to mvn + and
1576 const op_reg = switch (operand) {
1577 .register => |r| r,
1578 else => try self.copyToTmpRegister(operand_ty, operand),
1579 };
1580 const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg);
1581 defer self.register_manager.unlockReg(reg_lock);
1582
1583 const dest_reg = blk: {
1584 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
1585 break :blk op_reg;
1586 }
1587
1588 const reg = try self.register_manager.allocReg(null, gp);
1589 break :blk reg;
1590 };
1591
1592 _ = try self.addInst(.{
1593 .tag = .xor,
1594 .data = .{
1595 .arithmetic_3op = .{
1596 .is_imm = true,
1597 .rd = dest_reg,
1598 .rs1 = op_reg,
1599 .rs2_or_imm = .{ .imm = 1 },
1600 },
1601 },
1602 });
1603
1604 break :result MCValue{ .register = dest_reg };
1605 },
1606 .Vector => return self.fail("TODO bitwise not for vectors", .{}),
1607 .Int => {
1608 const int_info = operand_ty.intInfo(self.target.*);
1609 if (int_info.bits <= 64) {
1610 const op_reg = switch (operand) {
1611 .register => |r| r,
1612 else => try self.copyToTmpRegister(operand_ty, operand),
1613 };
1614 const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg);
1615 defer self.register_manager.unlockReg(reg_lock);
1616
1617 const dest_reg = blk: {
1618 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
1619 break :blk op_reg;
1620 }
1621
1622 const reg = try self.register_manager.allocReg(null, gp);
1623 break :blk reg;
1624 };
1625
1626 _ = try self.addInst(.{
1627 .tag = .not,
1628 .data = .{
1629 .arithmetic_2op = .{
1630 .is_imm = false,
1631 .rs1 = dest_reg,
1632 .rs2_or_imm = .{ .rs2 = op_reg },
1633 },
1634 },
1635 });
1636
1637 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
1638
1639 break :result MCValue{ .register = dest_reg };
1640 } else {
1641 return self.fail("TODO AArch64 not on integers > u64/i64", .{});
1642 }
1643 },
1644 else => unreachable,
1645 }
1646 },
1647 }
1648 };
1649 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1650}
1651
1652fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
1653 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1654 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1655 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch});
1656 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
1657}
1658
14051659fn airRet(self: *Self, inst: Air.Inst.Index) !void {
14061660 const un_op = self.air.instructions.items(.data)[inst].un_op;
14071661 const operand = try self.resolveInst(un_op);
......@@ -1422,6 +1676,26 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
14221676 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
14231677}
14241678
1679fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
1680 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1681 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1682 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1683 const ptr = try self.resolveInst(bin_op.lhs);
1684 const ptr_ty = self.air.typeOf(bin_op.lhs);
1685 const len = try self.resolveInst(bin_op.rhs);
1686 const len_ty = self.air.typeOf(bin_op.rhs);
1687
1688 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1689 const ptr_bytes = @divExact(ptr_bits, 8);
1690
1691 const stack_offset = try self.allocMem(inst, ptr_bytes * 2, ptr_bytes * 2);
1692 try self.genSetStack(ptr_ty, stack_offset, ptr);
1693 try self.genSetStack(len_ty, stack_offset - ptr_bytes, len);
1694 break :result MCValue{ .stack_offset = stack_offset };
1695 };
1696 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1697}
1698
14251699fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
14261700 const is_volatile = false; // TODO
14271701 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -1505,6 +1779,75 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
15051779 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
15061780}
15071781
1782fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
1783 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1784 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
1785 const operand = extra.struct_operand;
1786 const index = extra.field_index;
1787 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1788 const mcv = try self.resolveInst(operand);
1789 const struct_ty = self.air.typeOf(operand);
1790 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));
1791
1792 switch (mcv) {
1793 .dead, .unreach => unreachable,
1794 .stack_offset => |off| {
1795 break :result MCValue{ .stack_offset = off - struct_field_offset };
1796 },
1797 .memory => |addr| {
1798 break :result MCValue{ .memory = addr + struct_field_offset };
1799 },
1800 .register_with_overflow => |rwo| {
1801 switch (index) {
1802 0 => {
1803 // get wrapped value: return register
1804 break :result MCValue{ .register = rwo.reg };
1805 },
1806 1 => {
1807 // TODO return special MCValue condition flags
1808 // get overflow bit: set register to C flag
1809 // resp. V flag
1810 const dest_reg = try self.register_manager.allocReg(null, gp);
1811
1812 // TODO handle floating point CCRs
1813 assert(rwo.flag.ccr == .xcc or rwo.flag.ccr == .icc);
1814
1815 _ = try self.addInst(.{
1816 .tag = .mov,
1817 .data = .{
1818 .arithmetic_2op = .{
1819 .is_imm = false,
1820 .rs1 = dest_reg,
1821 .rs2_or_imm = .{ .rs2 = .g0 },
1822 },
1823 },
1824 });
1825
1826 _ = try self.addInst(.{
1827 .tag = .movcc,
1828 .data = .{
1829 .conditional_move = .{
1830 .ccr = rwo.flag.ccr,
1831 .cond = .{ .icond = rwo.flag.cond },
1832 .is_imm = true,
1833 .rd = dest_reg,
1834 .rs2_or_imm = .{ .imm = 1 },
1835 },
1836 },
1837 });
1838
1839 break :result MCValue{ .register = dest_reg };
1840 },
1841 else => unreachable,
1842 }
1843 },
1844 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
1845 }
1846 };
1847
1848 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
1849}
1850
15081851fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
15091852 _ = self;
15101853 _ = inst;
......@@ -1537,6 +1880,20 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
15371880 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
15381881}
15391882
1883/// E to E!T
1884fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
1885 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1886 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1887 const error_union_ty = self.air.getRefType(ty_op.ty);
1888 const payload_ty = error_union_ty.errorUnionPayload();
1889 const mcv = try self.resolveInst(ty_op.operand);
1890 if (!payload_ty.hasRuntimeBits()) break :result mcv;
1891
1892 return self.fail("TODO implement wrap errunion error for non-empty payloads", .{});
1893 };
1894 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1895}
1896
15401897// Common helper functions
15411898
15421899/// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
......@@ -1571,8 +1928,8 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u
15711928 if (abi_align > self.stack_align)
15721929 self.stack_align = abi_align;
15731930 // TODO find a free slot instead of always appending
1574 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align);
1575 self.next_stack_offset = offset + abi_size;
1931 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size;
1932 self.next_stack_offset = offset;
15761933 if (self.next_stack_offset > self.max_end_stack)
15771934 self.max_end_stack = self.next_stack_offset;
15781935 try self.stack.putNoClobber(self.gpa, offset, .{
......@@ -1708,21 +2065,34 @@ fn binOp(
17082065 assert(lhs_ty.eql(rhs_ty, mod));
17092066 const int_info = lhs_ty.intInfo(self.target.*);
17102067 if (int_info.bits <= 64) {
1711 // If LHS is immediate, then swap it with RHS.
1712 const lhs_is_imm = lhs == .immediate;
1713 const new_lhs = if (lhs_is_imm) rhs else lhs;
1714 const new_rhs = if (lhs_is_imm) lhs else rhs;
1715 const new_lhs_ty = if (lhs_is_imm) rhs_ty else lhs_ty;
1716 const new_rhs_ty = if (lhs_is_imm) lhs_ty else rhs_ty;
1717
1718 // At this point, RHS might be an immediate
1719 // If it's a power of two immediate then we emit an shl instead
1720 // TODO add similar checks for LHS
1721 if (new_rhs == .immediate and math.isPowerOfTwo(new_rhs.immediate)) {
1722 return try self.binOp(.shl, new_lhs, .{ .immediate = math.log2(new_rhs.immediate) }, new_lhs_ty, Type.usize, metadata);
1723 }
2068 // Only say yes if the operation is
2069 // commutative, i.e. we can swap both of the
2070 // operands
2071 const lhs_immediate_ok = switch (tag) {
2072 .mul => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12),
2073 else => unreachable,
2074 };
2075 const rhs_immediate_ok = switch (tag) {
2076 .mul => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12),
2077 else => unreachable,
2078 };
17242079
1725 return try self.binOpRegister(.mulx, new_lhs, new_rhs, new_lhs_ty, new_rhs_ty, metadata);
2080 const mir_tag: Mir.Inst.Tag = switch (tag) {
2081 .mul => .mulx,
2082 else => unreachable,
2083 };
2084
2085 if (rhs_immediate_ok) {
2086 // At this point, rhs is an immediate
2087 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
2088 } else if (lhs_immediate_ok) {
2089 // swap lhs and rhs
2090 // At this point, lhs is an immediate
2091 return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);
2092 } else {
2093 // TODO convert large immediates to register before adding
2094 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
2095 }
17262096 } else {
17272097 return self.fail("TODO binary operations on int with bits > 64", .{});
17282098 }
......@@ -1867,6 +2237,7 @@ fn binOpImmediate(
18672237 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
18682238
18692239 const dest_reg = switch (mir_tag) {
2240 .cmp => undefined, // cmp has no destination register
18702241 else => if (metadata) |md| blk: {
18712242 if (lhs_is_register and self.reuseOperand(
18722243 md.inst,
......@@ -1887,6 +2258,7 @@ fn binOpImmediate(
18872258
18882259 const mir_data: Mir.Inst.Data = switch (mir_tag) {
18892260 .add,
2261 .addcc,
18902262 .mulx,
18912263 .subcc,
18922264 => .{
......@@ -1985,6 +2357,7 @@ fn binOpRegister(
19852357 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
19862358
19872359 const dest_reg = switch (mir_tag) {
2360 .cmp => undefined, // cmp has no destination register
19882361 else => if (metadata) |md| blk: {
19892362 if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) {
19902363 break :blk lhs_reg;
......@@ -2003,6 +2376,7 @@ fn binOpRegister(
20032376
20042377 const mir_data: Mir.Inst.Data = switch (mir_tag) {
20052378 .add,
2379 .addcc,
20062380 .mulx,
20072381 .subcc,
20082382 => .{
......@@ -2293,8 +2667,47 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
22932667 switch (mcv) {
22942668 .dead => unreachable,
22952669 .unreach, .none => return, // Nothing to do.
2296 .compare_flags_signed => return self.fail("TODO: genSetReg for compare_flags_signed", .{}),
2297 .compare_flags_unsigned => return self.fail("TODO: genSetReg for compare_flags_unsigned", .{}),
2670 .compare_flags_signed,
2671 .compare_flags_unsigned,
2672 => {
2673 const condition = switch (mcv) {
2674 .compare_flags_unsigned => |op| Instruction.ICondition.fromCompareOperatorUnsigned(op.cmp),
2675 .compare_flags_signed => |op| Instruction.ICondition.fromCompareOperatorSigned(op.cmp),
2676 else => unreachable,
2677 };
2678
2679 const ccr = switch (mcv) {
2680 .compare_flags_unsigned => |op| op.ccr,
2681 .compare_flags_signed => |op| op.ccr,
2682 else => unreachable,
2683 };
2684 // TODO handle floating point CCRs
2685 assert(ccr == .xcc or ccr == .icc);
2686
2687 _ = try self.addInst(.{
2688 .tag = .mov,
2689 .data = .{
2690 .arithmetic_2op = .{
2691 .is_imm = false,
2692 .rs1 = reg,
2693 .rs2_or_imm = .{ .rs2 = .g0 },
2694 },
2695 },
2696 });
2697
2698 _ = try self.addInst(.{
2699 .tag = .movcc,
2700 .data = .{
2701 .conditional_move = .{
2702 .ccr = ccr,
2703 .cond = .{ .icond = condition },
2704 .is_imm = true,
2705 .rd = reg,
2706 .rs2_or_imm = .{ .imm = 1 },
2707 },
2708 },
2709 });
2710 },
22982711 .undef => {
22992712 if (!self.wantSafety())
23002713 return; // The already existing value will do just fine.
......@@ -2302,8 +2715,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
23022715 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
23032716 },
23042717 .ptr_stack_offset => |off| {
2305 const simm13 = math.cast(u12, off + abi.stack_bias + abi.stack_reserved_area) orelse
2306 return self.fail("TODO larger stack offsets", .{});
2718 const real_offset = off + abi.stack_bias + abi.stack_reserved_area;
2719 const simm13 = math.cast(i13, real_offset) orelse
2720 return self.fail("TODO larger stack offsets: {}", .{real_offset});
23072721
23082722 _ = try self.addInst(.{
23092723 .tag = .add,
......@@ -2427,6 +2841,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
24272841 },
24282842 });
24292843 },
2844 .register_with_overflow => unreachable,
24302845 .memory => |addr| {
24312846 // The value is in memory at a hard-coded address.
24322847 // If the type is a pointer, it means the pointer address is at this memory location.
......@@ -2436,7 +2851,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
24362851 .stack_offset => |off| {
24372852 const real_offset = off + abi.stack_bias + abi.stack_reserved_area;
24382853 const simm13 = math.cast(i13, real_offset) orelse
2439 return self.fail("TODO larger stack offsets", .{});
2854 return self.fail("TODO larger stack offsets: {}", .{real_offset});
24402855 try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*));
24412856 },
24422857 }
......@@ -2470,9 +2885,50 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
24702885 .register => |reg| {
24712886 const real_offset = stack_offset + abi.stack_bias + abi.stack_reserved_area;
24722887 const simm13 = math.cast(i13, real_offset) orelse
2473 return self.fail("TODO larger stack offsets", .{});
2888 return self.fail("TODO larger stack offsets: {}", .{real_offset});
24742889 return self.genStore(reg, .sp, i13, simm13, abi_size);
24752890 },
2891 .register_with_overflow => |rwo| {
2892 const reg_lock = self.register_manager.lockReg(rwo.reg);
2893 defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
2894
2895 const wrapped_ty = ty.structFieldType(0);
2896 try self.genSetStack(wrapped_ty, stack_offset, .{ .register = rwo.reg });
2897
2898 const overflow_bit_ty = ty.structFieldType(1);
2899 const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*));
2900 const cond_reg = try self.register_manager.allocReg(null, gp);
2901
2902 // TODO handle floating point CCRs
2903 assert(rwo.flag.ccr == .xcc or rwo.flag.ccr == .icc);
2904
2905 _ = try self.addInst(.{
2906 .tag = .mov,
2907 .data = .{
2908 .arithmetic_2op = .{
2909 .is_imm = false,
2910 .rs1 = cond_reg,
2911 .rs2_or_imm = .{ .rs2 = .g0 },
2912 },
2913 },
2914 });
2915
2916 _ = try self.addInst(.{
2917 .tag = .movcc,
2918 .data = .{
2919 .conditional_move = .{
2920 .ccr = rwo.flag.ccr,
2921 .cond = .{ .icond = rwo.flag.cond },
2922 .is_imm = true,
2923 .rd = cond_reg,
2924 .rs2_or_imm = .{ .imm = 1 },
2925 },
2926 },
2927 });
2928 try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{
2929 .register = cond_reg,
2930 });
2931 },
24762932 .memory, .stack_offset => {
24772933 switch (mcv) {
24782934 .stack_offset => |off| {
......@@ -2647,7 +3103,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
26473103 } },
26483104 });
26493105
2650 return MCValue{ .compare_flags_unsigned = .gt };
3106 return MCValue{ .compare_flags_unsigned = .{ .cmp = .gt, .ccr = .xcc } };
26513107 } else {
26523108 return self.fail("TODO isErr for errors with size > 8", .{});
26533109 }
......@@ -2661,8 +3117,8 @@ fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
26613117 const is_err_result = try self.isErr(ty, operand);
26623118 switch (is_err_result) {
26633119 .compare_flags_unsigned => |op| {
2664 assert(op == .gt);
2665 return MCValue{ .compare_flags_unsigned = .lte };
3120 assert(op.cmp == .gt);
3121 return MCValue{ .compare_flags_unsigned = .{ .cmp = .lte, .ccr = op.ccr } };
26663122 },
26673123 .immediate => |imm| {
26683124 assert(imm == 0);
......@@ -2714,6 +3170,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
27143170 .dead => unreachable,
27153171 .compare_flags_unsigned,
27163172 .compare_flags_signed,
3173 .register_with_overflow,
27173174 => unreachable, // cannot hold an address
27183175 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
27193176 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
......@@ -2801,6 +3258,7 @@ fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
28013258 const tag = self.mir_instructions.items(.tag)[inst];
28023259 switch (tag) {
28033260 .bpcc => self.mir_instructions.items(.data)[inst].branch_predict_int.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len),
3261 .bpr => self.mir_instructions.items(.data)[inst].branch_predict_reg.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len),
28043262 else => unreachable,
28053263 }
28063264}
......@@ -2817,6 +3275,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
28173275 .register => |reg| {
28183276 self.register_manager.freeReg(reg);
28193277 },
3278 .register_with_overflow => |rwo| {
3279 self.register_manager.freeReg(rwo.reg);
3280 self.compare_flags_inst = null;
3281 },
28203282 .compare_flags_signed, .compare_flags_unsigned => {
28213283 self.compare_flags_inst = null;
28223284 },
......@@ -2918,7 +3380,7 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
29183380 const ref_int = @enumToInt(inst);
29193381 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
29203382 const tv = Air.Inst.Ref.typed_value_map[ref_int];
2921 if (!tv.ty.hasRuntimeBits()) {
3383 if (!tv.ty.hasRuntimeBits() and !tv.ty.isError()) {
29223384 return MCValue{ .none = {} };
29233385 }
29243386 return self.genTypedValue(tv);
......@@ -2926,7 +3388,7 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
29263388
29273389 // If the type has no codegen bits, no need to store it.
29283390 const inst_ty = self.air.typeOf(inst);
2929 if (!inst_ty.hasRuntimeBits())
3391 if (!inst_ty.hasRuntimeBits() and !inst_ty.isError())
29303392 return MCValue{ .none = {} };
29313393
29323394 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
......@@ -3017,14 +3479,14 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
30173479fn spillCompareFlagsIfOccupied(self: *Self) !void {
30183480 if (self.compare_flags_inst) |inst_to_save| {
30193481 const mcv = self.getResolvedInstValue(inst_to_save);
3020 switch (mcv) {
3482 const new_mcv = switch (mcv) {
30213483 .compare_flags_signed,
30223484 .compare_flags_unsigned,
3023 => {},
3485 => try self.allocRegOrMem(inst_to_save, true),
3486 .register_with_overflow => try self.allocRegOrMem(inst_to_save, false),
30243487 else => unreachable, // mcv doesn't occupy the compare flags
3025 }
3488 };
30263489
3027 const new_mcv = try self.allocRegOrMem(inst_to_save, true);
30283490 try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);
30293491 log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv });
30303492
......@@ -3032,6 +3494,13 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
30323494 try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);
30333495
30343496 self.compare_flags_inst = null;
3497
3498 // TODO consolidate with register manager and spillInstruction
3499 // this call should really belong in the register manager!
3500 switch (mcv) {
3501 .register_with_overflow => |rwo| self.register_manager.freeReg(rwo.reg),
3502 else => {},
3503 }
30353504 }
30363505}
30373506
......@@ -3055,6 +3524,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
30553524 .dead => unreachable,
30563525 .compare_flags_unsigned,
30573526 .compare_flags_signed,
3527 .register_with_overflow,
30583528 => unreachable, // cannot hold an address
30593529 .immediate => |imm| {
30603530 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
......@@ -3175,11 +3645,13 @@ fn truncRegister(
31753645 });
31763646 },
31773647 64 => {
3648 if (dest_reg == operand_reg)
3649 return; // Copy register to itself; nothing to do.
31783650 _ = try self.addInst(.{
31793651 .tag = .mov,
31803652 .data = .{
31813653 .arithmetic_2op = .{
3182 .is_imm = true,
3654 .is_imm = false,
31833655 .rs1 = dest_reg,
31843656 .rs2_or_imm = .{ .rs2 = operand_reg },
31853657 },
src/arch/sparc64/Emit.zig+43
......@@ -79,6 +79,7 @@ pub fn emitMir(
7979 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
8080
8181 .add => try emit.mirArithmetic3Op(inst),
82 .addcc => try emit.mirArithmetic3Op(inst),
8283
8384 .bpr => try emit.mirConditionalBranch(inst),
8485 .bpcc => try emit.mirConditionalBranch(inst),
......@@ -93,6 +94,10 @@ pub fn emitMir(
9394 .ldx => try emit.mirArithmetic3Op(inst),
9495
9596 .@"or" => try emit.mirArithmetic3Op(inst),
97 .xor => try emit.mirArithmetic3Op(inst),
98 .xnor => try emit.mirArithmetic3Op(inst),
99
100 .movcc => try emit.mirConditionalMove(inst),
96101
97102 .mulx => try emit.mirArithmetic3Op(inst),
98103
......@@ -125,6 +130,8 @@ pub fn emitMir(
125130 .cmp => try emit.mirArithmetic2Op(inst),
126131
127132 .mov => try emit.mirArithmetic2Op(inst),
133
134 .not => try emit.mirArithmetic2Op(inst),
128135 }
129136 }
130137}
......@@ -185,6 +192,7 @@ fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void {
185192 .@"return" => try emit.writeInstruction(Instruction.@"return"(i13, rs1, imm)),
186193 .cmp => try emit.writeInstruction(Instruction.subcc(i13, rs1, imm, .g0)),
187194 .mov => try emit.writeInstruction(Instruction.@"or"(i13, .g0, imm, rs1)),
195 .not => try emit.writeInstruction(Instruction.xnor(i13, .g0, imm, rs1)),
188196 else => unreachable,
189197 }
190198 } else {
......@@ -193,6 +201,7 @@ fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void {
193201 .@"return" => try emit.writeInstruction(Instruction.@"return"(Register, rs1, rs2)),
194202 .cmp => try emit.writeInstruction(Instruction.subcc(Register, rs1, rs2, .g0)),
195203 .mov => try emit.writeInstruction(Instruction.@"or"(Register, .g0, rs2, rs1)),
204 .not => try emit.writeInstruction(Instruction.xnor(Register, .g0, rs2, rs1)),
196205 else => unreachable,
197206 }
198207 }
......@@ -209,12 +218,15 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
209218 const imm = data.rs2_or_imm.imm;
210219 switch (tag) {
211220 .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)),
221 .addcc => try emit.writeInstruction(Instruction.addcc(i13, rs1, imm, rd)),
212222 .jmpl => try emit.writeInstruction(Instruction.jmpl(i13, rs1, imm, rd)),
213223 .ldub => try emit.writeInstruction(Instruction.ldub(i13, rs1, imm, rd)),
214224 .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)),
215225 .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)),
216226 .ldx => try emit.writeInstruction(Instruction.ldx(i13, rs1, imm, rd)),
217227 .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)),
228 .xor => try emit.writeInstruction(Instruction.xor(i13, rs1, imm, rd)),
229 .xnor => try emit.writeInstruction(Instruction.xnor(i13, rs1, imm, rd)),
218230 .mulx => try emit.writeInstruction(Instruction.mulx(i13, rs1, imm, rd)),
219231 .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)),
220232 .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)),
......@@ -230,12 +242,15 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
230242 const rs2 = data.rs2_or_imm.rs2;
231243 switch (tag) {
232244 .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)),
245 .addcc => try emit.writeInstruction(Instruction.addcc(Register, rs1, rs2, rd)),
233246 .jmpl => try emit.writeInstruction(Instruction.jmpl(Register, rs1, rs2, rd)),
234247 .ldub => try emit.writeInstruction(Instruction.ldub(Register, rs1, rs2, rd)),
235248 .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)),
236249 .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)),
237250 .ldx => try emit.writeInstruction(Instruction.ldx(Register, rs1, rs2, rd)),
238251 .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)),
252 .xor => try emit.writeInstruction(Instruction.xor(Register, rs1, rs2, rd)),
253 .xnor => try emit.writeInstruction(Instruction.xnor(Register, rs1, rs2, rd)),
239254 .mulx => try emit.writeInstruction(Instruction.mulx(Register, rs1, rs2, rd)),
240255 .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)),
241256 .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)),
......@@ -294,6 +309,34 @@ fn mirConditionalBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
294309 }
295310}
296311
312fn mirConditionalMove(emit: *Emit, inst: Mir.Inst.Index) !void {
313 const tag = emit.mir.instructions.items(.tag)[inst];
314
315 switch (tag) {
316 .movcc => {
317 const data = emit.mir.instructions.items(.data)[inst].conditional_move;
318 if (data.is_imm) {
319 try emit.writeInstruction(Instruction.movcc(
320 i11,
321 data.cond,
322 data.ccr,
323 data.rs2_or_imm.imm,
324 data.rd,
325 ));
326 } else {
327 try emit.writeInstruction(Instruction.movcc(
328 Register,
329 data.cond,
330 data.ccr,
331 data.rs2_or_imm.rs2,
332 data.rd,
333 ));
334 }
335 },
336 else => unreachable,
337 }
338}
339
297340fn mirNop(emit: *Emit) !void {
298341 try emit.writeInstruction(Instruction.nop());
299342}
src/arch/sparc64/Mir.zig+30
......@@ -42,6 +42,7 @@ pub const Inst = struct {
4242 /// This uses the arithmetic_3op field.
4343 // TODO add other operations.
4444 add,
45 addcc,
4546
4647 /// A.3 Branch on Integer Register with Prediction (BPr)
4748 /// This uses the branch_predict_reg field.
......@@ -73,6 +74,12 @@ pub const Inst = struct {
7374 /// This uses the arithmetic_3op field.
7475 // TODO add other operations.
7576 @"or",
77 xor,
78 xnor,
79
80 /// A.35 Move Integer Register on Condition (MOVcc)
81 /// This uses the conditional_move field.
82 movcc,
7683
7784 /// A.37 Multiply and Divide (64-bit)
7885 /// This uses the arithmetic_3op field.
......@@ -142,6 +149,13 @@ pub const Inst = struct {
142149 /// being the *destination* register.
143150 // TODO is it okay to abuse rs1 in this way?
144151 mov, // mov rs2/imm, rs1 -> or %g0, rs2/imm, rs1
152
153 /// Bitwise negation
154 /// This uses the arithmetic_2op field, with rs1
155 /// being the *destination* register.
156 // TODO is it okay to abuse rs1 in this way?
157 // TODO this differs from official encoding for convenience, fix it later
158 not, // not rs2/imm, rs1 -> xnor %g0, rs2/imm, rs1
145159 };
146160
147161 /// The position of an MIR instruction within the `Mir` instructions array.
......@@ -216,6 +230,22 @@ pub const Inst = struct {
216230 inst: Index,
217231 },
218232
233 /// Conditional move.
234 /// if is_imm true then it uses the imm field of rs2_or_imm,
235 /// otherwise it uses rs2 field.
236 ///
237 /// Used by e.g. movcc
238 conditional_move: struct {
239 is_imm: bool,
240 ccr: Instruction.CCR,
241 cond: Instruction.Condition,
242 rd: Register,
243 rs2_or_imm: union {
244 rs2: Register,
245 imm: i11,
246 },
247 },
248
219249 /// No additional data
220250 ///
221251 /// Used by e.g. flushw
src/arch/sparc64/abi.zig+5
......@@ -13,6 +13,11 @@ pub const stack_bias = 2047;
1313// The first 128 bytes of the stack is reserved for register saving purposes.
1414// The ABI also requires to reserve space in the stack for the first six
1515// outgoing arguments, even though they are usually passed in registers.
16// TODO Don't allocate the argument space in leaf functions
17// TODO Save an RO copy of outgoing arguments in reserved area when building in Debug
18// TODO Should we also save it in ReleaseSafe? Solaris and OpenBSD binaries seem to ship
19// with argument copying enabled and it doesn't seem to give them big slowdowns so
20// I guess it would be okay to do in ReleaseSafe?
1621pub const stack_reserved_area = 128 + 48;
1722
1823// There are no callee-preserved registers since the windowing
src/arch/sparc64/bits.zig+33-1
......@@ -644,7 +644,7 @@ pub const Instruction = union(enum) {
644644 .gt => .gu,
645645 .neq => .ne,
646646 .lt => .cs,
647 .lte => .le,
647 .lte => .leu,
648648 .eq => .eq,
649649 };
650650 }
......@@ -1141,6 +1141,14 @@ pub const Instruction = union(enum) {
11411141 };
11421142 }
11431143
1144 pub fn addcc(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1145 return switch (s2) {
1146 Register => format3a(0b10, 0b01_0000, rs1, rs2, rd),
1147 i13 => format3b(0b10, 0b01_0000, rs1, rs2, rd),
1148 else => unreachable,
1149 };
1150 }
1151
11441152 pub fn bpcc(cond: ICondition, annul: bool, pt: bool, ccr: CCR, disp: i21) Instruction {
11451153 return format2c(0b001, .{ .icond = cond }, annul, pt, ccr, disp);
11461154 }
......@@ -1197,6 +1205,30 @@ pub const Instruction = union(enum) {
11971205 };
11981206 }
11991207
1208 pub fn xor(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1209 return switch (s2) {
1210 Register => format3a(0b10, 0b00_0011, rs1, rs2, rd),
1211 i13 => format3b(0b10, 0b00_0011, rs1, rs2, rd),
1212 else => unreachable,
1213 };
1214 }
1215
1216 pub fn xnor(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1217 return switch (s2) {
1218 Register => format3a(0b10, 0b00_0111, rs1, rs2, rd),
1219 i13 => format3b(0b10, 0b00_0111, rs1, rs2, rd),
1220 else => unreachable,
1221 };
1222 }
1223
1224 pub fn movcc(comptime s2: type, cond: Condition, ccr: CCR, rs2: s2, rd: Register) Instruction {
1225 return switch (s2) {
1226 Register => format4c(0b10_1100, cond, ccr, rs2, rd),
1227 i11 => format4d(0b10_1100, cond, ccr, rs2, rd),
1228 else => unreachable,
1229 };
1230 }
1231
12001232 pub fn mulx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
12011233 return switch (s2) {
12021234 Register => format3a(0b10, 0b00_1001, rs1, rs2, rd),
test/behavior/align.zig+1
......@@ -505,6 +505,7 @@ test "align(N) on functions" {
505505 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
506506 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
507507 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
508 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
508509
509510 // function alignment is a compile error on wasm32/wasm64
510511 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;