authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-20 20:14:03+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-20 20:25:12+02:00
logd968d9d10329234afc1d7743bb9981245695ca2b
treef856364f1a31d9bb5615ddc1ed600a26949e7cca
parent9e276d32f3f8d980e4bf4c0a52df0fa97717aacb

llvm: add attributes to the arguments of function pointer calls

Closes #13605

6 files changed, 152 insertions(+), 11 deletions(-)

src/codegen/llvm.zig+64-3
......@@ -4700,9 +4700,9 @@ pub const FuncGen = struct {
47004700 break :blk ret_ptr;
47014701 };
47024702
4703 if (fn_info.return_type.isError() and
4704 self.dg.module.comp.bin_file.options.error_return_tracing)
4705 {
4703 const err_return_tracing = fn_info.return_type.isError() and
4704 self.dg.module.comp.bin_file.options.error_return_tracing;
4705 if (err_return_tracing) {
47064706 try llvm_args.append(self.err_ret_trace.?);
47074707 }
47084708
......@@ -4890,6 +4890,66 @@ pub const FuncGen = struct {
48904890 "",
48914891 );
48924892
4893 if (callee_ty.zigTypeTag() == .Pointer) {
4894 // Add argument attributes for function pointer calls.
4895 it = iterateParamTypes(self.dg, fn_info);
4896 it.llvm_index += @boolToInt(sret);
4897 it.llvm_index += @boolToInt(err_return_tracing);
4898 while (it.next()) |lowering| switch (lowering) {
4899 .byval => {
4900 const param_index = it.zig_index - 1;
4901 const param_ty = fn_info.param_types[param_index];
4902 if (!isByRef(param_ty)) {
4903 self.dg.addByValParamAttrs(call, param_ty, param_index, fn_info, it.llvm_index - 1);
4904 }
4905 },
4906 .byref => {
4907 const param_index = it.zig_index - 1;
4908 const param_ty = fn_info.param_types[param_index];
4909 const param_llvm_ty = try self.dg.lowerType(param_ty);
4910 const alignment = param_ty.abiAlignment(target);
4911 self.dg.addByRefParamAttrs(call, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty);
4912 },
4913 .byref_mut => {
4914 self.dg.addArgAttr(call, it.llvm_index - 1, "noundef");
4915 },
4916 // No attributes needed for these.
4917 .no_bits,
4918 .abi_sized_int,
4919 .multiple_llvm_types,
4920 .as_u16,
4921 .float_array,
4922 .i32_array,
4923 .i64_array,
4924 => continue,
4925
4926 .slice => {
4927 assert(!it.byval_attr);
4928 const param_ty = fn_info.param_types[it.zig_index - 1];
4929 const ptr_info = param_ty.ptrInfo().data;
4930 const llvm_arg_i = it.llvm_index - 2;
4931
4932 if (math.cast(u5, it.zig_index - 1)) |i| {
4933 if (@truncate(u1, fn_info.noalias_bits >> i) != 0) {
4934 self.dg.addArgAttr(call, llvm_arg_i, "noalias");
4935 }
4936 }
4937 if (param_ty.zigTypeTag() != .Optional) {
4938 self.dg.addArgAttr(call, llvm_arg_i, "nonnull");
4939 }
4940 if (!ptr_info.mutable) {
4941 self.dg.addArgAttr(call, llvm_arg_i, "readonly");
4942 }
4943 if (ptr_info.@"align" != 0) {
4944 self.dg.addArgAttrInt(call, llvm_arg_i, "align", ptr_info.@"align");
4945 } else {
4946 const elem_align = @max(ptr_info.pointee_type.abiAlignment(target), 1);
4947 self.dg.addArgAttrInt(call, llvm_arg_i, "align", elem_align);
4948 }
4949 },
4950 };
4951 }
4952
48934953 if (return_type.isNoReturn() and attr != .AlwaysTail) {
48944954 _ = self.builder.buildUnreachable();
48954955 return null;
......@@ -10469,6 +10529,7 @@ const ParamTypeIterator = struct {
1046910529 it.llvm_index += 1;
1047010530 var buf: Type.Payload.ElemType = undefined;
1047110531 if (ty.isSlice() or (ty.zigTypeTag() == .Optional and ty.optionalChild(&buf).isSlice())) {
10532 it.llvm_index += 1;
1047210533 return .slice;
1047310534 } else if (isByRef(ty)) {
1047410535 return .byref;
src/codegen/llvm/bindings.zig+2-2
......@@ -88,8 +88,8 @@ pub const Context = opaque {
8888};
8989
9090pub const Value = opaque {
91 pub const addAttributeAtIndex = LLVMAddAttributeAtIndex;
92 extern fn LLVMAddAttributeAtIndex(*Value, Idx: AttributeIndex, A: *Attribute) void;
91 pub const addAttributeAtIndex = ZigLLVMAddAttributeAtIndex;
92 extern fn ZigLLVMAddAttributeAtIndex(*Value, Idx: AttributeIndex, A: *Attribute) void;
9393
9494 pub const removeEnumAttributeAtIndex = LLVMRemoveEnumAttributeAtIndex;
9595 extern fn LLVMRemoveEnumAttributeAtIndex(F: *Value, Idx: AttributeIndex, KindID: c_uint) void;
src/zig_llvm.cpp+24-6
......@@ -444,6 +444,15 @@ LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
444444 return wrap(call_inst);
445445}
446446
447void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef A) {
448 if (isa<Function>(unwrap(Val))) {
449 unwrap<Function>(Val)->addAttributeAtIndex(Idx, unwrap(A));
450 } else {
451 unwrap<CallInst>(Val)->addAttributeAtIndex(Idx, unwrap(A));
452 }
453}
454
455
447456LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign,
448457 LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool isVolatile)
449458{
......@@ -1065,12 +1074,21 @@ void ZigLLVMSetFastMath(LLVMBuilderRef builder_wrapped, bool on_state) {
10651074 }
10661075}
10671076
1068void ZigLLVMAddByValAttr(LLVMValueRef fn_ref, unsigned ArgNo, LLVMTypeRef type_val) {
1069 Function *func = unwrap<Function>(fn_ref);
1070 AttrBuilder attr_builder(func->getContext());
1071 Type *llvm_type = unwrap<Type>(type_val);
1072 attr_builder.addByValAttr(llvm_type);
1073 func->addParamAttrs(ArgNo, attr_builder);
1077void ZigLLVMAddByValAttr(LLVMValueRef Val, unsigned ArgNo, LLVMTypeRef type_val) {
1078 if (isa<Function>(unwrap(Val))) {
1079 Function *func = unwrap<Function>(Val);
1080 AttrBuilder attr_builder(func->getContext());
1081 Type *llvm_type = unwrap<Type>(type_val);
1082 attr_builder.addByValAttr(llvm_type);
1083 func->addParamAttrs(ArgNo, attr_builder);
1084 } else {
1085 CallInst *call = unwrap<CallInst>(Val);
1086 AttrBuilder attr_builder(call->getContext());
1087 Type *llvm_type = unwrap<Type>(type_val);
1088 attr_builder.addByValAttr(llvm_type);
1089 // NOTE: +1 here since index 0 refers to the return value
1090 call->addAttributeAtIndex(ArgNo + 1, attr_builder.getAttribute(Attribute::ByVal));
1091 }
10741092}
10751093
10761094void ZigLLVMAddSretAttr(LLVMValueRef fn_ref, LLVMTypeRef type_val) {
src/zig_llvm.h+2
......@@ -129,6 +129,8 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMTypeRef functio
129129 LLVMValueRef Fn, LLVMValueRef *Args, unsigned NumArgs, enum ZigLLVM_CallingConv CC,
130130 enum ZigLLVM_CallAttr attr, const char *Name);
131131
132ZIG_EXTERN_C void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef A);
133
132134ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign,
133135 LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool isVolatile);
134136
test/c_abi/cfuncs.c+29
......@@ -842,3 +842,32 @@ struct ByRef c_modify_by_ref_param(struct ByRef in) {
842842 in.val = 42;
843843 return in;
844844}
845
846struct ByVal {
847 struct {
848 unsigned long x;
849 unsigned long y;
850 unsigned long z;
851 } origin;
852 struct {
853 unsigned long width;
854 unsigned long height;
855 unsigned long depth;
856 } size;
857};
858
859void c_func_ptr_byval(void *a, void *b, struct ByVal in, unsigned long c, void *d, unsigned long e) {
860 assert_or_panic((intptr_t)a == 1);
861 assert_or_panic((intptr_t)b == 2);
862
863 assert_or_panic(in.origin.x == 9);
864 assert_or_panic(in.origin.y == 10);
865 assert_or_panic(in.origin.z == 11);
866 assert_or_panic(in.size.width == 12);
867 assert_or_panic(in.size.height == 13);
868 assert_or_panic(in.size.depth == 14);
869
870 assert_or_panic(c == 3);
871 assert_or_panic((intptr_t)d == 4);
872 assert_or_panic(e == 5);
873}
test/c_abi/main.zig+31
......@@ -1001,3 +1001,34 @@ test "C function modifies by ref param" {
10011001 const res = c_modify_by_ref_param(.{ .val = 1, .arr = undefined });
10021002 try expect(res.val == 42);
10031003}
1004
1005const ByVal = extern struct {
1006 origin: extern struct {
1007 x: c_ulong,
1008 y: c_ulong,
1009 z: c_ulong,
1010 },
1011 size: extern struct {
1012 width: c_ulong,
1013 height: c_ulong,
1014 depth: c_ulong,
1015 },
1016};
1017
1018extern fn c_func_ptr_byval(*anyopaque, *anyopaque, ByVal, c_ulong, *anyopaque, c_ulong) void;
1019test "C function that takes byval struct called via function pointer" {
1020 if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
1021
1022 var fn_ptr = &c_func_ptr_byval;
1023 fn_ptr(
1024 @intToPtr(*anyopaque, 1),
1025 @intToPtr(*anyopaque, 2),
1026 ByVal{
1027 .origin = .{ .x = 9, .y = 10, .z = 11 },
1028 .size = .{ .width = 12, .height = 13, .depth = 14 },
1029 },
1030 @as(c_ulong, 3),
1031 @intToPtr(*anyopaque, 4),
1032 @as(c_ulong, 5),
1033 );
1034}