Add unit test for ENABLE_RR_EMIT_ASM_FILE

Make ReactorUnitTests use C++17 so we can use std::filesystem.
Since ReactorUnitTests is also built in other build systems, this is
made optional by conditionally compiling based on the __cplusplus macro.

- CMake: ensure that __cplusplus is defined on MSVC
- Also replaced "swiftshader" with "reactor" in the assembly output
filename.
- Added run of unit test to Kokoro.

Bug: b/174358505
Bug: b/174843857
Change-Id: I9c558957f7179e4c295b6d32d78375e18f9e65dd
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/50868
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ebe5246..b21d9ed 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,10 +14,15 @@
 
 cmake_minimum_required(VERSION 3.13)
 
-set(CMAKE_CXX_STANDARD 14)
-
 project(SwiftShader C CXX ASM)
 
+set(CMAKE_CXX_STANDARD 14)
+set(CXX_STANDARD_REQUIRED ON)
+# MSVC doesn't define __cplusplus by default
+if(MSVC)
+    string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus")
+endif()
+
 ###########################################################
 # Detect system
 ###########################################################
diff --git a/src/Reactor/LLVMAsm.cpp b/src/Reactor/LLVMAsm.cpp
index e7b19e6..ab6e093 100644
--- a/src/Reactor/LLVMAsm.cpp
+++ b/src/Reactor/LLVMAsm.cpp
@@ -51,7 +51,7 @@
 
 	static size_t counter = 0;
 	std::stringstream f;
-	f << "swiftshader_jit_llvm_" << std::setfill('0') << std::setw(4) << counter++ << "_" << routineName << ".asm";
+	f << "reactor_jit_llvm_" << std::setfill('0') << std::setw(4) << counter++ << "_" << routineName << ".asm";
 	return f.str();
 }
 
diff --git a/tests/ReactorUnitTests/CMakeLists.txt b/tests/ReactorUnitTests/CMakeLists.txt
index 59e0406..0b98072 100644
--- a/tests/ReactorUnitTests/CMakeLists.txt
+++ b/tests/ReactorUnitTests/CMakeLists.txt
@@ -31,6 +31,11 @@
     ${REACTOR_UNIT_TESTS_SRC_FILES}
 )
 
+target_compile_features(ReactorUnitTests
+    PUBLIC
+        cxx_std_17
+)
+
 set_target_properties(ReactorUnitTests PROPERTIES
     FOLDER "Tests"
     RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
diff --git a/tests/ReactorUnitTests/ReactorUnitTests.cpp b/tests/ReactorUnitTests/ReactorUnitTests.cpp
index 292aa2f..7b5bb1b 100644
--- a/tests/ReactorUnitTests/ReactorUnitTests.cpp
+++ b/tests/ReactorUnitTests/ReactorUnitTests.cpp
@@ -20,9 +20,19 @@
 
 #include <array>
 #include <cmath>
+#include <fstream>
 #include <thread>
 #include <tuple>
 
+// TODO(b/174843857): Remove once we upgrade to C++17
+#if(__cplusplus >= 201703L)
+#	define HAS_STD_FILESYSTEM
+#endif
+
+#if(defined(HAS_STD_FILESYSTEM))
+#	include <filesystem>
+#endif
+
 using namespace rr;
 
 std::string testName()
@@ -3249,6 +3259,75 @@
 	EXPECT_EQ(result, expected);
 }
 
+#if defined(ENABLE_RR_EMIT_ASM_FILE) && defined(HAS_STD_FILESYSTEM)
+TEST(ReactorUnitTests, EmitAsm)
+{
+	// Only supported by LLVM for now
+	if(BackendName().find("LLVM") == std::string::npos) return;
+
+	namespace fs = std::filesystem;
+
+	FunctionT<int(void)> function;
+	{
+		Int sum;
+		For(Int i = 0, i < 10, i++)
+		{
+			sum += i;
+		}
+		Return(sum);
+	}
+
+	auto routine = function(testName().c_str());
+
+	// Returns path to first match of filename in current directory
+	auto findFile = [](const std::string filename) -> fs::path {
+		for(auto &p : fs::directory_iterator("."))
+		{
+			if(!p.is_regular_file())
+				continue;
+			auto currFilename = p.path().filename().string();
+			auto index = currFilename.find(testName());
+			if(index != std::string::npos)
+			{
+				return p.path();
+			}
+		}
+		return {};
+	};
+
+	fs::path path = findFile(testName());
+	EXPECT_FALSE(path.empty());
+
+	// Make sure an asm file was created
+	std::ifstream fin(path);
+	EXPECT_TRUE(fin);
+
+	// Make sure address of routine is in the file
+	auto findAddressInFile = [](std::ifstream &fin, size_t address) {
+		std::string addressString = [&] {
+			std::stringstream addressSS;
+			addressSS << "0x" << std::uppercase << std::hex << address;
+			return addressSS.str();
+		}();
+
+		std::string token;
+		while(fin >> token)
+		{
+			if(token.find(addressString) != std::string::npos)
+				return true;
+		}
+		return false;
+	};
+
+	size_t address = reinterpret_cast<size_t>(routine.getEntry());
+	EXPECT_TRUE(findAddressInFile(fin, address));
+
+	// Delete the file in case subsequent runs generate one with a different sequence number
+	fin.close();
+	std::filesystem::remove(path);
+}
+#endif
+
 ////////////////////////////////
 // Trait compile time checks. //
 ////////////////////////////////
diff --git a/tests/kokoro/gcp_ubuntu/continuous.sh b/tests/kokoro/gcp_ubuntu/continuous.sh
index 15ece27..ad1d479 100755
--- a/tests/kokoro/gcp_ubuntu/continuous.sh
+++ b/tests/kokoro/gcp_ubuntu/continuous.sh
@@ -54,6 +54,14 @@
 cd ..
 build/ReactorUnitTests --gtest_filter=ReactorUnitTests.Print*
 
+# Incrementally build with REACTOR_EMIT_ASM_FILE and run unit test
+cd build
+cmake .. "-DREACTOR_EMIT_ASM_FILE=1"
+cmake --build . --target ReactorUnitTests -- -j $(nproc)
+cmake .. "-DREACTOR_EMIT_ASM_FILE=0"
+cd ..
+build/ReactorUnitTests --gtest_filter=ReactorUnitTests.EmitAsm
+
 # Incrementally build with REACTOR_EMIT_DEBUG_INFO to ensure it builds
 cd build
 cmake .. "-DREACTOR_EMIT_DEBUG_INFO=1"
diff --git a/tests/kokoro/gcp_windows/continuous.bat b/tests/kokoro/gcp_windows/continuous.bat
index 0a1b0c5..4873f25 100644
--- a/tests/kokoro/gcp_windows/continuous.bat
+++ b/tests/kokoro/gcp_windows/continuous.bat
@@ -45,6 +45,13 @@
 Debug\ReactorUnitTests.exe --gtest_filter=ReactorUnitTests.Print* || goto :error
 cmake "-DREACTOR_ENABLE_PRINT=0" .. || goto :error
 
+REM Incrementally build with REACTOR_EMIT_ASM_FILE and run unit test
+cd %SRC%\build || goto :error
+cmake "-DREACTOR_EMIT_ASM_FILE=1" .. || goto :error
+cmake --build . --target ReactorUnitTests || goto :error
+Debug\ReactorUnitTests.exe --gtest_filter=ReactorUnitTests.EmitAsm || goto :error
+cmake "-DREACTOR_EMIT_ASM_FILE=0" .. || goto :error
+
 REM Incrementally build with REACTOR_EMIT_DEBUG_INFO to ensure it builds
 cd %SRC%\build || goto :error
 cmake "-DREACTOR_EMIT_DEBUG_INFO=1" .. || goto :error