Skip to content

Commit 54b072e

Browse files
committed
jit: do not compile methods with different optlevel in the same llvm module
Currently, a module running with non-default opt-level could "poison" the opt-level of a method in another module if they happened to get compiled together (due to the optimization level picked for the LLVM module is picked as the minimum of the methods involved).
1 parent 9c0582f commit 54b072e

2 files changed

Lines changed: 118 additions & 57 deletions

File tree

Compiler/test/codegen.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,3 +1218,32 @@ _blackbox_licmloop_simple(1.0)
12181218
@test count(re_licmcall, ir_bb_simple) == 5 # unrolled, not hoisted
12191219
@test !occursin(re_raw_arg, ir_bb_simple)
12201220
end
1221+
1222+
module OptLevelNoLeakHeavy
1223+
@noinline function ksum1(a)
1224+
s = 0.0
1225+
@inbounds @simd for i in eachindex(a)
1226+
s += a[i] * a[i]
1227+
end
1228+
return s
1229+
end
1230+
@noinline function ksum2(a)
1231+
s = 0.0
1232+
@inbounds @simd for i in eachindex(a)
1233+
s += a[i] * a[i]
1234+
end
1235+
return s
1236+
end
1237+
end
1238+
module OptLevelNoLeakSlow
1239+
Base.Experimental.@optlevel 0
1240+
using ..OptLevelNoLeakHeavy: ksum2
1241+
call2(a) = ksum2(a)
1242+
end
1243+
@testset "per-module optlevel does not leak across co-compiled modules" begin
1244+
Random.seed!(1)
1245+
a = rand(100_000)
1246+
expected = OptLevelNoLeakHeavy.ksum1(a) # compiled directly at the default level
1247+
OptLevelNoLeakSlow.call2(a) # ksum2 first reached (co-compiled) via an -O0 module
1248+
@test OptLevelNoLeakHeavy.ksum2(a) === expected
1249+
end

src/jitlayers.cpp

Lines changed: 89 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -442,62 +442,100 @@ static void jl_do_dump_compile(jl_code_instance_t *codeinst, uint64_t time)
442442
julia_double_to_half(orig_time + time * 1e-9));
443443
}
444444

445+
static constexpr size_t N_optlevels = 4;
446+
447+
static int jl_clamp_optlevel(int requested) JL_NOTSAFEPOINT
448+
{
449+
int opt_level = std::max(static_cast<int>(jl_options.opt_level), 0);
450+
if (requested >= 0 && requested < opt_level)
451+
opt_level = requested;
452+
int opt_level_min = std::max(static_cast<int>(jl_options.opt_level_min), 0);
453+
if (opt_level < opt_level_min)
454+
opt_level = opt_level_min;
455+
return std::min(opt_level, static_cast<int>(N_optlevels) - 1);
456+
}
457+
458+
static int jl_codeinst_optlevel(jl_code_instance_t *ci) JL_NOTSAFEPOINT
459+
{
460+
jl_method_instance_t *mi = jl_get_ci_mi(ci);
461+
jl_value_t *def = mi->def.value;
462+
int raw = jl_get_module_optlevel(jl_is_method(def) ? ((jl_method_t*)def)->module
463+
: mi->def.module);
464+
return jl_clamp_optlevel(raw);
465+
}
466+
445467
extern "C" JL_DLLEXPORT_CODEGEN void
446468
jl_emit_codeinsts_to_jit_impl(jl_code_instance_t **codeinsts, jl_code_info_t **srcs, int len)
447469
{
448470
if (len == 0)
449471
return;
450472

451473
JL_TIMING(CODEINST_COMPILE, CODEINST_COMPILE);
452-
const char *name = name_from_method_instance(jl_get_ci_mi(codeinsts[len - 1]));
453-
auto ctx = std::make_unique<LLVMContext>();
454-
auto &dl = jl_ExecutionEngine->getDataLayout();
455-
auto &tt = jl_ExecutionEngine->getTargetTriple();
456-
auto mod = jl_create_llvm_module(name, *ctx, dl, tt);
457-
jl_codegen_output_t out{*mod};
458-
out.get_context().setDiscardValueNames(true);
459-
out.imaging_mode = false;
460-
JL_GC_PUSH1(&out.temporary_roots);
461474

475+
// Partition the batch by effective optimization level and emit each group as
476+
// its own LLVM module.
477+
std::map<int, SmallVector<int, 0>> groups;
462478
for (int i = 0; i < len; ++i) {
463-
jl_code_instance_t *codeinst = codeinsts[i];
464-
jl_code_info_t *src = srcs[i];
465-
jl_method_instance_t *mi = jl_get_ci_mi(codeinst);
466-
467-
if (jl_atomic_load_relaxed(&codeinst->invoke))
468-
continue;
479+
if (jl_atomic_load_relaxed(&codeinsts[i]->invoke))
480+
continue; // already compiled
481+
groups[jl_codeinst_optlevel(codeinsts[i])].push_back(i);
482+
}
483+
484+
for (auto &group : groups) {
485+
SmallVector<int, 0> &idxs = group.second;
486+
const char *name = name_from_method_instance(jl_get_ci_mi(codeinsts[idxs.back()]));
487+
auto ctx = std::make_unique<LLVMContext>();
488+
auto &dl = jl_ExecutionEngine->getDataLayout();
489+
auto &tt = jl_ExecutionEngine->getTargetTriple();
490+
auto mod = jl_create_llvm_module(name, *ctx, dl, tt);
491+
jl_codegen_output_t out{*mod};
492+
out.get_context().setDiscardValueNames(true);
493+
out.imaging_mode = false;
494+
JL_GC_PUSH1(&out.temporary_roots);
495+
496+
bool failed = false;
497+
for (int i : idxs) {
498+
jl_code_instance_t *codeinst = codeinsts[i];
499+
jl_code_info_t *src = srcs[i];
500+
jl_method_instance_t *mi = jl_get_ci_mi(codeinst);
501+
502+
if (jl_atomic_load_relaxed(&codeinst->invoke))
503+
continue;
469504

470-
out.temporary_roots = jl_alloc_array_1d(jl_array_any_type, 0);
471-
out.temporary_roots_set.clear();
472-
if (!jl_emit_codeinst(out, codeinst, src)) { // contains safepoints
473-
JL_GC_POP();
474-
return;
475-
}
505+
out.temporary_roots = jl_alloc_array_1d(jl_array_any_type, 0);
506+
out.temporary_roots_set.clear();
507+
if (!jl_emit_codeinst(out, codeinst, src)) { // contains safepoints
508+
failed = true;
509+
break;
510+
}
476511

477-
// contains safepoints
478-
jl_promote_method_roots(out, mi);
479-
emit_always_inline(out, jl_get_method_ir); // contains safepoints
512+
// contains safepoints
513+
jl_promote_method_roots(out, mi);
514+
emit_always_inline(out, jl_get_method_ir); // contains safepoints
480515

481-
// Non-opaque-closure MethodInstances are considered globally rooted
482-
// through their methods, but for OC, we need to create a global root
483-
// here.
484-
if (jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure)
485-
jl_as_global_root((jl_value_t*)mi, 1);
486-
}
516+
// Non-opaque-closure MethodInstances are considered globally rooted
517+
// through their methods, but for OC, we need to create a global root
518+
// here.
519+
if (jl_is_method(mi->def.value) && mi->def.method->is_for_opaque_closure)
520+
jl_as_global_root((jl_value_t*)mi, 1);
521+
}
487522

488-
out.temporary_roots = nullptr;
489-
out.temporary_roots_set.clear();
490-
JL_GC_POP();
523+
out.temporary_roots = nullptr;
524+
out.temporary_roots_set.clear();
525+
JL_GC_POP();
491526

492-
if (out.ci_funcs.empty())
493-
return;
527+
if (failed)
528+
return;
529+
if (out.ci_funcs.empty())
530+
continue;
494531

495-
emit_llvmcall_modules(out);
532+
emit_llvmcall_modules(out);
496533

497-
auto &ES = jl_ExecutionEngine->getExecutionSession();
498-
jl_emitted_output_t emitted =
499-
out.finish(std::move(ctx), std::move(mod), *ES.getSymbolStringPool());
500-
jl_ExecutionEngine->addOutput(std::move(emitted));
534+
auto &ES = jl_ExecutionEngine->getExecutionSession();
535+
jl_emitted_output_t emitted =
536+
out.finish(std::move(ctx), std::move(mod), *ES.getSymbolStringPool());
537+
jl_ExecutionEngine->addOutput(std::move(emitted));
538+
}
501539
}
502540

503541
extern "C" JL_DLLEXPORT_CODEGEN
@@ -625,32 +663,26 @@ static auto countBasicBlocks(const Function &F) JL_NOTSAFEPOINT
625663
return std::distance(F.begin(), F.end());
626664
}
627665

628-
static constexpr size_t N_optlevels = 4;
629-
630666
static void selectOptLevel(Module &M) JL_NOTSAFEPOINT {
631-
size_t opt_level = std::max(static_cast<int>(jl_options.opt_level), 0);
632-
do {
633-
if (jl_generating_output()) {
634-
opt_level = 0;
635-
break;
636-
}
637-
size_t opt_level_min = std::max(static_cast<int>(jl_options.opt_level_min), 0);
667+
size_t opt_level;
668+
if (jl_generating_output()) {
669+
opt_level = 0;
670+
}
671+
else {
672+
int requested = -1;
638673
for (auto &F : M) {
639674
if (!F.isDeclaration()) {
640675
Attribute attr = F.getFnAttribute("julia-optimization-level");
641676
StringRef val = attr.getValueAsString();
642677
if (val != "") {
643-
size_t ol = (size_t)val[0] - '0';
644-
if (ol < opt_level)
645-
opt_level = ol;
678+
int ol = (int)val[0] - '0';
679+
if (ol >= 0 && (requested < 0 || ol < requested))
680+
requested = ol;
646681
}
647682
}
648683
}
649-
if (opt_level < opt_level_min)
650-
opt_level = opt_level_min;
651-
} while (0);
652-
// currently -O3 is max
653-
opt_level = std::min(opt_level, N_optlevels - 1);
684+
opt_level = jl_clamp_optlevel(requested);
685+
}
654686
M.addModuleFlag(Module::Warning, "julia.optlevel", opt_level);
655687
}
656688

0 commit comments

Comments
 (0)