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
This commit is contained in:
Timon Kruiper 2020-05-28 19:45:11 +02:00 committed by Andrew Kelley
parent 34101127c6
commit 6e89692d81
3 changed files with 134 additions and 0 deletions

View File

@ -2062,6 +2062,78 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
} }
} }
return true; 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) { if (source_node != nullptr) {

View File

@ -63,6 +63,20 @@ void zig_split_struct_ints(struct SplitStructInts);
struct BigStruct zig_big_struct_both(struct BigStruct); 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) { void run_c_tests(void) {
zig_u8(0xff); zig_u8(0xff);
zig_u16(0xfffe); zig_u16(0xfffe);
@ -226,3 +240,17 @@ struct BigStruct c_big_struct_both(struct BigStruct x) {
struct BigStruct y = {10, 11, 12, 13, 14}; struct BigStruct y = {10, 11, 12, 13, 14};
return y; 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);
}

View File

@ -261,3 +261,37 @@ export fn zig_big_struct_both(x: BigStruct) BigStruct {
}; };
return s; 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);
}