Commit 55bc37af by Ben Clayton

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: 's avatarBen Clayton <bclayton@google.com> Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com>
parent 8c4c9e37
...@@ -1535,7 +1535,7 @@ namespace sw ...@@ -1535,7 +1535,7 @@ namespace sw
} }
} }
return function(vk::ReactorOptimizationLevel, "BlitRoutine"); return function(vk::ReactorConfig, "BlitRoutine");
} }
Routine *Blitter::getBlitRoutine(const State &state) Routine *Blitter::getBlitRoutine(const State &state)
...@@ -1890,7 +1890,7 @@ namespace sw ...@@ -1890,7 +1890,7 @@ namespace sw
} }
} }
return function(vk::ReactorOptimizationLevel, "BlitRoutine"); return function(vk::ReactorConfig, "BlitRoutine");
} }
void Blitter::updateBorders(vk::Image* image, const VkImageSubresourceLayers& subresourceLayers) void Blitter::updateBorders(vk::Image* image, const VkImageSubresourceLayers& subresourceLayers)
......
...@@ -238,7 +238,7 @@ namespace sw ...@@ -238,7 +238,7 @@ namespace sw
{ {
QuadRasterizer *generator = new PixelProgram(state, pipelineLayout, pixelShader, descriptorSets); QuadRasterizer *generator = new PixelProgram(state, pipelineLayout, pixelShader, descriptorSets);
generator->generate(); generator->generate();
routine = (*generator)(vk::ReactorOptimizationLevel, "PixelRoutine_%0.8X", state.shaderID); routine = (*generator)(vk::ReactorConfig, "PixelRoutine_%0.8X", state.shaderID);
delete generator; delete generator;
routineCache->add(state, routine); routineCache->add(state, routine);
......
...@@ -105,7 +105,7 @@ namespace sw ...@@ -105,7 +105,7 @@ namespace sw
{ {
VertexRoutine *generator = new VertexProgram(state, pipelineLayout, vertexShader, descriptorSets); VertexRoutine *generator = new VertexProgram(state, pipelineLayout, vertexShader, descriptorSets);
generator->generate(); generator->generate();
routine = (*generator)(vk::ReactorOptimizationLevel, "VertexRoutine_%0.8X", state.shaderID); routine = (*generator)(vk::ReactorConfig, "VertexRoutine_%0.8X", state.shaderID);
delete generator; delete generator;
routineCache->add(state, routine); routineCache->add(state, routine);
......
...@@ -401,19 +401,19 @@ namespace sw ...@@ -401,19 +401,19 @@ namespace sw
html += "<h2><em>Compiler optimizations</em></h2>\n"; html += "<h2><em>Compiler optimizations</em></h2>\n";
html += "<table>\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 += "<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='0'" + (config.optimization[pass] == rr::Optimization::Pass::Disabled ? 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='1'" + (config.optimization[pass] == rr::Optimization::Pass::InstructionCombining ? 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='2'" + (config.optimization[pass] == rr::Optimization::Pass::CFGSimplification ? 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='3'" + (config.optimization[pass] == rr::Optimization::Pass::LICM ? 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='4'" + (config.optimization[pass] == rr::Optimization::Pass::AggressiveDCE ? 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='5'" + (config.optimization[pass] == rr::Optimization::Pass::GVN ? 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='6'" + (config.optimization[pass] == rr::Optimization::Pass::Reassociate ? 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='7'" + (config.optimization[pass] == rr::Optimization::Pass::DeadStoreElimination ? 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='8'" + (config.optimization[pass] == rr::Optimization::Pass::SCCP ? 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='9'" + (config.optimization[pass] == rr::Optimization::Pass::ScalarReplAggregates ? selected : empty) + ">Scalar Replacement of Aggregates</option>\n";
html += "</select></td></tr>\n"; html += "</select></td></tr>\n";
} }
...@@ -652,7 +652,7 @@ namespace sw ...@@ -652,7 +652,7 @@ namespace sw
} }
else if(sscanf(post, "optimization%d=%d", &index, &integer)) 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")) else if(strstr(post, "disableServer=on"))
{ {
...@@ -737,9 +737,10 @@ namespace sw ...@@ -737,9 +737,10 @@ namespace sw
config.enableSSSE3 = ini.getBoolean("Processor", "EnableSSSE3", true); config.enableSSSE3 = ini.getBoolean("Processor", "EnableSSSE3", true);
config.enableSSE4_1 = ini.getBoolean("Processor", "EnableSSE4_1", 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); config.disableServer = ini.getBoolean("Testing", "DisableServer", false);
...@@ -795,9 +796,9 @@ namespace sw ...@@ -795,9 +796,9 @@ namespace sw
ini.addValue("Processor", "EnableSSSE3", itoa(config.enableSSSE3)); ini.addValue("Processor", "EnableSSSE3", itoa(config.enableSSSE3));
ini.addValue("Processor", "EnableSSE4_1", itoa(config.enableSSE4_1)); 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)); ini.addValue("Testing", "DisableServer", itoa(config.disableServer));
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "Common/MutexLock.hpp" #include "Common/MutexLock.hpp"
#include "Common/Socket.hpp" #include "Common/Socket.hpp"
#include <array>
#include <string> #include <string>
namespace sw namespace sw
...@@ -48,7 +49,7 @@ namespace sw ...@@ -48,7 +49,7 @@ namespace sw
bool enableSSE3; bool enableSSE3;
bool enableSSSE3; bool enableSSSE3;
bool enableSSE4_1; bool enableSSE4_1;
rr::Optimization optimization[10]; std::array<rr::Optimization::Pass, 10> optimization;
bool disableServer; bool disableServer;
bool keepSystemCursor; bool keepSystemCursor;
bool forceWindowed; bool forceWindowed;
......
...@@ -453,7 +453,7 @@ namespace sw ...@@ -453,7 +453,7 @@ namespace sw
Return(1); 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) 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)
......
...@@ -231,7 +231,7 @@ SpirvShader::ImageSampler *SpirvShader::emitSamplerFunction(ImageInstruction ins ...@@ -231,7 +231,7 @@ SpirvShader::ImageSampler *SpirvShader::emitSamplerFunction(ImageInstruction ins
} }
} }
return (ImageSampler*)function(vk::ReactorOptimizationLevel, "sampler")->getEntry(); return (ImageSampler*)function(vk::ReactorConfig, "sampler")->getEntry();
} }
sw::TextureType SpirvShader::convertTextureType(VkImageViewType imageViewType) sw::TextureType SpirvShader::convertTextureType(VkImageViewType imageViewType)
......
...@@ -133,7 +133,7 @@ private: ...@@ -133,7 +133,7 @@ private:
// called without building a new rr::Function or rr::Coroutine. // called without building a new rr::Function or rr::Coroutine.
// While automatically called by operator(), finalize() should be called // While automatically called by operator(), finalize() should be called
// as early as possible to release the global Reactor mutex lock. // 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 // Starts execution of the coroutine and returns a unique_ptr to a
// Stream<> that exposes the await() function for obtaining yielded // Stream<> that exposes the await() function for obtaining yielded
...@@ -164,11 +164,11 @@ private: ...@@ -164,11 +164,11 @@ private:
} }
template<typename Return, typename... Arguments> 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) if(core != nullptr)
{ {
routine.reset(core->acquireCoroutine("coroutine", optLevel)); routine.reset(core->acquireCoroutine("coroutine", cfg));
core.reset(nullptr); core.reset(nullptr);
} }
} }
......
...@@ -105,6 +105,20 @@ namespace rr ...@@ -105,6 +105,20 @@ namespace rr
namespace 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 class LLVMInitializer
{ {
protected: protected:
...@@ -228,7 +242,7 @@ namespace ...@@ -228,7 +242,7 @@ namespace
std::unique_ptr<llvm::Module> module, std::unique_ptr<llvm::Module> module,
llvm::Function **funcs, llvm::Function **funcs,
size_t count, size_t count,
rr::OptimizationLevel optLevel) : const rr::Config &config) :
resolver(createLegacyLookupResolver( resolver(createLegacyLookupResolver(
session, session,
[&](const std::string &name) { [&](const std::string &name) {
...@@ -251,7 +265,7 @@ namespace ...@@ -251,7 +265,7 @@ namespace
#ifdef ENABLE_RR_DEBUG_INFO #ifdef ENABLE_RR_DEBUG_INFO
.setOptLevel(llvm::CodeGenOpt::None) .setOptLevel(llvm::CodeGenOpt::None)
#else #else
.setOptLevel(toLLVM(optLevel)) .setOptLevel(toLLVM(config.getOptimization().getLevel()))
#endif // ENABLE_RR_DEBUG_INFO #endif // ENABLE_RR_DEBUG_INFO
.setMCPU(JITGlobals::get()->mcpu) .setMCPU(JITGlobals::get()->mcpu)
.setMArch(JITGlobals::get()->march) .setMArch(JITGlobals::get()->march)
...@@ -318,15 +332,15 @@ namespace ...@@ -318,15 +332,15 @@ namespace
} }
private: private:
static ::llvm::CodeGenOpt::Level toLLVM(rr::OptimizationLevel level) static ::llvm::CodeGenOpt::Level toLLVM(rr::Optimization::Level level)
{ {
switch (level) switch (level)
{ {
case rr::OptimizationLevel::None: return ::llvm::CodeGenOpt::None; case rr::Optimization::Level::None: return ::llvm::CodeGenOpt::None;
case rr::OptimizationLevel::Less: return ::llvm::CodeGenOpt::Less; case rr::Optimization::Level::Less: return ::llvm::CodeGenOpt::Less;
case rr::OptimizationLevel::Default: return ::llvm::CodeGenOpt::Default; case rr::Optimization::Level::Default: return ::llvm::CodeGenOpt::Default;
case rr::OptimizationLevel::Aggressive: return ::llvm::CodeGenOpt::Aggressive; case rr::Optimization::Level::Aggressive: return ::llvm::CodeGenOpt::Aggressive;
default: UNREACHABLE("Unknown OptimizationLevel %d", int(level)); default: UNREACHABLE("Unknown Optimization Level %d", int(level));
} }
return ::llvm::CodeGenOpt::Default; return ::llvm::CodeGenOpt::Default;
} }
...@@ -343,15 +357,17 @@ namespace ...@@ -343,15 +357,17 @@ namespace
class JITBuilder class JITBuilder
{ {
public: public:
JITBuilder(): JITBuilder(const rr::Config &config) :
config(config),
module(new llvm::Module("", context)), module(new llvm::Module("", context)),
builder(new llvm::IRBuilder<>(context)) builder(new llvm::IRBuilder<>(context))
{ {
module->setDataLayout(JITGlobals::get()->dataLayout); module->setDataLayout(JITGlobals::get()->dataLayout);
} }
void optimize() void optimize(const rr::Config &cfg)
{ {
#ifdef ENABLE_RR_DEBUG_INFO #ifdef ENABLE_RR_DEBUG_INFO
if (debugInfo != nullptr) if (debugInfo != nullptr)
{ {
...@@ -362,36 +378,35 @@ namespace ...@@ -362,36 +378,35 @@ namespace
std::unique_ptr<llvm::legacy::PassManager> passManager( std::unique_ptr<llvm::legacy::PassManager> passManager(
new llvm::legacy::PassManager()); new llvm::legacy::PassManager());
passManager->add(llvm::createSROAPass()); for(auto pass : cfg.getOptimization().getPasses())
for(int pass = 0; pass < 10 && rr::optimization[pass] != rr::Disabled; pass++)
{ {
switch(rr::optimization[pass]) switch(pass)
{ {
case rr::Disabled: break; case rr::Optimization::Pass::Disabled: break;
case rr::CFGSimplification: passManager->add(llvm::createCFGSimplificationPass()); break; case rr::Optimization::Pass::CFGSimplification: passManager->add(llvm::createCFGSimplificationPass()); break;
case rr::LICM: passManager->add(llvm::createLICMPass()); break; case rr::Optimization::Pass::LICM: passManager->add(llvm::createLICMPass()); break;
case rr::AggressiveDCE: passManager->add(llvm::createAggressiveDCEPass()); break; case rr::Optimization::Pass::AggressiveDCE: passManager->add(llvm::createAggressiveDCEPass()); break;
case rr::GVN: passManager->add(llvm::createGVNPass()); break; case rr::Optimization::Pass::GVN: passManager->add(llvm::createGVNPass()); break;
case rr::InstructionCombining: passManager->add(llvm::createInstructionCombiningPass()); break; case rr::Optimization::Pass::InstructionCombining: passManager->add(llvm::createInstructionCombiningPass()); break;
case rr::Reassociate: passManager->add(llvm::createReassociatePass()); break; case rr::Optimization::Pass::Reassociate: passManager->add(llvm::createReassociatePass()); break;
case rr::DeadStoreElimination: passManager->add(llvm::createDeadStoreEliminationPass()); break; case rr::Optimization::Pass::DeadStoreElimination: passManager->add(llvm::createDeadStoreEliminationPass()); break;
case rr::SCCP: passManager->add(llvm::createSCCPPass()); break; case rr::Optimization::Pass::SCCP: passManager->add(llvm::createSCCPPass()); break;
case rr::ScalarReplAggregates: passManager->add(llvm::createSROAPass()); break; case rr::Optimization::Pass::ScalarReplAggregates: passManager->add(llvm::createSROAPass()); break;
default: default:
UNREACHABLE("optimization[pass]: %d, pass: %d", int(rr::optimization[pass]), int(pass)); UNREACHABLE("pass: %d", int(pass));
} }
} }
passManager->run(*module); 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); 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; llvm::LLVMContext context;
std::unique_ptr<llvm::Module> module; std::unique_ptr<llvm::Module> module;
std::unique_ptr<llvm::IRBuilder<>> builder; std::unique_ptr<llvm::IRBuilder<>> builder;
...@@ -1002,8 +1017,6 @@ namespace rr ...@@ -1002,8 +1017,6 @@ namespace rr
return it->second; return it->second;
} }
Optimization optimization[10] = {InstructionCombining, Disabled};
// The abstract Type* types are implemented as LLVM types, except that // 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 // 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 // and VFP in ARM, and eliminate the overhead of converting them to explicit
...@@ -1134,7 +1147,7 @@ namespace rr ...@@ -1134,7 +1147,7 @@ namespace rr
::codegenMutex.lock(); // Reactor and LLVM are currently not thread safe ::codegenMutex.lock(); // Reactor and LLVM are currently not thread safe
ASSERT(jit == nullptr); ASSERT(jit == nullptr);
jit.reset(new JITBuilder()); jit.reset(new JITBuilder(Nucleus::getDefaultConfig()));
} }
Nucleus::~Nucleus() Nucleus::~Nucleus()
...@@ -1143,8 +1156,29 @@ namespace rr ...@@ -1143,8 +1156,29 @@ namespace rr
::codegenMutex.unlock(); ::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()) if(jit->builder->GetInsertBlock()->empty() || !jit->builder->GetInsertBlock()->back().isTerminator())
{ {
llvm::Type *type = jit->function->getReturnType(); llvm::Type *type = jit->function->getReturnType();
...@@ -1181,7 +1215,7 @@ namespace rr ...@@ -1181,7 +1215,7 @@ namespace rr
} }
#endif // defined(ENABLE_RR_LLVM_IR_VERIFICATION) || !defined(NDEBUG) #endif // defined(ENABLE_RR_LLVM_IR_VERIFICATION) || !defined(NDEBUG)
optimize(); jit->optimize(cfg);
if(false) if(false)
{ {
...@@ -1190,17 +1224,12 @@ namespace rr ...@@ -1190,17 +1224,12 @@ namespace rr
jit->module->print(file, 0); jit->module->print(file, 0);
} }
auto routine = jit->acquireRoutine(&jit->function, 1, optimizationLevel); auto routine = jit->acquireRoutine(&jit->function, 1, cfg);
jit.reset(); jit.reset();
return routine; return routine;
} }
void Nucleus::optimize()
{
jit->optimize();
}
Value *Nucleus::allocateStackVariable(Type *type, int arraySize) Value *Nucleus::allocateStackVariable(Type *type, int arraySize)
{ {
// Need to allocate it in the entry block for mem2reg to work // Need to allocate it in the entry block for mem2reg to work
...@@ -4678,7 +4707,7 @@ void Nucleus::yield(Value* val) ...@@ -4678,7 +4707,7 @@ void Nucleus::yield(Value* val)
jit->builder->SetInsertPoint(resumeBlock); 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()"); ASSERT_MSG(jit->coroutine.id != nullptr, "acquireCoroutine() called without a call to createCoroutine()");
...@@ -4707,7 +4736,8 @@ Routine* Nucleus::acquireCoroutine(const char *name, OptimizationLevel optimizat ...@@ -4707,7 +4736,8 @@ Routine* Nucleus::acquireCoroutine(const char *name, OptimizationLevel optimizat
pm.add(llvm::createCoroCleanupPass()); pm.add(llvm::createCoroCleanupPass());
pm.run(*jit->module); pm.run(*jit->module);
optimize(); auto cfg = cfgEdit.apply(jit->config);
jit->optimize(cfg);
if(false) if(false)
{ {
...@@ -4720,7 +4750,7 @@ Routine* Nucleus::acquireCoroutine(const char *name, OptimizationLevel optimizat ...@@ -4720,7 +4750,7 @@ Routine* Nucleus::acquireCoroutine(const char *name, OptimizationLevel optimizat
funcs[Nucleus::CoroutineEntryBegin] = jit->function; funcs[Nucleus::CoroutineEntryBegin] = jit->function;
funcs[Nucleus::CoroutineEntryAwait] = jit->coroutine.await; funcs[Nucleus::CoroutineEntryAwait] = jit->coroutine.await;
funcs[Nucleus::CoroutineEntryDestroy] = jit->coroutine.destroy; funcs[Nucleus::CoroutineEntryDestroy] = jit->coroutine.destroy;
auto routine = jit->acquireRoutine(funcs, Nucleus::CoroutineEntryCount, optimizationLevel); auto routine = jit->acquireRoutine(funcs, Nucleus::CoroutineEntryCount, cfg);
jit.reset(); jit.reset();
return routine; return routine;
......
...@@ -33,30 +33,85 @@ namespace rr ...@@ -33,30 +33,85 @@ namespace rr
class BasicBlock; class BasicBlock;
class Routine; class Routine;
enum Optimization // Optimization holds the optimization settings for code generation.
class Optimization
{ {
Disabled = 0, public:
InstructionCombining = 1, enum class Level
CFGSimplification = 2, {
LICM = 3, None,
AggressiveDCE = 4, Less,
GVN = 5, Default,
Reassociate = 6, Aggressive,
DeadStoreElimination = 7, };
SCCP = 8,
ScalarReplAggregates = 9, enum class Pass
{
OptimizationCount Disabled,
}; InstructionCombining,
CFGSimplification,
LICM,
AggressiveDCE,
GVN,
Reassociate,
DeadStoreElimination,
SCCP,
ScalarReplAggregates,
Count,
};
extern Optimization optimization[10]; using Passes = std::vector<Pass>;
enum class OptimizationLevel 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;
};
// Config holds the Reactor configuration settings.
class Config
{ {
None, public:
Less, // Edit holds a number of modifications to a config, that can be applied
Default, // on an existing Config to produce a new Config with the specified
Aggressive, // 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 class Nucleus
...@@ -66,7 +121,13 @@ namespace rr ...@@ -66,7 +121,13 @@ namespace rr
virtual ~Nucleus(); 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 Value *allocateStackVariable(Type *type, int arraySize = 0);
static BasicBlock *createBasicBlock(); static BasicBlock *createBasicBlock();
...@@ -93,7 +154,7 @@ namespace rr ...@@ -93,7 +154,7 @@ namespace rr
}; };
static void createCoroutine(Type *ReturnType, std::vector<Type*> &Params); 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*); static void yield(Value*);
// Terminators // Terminators
...@@ -219,9 +280,6 @@ namespace rr ...@@ -219,9 +280,6 @@ namespace rr
static Value *createConstantVector(const double *constants, Type *type); static Value *createConstantVector(const double *constants, Type *type);
static Type *getPointerType(Type *elementType); static Type *getPointerType(Type *elementType);
private:
void optimize();
}; };
} }
......
...@@ -21,8 +21,59 @@ ...@@ -21,8 +21,59 @@
#define REACTOR_MATERIALIZE_LVALUES_ON_DEFINITION 0 #define REACTOR_MATERIALIZE_LVALUES_ON_DEFINITION 0
#endif #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 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. // Set of variables that do not have a stack location yet.
std::unordered_set<Variable*> Variable::unmaterializedVariables; std::unordered_set<Variable*> Variable::unmaterializedVariables;
......
...@@ -2465,7 +2465,7 @@ namespace rr ...@@ -2465,7 +2465,7 @@ namespace rr
} }
Routine *operator()(const char *name, ...); Routine *operator()(const char *name, ...);
Routine *operator()(OptimizationLevel optLevel, const char *name, ...); Routine *operator()(const Config::Edit &cfg, const char *name, ...);
protected: protected:
Nucleus *core; Nucleus *core;
...@@ -3040,11 +3040,11 @@ namespace rr ...@@ -3040,11 +3040,11 @@ namespace rr
vsnprintf(fullName, 1024, name, vararg); vsnprintf(fullName, 1024, name, vararg);
va_end(vararg); va_end(vararg);
return core->acquireRoutine(fullName, OptimizationLevel::Default); return core->acquireRoutine(fullName, Config::Edit::None);
} }
template<typename Return, typename... Arguments> 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]; char fullName[1024 + 1];
...@@ -3053,7 +3053,7 @@ namespace rr ...@@ -3053,7 +3053,7 @@ namespace rr
vsnprintf(fullName, 1024, name, vararg); vsnprintf(fullName, 1024, name, vararg);
va_end(vararg); va_end(vararg);
return core->acquireRoutine(fullName, optLevel); return core->acquireRoutine(fullName, cfg);
} }
template<class T, class S> template<class T, class S>
......
...@@ -55,6 +55,18 @@ ...@@ -55,6 +55,18 @@
namespace 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::GlobalContext *context = nullptr;
Ice::Cfg *function = nullptr; Ice::Cfg *function = nullptr;
Ice::CfgNode *basicBlock = nullptr; Ice::CfgNode *basicBlock = nullptr;
...@@ -77,6 +89,19 @@ namespace ...@@ -77,6 +89,19 @@ namespace
#define __x86_64__ 1 #define __x86_64__ 1
#endif #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 class CPUID
{ {
public: public:
...@@ -204,8 +229,6 @@ namespace rr ...@@ -204,8 +229,6 @@ namespace rr
return Ice::typeWidthInBytes(T(type)); return Ice::typeWidthInBytes(T(type));
} }
Optimization optimization[10] = {InstructionCombining, Disabled};
using ElfHeader = std::conditional<sizeof(void*) == 8, Elf64_Ehdr, Elf32_Ehdr>::type; using ElfHeader = std::conditional<sizeof(void*) == 8, Elf64_Ehdr, Elf32_Ehdr>::type;
using SectionHeader = std::conditional<sizeof(void*) == 8, Elf64_Shdr, Elf32_Shdr>::type; using SectionHeader = std::conditional<sizeof(void*) == 8, Elf64_Shdr, Elf32_Shdr>::type;
...@@ -548,7 +571,7 @@ namespace rr ...@@ -548,7 +571,7 @@ namespace rr
Flags.setTargetInstructionSet(CPUID::SSE4_1 ? Ice::X86InstructionSet_SSE4_1 : Ice::X86InstructionSet_SSE2); Flags.setTargetInstructionSet(CPUID::SSE4_1 ? Ice::X86InstructionSet_SSE4_1 : Ice::X86InstructionSet_SSE2);
#endif #endif
Flags.setOutFileType(Ice::FT_Elf); Flags.setOutFileType(Ice::FT_Elf);
Flags.setOptLevel(Ice::Opt_2); Flags.setOptLevel(toIce(getDefaultConfig().getOptimization().getLevel()));
Flags.setApplicationBinaryInterface(Ice::ABI_Platform); Flags.setApplicationBinaryInterface(Ice::ABI_Platform);
Flags.setVerbose(false ? Ice::IceV_Most : Ice::IceV_None); Flags.setVerbose(false ? Ice::IceV_Most : Ice::IceV_None);
Flags.setDisableHybridAssembly(true); Flags.setDisableHybridAssembly(true);
...@@ -585,7 +608,26 @@ namespace rr ...@@ -585,7 +608,26 @@ namespace rr
::codegenMutex.unlock(); ::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) if(basicBlock->getInsts().empty() || basicBlock->getInsts().back().getKind() != Ice::Inst::Ret)
{ {
...@@ -594,7 +636,7 @@ namespace rr ...@@ -594,7 +636,7 @@ namespace rr
::function->setFunctionName(Ice::GlobalString::createWithString(::context, name)); ::function->setFunctionName(Ice::GlobalString::createWithString(::context, name));
optimize(); rr::optimize(::function);
::function->translate(); ::function->translate();
ASSERT(!::function->hasError()); ASSERT(!::function->hasError());
...@@ -624,11 +666,6 @@ namespace rr ...@@ -624,11 +666,6 @@ namespace rr
return handoffRoutine; return handoffRoutine;
} }
void Nucleus::optimize()
{
rr::optimize(::function);
}
Value *Nucleus::allocateStackVariable(Type *t, int arraySize) Value *Nucleus::allocateStackVariable(Type *t, int arraySize)
{ {
Ice::Type type = T(t); Ice::Type type = T(t);
...@@ -3506,7 +3543,7 @@ namespace rr ...@@ -3506,7 +3543,7 @@ namespace rr
void FlushDebug() {} void FlushDebug() {}
void Nucleus::createCoroutine(Type *YieldType, std::vector<Type*> &Params) { UNIMPLEMENTED("createCoroutine"); } 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"); } void Nucleus::yield(Value* val) { UNIMPLEMENTED("Yield"); }
} }
...@@ -2855,10 +2855,13 @@ namespace sw ...@@ -2855,10 +2855,13 @@ namespace sw
CPUID::setEnableSSE2(configuration.enableSSE2); CPUID::setEnableSSE2(configuration.enableSSE2);
CPUID::setEnableSSE(configuration.enableSSE); 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; forceWindowed = configuration.forceWindowed;
complementaryDepthBuffer = configuration.complementaryDepthBuffer; complementaryDepthBuffer = configuration.complementaryDepthBuffer;
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
#include "Version.h" #include "Version.h"
#include "Reactor/Nucleus.hpp" // ReactorOptimizationLevel #include "Reactor/Nucleus.hpp" // ReactorConfig
#include <Vulkan/VulkanPlatform.h> #include <Vulkan/VulkanPlatform.h>
...@@ -79,8 +79,8 @@ enum ...@@ -79,8 +79,8 @@ enum
MAX_POINT_SIZE = 1, // Large points are not supported. If/when we turn this on, must be >= 64. 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. // Configuration to use for JIT functions.
static constexpr auto ReactorOptimizationLevel = rr::OptimizationLevel::Default; static const auto ReactorConfig = rr::Config::Edit().set(rr::Optimization::Level::Default);
} }
......
...@@ -262,7 +262,7 @@ std::shared_ptr<sw::ComputeProgram> createProgram(const vk::PipelineCache::Compu ...@@ -262,7 +262,7 @@ std::shared_ptr<sw::ComputeProgram> createProgram(const vk::PipelineCache::Compu
// TODO(b/119409619): use allocator. // TODO(b/119409619): use allocator.
auto program = std::make_shared<sw::ComputeProgram>(key.getShader(), key.getLayout(), descriptorSets); auto program = std::make_shared<sw::ComputeProgram>(key.getShader(), key.getLayout(), descriptorSets);
program->generate(); program->generate();
program->finalize(vk::ReactorOptimizationLevel); program->finalize(vk::ReactorConfig);
return program; return program;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment