diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a629624c58..8459920d61 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4700,9 +4700,9 @@ pub const FuncGen = struct { break :blk ret_ptr; }; - if (fn_info.return_type.isError() and - self.dg.module.comp.bin_file.options.error_return_tracing) - { + const err_return_tracing = fn_info.return_type.isError() and + self.dg.module.comp.bin_file.options.error_return_tracing; + if (err_return_tracing) { try llvm_args.append(self.err_ret_trace.?); } @@ -4890,6 +4890,66 @@ pub const FuncGen = struct { "", ); + if (callee_ty.zigTypeTag() == .Pointer) { + // Add argument attributes for function pointer calls. + it = iterateParamTypes(self.dg, fn_info); + it.llvm_index += @boolToInt(sret); + it.llvm_index += @boolToInt(err_return_tracing); + while (it.next()) |lowering| switch (lowering) { + .byval => { + const param_index = it.zig_index - 1; + const param_ty = fn_info.param_types[param_index]; + if (!isByRef(param_ty)) { + self.dg.addByValParamAttrs(call, param_ty, param_index, fn_info, it.llvm_index - 1); + } + }, + .byref => { + const param_index = it.zig_index - 1; + const param_ty = fn_info.param_types[param_index]; + const param_llvm_ty = try self.dg.lowerType(param_ty); + const alignment = param_ty.abiAlignment(target); + self.dg.addByRefParamAttrs(call, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty); + }, + .byref_mut => { + self.dg.addArgAttr(call, it.llvm_index - 1, "noundef"); + }, + // No attributes needed for these. + .no_bits, + .abi_sized_int, + .multiple_llvm_types, + .as_u16, + .float_array, + .i32_array, + .i64_array, + => continue, + + .slice => { + assert(!it.byval_attr); + const param_ty = fn_info.param_types[it.zig_index - 1]; + const ptr_info = param_ty.ptrInfo().data; + const llvm_arg_i = it.llvm_index - 2; + + if (math.cast(u5, it.zig_index - 1)) |i| { + if (@truncate(u1, fn_info.noalias_bits >> i) != 0) { + self.dg.addArgAttr(call, llvm_arg_i, "noalias"); + } + } + if (param_ty.zigTypeTag() != .Optional) { + self.dg.addArgAttr(call, llvm_arg_i, "nonnull"); + } + if (!ptr_info.mutable) { + self.dg.addArgAttr(call, llvm_arg_i, "readonly"); + } + if (ptr_info.@"align" != 0) { + self.dg.addArgAttrInt(call, llvm_arg_i, "align", ptr_info.@"align"); + } else { + const elem_align = @max(ptr_info.pointee_type.abiAlignment(target), 1); + self.dg.addArgAttrInt(call, llvm_arg_i, "align", elem_align); + } + }, + }; + } + if (return_type.isNoReturn() and attr != .AlwaysTail) { _ = self.builder.buildUnreachable(); return null; @@ -10469,6 +10529,7 @@ const ParamTypeIterator = struct { it.llvm_index += 1; var buf: Type.Payload.ElemType = undefined; if (ty.isSlice() or (ty.zigTypeTag() == .Optional and ty.optionalChild(&buf).isSlice())) { + it.llvm_index += 1; return .slice; } else if (isByRef(ty)) { return .byref; diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 1b462312cd..90d0f51c7b 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -88,8 +88,8 @@ pub const Context = opaque { }; pub const Value = opaque { - pub const addAttributeAtIndex = LLVMAddAttributeAtIndex; - extern fn LLVMAddAttributeAtIndex(*Value, Idx: AttributeIndex, A: *Attribute) void; + pub const addAttributeAtIndex = ZigLLVMAddAttributeAtIndex; + extern fn ZigLLVMAddAttributeAtIndex(*Value, Idx: AttributeIndex, A: *Attribute) void; pub const removeEnumAttributeAtIndex = LLVMRemoveEnumAttributeAtIndex; extern fn LLVMRemoveEnumAttributeAtIndex(F: *Value, Idx: AttributeIndex, KindID: c_uint) void; diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 7134df6a9c..c38e311f67 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -444,6 +444,15 @@ LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, return wrap(call_inst); } +void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef A) { + if (isa(unwrap(Val))) { + unwrap(Val)->addAttributeAtIndex(Idx, unwrap(A)); + } else { + unwrap(Val)->addAttributeAtIndex(Idx, unwrap(A)); + } +} + + LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool isVolatile) { @@ -1065,12 +1074,21 @@ void ZigLLVMSetFastMath(LLVMBuilderRef builder_wrapped, bool on_state) { } } -void ZigLLVMAddByValAttr(LLVMValueRef fn_ref, unsigned ArgNo, LLVMTypeRef type_val) { - Function *func = unwrap(fn_ref); - AttrBuilder attr_builder(func->getContext()); - Type *llvm_type = unwrap(type_val); - attr_builder.addByValAttr(llvm_type); - func->addParamAttrs(ArgNo, attr_builder); +void ZigLLVMAddByValAttr(LLVMValueRef Val, unsigned ArgNo, LLVMTypeRef type_val) { + if (isa(unwrap(Val))) { + Function *func = unwrap(Val); + AttrBuilder attr_builder(func->getContext()); + Type *llvm_type = unwrap(type_val); + attr_builder.addByValAttr(llvm_type); + func->addParamAttrs(ArgNo, attr_builder); + } else { + CallInst *call = unwrap(Val); + AttrBuilder attr_builder(call->getContext()); + Type *llvm_type = unwrap(type_val); + attr_builder.addByValAttr(llvm_type); + // NOTE: +1 here since index 0 refers to the return value + call->addAttributeAtIndex(ArgNo + 1, attr_builder.getAttribute(Attribute::ByVal)); + } } void ZigLLVMAddSretAttr(LLVMValueRef fn_ref, LLVMTypeRef type_val) { diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 0e210f9545..2829801a46 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -129,6 +129,8 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMTypeRef functio LLVMValueRef Fn, LLVMValueRef *Args, unsigned NumArgs, enum ZigLLVM_CallingConv CC, enum ZigLLVM_CallAttr attr, const char *Name); +ZIG_EXTERN_C void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef A); + ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool isVolatile); diff --git a/test/c_abi/cfuncs.c b/test/c_abi/cfuncs.c index 795701a18a..20896669fe 100644 --- a/test/c_abi/cfuncs.c +++ b/test/c_abi/cfuncs.c @@ -842,3 +842,32 @@ struct ByRef c_modify_by_ref_param(struct ByRef in) { in.val = 42; return in; } + +struct ByVal { + struct { + unsigned long x; + unsigned long y; + unsigned long z; + } origin; + struct { + unsigned long width; + unsigned long height; + unsigned long depth; + } size; +}; + +void c_func_ptr_byval(void *a, void *b, struct ByVal in, unsigned long c, void *d, unsigned long e) { + assert_or_panic((intptr_t)a == 1); + assert_or_panic((intptr_t)b == 2); + + assert_or_panic(in.origin.x == 9); + assert_or_panic(in.origin.y == 10); + assert_or_panic(in.origin.z == 11); + assert_or_panic(in.size.width == 12); + assert_or_panic(in.size.height == 13); + assert_or_panic(in.size.depth == 14); + + assert_or_panic(c == 3); + assert_or_panic((intptr_t)d == 4); + assert_or_panic(e == 5); +} diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index a5b8bda9c1..47f0455744 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -1001,3 +1001,34 @@ test "C function modifies by ref param" { const res = c_modify_by_ref_param(.{ .val = 1, .arr = undefined }); try expect(res.val == 42); } + +const ByVal = extern struct { + origin: extern struct { + x: c_ulong, + y: c_ulong, + z: c_ulong, + }, + size: extern struct { + width: c_ulong, + height: c_ulong, + depth: c_ulong, + }, +}; + +extern fn c_func_ptr_byval(*anyopaque, *anyopaque, ByVal, c_ulong, *anyopaque, c_ulong) void; +test "C function that takes byval struct called via function pointer" { + if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; + + var fn_ptr = &c_func_ptr_byval; + fn_ptr( + @intToPtr(*anyopaque, 1), + @intToPtr(*anyopaque, 2), + ByVal{ + .origin = .{ .x = 9, .y = 10, .z = 11 }, + .size = .{ .width = 12, .height = 13, .depth = 14 }, + }, + @as(c_ulong, 3), + @intToPtr(*anyopaque, 4), + @as(c_ulong, 5), + ); +}