From 6e89692d81bd5d50a634c32a1588a7c505860f32 Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Thu, 28 May 2020 19:45:11 +0200 Subject: [PATCH] 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 --- src/codegen.cpp | 72 ++++++++++++++++++++++++++++++++++++++ test/stage1/c_abi/cfuncs.c | 28 +++++++++++++++ test/stage1/c_abi/main.zig | 34 ++++++++++++++++++ 3 files changed, 134 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index e91eafa458..92317f0965 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2062,6 +2062,78 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ } } return true; + } else if (abi_class == X64CABIClass_SSE) { + // For now only handle structs with only floats/doubles in it. + if (ty->id != ZigTypeIdStruct) { + if (source_node != nullptr) { + give_up_with_c_abi_error(g, source_node); + } + // otherwise allow codegen code to report a compile error + return false; + } + + for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) { + if (ty->data.structure.fields[i]->type_entry->id != ZigTypeIdFloat) { + if (source_node != nullptr) { + give_up_with_c_abi_error(g, source_node); + } + // otherwise allow codegen code to report a compile error + return false; + } + } + + // The SystemV ABI says that we have to setup 1 FP register per f64. + // So two f32 can be passed in one f64, but 3 f32 have to be passed in 2 FP registers. + // To achieve this with LLVM API, we pass multiple f64 parameters to the LLVM function if + // the type is bigger than 8 bytes. + + // Example: + // extern struct { + // x: f32, + // y: f32, + // z: f32, + // }; + // const ptr = (*f64)*Struct; + // Register 1: ptr.* + // Register 2: (ptr + 1).* + + // One floating point register per f64 or 2 f32's + size_t number_of_fp_regs = (size_t)ceilf((float)ty_size / (float)8); + + switch (fn_walk->id) { + case FnWalkIdAttrs: { + fn_walk->data.attrs.gen_i += 1; + break; + } + case FnWalkIdCall: { + LLVMValueRef f64_ptr_to_struct = LLVMBuildBitCast(g->builder, val, LLVMPointerType(LLVMDoubleType(), 0), ""); + for (uint32_t i = 0; i < number_of_fp_regs; i += 1) { + LLVMValueRef index = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, i, false); + LLVMValueRef indices[] = { index }; + LLVMValueRef adjusted_ptr_to_struct = LLVMBuildInBoundsGEP(g->builder, f64_ptr_to_struct, indices, 1, ""); + LLVMValueRef loaded = LLVMBuildLoad(g->builder, adjusted_ptr_to_struct, ""); + fn_walk->data.call.gen_param_values->append(loaded); + } + break; + } + case FnWalkIdTypes: { + for (uint32_t i = 0; i < number_of_fp_regs; i += 1) { + fn_walk->data.types.gen_param_types->append(get_llvm_type(g, g->builtin_types.entry_f64)); + fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, g->builtin_types.entry_f64)); + } + break; + } + case FnWalkIdVars: + case FnWalkIdInits: { + // TODO: Handle exporting functions + if (source_node != nullptr) { + give_up_with_c_abi_error(g, source_node); + } + // otherwise allow codegen code to report a compile error + return false; + } + } + return true; } } if (source_node != nullptr) { diff --git a/test/stage1/c_abi/cfuncs.c b/test/stage1/c_abi/cfuncs.c index a26f8c98a1..0e7bd2906f 100644 --- a/test/stage1/c_abi/cfuncs.c +++ b/test/stage1/c_abi/cfuncs.c @@ -63,6 +63,20 @@ void zig_split_struct_ints(struct SplitStructInts); struct BigStruct zig_big_struct_both(struct BigStruct); +typedef struct Vector3 { + float x; + float y; + float z; +} Vector3; + +typedef struct Vector5 { + float x; + float y; + float z; + float w; + float q; +} Vector5; + void run_c_tests(void) { zig_u8(0xff); zig_u16(0xfffe); @@ -226,3 +240,17 @@ struct BigStruct c_big_struct_both(struct BigStruct x) { struct BigStruct y = {10, 11, 12, 13, 14}; return y; } + +void c_small_struct_floats(Vector3 vec) { + assert_or_panic(vec.x == 3.0); + assert_or_panic(vec.y == 6.0); + assert_or_panic(vec.z == 12.0); +} + +void c_big_struct_floats(Vector5 vec) { + assert_or_panic(vec.x == 76.0); + assert_or_panic(vec.y == -1.0); + assert_or_panic(vec.z == -12.0); + assert_or_panic(vec.w == 69); + assert_or_panic(vec.q == 55); +} diff --git a/test/stage1/c_abi/main.zig b/test/stage1/c_abi/main.zig index 3c2b731524..9090bb481e 100644 --- a/test/stage1/c_abi/main.zig +++ b/test/stage1/c_abi/main.zig @@ -261,3 +261,37 @@ export fn zig_big_struct_both(x: BigStruct) BigStruct { }; return s; } + +const Vector3 = extern struct { + x: f32, + y: f32, + z: f32, +}; +extern fn c_small_struct_floats(Vector3) void; + +const Vector5 = extern struct { + x: f32, + y: f32, + z: f32, + w: f32, + q: f32, +}; +extern fn c_big_struct_floats(Vector5) void; + +test "C ABI structs of floats as parameter" { + var v3 = Vector3{ + .x = 3.0, + .y = 6.0, + .z = 12.0, + }; + c_small_struct_floats(v3); + + var v5 = Vector5{ + .x = 76.0, + .y = -1.0, + .z = -12.0, + .w = 69.0, + .q = 55, + }; + c_big_struct_floats(v5); +}