Add a pragma option to zero-initialize Reactor local variables

MemorySanitizer errors in Reactor routines are either caused by
uninitialized heap allocations, incorrect instrumentation (false
positives), or uninitialized local variables (including shader
variables). To help confirm or eliminate the latter as a possible
cause, as well as to provide a convenient means to locate the
uninitialized variable through a divide-and-conquer strategy, this
change provides the `InitializeLocalVariables` pragma.

It is only supported by the LLVM backend, since this is also the only
backend which supports MemorySanitizer instrumentation.

Bug: b/191149148
Change-Id: I2f58c171e20bbcc19afdbc88bf61bf04a5bf477f
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/53688
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
diff --git a/src/Reactor/LLVMReactor.cpp b/src/Reactor/LLVMReactor.cpp
index 5538529..6632ffd 100644
--- a/src/Reactor/LLVMReactor.cpp
+++ b/src/Reactor/LLVMReactor.cpp
@@ -17,6 +17,7 @@
 #include "CPUID.hpp"
 #include "Debug.hpp"
 #include "LLVMReactorDebugInfo.hpp"
+#include "PragmaInternals.hpp"
 #include "Print.hpp"
 #include "Reactor.hpp"
 #include "x86.hpp"
@@ -631,6 +632,18 @@
 
 	entryBlock.getInstList().push_front(declaration);
 
+	if(getPragmaState(InitializeLocalVariables))
+	{
+		llvm::Type *i8PtrTy = llvm::Type::getInt8Ty(*jit->context)->getPointerTo();
+		llvm::Type *i32Ty = llvm::Type::getInt32Ty(*jit->context);
+		llvm::Function *memset = llvm::Intrinsic::getDeclaration(jit->module.get(), llvm::Intrinsic::memset, { i8PtrTy, i32Ty });
+
+		jit->builder->CreateCall(memset, { jit->builder->CreatePointerCast(declaration, i8PtrTy),
+		                                   V(Nucleus::createConstantByte((unsigned char)0)),
+		                                   V(Nucleus::createConstantInt((int)typeSize(type) * (arraySize ? arraySize : 1))),
+		                                   V(Nucleus::createConstantBool(false)) });
+	}
+
 	return V(declaration);
 }
 
diff --git a/src/Reactor/Pragma.cpp b/src/Reactor/Pragma.cpp
index 38e22ad..1175352 100644
--- a/src/Reactor/Pragma.cpp
+++ b/src/Reactor/Pragma.cpp
@@ -31,6 +31,7 @@
 struct PragmaState
 {
 	bool memorySanitizerInstrumentation = true;
+	bool initializeLocalVariables = false;
 	int optimizationLevel = 2;  // Default
 };
 
@@ -69,6 +70,9 @@
 	case MemorySanitizerInstrumentation:
 		state.memorySanitizerInstrumentation = enable;
 		break;
+	case InitializeLocalVariables:
+		state.initializeLocalVariables = enable;
+		break;
 	default:
 		UNSUPPORTED("Unknown Boolean pragma option %d", int(option));
 	}
@@ -96,6 +100,8 @@
 	{
 	case MemorySanitizerInstrumentation:
 		return state.memorySanitizerInstrumentation;
+	case InitializeLocalVariables:
+		return state.initializeLocalVariables;
 	default:
 		UNSUPPORTED("Unknown Boolean pragma option %d", int(option));
 		return false;
diff --git a/src/Reactor/Pragma.hpp b/src/Reactor/Pragma.hpp
index 3b86a03..ff4f00c 100644
--- a/src/Reactor/Pragma.hpp
+++ b/src/Reactor/Pragma.hpp
@@ -22,6 +22,7 @@
 enum BooleanPragmaOption
 {
 	MemorySanitizerInstrumentation,
+	InitializeLocalVariables,
 };
 
 enum IntegerPragmaOption
diff --git a/src/Reactor/SubzeroReactor.cpp b/src/Reactor/SubzeroReactor.cpp
index 5518086..86abd9b 100644
--- a/src/Reactor/SubzeroReactor.cpp
+++ b/src/Reactor/SubzeroReactor.cpp
@@ -104,6 +104,8 @@
 	auto alloca = Ice::InstAlloca::create(function, address, bytes, typeSize);  // SRoA depends on the alignment to match the type size.
 	function->getEntryNode()->getInsts().push_front(alloca);
 
+	ASSERT(!rr::getPragmaState(rr::InitializeLocalVariables) && "Subzero does not support initializing local variables");
+
 	return address;
 }