Reactor: Add support for specifying and modifying default configuration settings.
rr::Config holds the full reactor configuration state.
rr::Config::Edit holds edits on a config, which can be applied on top of the current defaults.
Default configurations are updated atomically, preventing modifications to the default state from tearing.
Bug: b/137167988
Change-Id: Ib05f2cfc31ab22fb9a891a267fffe33c18691028
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/33768
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Device/Blitter.cpp b/src/Device/Blitter.cpp
index 2ca5f3f..e5b41fc 100644
--- a/src/Device/Blitter.cpp
+++ b/src/Device/Blitter.cpp
@@ -1535,7 +1535,7 @@
}
}
- return function(vk::ReactorOptimizationLevel, "BlitRoutine");
+ return function(vk::ReactorConfig, "BlitRoutine");
}
Routine *Blitter::getBlitRoutine(const State &state)
@@ -1890,7 +1890,7 @@
}
}
- return function(vk::ReactorOptimizationLevel, "BlitRoutine");
+ return function(vk::ReactorConfig, "BlitRoutine");
}
void Blitter::updateBorders(vk::Image* image, const VkImageSubresourceLayers& subresourceLayers)
diff --git a/src/Device/PixelProcessor.cpp b/src/Device/PixelProcessor.cpp
index 51f4517..286880f 100644
--- a/src/Device/PixelProcessor.cpp
+++ b/src/Device/PixelProcessor.cpp
@@ -238,7 +238,7 @@
{
QuadRasterizer *generator = new PixelProgram(state, pipelineLayout, pixelShader, descriptorSets);
generator->generate();
- routine = (*generator)(vk::ReactorOptimizationLevel, "PixelRoutine_%0.8X", state.shaderID);
+ routine = (*generator)(vk::ReactorConfig, "PixelRoutine_%0.8X", state.shaderID);
delete generator;
routineCache->add(state, routine);
diff --git a/src/Device/VertexProcessor.cpp b/src/Device/VertexProcessor.cpp
index e8796c3..6dc4367 100644
--- a/src/Device/VertexProcessor.cpp
+++ b/src/Device/VertexProcessor.cpp
@@ -105,7 +105,7 @@
{
VertexRoutine *generator = new VertexProgram(state, pipelineLayout, vertexShader, descriptorSets);
generator->generate();
- routine = (*generator)(vk::ReactorOptimizationLevel, "VertexRoutine_%0.8X", state.shaderID);
+ routine = (*generator)(vk::ReactorConfig, "VertexRoutine_%0.8X", state.shaderID);
delete generator;
routineCache->add(state, routine);
diff --git a/src/Main/SwiftConfig.cpp b/src/Main/SwiftConfig.cpp
index aa17aa8..145ff5c 100644
--- a/src/Main/SwiftConfig.cpp
+++ b/src/Main/SwiftConfig.cpp
@@ -401,19 +401,19 @@
html += "<h2><em>Compiler optimizations</em></h2>\n";
html += "<table>\n";
- for(int pass = 0; pass < 10; pass++)
+ for(size_t pass = 0; pass < config.optimization.size(); pass++)
{
html += "<tr><td>Optimization pass " + itoa(pass + 1) + ":</td><td><select name='optimization" + itoa(pass + 1) + "' title='An optimization pass for the shader compiler.'>\n";
- html += "<option value='0'" + (config.optimization[pass] == 0 ? selected : empty) + ">Disabled" + (pass > 0 ? " (default)" : "") + "</option>\n";
- html += "<option value='1'" + (config.optimization[pass] == 1 ? selected : empty) + ">Instruction Combining" + (pass == 0 ? " (default)" : "") + "</option>\n";
- html += "<option value='2'" + (config.optimization[pass] == 2 ? selected : empty) + ">Control Flow Simplification</option>\n";
- html += "<option value='3'" + (config.optimization[pass] == 3 ? selected : empty) + ">Loop Invariant Code Motion</option>\n";
- html += "<option value='4'" + (config.optimization[pass] == 4 ? selected : empty) + ">Aggressive Dead Code Elimination</option>\n";
- html += "<option value='5'" + (config.optimization[pass] == 5 ? selected : empty) + ">Global Value Numbering</option>\n";
- html += "<option value='6'" + (config.optimization[pass] == 6 ? selected : empty) + ">Commutative Expressions Reassociation</option>\n";
- html += "<option value='7'" + (config.optimization[pass] == 7 ? selected : empty) + ">Dead Store Elimination</option>\n";
- html += "<option value='8'" + (config.optimization[pass] == 8 ? selected : empty) + ">Sparse Conditional Copy Propagation</option>\n";
- html += "<option value='9'" + (config.optimization[pass] == 9 ? selected : empty) + ">Scalar Replacement of Aggregates</option>\n";
+ html += "<option value='0'" + (config.optimization[pass] == rr::Optimization::Pass::Disabled ? selected : empty) + ">Disabled" + (pass > 0 ? " (default)" : "") + "</option>\n";
+ html += "<option value='1'" + (config.optimization[pass] == rr::Optimization::Pass::InstructionCombining ? selected : empty) + ">Instruction Combining" + (pass == 0 ? " (default)" : "") + "</option>\n";
+ html += "<option value='2'" + (config.optimization[pass] == rr::Optimization::Pass::CFGSimplification ? selected : empty) + ">Control Flow Simplification</option>\n";
+ html += "<option value='3'" + (config.optimization[pass] == rr::Optimization::Pass::LICM ? selected : empty) + ">Loop Invariant Code Motion</option>\n";
+ html += "<option value='4'" + (config.optimization[pass] == rr::Optimization::Pass::AggressiveDCE ? selected : empty) + ">Aggressive Dead Code Elimination</option>\n";
+ html += "<option value='5'" + (config.optimization[pass] == rr::Optimization::Pass::GVN ? selected : empty) + ">Global Value Numbering</option>\n";
+ html += "<option value='6'" + (config.optimization[pass] == rr::Optimization::Pass::Reassociate ? selected : empty) + ">Commutative Expressions Reassociation</option>\n";
+ html += "<option value='7'" + (config.optimization[pass] == rr::Optimization::Pass::DeadStoreElimination ? selected : empty) + ">Dead Store Elimination</option>\n";
+ html += "<option value='8'" + (config.optimization[pass] == rr::Optimization::Pass::SCCP ? selected : empty) + ">Sparse Conditional Copy Propagation</option>\n";
+ html += "<option value='9'" + (config.optimization[pass] == rr::Optimization::Pass::ScalarReplAggregates ? selected : empty) + ">Scalar Replacement of Aggregates</option>\n";
html += "</select></td></tr>\n";
}
@@ -652,7 +652,7 @@
}
else if(sscanf(post, "optimization%d=%d", &index, &integer))
{
- config.optimization[index - 1] = (rr::Optimization)integer;
+ config.optimization[index - 1] = (rr::Optimization::Pass)integer;
}
else if(strstr(post, "disableServer=on"))
{
@@ -737,9 +737,10 @@
config.enableSSSE3 = ini.getBoolean("Processor", "EnableSSSE3", true);
config.enableSSE4_1 = ini.getBoolean("Processor", "EnableSSE4_1", true);
- for(int pass = 0; pass < 10; pass++)
+ for(size_t pass = 0; pass < config.optimization.size(); pass++)
{
- config.optimization[pass] = (rr::Optimization)ini.getInteger("Optimization", "OptimizationPass" + itoa(pass + 1), pass == 0 ? rr::InstructionCombining : rr::Disabled);
+ auto def = pass == 0 ? rr::Optimization::Pass::InstructionCombining : rr::Optimization::Pass::Disabled;
+ config.optimization[pass] = (rr::Optimization::Pass)ini.getInteger("Optimization", "OptimizationPass" + itoa(pass + 1), (int)def);
}
config.disableServer = ini.getBoolean("Testing", "DisableServer", false);
@@ -795,9 +796,9 @@
ini.addValue("Processor", "EnableSSSE3", itoa(config.enableSSSE3));
ini.addValue("Processor", "EnableSSE4_1", itoa(config.enableSSE4_1));
- for(int pass = 0; pass < 10; pass++)
+ for(size_t pass = 0; pass < config.optimization.size(); pass++)
{
- ini.addValue("Optimization", "OptimizationPass" + itoa(pass + 1), itoa(config.optimization[pass]));
+ ini.addValue("Optimization", "OptimizationPass" + itoa(pass + 1), itoa((int)config.optimization[pass]));
}
ini.addValue("Testing", "DisableServer", itoa(config.disableServer));
diff --git a/src/Main/SwiftConfig.hpp b/src/Main/SwiftConfig.hpp
index ad3dcb5..a40648c 100644
--- a/src/Main/SwiftConfig.hpp
+++ b/src/Main/SwiftConfig.hpp
@@ -21,6 +21,7 @@
#include "Common/MutexLock.hpp"
#include "Common/Socket.hpp"
+#include <array>
#include <string>
namespace sw
@@ -48,7 +49,7 @@
bool enableSSE3;
bool enableSSSE3;
bool enableSSE4_1;
- rr::Optimization optimization[10];
+ std::array<rr::Optimization::Pass, 10> optimization;
bool disableServer;
bool keepSystemCursor;
bool forceWindowed;
diff --git a/src/Pipeline/SetupRoutine.cpp b/src/Pipeline/SetupRoutine.cpp
index 1973a71..6bd887d 100644
--- a/src/Pipeline/SetupRoutine.cpp
+++ b/src/Pipeline/SetupRoutine.cpp
@@ -453,7 +453,7 @@
Return(1);
}
- routine = function(vk::ReactorOptimizationLevel, "SetupRoutine");
+ routine = function(vk::ReactorConfig, "SetupRoutine");
}
void SetupRoutine::setupGradient(Pointer<Byte> &primitive, Pointer<Byte> &triangle, Float4 &w012, Float4 (&m)[3], Pointer<Byte> &v0, Pointer<Byte> &v1, Pointer<Byte> &v2, int attribute, int planeEquation, bool flat, bool perspective, int component)
diff --git a/src/Pipeline/SpirvShaderSampling.cpp b/src/Pipeline/SpirvShaderSampling.cpp
index e02c32a..b7d8c66 100644
--- a/src/Pipeline/SpirvShaderSampling.cpp
+++ b/src/Pipeline/SpirvShaderSampling.cpp
@@ -231,7 +231,7 @@
}
}
- return (ImageSampler*)function(vk::ReactorOptimizationLevel, "sampler")->getEntry();
+ return (ImageSampler*)function(vk::ReactorConfig, "sampler")->getEntry();
}
sw::TextureType SpirvShader::convertTextureType(VkImageViewType imageViewType)
diff --git a/src/Reactor/Coroutine.hpp b/src/Reactor/Coroutine.hpp
index 993e7e0..3eadec2 100644
--- a/src/Reactor/Coroutine.hpp
+++ b/src/Reactor/Coroutine.hpp
@@ -133,7 +133,7 @@
// called without building a new rr::Function or rr::Coroutine.
// While automatically called by operator(), finalize() should be called
// as early as possible to release the global Reactor mutex lock.
- inline void finalize(OptimizationLevel optLevel = OptimizationLevel::Default);
+ inline void finalize(const Config::Edit &cfg = Config::Edit::None);
// Starts execution of the coroutine and returns a unique_ptr to a
// Stream<> that exposes the await() function for obtaining yielded
@@ -164,11 +164,11 @@
}
template<typename Return, typename... Arguments>
- void Coroutine<Return(Arguments...)>::finalize(OptimizationLevel optLevel /* = OptimizationLevel::Default */)
+ void Coroutine<Return(Arguments...)>::finalize(const Config::Edit &cfg /* = Config::Edit::None */)
{
if(core != nullptr)
{
- routine.reset(core->acquireCoroutine("coroutine", optLevel));
+ routine.reset(core->acquireCoroutine("coroutine", cfg));
core.reset(nullptr);
}
}
diff --git a/src/Reactor/LLVMReactor.cpp b/src/Reactor/LLVMReactor.cpp
index c2c7ae7..2e4e5ba 100644
--- a/src/Reactor/LLVMReactor.cpp
+++ b/src/Reactor/LLVMReactor.cpp
@@ -105,6 +105,20 @@
namespace
{
+ // Default configuration settings. Must be accessed under mutex lock.
+ std::mutex defaultConfigLock;
+ rr::Config &defaultConfig()
+ {
+ // This uses a static in a function to avoid the cost of a global static
+ // initializer. See http://neugierig.org/software/chromium/notes/2011/08/static-initializers.html
+ static rr::Config config = rr::Config::Edit()
+ .set(rr::Optimization::Level::Default)
+ .add(rr::Optimization::Pass::ScalarReplAggregates)
+ .add(rr::Optimization::Pass::InstructionCombining)
+ .apply({});
+ return config;
+ }
+
class LLVMInitializer
{
protected:
@@ -228,7 +242,7 @@
std::unique_ptr<llvm::Module> module,
llvm::Function **funcs,
size_t count,
- rr::OptimizationLevel optLevel) :
+ const rr::Config &config) :
resolver(createLegacyLookupResolver(
session,
[&](const std::string &name) {
@@ -251,7 +265,7 @@
#ifdef ENABLE_RR_DEBUG_INFO
.setOptLevel(llvm::CodeGenOpt::None)
#else
- .setOptLevel(toLLVM(optLevel))
+ .setOptLevel(toLLVM(config.getOptimization().getLevel()))
#endif // ENABLE_RR_DEBUG_INFO
.setMCPU(JITGlobals::get()->mcpu)
.setMArch(JITGlobals::get()->march)
@@ -318,15 +332,15 @@
}
private:
- static ::llvm::CodeGenOpt::Level toLLVM(rr::OptimizationLevel level)
+ static ::llvm::CodeGenOpt::Level toLLVM(rr::Optimization::Level level)
{
switch (level)
{
- case rr::OptimizationLevel::None: return ::llvm::CodeGenOpt::None;
- case rr::OptimizationLevel::Less: return ::llvm::CodeGenOpt::Less;
- case rr::OptimizationLevel::Default: return ::llvm::CodeGenOpt::Default;
- case rr::OptimizationLevel::Aggressive: return ::llvm::CodeGenOpt::Aggressive;
- default: UNREACHABLE("Unknown OptimizationLevel %d", int(level));
+ case rr::Optimization::Level::None: return ::llvm::CodeGenOpt::None;
+ case rr::Optimization::Level::Less: return ::llvm::CodeGenOpt::Less;
+ case rr::Optimization::Level::Default: return ::llvm::CodeGenOpt::Default;
+ case rr::Optimization::Level::Aggressive: return ::llvm::CodeGenOpt::Aggressive;
+ default: UNREACHABLE("Unknown Optimization Level %d", int(level));
}
return ::llvm::CodeGenOpt::Default;
}
@@ -343,15 +357,17 @@
class JITBuilder
{
public:
- JITBuilder():
+ JITBuilder(const rr::Config &config) :
+ config(config),
module(new llvm::Module("", context)),
builder(new llvm::IRBuilder<>(context))
{
module->setDataLayout(JITGlobals::get()->dataLayout);
}
- void optimize()
+ void optimize(const rr::Config &cfg)
{
+
#ifdef ENABLE_RR_DEBUG_INFO
if (debugInfo != nullptr)
{
@@ -362,36 +378,35 @@
std::unique_ptr<llvm::legacy::PassManager> passManager(
new llvm::legacy::PassManager());
- passManager->add(llvm::createSROAPass());
-
- for(int pass = 0; pass < 10 && rr::optimization[pass] != rr::Disabled; pass++)
+ for(auto pass : cfg.getOptimization().getPasses())
{
- switch(rr::optimization[pass])
+ switch(pass)
{
- case rr::Disabled: break;
- case rr::CFGSimplification: passManager->add(llvm::createCFGSimplificationPass()); break;
- case rr::LICM: passManager->add(llvm::createLICMPass()); break;
- case rr::AggressiveDCE: passManager->add(llvm::createAggressiveDCEPass()); break;
- case rr::GVN: passManager->add(llvm::createGVNPass()); break;
- case rr::InstructionCombining: passManager->add(llvm::createInstructionCombiningPass()); break;
- case rr::Reassociate: passManager->add(llvm::createReassociatePass()); break;
- case rr::DeadStoreElimination: passManager->add(llvm::createDeadStoreEliminationPass()); break;
- case rr::SCCP: passManager->add(llvm::createSCCPPass()); break;
- case rr::ScalarReplAggregates: passManager->add(llvm::createSROAPass()); break;
+ case rr::Optimization::Pass::Disabled: break;
+ case rr::Optimization::Pass::CFGSimplification: passManager->add(llvm::createCFGSimplificationPass()); break;
+ case rr::Optimization::Pass::LICM: passManager->add(llvm::createLICMPass()); break;
+ case rr::Optimization::Pass::AggressiveDCE: passManager->add(llvm::createAggressiveDCEPass()); break;
+ case rr::Optimization::Pass::GVN: passManager->add(llvm::createGVNPass()); break;
+ case rr::Optimization::Pass::InstructionCombining: passManager->add(llvm::createInstructionCombiningPass()); break;
+ case rr::Optimization::Pass::Reassociate: passManager->add(llvm::createReassociatePass()); break;
+ case rr::Optimization::Pass::DeadStoreElimination: passManager->add(llvm::createDeadStoreEliminationPass()); break;
+ case rr::Optimization::Pass::SCCP: passManager->add(llvm::createSCCPPass()); break;
+ case rr::Optimization::Pass::ScalarReplAggregates: passManager->add(llvm::createSROAPass()); break;
default:
- UNREACHABLE("optimization[pass]: %d, pass: %d", int(rr::optimization[pass]), int(pass));
+ UNREACHABLE("pass: %d", int(pass));
}
}
passManager->run(*module);
}
- rr::Routine *acquireRoutine(llvm::Function **funcs, size_t count, rr::OptimizationLevel optLevel)
+ rr::Routine *acquireRoutine(llvm::Function **funcs, size_t count, const rr::Config &cfg)
{
ASSERT(module);
- return new JITRoutine(std::move(module), funcs, count, optLevel);
+ return new JITRoutine(std::move(module), funcs, count, cfg);
}
+ const rr::Config config;
llvm::LLVMContext context;
std::unique_ptr<llvm::Module> module;
std::unique_ptr<llvm::IRBuilder<>> builder;
@@ -1002,8 +1017,6 @@
return it->second;
}
- Optimization optimization[10] = {InstructionCombining, Disabled};
-
// The abstract Type* types are implemented as LLVM types, except that
// 64-bit vectors are emulated using 128-bit ones to avoid use of MMX in x86
// and VFP in ARM, and eliminate the overhead of converting them to explicit
@@ -1134,7 +1147,7 @@
::codegenMutex.lock(); // Reactor and LLVM are currently not thread safe
ASSERT(jit == nullptr);
- jit.reset(new JITBuilder());
+ jit.reset(new JITBuilder(Nucleus::getDefaultConfig()));
}
Nucleus::~Nucleus()
@@ -1143,8 +1156,29 @@
::codegenMutex.unlock();
}
- Routine *Nucleus::acquireRoutine(const char *name, OptimizationLevel optimizationLevel)
+ void Nucleus::setDefaultConfig(const Config &cfg)
{
+ std::unique_lock<std::mutex> lock(::defaultConfigLock);
+ ::defaultConfig() = cfg;
+ }
+
+ void Nucleus::adjustDefaultConfig(const Config::Edit &cfgEdit)
+ {
+ std::unique_lock<std::mutex> lock(::defaultConfigLock);
+ auto &config = ::defaultConfig();
+ config = cfgEdit.apply(config);
+ }
+
+ Config Nucleus::getDefaultConfig()
+ {
+ std::unique_lock<std::mutex> lock(::defaultConfigLock);
+ return ::defaultConfig();
+ }
+
+ Routine *Nucleus::acquireRoutine(const char *name, const Config::Edit &cfgEdit /* = Config::Edit::None */)
+ {
+ auto cfg = cfgEdit.apply(jit->config);
+
if(jit->builder->GetInsertBlock()->empty() || !jit->builder->GetInsertBlock()->back().isTerminator())
{
llvm::Type *type = jit->function->getReturnType();
@@ -1181,7 +1215,7 @@
}
#endif // defined(ENABLE_RR_LLVM_IR_VERIFICATION) || !defined(NDEBUG)
- optimize();
+ jit->optimize(cfg);
if(false)
{
@@ -1190,17 +1224,12 @@
jit->module->print(file, 0);
}
- auto routine = jit->acquireRoutine(&jit->function, 1, optimizationLevel);
+ auto routine = jit->acquireRoutine(&jit->function, 1, cfg);
jit.reset();
return routine;
}
- void Nucleus::optimize()
- {
- jit->optimize();
- }
-
Value *Nucleus::allocateStackVariable(Type *type, int arraySize)
{
// Need to allocate it in the entry block for mem2reg to work
@@ -4678,7 +4707,7 @@
jit->builder->SetInsertPoint(resumeBlock);
}
-Routine* Nucleus::acquireCoroutine(const char *name, OptimizationLevel optimizationLevel)
+Routine* Nucleus::acquireCoroutine(const char *name, const Config::Edit &cfgEdit /* = Config::Edit::None */)
{
ASSERT_MSG(jit->coroutine.id != nullptr, "acquireCoroutine() called without a call to createCoroutine()");
@@ -4707,7 +4736,8 @@
pm.add(llvm::createCoroCleanupPass());
pm.run(*jit->module);
- optimize();
+ auto cfg = cfgEdit.apply(jit->config);
+ jit->optimize(cfg);
if(false)
{
@@ -4720,7 +4750,7 @@
funcs[Nucleus::CoroutineEntryBegin] = jit->function;
funcs[Nucleus::CoroutineEntryAwait] = jit->coroutine.await;
funcs[Nucleus::CoroutineEntryDestroy] = jit->coroutine.destroy;
- auto routine = jit->acquireRoutine(funcs, Nucleus::CoroutineEntryCount, optimizationLevel);
+ auto routine = jit->acquireRoutine(funcs, Nucleus::CoroutineEntryCount, cfg);
jit.reset();
return routine;
diff --git a/src/Reactor/Nucleus.hpp b/src/Reactor/Nucleus.hpp
index 819d100..0bc953b 100644
--- a/src/Reactor/Nucleus.hpp
+++ b/src/Reactor/Nucleus.hpp
@@ -33,30 +33,85 @@
class BasicBlock;
class Routine;
- enum Optimization
+ // Optimization holds the optimization settings for code generation.
+ class Optimization
{
- Disabled = 0,
- InstructionCombining = 1,
- CFGSimplification = 2,
- LICM = 3,
- AggressiveDCE = 4,
- GVN = 5,
- Reassociate = 6,
- DeadStoreElimination = 7,
- SCCP = 8,
- ScalarReplAggregates = 9,
+ public:
+ enum class Level
+ {
+ None,
+ Less,
+ Default,
+ Aggressive,
+ };
- OptimizationCount
+ enum class Pass
+ {
+ Disabled,
+ InstructionCombining,
+ CFGSimplification,
+ LICM,
+ AggressiveDCE,
+ GVN,
+ Reassociate,
+ DeadStoreElimination,
+ SCCP,
+ ScalarReplAggregates,
+
+ Count,
+ };
+
+ using Passes = std::vector<Pass>;
+
+ Optimization() = default;
+ Optimization(Level level, const Passes & passes) : level(level), passes(passes) {}
+
+ Level getLevel() const { return level; }
+ const Passes & getPasses() const { return passes; }
+
+ private:
+ Level level = Level::Default;
+ Passes passes;
};
- extern Optimization optimization[10];
-
- enum class OptimizationLevel
+ // Config holds the Reactor configuration settings.
+ class Config
{
- None,
- Less,
- Default,
- Aggressive,
+ public:
+ // Edit holds a number of modifications to a config, that can be applied
+ // on an existing Config to produce a new Config with the specified
+ // changes.
+ class Edit
+ {
+ public:
+ static const Edit None;
+
+ Edit & set(Optimization::Level level) { optLevel = level; optLevelChanged = true; return *this; }
+ Edit & add(Optimization::Pass pass) { optPassEdits.push_back({ListEdit::Add, pass}); return *this; }
+ Edit & remove(Optimization::Pass pass) { optPassEdits.push_back({ListEdit::Remove, pass}); return *this; }
+ Edit & clearOptimizationPasses() { optPassEdits.push_back({ListEdit::Clear, Optimization::Pass::Disabled}); return *this; }
+
+ Config apply(const Config &cfg) const;
+
+ private:
+ enum class ListEdit { Add, Remove, Clear };
+ using OptPassesEdit = std::pair<ListEdit, Optimization::Pass>;
+
+ template <typename T>
+ void apply(const std::vector<std::pair<ListEdit, T>> & edits, std::vector<T>& list) const;
+
+ Optimization::Level optLevel;
+ bool optLevelChanged = false;
+ std::vector<OptPassesEdit> optPassEdits;
+ };
+
+ Config() = default;
+ Config(const Optimization & optimization) : optimization(optimization) {}
+
+ const Optimization & getOptimization() const { return optimization; }
+
+ private:
+ Optimization optimization;
};
class Nucleus
@@ -66,7 +121,13 @@
virtual ~Nucleus();
- Routine *acquireRoutine(const char *name, OptimizationLevel optimizationLevel);
+ // Default configuration to use when no other configuration is specified.
+ // The new configuration will be applied to subsequent reactor calls.
+ static void setDefaultConfig(const Config &cfg);
+ static void adjustDefaultConfig(const Config::Edit &cfgEdit);
+ static Config getDefaultConfig();
+
+ Routine *acquireRoutine(const char *name, const Config::Edit &cfgEdit = Config::Edit::None);
static Value *allocateStackVariable(Type *type, int arraySize = 0);
static BasicBlock *createBasicBlock();
@@ -93,7 +154,7 @@
};
static void createCoroutine(Type *ReturnType, std::vector<Type*> &Params);
- Routine *acquireCoroutine(const char *name, OptimizationLevel optimizationLevel);
+ Routine *acquireCoroutine(const char *name, const Config::Edit &cfg = Config::Edit::None);
static void yield(Value*);
// Terminators
@@ -219,9 +280,6 @@
static Value *createConstantVector(const double *constants, Type *type);
static Type *getPointerType(Type *elementType);
-
- private:
- void optimize();
};
}
diff --git a/src/Reactor/Reactor.cpp b/src/Reactor/Reactor.cpp
index bb94cf5..60ee656 100644
--- a/src/Reactor/Reactor.cpp
+++ b/src/Reactor/Reactor.cpp
@@ -21,8 +21,59 @@
#define REACTOR_MATERIALIZE_LVALUES_ON_DEFINITION 0
#endif
+namespace
+{
+ // Introduced in C++20.
+ template <class ForwardIterator, class UnaryPredicate>
+ ForwardIterator remove_if(ForwardIterator first, ForwardIterator last,
+ UnaryPredicate pred)
+ {
+ ForwardIterator result = first;
+ while (first!=last) {
+ if (!pred(*first)) {
+ *result = std::move(*first);
+ ++result;
+ }
+ ++first;
+ }
+ return result;
+ }
+}
+
namespace rr
{
+ const Config::Edit Config::Edit::None = {};
+
+ Config Config::Edit::apply(const Config &cfg) const
+ {
+ if (this == &None) { return cfg; }
+
+ auto level = optLevelChanged ? optLevel : cfg.optimization.getLevel();
+ auto passes = cfg.optimization.getPasses();
+ apply(optPassEdits, passes);
+ return Config{ Optimization{level, passes} };
+ }
+
+ template <typename T>
+ void rr::Config::Edit::apply(const std::vector<std::pair<ListEdit, T>> & edits, std::vector<T>& list) const
+ {
+ for (auto & edit : edits)
+ {
+ switch (edit.first)
+ {
+ case ListEdit::Add:
+ list.push_back(edit.second);
+ break;
+ case ListEdit::Remove:
+ ::remove_if(list.begin(), list.end(), [&](T item) { return item == edit.second; });
+ break;
+ case ListEdit::Clear:
+ list.clear();
+ break;
+ }
+ }
+ }
+
// Set of variables that do not have a stack location yet.
std::unordered_set<Variable*> Variable::unmaterializedVariables;
diff --git a/src/Reactor/Reactor.hpp b/src/Reactor/Reactor.hpp
index 1391275..4c82780 100644
--- a/src/Reactor/Reactor.hpp
+++ b/src/Reactor/Reactor.hpp
@@ -2465,7 +2465,7 @@
}
Routine *operator()(const char *name, ...);
- Routine *operator()(OptimizationLevel optLevel, const char *name, ...);
+ Routine *operator()(const Config::Edit &cfg, const char *name, ...);
protected:
Nucleus *core;
@@ -3040,11 +3040,11 @@
vsnprintf(fullName, 1024, name, vararg);
va_end(vararg);
- return core->acquireRoutine(fullName, OptimizationLevel::Default);
+ return core->acquireRoutine(fullName, Config::Edit::None);
}
template<typename Return, typename... Arguments>
- Routine *Function<Return(Arguments...)>::operator()(OptimizationLevel optLevel, const char *name, ...)
+ Routine *Function<Return(Arguments...)>::operator()(const Config::Edit &cfg, const char *name, ...)
{
char fullName[1024 + 1];
@@ -3053,7 +3053,7 @@
vsnprintf(fullName, 1024, name, vararg);
va_end(vararg);
- return core->acquireRoutine(fullName, optLevel);
+ return core->acquireRoutine(fullName, cfg);
}
template<class T, class S>
diff --git a/src/Reactor/SubzeroReactor.cpp b/src/Reactor/SubzeroReactor.cpp
index bcc2f7e..178c075 100644
--- a/src/Reactor/SubzeroReactor.cpp
+++ b/src/Reactor/SubzeroReactor.cpp
@@ -55,6 +55,18 @@
namespace
{
+ // Default configuration settings. Must be accessed under mutex lock.
+ std::mutex defaultConfigLock;
+ rr::Config &defaultConfig()
+ {
+ // This uses a static in a function to avoid the cost of a global static
+ // initializer. See http://neugierig.org/software/chromium/notes/2011/08/static-initializers.html
+ static rr::Config config = rr::Config::Edit()
+ .set(rr::Optimization::Level::Default)
+ .apply({});
+ return config;
+ }
+
Ice::GlobalContext *context = nullptr;
Ice::Cfg *function = nullptr;
Ice::CfgNode *basicBlock = nullptr;
@@ -77,6 +89,19 @@
#define __x86_64__ 1
#endif
+ static Ice::OptLevel toIce(rr::Optimization::Level level)
+ {
+ switch (level)
+ {
+ case rr::Optimization::Level::None: return Ice::Opt_0;
+ case rr::Optimization::Level::Less: return Ice::Opt_1;
+ case rr::Optimization::Level::Default: return Ice::Opt_2;
+ case rr::Optimization::Level::Aggressive: return Ice::Opt_2;
+ default: UNREACHABLE("Unknown Optimization Level %d", int(level));
+ }
+ return Ice::Opt_2;
+ }
+
class CPUID
{
public:
@@ -204,8 +229,6 @@
return Ice::typeWidthInBytes(T(type));
}
- Optimization optimization[10] = {InstructionCombining, Disabled};
-
using ElfHeader = std::conditional<sizeof(void*) == 8, Elf64_Ehdr, Elf32_Ehdr>::type;
using SectionHeader = std::conditional<sizeof(void*) == 8, Elf64_Shdr, Elf32_Shdr>::type;
@@ -548,7 +571,7 @@
Flags.setTargetInstructionSet(CPUID::SSE4_1 ? Ice::X86InstructionSet_SSE4_1 : Ice::X86InstructionSet_SSE2);
#endif
Flags.setOutFileType(Ice::FT_Elf);
- Flags.setOptLevel(Ice::Opt_2);
+ Flags.setOptLevel(toIce(getDefaultConfig().getOptimization().getLevel()));
Flags.setApplicationBinaryInterface(Ice::ABI_Platform);
Flags.setVerbose(false ? Ice::IceV_Most : Ice::IceV_None);
Flags.setDisableHybridAssembly(true);
@@ -585,7 +608,26 @@
::codegenMutex.unlock();
}
- Routine *Nucleus::acquireRoutine(const char *name, OptimizationLevel optimizationLevel)
+ void Nucleus::setDefaultConfig(const Config &cfg)
+ {
+ std::unique_lock<std::mutex> lock(::defaultConfigLock);
+ ::defaultConfig() = cfg;
+ }
+
+ void Nucleus::adjustDefaultConfig(const Config::Edit &cfgEdit)
+ {
+ std::unique_lock<std::mutex> lock(::defaultConfigLock);
+ auto &config = ::defaultConfig();
+ config = cfgEdit.apply(config);
+ }
+
+ Config Nucleus::getDefaultConfig()
+ {
+ std::unique_lock<std::mutex> lock(::defaultConfigLock);
+ return ::defaultConfig();
+ }
+
+ Routine *Nucleus::acquireRoutine(const char *name, const Config::Edit &cfgEdit /* = Config::Edit::None */)
{
if(basicBlock->getInsts().empty() || basicBlock->getInsts().back().getKind() != Ice::Inst::Ret)
{
@@ -594,7 +636,7 @@
::function->setFunctionName(Ice::GlobalString::createWithString(::context, name));
- optimize();
+ rr::optimize(::function);
::function->translate();
ASSERT(!::function->hasError());
@@ -624,11 +666,6 @@
return handoffRoutine;
}
- void Nucleus::optimize()
- {
- rr::optimize(::function);
- }
-
Value *Nucleus::allocateStackVariable(Type *t, int arraySize)
{
Ice::Type type = T(t);
@@ -3506,7 +3543,7 @@
void FlushDebug() {}
void Nucleus::createCoroutine(Type *YieldType, std::vector<Type*> &Params) { UNIMPLEMENTED("createCoroutine"); }
- Routine* Nucleus::acquireCoroutine(const char *name, OptimizationLevel optimizationLevel) { UNIMPLEMENTED("acquireCoroutine"); return nullptr; }
+ Routine* Nucleus::acquireCoroutine(const char *name, const Config::Edit &cfgEdit /* = Config::Edit::None */) { UNIMPLEMENTED("acquireCoroutine"); return nullptr; }
void Nucleus::yield(Value* val) { UNIMPLEMENTED("Yield"); }
}
diff --git a/src/Renderer/Renderer.cpp b/src/Renderer/Renderer.cpp
index 87b8dd1..fe9f52e 100644
--- a/src/Renderer/Renderer.cpp
+++ b/src/Renderer/Renderer.cpp
@@ -2855,10 +2855,13 @@
CPUID::setEnableSSE2(configuration.enableSSE2);
CPUID::setEnableSSE(configuration.enableSSE);
- for(int pass = 0; pass < 10; pass++)
+ rr::Config::Edit cfg;
+ cfg.clearOptimizationPasses();
+ for(auto pass : configuration.optimization)
{
- optimization[pass] = configuration.optimization[pass];
+ if (pass != rr::Optimization::Pass::Disabled) { cfg.add(pass); }
}
+ rr::Nucleus::adjustDefaultConfig(cfg);
forceWindowed = configuration.forceWindowed;
complementaryDepthBuffer = configuration.complementaryDepthBuffer;
diff --git a/src/Vulkan/VkConfig.h b/src/Vulkan/VkConfig.h
index adaa353..f6465b0 100644
--- a/src/Vulkan/VkConfig.h
+++ b/src/Vulkan/VkConfig.h
@@ -17,7 +17,7 @@
#include "Version.h"
-#include "Reactor/Nucleus.hpp" // ReactorOptimizationLevel
+#include "Reactor/Nucleus.hpp" // ReactorConfig
#include <Vulkan/VulkanPlatform.h>
@@ -79,8 +79,8 @@
MAX_POINT_SIZE = 1, // Large points are not supported. If/when we turn this on, must be >= 64.
};
-// Optimization level to use for JIT functions.
-static constexpr auto ReactorOptimizationLevel = rr::OptimizationLevel::Default;
+// Configuration to use for JIT functions.
+static const auto ReactorConfig = rr::Config::Edit().set(rr::Optimization::Level::Default);
}
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp
index ba1b8db..a9f1cec 100644
--- a/src/Vulkan/VkPipeline.cpp
+++ b/src/Vulkan/VkPipeline.cpp
@@ -262,7 +262,7 @@
// TODO(b/119409619): use allocator.
auto program = std::make_shared<sw::ComputeProgram>(key.getShader(), key.getLayout(), descriptorSets);
program->generate();
- program->finalize(vk::ReactorOptimizationLevel);
+ program->finalize(vk::ReactorConfig);
return program;
}