| ... | ... | @@ -21,6 +21,7 @@ const Value = @import("../value.zig").Value; |
| 21 | 21 | const Type = @import("../type.zig").Type; |
| 22 | 22 | const LazySrcLoc = Module.LazySrcLoc; |
| 23 | 23 | const CType = @import("../type.zig").CType; |
| 24 | const x86_64_abi = @import("../arch/x86_64/abi.zig"); |
| 24 | 25 | |
| 25 | 26 | const Error = error{ OutOfMemory, CodegenFail }; |
| 26 | 27 | |
| ... | ... | @@ -2391,27 +2392,20 @@ pub const DeclGen = struct { |
| 2391 | 2392 | }, |
| 2392 | 2393 | .Fn => { |
| 2393 | 2394 | const fn_info = t.fnInfo(); |
| 2394 | | const sret = firstParamSRet(fn_info, target); |
| 2395 | | const return_type = fn_info.return_type; |
| 2396 | | const llvm_sret_ty = if (return_type.hasRuntimeBitsIgnoreComptime()) |
| 2397 | | try dg.llvmType(return_type) |
| 2398 | | else |
| 2399 | | dg.context.voidType(); |
| 2400 | | const llvm_ret_ty = if (sret) dg.context.voidType() else llvm_sret_ty; |
| 2395 | const llvm_ret_ty = try lowerFnRetTy(dg, fn_info); |
| 2401 | 2396 | |
| 2402 | 2397 | var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa); |
| 2403 | 2398 | defer llvm_params.deinit(); |
| 2404 | 2399 | |
| 2405 | | if (sret) { |
| 2400 | if (firstParamSRet(fn_info, target)) { |
| 2401 | const llvm_sret_ty = try dg.llvmType(fn_info.return_type); |
| 2406 | 2402 | try llvm_params.append(llvm_sret_ty.pointerType(0)); |
| 2407 | 2403 | } |
| 2408 | 2404 | |
| 2409 | 2405 | for (fn_info.param_types) |param_ty| { |
| 2410 | 2406 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 2411 | 2407 | |
| 2412 | | const raw_llvm_ty = try dg.llvmType(param_ty); |
| 2413 | | const actual_llvm_ty = if (!isByRef(param_ty)) raw_llvm_ty else raw_llvm_ty.pointerType(0); |
| 2414 | | try llvm_params.append(actual_llvm_ty); |
| 2408 | try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, param_ty)); |
| 2415 | 2409 | } |
| 2416 | 2410 | |
| 2417 | 2411 | return llvm.functionType( |
| ... | ... | @@ -3704,24 +3698,45 @@ pub const FuncGen = struct { |
| 3704 | 3698 | break :blk ret_ptr; |
| 3705 | 3699 | }; |
| 3706 | 3700 | |
| 3707 | | if (fn_info.is_var_args) { |
| 3708 | | for (args) |arg| { |
| 3709 | | try llvm_args.append(try self.resolveInst(arg)); |
| 3710 | | } |
| 3711 | | } else { |
| 3712 | | for (args) |arg, i| { |
| 3713 | | const param_ty = fn_info.param_types[i]; |
| 3714 | | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 3701 | for (args) |arg| { |
| 3702 | const param_ty = self.air.typeOf(arg); |
| 3703 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 3715 | 3704 | |
| 3716 | | try llvm_args.append(try self.resolveInst(arg)); |
| 3705 | const llvm_arg = try self.resolveInst(arg); |
| 3706 | const abi_llvm_ty = try lowerFnParamTy(self.dg, fn_info.cc, param_ty); |
| 3707 | const param_llvm_ty = llvm_arg.typeOf(); |
| 3708 | if (abi_llvm_ty == param_llvm_ty) { |
| 3709 | try llvm_args.append(llvm_arg); |
| 3710 | continue; |
| 3717 | 3711 | } |
| 3712 | |
| 3713 | // In this case the function param type is honoring the calling convention |
| 3714 | // by having a different LLVM type than the usual one. We solve this here |
| 3715 | // at the callsite by bitcasting a pointer to our canonical type, then |
| 3716 | // loading it if necessary. |
| 3717 | const alignment = param_ty.abiAlignment(target); |
| 3718 | const ptr_abi_ty = abi_llvm_ty.pointerType(0); |
| 3719 | |
| 3720 | const casted_ptr = if (isByRef(param_ty)) |
| 3721 | self.builder.buildBitCast(llvm_arg, ptr_abi_ty, "") |
| 3722 | else p: { |
| 3723 | const arg_ptr = self.buildAlloca(param_llvm_ty); |
| 3724 | arg_ptr.setAlignment(alignment); |
| 3725 | const store_inst = self.builder.buildStore(llvm_arg, arg_ptr); |
| 3726 | store_inst.setAlignment(alignment); |
| 3727 | break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, ""); |
| 3728 | }; |
| 3729 | |
| 3730 | const load_inst = self.builder.buildLoad(casted_ptr, ""); |
| 3731 | load_inst.setAlignment(alignment); |
| 3732 | try llvm_args.append(load_inst); |
| 3718 | 3733 | } |
| 3719 | 3734 | |
| 3720 | 3735 | const call = self.builder.buildCall( |
| 3721 | 3736 | llvm_fn, |
| 3722 | 3737 | llvm_args.items.ptr, |
| 3723 | 3738 | @intCast(c_uint, llvm_args.items.len), |
| 3724 | | toLlvmCallConv(zig_fn_ty.fnCallingConvention(), target), |
| 3739 | toLlvmCallConv(fn_info.cc, target), |
| 3725 | 3740 | attr, |
| 3726 | 3741 | "", |
| 3727 | 3742 | ); |
| ... | ... | @@ -3735,8 +3750,9 @@ pub const FuncGen = struct { |
| 3735 | 3750 | return null; |
| 3736 | 3751 | } |
| 3737 | 3752 | |
| 3753 | const llvm_ret_ty = try self.dg.llvmType(return_type); |
| 3754 | |
| 3738 | 3755 | if (ret_ptr) |rp| { |
| 3739 | | const llvm_ret_ty = try self.dg.llvmType(return_type); |
| 3740 | 3756 | call.setCallSret(llvm_ret_ty); |
| 3741 | 3757 | if (isByRef(return_type)) { |
| 3742 | 3758 | return rp; |
| ... | ... | @@ -3748,10 +3764,31 @@ pub const FuncGen = struct { |
| 3748 | 3764 | } |
| 3749 | 3765 | } |
| 3750 | 3766 | |
| 3767 | const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info); |
| 3768 | |
| 3769 | if (abi_ret_ty != llvm_ret_ty) { |
| 3770 | // In this case the function return type is honoring the calling convention by having |
| 3771 | // a different LLVM type than the usual one. We solve this here at the callsite |
| 3772 | // by bitcasting a pointer to our canonical type, then loading it if necessary. |
| 3773 | const rp = self.buildAlloca(llvm_ret_ty); |
| 3774 | const alignment = return_type.abiAlignment(target); |
| 3775 | rp.setAlignment(alignment); |
| 3776 | const ptr_abi_ty = abi_ret_ty.pointerType(0); |
| 3777 | const casted_ptr = self.builder.buildBitCast(rp, ptr_abi_ty, ""); |
| 3778 | const store_inst = self.builder.buildStore(call, casted_ptr); |
| 3779 | store_inst.setAlignment(alignment); |
| 3780 | if (isByRef(return_type)) { |
| 3781 | return rp; |
| 3782 | } else { |
| 3783 | const load_inst = self.builder.buildLoad(rp, ""); |
| 3784 | load_inst.setAlignment(alignment); |
| 3785 | return load_inst; |
| 3786 | } |
| 3787 | } |
| 3788 | |
| 3751 | 3789 | if (isByRef(return_type)) { |
| 3752 | 3790 | // our by-ref status disagrees with sret so we must allocate, store, |
| 3753 | 3791 | // and return the allocation pointer. |
| 3754 | | const llvm_ret_ty = try self.dg.llvmType(return_type); |
| 3755 | 3792 | const rp = self.buildAlloca(llvm_ret_ty); |
| 3756 | 3793 | const alignment = return_type.abiAlignment(target); |
| 3757 | 3794 | rp.setAlignment(alignment); |
| ... | ... | @@ -3781,8 +3818,26 @@ pub const FuncGen = struct { |
| 3781 | 3818 | _ = self.builder.buildRetVoid(); |
| 3782 | 3819 | return null; |
| 3783 | 3820 | } |
| 3821 | const fn_info = self.dg.decl.ty.fnInfo(); |
| 3822 | const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info); |
| 3784 | 3823 | const operand = try self.resolveInst(un_op); |
| 3785 | | _ = self.builder.buildRet(operand); |
| 3824 | const llvm_ret_ty = operand.typeOf(); |
| 3825 | if (abi_ret_ty == llvm_ret_ty) { |
| 3826 | _ = self.builder.buildRet(operand); |
| 3827 | return null; |
| 3828 | } |
| 3829 | |
| 3830 | const target = self.dg.module.getTarget(); |
| 3831 | const alignment = ret_ty.abiAlignment(target); |
| 3832 | const ptr_abi_ty = abi_ret_ty.pointerType(0); |
| 3833 | const rp = self.buildAlloca(llvm_ret_ty); |
| 3834 | rp.setAlignment(alignment); |
| 3835 | const store_inst = self.builder.buildStore(operand, rp); |
| 3836 | store_inst.setAlignment(alignment); |
| 3837 | const casted_ptr = self.builder.buildBitCast(rp, ptr_abi_ty, ""); |
| 3838 | const load_inst = self.builder.buildLoad(casted_ptr, ""); |
| 3839 | load_inst.setAlignment(alignment); |
| 3840 | _ = self.builder.buildRet(load_inst); |
| 3786 | 3841 | return null; |
| 3787 | 3842 | } |
| 3788 | 3843 | |
| ... | ... | @@ -3794,9 +3849,16 @@ pub const FuncGen = struct { |
| 3794 | 3849 | _ = self.builder.buildRetVoid(); |
| 3795 | 3850 | return null; |
| 3796 | 3851 | } |
| 3797 | | const target = self.dg.module.getTarget(); |
| 3798 | 3852 | const ptr = try self.resolveInst(un_op); |
| 3799 | | const loaded = self.builder.buildLoad(ptr, ""); |
| 3853 | const target = self.dg.module.getTarget(); |
| 3854 | const fn_info = self.dg.decl.ty.fnInfo(); |
| 3855 | const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info); |
| 3856 | const llvm_ret_ty = try self.dg.llvmType(ret_ty); |
| 3857 | const casted_ptr = if (abi_ret_ty == llvm_ret_ty) ptr else p: { |
| 3858 | const ptr_abi_ty = abi_ret_ty.pointerType(0); |
| 3859 | break :p self.builder.buildBitCast(ptr, ptr_abi_ty, ""); |
| 3860 | }; |
| 3861 | const loaded = self.builder.buildLoad(casted_ptr, ""); |
| 3800 | 3862 | loaded.setAlignment(ret_ty.abiAlignment(target)); |
| 3801 | 3863 | _ = self.builder.buildRet(loaded); |
| 3802 | 3864 | return null; |
| ... | ... | @@ -7711,20 +7773,211 @@ fn llvmFieldIndex( |
| 7711 | 7773 | return null; |
| 7712 | 7774 | } |
| 7713 | 7775 | } |
| 7776 | |
| 7714 | 7777 | fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { |
| 7715 | 7778 | switch (fn_info.cc) { |
| 7716 | 7779 | .Unspecified, .Inline => return isByRef(fn_info.return_type), |
| 7717 | | .C => {}, |
| 7780 | .C => switch (target.cpu.arch) { |
| 7781 | .mips, .mipsel => return false, |
| 7782 | .x86_64 => switch (target.os.tag) { |
| 7783 | .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory, |
| 7784 | else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory, |
| 7785 | }, |
| 7786 | else => return false, // TODO investigate C ABI for other architectures |
| 7787 | }, |
| 7718 | 7788 | else => return false, |
| 7719 | 7789 | } |
| 7720 | | const x86_64_abi = @import("../arch/x86_64/abi.zig"); |
| 7721 | | switch (target.cpu.arch) { |
| 7722 | | .mips, .mipsel => return false, |
| 7723 | | .x86_64 => switch (target.os.tag) { |
| 7724 | | .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory, |
| 7725 | | else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory, |
| 7790 | } |
| 7791 | |
| 7792 | /// In order to support the C calling convention, some return types need to be lowered |
| 7793 | /// completely differently in the function prototype to honor the C ABI, and then |
| 7794 | /// be effectively bitcasted to the actual return type. |
| 7795 | fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.Type { |
| 7796 | if (!fn_info.return_type.hasRuntimeBitsIgnoreComptime()) { |
| 7797 | return dg.context.voidType(); |
| 7798 | } |
| 7799 | const target = dg.module.getTarget(); |
| 7800 | switch (fn_info.cc) { |
| 7801 | .Unspecified, .Inline => { |
| 7802 | if (isByRef(fn_info.return_type)) { |
| 7803 | return dg.context.voidType(); |
| 7804 | } else { |
| 7805 | return dg.llvmType(fn_info.return_type); |
| 7806 | } |
| 7807 | }, |
| 7808 | .C => { |
| 7809 | const is_scalar = switch (fn_info.return_type.zigTypeTag()) { |
| 7810 | .Void, |
| 7811 | .Bool, |
| 7812 | .NoReturn, |
| 7813 | .Int, |
| 7814 | .Float, |
| 7815 | .Pointer, |
| 7816 | .Optional, |
| 7817 | .ErrorSet, |
| 7818 | .Enum, |
| 7819 | .AnyFrame, |
| 7820 | .Vector, |
| 7821 | => true, |
| 7822 | |
| 7823 | else => false, |
| 7824 | }; |
| 7825 | switch (target.cpu.arch) { |
| 7826 | .mips, .mipsel => return dg.llvmType(fn_info.return_type), |
| 7827 | .x86_64 => switch (target.os.tag) { |
| 7828 | .windows => switch (x86_64_abi.classifyWindows(fn_info.return_type, target)) { |
| 7829 | .integer => { |
| 7830 | if (is_scalar) { |
| 7831 | return dg.llvmType(fn_info.return_type); |
| 7832 | } else { |
| 7833 | const abi_size = fn_info.return_type.abiSize(target); |
| 7834 | return dg.context.intType(@intCast(c_uint, abi_size * 8)); |
| 7835 | } |
| 7836 | }, |
| 7837 | .memory => return dg.context.voidType(), |
| 7838 | .sse => return dg.llvmType(fn_info.return_type), |
| 7839 | else => unreachable, |
| 7840 | }, |
| 7841 | else => { |
| 7842 | if (is_scalar) { |
| 7843 | return dg.llvmType(fn_info.return_type); |
| 7844 | } |
| 7845 | const classes = x86_64_abi.classifySystemV(fn_info.return_type, target); |
| 7846 | if (classes[0] == .memory) { |
| 7847 | return dg.context.voidType(); |
| 7848 | } |
| 7849 | var llvm_types_buffer: [8]*const llvm.Type = undefined; |
| 7850 | var llvm_types_index: u32 = 0; |
| 7851 | for (classes) |class| { |
| 7852 | switch (class) { |
| 7853 | .integer => { |
| 7854 | llvm_types_buffer[llvm_types_index] = dg.context.intType(64); |
| 7855 | llvm_types_index += 1; |
| 7856 | }, |
| 7857 | .sse => { |
| 7858 | @panic("TODO"); |
| 7859 | }, |
| 7860 | .sseup => { |
| 7861 | @panic("TODO"); |
| 7862 | }, |
| 7863 | .x87 => { |
| 7864 | @panic("TODO"); |
| 7865 | }, |
| 7866 | .x87up => { |
| 7867 | @panic("TODO"); |
| 7868 | }, |
| 7869 | .complex_x87 => { |
| 7870 | @panic("TODO"); |
| 7871 | }, |
| 7872 | .memory => unreachable, // handled above |
| 7873 | .none => break, |
| 7874 | } |
| 7875 | } |
| 7876 | if (classes[0] == .integer and classes[1] == .none) { |
| 7877 | return llvm_types_buffer[0]; |
| 7878 | } |
| 7879 | return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); |
| 7880 | }, |
| 7881 | }, |
| 7882 | // TODO investigate C ABI for other architectures |
| 7883 | else => return dg.llvmType(fn_info.return_type), |
| 7884 | } |
| 7885 | }, |
| 7886 | else => return dg.llvmType(fn_info.return_type), |
| 7887 | } |
| 7888 | } |
| 7889 | |
| 7890 | fn lowerFnParamTy(dg: *DeclGen, cc: std.builtin.CallingConvention, ty: Type) !*const llvm.Type { |
| 7891 | assert(ty.hasRuntimeBitsIgnoreComptime()); |
| 7892 | const target = dg.module.getTarget(); |
| 7893 | switch (cc) { |
| 7894 | .Unspecified, .Inline => { |
| 7895 | const raw_llvm_ty = try dg.llvmType(ty); |
| 7896 | if (isByRef(ty)) { |
| 7897 | return raw_llvm_ty.pointerType(0); |
| 7898 | } else { |
| 7899 | return raw_llvm_ty; |
| 7900 | } |
| 7901 | }, |
| 7902 | .C => { |
| 7903 | const is_scalar = switch (ty.zigTypeTag()) { |
| 7904 | .Void, |
| 7905 | .Bool, |
| 7906 | .NoReturn, |
| 7907 | .Int, |
| 7908 | .Float, |
| 7909 | .Pointer, |
| 7910 | .Optional, |
| 7911 | .ErrorSet, |
| 7912 | .Enum, |
| 7913 | .AnyFrame, |
| 7914 | .Vector, |
| 7915 | => true, |
| 7916 | |
| 7917 | else => false, |
| 7918 | }; |
| 7919 | switch (target.cpu.arch) { |
| 7920 | .mips, .mipsel => return dg.llvmType(ty), |
| 7921 | .x86_64 => switch (target.os.tag) { |
| 7922 | .windows => switch (x86_64_abi.classifyWindows(ty, target)) { |
| 7923 | .integer => { |
| 7924 | if (is_scalar) { |
| 7925 | return dg.llvmType(ty); |
| 7926 | } else { |
| 7927 | const abi_size = ty.abiSize(target); |
| 7928 | return dg.context.intType(@intCast(c_uint, abi_size * 8)); |
| 7929 | } |
| 7930 | }, |
| 7931 | .memory => return (try dg.llvmType(ty)).pointerType(0), |
| 7932 | .sse => return dg.llvmType(ty), |
| 7933 | else => unreachable, |
| 7934 | }, |
| 7935 | else => { |
| 7936 | if (is_scalar) { |
| 7937 | return dg.llvmType(ty); |
| 7938 | } |
| 7939 | const classes = x86_64_abi.classifySystemV(ty, target); |
| 7940 | if (classes[0] == .memory) { |
| 7941 | return (try dg.llvmType(ty)).pointerType(0); |
| 7942 | } |
| 7943 | var llvm_types_buffer: [8]*const llvm.Type = undefined; |
| 7944 | var llvm_types_index: u32 = 0; |
| 7945 | for (classes) |class| { |
| 7946 | switch (class) { |
| 7947 | .integer => { |
| 7948 | llvm_types_buffer[llvm_types_index] = dg.context.intType(64); |
| 7949 | llvm_types_index += 1; |
| 7950 | }, |
| 7951 | .sse => { |
| 7952 | @panic("TODO"); |
| 7953 | }, |
| 7954 | .sseup => { |
| 7955 | @panic("TODO"); |
| 7956 | }, |
| 7957 | .x87 => { |
| 7958 | @panic("TODO"); |
| 7959 | }, |
| 7960 | .x87up => { |
| 7961 | @panic("TODO"); |
| 7962 | }, |
| 7963 | .complex_x87 => { |
| 7964 | @panic("TODO"); |
| 7965 | }, |
| 7966 | .memory => unreachable, // handled above |
| 7967 | .none => break, |
| 7968 | } |
| 7969 | } |
| 7970 | if (classes[0] == .integer and classes[1] == .none) { |
| 7971 | return llvm_types_buffer[0]; |
| 7972 | } |
| 7973 | return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); |
| 7974 | }, |
| 7975 | }, |
| 7976 | // TODO investigate C ABI for other architectures |
| 7977 | else => return dg.llvmType(ty), |
| 7978 | } |
| 7726 | 7979 | }, |
| 7727 | | else => return false, // TODO investigate C ABI for other architectures |
| 7980 | else => return dg.llvmType(ty), |
| 7728 | 7981 | } |
| 7729 | 7982 | } |
| 7730 | 7983 | |