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 {...@@ -502,18 +502,18 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
502502
503 switch (air_tags[inst]) {503 switch (air_tags[inst]) {
504 // zig fmt: off504 // zig fmt: off
505 .add, .ptr_add => try self.airAdd(inst),505 .add, .ptr_add => try self.airBinOp(inst),
506 .addwrap => try self.airAddWrap(inst),506 .addwrap => try self.airAddWrap(inst),
507 .add_sat => try self.airAddSat(inst),507 .add_sat => try self.airAddSat(inst),
508 .sub, .ptr_sub => try self.airSub(inst),508 .sub, .ptr_sub => try self.airBinOp(inst),
509 .subwrap => try self.airSubWrap(inst),509 .subwrap => try self.airSubWrap(inst),
510 .sub_sat => try self.airSubSat(inst),510 .sub_sat => try self.airSubSat(inst),
511 .mul => try self.airMul(inst),511 .mul => try self.airBinOp(inst),
512 .mulwrap => try self.airMulWrap(inst),512 .mulwrap => try self.airMulWrap(inst),
513 .mul_sat => try self.airMulSat(inst),513 .mul_sat => try self.airMulSat(inst),
514 .rem => try self.airRem(inst),514 .rem => try self.airRem(inst),
515 .mod => try self.airMod(inst),515 .mod => try self.airMod(inst),
516 .shl, .shl_exact => try self.airShl(inst),516 .shl, .shl_exact => try self.airBinOp(inst),
517 .shl_sat => try self.airShlSat(inst),517 .shl_sat => try self.airShlSat(inst),
518 .min => try self.airMin(inst),518 .min => try self.airMin(inst),
519 .max => try self.airMax(inst),519 .max => try self.airMax(inst),
...@@ -548,12 +548,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -548,12 +548,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
548 .cmp_gt => try self.airCmp(inst, .gt),548 .cmp_gt => try self.airCmp(inst, .gt),
549 .cmp_neq => try self.airCmp(inst, .neq),549 .cmp_neq => try self.airCmp(inst, .neq),
550550
551 .bool_and => try self.airBoolOp(inst),551 .bool_and => try self.airBinOp(inst),
552 .bool_or => try self.airBoolOp(inst),552 .bool_or => try self.airBinOp(inst),
553 .bit_and => try self.airBitAnd(inst),553 .bit_and => try self.airBinOp(inst),
554 .bit_or => try self.airBitOr(inst),554 .bit_or => try self.airBinOp(inst),
555 .xor => try self.airXor(inst),555 .xor => try self.airBinOp(inst),
556 .shr, .shr_exact => try self.airShr(inst),556 .shr, .shr_exact => try self.airBinOp(inst),
557557
558 .alloc => try self.airAlloc(inst),558 .alloc => try self.airAlloc(inst),
559 .ret_ptr => try self.airRetPtr(inst),559 .ret_ptr => try self.airRetPtr(inst),
...@@ -887,6 +887,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {...@@ -887,6 +887,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
887 const ty_op = self.air.instructions.items(.data)[inst].ty_op;887 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
888 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {888 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
889 const operand = try self.resolveInst(ty_op.operand);889 const operand = try self.resolveInst(ty_op.operand);
890 const operand_ty = self.air.typeOf(ty_op.operand);
890 switch (operand) {891 switch (operand) {
891 .dead => unreachable,892 .dead => unreachable,
892 .unreach => unreachable,893 .unreach => unreachable,
...@@ -917,7 +918,68 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {...@@ -917,7 +918,68 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
917 break :result r;918 break :result r;
918 },919 },
919 else => {920 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 }
921 },983 },
922 }984 }
923 };985 };
...@@ -943,9 +1005,15 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {...@@ -943,9 +1005,15 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
943 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1005 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
944}1006}
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];
947 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1010 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);
949 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1017 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
950}1018}
9511019
...@@ -961,12 +1029,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -961,12 +1029,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
961 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1029 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
962}1030}
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
970fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {1032fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
971 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1033 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
972 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch});1034 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 {...@@ -979,12 +1041,6 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
979 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1041 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
980}1042}
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
988fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {1044fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {
989 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1045 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
990 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch});1046 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 {...@@ -1035,42 +1091,12 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void {
1035 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1091 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1036}1092}
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
1062fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {1094fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
1063 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1095 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1064 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});1096 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
1065 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1097 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1066}1098}
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
1074fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {1100fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
1075 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1101 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1076 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});1102 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 {...@@ -1252,7 +1278,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
12521278
1253 const slice_ty = self.air.typeOf(bin_op.lhs);1279 const slice_ty = self.air.typeOf(bin_op.lhs);
1254 const elem_ty = slice_ty.childType();1280 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
1257 var buf: Type.SlicePtrFieldTypeBuffer = undefined;1283 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1258 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);1284 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
...@@ -1307,22 +1333,25 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -1307,22 +1333,25 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
1307 else => {1333 else => {
1308 const dst_mcv = try self.allocRegOrMem(inst, true);1334 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 );
1311 assert(offset_mcv == .register); // result of multiplication should always be register1344 assert(offset_mcv == .register); // result of multiplication should always be register
1312 self.register_manager.freezeRegs(&.{offset_mcv.register});1345 self.register_manager.freezeRegs(&.{offset_mcv.register});
13131346
1314 const addr_reg = try self.register_manager.allocReg(null);1347 const addr_mcv = try self.binOp(.add, null, base_mcv, offset_mcv, Type.usize, Type.usize);
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);
13191348
1320 // At this point in time, neither the base register1349 // At this point in time, neither the base register
1321 // nor the offset register contains any valuable data1350 // nor the offset register contains any valuable data
1322 // anymore.1351 // anymore.
1323 self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register });1352 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
1327 break :result dst_mcv;1356 break :result dst_mcv;
1328 },1357 },
...@@ -1499,31 +1528,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -1499,31 +1528,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
1499 const tmp_reg = regs[3];1528 const tmp_reg = regs[3];
15001529
1501 // sub dst_reg, fp, #off1530 // sub dst_reg, fp, #off
1502 const adj_off = off + elem_size;1531 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off });
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 });
15141532
1515 // mov len, #elem_size1533 // mov len, #elem_size
1516 const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(elem_size)) |x| x else {1534 try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size });
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 });
15271535
1528 // memcpy(src, dst, len)1536 // memcpy(src, dst, len)
1529 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);1537 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...@@ -1673,16 +1681,16 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
1673 self.register_manager.freezeRegs(&.{addr_reg});1681 self.register_manager.freezeRegs(&.{addr_reg});
1674 defer self.register_manager.unfreezeRegs(&.{addr_reg});1682 defer self.register_manager.unfreezeRegs(&.{addr_reg});
16751683
1676 const dst_reg = try self.register_manager.allocReg(inst);1684 const dest = try self.binOp(
1677 try self.genBinOpCode(1685 .add,
1678 dst_reg,1686 null,
1679 .{ .register = addr_reg },1687 .{ .register = addr_reg },
1680 .{ .register = offset_reg },1688 .{ .register = offset_reg },
1681 false,1689 Type.usize,
1682 .add,1690 Type.usize,
1683 .unsigned,
1684 );1691 );
1685 break :result MCValue{ .register = dst_reg };1692
1693 break :result dest;
1686 },1694 },
1687 }1695 }
1688 };1696 };
...@@ -1720,403 +1728,440 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -1720,403 +1728,440 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
1720 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });1728 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
1721}1729}
17221730
1723fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {1731/// Don't call this function directly. Use binOp instead.
1724 return switch (mcv) {1732///
1725 .none => unreachable,1733/// Calling this function signals an intention to generate a Mir
1726 .undef => unreachable,1734/// instruction of the form
1727 .dead, .unreach => unreachable,1735///
1728 .compare_flags_unsigned => unreachable,1736/// op dest, lhs, rhs
1729 .compare_flags_signed => unreachable,1737///
1730 .ptr_stack_offset => unreachable,1738/// Asserts that generating an instruction of that form is possible.
1731 .ptr_embedded_in_code => unreachable,1739fn binOpRegister(
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(
1768 self: *Self,1740 self: *Self,
1769 inst: Air.Inst.Index,1741 tag: Air.Inst.Tag,
1770 op_lhs: Air.Inst.Ref,1742 maybe_inst: ?Air.Inst.Index,
1771 op_rhs: Air.Inst.Ref,1743 lhs: MCValue,
1772 op: Air.Inst.Tag,1744 rhs: MCValue,
1773 bits: u16,1745 lhs_ty: Type,
1774 signedness: std.builtin.Signedness,1746 rhs_ty: Type,
1775) !MCValue {1747) !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
1783 const lhs_is_register = lhs == .register;1748 const lhs_is_register = lhs == .register;
1784 const rhs_is_register = rhs == .register;1749 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
1797 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});1751 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
1798 defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register});
1799 if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});1752 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
1809 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1754 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;1756 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
1826 } else {1757 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1827 // Allocate 1 or 2 registers1758 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1828 if (lhs_should_be_register and rhs_should_be_register) {1759 break :inst Air.refToIndex(bin_op.lhs).?;
1829 if (lhs_is_register and rhs_is_register) {1760 } else null;
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;
18451761
1846 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);1762 const reg = try self.register_manager.allocReg(track_inst);
1847 }1763 self.register_manager.freezeRegs(&.{reg});
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 }
18641764
1865 swap_lhs_and_rhs = true;1765 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
1866 } else unreachable; // binary operation on two immediates
1867 }
18681766
1869 // Move the operands to the newly allocated registers1767 break :blk reg;
1870 if (lhs_mcv == .register and !lhs_is_register) {1768 };
1871 try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs);1769 defer self.register_manager.unfreezeRegs(&.{lhs_reg});
1872 }
1873 if (rhs_mcv == .register and !rhs_is_register) {
1874 try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs);
1875 }
18761770
1877 try self.genBinOpCode(1771 const rhs_reg = if (rhs_is_register) rhs.register else blk: {
1878 dst_mcv.register,1772 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1879 lhs_mcv,1773 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1880 rhs_mcv,1774 break :inst Air.refToIndex(bin_op.rhs).?;
1881 swap_lhs_and_rhs,1775 } else null;
1882 op,
1883 signedness,
1884 );
1885 return dst_mcv;
1886}
18871776
1888fn genBinOpCode(1777 const reg = try self.register_manager.allocReg(track_inst);
1889 self: *Self,1778 self.register_manager.freezeRegs(&.{reg});
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);
18981779
1899 const op1 = if (swap_lhs_and_rhs) rhs_mcv.register else lhs_mcv.register;1780 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
1900 const op2 = if (swap_lhs_and_rhs) lhs_mcv else rhs_mcv;
19011781
1902 const operand = switch (op2) {1782 break :blk reg;
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,
1917 };1783 };
1784 defer self.register_manager.unfreezeRegs(&.{rhs_reg});
19181785
1919 switch (op) {1786 const dest_reg = switch (tag) {
1920 .add,1787 .cmp_eq => .r0, // cmp has no destination regardless
1921 .bool_and,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,
1922 .bit_and,1809 .bit_and,
1810 .bool_and,
1811 => .@"and",
1812 .bit_or,
1923 .bool_or,1813 .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,
1924 .bit_or,1833 .bit_or,
1925 .not,1834 .bool_or,
1926 .xor,1835 .xor,
1927 => {1836 .ptr_add,
1928 const tag: Mir.Inst.Tag = switch (op) {1837 .ptr_sub,
1929 .add => .add,1838 => .{ .rr_op = .{
1930 .bool_and, .bit_and => .@"and",1839 .rd = dest_reg,
1931 .bool_or, .bit_or => .orr,1840 .rn = lhs_reg,
1932 .not, .xor => .eor,1841 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
1933 else => unreachable,1842 } },
1934 };1843 .shl,
19351844 .shl_exact,
1936 _ = try self.addInst(.{1845 .shr,
1937 .tag = tag,1846 .shr_exact,
1938 .data = .{ .rr_op = .{1847 => .{ .rr_shift = .{
1939 .rd = dst_reg,1848 .rd = dest_reg,
1940 .rn = op1,1849 .rm = lhs_reg,
1941 .op = operand,1850 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
1942 } },1851 } },
1943 });1852 .mul => .{ .rrr = .{
1944 },1853 .rd = dest_reg,
1945 .sub => {1854 .rn = lhs_reg,
1946 const tag: Mir.Inst.Tag = if (swap_lhs_and_rhs) .rsb else .sub;1855 .rm = rhs_reg,
1856 } },
1857 else => unreachable,
1858 };
19471859
1948 _ = try self.addInst(.{1860 _ = try self.addInst(.{
1949 .tag = tag,1861 .tag = mir_tag,
1950 .data = .{ .rr_op = .{1862 .data = mir_data,
1951 .rd = dst_reg,1863 });
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 };
19731864
1974 const tag: Mir.Inst.Tag = switch (op) {1865 return MCValue{ .register = dest_reg };
1975 .shl => .lsl,1866}
1976 .shr => switch (signedness) {1867
1977 .signed => Mir.Inst.Tag.asr,1868/// Don't call this function directly. Use binOp instead.
1978 .unsigned => Mir.Inst.Tag.lsr,1869///
1979 },1870/// Calling this function signals an intention to generate a Mir
1980 else => unreachable,1871/// instruction of the form
1981 };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(.{1891 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
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}
19951892
1996fn genMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {1893 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1997 const lhs = try self.resolveInst(op_lhs);
1998 const rhs = try self.resolveInst(op_rhs);
19991894
2000 const lhs_is_register = lhs == .register;1895 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
2001 const rhs_is_register = rhs == .register;1896 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
2002 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);1897 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2003 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);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});1903 const reg = try self.register_manager.allocReg(track_inst);
2006 defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register});1904 self.register_manager.freezeRegs(&.{reg});
2007 if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
2008 defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register});
20091905
2010 // Destination must be a register1906 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
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;
20161907
2017 // Allocate registers for operands and/or destination1908 break :blk reg;
2018 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1909 };
2019 if (reuse_lhs) {1910 defer self.register_manager.unfreezeRegs(&.{lhs_reg});
2020 // Allocate 0 or 1 registers1911
2021 if (!rhs_is_register) {1912 const dest_reg = switch (tag) {
2022 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?) };1913 .cmp_eq => .r0, // cmp has no destination reg
2023 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);1914 else => if (maybe_inst) |inst| blk: {
2024 }1915 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2025 dst_mcv = lhs;1916
2026 } else if (reuse_rhs) {1917 if (lhs_is_register and self.reuseOperand(
2027 // Allocate 0 or 1 registers1918 inst,
2028 if (!lhs_is_register) {1919 if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs,
2029 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?) };1920 if (lhs_and_rhs_swapped) 1 else 0,
2030 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv);1921 lhs,
2031 }1922 )) {
2032 dst_mcv = rhs;1923 break :blk lhs_reg;
2033 } else {1924 } else {
2034 // Allocate 1 or 2 registers1925 break :blk try self.register_manager.allocReg(inst);
2035 if (lhs_is_register and rhs_is_register) {1926 }
2036 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) };1927 } else try self.register_manager.allocReg(null),
2037 } else if (lhs_is_register) {1928 };
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;
20511929
2052 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);1930 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
2053 }
2054 }
20551931
2056 // Move the operands to the newly allocated registers1932 const mir_tag: Mir.Inst.Tag = switch (tag) {
2057 if (!lhs_is_register) {1933 .add => .add,
2058 try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs);1934 .sub => .sub,
2059 }1935 .cmp_eq => .cmp,
2060 if (!rhs_is_register) {1936 .bit_and,
2061 try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs);1937 .bool_and,
2062 }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
2064 _ = try self.addInst(.{1980 _ = try self.addInst(.{
2065 .tag = .mul,1981 .tag = mir_tag,
2066 .data = .{ .rrr = .{1982 .data = mir_data,
2067 .rd = dst_mcv.register,
2068 .rn = lhs_mcv.register,
2069 .rm = rhs_mcv.register,
2070 } },
2071 });1983 });
2072 return dst_mcv;
2073}
20741984
2075fn genMulConstant(self: *Self, op: Air.Inst.Ref, imm: u32) !MCValue {1985 return MCValue{ .register = dest_reg };
2076 const lhs = try self.resolveInst(op);1986}
2077 const rhs = MCValue{ .immediate = imm };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});2154 if (pointee_ty.abiSize(self.target.*) > 1) {
2082 defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register});2155 return self.fail("TODO ptr_add, ptr_sub with more element sizes", .{});
20832156 }
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 }
21042157
2105 // Move the operands to the newly allocated registers2158 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2106 if (!lhs_is_register) {2159 },
2107 try self.genSetReg(self.air.typeOf(op), lhs_mcv.register, lhs);2160 else => unreachable,
2161 }
2162 },
2163 else => unreachable,
2108 }2164 }
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;
2120}2165}
21212166
2122fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, abi_size: u32) !void {2167fn 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 {...@@ -2492,75 +2537,38 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
2492 const rhs = try self.resolveInst(bin_op.rhs);2537 const rhs = try self.resolveInst(bin_op.rhs);
2493 const lhs_ty = self.air.typeOf(bin_op.lhs);2538 const lhs_ty = self.air.typeOf(bin_op.lhs);
24942539
2495 if (lhs_ty.abiSize(self.target.*) > 4) {2540 switch (lhs_ty.zigTypeTag()) {
2496 return self.fail("TODO cmp for types with size > 4", .{});2541 .Vector => return self.fail("TODO ARM cmp vectors", .{}),
2497 }2542 .Optional => return self.fail("TODO ARM cmp optionals", .{}),
24982543 .Float => return self.fail("TODO ARM cmp floats", .{}),
2499 const signedness: std.builtin.Signedness = blk: {2544 .Int, .Bool, .Pointer, .ErrorSet, .Enum => {
2500 // by default we tell the operand type is unsigned (i.e. bools and enum values)2545 var int_buffer: Type.Payload.Bits = undefined;
2501 if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned;2546 const int_ty = switch (lhs_ty.zigTypeTag()) {
25022547 .Enum => lhs_ty.intTagType(&int_buffer),
2503 // incase of an actual integer, we emit the correct signedness2548 .Int => lhs_ty,
2504 break :blk lhs_ty.intInfo(self.target.*).signedness;2549 .Bool => Type.initTag(.u1),
2505 };2550 .Pointer => Type.usize,
25062551 .ErrorSet => Type.initTag(.u16),
2507 try self.spillCompareFlagsIfOccupied();2552 else => unreachable,
2508 self.compare_flags_inst = inst;2553 };
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);
25142554
2515 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});2555 const int_info = int_ty.intInfo(self.target.*);
2516 defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register});2556 if (int_info.bits <= 32) {
2517 if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});2557 try self.spillCompareFlagsIfOccupied();
2518 defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register});2558 self.compare_flags_inst = inst;
25192559
2520 var lhs_mcv = lhs;2560 _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty);
2521 var rhs_mcv = rhs;
25222561
2523 // Allocate registers2562 break :result switch (int_info.signedness) {
2524 if (rhs_should_be_register) {2563 .signed => MCValue{ .compare_flags_signed = op },
2525 if (!lhs_is_register and !rhs_is_register) {2564 .unsigned => MCValue{ .compare_flags_unsigned = op },
2526 const regs = try self.register_manager.allocRegs(2, .{2565 };
2527 Air.refToIndex(bin_op.lhs).?, Air.refToIndex(bin_op.rhs).?,2566 } else {
2528 });2567 return self.fail("TODO ARM cmp for ints > 32 bits", .{});
2529 lhs_mcv = MCValue{ .register = regs[0] };2568 }
2530 rhs_mcv = MCValue{ .register = regs[1] };2569 },
2531 } else if (!rhs_is_register) {2570 else => unreachable,
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);
2554 }2571 }
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 };
2564 };2572 };
2565 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2573 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2566}2574}
...@@ -2787,7 +2795,14 @@ fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue {...@@ -2787,7 +2795,14 @@ fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
2787 else => .{ .register = try self.copyToTmpRegister(ty, operand) },2795 else => .{ .register = try self.copyToTmpRegister(ty, operand) },
2788 };2796 };
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
2792 return MCValue{ .compare_flags_unsigned = .eq };2807 return MCValue{ .compare_flags_unsigned = .eq };
2793 } else {2808 } else {
...@@ -2817,7 +2832,14 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {...@@ -2817,7 +2832,14 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
2817 else => .{ .register = try self.copyToTmpRegister(error_type, operand) },2832 else => .{ .register = try self.copyToTmpRegister(error_type, operand) },
2818 };2833 };
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
2822 return MCValue{ .compare_flags_unsigned = .gt };2844 return MCValue{ .compare_flags_unsigned = .gt };
2823 } else {2845 } else {
...@@ -3028,17 +3050,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3028,17 +3050,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {
3028 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });3050 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
3029}3051}
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
3042fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {3053fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
3043 const block_data = self.blocks.getPtr(block).?;3054 const block_data = self.blocks.getPtr(block).?;
30443055
...@@ -3342,17 +3353,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -3342,17 +3353,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
3342 });3353 });
33433354
3344 // mov len, #abi_size3355 // mov len, #abi_size
3345 const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(abi_size)) |x| x else {3356 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
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 });
33563357
3357 // memcpy(src, dst, len)3358 // memcpy(src, dst, len)
3358 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);3359 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...@@ -3738,17 +3739,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
3738 });3739 });
37393740
3740 // mov len, #abi_size3741 // mov len, #abi_size
3741 const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(abi_size)) |x| x else {3742 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size });
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 });
37523743
3753 // memcpy(src, dst, len)3744 // memcpy(src, dst, len)
3754 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);3745 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);