authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-04-21 20:22:41-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-04-21 20:27:06-07:00
log9c5fe5b5a435729f2bfc61a45dc6ebd0969faf89
tree7cc5036a66a18b7e43388c9d1c99f581c908fd9b
parent804d0661f5afdc1ca10a3f6d127a7631bcfa940b

LLVM: C calling convention lowering fixes

For parameters and return types of functions with the C calling convention, the LLVM backend now has a special lowering for the function type that makes the function adhere to the C ABI. The AIR instruction lowerings for call, ret, and ret_load are adjusted to bitcast the real type to the ABI type if necessary. More work on this will need to be done, however, this improvement is enough that stage3 now passes all the same behavior tests that stage2 passes - notably, translate-c no longer has a segfault due to C ABI issues with Zig's Clang C API wrapper.

4 files changed, 300 insertions(+), 46 deletions(-)

src/arch/x86_64/abi.zig+3-3
......@@ -106,15 +106,15 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class {
106106 return result;
107107 },
108108 128 => {
109 // "Arguments of types__float128,_Decimal128and__m128are
109 // "Arguments of types__float128, _Decimal128 and__m128 are
110110 // split into two halves. The least significant ones belong
111 // to class SSE, the mostsignificant one to class SSEUP."
111 // to class SSE, the most significant one to class SSEUP."
112112 result[0] = .sse;
113113 result[1] = .sseup;
114114 return result;
115115 },
116116 else => {
117 // "The 64-bit mantissa of arguments of typelong double
117 // "The 64-bit mantissa of arguments of type long double
118118 // belongs to classX87, the 16-bit exponent plus 6 bytes
119119 // of padding belongs to class X87UP."
120120 result[0] = .x87;
src/codegen/llvm.zig+287-34
......@@ -21,6 +21,7 @@ const Value = @import("../value.zig").Value;
2121const Type = @import("../type.zig").Type;
2222const LazySrcLoc = Module.LazySrcLoc;
2323const CType = @import("../type.zig").CType;
24const x86_64_abi = @import("../arch/x86_64/abi.zig");
2425
2526const Error = error{ OutOfMemory, CodegenFail };
2627
......@@ -2391,27 +2392,20 @@ pub const DeclGen = struct {
23912392 },
23922393 .Fn => {
23932394 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);
24012396
24022397 var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa);
24032398 defer llvm_params.deinit();
24042399
2405 if (sret) {
2400 if (firstParamSRet(fn_info, target)) {
2401 const llvm_sret_ty = try dg.llvmType(fn_info.return_type);
24062402 try llvm_params.append(llvm_sret_ty.pointerType(0));
24072403 }
24082404
24092405 for (fn_info.param_types) |param_ty| {
24102406 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
24112407
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));
24152409 }
24162410
24172411 return llvm.functionType(
......@@ -3704,24 +3698,45 @@ pub const FuncGen = struct {
37043698 break :blk ret_ptr;
37053699 };
37063700
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;
37153704
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;
37173711 }
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);
37183733 }
37193734
37203735 const call = self.builder.buildCall(
37213736 llvm_fn,
37223737 llvm_args.items.ptr,
37233738 @intCast(c_uint, llvm_args.items.len),
3724 toLlvmCallConv(zig_fn_ty.fnCallingConvention(), target),
3739 toLlvmCallConv(fn_info.cc, target),
37253740 attr,
37263741 "",
37273742 );
......@@ -3735,8 +3750,9 @@ pub const FuncGen = struct {
37353750 return null;
37363751 }
37373752
3753 const llvm_ret_ty = try self.dg.llvmType(return_type);
3754
37383755 if (ret_ptr) |rp| {
3739 const llvm_ret_ty = try self.dg.llvmType(return_type);
37403756 call.setCallSret(llvm_ret_ty);
37413757 if (isByRef(return_type)) {
37423758 return rp;
......@@ -3748,10 +3764,31 @@ pub const FuncGen = struct {
37483764 }
37493765 }
37503766
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
37513789 if (isByRef(return_type)) {
37523790 // our by-ref status disagrees with sret so we must allocate, store,
37533791 // and return the allocation pointer.
3754 const llvm_ret_ty = try self.dg.llvmType(return_type);
37553792 const rp = self.buildAlloca(llvm_ret_ty);
37563793 const alignment = return_type.abiAlignment(target);
37573794 rp.setAlignment(alignment);
......@@ -3781,8 +3818,26 @@ pub const FuncGen = struct {
37813818 _ = self.builder.buildRetVoid();
37823819 return null;
37833820 }
3821 const fn_info = self.dg.decl.ty.fnInfo();
3822 const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info);
37843823 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);
37863841 return null;
37873842 }
37883843
......@@ -3794,9 +3849,16 @@ pub const FuncGen = struct {
37943849 _ = self.builder.buildRetVoid();
37953850 return null;
37963851 }
3797 const target = self.dg.module.getTarget();
37983852 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, "");
38003862 loaded.setAlignment(ret_ty.abiAlignment(target));
38013863 _ = self.builder.buildRet(loaded);
38023864 return null;
......@@ -7711,20 +7773,211 @@ fn llvmFieldIndex(
77117773 return null;
77127774 }
77137775}
7776
77147777fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool {
77157778 switch (fn_info.cc) {
77167779 .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 },
77187788 else => return false,
77197789 }
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.
7795fn 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
7890fn 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 }
77267979 },
7727 else => return false, // TODO investigate C ABI for other architectures
7980 else => return dg.llvmType(ty),
77287981 }
77297982}
77307983
src/translate_c.zig+8-8
......@@ -384,16 +384,16 @@ pub fn translate(
384384
385385 // For memory that has the same lifetime as the Ast that we return
386386 // from this function.
387 var arena = std.heap.ArenaAllocator.init(gpa);
388 errdefer arena.deinit();
389 const arena_allocator = arena.allocator();
387 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
388 errdefer arena_allocator.deinit();
389 const arena = arena_allocator.allocator();
390390
391391 var context = Context{
392392 .gpa = gpa,
393 .arena = arena_allocator,
393 .arena = arena,
394394 .source_manager = ast_unit.getSourceManager(),
395395 .alias_list = AliasList.init(gpa),
396 .global_scope = try arena_allocator.create(Scope.Root),
396 .global_scope = try arena.create(Scope.Root),
397397 .clang_context = ast_unit.getASTContext(),
398398 .pattern_list = try PatternList.init(gpa),
399399 .zig_is_stage1 = zig_is_stage1,
......@@ -412,9 +412,9 @@ pub fn translate(
412412
413413 inline for (@typeInfo(std.zig.c_builtins).Struct.decls) |decl| {
414414 if (decl.is_pub) {
415 const builtin = try Tag.pub_var_simple.create(context.arena, .{
415 const builtin = try Tag.pub_var_simple.create(arena, .{
416416 .name = decl.name,
417 .init = try Tag.import_c_builtin.create(context.arena, decl.name),
417 .init = try Tag.import_c_builtin.create(arena, decl.name),
418418 });
419419 try addTopLevelDecl(&context, decl.name, builtin);
420420 }
......@@ -431,7 +431,7 @@ pub fn translate(
431431 try addMacros(&context);
432432 for (context.alias_list.items) |alias| {
433433 if (!context.global_scope.sym_table.contains(alias.alias)) {
434 const node = try Tag.alias.create(context.arena, .{ .actual = alias.alias, .mangled = alias.name });
434 const node = try Tag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name });
435435 try addTopLevelDecl(&context, alias.alias, node);
436436 }
437437 }
src/zig_clang.h+2-1
......@@ -1050,7 +1050,8 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangLexer_getLocForEndOfToken(str
10501050 const ZigClangSourceManager *, const ZigClangASTUnit *);
10511051
10521052// Can return null.
1053ZIG_EXTERN_C struct ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char **args_end,
1053ZIG_EXTERN_C struct ZigClangASTUnit *ZigClangLoadFromCommandLine(
1054 const char **args_begin, const char **args_end,
10541055 struct Stage2ErrorMsg **errors_ptr, size_t *errors_len, const char *resources_path);
10551056ZIG_EXTERN_C void ZigClangASTUnit_delete(struct ZigClangASTUnit *);
10561057ZIG_EXTERN_C void ZigClangErrorMsg_delete(struct Stage2ErrorMsg *ptr, size_t len);