Add Print helpers to reactor

New tools for debugging JITed code.

rr::Print() is a new function that emits a call to printf() using the provided message and optional values. There is also an overload that accepts additional file, line, and function parameters to help identify the call site.

Format values passed to rr::Print() are implicity cast to a new rr::PrintValues type which does the work of dispatching the value type to the correct printf() format specifier(s) and argument value(s). A single rr::Print() format value can automatically be expanded into multiple printf values - for example an rr::Float4 will expand to "%f %f %f %f" and four scalar float values.

rr::Print() format values can be any of the following types:
  * Reactor LValues, RValues, Pointers.
  * Standard Plain-Old-Value types (int, float, bool, etc)
  * Custom types that specialize the PrintValue::Ty template struct.
  * Static arrays in the form T[N] where T can be any of the above.

The sw::Vector4f and sw::Vector4s types have already had custom formatters written.

These new functions and types described above are typically not called directly. Instead there are two helper macros which simplifies usage:

RR_LOG() is a new macro that wraps rr::Print(), automatically populating the function, file and line parameters and appending a newline to the string.

RR_WATCH() is a new helper macro that prints the name and value of all the supplied arguments. For example, if you had the Int and bool variables 'foo' and 'bar' that you want to print, you can simply write:

    RR_WATCH(foo, bar)

When this JIT compiled code is executed, it will print:
    "foo: 1, bar: true"

All of this code is disabled in non-debug builds, or if the reactor backend is not LLVM 7+.

Change-Id: Ia39b1e507b6afaa3bb1d33e40b1333017f4b4f21
Reviewed-on: https://swiftshader-review.googlesource.com/c/24768
Tested-by: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Reactor/LLVMReactor.cpp b/src/Reactor/LLVMReactor.cpp
index 6c8d9c6..74b6261 100644
--- a/src/Reactor/LLVMReactor.cpp
+++ b/src/Reactor/LLVMReactor.cpp
@@ -79,8 +79,9 @@
 	#include <unordered_map>
 #endif
 
-#include <numeric>
 #include <fstream>
+#include <numeric>
+#include <thread>
 
 #if defined(__i386__) || defined(__x86_64__)
 #include <xmmintrin.h>
@@ -117,6 +118,16 @@
 
 	rr::MutexLock codegenMutex;
 
+	std::string replace(std::string str, const std::string& substr, const std::string& replacement)
+	{
+		size_t pos = 0;
+		while((pos = str.find(substr, pos)) != std::string::npos) {
+			str.replace(pos, substr.length(), replacement);
+			pos += replacement.length();
+		}
+		return str;
+	}
+
 #if REACTOR_LLVM_VERSION >= 7
 	llvm::Value *lowerPAVG(llvm::Value *x, llvm::Value *y)
 	{
@@ -538,12 +549,20 @@
 			func_.emplace("floorf", reinterpret_cast<void*>(floorf));
 			func_.emplace("nearbyintf", reinterpret_cast<void*>(nearbyintf));
 			func_.emplace("truncf", reinterpret_cast<void*>(truncf));
+			func_.emplace("printf", reinterpret_cast<void*>(printf));
+			func_.emplace("puts", reinterpret_cast<void*>(puts));
 		}
 
 		void *findSymbol(const std::string &name) const
 		{
-			FunctionMap::const_iterator it = func_.find(name);
-			return (it != func_.end()) ? it->second : nullptr;
+			// Trim off any underscores from the start of the symbol. LLVM likes
+			// to append these on macOS.
+			const char* trimmed = name.c_str();
+			while (trimmed[0] == '_') { trimmed++; }
+
+			FunctionMap::const_iterator it = func_.find(trimmed);
+			assert(it != func_.end()); // Missing functions will likely make the module fail in exciting non-obvious ways.
+			return it->second;
 		}
 	};
 
@@ -7512,4 +7531,97 @@
 		}
 	}
 #endif  // defined(__i386__) || defined(__x86_64__)
+
+#if ENABLE_RR_PRINT
+	// extractAll returns a vector containing the extracted n scalar value of
+	// the vector vec.
+	static std::vector<Value*> extractAll(Value* vec, int n)
+	{
+		std::vector<Value*> elements;
+		elements.reserve(n);
+		for (int i = 0; i < n; i++)
+		{
+			auto el = V(::builder->CreateExtractElement(V(vec), i));
+			elements.push_back(el);
+		}
+		return elements;
+	}
+
+	// toDouble returns all the float values in vals extended to doubles.
+	static std::vector<Value*> toDouble(const std::vector<Value*>& vals)
+	{
+		auto doubleTy = ::llvm::Type::getDoubleTy(*::context);
+		std::vector<Value*> elements;
+		elements.reserve(vals.size());
+		for (auto v : vals)
+		{
+			elements.push_back(V(::builder->CreateFPExt(V(v), doubleTy)));
+		}
+		return elements;
+	}
+
+	std::vector<Value*> PrintValue::Ty<Byte4>::val(const RValue<Byte4>& v) { return extractAll(v.value, 4); }
+	std::vector<Value*> PrintValue::Ty<Int4>::val(const RValue<Int4>& v) { return extractAll(v.value, 4); }
+	std::vector<Value*> PrintValue::Ty<UInt4>::val(const RValue<UInt4>& v) { return extractAll(v.value, 4); }
+	std::vector<Value*> PrintValue::Ty<Short4>::val(const RValue<Short4>& v) { return extractAll(v.value, 4); }
+	std::vector<Value*> PrintValue::Ty<UShort4>::val(const RValue<UShort4>& v) { return extractAll(v.value, 4); }
+	std::vector<Value*> PrintValue::Ty<Float>::val(const RValue<Float>& v) { return toDouble({v.value}); }
+	std::vector<Value*> PrintValue::Ty<Float4>::val(const RValue<Float4>& v) { return toDouble(extractAll(v.value, 4)); }
+
+	void Printv(const char* function, const char* file, int line, const char* fmt, std::initializer_list<PrintValue> args)
+	{
+		// LLVM types used below.
+		auto i32Ty = ::llvm::Type::getInt32Ty(*::context);
+		auto intTy = ::llvm::Type::getInt64Ty(*::context); // TODO: Natural int width.
+		auto i8PtrTy = ::llvm::Type::getInt8PtrTy(*::context);
+		auto funcTy = ::llvm::FunctionType::get(i32Ty, {i8PtrTy}, true);
+
+		auto func = ::module->getOrInsertFunction("printf", funcTy);
+
+		// Build the printf format message string.
+		std::string str;
+		if (file != nullptr) { str += (line > 0) ? "%s:%d " : "%s "; }
+		if (function != nullptr) { str += "%s "; }
+		str += fmt;
+
+		// Perform subsitution on all '{n}' bracketed indices in the format
+		// message.
+		int i = 0;
+		for (const PrintValue& arg : args)
+		{
+			str = replace(str, "{" + std::to_string(i++) + "}", arg.format);
+		}
+
+		::llvm::SmallVector<::llvm::Value*, 8> vals;
+
+		// The format message is always the first argument.
+		vals.push_back(::builder->CreateGlobalStringPtr(str));
+
+		// Add optional file, line and function info if provided.
+		if (file != nullptr)
+		{
+			vals.push_back(::builder->CreateGlobalStringPtr(file));
+			if (line > 0)
+			{
+				vals.push_back(::llvm::ConstantInt::get(intTy, line));
+			}
+		}
+		if (function != nullptr)
+		{
+			vals.push_back(::builder->CreateGlobalStringPtr(function));
+		}
+
+		// Add all format arguments.
+		for (const PrintValue& arg : args)
+		{
+			for (auto val : arg.values)
+			{
+				vals.push_back(V(val));
+			}
+		}
+
+		::builder->CreateCall(func, vals);
+	}
+#endif // ENABLE_RR_PRINT
+
 }