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/CMakeLists.txt b/CMakeLists.txt index aaf2be0..4451f44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -543,9 +543,8 @@ endif() endif() -if((${REACTOR_BACKEND} STREQUAL "Subzero") AND (REACTOR_ENABLE_PRINT OR REACTOR_EMIT_PRINT_LOCATION OR REACTOR_EMIT_DEBUG_INFO)) +if((${REACTOR_BACKEND} STREQUAL "Subzero") AND (REACTOR_EMIT_PRINT_LOCATION OR REACTOR_EMIT_DEBUG_INFO)) message(WARNING "REACTOR_ENABLE_PRINT, REACTOR_EMIT_PRINT_LOCATION, and REACTOR_EMIT_DEBUG_INFO are not supported by Subzero, disabling.") - set(REACTOR_ENABLE_PRINT "Off") set(REACTOR_EMIT_PRINT_LOCATION "Off") set(REACTOR_EMIT_DEBUG_INFO "Off") endif()
diff --git a/src/Reactor/LLVMReactor.cpp b/src/Reactor/LLVMReactor.cpp index c14012d..7f780a6 100644 --- a/src/Reactor/LLVMReactor.cpp +++ b/src/Reactor/LLVMReactor.cpp
@@ -77,19 +77,6 @@ return config; } -#ifdef ENABLE_RR_PRINT -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; -} -#endif // ENABLE_RR_PRINT - llvm::Value *lowerPAVG(llvm::Value *x, llvm::Value *y) { llvm::VectorType *ty = llvm::cast<llvm::VectorType>(x->getType()); @@ -1567,11 +1554,42 @@ jit->builder->CreateUnreachable(); } +Type *Nucleus::getType(Value *value) +{ + return T(V(value)->getType()); +} + +Type *Nucleus::getContainedType(Type *vectorType) +{ + return T(T(vectorType)->getContainedType(0)); +} + Type *Nucleus::getPointerType(Type *ElementType) { return T(llvm::PointerType::get(T(ElementType), 0)); } +static ::llvm::Type *getNaturalIntType() +{ + return ::llvm::Type::getIntNTy(jit->context, sizeof(int) * 8); +} + +Type *Nucleus::getPrintfStorageType(Type *valueType) +{ + llvm::Type *valueTy = T(valueType); + if(valueTy->isIntegerTy()) + { + return T(getNaturalIntType()); + } + if(valueTy->isFloatTy()) + { + return T(llvm::Type::getDoubleTy(jit->context)); + } + + UNIMPLEMENTED_NO_BUG("getPrintfStorageType: add more cases as needed"); + return {}; +} + Value *Nucleus::createNullValue(Type *Ty) { RR_DEBUG_INFO_UPDATE_LOC(); @@ -1670,6 +1688,12 @@ return V(llvm::ConstantVector::get(llvm::ArrayRef<llvm::Constant *>(constantVector, numElements))); } +Value *Nucleus::createConstantString(const char *v) +{ + auto ptr = jit->builder->CreateGlobalStringPtr(v); + return V(ptr); +} + Type *Void::getType() { return T(llvm::Type::getVoidTy(jit->context)); @@ -3827,189 +3851,13 @@ #endif // defined(__i386__) || defined(__x86_64__) #ifdef 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) +void VPrintf(const std::vector<Value *> &vals) { - std::vector<Value *> elements; - elements.reserve(n); - for(int i = 0; i < n; i++) - { - auto el = V(jit->builder->CreateExtractElement(V(vec), i)); - elements.push_back(el); - } - return elements; -} - -// toInt returns all the integer values in vals extended to a native width -// integer. -static std::vector<Value *> toInt(const std::vector<Value *> &vals, bool isSigned) -{ - auto intTy = ::llvm::Type::getIntNTy(jit->context, sizeof(int) * 8); // Natural integer width. - std::vector<Value *> elements; - elements.reserve(vals.size()); - for(auto v : vals) - { - if(isSigned) - { - elements.push_back(V(jit->builder->CreateSExt(V(v), intTy))); - } - else - { - elements.push_back(V(jit->builder->CreateZExt(V(v), intTy))); - } - } - 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(jit->context); - std::vector<Value *> elements; - elements.reserve(vals.size()); - for(auto v : vals) - { - elements.push_back(V(jit->builder->CreateFPExt(V(v), doubleTy))); - } - 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); -} - -std::vector<Value *> PrintValue::Ty<Byte4>::val(const RValue<Byte4> &v) -{ - return toInt(extractAll(v.value, 4), false); -} - -std::vector<Value *> PrintValue::Ty<Int>::val(const RValue<Int> &v) -{ - return toInt({ v.value }, true); -} - -std::vector<Value *> PrintValue::Ty<Int2>::val(const RValue<Int2> &v) -{ - return toInt(extractAll(v.value, 2), true); -} - -std::vector<Value *> PrintValue::Ty<Int4>::val(const RValue<Int4> &v) -{ - return toInt(extractAll(v.value, 4), true); -} - -std::vector<Value *> PrintValue::Ty<UInt>::val(const RValue<UInt> &v) -{ - return toInt({ v.value }, false); -} - -std::vector<Value *> PrintValue::Ty<UInt2>::val(const RValue<UInt2> &v) -{ - return toInt(extractAll(v.value, 2), false); -} - -std::vector<Value *> PrintValue::Ty<UInt4>::val(const RValue<UInt4> &v) -{ - return toInt(extractAll(v.value, 4), false); -} - -std::vector<Value *> PrintValue::Ty<Short>::val(const RValue<Short> &v) -{ - return toInt({ v.value }, true); -} - -std::vector<Value *> PrintValue::Ty<Short4>::val(const RValue<Short4> &v) -{ - return toInt(extractAll(v.value, 4), true); -} - -std::vector<Value *> PrintValue::Ty<UShort>::val(const RValue<UShort> &v) -{ - return toInt({ v.value }, false); -} - -std::vector<Value *> PrintValue::Ty<UShort4>::val(const RValue<UShort4> &v) -{ - return toInt(extractAll(v.value, 4), false); -} - -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)); -} - -std::vector<Value *> PrintValue::Ty<const char *>::val(const char *v) -{ - return { V(jit->builder->CreateGlobalStringPtr(v)) }; -} - -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(jit->context); - auto intTy = ::llvm::Type::getIntNTy(jit->context, sizeof(int) * 8); // Natural integer width. auto i8PtrTy = ::llvm::Type::getInt8PtrTy(jit->context); auto funcTy = ::llvm::FunctionType::get(i32Ty, { i8PtrTy }, true); - auto func = jit->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 substitution 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(jit->builder->CreateGlobalStringPtr(str)); - - // Add optional file, line and function info if provided. - if(file != nullptr) - { - vals.push_back(jit->builder->CreateGlobalStringPtr(file)); - if(line > 0) - { - vals.push_back(::llvm::ConstantInt::get(intTy, line)); - } - } - if(function != nullptr) - { - vals.push_back(jit->builder->CreateGlobalStringPtr(function)); - } - - // Add all format arguments. - for(const PrintValue &arg : args) - { - for(auto val : arg.values) - { - vals.push_back(V(val)); - } - } - - jit->builder->CreateCall(func, vals); + jit->builder->CreateCall(func, V(vals)); } #endif // ENABLE_RR_PRINT
diff --git a/src/Reactor/LLVMReactor.hpp b/src/Reactor/LLVMReactor.hpp index db7df10..fc3f1a2 100644 --- a/src/Reactor/LLVMReactor.hpp +++ b/src/Reactor/LLVMReactor.hpp
@@ -65,6 +65,17 @@ return reinterpret_cast<Value *>(t); } +inline std::vector<llvm::Value *> V(const std::vector<Value *> &values) +{ + std::vector<llvm::Value *> result; + result.reserve(values.size()); + for(auto &v : values) + { + result.push_back(V(v)); + } + return result; +} + // Emits a no-op instruction that will not be optimized away. // Useful for emitting something that can have a source location without // effect.
diff --git a/src/Reactor/Nucleus.hpp b/src/Reactor/Nucleus.hpp index 513f4fb..4c08ddd 100644 --- a/src/Reactor/Nucleus.hpp +++ b/src/Reactor/Nucleus.hpp
@@ -21,6 +21,7 @@ #include <cstdint> #include <functional> #include <memory> +#include <string> #include <vector> #ifdef None @@ -326,8 +327,13 @@ static Value *createNullPointer(Type *type); static Value *createConstantVector(const int64_t *constants, Type *type); static Value *createConstantVector(const double *constants, Type *type); + static Value *createConstantString(const char *v); + static Value *createConstantString(const std::string &v) { return createConstantString(v.c_str()); } + static Type *getType(Value *value); + static Type *getContainedType(Type *vectorType); static Type *getPointerType(Type *elementType); + static Type *getPrintfStorageType(Type *valueType); }; } // namespace rr
diff --git a/src/Reactor/Print.hpp b/src/Reactor/Print.hpp index a5bde3e..ca70465 100644 --- a/src/Reactor/Print.hpp +++ b/src/Reactor/Print.hpp
@@ -337,6 +337,10 @@ static std::vector<Value *> val(const RValue<T> &v) { return PrintValue::Ty<T>::val(v); } }; +// VPrintf emits a call to printf() using vals[0] as the format string, +// and vals[1..n] as the args. +void VPrintf(const std::vector<Value *> &vals); + // Printv emits a call to printf() using the function, file and line, // message and optional values. // See Printv below.
diff --git a/src/Reactor/Reactor.cpp b/src/Reactor/Reactor.cpp index 8801f2a..abe4882 100644 --- a/src/Reactor/Reactor.cpp +++ b/src/Reactor/Reactor.cpp
@@ -14,6 +14,7 @@ #include "Reactor.hpp" #include "Debug.hpp" +#include "Print.hpp" #include <cmath> @@ -4596,4 +4597,197 @@ // TODO: Long has no constructor that takes a uint64_t // Long CToReactor<uint64_t>::cast(uint64_t v) { return type(v); } +#ifdef ENABLE_RR_PRINT +static std::string replaceAll(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; +} + +// extractAll returns a vector containing the extracted n scalar value of +// the vector vec. +// TODO: Move to Reactor.cpp (LLVMReactor can use this too) +static std::vector<Value *> extractAll(Value *vec, int n) +{ + Type *elemTy = Nucleus::getContainedType(Nucleus::getType(vec)); + std::vector<Value *> elements; + elements.reserve(n); + for(int i = 0; i < n; i++) + { + auto el = Nucleus::createExtractElement(vec, elemTy, i); + elements.push_back(el); + } + return elements; +} + +// toInt returns all the integer values in vals extended to a printf-required storage value +static std::vector<Value *> toInt(const std::vector<Value *> &vals, bool isSigned) +{ + auto storageTy = Nucleus::getPrintfStorageType(Int::getType()); + std::vector<Value *> elements; + elements.reserve(vals.size()); + for(auto v : vals) + { + if(isSigned) + { + elements.push_back(Nucleus::createSExt(v, storageTy)); + } + else + { + elements.push_back(Nucleus::createZExt(v, storageTy)); + } + } + return elements; +} + +// toFloat returns all the float values in vals extended to extended to a printf-required storage value +static std::vector<Value *> toFloat(const std::vector<Value *> &vals) +{ + auto storageTy = Nucleus::getPrintfStorageType(Float::getType()); + std::vector<Value *> elements; + elements.reserve(vals.size()); + for(auto v : vals) + { + elements.push_back(Nucleus::createFPExt(v, storageTy)); + } + return elements; +} + +std::vector<Value *> PrintValue::Ty<Bool>::val(const RValue<Bool> &v) +{ + auto t = Nucleus::createConstantString("true"); + auto f = Nucleus::createConstantString("false"); + return { Nucleus::createSelect(v.value, t, f) }; +} + +std::vector<Value *> PrintValue::Ty<Byte>::val(const RValue<Byte> &v) +{ + return toInt({ v.value }, false); +} + +std::vector<Value *> PrintValue::Ty<Byte4>::val(const RValue<Byte4> &v) +{ + return toInt(extractAll(v.value, 4), false); +} + +std::vector<Value *> PrintValue::Ty<Int>::val(const RValue<Int> &v) +{ + return toInt({ v.value }, true); +} + +std::vector<Value *> PrintValue::Ty<Int2>::val(const RValue<Int2> &v) +{ + return toInt(extractAll(v.value, 2), true); +} + +std::vector<Value *> PrintValue::Ty<Int4>::val(const RValue<Int4> &v) +{ + return toInt(extractAll(v.value, 4), true); +} + +std::vector<Value *> PrintValue::Ty<UInt>::val(const RValue<UInt> &v) +{ + return toInt({ v.value }, false); +} + +std::vector<Value *> PrintValue::Ty<UInt2>::val(const RValue<UInt2> &v) +{ + return toInt(extractAll(v.value, 2), false); +} + +std::vector<Value *> PrintValue::Ty<UInt4>::val(const RValue<UInt4> &v) +{ + return toInt(extractAll(v.value, 4), false); +} + +std::vector<Value *> PrintValue::Ty<Short>::val(const RValue<Short> &v) +{ + return toInt({ v.value }, true); +} + +std::vector<Value *> PrintValue::Ty<Short4>::val(const RValue<Short4> &v) +{ + return toInt(extractAll(v.value, 4), true); +} + +std::vector<Value *> PrintValue::Ty<UShort>::val(const RValue<UShort> &v) +{ + return toInt({ v.value }, false); +} + +std::vector<Value *> PrintValue::Ty<UShort4>::val(const RValue<UShort4> &v) +{ + return toInt(extractAll(v.value, 4), false); +} + +std::vector<Value *> PrintValue::Ty<Float>::val(const RValue<Float> &v) +{ + return toFloat({ v.value }); +} + +std::vector<Value *> PrintValue::Ty<Float4>::val(const RValue<Float4> &v) +{ + return toFloat(extractAll(v.value, 4)); +} + +std::vector<Value *> PrintValue::Ty<const char *>::val(const char *v) +{ + return { Nucleus::createConstantString(v) }; +} + +void Printv(const char *function, const char *file, int line, const char *fmt, std::initializer_list<PrintValue> args) +{ + // 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 substitution on all '{n}' bracketed indices in the format + // message. + int i = 0; + for(const PrintValue &arg : args) + { + str = replaceAll(str, "{" + std::to_string(i++) + "}", arg.format); + } + + std::vector<Value *> vals; + vals.reserve(8); + + // The format message is always the first argument. + vals.push_back(Nucleus::createConstantString(str)); + + // Add optional file, line and function info if provided. + if(file != nullptr) + { + vals.push_back(Nucleus::createConstantString(file)); + if(line > 0) + { + vals.push_back(Nucleus::createConstantInt(line)); + } + } + if(function != nullptr) + { + vals.push_back(Nucleus::createConstantString(function)); + } + + // Add all format arguments. + for(const PrintValue &arg : args) + { + for(auto val : arg.values) + { + vals.push_back(val); + } + } + + // This call is implemented by each backend + VPrintf(vals); +} +#endif // ENABLE_RR_PRINT + } // namespace rr
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);