| ... | ... | @@ -1058,7 +1058,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1058 | 1058 | |
| 1059 | 1059 | _ = try self.addInst(.{ |
| 1060 | 1060 | .tag = .mvn, |
| 1061 | | .data = .{ .rr_imm6_shift = .{ |
| 1061 | .data = .{ .rr_imm6_logical_shift = .{ |
| 1062 | 1062 | .rd = dest_reg, |
| 1063 | 1063 | .rm = op_reg, |
| 1064 | 1064 | .imm6 = 0, |
| ... | ... | @@ -1188,6 +1188,7 @@ fn binOpRegister( |
| 1188 | 1188 | .sub, |
| 1189 | 1189 | .ptr_sub, |
| 1190 | 1190 | => .sub_shifted_register, |
| 1191 | .cmp_eq => .cmp_shifted_register, |
| 1191 | 1192 | .mul => .mul, |
| 1192 | 1193 | .bit_and, |
| 1193 | 1194 | .bool_and, |
| ... | ... | @@ -1219,6 +1220,12 @@ fn binOpRegister( |
| 1219 | 1220 | .imm6 = 0, |
| 1220 | 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 | 1229 | .mul, |
| 1223 | 1230 | .shl, |
| 1224 | 1231 | .shl_exact, |
| ... | ... | @@ -1296,20 +1303,23 @@ fn binOpImmediate( |
| 1296 | 1303 | }; |
| 1297 | 1304 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| 1298 | 1305 | |
| 1299 | | const dest_reg = if (maybe_inst) |inst| blk: { |
| 1300 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1306 | const dest_reg = switch (tag) { |
| 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( |
| 1303 | | inst, |
| 1304 | | if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, |
| 1305 | | if (lhs_and_rhs_swapped) 1 else 0, |
| 1306 | | lhs, |
| 1307 | | )) { |
| 1308 | | break :blk lhs_reg; |
| 1309 | | } else { |
| 1310 | | break :blk try self.register_manager.allocReg(inst); |
| 1311 | | } |
| 1312 | | } else try self.register_manager.allocReg(null); |
| 1311 | if (lhs_is_register and self.reuseOperand( |
| 1312 | inst, |
| 1313 | if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, |
| 1314 | if (lhs_and_rhs_swapped) 1 else 0, |
| 1315 | lhs, |
| 1316 | )) { |
| 1317 | break :blk lhs_reg; |
| 1318 | } else { |
| 1319 | break :blk try self.register_manager.allocReg(inst); |
| 1320 | } |
| 1321 | } else try self.register_manager.allocReg(null), |
| 1322 | }; |
| 1313 | 1323 | |
| 1314 | 1324 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1315 | 1325 | |
| ... | ... | @@ -1325,6 +1335,7 @@ fn binOpImmediate( |
| 1325 | 1335 | .signed => Mir.Inst.Tag.asr_immediate, |
| 1326 | 1336 | .unsigned => Mir.Inst.Tag.lsr_immediate, |
| 1327 | 1337 | }, |
| 1338 | .cmp_eq => .cmp_immediate, |
| 1328 | 1339 | else => unreachable, |
| 1329 | 1340 | }; |
| 1330 | 1341 | const mir_data: Mir.Inst.Data = switch (tag) { |
| ... | ... | @@ -1344,6 +1355,10 @@ fn binOpImmediate( |
| 1344 | 1355 | .rn = lhs_reg, |
| 1345 | 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 | 1362 | else => unreachable, |
| 1348 | 1363 | }; |
| 1349 | 1364 | |
| ... | ... | @@ -1381,6 +1396,7 @@ fn binOp( |
| 1381 | 1396 | // Arithmetic operations on integers and floats |
| 1382 | 1397 | .add, |
| 1383 | 1398 | .sub, |
| 1399 | .cmp_eq, |
| 1384 | 1400 | => { |
| 1385 | 1401 | switch (lhs_ty.zigTypeTag()) { |
| 1386 | 1402 | .Float => return self.fail("TODO binary operations on floats", .{}), |
| ... | ... | @@ -1394,12 +1410,13 @@ fn binOp( |
| 1394 | 1410 | // operands |
| 1395 | 1411 | const lhs_immediate_ok = switch (tag) { |
| 1396 | 1412 | .add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), |
| 1397 | | .sub => false, |
| 1413 | .sub, .cmp_eq => false, |
| 1398 | 1414 | else => unreachable, |
| 1399 | 1415 | }; |
| 1400 | 1416 | const rhs_immediate_ok = switch (tag) { |
| 1401 | 1417 | .add, |
| 1402 | 1418 | .sub, |
| 1419 | .cmp_eq, |
| 1403 | 1420 | => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), |
| 1404 | 1421 | else => unreachable, |
| 1405 | 1422 | }; |
| ... | ... | @@ -2036,8 +2053,7 @@ fn genInlineMemcpy( |
| 2036 | 2053 | // cmp count, len |
| 2037 | 2054 | _ = try self.addInst(.{ |
| 2038 | 2055 | .tag = .cmp_shifted_register, |
| 2039 | | .data = .{ .rrr_imm6_shift = .{ |
| 2040 | | .rd = .xzr, |
| 2056 | .data = .{ .rr_imm6_shift = .{ |
| 2041 | 2057 | .rn = count, |
| 2042 | 2058 | .rm = len, |
| 2043 | 2059 | .imm6 = 0, |
| ... | ... | @@ -2615,107 +2631,44 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 2615 | 2631 | |
| 2616 | 2632 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2617 | 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)) |
| 2620 | | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2621 | | |
| 2622 | | const ty = self.air.typeOf(bin_op.lhs); |
| 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, |
| 2652 | return self.fail("TODO AArch64 cmp optionals", .{}); |
| 2653 | }, |
| 2654 | .Float => return self.fail("TODO AArch64 cmp floats", .{}), |
| 2655 | else => unreachable, |
| 2648 | 2656 | }; |
| 2649 | 2657 | |
| 2650 | | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); |
| 2651 | | defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register}); |
| 2652 | | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); |
| 2653 | | defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register}); |
| 2658 | const int_info = int_ty.intInfo(self.target.*); |
| 2659 | if (int_info.bits <= 64) { |
| 2660 | _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); |
| 2654 | 2661 | |
| 2655 | | var lhs_mcv = lhs; |
| 2656 | | var rhs_mcv = rhs; |
| 2662 | try self.spillCompareFlagsIfOccupied(); |
| 2663 | self.compare_flags_inst = inst; |
| 2657 | 2664 | |
| 2658 | | // Allocate registers |
| 2659 | | if (rhs_should_be_register) { |
| 2660 | | if (!lhs_is_register and !rhs_is_register) { |
| 2661 | | const regs = try self.register_manager.allocRegs(2, .{ |
| 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 | | } |
| 2665 | break :result switch (int_info.signedness) { |
| 2666 | .signed => MCValue{ .compare_flags_signed = op }, |
| 2667 | .unsigned => MCValue{ .compare_flags_unsigned = op }, |
| 2668 | }; |
| 2671 | 2669 | } else { |
| 2672 | | if (!lhs_is_register) { |
| 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, |
| 2670 | return self.fail("TODO AArch64 cmp for ints > 64 bits", .{}); |
| 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 | 2673 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2721 | 2674 | } |