Implement rr::Print support for Subzero

Factored out most of the rr::Print code in LLVMReactor.cpp to
Reactor.cpp, and rewritten in terms of Nucleus. Added a couple of new
Nucleus functions to support this.

Bug: b/149477527
Change-Id: I0a28626f1aa6133a37f9e75abc08544f3de15a45
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/41188
Presubmit-Ready: Antonio Maiorano <amaiorano@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Tested-by: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Reactor/SubzeroReactor.cpp b/src/Reactor/SubzeroReactor.cpp
index f2dbdef..29aa1c8 100644
--- a/src/Reactor/SubzeroReactor.cpp
+++ b/src/Reactor/SubzeroReactor.cpp
@@ -14,6 +14,7 @@
 
 #include "Debug.hpp"
 #include "EmulatedReactor.hpp"
+#include "Print.hpp"
 #include "Reactor.hpp"
 
 #include "ExecutableMemory.hpp"
@@ -143,11 +144,8 @@
 }
 
 // Wrapper for calls on C functions with Ice types
-template<typename Return, typename... CArgs, typename... RArgs>
-Ice::Variable *Call(Ice::Cfg *function, Ice::CfgNode *basicBlock, Return(fptr)(CArgs...), RArgs &&... args)
+Ice::Variable *Call(Ice::Cfg *function, Ice::CfgNode *basicBlock, Ice::Type retTy, void const *fptr, const std::vector<Ice::Operand *> &iceArgs)
 {
-	Ice::Type retTy = T(rr::CToReactorT<Return>::getType());
-
 	// Subzero doesn't support boolean return values. Replace with an i32.
 	if(retTy == Ice::IceType_i1)
 	{
@@ -160,9 +158,7 @@
 		ret = function->makeVariable(retTy);
 	}
 
-	std::array<Ice::Variable *, sizeof...(args)> iceArgs{ { std::forward<RArgs>(args)... } };
-
-	auto call = Ice::InstCall::create(function, iceArgs.size(), ret, getConstantPointer(function->getContext(), reinterpret_cast<void const *>(fptr)), false);
+	auto call = Ice::InstCall::create(function, iceArgs.size(), ret, getConstantPointer(function->getContext(), fptr), false);
 	for(auto arg : iceArgs)
 	{
 		call->addArg(arg);
@@ -172,6 +168,15 @@
 	return ret;
 }
 
+// Wrapper for calls on C functions with Ice types
+template<typename Return, typename... CArgs, typename... RArgs>
+Ice::Variable *Call(Ice::Cfg *function, Ice::CfgNode *basicBlock, Return(fptr)(CArgs...), RArgs &&... args)
+{
+	Ice::Type retTy = T(rr::CToReactorT<Return>::getType());
+	std::vector<Ice::Operand *> iceArgs{ std::forward<RArgs>(args)... };
+	return Call(function, basicBlock, retTy, reinterpret_cast<void const *>(fptr), iceArgs);
+}
+
 // Returns a non-const variable copy of const v
 Ice::Variable *createUnconstCast(Ice::Cfg *function, Ice::CfgNode *basicBlock, Ice::Constant *v)
 {
@@ -414,6 +419,17 @@
 	return reinterpret_cast<Ice::Operand *>(v);
 }
 
+std::vector<Ice::Operand *> V(const std::vector<Value *> &values)
+{
+	std::vector<Ice::Operand *> result;
+	result.reserve(values.size());
+	for(auto &v : values)
+	{
+		result.push_back(V(v));
+	}
+	return result;
+}
+
 BasicBlock *B(Ice::CfgNode *b)
 {
 	return reinterpret_cast<BasicBlock *>(b);
@@ -798,6 +814,13 @@
 	std::vector<std::unique_ptr<uint8_t[]>> constantData;
 };
 
+#ifdef ENABLE_RR_PRINT
+void VPrintf(const std::vector<Value *> &vals)
+{
+	sz::Call(::function, ::basicBlock, Ice::IceType_i32, reinterpret_cast<const void *>(::printf), V(vals));
+}
+#endif  // ENABLE_RR_PRINT
+
 Nucleus::Nucleus()
 {
 	::codegenMutex.lock();  // Reactor is currently not thread safe
@@ -1701,7 +1724,7 @@
 Value *Nucleus::createExtractElement(Value *vector, Type *type, int index)
 {
 	auto result = ::function->makeVariable(T(type));
-	auto extract = Ice::InstExtractElement::create(::function, result, vector, ::context->getConstantInt32(index));
+	auto extract = Ice::InstExtractElement::create(::function, result, V(vector), ::context->getConstantInt32(index));
 	::basicBlock->appendInst(extract);
 
 	return V(result);
@@ -1764,11 +1787,58 @@
 	::basicBlock->appendInst(unreachable);
 }
 
+Type *Nucleus::getType(Value *value)
+{
+	return T(V(value)->getType());
+}
+
+Type *Nucleus::getContainedType(Type *vectorType)
+{
+	Ice::Type vecTy = T(vectorType);
+	switch(vecTy)
+	{
+		case Ice::IceType_v4i1: return T(Ice::IceType_i1);
+		case Ice::IceType_v8i1: return T(Ice::IceType_i1);
+		case Ice::IceType_v16i1: return T(Ice::IceType_i1);
+		case Ice::IceType_v16i8: return T(Ice::IceType_i8);
+		case Ice::IceType_v8i16: return T(Ice::IceType_i16);
+		case Ice::IceType_v4i32: return T(Ice::IceType_i32);
+		case Ice::IceType_v4f32: return T(Ice::IceType_f32);
+		default:
+			ASSERT_MSG(false, "getContainedType: input type is not a vector type");
+			return {};
+	}
+}
+
 Type *Nucleus::getPointerType(Type *ElementType)
 {
 	return T(sz::getPointerType(T(ElementType)));
 }
 
+static constexpr Ice::Type getNaturalIntType()
+{
+	constexpr size_t intSize = sizeof(int);
+	static_assert(intSize == 4 || intSize == 8, "");
+	return intSize == 4 ? Ice::IceType_i32 : Ice::IceType_i64;
+}
+
+Type *Nucleus::getPrintfStorageType(Type *valueType)
+{
+	Ice::Type valueTy = T(valueType);
+	switch(valueTy)
+	{
+		case Ice::IceType_i32:
+			return T(getNaturalIntType());
+
+		case Ice::IceType_f32:
+			return T(Ice::IceType_f64);
+
+		default:
+			UNIMPLEMENTED_NO_BUG("getPrintfStorageType: add more cases as needed");
+			return {};
+	}
+}
+
 Value *Nucleus::createNullValue(Type *Ty)
 {
 	if(Ice::isVectorType(T(Ty)))
@@ -1933,6 +2003,11 @@
 	return createConstantVector((const int64_t *)constants, type);
 }
 
+Value *Nucleus::createConstantString(const char *v)
+{
+	return V(IceConstantData(v, strlen(v) + 1));
+}
+
 Type *Void::getType()
 {
 	return T(Ice::IceType_void);