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
This commit is contained in:
Andrew Kelley 2019-02-19 12:07:56 -05:00
parent c9fb5240d6
commit db74832e40
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
6 changed files with 130 additions and 9 deletions

View File

@ -1345,15 +1345,11 @@ struct ZigFn {
// in the case of async functions this is the implicit return type according to the // in the case of async functions this is the implicit return type according to the
// zig source code, not according to zig ir // zig source code, not according to zig ir
ZigType *src_implicit_return_type; ZigType *src_implicit_return_type;
bool is_test;
FnInline fn_inline;
FnAnalState anal_state;
IrExecutable ir_executable; IrExecutable ir_executable;
IrExecutable analyzed_executable; IrExecutable analyzed_executable;
size_t prealloc_bbc; size_t prealloc_bbc;
AstNode **param_source_nodes; AstNode **param_source_nodes;
Buf **param_names; Buf **param_names;
uint32_t align_bytes;
AstNode *fn_no_inline_set_node; AstNode *fn_no_inline_set_node;
AstNode *fn_static_eval_set_node; AstNode *fn_static_eval_set_node;
@ -1363,13 +1359,22 @@ struct ZigFn {
Buf *section_name; Buf *section_name;
AstNode *set_alignstack_node; AstNode *set_alignstack_node;
uint32_t alignstack_value;
AstNode *set_cold_node; AstNode *set_cold_node;
bool is_cold;
ZigList<FnExport> export_list; ZigList<FnExport> export_list;
LLVMValueRef valgrind_client_request_array;
FnInline fn_inline;
FnAnalState anal_state;
uint32_t align_bytes;
uint32_t alignstack_value;
bool calls_or_awaits_errorable_fn; bool calls_or_awaits_errorable_fn;
bool is_cold;
bool is_test;
}; };
uint32_t fn_table_entry_hash(ZigFn*); uint32_t fn_table_entry_hash(ZigFn*);
@ -1612,6 +1617,12 @@ struct LinkLib {
bool provided_explicitly; bool provided_explicitly;
}; };
enum ValgrindSupport {
ValgrindSupportAuto,
ValgrindSupportDisabled,
ValgrindSupportEnabled,
};
// When adding fields, check if they should be added to the hash computation in build_with_cache // When adding fields, check if they should be added to the hash computation in build_with_cache
struct CodeGen { struct CodeGen {
//////////////////////////// Runtime State //////////////////////////// Runtime State
@ -1813,6 +1824,7 @@ struct CodeGen {
OutType out_type; OutType out_type;
ZigTarget zig_target; ZigTarget zig_target;
TargetSubsystem subsystem; TargetSubsystem subsystem;
ValgrindSupport valgrind_support;
bool is_static; bool is_static;
bool strip_debug_symbols; bool strip_debug_symbols;
bool is_test_build; bool is_test_build;

View File

@ -3341,6 +3341,77 @@ static bool value_is_all_undef(ConstExprValue *const_val) {
zig_unreachable(); zig_unreachable();
} }
static LLVMValueRef gen_valgrind_client_request(CodeGen *g, LLVMValueRef default_value, LLVMValueRef request,
LLVMValueRef a1, LLVMValueRef a2, LLVMValueRef a3, LLVMValueRef a4, LLVMValueRef a5)
{
if (!target_has_valgrind_support(&g->zig_target)) {
return default_value;
}
LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
bool asm_has_side_effects = true;
bool asm_is_alignstack = false;
if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
if (g->zig_target.os == OsLinux || target_is_darwin(&g->zig_target) || g->zig_target.os == OsSolaris ||
(g->zig_target.os == OsWindows && g->zig_target.env_type != ZigLLVM_MSVC))
{
if (g->cur_fn->valgrind_client_request_array == nullptr) {
LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
LLVMBasicBlockRef entry_block = LLVMGetEntryBasicBlock(g->cur_fn->llvm_value);
LLVMValueRef first_inst = LLVMGetFirstInstruction(entry_block);
LLVMPositionBuilderBefore(g->builder, first_inst);
LLVMTypeRef array_type_ref = LLVMArrayType(usize_type_ref, 6);
g->cur_fn->valgrind_client_request_array = LLVMBuildAlloca(g->builder, array_type_ref, "");
LLVMPositionBuilderAtEnd(g->builder, prev_block);
}
LLVMValueRef array_ptr = g->cur_fn->valgrind_client_request_array;
LLVMValueRef array_elements[] = {request, a1, a2, a3, a4, a5};
LLVMValueRef zero = LLVMConstInt(usize_type_ref, 0, false);
for (unsigned i = 0; i < 6; i += 1) {
LLVMValueRef indexes[] = {
zero,
LLVMConstInt(usize_type_ref, i, false),
};
LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indexes, 2, "");
LLVMBuildStore(g->builder, array_elements[i], elem_ptr);
}
Buf *asm_template = buf_create_from_str(
"rolq $$3, %rdi ; rolq $$13, %rdi\n"
"rolq $$61, %rdi ; rolq $$51, %rdi\n"
"xchgq %rbx,%rbx\n"
);
Buf *asm_constraints = buf_create_from_str(
"={rdx},{rax},0,~{cc},~{memory}"
);
unsigned input_and_output_count = 2;
LLVMValueRef array_ptr_as_usize = LLVMBuildPtrToInt(g->builder, array_ptr, usize_type_ref, "");
LLVMValueRef param_values[] = { array_ptr_as_usize, default_value };
LLVMTypeRef param_types[] = {usize_type_ref, usize_type_ref};
LLVMTypeRef function_type = LLVMFunctionType(usize_type_ref, param_types,
input_and_output_count, false);
LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(asm_template), buf_len(asm_template),
buf_ptr(asm_constraints), buf_len(asm_constraints), asm_has_side_effects, asm_is_alignstack,
LLVMInlineAsmDialectATT);
return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, "");
}
}
zig_unreachable();
}
static bool want_valgrind_support(CodeGen *g) {
if (!target_has_valgrind_support(&g->zig_target))
return false;
switch (g->valgrind_support) {
case ValgrindSupportDisabled:
return false;
case ValgrindSupportEnabled:
return true;
case ValgrindSupportAuto:
return g->build_mode == BuildModeDebug;
}
zig_unreachable();
}
static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) { static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) {
assert(type_has_bits(value_type)); assert(type_has_bits(value_type));
uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, value_type->type_ref); 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_
ZigType *usize = g->builtin_types.entry_usize; ZigType *usize = g->builtin_types.entry_usize;
LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false);
ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false); ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false);
// then tell valgrind that the memory is undefined even though we just memset it
if (want_valgrind_support(g)) {
static const uint32_t VG_USERREQ__MAKE_MEM_UNDEFINED = 1296236545;
LLVMValueRef zero = LLVMConstInt(usize->type_ref, 0, false);
LLVMValueRef req = LLVMConstInt(usize->type_ref, VG_USERREQ__MAKE_MEM_UNDEFINED, false);
LLVMValueRef ptr_as_usize = LLVMBuildPtrToInt(g->builder, dest_ptr, usize->type_ref, "");
gen_valgrind_client_request(g, zero, req, ptr_as_usize, byte_count, zero, zero, zero);
}
} }
static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) { static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) {
@ -7525,6 +7604,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode));
buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr));
buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing));
buf_appendf(contents, "pub const valgrind_support = %s;\n", bool_to_str(want_valgrind_support(g)));
buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n"); buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n");
@ -8489,6 +8569,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
cache_bool(ch, g->linker_rdynamic); cache_bool(ch, g->linker_rdynamic);
cache_bool(ch, g->each_lib_rpath); cache_bool(ch, g->each_lib_rpath);
cache_bool(ch, g->disable_pic); cache_bool(ch, g->disable_pic);
cache_bool(ch, g->valgrind_support);
cache_buf_opt(ch, g->mmacosx_version_min); cache_buf_opt(ch, g->mmacosx_version_min);
cache_buf_opt(ch, g->mios_version_min); cache_buf_opt(ch, g->mios_version_min);
cache_usize(ch, g->version_major); cache_usize(ch, g->version_major);

View File

@ -55,6 +55,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path)
codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); codegen_set_strip(child_gen, parent_gen->strip_debug_symbols);
codegen_set_is_static(child_gen, true); codegen_set_is_static(child_gen, true);
child_gen->disable_pic = parent_gen->disable_pic; child_gen->disable_pic = parent_gen->disable_pic;
child_gen->valgrind_support = ValgrindSupportDisabled;
codegen_set_out_name(child_gen, buf_create_from_str(aname)); codegen_set_out_name(child_gen, buf_create_from_str(aname));

View File

@ -49,6 +49,8 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
" --cache [auto|off|on] build in global cache, print out paths to stdout\n" " --cache [auto|off|on] build in global cache, print out paths to stdout\n"
" --color [auto|off|on] enable or disable colored error messages\n" " --color [auto|off|on] enable or disable colored error messages\n"
" --disable-pic disable Position Independent Code for libraries\n" " --disable-pic disable Position Independent Code for libraries\n"
" --disable-valgrind omit valgrind client requests in debug builds\n"
" --enable-valgrind include valgrind client requests release builds\n"
" --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n" " --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n"
" -ftime-report print timing diagnostics\n" " -ftime-report print timing diagnostics\n"
" --libc-include-dir [path] directory where libc stdlib.h resides\n" " --libc-include-dir [path] directory where libc stdlib.h resides\n"
@ -396,6 +398,7 @@ int main(int argc, char **argv) {
TargetSubsystem subsystem = TargetSubsystemAuto; TargetSubsystem subsystem = TargetSubsystemAuto;
bool is_single_threaded = false; bool is_single_threaded = false;
Buf *override_std_dir = nullptr; Buf *override_std_dir = nullptr;
ValgrindSupport valgrind_support = ValgrindSupportAuto;
if (argc >= 2 && strcmp(argv[1], "build") == 0) { if (argc >= 2 && strcmp(argv[1], "build") == 0) {
Buf zig_exe_path_buf = BUF_INIT; Buf zig_exe_path_buf = BUF_INIT;
@ -433,6 +436,7 @@ int main(int argc, char **argv) {
CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(),
override_std_dir); override_std_dir);
g->valgrind_support = valgrind_support;
g->enable_time_report = timing_info; g->enable_time_report = timing_info;
buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name);
codegen_set_out_name(g, buf_create_from_str("build")); codegen_set_out_name(g, buf_create_from_str("build"));
@ -520,6 +524,7 @@ int main(int argc, char **argv) {
os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path); os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path);
CodeGen *g = codegen_create(fmt_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), CodeGen *g = codegen_create(fmt_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(),
nullptr); nullptr);
g->valgrind_support = valgrind_support;
g->is_single_threaded = true; g->is_single_threaded = true;
codegen_set_out_name(g, buf_create_from_str("fmt")); codegen_set_out_name(g, buf_create_from_str("fmt"));
g->enable_cache = true; g->enable_cache = true;
@ -577,6 +582,10 @@ int main(int argc, char **argv) {
timing_info = true; timing_info = true;
} else if (strcmp(arg, "--disable-pic") == 0) { } else if (strcmp(arg, "--disable-pic") == 0) {
disable_pic = true; disable_pic = true;
} else if (strcmp(arg, "--enable-valgrind") == 0) {
valgrind_support = ValgrindSupportEnabled;
} else if (strcmp(arg, "--disable-valgrind") == 0) {
valgrind_support = ValgrindSupportDisabled;
} else if (strcmp(arg, "--system-linker-hack") == 0) { } else if (strcmp(arg, "--system-linker-hack") == 0) {
system_linker_hack = true; system_linker_hack = true;
} else if (strcmp(arg, "--single-threaded") == 0) { } else if (strcmp(arg, "--single-threaded") == 0) {
@ -849,6 +858,7 @@ int main(int argc, char **argv) {
switch (cmd) { switch (cmd) {
case CmdBuiltin: { case CmdBuiltin: {
CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir); CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir);
g->valgrind_support = valgrind_support;
g->is_single_threaded = is_single_threaded; g->is_single_threaded = is_single_threaded;
Buf *builtin_source = codegen_generate_builtin_source(g); Buf *builtin_source = codegen_generate_builtin_source(g);
if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) { 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) {
} }
CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir(), CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir(),
override_std_dir); override_std_dir);
g->valgrind_support = valgrind_support;
g->subsystem = subsystem; g->subsystem = subsystem;
if (disable_pic) { if (disable_pic) {

View File

@ -544,7 +544,7 @@ void get_target_triple(Buf *triple, const ZigTarget *target) {
} }
} }
static bool is_os_darwin(ZigTarget *target) { bool target_is_darwin(const ZigTarget *target) {
switch (target->os) { switch (target->os) {
case OsMacOSX: case OsMacOSX:
case OsIOS: case OsIOS:
@ -566,7 +566,7 @@ void resolve_target_object_format(ZigTarget *target) {
case ZigLLVM_thumb: case ZigLLVM_thumb:
case ZigLLVM_x86: case ZigLLVM_x86:
case ZigLLVM_x86_64: case ZigLLVM_x86_64:
if (is_os_darwin(target)) { if (target_is_darwin(target)) {
target->oformat = ZigLLVM_MachO; target->oformat = ZigLLVM_MachO;
} else if (target->os == OsWindows) { } else if (target->os == OsWindows) {
target->oformat = ZigLLVM_COFF; target->oformat = ZigLLVM_COFF;
@ -626,7 +626,7 @@ void resolve_target_object_format(ZigTarget *target) {
case ZigLLVM_ppc: case ZigLLVM_ppc:
case ZigLLVM_ppc64: case ZigLLVM_ppc64:
if (is_os_darwin(target)) { if (target_is_darwin(target)) {
target->oformat = ZigLLVM_MachO; target->oformat = ZigLLVM_MachO;
} else { } else {
target->oformat= ZigLLVM_ELF; target->oformat= ZigLLVM_ELF;
@ -1084,3 +1084,17 @@ bool target_is_arm(const ZigTarget *target) {
} }
zig_unreachable(); zig_unreachable();
} }
// Valgrind supports more, but Zig does not support them yet.
bool target_has_valgrind_support(const ZigTarget *target) {
switch (target->arch.arch) {
case ZigLLVM_UnknownArch:
zig_unreachable();
case ZigLLVM_x86_64:
return (target->os == OsLinux || target_is_darwin(target) || target->os == OsSolaris ||
(target->os == OsWindows && target->env_type != ZigLLVM_MSVC));
default:
return false;
}
zig_unreachable();
}

View File

@ -136,5 +136,7 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type);
bool target_is_arm(const ZigTarget *target); bool target_is_arm(const ZigTarget *target);
bool target_allows_addr_zero(const ZigTarget *target); bool target_allows_addr_zero(const ZigTarget *target);
bool target_has_valgrind_support(const ZigTarget *target);
bool target_is_darwin(const ZigTarget *target);
#endif #endif