Skip to content

Commit 443ec10

Browse files
authored
Add --target-sanitize=(memory|thread|address) (#59035)
Adds three hidden options that can enable the Memory / Thread / AddressSanitizer LLVM passes even when Julia is not built with any sanitizers enabled. In conjunction with #59033 this lets us build a ThreadSanitizer version of Julia in only a few minutes rather than hours. - Adds the `--target-sanitize=(memory|thread|address)` flags to enable those LLVM passes when compiling code ahead of time with `--output-*`. - JITted code will enable the passes for a sanitizer only if the runtime is built with it. - Adds the corresponding options to `CodegenParams`.
1 parent 998cb27 commit 443ec10

File tree

11 files changed

+194
-103
lines changed

11 files changed

+194
-103
lines changed

base/options.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ struct JLOptions
7070
gc_sweep_always_full::Int8
7171
compress_sysimage::Int8
7272
alert_on_critical_error::Int8
73+
target_sanitize_memory::Int8
74+
target_sanitize_thread::Int8
75+
target_sanitize_address::Int8
7376
end
7477

7578
# This runs early in the sysimage != is not defined yet

base/reflection.jl

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,17 +170,32 @@ struct CodegenParams
170170
"""
171171
force_emit_all::Cint
172172

173+
"""
174+
When enabled, run the MemorySanitizer pass.
175+
"""
176+
sanitize_memory::Cint
177+
"""
178+
When enabled, run the ThreadSanitizer pass.
179+
"""
180+
sanitize_thread::Cint
181+
"""
182+
When enabled, run the AddressSanitizer pass.
183+
"""
184+
sanitize_address::Cint
185+
173186
function CodegenParams(; track_allocations::Bool=true, code_coverage::Bool=true,
174187
prefer_specsig::Bool=false,
175188
gnu_pubnames::Bool=true, debug_info_kind::Cint = default_debug_info_kind(),
176189
debug_info_level::Cint = Cint(JLOptions().debug_level), safepoint_on_entry::Bool=true,
177-
gcstack_arg::Bool=true, use_jlplt::Bool=true, force_emit_all::Bool=false)
190+
gcstack_arg::Bool=true, use_jlplt::Bool=true, force_emit_all::Bool=false,
191+
sanitize_memory::Bool=false, sanitize_thread::Bool=false, sanitize_address::Bool=false)
178192
return new(
179193
Cint(track_allocations), Cint(code_coverage),
180194
Cint(prefer_specsig),
181195
Cint(gnu_pubnames), debug_info_kind,
182196
debug_info_level, Cint(safepoint_on_entry),
183-
Cint(gcstack_arg), Cint(use_jlplt), Cint(force_emit_all))
197+
Cint(gcstack_arg), Cint(use_jlplt), Cint(force_emit_all),
198+
Cint(sanitize_memory), Cint(sanitize_thread), Cint(sanitize_address))
184199
end
185200
end
186201

src/aotcompile.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ static void resolve_workqueue(jl_codegen_params_t &params, egal_set &method_root
477477
// emit specsig-to-(jl)invoke conversion
478478
proto.decl->setLinkage(GlobalVariable::InternalLinkage);
479479
//protodecl->setAlwaysInline();
480-
jl_init_function(proto.decl, params.TargetTriple);
480+
jl_init_function(proto.decl, params);
481481
jl_method_instance_t *mi = jl_get_ci_mi(codeinst);
482482
size_t nrealargs = jl_nparams(mi->specTypes); // number of actual arguments being passed
483483
bool is_opaque_closure = jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure;
@@ -777,6 +777,10 @@ void *jl_emit_native_impl(jl_array_t *codeinfos, LLVMOrcThreadSafeModuleRef llvm
777777
CreateNativeMax.updateMax(jl_array_nrows(codeinfos));
778778
if (cgparams == NULL)
779779
cgparams = &jl_default_cgparams;
780+
jl_cgparams_t target_cgparams = *cgparams;
781+
target_cgparams.sanitize_memory = jl_options.target_sanitize_memory;
782+
target_cgparams.sanitize_thread = jl_options.target_sanitize_thread;
783+
target_cgparams.sanitize_address = jl_options.target_sanitize_address;
780784
jl_native_code_desc_t *data = new jl_native_code_desc_t;
781785
orc::ThreadSafeContext ctx;
782786
orc::ThreadSafeModule backing;
@@ -795,7 +799,7 @@ void *jl_emit_native_impl(jl_array_t *codeinfos, LLVMOrcThreadSafeModuleRef llvm
795799
jl_codegen_params_t params(ctxt, std::move(target_info.first), std::move(target_info.second));
796800
if (!llvmmod)
797801
params.getContext().setDiscardValueNames(true);
798-
params.params = cgparams;
802+
params.params = &target_cgparams;
799803
assert(params.imaging_mode); // `_imaging_mode` controls if broken features like code-coverage are disabled
800804
params.external_linkage = external_linkage;
801805
params.temporary_roots = jl_alloc_array_1d(jl_array_any_type, 0);
@@ -1565,7 +1569,11 @@ static AOTOutputs add_output_impl(Module &M, TargetMachine &SourceTM, ShardTimer
15651569
SourceTM.getCodeModel(),
15661570
SourceTM.getOptLevel()));
15671571
fixupTM(*PMTM);
1568-
NewPM optimizer{std::move(PMTM), getOptLevel(jl_options.opt_level), OptimizationOptions::defaults(true, true)};
1572+
auto options = OptimizationOptions::defaults(true, true);
1573+
options.sanitize_memory = jl_options.target_sanitize_memory;
1574+
options.sanitize_thread = jl_options.target_sanitize_thread;
1575+
options.sanitize_address = jl_options.target_sanitize_address;
1576+
NewPM optimizer{std::move(PMTM), getOptLevel(jl_options.opt_level), options};
15691577
optimizer.run(M);
15701578
assert(!verifyLLVMIR(M));
15711579
bool inject_aliases = false;
@@ -2566,7 +2574,11 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t *dump, jl_method_instance_t *mi, jl_
25662574
}
25672575
assert(!verifyLLVMIR(*m.getModuleUnlocked()));
25682576
if (optimize) {
2569-
NewPM PM{jl_ExecutionEngine->cloneTargetMachine(), getOptLevel(jl_options.opt_level)};
2577+
auto opts = OptimizationOptions::defaults();
2578+
opts.sanitize_memory = params.sanitize_memory;
2579+
opts.sanitize_thread = params.sanitize_thread;
2580+
opts.sanitize_address = params.sanitize_address;
2581+
NewPM PM{jl_ExecutionEngine->cloneTargetMachine(), getOptLevel(jl_options.opt_level), opts};
25702582
//Safe b/c context lock is held by output
25712583
PM.run(*m.getModuleUnlocked());
25722584
assert(!verifyLLVMIR(*m.getModuleUnlocked()));

src/codegen.cpp

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2713,8 +2713,9 @@ static void jl_name_jlfuncparams_args(jl_codegen_params_t &params, Function *F)
27132713
F->getArg(3)->setName("sparams::Any");
27142714
}
27152715

2716-
void jl_init_function(Function *F, const Triple &TT) JL_NOTSAFEPOINT
2716+
void jl_init_function(Function *F, const jl_codegen_params_t &params) JL_NOTSAFEPOINT
27172717
{
2718+
auto &TT = params.TargetTriple;
27182719
// set any attributes that *must* be set on all functions
27192720
AttrBuilder attr(F->getContext());
27202721
if (TT.isOSWindows() && TT.getArch() == Triple::x86) {
@@ -2727,22 +2728,18 @@ void jl_init_function(Function *F, const Triple &TT) JL_NOTSAFEPOINT
27272728
attr.addUWTableAttr(llvm::UWTableKind::Default); // force NeedsWinEH
27282729
}
27292730
attr.addAttribute("frame-pointer", "all");
2730-
if (!TT.isOSWindows()) {
2731-
#if !defined(_COMPILER_ASAN_ENABLED_)
2731+
if (!TT.isOSWindows() && !JL_FEAT_TEST(params, sanitize_address)) {
27322732
// ASAN won't like us accessing undefined memory causing spurious issues,
27332733
// and Windows has platform-specific handling which causes it to mishandle
27342734
// this annotation. Other platforms should just ignore this if they don't
27352735
// implement it.
27362736
attr.addAttribute("probe-stack", "inline-asm");
27372737
//attr.addAttribute("stack-probe-size", "4096"); // can use this to change the default
2738-
#endif
27392738
}
2740-
#if defined(_COMPILER_ASAN_ENABLED_)
2741-
attr.addAttribute(Attribute::SanitizeAddress);
2742-
#endif
2743-
#if defined(_COMPILER_MSAN_ENABLED_)
2744-
attr.addAttribute(Attribute::SanitizeMemory);
2745-
#endif
2739+
if (JL_FEAT_TEST(params, sanitize_address))
2740+
attr.addAttribute(Attribute::SanitizeAddress);
2741+
if (JL_FEAT_TEST(params, sanitize_memory))
2742+
attr.addAttribute(Attribute::SanitizeMemory);
27462743
F->addFnAttrs(attr);
27472744
}
27482745

@@ -6247,7 +6244,7 @@ static std::pair<Function*, Function*> get_oc_function(jl_codectx_t &ctx, jl_met
62476244
F = Function::Create(get_func_sig(ctx.builder.getContext()),
62486245
Function::ExternalLinkage,
62496246
proto_oc, jl_Module);
6250-
jl_init_function(F, ctx.emission_context.TargetTriple);
6247+
jl_init_function(F, ctx.emission_context);
62516248
jl_name_jlfunc_args(ctx.emission_context, F);
62526249
F->setAttributes(AttributeList::get(ctx.builder.getContext(), {get_func_attrs(ctx.builder.getContext()), F->getAttributes()}));
62536250
}
@@ -6770,7 +6767,7 @@ static Function *emit_modifyhelper(jl_codectx_t &ctx2, const jl_cgval_t &op, con
67706767
ArgTy.push_back(ctx.builder.getPtrTy());
67716768
FunctionType *FT = FunctionType::get(elty, ArgTy, false);
67726769
Function *w = Function::Create(FT, GlobalVariable::PrivateLinkage, "", M);
6773-
jl_init_function(w, ctx.emission_context.TargetTriple);
6770+
jl_init_function(w, ctx.emission_context);
67746771
w->addFnAttr(Attribute::AlwaysInline);
67756772
w->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
67766773
Function::arg_iterator AI = w->arg_begin();
@@ -6829,7 +6826,7 @@ Function *emit_tojlinvoke(jl_code_instance_t *codeinst, Value *theFunc, Module *
68296826
Function *f = Function::Create(ctx.types().T_jlfunc,
68306827
GlobalVariable::InternalLinkage,
68316828
name, M);
6832-
jl_init_function(f, params.TargetTriple);
6829+
jl_init_function(f, params);
68336830
jl_name_jlfunc_args(params, f);
68346831
//f->setAlwaysInline();
68356832
ctx.f = f; // for jl_Module
@@ -7027,7 +7024,7 @@ void emit_specsig_to_fptr1(
70277024
static void emit_fptr1_wrapper(Module *M, StringRef gf_thunk_name, Value *target, jl_value_t *rettype_const, jl_value_t *declrt, jl_value_t *jlrettype, jl_codegen_params_t &params)
70287025
{
70297026
Function *w = Function::Create(get_func_sig(M->getContext()), GlobalVariable::ExternalLinkage, gf_thunk_name, M);
7030-
jl_init_function(w, params.TargetTriple);
7027+
jl_init_function(w, params);
70317028
w->setAttributes(AttributeList::get(M->getContext(), {get_func_attrs(M->getContext()), w->getAttributes()}));
70327029
w->addFnAttr(Attribute::OptimizeNone);
70337030
w->addFnAttr(Attribute::NoInline);
@@ -7071,7 +7068,7 @@ static void emit_specsig_to_specsig(
70717068
{
70727069
jl_returninfo_t returninfo = get_specsig_function(params, M, nullptr, gf_thunk_name, calltype, rettype, is_for_opaque_closure);
70737070
Function *gf_thunk = cast<Function>(returninfo.decl.getCallee());
7074-
jl_init_function(gf_thunk, params.TargetTriple);
7071+
jl_init_function(gf_thunk, params);
70757072
gf_thunk->setAttributes(AttributeList::get(gf_thunk->getContext(), {returninfo.attrs, gf_thunk->getAttributes()}));
70767073
emit_specsig_to_specsig(gf_thunk, returninfo.cc, returninfo.return_roots, calltype, rettype, is_for_opaque_closure, nargs, params, target, targetsig, targetrt, targetspec, rettype_const);
70777074
}
@@ -7317,7 +7314,7 @@ static Function *gen_cfun_wrapper(
73177314
Function *cw = Function::Create(functype,
73187315
GlobalVariable::ExternalLinkage,
73197316
funcName, M);
7320-
jl_init_function(cw, params.TargetTriple);
7317+
jl_init_function(cw, params);
73217318
cw->setAttributes(AttributeList::get(M->getContext(), {attributes, cw->getAttributes()}));
73227319

73237320
jl_codectx_t ctx(M->getContext(), params, 0, 0);
@@ -7539,7 +7536,7 @@ static Function *gen_cfun_wrapper(
75397536
FunctionType::get(getPointerTy(ctx.builder.getContext()), { getPointerTy(ctx.builder.getContext()), ctx.types().T_ppjlvalue }, false),
75407537
GlobalVariable::ExternalLinkage,
75417538
funcName, M);
7542-
jl_init_function(cw_make, ctx.emission_context.TargetTriple);
7539+
jl_init_function(cw_make, ctx.emission_context);
75437540
cw_make->getArg(0)->setName("wrapper");
75447541
cw_make->getArg(1)->setName("newval");
75457542
BasicBlock *b0 = BasicBlock::Create(ctx.builder.getContext(), "top", cw_make);
@@ -7799,7 +7796,7 @@ static void gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *abi, jl_va
77997796
{
78007797
++GeneratedInvokeWrappers;
78017798
Function *w = Function::Create(get_func_sig(M->getContext()), GlobalVariable::ExternalLinkage, funcName, M);
7802-
jl_init_function(w, params.TargetTriple);
7799+
jl_init_function(w, params);
78037800
jl_name_jlfunc_args(params, w);
78047801
w->setAttributes(AttributeList::get(M->getContext(), {get_func_attrs(M->getContext()), w->getAttributes()}));
78057802
w->addFnAttr(Attribute::OptimizeNone);
@@ -8040,7 +8037,7 @@ static jl_returninfo_t get_specsig_function(jl_codegen_params_t &params, Module
80408037
Function *f = M ? cast_or_null<Function>(M->getNamedValue(name)) : NULL;
80418038
if (f == NULL) {
80428039
f = Function::Create(ftype, GlobalVariable::ExternalLinkage, name, M);
8043-
jl_init_function(f, params.TargetTriple);
8040+
jl_init_function(f, params);
80448041
if (params.params->debug_info_level >= 2) {
80458042
ios_t sigbuf;
80468043
ios_mem(&sigbuf, 0);
@@ -8327,7 +8324,7 @@ static jl_llvm_functions_t
83278324
ArgNames, nreq);
83288325
f = cast<Function>(returninfo.decl.getCallee());
83298326
has_sret = (returninfo.cc == jl_returninfo_t::SRet || returninfo.cc == jl_returninfo_t::Union);
8330-
jl_init_function(f, ctx.emission_context.TargetTriple);
8327+
jl_init_function(f, ctx.emission_context);
83318328

83328329
// common pattern: see if all return statements are an argument in that
83338330
// case the apply-generic call can re-use the original box for the return
@@ -8366,7 +8363,7 @@ static jl_llvm_functions_t
83668363
f = Function::Create(needsparams ? ctx.types().T_jlfuncparams : ctx.types().T_jlfunc,
83678364
GlobalVariable::ExternalLinkage,
83688365
declarations.specFunctionObject, M);
8369-
jl_init_function(f, ctx.emission_context.TargetTriple);
8366+
jl_init_function(f, ctx.emission_context);
83708367
if (needsparams)
83718368
jl_name_jlfuncparams_args(ctx.emission_context, f);
83728369
else
@@ -8402,11 +8399,9 @@ static jl_llvm_functions_t
84028399
FnAttrs.addAttribute(Attribute::StackProtectStrong);
84038400
#endif
84048401

8405-
#ifdef _COMPILER_TSAN_ENABLED_
8406-
// TODO: enable this only when a argument like `-race` is passed to Julia
8407-
// add a macro for no_sanitize_thread
8408-
FnAttrs.addAttribute(llvm::Attribute::SanitizeThread);
8409-
#endif
8402+
// TODO: add a macro for no_sanitize_thread
8403+
if (JL_FEAT_TEST(ctx, sanitize_thread))
8404+
FnAttrs.addAttribute(llvm::Attribute::SanitizeThread);
84108405

84118406
// add the optimization level specified for this module, if any
84128407
int optlevel = jl_get_module_optlevel(ctx.module);
@@ -9773,7 +9768,7 @@ jl_llvm_functions_t jl_emit_codedecls(
97739768
Function *f = Function::Create(needsparams ? JuliaType::get_jlfuncparams_ty(M.getContext()) : JuliaType::get_jlfunc_ty(M.getContext()),
97749769
GlobalVariable::ExternalLinkage,
97759770
decls.specFunctionObject, M);
9776-
jl_init_function(f, params.TargetTriple);
9771+
jl_init_function(f, params);
97779772
f->setAttributes(AttributeList::get(M.getContext(), {get_func_attrs(M.getContext()), f->getAttributes()}));
97789773
}
97799774
});
@@ -9836,7 +9831,7 @@ static jl_llvm_functions_t jl_emit_oc_wrapper(orc::ThreadSafeModule &m, jl_codeg
98369831
std::string funcName = get_function_name(true, false, ctx.name, ctx.emission_context.TargetTriple);
98379832
jl_returninfo_t returninfo = get_specsig_function(params, M, NULL, funcName, mi->specTypes, rettype, true);
98389833
Function *gf_thunk = cast<Function>(returninfo.decl.getCallee());
9839-
jl_init_function(gf_thunk, ctx.emission_context.TargetTriple);
9834+
jl_init_function(gf_thunk, ctx.emission_context);
98409835
size_t nrealargs = jl_nparams(mi->specTypes);
98419836
emit_specsig_to_fptr1(gf_thunk, returninfo.cc, returninfo.return_roots,
98429837
mi->specTypes, rettype, true, nrealargs, ctx.emission_context,

src/init.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,23 @@ JL_DLLEXPORT jl_cgparams_t jl_default_cgparams = {
658658
/* safepoint_on_entry */ 1,
659659
/* gcstack_arg */ 1,
660660
/* use_jlplt*/ 1 ,
661-
/*force_emit_all=*/ 0};
661+
/*force_emit_all=*/ 0,
662+
#ifdef _COMPILER_MSAN_ENABLED_
663+
/* sanitize_memory */ 1,
664+
#else
665+
/* sanitize_memory */ 0,
666+
#endif
667+
#ifdef _COMPILER_TSAN_ENABLED_
668+
/* sanitize_thread */ 1,
669+
#else
670+
/* sanitize_thread */ 0,
671+
#endif
672+
#ifdef _COMPILER_ASAN_ENABLED_
673+
/* sanitize_address */ 1,
674+
#else
675+
/* sanitize_address */ 0,
676+
#endif
677+
};
662678

663679
static void init_global_mutexes(void) {
664680
JL_MUTEX_INIT(&jl_modules_mutex, "jl_modules_mutex");

src/jitlayers.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ static int jl_analyze_workqueue(jl_code_instance_t *callee, jl_codegen_params_t
482482
// emit specsig-to-(jl)invoke conversion
483483
proto.decl->setLinkage(GlobalVariable::InternalLinkage);
484484
//protodecl->setAlwaysInline();
485-
jl_init_function(proto.decl, params.TargetTriple);
485+
jl_init_function(proto.decl, params);
486486
// TODO: maybe this can be cached in codeinst->specfptr?
487487
int8_t gc_state = jl_gc_unsafe_enter(ct->ptls); // codegen may contain safepoints (such as jl_subtype calls)
488488
jl_method_instance_t *mi = jl_get_ci_mi(codeinst);
@@ -1450,7 +1450,7 @@ namespace {
14501450
auto operator()() JL_NOTSAFEPOINT {
14511451
auto TM = cantFail(JTMB.createTargetMachine());
14521452
fixupTM(*TM);
1453-
auto NPM = std::make_unique<NewPM>(std::move(TM), O);
1453+
auto NPM = std::make_unique<NewPM>(std::move(TM), O, OptimizationOptions::defaults());
14541454
// TODO this needs to be locked, as different resource pools may add to the printer vector at the same time
14551455
{
14561456
std::lock_guard<std::mutex> lock(llvm_printing_mutex);

src/jitlayers.h

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ struct OptimizationOptions {
9090
bool remove_ni;
9191
bool cleanup;
9292
bool warn_missed_transformations;
93+
bool sanitize_memory;
94+
bool sanitize_thread;
95+
bool sanitize_address;
9396

9497
static constexpr OptimizationOptions defaults(
9598
bool lower_intrinsics=true,
@@ -104,12 +107,29 @@ struct OptimizationOptions {
104107
bool enable_vector_pipeline=true,
105108
bool remove_ni=true,
106109
bool cleanup=true,
107-
bool warn_missed_transformations=false) {
110+
bool warn_missed_transformations=false,
111+
#ifdef _COMPILER_MSAN_ENABLED_
112+
bool sanitize_memory=true,
113+
#else
114+
bool sanitize_memory=false,
115+
#endif
116+
#ifdef _COMPILER_TSAN_ENABLED_
117+
bool sanitize_thread=true,
118+
#else
119+
bool sanitize_thread=false,
120+
#endif
121+
#ifdef _COMPILER_ASAN_ENABLED_
122+
bool sanitize_address=true
123+
#else
124+
bool sanitize_address=false
125+
#endif
126+
) JL_NOTSAFEPOINT {
108127
return {lower_intrinsics, dump_native, external_use, llvm_only,
109128
always_inline, enable_early_simplifications,
110129
enable_early_optimizations, enable_scalar_optimizations,
111130
enable_loop_optimizations, enable_vector_pipeline,
112-
remove_ni, cleanup, warn_missed_transformations};
131+
remove_ni, cleanup, warn_missed_transformations,
132+
sanitize_memory, sanitize_thread, sanitize_address};
113133
}
114134
};
115135

@@ -335,7 +355,7 @@ void emit_specsig_to_fptr1(
335355
jl_codegen_params_t &params,
336356
Function *target) JL_NOTSAFEPOINT;
337357
Function *get_or_emit_fptr1(StringRef Name, Module *M) JL_NOTSAFEPOINT;
338-
void jl_init_function(Function *F, const Triple &TT) JL_NOTSAFEPOINT;
358+
void jl_init_function(Function *F, const jl_codegen_params_t &params) JL_NOTSAFEPOINT;
339359

340360
void add_named_global(StringRef name, void *addr) JL_NOTSAFEPOINT;
341361

0 commit comments

Comments
 (0)