Fix rr::Print and add unit tests

* CMake: add REACTOR_ENABLE_PRINT to enable rr::Print macros (default 0).
  When enabled, defines ENABLE_RR_PRINT. Formerly, ENABLE_RR_PRINT was
  only defined in NDEBUG builds.
* CMake: REACTOR_EMIT_PRINT_LOCATION enables REACTOR_ENABLE_PRINT.
* Add missing includes of Print.hpp.
* Fix rr::Print compilation of non-const pointers.
* Fix rr::Print compilation of Int2 and UInt2.
* Fix rr::Print of Float outputting in square brackets.
* Fix rr::Print of Bool to output a '1' or '0' for 'true' and 'false'.
* Add unit tests for rr::Print of all primitive and Reactor types.
* Includes changes bclayton@ made to output "true"/"false" for Bool,
  and to correctly compare against implementation-specific printf of
  pointers.

Bug: b/149328074
Change-Id: Ibb17985201f1c2b432539cd1153293d1e1cbb5bb
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/41108
Reviewed-by: Ben Clayton <bclayton@google.com>
Tested-by: Antonio Maiorano <amaiorano@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0ff9cd9..045df81 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -120,6 +120,7 @@
 option_if_not_defined(SWIFTSHADER_DCHECK_ALWAYS_ON "Check validation macros even in release builds" 0)
 option_if_not_defined(REACTOR_EMIT_DEBUG_INFO "Emit debug info for JIT functions" 0)
 option_if_not_defined(REACTOR_EMIT_PRINT_LOCATION "Emit printing of location info for JIT functions" 0)
+option_if_not_defined(REACTOR_ENABLE_PRINT "Enable RR_PRINT macros" 0)
 option_if_not_defined(REACTOR_VERIFY_LLVM_IR "Check reactor-generated LLVM IR is valid even in release builds" 0)
 option_if_not_defined(SWIFTSHADER_LESS_DEBUG_INFO "Generate less debug info to reduce file size" 0)
 option_if_not_defined(SWIFTSHADER_ENABLE_VULKAN_DEBUGGER "Enable vulkan debugger support" 0)
@@ -543,8 +544,9 @@
 endif()
 
 if(REACTOR_EMIT_PRINT_LOCATION)
-    # This feature depends on REACTOR_EMIT_DEBUG_INFO, so enable it
+    # This feature depends on REACTOR_EMIT_DEBUG_INFO and REACTOR_ENABLE_PRINT
     set(REACTOR_EMIT_DEBUG_INFO "On")
+    set(REACTOR_ENABLE_PRINT "On")
     list(APPEND SWIFTSHADER_COMPILE_OPTIONS "-DENABLE_RR_EMIT_PRINT_LOCATION")
 endif()
 
@@ -553,6 +555,10 @@
     list(APPEND SWIFTSHADER_COMPILE_OPTIONS "-DENABLE_RR_DEBUG_INFO")
 endif()
 
+if(REACTOR_ENABLE_PRINT)
+    list(APPEND SWIFTSHADER_COMPILE_OPTIONS "-DENABLE_RR_PRINT")
+endif()
+
 if(REACTOR_VERIFY_LLVM_IR)
     list(APPEND SWIFTSHADER_COMPILE_OPTIONS "-DENABLE_RR_LLVM_IR_VERIFICATION")
 endif()
diff --git a/src/Reactor/LLVMReactor.cpp b/src/Reactor/LLVMReactor.cpp
index c8d74c2..c14012d 100644
--- a/src/Reactor/LLVMReactor.cpp
+++ b/src/Reactor/LLVMReactor.cpp
@@ -18,6 +18,7 @@
 #include "Debug.hpp"
 #include "EmulatedReactor.hpp"
 #include "LLVMReactorDebugInfo.hpp"
+#include "Print.hpp"
 #include "Reactor.hpp"
 #include "x86.hpp"
 
@@ -3874,6 +3875,13 @@
 	return elements;
 }
 
+std::vector<Value *> PrintValue::Ty<Bool>::val(const RValue<Bool> &v)
+{
+	auto t = jit->builder->CreateGlobalStringPtr("true");
+	auto f = jit->builder->CreateGlobalStringPtr("false");
+	return { V(jit->builder->CreateSelect(V(v.value), t, f)) };
+}
+
 std::vector<Value *> PrintValue::Ty<Byte>::val(const RValue<Byte> &v)
 {
 	return toInt({ v.value }, false);
@@ -3965,7 +3973,7 @@
 	if(function != nullptr) { str += "%s "; }
 	str += fmt;
 
-	// Perform subsitution on all '{n}' bracketed indices in the format
+	// Perform substitution on all '{n}' bracketed indices in the format
 	// message.
 	int i = 0;
 	for(const PrintValue &arg : args)
diff --git a/src/Reactor/LLVMReactorDebugInfo.cpp b/src/Reactor/LLVMReactorDebugInfo.cpp
index 21a759c..5810d44 100644
--- a/src/Reactor/LLVMReactorDebugInfo.cpp
+++ b/src/Reactor/LLVMReactorDebugInfo.cpp
@@ -18,6 +18,7 @@
 
 #	include "LLVMReactor.hpp"
 #	include "Reactor.hpp"
+#	include "Print.hpp"
 
 #	include "boost/stacktrace.hpp"
 
diff --git a/src/Reactor/Print.hpp b/src/Reactor/Print.hpp
index a0dee69..a5bde3e 100644
--- a/src/Reactor/Print.hpp
+++ b/src/Reactor/Print.hpp
@@ -15,10 +15,6 @@
 #ifndef rr_Print_hpp
 #define rr_Print_hpp
 
-#if !defined(NDEBUG)
-#	define ENABLE_RR_PRINT 1  // Enables RR_PRINT(), RR_WATCH()
-#endif                         // !defined(NDEBUG)
-
 #ifdef ENABLE_RR_PRINT
 
 #	include "Reactor.hpp"
@@ -147,9 +143,12 @@
 	PrintValue(double v)
 	    : format(std::to_string(v))
 	{}
+	PrintValue(const char *v)
+	    : format(v)
+	{}
 
 	template<typename T>
-	PrintValue(const T *v)
+	PrintValue(T *v)
 	    : format(addr(v))
 	{}
 
@@ -226,8 +225,8 @@
 template<>
 struct PrintValue::Ty<Bool>
 {
-	static std::string fmt(const RValue<Bool> &v) { return "%d"; }
-	static std::vector<Value *> val(const RValue<Bool> &v) { return { v.value }; }
+	static std::string fmt(const RValue<Bool> &v) { return "%s"; }
+	static std::vector<Value *> val(const RValue<Bool> &v);
 };
 template<>
 struct PrintValue::Ty<Byte>
@@ -250,7 +249,7 @@
 template<>
 struct PrintValue::Ty<Int2>
 {
-	static std::string fmt(const RValue<Int> &v) { return "[%d, %d]"; }
+	static std::string fmt(const RValue<Int2> &v) { return "[%d, %d]"; }
 	static std::vector<Value *> val(const RValue<Int2> &v);
 };
 template<>
@@ -268,7 +267,7 @@
 template<>
 struct PrintValue::Ty<UInt2>
 {
-	static std::string fmt(const RValue<UInt> &v) { return "[%u, %u]"; }
+	static std::string fmt(const RValue<UInt2> &v) { return "[%u, %u]"; }
 	static std::vector<Value *> val(const RValue<UInt2> &v);
 };
 template<>
@@ -304,7 +303,7 @@
 template<>
 struct PrintValue::Ty<Float>
 {
-	static std::string fmt(const RValue<Float> &v) { return "[%f]"; }
+	static std::string fmt(const RValue<Float> &v) { return "%f"; }
 	static std::vector<Value *> val(const RValue<Float> &v);
 };
 template<>
diff --git a/src/Reactor/ReactorUnitTests.cpp b/src/Reactor/ReactorUnitTests.cpp
index 508e815..63db739 100644
--- a/src/Reactor/ReactorUnitTests.cpp
+++ b/src/Reactor/ReactorUnitTests.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "Coroutine.hpp"
+#include "Print.hpp"
 #include "Reactor.hpp"
 
 #include "gtest/gtest.h"
@@ -107,6 +108,206 @@
 	return sum;
 }
 
+class StdOutCapture
+{
+public:
+	~StdOutCapture()
+	{
+		stopIfCapturing();
+	}
+
+	void start()
+	{
+		stopIfCapturing();
+		capturing = true;
+		testing::internal::CaptureStdout();
+	}
+
+	std::string stop()
+	{
+		assert(capturing);
+		capturing = false;
+		return testing::internal::GetCapturedStdout();
+	}
+
+private:
+	void stopIfCapturing()
+	{
+		if(capturing)
+		{
+			// This stops the capture
+			testing::internal::GetCapturedStdout();
+		}
+	}
+
+	bool capturing = false;
+};
+
+std::vector<std::string> split(const std::string &s)
+{
+	std::vector<std::string> result;
+	std::istringstream iss(s);
+	for(std::string line; std::getline(iss, line);)
+	{
+		result.push_back(line);
+	}
+	return result;
+}
+
+TEST(ReactorUnitTests, PrintPrimitiveTypes)
+{
+#ifdef ENABLE_RR_PRINT
+	FunctionT<void()> function;
+	{
+		bool b(true);
+		int8_t i8(-1);
+		uint8_t ui8(1);
+		int16_t i16(-1);
+		uint16_t ui16(1);
+		int32_t i32(-1);
+		uint32_t ui32(1);
+		int64_t i64(-1);
+		uint64_t ui64(1);
+		float f(1);
+		double d(2);
+		const char *cstr = "const char*";
+		std::string str = "std::string";
+		int *p = nullptr;
+
+		RR_WATCH(b);
+		RR_WATCH(i8);
+		RR_WATCH(ui8);
+		RR_WATCH(i16);
+		RR_WATCH(ui16);
+		RR_WATCH(i32);
+		RR_WATCH(ui32);
+		RR_WATCH(i64);
+		RR_WATCH(ui64);
+		RR_WATCH(f);
+		RR_WATCH(d);
+		RR_WATCH(cstr);
+		RR_WATCH(str);
+		RR_WATCH(p);
+	}
+
+	auto routine = function("one");
+
+	char pNullptr[64];
+	snprintf(pNullptr, sizeof(pNullptr), "  p: %p", nullptr);
+
+	const char *expected[] = {
+		"  b: true",
+		"  i8: -1",
+		"  ui8: 1",
+		"  i16: -1",
+		"  ui16: 1",
+		"  i32: -1",
+		"  ui32: 1",
+		"  i64: -1",
+		"  ui64: 1",
+		"  f: 1.000000",
+		"  d: 2.000000",
+		"  cstr: const char*",
+		"  str: std::string",
+		pNullptr,
+	};
+	constexpr size_t expectedSize = sizeof(expected) / sizeof(expected[0]);
+
+	StdOutCapture capture;
+	capture.start();
+	routine();
+	auto output = split(capture.stop());
+	for(size_t i = 0, j = 1; i < expectedSize; ++i, j += 2)
+	{
+		ASSERT_EQ(expected[i], output[j]);
+	}
+
+#endif
+}
+
+TEST(ReactorUnitTests, PrintReactorTypes)
+{
+#ifdef ENABLE_RR_PRINT
+	FunctionT<void()> function;
+	{
+		Bool b(true);
+		Int i(-1);
+		Int2 i2(-1, -2);
+		Int4 i4(-1, -2, -3, -4);
+		UInt ui(1);
+		UInt2 ui2(1, 2);
+		UInt4 ui4(1, 2, 3, 4);
+		Short s(-1);
+		Short4 s4(-1, -2, -3, -4);
+		UShort us(1);
+		UShort4 us4(1, 2, 3, 4);
+		Float f(1);
+		Float4 f4(1, 2, 3, 4);
+		Long l(i);
+		Pointer<Int> pi = nullptr;
+		RValue<Int> rvi = i;
+		Byte by('a');
+		Byte4 by4(i4);
+
+		RR_WATCH(b);
+		RR_WATCH(i);
+		RR_WATCH(i2);
+		RR_WATCH(i4);
+		RR_WATCH(ui);
+		RR_WATCH(ui2);
+		RR_WATCH(ui4);
+		RR_WATCH(s);
+		RR_WATCH(s4);
+		RR_WATCH(us);
+		RR_WATCH(us4);
+		RR_WATCH(f);
+		RR_WATCH(f4);
+		RR_WATCH(l);
+		RR_WATCH(pi);
+		RR_WATCH(rvi);
+		RR_WATCH(by);
+		RR_WATCH(by4);
+	}
+
+	auto routine = function("one");
+
+	char piNullptr[64];
+	snprintf(piNullptr, sizeof(piNullptr), "  pi: %p", nullptr);
+
+	const char *expected[] = {
+		"  b: true",
+		"  i: -1",
+		"  i2: [-1, -2]",
+		"  i4: [-1, -2, -3, -4]",
+		"  ui: 1",
+		"  ui2: [1, 2]",
+		"  ui4: [1, 2, 3, 4]",
+		"  s: -1",
+		"  s4: [-1, -2, -3, -4]",
+		"  us: 1",
+		"  us4: [1, 2, 3, 4]",
+		"  f: 1.000000",
+		"  f4: [1.000000, 2.000000, 3.000000, 4.000000]",
+		"  l: -1",
+		piNullptr,
+		"  rvi: -1",
+		"  by: 97",
+		"  by4: [255, 254, 253, 252]",
+	};
+	constexpr size_t expectedSize = sizeof(expected) / sizeof(expected[0]);
+
+	StdOutCapture capture;
+	capture.start();
+	routine();
+	auto output = split(capture.stop());
+	for(size_t i = 0, j = 1; i < expectedSize; ++i, j += 2)
+	{
+		ASSERT_EQ(expected[i], output[j]);
+	}
+
+#endif
+}
+
 TEST(ReactorUnitTests, Sample)
 {
 	FunctionT<int(int *, int)> function;
diff --git a/src/Shader/ShaderCore.hpp b/src/Shader/ShaderCore.hpp
index fd1ccb7..6a0b285 100644
--- a/src/Shader/ShaderCore.hpp
+++ b/src/Shader/ShaderCore.hpp
@@ -17,6 +17,7 @@
 
 #include "Shader.hpp"
 #include "Reactor/Reactor.hpp"
+#include "Reactor/Print.hpp"
 #include "Common/Debug.hpp"
 
 namespace sw