| ... | ... | @@ -1271,6 +1271,27 @@ fn asmRegisterRegisterRegister( |
| 1271 | 1271 | }); |
| 1272 | 1272 | } |
| 1273 | 1273 | |
| 1274 | fn asmRegisterRegisterRegisterRegister( |
| 1275 | self: *Self, |
| 1276 | tag: Mir.Inst.FixedTag, |
| 1277 | reg1: Register, |
| 1278 | reg2: Register, |
| 1279 | reg3: Register, |
| 1280 | reg4: Register, |
| 1281 | ) !void { |
| 1282 | _ = try self.addInst(.{ |
| 1283 | .tag = tag[1], |
| 1284 | .ops = .rrrr, |
| 1285 | .data = .{ .rrrr = .{ |
| 1286 | .fixes = tag[0], |
| 1287 | .r1 = reg1, |
| 1288 | .r2 = reg2, |
| 1289 | .r3 = reg3, |
| 1290 | .r4 = reg4, |
| 1291 | } }, |
| 1292 | }); |
| 1293 | } |
| 1294 | |
| 1274 | 1295 | fn asmRegisterRegisterRegisterImmediate( |
| 1275 | 1296 | self: *Self, |
| 1276 | 1297 | tag: Mir.Inst.FixedTag, |
| ... | ... | @@ -2203,6 +2224,10 @@ fn getFrameAddrAlignment(self: *Self, frame_addr: FrameAddr) u32 { |
| 2203 | 2224 | return @min(alloc_align, @bitCast(u32, frame_addr.off) & (alloc_align - 1)); |
| 2204 | 2225 | } |
| 2205 | 2226 | |
| 2227 | fn getFrameAddrSize(self: *Self, frame_addr: FrameAddr) u32 { |
| 2228 | return self.frame_allocs.get(@enumToInt(frame_addr.index)).abi_size - @intCast(u31, frame_addr.off); |
| 2229 | } |
| 2230 | |
| 2206 | 2231 | fn allocFrameIndex(self: *Self, alloc: FrameAlloc) !FrameIndex { |
| 2207 | 2232 | const frame_allocs_slice = self.frame_allocs.slice(); |
| 2208 | 2233 | const frame_size = frame_allocs_slice.items(.abi_size); |
| ... | ... | @@ -2594,115 +2619,202 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void { |
| 2594 | 2619 | |
| 2595 | 2620 | fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 2596 | 2621 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2622 | const result: MCValue = result: { |
| 2623 | const src_ty = self.air.typeOf(ty_op.operand); |
| 2624 | const src_int_info = src_ty.intInfo(self.target.*); |
| 2597 | 2625 | |
| 2598 | | const src_ty = self.air.typeOf(ty_op.operand); |
| 2599 | | const src_int_info = src_ty.intInfo(self.target.*); |
| 2600 | | const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*)); |
| 2601 | | const src_mcv = try self.resolveInst(ty_op.operand); |
| 2602 | | const src_lock = switch (src_mcv) { |
| 2603 | | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2604 | | else => null, |
| 2605 | | }; |
| 2606 | | defer if (src_lock) |lock| self.register_manager.unlockReg(lock); |
| 2626 | const dst_ty = self.air.typeOfIndex(inst); |
| 2627 | const dst_int_info = dst_ty.intInfo(self.target.*); |
| 2628 | const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); |
| 2607 | 2629 | |
| 2608 | | const dst_ty = self.air.typeOfIndex(inst); |
| 2609 | | const dst_int_info = dst_ty.intInfo(self.target.*); |
| 2610 | | const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); |
| 2611 | | const dst_mcv = if (dst_abi_size <= src_abi_size and |
| 2612 | | self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) |
| 2613 | | src_mcv |
| 2614 | | else |
| 2615 | | try self.allocRegOrMem(inst, true); |
| 2630 | const min_ty = if (dst_int_info.bits < src_int_info.bits) dst_ty else src_ty; |
| 2631 | const extend = switch (src_int_info.signedness) { |
| 2632 | .signed => dst_int_info, |
| 2633 | .unsigned => src_int_info, |
| 2634 | }.signedness; |
| 2616 | 2635 | |
| 2617 | | const min_ty = if (dst_int_info.bits < src_int_info.bits) dst_ty else src_ty; |
| 2618 | | const signedness: std.builtin.Signedness = if (dst_int_info.signedness == .signed and |
| 2619 | | src_int_info.signedness == .signed) .signed else .unsigned; |
| 2620 | | switch (dst_mcv) { |
| 2621 | | .register => |dst_reg| { |
| 2622 | | const min_abi_size = @min(dst_abi_size, src_abi_size); |
| 2623 | | const tag: Mir.Inst.FixedTag = switch (signedness) { |
| 2624 | | .signed => if (min_abi_size >= 4) .{ ._d, .movsx } else .{ ._, .movsx }, |
| 2625 | | .unsigned => if (min_abi_size >= 4) .{ ._, .mov } else .{ ._, .movzx }, |
| 2626 | | }; |
| 2627 | | const dst_alias = switch (tag[1]) { |
| 2628 | | .movsx => dst_reg.to64(), |
| 2629 | | .mov, .movzx => if (min_abi_size > 4) dst_reg.to64() else dst_reg.to32(), |
| 2630 | | else => unreachable, |
| 2636 | const src_mcv = try self.resolveInst(ty_op.operand); |
| 2637 | const src_storage_bits = switch (src_mcv) { |
| 2638 | .register, .register_offset => 64, |
| 2639 | .load_frame => |frame_addr| self.getFrameAddrSize(frame_addr) * 8, |
| 2640 | else => src_int_info.bits, |
| 2641 | }; |
| 2642 | |
| 2643 | const dst_mcv = if (dst_int_info.bits <= src_storage_bits and |
| 2644 | self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: { |
| 2645 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 2646 | try self.genCopy(min_ty, dst_mcv, src_mcv); |
| 2647 | break :dst dst_mcv; |
| 2648 | }; |
| 2649 | |
| 2650 | if (dst_int_info.bits <= src_int_info.bits) break :result if (dst_mcv.isRegister()) |
| 2651 | .{ .register = registerAlias(dst_mcv.getReg().?, abi_size) } |
| 2652 | else |
| 2653 | dst_mcv; |
| 2654 | |
| 2655 | if (dst_mcv.isRegister()) { |
| 2656 | try self.truncateRegister(src_ty, dst_mcv.getReg().?); |
| 2657 | break :result .{ .register = registerAlias(dst_mcv.getReg().?, abi_size) }; |
| 2658 | } |
| 2659 | |
| 2660 | const src_limbs_len = std.math.divCeil(u16, src_int_info.bits, 64) catch unreachable; |
| 2661 | const dst_limbs_len = std.math.divCeil(u16, dst_int_info.bits, 64) catch unreachable; |
| 2662 | |
| 2663 | const high_mcv = dst_mcv.address().offset((src_limbs_len - 1) * 8).deref(); |
| 2664 | const high_reg = try self.copyToTmpRegister(switch (src_int_info.signedness) { |
| 2665 | .signed => Type.isize, |
| 2666 | .unsigned => Type.usize, |
| 2667 | }, high_mcv); |
| 2668 | const high_lock = self.register_manager.lockRegAssumeUnused(high_reg); |
| 2669 | defer self.register_manager.unlockReg(high_lock); |
| 2670 | |
| 2671 | const high_bits = src_int_info.bits % 64; |
| 2672 | if (high_bits > 0) { |
| 2673 | var high_pl = Type.Payload.Bits{ |
| 2674 | .base = .{ .tag = switch (extend) { |
| 2675 | .signed => .int_signed, |
| 2676 | .unsigned => .int_unsigned, |
| 2677 | } }, |
| 2678 | .data = high_bits, |
| 2631 | 2679 | }; |
| 2632 | | switch (src_mcv) { |
| 2633 | | .register => |src_reg| { |
| 2634 | | try self.asmRegisterRegister( |
| 2635 | | tag, |
| 2636 | | dst_alias, |
| 2637 | | registerAlias(src_reg, min_abi_size), |
| 2680 | const high_ty = Type.initPayload(&high_pl.base); |
| 2681 | try self.truncateRegister(high_ty, high_reg); |
| 2682 | try self.genCopy(Type.usize, high_mcv, .{ .register = high_reg }); |
| 2683 | } |
| 2684 | |
| 2685 | if (dst_limbs_len > src_limbs_len) try self.genInlineMemset( |
| 2686 | dst_mcv.address().offset(src_limbs_len * 8), |
| 2687 | switch (extend) { |
| 2688 | .signed => extend: { |
| 2689 | const extend_mcv = MCValue{ .register = high_reg }; |
| 2690 | try self.genShiftBinOpMir( |
| 2691 | .{ ._r, .sa }, |
| 2692 | Type.isize, |
| 2693 | extend_mcv, |
| 2694 | .{ .immediate = 63 }, |
| 2638 | 2695 | ); |
| 2696 | break :extend extend_mcv; |
| 2639 | 2697 | }, |
| 2640 | | .memory, .indirect, .load_frame => try self.asmRegisterMemory( |
| 2641 | | tag, |
| 2642 | | dst_alias, |
| 2643 | | src_mcv.mem(Memory.PtrSize.fromSize(min_abi_size)), |
| 2644 | | ), |
| 2645 | | else => return self.fail("TODO airIntCast from {s} to {s}", .{ |
| 2646 | | @tagName(src_mcv), |
| 2647 | | @tagName(dst_mcv), |
| 2648 | | }), |
| 2649 | | } |
| 2650 | | if (self.regExtraBits(min_ty) > 0) try self.truncateRegister(min_ty, dst_reg); |
| 2651 | | }, |
| 2652 | | else => { |
| 2653 | | try self.genCopy(min_ty, dst_mcv, src_mcv); |
| 2654 | | const extra = dst_abi_size * 8 - dst_int_info.bits; |
| 2655 | | if (extra > 0) { |
| 2656 | | try self.genShiftBinOpMir( |
| 2657 | | switch (signedness) { |
| 2658 | | .signed => .{ ._l, .sa }, |
| 2659 | | .unsigned => .{ ._l, .sh }, |
| 2660 | | }, |
| 2661 | | dst_ty, |
| 2662 | | dst_mcv, |
| 2663 | | .{ .immediate = extra }, |
| 2664 | | ); |
| 2665 | | try self.genShiftBinOpMir( |
| 2666 | | switch (signedness) { |
| 2667 | | .signed => .{ ._r, .sa }, |
| 2668 | | .unsigned => .{ ._r, .sh }, |
| 2669 | | }, |
| 2670 | | dst_ty, |
| 2671 | | dst_mcv, |
| 2672 | | .{ .immediate = extra }, |
| 2673 | | ); |
| 2674 | | } |
| 2675 | | }, |
| 2676 | | } |
| 2677 | | return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); |
| 2698 | .unsigned => .{ .immediate = 0 }, |
| 2699 | }, |
| 2700 | .{ .immediate = (dst_limbs_len - src_limbs_len) * 8 }, |
| 2701 | ); |
| 2702 | |
| 2703 | break :result dst_mcv; |
| 2704 | }; |
| 2705 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2678 | 2706 | } |
| 2679 | 2707 | |
| 2680 | 2708 | fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 2681 | 2709 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2682 | 2710 | |
| 2683 | 2711 | const dst_ty = self.air.typeOfIndex(inst); |
| 2684 | | const dst_abi_size = dst_ty.abiSize(self.target.*); |
| 2685 | | if (dst_abi_size > 8) { |
| 2686 | | return self.fail("TODO implement trunc for abi sizes larger than 8", .{}); |
| 2687 | | } |
| 2712 | const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); |
| 2713 | const src_ty = self.air.typeOf(ty_op.operand); |
| 2714 | const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*)); |
| 2688 | 2715 | |
| 2689 | | const src_mcv = try self.resolveInst(ty_op.operand); |
| 2690 | | const src_lock = switch (src_mcv) { |
| 2691 | | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2692 | | else => null, |
| 2693 | | }; |
| 2694 | | defer if (src_lock) |lock| self.register_manager.unlockReg(lock); |
| 2716 | const result = result: { |
| 2717 | const src_mcv = try self.resolveInst(ty_op.operand); |
| 2718 | const src_lock = |
| 2719 | if (src_mcv.getReg()) |reg| self.register_manager.lockRegAssumeUnused(reg) else null; |
| 2720 | defer if (src_lock) |lock| self.register_manager.unlockReg(lock); |
| 2695 | 2721 | |
| 2696 | | const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) |
| 2697 | | src_mcv |
| 2698 | | else |
| 2699 | | try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); |
| 2722 | const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) |
| 2723 | src_mcv |
| 2724 | else |
| 2725 | try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); |
| 2726 | |
| 2727 | if (dst_ty.zigTypeTag() == .Vector) { |
| 2728 | assert(src_ty.zigTypeTag() == .Vector and dst_ty.vectorLen() == src_ty.vectorLen()); |
| 2729 | const dst_info = dst_ty.childType().intInfo(self.target.*); |
| 2730 | const src_info = src_ty.childType().intInfo(self.target.*); |
| 2731 | const mir_tag = if (@as(?Mir.Inst.FixedTag, switch (dst_info.bits) { |
| 2732 | 8 => switch (src_info.bits) { |
| 2733 | 16 => switch (dst_ty.vectorLen()) { |
| 2734 | 1...8 => if (self.hasFeature(.avx)) .{ .vp_b, .ackusw } else .{ .p_b, .ackusw }, |
| 2735 | 9...16 => if (self.hasFeature(.avx2)) .{ .vp_b, .ackusw } else null, |
| 2736 | else => null, |
| 2737 | }, |
| 2738 | else => null, |
| 2739 | }, |
| 2740 | 16 => switch (src_info.bits) { |
| 2741 | 32 => switch (dst_ty.vectorLen()) { |
| 2742 | 1...4 => if (self.hasFeature(.avx)) |
| 2743 | .{ .vp_w, .ackusd } |
| 2744 | else if (self.hasFeature(.sse4_1)) |
| 2745 | .{ .p_w, .ackusd } |
| 2746 | else |
| 2747 | null, |
| 2748 | 5...8 => if (self.hasFeature(.avx2)) .{ .vp_w, .ackusd } else null, |
| 2749 | else => null, |
| 2750 | }, |
| 2751 | else => null, |
| 2752 | }, |
| 2753 | else => null, |
| 2754 | })) |tag| tag else return self.fail("TODO implement airTrunc for {}", .{ |
| 2755 | dst_ty.fmt(self.bin_file.options.module.?), |
| 2756 | }); |
| 2700 | 2757 | |
| 2701 | | // when truncating a `u16` to `u5`, for example, those top 3 bits in the result |
| 2702 | | // have to be removed. this only happens if the dst if not a power-of-two size. |
| 2703 | | if (self.regExtraBits(dst_ty) > 0) try self.truncateRegister(dst_ty, dst_mcv.register.to64()); |
| 2758 | var mask_pl = Value.Payload.U64{ |
| 2759 | .base = .{ .tag = .int_u64 }, |
| 2760 | .data = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - dst_info.bits), |
| 2761 | }; |
| 2762 | const mask_val = Value.initPayload(&mask_pl.base); |
| 2704 | 2763 | |
| 2705 | | return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); |
| 2764 | var splat_pl = Value.Payload.SubValue{ |
| 2765 | .base = .{ .tag = .repeated }, |
| 2766 | .data = mask_val, |
| 2767 | }; |
| 2768 | const splat_val = Value.initPayload(&splat_pl.base); |
| 2769 | |
| 2770 | var full_pl = Type.Payload.Array{ |
| 2771 | .base = .{ .tag = .vector }, |
| 2772 | .data = .{ |
| 2773 | .len = @divExact(@as(u64, if (src_abi_size > 16) 256 else 128), src_info.bits), |
| 2774 | .elem_type = src_ty.childType(), |
| 2775 | }, |
| 2776 | }; |
| 2777 | const full_ty = Type.initPayload(&full_pl.base); |
| 2778 | const full_abi_size = @intCast(u32, full_ty.abiSize(self.target.*)); |
| 2779 | |
| 2780 | const splat_mcv = try self.genTypedValue(.{ .ty = full_ty, .val = splat_val }); |
| 2781 | const splat_addr_mcv: MCValue = switch (splat_mcv) { |
| 2782 | .memory, .indirect, .load_frame => splat_mcv.address(), |
| 2783 | else => .{ .register = try self.copyToTmpRegister(Type.usize, splat_mcv.address()) }, |
| 2784 | }; |
| 2785 | |
| 2786 | const dst_reg = registerAlias(dst_mcv.getReg().?, src_abi_size); |
| 2787 | if (self.hasFeature(.avx)) { |
| 2788 | try self.asmRegisterRegisterMemory( |
| 2789 | .{ .vp_, .@"and" }, |
| 2790 | dst_reg, |
| 2791 | dst_reg, |
| 2792 | splat_addr_mcv.deref().mem(Memory.PtrSize.fromSize(full_abi_size)), |
| 2793 | ); |
| 2794 | try self.asmRegisterRegisterRegister(mir_tag, dst_reg, dst_reg, dst_reg); |
| 2795 | } else { |
| 2796 | try self.asmRegisterMemory( |
| 2797 | .{ .p_, .@"and" }, |
| 2798 | dst_reg, |
| 2799 | splat_addr_mcv.deref().mem(Memory.PtrSize.fromSize(full_abi_size)), |
| 2800 | ); |
| 2801 | try self.asmRegisterRegister(mir_tag, dst_reg, dst_reg); |
| 2802 | } |
| 2803 | break :result dst_mcv; |
| 2804 | } |
| 2805 | |
| 2806 | if (dst_abi_size > 8) { |
| 2807 | return self.fail("TODO implement trunc for abi sizes larger than 8", .{}); |
| 2808 | } |
| 2809 | |
| 2810 | // when truncating a `u16` to `u5`, for example, those top 3 bits in the result |
| 2811 | // have to be removed. this only happens if the dst if not a power-of-two size. |
| 2812 | if (self.regExtraBits(dst_ty) > 0) |
| 2813 | try self.truncateRegister(dst_ty, dst_mcv.register.to64()); |
| 2814 | |
| 2815 | break :result dst_mcv; |
| 2816 | }; |
| 2817 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2706 | 2818 | } |
| 2707 | 2819 | |
| 2708 | 2820 | fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -2855,20 +2967,43 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { |
| 2855 | 2967 | defer self.register_manager.unlockReg(limit_lock); |
| 2856 | 2968 | |
| 2857 | 2969 | const reg_bits = self.regBitSize(ty); |
| 2970 | const reg_extra_bits = self.regExtraBits(ty); |
| 2858 | 2971 | const cc: Condition = if (ty.isSignedInt()) cc: { |
| 2972 | if (reg_extra_bits > 0) { |
| 2973 | try self.genShiftBinOpMir(.{ ._l, .sa }, ty, dst_mcv, .{ .immediate = reg_extra_bits }); |
| 2974 | } |
| 2859 | 2975 | try self.genSetReg(limit_reg, ty, dst_mcv); |
| 2860 | 2976 | try self.genShiftBinOpMir(.{ ._r, .sa }, ty, limit_mcv, .{ .immediate = reg_bits - 1 }); |
| 2861 | 2977 | try self.genBinOpMir(.{ ._, .xor }, ty, limit_mcv, .{ |
| 2862 | 2978 | .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1, |
| 2863 | 2979 | }); |
| 2980 | if (reg_extra_bits > 0) { |
| 2981 | const shifted_rhs_reg = try self.copyToTmpRegister(ty, rhs_mcv); |
| 2982 | const shifted_rhs_mcv = MCValue{ .register = shifted_rhs_reg }; |
| 2983 | const shifted_rhs_lock = self.register_manager.lockRegAssumeUnused(shifted_rhs_reg); |
| 2984 | defer self.register_manager.unlockReg(shifted_rhs_lock); |
| 2985 | |
| 2986 | try self.genShiftBinOpMir( |
| 2987 | .{ ._l, .sa }, |
| 2988 | ty, |
| 2989 | shifted_rhs_mcv, |
| 2990 | .{ .immediate = reg_extra_bits }, |
| 2991 | ); |
| 2992 | try self.genBinOpMir(.{ ._, .add }, ty, dst_mcv, shifted_rhs_mcv); |
| 2993 | } else try self.genBinOpMir(.{ ._, .add }, ty, dst_mcv, rhs_mcv); |
| 2864 | 2994 | break :cc .o; |
| 2865 | 2995 | } else cc: { |
| 2866 | 2996 | try self.genSetReg(limit_reg, ty, .{ |
| 2867 | | .immediate = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits), |
| 2997 | .immediate = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - ty.bitSize(self.target.*)), |
| 2868 | 2998 | }); |
| 2999 | |
| 3000 | try self.genBinOpMir(.{ ._, .add }, ty, dst_mcv, rhs_mcv); |
| 3001 | if (reg_extra_bits > 0) { |
| 3002 | try self.genBinOpMir(.{ ._, .cmp }, ty, dst_mcv, limit_mcv); |
| 3003 | break :cc .a; |
| 3004 | } |
| 2869 | 3005 | break :cc .c; |
| 2870 | 3006 | }; |
| 2871 | | try self.genBinOpMir(.{ ._, .add }, ty, dst_mcv, rhs_mcv); |
| 2872 | 3007 | |
| 2873 | 3008 | const cmov_abi_size = @max(@intCast(u32, ty.abiSize(self.target.*)), 2); |
| 2874 | 3009 | try self.asmCmovccRegisterRegister( |
| ... | ... | @@ -2877,6 +3012,10 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { |
| 2877 | 3012 | cc, |
| 2878 | 3013 | ); |
| 2879 | 3014 | |
| 3015 | if (reg_extra_bits > 0 and ty.isSignedInt()) { |
| 3016 | try self.genShiftBinOpMir(.{ ._r, .sa }, ty, dst_mcv, .{ .immediate = reg_extra_bits }); |
| 3017 | } |
| 3018 | |
| 2880 | 3019 | return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2881 | 3020 | } |
| 2882 | 3021 | |
| ... | ... | @@ -2906,18 +3045,36 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { |
| 2906 | 3045 | defer self.register_manager.unlockReg(limit_lock); |
| 2907 | 3046 | |
| 2908 | 3047 | const reg_bits = self.regBitSize(ty); |
| 3048 | const reg_extra_bits = self.regExtraBits(ty); |
| 2909 | 3049 | const cc: Condition = if (ty.isSignedInt()) cc: { |
| 3050 | if (reg_extra_bits > 0) { |
| 3051 | try self.genShiftBinOpMir(.{ ._l, .sa }, ty, dst_mcv, .{ .immediate = reg_extra_bits }); |
| 3052 | } |
| 2910 | 3053 | try self.genSetReg(limit_reg, ty, dst_mcv); |
| 2911 | 3054 | try self.genShiftBinOpMir(.{ ._r, .sa }, ty, limit_mcv, .{ .immediate = reg_bits - 1 }); |
| 2912 | 3055 | try self.genBinOpMir(.{ ._, .xor }, ty, limit_mcv, .{ |
| 2913 | 3056 | .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1, |
| 2914 | 3057 | }); |
| 3058 | if (reg_extra_bits > 0) { |
| 3059 | const shifted_rhs_reg = try self.copyToTmpRegister(ty, rhs_mcv); |
| 3060 | const shifted_rhs_mcv = MCValue{ .register = shifted_rhs_reg }; |
| 3061 | const shifted_rhs_lock = self.register_manager.lockRegAssumeUnused(shifted_rhs_reg); |
| 3062 | defer self.register_manager.unlockReg(shifted_rhs_lock); |
| 3063 | |
| 3064 | try self.genShiftBinOpMir( |
| 3065 | .{ ._l, .sa }, |
| 3066 | ty, |
| 3067 | shifted_rhs_mcv, |
| 3068 | .{ .immediate = reg_extra_bits }, |
| 3069 | ); |
| 3070 | try self.genBinOpMir(.{ ._, .sub }, ty, dst_mcv, shifted_rhs_mcv); |
| 3071 | } else try self.genBinOpMir(.{ ._, .sub }, ty, dst_mcv, rhs_mcv); |
| 2915 | 3072 | break :cc .o; |
| 2916 | 3073 | } else cc: { |
| 2917 | 3074 | try self.genSetReg(limit_reg, ty, .{ .immediate = 0 }); |
| 3075 | try self.genBinOpMir(.{ ._, .sub }, ty, dst_mcv, rhs_mcv); |
| 2918 | 3076 | break :cc .c; |
| 2919 | 3077 | }; |
| 2920 | | try self.genBinOpMir(.{ ._, .sub }, ty, dst_mcv, rhs_mcv); |
| 2921 | 3078 | |
| 2922 | 3079 | const cmov_abi_size = @max(@intCast(u32, ty.abiSize(self.target.*)), 2); |
| 2923 | 3080 | try self.asmCmovccRegisterRegister( |
| ... | ... | @@ -2926,6 +3083,10 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { |
| 2926 | 3083 | cc, |
| 2927 | 3084 | ); |
| 2928 | 3085 | |
| 3086 | if (reg_extra_bits > 0 and ty.isSignedInt()) { |
| 3087 | try self.genShiftBinOpMir(.{ ._r, .sa }, ty, dst_mcv, .{ .immediate = reg_extra_bits }); |
| 3088 | } |
| 3089 | |
| 2929 | 3090 | return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2930 | 3091 | } |
| 2931 | 3092 | |
| ... | ... | @@ -3222,34 +3383,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 3222 | 3383 | self.regExtraBits(dst_ty) |
| 3223 | 3384 | else |
| 3224 | 3385 | dst_info.bits % 64; |
| 3225 | | const partial_mcv = if (dst_info.signedness == .signed and extra_bits > 0) dst: { |
| 3226 | | const rhs_lock: ?RegisterLock = switch (rhs) { |
| 3227 | | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 3228 | | else => null, |
| 3229 | | }; |
| 3230 | | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 3231 | | |
| 3232 | | const dst_reg: Register = blk: { |
| 3233 | | if (lhs.isRegister()) break :blk lhs.register; |
| 3234 | | break :blk try self.copyToTmpRegister(dst_ty, lhs); |
| 3235 | | }; |
| 3236 | | const dst_mcv = MCValue{ .register = dst_reg }; |
| 3237 | | const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); |
| 3238 | | defer self.register_manager.unlockReg(dst_reg_lock); |
| 3239 | | |
| 3240 | | const rhs_mcv: MCValue = blk: { |
| 3241 | | if (rhs.isRegister() or rhs.isMemory()) break :blk rhs; |
| 3242 | | break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) }; |
| 3243 | | }; |
| 3244 | | const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) { |
| 3245 | | .register => |reg| self.register_manager.lockReg(reg), |
| 3246 | | else => null, |
| 3247 | | }; |
| 3248 | | defer if (rhs_mcv_lock) |lock| self.register_manager.unlockReg(lock); |
| 3249 | | |
| 3250 | | try self.genIntMulComplexOpMir(Type.isize, dst_mcv, rhs_mcv); |
| 3251 | | break :dst dst_mcv; |
| 3252 | | } else try self.genMulDivBinOp(.mul, null, dst_ty, src_ty, lhs, rhs); |
| 3386 | const partial_mcv = try self.genMulDivBinOp(.mul, null, dst_ty, src_ty, lhs, rhs); |
| 3253 | 3387 | |
| 3254 | 3388 | switch (partial_mcv) { |
| 3255 | 3389 | .register => |reg| if (extra_bits == 0) { |
| ... | ... | @@ -3262,9 +3396,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 3262 | 3396 | break :result .{ .load_frame = .{ .index = frame_index } }; |
| 3263 | 3397 | }, |
| 3264 | 3398 | else => { |
| 3265 | | // For now, this is the only supported multiply that doesn't fit in a register, |
| 3266 | | // so cc being set is impossible. |
| 3267 | | |
| 3399 | // For now, this is the only supported multiply that doesn't fit in a register. |
| 3268 | 3400 | assert(dst_info.bits <= 128 and src_pl.data == 64); |
| 3269 | 3401 | |
| 3270 | 3402 | const frame_index = |
| ... | ... | @@ -3280,7 +3412,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 3280 | 3412 | .{ .frame = frame_index }, |
| 3281 | 3413 | @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)), |
| 3282 | 3414 | tuple_ty.structFieldType(1), |
| 3283 | | .{ .immediate = 0 }, |
| 3415 | .{ .immediate = 0 }, // cc being set is impossible |
| 3284 | 3416 | ); |
| 3285 | 3417 | } else try self.genSetFrameTruncatedOverflowCompare( |
| 3286 | 3418 | tuple_ty, |
| ... | ... | @@ -5558,31 +5690,13 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 5558 | 5690 | const dst_lock = self.register_manager.lockReg(dst_reg); |
| 5559 | 5691 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 5560 | 5692 | |
| 5561 | | // Shift by struct_field_offset. |
| 5562 | 5693 | try self.genShiftBinOpMir( |
| 5563 | 5694 | .{ ._r, .sh }, |
| 5564 | 5695 | Type.usize, |
| 5565 | 5696 | dst_mcv, |
| 5566 | 5697 | .{ .immediate = field_off }, |
| 5567 | 5698 | ); |
| 5568 | | |
| 5569 | | // Mask to field_bit_size bits |
| 5570 | | const field_bit_size = field_ty.bitSize(self.target.*); |
| 5571 | | const mask = ~@as(u64, 0) >> @intCast(u6, 64 - field_bit_size); |
| 5572 | | |
| 5573 | | const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); |
| 5574 | | try self.genBinOpMir(.{ ._, .@"and" }, Type.usize, dst_mcv, .{ .register = tmp_reg }); |
| 5575 | | |
| 5576 | | const signedness = |
| 5577 | | if (field_ty.isAbiInt()) field_ty.intInfo(self.target.*).signedness else .unsigned; |
| 5578 | | const field_byte_size = @intCast(u32, field_ty.abiSize(self.target.*)); |
| 5579 | | if (signedness == .signed and field_byte_size < 8) { |
| 5580 | | try self.asmRegisterRegister( |
| 5581 | | if (field_byte_size >= 4) .{ ._d, .movsx } else .{ ._, .movsx }, |
| 5582 | | dst_mcv.register, |
| 5583 | | registerAlias(dst_mcv.register, field_byte_size), |
| 5584 | | ); |
| 5585 | | } |
| 5699 | if (self.regExtraBits(field_ty) > 0) try self.truncateRegister(field_ty, dst_reg); |
| 5586 | 5700 | |
| 5587 | 5701 | break :result if (field_rc.supersetOf(gp)) |
| 5588 | 5702 | dst_mcv |
| ... | ... | @@ -6224,12 +6338,26 @@ fn genBinOp( |
| 6224 | 6338 | lhs_air: Air.Inst.Ref, |
| 6225 | 6339 | rhs_air: Air.Inst.Ref, |
| 6226 | 6340 | ) !MCValue { |
| 6227 | | const lhs_mcv = try self.resolveInst(lhs_air); |
| 6228 | | const rhs_mcv = try self.resolveInst(rhs_air); |
| 6229 | 6341 | const lhs_ty = self.air.typeOf(lhs_air); |
| 6230 | 6342 | const rhs_ty = self.air.typeOf(rhs_air); |
| 6231 | 6343 | const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*)); |
| 6232 | 6344 | |
| 6345 | const maybe_mask_reg = switch (air_tag) { |
| 6346 | else => null, |
| 6347 | .max, .min => if (lhs_ty.scalarType().isRuntimeFloat()) registerAlias( |
| 6348 | if (!self.hasFeature(.avx) and self.hasFeature(.sse4_1)) mask: { |
| 6349 | try self.register_manager.getReg(.xmm0, null); |
| 6350 | break :mask .xmm0; |
| 6351 | } else try self.register_manager.allocReg(null, sse), |
| 6352 | abi_size, |
| 6353 | ) else null, |
| 6354 | }; |
| 6355 | const mask_lock = |
| 6356 | if (maybe_mask_reg) |mask_reg| self.register_manager.lockRegAssumeUnused(mask_reg) else null; |
| 6357 | defer if (mask_lock) |lock| self.register_manager.unlockReg(lock); |
| 6358 | |
| 6359 | const lhs_mcv = try self.resolveInst(lhs_air); |
| 6360 | const rhs_mcv = try self.resolveInst(rhs_air); |
| 6233 | 6361 | switch (lhs_mcv) { |
| 6234 | 6362 | .immediate => |imm| switch (imm) { |
| 6235 | 6363 | 0 => switch (air_tag) { |
| ... | ... | @@ -6300,7 +6428,16 @@ fn genBinOp( |
| 6300 | 6428 | }; |
| 6301 | 6429 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 6302 | 6430 | |
| 6303 | | const src_mcv = if (flipped) lhs_mcv else rhs_mcv; |
| 6431 | const unmat_src_mcv = if (flipped) lhs_mcv else rhs_mcv; |
| 6432 | const src_mcv: MCValue = if (maybe_mask_reg) |mask_reg| |
| 6433 | if (self.hasFeature(.avx) and unmat_src_mcv.isRegister() and maybe_inst != null and |
| 6434 | self.liveness.operandDies(maybe_inst.?, if (flipped) 0 else 1)) unmat_src_mcv else src: { |
| 6435 | try self.genSetReg(mask_reg, rhs_ty, unmat_src_mcv); |
| 6436 | break :src .{ .register = mask_reg }; |
| 6437 | } |
| 6438 | else |
| 6439 | unmat_src_mcv; |
| 6440 | |
| 6304 | 6441 | if (!vec_op) { |
| 6305 | 6442 | switch (air_tag) { |
| 6306 | 6443 | .add, |
| ... | ... | @@ -7009,18 +7146,26 @@ fn genBinOp( |
| 7009 | 7146 | })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7010 | 7147 | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7011 | 7148 | }); |
| 7149 | |
| 7150 | const lhs_copy_reg = if (maybe_mask_reg) |_| registerAlias( |
| 7151 | if (copied_to_dst) try self.copyToTmpRegister(lhs_ty, dst_mcv) else lhs_mcv.getReg().?, |
| 7152 | abi_size, |
| 7153 | ) else null; |
| 7154 | const lhs_copy_lock = if (lhs_copy_reg) |reg| self.register_manager.lockReg(reg) else null; |
| 7155 | defer if (lhs_copy_lock) |lock| self.register_manager.unlockReg(lock); |
| 7156 | |
| 7012 | 7157 | if (self.hasFeature(.avx)) { |
| 7013 | | const src1_alias = |
| 7158 | const lhs_reg = |
| 7014 | 7159 | if (copied_to_dst) dst_reg else registerAlias(lhs_mcv.getReg().?, abi_size); |
| 7015 | 7160 | if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory( |
| 7016 | 7161 | mir_tag, |
| 7017 | 7162 | dst_reg, |
| 7018 | | src1_alias, |
| 7163 | lhs_reg, |
| 7019 | 7164 | src_mcv.mem(Memory.PtrSize.fromSize(abi_size)), |
| 7020 | 7165 | ) else try self.asmRegisterRegisterRegister( |
| 7021 | 7166 | mir_tag, |
| 7022 | 7167 | dst_reg, |
| 7023 | | src1_alias, |
| 7168 | lhs_reg, |
| 7024 | 7169 | registerAlias(if (src_mcv.isRegister()) |
| 7025 | 7170 | src_mcv.getReg().? |
| 7026 | 7171 | else |
| ... | ... | @@ -7041,9 +7186,10 @@ fn genBinOp( |
| 7041 | 7186 | try self.copyToTmpRegister(rhs_ty, src_mcv), abi_size), |
| 7042 | 7187 | ); |
| 7043 | 7188 | } |
| 7189 | |
| 7044 | 7190 | switch (air_tag) { |
| 7045 | 7191 | .add, .addwrap, .sub, .subwrap, .mul, .mulwrap, .div_float, .div_exact => {}, |
| 7046 | | .div_trunc, .div_floor => try self.genRound( |
| 7192 | .div_trunc, .div_floor => if (self.hasFeature(.sse4_1)) try self.genRound( |
| 7047 | 7193 | lhs_ty, |
| 7048 | 7194 | dst_reg, |
| 7049 | 7195 | .{ .register = dst_reg }, |
| ... | ... | @@ -7052,11 +7198,240 @@ fn genBinOp( |
| 7052 | 7198 | .div_floor => 0b1_0_01, |
| 7053 | 7199 | else => unreachable, |
| 7054 | 7200 | }, |
| 7055 | | ), |
| 7201 | ) else return self.fail("TODO implement genBinOp for {s} {} without sse4_1 feature", .{ |
| 7202 | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7203 | }), |
| 7056 | 7204 | .bit_and, .bit_or, .xor => {}, |
| 7057 | | .max, .min => {}, // TODO: unordered select |
| 7205 | .max, .min => if (maybe_mask_reg) |mask_reg| if (self.hasFeature(.avx)) { |
| 7206 | const rhs_copy_reg = registerAlias(src_mcv.getReg().?, abi_size); |
| 7207 | |
| 7208 | try self.asmRegisterRegisterRegisterImmediate( |
| 7209 | if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) { |
| 7210 | .Float => switch (lhs_ty.floatBits(self.target.*)) { |
| 7211 | 32 => .{ .v_ss, .cmp }, |
| 7212 | 64 => .{ .v_sd, .cmp }, |
| 7213 | 16, 80, 128 => null, |
| 7214 | else => unreachable, |
| 7215 | }, |
| 7216 | .Vector => switch (lhs_ty.childType().zigTypeTag()) { |
| 7217 | .Float => switch (lhs_ty.childType().floatBits(self.target.*)) { |
| 7218 | 32 => switch (lhs_ty.vectorLen()) { |
| 7219 | 1 => .{ .v_ss, .cmp }, |
| 7220 | 2...8 => .{ .v_ps, .cmp }, |
| 7221 | else => null, |
| 7222 | }, |
| 7223 | 64 => switch (lhs_ty.vectorLen()) { |
| 7224 | 1 => .{ .v_sd, .cmp }, |
| 7225 | 2...4 => .{ .v_pd, .cmp }, |
| 7226 | else => null, |
| 7227 | }, |
| 7228 | 16, 80, 128 => null, |
| 7229 | else => unreachable, |
| 7230 | }, |
| 7231 | else => unreachable, |
| 7232 | }, |
| 7233 | else => unreachable, |
| 7234 | })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7235 | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7236 | }), |
| 7237 | mask_reg, |
| 7238 | rhs_copy_reg, |
| 7239 | rhs_copy_reg, |
| 7240 | Immediate.u(3), // unord |
| 7241 | ); |
| 7242 | try self.asmRegisterRegisterRegisterRegister( |
| 7243 | if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) { |
| 7244 | .Float => switch (lhs_ty.floatBits(self.target.*)) { |
| 7245 | 32 => .{ .v_ps, .blendv }, |
| 7246 | 64 => .{ .v_pd, .blendv }, |
| 7247 | 16, 80, 128 => null, |
| 7248 | else => unreachable, |
| 7249 | }, |
| 7250 | .Vector => switch (lhs_ty.childType().zigTypeTag()) { |
| 7251 | .Float => switch (lhs_ty.childType().floatBits(self.target.*)) { |
| 7252 | 32 => switch (lhs_ty.vectorLen()) { |
| 7253 | 1...8 => .{ .v_ps, .blendv }, |
| 7254 | else => null, |
| 7255 | }, |
| 7256 | 64 => switch (lhs_ty.vectorLen()) { |
| 7257 | 1...4 => .{ .v_pd, .blendv }, |
| 7258 | else => null, |
| 7259 | }, |
| 7260 | 16, 80, 128 => null, |
| 7261 | else => unreachable, |
| 7262 | }, |
| 7263 | else => unreachable, |
| 7264 | }, |
| 7265 | else => unreachable, |
| 7266 | })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7267 | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7268 | }), |
| 7269 | dst_reg, |
| 7270 | dst_reg, |
| 7271 | lhs_copy_reg.?, |
| 7272 | mask_reg, |
| 7273 | ); |
| 7274 | } else { |
| 7275 | const has_blend = self.hasFeature(.sse4_1); |
| 7276 | try self.asmRegisterRegisterImmediate( |
| 7277 | if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) { |
| 7278 | .Float => switch (lhs_ty.floatBits(self.target.*)) { |
| 7279 | 32 => .{ ._ss, .cmp }, |
| 7280 | 64 => .{ ._sd, .cmp }, |
| 7281 | 16, 80, 128 => null, |
| 7282 | else => unreachable, |
| 7283 | }, |
| 7284 | .Vector => switch (lhs_ty.childType().zigTypeTag()) { |
| 7285 | .Float => switch (lhs_ty.childType().floatBits(self.target.*)) { |
| 7286 | 32 => switch (lhs_ty.vectorLen()) { |
| 7287 | 1 => .{ ._ss, .cmp }, |
| 7288 | 2...4 => .{ ._ps, .cmp }, |
| 7289 | else => null, |
| 7290 | }, |
| 7291 | 64 => switch (lhs_ty.vectorLen()) { |
| 7292 | 1 => .{ ._sd, .cmp }, |
| 7293 | 2 => .{ ._pd, .cmp }, |
| 7294 | else => null, |
| 7295 | }, |
| 7296 | 16, 80, 128 => null, |
| 7297 | else => unreachable, |
| 7298 | }, |
| 7299 | else => unreachable, |
| 7300 | }, |
| 7301 | else => unreachable, |
| 7302 | })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7303 | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7304 | }), |
| 7305 | mask_reg, |
| 7306 | mask_reg, |
| 7307 | Immediate.u(if (has_blend) 3 else 7), // unord, ord |
| 7308 | ); |
| 7309 | if (has_blend) try self.asmRegisterRegisterRegister( |
| 7310 | if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) { |
| 7311 | .Float => switch (lhs_ty.floatBits(self.target.*)) { |
| 7312 | 32 => .{ ._ps, .blendv }, |
| 7313 | 64 => .{ ._pd, .blendv }, |
| 7314 | 16, 80, 128 => null, |
| 7315 | else => unreachable, |
| 7316 | }, |
| 7317 | .Vector => switch (lhs_ty.childType().zigTypeTag()) { |
| 7318 | .Float => switch (lhs_ty.childType().floatBits(self.target.*)) { |
| 7319 | 32 => switch (lhs_ty.vectorLen()) { |
| 7320 | 1...4 => .{ ._ps, .blendv }, |
| 7321 | else => null, |
| 7322 | }, |
| 7323 | 64 => switch (lhs_ty.vectorLen()) { |
| 7324 | 1...2 => .{ ._pd, .blendv }, |
| 7325 | else => null, |
| 7326 | }, |
| 7327 | 16, 80, 128 => null, |
| 7328 | else => unreachable, |
| 7329 | }, |
| 7330 | else => unreachable, |
| 7331 | }, |
| 7332 | else => unreachable, |
| 7333 | })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7334 | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7335 | }), |
| 7336 | dst_reg, |
| 7337 | lhs_copy_reg.?, |
| 7338 | mask_reg, |
| 7339 | ) else { |
| 7340 | try self.asmRegisterRegister( |
| 7341 | if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) { |
| 7342 | .Float => switch (lhs_ty.floatBits(self.target.*)) { |
| 7343 | 32 => .{ ._ps, .@"and" }, |
| 7344 | 64 => .{ ._pd, .@"and" }, |
| 7345 | 16, 80, 128 => null, |
| 7346 | else => unreachable, |
| 7347 | }, |
| 7348 | .Vector => switch (lhs_ty.childType().zigTypeTag()) { |
| 7349 | .Float => switch (lhs_ty.childType().floatBits(self.target.*)) { |
| 7350 | 32 => switch (lhs_ty.vectorLen()) { |
| 7351 | 1...4 => .{ ._ps, .@"and" }, |
| 7352 | else => null, |
| 7353 | }, |
| 7354 | 64 => switch (lhs_ty.vectorLen()) { |
| 7355 | 1...2 => .{ ._pd, .@"and" }, |
| 7356 | else => null, |
| 7357 | }, |
| 7358 | 16, 80, 128 => null, |
| 7359 | else => unreachable, |
| 7360 | }, |
| 7361 | else => unreachable, |
| 7362 | }, |
| 7363 | else => unreachable, |
| 7364 | })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7365 | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7366 | }), |
| 7367 | dst_reg, |
| 7368 | mask_reg, |
| 7369 | ); |
| 7370 | try self.asmRegisterRegister( |
| 7371 | if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) { |
| 7372 | .Float => switch (lhs_ty.floatBits(self.target.*)) { |
| 7373 | 32 => .{ ._ps, .andn }, |
| 7374 | 64 => .{ ._pd, .andn }, |
| 7375 | 16, 80, 128 => null, |
| 7376 | else => unreachable, |
| 7377 | }, |
| 7378 | .Vector => switch (lhs_ty.childType().zigTypeTag()) { |
| 7379 | .Float => switch (lhs_ty.childType().floatBits(self.target.*)) { |
| 7380 | 32 => switch (lhs_ty.vectorLen()) { |
| 7381 | 1...4 => .{ ._ps, .andn }, |
| 7382 | else => null, |
| 7383 | }, |
| 7384 | 64 => switch (lhs_ty.vectorLen()) { |
| 7385 | 1...2 => .{ ._pd, .andn }, |
| 7386 | else => null, |
| 7387 | }, |
| 7388 | 16, 80, 128 => null, |
| 7389 | else => unreachable, |
| 7390 | }, |
| 7391 | else => unreachable, |
| 7392 | }, |
| 7393 | else => unreachable, |
| 7394 | })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7395 | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7396 | }), |
| 7397 | mask_reg, |
| 7398 | lhs_copy_reg.?, |
| 7399 | ); |
| 7400 | try self.asmRegisterRegister( |
| 7401 | if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag()) { |
| 7402 | .Float => switch (lhs_ty.floatBits(self.target.*)) { |
| 7403 | 32 => .{ ._ps, .@"or" }, |
| 7404 | 64 => .{ ._pd, .@"or" }, |
| 7405 | 16, 80, 128 => null, |
| 7406 | else => unreachable, |
| 7407 | }, |
| 7408 | .Vector => switch (lhs_ty.childType().zigTypeTag()) { |
| 7409 | .Float => switch (lhs_ty.childType().floatBits(self.target.*)) { |
| 7410 | 32 => switch (lhs_ty.vectorLen()) { |
| 7411 | 1...4 => .{ ._ps, .@"or" }, |
| 7412 | else => null, |
| 7413 | }, |
| 7414 | 64 => switch (lhs_ty.vectorLen()) { |
| 7415 | 1...2 => .{ ._pd, .@"or" }, |
| 7416 | else => null, |
| 7417 | }, |
| 7418 | 16, 80, 128 => null, |
| 7419 | else => unreachable, |
| 7420 | }, |
| 7421 | else => unreachable, |
| 7422 | }, |
| 7423 | else => unreachable, |
| 7424 | })) |tag| tag else return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7425 | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7426 | }), |
| 7427 | dst_reg, |
| 7428 | mask_reg, |
| 7429 | ); |
| 7430 | } |
| 7431 | }, |
| 7058 | 7432 | else => unreachable, |
| 7059 | 7433 | } |
| 7434 | |
| 7060 | 7435 | return dst_mcv; |
| 7061 | 7436 | } |
| 7062 | 7437 | |
| ... | ... | @@ -7882,24 +8257,105 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 7882 | 8257 | const result = MCValue{ |
| 7883 | 8258 | .eflags = switch (ty.zigTypeTag()) { |
| 7884 | 8259 | else => result: { |
| 7885 | | var flipped = false; |
| 7886 | | const dst_mcv: MCValue = if (lhs_mcv.isRegister() or lhs_mcv.isMemory()) |
| 7887 | | lhs_mcv |
| 7888 | | else if (rhs_mcv.isRegister() or rhs_mcv.isMemory()) dst: { |
| 7889 | | flipped = true; |
| 7890 | | break :dst rhs_mcv; |
| 7891 | | } else .{ .register = try self.copyToTmpRegister(ty, lhs_mcv) }; |
| 7892 | | const dst_lock = switch (dst_mcv) { |
| 7893 | | .register => |reg| self.register_manager.lockReg(reg), |
| 7894 | | else => null, |
| 8260 | const abi_size = @intCast(u16, ty.abiSize(self.target.*)); |
| 8261 | const may_flip: enum { |
| 8262 | may_flip, |
| 8263 | must_flip, |
| 8264 | must_not_flip, |
| 8265 | } = if (abi_size > 8) switch (op) { |
| 8266 | .lt, .gte => .must_not_flip, |
| 8267 | .lte, .gt => .must_flip, |
| 8268 | .eq, .neq => .may_flip, |
| 8269 | } else .may_flip; |
| 8270 | |
| 8271 | const flipped = switch (may_flip) { |
| 8272 | .may_flip => !lhs_mcv.isRegister() and !lhs_mcv.isMemory(), |
| 8273 | .must_flip => true, |
| 8274 | .must_not_flip => false, |
| 8275 | }; |
| 8276 | const unmat_dst_mcv = if (flipped) rhs_mcv else lhs_mcv; |
| 8277 | const dst_mcv = if (unmat_dst_mcv.isRegister() or |
| 8278 | (abi_size <= 8 and unmat_dst_mcv.isMemory())) unmat_dst_mcv else dst: { |
| 8279 | const dst_mcv = try self.allocTempRegOrMem(ty, true); |
| 8280 | try self.genCopy(ty, dst_mcv, unmat_dst_mcv); |
| 8281 | break :dst dst_mcv; |
| 7895 | 8282 | }; |
| 8283 | const dst_lock = |
| 8284 | if (dst_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; |
| 7896 | 8285 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 8286 | |
| 7897 | 8287 | const src_mcv = if (flipped) lhs_mcv else rhs_mcv; |
| 8288 | const src_lock = |
| 8289 | if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; |
| 8290 | defer if (src_lock) |lock| self.register_manager.unlockReg(lock); |
| 7898 | 8291 | |
| 7899 | | try self.genBinOpMir(.{ ._, .cmp }, ty, dst_mcv, src_mcv); |
| 7900 | 8292 | break :result Condition.fromCompareOperator( |
| 7901 | 8293 | if (ty.isAbiInt()) ty.intInfo(self.target.*).signedness else .unsigned, |
| 7902 | | if (flipped) op.reverse() else op, |
| 8294 | result_op: { |
| 8295 | const flipped_op = if (flipped) op.reverse() else op; |
| 8296 | if (abi_size > 8) switch (flipped_op) { |
| 8297 | .lt, .gte => {}, |
| 8298 | .lte, .gt => unreachable, |
| 8299 | .eq, .neq => { |
| 8300 | const dst_addr_mcv: MCValue = switch (dst_mcv) { |
| 8301 | .memory, .indirect, .load_frame => dst_mcv.address(), |
| 8302 | else => .{ .register = try self.copyToTmpRegister( |
| 8303 | Type.usize, |
| 8304 | dst_mcv.address(), |
| 8305 | ) }, |
| 8306 | }; |
| 8307 | const dst_addr_lock = if (dst_addr_mcv.getReg()) |reg| |
| 8308 | self.register_manager.lockReg(reg) |
| 8309 | else |
| 8310 | null; |
| 8311 | defer if (dst_addr_lock) |lock| self.register_manager.unlockReg(lock); |
| 8312 | |
| 8313 | const src_addr_mcv: MCValue = switch (src_mcv) { |
| 8314 | .memory, .indirect, .load_frame => src_mcv.address(), |
| 8315 | else => .{ .register = try self.copyToTmpRegister( |
| 8316 | Type.usize, |
| 8317 | src_mcv.address(), |
| 8318 | ) }, |
| 8319 | }; |
| 8320 | const src_addr_lock = if (src_addr_mcv.getReg()) |reg| |
| 8321 | self.register_manager.lockReg(reg) |
| 8322 | else |
| 8323 | null; |
| 8324 | defer if (src_addr_lock) |lock| self.register_manager.unlockReg(lock); |
| 8325 | |
| 8326 | const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); |
| 8327 | const acc_reg = regs[0].to64(); |
| 8328 | const locks = self.register_manager.lockRegsAssumeUnused(2, regs); |
| 8329 | defer for (locks) |lock| self.register_manager.unlockReg(lock); |
| 8330 | |
| 8331 | const limbs_len = std.math.divCeil(u16, abi_size, 8) catch unreachable; |
| 8332 | var limb_i: u16 = 0; |
| 8333 | while (limb_i < limbs_len) : (limb_i += 1) { |
| 8334 | const tmp_reg = regs[@min(limb_i, 1)].to64(); |
| 8335 | try self.genSetReg( |
| 8336 | tmp_reg, |
| 8337 | Type.usize, |
| 8338 | dst_addr_mcv.offset(limb_i * 8).deref(), |
| 8339 | ); |
| 8340 | try self.genBinOpMir( |
| 8341 | .{ ._, .xor }, |
| 8342 | Type.usize, |
| 8343 | .{ .register = tmp_reg }, |
| 8344 | src_addr_mcv.offset(limb_i * 8).deref(), |
| 8345 | ); |
| 8346 | if (limb_i > 0) try self.asmRegisterRegister( |
| 8347 | .{ ._, .@"or" }, |
| 8348 | acc_reg, |
| 8349 | tmp_reg, |
| 8350 | ); |
| 8351 | } |
| 8352 | try self.asmRegisterRegister(.{ ._, .@"test" }, acc_reg, acc_reg); |
| 8353 | break :result_op flipped_op; |
| 8354 | }, |
| 8355 | }; |
| 8356 | try self.genBinOpMir(.{ ._, .cmp }, ty, dst_mcv, src_mcv); |
| 8357 | break :result_op flipped_op; |
| 8358 | }, |
| 7903 | 8359 | ); |
| 7904 | 8360 | }, |
| 7905 | 8361 | .Float => result: { |
| ... | ... | @@ -9282,7 +9738,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr |
| 9282 | 9738 | 17...32 => if (self.hasFeature(.avx)) .{ .v_, .movdqa } else null, |
| 9283 | 9739 | else => null, |
| 9284 | 9740 | }, |
| 9285 | | .Float => switch (ty.floatBits(self.target.*)) { |
| 9741 | .Float => switch (ty.scalarType().floatBits(self.target.*)) { |
| 9286 | 9742 | 16, 128 => switch (abi_size) { |
| 9287 | 9743 | 2...4 => if (self.hasFeature(.avx)) .{ .v_d, .mov } else .{ ._d, .mov }, |
| 9288 | 9744 | 5...8 => if (self.hasFeature(.avx)) .{ .v_q, .mov } else .{ ._q, .mov }, |
| ... | ... | @@ -9597,63 +10053,6 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal |
| 9597 | 10053 | } |
| 9598 | 10054 | } |
| 9599 | 10055 | |
| 9600 | | /// Like `genInlineMemcpy` but copies value from a register to an address via dereferencing |
| 9601 | | /// of destination register. |
| 9602 | | /// Boils down to MOV r/m64, r64. |
| 9603 | | fn genInlineMemcpyRegisterRegister( |
| 9604 | | self: *Self, |
| 9605 | | ty: Type, |
| 9606 | | dst_reg: Register, |
| 9607 | | src_reg: Register, |
| 9608 | | offset: i32, |
| 9609 | | ) InnerError!void { |
| 9610 | | assert(dst_reg.bitSize() == 64); |
| 9611 | | |
| 9612 | | const dst_reg_lock = self.register_manager.lockReg(dst_reg); |
| 9613 | | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 9614 | | |
| 9615 | | const src_reg_lock = self.register_manager.lockReg(src_reg); |
| 9616 | | defer if (src_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 9617 | | |
| 9618 | | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 9619 | | |
| 9620 | | if (!math.isPowerOfTwo(abi_size)) { |
| 9621 | | const tmp_reg = try self.copyToTmpRegister(ty, .{ .register = src_reg }); |
| 9622 | | |
| 9623 | | var next_offset = offset; |
| 9624 | | var remainder = abi_size; |
| 9625 | | while (remainder > 0) { |
| 9626 | | const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder)); |
| 9627 | | try self.asmMemoryRegister( |
| 9628 | | .{ ._, .mov }, |
| 9629 | | Memory.sib(Memory.PtrSize.fromSize(nearest_power_of_two), .{ |
| 9630 | | .base = dst_reg, |
| 9631 | | .disp = -next_offset, |
| 9632 | | }), |
| 9633 | | registerAlias(tmp_reg, nearest_power_of_two), |
| 9634 | | ); |
| 9635 | | |
| 9636 | | if (nearest_power_of_two > 1) { |
| 9637 | | try self.genShiftBinOpMir(.{ ._r, .sh }, ty, .{ .register = tmp_reg }, .{ |
| 9638 | | .immediate = nearest_power_of_two * 8, |
| 9639 | | }); |
| 9640 | | } |
| 9641 | | |
| 9642 | | remainder -= nearest_power_of_two; |
| 9643 | | next_offset -= nearest_power_of_two; |
| 9644 | | } |
| 9645 | | } else { |
| 9646 | | try self.asmMemoryRegister( |
| 9647 | | switch (src_reg.class()) { |
| 9648 | | .general_purpose, .segment => .{ ._, .mov }, |
| 9649 | | .sse => .{ ._ss, .mov }, |
| 9650 | | }, |
| 9651 | | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = dst_reg, .disp = -offset }), |
| 9652 | | registerAlias(src_reg, abi_size), |
| 9653 | | ); |
| 9654 | | } |
| 9655 | | } |
| 9656 | | |
| 9657 | 10056 | fn genInlineMemcpy(self: *Self, dst_ptr: MCValue, src_ptr: MCValue, len: MCValue) InnerError!void { |
| 9658 | 10057 | try self.spillRegisters(&.{ .rdi, .rsi, .rcx }); |
| 9659 | 10058 | try self.genSetReg(.rdi, Type.usize, dst_ptr); |
| ... | ... | @@ -9754,20 +10153,60 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { |
| 9754 | 10153 | const result = result: { |
| 9755 | 10154 | const dst_rc = regClassForType(dst_ty); |
| 9756 | 10155 | const src_rc = regClassForType(src_ty); |
| 9757 | | const operand = try self.resolveInst(ty_op.operand); |
| 9758 | | if (dst_rc.supersetOf(src_rc) and self.reuseOperand(inst, ty_op.operand, 0, operand)) |
| 9759 | | break :result operand; |
| 10156 | const src_mcv = try self.resolveInst(ty_op.operand); |
| 9760 | 10157 | |
| 9761 | | const operand_lock = switch (operand) { |
| 9762 | | .register => |reg| self.register_manager.lockReg(reg), |
| 9763 | | .register_overflow => |ro| self.register_manager.lockReg(ro.reg), |
| 9764 | | else => null, |
| 10158 | const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; |
| 10159 | defer if (src_lock) |lock| self.register_manager.unlockReg(lock); |
| 10160 | |
| 10161 | const dst_mcv = if (dst_rc.supersetOf(src_rc) and |
| 10162 | self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) |
| 10163 | src_mcv |
| 10164 | else dst: { |
| 10165 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 10166 | try self.genCopy( |
| 10167 | if (!dst_mcv.isMemory() or src_mcv.isMemory()) dst_ty else src_ty, |
| 10168 | dst_mcv, |
| 10169 | src_mcv, |
| 10170 | ); |
| 10171 | break :dst dst_mcv; |
| 9765 | 10172 | }; |
| 9766 | | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); |
| 9767 | 10173 | |
| 9768 | | const dest = try self.allocRegOrMem(inst, true); |
| 9769 | | try self.genCopy(if (!dest.isMemory() or operand.isMemory()) dst_ty else src_ty, dest, operand); |
| 9770 | | break :result dest; |
| 10174 | const dst_signedness = |
| 10175 | if (dst_ty.isAbiInt()) dst_ty.intInfo(self.target.*).signedness else .unsigned; |
| 10176 | const src_signedness = |
| 10177 | if (src_ty.isAbiInt()) src_ty.intInfo(self.target.*).signedness else .unsigned; |
| 10178 | const abi_size = @intCast(u16, dst_ty.abiSize(self.target.*)); |
| 10179 | const bit_size = @intCast(u16, dst_ty.bitSize(self.target.*)); |
| 10180 | const dst_limbs_len = math.divCeil(u16, bit_size, 64) catch unreachable; |
| 10181 | if (dst_signedness != src_signedness and abi_size * 8 > bit_size) { |
| 10182 | const high_reg = if (dst_mcv.isRegister()) |
| 10183 | dst_mcv.getReg().? |
| 10184 | else |
| 10185 | try self.copyToTmpRegister( |
| 10186 | Type.usize, |
| 10187 | dst_mcv.address().offset((dst_limbs_len - 1) * 8).deref(), |
| 10188 | ); |
| 10189 | const high_lock = self.register_manager.lockReg(high_reg); |
| 10190 | defer if (high_lock) |lock| self.register_manager.unlockReg(lock); |
| 10191 | |
| 10192 | var high_pl = Type.Payload.Bits{ |
| 10193 | .base = .{ .tag = switch (dst_signedness) { |
| 10194 | .signed => .int_signed, |
| 10195 | .unsigned => .int_unsigned, |
| 10196 | } }, |
| 10197 | .data = bit_size % 64, |
| 10198 | }; |
| 10199 | const high_ty = Type.initPayload(&high_pl.base); |
| 10200 | |
| 10201 | try self.truncateRegister(high_ty, high_reg); |
| 10202 | if (!dst_mcv.isRegister()) try self.genCopy( |
| 10203 | Type.usize, |
| 10204 | dst_mcv.address().offset((dst_limbs_len - 1) * 8).deref(), |
| 10205 | .{ .register = high_reg }, |
| 10206 | ); |
| 10207 | } |
| 10208 | |
| 10209 | break :result dst_mcv; |
| 9771 | 10210 | }; |
| 9772 | 10211 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 9773 | 10212 | } |
| ... | ... | @@ -9803,7 +10242,7 @@ fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void { |
| 9803 | 10242 | if (src_ty.isAbiInt()) src_ty.intInfo(self.target.*).signedness else .unsigned; |
| 9804 | 10243 | const dst_ty = self.air.typeOfIndex(inst); |
| 9805 | 10244 | |
| 9806 | | const src_size = std.math.divCeil(u32, @max(switch (src_signedness) { |
| 10245 | const src_size = math.divCeil(u32, @max(switch (src_signedness) { |
| 9807 | 10246 | .signed => src_bits, |
| 9808 | 10247 | .unsigned => src_bits + 1, |
| 9809 | 10248 | }, 32), 8) catch unreachable; |
| ... | ... | @@ -9856,7 +10295,7 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 9856 | 10295 | const dst_signedness = |
| 9857 | 10296 | if (dst_ty.isAbiInt()) dst_ty.intInfo(self.target.*).signedness else .unsigned; |
| 9858 | 10297 | |
| 9859 | | const dst_size = std.math.divCeil(u32, @max(switch (dst_signedness) { |
| 10298 | const dst_size = math.divCeil(u32, @max(switch (dst_signedness) { |
| 9860 | 10299 | .signed => dst_bits, |
| 9861 | 10300 | .unsigned => dst_bits + 1, |
| 9862 | 10301 | }, 32), 8) catch unreachable; |
| ... | ... | @@ -9912,14 +10351,30 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { |
| 9912 | 10351 | |
| 9913 | 10352 | const exp_mcv = try self.resolveInst(extra.expected_value); |
| 9914 | 10353 | if (val_abi_size > 8) { |
| 9915 | | try self.genSetReg(.rax, Type.usize, exp_mcv); |
| 9916 | | try self.genSetReg(.rdx, Type.usize, exp_mcv.address().offset(8).deref()); |
| 10354 | const exp_addr_mcv: MCValue = switch (exp_mcv) { |
| 10355 | .memory, .indirect, .load_frame => exp_mcv.address(), |
| 10356 | else => .{ .register = try self.copyToTmpRegister(Type.usize, exp_mcv.address()) }, |
| 10357 | }; |
| 10358 | const exp_addr_lock = |
| 10359 | if (exp_addr_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; |
| 10360 | defer if (exp_addr_lock) |lock| self.register_manager.unlockReg(lock); |
| 10361 | |
| 10362 | try self.genSetReg(.rax, Type.usize, exp_addr_mcv.deref()); |
| 10363 | try self.genSetReg(.rdx, Type.usize, exp_addr_mcv.offset(8).deref()); |
| 9917 | 10364 | } else try self.genSetReg(.rax, val_ty, exp_mcv); |
| 9918 | 10365 | |
| 9919 | 10366 | const new_mcv = try self.resolveInst(extra.new_value); |
| 9920 | 10367 | const new_reg = if (val_abi_size > 8) new: { |
| 9921 | | try self.genSetReg(.rbx, Type.usize, new_mcv); |
| 9922 | | try self.genSetReg(.rcx, Type.usize, new_mcv.address().offset(8).deref()); |
| 10368 | const new_addr_mcv: MCValue = switch (new_mcv) { |
| 10369 | .memory, .indirect, .load_frame => new_mcv.address(), |
| 10370 | else => .{ .register = try self.copyToTmpRegister(Type.usize, new_mcv.address()) }, |
| 10371 | }; |
| 10372 | const new_addr_lock = |
| 10373 | if (new_addr_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; |
| 10374 | defer if (new_addr_lock) |lock| self.register_manager.unlockReg(lock); |
| 10375 | |
| 10376 | try self.genSetReg(.rbx, Type.usize, new_addr_mcv.deref()); |
| 10377 | try self.genSetReg(.rcx, Type.usize, new_addr_mcv.offset(8).deref()); |
| 9923 | 10378 | break :new null; |
| 9924 | 10379 | } else try self.copyToTmpRegister(val_ty, new_mcv); |
| 9925 | 10380 | const new_lock = if (new_reg) |reg| self.register_manager.lockRegAssumeUnused(reg) else null; |
| ... | ... | @@ -10763,8 +11218,8 @@ fn airSelect(self: *Self, inst: Air.Inst.Index) !void { |
| 10763 | 11218 | } |
| 10764 | 11219 | |
| 10765 | 11220 | fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { |
| 10766 | | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 10767 | | _ = ty_op; |
| 11221 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 11222 | _ = ty_pl; |
| 10768 | 11223 | return self.fail("TODO implement airShuffle for x86_64", .{}); |
| 10769 | 11224 | //return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 10770 | 11225 | } |
| ... | ... | @@ -10894,6 +11349,12 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 10894 | 11349 | const elem_off = @intCast(i32, elem_size * elem_i); |
| 10895 | 11350 | try self.genSetMem(.{ .frame = frame_index }, elem_off, elem_ty, mat_elem_mcv); |
| 10896 | 11351 | } |
| 11352 | if (result_ty.sentinel()) |sentinel| try self.genSetMem( |
| 11353 | .{ .frame = frame_index }, |
| 11354 | @intCast(i32, elem_size * elements.len), |
| 11355 | elem_ty, |
| 11356 | try self.genTypedValue(.{ .ty = elem_ty, .val = sentinel }), |
| 11357 | ); |
| 10897 | 11358 | break :result .{ .load_frame = .{ .index = frame_index } }; |
| 10898 | 11359 | }, |
| 10899 | 11360 | .Vector => return self.fail("TODO implement aggregate_init for vectors", .{}), |