Commit d968d9d103
Changed files (6)
src
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;
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;
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<Function>(unwrap(Val))) {
+ unwrap<Function>(Val)->addAttributeAtIndex(Idx, unwrap(A));
+ } else {
+ unwrap<CallInst>(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<Function>(fn_ref);
- AttrBuilder attr_builder(func->getContext());
- Type *llvm_type = unwrap<Type>(type_val);
- attr_builder.addByValAttr(llvm_type);
- func->addParamAttrs(ArgNo, attr_builder);
+void ZigLLVMAddByValAttr(LLVMValueRef Val, unsigned ArgNo, LLVMTypeRef type_val) {
+ if (isa<Function>(unwrap(Val))) {
+ Function *func = unwrap<Function>(Val);
+ AttrBuilder attr_builder(func->getContext());
+ Type *llvm_type = unwrap<Type>(type_val);
+ attr_builder.addByValAttr(llvm_type);
+ func->addParamAttrs(ArgNo, attr_builder);
+ } else {
+ CallInst *call = unwrap<CallInst>(Val);
+ AttrBuilder attr_builder(call->getContext());
+ Type *llvm_type = unwrap<Type>(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) {
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);
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);
+}
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),
+ );
+}