authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-19 12:07:56-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-19 12:07:56-05:00
logdb74832e40f98960e5dc3e46c8196c59033ee0f9
tree95d7e039968d665d0e0001c45c9c9c1f3e2a622d
parentc9fb5240d6305bcef7db5e12b7676ba3d741c11e
signature Commit is signed but in an unrecognized format.

valgrind client requests for undefined values

with this change, when you assign undefined, zig emits a few assembly instructions to tell valgrind that the memory is undefined it's on by default for debug builds, and disabled otherwise. only support for linux, darwin, solaris, mingw on x86_64 is currently implemented. --disable-valgrind turns it off even in debug mode. --enable-valgrind turns it on even in release modes. It's always disabled for compiler_rt.a and builtin.a. Adds `@import("builtin").valgrind_support` which lets code know at comptime whether valgrind client requests are enabled. See #1989

6 files changed, 130 insertions(+), 9 deletions(-)

src/all_types.hpp+18-6
......@@ -1345,15 +1345,11 @@ struct ZigFn {
13451345 // in the case of async functions this is the implicit return type according to the
13461346 // zig source code, not according to zig ir
13471347 ZigType *src_implicit_return_type;
1348 bool is_test;
1349 FnInline fn_inline;
1350 FnAnalState anal_state;
13511348 IrExecutable ir_executable;
13521349 IrExecutable analyzed_executable;
13531350 size_t prealloc_bbc;
13541351 AstNode **param_source_nodes;
13551352 Buf **param_names;
1356 uint32_t align_bytes;
13571353
13581354 AstNode *fn_no_inline_set_node;
13591355 AstNode *fn_static_eval_set_node;
......@@ -1363,13 +1359,22 @@ struct ZigFn {
13631359
13641360 Buf *section_name;
13651361 AstNode *set_alignstack_node;
1366 uint32_t alignstack_value;
13671362
13681363 AstNode *set_cold_node;
1369 bool is_cold;
13701364
13711365 ZigList<FnExport> export_list;
1366
1367 LLVMValueRef valgrind_client_request_array;
1368
1369 FnInline fn_inline;
1370 FnAnalState anal_state;
1371
1372 uint32_t align_bytes;
1373 uint32_t alignstack_value;
1374
13721375 bool calls_or_awaits_errorable_fn;
1376 bool is_cold;
1377 bool is_test;
13731378};
13741379
13751380uint32_t fn_table_entry_hash(ZigFn*);
......@@ -1612,6 +1617,12 @@ struct LinkLib {
16121617 bool provided_explicitly;
16131618};
16141619
1620enum ValgrindSupport {
1621 ValgrindSupportAuto,
1622 ValgrindSupportDisabled,
1623 ValgrindSupportEnabled,
1624};
1625
16151626// When adding fields, check if they should be added to the hash computation in build_with_cache
16161627struct CodeGen {
16171628 //////////////////////////// Runtime State
......@@ -1813,6 +1824,7 @@ struct CodeGen {
18131824 OutType out_type;
18141825 ZigTarget zig_target;
18151826 TargetSubsystem subsystem;
1827 ValgrindSupport valgrind_support;
18161828 bool is_static;
18171829 bool strip_debug_symbols;
18181830 bool is_test_build;
src/codegen.cpp+81
......@@ -3341,6 +3341,77 @@ static bool value_is_all_undef(ConstExprValue *const_val) {
33413341 zig_unreachable();
33423342}
33433343
3344static LLVMValueRef gen_valgrind_client_request(CodeGen *g, LLVMValueRef default_value, LLVMValueRef request,
3345 LLVMValueRef a1, LLVMValueRef a2, LLVMValueRef a3, LLVMValueRef a4, LLVMValueRef a5)
3346{
3347 if (!target_has_valgrind_support(&g->zig_target)) {
3348 return default_value;
3349 }
3350 LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
3351 bool asm_has_side_effects = true;
3352 bool asm_is_alignstack = false;
3353 if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
3354 if (g->zig_target.os == OsLinux || target_is_darwin(&g->zig_target) || g->zig_target.os == OsSolaris ||
3355 (g->zig_target.os == OsWindows && g->zig_target.env_type != ZigLLVM_MSVC))
3356 {
3357 if (g->cur_fn->valgrind_client_request_array == nullptr) {
3358 LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
3359 LLVMBasicBlockRef entry_block = LLVMGetEntryBasicBlock(g->cur_fn->llvm_value);
3360 LLVMValueRef first_inst = LLVMGetFirstInstruction(entry_block);
3361 LLVMPositionBuilderBefore(g->builder, first_inst);
3362 LLVMTypeRef array_type_ref = LLVMArrayType(usize_type_ref, 6);
3363 g->cur_fn->valgrind_client_request_array = LLVMBuildAlloca(g->builder, array_type_ref, "");
3364 LLVMPositionBuilderAtEnd(g->builder, prev_block);
3365 }
3366 LLVMValueRef array_ptr = g->cur_fn->valgrind_client_request_array;
3367 LLVMValueRef array_elements[] = {request, a1, a2, a3, a4, a5};
3368 LLVMValueRef zero = LLVMConstInt(usize_type_ref, 0, false);
3369 for (unsigned i = 0; i < 6; i += 1) {
3370 LLVMValueRef indexes[] = {
3371 zero,
3372 LLVMConstInt(usize_type_ref, i, false),
3373 };
3374 LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indexes, 2, "");
3375 LLVMBuildStore(g->builder, array_elements[i], elem_ptr);
3376 }
3377
3378 Buf *asm_template = buf_create_from_str(
3379 "rolq $$3, %rdi ; rolq $$13, %rdi\n"
3380 "rolq $$61, %rdi ; rolq $$51, %rdi\n"
3381 "xchgq %rbx,%rbx\n"
3382 );
3383 Buf *asm_constraints = buf_create_from_str(
3384 "={rdx},{rax},0,~{cc},~{memory}"
3385 );
3386 unsigned input_and_output_count = 2;
3387 LLVMValueRef array_ptr_as_usize = LLVMBuildPtrToInt(g->builder, array_ptr, usize_type_ref, "");
3388 LLVMValueRef param_values[] = { array_ptr_as_usize, default_value };
3389 LLVMTypeRef param_types[] = {usize_type_ref, usize_type_ref};
3390 LLVMTypeRef function_type = LLVMFunctionType(usize_type_ref, param_types,
3391 input_and_output_count, false);
3392 LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(asm_template), buf_len(asm_template),
3393 buf_ptr(asm_constraints), buf_len(asm_constraints), asm_has_side_effects, asm_is_alignstack,
3394 LLVMInlineAsmDialectATT);
3395 return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, "");
3396 }
3397 }
3398 zig_unreachable();
3399}
3400
3401static bool want_valgrind_support(CodeGen *g) {
3402 if (!target_has_valgrind_support(&g->zig_target))
3403 return false;
3404 switch (g->valgrind_support) {
3405 case ValgrindSupportDisabled:
3406 return false;
3407 case ValgrindSupportEnabled:
3408 return true;
3409 case ValgrindSupportAuto:
3410 return g->build_mode == BuildModeDebug;
3411 }
3412 zig_unreachable();
3413}
3414
33443415static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) {
33453416 assert(type_has_bits(value_type));
33463417 uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, value_type->type_ref);
......@@ -3353,6 +3424,14 @@ static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_
33533424 ZigType *usize = g->builtin_types.entry_usize;
33543425 LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false);
33553426 ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false);
3427 // then tell valgrind that the memory is undefined even though we just memset it
3428 if (want_valgrind_support(g)) {
3429 static const uint32_t VG_USERREQ__MAKE_MEM_UNDEFINED = 1296236545;
3430 LLVMValueRef zero = LLVMConstInt(usize->type_ref, 0, false);
3431 LLVMValueRef req = LLVMConstInt(usize->type_ref, VG_USERREQ__MAKE_MEM_UNDEFINED, false);
3432 LLVMValueRef ptr_as_usize = LLVMBuildPtrToInt(g->builder, dest_ptr, usize->type_ref, "");
3433 gen_valgrind_client_request(g, zero, req, ptr_as_usize, byte_count, zero, zero, zero);
3434 }
33563435}
33573436
33583437static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) {
......@@ -7525,6 +7604,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
75257604 buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode));
75267605 buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr));
75277606 buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing));
7607 buf_appendf(contents, "pub const valgrind_support = %s;\n", bool_to_str(want_valgrind_support(g)));
75287608
75297609 buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n");
75307610
......@@ -8489,6 +8569,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
84898569 cache_bool(ch, g->linker_rdynamic);
84908570 cache_bool(ch, g->each_lib_rpath);
84918571 cache_bool(ch, g->disable_pic);
8572 cache_bool(ch, g->valgrind_support);
84928573 cache_buf_opt(ch, g->mmacosx_version_min);
84938574 cache_buf_opt(ch, g->mios_version_min);
84948575 cache_usize(ch, g->version_major);
src/link.cpp+1
......@@ -55,6 +55,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path)
5555 codegen_set_strip(child_gen, parent_gen->strip_debug_symbols);
5656 codegen_set_is_static(child_gen, true);
5757 child_gen->disable_pic = parent_gen->disable_pic;
58 child_gen->valgrind_support = ValgrindSupportDisabled;
5859
5960 codegen_set_out_name(child_gen, buf_create_from_str(aname));
6061
src/main.cpp+11
......@@ -49,6 +49,8 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
4949 " --cache [auto|off|on] build in global cache, print out paths to stdout\n"
5050 " --color [auto|off|on] enable or disable colored error messages\n"
5151 " --disable-pic disable Position Independent Code for libraries\n"
52 " --disable-valgrind omit valgrind client requests in debug builds\n"
53 " --enable-valgrind include valgrind client requests release builds\n"
5254 " --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n"
5355 " -ftime-report print timing diagnostics\n"
5456 " --libc-include-dir [path] directory where libc stdlib.h resides\n"
......@@ -396,6 +398,7 @@ int main(int argc, char **argv) {
396398 TargetSubsystem subsystem = TargetSubsystemAuto;
397399 bool is_single_threaded = false;
398400 Buf *override_std_dir = nullptr;
401 ValgrindSupport valgrind_support = ValgrindSupportAuto;
399402
400403 if (argc >= 2 && strcmp(argv[1], "build") == 0) {
401404 Buf zig_exe_path_buf = BUF_INIT;
......@@ -433,6 +436,7 @@ int main(int argc, char **argv) {
433436
434437 CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(),
435438 override_std_dir);
439 g->valgrind_support = valgrind_support;
436440 g->enable_time_report = timing_info;
437441 buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name);
438442 codegen_set_out_name(g, buf_create_from_str("build"));
......@@ -520,6 +524,7 @@ int main(int argc, char **argv) {
520524 os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path);
521525 CodeGen *g = codegen_create(fmt_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(),
522526 nullptr);
527 g->valgrind_support = valgrind_support;
523528 g->is_single_threaded = true;
524529 codegen_set_out_name(g, buf_create_from_str("fmt"));
525530 g->enable_cache = true;
......@@ -577,6 +582,10 @@ int main(int argc, char **argv) {
577582 timing_info = true;
578583 } else if (strcmp(arg, "--disable-pic") == 0) {
579584 disable_pic = true;
585 } else if (strcmp(arg, "--enable-valgrind") == 0) {
586 valgrind_support = ValgrindSupportEnabled;
587 } else if (strcmp(arg, "--disable-valgrind") == 0) {
588 valgrind_support = ValgrindSupportDisabled;
580589 } else if (strcmp(arg, "--system-linker-hack") == 0) {
581590 system_linker_hack = true;
582591 } else if (strcmp(arg, "--single-threaded") == 0) {
......@@ -849,6 +858,7 @@ int main(int argc, char **argv) {
849858 switch (cmd) {
850859 case CmdBuiltin: {
851860 CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir);
861 g->valgrind_support = valgrind_support;
852862 g->is_single_threaded = is_single_threaded;
853863 Buf *builtin_source = codegen_generate_builtin_source(g);
854864 if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
......@@ -909,6 +919,7 @@ int main(int argc, char **argv) {
909919 }
910920 CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir(),
911921 override_std_dir);
922 g->valgrind_support = valgrind_support;
912923 g->subsystem = subsystem;
913924
914925 if (disable_pic) {
src/target.cpp+17-3
......@@ -544,7 +544,7 @@ void get_target_triple(Buf *triple, const ZigTarget *target) {
544544 }
545545}
546546
547static bool is_os_darwin(ZigTarget *target) {
547bool target_is_darwin(const ZigTarget *target) {
548548 switch (target->os) {
549549 case OsMacOSX:
550550 case OsIOS:
......@@ -566,7 +566,7 @@ void resolve_target_object_format(ZigTarget *target) {
566566 case ZigLLVM_thumb:
567567 case ZigLLVM_x86:
568568 case ZigLLVM_x86_64:
569 if (is_os_darwin(target)) {
569 if (target_is_darwin(target)) {
570570 target->oformat = ZigLLVM_MachO;
571571 } else if (target->os == OsWindows) {
572572 target->oformat = ZigLLVM_COFF;
......@@ -626,7 +626,7 @@ void resolve_target_object_format(ZigTarget *target) {
626626
627627 case ZigLLVM_ppc:
628628 case ZigLLVM_ppc64:
629 if (is_os_darwin(target)) {
629 if (target_is_darwin(target)) {
630630 target->oformat = ZigLLVM_MachO;
631631 } else {
632632 target->oformat= ZigLLVM_ELF;
......@@ -1084,3 +1084,17 @@ bool target_is_arm(const ZigTarget *target) {
10841084 }
10851085 zig_unreachable();
10861086}
1087
1088// Valgrind supports more, but Zig does not support them yet.
1089bool target_has_valgrind_support(const ZigTarget *target) {
1090 switch (target->arch.arch) {
1091 case ZigLLVM_UnknownArch:
1092 zig_unreachable();
1093 case ZigLLVM_x86_64:
1094 return (target->os == OsLinux || target_is_darwin(target) || target->os == OsSolaris ||
1095 (target->os == OsWindows && target->env_type != ZigLLVM_MSVC));
1096 default:
1097 return false;
1098 }
1099 zig_unreachable();
1100}
src/target.hpp+2
......@@ -136,5 +136,7 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type);
136136
137137bool target_is_arm(const ZigTarget *target);
138138bool target_allows_addr_zero(const ZigTarget *target);
139bool target_has_valgrind_support(const ZigTarget *target);
140bool target_is_darwin(const ZigTarget *target);
139141
140142#endif