| ... | ... | @@ -812,37 +812,102 @@ static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) { |
| 812 | 812 | LLVMBuildUnreachable(g->builder); |
| 813 | 813 | } |
| 814 | 814 | |
| 815 | | static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, |
| 816 | | LLVMIntPredicate lower_pred, LLVMValueRef lower_value, |
| 817 | | LLVMIntPredicate upper_pred, LLVMValueRef upper_value) |
| 818 | | { |
| 819 | | if (!lower_value && !upper_value) { |
| 820 | | return; |
| 821 | | } |
| 822 | | if (upper_value && !lower_value) { |
| 823 | | lower_value = upper_value; |
| 824 | | lower_pred = upper_pred; |
| 825 | | upper_value = nullptr; |
| 815 | static const char *pred_name(LLVMIntPredicate pred) { |
| 816 | switch (pred) { |
| 817 | case LLVMIntEQ: return "eq"; |
| 818 | case LLVMIntNE: return "ne"; |
| 819 | case LLVMIntULT: return "lt"; |
| 820 | case LLVMIntULE: return "le"; |
| 821 | default: |
| 822 | zig_unreachable(); |
| 826 | 823 | } |
| 824 | } |
| 825 | |
| 826 | static LLVMValueRef get_bounds_check_fn_val(CodeGen *g, LLVMIntPredicate pred, uint32_t bit_count) { |
| 827 | ZigLLVMFnKey key = {}; |
| 828 | key.id = ZigLLVMFnIdBoundsCheck; |
| 829 | key.data.bounds_check.pred = pred; |
| 830 | key.data.bounds_check.bit_count = bit_count; |
| 831 | |
| 832 | auto existing_entry = g->llvm_fn_table.maybe_get(key); |
| 833 | if (existing_entry) |
| 834 | return existing_entry->value; |
| 835 | |
| 836 | Buf *desired_name = buf_sprintf("__zig_bounds_check_%s_%" PRIu32, pred_name(pred), bit_count); |
| 837 | Buf *fn_name = get_mangled_name(g, desired_name, false); |
| 838 | LLVMTypeRef type_ref = LLVMIntType(bit_count); |
| 839 | LLVMTypeRef arg_types[] = { type_ref, type_ref }; |
| 840 | LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false); |
| 841 | LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref); |
| 842 | LLVMSetLinkage(fn_val, LLVMInternalLinkage); |
| 843 | LLVMSetFunctionCallConv(fn_val, LLVMFastCallConv); |
| 844 | |
| 845 | auto prev_state = save_and_clear_builder_state(g); |
| 846 | |
| 847 | LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); |
| 848 | LLVMPositionBuilderAtEnd(g->builder, entry_block); |
| 827 | 849 | |
| 828 | | LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "BoundsCheckFail"); |
| 829 | | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "BoundsCheckOk"); |
| 830 | | LLVMBasicBlockRef lower_ok_block = upper_value ? |
| 831 | | LLVMAppendBasicBlock(g->cur_fn_val, "FirstBoundsCheckOk") : ok_block; |
| 850 | LLVMValueRef target_val = LLVMGetParam(fn_val, 0); |
| 851 | LLVMValueRef bound_val = LLVMGetParam(fn_val, 1); |
| 832 | 852 | |
| 833 | | LLVMValueRef lower_ok_val = LLVMBuildICmp(g->builder, lower_pred, target_val, lower_value, ""); |
| 834 | | LLVMBuildCondBr(g->builder, lower_ok_val, lower_ok_block, bounds_check_fail_block); |
| 853 | LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(fn_val, "BoundsCheckFail"); |
| 854 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(fn_val, "BoundsCheckOk"); |
| 855 | |
| 856 | LLVMValueRef ok_val = LLVMBuildICmp(g->builder, pred, target_val, bound_val, ""); |
| 857 | LLVMBuildCondBr(g->builder, ok_val, ok_block, bounds_check_fail_block); |
| 835 | 858 | |
| 836 | 859 | LLVMPositionBuilderAtEnd(g->builder, bounds_check_fail_block); |
| 837 | 860 | gen_debug_safety_crash(g, PanicMsgIdBoundsCheckFailure); |
| 838 | 861 | |
| 839 | | if (upper_value) { |
| 840 | | LLVMPositionBuilderAtEnd(g->builder, lower_ok_block); |
| 841 | | LLVMValueRef upper_ok_val = LLVMBuildICmp(g->builder, upper_pred, target_val, upper_value, ""); |
| 842 | | LLVMBuildCondBr(g->builder, upper_ok_val, ok_block, bounds_check_fail_block); |
| 862 | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 863 | LLVMBuildRetVoid(g->builder); |
| 864 | |
| 865 | restore_builder_state(g, prev_state); |
| 866 | g->llvm_fn_table.put(key, fn_val); |
| 867 | return fn_val; |
| 868 | } |
| 869 | |
| 870 | static void add_one_bounds_check(CodeGen *g, LLVMValueRef target_val, LLVMIntPredicate pred, LLVMValueRef bound_val) { |
| 871 | LLVMValueRef arg1; |
| 872 | LLVMValueRef arg2; |
| 873 | switch (pred) { |
| 874 | case LLVMIntEQ: |
| 875 | case LLVMIntNE: |
| 876 | case LLVMIntULT: |
| 877 | case LLVMIntULE: |
| 878 | arg1 = target_val; |
| 879 | arg2 = bound_val; |
| 880 | break; |
| 881 | case LLVMIntUGT: |
| 882 | arg1 = bound_val; |
| 883 | arg2 = target_val; |
| 884 | pred = LLVMIntULE; |
| 885 | break; |
| 886 | case LLVMIntUGE: |
| 887 | arg1 = bound_val; |
| 888 | arg2 = target_val; |
| 889 | pred = LLVMIntULT; |
| 890 | break; |
| 891 | default: |
| 892 | zig_unreachable(); |
| 843 | 893 | } |
| 894 | uint32_t bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(target_val)); |
| 895 | LLVMValueRef fn_val = get_bounds_check_fn_val(g, pred, bit_count); |
| 896 | LLVMValueRef params[] = { arg1, arg2, }; |
| 897 | LLVMBuildCall(g->builder, fn_val, params, 2, ""); |
| 898 | } |
| 844 | 899 | |
| 845 | | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 900 | static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, |
| 901 | LLVMIntPredicate lower_pred, LLVMValueRef lower_value, |
| 902 | LLVMIntPredicate upper_pred, LLVMValueRef upper_value) |
| 903 | { |
| 904 | if (lower_value) { |
| 905 | add_one_bounds_check(g, target_val, lower_pred, lower_value); |
| 906 | } |
| 907 | |
| 908 | if (upper_value) { |
| 909 | add_one_bounds_check(g, target_val, upper_pred, upper_value); |
| 910 | } |
| 846 | 911 | } |
| 847 | 912 | |
| 848 | 913 | static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_debug_safety, TypeTableEntry *actual_type, |