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 {
107107 X64CABIClass_MEMORY_nobyval,
108108 X64CABIClass_INTEGER,
109109 X64CABIClass_SSE,
110 X64CABIClass_AGG,
110111};
111112
112113struct Stage1Zir {
......@@ -1569,8 +1570,9 @@ struct ZigType {
15691570
15701571 // These are not supposed to be accessed directly. They're
15711572 // null during semantic analysis, memoized with get_llvm_type
1572 // and get_llvm_di_type
1573 // get_llvm_c_abi_type and get_llvm_di_type
15731574 LLVMTypeRef llvm_type;
1575 LLVMTypeRef llvm_c_abi_type;
15741576 ZigLLVMDIType *llvm_di_type;
15751577
15761578 union {
......@@ -1624,6 +1626,7 @@ struct GlobalExport {
16241626
16251627struct ZigFn {
16261628 LLVMValueRef llvm_value;
1629 LLVMValueRef abi_return_value; // alloca used when converting at SysV ABI boundaries
16271630 const char *llvm_name;
16281631 AstNode *proto_node;
16291632 AstNode *body_node;
src/stage1/analyze.cpp+120-4
......@@ -6063,6 +6063,12 @@ Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result) {
60636063 return ErrorNone;
60646064}
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
60666072// Whether you can infer the value based solely on the type.
60676073OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) {
60686074 assert(type_entry != nullptr);
......@@ -8376,6 +8382,9 @@ static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size
83768382 // be memory.
83778383 return X64CABIClass_MEMORY;
83788384 }
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"
83798388 X64CABIClass working_class = X64CABIClass_Unknown;
83808389 for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
83818390 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
83858394 working_class = field_class;
83868395 }
83878396 }
8388 return working_class;
8397 if (working_class == X64CABIClass_MEMORY) {
8398 return X64CABIClass_MEMORY;
8399 }
8400 return X64CABIClass_AGG;
83898401 }
83908402 case ZigTypeIdUnion: {
83918403 // "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
84078419 X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry);
84088420 if (field_class == X64CABIClass_Unknown)
84098421 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) {
84118423 working_class = field_class;
84128424 }
84138425 }
......@@ -8678,6 +8690,95 @@ static LLVMTypeRef get_llvm_type_of_n_bytes(unsigned byte_size) {
86788690 LLVMInt8Type() : LLVMArrayType(LLVMInt8Type(), byte_size);
86798691}
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
86818782static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveStatus wanted_resolve_status,
86828783 ZigType *async_frame_type)
86838784{
......@@ -8936,6 +9037,9 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
89369037 g->type_resolve_stack.swap_remove(struct_type->data.structure.llvm_full_type_queue_index);
89379038 struct_type->data.structure.llvm_full_type_queue_index = SIZE_MAX;
89389039 }
9040
9041 if (struct_type->abi_size <= 16 && struct_type->data.structure.layout == ContainerLayoutExtern)
9042 resolve_llvm_c_abi_type(g, struct_type);
89399043}
89409044
89419045// 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) {
95369640 assert(gen_param_types.items[i] != nullptr);
95379641 }
95389642
9539 fn_type->data.fn.raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type),
9540 gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args);
9643 if (!first_arg_return && fn_returns_c_abi_small_struct(fn_type_id)) {
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 }
95419650 const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref);
95429651 fn_type->llvm_type = LLVMPointerType(fn_type->data.fn.raw_type_ref, fn_addrspace);
95439652 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
98279936 zig_unreachable();
98289937}
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
98309946LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type) {
98319947 assertNoError(type_resolve(g, type, ResolveStatusLLVMFull));
98329948 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);
5454bool type_has_bits(CodeGen *g, ZigType *type_entry);
5555Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result);
5656
57bool fn_returns_c_abi_small_struct(FnTypeId *fn_type_id);
58
5759enum ExternPosition {
5860 ExternPositionFunctionParameter,
5961 ExternPositionFunctionReturn,
......@@ -268,6 +270,7 @@ Buf *type_bare_name(ZigType *t);
268270Buf *type_h_name(ZigType *t);
269271
270272LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type);
273LLVMTypeRef get_llvm_c_abi_type(CodeGen *g, ZigType *type);
271274ZigLLVMDIType *get_llvm_di_type(CodeGen *g, ZigType *type);
272275
273276void 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_
21422142 }
21432143 }
21442144 return true;
2145 } else if (abi_class == X64CABIClass_SSE) {
2146 // For now only handle structs with only floats/doubles in it.
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.
2145 } else if (abi_class == X64CABIClass_AGG) {
2146 // The SystemV ABI says that we have to setup 1 register per eightbyte.
21662147 // 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 if
2168 // the type is bigger than 8 bytes.
2148 // Similarly, two i32 can be passed in one i64, but 3 i32 have to be passed in 2 registers.
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
21702156 // Example:
21712157 // extern struct {
21722158 // x: f32,
21732159 // y: f32,
2174 // z: f32,
2160 // z: i32,
21752161 // };
2176 // const ptr = (*f64)*Struct;
2177 // Register 1: ptr.*
2178 // Register 2: (ptr + 1).*
2162 // LLVM abi type: { double, i32 }
2163 // const ptr = (*abi_type)*Struct;
2164 // FP Register 1: abi_type[0]
2165 // Register 1: abi_type[1]
21792166
2180 // One floating point register per f64 or 2 f32's
2181 size_t number_of_fp_regs = (ty_size + 7) / 8;
2167 // However, if the struct fits in one register, then we'll pass it as such
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
21832174 switch (fn_walk->id) {
21842175 case FnWalkIdAttrs: {
2185 fn_walk->data.attrs.gen_i += number_of_fp_regs;
2176 fn_walk->data.attrs.gen_i += number_of_regs;
21862177 break;
21872178 }
21882179 case FnWalkIdCall: {
2189 LLVMValueRef f64_ptr_to_struct = LLVMBuildBitCast(g->builder, val, LLVMPointerType(LLVMDoubleType(), 0), "");
2190 for (uint32_t i = 0; i < number_of_fp_regs; i += 1) {
2191 LLVMValueRef index = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, i, false);
2192 LLVMValueRef indices[] = { index };
2193 LLVMValueRef adjusted_ptr_to_struct = LLVMBuildInBoundsGEP(g->builder, f64_ptr_to_struct, indices, 1, "");
2180 LLVMValueRef abi_ptr_to_struct = LLVMBuildBitCast(g->builder, val, LLVMPointerType(abi_type, 0), "");
2181 if (number_of_regs == 1) {
2182 LLVMValueRef loaded = LLVMBuildLoad(g->builder, abi_ptr_to_struct, "");
2183 fn_walk->data.call.gen_param_values->append(loaded);
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, "");
21942191 LLVMValueRef loaded = LLVMBuildLoad(g->builder, adjusted_ptr_to_struct, "");
21952192 fn_walk->data.call.gen_param_values->append(loaded);
21962193 }
21972194 break;
21982195 }
21992196 case FnWalkIdTypes: {
2200 for (uint32_t i = 0; i < number_of_fp_regs; i += 1) {
2201 fn_walk->data.types.gen_param_types->append(get_llvm_type(g, g->builtin_types.entry_f64));
2197 if (number_of_regs == 1) {
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));
22022204 fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, g->builtin_types.entry_f64));
22032205 }
22042206 break;
22052207 }
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 }
22072215 case FnWalkIdInits: {
2208 // TODO: Handle exporting functions
2209 if (source_node != nullptr) {
2210 give_up_with_c_abi_error(g, source_node);
2216 // since we're representing the struct differently as an arg, and potentially
2217 // splitting it, we have to do some work to put it back together.
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;
22112236 }
2212 // otherwise allow codegen code to report a compile error
2213 return false;
2237 if (var->decl_node) {
2238 gen_var_debug_decl(g, var);
2239 }
2240 fn_walk->data.inits.gen_i += 1;
2241 break;
22142242 }
22152243 }
22162244 return true;
......@@ -2654,13 +2682,36 @@ static void gen_async_return(CodeGen *g, Stage1AirInstReturn *instruction) {
26542682 LLVMBuildRetVoid(g->builder);
26552683}
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
26572706static LLVMValueRef ir_render_return(CodeGen *g, Stage1Air *executable, Stage1AirInstReturn *instruction) {
26582707 if (fn_is_async(g->cur_fn)) {
26592708 gen_async_return(g, instruction);
26602709 return nullptr;
26612710 }
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)) {
26642715 if (instruction->operand == nullptr) {
26652716 LLVMBuildRetVoid(g->builder);
26662717 return nullptr;
......@@ -2671,6 +2722,16 @@ static LLVMValueRef ir_render_return(CodeGen *g, Stage1Air *executable, Stage1Ai
26712722 ZigType *return_type = instruction->operand->value->type;
26722723 gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value);
26732724 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 }
26742735 } else if (g->cur_fn->type_entry->data.fn.fn_type_id.cc != CallingConventionAsync &&
26752736 handle_is_ptr(g, g->cur_fn->type_entry->data.fn.fn_type_id.return_type))
26762737 {
......@@ -4678,6 +4739,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, Stage1Air *executable, Stage1AirI
46784739 } else if (first_arg_ret) {
46794740 ZigLLVMSetCallSret(result, get_llvm_type(g, src_return_type));
46804741 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;
46814748 } else if (handle_is_ptr(g, src_return_type)) {
46824749 LLVMValueRef store_instr = LLVMBuildStore(g->builder, result, result_loc);
46834750 LLVMSetAlignment(store_instr, get_ptr_align(g, instruction->result_loc->value->type));
......@@ -8291,6 +8358,11 @@ static void do_code_gen(CodeGen *g) {
82918358 g->cur_err_ret_trace_val_stack = nullptr;
82928359 }
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
82948366 if (!is_async) {
82958367 // allocate async frames for nosuspend calls & awaits to async functions
82968368 ZigType *largest_call_frame_type = nullptr;
test/stage1/c_abi/cfuncs.c+89
......@@ -61,7 +61,20 @@ struct SmallStructInts {
6161 uint8_t c;
6262 uint8_t d;
6363};
64
6465void 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
6679struct SplitStructInts {
6780 uint64_t a;
......@@ -70,6 +83,14 @@ struct SplitStructInts {
7083};
7184void 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
7394struct BigStruct zig_big_struct_both(struct BigStruct);
7495
7596typedef struct Vector3 {
......@@ -121,6 +142,16 @@ void run_c_tests(void) {
121142 zig_split_struct_ints(s);
122143 }
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
124155 {
125156 struct BigStruct s = {30, 31, 32, 33, 34};
126157 struct BigStruct res = zig_big_struct_both(s);
......@@ -230,6 +261,44 @@ void c_small_struct_ints(struct SmallStructInts x) {
230261 assert_or_panic(x.b == 2);
231262 assert_or_panic(x.c == 3);
232263 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;
233302}
234303
235304void c_split_struct_ints(struct SplitStructInts x) {
......@@ -238,6 +307,26 @@ void c_split_struct_ints(struct SplitStructInts x) {
238307 assert_or_panic(x.c == 1337);
239308}
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
241330struct BigStruct c_big_struct_both(struct BigStruct x) {
242331 assert_or_panic(x.a == 1);
243332 assert_or_panic(x.b == 2);
test/stage1/c_abi/main.zig+87
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const print = std.debug.print;
23const expect = std.testing.expect;
34
45extern fn run_c_tests() void;
......@@ -170,6 +171,34 @@ export fn zig_big_union(x: BigUnion) void {
170171 expect(x.a.e == 5) catch @panic("test failure");
171172}
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
173202const SmallStructInts = extern struct {
174203 a: u8,
175204 b: u8,
......@@ -177,6 +206,7 @@ const SmallStructInts = extern struct {
177206 d: u8,
178207};
179208extern fn c_small_struct_ints(SmallStructInts) void;
209extern fn c_ret_small_struct_ints() SmallStructInts;
180210
181211test "C ABI small struct of ints" {
182212 var s = SmallStructInts{
......@@ -186,6 +216,11 @@ test "C ABI small struct of ints" {
186216 .d = 4,
187217 };
188218 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");
189224}
190225
191226export fn zig_small_struct_ints(x: SmallStructInts) void {
......@@ -217,6 +252,33 @@ export fn zig_split_struct_ints(x: SplitStructInt) void {
217252 expect(x.c == 1337) catch @panic("test failure");
218253}
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
220282extern fn c_big_struct_both(BigStruct) BigStruct;
221283
222284test "C ABI sret and byval together" {
......@@ -315,6 +377,31 @@ export fn zig_ret_i64() i64 {
315377 return -1;
316378}
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
318405extern fn c_ret_bool() bool;
319406extern fn c_ret_u8() u8;
320407extern fn c_ret_u16() u16;