| author | |
| committer | |
| log | 9c169f3cf763c58f91b485f907f80cd8d08c4e12 |
| tree | a6260d6bf626e87f22630cf466ce856cdec14531 |
| parent | 9017efee220950b11070242415b6dc456d4442df |
| signature |
also panic instead of emitting bad code for returning small structs
See #14819 files changed, 193 insertions(+), 129 deletions(-)
src/all_types.hpp+7| ... | @@ -40,6 +40,13 @@ struct Tld; | ... | @@ -40,6 +40,13 @@ struct Tld; |
| 40 | struct TldExport; | 40 | struct TldExport; |
| 41 | struct IrAnalyze; | 41 | struct IrAnalyze; |
| 42 | 42 | ||
| 43 | enum X64CABIClass { | ||
| 44 | X64CABIClass_Unknown, | ||
| 45 | X64CABIClass_MEMORY, | ||
| 46 | X64CABIClass_INTEGER, | ||
| 47 | X64CABIClass_SSE, | ||
| 48 | }; | ||
| 49 | |||
| 43 | struct IrExecutable { | 50 | struct 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 | } |
| 1011 | 1011 | ||
| 1012 | bool calling_convention_does_first_arg_return(CallingConvention cc) { | ||
| 1013 | return cc == CallingConventionUnspecified; | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | const char *calling_convention_name(CallingConvention cc) { | 1012 | const 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 | } |
| 1063 | 1059 | ||
| 1060 | bool 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 | |||
| 1064 | ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { | 1080 | ZigType *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 information | 1132 | // next, loop over the parameters again and compute debug information |
| 1117 | // and codegen information | 1133 | // 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 | |||
| 6386 | X64CABIClass 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 | ||
| 6457 | bool 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); |
| 182 | ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id); | 182 | ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id); |
| 183 | Result<bool> type_is_copyable(CodeGen *g, ZigType *type_entry); | 183 | Result<bool> type_is_copyable(CodeGen *g, ZigType *type_entry); |
| 184 | LinkLib *create_link_lib(Buf *name); | 184 | LinkLib *create_link_lib(Buf *name); |
| 185 | bool calling_convention_does_first_arg_return(CallingConvention cc); | ||
| 186 | LinkLib *add_link_lib(CodeGen *codegen, Buf *lib); | 185 | LinkLib *add_link_lib(CodeGen *codegen, Buf *lib); |
| 187 | 186 | ||
| 188 | uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry); | 187 | uint32_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); |
| 211 | const char *calling_convention_name(CallingConvention cc); | 210 | const char *calling_convention_name(CallingConvention cc); |
| 212 | 211 | ||
| 213 | void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk); | 212 | void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk); |
| 214 | 213 | X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty); | |
| 214 | bool type_is_c_abi_int(CodeGen *g, ZigType *ty); | ||
| 215 | bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id); | ||
| 215 | 216 | ||
| 216 | #endif | 217 | #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 | } |
| 579 | 579 | ||
| 580 | unsigned init_gen_i = 0; | ||
| 580 | if (!type_has_bits(return_type)) { | 581 | if (!type_has_bits(return_type)) { |
| 581 | // nothing to do | 582 | // 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 | } |
| 588 | 593 | ||
| 589 | // set parameter attributes | 594 | // 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); |
| 594 | 600 | ||
| 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 | } |
| 1907 | 1913 | ||
| 1908 | enum X64CABIClass { | ||
| 1909 | X64CABIClass_Unknown, | ||
| 1910 | X64CABIClass_MEMORY, | ||
| 1911 | X64CABIClass_INTEGER, | ||
| 1912 | X64CABIClass_SSE, | ||
| 1913 | }; | ||
| 1914 | |||
| 1915 | static 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 | ||
| 1986 | static 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 | |||
| 1997 | static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) { | 1914 | static 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; |
| 2329 | 2246 | ||
| 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 |
| 3551 | 3466 | ||
| 3552 | CallingConvention cc = fn_type->data.fn.fn_type_id.cc; | 3467 | CallingConvention cc = fn_type->data.fn.fn_type_id.cc; |
| 3553 | 3468 | ||
| 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; |
| 6265 | 6180 | ||
| 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) { |
| 6344 | 6259 | ||
| 6345 | ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); | 6260 | ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); |
| 6346 | 6261 | ||
| 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 variables | 6264 | // 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); |
| 6356 | 6273 | ||
| ... | @@ -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); |
| 6433 | 6351 | ||
| 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 @@ | ||
| 1 | const assert = @import("std").debug.assert; | ||
| 2 | |||
| 3 | const S = extern struct { | ||
| 4 | x: i32, | ||
| 5 | }; | ||
| 6 | |||
| 7 | extern fn ret_struct() S { | ||
| 8 | return S{ .x = 42 }; | ||
| 9 | } | ||
| 10 | |||
| 11 | test "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 { |
| 5 | 5 | ||
| 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); | ||
| 8 | 9 | ||
| 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 | }; |
| 60 | void zig_split_struct_ints(struct SplitStructInts); | 60 | void zig_split_struct_ints(struct SplitStructInts); |
| 61 | 61 | ||
| 62 | struct BigStruct zig_big_struct_both(struct BigStruct); | ||
| 63 | |||
| 62 | void run_c_tests(void) { | 64 | void 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) { |
| 77 | 79 | ||
| 78 | zig_bool(true); | 80 | zig_bool(true); |
| 79 | 81 | ||
| 80 | // TODO making this non-static crashes for some reason | 82 | 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); |
| 83 | 84 | ||
| 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 | } |
| 99 | 110 | ||
| 100 | void c_u8(uint8_t x) { | 111 | void 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 | |||
| 200 | struct 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 | |||
| 207 | extern fn c_big_struct_both(BigStruct) BigStruct; | ||
| 208 | |||
| 209 | test "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 | |||
| 225 | export 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 | } |