| ... | @@ -21,6 +21,7 @@ const Value = @import("../value.zig").Value; | ... | @@ -21,6 +21,7 @@ const Value = @import("../value.zig").Value; |
| 21 | const Type = @import("../type.zig").Type; | 21 | const Type = @import("../type.zig").Type; |
| 22 | const LazySrcLoc = Module.LazySrcLoc; | 22 | const LazySrcLoc = Module.LazySrcLoc; |
| 23 | const CType = @import("../type.zig").CType; | 23 | const CType = @import("../type.zig").CType; |
| | 24 | const x86_64_abi = @import("../arch/x86_64/abi.zig"); |
| 24 | | 25 | |
| 25 | const Error = error{ OutOfMemory, CodegenFail }; | 26 | const Error = error{ OutOfMemory, CodegenFail }; |
| 26 | | 27 | |
| ... | @@ -2391,27 +2392,20 @@ pub const DeclGen = struct { | ... | @@ -2391,27 +2392,20 @@ pub const DeclGen = struct { |
| 2391 | }, | 2392 | }, |
| 2392 | .Fn => { | 2393 | .Fn => { |
| 2393 | const fn_info = t.fnInfo(); | 2394 | const fn_info = t.fnInfo(); |
| 2394 | const sret = firstParamSRet(fn_info, target); | 2395 | const llvm_ret_ty = try lowerFnRetTy(dg, fn_info); |
| 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; | | |
| 2401 | | 2396 | |
| 2402 | var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa); | 2397 | var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa); |
| 2403 | defer llvm_params.deinit(); | 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 | try llvm_params.append(llvm_sret_ty.pointerType(0)); | 2402 | try llvm_params.append(llvm_sret_ty.pointerType(0)); |
| 2407 | } | 2403 | } |
| 2408 | | 2404 | |
| 2409 | for (fn_info.param_types) |param_ty| { | 2405 | for (fn_info.param_types) |param_ty| { |
| 2410 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; | 2406 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 2411 | | 2407 | |
| 2412 | const raw_llvm_ty = try dg.llvmType(param_ty); | 2408 | try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, 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); | | |
| 2415 | } | 2409 | } |
| 2416 | | 2410 | |
| 2417 | return llvm.functionType( | 2411 | return llvm.functionType( |
| ... | @@ -3704,24 +3698,45 @@ pub const FuncGen = struct { | ... | @@ -3704,24 +3698,45 @@ pub const FuncGen = struct { |
| 3704 | break :blk ret_ptr; | 3698 | break :blk ret_ptr; |
| 3705 | }; | 3699 | }; |
| 3706 | | 3700 | |
| 3707 | if (fn_info.is_var_args) { | 3701 | for (args) |arg| { |
| 3708 | for (args) |arg| { | 3702 | const param_ty = self.air.typeOf(arg); |
| 3709 | try llvm_args.append(try self.resolveInst(arg)); | 3703 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 3710 | } | | |
| 3711 | } else { | | |
| 3712 | for (args) |arg, i| { | | |
| 3713 | const param_ty = fn_info.param_types[i]; | | |
| 3714 | 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 | const call = self.builder.buildCall( | 3735 | const call = self.builder.buildCall( |
| 3721 | llvm_fn, | 3736 | llvm_fn, |
| 3722 | llvm_args.items.ptr, | 3737 | llvm_args.items.ptr, |
| 3723 | @intCast(c_uint, llvm_args.items.len), | 3738 | @intCast(c_uint, llvm_args.items.len), |
| 3724 | toLlvmCallConv(zig_fn_ty.fnCallingConvention(), target), | 3739 | toLlvmCallConv(fn_info.cc, target), |
| 3725 | attr, | 3740 | attr, |
| 3726 | "", | 3741 | "", |
| 3727 | ); | 3742 | ); |
| ... | @@ -3735,8 +3750,9 @@ pub const FuncGen = struct { | ... | @@ -3735,8 +3750,9 @@ pub const FuncGen = struct { |
| 3735 | return null; | 3750 | return null; |
| 3736 | } | 3751 | } |
| 3737 | | 3752 | |
| | 3753 | const llvm_ret_ty = try self.dg.llvmType(return_type); |
| | 3754 | |
| 3738 | if (ret_ptr) |rp| { | 3755 | if (ret_ptr) |rp| { |
| 3739 | const llvm_ret_ty = try self.dg.llvmType(return_type); | | |
| 3740 | call.setCallSret(llvm_ret_ty); | 3756 | call.setCallSret(llvm_ret_ty); |
| 3741 | if (isByRef(return_type)) { | 3757 | if (isByRef(return_type)) { |
| 3742 | return rp; | 3758 | return rp; |
| ... | @@ -3748,10 +3764,31 @@ pub const FuncGen = struct { | ... | @@ -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 | if (isByRef(return_type)) { | 3789 | if (isByRef(return_type)) { |
| 3752 | // our by-ref status disagrees with sret so we must allocate, store, | 3790 | // our by-ref status disagrees with sret so we must allocate, store, |
| 3753 | // and return the allocation pointer. | 3791 | // and return the allocation pointer. |
| 3754 | const llvm_ret_ty = try self.dg.llvmType(return_type); | | |
| 3755 | const rp = self.buildAlloca(llvm_ret_ty); | 3792 | const rp = self.buildAlloca(llvm_ret_ty); |
| 3756 | const alignment = return_type.abiAlignment(target); | 3793 | const alignment = return_type.abiAlignment(target); |
| 3757 | rp.setAlignment(alignment); | 3794 | rp.setAlignment(alignment); |
| ... | @@ -3781,8 +3818,26 @@ pub const FuncGen = struct { | ... | @@ -3781,8 +3818,26 @@ pub const FuncGen = struct { |
| 3781 | _ = self.builder.buildRetVoid(); | 3818 | _ = self.builder.buildRetVoid(); |
| 3782 | return null; | 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 | const operand = try self.resolveInst(un_op); | 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 | return null; | 3841 | return null; |
| 3787 | } | 3842 | } |
| 3788 | | 3843 | |
| ... | @@ -3794,9 +3849,16 @@ pub const FuncGen = struct { | ... | @@ -3794,9 +3849,16 @@ pub const FuncGen = struct { |
| 3794 | _ = self.builder.buildRetVoid(); | 3849 | _ = self.builder.buildRetVoid(); |
| 3795 | return null; | 3850 | return null; |
| 3796 | } | 3851 | } |
| 3797 | const target = self.dg.module.getTarget(); | | |
| 3798 | const ptr = try self.resolveInst(un_op); | 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 | loaded.setAlignment(ret_ty.abiAlignment(target)); | 3862 | loaded.setAlignment(ret_ty.abiAlignment(target)); |
| 3801 | _ = self.builder.buildRet(loaded); | 3863 | _ = self.builder.buildRet(loaded); |
| 3802 | return null; | 3864 | return null; |
| ... | @@ -7711,20 +7773,211 @@ fn llvmFieldIndex( | ... | @@ -7711,20 +7773,211 @@ fn llvmFieldIndex( |
| 7711 | return null; | 7773 | return null; |
| 7712 | } | 7774 | } |
| 7713 | } | 7775 | } |
| | 7776 | |
| 7714 | fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { | 7777 | fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { |
| 7715 | switch (fn_info.cc) { | 7778 | switch (fn_info.cc) { |
| 7716 | .Unspecified, .Inline => return isByRef(fn_info.return_type), | 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 | else => return false, | 7788 | else => return false, |
| 7719 | } | 7789 | } |
| 7720 | const x86_64_abi = @import("../arch/x86_64/abi.zig"); | 7790 | } |
| 7721 | switch (target.cpu.arch) { | 7791 | |
| 7722 | .mips, .mipsel => return false, | 7792 | /// In order to support the C calling convention, some return types need to be lowered |
| 7723 | .x86_64 => switch (target.os.tag) { | 7793 | /// completely differently in the function prototype to honor the C ABI, and then |
| 7724 | .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory, | 7794 | /// be effectively bitcasted to the actual return type. |
| 7725 | else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory, | 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 | |