| ... | @@ -1058,7 +1058,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1058,7 +1058,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1058 | | 1058 | |
| 1059 | _ = try self.addInst(.{ | 1059 | _ = try self.addInst(.{ |
| 1060 | .tag = .mvn, | 1060 | .tag = .mvn, |
| 1061 | .data = .{ .rr_imm6_shift = .{ | 1061 | .data = .{ .rr_imm6_logical_shift = .{ |
| 1062 | .rd = dest_reg, | 1062 | .rd = dest_reg, |
| 1063 | .rm = op_reg, | 1063 | .rm = op_reg, |
| 1064 | .imm6 = 0, | 1064 | .imm6 = 0, |
| ... | @@ -1188,6 +1188,7 @@ fn binOpRegister( | ... | @@ -1188,6 +1188,7 @@ fn binOpRegister( |
| 1188 | .sub, | 1188 | .sub, |
| 1189 | .ptr_sub, | 1189 | .ptr_sub, |
| 1190 | => .sub_shifted_register, | 1190 | => .sub_shifted_register, |
| | 1191 | .cmp_eq => .cmp_shifted_register, |
| 1191 | .mul => .mul, | 1192 | .mul => .mul, |
| 1192 | .bit_and, | 1193 | .bit_and, |
| 1193 | .bool_and, | 1194 | .bool_and, |
| ... | @@ -1219,6 +1220,12 @@ fn binOpRegister( | ... | @@ -1219,6 +1220,12 @@ fn binOpRegister( |
| 1219 | .imm6 = 0, | 1220 | .imm6 = 0, |
| 1220 | .shift = .lsl, | 1221 | .shift = .lsl, |
| 1221 | } }, | 1222 | } }, |
| | 1223 | .cmp_eq => .{ .rr_imm6_shift = .{ |
| | 1224 | .rn = lhs_reg, |
| | 1225 | .rm = rhs_reg, |
| | 1226 | .imm6 = 0, |
| | 1227 | .shift = .lsl, |
| | 1228 | } }, |
| 1222 | .mul, | 1229 | .mul, |
| 1223 | .shl, | 1230 | .shl, |
| 1224 | .shl_exact, | 1231 | .shl_exact, |
| ... | @@ -1296,20 +1303,23 @@ fn binOpImmediate( | ... | @@ -1296,20 +1303,23 @@ fn binOpImmediate( |
| 1296 | }; | 1303 | }; |
| 1297 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); | 1304 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| 1298 | | 1305 | |
| 1299 | const dest_reg = if (maybe_inst) |inst| blk: { | 1306 | const dest_reg = switch (tag) { |
| 1300 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1307 | .cmp_eq => undefined, // cmp has no destination register |
| | 1308 | else => if (maybe_inst) |inst| blk: { |
| | 1309 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1301 | | 1310 | |
| 1302 | if (lhs_is_register and self.reuseOperand( | 1311 | if (lhs_is_register and self.reuseOperand( |
| 1303 | inst, | 1312 | inst, |
| 1304 | if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, | 1313 | if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, |
| 1305 | if (lhs_and_rhs_swapped) 1 else 0, | 1314 | if (lhs_and_rhs_swapped) 1 else 0, |
| 1306 | lhs, | 1315 | lhs, |
| 1307 | )) { | 1316 | )) { |
| 1308 | break :blk lhs_reg; | 1317 | break :blk lhs_reg; |
| 1309 | } else { | 1318 | } else { |
| 1310 | break :blk try self.register_manager.allocReg(inst); | 1319 | break :blk try self.register_manager.allocReg(inst); |
| 1311 | } | 1320 | } |
| 1312 | } else try self.register_manager.allocReg(null); | 1321 | } else try self.register_manager.allocReg(null), |
| | 1322 | }; |
| 1313 | | 1323 | |
| 1314 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | 1324 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1315 | | 1325 | |
| ... | @@ -1325,6 +1335,7 @@ fn binOpImmediate( | ... | @@ -1325,6 +1335,7 @@ fn binOpImmediate( |
| 1325 | .signed => Mir.Inst.Tag.asr_immediate, | 1335 | .signed => Mir.Inst.Tag.asr_immediate, |
| 1326 | .unsigned => Mir.Inst.Tag.lsr_immediate, | 1336 | .unsigned => Mir.Inst.Tag.lsr_immediate, |
| 1327 | }, | 1337 | }, |
| | 1338 | .cmp_eq => .cmp_immediate, |
| 1328 | else => unreachable, | 1339 | else => unreachable, |
| 1329 | }; | 1340 | }; |
| 1330 | const mir_data: Mir.Inst.Data = switch (tag) { | 1341 | const mir_data: Mir.Inst.Data = switch (tag) { |
| ... | @@ -1344,6 +1355,10 @@ fn binOpImmediate( | ... | @@ -1344,6 +1355,10 @@ fn binOpImmediate( |
| 1344 | .rn = lhs_reg, | 1355 | .rn = lhs_reg, |
| 1345 | .shift = @intCast(u6, rhs.immediate), | 1356 | .shift = @intCast(u6, rhs.immediate), |
| 1346 | } }, | 1357 | } }, |
| | 1358 | .cmp_eq => .{ .r_imm12_sh = .{ |
| | 1359 | .rn = lhs_reg, |
| | 1360 | .imm12 = @intCast(u12, rhs.immediate), |
| | 1361 | } }, |
| 1347 | else => unreachable, | 1362 | else => unreachable, |
| 1348 | }; | 1363 | }; |
| 1349 | | 1364 | |
| ... | @@ -1381,6 +1396,7 @@ fn binOp( | ... | @@ -1381,6 +1396,7 @@ fn binOp( |
| 1381 | // Arithmetic operations on integers and floats | 1396 | // Arithmetic operations on integers and floats |
| 1382 | .add, | 1397 | .add, |
| 1383 | .sub, | 1398 | .sub, |
| | 1399 | .cmp_eq, |
| 1384 | => { | 1400 | => { |
| 1385 | switch (lhs_ty.zigTypeTag()) { | 1401 | switch (lhs_ty.zigTypeTag()) { |
| 1386 | .Float => return self.fail("TODO binary operations on floats", .{}), | 1402 | .Float => return self.fail("TODO binary operations on floats", .{}), |
| ... | @@ -1394,12 +1410,13 @@ fn binOp( | ... | @@ -1394,12 +1410,13 @@ fn binOp( |
| 1394 | // operands | 1410 | // operands |
| 1395 | const lhs_immediate_ok = switch (tag) { | 1411 | const lhs_immediate_ok = switch (tag) { |
| 1396 | .add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), | 1412 | .add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), |
| 1397 | .sub => false, | 1413 | .sub, .cmp_eq => false, |
| 1398 | else => unreachable, | 1414 | else => unreachable, |
| 1399 | }; | 1415 | }; |
| 1400 | const rhs_immediate_ok = switch (tag) { | 1416 | const rhs_immediate_ok = switch (tag) { |
| 1401 | .add, | 1417 | .add, |
| 1402 | .sub, | 1418 | .sub, |
| | 1419 | .cmp_eq, |
| 1403 | => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), | 1420 | => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), |
| 1404 | else => unreachable, | 1421 | else => unreachable, |
| 1405 | }; | 1422 | }; |
| ... | @@ -2036,8 +2053,7 @@ fn genInlineMemcpy( | ... | @@ -2036,8 +2053,7 @@ fn genInlineMemcpy( |
| 2036 | // cmp count, len | 2053 | // cmp count, len |
| 2037 | _ = try self.addInst(.{ | 2054 | _ = try self.addInst(.{ |
| 2038 | .tag = .cmp_shifted_register, | 2055 | .tag = .cmp_shifted_register, |
| 2039 | .data = .{ .rrr_imm6_shift = .{ | 2056 | .data = .{ .rr_imm6_shift = .{ |
| 2040 | .rd = .xzr, | | |
| 2041 | .rn = count, | 2057 | .rn = count, |
| 2042 | .rm = len, | 2058 | .rm = len, |
| 2043 | .imm6 = 0, | 2059 | .imm6 = 0, |
| ... | @@ -2615,107 +2631,44 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2615,107 +2631,44 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 2615 | | 2631 | |
| 2616 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | 2632 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2617 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 2633 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 2634 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| | 2635 | const lhs = try self.resolveInst(bin_op.lhs); |
| | 2636 | const rhs = try self.resolveInst(bin_op.rhs); |
| | 2637 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| | 2638 | |
| | 2639 | var int_buffer: Type.Payload.Bits = undefined; |
| | 2640 | const int_ty = switch (lhs_ty.zigTypeTag()) { |
| | 2641 | .Vector => return self.fail("TODO AArch64 cmp vectors", .{}), |
| | 2642 | .Enum => lhs_ty.intTagType(&int_buffer), |
| | 2643 | .Int => lhs_ty, |
| | 2644 | .Bool => Type.initTag(.u1), |
| | 2645 | .Pointer => Type.usize, |
| | 2646 | .ErrorSet => Type.initTag(.u16), |
| | 2647 | .Optional => blk: { |
| | 2648 | if (lhs_ty.isPtrLikeOptional()) { |
| | 2649 | break :blk Type.usize; |
| | 2650 | } |
| 2618 | | 2651 | |
| 2619 | if (self.liveness.isUnused(inst)) | 2652 | return self.fail("TODO AArch64 cmp optionals", .{}); |
| 2620 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); | 2653 | }, |
| 2621 | | 2654 | .Float => return self.fail("TODO AArch64 cmp floats", .{}), |
| 2622 | const ty = self.air.typeOf(bin_op.lhs); | 2655 | else => unreachable, |
| 2623 | | | |
| 2624 | if (ty.abiSize(self.target.*) > 8) { | | |
| 2625 | return self.fail("TODO cmp for types with size > 8", .{}); | | |
| 2626 | } | | |
| 2627 | | | |
| 2628 | try self.spillCompareFlagsIfOccupied(); | | |
| 2629 | self.compare_flags_inst = inst; | | |
| 2630 | | | |
| 2631 | const signedness: std.builtin.Signedness = blk: { | | |
| 2632 | // by default we tell the operand type is unsigned (i.e. bools and enum values) | | |
| 2633 | if (ty.zigTypeTag() != .Int) break :blk .unsigned; | | |
| 2634 | | | |
| 2635 | // incase of an actual integer, we emit the correct signedness | | |
| 2636 | break :blk ty.intInfo(self.target.*).signedness; | | |
| 2637 | }; | | |
| 2638 | | | |
| 2639 | const lhs = try self.resolveInst(bin_op.lhs); | | |
| 2640 | const rhs = try self.resolveInst(bin_op.rhs); | | |
| 2641 | const result: MCValue = result: { | | |
| 2642 | const lhs_is_register = lhs == .register; | | |
| 2643 | const rhs_is_register = rhs == .register; | | |
| 2644 | // lhs should always be a register | | |
| 2645 | const rhs_should_be_register = switch (rhs) { | | |
| 2646 | .immediate => |imm| imm < 0 or imm > std.math.maxInt(u12), | | |
| 2647 | else => true, | | |
| 2648 | }; | 2656 | }; |
| 2649 | | 2657 | |
| 2650 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); | 2658 | const int_info = int_ty.intInfo(self.target.*); |
| 2651 | defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register}); | 2659 | if (int_info.bits <= 64) { |
| 2652 | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); | 2660 | _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); |
| 2653 | defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register}); | | |
| 2654 | | 2661 | |
| 2655 | var lhs_mcv = lhs; | 2662 | try self.spillCompareFlagsIfOccupied(); |
| 2656 | var rhs_mcv = rhs; | 2663 | self.compare_flags_inst = inst; |
| 2657 | | 2664 | |
| 2658 | // Allocate registers | 2665 | break :result switch (int_info.signedness) { |
| 2659 | if (rhs_should_be_register) { | 2666 | .signed => MCValue{ .compare_flags_signed = op }, |
| 2660 | if (!lhs_is_register and !rhs_is_register) { | 2667 | .unsigned => MCValue{ .compare_flags_unsigned = op }, |
| 2661 | const regs = try self.register_manager.allocRegs(2, .{ | 2668 | }; |
| 2662 | Air.refToIndex(bin_op.lhs).?, Air.refToIndex(bin_op.rhs).?, | | |
| 2663 | }); | | |
| 2664 | lhs_mcv = MCValue{ .register = regs[0] }; | | |
| 2665 | rhs_mcv = MCValue{ .register = regs[1] }; | | |
| 2666 | } else if (!rhs_is_register) { | | |
| 2667 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.rhs).?) }; | | |
| 2668 | } else if (!lhs_is_register) { | | |
| 2669 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.lhs).?) }; | | |
| 2670 | } | | |
| 2671 | } else { | 2669 | } else { |
| 2672 | if (!lhs_is_register) { | 2670 | return self.fail("TODO AArch64 cmp for ints > 64 bits", .{}); |
| 2673 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.lhs).?) }; | | |
| 2674 | } | | |
| 2675 | } | | |
| 2676 | | | |
| 2677 | // Move the operands to the newly allocated registers | | |
| 2678 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | | |
| 2679 | if (lhs_mcv == .register and !lhs_is_register) { | | |
| 2680 | try self.genSetReg(ty, lhs_mcv.register, lhs); | | |
| 2681 | branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.lhs).?, lhs); | | |
| 2682 | } | | |
| 2683 | if (rhs_mcv == .register and !rhs_is_register) { | | |
| 2684 | try self.genSetReg(ty, rhs_mcv.register, rhs); | | |
| 2685 | branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.rhs).?, rhs); | | |
| 2686 | } | | |
| 2687 | | | |
| 2688 | // The destination register is not present in the cmp instruction | | |
| 2689 | // The signedness of the integer does not matter for the cmp instruction | | |
| 2690 | switch (rhs_mcv) { | | |
| 2691 | .register => |reg| { | | |
| 2692 | _ = try self.addInst(.{ | | |
| 2693 | .tag = .cmp_shifted_register, | | |
| 2694 | .data = .{ .rrr_imm6_shift = .{ | | |
| 2695 | .rd = .xzr, | | |
| 2696 | .rn = lhs_mcv.register, | | |
| 2697 | .rm = reg, | | |
| 2698 | .imm6 = 0, | | |
| 2699 | .shift = .lsl, | | |
| 2700 | } }, | | |
| 2701 | }); | | |
| 2702 | }, | | |
| 2703 | .immediate => |imm| { | | |
| 2704 | _ = try self.addInst(.{ | | |
| 2705 | .tag = .cmp_immediate, | | |
| 2706 | .data = .{ .r_imm12_sh = .{ | | |
| 2707 | .rn = lhs_mcv.register, | | |
| 2708 | .imm12 = @intCast(u12, imm), | | |
| 2709 | } }, | | |
| 2710 | }); | | |
| 2711 | }, | | |
| 2712 | else => unreachable, | | |
| 2713 | } | 2671 | } |
| 2714 | | | |
| 2715 | break :result switch (signedness) { | | |
| 2716 | .signed => MCValue{ .compare_flags_signed = op }, | | |
| 2717 | .unsigned => MCValue{ .compare_flags_unsigned = op }, | | |
| 2718 | }; | | |
| 2719 | }; | 2672 | }; |
| 2720 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 2673 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2721 | } | 2674 | } |