| author | |
| committer | |
| log | db74832e40f98960e5dc3e46c8196c59033ee0f9 |
| tree | 95d7e039968d665d0e0001c45c9c9c1f3e2a622d |
| parent | c9fb5240d6305bcef7db5e12b7676ba3d741c11e |
| signature | Commit is signed but in an unrecognized format. |
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 #19896 files changed, 130 insertions(+), 9 deletions(-)
src/all_types.hpp+18-6| ... | ... | @@ -1345,15 +1345,11 @@ struct ZigFn { |
| 1345 | 1345 | // in the case of async functions this is the implicit return type according to the |
| 1346 | 1346 | // zig source code, not according to zig ir |
| 1347 | 1347 | ZigType *src_implicit_return_type; |
| 1348 | bool is_test; | |
| 1349 | FnInline fn_inline; | |
| 1350 | FnAnalState anal_state; | |
| 1351 | 1348 | IrExecutable ir_executable; |
| 1352 | 1349 | IrExecutable analyzed_executable; |
| 1353 | 1350 | size_t prealloc_bbc; |
| 1354 | 1351 | AstNode **param_source_nodes; |
| 1355 | 1352 | Buf **param_names; |
| 1356 | uint32_t align_bytes; | |
| 1357 | 1353 | |
| 1358 | 1354 | AstNode *fn_no_inline_set_node; |
| 1359 | 1355 | AstNode *fn_static_eval_set_node; |
| ... | ... | @@ -1363,13 +1359,22 @@ struct ZigFn { |
| 1363 | 1359 | |
| 1364 | 1360 | Buf *section_name; |
| 1365 | 1361 | AstNode *set_alignstack_node; |
| 1366 | uint32_t alignstack_value; | |
| 1367 | 1362 | |
| 1368 | 1363 | AstNode *set_cold_node; |
| 1369 | bool is_cold; | |
| 1370 | 1364 | |
| 1371 | 1365 | 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 | ||
| 1372 | 1375 | bool calls_or_awaits_errorable_fn; |
| 1376 | bool is_cold; | |
| 1377 | bool is_test; | |
| 1373 | 1378 | }; |
| 1374 | 1379 | |
| 1375 | 1380 | uint32_t fn_table_entry_hash(ZigFn*); |
| ... | ... | @@ -1612,6 +1617,12 @@ struct LinkLib { |
| 1612 | 1617 | bool provided_explicitly; |
| 1613 | 1618 | }; |
| 1614 | 1619 | |
| 1620 | enum ValgrindSupport { | |
| 1621 | ValgrindSupportAuto, | |
| 1622 | ValgrindSupportDisabled, | |
| 1623 | ValgrindSupportEnabled, | |
| 1624 | }; | |
| 1625 | ||
| 1615 | 1626 | // When adding fields, check if they should be added to the hash computation in build_with_cache |
| 1616 | 1627 | struct CodeGen { |
| 1617 | 1628 | //////////////////////////// Runtime State |
| ... | ... | @@ -1813,6 +1824,7 @@ struct CodeGen { |
| 1813 | 1824 | OutType out_type; |
| 1814 | 1825 | ZigTarget zig_target; |
| 1815 | 1826 | TargetSubsystem subsystem; |
| 1827 | ValgrindSupport valgrind_support; | |
| 1816 | 1828 | bool is_static; |
| 1817 | 1829 | bool strip_debug_symbols; |
| 1818 | 1830 | bool is_test_build; |
src/codegen.cpp+81| ... | ... | @@ -3341,6 +3341,77 @@ static bool value_is_all_undef(ConstExprValue *const_val) { |
| 3341 | 3341 | zig_unreachable(); |
| 3342 | 3342 | } |
| 3343 | 3343 | |
| 3344 | static 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 | ||
| 3401 | static 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 | ||
| 3344 | 3415 | static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) { |
| 3345 | 3416 | assert(type_has_bits(value_type)); |
| 3346 | 3417 | 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_ |
| 3353 | 3424 | ZigType *usize = g->builtin_types.entry_usize; |
| 3354 | 3425 | LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); |
| 3355 | 3426 | 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 | } | |
| 3356 | 3435 | } |
| 3357 | 3436 | |
| 3358 | 3437 | static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) { |
| ... | ... | @@ -7525,6 +7604,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { |
| 7525 | 7604 | buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); |
| 7526 | 7605 | buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); |
| 7527 | 7606 | 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))); | |
| 7528 | 7608 | |
| 7529 | 7609 | buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n"); |
| 7530 | 7610 | |
| ... | ... | @@ -8489,6 +8569,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { |
| 8489 | 8569 | cache_bool(ch, g->linker_rdynamic); |
| 8490 | 8570 | cache_bool(ch, g->each_lib_rpath); |
| 8491 | 8571 | cache_bool(ch, g->disable_pic); |
| 8572 | cache_bool(ch, g->valgrind_support); | |
| 8492 | 8573 | cache_buf_opt(ch, g->mmacosx_version_min); |
| 8493 | 8574 | cache_buf_opt(ch, g->mios_version_min); |
| 8494 | 8575 | 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) |
| 55 | 55 | codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); |
| 56 | 56 | codegen_set_is_static(child_gen, true); |
| 57 | 57 | child_gen->disable_pic = parent_gen->disable_pic; |
| 58 | child_gen->valgrind_support = ValgrindSupportDisabled; | |
| 58 | 59 | |
| 59 | 60 | codegen_set_out_name(child_gen, buf_create_from_str(aname)); |
| 60 | 61 |
src/main.cpp+11| ... | ... | @@ -49,6 +49,8 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { |
| 49 | 49 | " --cache [auto|off|on] build in global cache, print out paths to stdout\n" |
| 50 | 50 | " --color [auto|off|on] enable or disable colored error messages\n" |
| 51 | 51 | " --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" | |
| 52 | 54 | " --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n" |
| 53 | 55 | " -ftime-report print timing diagnostics\n" |
| 54 | 56 | " --libc-include-dir [path] directory where libc stdlib.h resides\n" |
| ... | ... | @@ -396,6 +398,7 @@ int main(int argc, char **argv) { |
| 396 | 398 | TargetSubsystem subsystem = TargetSubsystemAuto; |
| 397 | 399 | bool is_single_threaded = false; |
| 398 | 400 | Buf *override_std_dir = nullptr; |
| 401 | ValgrindSupport valgrind_support = ValgrindSupportAuto; | |
| 399 | 402 | |
| 400 | 403 | if (argc >= 2 && strcmp(argv[1], "build") == 0) { |
| 401 | 404 | Buf zig_exe_path_buf = BUF_INIT; |
| ... | ... | @@ -433,6 +436,7 @@ int main(int argc, char **argv) { |
| 433 | 436 | |
| 434 | 437 | CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), |
| 435 | 438 | override_std_dir); |
| 439 | g->valgrind_support = valgrind_support; | |
| 436 | 440 | g->enable_time_report = timing_info; |
| 437 | 441 | buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); |
| 438 | 442 | codegen_set_out_name(g, buf_create_from_str("build")); |
| ... | ... | @@ -520,6 +524,7 @@ int main(int argc, char **argv) { |
| 520 | 524 | os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path); |
| 521 | 525 | CodeGen *g = codegen_create(fmt_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), |
| 522 | 526 | nullptr); |
| 527 | g->valgrind_support = valgrind_support; | |
| 523 | 528 | g->is_single_threaded = true; |
| 524 | 529 | codegen_set_out_name(g, buf_create_from_str("fmt")); |
| 525 | 530 | g->enable_cache = true; |
| ... | ... | @@ -577,6 +582,10 @@ int main(int argc, char **argv) { |
| 577 | 582 | timing_info = true; |
| 578 | 583 | } else if (strcmp(arg, "--disable-pic") == 0) { |
| 579 | 584 | 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; | |
| 580 | 589 | } else if (strcmp(arg, "--system-linker-hack") == 0) { |
| 581 | 590 | system_linker_hack = true; |
| 582 | 591 | } else if (strcmp(arg, "--single-threaded") == 0) { |
| ... | ... | @@ -849,6 +858,7 @@ int main(int argc, char **argv) { |
| 849 | 858 | switch (cmd) { |
| 850 | 859 | case CmdBuiltin: { |
| 851 | 860 | CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir); |
| 861 | g->valgrind_support = valgrind_support; | |
| 852 | 862 | g->is_single_threaded = is_single_threaded; |
| 853 | 863 | Buf *builtin_source = codegen_generate_builtin_source(g); |
| 854 | 864 | 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) { |
| 909 | 919 | } |
| 910 | 920 | CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir(), |
| 911 | 921 | override_std_dir); |
| 922 | g->valgrind_support = valgrind_support; | |
| 912 | 923 | g->subsystem = subsystem; |
| 913 | 924 | |
| 914 | 925 | if (disable_pic) { |
src/target.cpp+17-3| ... | ... | @@ -544,7 +544,7 @@ void get_target_triple(Buf *triple, const ZigTarget *target) { |
| 544 | 544 | } |
| 545 | 545 | } |
| 546 | 546 | |
| 547 | static bool is_os_darwin(ZigTarget *target) { | |
| 547 | bool target_is_darwin(const ZigTarget *target) { | |
| 548 | 548 | switch (target->os) { |
| 549 | 549 | case OsMacOSX: |
| 550 | 550 | case OsIOS: |
| ... | ... | @@ -566,7 +566,7 @@ void resolve_target_object_format(ZigTarget *target) { |
| 566 | 566 | case ZigLLVM_thumb: |
| 567 | 567 | case ZigLLVM_x86: |
| 568 | 568 | case ZigLLVM_x86_64: |
| 569 | if (is_os_darwin(target)) { | |
| 569 | if (target_is_darwin(target)) { | |
| 570 | 570 | target->oformat = ZigLLVM_MachO; |
| 571 | 571 | } else if (target->os == OsWindows) { |
| 572 | 572 | target->oformat = ZigLLVM_COFF; |
| ... | ... | @@ -626,7 +626,7 @@ void resolve_target_object_format(ZigTarget *target) { |
| 626 | 626 | |
| 627 | 627 | case ZigLLVM_ppc: |
| 628 | 628 | case ZigLLVM_ppc64: |
| 629 | if (is_os_darwin(target)) { | |
| 629 | if (target_is_darwin(target)) { | |
| 630 | 630 | target->oformat = ZigLLVM_MachO; |
| 631 | 631 | } else { |
| 632 | 632 | target->oformat= ZigLLVM_ELF; |
| ... | ... | @@ -1084,3 +1084,17 @@ bool target_is_arm(const ZigTarget *target) { |
| 1084 | 1084 | } |
| 1085 | 1085 | zig_unreachable(); |
| 1086 | 1086 | } |
| 1087 | ||
| 1088 | // Valgrind supports more, but Zig does not support them yet. | |
| 1089 | bool 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); |
| 136 | 136 | |
| 137 | 137 | bool target_is_arm(const ZigTarget *target); |
| 138 | 138 | bool target_allows_addr_zero(const ZigTarget *target); |
| 139 | bool target_has_valgrind_support(const ZigTarget *target); | |
| 140 | bool target_is_darwin(const ZigTarget *target); | |
| 139 | 141 | |
| 140 | 142 | #endif |