Skip to content

Commit

Permalink
add per-module optimization level macro (#34896)
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffBezanson authored and staticfloat committed Apr 21, 2020
1 parent bf38141 commit b2847ae
Show file tree
Hide file tree
Showing 13 changed files with 123 additions and 14 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ New language features

* Support for Unicode 13.0.0 (via utf8proc 2.5) ([#35282]).

* The compiler optimization level can now be set per-module using the experimental macro
`Base.Experimental.@optlevel n`. For code that is not performance-critical, setting
this to 0 or 1 can provide significant latency improvements ([#34896]).

Language changes
----------------

Expand Down
15 changes: 15 additions & 0 deletions base/experimental.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,20 @@ macro sync(block)
end
end

"""
Experimental.@optlevel n::Int
Set the optimization level (equivalent to the `-O` command line argument)
for code in the current module. Submodules inherit the setting of their
parent module.
Supported values are 0, 1, 2, and 3.
The effective optimization level is the minimum of that specified on the
command line and in per-module settings.
"""
macro optlevel(n::Int)
return Expr(:meta, :optlevel, n)
end

end
2 changes: 2 additions & 0 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ jl_sym_t *throw_undef_if_not_sym; jl_sym_t *getfield_undefref_sym;
jl_sym_t *gc_preserve_begin_sym; jl_sym_t *gc_preserve_end_sym;
jl_sym_t *coverageeffect_sym; jl_sym_t *escape_sym;
jl_sym_t *aliasscope_sym; jl_sym_t *popaliasscope_sym;
jl_sym_t *optlevel_sym;

static uint8_t flisp_system_image[] = {
#include <julia_flisp.boot.inc>
Expand Down Expand Up @@ -380,6 +381,7 @@ void jl_init_frontend(void)
isdefined_sym = jl_symbol("isdefined");
nospecialize_sym = jl_symbol("nospecialize");
specialize_sym = jl_symbol("specialize");
optlevel_sym = jl_symbol("optlevel");
macrocall_sym = jl_symbol("macrocall");
escape_sym = jl_symbol("escape");
hygienicscope_sym = jl_symbol("hygienic-scope");
Expand Down
17 changes: 9 additions & 8 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5242,6 +5242,14 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
#ifdef JL_DEBUG_BUILD
f->addFnAttr(Attribute::StackProtectStrong);
#endif

// add the optimization level specified for this module, if any
int optlevel = jl_get_module_optlevel(ctx.module);
if (optlevel >= 0 && optlevel <= 3) {
static const char* const optLevelStrings[] = { "0", "1", "2", "3" };
f->addFnAttr("julia-optimization-level", optLevelStrings[optlevel]);
}

ctx.f = f;

// Step 4b. determine debug info signature and other type info for locals
Expand Down Expand Up @@ -7458,14 +7466,7 @@ extern "C" void jl_init_llvm(void)
#else
None;
#endif
auto optlevel =
#ifdef DISABLE_OPT
CodeGenOpt::None;
#else
(jl_options.opt_level < 2 ? CodeGenOpt::None :
jl_options.opt_level == 2 ? CodeGenOpt::Default :
CodeGenOpt::Aggressive);
#endif
auto optlevel = CodeGenOptLevelFor(jl_options.opt_level);
jl_TargetMachine = TheTarget->createTargetMachine(
TheTriple.getTriple(), TheCPU, FeaturesStr,
options,
Expand Down
2 changes: 2 additions & 0 deletions src/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@ static void jl_serialize_module(jl_serializer_state *s, jl_module_t *m)
write_uint64(s->s, m->build_id);
write_int32(s->s, m->counter);
write_int32(s->s, m->nospecialize);
write_int32(s->s, m->optlevel);
}

static int is_ir_node(jl_value_t *v)
Expand Down Expand Up @@ -1915,6 +1916,7 @@ static jl_value_t *jl_deserialize_value_module(jl_serializer_state *s) JL_GC_DIS
m->build_id = read_uint64(s->s);
m->counter = read_int32(s->s);
m->nospecialize = read_int32(s->s);
m->optlevel = read_int32(s->s);
m->primary_world = jl_world_counter;
return (jl_value_t*)m;
}
Expand Down
6 changes: 6 additions & 0 deletions src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,12 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip,
if (jl_expr_nargs(stmt) == 1 && jl_exprarg(stmt, 0) == (jl_value_t*)specialize_sym) {
jl_set_module_nospecialize(s->module, 0);
}
if (jl_expr_nargs(stmt) == 2 && jl_exprarg(stmt, 0) == (jl_value_t*)optlevel_sym) {
if (jl_is_long(jl_exprarg(stmt, 1))) {
int n = jl_unbox_long(jl_exprarg(stmt, 1));
jl_set_module_optlevel(s->module, n);
}
}
}
else {
eval_stmt_value(stmt, s);
Expand Down
60 changes: 55 additions & 5 deletions src/jitlayers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -436,11 +436,61 @@ void JuliaOJIT::DebugObjectRegistrar::operator()(RTDyldObjHandleT H,
static_cast<const RuntimeDyld::LoadedObjectInfo*>(&LOS));
}

CodeGenOpt::Level CodeGenOptLevelFor(int optlevel)
{
#ifdef DISABLE_OPT
return CodeGenOpt::None;
#else
return optlevel < 2 ? CodeGenOpt::None :
optlevel == 2 ? CodeGenOpt::Default :
CodeGenOpt::Aggressive;
#endif
}

static void addPassesForOptLevel(legacy::PassManager &PM, TargetMachine &TM, raw_svector_ostream &ObjStream, MCContext *Ctx, int optlevel)
{
auto oldlevel = TM.getOptLevel();
TM.setOptLevel(CodeGenOptLevelFor(optlevel));
addTargetPasses(&PM, &TM);
addOptimizationPasses(&PM, optlevel);
if (TM.addPassesToEmitMC(PM, Ctx, ObjStream))
llvm_unreachable("Target does not support MC emission.");
TM.setOptLevel(oldlevel);
}

CompilerResultT JuliaOJIT::CompilerT::operator()(Module &M)
{
JL_TIMING(LLVM_OPT);
jit.PM.run(M);
int optlevel;
if (jl_generating_output()) {
optlevel = 0;
}
else {
optlevel = jl_options.opt_level;
for (auto &F : M.functions()) {
if (!F.getBasicBlockList().empty()) {
Attribute attr = F.getFnAttribute("julia-optimization-level");
StringRef val = attr.getValueAsString();
if (val != "") {
int ol = (int)val[0] - '0';
if (ol >= 0 && ol < optlevel)
optlevel = ol;
}
}
}
}
auto oldlevel = jit.TM.getOptLevel();
jit.TM.setOptLevel(CodeGenOptLevelFor(optlevel));
if (optlevel == 0)
jit.PM0.run(M);
else if (optlevel == 1)
jit.PM1.run(M);
else if (optlevel == 2)
jit.PM2.run(M);
else if (optlevel >= 3)
jit.PM3.run(M);
jit.TM.setOptLevel(oldlevel);

std::unique_ptr<MemoryBuffer> ObjBuffer(
new SmallVectorMemoryBuffer(std::move(jit.ObjBufferSV)));
auto Obj = object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef());
Expand Down Expand Up @@ -490,10 +540,10 @@ JuliaOJIT::JuliaOJIT(TargetMachine &TM)
CompilerT(this)
)
{
addTargetPasses(&PM, &TM);
addOptimizationPasses(&PM, jl_generating_output() ? 0 : jl_options.opt_level);
if (TM.addPassesToEmitMC(PM, Ctx, ObjStream))
llvm_unreachable("Target does not support MC emission.");
addPassesForOptLevel(PM0, TM, ObjStream, Ctx, 0);
addPassesForOptLevel(PM1, TM, ObjStream, Ctx, 1);
addPassesForOptLevel(PM2, TM, ObjStream, Ctx, 2);
addPassesForOptLevel(PM3, TM, ObjStream, Ctx, 3);

// Make sure SectionMemoryManager::getSymbolAddressInProcess can resolve
// symbols in the program as well. The nullptr argument to the function
Expand Down
7 changes: 6 additions & 1 deletion src/jitlayers.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,10 @@ class JuliaOJIT {
// object fits in its entirety
SmallVector<char, 4096> ObjBufferSV;
raw_svector_ostream ObjStream;
legacy::PassManager PM;
legacy::PassManager PM0; // per-optlevel pass managers
legacy::PassManager PM1;
legacy::PassManager PM2;
legacy::PassManager PM3;
MCContext *Ctx;
std::shared_ptr<RTDyldMemoryManager> MemMgr;
DebugObjectRegistrar registrar;
Expand Down Expand Up @@ -243,3 +246,5 @@ static inline bool isIntrinsicFunction(Function *F)
{
return F->isIntrinsic() || F->getName().startswith("julia.");
}

CodeGenOpt::Level CodeGenOptLevelFor(int optlevel);
3 changes: 3 additions & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ typedef struct _jl_module_t {
size_t primary_world;
uint32_t counter;
int32_t nospecialize; // global bit flags: initialization for new methods
int32_t optlevel;
uint8_t istopmod;
jl_mutex_t lock;
} jl_module_t;
Expand Down Expand Up @@ -1454,6 +1455,8 @@ extern JL_DLLEXPORT jl_module_t *jl_base_module JL_GLOBALLY_ROOTED;
extern JL_DLLEXPORT jl_module_t *jl_top_module JL_GLOBALLY_ROOTED;
JL_DLLEXPORT jl_module_t *jl_new_module(jl_sym_t *name);
JL_DLLEXPORT void jl_set_module_nospecialize(jl_module_t *self, int on);
JL_DLLEXPORT void jl_set_module_optlevel(jl_module_t *self, int lvl);
JL_DLLEXPORT int jl_get_module_optlevel(jl_module_t *m);
// get binding for reading
JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var);
JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var);
Expand Down
1 change: 1 addition & 0 deletions src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,7 @@ extern jl_sym_t *throw_undef_if_not_sym; extern jl_sym_t *getfield_undefref_sym;
extern jl_sym_t *gc_preserve_begin_sym; extern jl_sym_t *gc_preserve_end_sym;
extern jl_sym_t *failed_sym; extern jl_sym_t *done_sym; extern jl_sym_t *runnable_sym;
extern jl_sym_t *coverageeffect_sym; extern jl_sym_t *escape_sym;
extern jl_sym_t *optlevel_sym;

struct _jl_sysimg_fptrs_t;

Expand Down
16 changes: 16 additions & 0 deletions src/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ JL_DLLEXPORT jl_module_t *jl_new_module(jl_sym_t *name)
m->primary_world = 0;
m->counter = 1;
m->nospecialize = 0;
m->optlevel = -1;
JL_MUTEX_INIT(&m->lock);
htable_new(&m->bindings, 0);
arraylist_new(&m->usings, 0);
Expand Down Expand Up @@ -72,6 +73,21 @@ JL_DLLEXPORT void jl_set_module_nospecialize(jl_module_t *self, int on)
self->nospecialize = (on ? -1 : 0);
}

JL_DLLEXPORT void jl_set_module_optlevel(jl_module_t *self, int lvl)
{
self->optlevel = lvl;
}

JL_DLLEXPORT int jl_get_module_optlevel(jl_module_t *m)
{
int lvl = m->optlevel;
while (lvl == -1 && m->parent != m && m != jl_base_module) {
m = m->parent;
lvl = m->optlevel;
}
return lvl;
}

JL_DLLEXPORT void jl_set_istopmod(jl_module_t *self, uint8_t isprimary)
{
self->istopmod = 1;
Expand Down
2 changes: 2 additions & 0 deletions stdlib/InteractiveUtils/src/InteractiveUtils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

module InteractiveUtils

Base.Experimental.@optlevel 1

export apropos, edit, less, code_warntype, code_llvm, code_native, methodswith, varinfo,
versioninfo, subtypes, supertypes, @which, @edit, @less, @functionloc, @code_warntype,
@code_typed, @code_lowered, @code_llvm, @code_native, clipboard
Expand Down
2 changes: 2 additions & 0 deletions stdlib/REPL/src/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Run Evaluate Print Loop (REPL)
"""
module REPL

Base.Experimental.@optlevel 1

using Base.Meta, Sockets
import InteractiveUtils

Expand Down

0 comments on commit b2847ae

Please sign in to comment.