| author | |
| committer | |
| log | d26905c102f45382a5aa4bf59deda0ccc8c6e50f |
| tree | e684112b364f7b896818c2d64cfb6da2cabc5495 |
| parent | 7186e92c86982950d0aa7c0c2deef9ef96bc1264 |
it would work but LLVM is not correctly spilling the addresses.
See #8215 files changed, 375 insertions(+), 76 deletions(-)
src/all_types.hpp+19| ... | @@ -1656,6 +1656,8 @@ struct CodeGen { | ... | @@ -1656,6 +1656,8 @@ struct CodeGen { |
| 1656 | LLVMValueRef coro_save_fn_val; | 1656 | LLVMValueRef coro_save_fn_val; |
| 1657 | LLVMValueRef coro_promise_fn_val; | 1657 | LLVMValueRef coro_promise_fn_val; |
| 1658 | LLVMValueRef coro_alloc_helper_fn_val; | 1658 | LLVMValueRef coro_alloc_helper_fn_val; |
| 1659 | LLVMValueRef merge_err_ret_traces_fn_val; | ||
| 1660 | LLVMValueRef add_error_return_trace_addr_fn_val; | ||
| 1659 | bool error_during_imports; | 1661 | bool error_during_imports; |
| 1660 | 1662 | ||
| 1661 | const char **clang_argv; | 1663 | const char **clang_argv; |
| ... | @@ -2054,6 +2056,7 @@ enum IrInstructionId { | ... | @@ -2054,6 +2056,7 @@ enum IrInstructionId { |
| 2054 | IrInstructionIdAwaitBookkeeping, | 2056 | IrInstructionIdAwaitBookkeeping, |
| 2055 | IrInstructionIdSaveErrRetAddr, | 2057 | IrInstructionIdSaveErrRetAddr, |
| 2056 | IrInstructionIdAddImplicitReturnType, | 2058 | IrInstructionIdAddImplicitReturnType, |
| 2059 | IrInstructionIdMergeErrRetTraces, | ||
| 2057 | }; | 2060 | }; |
| 2058 | 2061 | ||
| 2059 | struct IrInstruction { | 2062 | struct IrInstruction { |
| ... | @@ -2892,6 +2895,11 @@ struct IrInstructionExport { | ... | @@ -2892,6 +2895,11 @@ struct IrInstructionExport { |
| 2892 | 2895 | ||
| 2893 | struct IrInstructionErrorReturnTrace { | 2896 | struct IrInstructionErrorReturnTrace { |
| 2894 | IrInstruction base; | 2897 | IrInstruction base; |
| 2898 | |||
| 2899 | enum Nullable { | ||
| 2900 | Null, | ||
| 2901 | NonNull, | ||
| 2902 | } nullable; | ||
| 2895 | }; | 2903 | }; |
| 2896 | 2904 | ||
| 2897 | struct IrInstructionErrorUnion { | 2905 | struct IrInstructionErrorUnion { |
| ... | @@ -3024,6 +3032,13 @@ struct IrInstructionAddImplicitReturnType { | ... | @@ -3024,6 +3032,13 @@ struct IrInstructionAddImplicitReturnType { |
| 3024 | IrInstruction *value; | 3032 | IrInstruction *value; |
| 3025 | }; | 3033 | }; |
| 3026 | 3034 | ||
| 3035 | struct IrInstructionMergeErrRetTraces { | ||
| 3036 | IrInstruction base; | ||
| 3037 | |||
| 3038 | IrInstruction *coro_promise_ptr; | ||
| 3039 | TypeStructField *resolved_field; | ||
| 3040 | }; | ||
| 3041 | |||
| 3027 | static const size_t slice_ptr_index = 0; | 3042 | static const size_t slice_ptr_index = 0; |
| 3028 | static const size_t slice_len_index = 1; | 3043 | static const size_t slice_len_index = 1; |
| 3029 | 3044 | ||
| ... | @@ -3033,11 +3048,15 @@ static const size_t maybe_null_index = 1; | ... | @@ -3033,11 +3048,15 @@ static const size_t maybe_null_index = 1; |
| 3033 | static const size_t err_union_err_index = 0; | 3048 | static const size_t err_union_err_index = 0; |
| 3034 | static const size_t err_union_payload_index = 1; | 3049 | static const size_t err_union_payload_index = 1; |
| 3035 | 3050 | ||
| 3051 | // TODO call graph analysis to find out what this number needs to be for every function | ||
| 3052 | static const size_t stack_trace_ptr_count = 30; | ||
| 3053 | |||
| 3036 | #define ASYNC_ALLOC_FIELD_NAME "allocFn" | 3054 | #define ASYNC_ALLOC_FIELD_NAME "allocFn" |
| 3037 | #define ASYNC_FREE_FIELD_NAME "freeFn" | 3055 | #define ASYNC_FREE_FIELD_NAME "freeFn" |
| 3038 | #define AWAITER_HANDLE_FIELD_NAME "awaiter_handle" | 3056 | #define AWAITER_HANDLE_FIELD_NAME "awaiter_handle" |
| 3039 | #define RESULT_FIELD_NAME "result" | 3057 | #define RESULT_FIELD_NAME "result" |
| 3040 | #define RESULT_PTR_FIELD_NAME "result_ptr" | 3058 | #define RESULT_PTR_FIELD_NAME "result_ptr" |
| 3059 | #define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr" | ||
| 3041 | 3060 | ||
| 3042 | 3061 | ||
| 3043 | enum FloatMode { | 3062 | enum FloatMode { |
src/analyze.cpp+19-3| ... | @@ -468,10 +468,26 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) | ... | @@ -468,10 +468,26 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) |
| 468 | 468 | ||
| 469 | TypeTableEntry *awaiter_handle_type = get_maybe_type(g, g->builtin_types.entry_promise); | 469 | TypeTableEntry *awaiter_handle_type = get_maybe_type(g, g->builtin_types.entry_promise); |
| 470 | TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false); | 470 | TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false); |
| 471 | const char *field_names[] = {AWAITER_HANDLE_FIELD_NAME, RESULT_FIELD_NAME, RESULT_PTR_FIELD_NAME}; | 471 | |
| 472 | TypeTableEntry *field_types[] = {awaiter_handle_type, return_type, result_ptr_type}; | 472 | ZigList<const char *> field_names = {}; |
| 473 | field_names.append(AWAITER_HANDLE_FIELD_NAME); | ||
| 474 | field_names.append(RESULT_FIELD_NAME); | ||
| 475 | field_names.append(RESULT_PTR_FIELD_NAME); | ||
| 476 | if (g->have_err_ret_tracing) { | ||
| 477 | field_names.append(ERR_RET_TRACE_PTR_FIELD_NAME); | ||
| 478 | } | ||
| 479 | |||
| 480 | ZigList<TypeTableEntry *> field_types = {}; | ||
| 481 | field_types.append(awaiter_handle_type); | ||
| 482 | field_types.append(return_type); | ||
| 483 | field_types.append(result_ptr_type); | ||
| 484 | if (g->have_err_ret_tracing) { | ||
| 485 | field_types.append(get_ptr_to_stack_trace_type(g)); | ||
| 486 | } | ||
| 487 | |||
| 488 | assert(field_names.length == field_types.length); | ||
| 473 | Buf *name = buf_sprintf("AsyncFramePromise(%s)", buf_ptr(&return_type->name)); | 489 | Buf *name = buf_sprintf("AsyncFramePromise(%s)", buf_ptr(&return_type->name)); |
| 474 | TypeTableEntry *entry = get_struct_type(g, buf_ptr(name), field_names, field_types, 3); | 490 | TypeTableEntry *entry = get_struct_type(g, buf_ptr(name), field_names.items, field_types.items, field_names.length); |
| 475 | 491 | ||
| 476 | return_type->promise_frame_parent = entry; | 492 | return_type->promise_frame_parent = entry; |
| 477 | return entry; | 493 | return entry; |
src/codegen.cpp+217-23| ... | @@ -1114,22 +1114,19 @@ static LLVMValueRef get_return_address_fn_val(CodeGen *g) { | ... | @@ -1114,22 +1114,19 @@ static LLVMValueRef get_return_address_fn_val(CodeGen *g) { |
| 1114 | return g->return_address_fn_val; | 1114 | return g->return_address_fn_val; |
| 1115 | } | 1115 | } |
| 1116 | 1116 | ||
| 1117 | static LLVMValueRef get_return_err_fn(CodeGen *g) { | 1117 | static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) { |
| 1118 | if (g->return_err_fn != nullptr) | 1118 | if (g->add_error_return_trace_addr_fn_val != nullptr) |
| 1119 | return g->return_err_fn; | 1119 | return g->add_error_return_trace_addr_fn_val; |
| 1120 | |||
| 1121 | assert(g->err_tag_type != nullptr); | ||
| 1122 | 1120 | ||
| 1123 | LLVMTypeRef arg_types[] = { | 1121 | LLVMTypeRef arg_types[] = { |
| 1124 | // error return trace pointer | ||
| 1125 | get_ptr_to_stack_trace_type(g)->type_ref, | 1122 | get_ptr_to_stack_trace_type(g)->type_ref, |
| 1123 | g->builtin_types.entry_usize->type_ref, | ||
| 1126 | }; | 1124 | }; |
| 1127 | LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 1, false); | 1125 | LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false); |
| 1128 | 1126 | ||
| 1129 | Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_return_error"), false); | 1127 | Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_add_err_ret_trace_addr"), false); |
| 1130 | LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref); | 1128 | LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref); |
| 1131 | addLLVMFnAttr(fn_val, "noinline"); // so that we can look at return address | 1129 | addLLVMFnAttr(fn_val, "alwaysinline"); |
| 1132 | addLLVMFnAttr(fn_val, "cold"); | ||
| 1133 | LLVMSetLinkage(fn_val, LLVMInternalLinkage); | 1130 | LLVMSetLinkage(fn_val, LLVMInternalLinkage); |
| 1134 | LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); | 1131 | LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); |
| 1135 | addLLVMFnAttr(fn_val, "nounwind"); | 1132 | addLLVMFnAttr(fn_val, "nounwind"); |
| ... | @@ -1151,6 +1148,8 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { | ... | @@ -1151,6 +1148,8 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { |
| 1151 | // stack_trace.instruction_addresses[stack_trace.index % stack_trace.instruction_addresses.len] = return_address; | 1148 | // stack_trace.instruction_addresses[stack_trace.index % stack_trace.instruction_addresses.len] = return_address; |
| 1152 | 1149 | ||
| 1153 | LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0); | 1150 | LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0); |
| 1151 | LLVMValueRef address_value = LLVMGetParam(fn_val, 1); | ||
| 1152 | |||
| 1154 | size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; | 1153 | size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; |
| 1155 | LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, ""); | 1154 | LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, ""); |
| 1156 | size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; | 1155 | size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; |
| ... | @@ -1172,15 +1171,10 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { | ... | @@ -1172,15 +1171,10 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { |
| 1172 | LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); | 1171 | LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); |
| 1173 | LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, ""); | 1172 | LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, ""); |
| 1174 | 1173 | ||
| 1175 | LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref); | ||
| 1176 | LLVMValueRef return_address_ptr = LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, ""); | ||
| 1177 | LLVMValueRef return_address = LLVMBuildPtrToInt(g->builder, return_address_ptr, usize_type_ref, ""); | ||
| 1178 | |||
| 1179 | LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, return_address, usize_type_ref, ""); | ||
| 1180 | gen_store_untyped(g, address_value, address_slot, 0, false); | 1174 | gen_store_untyped(g, address_value, address_slot, 0, false); |
| 1181 | 1175 | ||
| 1182 | // stack_trace.index += 1; | 1176 | // stack_trace.index += 1; |
| 1183 | LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), ""); | 1177 | LLVMValueRef index_plus_one_val = LLVMBuildNUWAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), ""); |
| 1184 | gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false); | 1178 | gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false); |
| 1185 | 1179 | ||
| 1186 | // return; | 1180 | // return; |
| ... | @@ -1189,6 +1183,187 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { | ... | @@ -1189,6 +1183,187 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { |
| 1189 | LLVMPositionBuilderAtEnd(g->builder, prev_block); | 1183 | LLVMPositionBuilderAtEnd(g->builder, prev_block); |
| 1190 | LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); | 1184 | LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); |
| 1191 | 1185 | ||
| 1186 | g->add_error_return_trace_addr_fn_val = fn_val; | ||
| 1187 | return fn_val; | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) { | ||
| 1191 | if (g->merge_err_ret_traces_fn_val) | ||
| 1192 | return g->merge_err_ret_traces_fn_val; | ||
| 1193 | |||
| 1194 | assert(g->stack_trace_type != nullptr); | ||
| 1195 | |||
| 1196 | LLVMTypeRef param_types[] = { | ||
| 1197 | get_ptr_to_stack_trace_type(g)->type_ref, | ||
| 1198 | get_ptr_to_stack_trace_type(g)->type_ref, | ||
| 1199 | }; | ||
| 1200 | LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), param_types, 2, false); | ||
| 1201 | |||
| 1202 | Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_merge_error_return_traces"), false); | ||
| 1203 | LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref); | ||
| 1204 | LLVMSetLinkage(fn_val, LLVMInternalLinkage); | ||
| 1205 | LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); | ||
| 1206 | addLLVMFnAttr(fn_val, "nounwind"); | ||
| 1207 | add_uwtable_attr(g, fn_val); | ||
| 1208 | addLLVMArgAttr(fn_val, (unsigned)0, "nonnull"); | ||
| 1209 | addLLVMArgAttr(fn_val, (unsigned)0, "noalias"); | ||
| 1210 | addLLVMArgAttr(fn_val, (unsigned)0, "writeonly"); | ||
| 1211 | addLLVMArgAttr(fn_val, (unsigned)1, "nonnull"); | ||
| 1212 | addLLVMArgAttr(fn_val, (unsigned)1, "noalias"); | ||
| 1213 | addLLVMArgAttr(fn_val, (unsigned)1, "readonly"); | ||
| 1214 | if (g->build_mode == BuildModeDebug) { | ||
| 1215 | ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true"); | ||
| 1216 | ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr); | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | // this is above the ZigLLVMClearCurrentDebugLocation | ||
| 1220 | LLVMValueRef add_error_return_trace_addr_fn_val = get_add_error_return_trace_addr_fn(g); | ||
| 1221 | |||
| 1222 | LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); | ||
| 1223 | LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); | ||
| 1224 | LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); | ||
| 1225 | LLVMPositionBuilderAtEnd(g->builder, entry_block); | ||
| 1226 | ZigLLVMClearCurrentDebugLocation(g->builder); | ||
| 1227 | |||
| 1228 | // var frame_index: usize = undefined; | ||
| 1229 | // var frames_left: usize = undefined; | ||
| 1230 | // if (src_stack_trace.index < src_stack_trace.instruction_addresses.len) { | ||
| 1231 | // frame_index = 0; | ||
| 1232 | // frames_left = src_stack_trace.index; | ||
| 1233 | // if (frames_left == 0) return; | ||
| 1234 | // } else { | ||
| 1235 | // frame_index = (src_stack_trace.index + 1) % src_stack_trace.instruction_addresses.len; | ||
| 1236 | // frames_left = src_stack_trace.instruction_addresses.len; | ||
| 1237 | // } | ||
| 1238 | // while (true) { | ||
| 1239 | // __zig_add_err_ret_trace_addr(dest_stack_trace, src_stack_trace.instruction_addresses[frame_index]); | ||
| 1240 | // frames_left -= 1; | ||
| 1241 | // if (frames_left == 0) return; | ||
| 1242 | // frame_index = (frame_index + 1) % src_stack_trace.instruction_addresses.len; | ||
| 1243 | // } | ||
| 1244 | LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(fn_val, "Return"); | ||
| 1245 | |||
| 1246 | LLVMValueRef frame_index_ptr = LLVMBuildAlloca(g->builder, g->builtin_types.entry_usize->type_ref, "frame_index"); | ||
| 1247 | LLVMValueRef frames_left_ptr = LLVMBuildAlloca(g->builder, g->builtin_types.entry_usize->type_ref, "frames_left"); | ||
| 1248 | |||
| 1249 | LLVMValueRef dest_stack_trace_ptr = LLVMGetParam(fn_val, 0); | ||
| 1250 | LLVMValueRef src_stack_trace_ptr = LLVMGetParam(fn_val, 1); | ||
| 1251 | |||
| 1252 | size_t src_index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; | ||
| 1253 | size_t src_addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index; | ||
| 1254 | LLVMValueRef src_index_field_ptr = LLVMBuildStructGEP(g->builder, src_stack_trace_ptr, | ||
| 1255 | (unsigned)src_index_field_index, ""); | ||
| 1256 | LLVMValueRef src_addresses_field_ptr = LLVMBuildStructGEP(g->builder, src_stack_trace_ptr, | ||
| 1257 | (unsigned)src_addresses_field_index, ""); | ||
| 1258 | TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry; | ||
| 1259 | size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; | ||
| 1260 | LLVMValueRef src_ptr_field_ptr = LLVMBuildStructGEP(g->builder, src_addresses_field_ptr, (unsigned)ptr_field_index, ""); | ||
| 1261 | size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index; | ||
| 1262 | LLVMValueRef src_len_field_ptr = LLVMBuildStructGEP(g->builder, src_addresses_field_ptr, (unsigned)len_field_index, ""); | ||
| 1263 | LLVMValueRef src_index_val = LLVMBuildLoad(g->builder, src_index_field_ptr, ""); | ||
| 1264 | LLVMValueRef src_ptr_val = LLVMBuildLoad(g->builder, src_ptr_field_ptr, ""); | ||
| 1265 | LLVMValueRef src_len_val = LLVMBuildLoad(g->builder, src_len_field_ptr, ""); | ||
| 1266 | LLVMValueRef no_wrap_bit = LLVMBuildICmp(g->builder, LLVMIntULT, src_index_val, src_len_val, ""); | ||
| 1267 | LLVMBasicBlockRef no_wrap_block = LLVMAppendBasicBlock(fn_val, "NoWrap"); | ||
| 1268 | LLVMBasicBlockRef yes_wrap_block = LLVMAppendBasicBlock(fn_val, "YesWrap"); | ||
| 1269 | LLVMBasicBlockRef loop_block = LLVMAppendBasicBlock(fn_val, "Loop"); | ||
| 1270 | LLVMBuildCondBr(g->builder, no_wrap_bit, no_wrap_block, yes_wrap_block); | ||
| 1271 | |||
| 1272 | LLVMPositionBuilderAtEnd(g->builder, no_wrap_block); | ||
| 1273 | LLVMValueRef usize_zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref); | ||
| 1274 | LLVMBuildStore(g->builder, usize_zero, frame_index_ptr); | ||
| 1275 | LLVMBuildStore(g->builder, src_index_val, frames_left_ptr); | ||
| 1276 | LLVMValueRef frames_left_eq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, src_index_val, usize_zero, ""); | ||
| 1277 | LLVMBuildCondBr(g->builder, frames_left_eq_zero_bit, return_block, loop_block); | ||
| 1278 | |||
| 1279 | LLVMPositionBuilderAtEnd(g->builder, yes_wrap_block); | ||
| 1280 | LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->type_ref, 1, false); | ||
| 1281 | LLVMValueRef plus_one = LLVMBuildNUWAdd(g->builder, src_index_val, usize_one, ""); | ||
| 1282 | LLVMValueRef mod_len = LLVMBuildURem(g->builder, plus_one, src_len_val, ""); | ||
| 1283 | LLVMBuildStore(g->builder, mod_len, frame_index_ptr); | ||
| 1284 | LLVMBuildStore(g->builder, src_len_val, frames_left_ptr); | ||
| 1285 | LLVMBuildBr(g->builder, loop_block); | ||
| 1286 | |||
| 1287 | LLVMPositionBuilderAtEnd(g->builder, loop_block); | ||
| 1288 | LLVMValueRef ptr_index = LLVMBuildLoad(g->builder, frame_index_ptr, ""); | ||
| 1289 | LLVMValueRef addr_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr_val, &ptr_index, 1, ""); | ||
| 1290 | LLVMValueRef this_addr_val = LLVMBuildLoad(g->builder, addr_ptr, ""); | ||
| 1291 | LLVMValueRef args[] = {dest_stack_trace_ptr, this_addr_val}; | ||
| 1292 | LLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, ""); | ||
| 1293 | LLVMValueRef prev_frames_left = LLVMBuildLoad(g->builder, frames_left_ptr, ""); | ||
| 1294 | LLVMValueRef new_frames_left = LLVMBuildNUWSub(g->builder, prev_frames_left, usize_one, ""); | ||
| 1295 | LLVMValueRef done_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, new_frames_left, usize_zero, ""); | ||
| 1296 | LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(fn_val, "Continue"); | ||
| 1297 | LLVMBuildCondBr(g->builder, done_bit, return_block, continue_block); | ||
| 1298 | |||
| 1299 | LLVMPositionBuilderAtEnd(g->builder, return_block); | ||
| 1300 | LLVMBuildRetVoid(g->builder); | ||
| 1301 | |||
| 1302 | LLVMPositionBuilderAtEnd(g->builder, continue_block); | ||
| 1303 | LLVMBuildStore(g->builder, new_frames_left, frames_left_ptr); | ||
| 1304 | LLVMValueRef prev_index = LLVMBuildLoad(g->builder, frame_index_ptr, ""); | ||
| 1305 | LLVMValueRef index_plus_one = LLVMBuildNUWAdd(g->builder, prev_index, usize_one, ""); | ||
| 1306 | LLVMValueRef index_mod_len = LLVMBuildURem(g->builder, index_plus_one, src_len_val, ""); | ||
| 1307 | LLVMBuildStore(g->builder, index_mod_len, frame_index_ptr); | ||
| 1308 | LLVMBuildBr(g->builder, loop_block); | ||
| 1309 | |||
| 1310 | LLVMPositionBuilderAtEnd(g->builder, prev_block); | ||
| 1311 | LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); | ||
| 1312 | |||
| 1313 | g->merge_err_ret_traces_fn_val = fn_val; | ||
| 1314 | return fn_val; | ||
| 1315 | |||
| 1316 | } | ||
| 1317 | |||
| 1318 | static LLVMValueRef get_return_err_fn(CodeGen *g) { | ||
| 1319 | if (g->return_err_fn != nullptr) | ||
| 1320 | return g->return_err_fn; | ||
| 1321 | |||
| 1322 | assert(g->err_tag_type != nullptr); | ||
| 1323 | |||
| 1324 | LLVMTypeRef arg_types[] = { | ||
| 1325 | // error return trace pointer | ||
| 1326 | get_ptr_to_stack_trace_type(g)->type_ref, | ||
| 1327 | }; | ||
| 1328 | LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 1, false); | ||
| 1329 | |||
| 1330 | Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_return_error"), false); | ||
| 1331 | LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref); | ||
| 1332 | addLLVMFnAttr(fn_val, "noinline"); // so that we can look at return address | ||
| 1333 | addLLVMFnAttr(fn_val, "cold"); | ||
| 1334 | LLVMSetLinkage(fn_val, LLVMInternalLinkage); | ||
| 1335 | LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); | ||
| 1336 | addLLVMFnAttr(fn_val, "nounwind"); | ||
| 1337 | add_uwtable_attr(g, fn_val); | ||
| 1338 | addLLVMArgAttr(fn_val, (unsigned)0, "nonnull"); | ||
| 1339 | if (g->build_mode == BuildModeDebug) { | ||
| 1340 | ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true"); | ||
| 1341 | ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr); | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | // this is above the ZigLLVMClearCurrentDebugLocation | ||
| 1345 | LLVMValueRef add_error_return_trace_addr_fn_val = get_add_error_return_trace_addr_fn(g); | ||
| 1346 | |||
| 1347 | LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); | ||
| 1348 | LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); | ||
| 1349 | LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); | ||
| 1350 | LLVMPositionBuilderAtEnd(g->builder, entry_block); | ||
| 1351 | ZigLLVMClearCurrentDebugLocation(g->builder); | ||
| 1352 | |||
| 1353 | LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0); | ||
| 1354 | |||
| 1355 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref; | ||
| 1356 | LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref); | ||
| 1357 | LLVMValueRef return_address_ptr = LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, ""); | ||
| 1358 | LLVMValueRef return_address = LLVMBuildPtrToInt(g->builder, return_address_ptr, usize_type_ref, ""); | ||
| 1359 | |||
| 1360 | LLVMValueRef args[] = { err_ret_trace_ptr, return_address }; | ||
| 1361 | LLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, ""); | ||
| 1362 | LLVMBuildRetVoid(g->builder); | ||
| 1363 | |||
| 1364 | LLVMPositionBuilderAtEnd(g->builder, prev_block); | ||
| 1365 | LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); | ||
| 1366 | |||
| 1192 | g->return_err_fn = fn_val; | 1367 | g->return_err_fn = fn_val; |
| 1193 | return fn_val; | 1368 | return fn_val; |
| 1194 | } | 1369 | } |
| ... | @@ -1641,7 +1816,6 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut | ... | @@ -1641,7 +1816,6 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut |
| 1641 | }; | 1816 | }; |
| 1642 | LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1, | 1817 | LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1, |
| 1643 | get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); | 1818 | get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); |
| 1644 | LLVMSetTailCall(call_instruction, true); | ||
| 1645 | return call_instruction; | 1819 | return call_instruction; |
| 1646 | } | 1820 | } |
| 1647 | 1821 | ||
| ... | @@ -4204,6 +4378,22 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable, | ... | @@ -4204,6 +4378,22 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable, |
| 4204 | return LLVMBuildIntToPtr(g->builder, uncasted_result, operand_type->type_ref, ""); | 4378 | return LLVMBuildIntToPtr(g->builder, uncasted_result, operand_type->type_ref, ""); |
| 4205 | } | 4379 | } |
| 4206 | 4380 | ||
| 4381 | static LLVMValueRef ir_render_merge_err_ret_traces(CodeGen *g, IrExecutable *executable, | ||
| 4382 | IrInstructionMergeErrRetTraces *instruction) | ||
| 4383 | { | ||
| 4384 | assert(g->have_err_ret_tracing); | ||
| 4385 | |||
| 4386 | LLVMValueRef coro_promise_ptr = ir_llvm_value(g, instruction->coro_promise_ptr); | ||
| 4387 | TypeStructField *field = instruction->resolved_field; | ||
| 4388 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, coro_promise_ptr, field->gen_index, ""); | ||
| 4389 | LLVMValueRef src_trace_ptr = LLVMBuildLoad(g->builder, ptr_field_ptr, ""); | ||
| 4390 | LLVMValueRef dest_trace_ptr = get_cur_err_ret_trace_val(g, instruction->base.scope); | ||
| 4391 | |||
| 4392 | LLVMValueRef args[] = { dest_trace_ptr, src_trace_ptr }; | ||
| 4393 | LLVMBuildCall(g->builder, get_merge_err_ret_traces_fn_val(g), args, 2, ""); | ||
| 4394 | return nullptr; | ||
| 4395 | } | ||
| 4396 | |||
| 4207 | static void set_debug_location(CodeGen *g, IrInstruction *instruction) { | 4397 | static void set_debug_location(CodeGen *g, IrInstruction *instruction) { |
| 4208 | AstNode *source_node = instruction->source_node; | 4398 | AstNode *source_node = instruction->source_node; |
| 4209 | Scope *scope = instruction->scope; | 4399 | Scope *scope = instruction->scope; |
| ... | @@ -4421,6 +4611,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, | ... | @@ -4421,6 +4611,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 4421 | return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction); | 4611 | return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction); |
| 4422 | case IrInstructionIdSaveErrRetAddr: | 4612 | case IrInstructionIdSaveErrRetAddr: |
| 4423 | return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction); | 4613 | return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction); |
| 4614 | case IrInstructionIdMergeErrRetTraces: | ||
| 4615 | return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction); | ||
| 4424 | } | 4616 | } |
| 4425 | zig_unreachable(); | 4617 | zig_unreachable(); |
| 4426 | } | 4618 | } |
| ... | @@ -5313,12 +5505,12 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -5313,12 +5505,12 @@ static void do_code_gen(CodeGen *g) { |
| 5313 | bool is_async = fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; | 5505 | bool is_async = fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; |
| 5314 | bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn && | 5506 | bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn && |
| 5315 | (is_async || !have_err_ret_trace_arg); | 5507 | (is_async || !have_err_ret_trace_arg); |
| 5316 | if (have_err_ret_trace_stack) { | 5508 | bool have_exactly_one_err_ret_value = !have_err_ret_trace_stack && g->have_err_ret_tracing && is_async && |
| 5317 | // TODO call graph analysis to find out what this number needs to be for every function | 5509 | type_can_fail(fn_table_entry->type_entry->data.fn.fn_type_id.return_type); |
| 5318 | static const size_t stack_trace_ptr_count = 30; | 5510 | if (have_err_ret_trace_stack || have_exactly_one_err_ret_value) { |
| 5319 | |||
| 5320 | TypeTableEntry *usize = g->builtin_types.entry_usize; | 5511 | TypeTableEntry *usize = g->builtin_types.entry_usize; |
| 5321 | TypeTableEntry *array_type = get_array_type(g, usize, stack_trace_ptr_count); | 5512 | uint32_t ret_addr_count = have_exactly_one_err_ret_value ? 1 : stack_trace_ptr_count; |
| 5513 | TypeTableEntry *array_type = get_array_type(g, usize, ret_addr_count); | ||
| 5322 | LLVMValueRef err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses", | 5514 | LLVMValueRef err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses", |
| 5323 | get_abi_alignment(g, array_type)); | 5515 | get_abi_alignment(g, array_type)); |
| 5324 | g->cur_err_ret_trace_val_stack = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type)); | 5516 | g->cur_err_ret_trace_val_stack = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type)); |
| ... | @@ -5341,7 +5533,7 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -5341,7 +5533,7 @@ static void do_code_gen(CodeGen *g) { |
| 5341 | 5533 | ||
| 5342 | size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index; | 5534 | size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index; |
| 5343 | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, ""); | 5535 | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, ""); |
| 5344 | gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false)); | 5536 | gen_store(g, LLVMConstInt(usize->type_ref, ret_addr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false)); |
| 5345 | } else { | 5537 | } else { |
| 5346 | g->cur_err_ret_trace_val_stack = nullptr; | 5538 | g->cur_err_ret_trace_val_stack = nullptr; |
| 5347 | } | 5539 | } |
| ... | @@ -5943,6 +6135,8 @@ static void define_builtin_compile_vars(CodeGen *g) { | ... | @@ -5943,6 +6135,8 @@ static void define_builtin_compile_vars(CodeGen *g) { |
| 5943 | os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); | 6135 | os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); |
| 5944 | Buf *contents = buf_alloc(); | 6136 | Buf *contents = buf_alloc(); |
| 5945 | 6137 | ||
| 6138 | // Modifications to this struct must be coordinated with code that does anything with | ||
| 6139 | // g->stack_trace_type. There are hard-coded references to the field indexes. | ||
| 5946 | buf_append_str(contents, | 6140 | buf_append_str(contents, |
| 5947 | "pub const StackTrace = struct {\n" | 6141 | "pub const StackTrace = struct {\n" |
| 5948 | " index: usize,\n" | 6142 | " index: usize,\n" |
src/ir.cpp+97-49| ... | @@ -725,6 +725,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitRetur | ... | @@ -725,6 +725,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitRetur |
| 725 | return IrInstructionIdAddImplicitReturnType; | 725 | return IrInstructionIdAddImplicitReturnType; |
| 726 | } | 726 | } |
| 727 | 727 | ||
| 728 | static constexpr IrInstructionId ir_instruction_id(IrInstructionMergeErrRetTraces *) { | ||
| 729 | return IrInstructionIdMergeErrRetTraces; | ||
| 730 | } | ||
| 731 | |||
| 728 | template<typename T> | 732 | template<typename T> |
| 729 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { | 733 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 730 | T *special_instruction = allocate<T>(1); | 734 | T *special_instruction = allocate<T>(1); |
| ... | @@ -972,6 +976,12 @@ static IrInstruction *ir_build_const_promise_init(IrBuilder *irb, Scope *scope, | ... | @@ -972,6 +976,12 @@ static IrInstruction *ir_build_const_promise_init(IrBuilder *irb, Scope *scope, |
| 972 | const_instruction->base.value.data.x_struct.fields[1].special = ConstValSpecialUndef; | 976 | const_instruction->base.value.data.x_struct.fields[1].special = ConstValSpecialUndef; |
| 973 | const_instruction->base.value.data.x_struct.fields[2].type = struct_type->data.structure.fields[2].type_entry; | 977 | const_instruction->base.value.data.x_struct.fields[2].type = struct_type->data.structure.fields[2].type_entry; |
| 974 | const_instruction->base.value.data.x_struct.fields[2].special = ConstValSpecialUndef; | 978 | const_instruction->base.value.data.x_struct.fields[2].special = ConstValSpecialUndef; |
| 979 | if (irb->codegen->have_err_ret_tracing) { | ||
| 980 | assert(struct_type->data.structure.src_field_count == 4); | ||
| 981 | |||
| 982 | const_instruction->base.value.data.x_struct.fields[3].type = struct_type->data.structure.fields[3].type_entry; | ||
| 983 | const_instruction->base.value.data.x_struct.fields[3].special = ConstValSpecialUndef; | ||
| 984 | } | ||
| 975 | return &const_instruction->base; | 985 | return &const_instruction->base; |
| 976 | } | 986 | } |
| 977 | 987 | ||
| ... | @@ -2495,8 +2505,9 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s | ... | @@ -2495,8 +2505,9 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s |
| 2495 | return &instruction->base; | 2505 | return &instruction->base; |
| 2496 | } | 2506 | } |
| 2497 | 2507 | ||
| 2498 | static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node) { | 2508 | static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Nullable nullable) { |
| 2499 | IrInstructionErrorReturnTrace *instruction = ir_build_instruction<IrInstructionErrorReturnTrace>(irb, scope, source_node); | 2509 | IrInstructionErrorReturnTrace *instruction = ir_build_instruction<IrInstructionErrorReturnTrace>(irb, scope, source_node); |
| 2510 | instruction->nullable = nullable; | ||
| 2500 | 2511 | ||
| 2501 | return &instruction->base; | 2512 | return &instruction->base; |
| 2502 | } | 2513 | } |
| ... | @@ -2717,6 +2728,18 @@ static IrInstruction *ir_build_add_implicit_return_type(IrBuilder *irb, Scope *s | ... | @@ -2717,6 +2728,18 @@ static IrInstruction *ir_build_add_implicit_return_type(IrBuilder *irb, Scope *s |
| 2717 | return &instruction->base; | 2728 | return &instruction->base; |
| 2718 | } | 2729 | } |
| 2719 | 2730 | ||
| 2731 | static IrInstruction *ir_build_merge_err_ret_traces(IrBuilder *irb, Scope *scope, AstNode *source_node, | ||
| 2732 | IrInstruction *coro_promise_ptr, TypeStructField *resolved_field) | ||
| 2733 | { | ||
| 2734 | IrInstructionMergeErrRetTraces *instruction = ir_build_instruction<IrInstructionMergeErrRetTraces>(irb, scope, source_node); | ||
| 2735 | instruction->coro_promise_ptr = coro_promise_ptr; | ||
| 2736 | instruction->resolved_field = resolved_field; | ||
| 2737 | |||
| 2738 | ir_ref_instruction(coro_promise_ptr, irb->current_basic_block); | ||
| 2739 | |||
| 2740 | return &instruction->base; | ||
| 2741 | } | ||
| 2742 | |||
| 2720 | static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { | 2743 | static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { |
| 2721 | results[ReturnKindUnconditional] = 0; | 2744 | results[ReturnKindUnconditional] = 0; |
| 2722 | results[ReturnKindError] = 0; | 2745 | results[ReturnKindError] = 0; |
| ... | @@ -2822,34 +2845,6 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode | ... | @@ -2822,34 +2845,6 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode |
| 2822 | // the above blocks are rendered by ir_gen after the rest of codegen | 2845 | // the above blocks are rendered by ir_gen after the rest of codegen |
| 2823 | } | 2846 | } |
| 2824 | 2847 | ||
| 2825 | static bool exec_have_err_ret_trace(CodeGen *g, IrExecutable *exec) { | ||
| 2826 | if (!g->have_err_ret_tracing) | ||
| 2827 | return false; | ||
| 2828 | FnTableEntry *fn_entry = exec_fn_entry(exec); | ||
| 2829 | if (fn_entry == nullptr) | ||
| 2830 | return false; | ||
| 2831 | if (exec->is_inline) | ||
| 2832 | return false; | ||
| 2833 | return type_can_fail(fn_entry->type_entry->data.fn.fn_type_id.return_type); | ||
| 2834 | } | ||
| 2835 | |||
| 2836 | static void ir_gen_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *node) { | ||
| 2837 | if (!exec_have_err_ret_trace(irb->codegen, irb->exec)) | ||
| 2838 | return; | ||
| 2839 | |||
| 2840 | bool is_async = exec_is_async(irb->exec); | ||
| 2841 | |||
| 2842 | if (is_async) { | ||
| 2843 | //IrInstruction *err_ret_addr_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_err_ret_addr_ptr); | ||
| 2844 | //IrInstruction *return_address_ptr = ir_build_instr_addr(irb, scope, node); | ||
| 2845 | //IrInstruction *return_address_usize = ir_build_ptr_to_int(irb, scope, node, return_address_ptr); | ||
| 2846 | //ir_build_store_ptr(irb, scope, node, err_ret_addr_ptr, return_address_usize); | ||
| 2847 | return; | ||
| 2848 | } | ||
| 2849 | |||
| 2850 | ir_build_save_err_ret_addr(irb, scope, node); | ||
| 2851 | } | ||
| 2852 | |||
| 2853 | static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { | 2848 | static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { |
| 2854 | assert(node->type == NodeTypeReturnExpr); | 2849 | assert(node->type == NodeTypeReturnExpr); |
| 2855 | 2850 | ||
| ... | @@ -2895,8 +2890,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, | ... | @@ -2895,8 +2890,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, |
| 2895 | 2890 | ||
| 2896 | IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value); | 2891 | IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value); |
| 2897 | 2892 | ||
| 2893 | bool should_inline = ir_should_inline(irb->exec, scope); | ||
| 2898 | IrInstruction *is_comptime; | 2894 | IrInstruction *is_comptime; |
| 2899 | if (ir_should_inline(irb->exec, scope)) { | 2895 | if (should_inline) { |
| 2900 | is_comptime = ir_build_const_bool(irb, scope, node, true); | 2896 | is_comptime = ir_build_const_bool(irb, scope, node, true); |
| 2901 | } else { | 2897 | } else { |
| 2902 | is_comptime = ir_build_test_comptime(irb, scope, node, is_err); | 2898 | is_comptime = ir_build_test_comptime(irb, scope, node, is_err); |
| ... | @@ -2909,7 +2905,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, | ... | @@ -2909,7 +2905,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, |
| 2909 | if (have_err_defers) { | 2905 | if (have_err_defers) { |
| 2910 | ir_gen_defers_for_block(irb, scope, outer_scope, true); | 2906 | ir_gen_defers_for_block(irb, scope, outer_scope, true); |
| 2911 | } | 2907 | } |
| 2912 | ir_gen_save_err_ret_addr(irb, scope, node); | 2908 | if (irb->codegen->have_err_ret_tracing && !should_inline) { |
| 2909 | ir_build_save_err_ret_addr(irb, scope, node); | ||
| 2910 | } | ||
| 2913 | ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); | 2911 | ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); |
| 2914 | 2912 | ||
| 2915 | ir_set_cursor_at_end_and_append_block(irb, ok_block); | 2913 | ir_set_cursor_at_end_and_append_block(irb, ok_block); |
| ... | @@ -2938,7 +2936,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, | ... | @@ -2938,7 +2936,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, |
| 2938 | IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); | 2936 | IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); |
| 2939 | IrBasicBlock *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue"); | 2937 | IrBasicBlock *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue"); |
| 2940 | IrInstruction *is_comptime; | 2938 | IrInstruction *is_comptime; |
| 2941 | if (ir_should_inline(irb->exec, scope)) { | 2939 | bool should_inline = ir_should_inline(irb->exec, scope); |
| 2940 | if (should_inline) { | ||
| 2942 | is_comptime = ir_build_const_bool(irb, scope, node, true); | 2941 | is_comptime = ir_build_const_bool(irb, scope, node, true); |
| 2943 | } else { | 2942 | } else { |
| 2944 | is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val); | 2943 | is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val); |
| ... | @@ -2948,7 +2947,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, | ... | @@ -2948,7 +2947,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, |
| 2948 | ir_set_cursor_at_end_and_append_block(irb, return_block); | 2947 | ir_set_cursor_at_end_and_append_block(irb, return_block); |
| 2949 | ir_gen_defers_for_block(irb, scope, outer_scope, true); | 2948 | ir_gen_defers_for_block(irb, scope, outer_scope, true); |
| 2950 | IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr); | 2949 | IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr); |
| 2951 | ir_gen_save_err_ret_addr(irb, scope, node); | 2950 | if (irb->codegen->have_err_ret_tracing && !should_inline) { |
| 2951 | ir_build_save_err_ret_addr(irb, scope, node); | ||
| 2952 | } | ||
| 2952 | ir_gen_async_return(irb, scope, node, err_val, false); | 2953 | ir_gen_async_return(irb, scope, node, err_val, false); |
| 2953 | 2954 | ||
| 2954 | ir_set_cursor_at_end_and_append_block(irb, continue_block); | 2955 | ir_set_cursor_at_end_and_append_block(irb, continue_block); |
| ... | @@ -4242,7 +4243,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo | ... | @@ -4242,7 +4243,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4242 | } | 4243 | } |
| 4243 | case BuiltinFnIdErrorReturnTrace: | 4244 | case BuiltinFnIdErrorReturnTrace: |
| 4244 | { | 4245 | { |
| 4245 | return ir_build_error_return_trace(irb, scope, node); | 4246 | return ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null); |
| 4246 | } | 4247 | } |
| 4247 | case BuiltinFnIdAtomicRmw: | 4248 | case BuiltinFnIdAtomicRmw: |
| 4248 | { | 4249 | { |
| ... | @@ -6148,10 +6149,13 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast | ... | @@ -6148,10 +6149,13 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast |
| 6148 | IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_await_handle); | 6149 | IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_await_handle); |
| 6149 | IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend"); | 6150 | IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend"); |
| 6150 | IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, parent_scope, "NoSuspend"); | 6151 | IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, parent_scope, "NoSuspend"); |
| 6151 | IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "Merge"); | 6152 | IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "MergeSuspend"); |
| 6152 | ir_build_cond_br(irb, parent_scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); | 6153 | ir_build_cond_br(irb, parent_scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); |
| 6153 | 6154 | ||
| 6154 | ir_set_cursor_at_end_and_append_block(irb, no_suspend_block); | 6155 | ir_set_cursor_at_end_and_append_block(irb, no_suspend_block); |
| 6156 | if (irb->codegen->have_err_ret_tracing) { | ||
| 6157 | ir_build_merge_err_ret_traces(irb, parent_scope, node, coro_promise_ptr, nullptr); | ||
| 6158 | } | ||
| 6155 | Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); | 6159 | Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); |
| 6156 | IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name); | 6160 | IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name); |
| 6157 | IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr); | 6161 | IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr); |
| ... | @@ -6460,13 +6464,19 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec | ... | @@ -6460,13 +6464,19 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec |
| 6460 | irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); | 6464 | irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); |
| 6461 | 6465 | ||
| 6462 | Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME); | 6466 | Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME); |
| 6463 | irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr, | 6467 | irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, |
| 6464 | awaiter_handle_field_name); | 6468 | awaiter_handle_field_name); |
| 6465 | Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); | 6469 | Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); |
| 6466 | irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr, result_field_name); | 6470 | irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); |
| 6467 | result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); | 6471 | result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); |
| 6468 | irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr, result_ptr_field_name); | 6472 | irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name); |
| 6469 | ir_build_store_ptr(irb, coro_scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr); | 6473 | ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr); |
| 6474 | if (irb->codegen->have_err_ret_tracing) { | ||
| 6475 | IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); | ||
| 6476 | Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); | ||
| 6477 | IrInstruction *coro_err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); | ||
| 6478 | ir_build_store_ptr(irb, scope, node, coro_err_ret_trace_ptr_field_ptr, err_ret_trace_ptr); | ||
| 6479 | } | ||
| 6470 | 6480 | ||
| 6471 | 6481 | ||
| 6472 | irb->exec->coro_early_final = ir_create_basic_block(irb, scope, "CoroEarlyFinal"); | 6482 | irb->exec->coro_early_final = ir_create_basic_block(irb, scope, "CoroEarlyFinal"); |
| ... | @@ -11579,18 +11589,25 @@ static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) { | ... | @@ -11579,18 +11589,25 @@ static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) { |
| 11579 | static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, | 11589 | static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, |
| 11580 | IrInstructionErrorReturnTrace *instruction) | 11590 | IrInstructionErrorReturnTrace *instruction) |
| 11581 | { | 11591 | { |
| 11582 | TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen); | 11592 | if (instruction->nullable == IrInstructionErrorReturnTrace::Null) { |
| 11583 | TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); | 11593 | TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen); |
| 11584 | if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) { | 11594 | TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); |
| 11585 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | 11595 | if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) { |
| 11586 | out_val->data.x_maybe = nullptr; | 11596 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); |
| 11597 | out_val->data.x_maybe = nullptr; | ||
| 11598 | return nullable_type; | ||
| 11599 | } | ||
| 11600 | IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, | ||
| 11601 | instruction->base.source_node, instruction->nullable); | ||
| 11602 | ir_link_new_instruction(new_instruction, &instruction->base); | ||
| 11587 | return nullable_type; | 11603 | return nullable_type; |
| 11604 | } else { | ||
| 11605 | assert(ira->codegen->have_err_ret_tracing); | ||
| 11606 | IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, | ||
| 11607 | instruction->base.source_node, instruction->nullable); | ||
| 11608 | ir_link_new_instruction(new_instruction, &instruction->base); | ||
| 11609 | return get_ptr_to_stack_trace_type(ira->codegen); | ||
| 11588 | } | 11610 | } |
| 11589 | |||
| 11590 | IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, | ||
| 11591 | instruction->base.source_node); | ||
| 11592 | ir_link_new_instruction(new_instruction, &instruction->base); | ||
| 11593 | return nullable_type; | ||
| 11594 | } | 11611 | } |
| 11595 | 11612 | ||
| 11596 | static TypeTableEntry *ir_analyze_instruction_error_union(IrAnalyze *ira, | 11613 | static TypeTableEntry *ir_analyze_instruction_error_union(IrAnalyze *ira, |
| ... | @@ -17904,6 +17921,34 @@ static TypeTableEntry *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira, | ... | @@ -17904,6 +17921,34 @@ static TypeTableEntry *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira, |
| 17904 | return out_val->type; | 17921 | return out_val->type; |
| 17905 | } | 17922 | } |
| 17906 | 17923 | ||
| 17924 | static TypeTableEntry *ir_analyze_instruction_merge_err_ret_traces(IrAnalyze *ira, | ||
| 17925 | IrInstructionMergeErrRetTraces *instruction) | ||
| 17926 | { | ||
| 17927 | IrInstruction *coro_promise_ptr = instruction->coro_promise_ptr->other; | ||
| 17928 | if (type_is_invalid(coro_promise_ptr->value.type)) | ||
| 17929 | return ira->codegen->builtin_types.entry_invalid; | ||
| 17930 | |||
| 17931 | assert(coro_promise_ptr->value.type->id == TypeTableEntryIdPointer); | ||
| 17932 | TypeTableEntry *promise_frame_type = coro_promise_ptr->value.type->data.pointer.child_type; | ||
| 17933 | assert(promise_frame_type->id == TypeTableEntryIdStruct); | ||
| 17934 | TypeTableEntry *promise_result_type = promise_frame_type->data.structure.fields[1].type_entry; | ||
| 17935 | |||
| 17936 | if (!type_can_fail(promise_result_type)) { | ||
| 17937 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | ||
| 17938 | out_val->type = ira->codegen->builtin_types.entry_void; | ||
| 17939 | return out_val->type; | ||
| 17940 | } | ||
| 17941 | |||
| 17942 | TypeStructField *field = find_struct_type_field(promise_frame_type, buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME)); | ||
| 17943 | assert(field != nullptr); | ||
| 17944 | |||
| 17945 | IrInstruction *result = ir_build_merge_err_ret_traces(&ira->new_irb, instruction->base.scope, | ||
| 17946 | instruction->base.source_node, coro_promise_ptr, field); | ||
| 17947 | ir_link_new_instruction(result, &instruction->base); | ||
| 17948 | result->value.type = ira->codegen->builtin_types.entry_void; | ||
| 17949 | return result->value.type; | ||
| 17950 | } | ||
| 17951 | |||
| 17907 | static TypeTableEntry *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) { | 17952 | static TypeTableEntry *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) { |
| 17908 | IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope, | 17953 | IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope, |
| 17909 | instruction->base.source_node); | 17954 | instruction->base.source_node); |
| ... | @@ -18155,6 +18200,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi | ... | @@ -18155,6 +18200,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 18155 | return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction); | 18200 | return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction); |
| 18156 | case IrInstructionIdAddImplicitReturnType: | 18201 | case IrInstructionIdAddImplicitReturnType: |
| 18157 | return ir_analyze_instruction_add_implicit_return_type(ira, (IrInstructionAddImplicitReturnType *)instruction); | 18202 | return ir_analyze_instruction_add_implicit_return_type(ira, (IrInstructionAddImplicitReturnType *)instruction); |
| 18203 | case IrInstructionIdMergeErrRetTraces: | ||
| 18204 | return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction); | ||
| 18158 | } | 18205 | } |
| 18159 | zig_unreachable(); | 18206 | zig_unreachable(); |
| 18160 | } | 18207 | } |
| ... | @@ -18282,6 +18329,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { | ... | @@ -18282,6 +18329,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 18282 | case IrInstructionIdAwaitBookkeeping: | 18329 | case IrInstructionIdAwaitBookkeeping: |
| 18283 | case IrInstructionIdSaveErrRetAddr: | 18330 | case IrInstructionIdSaveErrRetAddr: |
| 18284 | case IrInstructionIdAddImplicitReturnType: | 18331 | case IrInstructionIdAddImplicitReturnType: |
| 18332 | case IrInstructionIdMergeErrRetTraces: | ||
| 18285 | return true; | 18333 | return true; |
| 18286 | 18334 | ||
| 18287 | case IrInstructionIdPhi: | 18335 | case IrInstructionIdPhi: |
src/ir_print.cpp+23-1| ... | @@ -1024,7 +1024,16 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) { | ... | @@ -1024,7 +1024,16 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) { |
| 1024 | } | 1024 | } |
| 1025 | 1025 | ||
| 1026 | static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) { | 1026 | static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) { |
| 1027 | fprintf(irp->f, "@errorReturnTrace()"); | 1027 | fprintf(irp->f, "@errorReturnTrace("); |
| 1028 | switch (instruction->nullable) { | ||
| 1029 | case IrInstructionErrorReturnTrace::Null: | ||
| 1030 | fprintf(irp->f, "Null"); | ||
| 1031 | break; | ||
| 1032 | case IrInstructionErrorReturnTrace::NonNull: | ||
| 1033 | fprintf(irp->f, "NonNull"); | ||
| 1034 | break; | ||
| 1035 | } | ||
| 1036 | fprintf(irp->f, ")"); | ||
| 1028 | } | 1037 | } |
| 1029 | 1038 | ||
| 1030 | static void ir_print_error_union(IrPrint *irp, IrInstructionErrorUnion *instruction) { | 1039 | static void ir_print_error_union(IrPrint *irp, IrInstructionErrorUnion *instruction) { |
| ... | @@ -1179,6 +1188,16 @@ static void ir_print_add_implicit_return_type(IrPrint *irp, IrInstructionAddImpl | ... | @@ -1179,6 +1188,16 @@ static void ir_print_add_implicit_return_type(IrPrint *irp, IrInstructionAddImpl |
| 1179 | fprintf(irp->f, ")"); | 1188 | fprintf(irp->f, ")"); |
| 1180 | } | 1189 | } |
| 1181 | 1190 | ||
| 1191 | static void ir_print_merge_err_ret_traces(IrPrint *irp, IrInstructionMergeErrRetTraces *instruction) { | ||
| 1192 | fprintf(irp->f, "@mergeErrRetTraces("); | ||
| 1193 | ir_print_other_instruction(irp, instruction->coro_promise_ptr); | ||
| 1194 | fprintf(irp->f, ","); | ||
| 1195 | if (instruction->resolved_field != nullptr) { | ||
| 1196 | fprintf(irp->f, "field '%s'", buf_ptr(instruction->resolved_field->name)); | ||
| 1197 | } | ||
| 1198 | fprintf(irp->f, ")"); | ||
| 1199 | } | ||
| 1200 | |||
| 1182 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | 1201 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1183 | ir_print_prefix(irp, instruction); | 1202 | ir_print_prefix(irp, instruction); |
| 1184 | switch (instruction->id) { | 1203 | switch (instruction->id) { |
| ... | @@ -1559,6 +1578,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | ... | @@ -1559,6 +1578,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1559 | case IrInstructionIdAddImplicitReturnType: | 1578 | case IrInstructionIdAddImplicitReturnType: |
| 1560 | ir_print_add_implicit_return_type(irp, (IrInstructionAddImplicitReturnType *)instruction); | 1579 | ir_print_add_implicit_return_type(irp, (IrInstructionAddImplicitReturnType *)instruction); |
| 1561 | break; | 1580 | break; |
| 1581 | case IrInstructionIdMergeErrRetTraces: | ||
| 1582 | ir_print_merge_err_ret_traces(irp, (IrInstructionMergeErrRetTraces *)instruction); | ||
| 1583 | break; | ||
| 1562 | } | 1584 | } |
| 1563 | fprintf(irp->f, "\n"); | 1585 | fprintf(irp->f, "\n"); |
| 1564 | } | 1586 | } |