| author | |
| committer | |
| log | 32ea6f54e5f05c4173828c4f4c8ab9965a929120 |
| tree | f4c48c5be138070207c19629c08d763c8a6a1325 |
| parent | 7ec783876a565662223268a70ba984e0a132b94a |
12 files changed, 238 insertions(+), 33 deletions(-)
build.zig+6-6| ... | @@ -10,7 +10,7 @@ const ArrayList = std.ArrayList; | ... | @@ -10,7 +10,7 @@ const ArrayList = std.ArrayList; |
| 10 | const Buffer = std.Buffer; | 10 | const Buffer = std.Buffer; |
| 11 | const io = std.io; | 11 | const io = std.io; |
| 12 | 12 | ||
| 13 | pub fn build(b: &Builder) { | 13 | pub fn build(b: &Builder) -> %void { |
| 14 | const mode = b.standardReleaseOptions(); | 14 | const mode = b.standardReleaseOptions(); |
| 15 | 15 | ||
| 16 | var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); | 16 | var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); |
| ... | @@ -36,7 +36,7 @@ pub fn build(b: &Builder) { | ... | @@ -36,7 +36,7 @@ pub fn build(b: &Builder) { |
| 36 | const test_step = b.step("test", "Run all the tests"); | 36 | const test_step = b.step("test", "Run all the tests"); |
| 37 | 37 | ||
| 38 | // find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library | 38 | // find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library |
| 39 | const build_info = b.exec([][]const u8{b.zig_exe, "BUILD_INFO"}); | 39 | const build_info = try b.exec([][]const u8{b.zig_exe, "BUILD_INFO"}); |
| 40 | var index: usize = 0; | 40 | var index: usize = 0; |
| 41 | const cmake_binary_dir = nextValue(&index, build_info); | 41 | const cmake_binary_dir = nextValue(&index, build_info); |
| 42 | const cxx_compiler = nextValue(&index, build_info); | 42 | const cxx_compiler = nextValue(&index, build_info); |
| ... | @@ -68,7 +68,7 @@ pub fn build(b: &Builder) { | ... | @@ -68,7 +68,7 @@ pub fn build(b: &Builder) { |
| 68 | dependOnLib(exe, llvm); | 68 | dependOnLib(exe, llvm); |
| 69 | 69 | ||
| 70 | if (exe.target.getOs() == builtin.Os.linux) { | 70 | if (exe.target.getOs() == builtin.Os.linux) { |
| 71 | const libstdcxx_path_padded = b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"}); | 71 | const libstdcxx_path_padded = try b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"}); |
| 72 | const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next(); | 72 | const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next(); |
| 73 | exe.addObjectFile(libstdcxx_path); | 73 | exe.addObjectFile(libstdcxx_path); |
| 74 | 74 | ||
| ... | @@ -155,9 +155,9 @@ const LibraryDep = struct { | ... | @@ -155,9 +155,9 @@ const LibraryDep = struct { |
| 155 | }; | 155 | }; |
| 156 | 156 | ||
| 157 | fn findLLVM(b: &Builder, llvm_config_exe: []const u8) -> %LibraryDep { | 157 | fn findLLVM(b: &Builder, llvm_config_exe: []const u8) -> %LibraryDep { |
| 158 | const libs_output = b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"}); | 158 | const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"}); |
| 159 | const includes_output = b.exec([][]const u8{llvm_config_exe, "--includedir"}); | 159 | const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"}); |
| 160 | const libdir_output = b.exec([][]const u8{llvm_config_exe, "--libdir"}); | 160 | const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"}); |
| 161 | 161 | ||
| 162 | var result = LibraryDep { | 162 | var result = LibraryDep { |
| 163 | .libs = ArrayList([]const u8).init(b.allocator), | 163 | .libs = ArrayList([]const u8).init(b.allocator), |
src/all_types.hpp+2| ... | @@ -1205,6 +1205,7 @@ struct FnTableEntry { | ... | @@ -1205,6 +1205,7 @@ struct FnTableEntry { |
| 1205 | uint32_t alignstack_value; | 1205 | uint32_t alignstack_value; |
| 1206 | 1206 | ||
| 1207 | ZigList<FnExport> export_list; | 1207 | ZigList<FnExport> export_list; |
| 1208 | bool calls_errorable_function; | ||
| 1208 | }; | 1209 | }; |
| 1209 | 1210 | ||
| 1210 | uint32_t fn_table_entry_hash(FnTableEntry*); | 1211 | uint32_t fn_table_entry_hash(FnTableEntry*); |
| ... | @@ -1530,6 +1531,7 @@ struct CodeGen { | ... | @@ -1530,6 +1531,7 @@ struct CodeGen { |
| 1530 | FnTableEntry *panic_fn; | 1531 | FnTableEntry *panic_fn; |
| 1531 | LLVMValueRef cur_ret_ptr; | 1532 | LLVMValueRef cur_ret_ptr; |
| 1532 | LLVMValueRef cur_fn_val; | 1533 | LLVMValueRef cur_fn_val; |
| 1534 | LLVMValueRef cur_err_ret_trace_val; | ||
| 1533 | bool c_want_stdint; | 1535 | bool c_want_stdint; |
| 1534 | bool c_want_stdbool; | 1536 | bool c_want_stdbool; |
| 1535 | AstNode *root_export_decl; | 1537 | AstNode *root_export_decl; |
src/analyze.cpp+5-1| ... | @@ -869,7 +869,7 @@ static const char *calling_convention_fn_type_str(CallingConvention cc) { | ... | @@ -869,7 +869,7 @@ static const char *calling_convention_fn_type_str(CallingConvention cc) { |
| 869 | zig_unreachable(); | 869 | zig_unreachable(); |
| 870 | } | 870 | } |
| 871 | 871 | ||
| 872 | static TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) { | 872 | TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) { |
| 873 | if (g->stack_trace_type == nullptr) { | 873 | if (g->stack_trace_type == nullptr) { |
| 874 | ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace"); | 874 | ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace"); |
| 875 | assert(stack_trace_type_val->type->id == TypeTableEntryIdMetaType); | 875 | assert(stack_trace_type_val->type->id == TypeTableEntryIdMetaType); |
| ... | @@ -1191,6 +1191,9 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c | ... | @@ -1191,6 +1191,9 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c |
| 1191 | } | 1191 | } |
| 1192 | 1192 | ||
| 1193 | TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type); | 1193 | TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type); |
| 1194 | if (type_is_invalid(type_entry)) { | ||
| 1195 | return g->builtin_types.entry_invalid; | ||
| 1196 | } | ||
| 1194 | if (fn_type_id.cc != CallingConventionUnspecified) { | 1197 | if (fn_type_id.cc != CallingConventionUnspecified) { |
| 1195 | type_ensure_zero_bits_known(g, type_entry); | 1198 | type_ensure_zero_bits_known(g, type_entry); |
| 1196 | if (!type_has_bits(type_entry)) { | 1199 | if (!type_has_bits(type_entry)) { |
| ... | @@ -2586,6 +2589,7 @@ static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntr | ... | @@ -2586,6 +2589,7 @@ static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntr |
| 2586 | } | 2589 | } |
| 2587 | 2590 | ||
| 2588 | static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { | 2591 | static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { |
| 2592 | return; // TODO | ||
| 2589 | AstNode *proto_node = panic_fn->proto_node; | 2593 | AstNode *proto_node = panic_fn->proto_node; |
| 2590 | assert(proto_node->type == NodeTypeFnProto); | 2594 | assert(proto_node->type == NodeTypeFnProto); |
| 2591 | TypeTableEntry *fn_type = panic_fn->type_entry; | 2595 | TypeTableEntry *fn_type = panic_fn->type_entry; |
src/analyze.hpp+1| ... | @@ -187,6 +187,7 @@ void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, G | ... | @@ -187,6 +187,7 @@ void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, G |
| 187 | 187 | ||
| 188 | 188 | ||
| 189 | ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name); | 189 | ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name); |
| 190 | TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g); | ||
| 190 | 191 | ||
| 191 | 192 | ||
| 192 | #endif | 193 | #endif |
src/codegen.cpp+93-13| ... | @@ -22,6 +22,8 @@ | ... | @@ -22,6 +22,8 @@ |
| 22 | #include <stdio.h> | 22 | #include <stdio.h> |
| 23 | #include <errno.h> | 23 | #include <errno.h> |
| 24 | 24 | ||
| 25 | static const size_t stack_trace_ptr_count = 31; | ||
| 26 | |||
| 25 | static void init_darwin_native(CodeGen *g) { | 27 | static void init_darwin_native(CodeGen *g) { |
| 26 | char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET"); | 28 | char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET"); |
| 27 | char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET"); | 29 | char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET"); |
| ... | @@ -867,16 +869,24 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) { | ... | @@ -867,16 +869,24 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) { |
| 867 | return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(str_type->type_ref, 0)); | 869 | return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(str_type->type_ref, 0)); |
| 868 | } | 870 | } |
| 869 | 871 | ||
| 870 | static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) { | 872 | static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace_arg) { |
| 871 | assert(g->panic_fn != nullptr); | 873 | assert(g->panic_fn != nullptr); |
| 872 | LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn); | 874 | LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn); |
| 873 | LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc); | 875 | LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc); |
| 874 | ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, ZigLLVM_FnInlineAuto, ""); | 876 | if (stack_trace_arg == nullptr) { |
| 877 | TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g); | ||
| 878 | stack_trace_arg = LLVMConstNull(ptr_to_stack_trace_type->type_ref); | ||
| 879 | } | ||
| 880 | LLVMValueRef args[] = { | ||
| 881 | msg_arg, | ||
| 882 | stack_trace_arg, | ||
| 883 | }; | ||
| 884 | ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, ""); | ||
| 875 | LLVMBuildUnreachable(g->builder); | 885 | LLVMBuildUnreachable(g->builder); |
| 876 | } | 886 | } |
| 877 | 887 | ||
| 878 | static void gen_debug_safety_crash(CodeGen *g, PanicMsgId msg_id) { | 888 | static void gen_debug_safety_crash(CodeGen *g, PanicMsgId msg_id) { |
| 879 | gen_panic(g, get_panic_msg_ptr_val(g, msg_id)); | 889 | gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr); |
| 880 | } | 890 | } |
| 881 | 891 | ||
| 882 | static LLVMValueRef get_memcpy_fn_val(CodeGen *g) { | 892 | static LLVMValueRef get_memcpy_fn_val(CodeGen *g) { |
| ... | @@ -956,7 +966,11 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { | ... | @@ -956,7 +966,11 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { |
| 956 | LLVMValueRef offset_buf_ptr = LLVMConstInBoundsGEP(global_array, offset_ptr_indices, 2); | 966 | LLVMValueRef offset_buf_ptr = LLVMConstInBoundsGEP(global_array, offset_ptr_indices, 2); |
| 957 | 967 | ||
| 958 | Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_fail_unwrap"), false); | 968 | Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_fail_unwrap"), false); |
| 959 | LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), &g->err_tag_type->type_ref, 1, false); | 969 | LLVMTypeRef arg_types[] = { |
| 970 | g->err_tag_type->type_ref, | ||
| 971 | g->ptr_to_stack_trace_type->type_ref, | ||
| 972 | }; | ||
| 973 | LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false); | ||
| 960 | LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref); | 974 | LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref); |
| 961 | addLLVMFnAttr(fn_val, "noreturn"); | 975 | addLLVMFnAttr(fn_val, "noreturn"); |
| 962 | addLLVMFnAttr(fn_val, "cold"); | 976 | addLLVMFnAttr(fn_val, "cold"); |
| ... | @@ -1008,7 +1022,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { | ... | @@ -1008,7 +1022,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { |
| 1008 | LLVMValueRef global_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, global_slice, slice_len_index, ""); | 1022 | LLVMValueRef global_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, global_slice, slice_len_index, ""); |
| 1009 | gen_store(g, full_buf_len, global_slice_len_field_ptr, u8_ptr_type); | 1023 | gen_store(g, full_buf_len, global_slice_len_field_ptr, u8_ptr_type); |
| 1010 | 1024 | ||
| 1011 | gen_panic(g, global_slice); | 1025 | gen_panic(g, global_slice, LLVMGetParam(fn_val, 1)); |
| 1012 | 1026 | ||
| 1013 | LLVMPositionBuilderAtEnd(g->builder, prev_block); | 1027 | LLVMPositionBuilderAtEnd(g->builder, prev_block); |
| 1014 | LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); | 1028 | LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); |
| ... | @@ -1019,7 +1033,16 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { | ... | @@ -1019,7 +1033,16 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { |
| 1019 | 1033 | ||
| 1020 | static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) { | 1034 | static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) { |
| 1021 | LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g); | 1035 | LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g); |
| 1022 | ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified), | 1036 | LLVMValueRef err_ret_trace_val = g->cur_err_ret_trace_val; |
| 1037 | if (err_ret_trace_val == nullptr) { | ||
| 1038 | TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g); | ||
| 1039 | err_ret_trace_val = LLVMConstNull(ptr_to_stack_trace_type->type_ref); | ||
| 1040 | } | ||
| 1041 | LLVMValueRef args[] = { | ||
| 1042 | err_val, | ||
| 1043 | err_ret_trace_val, | ||
| 1044 | }; | ||
| 1045 | ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, get_llvm_cc(g, CallingConventionUnspecified), | ||
| 1023 | ZigLLVM_FnInlineAuto, ""); | 1046 | ZigLLVM_FnInlineAuto, ""); |
| 1024 | LLVMBuildUnreachable(g->builder); | 1047 | LLVMBuildUnreachable(g->builder); |
| 1025 | } | 1048 | } |
| ... | @@ -1299,6 +1322,50 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { | ... | @@ -1299,6 +1322,50 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { |
| 1299 | static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { | 1322 | static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { |
| 1300 | LLVMValueRef value = ir_llvm_value(g, return_instruction->value); | 1323 | LLVMValueRef value = ir_llvm_value(g, return_instruction->value); |
| 1301 | TypeTableEntry *return_type = return_instruction->value->value.type; | 1324 | TypeTableEntry *return_type = return_instruction->value->value.type; |
| 1325 | |||
| 1326 | bool is_err_return = false; | ||
| 1327 | if (return_type->id == TypeTableEntryIdErrorUnion) { | ||
| 1328 | if (return_instruction->value->value.special == ConstValSpecialStatic) { | ||
| 1329 | is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr; | ||
| 1330 | } else if (return_instruction->value->value.special == ConstValSpecialRuntime) { | ||
| 1331 | is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError; | ||
| 1332 | // TODO: emit a branch to check if the return value is an error | ||
| 1333 | } | ||
| 1334 | } else if (return_type->id == TypeTableEntryIdPureError) { | ||
| 1335 | is_err_return = true; | ||
| 1336 | } | ||
| 1337 | if (is_err_return) { | ||
| 1338 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref; | ||
| 1339 | |||
| 1340 | // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = @instructionPointer(); | ||
| 1341 | // stack_trace.index += 1; | ||
| 1342 | |||
| 1343 | LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError"); | ||
| 1344 | |||
| 1345 | LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block); | ||
| 1346 | size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; | ||
| 1347 | LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, ""); | ||
| 1348 | size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; | ||
| 1349 | LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)addresses_field_index, ""); | ||
| 1350 | |||
| 1351 | // stack_trace.instruction_addresses[stack_trace.index % stack_trace_ptr_count] = @instructionPointer(); | ||
| 1352 | LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, ""); | ||
| 1353 | LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), ""); | ||
| 1354 | LLVMValueRef address_indices[] = { | ||
| 1355 | LLVMConstNull(usize_type_ref), | ||
| 1356 | modded_val, | ||
| 1357 | }; | ||
| 1358 | LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, addresses_field_ptr, address_indices, 2, ""); | ||
| 1359 | LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, block_address, usize_type_ref, ""); | ||
| 1360 | gen_store_untyped(g, address_value, address_slot, 0, false); | ||
| 1361 | |||
| 1362 | // stack_trace.index += 1; | ||
| 1363 | LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), ""); | ||
| 1364 | gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false); | ||
| 1365 | |||
| 1366 | LLVMBuildBr(g->builder, return_block); | ||
| 1367 | LLVMPositionBuilderAtEnd(g->builder, return_block); | ||
| 1368 | } | ||
| 1302 | if (handle_is_ptr(return_type)) { | 1369 | if (handle_is_ptr(return_type)) { |
| 1303 | if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) { | 1370 | if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) { |
| 1304 | assert(g->cur_ret_ptr); | 1371 | assert(g->cur_ret_ptr); |
| ... | @@ -2353,7 +2420,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr | ... | @@ -2353,7 +2420,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 2353 | } | 2420 | } |
| 2354 | } | 2421 | } |
| 2355 | if (last_arg_err_ret_stack) { | 2422 | if (last_arg_err_ret_stack) { |
| 2356 | gen_param_values[gen_param_index] = LLVMGetUndef(g->ptr_to_stack_trace_type->type_ref); | 2423 | gen_param_values[gen_param_index] = g->cur_err_ret_trace_val; |
| 2357 | gen_param_index += 1; | 2424 | gen_param_index += 1; |
| 2358 | } | 2425 | } |
| 2359 | 2426 | ||
| ... | @@ -3482,7 +3549,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec | ... | @@ -3482,7 +3549,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec |
| 3482 | } | 3549 | } |
| 3483 | 3550 | ||
| 3484 | static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) { | 3551 | static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) { |
| 3485 | gen_panic(g, ir_llvm_value(g, instruction->msg)); | 3552 | gen_panic(g, ir_llvm_value(g, instruction->msg), nullptr); |
| 3486 | return nullptr; | 3553 | return nullptr; |
| 3487 | } | 3554 | } |
| 3488 | 3555 | ||
| ... | @@ -4501,7 +4568,8 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -4501,7 +4568,8 @@ static void do_code_gen(CodeGen *g) { |
| 4501 | LLVMValueRef fn = fn_llvm_value(g, fn_table_entry); | 4568 | LLVMValueRef fn = fn_llvm_value(g, fn_table_entry); |
| 4502 | g->cur_fn = fn_table_entry; | 4569 | g->cur_fn = fn_table_entry; |
| 4503 | g->cur_fn_val = fn; | 4570 | g->cur_fn_val = fn; |
| 4504 | if (handle_is_ptr(fn_table_entry->type_entry->data.fn.fn_type_id.return_type)) { | 4571 | TypeTableEntry *return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; |
| 4572 | if (handle_is_ptr(return_type)) { | ||
| 4505 | g->cur_ret_ptr = LLVMGetParam(fn, 0); | 4573 | g->cur_ret_ptr = LLVMGetParam(fn, 0); |
| 4506 | } else { | 4574 | } else { |
| 4507 | g->cur_ret_ptr = nullptr; | 4575 | g->cur_ret_ptr = nullptr; |
| ... | @@ -4510,6 +4578,18 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -4510,6 +4578,18 @@ static void do_code_gen(CodeGen *g) { |
| 4510 | build_all_basic_blocks(g, fn_table_entry); | 4578 | build_all_basic_blocks(g, fn_table_entry); |
| 4511 | clear_debug_source_node(g); | 4579 | clear_debug_source_node(g); |
| 4512 | 4580 | ||
| 4581 | if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) { | ||
| 4582 | g->cur_err_ret_trace_val = LLVMGetParam(fn, LLVMCountParamTypes(fn_table_entry->type_entry->data.fn.raw_type_ref) - 1); | ||
| 4583 | } else if (fn_table_entry->calls_errorable_function) { | ||
| 4584 | g->cur_err_ret_trace_val = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type)); | ||
| 4585 | size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; | ||
| 4586 | LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, ""); | ||
| 4587 | TypeTableEntry *usize = g->builtin_types.entry_usize; | ||
| 4588 | gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false); | ||
| 4589 | } else { | ||
| 4590 | g->cur_err_ret_trace_val = nullptr; | ||
| 4591 | } | ||
| 4592 | |||
| 4513 | // allocate temporary stack data | 4593 | // allocate temporary stack data |
| 4514 | for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) { | 4594 | for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) { |
| 4515 | IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i); | 4595 | IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i); |
| ... | @@ -5096,12 +5176,11 @@ static void define_builtin_compile_vars(CodeGen *g) { | ... | @@ -5096,12 +5176,11 @@ static void define_builtin_compile_vars(CodeGen *g) { |
| 5096 | os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); | 5176 | os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); |
| 5097 | Buf *contents = buf_alloc(); | 5177 | Buf *contents = buf_alloc(); |
| 5098 | 5178 | ||
| 5099 | buf_append_str(contents, | 5179 | buf_appendf(contents, |
| 5100 | "pub const StackTrace = struct {\n" | 5180 | "pub const StackTrace = struct {\n" |
| 5101 | " index: usize,\n" | 5181 | " index: usize,\n" |
| 5102 | " instruction_addresses: [31]usize,\n" | 5182 | " instruction_addresses: [%" ZIG_PRI_usize "]usize,\n" |
| 5103 | "};\n\n" | 5183 | "};\n\n", stack_trace_ptr_count); |
| 5104 | ); | ||
| 5105 | 5184 | ||
| 5106 | const char *cur_os = nullptr; | 5185 | const char *cur_os = nullptr; |
| 5107 | { | 5186 | { |
| ... | @@ -5266,6 +5345,7 @@ static void define_builtin_compile_vars(CodeGen *g) { | ... | @@ -5266,6 +5345,7 @@ static void define_builtin_compile_vars(CodeGen *g) { |
| 5266 | g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); | 5345 | g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); |
| 5267 | g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); | 5346 | g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); |
| 5268 | g->compile_var_import = add_source_file(g, g->compile_var_package, abs_full_path, contents); | 5347 | g->compile_var_import = add_source_file(g, g->compile_var_package, abs_full_path, contents); |
| 5348 | scan_import(g, g->compile_var_import); | ||
| 5269 | } | 5349 | } |
| 5270 | 5350 | ||
| 5271 | static void init(CodeGen *g) { | 5351 | static void init(CodeGen *g) { |
src/ir.cpp+12| ... | @@ -10043,9 +10043,21 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal | ... | @@ -10043,9 +10043,21 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 10043 | TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; | 10043 | TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; |
| 10044 | ir_add_alloca(ira, new_call_instruction, return_type); | 10044 | ir_add_alloca(ira, new_call_instruction, return_type); |
| 10045 | 10045 | ||
| 10046 | if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) { | ||
| 10047 | parent_fn_entry->calls_errorable_function = true; | ||
| 10048 | } | ||
| 10049 | |||
| 10046 | return ir_finish_anal(ira, return_type); | 10050 | return ir_finish_anal(ira, return_type); |
| 10047 | } | 10051 | } |
| 10048 | 10052 | ||
| 10053 | FnTableEntry *parent_fn_entry = exec_fn_entry(ira->new_irb.exec); | ||
| 10054 | assert(fn_type_id->return_type != nullptr); | ||
| 10055 | assert(parent_fn_entry != nullptr); | ||
| 10056 | if (fn_type_id->return_type->id == TypeTableEntryIdPureError || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) { | ||
| 10057 | parent_fn_entry->calls_errorable_function = true; | ||
| 10058 | } | ||
| 10059 | |||
| 10060 | |||
| 10049 | IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count); | 10061 | IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count); |
| 10050 | size_t next_arg_index = 0; | 10062 | size_t next_arg_index = 0; |
| 10051 | if (first_arg_ptr) { | 10063 | if (first_arg_ptr) { |
std/build.zig+2-4| ... | @@ -721,11 +721,9 @@ pub const Builder = struct { | ... | @@ -721,11 +721,9 @@ pub const Builder = struct { |
| 721 | return error.FileNotFound; | 721 | return error.FileNotFound; |
| 722 | } | 722 | } |
| 723 | 723 | ||
| 724 | pub fn exec(self: &Builder, argv: []const []const u8) -> []u8 { | 724 | pub fn exec(self: &Builder, argv: []const []const u8) -> %[]u8 { |
| 725 | const max_output_size = 100 * 1024; | 725 | const max_output_size = 100 * 1024; |
| 726 | const result = os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size) catch |err| { | 726 | const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size); |
| 727 | std.debug.panic("Unable to spawn {}: {}", argv[0], @errorName(err)); | ||
| 728 | }; | ||
| 729 | switch (result.term) { | 727 | switch (result.term) { |
| 730 | os.ChildProcess.Term.Exited => |code| { | 728 | os.ChildProcess.Term.Exited => |code| { |
| 731 | if (code != 0) { | 729 | if (code != 0) { |
std/debug/index.zig+106-4| ... | @@ -37,10 +37,16 @@ fn getStderrStream() -> %&io.OutStream { | ... | @@ -37,10 +37,16 @@ fn getStderrStream() -> %&io.OutStream { |
| 37 | } | 37 | } |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. | ||
| 41 | pub fn dumpCurrentStackTrace() { | ||
| 42 | const stderr = getStderrStream() catch return; | ||
| 43 | writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return; | ||
| 44 | } | ||
| 45 | |||
| 40 | /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. | 46 | /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. |
| 41 | pub fn dumpStackTrace() { | 47 | pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) { |
| 42 | const stderr = getStderrStream() catch return; | 48 | const stderr = getStderrStream() catch return; |
| 43 | writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return; | 49 | writeStackTrace(stack_trace, stderr, global_allocator, stderr_file.isTty()) catch return; |
| 44 | } | 50 | } |
| 45 | 51 | ||
| 46 | /// This function invokes undefined behavior when `ok` is `false`. | 52 | /// This function invokes undefined behavior when `ok` is `false`. |
| ... | @@ -88,7 +94,7 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn { | ... | @@ -88,7 +94,7 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn { |
| 88 | 94 | ||
| 89 | const stderr = getStderrStream() catch os.abort(); | 95 | const stderr = getStderrStream() catch os.abort(); |
| 90 | stderr.print(format ++ "\n", args) catch os.abort(); | 96 | stderr.print(format ++ "\n", args) catch os.abort(); |
| 91 | writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort(); | 97 | writeCurrentStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort(); |
| 92 | 98 | ||
| 93 | os.abort(); | 99 | os.abort(); |
| 94 | } | 100 | } |
| ... | @@ -101,7 +107,103 @@ const RESET = "\x1b[0m"; | ... | @@ -101,7 +107,103 @@ const RESET = "\x1b[0m"; |
| 101 | error PathNotFound; | 107 | error PathNotFound; |
| 102 | error InvalidDebugInfo; | 108 | error InvalidDebugInfo; |
| 103 | 109 | ||
| 104 | pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool, | 110 | pub fn writeStackTrace(st_addrs: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool) -> %void { |
| 111 | switch (builtin.object_format) { | ||
| 112 | builtin.ObjectFormat.elf => { | ||
| 113 | var stack_trace = ElfStackTrace { | ||
| 114 | .self_exe_file = undefined, | ||
| 115 | .elf = undefined, | ||
| 116 | .debug_info = undefined, | ||
| 117 | .debug_abbrev = undefined, | ||
| 118 | .debug_str = undefined, | ||
| 119 | .debug_line = undefined, | ||
| 120 | .debug_ranges = null, | ||
| 121 | .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator), | ||
| 122 | .compile_unit_list = ArrayList(CompileUnit).init(allocator), | ||
| 123 | }; | ||
| 124 | const st = &stack_trace; | ||
| 125 | st.self_exe_file = try os.openSelfExe(); | ||
| 126 | defer st.self_exe_file.close(); | ||
| 127 | |||
| 128 | try st.elf.openFile(allocator, &st.self_exe_file); | ||
| 129 | defer st.elf.close(); | ||
| 130 | |||
| 131 | st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; | ||
| 132 | st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; | ||
| 133 | st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo; | ||
| 134 | st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo; | ||
| 135 | st.debug_ranges = (try st.elf.findSection(".debug_ranges")); | ||
| 136 | try scanAllCompileUnits(st); | ||
| 137 | |||
| 138 | var ignored_count: usize = 0; | ||
| 139 | |||
| 140 | var frame_index: usize = undefined; | ||
| 141 | var frames_left: usize = undefined; | ||
| 142 | if (st_addrs.index < st_addrs.instruction_addresses.len) { | ||
| 143 | frame_index = 0; | ||
| 144 | frames_left = st_addrs.index; | ||
| 145 | } else { | ||
| 146 | frame_index = (st_addrs.index + 1) % st_addrs.instruction_addresses.len; | ||
| 147 | frames_left = st_addrs.instruction_addresses.len; | ||
| 148 | } | ||
| 149 | |||
| 150 | while (frames_left != 0) : ({frames_left -= 1; frame_index = (frame_index + 1) % st_addrs.instruction_addresses.len;}) { | ||
| 151 | const return_address = st_addrs.instruction_addresses[frame_index]; | ||
| 152 | |||
| 153 | // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal | ||
| 154 | // at compile time. I'll call it issue #313 | ||
| 155 | const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; | ||
| 156 | |||
| 157 | const compile_unit = findCompileUnit(st, return_address) catch { | ||
| 158 | try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", | ||
| 159 | return_address); | ||
| 160 | continue; | ||
| 161 | }; | ||
| 162 | const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name); | ||
| 163 | if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| { | ||
| 164 | defer line_info.deinit(); | ||
| 165 | try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ | ||
| 166 | DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", | ||
| 167 | line_info.file_name, line_info.line, line_info.column, | ||
| 168 | return_address, compile_unit_name); | ||
| 169 | if (printLineFromFile(st.allocator(), out_stream, line_info)) { | ||
| 170 | if (line_info.column == 0) { | ||
| 171 | try out_stream.write("\n"); | ||
| 172 | } else { | ||
| 173 | {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { | ||
| 174 | try out_stream.writeByte(' '); | ||
| 175 | }} | ||
| 176 | try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); | ||
| 177 | } | ||
| 178 | } else |err| switch (err) { | ||
| 179 | error.EndOfFile, error.PathNotFound => {}, | ||
| 180 | else => return err, | ||
| 181 | } | ||
| 182 | } else |err| switch (err) { | ||
| 183 | error.MissingDebugInfo, error.InvalidDebugInfo => { | ||
| 184 | try out_stream.print(ptr_hex ++ " in ??? ({})\n", | ||
| 185 | return_address, compile_unit_name); | ||
| 186 | }, | ||
| 187 | else => return err, | ||
| 188 | } | ||
| 189 | } | ||
| 190 | }, | ||
| 191 | builtin.ObjectFormat.coff => { | ||
| 192 | try out_stream.write("(stack trace unavailable for COFF object format)\n"); | ||
| 193 | }, | ||
| 194 | builtin.ObjectFormat.macho => { | ||
| 195 | try out_stream.write("(stack trace unavailable for Mach-O object format)\n"); | ||
| 196 | }, | ||
| 197 | builtin.ObjectFormat.wasm => { | ||
| 198 | try out_stream.write("(stack trace unavailable for WASM object format)\n"); | ||
| 199 | }, | ||
| 200 | builtin.ObjectFormat.unknown => { | ||
| 201 | try out_stream.write("(stack trace unavailable for unknown object format)\n"); | ||
| 202 | }, | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool, | ||
| 105 | ignore_frame_count: usize) -> %void | 207 | ignore_frame_count: usize) -> %void |
| 106 | { | 208 | { |
| 107 | switch (builtin.object_format) { | 209 | switch (builtin.object_format) { |
std/special/build_runner.zig+2-2| ... | @@ -112,7 +112,7 @@ pub fn main() -> %void { | ... | @@ -112,7 +112,7 @@ pub fn main() -> %void { |
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | builder.setInstallPrefix(prefix); | 114 | builder.setInstallPrefix(prefix); |
| 115 | root.build(&builder); | 115 | root.build(&builder) catch unreachable; |
| 116 | 116 | ||
| 117 | if (builder.validateUserInputDidItFail()) | 117 | if (builder.validateUserInputDidItFail()) |
| 118 | return usageAndErr(&builder, true, try stderr_stream); | 118 | return usageAndErr(&builder, true, try stderr_stream); |
| ... | @@ -129,7 +129,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) | ... | @@ -129,7 +129,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) |
| 129 | // run the build script to collect the options | 129 | // run the build script to collect the options |
| 130 | if (!already_ran_build) { | 130 | if (!already_ran_build) { |
| 131 | builder.setInstallPrefix(null); | 131 | builder.setInstallPrefix(null); |
| 132 | root.build(builder); | 132 | root.build(builder) catch unreachable; |
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | // This usage text has to be synchronized with src/main.cpp | 135 | // This usage text has to be synchronized with src/main.cpp |
std/special/builtin.zig+1-1| ... | @@ -5,7 +5,7 @@ const builtin = @import("builtin"); | ... | @@ -5,7 +5,7 @@ const builtin = @import("builtin"); |
| 5 | 5 | ||
| 6 | // Avoid dragging in the debug safety mechanisms into this .o file, | 6 | // Avoid dragging in the debug safety mechanisms into this .o file, |
| 7 | // unless we're trying to test this file. | 7 | // unless we're trying to test this file. |
| 8 | pub coldcc fn panic(msg: []const u8) -> noreturn { | 8 | pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { |
| 9 | if (builtin.is_test) { | 9 | if (builtin.is_test) { |
| 10 | @import("std").debug.panic("{}", msg); | 10 | @import("std").debug.panic("{}", msg); |
| 11 | } else { | 11 | } else { |
std/special/compiler_rt/index.zig+1-1| ... | @@ -74,7 +74,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; | ... | @@ -74,7 +74,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; |
| 74 | 74 | ||
| 75 | // Avoid dragging in the debug safety mechanisms into this .o file, | 75 | // Avoid dragging in the debug safety mechanisms into this .o file, |
| 76 | // unless we're trying to test this file. | 76 | // unless we're trying to test this file. |
| 77 | pub coldcc fn panic(msg: []const u8) -> noreturn { | 77 | pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { |
| 78 | if (is_test) { | 78 | if (is_test) { |
| 79 | @import("std").debug.panic("{}", msg); | 79 | @import("std").debug.panic("{}", msg); |
| 80 | } else { | 80 | } else { |
std/special/panic.zig+7-1| ... | @@ -4,14 +4,20 @@ | ... | @@ -4,14 +4,20 @@ |
| 4 | // have to be added in the compiler. | 4 | // have to be added in the compiler. |
| 5 | 5 | ||
| 6 | const builtin = @import("builtin"); | 6 | const builtin = @import("builtin"); |
| 7 | const std = @import("std"); | ||
| 7 | 8 | ||
| 8 | pub coldcc fn panic(msg: []const u8) -> noreturn { | 9 | pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn { |
| 9 | switch (builtin.os) { | 10 | switch (builtin.os) { |
| 10 | // TODO: fix panic in zen. | 11 | // TODO: fix panic in zen. |
| 11 | builtin.Os.freestanding, builtin.Os.zen => { | 12 | builtin.Os.freestanding, builtin.Os.zen => { |
| 12 | while (true) {} | 13 | while (true) {} |
| 13 | }, | 14 | }, |
| 14 | else => { | 15 | else => { |
| 16 | if (error_return_trace) |trace| { | ||
| 17 | std.debug.warn("{}\n", msg); | ||
| 18 | std.debug.dumpStackTrace(trace); | ||
| 19 | @import("std").debug.panic(""); | ||
| 20 | } | ||
| 15 | @import("std").debug.panic("{}", msg); | 21 | @import("std").debug.panic("{}", msg); |
| 16 | }, | 22 | }, |
| 17 | } | 23 | } |