authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-03 01:23:35-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-03 01:25:03-04:00
log3552180143a86432bfec3e71ba2fdd7aaebb0549
tree1bec759d22529875d4518fb8991e6972b4ad1ffe
parentf8117a0799f6c691bf65f3b3f4726bf0e6fd6be2
signature Commit is signed but in an unrecognized format.

optimize `@memset` with `undefined`

When using `@memset` to set bytes to `undefined`, Zig notices this case and does a single Valgrind client request rather than N. Speeds up all allocators in safe modes. Closes #2388

2 files changed, 33 insertions(+), 24 deletions(-)

src/codegen.cpp+31-16
...@@ -1036,7 +1036,7 @@ static LLVMValueRef get_write_register_fn_val(CodeGen *g) {...@@ -1036,7 +1036,7 @@ static LLVMValueRef get_write_register_fn_val(CodeGen *g) {
1036 // !0 = !{!"sp\00"}1036 // !0 = !{!"sp\00"}
10371037
1038 LLVMTypeRef param_types[] = {1038 LLVMTypeRef param_types[] = {
1039 LLVMMetadataTypeInContext(LLVMGetGlobalContext()), 1039 LLVMMetadataTypeInContext(LLVMGetGlobalContext()),
1040 LLVMIntType(g->pointer_size_bytes * 8),1040 LLVMIntType(g->pointer_size_bytes * 8),
1041 };1041 };
10421042
...@@ -3491,6 +3491,15 @@ static bool want_valgrind_support(CodeGen *g) {...@@ -3491,6 +3491,15 @@ static bool want_valgrind_support(CodeGen *g) {
3491 zig_unreachable();3491 zig_unreachable();
3492}3492}
34933493
3494static void gen_valgrind_undef(CodeGen *g, LLVMValueRef dest_ptr, LLVMValueRef byte_count) {
3495 static const uint32_t VG_USERREQ__MAKE_MEM_UNDEFINED = 1296236545;
3496 ZigType *usize = g->builtin_types.entry_usize;
3497 LLVMValueRef zero = LLVMConstInt(usize->llvm_type, 0, false);
3498 LLVMValueRef req = LLVMConstInt(usize->llvm_type, VG_USERREQ__MAKE_MEM_UNDEFINED, false);
3499 LLVMValueRef ptr_as_usize = LLVMBuildPtrToInt(g->builder, dest_ptr, usize->llvm_type, "");
3500 gen_valgrind_client_request(g, zero, req, ptr_as_usize, byte_count, zero, zero, zero);
3501}
3502
3494static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) {3503static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) {
3495 assert(type_has_bits(value_type));3504 assert(type_has_bits(value_type));
3496 uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, get_llvm_type(g, value_type));3505 uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, get_llvm_type(g, value_type));
...@@ -3505,11 +3514,7 @@ static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_...@@ -3505,11 +3514,7 @@ static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_
3505 ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false);3514 ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false);
3506 // then tell valgrind that the memory is undefined even though we just memset it3515 // then tell valgrind that the memory is undefined even though we just memset it
3507 if (want_valgrind_support(g)) {3516 if (want_valgrind_support(g)) {
3508 static const uint32_t VG_USERREQ__MAKE_MEM_UNDEFINED = 1296236545;3517 gen_valgrind_undef(g, dest_ptr, byte_count);
3509 LLVMValueRef zero = LLVMConstInt(usize->llvm_type, 0, false);
3510 LLVMValueRef req = LLVMConstInt(usize->llvm_type, VG_USERREQ__MAKE_MEM_UNDEFINED, false);
3511 LLVMValueRef ptr_as_usize = LLVMBuildPtrToInt(g->builder, dest_ptr, usize->llvm_type, "");
3512 gen_valgrind_client_request(g, zero, req, ptr_as_usize, byte_count, zero, zero, zero);
3513 }3518 }
3514}3519}
35153520
...@@ -3519,14 +3524,14 @@ static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, Ir...@@ -3519,14 +3524,14 @@ static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, Ir
3519 if (!type_has_bits(ptr_type))3524 if (!type_has_bits(ptr_type))
3520 return nullptr;3525 return nullptr;
35213526
3522 bool have_init_expr = !value_is_all_undef(&instruction->value->value); 3527 bool have_init_expr = !value_is_all_undef(&instruction->value->value);
3523 if (have_init_expr) {3528 if (have_init_expr) {
3524 LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);3529 LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
3525 LLVMValueRef value = ir_llvm_value(g, instruction->value);3530 LLVMValueRef value = ir_llvm_value(g, instruction->value);
3526 gen_assign_raw(g, ptr, ptr_type, value);3531 gen_assign_raw(g, ptr, ptr_type, value);
3527 } else if (ir_want_runtime_safety(g, &instruction->base)) {3532 } else if (ir_want_runtime_safety(g, &instruction->base)) {
3528 gen_undef_init(g, get_ptr_align(g, ptr_type), instruction->value->value.type,3533 gen_undef_init(g, get_ptr_align(g, ptr_type), instruction->value->value.type,
3529 ir_llvm_value(g, instruction->ptr)); 3534 ir_llvm_value(g, instruction->ptr));
3530 }3535 }
3531 return nullptr;3536 return nullptr;
3532}3537}
...@@ -3729,7 +3734,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3729,7 +3734,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3729 }3734 }
3730 FnWalk fn_walk = {};3735 FnWalk fn_walk = {};
3731 fn_walk.id = FnWalkIdCall;3736 fn_walk.id = FnWalkIdCall;
3732 fn_walk.data.call.inst = instruction; 3737 fn_walk.data.call.inst = instruction;
3733 fn_walk.data.call.is_var_args = is_var_args;3738 fn_walk.data.call.is_var_args = is_var_args;
3734 fn_walk.data.call.gen_param_values = &gen_param_values;3739 fn_walk.data.call.gen_param_values = &gen_param_values;
3735 walk_function_params(g, fn_type, &fn_walk);3740 walk_function_params(g, fn_type, &fn_walk);
...@@ -3749,7 +3754,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3749,7 +3754,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
37493754
3750 LLVMCallConv llvm_cc = get_llvm_cc(g, cc);3755 LLVMCallConv llvm_cc = get_llvm_cc(g, cc);
3751 LLVMValueRef result;3756 LLVMValueRef result;
3752 3757
3753 if (instruction->new_stack == nullptr) {3758 if (instruction->new_stack == nullptr) {
3754 result = ZigLLVMBuildCall(g->builder, fn_val,3759 result = ZigLLVMBuildCall(g->builder, fn_val,
3755 gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, "");3760 gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, "");
...@@ -4229,7 +4234,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) {...@@ -4229,7 +4234,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) {
4229 LLVMTypeRef tag_int_llvm_type = get_llvm_type(g, tag_int_type);4234 LLVMTypeRef tag_int_llvm_type = get_llvm_type(g, tag_int_type);
4230 LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMPointerType(get_llvm_type(g, u8_slice_type), 0),4235 LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMPointerType(get_llvm_type(g, u8_slice_type), 0),
4231 &tag_int_llvm_type, 1, false);4236 &tag_int_llvm_type, 1, false);
4232 4237
4233 Buf *fn_name = get_mangled_name(g, buf_sprintf("__zig_tag_name_%s", buf_ptr(&enum_type->name)), false);4238 Buf *fn_name = get_mangled_name(g, buf_sprintf("__zig_tag_name_%s", buf_ptr(&enum_type->name)), false);
4234 LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);4239 LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
4235 LLVMSetLinkage(fn_val, LLVMInternalLinkage);4240 LLVMSetLinkage(fn_val, LLVMInternalLinkage);
...@@ -4529,17 +4534,27 @@ static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrI...@@ -4529,17 +4534,27 @@ static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrI
45294534
4530static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutable *executable, IrInstructionMemset *instruction) {4535static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutable *executable, IrInstructionMemset *instruction) {
4531 LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr);4536 LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr);
4532 LLVMValueRef char_val = ir_llvm_value(g, instruction->byte);
4533 LLVMValueRef len_val = ir_llvm_value(g, instruction->count);4537 LLVMValueRef len_val = ir_llvm_value(g, instruction->count);
45344538
4535 LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);4539 LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
4536
4537 LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");4540 LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
45384541
4539 ZigType *ptr_type = instruction->dest_ptr->value.type;4542 ZigType *ptr_type = instruction->dest_ptr->value.type;
4540 assert(ptr_type->id == ZigTypeIdPointer);4543 assert(ptr_type->id == ZigTypeIdPointer);
45414544
4542 ZigLLVMBuildMemSet(g->builder, dest_ptr_casted, char_val, len_val, get_ptr_align(g, ptr_type), ptr_type->data.pointer.is_volatile);4545 bool val_is_undef = value_is_all_undef(&instruction->byte->value);
4546 LLVMValueRef fill_char;
4547 if (val_is_undef) {
4548 fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
4549 } else {
4550 fill_char = ir_llvm_value(g, instruction->byte);
4551 }
4552 ZigLLVMBuildMemSet(g->builder, dest_ptr_casted, fill_char, len_val, get_ptr_align(g, ptr_type),
4553 ptr_type->data.pointer.is_volatile);
4554
4555 if (val_is_undef && want_valgrind_support(g)) {
4556 gen_valgrind_undef(g, dest_ptr_casted, len_val);
4557 }
4543 return nullptr;4558 return nullptr;
4544}4559}
45454560
...@@ -6944,7 +6959,7 @@ static void do_code_gen(CodeGen *g) {...@@ -6944,7 +6959,7 @@ static void do_code_gen(CodeGen *g) {
6944 ir_render(g, fn_table_entry);6959 ir_render(g, fn_table_entry);
69456960
6946 }6961 }
6947 6962
6948 assert(!g->errors.length);6963 assert(!g->errors.length);
69496964
6950 if (buf_len(&g->global_asm) != 0) {6965 if (buf_len(&g->global_asm) != 0) {
...@@ -7752,7 +7767,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {...@@ -7752,7 +7767,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
7752 assert(ContainerLayoutAuto == 0);7767 assert(ContainerLayoutAuto == 0);
7753 assert(ContainerLayoutExtern == 1);7768 assert(ContainerLayoutExtern == 1);
7754 assert(ContainerLayoutPacked == 2);7769 assert(ContainerLayoutPacked == 2);
7755 7770
7756 assert(CallingConventionUnspecified == 0);7771 assert(CallingConventionUnspecified == 0);
7757 assert(CallingConventionC == 1);7772 assert(CallingConventionC == 1);
7758 assert(CallingConventionCold == 2);7773 assert(CallingConventionCold == 2);
std/mem.zig+2-8
...@@ -104,10 +104,7 @@ pub const Allocator = struct {...@@ -104,10 +104,7 @@ pub const Allocator = struct {
104 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;104 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
105 const byte_slice = try self.reallocFn(self, ([*]u8)(undefined)[0..0], undefined, byte_count, alignment);105 const byte_slice = try self.reallocFn(self, ([*]u8)(undefined)[0..0], undefined, byte_count, alignment);
106 assert(byte_slice.len == byte_count);106 assert(byte_slice.len == byte_count);
107 // This loop gets optimized out in ReleaseFast mode107 @memset(byte_slice.ptr, undefined, byte_slice.len);
108 for (byte_slice) |*byte| {
109 byte.* = undefined;
110 }
111 return @bytesToSlice(T, @alignCast(alignment, byte_slice));108 return @bytesToSlice(T, @alignCast(alignment, byte_slice));
112 }109 }
113110
...@@ -153,10 +150,7 @@ pub const Allocator = struct {...@@ -153,10 +150,7 @@ pub const Allocator = struct {
153 const byte_slice = try self.reallocFn(self, old_byte_slice, Slice.alignment, byte_count, new_alignment);150 const byte_slice = try self.reallocFn(self, old_byte_slice, Slice.alignment, byte_count, new_alignment);
154 assert(byte_slice.len == byte_count);151 assert(byte_slice.len == byte_count);
155 if (new_n > old_mem.len) {152 if (new_n > old_mem.len) {
156 // This loop gets optimized out in ReleaseFast mode153 @memset(byte_slice.ptr + old_byte_slice.len, undefined, byte_slice.len - old_byte_slice.len);
157 for (byte_slice[old_byte_slice.len..]) |*byte| {
158 byte.* = undefined;
159 }
160 }154 }
161 return @bytesToSlice(T, @alignCast(new_alignment, byte_slice));155 return @bytesToSlice(T, @alignCast(new_alignment, byte_slice));
162 }156 }