authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-02-19 15:37:06+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-19 15:37:06+01:00
loge86a89d3f0e911b99fa6432425a2799aada6f801
treeb2d03a6b15be1c73e51975a5328d7436ebb21116
parent2f0204aca303daf899a97c740719a62398adc206
parent669603029e5d484411f8ed8a6d6e48d9f3a344b1
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10926 from joachimschmidt557/stage2-arm

stage2 ARM: move to binOp lowering mechanism

1 files changed, 540 insertions(+), 549 deletions(-)

src/arch/arm/CodeGen.zig+540-549
......@@ -502,18 +502,18 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
502502
503503 switch (air_tags[inst]) {
504504 // zig fmt: off
505 .add, .ptr_add => try self.airAdd(inst),
505 .add, .ptr_add => try self.airBinOp(inst),
506506 .addwrap => try self.airAddWrap(inst),
507507 .add_sat => try self.airAddSat(inst),
508 .sub, .ptr_sub => try self.airSub(inst),
508 .sub, .ptr_sub => try self.airBinOp(inst),
509509 .subwrap => try self.airSubWrap(inst),
510510 .sub_sat => try self.airSubSat(inst),
511 .mul => try self.airMul(inst),
511 .mul => try self.airBinOp(inst),
512512 .mulwrap => try self.airMulWrap(inst),
513513 .mul_sat => try self.airMulSat(inst),
514514 .rem => try self.airRem(inst),
515515 .mod => try self.airMod(inst),
516 .shl, .shl_exact => try self.airShl(inst),
516 .shl, .shl_exact => try self.airBinOp(inst),
517517 .shl_sat => try self.airShlSat(inst),
518518 .min => try self.airMin(inst),
519519 .max => try self.airMax(inst),
......@@ -548,12 +548,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
548548 .cmp_gt => try self.airCmp(inst, .gt),
549549 .cmp_neq => try self.airCmp(inst, .neq),
550550
551 .bool_and => try self.airBoolOp(inst),
552 .bool_or => try self.airBoolOp(inst),
553 .bit_and => try self.airBitAnd(inst),
554 .bit_or => try self.airBitOr(inst),
555 .xor => try self.airXor(inst),
556 .shr, .shr_exact => try self.airShr(inst),
551 .bool_and => try self.airBinOp(inst),
552 .bool_or => try self.airBinOp(inst),
553 .bit_and => try self.airBinOp(inst),
554 .bit_or => try self.airBinOp(inst),
555 .xor => try self.airBinOp(inst),
556 .shr, .shr_exact => try self.airBinOp(inst),
557557
558558 .alloc => try self.airAlloc(inst),
559559 .ret_ptr => try self.airRetPtr(inst),
......@@ -887,6 +887,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
887887 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
888888 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
889889 const operand = try self.resolveInst(ty_op.operand);
890 const operand_ty = self.air.typeOf(ty_op.operand);
890891 switch (operand) {
891892 .dead => unreachable,
892893 .unreach => unreachable,
......@@ -917,7 +918,68 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
917918 break :result r;
918919 },
919920 else => {
920 break :result try self.genBinOp(inst, ty_op.operand, .bool_true, .not);
921 switch (operand_ty.zigTypeTag()) {
922 .Bool => {
923 const op_reg = switch (operand) {
924 .register => |r| r,
925 else => try self.copyToTmpRegister(operand_ty, operand),
926 };
927 self.register_manager.freezeRegs(&.{op_reg});
928 defer self.register_manager.unfreezeRegs(&.{op_reg});
929
930 const dest_reg = blk: {
931 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
932 break :blk op_reg;
933 }
934
935 break :blk try self.register_manager.allocReg(null);
936 };
937
938 _ = try self.addInst(.{
939 .tag = .eor,
940 .data = .{ .rr_op = .{
941 .rd = dest_reg,
942 .rn = op_reg,
943 .op = Instruction.Operand.fromU32(1).?,
944 } },
945 });
946
947 break :result MCValue{ .register = dest_reg };
948 },
949 .Int => {
950 const int_info = operand_ty.intInfo(self.target.*);
951 if (int_info.bits <= 32) {
952 const op_reg = switch (operand) {
953 .register => |r| r,
954 else => try self.copyToTmpRegister(operand_ty, operand),
955 };
956 self.register_manager.freezeRegs(&.{op_reg});
957 defer self.register_manager.unfreezeRegs(&.{op_reg});
958
959 const dest_reg = blk: {
960 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
961 break :blk op_reg;
962 }
963
964 break :blk try self.register_manager.allocReg(null);
965 };
966
967 _ = try self.addInst(.{
968 .tag = .mvn,
969 .data = .{ .rr_op = .{
970 .rd = dest_reg,
971 .rn = undefined,
972 .op = Instruction.Operand.reg(op_reg, Instruction.Operand.Shift.none),
973 } },
974 });
975
976 break :result MCValue{ .register = dest_reg };
977 } else {
978 return self.fail("TODO ARM not on integers > u32/i32", .{});
979 }
980 },
981 else => unreachable,
982 }
921983 },
922984 }
923985 };
......@@ -943,9 +1005,15 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
9431005 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
9441006}
9451007
946fn airAdd(self: *Self, inst: Air.Inst.Index) !void {
1008fn airBinOp(self: *Self, inst: Air.Inst.Index) !void {
1009 const tag = self.air.instructions.items(.tag)[inst];
9471010 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
948 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .add);
1011 const lhs = try self.resolveInst(bin_op.lhs);
1012 const rhs = try self.resolveInst(bin_op.rhs);
1013 const lhs_ty = self.air.typeOf(bin_op.lhs);
1014 const rhs_ty = self.air.typeOf(bin_op.rhs);
1015
1016 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
9491017 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
9501018}
9511019
......@@ -961,12 +1029,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
9611029 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
9621030}
9631031
964fn airSub(self: *Self, inst: Air.Inst.Index) !void {
965 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
966 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .sub);
967 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
968}
969
9701032fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
9711033 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
9721034 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch});
......@@ -979,12 +1041,6 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
9791041 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
9801042}
9811043
982fn airMul(self: *Self, inst: Air.Inst.Index) !void {
983 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
984 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genMul(inst, bin_op.lhs, bin_op.rhs);
985 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
986}
987
9881044fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {
9891045 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
9901046 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch});
......@@ -1035,42 +1091,12 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void {
10351091 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
10361092}
10371093
1038fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
1039 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1040 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and);
1041 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1042}
1043
1044fn airBitOr(self: *Self, inst: Air.Inst.Index) !void {
1045 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1046 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or);
1047 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1048}
1049
1050fn airXor(self: *Self, inst: Air.Inst.Index) !void {
1051 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1052 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .xor);
1053 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1054}
1055
1056fn airShl(self: *Self, inst: Air.Inst.Index) !void {
1057 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1058 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .shl);
1059 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1060}
1061
10621094fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
10631095 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
10641096 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
10651097 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
10661098}
10671099
1068fn airShr(self: *Self, inst: Air.Inst.Index) !void {
1069 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1070 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .shr);
1071 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1072}
1073
10741100fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
10751101 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
10761102 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});
......@@ -1252,7 +1278,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
12521278
12531279 const slice_ty = self.air.typeOf(bin_op.lhs);
12541280 const elem_ty = slice_ty.childType();
1255 const elem_size = elem_ty.abiSize(self.target.*);
1281 const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
12561282
12571283 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
12581284 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
......@@ -1307,22 +1333,25 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
13071333 else => {
13081334 const dst_mcv = try self.allocRegOrMem(inst, true);
13091335
1310 const offset_mcv = try self.genMulConstant(bin_op.rhs, @intCast(u32, elem_size));
1336 const offset_mcv = try self.binOp(
1337 .mul,
1338 null,
1339 index_mcv,
1340 .{ .immediate = elem_size },
1341 Type.usize,
1342 Type.usize,
1343 );
13111344 assert(offset_mcv == .register); // result of multiplication should always be register
13121345 self.register_manager.freezeRegs(&.{offset_mcv.register});
13131346
1314 const addr_reg = try self.register_manager.allocReg(null);
1315 self.register_manager.freezeRegs(&.{addr_reg});
1316 defer self.register_manager.unfreezeRegs(&.{addr_reg});
1317
1318 try self.genBinOpCode(addr_reg, base_mcv, offset_mcv, false, .add, .unsigned);
1347 const addr_mcv = try self.binOp(.add, null, base_mcv, offset_mcv, Type.usize, Type.usize);
13191348
13201349 // At this point in time, neither the base register
13211350 // nor the offset register contains any valuable data
13221351 // anymore.
13231352 self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register });
13241353
1325 try self.load(dst_mcv, .{ .register = addr_reg }, slice_ptr_field_type);
1354 try self.load(dst_mcv, addr_mcv, slice_ptr_field_type);
13261355
13271356 break :result dst_mcv;
13281357 },
......@@ -1499,31 +1528,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
14991528 const tmp_reg = regs[3];
15001529
15011530 // sub dst_reg, fp, #off
1502 const adj_off = off + elem_size;
1503 const offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_off)) |x| x else {
1504 return self.fail("TODO load: set reg to stack offset with all possible offsets", .{});
1505 };
1506 _ = try self.addInst(.{
1507 .tag = .sub,
1508 .data = .{ .rr_op = .{
1509 .rd = dst_reg,
1510 .rn = .fp,
1511 .op = offset_op,
1512 } },
1513 });
1531 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off });
15141532
15151533 // mov len, #elem_size
1516 const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(elem_size)) |x| x else {
1517 return self.fail("TODO load: set reg to elem_size with all possible sizes", .{});
1518 };
1519 _ = try self.addInst(.{
1520 .tag = .mov,
1521 .data = .{ .rr_op = .{
1522 .rd = len_reg,
1523 .rn = .r0,
1524 .op = len_op,
1525 } },
1526 });
1534 try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size });
15271535
15281536 // memcpy(src, dst, len)
15291537 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
......@@ -1673,16 +1681,16 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
16731681 self.register_manager.freezeRegs(&.{addr_reg});
16741682 defer self.register_manager.unfreezeRegs(&.{addr_reg});
16751683
1676 const dst_reg = try self.register_manager.allocReg(inst);
1677 try self.genBinOpCode(
1678 dst_reg,
1684 const dest = try self.binOp(
1685 .add,
1686 null,
16791687 .{ .register = addr_reg },
16801688 .{ .register = offset_reg },
1681 false,
1682 .add,
1683 .unsigned,
1689 Type.usize,
1690 Type.usize,
16841691 );
1685 break :result MCValue{ .register = dst_reg };
1692
1693 break :result dest;
16861694 },
16871695 }
16881696 };
......@@ -1720,403 +1728,440 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
17201728 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
17211729}
17221730
1723fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {
1724 return switch (mcv) {
1725 .none => unreachable,
1726 .undef => unreachable,
1727 .dead, .unreach => unreachable,
1728 .compare_flags_unsigned => unreachable,
1729 .compare_flags_signed => unreachable,
1730 .ptr_stack_offset => unreachable,
1731 .ptr_embedded_in_code => unreachable,
1732 .immediate => |imm| blk: {
1733 if (imm > std.math.maxInt(u32)) return self.fail("TODO ARM binary arithmetic immediate larger than u32", .{});
1734
1735 // Load immediate into register if it doesn't fit
1736 // in an operand
1737 break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) == null;
1738 },
1739 .register => true,
1740 .stack_offset,
1741 .stack_argument_offset,
1742 .embedded_in_code,
1743 .memory,
1744 => true,
1745 };
1746}
1747
1748fn genBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue {
1749 // In the case of bitshifts, the type of rhs is different
1750 // from the resulting type
1751 const ty = self.air.typeOf(op_lhs);
1752
1753 switch (ty.zigTypeTag()) {
1754 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
1755 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
1756 .Bool => {
1757 return self.genBinIntOp(inst, op_lhs, op_rhs, op, 1, .unsigned);
1758 },
1759 .Int => {
1760 const int_info = ty.intInfo(self.target.*);
1761 return self.genBinIntOp(inst, op_lhs, op_rhs, op, int_info.bits, int_info.signedness);
1762 },
1763 else => unreachable,
1764 }
1765}
1766
1767fn genBinIntOp(
1731/// Don't call this function directly. Use binOp instead.
1732///
1733/// Calling this function signals an intention to generate a Mir
1734/// instruction of the form
1735///
1736/// op dest, lhs, rhs
1737///
1738/// Asserts that generating an instruction of that form is possible.
1739fn binOpRegister(
17681740 self: *Self,
1769 inst: Air.Inst.Index,
1770 op_lhs: Air.Inst.Ref,
1771 op_rhs: Air.Inst.Ref,
1772 op: Air.Inst.Tag,
1773 bits: u16,
1774 signedness: std.builtin.Signedness,
1741 tag: Air.Inst.Tag,
1742 maybe_inst: ?Air.Inst.Index,
1743 lhs: MCValue,
1744 rhs: MCValue,
1745 lhs_ty: Type,
1746 rhs_ty: Type,
17751747) !MCValue {
1776 if (bits > 32) {
1777 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
1778 }
1779
1780 const lhs = try self.resolveInst(op_lhs);
1781 const rhs = try self.resolveInst(op_rhs);
1782
17831748 const lhs_is_register = lhs == .register;
17841749 const rhs_is_register = rhs == .register;
1785 const lhs_should_be_register = switch (op) {
1786 .shr, .shl => true,
1787 else => try self.armOperandShouldBeRegister(lhs),
1788 };
1789 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
1790 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
1791 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
1792 const can_swap_lhs_and_rhs = switch (op) {
1793 .shr, .shl => false,
1794 else => true,
1795 };
17961750
17971751 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
1798 defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register});
17991752 if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
1800 defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register});
18011753
1802 // Destination must be a register
1803 var dst_mcv: MCValue = undefined;
1804 var lhs_mcv = lhs;
1805 var rhs_mcv = rhs;
1806 var swap_lhs_and_rhs = false;
1807
1808 // Allocate registers for operands and/or destination
18091754 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1810 if (reuse_lhs) {
1811 // Allocate 0 or 1 registers
1812 if (!rhs_is_register and rhs_should_be_register) {
1813 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?) };
1814 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1815 }
1816 dst_mcv = lhs;
1817 } else if (reuse_rhs and can_swap_lhs_and_rhs) {
1818 // Allocate 0 or 1 registers
1819 if (!lhs_is_register and lhs_should_be_register) {
1820 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?) };
1821 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv);
1822 }
1823 dst_mcv = rhs;
18241755
1825 swap_lhs_and_rhs = true;
1826 } else {
1827 // Allocate 1 or 2 registers
1828 if (lhs_should_be_register and rhs_should_be_register) {
1829 if (lhs_is_register and rhs_is_register) {
1830 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) };
1831 } else if (lhs_is_register) {
1832 // Move RHS to register
1833 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) };
1834 rhs_mcv = dst_mcv;
1835 } else if (rhs_is_register) {
1836 // Move LHS to register
1837 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) };
1838 lhs_mcv = dst_mcv;
1839 } else {
1840 // Move LHS and RHS to register
1841 const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? });
1842 lhs_mcv = MCValue{ .register = regs[0] };
1843 rhs_mcv = MCValue{ .register = regs[1] };
1844 dst_mcv = lhs_mcv;
1756 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
1757 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1758 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1759 break :inst Air.refToIndex(bin_op.lhs).?;
1760 } else null;
18451761
1846 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1847 }
1848 } else if (lhs_should_be_register) {
1849 // RHS is immediate
1850 if (lhs_is_register) {
1851 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) };
1852 } else {
1853 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) };
1854 lhs_mcv = dst_mcv;
1855 }
1856 } else if (rhs_should_be_register and can_swap_lhs_and_rhs) {
1857 // LHS is immediate
1858 if (rhs_is_register) {
1859 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) };
1860 } else {
1861 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) };
1862 rhs_mcv = dst_mcv;
1863 }
1762 const reg = try self.register_manager.allocReg(track_inst);
1763 self.register_manager.freezeRegs(&.{reg});
18641764
1865 swap_lhs_and_rhs = true;
1866 } else unreachable; // binary operation on two immediates
1867 }
1765 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
18681766
1869 // Move the operands to the newly allocated registers
1870 if (lhs_mcv == .register and !lhs_is_register) {
1871 try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs);
1872 }
1873 if (rhs_mcv == .register and !rhs_is_register) {
1874 try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs);
1875 }
1767 break :blk reg;
1768 };
1769 defer self.register_manager.unfreezeRegs(&.{lhs_reg});
18761770
1877 try self.genBinOpCode(
1878 dst_mcv.register,
1879 lhs_mcv,
1880 rhs_mcv,
1881 swap_lhs_and_rhs,
1882 op,
1883 signedness,
1884 );
1885 return dst_mcv;
1886}
1771 const rhs_reg = if (rhs_is_register) rhs.register else blk: {
1772 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1773 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1774 break :inst Air.refToIndex(bin_op.rhs).?;
1775 } else null;
18871776
1888fn genBinOpCode(
1889 self: *Self,
1890 dst_reg: Register,
1891 lhs_mcv: MCValue,
1892 rhs_mcv: MCValue,
1893 swap_lhs_and_rhs: bool,
1894 op: Air.Inst.Tag,
1895 signedness: std.builtin.Signedness,
1896) !void {
1897 assert(lhs_mcv == .register or rhs_mcv == .register);
1777 const reg = try self.register_manager.allocReg(track_inst);
1778 self.register_manager.freezeRegs(&.{reg});
18981779
1899 const op1 = if (swap_lhs_and_rhs) rhs_mcv.register else lhs_mcv.register;
1900 const op2 = if (swap_lhs_and_rhs) lhs_mcv else rhs_mcv;
1780 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
19011781
1902 const operand = switch (op2) {
1903 .none => unreachable,
1904 .undef => unreachable,
1905 .dead, .unreach => unreachable,
1906 .compare_flags_unsigned => unreachable,
1907 .compare_flags_signed => unreachable,
1908 .ptr_stack_offset => unreachable,
1909 .ptr_embedded_in_code => unreachable,
1910 .immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?,
1911 .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none),
1912 .stack_offset,
1913 .stack_argument_offset,
1914 .embedded_in_code,
1915 .memory,
1916 => unreachable,
1782 break :blk reg;
19171783 };
1784 defer self.register_manager.unfreezeRegs(&.{rhs_reg});
19181785
1919 switch (op) {
1920 .add,
1921 .bool_and,
1786 const dest_reg = switch (tag) {
1787 .cmp_eq => .r0, // cmp has no destination regardless
1788 else => if (maybe_inst) |inst| blk: {
1789 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1790
1791 if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
1792 break :blk lhs_reg;
1793 } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
1794 break :blk rhs_reg;
1795 } else {
1796 break :blk try self.register_manager.allocReg(inst);
1797 }
1798 } else try self.register_manager.allocReg(null),
1799 };
1800
1801 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
1802 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
1803
1804 const mir_tag: Mir.Inst.Tag = switch (tag) {
1805 .add, .ptr_add => .add,
1806 .sub, .ptr_sub => .sub,
1807 .cmp_eq => .cmp,
1808 .mul => .mul,
19221809 .bit_and,
1810 .bool_and,
1811 => .@"and",
1812 .bit_or,
19231813 .bool_or,
1814 => .orr,
1815 .shl,
1816 .shl_exact,
1817 => .lsl,
1818 .shr,
1819 .shr_exact,
1820 => switch (lhs_ty.intInfo(self.target.*).signedness) {
1821 .signed => Mir.Inst.Tag.asr,
1822 .unsigned => Mir.Inst.Tag.lsr,
1823 },
1824 .xor => .eor,
1825 else => unreachable,
1826 };
1827 const mir_data: Mir.Inst.Data = switch (tag) {
1828 .add,
1829 .sub,
1830 .cmp_eq,
1831 .bit_and,
1832 .bool_and,
19241833 .bit_or,
1925 .not,
1834 .bool_or,
19261835 .xor,
1927 => {
1928 const tag: Mir.Inst.Tag = switch (op) {
1929 .add => .add,
1930 .bool_and, .bit_and => .@"and",
1931 .bool_or, .bit_or => .orr,
1932 .not, .xor => .eor,
1933 else => unreachable,
1934 };
1935
1936 _ = try self.addInst(.{
1937 .tag = tag,
1938 .data = .{ .rr_op = .{
1939 .rd = dst_reg,
1940 .rn = op1,
1941 .op = operand,
1942 } },
1943 });
1944 },
1945 .sub => {
1946 const tag: Mir.Inst.Tag = if (swap_lhs_and_rhs) .rsb else .sub;
1836 .ptr_add,
1837 .ptr_sub,
1838 => .{ .rr_op = .{
1839 .rd = dest_reg,
1840 .rn = lhs_reg,
1841 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
1842 } },
1843 .shl,
1844 .shl_exact,
1845 .shr,
1846 .shr_exact,
1847 => .{ .rr_shift = .{
1848 .rd = dest_reg,
1849 .rm = lhs_reg,
1850 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
1851 } },
1852 .mul => .{ .rrr = .{
1853 .rd = dest_reg,
1854 .rn = lhs_reg,
1855 .rm = rhs_reg,
1856 } },
1857 else => unreachable,
1858 };
19471859
1948 _ = try self.addInst(.{
1949 .tag = tag,
1950 .data = .{ .rr_op = .{
1951 .rd = dst_reg,
1952 .rn = op1,
1953 .op = operand,
1954 } },
1955 });
1956 },
1957 .cmp_eq => {
1958 _ = try self.addInst(.{
1959 .tag = .cmp,
1960 .data = .{ .rr_op = .{
1961 .rd = .r0,
1962 .rn = op1,
1963 .op = operand,
1964 } },
1965 });
1966 },
1967 .shl, .shr => {
1968 assert(!swap_lhs_and_rhs);
1969 const shift_amount = switch (operand) {
1970 .register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
1971 .immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
1972 };
1860 _ = try self.addInst(.{
1861 .tag = mir_tag,
1862 .data = mir_data,
1863 });
19731864
1974 const tag: Mir.Inst.Tag = switch (op) {
1975 .shl => .lsl,
1976 .shr => switch (signedness) {
1977 .signed => Mir.Inst.Tag.asr,
1978 .unsigned => Mir.Inst.Tag.lsr,
1979 },
1980 else => unreachable,
1981 };
1865 return MCValue{ .register = dest_reg };
1866}
1867
1868/// Don't call this function directly. Use binOp instead.
1869///
1870/// Calling this function signals an intention to generate a Mir
1871/// instruction of the form
1872///
1873/// op dest, lhs, #rhs_imm
1874///
1875/// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to
1876/// rhs and vice versa. This parameter is only used when maybe_inst !=
1877/// null.
1878///
1879/// Asserts that generating an instruction of that form is possible.
1880fn binOpImmediate(
1881 self: *Self,
1882 tag: Air.Inst.Tag,
1883 maybe_inst: ?Air.Inst.Index,
1884 lhs: MCValue,
1885 rhs: MCValue,
1886 lhs_ty: Type,
1887 lhs_and_rhs_swapped: bool,
1888) !MCValue {
1889 const lhs_is_register = lhs == .register;
19821890
1983 _ = try self.addInst(.{
1984 .tag = tag,
1985 .data = .{ .rr_shift = .{
1986 .rd = dst_reg,
1987 .rm = op1,
1988 .shift_amount = shift_amount,
1989 } },
1990 });
1991 },
1992 else => unreachable, // not a binary instruction
1993 }
1994}
1891 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
19951892
1996fn genMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
1997 const lhs = try self.resolveInst(op_lhs);
1998 const rhs = try self.resolveInst(op_rhs);
1893 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
19991894
2000 const lhs_is_register = lhs == .register;
2001 const rhs_is_register = rhs == .register;
2002 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
2003 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
1895 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
1896 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1897 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1898 break :inst Air.refToIndex(
1899 if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs,
1900 ).?;
1901 } else null;
20041902
2005 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
2006 defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register});
2007 if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
2008 defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register});
1903 const reg = try self.register_manager.allocReg(track_inst);
1904 self.register_manager.freezeRegs(&.{reg});
20091905
2010 // Destination must be a register
2011 // LHS must be a register
2012 // RHS must be a register
2013 var dst_mcv: MCValue = undefined;
2014 var lhs_mcv: MCValue = lhs;
2015 var rhs_mcv: MCValue = rhs;
1906 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
20161907
2017 // Allocate registers for operands and/or destination
2018 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2019 if (reuse_lhs) {
2020 // Allocate 0 or 1 registers
2021 if (!rhs_is_register) {
2022 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?) };
2023 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
2024 }
2025 dst_mcv = lhs;
2026 } else if (reuse_rhs) {
2027 // Allocate 0 or 1 registers
2028 if (!lhs_is_register) {
2029 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?) };
2030 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv);
2031 }
2032 dst_mcv = rhs;
2033 } else {
2034 // Allocate 1 or 2 registers
2035 if (lhs_is_register and rhs_is_register) {
2036 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) };
2037 } else if (lhs_is_register) {
2038 // Move RHS to register
2039 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) };
2040 rhs_mcv = dst_mcv;
2041 } else if (rhs_is_register) {
2042 // Move LHS to register
2043 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) };
2044 lhs_mcv = dst_mcv;
2045 } else {
2046 // Move LHS and RHS to register
2047 const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? });
2048 lhs_mcv = MCValue{ .register = regs[0] };
2049 rhs_mcv = MCValue{ .register = regs[1] };
2050 dst_mcv = lhs_mcv;
1908 break :blk reg;
1909 };
1910 defer self.register_manager.unfreezeRegs(&.{lhs_reg});
1911
1912 const dest_reg = switch (tag) {
1913 .cmp_eq => .r0, // cmp has no destination reg
1914 else => if (maybe_inst) |inst| blk: {
1915 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1916
1917 if (lhs_is_register and self.reuseOperand(
1918 inst,
1919 if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs,
1920 if (lhs_and_rhs_swapped) 1 else 0,
1921 lhs,
1922 )) {
1923 break :blk lhs_reg;
1924 } else {
1925 break :blk try self.register_manager.allocReg(inst);
1926 }
1927 } else try self.register_manager.allocReg(null),
1928 };
20511929
2052 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
2053 }
2054 }
1930 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
20551931
2056 // Move the operands to the newly allocated registers
2057 if (!lhs_is_register) {
2058 try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs);
2059 }
2060 if (!rhs_is_register) {
2061 try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs);
2062 }
1932 const mir_tag: Mir.Inst.Tag = switch (tag) {
1933 .add => .add,
1934 .sub => .sub,
1935 .cmp_eq => .cmp,
1936 .bit_and,
1937 .bool_and,
1938 => .@"and",
1939 .bit_or,
1940 .bool_or,
1941 => .orr,
1942 .shl,
1943 .shl_exact,
1944 => .lsl,
1945 .shr,
1946 .shr_exact,
1947 => switch (lhs_ty.intInfo(self.target.*).signedness) {
1948 .signed => Mir.Inst.Tag.asr,
1949 .unsigned => Mir.Inst.Tag.lsr,
1950 },
1951 .xor => .eor,
1952 else => unreachable,
1953 };
1954 const mir_data: Mir.Inst.Data = switch (tag) {
1955 .add,
1956 .sub,
1957 .cmp_eq,
1958 .bit_and,
1959 .bool_and,
1960 .bit_or,
1961 .bool_or,
1962 .xor,
1963 => .{ .rr_op = .{
1964 .rd = dest_reg,
1965 .rn = lhs_reg,
1966 .op = Instruction.Operand.fromU32(rhs.immediate).?,
1967 } },
1968 .shl,
1969 .shl_exact,
1970 .shr,
1971 .shr_exact,
1972 => .{ .rr_shift = .{
1973 .rd = dest_reg,
1974 .rm = lhs_reg,
1975 .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs.immediate)),
1976 } },
1977 else => unreachable,
1978 };
20631979
20641980 _ = try self.addInst(.{
2065 .tag = .mul,
2066 .data = .{ .rrr = .{
2067 .rd = dst_mcv.register,
2068 .rn = lhs_mcv.register,
2069 .rm = rhs_mcv.register,
2070 } },
1981 .tag = mir_tag,
1982 .data = mir_data,
20711983 });
2072 return dst_mcv;
2073}
20741984
2075fn genMulConstant(self: *Self, op: Air.Inst.Ref, imm: u32) !MCValue {
2076 const lhs = try self.resolveInst(op);
2077 const rhs = MCValue{ .immediate = imm };
1985 return MCValue{ .register = dest_reg };
1986}
1987
1988/// For all your binary operation needs, this function will generate
1989/// the corresponding Mir instruction(s). Returns the location of the
1990/// result.
1991///
1992/// If the binary operation itself happens to be an Air instruction,
1993/// pass the corresponding index in the inst parameter. That helps
1994/// this function do stuff like reusing operands.
1995///
1996/// This function does not do any lowering to Mir itself, but instead
1997/// looks at the lhs and rhs and determines which kind of lowering
1998/// would be best suitable and then delegates the lowering to other
1999/// functions.
2000fn binOp(
2001 self: *Self,
2002 tag: Air.Inst.Tag,
2003 maybe_inst: ?Air.Inst.Index,
2004 lhs: MCValue,
2005 rhs: MCValue,
2006 lhs_ty: Type,
2007 rhs_ty: Type,
2008) !MCValue {
2009 switch (tag) {
2010 .add,
2011 .sub,
2012 .cmp_eq,
2013 => {
2014 switch (lhs_ty.zigTypeTag()) {
2015 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
2016 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
2017 .Int => {
2018 assert(lhs_ty.eql(rhs_ty));
2019 const int_info = lhs_ty.intInfo(self.target.*);
2020 if (int_info.bits <= 32) {
2021 // Only say yes if the operation is
2022 // commutative, i.e. we can swap both of the
2023 // operands
2024 const lhs_immediate_ok = switch (tag) {
2025 .add => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null,
2026 .sub,
2027 .cmp_eq,
2028 => false,
2029 else => unreachable,
2030 };
2031 const rhs_immediate_ok = switch (tag) {
2032 .add,
2033 .sub,
2034 .cmp_eq,
2035 => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null,
2036 else => unreachable,
2037 };
20782038
2079 const lhs_is_register = lhs == .register;
2039 if (rhs_immediate_ok) {
2040 return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
2041 } else if (lhs_immediate_ok) {
2042 // swap lhs and rhs
2043 return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true);
2044 } else {
2045 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2046 }
2047 } else {
2048 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
2049 }
2050 },
2051 else => unreachable,
2052 }
2053 },
2054 .mul => {
2055 switch (lhs_ty.zigTypeTag()) {
2056 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
2057 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
2058 .Int => {
2059 assert(lhs_ty.eql(rhs_ty));
2060 const int_info = lhs_ty.intInfo(self.target.*);
2061 if (int_info.bits <= 32) {
2062 // TODO add optimisations for multiplication
2063 // with immediates, for example a * 2 can be
2064 // lowered to a << 1
2065 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2066 } else {
2067 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
2068 }
2069 },
2070 else => unreachable,
2071 }
2072 },
2073 .bit_and,
2074 .bit_or,
2075 .xor,
2076 => {
2077 switch (lhs_ty.zigTypeTag()) {
2078 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
2079 .Int => {
2080 assert(lhs_ty.eql(rhs_ty));
2081 const int_info = lhs_ty.intInfo(self.target.*);
2082 if (int_info.bits <= 32) {
2083 const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null;
2084 const rhs_immediate_ok = rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null;
2085
2086 if (rhs_immediate_ok) {
2087 return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
2088 } else if (lhs_immediate_ok) {
2089 // swap lhs and rhs
2090 return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true);
2091 } else {
2092 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2093 }
2094 } else {
2095 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
2096 }
2097 },
2098 else => unreachable,
2099 }
2100 },
2101 .shl,
2102 .shr,
2103 => {
2104 switch (lhs_ty.zigTypeTag()) {
2105 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
2106 .Int => {
2107 const int_info = lhs_ty.intInfo(self.target.*);
2108 if (int_info.bits <= 32) {
2109 const rhs_immediate_ok = rhs == .immediate;
2110
2111 if (rhs_immediate_ok) {
2112 return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
2113 } else {
2114 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2115 }
2116 } else {
2117 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
2118 }
2119 },
2120 else => unreachable,
2121 }
2122 },
2123 .bool_and,
2124 .bool_or,
2125 => {
2126 switch (lhs_ty.zigTypeTag()) {
2127 .Bool => {
2128 const lhs_immediate_ok = lhs == .immediate;
2129 const rhs_immediate_ok = rhs == .immediate;
2130
2131 if (rhs_immediate_ok) {
2132 return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
2133 } else if (lhs_immediate_ok) {
2134 // swap lhs and rhs
2135 return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true);
2136 } else {
2137 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2138 }
2139 },
2140 else => unreachable,
2141 }
2142 },
2143 .ptr_add,
2144 .ptr_sub,
2145 => {
2146 switch (lhs_ty.zigTypeTag()) {
2147 .Pointer => {
2148 const ptr_ty = lhs_ty;
2149 const pointee_ty = switch (ptr_ty.ptrSize()) {
2150 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
2151 else => ptr_ty.childType(),
2152 };
20802153
2081 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
2082 defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register});
2083
2084 // Destination must be a register
2085 // LHS must be a register
2086 // RHS must be a register
2087 var dst_mcv: MCValue = undefined;
2088 var lhs_mcv: MCValue = lhs;
2089 var rhs_mcv: MCValue = rhs;
2090
2091 // Allocate registers for operands and/or destination
2092 // Allocate 1 or 2 registers
2093 if (lhs_is_register) {
2094 // Move RHS to register
2095 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(null) };
2096 rhs_mcv = dst_mcv;
2097 } else {
2098 // Move LHS and RHS to register
2099 const regs = try self.register_manager.allocRegs(2, .{ null, null });
2100 lhs_mcv = MCValue{ .register = regs[0] };
2101 rhs_mcv = MCValue{ .register = regs[1] };
2102 dst_mcv = lhs_mcv;
2103 }
2154 if (pointee_ty.abiSize(self.target.*) > 1) {
2155 return self.fail("TODO ptr_add, ptr_sub with more element sizes", .{});
2156 }
21042157
2105 // Move the operands to the newly allocated registers
2106 if (!lhs_is_register) {
2107 try self.genSetReg(self.air.typeOf(op), lhs_mcv.register, lhs);
2158 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2159 },
2160 else => unreachable,
2161 }
2162 },
2163 else => unreachable,
21082164 }
2109 try self.genSetReg(Type.initTag(.usize), rhs_mcv.register, rhs);
2110
2111 _ = try self.addInst(.{
2112 .tag = .mul,
2113 .data = .{ .rrr = .{
2114 .rd = dst_mcv.register,
2115 .rn = lhs_mcv.register,
2116 .rm = rhs_mcv.register,
2117 } },
2118 });
2119 return dst_mcv;
21202165}
21212166
21222167fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, abi_size: u32) !void {
......@@ -2492,75 +2537,38 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
24922537 const rhs = try self.resolveInst(bin_op.rhs);
24932538 const lhs_ty = self.air.typeOf(bin_op.lhs);
24942539
2495 if (lhs_ty.abiSize(self.target.*) > 4) {
2496 return self.fail("TODO cmp for types with size > 4", .{});
2497 }
2498
2499 const signedness: std.builtin.Signedness = blk: {
2500 // by default we tell the operand type is unsigned (i.e. bools and enum values)
2501 if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned;
2502
2503 // incase of an actual integer, we emit the correct signedness
2504 break :blk lhs_ty.intInfo(self.target.*).signedness;
2505 };
2506
2507 try self.spillCompareFlagsIfOccupied();
2508 self.compare_flags_inst = inst;
2509
2510 const lhs_is_register = lhs == .register;
2511 const rhs_is_register = rhs == .register;
2512 // lhs should always be a register
2513 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
2540 switch (lhs_ty.zigTypeTag()) {
2541 .Vector => return self.fail("TODO ARM cmp vectors", .{}),
2542 .Optional => return self.fail("TODO ARM cmp optionals", .{}),
2543 .Float => return self.fail("TODO ARM cmp floats", .{}),
2544 .Int, .Bool, .Pointer, .ErrorSet, .Enum => {
2545 var int_buffer: Type.Payload.Bits = undefined;
2546 const int_ty = switch (lhs_ty.zigTypeTag()) {
2547 .Enum => lhs_ty.intTagType(&int_buffer),
2548 .Int => lhs_ty,
2549 .Bool => Type.initTag(.u1),
2550 .Pointer => Type.usize,
2551 .ErrorSet => Type.initTag(.u16),
2552 else => unreachable,
2553 };
25142554
2515 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
2516 defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register});
2517 if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
2518 defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register});
2555 const int_info = int_ty.intInfo(self.target.*);
2556 if (int_info.bits <= 32) {
2557 try self.spillCompareFlagsIfOccupied();
2558 self.compare_flags_inst = inst;
25192559
2520 var lhs_mcv = lhs;
2521 var rhs_mcv = rhs;
2560 _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty);
25222561
2523 // Allocate registers
2524 if (rhs_should_be_register) {
2525 if (!lhs_is_register and !rhs_is_register) {
2526 const regs = try self.register_manager.allocRegs(2, .{
2527 Air.refToIndex(bin_op.lhs).?, Air.refToIndex(bin_op.rhs).?,
2528 });
2529 lhs_mcv = MCValue{ .register = regs[0] };
2530 rhs_mcv = MCValue{ .register = regs[1] };
2531 } else if (!rhs_is_register) {
2532 const track_inst = if (self.liveness.operandDies(inst, 1)) null else Air.refToIndex(bin_op.rhs).?;
2533 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(track_inst) };
2534 } else if (!lhs_is_register) {
2535 const track_inst = if (self.liveness.operandDies(inst, 0)) null else Air.refToIndex(bin_op.lhs).?;
2536 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(track_inst) };
2537 }
2538 } else {
2539 if (!lhs_is_register) {
2540 const track_inst = if (self.liveness.operandDies(inst, 0)) null else Air.refToIndex(bin_op.lhs).?;
2541 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(track_inst) };
2542 }
2543 }
2544
2545 // Move the operands to the newly allocated registers
2546 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2547 if (lhs_mcv == .register and !lhs_is_register) {
2548 try self.genSetReg(lhs_ty, lhs_mcv.register, lhs);
2549 branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.lhs).?, lhs);
2550 }
2551 if (rhs_mcv == .register and !rhs_is_register) {
2552 try self.genSetReg(lhs_ty, rhs_mcv.register, rhs);
2553 branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.rhs).?, rhs);
2562 break :result switch (int_info.signedness) {
2563 .signed => MCValue{ .compare_flags_signed = op },
2564 .unsigned => MCValue{ .compare_flags_unsigned = op },
2565 };
2566 } else {
2567 return self.fail("TODO ARM cmp for ints > 32 bits", .{});
2568 }
2569 },
2570 else => unreachable,
25542571 }
2555
2556 // The destination register is not present in the cmp instruction
2557 // The signedness of the integer does not matter for the cmp instruction
2558 try self.genBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq, undefined);
2559
2560 break :result switch (signedness) {
2561 .signed => MCValue{ .compare_flags_signed = op },
2562 .unsigned => MCValue{ .compare_flags_unsigned = op },
2563 };
25642572 };
25652573 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
25662574}
......@@ -2787,7 +2795,14 @@ fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
27872795 else => .{ .register = try self.copyToTmpRegister(ty, operand) },
27882796 };
27892797
2790 try self.genBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined);
2798 _ = try self.addInst(.{
2799 .tag = .cmp,
2800 .data = .{ .rr_op = .{
2801 .rd = undefined,
2802 .rn = reg_mcv.register,
2803 .op = Instruction.Operand.fromU32(0).?,
2804 } },
2805 });
27912806
27922807 return MCValue{ .compare_flags_unsigned = .eq };
27932808 } else {
......@@ -2817,7 +2832,14 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
28172832 else => .{ .register = try self.copyToTmpRegister(error_type, operand) },
28182833 };
28192834
2820 try self.genBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined);
2835 _ = try self.addInst(.{
2836 .tag = .cmp,
2837 .data = .{ .rr_op = .{
2838 .rd = undefined,
2839 .rn = reg_mcv.register,
2840 .op = Instruction.Operand.fromU32(0).?,
2841 } },
2842 });
28212843
28222844 return MCValue{ .compare_flags_unsigned = .gt };
28232845 } else {
......@@ -3028,17 +3050,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {
30283050 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
30293051}
30303052
3031fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void {
3032 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3033 const air_tags = self.air.instructions.items(.tag);
3034 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (air_tags[inst]) {
3035 .bool_and => try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_and),
3036 .bool_or => try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_or),
3037 else => unreachable, // Not a boolean operation
3038 };
3039 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3040}
3041
30423053fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
30433054 const block_data = self.blocks.getPtr(block).?;
30443055
......@@ -3342,17 +3353,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
33423353 });
33433354
33443355 // mov len, #abi_size
3345 const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(abi_size)) |x| x else {
3346 return self.fail("TODO load: set reg to elem_size with all possible sizes", .{});
3347 };
3348 _ = try self.addInst(.{
3349 .tag = .mov,
3350 .data = .{ .rr_op = .{
3351 .rd = len_reg,
3352 .rn = .r0,
3353 .op = len_op,
3354 } },
3355 });
3356 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
33563357
33573358 // memcpy(src, dst, len)
33583359 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
......@@ -3738,17 +3739,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
37383739 });
37393740
37403741 // mov len, #abi_size
3741 const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(abi_size)) |x| x else {
3742 return self.fail("TODO load: set reg to elem_size with all possible sizes", .{});
3743 };
3744 _ = try self.addInst(.{
3745 .tag = .mov,
3746 .data = .{ .rr_op = .{
3747 .rd = len_reg,
3748 .rn = .r0,
3749 .op = len_op,
3750 } },
3751 });
3742 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
37523743
37533744 // memcpy(src, dst, len)
37543745 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);