Commit db74832e40
Changed files (6)
src/all_types.hpp
@@ -1345,15 +1345,11 @@ struct ZigFn {
// in the case of async functions this is the implicit return type according to the
// zig source code, not according to zig ir
ZigType *src_implicit_return_type;
- bool is_test;
- FnInline fn_inline;
- FnAnalState anal_state;
IrExecutable ir_executable;
IrExecutable analyzed_executable;
size_t prealloc_bbc;
AstNode **param_source_nodes;
Buf **param_names;
- uint32_t align_bytes;
AstNode *fn_no_inline_set_node;
AstNode *fn_static_eval_set_node;
@@ -1363,13 +1359,22 @@ struct ZigFn {
Buf *section_name;
AstNode *set_alignstack_node;
- uint32_t alignstack_value;
AstNode *set_cold_node;
- bool is_cold;
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 is_cold;
+ bool is_test;
};
uint32_t fn_table_entry_hash(ZigFn*);
@@ -1612,6 +1617,12 @@ struct LinkLib {
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
struct CodeGen {
//////////////////////////// Runtime State
@@ -1813,6 +1824,7 @@ struct CodeGen {
OutType out_type;
ZigTarget zig_target;
TargetSubsystem subsystem;
+ ValgrindSupport valgrind_support;
bool is_static;
bool strip_debug_symbols;
bool is_test_build;
src/codegen.cpp
@@ -3341,6 +3341,77 @@ static bool value_is_all_undef(ConstExprValue *const_val) {
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) {
assert(type_has_bits(value_type));
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;
LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_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) {
@@ -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 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 valgrind_support = %s;\n", bool_to_str(want_valgrind_support(g)));
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->each_lib_rpath);
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->mios_version_min);
cache_usize(ch, g->version_major);
src/link.cpp
@@ -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_is_static(child_gen, true);
child_gen->disable_pic = parent_gen->disable_pic;
+ child_gen->valgrind_support = ValgrindSupportDisabled;
codegen_set_out_name(child_gen, buf_create_from_str(aname));
src/main.cpp
@@ -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"
" --color [auto|off|on] enable or disable colored error messages\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"
" -ftime-report print timing diagnostics\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;
bool is_single_threaded = false;
Buf *override_std_dir = nullptr;
+ ValgrindSupport valgrind_support = ValgrindSupportAuto;
if (argc >= 2 && strcmp(argv[1], "build") == 0) {
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(),
override_std_dir);
+ g->valgrind_support = valgrind_support;
g->enable_time_report = timing_info;
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"));
@@ -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);
CodeGen *g = codegen_create(fmt_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(),
nullptr);
+ g->valgrind_support = valgrind_support;
g->is_single_threaded = true;
codegen_set_out_name(g, buf_create_from_str("fmt"));
g->enable_cache = true;
@@ -577,6 +582,10 @@ int main(int argc, char **argv) {
timing_info = true;
} else if (strcmp(arg, "--disable-pic") == 0) {
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) {
system_linker_hack = true;
} else if (strcmp(arg, "--single-threaded") == 0) {
@@ -849,6 +858,7 @@ int main(int argc, char **argv) {
switch (cmd) {
case CmdBuiltin: {
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;
Buf *builtin_source = codegen_generate_builtin_source(g);
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(),
override_std_dir);
+ g->valgrind_support = valgrind_support;
g->subsystem = subsystem;
if (disable_pic) {
src/target.cpp
@@ -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) {
case OsMacOSX:
case OsIOS:
@@ -566,7 +566,7 @@ void resolve_target_object_format(ZigTarget *target) {
case ZigLLVM_thumb:
case ZigLLVM_x86:
case ZigLLVM_x86_64:
- if (is_os_darwin(target)) {
+ if (target_is_darwin(target)) {
target->oformat = ZigLLVM_MachO;
} else if (target->os == OsWindows) {
target->oformat = ZigLLVM_COFF;
@@ -626,7 +626,7 @@ void resolve_target_object_format(ZigTarget *target) {
case ZigLLVM_ppc:
case ZigLLVM_ppc64:
- if (is_os_darwin(target)) {
+ if (target_is_darwin(target)) {
target->oformat = ZigLLVM_MachO;
} else {
target->oformat= ZigLLVM_ELF;
@@ -1084,3 +1084,17 @@ bool target_is_arm(const ZigTarget *target) {
}
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();
+}
src/target.hpp
@@ -136,5 +136,7 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type);
bool target_is_arm(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