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 {...@@ -106,15 +106,15 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class {
106 return result;106 return result;
107 },107 },
108 128 => {108 128 => {
109 // "Arguments of types__float128,_Decimal128and__m128are109 // "Arguments of types__float128, _Decimal128 and__m128 are
110 // split into two halves. The least significant ones belong110 // 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."
112 result[0] = .sse;112 result[0] = .sse;
113 result[1] = .sseup;113 result[1] = .sseup;
114 return result;114 return result;
115 },115 },
116 else => {116 else => {
117 // "The 64-bit mantissa of arguments of typelong double117 // "The 64-bit mantissa of arguments of type long double
118 // belongs to classX87, the 16-bit exponent plus 6 bytes118 // belongs to classX87, the 16-bit exponent plus 6 bytes
119 // of padding belongs to class X87UP."119 // of padding belongs to class X87UP."
120 result[0] = .x87;120 result[0] = .x87;
src/codegen/llvm.zig+287-34
...@@ -21,6 +21,7 @@ const Value = @import("../value.zig").Value;...@@ -21,6 +21,7 @@ const Value = @import("../value.zig").Value;
21const Type = @import("../type.zig").Type;21const Type = @import("../type.zig").Type;
22const LazySrcLoc = Module.LazySrcLoc;22const LazySrcLoc = Module.LazySrcLoc;
23const CType = @import("../type.zig").CType;23const CType = @import("../type.zig").CType;
24const x86_64_abi = @import("../arch/x86_64/abi.zig");
2425
25const Error = error{ OutOfMemory, CodegenFail };26const Error = error{ OutOfMemory, CodegenFail };
2627
...@@ -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;
24012396
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();
24042399
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 }
24082404
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;
24112407
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 }
24162410
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 };
37063700
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;
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;
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 }
37193734
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 }
37373752
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 }
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
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 }
37883843
...@@ -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
7714fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool {7777fn 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,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 }
7726 },7979 },
7727 else => return false, // TODO investigate C ABI for other architectures7980 else => return dg.llvmType(ty),
7728 }7981 }
7729}7982}
77307983
src/translate_c.zig+8-8
...@@ -384,16 +384,16 @@ pub fn translate(...@@ -384,16 +384,16 @@ pub fn translate(
384384
385 // For memory that has the same lifetime as the Ast that we return385 // For memory that has the same lifetime as the Ast that we return
386 // from this function.386 // from this function.
387 var arena = std.heap.ArenaAllocator.init(gpa);387 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
388 errdefer arena.deinit();388 errdefer arena_allocator.deinit();
389 const arena_allocator = arena.allocator();389 const arena = arena_allocator.allocator();
390390
391 var context = Context{391 var context = Context{
392 .gpa = gpa,392 .gpa = gpa,
393 .arena = arena_allocator,393 .arena = arena,
394 .source_manager = ast_unit.getSourceManager(),394 .source_manager = ast_unit.getSourceManager(),
395 .alias_list = AliasList.init(gpa),395 .alias_list = AliasList.init(gpa),
396 .global_scope = try arena_allocator.create(Scope.Root),396 .global_scope = try arena.create(Scope.Root),
397 .clang_context = ast_unit.getASTContext(),397 .clang_context = ast_unit.getASTContext(),
398 .pattern_list = try PatternList.init(gpa),398 .pattern_list = try PatternList.init(gpa),
399 .zig_is_stage1 = zig_is_stage1,399 .zig_is_stage1 = zig_is_stage1,
...@@ -412,9 +412,9 @@ pub fn translate(...@@ -412,9 +412,9 @@ pub fn translate(
412412
413 inline for (@typeInfo(std.zig.c_builtins).Struct.decls) |decl| {413 inline for (@typeInfo(std.zig.c_builtins).Struct.decls) |decl| {
414 if (decl.is_pub) {414 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, .{
416 .name = decl.name,416 .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),
418 });418 });
419 try addTopLevelDecl(&context, decl.name, builtin);419 try addTopLevelDecl(&context, decl.name, builtin);
420 }420 }
...@@ -431,7 +431,7 @@ pub fn translate(...@@ -431,7 +431,7 @@ pub fn translate(
431 try addMacros(&context);431 try addMacros(&context);
432 for (context.alias_list.items) |alias| {432 for (context.alias_list.items) |alias| {
433 if (!context.global_scope.sym_table.contains(alias.alias)) {433 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 });
435 try addTopLevelDecl(&context, alias.alias, node);435 try addTopLevelDecl(&context, alias.alias, node);
436 }436 }
437 }437 }
src/zig_clang.h+2-1
...@@ -1050,7 +1050,8 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangLexer_getLocForEndOfToken(str...@@ -1050,7 +1050,8 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangLexer_getLocForEndOfToken(str
1050 const ZigClangSourceManager *, const ZigClangASTUnit *);1050 const ZigClangSourceManager *, const ZigClangASTUnit *);
10511051
1052// Can return null.1052// 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,
1054 struct Stage2ErrorMsg **errors_ptr, size_t *errors_len, const char *resources_path);1055 struct Stage2ErrorMsg **errors_ptr, size_t *errors_len, const char *resources_path);
1055ZIG_EXTERN_C void ZigClangASTUnit_delete(struct ZigClangASTUnit *);1056ZIG_EXTERN_C void ZigClangASTUnit_delete(struct ZigClangASTUnit *);
1056ZIG_EXTERN_C void ZigClangErrorMsg_delete(struct Stage2ErrorMsg *ptr, size_t len);1057ZIG_EXTERN_C void ZigClangErrorMsg_delete(struct Stage2ErrorMsg *ptr, size_t len);