authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-07 20:09:33-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-07 20:09:33-04:00
log9c169f3cf763c58f91b485f907f80cd8d08c4e12
treea6260d6bf626e87f22630cf466ce856cdec14531
parent9017efee220950b11070242415b6dc456d4442df
signaturelock-open Commit is signed but in an unrecognized format.

C ABI: support returning large structs on x86_64

also panic instead of emitting bad code for returning small structs See #1481

9 files changed, 193 insertions(+), 129 deletions(-)

src/all_types.hpp+7
...@@ -40,6 +40,13 @@ struct Tld;...@@ -40,6 +40,13 @@ struct Tld;
40struct TldExport;40struct TldExport;
41struct IrAnalyze;41struct IrAnalyze;
4242
43enum X64CABIClass {
44 X64CABIClass_Unknown,
45 X64CABIClass_MEMORY,
46 X64CABIClass_INTEGER,
47 X64CABIClass_SSE,
48};
49
43struct IrExecutable {50struct IrExecutable {
44 ZigList<IrBasicBlock *> basic_block_list;51 ZigList<IrBasicBlock *> basic_block_list;
45 Buf *name;52 Buf *name;
src/analyze.cpp+103-6
...@@ -1009,10 +1009,6 @@ ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry) {...@@ -1009,10 +1009,6 @@ ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry) {
1009 return bound_fn_type;1009 return bound_fn_type;
1010}1010}
10111011
1012bool calling_convention_does_first_arg_return(CallingConvention cc) {
1013 return cc == CallingConventionUnspecified;
1014}
1015
1016const char *calling_convention_name(CallingConvention cc) {1012const char *calling_convention_name(CallingConvention cc) {
1017 switch (cc) {1013 switch (cc) {
1018 case CallingConventionUnspecified: return "undefined";1014 case CallingConventionUnspecified: return "undefined";
...@@ -1061,6 +1057,26 @@ ZigType *get_ptr_to_stack_trace_type(CodeGen *g) {...@@ -1061,6 +1057,26 @@ ZigType *get_ptr_to_stack_trace_type(CodeGen *g) {
1061 return g->ptr_to_stack_trace_type;1057 return g->ptr_to_stack_trace_type;
1062}1058}
10631059
1060bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {
1061 if (fn_type_id->cc == CallingConventionUnspecified) {
1062 return handle_is_ptr(fn_type_id->return_type);
1063 }
1064 if (fn_type_id->cc != CallingConventionC) {
1065 return false;
1066 }
1067 if (type_is_c_abi_int(g, fn_type_id->return_type)) {
1068 return false;
1069 }
1070 if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
1071 X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type);
1072 if (abi_class == X64CABIClass_MEMORY) {
1073 return true;
1074 }
1075 zig_panic("TODO implement C ABI for x86_64 return types. '%s'", buf_ptr(&fn_type_id->return_type->name));
1076 }
1077 zig_panic("TODO implement C ABI for this architecture");
1078}
1079
1064ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {1080ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
1065 Error err;1081 Error err;
1066 auto table_entry = g->fn_type_table.maybe_get(fn_type_id);1082 auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
...@@ -1116,8 +1132,7 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {...@@ -1116,8 +1132,7 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
1116 // next, loop over the parameters again and compute debug information1132 // next, loop over the parameters again and compute debug information
1117 // and codegen information1133 // and codegen information
1118 if (!skip_debug_info) {1134 if (!skip_debug_info) {
1119 bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) &&1135 bool first_arg_return = want_first_arg_sret(g, fn_type_id);
1120 handle_is_ptr(fn_type_id->return_type);
1121 bool is_async = fn_type_id->cc == CallingConventionAsync;1136 bool is_async = fn_type_id->cc == CallingConventionAsync;
1122 bool is_c_abi = fn_type_id->cc == CallingConventionC;1137 bool is_c_abi = fn_type_id->cc == CallingConventionC;
1123 bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id);1138 bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id);
...@@ -6367,3 +6382,85 @@ not_integer:...@@ -6367,3 +6382,85 @@ not_integer:
6367 }6382 }
6368 return nullptr;6383 return nullptr;
6369}6384}
6385
6386X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) {
6387 size_t ty_size = type_size(g, ty);
6388 if (get_codegen_ptr_type(ty) != nullptr)
6389 return X64CABIClass_INTEGER;
6390 switch (ty->id) {
6391 case ZigTypeIdEnum:
6392 case ZigTypeIdInt:
6393 case ZigTypeIdBool:
6394 return X64CABIClass_INTEGER;
6395 case ZigTypeIdFloat:
6396 return X64CABIClass_SSE;
6397 case ZigTypeIdStruct: {
6398 // "If the size of an object is larger than four eightbytes, or it contains unaligned
6399 // fields, it has class MEMORY"
6400 if (ty_size > 32)
6401 return X64CABIClass_MEMORY;
6402 if (ty->data.structure.layout != ContainerLayoutExtern) {
6403 // TODO determine whether packed structs have any unaligned fields
6404 return X64CABIClass_Unknown;
6405 }
6406 // "If the size of the aggregate exceeds two eightbytes and the first eight-
6407 // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument
6408 // is passed in memory."
6409 if (ty_size > 16) {
6410 // Zig doesn't support vectors and large fp registers yet, so this will always
6411 // be memory.
6412 return X64CABIClass_MEMORY;
6413 }
6414 X64CABIClass working_class = X64CABIClass_Unknown;
6415 for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
6416 X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields->type_entry);
6417 if (field_class == X64CABIClass_Unknown)
6418 return X64CABIClass_Unknown;
6419 if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) {
6420 working_class = field_class;
6421 }
6422 }
6423 return working_class;
6424 }
6425 case ZigTypeIdUnion: {
6426 // "If the size of an object is larger than four eightbytes, or it contains unaligned
6427 // fields, it has class MEMORY"
6428 if (ty_size > 32)
6429 return X64CABIClass_MEMORY;
6430 if (ty->data.unionation.layout != ContainerLayoutExtern)
6431 return X64CABIClass_MEMORY;
6432 // "If the size of the aggregate exceeds two eightbytes and the first eight-
6433 // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument
6434 // is passed in memory."
6435 if (ty_size > 16) {
6436 // Zig doesn't support vectors and large fp registers yet, so this will always
6437 // be memory.
6438 return X64CABIClass_MEMORY;
6439 }
6440 X64CABIClass working_class = X64CABIClass_Unknown;
6441 for (uint32_t i = 0; i < ty->data.unionation.src_field_count; i += 1) {
6442 X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry);
6443 if (field_class == X64CABIClass_Unknown)
6444 return X64CABIClass_Unknown;
6445 if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) {
6446 working_class = field_class;
6447 }
6448 }
6449 return working_class;
6450 }
6451 default:
6452 return X64CABIClass_Unknown;
6453 }
6454}
6455
6456// NOTE this does not depend on x86_64
6457bool type_is_c_abi_int(CodeGen *g, ZigType *ty) {
6458 return (ty->id == ZigTypeIdInt ||
6459 ty->id == ZigTypeIdFloat ||
6460 ty->id == ZigTypeIdBool ||
6461 ty->id == ZigTypeIdEnum ||
6462 ty->id == ZigTypeIdVoid ||
6463 ty->id == ZigTypeIdUnreachable ||
6464 get_codegen_ptr_type(ty) != nullptr);
6465}
6466
src/analyze.hpp+3-2
...@@ -182,7 +182,6 @@ size_t type_id_index(ZigType *entry);...@@ -182,7 +182,6 @@ size_t type_id_index(ZigType *entry);
182ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id);182ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id);
183Result<bool> type_is_copyable(CodeGen *g, ZigType *type_entry);183Result<bool> type_is_copyable(CodeGen *g, ZigType *type_entry);
184LinkLib *create_link_lib(Buf *name);184LinkLib *create_link_lib(Buf *name);
185bool calling_convention_does_first_arg_return(CallingConvention cc);
186LinkLib *add_link_lib(CodeGen *codegen, Buf *lib);185LinkLib *add_link_lib(CodeGen *codegen, Buf *lib);
187186
188uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry);187uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry);
...@@ -211,6 +210,8 @@ bool calling_convention_allows_zig_types(CallingConvention cc);...@@ -211,6 +210,8 @@ bool calling_convention_allows_zig_types(CallingConvention cc);
211const char *calling_convention_name(CallingConvention cc);210const char *calling_convention_name(CallingConvention cc);
212211
213void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk);212void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk);
214213X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty);
214bool type_is_c_abi_int(CodeGen *g, ZigType *ty);
215bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id);
215216
216#endif217#endif
src/codegen.cpp+22-104
...@@ -577,19 +577,25 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {...@@ -577,19 +577,25 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
577 // use the ABI alignment, which is fine.577 // use the ABI alignment, which is fine.
578 }578 }
579579
580 unsigned init_gen_i = 0;
580 if (!type_has_bits(return_type)) {581 if (!type_has_bits(return_type)) {
581 // nothing to do582 // nothing to do
582 } else if (type_is_codegen_pointer(return_type)) {583 } else if (type_is_codegen_pointer(return_type)) {
583 addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");584 addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");
584 } else if (handle_is_ptr(return_type) && calling_convention_does_first_arg_return(cc)) {585 } else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) {
585 addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");586 addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
586 addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull");587 addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull");
588 if (cc == CallingConventionC) {
589 addLLVMArgAttr(fn_table_entry->llvm_value, 0, "noalias");
590 }
591 init_gen_i = 1;
587 }592 }
588593
589 // set parameter attributes594 // set parameter attributes
590 FnWalk fn_walk = {};595 FnWalk fn_walk = {};
591 fn_walk.id = FnWalkIdAttrs;596 fn_walk.id = FnWalkIdAttrs;
592 fn_walk.data.attrs.fn = fn_table_entry;597 fn_walk.data.attrs.fn = fn_table_entry;
598 fn_walk.data.attrs.gen_i = init_gen_i;
593 walk_function_params(g, fn_type, &fn_walk);599 walk_function_params(g, fn_type, &fn_walk);
594600
595 uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);601 uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
...@@ -1905,95 +1911,6 @@ static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *na...@@ -1905,95 +1911,6 @@ static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *na
1905 return result;1911 return result;
1906}1912}
19071913
1908enum X64CABIClass {
1909 X64CABIClass_Unknown,
1910 X64CABIClass_MEMORY,
1911 X64CABIClass_INTEGER,
1912 X64CABIClass_SSE,
1913};
1914
1915static X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) {
1916 size_t ty_size = type_size(g, ty);
1917 if (get_codegen_ptr_type(ty) != nullptr)
1918 return X64CABIClass_INTEGER;
1919 switch (ty->id) {
1920 case ZigTypeIdEnum:
1921 case ZigTypeIdInt:
1922 case ZigTypeIdBool:
1923 return X64CABIClass_INTEGER;
1924 case ZigTypeIdFloat:
1925 return X64CABIClass_SSE;
1926 case ZigTypeIdStruct: {
1927 // "If the size of an object is larger than four eightbytes, or it contains unaligned
1928 // fields, it has class MEMORY"
1929 if (ty_size > 32)
1930 return X64CABIClass_MEMORY;
1931 if (ty->data.structure.layout != ContainerLayoutExtern) {
1932 // TODO determine whether packed structs have any unaligned fields
1933 return X64CABIClass_Unknown;
1934 }
1935 // "If the size of the aggregate exceeds two eightbytes and the first eight-
1936 // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument
1937 // is passed in memory."
1938 if (ty_size > 16) {
1939 // Zig doesn't support vectors and large fp registers yet, so this will always
1940 // be memory.
1941 return X64CABIClass_MEMORY;
1942 }
1943 X64CABIClass working_class = X64CABIClass_Unknown;
1944 for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
1945 X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields->type_entry);
1946 if (field_class == X64CABIClass_Unknown)
1947 return X64CABIClass_Unknown;
1948 if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) {
1949 working_class = field_class;
1950 }
1951 }
1952 return working_class;
1953 }
1954 case ZigTypeIdUnion: {
1955 // "If the size of an object is larger than four eightbytes, or it contains unaligned
1956 // fields, it has class MEMORY"
1957 if (ty_size > 32)
1958 return X64CABIClass_MEMORY;
1959 if (ty->data.unionation.layout != ContainerLayoutExtern)
1960 return X64CABIClass_MEMORY;
1961 // "If the size of the aggregate exceeds two eightbytes and the first eight-
1962 // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument
1963 // is passed in memory."
1964 if (ty_size > 16) {
1965 // Zig doesn't support vectors and large fp registers yet, so this will always
1966 // be memory.
1967 return X64CABIClass_MEMORY;
1968 }
1969 X64CABIClass working_class = X64CABIClass_Unknown;
1970 for (uint32_t i = 0; i < ty->data.unionation.src_field_count; i += 1) {
1971 X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry);
1972 if (field_class == X64CABIClass_Unknown)
1973 return X64CABIClass_Unknown;
1974 if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) {
1975 working_class = field_class;
1976 }
1977 }
1978 return working_class;
1979 }
1980 default:
1981 return X64CABIClass_Unknown;
1982 }
1983}
1984
1985// NOTE this does not depend on x86_64
1986static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) {
1987 size_t ty_size = type_size(g, ty);
1988 if (ty_size > g->pointer_size_bytes)
1989 return false;
1990 return (ty->id == ZigTypeIdInt ||
1991 ty->id == ZigTypeIdFloat ||
1992 ty->id == ZigTypeIdBool ||
1993 ty->id == ZigTypeIdEnum ||
1994 get_codegen_ptr_type(ty) != nullptr);
1995}
1996
1997static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) {1914static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) {
1998 // Initialized from the type for some walks, but because of C var args,1915 // Initialized from the type for some walks, but because of C var args,
1999 // initialized based on callsite instructions for that one.1916 // initialized based on callsite instructions for that one.
...@@ -2327,15 +2244,13 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns...@@ -2327,15 +2244,13 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
2327 LLVMValueRef value = ir_llvm_value(g, return_instruction->value);2244 LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
2328 ZigType *return_type = return_instruction->value->value.type;2245 ZigType *return_type = return_instruction->value->value.type;
23292246
2330 if (handle_is_ptr(return_type)) {2247 if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) {
2331 if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) {2248 assert(g->cur_ret_ptr);
2332 assert(g->cur_ret_ptr);2249 gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value);
2333 gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value);2250 LLVMBuildRetVoid(g->builder);
2334 LLVMBuildRetVoid(g->builder);2251 } else if (handle_is_ptr(return_type)) {
2335 } else {2252 LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, "");
2336 LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, "");2253 LLVMBuildRet(g->builder, by_val_value);
2337 LLVMBuildRet(g->builder, by_val_value);
2338 }
2339 } else {2254 } else {
2340 LLVMBuildRet(g->builder, value);2255 LLVMBuildRet(g->builder, value);
2341 }2256 }
...@@ -3551,8 +3466,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3551,8 +3466,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
35513466
3552 CallingConvention cc = fn_type->data.fn.fn_type_id.cc;3467 CallingConvention cc = fn_type->data.fn.fn_type_id.cc;
35533468
3554 bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type) &&3469 bool first_arg_ret = ret_has_bits && want_first_arg_sret(g, fn_type_id);
3555 calling_convention_does_first_arg_return(cc);
3556 bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id);3470 bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id);
3557 bool is_var_args = fn_type_id->is_var_args;3471 bool is_var_args = fn_type_id->is_var_args;
3558 ZigList<LLVMValueRef> gen_param_values = {};3472 ZigList<LLVMValueRef> gen_param_values = {};
...@@ -6260,13 +6174,14 @@ static void do_code_gen(CodeGen *g) {...@@ -6260,13 +6174,14 @@ static void do_code_gen(CodeGen *g) {
6260 // Generate function definitions.6174 // Generate function definitions.
6261 for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) {6175 for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) {
6262 ZigFn *fn_table_entry = g->fn_defs.at(fn_i);6176 ZigFn *fn_table_entry = g->fn_defs.at(fn_i);
6263 CallingConvention cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc;6177 FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;
6178 CallingConvention cc = fn_type_id->cc;
6264 bool is_c_abi = cc == CallingConventionC;6179 bool is_c_abi = cc == CallingConventionC;
62656180
6266 LLVMValueRef fn = fn_llvm_value(g, fn_table_entry);6181 LLVMValueRef fn = fn_llvm_value(g, fn_table_entry);
6267 g->cur_fn = fn_table_entry;6182 g->cur_fn = fn_table_entry;
6268 g->cur_fn_val = fn;6183 g->cur_fn_val = fn;
6269 ZigType *return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;6184 ZigType *return_type = fn_type_id->return_type;
6270 if (handle_is_ptr(return_type)) {6185 if (handle_is_ptr(return_type)) {
6271 g->cur_ret_ptr = LLVMGetParam(fn, 0);6186 g->cur_ret_ptr = LLVMGetParam(fn, 0);
6272 } else {6187 } else {
...@@ -6344,13 +6259,15 @@ static void do_code_gen(CodeGen *g) {...@@ -6344,13 +6259,15 @@ static void do_code_gen(CodeGen *g) {
63446259
6345 ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base);6260 ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base);
63466261
6262 unsigned gen_i_init = want_first_arg_sret(g, fn_type_id) ? 1 : 0;
6263
6347 // create debug variable declarations for variables and allocate all local variables6264 // create debug variable declarations for variables and allocate all local variables
6348 FnWalk fn_walk_var = {};6265 FnWalk fn_walk_var = {};
6349 fn_walk_var.id = FnWalkIdVars;6266 fn_walk_var.id = FnWalkIdVars;
6350 fn_walk_var.data.vars.import = import;6267 fn_walk_var.data.vars.import = import;
6351 fn_walk_var.data.vars.fn = fn_table_entry;6268 fn_walk_var.data.vars.fn = fn_table_entry;
6352 fn_walk_var.data.vars.llvm_fn = fn;6269 fn_walk_var.data.vars.llvm_fn = fn;
6353 fn_walk_var.data.vars.gen_i = 0;6270 fn_walk_var.data.vars.gen_i = gen_i_init;
6354 for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) {6271 for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) {
6355 ZigVar *var = fn_table_entry->variable_list.at(var_i);6272 ZigVar *var = fn_table_entry->variable_list.at(var_i);
63566273
...@@ -6429,6 +6346,7 @@ static void do_code_gen(CodeGen *g) {...@@ -6429,6 +6346,7 @@ static void do_code_gen(CodeGen *g) {
6429 fn_walk_init.id = FnWalkIdInits;6346 fn_walk_init.id = FnWalkIdInits;
6430 fn_walk_init.data.inits.fn = fn_table_entry;6347 fn_walk_init.data.inits.fn = fn_table_entry;
6431 fn_walk_init.data.inits.llvm_fn = fn;6348 fn_walk_init.data.inits.llvm_fn = fn;
6349 fn_walk_init.data.inits.gen_i = gen_i_init;
6432 walk_function_params(g, fn_table_entry->type_entry, &fn_walk_init);6350 walk_function_params(g, fn_table_entry->type_entry, &fn_walk_init);
64336351
6434 ir_render(g, fn_table_entry);6352 ir_render(g, fn_table_entry);
test/behavior.zig-1
...@@ -9,7 +9,6 @@ comptime {...@@ -9,7 +9,6 @@ comptime {
9 _ = @import("cases/bitcast.zig");9 _ = @import("cases/bitcast.zig");
10 _ = @import("cases/bool.zig");10 _ = @import("cases/bool.zig");
11 _ = @import("cases/bugs/1111.zig");11 _ = @import("cases/bugs/1111.zig");
12 _ = @import("cases/bugs/1230.zig");
13 _ = @import("cases/bugs/1277.zig");12 _ = @import("cases/bugs/1277.zig");
14 _ = @import("cases/bugs/1421.zig");13 _ = @import("cases/bugs/1421.zig");
15 _ = @import("cases/bugs/394.zig");14 _ = @import("cases/bugs/394.zig");
test/cases/bugs/1230.zig deleted-14
...@@ -1,14 +0,0 @@
1const assert = @import("std").debug.assert;
2
3const S = extern struct {
4 x: i32,
5};
6
7extern fn ret_struct() S {
8 return S{ .x = 42 };
9}
10
11test "extern return small struct (bug 1230)" {
12 const s = ret_struct();
13 assert(s.x == 42);
14}
test/stage1/c_abi/build.zig+1
...@@ -5,6 +5,7 @@ pub fn build(b: *Builder) void {...@@ -5,6 +5,7 @@ pub fn build(b: *Builder) void {
55
6 const c_obj = b.addCObject("cfuncs", "cfuncs.c");6 const c_obj = b.addCObject("cfuncs", "cfuncs.c");
7 c_obj.setBuildMode(rel_opts);7 c_obj.setBuildMode(rel_opts);
8 c_obj.setNoStdLib(true);
89
9 const main = b.addTest("main.zig");10 const main = b.addTest("main.zig");
10 main.setBuildMode(rel_opts);11 main.setBuildMode(rel_opts);
test/stage1/c_abi/cfuncs.c+23-2
...@@ -59,6 +59,8 @@ struct SplitStructInts {...@@ -59,6 +59,8 @@ struct SplitStructInts {
59};59};
60void zig_split_struct_ints(struct SplitStructInts);60void zig_split_struct_ints(struct SplitStructInts);
6161
62struct BigStruct zig_big_struct_both(struct BigStruct);
63
62void run_c_tests(void) {64void run_c_tests(void) {
63 zig_u8(0xff);65 zig_u8(0xff);
64 zig_u16(0xfffe);66 zig_u16(0xfffe);
...@@ -77,8 +79,7 @@ void run_c_tests(void) {...@@ -77,8 +79,7 @@ void run_c_tests(void) {
7779
78 zig_bool(true);80 zig_bool(true);
7981
80 // TODO making this non-static crashes for some reason82 uint8_t array[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
81 static uint8_t array[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
82 zig_array(array);83 zig_array(array);
8384
84 {85 {
...@@ -95,6 +96,16 @@ void run_c_tests(void) {...@@ -95,6 +96,16 @@ void run_c_tests(void) {
95 struct SplitStructInts s = {1234, 100, 1337};96 struct SplitStructInts s = {1234, 100, 1337};
96 zig_split_struct_ints(s);97 zig_split_struct_ints(s);
97 }98 }
99
100 {
101 struct BigStruct s = {30, 31, 32, 33, 34};
102 struct BigStruct res = zig_big_struct_both(s);
103 assert_or_panic(res.a == 20);
104 assert_or_panic(res.b == 21);
105 assert_or_panic(res.c == 22);
106 assert_or_panic(res.d == 23);
107 assert_or_panic(res.e == 24);
108 }
98}109}
99110
100void c_u8(uint8_t x) {111void c_u8(uint8_t x) {
...@@ -185,3 +196,13 @@ void c_split_struct_ints(struct SplitStructInts x) {...@@ -185,3 +196,13 @@ void c_split_struct_ints(struct SplitStructInts x) {
185 assert_or_panic(x.b == 100);196 assert_or_panic(x.b == 100);
186 assert_or_panic(x.c == 1337);197 assert_or_panic(x.c == 1337);
187}198}
199
200struct BigStruct c_big_struct_both(struct BigStruct x) {
201 assert_or_panic(x.a == 1);
202 assert_or_panic(x.b == 2);
203 assert_or_panic(x.c == 3);
204 assert_or_panic(x.d == 4);
205 assert_or_panic(x.e == 5);
206 struct BigStruct y = {10, 11, 12, 13, 14};
207 return y;
208}
test/stage1/c_abi/main.zig+34
...@@ -203,3 +203,37 @@ export fn zig_split_struct_ints(x: SplitStructInt) void {...@@ -203,3 +203,37 @@ export fn zig_split_struct_ints(x: SplitStructInt) void {
203 assertOrPanic(x.b == 100);203 assertOrPanic(x.b == 100);
204 assertOrPanic(x.c == 1337);204 assertOrPanic(x.c == 1337);
205}205}
206
207extern fn c_big_struct_both(BigStruct) BigStruct;
208
209test "C ABI sret and byval together" {
210 var s = BigStruct{
211 .a = 1,
212 .b = 2,
213 .c = 3,
214 .d = 4,
215 .e = 5,
216 };
217 var y = c_big_struct_both(s);
218 assertOrPanic(y.a == 10);
219 assertOrPanic(y.b == 11);
220 assertOrPanic(y.c == 12);
221 assertOrPanic(y.d == 13);
222 assertOrPanic(y.e == 14);
223}
224
225export fn zig_big_struct_both(x: BigStruct) BigStruct {
226 assertOrPanic(x.a == 30);
227 assertOrPanic(x.b == 31);
228 assertOrPanic(x.c == 32);
229 assertOrPanic(x.d == 33);
230 assertOrPanic(x.e == 34);
231 var s = BigStruct{
232 .a = 20,
233 .b = 21,
234 .c = 22,
235 .d = 23,
236 .e = 24,
237 };
238 return s;
239}