| ... | ... | @@ -502,18 +502,18 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 502 | 502 | |
| 503 | 503 | switch (air_tags[inst]) { |
| 504 | 504 | // zig fmt: off |
| 505 | | .add, .ptr_add => try self.airAdd(inst), |
| 505 | .add, .ptr_add => try self.airBinOp(inst), |
| 506 | 506 | .addwrap => try self.airAddWrap(inst), |
| 507 | 507 | .add_sat => try self.airAddSat(inst), |
| 508 | | .sub, .ptr_sub => try self.airSub(inst), |
| 508 | .sub, .ptr_sub => try self.airBinOp(inst), |
| 509 | 509 | .subwrap => try self.airSubWrap(inst), |
| 510 | 510 | .sub_sat => try self.airSubSat(inst), |
| 511 | | .mul => try self.airMul(inst), |
| 511 | .mul => try self.airBinOp(inst), |
| 512 | 512 | .mulwrap => try self.airMulWrap(inst), |
| 513 | 513 | .mul_sat => try self.airMulSat(inst), |
| 514 | 514 | .rem => try self.airRem(inst), |
| 515 | 515 | .mod => try self.airMod(inst), |
| 516 | | .shl, .shl_exact => try self.airShl(inst), |
| 516 | .shl, .shl_exact => try self.airBinOp(inst), |
| 517 | 517 | .shl_sat => try self.airShlSat(inst), |
| 518 | 518 | .min => try self.airMin(inst), |
| 519 | 519 | .max => try self.airMax(inst), |
| ... | ... | @@ -548,12 +548,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 548 | 548 | .cmp_gt => try self.airCmp(inst, .gt), |
| 549 | 549 | .cmp_neq => try self.airCmp(inst, .neq), |
| 550 | 550 | |
| 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), |
| 557 | 557 | |
| 558 | 558 | .alloc => try self.airAlloc(inst), |
| 559 | 559 | .ret_ptr => try self.airRetPtr(inst), |
| ... | ... | @@ -887,6 +887,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 887 | 887 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 888 | 888 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 889 | 889 | const operand = try self.resolveInst(ty_op.operand); |
| 890 | const operand_ty = self.air.typeOf(ty_op.operand); |
| 890 | 891 | switch (operand) { |
| 891 | 892 | .dead => unreachable, |
| 892 | 893 | .unreach => unreachable, |
| ... | ... | @@ -917,7 +918,68 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 917 | 918 | break :result r; |
| 918 | 919 | }, |
| 919 | 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 | 1005 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 944 | 1006 | } |
| 945 | 1007 | |
| 946 | | fn airAdd(self: *Self, inst: Air.Inst.Index) !void { |
| 1008 | fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1009 | const tag = self.air.instructions.items(.tag)[inst]; |
| 947 | 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 | 1017 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 950 | 1018 | } |
| 951 | 1019 | |
| ... | ... | @@ -961,12 +1029,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { |
| 961 | 1029 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 962 | 1030 | } |
| 963 | 1031 | |
| 964 | | fn 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 | | |
| 970 | 1032 | fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void { |
| 971 | 1033 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 972 | 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 | 1041 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 980 | 1042 | } |
| 981 | 1043 | |
| 982 | | fn 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 | | |
| 988 | 1044 | fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void { |
| 989 | 1045 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 990 | 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 | 1091 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1036 | 1092 | } |
| 1037 | 1093 | |
| 1038 | | fn 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 | | |
| 1044 | | fn 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 | | |
| 1050 | | fn 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 | | |
| 1056 | | fn 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 | | |
| 1062 | 1094 | fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1063 | 1095 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1064 | 1096 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); |
| 1065 | 1097 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1066 | 1098 | } |
| 1067 | 1099 | |
| 1068 | | fn 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 | | |
| 1074 | 1100 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1075 | 1101 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1076 | 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 | 1278 | |
| 1253 | 1279 | const slice_ty = self.air.typeOf(bin_op.lhs); |
| 1254 | 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.*)); |
| 1256 | 1282 | |
| 1257 | 1283 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 1258 | 1284 | const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); |
| ... | ... | @@ -1307,22 +1333,25 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1307 | 1333 | else => { |
| 1308 | 1334 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 1309 | 1335 | |
| 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 | 1344 | assert(offset_mcv == .register); // result of multiplication should always be register |
| 1312 | 1345 | self.register_manager.freezeRegs(&.{offset_mcv.register}); |
| 1313 | 1346 | |
| 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); |
| 1319 | 1348 | |
| 1320 | 1349 | // At this point in time, neither the base register |
| 1321 | 1350 | // nor the offset register contains any valuable data |
| 1322 | 1351 | // anymore. |
| 1323 | 1352 | self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register }); |
| 1324 | 1353 | |
| 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); |
| 1326 | 1355 | |
| 1327 | 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 | 1528 | const tmp_reg = regs[3]; |
| 1500 | 1529 | |
| 1501 | 1530 | // 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 }); |
| 1514 | 1532 | |
| 1515 | 1533 | // 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 }); |
| 1527 | 1535 | |
| 1528 | 1536 | // memcpy(src, dst, len) |
| 1529 | 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 | 1681 | self.register_manager.freezeRegs(&.{addr_reg}); |
| 1674 | 1682 | defer self.register_manager.unfreezeRegs(&.{addr_reg}); |
| 1675 | 1683 | |
| 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, |
| 1679 | 1687 | .{ .register = addr_reg }, |
| 1680 | 1688 | .{ .register = offset_reg }, |
| 1681 | | false, |
| 1682 | | .add, |
| 1683 | | .unsigned, |
| 1689 | Type.usize, |
| 1690 | Type.usize, |
| 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 | 1728 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); |
| 1721 | 1729 | } |
| 1722 | 1730 | |
| 1723 | | fn 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 | | |
| 1748 | | fn 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 | | |
| 1767 | | fn 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. |
| 1739 | fn binOpRegister( |
| 1768 | 1740 | 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, |
| 1775 | 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 | 1748 | const lhs_is_register = lhs == .register; |
| 1784 | 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 | | }; |
| 1796 | 1750 | |
| 1797 | 1751 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); |
| 1798 | | defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register}); |
| 1799 | 1752 | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); |
| 1800 | | defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register}); |
| 1801 | 1753 | |
| 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 | 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; |
| 1824 | 1755 | |
| 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; |
| 1845 | 1761 | |
| 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}); |
| 1864 | 1764 | |
| 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 }); |
| 1868 | 1766 | |
| 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}); |
| 1876 | 1770 | |
| 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; |
| 1887 | 1776 | |
| 1888 | | fn 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}); |
| 1898 | 1779 | |
| 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 }); |
| 1901 | 1781 | |
| 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; |
| 1917 | 1783 | }; |
| 1784 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| 1918 | 1785 | |
| 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, |
| 1922 | 1809 | .bit_and, |
| 1810 | .bool_and, |
| 1811 | => .@"and", |
| 1812 | .bit_or, |
| 1923 | 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 | 1833 | .bit_or, |
| 1925 | | .not, |
| 1834 | .bool_or, |
| 1926 | 1835 | .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 | }; |
| 1947 | 1859 | |
| 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 | }); |
| 1973 | 1864 | |
| 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. |
| 1880 | fn 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; |
| 1982 | 1890 | |
| 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}); |
| 1995 | 1892 | |
| 1996 | | fn 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]; |
| 1999 | 1894 | |
| 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; |
| 2004 | 1902 | |
| 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}); |
| 2009 | 1905 | |
| 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 }); |
| 2016 | 1907 | |
| 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 | }; |
| 2051 | 1929 | |
| 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); |
| 2055 | 1931 | |
| 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 | }; |
| 2063 | 1979 | |
| 2064 | 1980 | _ = 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, |
| 2071 | 1983 | }); |
| 2072 | | return dst_mcv; |
| 2073 | | } |
| 2074 | 1984 | |
| 2075 | | fn 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. |
| 2000 | fn 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 | }; |
| 2078 | 2038 | |
| 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 | }; |
| 2080 | 2153 | |
| 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 | } |
| 2104 | 2157 | |
| 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, |
| 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 | } |
| 2121 | 2166 | |
| 2122 | 2167 | fn 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 | 2537 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2493 | 2538 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 2494 | 2539 | |
| 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 | }; |
| 2514 | 2554 | |
| 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; |
| 2519 | 2559 | |
| 2520 | | var lhs_mcv = lhs; |
| 2521 | | var rhs_mcv = rhs; |
| 2560 | _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); |
| 2522 | 2561 | |
| 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, |
| 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 | 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 | 2795 | else => .{ .register = try self.copyToTmpRegister(ty, operand) }, |
| 2788 | 2796 | }; |
| 2789 | 2797 | |
| 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 | }); |
| 2791 | 2806 | |
| 2792 | 2807 | return MCValue{ .compare_flags_unsigned = .eq }; |
| 2793 | 2808 | } else { |
| ... | ... | @@ -2817,7 +2832,14 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2817 | 2832 | else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, |
| 2818 | 2833 | }; |
| 2819 | 2834 | |
| 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 | }); |
| 2821 | 2843 | |
| 2822 | 2844 | return MCValue{ .compare_flags_unsigned = .gt }; |
| 2823 | 2845 | } else { |
| ... | ... | @@ -3028,17 +3050,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { |
| 3028 | 3050 | return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); |
| 3029 | 3051 | } |
| 3030 | 3052 | |
| 3031 | | fn 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 | | |
| 3042 | 3053 | fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 3043 | 3054 | const block_data = self.blocks.getPtr(block).?; |
| 3044 | 3055 | |
| ... | ... | @@ -3342,17 +3353,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3342 | 3353 | }); |
| 3343 | 3354 | |
| 3344 | 3355 | // 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 }); |
| 3356 | 3357 | |
| 3357 | 3358 | // memcpy(src, dst, len) |
| 3358 | 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 | 3739 | }); |
| 3739 | 3740 | |
| 3740 | 3741 | // 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 }); |
| 3752 | 3743 | |
| 3753 | 3744 | // memcpy(src, dst, len) |
| 3754 | 3745 | try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); |