authorgravatar for amro@bndb.shBelhorma Bendebiche <amro@bndb.sh> 2021-07-23 12:43:38-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-28 18:13:17-04:00
logf5d9d739d70f5d99756e277cc7c77484d60ddf42
tree27c6691dccdc0a0c750eac384e362f5655f4d3e0
parent2f9e498c6fa0395e4318b2359291353b495a94e1

stage1: Expand SysV C ABI support for small structs

While the SysV ABI is not that complicated, LLVM does not allow us direct access to enforce it. By mimicking the IR generated by clang, we can trick LLVM into doing the right thing. This involves two main additions: 1. `AGG` ABI class This is not part of the spec, but since we have to track class per eightbyte and not per struct, the current enum is not enough. I considered adding multiple classes like: `INTEGER_INTEGER`, `INTEGER_SSE`, `SSE_INTEGER`. However, all of those cases would trigger the same code path so it's simpler to collapse into one. This class is only used on SysV. 2. LLVM C ABI type Clang uses different types in C ABI function signatures than the original structs passed in, and does conversion. For example, this struct: `{ i8, i8, float }` would use `{ i16, float }` at ABI boundaries. When passed as an argument, it is instead split into two arguments `i16` and `float`. Therefore, for every struct that passes ABI boundaries we need to keep track of its corresponding ABI type. Here are some more examples: ``` | Struct | ABI equivalent | | { i8, i8, i8, i8 } | i32 | | { float, float } | double | | { float, i32, i8 } | { float, i64 } | ``` Then, we must update function calls, returns, parameter lists and inits to properly convert back and forth as needed.

6 files changed, 419 insertions(+), 49 deletions(-)

src/stage1/all_types.hpp+4-1
...@@ -107,6 +107,7 @@ enum X64CABIClass {...@@ -107,6 +107,7 @@ enum X64CABIClass {
107 X64CABIClass_MEMORY_nobyval,107 X64CABIClass_MEMORY_nobyval,
108 X64CABIClass_INTEGER,108 X64CABIClass_INTEGER,
109 X64CABIClass_SSE,109 X64CABIClass_SSE,
110 X64CABIClass_AGG,
110};111};
111112
112struct Stage1Zir {113struct Stage1Zir {
...@@ -1569,8 +1570,9 @@ struct ZigType {...@@ -1569,8 +1570,9 @@ struct ZigType {
15691570
1570 // These are not supposed to be accessed directly. They're1571 // These are not supposed to be accessed directly. They're
1571 // null during semantic analysis, memoized with get_llvm_type1572 // null during semantic analysis, memoized with get_llvm_type
1572 // and get_llvm_di_type1573 // get_llvm_c_abi_type and get_llvm_di_type
1573 LLVMTypeRef llvm_type;1574 LLVMTypeRef llvm_type;
1575 LLVMTypeRef llvm_c_abi_type;
1574 ZigLLVMDIType *llvm_di_type;1576 ZigLLVMDIType *llvm_di_type;
15751577
1576 union {1578 union {
...@@ -1624,6 +1626,7 @@ struct GlobalExport {...@@ -1624,6 +1626,7 @@ struct GlobalExport {
16241626
1625struct ZigFn {1627struct ZigFn {
1626 LLVMValueRef llvm_value;1628 LLVMValueRef llvm_value;
1629 LLVMValueRef abi_return_value; // alloca used when converting at SysV ABI boundaries
1627 const char *llvm_name;1630 const char *llvm_name;
1628 AstNode *proto_node;1631 AstNode *proto_node;
1629 AstNode *body_node;1632 AstNode *body_node;
src/stage1/analyze.cpp+120-4
...@@ -6063,6 +6063,12 @@ Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result) {...@@ -6063,6 +6063,12 @@ Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result) {
6063 return ErrorNone;6063 return ErrorNone;
6064}6064}
60656065
6066bool fn_returns_c_abi_small_struct(FnTypeId *fn_type_id) {
6067 ZigType *type = fn_type_id->return_type;
6068 return !calling_convention_allows_zig_types(fn_type_id->cc) &&
6069 type->id == ZigTypeIdStruct && type->abi_size <= 16;
6070}
6071
6066// Whether you can infer the value based solely on the type.6072// Whether you can infer the value based solely on the type.
6067OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) {6073OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) {
6068 assert(type_entry != nullptr);6074 assert(type_entry != nullptr);
...@@ -8376,6 +8382,9 @@ static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size...@@ -8376,6 +8382,9 @@ static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size
8376 // be memory.8382 // be memory.
8377 return X64CABIClass_MEMORY;8383 return X64CABIClass_MEMORY;
8378 }8384 }
8385 // "If the size of the aggregate exceeds a single eightbyte, each is classified
8386 // separately.".
8387 // "If one of the classes is MEMORY, the whole argument is passed in memory"
8379 X64CABIClass working_class = X64CABIClass_Unknown;8388 X64CABIClass working_class = X64CABIClass_Unknown;
8380 for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) {8389 for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
8381 X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields[0]->type_entry);8390 X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields[0]->type_entry);
...@@ -8385,7 +8394,10 @@ static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size...@@ -8385,7 +8394,10 @@ static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size
8385 working_class = field_class;8394 working_class = field_class;
8386 }8395 }
8387 }8396 }
8388 return working_class;8397 if (working_class == X64CABIClass_MEMORY) {
8398 return X64CABIClass_MEMORY;
8399 }
8400 return X64CABIClass_AGG;
8389 }8401 }
8390 case ZigTypeIdUnion: {8402 case ZigTypeIdUnion: {
8391 // "If the size of an object is larger than four eightbytes, or it contains unaligned8403 // "If the size of an object is larger than four eightbytes, or it contains unaligned
...@@ -8407,7 +8419,7 @@ static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size...@@ -8407,7 +8419,7 @@ static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size
8407 X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry);8419 X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry);
8408 if (field_class == X64CABIClass_Unknown)8420 if (field_class == X64CABIClass_Unknown)
8409 return X64CABIClass_Unknown;8421 return X64CABIClass_Unknown;
8410 if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) {8422 if (i == 0 || field_class == X64CABIClass_MEMORY || field_class == X64CABIClass_INTEGER || working_class == X64CABIClass_SSE) {
8411 working_class = field_class;8423 working_class = field_class;
8412 }8424 }
8413 }8425 }
...@@ -8678,6 +8690,95 @@ static LLVMTypeRef get_llvm_type_of_n_bytes(unsigned byte_size) {...@@ -8678,6 +8690,95 @@ static LLVMTypeRef get_llvm_type_of_n_bytes(unsigned byte_size) {
8678 LLVMInt8Type() : LLVMArrayType(LLVMInt8Type(), byte_size);8690 LLVMInt8Type() : LLVMArrayType(LLVMInt8Type(), byte_size);
8679}8691}
86808692
8693static LLVMTypeRef llvm_int_for_size(size_t size) {
8694 if (size > 4) {
8695 return LLVMInt64Type();
8696 } else if (size > 2) {
8697 return LLVMInt32Type();
8698 } else if (size == 2) {
8699 return LLVMInt16Type();
8700 } else {
8701 return LLVMInt8Type();
8702 }
8703}
8704
8705static LLVMTypeRef llvm_sse_for_size(size_t size) {
8706 if (size > 4)
8707 return LLVMDoubleType();
8708 else
8709 return LLVMFloatType();
8710}
8711
8712// Since it's not possible to control calling convention or register
8713// allocation in LLVM, clang seems to use intermediate types to manipulate
8714// LLVM into doing the right thing. It uses a float to force SSE registers,
8715// and a struct when 2 registers must be used. Some examples:
8716// { f32 } -> float
8717// { f32, i32 } -> { float, i32 }
8718// { i32, i32, f32 } -> { i64, float }
8719//
8720// The implementation below does not match clang 1:1. For instance, clang
8721// uses `<2x float>` while we generate `double`. There's a lot more edge
8722// cases and complexity when converting back and forth in clang though,
8723// so below is the simplest implementation that passes all tests.
8724static Error resolve_llvm_c_abi_type(CodeGen *g, ZigType *ty) {
8725 size_t ty_size = type_size(g, ty);
8726 LLVMTypeRef abi_type;
8727 switch (ty->id) {
8728 case ZigTypeIdEnum:
8729 case ZigTypeIdInt:
8730 case ZigTypeIdBool:
8731 abi_type = llvm_int_for_size(ty_size);
8732 break;
8733 case ZigTypeIdFloat:
8734 case ZigTypeIdVector:
8735 abi_type = llvm_sse_for_size(ty_size);
8736 break;
8737 case ZigTypeIdStruct: {
8738 uint32_t eightbyte_index = 0;
8739 size_t type_sizes[] = {0, 0};
8740 X64CABIClass type_classes[] = {X64CABIClass_Unknown, X64CABIClass_Unknown};
8741 for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
8742 if (ty->data.structure.fields[i]->offset >= 8) {
8743 eightbyte_index = 1;
8744 }
8745 X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields[i]->type_entry);
8746
8747 if (field_class == X64CABIClass_INTEGER) {
8748 type_classes[eightbyte_index] = X64CABIClass_INTEGER;
8749 } else if (type_classes[eightbyte_index] == X64CABIClass_Unknown) {
8750 type_classes[eightbyte_index] = field_class;
8751 }
8752 type_sizes[eightbyte_index] += ty->data.structure.fields[i]->type_entry->abi_size;
8753 }
8754
8755 LLVMTypeRef return_elem_types[] = {
8756 LLVMVoidType(),
8757 LLVMVoidType(),
8758 };
8759 for (uint32_t i = 0; i <= eightbyte_index; i += 1) {
8760 if (type_classes[i] == X64CABIClass_INTEGER) {
8761 return_elem_types[i] = llvm_int_for_size(type_sizes[i]);
8762 } else {
8763 return_elem_types[i] = llvm_sse_for_size(type_sizes[i]);
8764 }
8765 }
8766 if (eightbyte_index == 0) {
8767 abi_type = return_elem_types[0];
8768 } else {
8769 abi_type = LLVMStructType(return_elem_types, 2, false);
8770 }
8771 break;
8772 }
8773 case ZigTypeIdUnion:
8774 default:
8775 // currently unreachable
8776 zig_panic("TODO: support C ABI unions");
8777 }
8778 ty->llvm_c_abi_type = abi_type;
8779 return ErrorNone;
8780}
8781
8681static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveStatus wanted_resolve_status,8782static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveStatus wanted_resolve_status,
8682 ZigType *async_frame_type)8783 ZigType *async_frame_type)
8683{8784{
...@@ -8936,6 +9037,9 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS...@@ -8936,6 +9037,9 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
8936 g->type_resolve_stack.swap_remove(struct_type->data.structure.llvm_full_type_queue_index);9037 g->type_resolve_stack.swap_remove(struct_type->data.structure.llvm_full_type_queue_index);
8937 struct_type->data.structure.llvm_full_type_queue_index = SIZE_MAX;9038 struct_type->data.structure.llvm_full_type_queue_index = SIZE_MAX;
8938 }9039 }
9040
9041 if (struct_type->abi_size <= 16 && struct_type->data.structure.layout == ContainerLayoutExtern)
9042 resolve_llvm_c_abi_type(g, struct_type);
8939}9043}
89409044
8941// This is to be used instead of void for debug info types, to avoid tripping9045// This is to be used instead of void for debug info types, to avoid tripping
...@@ -9536,8 +9640,13 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) {...@@ -9536,8 +9640,13 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) {
9536 assert(gen_param_types.items[i] != nullptr);9640 assert(gen_param_types.items[i] != nullptr);
9537 }9641 }
95389642
9539 fn_type->data.fn.raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type),9643 if (!first_arg_return && fn_returns_c_abi_small_struct(fn_type_id)) {
9540 gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args);9644 fn_type->data.fn.raw_type_ref = LLVMFunctionType(get_llvm_c_abi_type(g, gen_return_type),
9645 gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args);
9646 } else {
9647 fn_type->data.fn.raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type),
9648 gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args);
9649 }
9541 const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref);9650 const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref);
9542 fn_type->llvm_type = LLVMPointerType(fn_type->data.fn.raw_type_ref, fn_addrspace);9651 fn_type->llvm_type = LLVMPointerType(fn_type->data.fn.raw_type_ref, fn_addrspace);
9543 fn_type->data.fn.raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0);9652 fn_type->data.fn.raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0);
...@@ -9827,6 +9936,13 @@ static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_r...@@ -9827,6 +9936,13 @@ static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_r
9827 zig_unreachable();9936 zig_unreachable();
9828}9937}
98299938
9939LLVMTypeRef get_llvm_c_abi_type(CodeGen *g, ZigType *type) {
9940 assertNoError(type_resolve(g, type, ResolveStatusLLVMFull));
9941 assert(type->abi_size == 0 || type->abi_size >= LLVMABISizeOfType(g->target_data_ref, type->llvm_type));
9942 assert(type->abi_align == 0 || type->abi_align >= LLVMABIAlignmentOfType(g->target_data_ref, type->llvm_type));
9943 return type->llvm_c_abi_type;
9944}
9945
9830LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type) {9946LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type) {
9831 assertNoError(type_resolve(g, type, ResolveStatusLLVMFull));9947 assertNoError(type_resolve(g, type, ResolveStatusLLVMFull));
9832 assert(type->abi_size == 0 || type->abi_size >= LLVMABISizeOfType(g->target_data_ref, type->llvm_type));9948 assert(type->abi_size == 0 || type->abi_size >= LLVMABISizeOfType(g->target_data_ref, type->llvm_type));
src/stage1/analyze.hpp+3
...@@ -54,6 +54,8 @@ uint32_t get_async_frame_align_bytes(CodeGen *g);...@@ -54,6 +54,8 @@ uint32_t get_async_frame_align_bytes(CodeGen *g);
54bool type_has_bits(CodeGen *g, ZigType *type_entry);54bool type_has_bits(CodeGen *g, ZigType *type_entry);
55Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result);55Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result);
5656
57bool fn_returns_c_abi_small_struct(FnTypeId *fn_type_id);
58
57enum ExternPosition {59enum ExternPosition {
58 ExternPositionFunctionParameter,60 ExternPositionFunctionParameter,
59 ExternPositionFunctionReturn,61 ExternPositionFunctionReturn,
...@@ -268,6 +270,7 @@ Buf *type_bare_name(ZigType *t);...@@ -268,6 +270,7 @@ Buf *type_bare_name(ZigType *t);
268Buf *type_h_name(ZigType *t);270Buf *type_h_name(ZigType *t);
269271
270LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type);272LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type);
273LLVMTypeRef get_llvm_c_abi_type(CodeGen *g, ZigType *type);
271ZigLLVMDIType *get_llvm_di_type(CodeGen *g, ZigType *type);274ZigLLVMDIType *get_llvm_di_type(CodeGen *g, ZigType *type);
272275
273void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_path, bool translate_c,276void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_path, bool translate_c,
src/stage1/codegen.cpp+116-44
...@@ -2142,75 +2142,103 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_...@@ -2142,75 +2142,103 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
2142 }2142 }
2143 }2143 }
2144 return true;2144 return true;
2145 } else if (abi_class == X64CABIClass_SSE) {2145 } else if (abi_class == X64CABIClass_AGG) {
2146 // For now only handle structs with only floats/doubles in it.2146 // The SystemV ABI says that we have to setup 1 register per eightbyte.
2147 if (ty->id != ZigTypeIdStruct) {
2148 if (source_node != nullptr) {
2149 give_up_with_c_abi_error(g, source_node);
2150 }
2151 // otherwise allow codegen code to report a compile error
2152 return false;
2153 }
2154
2155 for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
2156 if (ty->data.structure.fields[i]->type_entry->id != ZigTypeIdFloat) {
2157 if (source_node != nullptr) {
2158 give_up_with_c_abi_error(g, source_node);
2159 }
2160 // otherwise allow codegen code to report a compile error
2161 return false;
2162 }
2163 }
2164
2165 // The SystemV ABI says that we have to setup 1 FP register per f64.
2166 // So two f32 can be passed in one f64, but 3 f32 have to be passed in 2 FP registers.2147 // So two f32 can be passed in one f64, but 3 f32 have to be passed in 2 FP registers.
2167 // To achieve this with LLVM API, we pass multiple f64 parameters to the LLVM function if2148 // Similarly, two i32 can be passed in one i64, but 3 i32 have to be passed in 2 registers.
2168 // the type is bigger than 8 bytes.2149 // LLVM does not allow us to control registers in this way, nor to request specific
2150 // ABI conventions. So we have to trick it into allocating the right registers, based
2151 // on how clang does it.
2152
2153 // First, we get the LLVM type corresponding to the C abi for the struct, then
2154 // we pass each field as an argument.
21692155
2170 // Example:2156 // Example:
2171 // extern struct {2157 // extern struct {
2172 // x: f32,2158 // x: f32,
2173 // y: f32,2159 // y: f32,
2174 // z: f32,2160 // z: i32,
2175 // };2161 // };
2176 // const ptr = (*f64)*Struct;2162 // LLVM abi type: { double, i32 }
2177 // Register 1: ptr.*2163 // const ptr = (*abi_type)*Struct;
2178 // Register 2: (ptr + 1).*2164 // FP Register 1: abi_type[0]
2165 // Register 1: abi_type[1]
21792166
2180 // One floating point register per f64 or 2 f32's2167 // However, if the struct fits in one register, then we'll pass it as such
2181 size_t number_of_fp_regs = (ty_size + 7) / 8;2168 size_t number_of_regs = (size_t)ceilf((float)ty_size / (float)8);
2169
2170 LLVMTypeRef abi_type = get_llvm_c_abi_type(g, ty);
2171
2172 assert(ty_size <= 16);
21822173
2183 switch (fn_walk->id) {2174 switch (fn_walk->id) {
2184 case FnWalkIdAttrs: {2175 case FnWalkIdAttrs: {
2185 fn_walk->data.attrs.gen_i += number_of_fp_regs;2176 fn_walk->data.attrs.gen_i += number_of_regs;
2186 break;2177 break;
2187 }2178 }
2188 case FnWalkIdCall: {2179 case FnWalkIdCall: {
2189 LLVMValueRef f64_ptr_to_struct = LLVMBuildBitCast(g->builder, val, LLVMPointerType(LLVMDoubleType(), 0), "");2180 LLVMValueRef abi_ptr_to_struct = LLVMBuildBitCast(g->builder, val, LLVMPointerType(abi_type, 0), "");
2190 for (uint32_t i = 0; i < number_of_fp_regs; i += 1) {2181 if (number_of_regs == 1) {
2191 LLVMValueRef index = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, i, false);2182 LLVMValueRef loaded = LLVMBuildLoad(g->builder, abi_ptr_to_struct, "");
2192 LLVMValueRef indices[] = { index };2183 fn_walk->data.call.gen_param_values->append(loaded);
2193 LLVMValueRef adjusted_ptr_to_struct = LLVMBuildInBoundsGEP(g->builder, f64_ptr_to_struct, indices, 1, "");2184 break;
2185 }
2186 for (uint32_t i = 0; i < number_of_regs; i += 1) {
2187 LLVMValueRef zero = LLVMConstInt(LLVMInt32Type(), 0, false);
2188 LLVMValueRef index = LLVMConstInt(LLVMInt32Type(), i, false);
2189 LLVMValueRef indices[] = { zero, index };
2190 LLVMValueRef adjusted_ptr_to_struct = LLVMBuildInBoundsGEP(g->builder, abi_ptr_to_struct, indices, 2, "");
2194 LLVMValueRef loaded = LLVMBuildLoad(g->builder, adjusted_ptr_to_struct, "");2191 LLVMValueRef loaded = LLVMBuildLoad(g->builder, adjusted_ptr_to_struct, "");
2195 fn_walk->data.call.gen_param_values->append(loaded);2192 fn_walk->data.call.gen_param_values->append(loaded);
2196 }2193 }
2197 break;2194 break;
2198 }2195 }
2199 case FnWalkIdTypes: {2196 case FnWalkIdTypes: {
2200 for (uint32_t i = 0; i < number_of_fp_regs; i += 1) {2197 if (number_of_regs == 1) {
2201 fn_walk->data.types.gen_param_types->append(get_llvm_type(g, g->builtin_types.entry_f64));2198 fn_walk->data.types.gen_param_types->append(abi_type);
2199 fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, g->builtin_types.entry_f64));
2200 break;
2201 }
2202 for (uint32_t i = 0; i < number_of_regs; i += 1) {
2203 fn_walk->data.types.gen_param_types->append(LLVMStructGetTypeAtIndex(abi_type, i));
2202 fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, g->builtin_types.entry_f64));2204 fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, g->builtin_types.entry_f64));
2203 }2205 }
2204 break;2206 break;
2205 }2207 }
2206 case FnWalkIdVars:2208 case FnWalkIdVars: {
2209 var->value_ref = build_alloca(g, ty, var->name, var->align_bytes);
2210 di_arg_index = fn_walk->data.vars.gen_i;
2211 fn_walk->data.vars.gen_i += 1;
2212 dest_ty = ty;
2213 goto var_ok;
2214 }
2207 case FnWalkIdInits: {2215 case FnWalkIdInits: {
2208 // TODO: Handle exporting functions2216 // since we're representing the struct differently as an arg, and potentially
2209 if (source_node != nullptr) {2217 // splitting it, we have to do some work to put it back together.
2210 give_up_with_c_abi_error(g, source_node);2218 // the one reg case is straightforward, but if we used two registers we have
2219 // to iterate through the struct abi repr fields and load them one by one.
2220 if (number_of_regs == 1) {
2221 LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i);
2222 LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(abi_type, 0);
2223 LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, "");
2224 gen_store_untyped(g, arg, bitcasted, var->align_bytes, false);
2225 } else {
2226 LLVMValueRef abi_ptr_to_struct = LLVMBuildBitCast(g->builder, var->value_ref, LLVMPointerType(abi_type, 0), "");
2227 for (uint32_t i = 0; i < number_of_regs; i += 1) {
2228 LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i + i);
2229 LLVMValueRef zero = LLVMConstInt(LLVMInt32Type(), 0, false);
2230 LLVMValueRef index = LLVMConstInt(LLVMInt32Type(), i, false);
2231 LLVMValueRef indices[] = { zero, index };
2232 LLVMValueRef adjusted_ptr_to_struct = LLVMBuildInBoundsGEP(g->builder, abi_ptr_to_struct, indices, 2, "");
2233 LLVMBuildStore(g->builder, arg, adjusted_ptr_to_struct);
2234 }
2235 fn_walk->data.inits.gen_i += 1;
2211 }2236 }
2212 // otherwise allow codegen code to report a compile error2237 if (var->decl_node) {
2213 return false;2238 gen_var_debug_decl(g, var);
2239 }
2240 fn_walk->data.inits.gen_i += 1;
2241 break;
2214 }2242 }
2215 }2243 }
2216 return true;2244 return true;
...@@ -2654,13 +2682,36 @@ static void gen_async_return(CodeGen *g, Stage1AirInstReturn *instruction) {...@@ -2654,13 +2682,36 @@ static void gen_async_return(CodeGen *g, Stage1AirInstReturn *instruction) {
2654 LLVMBuildRetVoid(g->builder);2682 LLVMBuildRetVoid(g->builder);
2655}2683}
26562684
2685static LLVMValueRef gen_convert_to_c_abi(CodeGen *g, LLVMValueRef location, LLVMValueRef value) {
2686 ZigType *return_type = g->cur_fn->type_entry->data.fn.gen_return_type;
2687 size_t size = type_size(g, return_type);
2688
2689 LLVMTypeRef abi_return_type = get_llvm_c_abi_type(g, return_type);
2690 LLVMTypeRef abi_return_type_pointer = LLVMPointerType(abi_return_type, 0);
2691
2692 if (size < 8) {
2693 LLVMValueRef bitcast = LLVMBuildBitCast(g->builder, value, abi_return_type_pointer, "");
2694 return LLVMBuildLoad(g->builder, bitcast, "");
2695 } else {
2696 LLVMTypeRef i8ptr = LLVMPointerType(LLVMInt8Type(), 0);
2697 LLVMValueRef bc_location = LLVMBuildBitCast(g->builder, location, i8ptr, "");
2698 LLVMValueRef bc_value = LLVMBuildBitCast(g->builder, value, i8ptr, "");
2699
2700 LLVMValueRef len = LLVMConstInt(LLVMInt64Type(), size, false);
2701 ZigLLVMBuildMemCpy(g->builder, bc_location, 8, bc_value, return_type->abi_align, len, false);
2702 return LLVMBuildLoad(g->builder, location, "");
2703 }
2704}
2705
2657static LLVMValueRef ir_render_return(CodeGen *g, Stage1Air *executable, Stage1AirInstReturn *instruction) {2706static LLVMValueRef ir_render_return(CodeGen *g, Stage1Air *executable, Stage1AirInstReturn *instruction) {
2658 if (fn_is_async(g->cur_fn)) {2707 if (fn_is_async(g->cur_fn)) {
2659 gen_async_return(g, instruction);2708 gen_async_return(g, instruction);
2660 return nullptr;2709 return nullptr;
2661 }2710 }
26622711
2663 if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) {2712 FnTypeId *fn_type_id = &g->cur_fn->type_entry->data.fn.fn_type_id;
2713
2714 if (want_first_arg_sret(g, fn_type_id)) {
2664 if (instruction->operand == nullptr) {2715 if (instruction->operand == nullptr) {
2665 LLVMBuildRetVoid(g->builder);2716 LLVMBuildRetVoid(g->builder);
2666 return nullptr;2717 return nullptr;
...@@ -2671,6 +2722,16 @@ static LLVMValueRef ir_render_return(CodeGen *g, Stage1Air *executable, Stage1Ai...@@ -2671,6 +2722,16 @@ static LLVMValueRef ir_render_return(CodeGen *g, Stage1Air *executable, Stage1Ai
2671 ZigType *return_type = instruction->operand->value->type;2722 ZigType *return_type = instruction->operand->value->type;
2672 gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value);2723 gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value);
2673 LLVMBuildRetVoid(g->builder);2724 LLVMBuildRetVoid(g->builder);
2725 } else if (fn_returns_c_abi_small_struct(fn_type_id)) {
2726 LLVMValueRef location = g->cur_fn->abi_return_value;
2727 if (instruction->operand == nullptr) {
2728 LLVMValueRef converted = gen_convert_to_c_abi(g, location, g->cur_ret_ptr);
2729 LLVMBuildRet(g->builder, converted);
2730 } else {
2731 LLVMValueRef value = ir_llvm_value(g, instruction->operand);
2732 LLVMValueRef converted = gen_convert_to_c_abi(g, location, value);
2733 LLVMBuildRet(g->builder, converted);
2734 }
2674 } else if (g->cur_fn->type_entry->data.fn.fn_type_id.cc != CallingConventionAsync &&2735 } else if (g->cur_fn->type_entry->data.fn.fn_type_id.cc != CallingConventionAsync &&
2675 handle_is_ptr(g, g->cur_fn->type_entry->data.fn.fn_type_id.return_type))2736 handle_is_ptr(g, g->cur_fn->type_entry->data.fn.fn_type_id.return_type))
2676 {2737 {
...@@ -4678,6 +4739,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, Stage1Air *executable, Stage1AirI...@@ -4678,6 +4739,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, Stage1Air *executable, Stage1AirI
4678 } else if (first_arg_ret) {4739 } else if (first_arg_ret) {
4679 ZigLLVMSetCallSret(result, get_llvm_type(g, src_return_type));4740 ZigLLVMSetCallSret(result, get_llvm_type(g, src_return_type));
4680 return result_loc;4741 return result_loc;
4742 } else if (fn_returns_c_abi_small_struct(fn_type_id)) {
4743 LLVMTypeRef abi_type = get_llvm_c_abi_type(g, src_return_type);
4744 LLVMTypeRef abi_type_ptr = LLVMPointerType(abi_type, 0);
4745 LLVMValueRef bitcast = LLVMBuildBitCast(g->builder, result_loc, abi_type_ptr, "");
4746 LLVMBuildStore(g->builder, result, bitcast);
4747 return result_loc;
4681 } else if (handle_is_ptr(g, src_return_type)) {4748 } else if (handle_is_ptr(g, src_return_type)) {
4682 LLVMValueRef store_instr = LLVMBuildStore(g->builder, result, result_loc);4749 LLVMValueRef store_instr = LLVMBuildStore(g->builder, result, result_loc);
4683 LLVMSetAlignment(store_instr, get_ptr_align(g, instruction->result_loc->value->type));4750 LLVMSetAlignment(store_instr, get_ptr_align(g, instruction->result_loc->value->type));
...@@ -8291,6 +8358,11 @@ static void do_code_gen(CodeGen *g) {...@@ -8291,6 +8358,11 @@ static void do_code_gen(CodeGen *g) {
8291 g->cur_err_ret_trace_val_stack = nullptr;8358 g->cur_err_ret_trace_val_stack = nullptr;
8292 }8359 }
82938360
8361 if (fn_returns_c_abi_small_struct(fn_type_id)) {
8362 LLVMTypeRef abi_type = get_llvm_c_abi_type(g, fn_type_id->return_type);
8363 fn_table_entry->abi_return_value = LLVMBuildAlloca(g->builder, abi_type, "");
8364 }
8365
8294 if (!is_async) {8366 if (!is_async) {
8295 // allocate async frames for nosuspend calls & awaits to async functions8367 // allocate async frames for nosuspend calls & awaits to async functions
8296 ZigType *largest_call_frame_type = nullptr;8368 ZigType *largest_call_frame_type = nullptr;
test/stage1/c_abi/cfuncs.c+89
...@@ -61,7 +61,20 @@ struct SmallStructInts {...@@ -61,7 +61,20 @@ struct SmallStructInts {
61 uint8_t c;61 uint8_t c;
62 uint8_t d;62 uint8_t d;
63};63};
64
64void zig_small_struct_ints(struct SmallStructInts);65void zig_small_struct_ints(struct SmallStructInts);
66struct SmallStructInts zig_ret_small_struct_ints();
67
68struct MedStructMixed {
69 uint32_t a;
70 float b;
71 float c;
72 uint32_t d;
73};
74
75void zig_med_struct_mixed(struct MedStructMixed);
76struct MedStructMixed zig_ret_med_struct_mixed();
77
6578
66struct SplitStructInts {79struct SplitStructInts {
67 uint64_t a;80 uint64_t a;
...@@ -70,6 +83,14 @@ struct SplitStructInts {...@@ -70,6 +83,14 @@ struct SplitStructInts {
70};83};
71void zig_split_struct_ints(struct SplitStructInts);84void zig_split_struct_ints(struct SplitStructInts);
7285
86struct SplitStructMixed {
87 uint64_t a;
88 uint8_t b;
89 float c;
90};
91void zig_split_struct_mixed(struct SplitStructMixed);
92struct SplitStructMixed zig_ret_split_struct_mixed();
93
73struct BigStruct zig_big_struct_both(struct BigStruct);94struct BigStruct zig_big_struct_both(struct BigStruct);
7495
75typedef struct Vector3 {96typedef struct Vector3 {
...@@ -121,6 +142,16 @@ void run_c_tests(void) {...@@ -121,6 +142,16 @@ void run_c_tests(void) {
121 zig_split_struct_ints(s);142 zig_split_struct_ints(s);
122 }143 }
123144
145 {
146 struct MedStructMixed s = {1234, 100.0f, 1337.0f};
147 zig_med_struct_mixed(s);
148 }
149
150 {
151 struct SplitStructMixed s = {1234, 100, 1337.0f};
152 zig_split_struct_mixed(s);
153 }
154
124 {155 {
125 struct BigStruct s = {30, 31, 32, 33, 34};156 struct BigStruct s = {30, 31, 32, 33, 34};
126 struct BigStruct res = zig_big_struct_both(s);157 struct BigStruct res = zig_big_struct_both(s);
...@@ -230,6 +261,44 @@ void c_small_struct_ints(struct SmallStructInts x) {...@@ -230,6 +261,44 @@ void c_small_struct_ints(struct SmallStructInts x) {
230 assert_or_panic(x.b == 2);261 assert_or_panic(x.b == 2);
231 assert_or_panic(x.c == 3);262 assert_or_panic(x.c == 3);
232 assert_or_panic(x.d == 4);263 assert_or_panic(x.d == 4);
264
265 struct SmallStructInts y = zig_ret_small_struct_ints();
266
267 assert_or_panic(y.a == 1);
268 assert_or_panic(y.b == 2);
269 assert_or_panic(y.c == 3);
270 assert_or_panic(y.d == 4);
271}
272
273struct SmallStructInts c_ret_small_struct_ints() {
274 struct SmallStructInts s = {
275 .a = 1,
276 .b = 2,
277 .c = 3,
278 .d = 4,
279 };
280 return s;
281}
282
283void c_med_struct_mixed(struct MedStructMixed x) {
284 assert_or_panic(x.a == 1234);
285 assert_or_panic(x.b == 100.0f);
286 assert_or_panic(x.c == 1337.0f);
287
288 struct MedStructMixed y = zig_ret_med_struct_mixed();
289
290 assert_or_panic(y.a == 1234);
291 assert_or_panic(y.b == 100.0f);
292 assert_or_panic(y.c == 1337.0f);
293}
294
295struct MedStructMixed c_ret_med_struct_mixed() {
296 struct MedStructMixed s = {
297 .a = 1234,
298 .b = 100.0,
299 .c = 1337.0,
300 };
301 return s;
233}302}
234303
235void c_split_struct_ints(struct SplitStructInts x) {304void c_split_struct_ints(struct SplitStructInts x) {
...@@ -238,6 +307,26 @@ void c_split_struct_ints(struct SplitStructInts x) {...@@ -238,6 +307,26 @@ void c_split_struct_ints(struct SplitStructInts x) {
238 assert_or_panic(x.c == 1337);307 assert_or_panic(x.c == 1337);
239}308}
240309
310void c_split_struct_mixed(struct SplitStructMixed x) {
311 assert_or_panic(x.a == 1234);
312 assert_or_panic(x.b == 100);
313 assert_or_panic(x.c == 1337.0f);
314 struct SplitStructMixed y = zig_ret_split_struct_mixed();
315
316 assert_or_panic(y.a == 1234);
317 assert_or_panic(y.b == 100);
318 assert_or_panic(y.c == 1337.0f);
319}
320
321struct SplitStructMixed c_ret_split_struct_mixed() {
322 struct SplitStructMixed s = {
323 .a = 1234,
324 .b = 100,
325 .c = 1337.0f,
326 };
327 return s;
328}
329
241struct BigStruct c_big_struct_both(struct BigStruct x) {330struct BigStruct c_big_struct_both(struct BigStruct x) {
242 assert_or_panic(x.a == 1);331 assert_or_panic(x.a == 1);
243 assert_or_panic(x.b == 2);332 assert_or_panic(x.b == 2);
test/stage1/c_abi/main.zig+87
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const print = std.debug.print;
2const expect = std.testing.expect;3const expect = std.testing.expect;
34
4extern fn run_c_tests() void;5extern fn run_c_tests() void;
...@@ -170,6 +171,34 @@ export fn zig_big_union(x: BigUnion) void {...@@ -170,6 +171,34 @@ export fn zig_big_union(x: BigUnion) void {
170 expect(x.a.e == 5) catch @panic("test failure");171 expect(x.a.e == 5) catch @panic("test failure");
171}172}
172173
174const MedStructMixed = extern struct {
175 a: u32,
176 b: f32,
177 c: f32,
178 d: u32 = 0,
179};
180extern fn c_med_struct_mixed(MedStructMixed) void;
181extern fn c_ret_med_struct_mixed() MedStructMixed;
182
183test "C ABI medium struct of ints and floats" {
184 var s = MedStructMixed{
185 .a = 1234,
186 .b = 100.0,
187 .c = 1337.0,
188 };
189 c_med_struct_mixed(s);
190 var s2 = c_ret_med_struct_mixed();
191 expect(s2.a == 1234) catch @panic("test failure");
192 expect(s2.b == 100.0) catch @panic("test failure");
193 expect(s2.c == 1337.0) catch @panic("test failure");
194}
195
196export fn zig_med_struct_mixed(x: MedStructMixed) void {
197 expect(x.a == 1234) catch @panic("test failure");
198 expect(x.b == 100.0) catch @panic("test failure");
199 expect(x.c == 1337.0) catch @panic("test failure");
200}
201
173const SmallStructInts = extern struct {202const SmallStructInts = extern struct {
174 a: u8,203 a: u8,
175 b: u8,204 b: u8,
...@@ -177,6 +206,7 @@ const SmallStructInts = extern struct {...@@ -177,6 +206,7 @@ const SmallStructInts = extern struct {
177 d: u8,206 d: u8,
178};207};
179extern fn c_small_struct_ints(SmallStructInts) void;208extern fn c_small_struct_ints(SmallStructInts) void;
209extern fn c_ret_small_struct_ints() SmallStructInts;
180210
181test "C ABI small struct of ints" {211test "C ABI small struct of ints" {
182 var s = SmallStructInts{212 var s = SmallStructInts{
...@@ -186,6 +216,11 @@ test "C ABI small struct of ints" {...@@ -186,6 +216,11 @@ test "C ABI small struct of ints" {
186 .d = 4,216 .d = 4,
187 };217 };
188 c_small_struct_ints(s);218 c_small_struct_ints(s);
219 var s2 = c_ret_small_struct_ints();
220 expect(s2.a == 1) catch @panic("test failure");
221 expect(s2.b == 2) catch @panic("test failure");
222 expect(s2.c == 3) catch @panic("test failure");
223 expect(s2.d == 4) catch @panic("test failure");
189}224}
190225
191export fn zig_small_struct_ints(x: SmallStructInts) void {226export fn zig_small_struct_ints(x: SmallStructInts) void {
...@@ -217,6 +252,33 @@ export fn zig_split_struct_ints(x: SplitStructInt) void {...@@ -217,6 +252,33 @@ export fn zig_split_struct_ints(x: SplitStructInt) void {
217 expect(x.c == 1337) catch @panic("test failure");252 expect(x.c == 1337) catch @panic("test failure");
218}253}
219254
255const SplitStructMixed = extern struct {
256 a: u64,
257 b: u8,
258 c: f32,
259};
260extern fn c_split_struct_mixed(SplitStructMixed) void;
261extern fn c_ret_split_struct_mixed() SplitStructMixed;
262
263test "C ABI split struct of ints and floats" {
264 var s = SplitStructMixed{
265 .a = 1234,
266 .b = 100,
267 .c = 1337.0,
268 };
269 c_split_struct_mixed(s);
270 var s2 = c_ret_split_struct_mixed();
271 expect(s2.a == 1234) catch @panic("test failure");
272 expect(s2.b == 100) catch @panic("test failure");
273 expect(s2.c == 1337.0) catch @panic("test failure");
274}
275
276export fn zig_split_struct_mixed(x: SplitStructMixed) void {
277 expect(x.a == 1234) catch @panic("test failure");
278 expect(x.b == 100) catch @panic("test failure");
279 expect(x.c == 1337.0) catch @panic("test failure");
280}
281
220extern fn c_big_struct_both(BigStruct) BigStruct;282extern fn c_big_struct_both(BigStruct) BigStruct;
221283
222test "C ABI sret and byval together" {284test "C ABI sret and byval together" {
...@@ -315,6 +377,31 @@ export fn zig_ret_i64() i64 {...@@ -315,6 +377,31 @@ export fn zig_ret_i64() i64 {
315 return -1;377 return -1;
316}378}
317379
380export fn zig_ret_small_struct_ints() SmallStructInts {
381 return .{
382 .a = 1,
383 .b = 2,
384 .c = 3,
385 .d = 4,
386 };
387}
388
389export fn zig_ret_med_struct_mixed() MedStructMixed {
390 return .{
391 .a = 1234,
392 .b = 100.0,
393 .c = 1337.0,
394 };
395}
396
397export fn zig_ret_split_struct_mixed() SplitStructMixed {
398 return .{
399 .a = 1234,
400 .b = 100,
401 .c = 1337.0,
402 };
403}
404
318extern fn c_ret_bool() bool;405extern fn c_ret_bool() bool;
319extern fn c_ret_u8() u8;406extern fn c_ret_u8() u8;
320extern fn c_ret_u16() u16;407extern fn c_ret_u16() u16;