diff options
| author | Timon Kruiper <timonkruiper@gmail.com> | 2020-05-28 19:45:11 +0200 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-05-28 22:56:33 -0400 |
| commit | 6e89692d81bd5d50a634c32a1588a7c505860f32 (patch) | |
| tree | 8ecdac5ab696390a38ba468df55a2ea8a6ea50eb /src/codegen.cpp | |
| parent | 34101127c60c893db77136b9a53501a2e2a3ea4f (diff) | |
| download | zig-6e89692d81bd5d50a634c32a1588a7c505860f32.tar.gz zig-6e89692d81bd5d50a634c32a1588a7c505860f32.zip | |
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
Diffstat (limited to 'src/codegen.cpp')
| -rw-r--r-- | src/codegen.cpp | 72 |
1 files changed, 72 insertions, 0 deletions
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) { |
