| ... | ... | @@ -330,6 +330,46 @@ static LLVMValueRef get_handle_value(CodeGen *g, AstNode *source_node, LLVMValue |
| 330 | 330 | } |
| 331 | 331 | } |
| 332 | 332 | |
| 333 | static bool want_debug_safety(CodeGen *g, AstNode *node) { |
| 334 | return !g->is_release_build && !node->block_context->safety_off; |
| 335 | } |
| 336 | |
| 337 | static void add_bounds_check(CodeGen *g, AstNode *source_node, LLVMValueRef target_val, |
| 338 | LLVMIntPredicate lower_pred, LLVMValueRef lower_value, |
| 339 | LLVMIntPredicate upper_pred, LLVMValueRef upper_value) |
| 340 | { |
| 341 | if (!lower_value && !upper_value) { |
| 342 | return; |
| 343 | } |
| 344 | if (upper_value && !lower_value) { |
| 345 | lower_value = upper_value; |
| 346 | lower_pred = upper_pred; |
| 347 | upper_value = nullptr; |
| 348 | } |
| 349 | |
| 350 | add_debug_source_node(g, source_node); |
| 351 | |
| 352 | LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckFail"); |
| 353 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckOk"); |
| 354 | LLVMBasicBlockRef lower_ok_block = upper_value ? |
| 355 | LLVMAppendBasicBlock(g->cur_fn->fn_value, "FirstBoundsCheckOk") : ok_block; |
| 356 | |
| 357 | LLVMValueRef lower_ok_val = LLVMBuildICmp(g->builder, lower_pred, target_val, lower_value, ""); |
| 358 | LLVMBuildCondBr(g->builder, lower_ok_val, lower_ok_block, bounds_check_fail_block); |
| 359 | |
| 360 | LLVMPositionBuilderAtEnd(g->builder, bounds_check_fail_block); |
| 361 | LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); |
| 362 | LLVMBuildUnreachable(g->builder); |
| 363 | |
| 364 | if (upper_value) { |
| 365 | LLVMPositionBuilderAtEnd(g->builder, lower_ok_block); |
| 366 | LLVMValueRef upper_ok_val = LLVMBuildICmp(g->builder, upper_pred, target_val, upper_value, ""); |
| 367 | LLVMBuildCondBr(g->builder, upper_ok_val, ok_block, bounds_check_fail_block); |
| 368 | } |
| 369 | |
| 370 | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 371 | } |
| 372 | |
| 333 | 373 | static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) { |
| 334 | 374 | assert(node->type == NodeTypeFnCallExpr); |
| 335 | 375 | assert(g->generate_error_name_table); |
| ... | ... | @@ -344,25 +384,10 @@ static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) { |
| 344 | 384 | LLVMValueRef err_val = gen_expr(g, err_val_node); |
| 345 | 385 | add_debug_source_node(g, node); |
| 346 | 386 | |
| 347 | | if (!g->is_release_build) { |
| 348 | | LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckFail"); |
| 349 | | LLVMBasicBlockRef lower_ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "LowerBoundsCheckOk"); |
| 350 | | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckOk"); |
| 351 | | |
| 387 | if (want_debug_safety(g, node)) { |
| 352 | 388 | LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val)); |
| 353 | | LLVMValueRef is_zero_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, ""); |
| 354 | | LLVMBuildCondBr(g->builder, is_zero_val, bounds_check_fail_block, lower_ok_block); |
| 355 | | |
| 356 | | LLVMPositionBuilderAtEnd(g->builder, bounds_check_fail_block); |
| 357 | | LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); |
| 358 | | LLVMBuildUnreachable(g->builder); |
| 359 | | |
| 360 | | LLVMPositionBuilderAtEnd(g->builder, lower_ok_block); |
| 361 | 389 | LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->error_decls.length, false); |
| 362 | | LLVMValueRef is_too_big_val = LLVMBuildICmp(g->builder, LLVMIntUGE, err_val, end_val, ""); |
| 363 | | LLVMBuildCondBr(g->builder, is_too_big_val, bounds_check_fail_block, ok_block); |
| 364 | | |
| 365 | | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 390 | add_bounds_check(g, node, err_val, LLVMIntNE, zero, LLVMIntULT, end_val); |
| 366 | 391 | } |
| 367 | 392 | |
| 368 | 393 | LLVMValueRef indices[] = { |
| ... | ... | @@ -869,6 +894,11 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal |
| 869 | 894 | } |
| 870 | 895 | |
| 871 | 896 | if (array_type->id == TypeTableEntryIdArray) { |
| 897 | if (want_debug_safety(g, source_node)) { |
| 898 | LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_isize->type_ref, |
| 899 | array_type->data.array.len, false); |
| 900 | add_bounds_check(g, source_node, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end); |
| 901 | } |
| 872 | 902 | LLVMValueRef indices[] = { |
| 873 | 903 | LLVMConstNull(g->builtin_types.entry_isize->type_ref), |
| 874 | 904 | subscript_value |
| ... | ... | @@ -887,6 +917,15 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal |
| 887 | 917 | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); |
| 888 | 918 | assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); |
| 889 | 919 | |
| 920 | if (want_debug_safety(g, source_node)) { |
| 921 | add_debug_source_node(g, source_node); |
| 922 | int len_index = array_type->data.structure.fields[1].gen_index; |
| 923 | assert(len_index >= 0); |
| 924 | LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, ""); |
| 925 | LLVMValueRef len = LLVMBuildLoad(g->builder, len_ptr, ""); |
| 926 | add_bounds_check(g, source_node, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len); |
| 927 | } |
| 928 | |
| 890 | 929 | add_debug_source_node(g, source_node); |
| 891 | 930 | int ptr_index = array_type->data.structure.fields[0].gen_index; |
| 892 | 931 | assert(ptr_index >= 0); |
| ... | ... | @@ -907,7 +946,6 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { |
| 907 | 946 | LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node); |
| 908 | 947 | |
| 909 | 948 | LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript); |
| 910 | | |
| 911 | 949 | return gen_array_elem_ptr(g, node, array_ptr, array_type, subscript_value); |
| 912 | 950 | } |
| 913 | 951 | |
| ... | ... | @@ -969,6 +1007,15 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) { |
| 969 | 1007 | end_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref, array_type->data.array.len, false); |
| 970 | 1008 | } |
| 971 | 1009 | |
| 1010 | if (want_debug_safety(g, node)) { |
| 1011 | add_bounds_check(g, node, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); |
| 1012 | if (node->data.slice_expr.end) { |
| 1013 | LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_isize->type_ref, |
| 1014 | array_type->data.array.len, false); |
| 1015 | add_bounds_check(g, node, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); |
| 1016 | } |
| 1017 | } |
| 1018 | |
| 972 | 1019 | add_debug_source_node(g, node); |
| 973 | 1020 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, ""); |
| 974 | 1021 | LLVMValueRef indices[] = { |
| ... | ... | @@ -987,6 +1034,10 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) { |
| 987 | 1034 | LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start); |
| 988 | 1035 | LLVMValueRef end_val = gen_expr(g, node->data.slice_expr.end); |
| 989 | 1036 | |
| 1037 | if (want_debug_safety(g, node)) { |
| 1038 | add_bounds_check(g, node, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); |
| 1039 | } |
| 1040 | |
| 990 | 1041 | add_debug_source_node(g, node); |
| 991 | 1042 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, ""); |
| 992 | 1043 | LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); |
| ... | ... | @@ -1002,22 +1053,33 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) { |
| 1002 | 1053 | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); |
| 1003 | 1054 | assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); |
| 1004 | 1055 | |
| 1056 | int ptr_index = array_type->data.structure.fields[0].gen_index; |
| 1057 | assert(ptr_index >= 0); |
| 1058 | int len_index = array_type->data.structure.fields[1].gen_index; |
| 1059 | assert(len_index >= 0); |
| 1060 | |
| 1061 | LLVMValueRef prev_end = nullptr; |
| 1062 | if (!node->data.slice_expr.end || want_debug_safety(g, node)) { |
| 1063 | add_debug_source_node(g, node); |
| 1064 | LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, ""); |
| 1065 | prev_end = LLVMBuildLoad(g->builder, src_len_ptr, ""); |
| 1066 | } |
| 1067 | |
| 1005 | 1068 | LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start); |
| 1006 | 1069 | LLVMValueRef end_val; |
| 1007 | 1070 | if (node->data.slice_expr.end) { |
| 1008 | 1071 | end_val = gen_expr(g, node->data.slice_expr.end); |
| 1009 | 1072 | } else { |
| 1010 | | add_debug_source_node(g, node); |
| 1011 | | int len_index = array_type->data.structure.fields[1].gen_index; |
| 1012 | | assert(len_index >= 0); |
| 1013 | | LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, ""); |
| 1014 | | end_val = LLVMBuildLoad(g->builder, src_len_ptr, ""); |
| 1073 | end_val = prev_end; |
| 1015 | 1074 | } |
| 1016 | 1075 | |
| 1017 | | int ptr_index = array_type->data.structure.fields[0].gen_index; |
| 1018 | | assert(ptr_index >= 0); |
| 1019 | | int len_index = array_type->data.structure.fields[1].gen_index; |
| 1020 | | assert(len_index >= 0); |
| 1076 | if (want_debug_safety(g, node)) { |
| 1077 | assert(prev_end); |
| 1078 | add_bounds_check(g, node, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); |
| 1079 | if (node->data.slice_expr.end) { |
| 1080 | add_bounds_check(g, node, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end); |
| 1081 | } |
| 1082 | } |
| 1021 | 1083 | |
| 1022 | 1084 | add_debug_source_node(g, node); |
| 1023 | 1085 | LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, ""); |
| ... | ... | @@ -1225,7 +1287,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { |
| 1225 | 1287 | assert(expr_type->id == TypeTableEntryIdErrorUnion); |
| 1226 | 1288 | TypeTableEntry *child_type = expr_type->data.error.child_type; |
| 1227 | 1289 | |
| 1228 | | if (!g->is_release_build) { |
| 1290 | if (want_debug_safety(g, node)) { |
| 1229 | 1291 | LLVMValueRef err_val; |
| 1230 | 1292 | if (type_has_bits(child_type)) { |
| 1231 | 1293 | add_debug_source_node(g, node); |
| ... | ... | @@ -1263,7 +1325,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { |
| 1263 | 1325 | assert(expr_type->id == TypeTableEntryIdMaybe); |
| 1264 | 1326 | TypeTableEntry *child_type = expr_type->data.maybe.child_type; |
| 1265 | 1327 | |
| 1266 | | if (!g->is_release_build) { |
| 1328 | if (want_debug_safety(g, node)) { |
| 1267 | 1329 | add_debug_source_node(g, node); |
| 1268 | 1330 | LLVMValueRef cond_val; |
| 1269 | 1331 | if (child_type->id == TypeTableEntryIdPointer || |
| ... | ... | @@ -2261,7 +2323,7 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) { |
| 2261 | 2323 | } else if (type_entry->id == TypeTableEntryIdUnreachable) { |
| 2262 | 2324 | assert(node->data.container_init_expr.entries.length == 0); |
| 2263 | 2325 | add_debug_source_node(g, node); |
| 2264 | | if (!g->is_release_build) { |
| 2326 | if (want_debug_safety(g, node)) { |
| 2265 | 2327 | LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); |
| 2266 | 2328 | } |
| 2267 | 2329 | LLVMBuildUnreachable(g->builder); |
| ... | ... | @@ -2575,7 +2637,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa |
| 2575 | 2637 | } |
| 2576 | 2638 | } |
| 2577 | 2639 | } |
| 2578 | | if (!ignore_uninit && !g->is_release_build) { |
| 2640 | if (!ignore_uninit && want_debug_safety(g, source_node)) { |
| 2579 | 2641 | TypeTableEntry *isize = g->builtin_types.entry_isize; |
| 2580 | 2642 | uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref); |
| 2581 | 2643 | uint64_t align_bytes = get_memcpy_align(g, variable->type); |
| ... | ... | @@ -2790,7 +2852,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { |
| 2790 | 2852 | if (!else_prong) { |
| 2791 | 2853 | LLVMPositionBuilderAtEnd(g->builder, else_block); |
| 2792 | 2854 | add_debug_source_node(g, node); |
| 2793 | | if (!g->is_release_build) { |
| 2855 | if (want_debug_safety(g, node)) { |
| 2794 | 2856 | LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); |
| 2795 | 2857 | } |
| 2796 | 2858 | LLVMBuildUnreachable(g->builder); |
| ... | ... | @@ -3383,6 +3445,10 @@ static void do_code_gen(CodeGen *g) { |
| 3383 | 3445 | |
| 3384 | 3446 | // Generate the list of test function pointers. |
| 3385 | 3447 | if (g->is_test_build) { |
| 3448 | if (g->test_fn_count == 0) { |
| 3449 | fprintf(stderr, "No tests to run.\n"); |
| 3450 | exit(0); |
| 3451 | } |
| 3386 | 3452 | assert(g->test_fn_count > 0); |
| 3387 | 3453 | assert(next_test_index == g->test_fn_count); |
| 3388 | 3454 | |