| ... | ... | @@ -58,6 +58,15 @@ target: *const std.Target, |
| 58 | 58 | owner: Owner, |
| 59 | 59 | err_msg: ?*ErrorMsg, |
| 60 | 60 | args: []MCValue, |
| 61 | va_info: union { |
| 62 | sysv: struct { |
| 63 | gp_count: u32, |
| 64 | fp_count: u32, |
| 65 | overflow_arg_area: FrameAddr, |
| 66 | reg_save_area: FrameAddr, |
| 67 | }, |
| 68 | win64: struct {}, |
| 69 | }, |
| 61 | 70 | ret_mcv: InstTracking, |
| 62 | 71 | fn_type: Type, |
| 63 | 72 | arg_index: u32, |
| ... | ... | @@ -745,6 +754,7 @@ pub fn generate( |
| 745 | 754 | .owner = .{ .func_index = func_index }, |
| 746 | 755 | .err_msg = null, |
| 747 | 756 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 757 | .va_info = undefined, // populated after `resolveCallingConventionValues` |
| 748 | 758 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| 749 | 759 | .fn_type = fn_type, |
| 750 | 760 | .arg_index = 0, |
| ... | ... | @@ -785,6 +795,7 @@ pub fn generate( |
| 785 | 795 | ); |
| 786 | 796 | |
| 787 | 797 | const fn_info = mod.typeToFunc(fn_type).?; |
| 798 | const cc = abi.resolveCallingConvention(fn_info.cc, function.target.*); |
| 788 | 799 | var call_info = function.resolveCallingConventionValues(fn_info, &.{}, .args_frame) catch |err| switch (err) { |
| 789 | 800 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 790 | 801 | error.OutOfRegisters => return Result{ |
| ... | ... | @@ -819,6 +830,16 @@ pub fn generate( |
| 819 | 830 | .alignment = call_info.stack_align, |
| 820 | 831 | }), |
| 821 | 832 | ); |
| 833 | function.va_info = switch (cc) { |
| 834 | .SysV => .{ .sysv = .{ |
| 835 | .gp_count = call_info.gp_count, |
| 836 | .fp_count = call_info.fp_count, |
| 837 | .overflow_arg_area = .{ .index = .args_frame, .off = call_info.stack_byte_count }, |
| 838 | .reg_save_area = undefined, |
| 839 | } }, |
| 840 | .Win64 => .{ .win64 = .{} }, |
| 841 | else => undefined, |
| 842 | }; |
| 822 | 843 | |
| 823 | 844 | function.gen() catch |err| switch (err) { |
| 824 | 845 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| ... | ... | @@ -839,7 +860,7 @@ pub fn generate( |
| 839 | 860 | .lower = .{ |
| 840 | 861 | .allocator = bin_file.allocator, |
| 841 | 862 | .mir = mir, |
| 842 | | .cc = abi.resolveCallingConvention(fn_info.cc, function.target.*), |
| 863 | .cc = cc, |
| 843 | 864 | .src_loc = src_loc, |
| 844 | 865 | }, |
| 845 | 866 | .bin_file = bin_file, |
| ... | ... | @@ -894,6 +915,7 @@ pub fn generateLazy( |
| 894 | 915 | .owner = .{ .lazy_sym = lazy_sym }, |
| 895 | 916 | .err_msg = null, |
| 896 | 917 | .args = undefined, |
| 918 | .va_info = undefined, |
| 897 | 919 | .ret_mcv = undefined, |
| 898 | 920 | .fn_type = undefined, |
| 899 | 921 | .arg_index = undefined, |
| ... | ... | @@ -1660,7 +1682,8 @@ fn asmMemoryRegisterImmediate( |
| 1660 | 1682 | |
| 1661 | 1683 | fn gen(self: *Self) InnerError!void { |
| 1662 | 1684 | const mod = self.bin_file.options.module.?; |
| 1663 | | const cc = self.fn_type.fnCallingConvention(mod); |
| 1685 | const fn_info = mod.typeToFunc(self.fn_type).?; |
| 1686 | const cc = abi.resolveCallingConvention(fn_info.cc, self.target.*); |
| 1664 | 1687 | if (cc != .Naked) { |
| 1665 | 1688 | try self.asmRegister(.{ ._, .push }, .rbp); |
| 1666 | 1689 | const backpatch_push_callee_preserved_regs = try self.asmPlaceholder(); |
| ... | ... | @@ -1690,6 +1713,42 @@ fn gen(self: *Self) InnerError!void { |
| 1690 | 1713 | else => unreachable, |
| 1691 | 1714 | } |
| 1692 | 1715 | |
| 1716 | if (fn_info.is_var_args) switch (cc) { |
| 1717 | .SysV => { |
| 1718 | const info = &self.va_info.sysv; |
| 1719 | const reg_save_area_fi = try self.allocFrameIndex(FrameAlloc.init(.{ |
| 1720 | .size = abi.SysV.c_abi_int_param_regs.len * 8 + |
| 1721 | abi.SysV.c_abi_sse_param_regs.len * 16, |
| 1722 | .alignment = .@"16", |
| 1723 | })); |
| 1724 | info.reg_save_area = .{ .index = reg_save_area_fi }; |
| 1725 | |
| 1726 | for (abi.SysV.c_abi_int_param_regs[info.gp_count..], info.gp_count..) |reg, reg_i| |
| 1727 | try self.genSetMem( |
| 1728 | .{ .frame = reg_save_area_fi }, |
| 1729 | @intCast(reg_i * 8), |
| 1730 | Type.usize, |
| 1731 | .{ .register = reg }, |
| 1732 | ); |
| 1733 | |
| 1734 | try self.asmRegisterImmediate(.{ ._, .cmp }, .al, Immediate.u(info.fp_count)); |
| 1735 | const skip_sse_reloc = try self.asmJccReloc(undefined, .na); |
| 1736 | |
| 1737 | const vec_2_f64 = try mod.vectorType(.{ .len = 2, .child = .f64_type }); |
| 1738 | for (abi.SysV.c_abi_sse_param_regs[info.fp_count..], info.fp_count..) |reg, reg_i| |
| 1739 | try self.genSetMem( |
| 1740 | .{ .frame = reg_save_area_fi }, |
| 1741 | @intCast(abi.SysV.c_abi_int_param_regs.len * 8 + reg_i * 16), |
| 1742 | vec_2_f64, |
| 1743 | .{ .register = reg }, |
| 1744 | ); |
| 1745 | |
| 1746 | try self.performReloc(skip_sse_reloc); |
| 1747 | }, |
| 1748 | .Win64 => return self.fail("TODO implement gen var arg function for Win64", .{}), |
| 1749 | else => unreachable, |
| 1750 | }; |
| 1751 | |
| 1693 | 1752 | try self.asmPseudo(.pseudo_dbg_prologue_end_none); |
| 1694 | 1753 | |
| 1695 | 1754 | try self.genBody(self.air.getMainBody()); |
| ... | ... | @@ -2064,10 +2123,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 2064 | 2123 | .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), |
| 2065 | 2124 | .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), |
| 2066 | 2125 | |
| 2067 | | .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), |
| 2068 | | .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), |
| 2069 | | .c_va_end => return self.fail("TODO implement c_va_end", .{}), |
| 2070 | | .c_va_start => return self.fail("TODO implement c_va_start", .{}), |
| 2126 | .c_va_arg => try self.airVaArg(inst), |
| 2127 | .c_va_copy => try self.airVaCopy(inst), |
| 2128 | .c_va_end => try self.airVaEnd(inst), |
| 2129 | .c_va_start => try self.airVaStart(inst), |
| 2071 | 2130 | |
| 2072 | 2131 | .wasm_memory_size => unreachable, |
| 2073 | 2132 | .wasm_memory_grow => unreachable, |
| ... | ... | @@ -2105,7 +2164,7 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { |
| 2105 | 2164 | switch (lazy_sym.ty.zigTypeTag(mod)) { |
| 2106 | 2165 | .Enum => { |
| 2107 | 2166 | const enum_ty = lazy_sym.ty; |
| 2108 | | wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(self.bin_file.options.module.?)}); |
| 2167 | wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(mod)}); |
| 2109 | 2168 | |
| 2110 | 2169 | const resolved_cc = abi.resolveCallingConvention(.Unspecified, self.target.*); |
| 2111 | 2170 | const param_regs = abi.getCAbiIntParamRegs(resolved_cc); |
| ... | ... | @@ -2153,7 +2212,7 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { |
| 2153 | 2212 | }, |
| 2154 | 2213 | else => return self.fail( |
| 2155 | 2214 | "TODO implement {s} for {}", |
| 2156 | | .{ @tagName(lazy_sym.kind), lazy_sym.ty.fmt(self.bin_file.options.module.?) }, |
| 2215 | .{ @tagName(lazy_sym.kind), lazy_sym.ty.fmt(mod) }, |
| 2157 | 2216 | ), |
| 2158 | 2217 | } |
| 2159 | 2218 | } |
| ... | ... | @@ -2914,9 +2973,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 2914 | 2973 | else => null, |
| 2915 | 2974 | }, |
| 2916 | 2975 | else => null, |
| 2917 | | }) orelse return self.fail("TODO implement airTrunc for {}", .{ |
| 2918 | | dst_ty.fmt(self.bin_file.options.module.?), |
| 2919 | | }); |
| 2976 | }) orelse return self.fail("TODO implement airTrunc for {}", .{dst_ty.fmt(mod)}); |
| 2920 | 2977 | |
| 2921 | 2978 | const elem_ty = src_ty.childType(mod); |
| 2922 | 2979 | const mask_val = try mod.intValue(elem_ty, @as(u64, math.maxInt(u64)) >> @intCast(64 - dst_info.bits)); |
| ... | ... | @@ -4568,7 +4625,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void { |
| 4568 | 4625 | .{ .immediate = 128 - src_bits }, |
| 4569 | 4626 | ); |
| 4570 | 4627 | } |
| 4571 | | } else return self.fail("TODO airClz of {}", .{src_ty.fmt(self.bin_file.options.module.?)}); |
| 4628 | } else return self.fail("TODO airClz of {}", .{src_ty.fmt(mod)}); |
| 4572 | 4629 | break :result dst_mcv; |
| 4573 | 4630 | } |
| 4574 | 4631 | |
| ... | ... | @@ -4697,12 +4754,11 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void { |
| 4697 | 4754 | try self.genBinOpMir(.{ ._, .add }, dst_ty, dst_mcv, .{ .immediate = 64 }); |
| 4698 | 4755 | try self.genBinOpMir(.{ ._, .tzcnt }, Type.u64, tmp_mcv, mat_src_mcv); |
| 4699 | 4756 | try self.asmCmovccRegisterRegister(dst_reg.to32(), tmp_reg.to32(), .nc); |
| 4700 | | } else return self.fail("TODO airCtz of {}", .{src_ty.fmt(self.bin_file.options.module.?)}); |
| 4757 | } else return self.fail("TODO airCtz of {}", .{src_ty.fmt(mod)}); |
| 4701 | 4758 | break :result dst_mcv; |
| 4702 | 4759 | } |
| 4703 | 4760 | |
| 4704 | | if (src_bits > 64) |
| 4705 | | return self.fail("TODO airCtz of {}", .{src_ty.fmt(self.bin_file.options.module.?)}); |
| 4761 | if (src_bits > 64) return self.fail("TODO airCtz of {}", .{src_ty.fmt(mod)}); |
| 4706 | 4762 | |
| 4707 | 4763 | const width_reg = try self.copyToTmpRegister(dst_ty, .{ .immediate = src_bits }); |
| 4708 | 4764 | const width_lock = self.register_manager.lockRegAssumeUnused(width_reg); |
| ... | ... | @@ -5129,9 +5185,7 @@ fn floatSign(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, ty: Type) |
| 5129 | 5185 | .abs => .{ .v_pd, .@"and" }, |
| 5130 | 5186 | else => unreachable, |
| 5131 | 5187 | }, |
| 5132 | | 80 => return self.fail("TODO implement floatSign for {}", .{ |
| 5133 | | ty.fmt(self.bin_file.options.module.?), |
| 5134 | | }), |
| 5188 | 80 => return self.fail("TODO implement floatSign for {}", .{ty.fmt(mod)}), |
| 5135 | 5189 | else => unreachable, |
| 5136 | 5190 | }, |
| 5137 | 5191 | registerAlias(dst_reg, abi_size), |
| ... | ... | @@ -5157,9 +5211,7 @@ fn floatSign(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, ty: Type) |
| 5157 | 5211 | .abs => .{ ._pd, .@"and" }, |
| 5158 | 5212 | else => unreachable, |
| 5159 | 5213 | }, |
| 5160 | | 80 => return self.fail("TODO implement floatSign for {}", .{ |
| 5161 | | ty.fmt(self.bin_file.options.module.?), |
| 5162 | | }), |
| 5214 | 80 => return self.fail("TODO implement floatSign for {}", .{ty.fmt(mod)}), |
| 5163 | 5215 | else => unreachable, |
| 5164 | 5216 | }, |
| 5165 | 5217 | registerAlias(dst_reg, abi_size), |
| ... | ... | @@ -5242,9 +5294,8 @@ fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: Ro |
| 5242 | 5294 | }, |
| 5243 | 5295 | else => unreachable, |
| 5244 | 5296 | } else null) orelse { |
| 5245 | | if (ty.zigTypeTag(mod) != .Float) return self.fail("TODO implement genRound for {}", .{ |
| 5246 | | ty.fmt(self.bin_file.options.module.?), |
| 5247 | | }); |
| 5297 | if (ty.zigTypeTag(mod) != .Float) |
| 5298 | return self.fail("TODO implement genRound for {}", .{ty.fmt(mod)}); |
| 5248 | 5299 | |
| 5249 | 5300 | var callee: ["__trunc?".len]u8 = undefined; |
| 5250 | 5301 | const res = try self.genCall(.{ .lib = .{ |
| ... | ... | @@ -5596,9 +5647,7 @@ fn packedLoad(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) Inn |
| 5596 | 5647 | const val_bit_off = ptr_info.packed_offset.bit_offset % limb_abi_bits; |
| 5597 | 5648 | const val_extra_bits = self.regExtraBits(val_ty); |
| 5598 | 5649 | |
| 5599 | | if (val_abi_size > 8) return self.fail("TODO implement packed load of {}", .{ |
| 5600 | | val_ty.fmt(self.bin_file.options.module.?), |
| 5601 | | }); |
| 5650 | if (val_abi_size > 8) return self.fail("TODO implement packed load of {}", .{val_ty.fmt(mod)}); |
| 5602 | 5651 | |
| 5603 | 5652 | const ptr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); |
| 5604 | 5653 | const ptr_lock = self.register_manager.lockRegAssumeUnused(ptr_reg); |
| ... | ... | @@ -5800,9 +5849,7 @@ fn packedStore(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) In |
| 5800 | 5849 | limb_mem, |
| 5801 | 5850 | registerAlias(tmp_reg, limb_abi_size), |
| 5802 | 5851 | ); |
| 5803 | | } else return self.fail("TODO: implement packed store of {}", .{ |
| 5804 | | src_ty.fmt(self.bin_file.options.module.?), |
| 5805 | | }); |
| 5852 | } else return self.fail("TODO: implement packed store of {}", .{src_ty.fmt(mod)}); |
| 5806 | 5853 | } |
| 5807 | 5854 | } |
| 5808 | 5855 | |
| ... | ... | @@ -6108,9 +6155,8 @@ fn genUnOp(self: *Self, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: |
| 6108 | 6155 | const mod = self.bin_file.options.module.?; |
| 6109 | 6156 | const src_ty = self.typeOf(src_air); |
| 6110 | 6157 | const src_mcv = try self.resolveInst(src_air); |
| 6111 | | if (src_ty.zigTypeTag(mod) == .Vector) { |
| 6112 | | return self.fail("TODO implement genUnOp for {}", .{src_ty.fmt(self.bin_file.options.module.?)}); |
| 6113 | | } |
| 6158 | if (src_ty.zigTypeTag(mod) == .Vector) |
| 6159 | return self.fail("TODO implement genUnOp for {}", .{src_ty.fmt(mod)}); |
| 6114 | 6160 | |
| 6115 | 6161 | switch (src_mcv) { |
| 6116 | 6162 | .eflags => |cc| switch (tag) { |
| ... | ... | @@ -6170,10 +6216,7 @@ fn genUnOp(self: *Self, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: |
| 6170 | 6216 | fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MCValue) !void { |
| 6171 | 6217 | const mod = self.bin_file.options.module.?; |
| 6172 | 6218 | const abi_size: u32 = @intCast(dst_ty.abiSize(mod)); |
| 6173 | | if (abi_size > 8) return self.fail("TODO implement {} for {}", .{ |
| 6174 | | mir_tag, |
| 6175 | | dst_ty.fmt(self.bin_file.options.module.?), |
| 6176 | | }); |
| 6219 | if (abi_size > 8) return self.fail("TODO implement {} for {}", .{ mir_tag, dst_ty.fmt(mod) }); |
| 6177 | 6220 | switch (dst_mcv) { |
| 6178 | 6221 | .none, |
| 6179 | 6222 | .unreach, |
| ... | ... | @@ -6529,7 +6572,7 @@ fn genMulDivBinOp( |
| 6529 | 6572 | .mul, .mul_wrap => dst_abi_size != src_abi_size and dst_abi_size != src_abi_size * 2, |
| 6530 | 6573 | .div_trunc, .div_floor, .div_exact, .rem, .mod => dst_abi_size != src_abi_size, |
| 6531 | 6574 | } or src_abi_size > 8) return self.fail("TODO implement genMulDivBinOp from {} to {}", .{ |
| 6532 | | src_ty.fmt(self.bin_file.options.module.?), dst_ty.fmt(self.bin_file.options.module.?), |
| 6575 | src_ty.fmt(mod), dst_ty.fmt(mod), |
| 6533 | 6576 | }); |
| 6534 | 6577 | const ty = if (dst_abi_size <= 8) dst_ty else src_ty; |
| 6535 | 6578 | const abi_size = if (dst_abi_size <= 8) dst_abi_size else src_abi_size; |
| ... | ... | @@ -6720,7 +6763,7 @@ fn genBinOp( |
| 6720 | 6763 | floatLibcAbiSuffix(lhs_ty), |
| 6721 | 6764 | }), |
| 6722 | 6765 | else => return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 6723 | | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 6766 | @tagName(air_tag), lhs_ty.fmt(mod), |
| 6724 | 6767 | }), |
| 6725 | 6768 | } catch unreachable, |
| 6726 | 6769 | } }, &.{ lhs_ty, rhs_ty }, &.{ .{ .air_ref = lhs_air }, .{ .air_ref = rhs_air } }); |
| ... | ... | @@ -6743,7 +6786,7 @@ fn genBinOp( |
| 6743 | 6786 | abi_size, |
| 6744 | 6787 | ) else null, |
| 6745 | 6788 | .rem, .mod => return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 6746 | | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 6789 | @tagName(air_tag), lhs_ty.fmt(mod), |
| 6747 | 6790 | }), |
| 6748 | 6791 | }; |
| 6749 | 6792 | const mask_lock = |
| ... | ... | @@ -6980,7 +7023,7 @@ fn genBinOp( |
| 6980 | 7023 | }, |
| 6981 | 7024 | |
| 6982 | 7025 | else => return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 6983 | | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7026 | @tagName(air_tag), lhs_ty.fmt(mod), |
| 6984 | 7027 | }), |
| 6985 | 7028 | } |
| 6986 | 7029 | return dst_mcv; |
| ... | ... | @@ -7567,7 +7610,7 @@ fn genBinOp( |
| 7567 | 7610 | }, |
| 7568 | 7611 | }, |
| 7569 | 7612 | }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7570 | | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7613 | @tagName(air_tag), lhs_ty.fmt(mod), |
| 7571 | 7614 | }); |
| 7572 | 7615 | |
| 7573 | 7616 | const lhs_copy_reg = if (maybe_mask_reg) |_| registerAlias( |
| ... | ... | @@ -7653,7 +7696,7 @@ fn genBinOp( |
| 7653 | 7696 | }, |
| 7654 | 7697 | else => unreachable, |
| 7655 | 7698 | }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7656 | | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7699 | @tagName(air_tag), lhs_ty.fmt(mod), |
| 7657 | 7700 | }), |
| 7658 | 7701 | mask_reg, |
| 7659 | 7702 | rhs_copy_reg, |
| ... | ... | @@ -7685,7 +7728,7 @@ fn genBinOp( |
| 7685 | 7728 | }, |
| 7686 | 7729 | else => unreachable, |
| 7687 | 7730 | }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7688 | | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7731 | @tagName(air_tag), lhs_ty.fmt(mod), |
| 7689 | 7732 | }), |
| 7690 | 7733 | dst_reg, |
| 7691 | 7734 | dst_reg, |
| ... | ... | @@ -7721,7 +7764,7 @@ fn genBinOp( |
| 7721 | 7764 | }, |
| 7722 | 7765 | else => unreachable, |
| 7723 | 7766 | }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7724 | | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7767 | @tagName(air_tag), lhs_ty.fmt(mod), |
| 7725 | 7768 | }), |
| 7726 | 7769 | mask_reg, |
| 7727 | 7770 | mask_reg, |
| ... | ... | @@ -7752,7 +7795,7 @@ fn genBinOp( |
| 7752 | 7795 | }, |
| 7753 | 7796 | else => unreachable, |
| 7754 | 7797 | }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7755 | | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7798 | @tagName(air_tag), lhs_ty.fmt(mod), |
| 7756 | 7799 | }), |
| 7757 | 7800 | dst_reg, |
| 7758 | 7801 | lhs_copy_reg.?, |
| ... | ... | @@ -7783,7 +7826,7 @@ fn genBinOp( |
| 7783 | 7826 | }, |
| 7784 | 7827 | else => unreachable, |
| 7785 | 7828 | }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7786 | | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7829 | @tagName(air_tag), lhs_ty.fmt(mod), |
| 7787 | 7830 | }), |
| 7788 | 7831 | dst_reg, |
| 7789 | 7832 | mask_reg, |
| ... | ... | @@ -7813,7 +7856,7 @@ fn genBinOp( |
| 7813 | 7856 | }, |
| 7814 | 7857 | else => unreachable, |
| 7815 | 7858 | }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7816 | | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7859 | @tagName(air_tag), lhs_ty.fmt(mod), |
| 7817 | 7860 | }), |
| 7818 | 7861 | mask_reg, |
| 7819 | 7862 | lhs_copy_reg.?, |
| ... | ... | @@ -7843,7 +7886,7 @@ fn genBinOp( |
| 7843 | 7886 | }, |
| 7844 | 7887 | else => unreachable, |
| 7845 | 7888 | }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 7846 | | @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 7889 | @tagName(air_tag), lhs_ty.fmt(mod), |
| 7847 | 7890 | }), |
| 7848 | 7891 | dst_reg, |
| 7849 | 7892 | mask_reg, |
| ... | ... | @@ -8645,6 +8688,9 @@ fn genCall(self: *Self, info: union(enum) { |
| 8645 | 8688 | else => unreachable, |
| 8646 | 8689 | }; |
| 8647 | 8690 | |
| 8691 | if (fn_info.is_var_args) |
| 8692 | try self.asmRegisterImmediate(.{ ._, .mov }, .al, Immediate.u(call_info.fp_count)); |
| 8693 | |
| 8648 | 8694 | // Due to incremental compilation, how function calls are generated depends |
| 8649 | 8695 | // on linking. |
| 8650 | 8696 | switch (info) { |
| ... | ... | @@ -9240,8 +9286,8 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { |
| 9240 | 9286 | } |
| 9241 | 9287 | |
| 9242 | 9288 | fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { |
| 9243 | | const ty_fn = self.air.instructions.items(.data)[inst].ty_fn; |
| 9244 | 9289 | const mod = self.bin_file.options.module.?; |
| 9290 | const ty_fn = self.air.instructions.items(.data)[inst].ty_fn; |
| 9245 | 9291 | const func = mod.funcInfo(ty_fn.func); |
| 9246 | 9292 | // TODO emit debug info for function change |
| 9247 | 9293 | _ = func; |
| ... | ... | @@ -9868,9 +9914,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 9868 | 9914 | |
| 9869 | 9915 | var outputs_extra_i = extra_i; |
| 9870 | 9916 | for (outputs) |output| { |
| 9871 | | const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); |
| 9872 | | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); |
| 9873 | | const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); |
| 9917 | const extra_bytes = mem.sliceAsBytes(self.air.extra[extra_i..]); |
| 9918 | const constraint = mem.sliceTo(mem.sliceAsBytes(self.air.extra[extra_i..]), 0); |
| 9919 | const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); |
| 9874 | 9920 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 9875 | 9921 | // for the string, we still use the next u32 for the null terminator. |
| 9876 | 9922 | extra_i += (constraint.len + name.len + (2 + 3)) / 4; |
| ... | ... | @@ -9915,16 +9961,16 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 9915 | 9961 | if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| { |
| 9916 | 9962 | _ = self.register_manager.lockRegAssumeUnused(reg); |
| 9917 | 9963 | }; |
| 9918 | | if (!std.mem.eql(u8, name, "_")) |
| 9964 | if (!mem.eql(u8, name, "_")) |
| 9919 | 9965 | arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len)); |
| 9920 | 9966 | args.appendAssumeCapacity(arg_mcv); |
| 9921 | 9967 | if (output == .none) result = arg_mcv; |
| 9922 | 9968 | } |
| 9923 | 9969 | |
| 9924 | 9970 | for (inputs) |input| { |
| 9925 | | const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); |
| 9926 | | const constraint = std.mem.sliceTo(input_bytes, 0); |
| 9927 | | const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); |
| 9971 | const input_bytes = mem.sliceAsBytes(self.air.extra[extra_i..]); |
| 9972 | const constraint = mem.sliceTo(input_bytes, 0); |
| 9973 | const name = mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); |
| 9928 | 9974 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 9929 | 9975 | // for the string, we still use the next u32 for the null terminator. |
| 9930 | 9976 | extra_i += (constraint.len + name.len + (2 + 3)) / 4; |
| ... | ... | @@ -9975,7 +10021,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 9975 | 10021 | if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| { |
| 9976 | 10022 | _ = self.register_manager.lockReg(reg); |
| 9977 | 10023 | }; |
| 9978 | | if (!std.mem.eql(u8, name, "_")) |
| 10024 | if (!mem.eql(u8, name, "_")) |
| 9979 | 10025 | arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len)); |
| 9980 | 10026 | args.appendAssumeCapacity(arg_mcv); |
| 9981 | 10027 | } |
| ... | ... | @@ -9983,7 +10029,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 9983 | 10029 | { |
| 9984 | 10030 | var clobber_i: u32 = 0; |
| 9985 | 10031 | while (clobber_i < clobbers_len) : (clobber_i += 1) { |
| 9986 | | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); |
| 10032 | const clobber = mem.sliceTo(mem.sliceAsBytes(self.air.extra[extra_i..]), 0); |
| 9987 | 10033 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 9988 | 10034 | // for the string, we still use the next u32 for the null terminator. |
| 9989 | 10035 | extra_i += clobber.len / 4 + 1; |
| ... | ... | @@ -10050,26 +10096,26 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 10050 | 10096 | arg_map.get(op_str["%[".len .. colon orelse op_str.len - "]".len]) orelse |
| 10051 | 10097 | return self.fail("no matching constraint: '{s}'", .{op_str}) |
| 10052 | 10098 | ]) { |
| 10053 | | .register => |reg| if (std.mem.eql(u8, modifier, "")) |
| 10099 | .register => |reg| if (mem.eql(u8, modifier, "")) |
| 10054 | 10100 | .{ .reg = reg } |
| 10055 | 10101 | else |
| 10056 | 10102 | return self.fail("invalid modifier: '{s}'", .{modifier}), |
| 10057 | | .memory => |addr| if (std.mem.eql(u8, modifier, "") or |
| 10058 | | std.mem.eql(u8, modifier, "P")) |
| 10103 | .memory => |addr| if (mem.eql(u8, modifier, "") or |
| 10104 | mem.eql(u8, modifier, "P")) |
| 10059 | 10105 | .{ .mem = Memory.sib( |
| 10060 | 10106 | mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}), |
| 10061 | 10107 | .{ .base = .{ .reg = .ds }, .disp = @intCast(@as(i64, @bitCast(addr))) }, |
| 10062 | 10108 | ) } |
| 10063 | 10109 | else |
| 10064 | 10110 | return self.fail("invalid modifier: '{s}'", .{modifier}), |
| 10065 | | .indirect => |reg_off| if (std.mem.eql(u8, modifier, "")) |
| 10111 | .indirect => |reg_off| if (mem.eql(u8, modifier, "")) |
| 10066 | 10112 | .{ .mem = Memory.sib( |
| 10067 | 10113 | mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}), |
| 10068 | 10114 | .{ .base = .{ .reg = reg_off.reg }, .disp = reg_off.off }, |
| 10069 | 10115 | ) } |
| 10070 | 10116 | else |
| 10071 | 10117 | return self.fail("invalid modifier: '{s}'", .{modifier}), |
| 10072 | | .load_frame => |frame_addr| if (std.mem.eql(u8, modifier, "")) |
| 10118 | .load_frame => |frame_addr| if (mem.eql(u8, modifier, "")) |
| 10073 | 10119 | .{ .mem = Memory.sib( |
| 10074 | 10120 | mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}), |
| 10075 | 10121 | .{ .base = .{ .frame = frame_addr.index }, .disp = frame_addr.off }, |
| ... | ... | @@ -10163,10 +10209,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 10163 | 10209 | } |
| 10164 | 10210 | |
| 10165 | 10211 | for (outputs, args.items[0..outputs.len]) |output, mcv| { |
| 10166 | | const extra_bytes = std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]); |
| 10212 | const extra_bytes = mem.sliceAsBytes(self.air.extra[outputs_extra_i..]); |
| 10167 | 10213 | const constraint = |
| 10168 | | std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]), 0); |
| 10169 | | const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); |
| 10214 | mem.sliceTo(mem.sliceAsBytes(self.air.extra[outputs_extra_i..]), 0); |
| 10215 | const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); |
| 10170 | 10216 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 10171 | 10217 | // for the string, we still use the next u32 for the null terminator. |
| 10172 | 10218 | outputs_extra_i += (constraint.len + name.len + (2 + 3)) / 4; |
| ... | ... | @@ -10428,7 +10474,7 @@ fn moveStrategy(self: *Self, ty: Type, aligned: bool) !MoveStrategy { |
| 10428 | 10474 | else => {}, |
| 10429 | 10475 | }, |
| 10430 | 10476 | } |
| 10431 | | return self.fail("TODO moveStrategy for {}", .{ty.fmt(self.bin_file.options.module.?)}); |
| 10477 | return self.fail("TODO moveStrategy for {}", .{ty.fmt(mod)}); |
| 10432 | 10478 | } |
| 10433 | 10479 | |
| 10434 | 10480 | fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError!void { |
| ... | ... | @@ -10626,9 +10672,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr |
| 10626 | 10672 | 80 => null, |
| 10627 | 10673 | else => unreachable, |
| 10628 | 10674 | }, |
| 10629 | | }) orelse return self.fail("TODO implement genSetReg for {}", .{ |
| 10630 | | ty.fmt(self.bin_file.options.module.?), |
| 10631 | | }), |
| 10675 | }) orelse return self.fail("TODO implement genSetReg for {}", .{ty.fmt(mod)}), |
| 10632 | 10676 | registerAlias(dst_reg, abi_size), |
| 10633 | 10677 | registerAlias(src_reg, abi_size), |
| 10634 | 10678 | ), |
| ... | ... | @@ -11585,7 +11629,7 @@ fn atomicOp( |
| 11585 | 11629 | try self.asmRegisterMemory(.{ ._, .xor }, .rcx, val_hi_mem); |
| 11586 | 11630 | }, |
| 11587 | 11631 | else => return self.fail("TODO implement x86 atomic loop for {} {s}", .{ |
| 11588 | | val_ty.fmt(self.bin_file.options.module.?), @tagName(op), |
| 11632 | val_ty.fmt(mod), @tagName(op), |
| 11589 | 11633 | }), |
| 11590 | 11634 | }; |
| 11591 | 11635 | try self.asmMemory(.{ .@"lock _16b", .cmpxchg }, ptr_mem); |
| ... | ... | @@ -12145,9 +12189,7 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { |
| 12145 | 12189 | else => unreachable, |
| 12146 | 12190 | }, |
| 12147 | 12191 | } |
| 12148 | | return self.fail("TODO implement airSplat for {}", .{ |
| 12149 | | vector_ty.fmt(self.bin_file.options.module.?), |
| 12150 | | }); |
| 12192 | return self.fail("TODO implement airSplat for {}", .{vector_ty.fmt(mod)}); |
| 12151 | 12193 | }; |
| 12152 | 12194 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 12153 | 12195 | } |
| ... | ... | @@ -12373,9 +12415,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { |
| 12373 | 12415 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; |
| 12374 | 12416 | const ty = self.typeOfIndex(inst); |
| 12375 | 12417 | |
| 12376 | | if (!self.hasFeature(.fma)) return self.fail("TODO implement airMulAdd for {}", .{ |
| 12377 | | ty.fmt(self.bin_file.options.module.?), |
| 12378 | | }); |
| 12418 | if (!self.hasFeature(.fma)) return self.fail("TODO implement airMulAdd for {}", .{ty.fmt(mod)}); |
| 12379 | 12419 | |
| 12380 | 12420 | const ops = [3]Air.Inst.Ref{ extra.lhs, extra.rhs, pl_op.operand }; |
| 12381 | 12421 | var mcvs: [3]MCValue = undefined; |
| ... | ... | @@ -12491,9 +12531,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { |
| 12491 | 12531 | else => unreachable, |
| 12492 | 12532 | } |
| 12493 | 12533 | else |
| 12494 | | unreachable) orelse return self.fail("TODO implement airMulAdd for {}", .{ |
| 12495 | | ty.fmt(self.bin_file.options.module.?), |
| 12496 | | }); |
| 12534 | unreachable) orelse return self.fail("TODO implement airMulAdd for {}", .{ty.fmt(mod)}); |
| 12497 | 12535 | |
| 12498 | 12536 | var mops: [3]MCValue = undefined; |
| 12499 | 12537 | for (order, mcvs) |mop_index, mcv| mops[mop_index - 1] = mcv; |
| ... | ... | @@ -12515,6 +12553,252 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { |
| 12515 | 12553 | return self.finishAir(inst, mops[0], ops); |
| 12516 | 12554 | } |
| 12517 | 12555 | |
| 12556 | fn airVaStart(self: *Self, inst: Air.Inst.Index) !void { |
| 12557 | const mod = self.bin_file.options.module.?; |
| 12558 | const va_list_ty = self.air.instructions.items(.data)[inst].ty; |
| 12559 | const ptr_anyopaque_ty = try mod.singleMutPtrType(Type.anyopaque); |
| 12560 | |
| 12561 | const result: MCValue = switch (abi.resolveCallingConvention( |
| 12562 | self.fn_type.fnCallingConvention(mod), |
| 12563 | self.target.*, |
| 12564 | )) { |
| 12565 | .SysV => result: { |
| 12566 | const info = self.va_info.sysv; |
| 12567 | const dst_fi = try self.allocFrameIndex(FrameAlloc.initType(va_list_ty, mod)); |
| 12568 | var field_off: u31 = 0; |
| 12569 | // gp_offset: c_uint, |
| 12570 | try self.genSetMem( |
| 12571 | .{ .frame = dst_fi }, |
| 12572 | field_off, |
| 12573 | Type.c_uint, |
| 12574 | .{ .immediate = info.gp_count * 8 }, |
| 12575 | ); |
| 12576 | field_off += @intCast(Type.c_uint.abiSize(mod)); |
| 12577 | // fp_offset: c_uint, |
| 12578 | try self.genSetMem( |
| 12579 | .{ .frame = dst_fi }, |
| 12580 | field_off, |
| 12581 | Type.c_uint, |
| 12582 | .{ .immediate = abi.SysV.c_abi_int_param_regs.len * 8 + info.fp_count * 16 }, |
| 12583 | ); |
| 12584 | field_off += @intCast(Type.c_uint.abiSize(mod)); |
| 12585 | // overflow_arg_area: *anyopaque, |
| 12586 | try self.genSetMem( |
| 12587 | .{ .frame = dst_fi }, |
| 12588 | field_off, |
| 12589 | ptr_anyopaque_ty, |
| 12590 | .{ .lea_frame = info.overflow_arg_area }, |
| 12591 | ); |
| 12592 | field_off += @intCast(ptr_anyopaque_ty.abiSize(mod)); |
| 12593 | // reg_save_area: *anyopaque, |
| 12594 | try self.genSetMem( |
| 12595 | .{ .frame = dst_fi }, |
| 12596 | field_off, |
| 12597 | ptr_anyopaque_ty, |
| 12598 | .{ .lea_frame = info.reg_save_area }, |
| 12599 | ); |
| 12600 | field_off += @intCast(ptr_anyopaque_ty.abiSize(mod)); |
| 12601 | break :result .{ .load_frame = .{ .index = dst_fi } }; |
| 12602 | }, |
| 12603 | .Win64 => return self.fail("TODO implement c_va_start for Win64", .{}), |
| 12604 | else => unreachable, |
| 12605 | }; |
| 12606 | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| 12607 | } |
| 12608 | |
| 12609 | fn airVaArg(self: *Self, inst: Air.Inst.Index) !void { |
| 12610 | const mod = self.bin_file.options.module.?; |
| 12611 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 12612 | const ty = self.typeOfIndex(inst); |
| 12613 | const promote_ty = self.promoteVarArg(ty); |
| 12614 | const ptr_anyopaque_ty = try mod.singleMutPtrType(Type.anyopaque); |
| 12615 | const unused = self.liveness.isUnused(inst); |
| 12616 | |
| 12617 | const result: MCValue = switch (abi.resolveCallingConvention( |
| 12618 | self.fn_type.fnCallingConvention(mod), |
| 12619 | self.target.*, |
| 12620 | )) { |
| 12621 | .SysV => result: { |
| 12622 | try self.spillEflagsIfOccupied(); |
| 12623 | |
| 12624 | const tmp_regs = |
| 12625 | try self.register_manager.allocRegs(2, .{ null, null }, abi.RegisterClass.gp); |
| 12626 | const offset_reg = tmp_regs[0].to32(); |
| 12627 | const addr_reg = tmp_regs[1].to64(); |
| 12628 | const tmp_locks = self.register_manager.lockRegsAssumeUnused(2, tmp_regs); |
| 12629 | defer for (tmp_locks) |lock| self.register_manager.unlockReg(lock); |
| 12630 | |
| 12631 | const promote_mcv = try self.allocTempRegOrMem(promote_ty, true); |
| 12632 | const promote_lock = switch (promote_mcv) { |
| 12633 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 12634 | else => null, |
| 12635 | }; |
| 12636 | defer if (promote_lock) |lock| self.register_manager.unlockReg(lock); |
| 12637 | |
| 12638 | const ptr_arg_list_reg = |
| 12639 | try self.copyToTmpRegister(self.typeOf(ty_op.operand), .{ .air_ref = ty_op.operand }); |
| 12640 | const ptr_arg_list_lock = self.register_manager.lockRegAssumeUnused(ptr_arg_list_reg); |
| 12641 | defer self.register_manager.unlockReg(ptr_arg_list_lock); |
| 12642 | |
| 12643 | const gp_offset: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 0 } }; |
| 12644 | const fp_offset: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 4 } }; |
| 12645 | const overflow_arg_area: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 8 } }; |
| 12646 | const reg_save_area: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 16 } }; |
| 12647 | |
| 12648 | const classes = mem.sliceTo(&abi.classifySystemV(promote_ty, mod, .arg), .none); |
| 12649 | switch (classes[0]) { |
| 12650 | .integer => { |
| 12651 | assert(classes.len == 1); |
| 12652 | |
| 12653 | try self.genSetReg(offset_reg, Type.c_uint, gp_offset); |
| 12654 | try self.asmRegisterImmediate(.{ ._, .cmp }, offset_reg, Immediate.u( |
| 12655 | abi.SysV.c_abi_int_param_regs.len * 8, |
| 12656 | )); |
| 12657 | const mem_reloc = try self.asmJccReloc(undefined, .ae); |
| 12658 | |
| 12659 | try self.genSetReg(addr_reg, ptr_anyopaque_ty, reg_save_area); |
| 12660 | if (!unused) |
| 12661 | try self.asmRegisterMemory(.{ ._, .lea }, addr_reg, Memory.sib(.qword, .{ |
| 12662 | .base = .{ .reg = addr_reg }, |
| 12663 | .scale_index = .{ .scale = 1, .index = offset_reg.to64() }, |
| 12664 | })); |
| 12665 | try self.asmRegisterMemory(.{ ._, .lea }, offset_reg, Memory.sib(.qword, .{ |
| 12666 | .base = .{ .reg = offset_reg.to64() }, |
| 12667 | .disp = 8, |
| 12668 | })); |
| 12669 | try self.genCopy(Type.c_uint, gp_offset, .{ .register = offset_reg }); |
| 12670 | const done_reloc = try self.asmJmpReloc(undefined); |
| 12671 | |
| 12672 | try self.performReloc(mem_reloc); |
| 12673 | try self.genSetReg(addr_reg, ptr_anyopaque_ty, overflow_arg_area); |
| 12674 | try self.asmRegisterMemory(.{ ._, .lea }, offset_reg.to64(), Memory.sib(.qword, .{ |
| 12675 | .base = .{ .reg = addr_reg }, |
| 12676 | .disp = @intCast(@max(promote_ty.abiSize(mod), 8)), |
| 12677 | })); |
| 12678 | try self.genCopy( |
| 12679 | ptr_anyopaque_ty, |
| 12680 | overflow_arg_area, |
| 12681 | .{ .register = offset_reg.to64() }, |
| 12682 | ); |
| 12683 | |
| 12684 | try self.performReloc(done_reloc); |
| 12685 | if (!unused) try self.genCopy(promote_ty, promote_mcv, .{ |
| 12686 | .indirect = .{ .reg = addr_reg }, |
| 12687 | }); |
| 12688 | }, |
| 12689 | .sse => { |
| 12690 | assert(classes.len == 1); |
| 12691 | |
| 12692 | try self.genSetReg(offset_reg, Type.c_uint, fp_offset); |
| 12693 | try self.asmRegisterImmediate(.{ ._, .cmp }, offset_reg, Immediate.u( |
| 12694 | abi.SysV.c_abi_int_param_regs.len * 8 + abi.SysV.c_abi_sse_param_regs.len * 16, |
| 12695 | )); |
| 12696 | const mem_reloc = try self.asmJccReloc(undefined, .ae); |
| 12697 | |
| 12698 | try self.genSetReg(addr_reg, ptr_anyopaque_ty, reg_save_area); |
| 12699 | if (!unused) |
| 12700 | try self.asmRegisterMemory(.{ ._, .lea }, addr_reg, Memory.sib(.qword, .{ |
| 12701 | .base = .{ .reg = addr_reg }, |
| 12702 | .scale_index = .{ .scale = 1, .index = offset_reg.to64() }, |
| 12703 | })); |
| 12704 | try self.asmRegisterMemory(.{ ._, .lea }, offset_reg, Memory.sib(.qword, .{ |
| 12705 | .base = .{ .reg = offset_reg.to64() }, |
| 12706 | .disp = 16, |
| 12707 | })); |
| 12708 | try self.genCopy(Type.c_uint, fp_offset, .{ .register = offset_reg }); |
| 12709 | const done_reloc = try self.asmJmpReloc(undefined); |
| 12710 | |
| 12711 | try self.performReloc(mem_reloc); |
| 12712 | try self.genSetReg(addr_reg, ptr_anyopaque_ty, overflow_arg_area); |
| 12713 | try self.asmRegisterMemory(.{ ._, .lea }, offset_reg.to64(), Memory.sib(.qword, .{ |
| 12714 | .base = .{ .reg = addr_reg }, |
| 12715 | .disp = @intCast(@max(promote_ty.abiSize(mod), 8)), |
| 12716 | })); |
| 12717 | try self.genCopy( |
| 12718 | ptr_anyopaque_ty, |
| 12719 | overflow_arg_area, |
| 12720 | .{ .register = offset_reg.to64() }, |
| 12721 | ); |
| 12722 | |
| 12723 | try self.performReloc(done_reloc); |
| 12724 | if (!unused) try self.genCopy(promote_ty, promote_mcv, .{ |
| 12725 | .indirect = .{ .reg = addr_reg }, |
| 12726 | }); |
| 12727 | }, |
| 12728 | .memory => { |
| 12729 | assert(classes.len == 1); |
| 12730 | unreachable; |
| 12731 | }, |
| 12732 | else => return self.fail("TODO implement c_va_arg for {} on SysV", .{ |
| 12733 | promote_ty.fmt(mod), |
| 12734 | }), |
| 12735 | } |
| 12736 | |
| 12737 | if (unused) break :result .unreach; |
| 12738 | if (ty.toIntern() == promote_ty.toIntern()) break :result promote_mcv; |
| 12739 | |
| 12740 | if (!promote_ty.isRuntimeFloat()) { |
| 12741 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 12742 | try self.genCopy(ty, dst_mcv, promote_mcv); |
| 12743 | break :result dst_mcv; |
| 12744 | } |
| 12745 | |
| 12746 | assert(ty.toIntern() == .f32_type and promote_ty.toIntern() == .f64_type); |
| 12747 | const dst_mcv = if (promote_mcv.isRegister()) |
| 12748 | promote_mcv |
| 12749 | else |
| 12750 | try self.copyToRegisterWithInstTracking(inst, ty, promote_mcv); |
| 12751 | const dst_reg = dst_mcv.getReg().?.to128(); |
| 12752 | const dst_lock = self.register_manager.lockReg(dst_reg); |
| 12753 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 12754 | |
| 12755 | if (self.hasFeature(.avx)) if (promote_mcv.isMemory()) try self.asmRegisterRegisterMemory( |
| 12756 | .{ .v_ss, .cvtsd2 }, |
| 12757 | dst_reg, |
| 12758 | dst_reg, |
| 12759 | promote_mcv.mem(.qword), |
| 12760 | ) else try self.asmRegisterRegisterRegister( |
| 12761 | .{ .v_ss, .cvtsd2 }, |
| 12762 | dst_reg, |
| 12763 | dst_reg, |
| 12764 | (if (promote_mcv.isRegister()) |
| 12765 | promote_mcv.getReg().? |
| 12766 | else |
| 12767 | try self.copyToTmpRegister(promote_ty, promote_mcv)).to128(), |
| 12768 | ) else if (promote_mcv.isMemory()) try self.asmRegisterMemory( |
| 12769 | .{ ._ss, .cvtsd2 }, |
| 12770 | dst_reg, |
| 12771 | promote_mcv.mem(.qword), |
| 12772 | ) else try self.asmRegisterRegister( |
| 12773 | .{ ._ss, .cvtsd2 }, |
| 12774 | dst_reg, |
| 12775 | (if (promote_mcv.isRegister()) |
| 12776 | promote_mcv.getReg().? |
| 12777 | else |
| 12778 | try self.copyToTmpRegister(promote_ty, promote_mcv)).to128(), |
| 12779 | ); |
| 12780 | break :result promote_mcv; |
| 12781 | }, |
| 12782 | .Win64 => return self.fail("TODO implement c_va_arg for Win64", .{}), |
| 12783 | else => unreachable, |
| 12784 | }; |
| 12785 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 12786 | } |
| 12787 | |
| 12788 | fn airVaCopy(self: *Self, inst: Air.Inst.Index) !void { |
| 12789 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 12790 | const ptr_va_list_ty = self.typeOf(ty_op.operand); |
| 12791 | |
| 12792 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 12793 | try self.load(dst_mcv, ptr_va_list_ty, .{ .air_ref = ty_op.operand }); |
| 12794 | return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); |
| 12795 | } |
| 12796 | |
| 12797 | fn airVaEnd(self: *Self, inst: Air.Inst.Index) !void { |
| 12798 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 12799 | return self.finishAir(inst, .unreach, .{ un_op, .none, .none }); |
| 12800 | } |
| 12801 | |
| 12518 | 12802 | fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue { |
| 12519 | 12803 | const mod = self.bin_file.options.module.?; |
| 12520 | 12804 | const ty = self.typeOf(ref); |
| ... | ... | @@ -12593,6 +12877,8 @@ const CallMCValues = struct { |
| 12593 | 12877 | return_value: InstTracking, |
| 12594 | 12878 | stack_byte_count: u31, |
| 12595 | 12879 | stack_align: Alignment, |
| 12880 | gp_count: u32, |
| 12881 | fp_count: u32, |
| 12596 | 12882 | |
| 12597 | 12883 | fn deinit(self: *CallMCValues, func: *Self) void { |
| 12598 | 12884 | func.gpa.free(self.args); |
| ... | ... | @@ -12616,8 +12902,8 @@ fn resolveCallingConventionValues( |
| 12616 | 12902 | for (param_types[0..fn_info.param_types.len], fn_info.param_types.get(ip)) |*dest, src| { |
| 12617 | 12903 | dest.* = src.toType(); |
| 12618 | 12904 | } |
| 12619 | | // TODO: promote var arg types |
| 12620 | | for (param_types[fn_info.param_types.len..], var_args) |*param_ty, arg_ty| param_ty.* = arg_ty; |
| 12905 | for (param_types[fn_info.param_types.len..], var_args) |*param_ty, arg_ty| |
| 12906 | param_ty.* = self.promoteVarArg(arg_ty); |
| 12621 | 12907 | |
| 12622 | 12908 | var result: CallMCValues = .{ |
| 12623 | 12909 | .args = try self.gpa.alloc(MCValue, param_types.len), |
| ... | ... | @@ -12625,6 +12911,8 @@ fn resolveCallingConventionValues( |
| 12625 | 12911 | .return_value = undefined, |
| 12626 | 12912 | .stack_byte_count = 0, |
| 12627 | 12913 | .stack_align = undefined, |
| 12914 | .gp_count = 0, |
| 12915 | .fp_count = 0, |
| 12628 | 12916 | }; |
| 12629 | 12917 | errdefer self.gpa.free(result.args); |
| 12630 | 12918 | |
| ... | ... | @@ -12638,10 +12926,10 @@ fn resolveCallingConventionValues( |
| 12638 | 12926 | result.stack_align = .@"8"; |
| 12639 | 12927 | }, |
| 12640 | 12928 | .C, .SysV, .Win64 => { |
| 12641 | | var ret_int_reg_i: usize = 0; |
| 12642 | | var ret_sse_reg_i: usize = 0; |
| 12643 | | var param_int_reg_i: usize = 0; |
| 12644 | | var param_sse_reg_i: usize = 0; |
| 12929 | var ret_int_reg_i: u32 = 0; |
| 12930 | var ret_sse_reg_i: u32 = 0; |
| 12931 | var param_int_reg_i: u32 = 0; |
| 12932 | var param_sse_reg_i: u32 = 0; |
| 12645 | 12933 | result.stack_align = .@"16"; |
| 12646 | 12934 | |
| 12647 | 12935 | switch (resolved_cc) { |
| ... | ... | @@ -12800,6 +13088,10 @@ fn resolveCallingConventionValues( |
| 12800 | 13088 | } }; |
| 12801 | 13089 | result.stack_byte_count += param_size; |
| 12802 | 13090 | } |
| 13091 | assert(param_int_reg_i <= 6); |
| 13092 | result.gp_count = param_int_reg_i; |
| 13093 | assert(param_sse_reg_i <= 16); |
| 13094 | result.fp_count = param_sse_reg_i; |
| 12803 | 13095 | }, |
| 12804 | 13096 | .Unspecified => { |
| 12805 | 13097 | result.stack_align = .@"16"; |
| ... | ... | @@ -13051,3 +13343,35 @@ fn floatLibcAbiSuffix(ty: Type) []const u8 { |
| 13051 | 13343 | else => unreachable, |
| 13052 | 13344 | }; |
| 13053 | 13345 | } |
| 13346 | |
| 13347 | fn promoteVarArg(self: *Self, ty: Type) Type { |
| 13348 | const mod = self.bin_file.options.module.?; |
| 13349 | switch (ty.zigTypeTag(mod)) { |
| 13350 | .Bool => return Type.c_int, |
| 13351 | else => { |
| 13352 | const int_info = ty.intInfo(mod); |
| 13353 | for ([_]Type{ |
| 13354 | Type.c_int, Type.c_uint, |
| 13355 | Type.c_long, Type.c_ulong, |
| 13356 | Type.c_longlong, Type.c_ulonglong, |
| 13357 | }) |promote_ty| { |
| 13358 | const promote_info = promote_ty.intInfo(mod); |
| 13359 | if (int_info.signedness == .signed and promote_info.signedness == .unsigned) continue; |
| 13360 | if (int_info.bits + @intFromBool(int_info.signedness == .unsigned and |
| 13361 | promote_info.signedness == .signed) <= promote_info.bits) return promote_ty; |
| 13362 | } |
| 13363 | unreachable; |
| 13364 | }, |
| 13365 | .Float => switch (ty.floatBits(self.target.*)) { |
| 13366 | 32, 64 => return Type.f64, |
| 13367 | else => |float_bits| { |
| 13368 | assert(float_bits == self.target.c_type_bit_size(.longdouble)); |
| 13369 | return Type.c_longdouble; |
| 13370 | }, |
| 13371 | }, |
| 13372 | .Pointer => { |
| 13373 | assert(!ty.isSlice(mod)); |
| 13374 | return ty; |
| 13375 | }, |
| 13376 | } |
| 13377 | } |