| ... | @@ -330,6 +330,46 @@ static LLVMValueRef get_handle_value(CodeGen *g, AstNode *source_node, LLVMValue | ... | @@ -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 | static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) { | 373 | static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) { |
| 334 | assert(node->type == NodeTypeFnCallExpr); | 374 | assert(node->type == NodeTypeFnCallExpr); |
| 335 | assert(g->generate_error_name_table); | 375 | assert(g->generate_error_name_table); |
| ... | @@ -344,25 +384,10 @@ static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) { | ... | @@ -344,25 +384,10 @@ static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) { |
| 344 | LLVMValueRef err_val = gen_expr(g, err_val_node); | 384 | LLVMValueRef err_val = gen_expr(g, err_val_node); |
| 345 | add_debug_source_node(g, node); | 385 | add_debug_source_node(g, node); |
| 346 | | 386 | |
| 347 | if (!g->is_release_build) { | 387 | if (want_debug_safety(g, node)) { |
| 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 | | | |
| 352 | LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val)); | 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 | LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->error_decls.length, false); | 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, ""); | 390 | add_bounds_check(g, node, err_val, LLVMIntNE, zero, LLVMIntULT, end_val); |
| 363 | LLVMBuildCondBr(g->builder, is_too_big_val, bounds_check_fail_block, ok_block); | | |
| 364 | | | |
| 365 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | | |
| 366 | } | 391 | } |
| 367 | | 392 | |
| 368 | LLVMValueRef indices[] = { | 393 | LLVMValueRef indices[] = { |
| ... | @@ -869,6 +894,11 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal | ... | @@ -869,6 +894,11 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal |
| 869 | } | 894 | } |
| 870 | | 895 | |
| 871 | if (array_type->id == TypeTableEntryIdArray) { | 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 | LLVMValueRef indices[] = { | 902 | LLVMValueRef indices[] = { |
| 873 | LLVMConstNull(g->builtin_types.entry_isize->type_ref), | 903 | LLVMConstNull(g->builtin_types.entry_isize->type_ref), |
| 874 | subscript_value | 904 | subscript_value |
| ... | @@ -887,6 +917,15 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal | ... | @@ -887,6 +917,15 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal |
| 887 | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); | 917 | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); |
| 888 | assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); | 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 | add_debug_source_node(g, source_node); | 929 | add_debug_source_node(g, source_node); |
| 891 | int ptr_index = array_type->data.structure.fields[0].gen_index; | 930 | int ptr_index = array_type->data.structure.fields[0].gen_index; |
| 892 | assert(ptr_index >= 0); | 931 | assert(ptr_index >= 0); |
| ... | @@ -907,7 +946,6 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { | ... | @@ -907,7 +946,6 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { |
| 907 | LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node); | 946 | LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node); |
| 908 | | 947 | |
| 909 | LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript); | 948 | LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript); |
| 910 | | | |
| 911 | return gen_array_elem_ptr(g, node, array_ptr, array_type, subscript_value); | 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,6 +1007,15 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) { |
| 969 | end_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref, array_type->data.array.len, false); | 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 | add_debug_source_node(g, node); | 1019 | add_debug_source_node(g, node); |
| 973 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, ""); | 1020 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, ""); |
| 974 | LLVMValueRef indices[] = { | 1021 | LLVMValueRef indices[] = { |
| ... | @@ -987,6 +1034,10 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) { | ... | @@ -987,6 +1034,10 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) { |
| 987 | LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start); | 1034 | LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start); |
| 988 | LLVMValueRef end_val = gen_expr(g, node->data.slice_expr.end); | 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 | add_debug_source_node(g, node); | 1041 | add_debug_source_node(g, node); |
| 991 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, ""); | 1042 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, ""); |
| 992 | LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); | 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,22 +1053,33 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) { |
| 1002 | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); | 1053 | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); |
| 1003 | assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); | 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 | LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start); | 1068 | LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start); |
| 1006 | LLVMValueRef end_val; | 1069 | LLVMValueRef end_val; |
| 1007 | if (node->data.slice_expr.end) { | 1070 | if (node->data.slice_expr.end) { |
| 1008 | end_val = gen_expr(g, node->data.slice_expr.end); | 1071 | end_val = gen_expr(g, node->data.slice_expr.end); |
| 1009 | } else { | 1072 | } else { |
| 1010 | add_debug_source_node(g, node); | 1073 | end_val = prev_end; |
| 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, ""); | | |
| 1015 | } | 1074 | } |
| 1016 | | 1075 | |
| 1017 | int ptr_index = array_type->data.structure.fields[0].gen_index; | 1076 | if (want_debug_safety(g, node)) { |
| 1018 | assert(ptr_index >= 0); | 1077 | assert(prev_end); |
| 1019 | int len_index = array_type->data.structure.fields[1].gen_index; | 1078 | add_bounds_check(g, node, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); |
| 1020 | assert(len_index >= 0); | 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 | add_debug_source_node(g, node); | 1084 | add_debug_source_node(g, node); |
| 1023 | LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, ""); | 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,7 +1287,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { |
| 1225 | assert(expr_type->id == TypeTableEntryIdErrorUnion); | 1287 | assert(expr_type->id == TypeTableEntryIdErrorUnion); |
| 1226 | TypeTableEntry *child_type = expr_type->data.error.child_type; | 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 | LLVMValueRef err_val; | 1291 | LLVMValueRef err_val; |
| 1230 | if (type_has_bits(child_type)) { | 1292 | if (type_has_bits(child_type)) { |
| 1231 | add_debug_source_node(g, node); | 1293 | add_debug_source_node(g, node); |
| ... | @@ -1263,7 +1325,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { | ... | @@ -1263,7 +1325,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { |
| 1263 | assert(expr_type->id == TypeTableEntryIdMaybe); | 1325 | assert(expr_type->id == TypeTableEntryIdMaybe); |
| 1264 | TypeTableEntry *child_type = expr_type->data.maybe.child_type; | 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 | add_debug_source_node(g, node); | 1329 | add_debug_source_node(g, node); |
| 1268 | LLVMValueRef cond_val; | 1330 | LLVMValueRef cond_val; |
| 1269 | if (child_type->id == TypeTableEntryIdPointer || | 1331 | if (child_type->id == TypeTableEntryIdPointer || |
| ... | @@ -2261,7 +2323,7 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) { | ... | @@ -2261,7 +2323,7 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) { |
| 2261 | } else if (type_entry->id == TypeTableEntryIdUnreachable) { | 2323 | } else if (type_entry->id == TypeTableEntryIdUnreachable) { |
| 2262 | assert(node->data.container_init_expr.entries.length == 0); | 2324 | assert(node->data.container_init_expr.entries.length == 0); |
| 2263 | add_debug_source_node(g, node); | 2325 | add_debug_source_node(g, node); |
| 2264 | if (!g->is_release_build) { | 2326 | if (want_debug_safety(g, node)) { |
| 2265 | LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); | 2327 | LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); |
| 2266 | } | 2328 | } |
| 2267 | LLVMBuildUnreachable(g->builder); | 2329 | LLVMBuildUnreachable(g->builder); |
| ... | @@ -2575,7 +2637,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa | ... | @@ -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 | TypeTableEntry *isize = g->builtin_types.entry_isize; | 2641 | TypeTableEntry *isize = g->builtin_types.entry_isize; |
| 2580 | uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref); | 2642 | uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref); |
| 2581 | uint64_t align_bytes = get_memcpy_align(g, variable->type); | 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,7 +2852,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { |
| 2790 | if (!else_prong) { | 2852 | if (!else_prong) { |
| 2791 | LLVMPositionBuilderAtEnd(g->builder, else_block); | 2853 | LLVMPositionBuilderAtEnd(g->builder, else_block); |
| 2792 | add_debug_source_node(g, node); | 2854 | add_debug_source_node(g, node); |
| 2793 | if (!g->is_release_build) { | 2855 | if (want_debug_safety(g, node)) { |
| 2794 | LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); | 2856 | LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); |
| 2795 | } | 2857 | } |
| 2796 | LLVMBuildUnreachable(g->builder); | 2858 | LLVMBuildUnreachable(g->builder); |
| ... | @@ -3383,6 +3445,10 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -3383,6 +3445,10 @@ static void do_code_gen(CodeGen *g) { |
| 3383 | | 3445 | |
| 3384 | // Generate the list of test function pointers. | 3446 | // Generate the list of test function pointers. |
| 3385 | if (g->is_test_build) { | 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 | assert(g->test_fn_count > 0); | 3452 | assert(g->test_fn_count > 0); |
| 3387 | assert(next_test_index == g->test_fn_count); | 3453 | assert(next_test_index == g->test_fn_count); |
| 3388 | | 3454 | |