Commit 13c6a58a61

Andrew Kelley <superjoe30@gmail.com>
2017-04-25 22:53:22
compile time improvement - move bounds checking to function calls
once again this barely had an effect: Before: ./build size: 1.3 MB hello.zig size: 301 KB full test: 1m31.253s debug test: 19.607s hello.zig timing: Name Start End Duration Percent Initialize 0.0000 0.0000 0.0000 0.0002 Semantic Analysis 0.0000 0.0431 0.0431 0.2262 Code Generation 0.0431 0.0660 0.0229 0.1201 LLVM Emit Object 0.0660 0.1765 0.1105 0.5795 Build Dependencies 0.1765 0.1890 0.0125 0.0655 LLVM Link 0.1890 0.1906 0.0016 0.0086 Generate .h 0.1906 0.1906 0.0000 0.0000 Total 0.0000 0.1906 0.1906 1.0000 After: ./build size: 1.3 MB hello.zig size: 300 KB full test: 1m31.882s debug test: 19.569s hello.zig timing: Name Start End Duration Percent Initialize 0.0000 0.0000 0.0000 0.0002 Semantic Analysis 0.0000 0.0425 0.0424 0.2228 Code Generation 0.0425 0.0661 0.0236 0.1239 LLVM Emit Object 0.0661 0.1762 0.1101 0.5782 Build Dependencies 0.1762 0.1888 0.0126 0.0664 LLVM Link 0.1888 0.1905 0.0016 0.0085 Generate .h 0.1905 0.1905 0.0000 0.0000 Total 0.0000 0.1905 0.1905 1.0000
1 parent 8614397
src/all_types.hpp
@@ -1257,6 +1257,7 @@ enum ZigLLVMFnId {
     ZigLLVMFnIdClz,
     ZigLLVMFnIdOverflowArithmetic,
     ZigLLVMFnIdOverflowArithmeticPanic,
+    ZigLLVMFnIdBoundsCheck,
 };
 
 enum AddSubMul {
@@ -1280,6 +1281,10 @@ struct ZigLLVMFnKey {
             uint32_t bit_count;
             bool is_signed;
         } overflow_arithmetic;
+        struct {
+            LLVMIntPredicate pred;
+            uint32_t bit_count;
+        } bounds_check;
     } data;
 };
 
src/analyze.cpp
@@ -4145,6 +4145,9 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
             return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 3329604261) +
                 ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 966805797) +
                 ((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 3679835291 : 1187552903);
+        case ZigLLVMFnIdBoundsCheck:
+            return (uint32_t)(x.data.bounds_check.pred) * (uint32_t)3146725107 +
+                x.data.bounds_check.bit_count * (uint32_t)2904561957;
     }
     zig_unreachable();
 }
@@ -4162,6 +4165,9 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
             return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
                 (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
                 (a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed);
+        case ZigLLVMFnIdBoundsCheck:
+            return a.data.bounds_check.pred == b.data.bounds_check.pred &&
+                a.data.bounds_check.bit_count == b.data.bounds_check.bit_count;
     }
     zig_unreachable();
 }
src/codegen.cpp
@@ -812,37 +812,102 @@ static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
     LLVMBuildUnreachable(g->builder);
 }
 
-static void add_bounds_check(CodeGen *g, LLVMValueRef target_val,
-        LLVMIntPredicate lower_pred, LLVMValueRef lower_value,
-        LLVMIntPredicate upper_pred, LLVMValueRef upper_value)
-{
-    if (!lower_value && !upper_value) {
-        return;
-    }
-    if (upper_value && !lower_value) {
-        lower_value = upper_value;
-        lower_pred = upper_pred;
-        upper_value = nullptr;
+static const char *pred_name(LLVMIntPredicate pred) {
+    switch (pred) {
+        case LLVMIntEQ: return "eq";
+        case LLVMIntNE: return "ne";
+        case LLVMIntULT: return "lt";
+        case LLVMIntULE: return "le";
+        default:
+            zig_unreachable();
     }
+}
+
+static LLVMValueRef get_bounds_check_fn_val(CodeGen *g, LLVMIntPredicate pred, uint32_t bit_count) {
+    ZigLLVMFnKey key = {};
+    key.id = ZigLLVMFnIdBoundsCheck;
+    key.data.bounds_check.pred = pred;
+    key.data.bounds_check.bit_count = bit_count;
+
+    auto existing_entry = g->llvm_fn_table.maybe_get(key);
+    if (existing_entry)
+        return existing_entry->value;
+
+    Buf *desired_name = buf_sprintf("__zig_bounds_check_%s_%" PRIu32, pred_name(pred), bit_count);
+    Buf *fn_name = get_mangled_name(g, desired_name, false);
+    LLVMTypeRef type_ref = LLVMIntType(bit_count);
+    LLVMTypeRef arg_types[] = { type_ref, type_ref };
+    LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false);
+    LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
+    LLVMSetLinkage(fn_val, LLVMInternalLinkage);
+    LLVMSetFunctionCallConv(fn_val, LLVMFastCallConv);
+
+    auto prev_state = save_and_clear_builder_state(g);
+
+    LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry");
+    LLVMPositionBuilderAtEnd(g->builder, entry_block);
 
-    LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "BoundsCheckFail");
-    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "BoundsCheckOk");
-    LLVMBasicBlockRef lower_ok_block = upper_value ?
-        LLVMAppendBasicBlock(g->cur_fn_val, "FirstBoundsCheckOk") : ok_block;
+    LLVMValueRef target_val = LLVMGetParam(fn_val, 0);
+    LLVMValueRef bound_val = LLVMGetParam(fn_val, 1);
 
-    LLVMValueRef lower_ok_val = LLVMBuildICmp(g->builder, lower_pred, target_val, lower_value, "");
-    LLVMBuildCondBr(g->builder, lower_ok_val, lower_ok_block, bounds_check_fail_block);
+    LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(fn_val, "BoundsCheckFail");
+    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(fn_val, "BoundsCheckOk");
+
+    LLVMValueRef ok_val = LLVMBuildICmp(g->builder, pred, target_val, bound_val, "");
+    LLVMBuildCondBr(g->builder, ok_val, ok_block, bounds_check_fail_block);
 
     LLVMPositionBuilderAtEnd(g->builder, bounds_check_fail_block);
     gen_debug_safety_crash(g, PanicMsgIdBoundsCheckFailure);
 
-    if (upper_value) {
-        LLVMPositionBuilderAtEnd(g->builder, lower_ok_block);
-        LLVMValueRef upper_ok_val = LLVMBuildICmp(g->builder, upper_pred, target_val, upper_value, "");
-        LLVMBuildCondBr(g->builder, upper_ok_val, ok_block, bounds_check_fail_block);
+    LLVMPositionBuilderAtEnd(g->builder, ok_block);
+    LLVMBuildRetVoid(g->builder);
+
+    restore_builder_state(g, prev_state);
+    g->llvm_fn_table.put(key, fn_val);
+    return fn_val;
+}
+
+static void add_one_bounds_check(CodeGen *g, LLVMValueRef target_val, LLVMIntPredicate pred, LLVMValueRef bound_val) {
+    LLVMValueRef arg1;
+    LLVMValueRef arg2;
+    switch (pred) {
+        case LLVMIntEQ:
+        case LLVMIntNE:
+        case LLVMIntULT:
+        case LLVMIntULE:
+            arg1 = target_val;
+            arg2 = bound_val;
+            break;
+        case LLVMIntUGT:
+            arg1 = bound_val;
+            arg2 = target_val;
+            pred = LLVMIntULE;
+            break;
+        case LLVMIntUGE:
+            arg1 = bound_val;
+            arg2 = target_val;
+            pred = LLVMIntULT;
+            break;
+        default:
+            zig_unreachable();
     }
+    uint32_t bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(target_val));
+    LLVMValueRef fn_val = get_bounds_check_fn_val(g, pred, bit_count);
+    LLVMValueRef params[] = { arg1, arg2, };
+    LLVMBuildCall(g->builder, fn_val, params, 2, "");
+}
 
-    LLVMPositionBuilderAtEnd(g->builder, ok_block);
+static void add_bounds_check(CodeGen *g, LLVMValueRef target_val,
+        LLVMIntPredicate lower_pred, LLVMValueRef lower_value,
+        LLVMIntPredicate upper_pred, LLVMValueRef upper_value)
+{
+    if (lower_value) {
+        add_one_bounds_check(g, target_val, lower_pred, lower_value);
+    }
+
+    if (upper_value) {
+        add_one_bounds_check(g, target_val, upper_pred, upper_value);
+    }
 }
 
 static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_debug_safety, TypeTableEntry *actual_type,
.gitignore
@@ -1,4 +1,5 @@
 build/
+build2/
 build-release/
 /.cproject
 /.project