Add an option to control where ASM listings are dumped.

This setting only matters when SwiftShader is built with
`-DREACTOR_EMIT_ASM_FILE=TRUE`. The default behaviour is
preserved, i.e. ASM listings are dumped in the working
directory.

Bug: b/216019572
Change-Id: I301eff079440f23663e9a9d493de7a9072a9c062
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/62230
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Daniele Vettorel <dvet@google.com>
Commit-Queue: Daniele Vettorel <dvet@google.com>
diff --git a/src/Reactor/LLVMAsm.cpp b/src/Reactor/LLVMAsm.cpp
index c4d831a..528301b 100644
--- a/src/Reactor/LLVMAsm.cpp
+++ b/src/Reactor/LLVMAsm.cpp
@@ -27,14 +27,14 @@
 namespace rr {
 namespace AsmFile {
 
-std::string generateFilename(std::string routineName)
+std::string generateFilename(const std::string &emitDir, std::string routineName)
 {
 	// Names from gtests sometimes have invalid file name characters
 	std::replace(routineName.begin(), routineName.end(), '/', '_');
 
 	static size_t counter = 0;
 	std::stringstream f;
-	f << "reactor_jit_llvm_" << std::setfill('0') << std::setw(4) << counter++ << "_" << routineName << ".asm";
+	f << emitDir << "reactor_jit_llvm_" << std::setfill('0') << std::setw(4) << counter++ << "_" << routineName << ".asm";
 	return f.str();
 }
 
diff --git a/src/Reactor/LLVMAsm.hpp b/src/Reactor/LLVMAsm.hpp
index b4dce34..3a4b08d 100644
--- a/src/Reactor/LLVMAsm.hpp
+++ b/src/Reactor/LLVMAsm.hpp
@@ -25,7 +25,7 @@
 namespace AsmFile {
 
 // Generate a unique name for the asm file
-std::string generateFilename(std::string routineName);
+std::string generateFilename(const std::string &emitDir, std::string routineName);
 
 // Emit an asm file for the current module
 bool emitAsmFile(const std::string &filename, llvm::orc::JITTargetMachineBuilder builder, llvm::Module &module);
diff --git a/src/Reactor/LLVMJIT.cpp b/src/Reactor/LLVMJIT.cpp
index d762518..6e87fdb 100644
--- a/src/Reactor/LLVMJIT.cpp
+++ b/src/Reactor/LLVMJIT.cpp
@@ -752,7 +752,7 @@
 		}
 
 #ifdef ENABLE_RR_EMIT_ASM_FILE
-		const auto asmFilename = rr::AsmFile::generateFilename(name);
+		const auto asmFilename = rr::AsmFile::generateFilename(config.getDebugConfig().asmEmitDir, name);
 		rr::AsmFile::emitAsmFile(asmFilename, JITGlobals::get()->getTargetMachineBuilder(config.getOptimization().getLevel()), *module);
 #endif
 
diff --git a/src/Reactor/Nucleus.hpp b/src/Reactor/Nucleus.hpp
index 73b23f6..502fb24 100644
--- a/src/Reactor/Nucleus.hpp
+++ b/src/Reactor/Nucleus.hpp
@@ -89,6 +89,11 @@
 	Passes passes;
 };
 
+struct DebugConfig
+{
+	std::string asmEmitDir = "";
+};
+
 // Config holds the Reactor configuration settings.
 class Config
 {
@@ -120,6 +125,12 @@
 			optPassEdits.push_back({ ListEdit::Clear, Optimization::Pass::Disabled });
 			return *this;
 		}
+		Edit &setDebugConfig(const DebugConfig &cfg)
+		{
+			debugCfg = cfg;
+			debugCfgChanged = true;
+			return *this;
+		}
 
 		Config apply(const Config &cfg) const;
 
@@ -138,17 +149,22 @@
 		Optimization::Level optLevel;
 		bool optLevelChanged = false;
 		std::vector<OptPassesEdit> optPassEdits;
+		DebugConfig debugCfg;
+		bool debugCfgChanged = false;
 	};
 
 	Config() = default;
-	Config(const Optimization &optimization)
+	Config(const Optimization &optimization, const DebugConfig &debugCfg)
 	    : optimization(optimization)
+	    , debugCfg(debugCfg)
 	{}
 
 	const Optimization &getOptimization() const { return optimization; }
+	const DebugConfig &getDebugConfig() const { return debugCfg; }
 
 private:
 	Optimization optimization;
+	DebugConfig debugCfg;
 };
 
 class Nucleus
diff --git a/src/Reactor/Reactor.cpp b/src/Reactor/Reactor.cpp
index f7deca0..148a46e 100644
--- a/src/Reactor/Reactor.cpp
+++ b/src/Reactor/Reactor.cpp
@@ -38,10 +38,11 @@
 
 Config Config::Edit::apply(const Config &cfg) const
 {
+	auto newDebugCfg = debugCfgChanged ? debugCfg : cfg.debugCfg;
 	auto level = optLevelChanged ? optLevel : cfg.optimization.getLevel();
 	auto passes = cfg.optimization.getPasses();
 	apply(optPassEdits, passes);
-	return Config{ Optimization{ level, passes } };
+	return Config{ Optimization{ level, passes }, newDebugCfg };
 }
 
 template<typename T>
diff --git a/src/System/CMakeLists.txt b/src/System/CMakeLists.txt
index 18d7b94..e19097c 100644
--- a/src/System/CMakeLists.txt
+++ b/src/System/CMakeLists.txt
@@ -56,6 +56,8 @@
     list(APPEND SYSTEM_COMPILE_OPTIONS
         "-Wexit-time-destructors"  # declaration requires an exit-time destructor
     )
+    # We use exit-time destructors for the global configuration.
+    SET_SOURCE_FILES_PROPERTIES("SwiftConfig.cpp" PROPERTIES COMPILE_FLAGS "-Wno-exit-time-destructors")
 endif()
 
 add_library(vk_system EXCLUDE_FROM_ALL
diff --git a/src/System/SwiftConfig.cpp b/src/System/SwiftConfig.cpp
index 46e3ae8..bd17286 100644
--- a/src/System/SwiftConfig.cpp
+++ b/src/System/SwiftConfig.cpp
@@ -89,6 +89,8 @@
 {
 	Configurator ini("SwiftShader.ini");
 	Configuration config{};
+
+	// Processor flags.
 	config.threadCount = ini.getInteger<uint32_t>("Processor", "ThreadCount", 0);
 	config.affinityMask = ini.getInteger<uint64_t>("Processor", "AffinityMask", 0xffffffffffffffff);
 	if(config.affinityMask == 0)
@@ -107,6 +109,13 @@
 		config.affinityPolicy = Configuration::AffinityPolicy::AnyOf;
 	}
 
+	// Debug flags.
+	config.asmEmitDir = ini.getValue("Debug", "AsmEmitDir");
+	if(config.asmEmitDir.size() > 0 && *config.asmEmitDir.rend() != '/')
+	{
+		config.asmEmitDir.push_back('/');
+	}
+
 	return config;
 }
 
@@ -132,4 +141,11 @@
 	});
 	return cfg;
 }
+
+rr::DebugConfig getReactorDebugConfig(const Configuration &config)
+{
+	rr::DebugConfig debugCfg;
+	debugCfg.asmEmitDir = config.asmEmitDir;
+	return debugCfg;
+}
 }  // namespace sw
\ No newline at end of file
diff --git a/src/System/SwiftConfig.hpp b/src/System/SwiftConfig.hpp
index 32ba2b4..7142885 100644
--- a/src/System/SwiftConfig.hpp
+++ b/src/System/SwiftConfig.hpp
@@ -17,6 +17,7 @@
 
 #include <stdint.h>
 
+#include "Reactor/Nucleus.hpp"
 #include "marl/scheduler.h"
 
 namespace sw {
@@ -37,6 +38,9 @@
 	// Core affinity and affinity policy used by the scheduler.
 	uint64_t affinityMask = 0xffffffffffffffff;
 	AffinityPolicy affinityPolicy = AffinityPolicy::AnyOf;
+
+	// Directory where ASM listings of JITted code will be emitted.
+	std::string asmEmitDir = "";
 };
 
 // Get the configuration as parsed from a configuration file.
@@ -44,6 +48,9 @@
 
 // Get the scheduler configuration given a configuration.
 marl::Scheduler::Config getSchedulerConfiguration(const Configuration &config);
+
+// Get the debug configuration for Reactor given a configuration.
+rr::DebugConfig getReactorDebugConfig(const Configuration &config);
 }  // namespace sw
 
 #endif
\ No newline at end of file
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index f615fe8..e4c89f2 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -114,6 +114,7 @@
 // Reactor.
 void setReactorDefaultConfig()
 {
+	auto swConfig = sw::getConfiguration();
 	auto cfg = rr::Config::Edit()
 	               .set(rr::Optimization::Level::Default)
 	               .clearOptimizationPasses()
@@ -122,7 +123,8 @@
 	               .add(rr::Optimization::Pass::CFGSimplification)
 	               .add(rr::Optimization::Pass::EarlyCSEPass)
 	               .add(rr::Optimization::Pass::CFGSimplification)
-	               .add(rr::Optimization::Pass::InstructionCombining);
+	               .add(rr::Optimization::Pass::InstructionCombining)
+	               .setDebugConfig(sw::getReactorDebugConfig(swConfig));
 
 	rr::Nucleus::adjustDefaultConfig(cfg);
 }