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
This commit is contained in:
Andrew Kelley 2017-04-25 16:53:22 -04:00
parent 8614397110
commit 13c6a58a61
4 changed files with 99 additions and 22 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
build/ build/
build2/
build-release/ build-release/
/.cproject /.cproject
/.project /.project

View file

@ -1257,6 +1257,7 @@ enum ZigLLVMFnId {
ZigLLVMFnIdClz, ZigLLVMFnIdClz,
ZigLLVMFnIdOverflowArithmetic, ZigLLVMFnIdOverflowArithmetic,
ZigLLVMFnIdOverflowArithmeticPanic, ZigLLVMFnIdOverflowArithmeticPanic,
ZigLLVMFnIdBoundsCheck,
}; };
enum AddSubMul { enum AddSubMul {
@ -1280,6 +1281,10 @@ struct ZigLLVMFnKey {
uint32_t bit_count; uint32_t bit_count;
bool is_signed; bool is_signed;
} overflow_arithmetic; } overflow_arithmetic;
struct {
LLVMIntPredicate pred;
uint32_t bit_count;
} bounds_check;
} data; } data;
}; };

View file

@ -4145,6 +4145,9 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 3329604261) + 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.add_sub_mul) * 966805797) +
((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 3679835291 : 1187552903); ((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(); 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) && 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.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
(a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed); (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(); zig_unreachable();
} }

View file

@ -812,37 +812,102 @@ static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
LLVMBuildUnreachable(g->builder); LLVMBuildUnreachable(g->builder);
} }
static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, static const char *pred_name(LLVMIntPredicate pred) {
LLVMIntPredicate lower_pred, LLVMValueRef lower_value, switch (pred) {
LLVMIntPredicate upper_pred, LLVMValueRef upper_value) case LLVMIntEQ: return "eq";
{ case LLVMIntNE: return "ne";
if (!lower_value && !upper_value) { case LLVMIntULT: return "lt";
return; case LLVMIntULE: return "le";
} default:
if (upper_value && !lower_value) { zig_unreachable();
lower_value = upper_value;
lower_pred = upper_pred;
upper_value = nullptr;
} }
}
LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "BoundsCheckFail"); static LLVMValueRef get_bounds_check_fn_val(CodeGen *g, LLVMIntPredicate pred, uint32_t bit_count) {
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "BoundsCheckOk"); ZigLLVMFnKey key = {};
LLVMBasicBlockRef lower_ok_block = upper_value ? key.id = ZigLLVMFnIdBoundsCheck;
LLVMAppendBasicBlock(g->cur_fn_val, "FirstBoundsCheckOk") : ok_block; key.data.bounds_check.pred = pred;
key.data.bounds_check.bit_count = bit_count;
LLVMValueRef lower_ok_val = LLVMBuildICmp(g->builder, lower_pred, target_val, lower_value, ""); auto existing_entry = g->llvm_fn_table.maybe_get(key);
LLVMBuildCondBr(g->builder, lower_ok_val, lower_ok_block, bounds_check_fail_block); 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);
LLVMValueRef target_val = LLVMGetParam(fn_val, 0);
LLVMValueRef bound_val = LLVMGetParam(fn_val, 1);
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); LLVMPositionBuilderAtEnd(g->builder, bounds_check_fail_block);
gen_debug_safety_crash(g, PanicMsgIdBoundsCheckFailure); gen_debug_safety_crash(g, PanicMsgIdBoundsCheckFailure);
if (upper_value) { LLVMPositionBuilderAtEnd(g->builder, ok_block);
LLVMPositionBuilderAtEnd(g->builder, lower_ok_block); LLVMBuildRetVoid(g->builder);
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); 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, "");
}
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);
} }
LLVMPositionBuilderAtEnd(g->builder, ok_block); 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, static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_debug_safety, TypeTableEntry *actual_type,