| author | |
| committer | |
| log | 4af844732a88393b172d33676732580b056910f6 |
| tree | 32a05bfbf51e9f8fc0339ca44658e04c21345788 |
| parent | 67735c6f1557092efe6e8c1712445c30655fe283 |
| parent | 7dd3c3814de0caf808bc112aa07044cdd8bba135 |
| signature |
22 files changed, 1309 insertions(+), 204 deletions(-)
build.zig+5-1| ... | ... | @@ -76,7 +76,11 @@ pub fn build(b: *Builder) !void { |
| 76 | 76 | |
| 77 | 77 | const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); |
| 78 | 78 | test_stage2_step.dependOn(&test_stage2.step); |
| 79 | test_step.dependOn(test_stage2_step); | |
| 79 | ||
| 80 | // TODO see https://github.com/ziglang/zig/issues/1364 | |
| 81 | if (false) { | |
| 82 | test_step.dependOn(test_stage2_step); | |
| 83 | } | |
| 80 | 84 | |
| 81 | 85 | const all_modes = []builtin.Mode{ |
| 82 | 86 | builtin.Mode.Debug, |
src/all_types.hpp+56| ... | ... | @@ -41,6 +41,13 @@ struct Tld; |
| 41 | 41 | struct TldExport; |
| 42 | 42 | struct IrAnalyze; |
| 43 | 43 | |
| 44 | enum X64CABIClass { | |
| 45 | X64CABIClass_Unknown, | |
| 46 | X64CABIClass_MEMORY, | |
| 47 | X64CABIClass_INTEGER, | |
| 48 | X64CABIClass_SSE, | |
| 49 | }; | |
| 50 | ||
| 44 | 51 | struct IrExecutable { |
| 45 | 52 | ZigList<IrBasicBlock *> basic_block_list; |
| 46 | 53 | Buf *name; |
| ... | ... | @@ -3282,4 +3289,53 @@ enum FloatMode { |
| 3282 | 3289 | FloatModeStrict, |
| 3283 | 3290 | }; |
| 3284 | 3291 | |
| 3292 | enum FnWalkId { | |
| 3293 | FnWalkIdAttrs, | |
| 3294 | FnWalkIdCall, | |
| 3295 | FnWalkIdTypes, | |
| 3296 | FnWalkIdVars, | |
| 3297 | FnWalkIdInits, | |
| 3298 | }; | |
| 3299 | ||
| 3300 | struct FnWalkAttrs { | |
| 3301 | ZigFn *fn; | |
| 3302 | unsigned gen_i; | |
| 3303 | }; | |
| 3304 | ||
| 3305 | struct FnWalkCall { | |
| 3306 | ZigList<LLVMValueRef> *gen_param_values; | |
| 3307 | IrInstructionCall *inst; | |
| 3308 | bool is_var_args; | |
| 3309 | }; | |
| 3310 | ||
| 3311 | struct FnWalkTypes { | |
| 3312 | ZigList<ZigLLVMDIType *> *param_di_types; | |
| 3313 | ZigList<LLVMTypeRef> *gen_param_types; | |
| 3314 | }; | |
| 3315 | ||
| 3316 | struct FnWalkVars { | |
| 3317 | ImportTableEntry *import; | |
| 3318 | LLVMValueRef llvm_fn; | |
| 3319 | ZigFn *fn; | |
| 3320 | ZigVar *var; | |
| 3321 | unsigned gen_i; | |
| 3322 | }; | |
| 3323 | ||
| 3324 | struct FnWalkInits { | |
| 3325 | LLVMValueRef llvm_fn; | |
| 3326 | ZigFn *fn; | |
| 3327 | unsigned gen_i; | |
| 3328 | }; | |
| 3329 | ||
| 3330 | struct FnWalk { | |
| 3331 | FnWalkId id; | |
| 3332 | union { | |
| 3333 | FnWalkAttrs attrs; | |
| 3334 | FnWalkCall call; | |
| 3335 | FnWalkTypes types; | |
| 3336 | FnWalkVars vars; | |
| 3337 | FnWalkInits inits; | |
| 3338 | } data; | |
| 3339 | }; | |
| 3340 | ||
| 3285 | 3341 | #endif |
src/analyze.cpp+148-37| ... | ... | @@ -1009,10 +1009,6 @@ ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry) { |
| 1009 | 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 | 1012 | const char *calling_convention_name(CallingConvention cc) { |
| 1017 | 1013 | switch (cc) { |
| 1018 | 1014 | case CallingConventionUnspecified: return "undefined"; |
| ... | ... | @@ -1061,6 +1057,27 @@ ZigType *get_ptr_to_stack_trace_type(CodeGen *g) { |
| 1061 | 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. type '%s'\nSee https://github.com/ziglang/zig/issues/1481", | |
| 1076 | buf_ptr(&fn_type_id->return_type->name)); | |
| 1077 | } | |
| 1078 | zig_panic("TODO implement C ABI for this architecture. See https://github.com/ziglang/zig/issues/1481"); | |
| 1079 | } | |
| 1080 | ||
| 1064 | 1081 | ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 1065 | 1082 | Error err; |
| 1066 | 1083 | auto table_entry = g->fn_type_table.maybe_get(fn_type_id); |
| ... | ... | @@ -1116,21 +1133,20 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 1116 | 1133 | // next, loop over the parameters again and compute debug information |
| 1117 | 1134 | // and codegen information |
| 1118 | 1135 | if (!skip_debug_info) { |
| 1119 | bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) && | |
| 1120 | handle_is_ptr(fn_type_id->return_type); | |
| 1136 | bool first_arg_return = want_first_arg_sret(g, fn_type_id); | |
| 1121 | 1137 | bool is_async = fn_type_id->cc == CallingConventionAsync; |
| 1138 | bool is_c_abi = fn_type_id->cc == CallingConventionC; | |
| 1122 | 1139 | bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id); |
| 1123 | 1140 | // +1 for maybe making the first argument the return value |
| 1124 | 1141 | // +1 for maybe first argument the error return trace |
| 1125 | 1142 | // +2 for maybe arguments async allocator and error code pointer |
| 1126 | LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(4 + fn_type_id->param_count); | |
| 1143 | ZigList<LLVMTypeRef> gen_param_types = {}; | |
| 1127 | 1144 | // +1 because 0 is the return type and |
| 1128 | 1145 | // +1 for maybe making first arg ret val and |
| 1129 | 1146 | // +1 for maybe first argument the error return trace |
| 1130 | 1147 | // +2 for maybe arguments async allocator and error code pointer |
| 1131 | ZigLLVMDIType **param_di_types = allocate<ZigLLVMDIType*>(5 + fn_type_id->param_count); | |
| 1132 | param_di_types[0] = fn_type_id->return_type->di_type; | |
| 1133 | size_t gen_param_index = 0; | |
| 1148 | ZigList<ZigLLVMDIType *> param_di_types = {}; | |
| 1149 | param_di_types.append(fn_type_id->return_type->di_type); | |
| 1134 | 1150 | ZigType *gen_return_type; |
| 1135 | 1151 | if (is_async) { |
| 1136 | 1152 | gen_return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false); |
| ... | ... | @@ -1138,10 +1154,8 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 1138 | 1154 | gen_return_type = g->builtin_types.entry_void; |
| 1139 | 1155 | } else if (first_arg_return) { |
| 1140 | 1156 | ZigType *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false); |
| 1141 | gen_param_types[gen_param_index] = gen_type->type_ref; | |
| 1142 | gen_param_index += 1; | |
| 1143 | // after the gen_param_index += 1 because 0 is the return type | |
| 1144 | param_di_types[gen_param_index] = gen_type->di_type; | |
| 1157 | gen_param_types.append(gen_type->type_ref); | |
| 1158 | param_di_types.append(gen_type->di_type); | |
| 1145 | 1159 | gen_return_type = g->builtin_types.entry_void; |
| 1146 | 1160 | } else { |
| 1147 | 1161 | gen_return_type = fn_type_id->return_type; |
| ... | ... | @@ -1150,28 +1164,22 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 1150 | 1164 | |
| 1151 | 1165 | if (prefix_arg_error_return_trace) { |
| 1152 | 1166 | ZigType *gen_type = get_ptr_to_stack_trace_type(g); |
| 1153 | gen_param_types[gen_param_index] = gen_type->type_ref; | |
| 1154 | gen_param_index += 1; | |
| 1155 | // after the gen_param_index += 1 because 0 is the return type | |
| 1156 | param_di_types[gen_param_index] = gen_type->di_type; | |
| 1167 | gen_param_types.append(gen_type->type_ref); | |
| 1168 | param_di_types.append(gen_type->di_type); | |
| 1157 | 1169 | } |
| 1158 | 1170 | if (is_async) { |
| 1159 | 1171 | { |
| 1160 | 1172 | // async allocator param |
| 1161 | 1173 | ZigType *gen_type = fn_type_id->async_allocator_type; |
| 1162 | gen_param_types[gen_param_index] = gen_type->type_ref; | |
| 1163 | gen_param_index += 1; | |
| 1164 | // after the gen_param_index += 1 because 0 is the return type | |
| 1165 | param_di_types[gen_param_index] = gen_type->di_type; | |
| 1174 | gen_param_types.append(gen_type->type_ref); | |
| 1175 | param_di_types.append(gen_type->di_type); | |
| 1166 | 1176 | } |
| 1167 | 1177 | |
| 1168 | 1178 | { |
| 1169 | 1179 | // error code pointer |
| 1170 | 1180 | ZigType *gen_type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false); |
| 1171 | gen_param_types[gen_param_index] = gen_type->type_ref; | |
| 1172 | gen_param_index += 1; | |
| 1173 | // after the gen_param_index += 1 because 0 is the return type | |
| 1174 | param_di_types[gen_param_index] = gen_type->di_type; | |
| 1181 | gen_param_types.append(gen_type->type_ref); | |
| 1182 | param_di_types.append(gen_type->di_type); | |
| 1175 | 1183 | } |
| 1176 | 1184 | } |
| 1177 | 1185 | |
| ... | ... | @@ -1187,6 +1195,9 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 1187 | 1195 | if ((err = ensure_complete_type(g, type_entry))) |
| 1188 | 1196 | return g->builtin_types.entry_invalid; |
| 1189 | 1197 | |
| 1198 | if (is_c_abi) | |
| 1199 | continue; | |
| 1200 | ||
| 1190 | 1201 | if (type_has_bits(type_entry)) { |
| 1191 | 1202 | ZigType *gen_type; |
| 1192 | 1203 | if (handle_is_ptr(type_entry)) { |
| ... | ... | @@ -1195,23 +1206,31 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 1195 | 1206 | } else { |
| 1196 | 1207 | gen_type = type_entry; |
| 1197 | 1208 | } |
| 1198 | gen_param_types[gen_param_index] = gen_type->type_ref; | |
| 1199 | gen_param_info->gen_index = gen_param_index; | |
| 1209 | gen_param_info->gen_index = gen_param_types.length; | |
| 1200 | 1210 | gen_param_info->type = gen_type; |
| 1211 | gen_param_types.append(gen_type->type_ref); | |
| 1201 | 1212 | |
| 1202 | gen_param_index += 1; | |
| 1203 | ||
| 1204 | // after the gen_param_index += 1 because 0 is the return type | |
| 1205 | param_di_types[gen_param_index] = gen_type->di_type; | |
| 1213 | param_di_types.append(gen_type->di_type); | |
| 1206 | 1214 | } |
| 1207 | 1215 | } |
| 1208 | 1216 | |
| 1209 | fn_type->data.fn.gen_param_count = gen_param_index; | |
| 1217 | if (is_c_abi) { | |
| 1218 | FnWalk fn_walk = {}; | |
| 1219 | fn_walk.id = FnWalkIdTypes; | |
| 1220 | fn_walk.data.types.param_di_types = &param_di_types; | |
| 1221 | fn_walk.data.types.gen_param_types = &gen_param_types; | |
| 1222 | walk_function_params(g, fn_type, &fn_walk); | |
| 1223 | } | |
| 1224 | ||
| 1225 | fn_type->data.fn.gen_param_count = gen_param_types.length; | |
| 1210 | 1226 | |
| 1227 | for (size_t i = 0; i < gen_param_types.length; i += 1) { | |
| 1228 | assert(gen_param_types.items[i] != nullptr); | |
| 1229 | } | |
| 1211 | 1230 | fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref, |
| 1212 | gen_param_types, (unsigned int)gen_param_index, fn_type_id->is_var_args); | |
| 1231 | gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args); | |
| 1213 | 1232 | fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0); |
| 1214 | fn_type->di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types, (int)(gen_param_index + 1), 0); | |
| 1233 | fn_type->di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0); | |
| 1215 | 1234 | } |
| 1216 | 1235 | |
| 1217 | 1236 | g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type); |
| ... | ... | @@ -5863,12 +5882,23 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { |
| 5863 | 5882 | } |
| 5864 | 5883 | case ZigTypeIdErrorUnion: |
| 5865 | 5884 | { |
| 5866 | buf_appendf(buf, "(error union %s constant)", buf_ptr(&type_entry->name)); | |
| 5885 | buf_appendf(buf, "%s(", buf_ptr(&type_entry->name)); | |
| 5886 | if (const_val->data.x_err_union.err == nullptr) { | |
| 5887 | render_const_value(g, buf, const_val->data.x_err_union.payload); | |
| 5888 | } else { | |
| 5889 | buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->data.error_union.err_set_type->name), | |
| 5890 | buf_ptr(&const_val->data.x_err_union.err->name)); | |
| 5891 | } | |
| 5892 | buf_appendf(buf, ")"); | |
| 5867 | 5893 | return; |
| 5868 | 5894 | } |
| 5869 | 5895 | case ZigTypeIdUnion: |
| 5870 | 5896 | { |
| 5871 | buf_appendf(buf, "(union %s constant)", buf_ptr(&type_entry->name)); | |
| 5897 | uint64_t tag = bigint_as_unsigned(&const_val->data.x_union.tag); | |
| 5898 | TypeUnionField *field = &type_entry->data.unionation.fields[tag]; | |
| 5899 | buf_appendf(buf, "%s { .%s = ", buf_ptr(&type_entry->name), buf_ptr(field->name)); | |
| 5900 | render_const_value(g, buf, const_val->data.x_union.payload); | |
| 5901 | buf_append_str(buf, "}"); | |
| 5872 | 5902 | return; |
| 5873 | 5903 | } |
| 5874 | 5904 | case ZigTypeIdErrorSet: |
| ... | ... | @@ -6375,3 +6405,84 @@ Error file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents) { |
| 6375 | 6405 | return os_fetch_file_path(resolved_path, contents, false); |
| 6376 | 6406 | } |
| 6377 | 6407 | } |
| 6408 | ||
| 6409 | X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) { | |
| 6410 | size_t ty_size = type_size(g, ty); | |
| 6411 | if (get_codegen_ptr_type(ty) != nullptr) | |
| 6412 | return X64CABIClass_INTEGER; | |
| 6413 | switch (ty->id) { | |
| 6414 | case ZigTypeIdEnum: | |
| 6415 | case ZigTypeIdInt: | |
| 6416 | case ZigTypeIdBool: | |
| 6417 | return X64CABIClass_INTEGER; | |
| 6418 | case ZigTypeIdFloat: | |
| 6419 | return X64CABIClass_SSE; | |
| 6420 | case ZigTypeIdStruct: { | |
| 6421 | // "If the size of an object is larger than four eightbytes, or it contains unaligned | |
| 6422 | // fields, it has class MEMORY" | |
| 6423 | if (ty_size > 32) | |
| 6424 | return X64CABIClass_MEMORY; | |
| 6425 | if (ty->data.structure.layout != ContainerLayoutExtern) { | |
| 6426 | // TODO determine whether packed structs have any unaligned fields | |
| 6427 | return X64CABIClass_Unknown; | |
| 6428 | } | |
| 6429 | // "If the size of the aggregate exceeds two eightbytes and the first eight- | |
| 6430 | // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument | |
| 6431 | // is passed in memory." | |
| 6432 | if (ty_size > 16) { | |
| 6433 | // Zig doesn't support vectors and large fp registers yet, so this will always | |
| 6434 | // be memory. | |
| 6435 | return X64CABIClass_MEMORY; | |
| 6436 | } | |
| 6437 | X64CABIClass working_class = X64CABIClass_Unknown; | |
| 6438 | for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) { | |
| 6439 | X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields->type_entry); | |
| 6440 | if (field_class == X64CABIClass_Unknown) | |
| 6441 | return X64CABIClass_Unknown; | |
| 6442 | if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { | |
| 6443 | working_class = field_class; | |
| 6444 | } | |
| 6445 | } | |
| 6446 | return working_class; | |
| 6447 | } | |
| 6448 | case ZigTypeIdUnion: { | |
| 6449 | // "If the size of an object is larger than four eightbytes, or it contains unaligned | |
| 6450 | // fields, it has class MEMORY" | |
| 6451 | if (ty_size > 32) | |
| 6452 | return X64CABIClass_MEMORY; | |
| 6453 | if (ty->data.unionation.layout != ContainerLayoutExtern) | |
| 6454 | return X64CABIClass_MEMORY; | |
| 6455 | // "If the size of the aggregate exceeds two eightbytes and the first eight- | |
| 6456 | // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument | |
| 6457 | // is passed in memory." | |
| 6458 | if (ty_size > 16) { | |
| 6459 | // Zig doesn't support vectors and large fp registers yet, so this will always | |
| 6460 | // be memory. | |
| 6461 | return X64CABIClass_MEMORY; | |
| 6462 | } | |
| 6463 | X64CABIClass working_class = X64CABIClass_Unknown; | |
| 6464 | for (uint32_t i = 0; i < ty->data.unionation.src_field_count; i += 1) { | |
| 6465 | X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry); | |
| 6466 | if (field_class == X64CABIClass_Unknown) | |
| 6467 | return X64CABIClass_Unknown; | |
| 6468 | if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { | |
| 6469 | working_class = field_class; | |
| 6470 | } | |
| 6471 | } | |
| 6472 | return working_class; | |
| 6473 | } | |
| 6474 | default: | |
| 6475 | return X64CABIClass_Unknown; | |
| 6476 | } | |
| 6477 | } | |
| 6478 | ||
| 6479 | // NOTE this does not depend on x86_64 | |
| 6480 | bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { | |
| 6481 | return (ty->id == ZigTypeIdInt || | |
| 6482 | ty->id == ZigTypeIdFloat || | |
| 6483 | ty->id == ZigTypeIdBool || | |
| 6484 | ty->id == ZigTypeIdEnum || | |
| 6485 | ty->id == ZigTypeIdVoid || | |
| 6486 | ty->id == ZigTypeIdUnreachable || | |
| 6487 | get_codegen_ptr_type(ty) != nullptr); | |
| 6488 | } |
src/analyze.hpp+5-2| ... | ... | @@ -182,7 +182,6 @@ size_t type_id_index(ZigType *entry); |
| 182 | 182 | ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id); |
| 183 | 183 | Result<bool> type_is_copyable(CodeGen *g, ZigType *type_entry); |
| 184 | 184 | LinkLib *create_link_lib(Buf *name); |
| 185 | bool calling_convention_does_first_arg_return(CallingConvention cc); | |
| 186 | 185 | LinkLib *add_link_lib(CodeGen *codegen, Buf *lib); |
| 187 | 186 | |
| 188 | 187 | uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry); |
| ... | ... | @@ -210,7 +209,11 @@ ZigType *get_primitive_type(CodeGen *g, Buf *name); |
| 210 | 209 | bool calling_convention_allows_zig_types(CallingConvention cc); |
| 211 | 210 | const char *calling_convention_name(CallingConvention cc); |
| 212 | 211 | |
| 213 | ||
| 214 | 212 | Error ATTRIBUTE_MUST_USE file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents); |
| 215 | 213 | |
| 214 | void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk); | |
| 215 | X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty); | |
| 216 | bool type_is_c_abi_int(CodeGen *g, ZigType *ty); | |
| 217 | bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id); | |
| 218 | ||
| 216 | 219 | #endif |
src/codegen.cpp+453-120| ... | ... | @@ -350,8 +350,12 @@ static void addLLVMFnAttrInt(LLVMValueRef fn_val, const char *attr_name, uint64_ |
| 350 | 350 | return addLLVMAttrInt(fn_val, -1, attr_name, attr_val); |
| 351 | 351 | } |
| 352 | 352 | |
| 353 | static void addLLVMArgAttr(LLVMValueRef arg_val, unsigned param_index, const char *attr_name) { | |
| 354 | return addLLVMAttr(arg_val, param_index + 1, attr_name); | |
| 353 | static void addLLVMArgAttr(LLVMValueRef fn_val, unsigned param_index, const char *attr_name) { | |
| 354 | return addLLVMAttr(fn_val, param_index + 1, attr_name); | |
| 355 | } | |
| 356 | ||
| 357 | static void addLLVMArgAttrInt(LLVMValueRef fn_val, unsigned param_index, const char *attr_name, uint64_t attr_val) { | |
| 358 | return addLLVMAttrInt(fn_val, param_index + 1, attr_name, attr_val); | |
| 355 | 359 | } |
| 356 | 360 | |
| 357 | 361 | static bool is_symbol_available(CodeGen *g, Buf *name) { |
| ... | ... | @@ -462,7 +466,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { |
| 462 | 466 | } |
| 463 | 467 | |
| 464 | 468 | bool external_linkage = linkage != GlobalLinkageIdInternal; |
| 465 | if (fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionStdcall && external_linkage && | |
| 469 | CallingConvention cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc; | |
| 470 | if (cc == CallingConventionStdcall && external_linkage && | |
| 466 | 471 | g->zig_target.arch.arch == ZigLLVM_x86) |
| 467 | 472 | { |
| 468 | 473 | // prevent llvm name mangling |
| ... | ... | @@ -506,17 +511,17 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { |
| 506 | 511 | break; |
| 507 | 512 | } |
| 508 | 513 | |
| 509 | if (fn_type->data.fn.fn_type_id.cc == CallingConventionNaked) { | |
| 514 | if (cc == CallingConventionNaked) { | |
| 510 | 515 | addLLVMFnAttr(fn_table_entry->llvm_value, "naked"); |
| 511 | 516 | } else { |
| 512 | 517 | LLVMSetFunctionCallConv(fn_table_entry->llvm_value, get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc)); |
| 513 | 518 | } |
| 514 | if (fn_type->data.fn.fn_type_id.cc == CallingConventionAsync) { | |
| 519 | if (cc == CallingConventionAsync) { | |
| 515 | 520 | addLLVMFnAttr(fn_table_entry->llvm_value, "optnone"); |
| 516 | 521 | addLLVMFnAttr(fn_table_entry->llvm_value, "noinline"); |
| 517 | 522 | } |
| 518 | 523 | |
| 519 | bool want_cold = fn_table_entry->is_cold || fn_type->data.fn.fn_type_id.cc == CallingConventionCold; | |
| 524 | bool want_cold = fn_table_entry->is_cold || cc == CallingConventionCold; | |
| 520 | 525 | if (want_cold) { |
| 521 | 526 | ZigLLVMAddFunctionAttrCold(fn_table_entry->llvm_value); |
| 522 | 527 | } |
| ... | ... | @@ -568,41 +573,26 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { |
| 568 | 573 | // use the ABI alignment, which is fine. |
| 569 | 574 | } |
| 570 | 575 | |
| 576 | unsigned init_gen_i = 0; | |
| 571 | 577 | if (!type_has_bits(return_type)) { |
| 572 | 578 | // nothing to do |
| 573 | 579 | } else if (type_is_codegen_pointer(return_type)) { |
| 574 | 580 | addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); |
| 575 | } else if (handle_is_ptr(return_type) && | |
| 576 | calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc)) | |
| 577 | { | |
| 581 | } else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) { | |
| 578 | 582 | addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); |
| 579 | 583 | addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull"); |
| 584 | if (cc == CallingConventionC) { | |
| 585 | addLLVMArgAttr(fn_table_entry->llvm_value, 0, "noalias"); | |
| 586 | } | |
| 587 | init_gen_i = 1; | |
| 580 | 588 | } |
| 581 | 589 | |
| 582 | ||
| 583 | 590 | // set parameter attributes |
| 584 | for (size_t param_i = 0; param_i < fn_type->data.fn.fn_type_id.param_count; param_i += 1) { | |
| 585 | FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; | |
| 586 | size_t gen_index = gen_info->gen_index; | |
| 587 | bool is_byval = gen_info->is_byval; | |
| 588 | ||
| 589 | if (gen_index == SIZE_MAX) { | |
| 590 | continue; | |
| 591 | } | |
| 592 | ||
| 593 | FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[param_i]; | |
| 594 | ||
| 595 | ZigType *param_type = gen_info->type; | |
| 596 | if (param_info->is_noalias) { | |
| 597 | addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "noalias"); | |
| 598 | } | |
| 599 | if ((param_type->id == ZigTypeIdPointer && param_type->data.pointer.is_const) || is_byval) { | |
| 600 | addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "readonly"); | |
| 601 | } | |
| 602 | if (param_type->id == ZigTypeIdPointer) { | |
| 603 | addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "nonnull"); | |
| 604 | } | |
| 605 | } | |
| 591 | FnWalk fn_walk = {}; | |
| 592 | fn_walk.id = FnWalkIdAttrs; | |
| 593 | fn_walk.data.attrs.fn = fn_table_entry; | |
| 594 | fn_walk.data.attrs.gen_i = init_gen_i; | |
| 595 | walk_function_params(g, fn_type, &fn_walk); | |
| 606 | 596 | |
| 607 | 597 | uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); |
| 608 | 598 | if (err_ret_trace_arg_index != UINT32_MAX) { |
| ... | ... | @@ -1854,6 +1844,7 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_ty |
| 1854 | 1844 | } |
| 1855 | 1845 | |
| 1856 | 1846 | static void gen_var_debug_decl(CodeGen *g, ZigVar *var) { |
| 1847 | assert(var->di_loc_var != nullptr); | |
| 1857 | 1848 | AstNode *source_node = var->decl_node; |
| 1858 | 1849 | ZigLLVMDILocation *debug_loc = ZigLLVMGetDebugLoc((unsigned)source_node->line + 1, |
| 1859 | 1850 | (unsigned)source_node->column + 1, get_di_scope(g, var->parent_scope)); |
| ... | ... | @@ -1884,6 +1875,353 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { |
| 1884 | 1875 | return instruction->llvm_value; |
| 1885 | 1876 | } |
| 1886 | 1877 | |
| 1878 | ATTRIBUTE_NORETURN | |
| 1879 | static void report_errors_and_exit(CodeGen *g) { | |
| 1880 | assert(g->errors.length != 0); | |
| 1881 | for (size_t i = 0; i < g->errors.length; i += 1) { | |
| 1882 | ErrorMsg *err = g->errors.at(i); | |
| 1883 | print_err_msg(err, g->err_color); | |
| 1884 | } | |
| 1885 | exit(1); | |
| 1886 | } | |
| 1887 | ||
| 1888 | static void report_errors_and_maybe_exit(CodeGen *g) { | |
| 1889 | if (g->errors.length != 0) { | |
| 1890 | report_errors_and_exit(g); | |
| 1891 | } | |
| 1892 | } | |
| 1893 | ||
| 1894 | ATTRIBUTE_NORETURN | |
| 1895 | static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { | |
| 1896 | ErrorMsg *msg = add_node_error(g, source_node, | |
| 1897 | buf_sprintf("TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481")); | |
| 1898 | add_error_note(g, msg, source_node, | |
| 1899 | buf_sprintf("pointers, integers, floats, bools, and enums work on all targets")); | |
| 1900 | report_errors_and_exit(g); | |
| 1901 | } | |
| 1902 | ||
| 1903 | static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { | |
| 1904 | assert(alignment > 0); | |
| 1905 | LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); | |
| 1906 | LLVMSetAlignment(result, alignment); | |
| 1907 | return result; | |
| 1908 | } | |
| 1909 | ||
| 1910 | static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) { | |
| 1911 | // Initialized from the type for some walks, but because of C var args, | |
| 1912 | // initialized based on callsite instructions for that one. | |
| 1913 | FnTypeParamInfo *param_info = nullptr; | |
| 1914 | ZigType *ty; | |
| 1915 | ZigType *dest_ty = nullptr; | |
| 1916 | AstNode *source_node = nullptr; | |
| 1917 | LLVMValueRef val; | |
| 1918 | LLVMValueRef llvm_fn; | |
| 1919 | unsigned di_arg_index; | |
| 1920 | ZigVar *var; | |
| 1921 | switch (fn_walk->id) { | |
| 1922 | case FnWalkIdAttrs: | |
| 1923 | if (src_i >= fn_type->data.fn.fn_type_id.param_count) | |
| 1924 | return false; | |
| 1925 | param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; | |
| 1926 | ty = param_info->type; | |
| 1927 | source_node = fn_walk->data.attrs.fn->proto_node; | |
| 1928 | llvm_fn = fn_walk->data.attrs.fn->llvm_value; | |
| 1929 | break; | |
| 1930 | case FnWalkIdCall: { | |
| 1931 | if (src_i >= fn_walk->data.call.inst->arg_count) | |
| 1932 | return false; | |
| 1933 | IrInstruction *arg = fn_walk->data.call.inst->args[src_i]; | |
| 1934 | ty = arg->value.type; | |
| 1935 | source_node = arg->source_node; | |
| 1936 | val = ir_llvm_value(g, arg); | |
| 1937 | break; | |
| 1938 | } | |
| 1939 | case FnWalkIdTypes: | |
| 1940 | if (src_i >= fn_type->data.fn.fn_type_id.param_count) | |
| 1941 | return false; | |
| 1942 | param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; | |
| 1943 | ty = param_info->type; | |
| 1944 | break; | |
| 1945 | case FnWalkIdVars: | |
| 1946 | assert(src_i < fn_type->data.fn.fn_type_id.param_count); | |
| 1947 | param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; | |
| 1948 | ty = param_info->type; | |
| 1949 | var = fn_walk->data.vars.var; | |
| 1950 | source_node = var->decl_node; | |
| 1951 | llvm_fn = fn_walk->data.vars.llvm_fn; | |
| 1952 | break; | |
| 1953 | case FnWalkIdInits: | |
| 1954 | if (src_i >= fn_type->data.fn.fn_type_id.param_count) | |
| 1955 | return false; | |
| 1956 | param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; | |
| 1957 | ty = param_info->type; | |
| 1958 | var = fn_walk->data.inits.fn->variable_list.at(src_i); | |
| 1959 | source_node = fn_walk->data.inits.fn->proto_node; | |
| 1960 | llvm_fn = fn_walk->data.inits.llvm_fn; | |
| 1961 | break; | |
| 1962 | } | |
| 1963 | ||
| 1964 | if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || | |
| 1965 | ty->id == ZigTypeIdInt // TODO investigate if we need to change this | |
| 1966 | ) { | |
| 1967 | switch (fn_walk->id) { | |
| 1968 | case FnWalkIdAttrs: { | |
| 1969 | ZigType *ptr_type = get_codegen_ptr_type(ty); | |
| 1970 | if (ptr_type != nullptr) { | |
| 1971 | if (ty->id != ZigTypeIdOptional) { | |
| 1972 | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); | |
| 1973 | } | |
| 1974 | if (ptr_type->data.pointer.is_const) { | |
| 1975 | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "readonly"); | |
| 1976 | } | |
| 1977 | if (param_info->is_noalias) { | |
| 1978 | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "noalias"); | |
| 1979 | } | |
| 1980 | } | |
| 1981 | fn_walk->data.attrs.gen_i += 1; | |
| 1982 | break; | |
| 1983 | } | |
| 1984 | case FnWalkIdCall: | |
| 1985 | fn_walk->data.call.gen_param_values->append(val); | |
| 1986 | break; | |
| 1987 | case FnWalkIdTypes: | |
| 1988 | fn_walk->data.types.gen_param_types->append(ty->type_ref); | |
| 1989 | fn_walk->data.types.param_di_types->append(ty->di_type); | |
| 1990 | break; | |
| 1991 | case FnWalkIdVars: { | |
| 1992 | var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); | |
| 1993 | di_arg_index = fn_walk->data.vars.gen_i; | |
| 1994 | fn_walk->data.vars.gen_i += 1; | |
| 1995 | dest_ty = ty; | |
| 1996 | goto var_ok; | |
| 1997 | } | |
| 1998 | case FnWalkIdInits: | |
| 1999 | clear_debug_source_node(g); | |
| 2000 | gen_store_untyped(g, LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i), var->value_ref, var->align_bytes, false); | |
| 2001 | if (var->decl_node) { | |
| 2002 | gen_var_debug_decl(g, var); | |
| 2003 | } | |
| 2004 | fn_walk->data.inits.gen_i += 1; | |
| 2005 | break; | |
| 2006 | } | |
| 2007 | return true; | |
| 2008 | } | |
| 2009 | ||
| 2010 | // Arrays are just pointers | |
| 2011 | if (ty->id == ZigTypeIdArray) { | |
| 2012 | assert(handle_is_ptr(ty)); | |
| 2013 | switch (fn_walk->id) { | |
| 2014 | case FnWalkIdAttrs: | |
| 2015 | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); | |
| 2016 | addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); | |
| 2017 | fn_walk->data.attrs.gen_i += 1; | |
| 2018 | break; | |
| 2019 | case FnWalkIdCall: | |
| 2020 | fn_walk->data.call.gen_param_values->append(val); | |
| 2021 | break; | |
| 2022 | case FnWalkIdTypes: { | |
| 2023 | ZigType *gen_type = get_pointer_to_type(g, ty, true); | |
| 2024 | fn_walk->data.types.gen_param_types->append(gen_type->type_ref); | |
| 2025 | fn_walk->data.types.param_di_types->append(gen_type->di_type); | |
| 2026 | break; | |
| 2027 | } | |
| 2028 | case FnWalkIdVars: { | |
| 2029 | var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); | |
| 2030 | di_arg_index = fn_walk->data.vars.gen_i; | |
| 2031 | dest_ty = get_pointer_to_type(g, ty, false); | |
| 2032 | fn_walk->data.vars.gen_i += 1; | |
| 2033 | goto var_ok; | |
| 2034 | } | |
| 2035 | case FnWalkIdInits: | |
| 2036 | if (var->decl_node) { | |
| 2037 | gen_var_debug_decl(g, var); | |
| 2038 | } | |
| 2039 | fn_walk->data.inits.gen_i += 1; | |
| 2040 | break; | |
| 2041 | } | |
| 2042 | return true; | |
| 2043 | } | |
| 2044 | ||
| 2045 | if (g->zig_target.arch.arch == ZigLLVM_x86_64) { | |
| 2046 | X64CABIClass abi_class = type_c_abi_x86_64_class(g, ty); | |
| 2047 | size_t ty_size = type_size(g, ty); | |
| 2048 | if (abi_class == X64CABIClass_MEMORY) { | |
| 2049 | assert(handle_is_ptr(ty)); | |
| 2050 | switch (fn_walk->id) { | |
| 2051 | case FnWalkIdAttrs: | |
| 2052 | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval"); | |
| 2053 | addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); | |
| 2054 | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); | |
| 2055 | fn_walk->data.attrs.gen_i += 1; | |
| 2056 | break; | |
| 2057 | case FnWalkIdCall: | |
| 2058 | fn_walk->data.call.gen_param_values->append(val); | |
| 2059 | break; | |
| 2060 | case FnWalkIdTypes: { | |
| 2061 | ZigType *gen_type = get_pointer_to_type(g, ty, true); | |
| 2062 | fn_walk->data.types.gen_param_types->append(gen_type->type_ref); | |
| 2063 | fn_walk->data.types.param_di_types->append(gen_type->di_type); | |
| 2064 | break; | |
| 2065 | } | |
| 2066 | case FnWalkIdVars: { | |
| 2067 | di_arg_index = fn_walk->data.vars.gen_i; | |
| 2068 | var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); | |
| 2069 | dest_ty = get_pointer_to_type(g, ty, false); | |
| 2070 | fn_walk->data.vars.gen_i += 1; | |
| 2071 | goto var_ok; | |
| 2072 | } | |
| 2073 | case FnWalkIdInits: | |
| 2074 | if (var->decl_node) { | |
| 2075 | gen_var_debug_decl(g, var); | |
| 2076 | } | |
| 2077 | fn_walk->data.inits.gen_i += 1; | |
| 2078 | break; | |
| 2079 | } | |
| 2080 | return true; | |
| 2081 | } else if (abi_class == X64CABIClass_INTEGER) { | |
| 2082 | switch (fn_walk->id) { | |
| 2083 | case FnWalkIdAttrs: | |
| 2084 | fn_walk->data.attrs.gen_i += 1; | |
| 2085 | break; | |
| 2086 | case FnWalkIdCall: { | |
| 2087 | LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); | |
| 2088 | LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, ""); | |
| 2089 | LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, ""); | |
| 2090 | fn_walk->data.call.gen_param_values->append(loaded); | |
| 2091 | break; | |
| 2092 | } | |
| 2093 | case FnWalkIdTypes: { | |
| 2094 | ZigType *gen_type = get_int_type(g, false, ty_size * 8); | |
| 2095 | fn_walk->data.types.gen_param_types->append(gen_type->type_ref); | |
| 2096 | fn_walk->data.types.param_di_types->append(gen_type->di_type); | |
| 2097 | break; | |
| 2098 | } | |
| 2099 | case FnWalkIdVars: { | |
| 2100 | di_arg_index = fn_walk->data.vars.gen_i; | |
| 2101 | var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); | |
| 2102 | fn_walk->data.vars.gen_i += 1; | |
| 2103 | dest_ty = ty; | |
| 2104 | goto var_ok; | |
| 2105 | } | |
| 2106 | case FnWalkIdInits: { | |
| 2107 | clear_debug_source_node(g); | |
| 2108 | LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i); | |
| 2109 | LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); | |
| 2110 | LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); | |
| 2111 | gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); | |
| 2112 | if (var->decl_node) { | |
| 2113 | gen_var_debug_decl(g, var); | |
| 2114 | } | |
| 2115 | fn_walk->data.inits.gen_i += 1; | |
| 2116 | break; | |
| 2117 | } | |
| 2118 | } | |
| 2119 | return true; | |
| 2120 | } | |
| 2121 | } | |
| 2122 | if (source_node != nullptr) { | |
| 2123 | give_up_with_c_abi_error(g, source_node); | |
| 2124 | } | |
| 2125 | // otherwise allow codegen code to report a compile error | |
| 2126 | return false; | |
| 2127 | ||
| 2128 | var_ok: | |
| 2129 | if (dest_ty != nullptr && var->decl_node) { | |
| 2130 | // arg index + 1 because the 0 index is return value | |
| 2131 | var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), | |
| 2132 | buf_ptr(&var->name), fn_walk->data.vars.import->di_file, | |
| 2133 | (unsigned)(var->decl_node->line + 1), | |
| 2134 | dest_ty->di_type, !g->strip_debug_symbols, 0, di_arg_index + 1); | |
| 2135 | } | |
| 2136 | return true; | |
| 2137 | } | |
| 2138 | ||
| 2139 | void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { | |
| 2140 | CallingConvention cc = fn_type->data.fn.fn_type_id.cc; | |
| 2141 | if (cc == CallingConventionC) { | |
| 2142 | size_t src_i = 0; | |
| 2143 | for (;;) { | |
| 2144 | if (!iter_function_params_c_abi(g, fn_type, fn_walk, src_i)) | |
| 2145 | break; | |
| 2146 | src_i += 1; | |
| 2147 | } | |
| 2148 | return; | |
| 2149 | } | |
| 2150 | if (fn_walk->id == FnWalkIdCall) { | |
| 2151 | IrInstructionCall *instruction = fn_walk->data.call.inst; | |
| 2152 | bool is_var_args = fn_walk->data.call.is_var_args; | |
| 2153 | for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) { | |
| 2154 | IrInstruction *param_instruction = instruction->args[call_i]; | |
| 2155 | ZigType *param_type = param_instruction->value.type; | |
| 2156 | if (is_var_args || type_has_bits(param_type)) { | |
| 2157 | LLVMValueRef param_value = ir_llvm_value(g, param_instruction); | |
| 2158 | assert(param_value); | |
| 2159 | fn_walk->data.call.gen_param_values->append(param_value); | |
| 2160 | } | |
| 2161 | } | |
| 2162 | return; | |
| 2163 | } | |
| 2164 | size_t next_var_i = 0; | |
| 2165 | for (size_t param_i = 0; param_i < fn_type->data.fn.fn_type_id.param_count; param_i += 1) { | |
| 2166 | FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; | |
| 2167 | size_t gen_index = gen_info->gen_index; | |
| 2168 | ||
| 2169 | if (gen_index == SIZE_MAX) { | |
| 2170 | continue; | |
| 2171 | } | |
| 2172 | ||
| 2173 | switch (fn_walk->id) { | |
| 2174 | case FnWalkIdAttrs: { | |
| 2175 | LLVMValueRef llvm_fn = fn_walk->data.attrs.fn->llvm_value; | |
| 2176 | bool is_byval = gen_info->is_byval; | |
| 2177 | FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[param_i]; | |
| 2178 | ||
| 2179 | ZigType *param_type = gen_info->type; | |
| 2180 | if (param_info->is_noalias) { | |
| 2181 | addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "noalias"); | |
| 2182 | } | |
| 2183 | if ((param_type->id == ZigTypeIdPointer && param_type->data.pointer.is_const) || is_byval) { | |
| 2184 | addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "readonly"); | |
| 2185 | } | |
| 2186 | if (param_type->id == ZigTypeIdPointer) { | |
| 2187 | addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "nonnull"); | |
| 2188 | } | |
| 2189 | break; | |
| 2190 | } | |
| 2191 | case FnWalkIdInits: { | |
| 2192 | ZigFn *fn_table_entry = fn_walk->data.inits.fn; | |
| 2193 | LLVMValueRef llvm_fn = fn_table_entry->llvm_value; | |
| 2194 | ZigVar *variable = fn_table_entry->variable_list.at(next_var_i); | |
| 2195 | assert(variable->src_arg_index != SIZE_MAX); | |
| 2196 | next_var_i += 1; | |
| 2197 | ||
| 2198 | assert(variable); | |
| 2199 | assert(variable->value_ref); | |
| 2200 | ||
| 2201 | if (!handle_is_ptr(variable->value->type)) { | |
| 2202 | clear_debug_source_node(g); | |
| 2203 | gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index), variable->value_ref, | |
| 2204 | variable->align_bytes, false); | |
| 2205 | } | |
| 2206 | ||
| 2207 | if (variable->decl_node) { | |
| 2208 | gen_var_debug_decl(g, variable); | |
| 2209 | } | |
| 2210 | break; | |
| 2211 | } | |
| 2212 | case FnWalkIdCall: | |
| 2213 | // handled before for loop | |
| 2214 | zig_unreachable(); | |
| 2215 | case FnWalkIdTypes: | |
| 2216 | // Not called for non-c-abi | |
| 2217 | zig_unreachable(); | |
| 2218 | case FnWalkIdVars: | |
| 2219 | // iter_function_params_c_abi is called directly for this one | |
| 2220 | zig_unreachable(); | |
| 2221 | } | |
| 2222 | } | |
| 2223 | } | |
| 2224 | ||
| 1887 | 2225 | static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *executable, |
| 1888 | 2226 | IrInstructionSaveErrRetAddr *save_err_ret_addr_instruction) |
| 1889 | 2227 | { |
| ... | ... | @@ -1902,15 +2240,13 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns |
| 1902 | 2240 | LLVMValueRef value = ir_llvm_value(g, return_instruction->value); |
| 1903 | 2241 | ZigType *return_type = return_instruction->value->value.type; |
| 1904 | 2242 | |
| 1905 | if (handle_is_ptr(return_type)) { | |
| 1906 | if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) { | |
| 1907 | assert(g->cur_ret_ptr); | |
| 1908 | gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); | |
| 1909 | LLVMBuildRetVoid(g->builder); | |
| 1910 | } else { | |
| 1911 | LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, ""); | |
| 1912 | LLVMBuildRet(g->builder, by_val_value); | |
| 1913 | } | |
| 2243 | if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { | |
| 2244 | assert(g->cur_ret_ptr); | |
| 2245 | gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); | |
| 2246 | LLVMBuildRetVoid(g->builder); | |
| 2247 | } else if (handle_is_ptr(return_type)) { | |
| 2248 | LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, ""); | |
| 2249 | LLVMBuildRet(g->builder, by_val_value); | |
| 1914 | 2250 | } else { |
| 1915 | 2251 | LLVMBuildRet(g->builder, value); |
| 1916 | 2252 | } |
| ... | ... | @@ -2878,7 +3214,8 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, |
| 2878 | 3214 | assert(var->value->type == init_value->value.type); |
| 2879 | 3215 | ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, |
| 2880 | 3216 | PtrLenSingle, var->align_bytes, 0, 0); |
| 2881 | gen_assign_raw(g, var->value_ref, var_ptr_type, ir_llvm_value(g, init_value)); | |
| 3217 | LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); | |
| 3218 | gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val); | |
| 2882 | 3219 | } else { |
| 2883 | 3220 | bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base); |
| 2884 | 3221 | if (want_safe) { |
| ... | ... | @@ -3124,40 +3461,30 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3124 | 3461 | ZigType *src_return_type = fn_type_id->return_type; |
| 3125 | 3462 | bool ret_has_bits = type_has_bits(src_return_type); |
| 3126 | 3463 | |
| 3127 | bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type) && | |
| 3128 | calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc); | |
| 3464 | CallingConvention cc = fn_type->data.fn.fn_type_id.cc; | |
| 3465 | ||
| 3466 | bool first_arg_ret = ret_has_bits && want_first_arg_sret(g, fn_type_id); | |
| 3129 | 3467 | bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id); |
| 3130 | // +2 for the async args | |
| 3131 | size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0) + 2; | |
| 3132 | 3468 | bool is_var_args = fn_type_id->is_var_args; |
| 3133 | LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count); | |
| 3134 | size_t gen_param_index = 0; | |
| 3469 | ZigList<LLVMValueRef> gen_param_values = {}; | |
| 3135 | 3470 | if (first_arg_ret) { |
| 3136 | gen_param_values[gen_param_index] = instruction->tmp_ptr; | |
| 3137 | gen_param_index += 1; | |
| 3471 | gen_param_values.append(instruction->tmp_ptr); | |
| 3138 | 3472 | } |
| 3139 | 3473 | if (prefix_arg_err_ret_stack) { |
| 3140 | gen_param_values[gen_param_index] = get_cur_err_ret_trace_val(g, instruction->base.scope); | |
| 3141 | gen_param_index += 1; | |
| 3474 | gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope)); | |
| 3142 | 3475 | } |
| 3143 | 3476 | if (instruction->is_async) { |
| 3144 | gen_param_values[gen_param_index] = ir_llvm_value(g, instruction->async_allocator); | |
| 3145 | gen_param_index += 1; | |
| 3477 | gen_param_values.append(ir_llvm_value(g, instruction->async_allocator)); | |
| 3146 | 3478 | |
| 3147 | 3479 | LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, ""); |
| 3148 | gen_param_values[gen_param_index] = err_val_ptr; | |
| 3149 | gen_param_index += 1; | |
| 3150 | } | |
| 3151 | for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) { | |
| 3152 | IrInstruction *param_instruction = instruction->args[call_i]; | |
| 3153 | ZigType *param_type = param_instruction->value.type; | |
| 3154 | if (is_var_args || type_has_bits(param_type)) { | |
| 3155 | LLVMValueRef param_value = ir_llvm_value(g, param_instruction); | |
| 3156 | assert(param_value); | |
| 3157 | gen_param_values[gen_param_index] = param_value; | |
| 3158 | gen_param_index += 1; | |
| 3159 | } | |
| 3480 | gen_param_values.append(err_val_ptr); | |
| 3160 | 3481 | } |
| 3482 | FnWalk fn_walk = {}; | |
| 3483 | fn_walk.id = FnWalkIdCall; | |
| 3484 | fn_walk.data.call.inst = instruction; | |
| 3485 | fn_walk.data.call.is_var_args = is_var_args; | |
| 3486 | fn_walk.data.call.gen_param_values = &gen_param_values; | |
| 3487 | walk_function_params(g, fn_type, &fn_walk); | |
| 3161 | 3488 | |
| 3162 | 3489 | ZigLLVM_FnInline fn_inline; |
| 3163 | 3490 | switch (instruction->fn_inline) { |
| ... | ... | @@ -3172,12 +3499,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3172 | 3499 | break; |
| 3173 | 3500 | } |
| 3174 | 3501 | |
| 3175 | LLVMCallConv llvm_cc = get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc); | |
| 3502 | LLVMCallConv llvm_cc = get_llvm_cc(g, cc); | |
| 3176 | 3503 | LLVMValueRef result; |
| 3177 | 3504 | |
| 3178 | 3505 | if (instruction->new_stack == nullptr) { |
| 3179 | 3506 | result = ZigLLVMBuildCall(g->builder, fn_val, |
| 3180 | gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, ""); | |
| 3507 | gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, ""); | |
| 3181 | 3508 | } else { |
| 3182 | 3509 | LLVMValueRef stacksave_fn_val = get_stacksave_fn_val(g); |
| 3183 | 3510 | LLVMValueRef stackrestore_fn_val = get_stackrestore_fn_val(g); |
| ... | ... | @@ -3186,7 +3513,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3186 | 3513 | LLVMValueRef old_stack_ref = LLVMBuildCall(g->builder, stacksave_fn_val, nullptr, 0, ""); |
| 3187 | 3514 | gen_set_stack_pointer(g, new_stack_addr); |
| 3188 | 3515 | result = ZigLLVMBuildCall(g->builder, fn_val, |
| 3189 | gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, ""); | |
| 3516 | gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, ""); | |
| 3190 | 3517 | LLVMBuildCall(g->builder, stackrestore_fn_val, &old_stack_ref, 1, ""); |
| 3191 | 3518 | } |
| 3192 | 3519 | |
| ... | ... | @@ -5533,12 +5860,24 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c |
| 5533 | 5860 | LLVMValueRef tag_value = bigint_to_llvm_const(type_entry->data.unionation.tag_type->type_ref, |
| 5534 | 5861 | &const_val->data.x_union.tag); |
| 5535 | 5862 | |
| 5536 | LLVMValueRef fields[2]; | |
| 5863 | LLVMValueRef fields[3]; | |
| 5537 | 5864 | fields[type_entry->data.unionation.gen_union_index] = union_value_ref; |
| 5538 | 5865 | fields[type_entry->data.unionation.gen_tag_index] = tag_value; |
| 5539 | 5866 | |
| 5540 | 5867 | if (make_unnamed_struct) { |
| 5541 | return LLVMConstStruct(fields, 2, false); | |
| 5868 | LLVMValueRef result = LLVMConstStruct(fields, 2, false); | |
| 5869 | uint64_t last_field_offset = LLVMOffsetOfElement(g->target_data_ref, LLVMTypeOf(result), 1); | |
| 5870 | uint64_t end_offset = last_field_offset + | |
| 5871 | LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(fields[1])); | |
| 5872 | uint64_t expected_sz = LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); | |
| 5873 | unsigned pad_sz = expected_sz - end_offset; | |
| 5874 | if (pad_sz != 0) { | |
| 5875 | fields[2] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz)); | |
| 5876 | result = LLVMConstStruct(fields, 3, false); | |
| 5877 | } | |
| 5878 | uint64_t actual_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(result)); | |
| 5879 | assert(actual_sz == expected_sz); | |
| 5880 | return result; | |
| 5542 | 5881 | } else { |
| 5543 | 5882 | return LLVMConstNamedStruct(type_entry->type_ref, fields, 2); |
| 5544 | 5883 | } |
| ... | ... | @@ -5578,13 +5917,29 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c |
| 5578 | 5917 | err_payload_value = gen_const_val(g, payload_val, ""); |
| 5579 | 5918 | make_unnamed_struct = is_llvm_value_unnamed_type(payload_val->type, err_payload_value); |
| 5580 | 5919 | } |
| 5581 | LLVMValueRef fields[] = { | |
| 5582 | err_tag_value, | |
| 5583 | err_payload_value, | |
| 5584 | }; | |
| 5585 | 5920 | if (make_unnamed_struct) { |
| 5586 | return LLVMConstStruct(fields, 2, false); | |
| 5921 | uint64_t payload_off = LLVMOffsetOfElement(g->target_data_ref, type_entry->type_ref, 1); | |
| 5922 | uint64_t err_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(err_tag_value)); | |
| 5923 | unsigned pad_sz = payload_off - err_sz; | |
| 5924 | if (pad_sz == 0) { | |
| 5925 | LLVMValueRef fields[] = { | |
| 5926 | err_tag_value, | |
| 5927 | err_payload_value, | |
| 5928 | }; | |
| 5929 | return LLVMConstStruct(fields, 2, false); | |
| 5930 | } else { | |
| 5931 | LLVMValueRef fields[] = { | |
| 5932 | err_tag_value, | |
| 5933 | LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz)), | |
| 5934 | err_payload_value, | |
| 5935 | }; | |
| 5936 | return LLVMConstStruct(fields, 3, false); | |
| 5937 | } | |
| 5587 | 5938 | } else { |
| 5939 | LLVMValueRef fields[] = { | |
| 5940 | err_tag_value, | |
| 5941 | err_payload_value, | |
| 5942 | }; | |
| 5588 | 5943 | return LLVMConstNamedStruct(type_entry->type_ref, fields, 2); |
| 5589 | 5944 | } |
| 5590 | 5945 | } |
| ... | ... | @@ -5717,23 +6072,6 @@ static void gen_global_var(CodeGen *g, ZigVar *var, LLVMValueRef init_val, |
| 5717 | 6072 | // TODO ^^ make an actual global variable |
| 5718 | 6073 | } |
| 5719 | 6074 | |
| 5720 | static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { | |
| 5721 | assert(alignment > 0); | |
| 5722 | LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); | |
| 5723 | LLVMSetAlignment(result, alignment); | |
| 5724 | return result; | |
| 5725 | } | |
| 5726 | ||
| 5727 | static void report_errors_and_maybe_exit(CodeGen *g) { | |
| 5728 | if (g->errors.length != 0) { | |
| 5729 | for (size_t i = 0; i < g->errors.length; i += 1) { | |
| 5730 | ErrorMsg *err = g->errors.at(i); | |
| 5731 | print_err_msg(err, g->err_color); | |
| 5732 | } | |
| 5733 | exit(1); | |
| 5734 | } | |
| 5735 | } | |
| 5736 | ||
| 5737 | 6075 | static void validate_inline_fns(CodeGen *g) { |
| 5738 | 6076 | for (size_t i = 0; i < g->inline_fns.length; i += 1) { |
| 5739 | 6077 | ZigFn *fn_entry = g->inline_fns.at(i); |
| ... | ... | @@ -5852,11 +6190,14 @@ static void do_code_gen(CodeGen *g) { |
| 5852 | 6190 | // Generate function definitions. |
| 5853 | 6191 | for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) { |
| 5854 | 6192 | ZigFn *fn_table_entry = g->fn_defs.at(fn_i); |
| 6193 | FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; | |
| 6194 | CallingConvention cc = fn_type_id->cc; | |
| 6195 | bool is_c_abi = cc == CallingConventionC; | |
| 5855 | 6196 | |
| 5856 | 6197 | LLVMValueRef fn = fn_llvm_value(g, fn_table_entry); |
| 5857 | 6198 | g->cur_fn = fn_table_entry; |
| 5858 | 6199 | g->cur_fn_val = fn; |
| 5859 | ZigType *return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; | |
| 6200 | ZigType *return_type = fn_type_id->return_type; | |
| 5860 | 6201 | if (handle_is_ptr(return_type)) { |
| 5861 | 6202 | g->cur_ret_ptr = LLVMGetParam(fn, 0); |
| 5862 | 6203 | } else { |
| ... | ... | @@ -5875,7 +6216,7 @@ static void do_code_gen(CodeGen *g) { |
| 5875 | 6216 | } |
| 5876 | 6217 | |
| 5877 | 6218 | // error return tracing setup |
| 5878 | bool is_async = fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; | |
| 6219 | bool is_async = cc == CallingConventionAsync; | |
| 5879 | 6220 | bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn && !is_async && !have_err_ret_trace_arg; |
| 5880 | 6221 | LLVMValueRef err_ret_array_val = nullptr; |
| 5881 | 6222 | if (have_err_ret_trace_stack) { |
| ... | ... | @@ -5934,7 +6275,15 @@ static void do_code_gen(CodeGen *g) { |
| 5934 | 6275 | |
| 5935 | 6276 | ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); |
| 5936 | 6277 | |
| 6278 | unsigned gen_i_init = want_first_arg_sret(g, fn_type_id) ? 1 : 0; | |
| 6279 | ||
| 5937 | 6280 | // create debug variable declarations for variables and allocate all local variables |
| 6281 | FnWalk fn_walk_var = {}; | |
| 6282 | fn_walk_var.id = FnWalkIdVars; | |
| 6283 | fn_walk_var.data.vars.import = import; | |
| 6284 | fn_walk_var.data.vars.fn = fn_table_entry; | |
| 6285 | fn_walk_var.data.vars.llvm_fn = fn; | |
| 6286 | fn_walk_var.data.vars.gen_i = gen_i_init; | |
| 5938 | 6287 | for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { |
| 5939 | 6288 | ZigVar *var = fn_table_entry->variable_list.at(var_i); |
| 5940 | 6289 | |
| ... | ... | @@ -5953,6 +6302,9 @@ static void do_code_gen(CodeGen *g) { |
| 5953 | 6302 | buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1), |
| 5954 | 6303 | var->value->type->di_type, !g->strip_debug_symbols, 0); |
| 5955 | 6304 | |
| 6305 | } else if (is_c_abi) { | |
| 6306 | fn_walk_var.data.vars.var = var; | |
| 6307 | iter_function_params_c_abi(g, fn_table_entry->type_entry, &fn_walk_var, var->src_arg_index); | |
| 5956 | 6308 | } else { |
| 5957 | 6309 | assert(var->gen_arg_index != SIZE_MAX); |
| 5958 | 6310 | ZigType *gen_type; |
| ... | ... | @@ -6004,33 +6356,14 @@ static void do_code_gen(CodeGen *g) { |
| 6004 | 6356 | gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false)); |
| 6005 | 6357 | } |
| 6006 | 6358 | |
| 6007 | FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; | |
| 6008 | ||
| 6009 | 6359 | // create debug variable declarations for parameters |
| 6010 | 6360 | // rely on the first variables in the variable_list being parameters. |
| 6011 | size_t next_var_i = 0; | |
| 6012 | for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { | |
| 6013 | FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i]; | |
| 6014 | if (info->gen_index == SIZE_MAX) | |
| 6015 | continue; | |
| 6016 | ||
| 6017 | ZigVar *variable = fn_table_entry->variable_list.at(next_var_i); | |
| 6018 | assert(variable->src_arg_index != SIZE_MAX); | |
| 6019 | next_var_i += 1; | |
| 6020 | ||
| 6021 | assert(variable); | |
| 6022 | assert(variable->value_ref); | |
| 6023 | ||
| 6024 | if (!handle_is_ptr(variable->value->type)) { | |
| 6025 | clear_debug_source_node(g); | |
| 6026 | gen_store_untyped(g, LLVMGetParam(fn, (unsigned)variable->gen_arg_index), variable->value_ref, | |
| 6027 | variable->align_bytes, false); | |
| 6028 | } | |
| 6029 | ||
| 6030 | if (variable->decl_node) { | |
| 6031 | gen_var_debug_decl(g, variable); | |
| 6032 | } | |
| 6033 | } | |
| 6361 | FnWalk fn_walk_init = {}; | |
| 6362 | fn_walk_init.id = FnWalkIdInits; | |
| 6363 | fn_walk_init.data.inits.fn = fn_table_entry; | |
| 6364 | fn_walk_init.data.inits.llvm_fn = fn; | |
| 6365 | fn_walk_init.data.inits.gen_i = gen_i_init; | |
| 6366 | walk_function_params(g, fn_table_entry->type_entry, &fn_walk_init); | |
| 6034 | 6367 | |
| 6035 | 6368 | ir_render(g, fn_table_entry); |
| 6036 | 6369 |
src/codegen.hpp-1| ... | ... | @@ -61,5 +61,4 @@ void codegen_translate_c(CodeGen *g, Buf *path); |
| 61 | 61 | |
| 62 | 62 | Buf *codegen_generate_builtin_source(CodeGen *g); |
| 63 | 63 | |
| 64 | ||
| 65 | 64 | #endif |
src/ir.cpp+8-3| ... | ... | @@ -13107,8 +13107,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, |
| 13107 | 13107 | assert(ira->codegen->errors.length != 0); |
| 13108 | 13108 | return ira->codegen->invalid_instruction; |
| 13109 | 13109 | } |
| 13110 | assert(var->value->type); | |
| 13111 | if (type_is_invalid(var->value->type)) | |
| 13110 | if (var->value->type == nullptr || type_is_invalid(var->value->type)) | |
| 13112 | 13111 | return ira->codegen->invalid_instruction; |
| 13113 | 13112 | |
| 13114 | 13113 | bool comptime_var_mem = ir_get_var_is_comptime(var); |
| ... | ... | @@ -19643,7 +19642,7 @@ static ZigType *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnP |
| 19643 | 19642 | return ira->codegen->builtin_types.entry_invalid; |
| 19644 | 19643 | if (type_requires_comptime(param_type)) { |
| 19645 | 19644 | if (!calling_convention_allows_zig_types(fn_type_id.cc)) { |
| 19646 | ir_add_error(ira, &instruction->base, | |
| 19645 | ir_add_error(ira, param_type_value, | |
| 19647 | 19646 | buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'", |
| 19648 | 19647 | buf_ptr(&param_type->name), calling_convention_name(fn_type_id.cc))); |
| 19649 | 19648 | return ira->codegen->builtin_types.entry_invalid; |
| ... | ... | @@ -19654,6 +19653,12 @@ static ZigType *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnP |
| 19654 | 19653 | out_val->data.x_type = get_generic_fn_type(ira->codegen, &fn_type_id); |
| 19655 | 19654 | return ira->codegen->builtin_types.entry_type; |
| 19656 | 19655 | } |
| 19656 | if (!type_has_bits(param_type) && !calling_convention_allows_zig_types(fn_type_id.cc)) { | |
| 19657 | ir_add_error(ira, param_type_value, | |
| 19658 | buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'", | |
| 19659 | buf_ptr(&param_type->name), calling_convention_name(fn_type_id.cc))); | |
| 19660 | return ira->codegen->builtin_types.entry_invalid; | |
| 19661 | } | |
| 19657 | 19662 | param_info->type = param_type; |
| 19658 | 19663 | } |
| 19659 | 19664 |
src/os.cpp+1-1| ... | ... | @@ -445,7 +445,7 @@ static Buf os_path_resolve_windows(Buf **paths_ptr, size_t paths_len) { |
| 445 | 445 | } |
| 446 | 446 | |
| 447 | 447 | // determine which disk designator we will result with, if any |
| 448 | char result_drive_buf[2] = {'_', ':'}; | |
| 448 | char result_drive_buf[3] = {'_', ':', '\0'}; // 0 needed for strlen later | |
| 449 | 449 | Slice<uint8_t> result_disk_designator = str(""); |
| 450 | 450 | WindowsPathKind have_drive_kind = WindowsPathKindNone; |
| 451 | 451 | bool have_abs_path = false; |
std/array_list.zig+27-2| ... | ... | @@ -62,6 +62,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { |
| 62 | 62 | return self.len; |
| 63 | 63 | } |
| 64 | 64 | |
| 65 | pub fn capacity(self: Self) usize { | |
| 66 | return self.items.len; | |
| 67 | } | |
| 68 | ||
| 65 | 69 | /// ArrayList takes ownership of the passed in slice. The slice must have been |
| 66 | 70 | /// allocated with `allocator`. |
| 67 | 71 | /// Deinitialize with `deinit` or use `toOwnedSlice`. |
| ... | ... | @@ -102,6 +106,11 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { |
| 102 | 106 | new_item_ptr.* = item; |
| 103 | 107 | } |
| 104 | 108 | |
| 109 | pub fn appendAssumeCapacity(self: *Self, item: T) void { | |
| 110 | const new_item_ptr = self.addOneAssumeCapacity(); | |
| 111 | new_item_ptr.* = item; | |
| 112 | } | |
| 113 | ||
| 105 | 114 | /// Removes the element at the specified index and returns it. |
| 106 | 115 | /// The empty slot is filled from the end of the list. |
| 107 | 116 | pub fn swapRemove(self: *Self, i: usize) T { |
| ... | ... | @@ -138,7 +147,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { |
| 138 | 147 | } |
| 139 | 148 | |
| 140 | 149 | pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { |
| 141 | var better_capacity = self.items.len; | |
| 150 | var better_capacity = self.capacity(); | |
| 142 | 151 | if (better_capacity >= new_capacity) return; |
| 143 | 152 | while (true) { |
| 144 | 153 | better_capacity += better_capacity / 2 + 8; |
| ... | ... | @@ -150,8 +159,13 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { |
| 150 | 159 | pub fn addOne(self: *Self) !*T { |
| 151 | 160 | const new_length = self.len + 1; |
| 152 | 161 | try self.ensureCapacity(new_length); |
| 162 | return self.addOneAssumeCapacity(); | |
| 163 | } | |
| 164 | ||
| 165 | pub fn addOneAssumeCapacity(self: *Self) *T { | |
| 166 | assert(self.count() < self.capacity()); | |
| 153 | 167 | const result = &self.items[self.len]; |
| 154 | self.len = new_length; | |
| 168 | self.len += 1; | |
| 155 | 169 | return result; |
| 156 | 170 | } |
| 157 | 171 | |
| ... | ... | @@ -191,6 +205,17 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { |
| 191 | 205 | }; |
| 192 | 206 | } |
| 193 | 207 | |
| 208 | test "std.ArrayList.init" { | |
| 209 | var bytes: [1024]u8 = undefined; | |
| 210 | const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; | |
| 211 | ||
| 212 | var list = ArrayList(i32).init(allocator); | |
| 213 | defer list.deinit(); | |
| 214 | ||
| 215 | assert(list.count() == 0); | |
| 216 | assert(list.capacity() == 0); | |
| 217 | } | |
| 218 | ||
| 194 | 219 | test "std.ArrayList.basic" { |
| 195 | 220 | var bytes: [1024]u8 = undefined; |
| 196 | 221 | const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; |
std/build.zig+27| ... | ... | @@ -48,6 +48,12 @@ pub const Builder = struct { |
| 48 | 48 | cache_root: []const u8, |
| 49 | 49 | release_mode: ?builtin.Mode, |
| 50 | 50 | |
| 51 | pub const CStd = enum { | |
| 52 | C89, | |
| 53 | C99, | |
| 54 | C11, | |
| 55 | }; | |
| 56 | ||
| 51 | 57 | const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8); |
| 52 | 58 | const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8); |
| 53 | 59 | |
| ... | ... | @@ -817,6 +823,7 @@ pub const LibExeObjStep = struct { |
| 817 | 823 | frameworks: BufSet, |
| 818 | 824 | verbose_link: bool, |
| 819 | 825 | no_rosegment: bool, |
| 826 | c_std: Builder.CStd, | |
| 820 | 827 | |
| 821 | 828 | // zig only stuff |
| 822 | 829 | root_src: ?[]const u8, |
| ... | ... | @@ -918,6 +925,7 @@ pub const LibExeObjStep = struct { |
| 918 | 925 | .object_src = undefined, |
| 919 | 926 | .disable_libc = true, |
| 920 | 927 | .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable, |
| 928 | .c_std = Builder.CStd.C99, | |
| 921 | 929 | }; |
| 922 | 930 | self.computeOutFileNames(); |
| 923 | 931 | return self; |
| ... | ... | @@ -952,6 +960,7 @@ pub const LibExeObjStep = struct { |
| 952 | 960 | .disable_libc = false, |
| 953 | 961 | .is_zig = false, |
| 954 | 962 | .linker_script = null, |
| 963 | .c_std = Builder.CStd.C99, | |
| 955 | 964 | |
| 956 | 965 | .root_src = undefined, |
| 957 | 966 | .verbose_link = false, |
| ... | ... | @@ -1392,6 +1401,13 @@ pub const LibExeObjStep = struct { |
| 1392 | 1401 | |
| 1393 | 1402 | const is_darwin = self.target.isDarwin(); |
| 1394 | 1403 | |
| 1404 | const c_std_arg = switch (self.c_std) { | |
| 1405 | Builder.CStd.C89 => "-std=c89", | |
| 1406 | Builder.CStd.C99 => "-std=c99", | |
| 1407 | Builder.CStd.C11 => "-std=c11", | |
| 1408 | }; | |
| 1409 | try cc_args.append(c_std_arg); | |
| 1410 | ||
| 1395 | 1411 | switch (self.kind) { |
| 1396 | 1412 | Kind.Obj => { |
| 1397 | 1413 | cc_args.append("-c") catch unreachable; |
| ... | ... | @@ -1678,6 +1694,17 @@ pub const TestStep = struct { |
| 1678 | 1694 | self.filter = text; |
| 1679 | 1695 | } |
| 1680 | 1696 | |
| 1697 | pub fn addObject(self: *TestStep, obj: *LibExeObjStep) void { | |
| 1698 | assert(obj.kind == LibExeObjStep.Kind.Obj); | |
| 1699 | ||
| 1700 | self.step.dependOn(&obj.step); | |
| 1701 | ||
| 1702 | self.object_files.append(obj.getOutputPath()) catch unreachable; | |
| 1703 | ||
| 1704 | // TODO should be some kind of isolated directory that only has this header in it | |
| 1705 | self.include_dirs.append(self.builder.cache_root) catch unreachable; | |
| 1706 | } | |
| 1707 | ||
| 1681 | 1708 | pub fn addObjectFile(self: *TestStep, path: []const u8) void { |
| 1682 | 1709 | self.object_files.append(path) catch unreachable; |
| 1683 | 1710 | } |
std/crypto/x25519.zig+12-1| ... | ... | @@ -4,6 +4,7 @@ |
| 4 | 4 | |
| 5 | 5 | const std = @import("../index.zig"); |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | const fmt = std.fmt; | |
| 7 | 8 | |
| 8 | 9 | const Endian = builtin.Endian; |
| 9 | 10 | const readInt = std.mem.readInt; |
| ... | ... | @@ -114,7 +115,7 @@ pub const X25519 = struct { |
| 114 | 115 | return !zerocmp(u8, out); |
| 115 | 116 | } |
| 116 | 117 | |
| 117 | pub fn createPublicKey(public_key: []const u8, private_key: []const u8) bool { | |
| 118 | pub fn createPublicKey(public_key: [] u8, private_key: []const u8) bool { | |
| 118 | 119 | var base_point = []u8{9} ++ []u8{0} ** 31; |
| 119 | 120 | return create(public_key, private_key, base_point); |
| 120 | 121 | } |
| ... | ... | @@ -573,6 +574,16 @@ const Fe = struct { |
| 573 | 574 | } |
| 574 | 575 | }; |
| 575 | 576 | |
| 577 | test "x25519 public key calculation from secret key" { | |
| 578 | var sk: [32]u8 = undefined; | |
| 579 | var pk_expected: [32]u8 = undefined; | |
| 580 | var pk_calculated: [32]u8 = undefined; | |
| 581 | try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); | |
| 582 | try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50"); | |
| 583 | std.debug.assert(X25519.createPublicKey(pk_calculated[0..], sk)); | |
| 584 | std.debug.assert(std.mem.eql(u8, pk_calculated, pk_expected)); | |
| 585 | } | |
| 586 | ||
| 576 | 587 | test "x25519 rfc7748 vector1" { |
| 577 | 588 | const secret_key = "\xa5\x46\xe3\x6b\xf0\x52\x7c\x9d\x3b\x16\x15\x4b\x82\x46\x5e\xdd\x62\x14\x4c\x0a\xc1\xfc\x5a\x18\x50\x6a\x22\x44\xba\x44\x9a\xc4"; |
| 578 | 589 | const public_key = "\xe6\xdb\x68\x67\x58\x30\x30\xdb\x35\x94\xc1\xa4\x24\xb1\x5f\x7c\x72\x66\x24\xec\x26\xb3\x35\x3b\x10\xa9\x03\xa6\xd0\xab\x1c\x4c"; |
std/os/index.zig+19-17| ... | ... | @@ -343,23 +343,25 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { |
| 343 | 343 | const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); |
| 344 | 344 | const rc = posix.write(fd, bytes.ptr + index, amt_to_write); |
| 345 | 345 | const write_err = posix.getErrno(rc); |
| 346 | if (write_err > 0) { | |
| 347 | return switch (write_err) { | |
| 348 | posix.EINTR => continue, | |
| 349 | posix.EINVAL, posix.EFAULT => unreachable, | |
| 350 | posix.EAGAIN => PosixWriteError.WouldBlock, | |
| 351 | posix.EBADF => PosixWriteError.FileClosed, | |
| 352 | posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired, | |
| 353 | posix.EDQUOT => PosixWriteError.DiskQuota, | |
| 354 | posix.EFBIG => PosixWriteError.FileTooBig, | |
| 355 | posix.EIO => PosixWriteError.InputOutput, | |
| 356 | posix.ENOSPC => PosixWriteError.NoSpaceLeft, | |
| 357 | posix.EPERM => PosixWriteError.AccessDenied, | |
| 358 | posix.EPIPE => PosixWriteError.BrokenPipe, | |
| 359 | else => unexpectedErrorPosix(write_err), | |
| 360 | }; | |
| 346 | switch (write_err) { | |
| 347 | 0 => { | |
| 348 | index += rc; | |
| 349 | continue; | |
| 350 | }, | |
| 351 | posix.EINTR => continue, | |
| 352 | posix.EINVAL => unreachable, | |
| 353 | posix.EFAULT => unreachable, | |
| 354 | posix.EAGAIN => return PosixWriteError.WouldBlock, | |
| 355 | posix.EBADF => return PosixWriteError.FileClosed, | |
| 356 | posix.EDESTADDRREQ => return PosixWriteError.DestinationAddressRequired, | |
| 357 | posix.EDQUOT => return PosixWriteError.DiskQuota, | |
| 358 | posix.EFBIG => return PosixWriteError.FileTooBig, | |
| 359 | posix.EIO => return PosixWriteError.InputOutput, | |
| 360 | posix.ENOSPC => return PosixWriteError.NoSpaceLeft, | |
| 361 | posix.EPERM => return PosixWriteError.AccessDenied, | |
| 362 | posix.EPIPE => return PosixWriteError.BrokenPipe, | |
| 363 | else => return unexpectedErrorPosix(write_err), | |
| 361 | 364 | } |
| 362 | index += rc; | |
| 363 | 365 | } |
| 364 | 366 | } |
| 365 | 367 | |
| ... | ... | @@ -1614,7 +1616,7 @@ pub const Dir = struct { |
| 1614 | 1616 | return null; |
| 1615 | 1617 | } |
| 1616 | 1618 | const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr); |
| 1617 | if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{'.', '.'})) | |
| 1619 | if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{ '.', '.' })) | |
| 1618 | 1620 | continue; |
| 1619 | 1621 | // Trust that Windows gives us valid UTF-16LE |
| 1620 | 1622 | const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable; |
test/behavior.zig+2-1| ... | ... | @@ -9,9 +9,10 @@ comptime { |
| 9 | 9 | _ = @import("cases/bitcast.zig"); |
| 10 | 10 | _ = @import("cases/bool.zig"); |
| 11 | 11 | _ = @import("cases/bugs/1111.zig"); |
| 12 | _ = @import("cases/bugs/1230.zig"); | |
| 13 | 12 | _ = @import("cases/bugs/1277.zig"); |
| 13 | _ = @import("cases/bugs/1381.zig"); | |
| 14 | 14 | _ = @import("cases/bugs/1421.zig"); |
| 15 | _ = @import("cases/bugs/1442.zig"); | |
| 15 | 16 | _ = @import("cases/bugs/394.zig"); |
| 16 | 17 | _ = @import("cases/bugs/655.zig"); |
| 17 | 18 | _ = @import("cases/bugs/656.zig"); |
test/build_examples.zig+6| ... | ... | @@ -28,4 +28,10 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void { |
| 28 | 28 | // TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it |
| 29 | 29 | cases.addBuildFile("test/standalone/load_dynamic_library/build.zig"); |
| 30 | 30 | } |
| 31 | ||
| 32 | if (!is_windows // TODO support compiling C files on windows with zig build system | |
| 33 | and builtin.arch == builtin.Arch.x86_64 // TODO add C ABI support for other architectures | |
| 34 | ) { | |
| 35 | cases.addBuildFile("test/stage1/c_abi/build.zig"); | |
| 36 | } | |
| 31 | 37 | } |
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/cases/bugs/1381.zig created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | const B = union(enum) { | |
| 4 | D: u8, | |
| 5 | E: u16, | |
| 6 | }; | |
| 7 | ||
| 8 | const A = union(enum) { | |
| 9 | B: B, | |
| 10 | C: u8, | |
| 11 | }; | |
| 12 | ||
| 13 | test "union that needs padding bytes inside an array" { | |
| 14 | var as = []A{ | |
| 15 | A{ .B = B{ .D = 1 } }, | |
| 16 | A{ .B = B{ .D = 1 } }, | |
| 17 | }; | |
| 18 | ||
| 19 | const a = as[0].B; | |
| 20 | std.debug.assertOrPanic(a.D == 1); | |
| 21 | } |
test/cases/bugs/1442.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | const Union = union(enum) { | |
| 4 | Text: []const u8, | |
| 5 | Color: u32, | |
| 6 | }; | |
| 7 | ||
| 8 | test "const error union field alignment" { | |
| 9 | var union_or_err: error!Union = Union{ .Color = 1234 }; | |
| 10 | std.debug.assertOrPanic((union_or_err catch unreachable).Color == 1234); | |
| 11 | } |
test/compile_errors.zig+19| ... | ... | @@ -1,6 +1,24 @@ |
| 1 | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | |
| 3 | 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4 | cases.add( | |
| 5 | "variable initialization compile error then referenced", | |
| 6 | \\fn Undeclared() type { | |
| 7 | \\ return T; | |
| 8 | \\} | |
| 9 | \\fn Gen() type { | |
| 10 | \\ const X = Undeclared(); | |
| 11 | \\ return struct { | |
| 12 | \\ x: X, | |
| 13 | \\ }; | |
| 14 | \\} | |
| 15 | \\export fn entry() void { | |
| 16 | \\ const S = Gen(); | |
| 17 | \\} | |
| 18 | , | |
| 19 | ".tmp_source.zig:2:12: error: use of undeclared identifier 'T'", | |
| 20 | ); | |
| 21 | ||
| 4 | 22 | cases.add( |
| 5 | 23 | "refer to the type of a generic function", |
| 6 | 24 | \\export fn entry() void { |
| ... | ... | @@ -1273,6 +1291,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1273 | 1291 | \\ |
| 1274 | 1292 | \\extern fn bar(x: *void) void { } |
| 1275 | 1293 | , |
| 1294 | ".tmp_source.zig:1:30: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'", | |
| 1276 | 1295 | ".tmp_source.zig:7:18: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'", |
| 1277 | 1296 | ); |
| 1278 | 1297 |
test/gen_h.zig+24-4| ... | ... | @@ -20,6 +20,9 @@ pub fn addCases(cases: *tests.GenHContext) void { |
| 20 | 20 | \\ A: i32, |
| 21 | 21 | \\ B: f32, |
| 22 | 22 | \\ C: bool, |
| 23 | \\ D: u64, | |
| 24 | \\ E: u64, | |
| 25 | \\ F: u64, | |
| 23 | 26 | \\}; |
| 24 | 27 | \\export fn entry(foo: Foo) void { } |
| 25 | 28 | , |
| ... | ... | @@ -27,6 +30,9 @@ pub fn addCases(cases: *tests.GenHContext) void { |
| 27 | 30 | \\ int32_t A; |
| 28 | 31 | \\ float B; |
| 29 | 32 | \\ bool C; |
| 33 | \\ uint64_t D; | |
| 34 | \\ uint64_t E; | |
| 35 | \\ uint64_t F; | |
| 30 | 36 | \\}; |
| 31 | 37 | \\ |
| 32 | 38 | \\TEST_EXPORT void entry(struct Foo foo); |
| ... | ... | @@ -34,17 +40,34 @@ pub fn addCases(cases: *tests.GenHContext) void { |
| 34 | 40 | ); |
| 35 | 41 | |
| 36 | 42 | cases.add("declare union", |
| 43 | \\const Big = extern struct { | |
| 44 | \\ A: u64, | |
| 45 | \\ B: u64, | |
| 46 | \\ C: u64, | |
| 47 | \\ D: u64, | |
| 48 | \\ E: u64, | |
| 49 | \\}; | |
| 37 | 50 | \\const Foo = extern union { |
| 38 | 51 | \\ A: i32, |
| 39 | 52 | \\ B: f32, |
| 40 | 53 | \\ C: bool, |
| 54 | \\ D: Big, | |
| 41 | 55 | \\}; |
| 42 | \\export fn entry(foo: Foo) void { } | |
| 56 | \\export fn entry(foo: Foo) void {} | |
| 43 | 57 | , |
| 58 | \\struct Big { | |
| 59 | \\ uint64_t A; | |
| 60 | \\ uint64_t B; | |
| 61 | \\ uint64_t C; | |
| 62 | \\ uint64_t D; | |
| 63 | \\ uint64_t E; | |
| 64 | \\}; | |
| 65 | \\ | |
| 44 | 66 | \\union Foo { |
| 45 | 67 | \\ int32_t A; |
| 46 | 68 | \\ float B; |
| 47 | 69 | \\ bool C; |
| 70 | \\ struct Big D; | |
| 48 | 71 | \\}; |
| 49 | 72 | \\ |
| 50 | 73 | \\TEST_EXPORT void entry(union Foo foo); |
| ... | ... | @@ -85,7 +108,6 @@ pub fn addCases(cases: *tests.GenHContext) void { |
| 85 | 108 | \\export fn a(s: *S) u8 { |
| 86 | 109 | \\ return s.a; |
| 87 | 110 | \\} |
| 88 | ||
| 89 | 111 | , |
| 90 | 112 | \\struct S; |
| 91 | 113 | \\TEST_EXPORT uint8_t a(struct S * s); |
| ... | ... | @@ -101,7 +123,6 @@ pub fn addCases(cases: *tests.GenHContext) void { |
| 101 | 123 | \\export fn a(s: *U) u8 { |
| 102 | 124 | \\ return s.A; |
| 103 | 125 | \\} |
| 104 | ||
| 105 | 126 | , |
| 106 | 127 | \\union U; |
| 107 | 128 | \\TEST_EXPORT uint8_t a(union U * s); |
| ... | ... | @@ -117,7 +138,6 @@ pub fn addCases(cases: *tests.GenHContext) void { |
| 117 | 138 | \\export fn a(s: *E) u8 { |
| 118 | 139 | \\ return @enumToInt(s.*); |
| 119 | 140 | \\} |
| 120 | ||
| 121 | 141 | , |
| 122 | 142 | \\enum E; |
| 123 | 143 | \\TEST_EXPORT uint8_t a(enum E * s); |
test/stage1/c_abi/build.zig created+18| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const rel_opts = b.standardReleaseOptions(); | |
| 5 | ||
| 6 | const c_obj = b.addCObject("cfuncs", "cfuncs.c"); | |
| 7 | c_obj.setBuildMode(rel_opts); | |
| 8 | c_obj.setNoStdLib(true); | |
| 9 | ||
| 10 | const main = b.addTest("main.zig"); | |
| 11 | main.setBuildMode(rel_opts); | |
| 12 | main.addObject(c_obj); | |
| 13 | ||
| 14 | const test_step = b.step("test", "Test the program"); | |
| 15 | test_step.dependOn(&main.step); | |
| 16 | ||
| 17 | b.default_step.dependOn(test_step); | |
| 18 | } |
test/stage1/c_abi/cfuncs.c created+208| ... | ... | @@ -0,0 +1,208 @@ |
| 1 | #include <inttypes.h> | |
| 2 | #include <stdlib.h> | |
| 3 | #include <stdbool.h> | |
| 4 | ||
| 5 | void zig_panic(); | |
| 6 | ||
| 7 | static void assert_or_panic(bool ok) { | |
| 8 | if (!ok) { | |
| 9 | zig_panic(); | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | void zig_u8(uint8_t); | |
| 14 | void zig_u16(uint16_t); | |
| 15 | void zig_u32(uint32_t); | |
| 16 | void zig_u64(uint64_t); | |
| 17 | void zig_i8(int8_t); | |
| 18 | void zig_i16(int16_t); | |
| 19 | void zig_i32(int32_t); | |
| 20 | void zig_i64(int64_t); | |
| 21 | ||
| 22 | void zig_f32(float); | |
| 23 | void zig_f64(double); | |
| 24 | ||
| 25 | void zig_ptr(void *); | |
| 26 | ||
| 27 | void zig_bool(bool); | |
| 28 | ||
| 29 | void zig_array(uint8_t[10]); | |
| 30 | ||
| 31 | struct BigStruct { | |
| 32 | uint64_t a; | |
| 33 | uint64_t b; | |
| 34 | uint64_t c; | |
| 35 | uint64_t d; | |
| 36 | uint8_t e; | |
| 37 | }; | |
| 38 | ||
| 39 | void zig_big_struct(struct BigStruct); | |
| 40 | ||
| 41 | union BigUnion { | |
| 42 | struct BigStruct a; | |
| 43 | }; | |
| 44 | ||
| 45 | void zig_big_union(union BigUnion); | |
| 46 | ||
| 47 | struct SmallStructInts { | |
| 48 | uint8_t a; | |
| 49 | uint8_t b; | |
| 50 | uint8_t c; | |
| 51 | uint8_t d; | |
| 52 | }; | |
| 53 | void zig_small_struct_ints(struct SmallStructInts); | |
| 54 | ||
| 55 | struct SplitStructInts { | |
| 56 | uint64_t a; | |
| 57 | uint8_t b; | |
| 58 | uint32_t c; | |
| 59 | }; | |
| 60 | void zig_split_struct_ints(struct SplitStructInts); | |
| 61 | ||
| 62 | struct BigStruct zig_big_struct_both(struct BigStruct); | |
| 63 | ||
| 64 | void run_c_tests(void) { | |
| 65 | zig_u8(0xff); | |
| 66 | zig_u16(0xfffe); | |
| 67 | zig_u32(0xfffffffd); | |
| 68 | zig_u64(0xfffffffffffffffc); | |
| 69 | ||
| 70 | zig_i8(-1); | |
| 71 | zig_i16(-2); | |
| 72 | zig_i32(-3); | |
| 73 | zig_i64(-4); | |
| 74 | ||
| 75 | zig_f32(12.34f); | |
| 76 | zig_f64(56.78); | |
| 77 | ||
| 78 | zig_ptr((void*)0xdeadbeefL); | |
| 79 | ||
| 80 | zig_bool(true); | |
| 81 | ||
| 82 | uint8_t array[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; | |
| 83 | zig_array(array); | |
| 84 | ||
| 85 | { | |
| 86 | struct BigStruct s = {1, 2, 3, 4, 5}; | |
| 87 | zig_big_struct(s); | |
| 88 | } | |
| 89 | ||
| 90 | { | |
| 91 | struct SmallStructInts s = {1, 2, 3, 4}; | |
| 92 | zig_small_struct_ints(s); | |
| 93 | } | |
| 94 | ||
| 95 | { | |
| 96 | struct SplitStructInts s = {1234, 100, 1337}; | |
| 97 | zig_split_struct_ints(s); | |
| 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 | } | |
| 109 | } | |
| 110 | ||
| 111 | void c_u8(uint8_t x) { | |
| 112 | assert_or_panic(x == 0xff); | |
| 113 | } | |
| 114 | ||
| 115 | void c_u16(uint16_t x) { | |
| 116 | assert_or_panic(x == 0xfffe); | |
| 117 | } | |
| 118 | ||
| 119 | void c_u32(uint32_t x) { | |
| 120 | assert_or_panic(x == 0xfffffffd); | |
| 121 | } | |
| 122 | ||
| 123 | void c_u64(uint64_t x) { | |
| 124 | assert_or_panic(x == 0xfffffffffffffffcULL); | |
| 125 | } | |
| 126 | ||
| 127 | void c_i8(int8_t x) { | |
| 128 | assert_or_panic(x == -1); | |
| 129 | } | |
| 130 | ||
| 131 | void c_i16(int16_t x) { | |
| 132 | assert_or_panic(x == -2); | |
| 133 | } | |
| 134 | ||
| 135 | void c_i32(int32_t x) { | |
| 136 | assert_or_panic(x == -3); | |
| 137 | } | |
| 138 | ||
| 139 | void c_i64(int64_t x) { | |
| 140 | assert_or_panic(x == -4); | |
| 141 | } | |
| 142 | ||
| 143 | void c_f32(float x) { | |
| 144 | assert_or_panic(x == 12.34f); | |
| 145 | } | |
| 146 | ||
| 147 | void c_f64(double x) { | |
| 148 | assert_or_panic(x == 56.78); | |
| 149 | } | |
| 150 | ||
| 151 | void c_ptr(void *x) { | |
| 152 | assert_or_panic(x == (void*)0xdeadbeefL); | |
| 153 | } | |
| 154 | ||
| 155 | void c_bool(bool x) { | |
| 156 | assert_or_panic(x); | |
| 157 | } | |
| 158 | ||
| 159 | void c_array(uint8_t x[10]) { | |
| 160 | assert_or_panic(x[0] == '1'); | |
| 161 | assert_or_panic(x[1] == '2'); | |
| 162 | assert_or_panic(x[2] == '3'); | |
| 163 | assert_or_panic(x[3] == '4'); | |
| 164 | assert_or_panic(x[4] == '5'); | |
| 165 | assert_or_panic(x[5] == '6'); | |
| 166 | assert_or_panic(x[6] == '7'); | |
| 167 | assert_or_panic(x[7] == '8'); | |
| 168 | assert_or_panic(x[8] == '9'); | |
| 169 | assert_or_panic(x[9] == '0'); | |
| 170 | } | |
| 171 | ||
| 172 | void c_big_struct(struct BigStruct x) { | |
| 173 | assert_or_panic(x.a == 1); | |
| 174 | assert_or_panic(x.b == 2); | |
| 175 | assert_or_panic(x.c == 3); | |
| 176 | assert_or_panic(x.d == 4); | |
| 177 | assert_or_panic(x.e == 5); | |
| 178 | } | |
| 179 | ||
| 180 | void c_big_union(union BigUnion x) { | |
| 181 | assert_or_panic(x.a.a == 1); | |
| 182 | assert_or_panic(x.a.b == 2); | |
| 183 | assert_or_panic(x.a.c == 3); | |
| 184 | assert_or_panic(x.a.d == 4); | |
| 185 | } | |
| 186 | ||
| 187 | void c_small_struct_ints(struct SmallStructInts x) { | |
| 188 | assert_or_panic(x.a == 1); | |
| 189 | assert_or_panic(x.b == 2); | |
| 190 | assert_or_panic(x.c == 3); | |
| 191 | assert_or_panic(x.d == 4); | |
| 192 | } | |
| 193 | ||
| 194 | void c_split_struct_ints(struct SplitStructInts x) { | |
| 195 | assert_or_panic(x.a == 1234); | |
| 196 | assert_or_panic(x.b == 100); | |
| 197 | assert_or_panic(x.c == 1337); | |
| 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 created+239| ... | ... | @@ -0,0 +1,239 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assertOrPanic = std.debug.assertOrPanic; | |
| 3 | ||
| 4 | extern fn run_c_tests() void; | |
| 5 | ||
| 6 | export fn zig_panic() noreturn { | |
| 7 | @panic("zig_panic called from C"); | |
| 8 | } | |
| 9 | ||
| 10 | test "C importing Zig ABI Tests" { | |
| 11 | run_c_tests(); | |
| 12 | } | |
| 13 | ||
| 14 | extern fn c_u8(u8) void; | |
| 15 | extern fn c_u16(u16) void; | |
| 16 | extern fn c_u32(u32) void; | |
| 17 | extern fn c_u64(u64) void; | |
| 18 | extern fn c_i8(i8) void; | |
| 19 | extern fn c_i16(i16) void; | |
| 20 | extern fn c_i32(i32) void; | |
| 21 | extern fn c_i64(i64) void; | |
| 22 | ||
| 23 | test "C ABI integers" { | |
| 24 | c_u8(0xff); | |
| 25 | c_u16(0xfffe); | |
| 26 | c_u32(0xfffffffd); | |
| 27 | c_u64(0xfffffffffffffffc); | |
| 28 | ||
| 29 | c_i8(-1); | |
| 30 | c_i16(-2); | |
| 31 | c_i32(-3); | |
| 32 | c_i64(-4); | |
| 33 | } | |
| 34 | ||
| 35 | export fn zig_u8(x: u8) void { | |
| 36 | assertOrPanic(x == 0xff); | |
| 37 | } | |
| 38 | export fn zig_u16(x: u16) void { | |
| 39 | assertOrPanic(x == 0xfffe); | |
| 40 | } | |
| 41 | export fn zig_u32(x: u32) void { | |
| 42 | assertOrPanic(x == 0xfffffffd); | |
| 43 | } | |
| 44 | export fn zig_u64(x: u64) void { | |
| 45 | assertOrPanic(x == 0xfffffffffffffffc); | |
| 46 | } | |
| 47 | export fn zig_i8(x: i8) void { | |
| 48 | assertOrPanic(x == -1); | |
| 49 | } | |
| 50 | export fn zig_i16(x: i16) void { | |
| 51 | assertOrPanic(x == -2); | |
| 52 | } | |
| 53 | export fn zig_i32(x: i32) void { | |
| 54 | assertOrPanic(x == -3); | |
| 55 | } | |
| 56 | export fn zig_i64(x: i64) void { | |
| 57 | assertOrPanic(x == -4); | |
| 58 | } | |
| 59 | ||
| 60 | extern fn c_f32(f32) void; | |
| 61 | extern fn c_f64(f64) void; | |
| 62 | ||
| 63 | test "C ABI floats" { | |
| 64 | c_f32(12.34); | |
| 65 | c_f64(56.78); | |
| 66 | } | |
| 67 | ||
| 68 | export fn zig_f32(x: f32) void { | |
| 69 | assertOrPanic(x == 12.34); | |
| 70 | } | |
| 71 | export fn zig_f64(x: f64) void { | |
| 72 | assertOrPanic(x == 56.78); | |
| 73 | } | |
| 74 | ||
| 75 | extern fn c_ptr(*c_void) void; | |
| 76 | ||
| 77 | test "C ABI pointer" { | |
| 78 | c_ptr(@intToPtr(*c_void, 0xdeadbeef)); | |
| 79 | } | |
| 80 | ||
| 81 | export fn zig_ptr(x: *c_void) void { | |
| 82 | assertOrPanic(@ptrToInt(x) == 0xdeadbeef); | |
| 83 | } | |
| 84 | ||
| 85 | extern fn c_bool(bool) void; | |
| 86 | ||
| 87 | test "C ABI bool" { | |
| 88 | c_bool(true); | |
| 89 | } | |
| 90 | ||
| 91 | export fn zig_bool(x: bool) void { | |
| 92 | assertOrPanic(x); | |
| 93 | } | |
| 94 | ||
| 95 | extern fn c_array([10]u8) void; | |
| 96 | ||
| 97 | test "C ABI array" { | |
| 98 | var array: [10]u8 = "1234567890"; | |
| 99 | c_array(array); | |
| 100 | } | |
| 101 | ||
| 102 | export fn zig_array(x: [10]u8) void { | |
| 103 | assertOrPanic(std.mem.eql(u8, x, "1234567890")); | |
| 104 | } | |
| 105 | ||
| 106 | const BigStruct = extern struct { | |
| 107 | a: u64, | |
| 108 | b: u64, | |
| 109 | c: u64, | |
| 110 | d: u64, | |
| 111 | e: u8, | |
| 112 | }; | |
| 113 | extern fn c_big_struct(BigStruct) void; | |
| 114 | ||
| 115 | test "C ABI big struct" { | |
| 116 | var s = BigStruct{ | |
| 117 | .a = 1, | |
| 118 | .b = 2, | |
| 119 | .c = 3, | |
| 120 | .d = 4, | |
| 121 | .e = 5, | |
| 122 | }; | |
| 123 | c_big_struct(s); | |
| 124 | } | |
| 125 | ||
| 126 | export fn zig_big_struct(x: BigStruct) void { | |
| 127 | assertOrPanic(x.a == 1); | |
| 128 | assertOrPanic(x.b == 2); | |
| 129 | assertOrPanic(x.c == 3); | |
| 130 | assertOrPanic(x.d == 4); | |
| 131 | assertOrPanic(x.e == 5); | |
| 132 | } | |
| 133 | ||
| 134 | const BigUnion = extern union { | |
| 135 | a: BigStruct, | |
| 136 | }; | |
| 137 | extern fn c_big_union(BigUnion) void; | |
| 138 | ||
| 139 | test "C ABI big union" { | |
| 140 | var x = BigUnion{ | |
| 141 | .a = BigStruct{ | |
| 142 | .a = 1, | |
| 143 | .b = 2, | |
| 144 | .c = 3, | |
| 145 | .d = 4, | |
| 146 | .e = 5, | |
| 147 | }, | |
| 148 | }; | |
| 149 | c_big_union(x); | |
| 150 | } | |
| 151 | ||
| 152 | export fn zig_big_union(x: BigUnion) void { | |
| 153 | assertOrPanic(x.a.a == 1); | |
| 154 | assertOrPanic(x.a.b == 2); | |
| 155 | assertOrPanic(x.a.c == 3); | |
| 156 | assertOrPanic(x.a.d == 4); | |
| 157 | assertOrPanic(x.a.e == 5); | |
| 158 | } | |
| 159 | ||
| 160 | const SmallStructInts = extern struct { | |
| 161 | a: u8, | |
| 162 | b: u8, | |
| 163 | c: u8, | |
| 164 | d: u8, | |
| 165 | }; | |
| 166 | extern fn c_small_struct_ints(SmallStructInts) void; | |
| 167 | ||
| 168 | test "C ABI small struct of ints" { | |
| 169 | var s = SmallStructInts{ | |
| 170 | .a = 1, | |
| 171 | .b = 2, | |
| 172 | .c = 3, | |
| 173 | .d = 4, | |
| 174 | }; | |
| 175 | c_small_struct_ints(s); | |
| 176 | } | |
| 177 | ||
| 178 | export fn zig_small_struct_ints(x: SmallStructInts) void { | |
| 179 | assertOrPanic(x.a == 1); | |
| 180 | assertOrPanic(x.b == 2); | |
| 181 | assertOrPanic(x.c == 3); | |
| 182 | assertOrPanic(x.d == 4); | |
| 183 | } | |
| 184 | ||
| 185 | const SplitStructInt = extern struct { | |
| 186 | a: u64, | |
| 187 | b: u8, | |
| 188 | c: u32, | |
| 189 | }; | |
| 190 | extern fn c_split_struct_ints(SplitStructInt) void; | |
| 191 | ||
| 192 | test "C ABI split struct of ints" { | |
| 193 | var s = SplitStructInt{ | |
| 194 | .a = 1234, | |
| 195 | .b = 100, | |
| 196 | .c = 1337, | |
| 197 | }; | |
| 198 | c_split_struct_ints(s); | |
| 199 | } | |
| 200 | ||
| 201 | export fn zig_split_struct_ints(x: SplitStructInt) void { | |
| 202 | assertOrPanic(x.a == 1234); | |
| 203 | assertOrPanic(x.b == 100); | |
| 204 | assertOrPanic(x.c == 1337); | |
| 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 | } |