authorgravatar for timonkruiper@gmail.comTimon Kruiper <timonkruiper@gmail.com> 2020-05-28 19:45:11+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-28 22:56:33-04:00
log6e89692d81bd5d50a634c32a1588a7c505860f32
tree8ecdac5ab696390a38ba468df55a2ea8a6ea50eb
parent34101127c60c893db77136b9a53501a2e2a3ea4f

C ABI: Add C support for passing structs of floats to an extern function

Currently this does not handle returning these structs yet. Related: #1481

3 files changed, 134 insertions(+), 0 deletions(-)

src/codegen.cpp+72
......@@ -2062,6 +2062,78 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
20622062 }
20632063 }
20642064 return true;
2065 } else if (abi_class == X64CABIClass_SSE) {
2066 // For now only handle structs with only floats/doubles in it.
2067 if (ty->id != ZigTypeIdStruct) {
2068 if (source_node != nullptr) {
2069 give_up_with_c_abi_error(g, source_node);
2070 }
2071 // otherwise allow codegen code to report a compile error
2072 return false;
2073 }
2074
2075 for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
2076 if (ty->data.structure.fields[i]->type_entry->id != ZigTypeIdFloat) {
2077 if (source_node != nullptr) {
2078 give_up_with_c_abi_error(g, source_node);
2079 }
2080 // otherwise allow codegen code to report a compile error
2081 return false;
2082 }
2083 }
2084
2085 // The SystemV ABI says that we have to setup 1 FP register per f64.
2086 // So two f32 can be passed in one f64, but 3 f32 have to be passed in 2 FP registers.
2087 // To achieve this with LLVM API, we pass multiple f64 parameters to the LLVM function if
2088 // the type is bigger than 8 bytes.
2089
2090 // Example:
2091 // extern struct {
2092 // x: f32,
2093 // y: f32,
2094 // z: f32,
2095 // };
2096 // const ptr = (*f64)*Struct;
2097 // Register 1: ptr.*
2098 // Register 2: (ptr + 1).*
2099
2100 // One floating point register per f64 or 2 f32's
2101 size_t number_of_fp_regs = (size_t)ceilf((float)ty_size / (float)8);
2102
2103 switch (fn_walk->id) {
2104 case FnWalkIdAttrs: {
2105 fn_walk->data.attrs.gen_i += 1;
2106 break;
2107 }
2108 case FnWalkIdCall: {
2109 LLVMValueRef f64_ptr_to_struct = LLVMBuildBitCast(g->builder, val, LLVMPointerType(LLVMDoubleType(), 0), "");
2110 for (uint32_t i = 0; i < number_of_fp_regs; i += 1) {
2111 LLVMValueRef index = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, i, false);
2112 LLVMValueRef indices[] = { index };
2113 LLVMValueRef adjusted_ptr_to_struct = LLVMBuildInBoundsGEP(g->builder, f64_ptr_to_struct, indices, 1, "");
2114 LLVMValueRef loaded = LLVMBuildLoad(g->builder, adjusted_ptr_to_struct, "");
2115 fn_walk->data.call.gen_param_values->append(loaded);
2116 }
2117 break;
2118 }
2119 case FnWalkIdTypes: {
2120 for (uint32_t i = 0; i < number_of_fp_regs; i += 1) {
2121 fn_walk->data.types.gen_param_types->append(get_llvm_type(g, g->builtin_types.entry_f64));
2122 fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, g->builtin_types.entry_f64));
2123 }
2124 break;
2125 }
2126 case FnWalkIdVars:
2127 case FnWalkIdInits: {
2128 // TODO: Handle exporting functions
2129 if (source_node != nullptr) {
2130 give_up_with_c_abi_error(g, source_node);
2131 }
2132 // otherwise allow codegen code to report a compile error
2133 return false;
2134 }
2135 }
2136 return true;
20652137 }
20662138 }
20672139 if (source_node != nullptr) {
test/stage1/c_abi/cfuncs.c+28
......@@ -63,6 +63,20 @@ void zig_split_struct_ints(struct SplitStructInts);
6363
6464struct BigStruct zig_big_struct_both(struct BigStruct);
6565
66typedef struct Vector3 {
67 float x;
68 float y;
69 float z;
70} Vector3;
71
72typedef struct Vector5 {
73 float x;
74 float y;
75 float z;
76 float w;
77 float q;
78} Vector5;
79
6680void run_c_tests(void) {
6781 zig_u8(0xff);
6882 zig_u16(0xfffe);
......@@ -226,3 +240,17 @@ struct BigStruct c_big_struct_both(struct BigStruct x) {
226240 struct BigStruct y = {10, 11, 12, 13, 14};
227241 return y;
228242}
243
244void c_small_struct_floats(Vector3 vec) {
245 assert_or_panic(vec.x == 3.0);
246 assert_or_panic(vec.y == 6.0);
247 assert_or_panic(vec.z == 12.0);
248}
249
250void c_big_struct_floats(Vector5 vec) {
251 assert_or_panic(vec.x == 76.0);
252 assert_or_panic(vec.y == -1.0);
253 assert_or_panic(vec.z == -12.0);
254 assert_or_panic(vec.w == 69);
255 assert_or_panic(vec.q == 55);
256}
test/stage1/c_abi/main.zig+34
......@@ -261,3 +261,37 @@ export fn zig_big_struct_both(x: BigStruct) BigStruct {
261261 };
262262 return s;
263263}
264
265const Vector3 = extern struct {
266 x: f32,
267 y: f32,
268 z: f32,
269};
270extern fn c_small_struct_floats(Vector3) void;
271
272const Vector5 = extern struct {
273 x: f32,
274 y: f32,
275 z: f32,
276 w: f32,
277 q: f32,
278};
279extern fn c_big_struct_floats(Vector5) void;
280
281test "C ABI structs of floats as parameter" {
282 var v3 = Vector3{
283 .x = 3.0,
284 .y = 6.0,
285 .z = 12.0,
286 };
287 c_small_struct_floats(v3);
288
289 var v5 = Vector5{
290 .x = 76.0,
291 .y = -1.0,
292 .z = -12.0,
293 .w = 69.0,
294 .q = 55,
295 };
296 c_big_struct_floats(v5);
297}