Implement Pragma() for Reactor MemorySanitizer instrumentation

Enabling MemorySanitizer instrumentation for all Reactor routines can
have too big of a performance impact. To support selective
instrumentation, Pragma(MemorySanitizerInstrumentation, true) can be
called to enable it for the next routine that gets created.

It must be called before the Function<> constructor. Pragma state is
thread local, so Pragma(MemorySanitizerInstrumentation, false) must be
called after the routine has been created, to disable it again if
instrumentation is not desired for subsequent routines created in this
thread.

Note that REACTOR_ENABLE_MEMORY_SANITIZER_INSTRUMENTATION=true enables
MemorySanitizer instrumentation for Reactor routines even if the pragma
state is false.

Bug: b/191050320
Change-Id: Ie71aadeae140e85bda31554788288e138df0d08c
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/54988
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Android.bp b/src/Android.bp
index ed1b022..2124801 100644
--- a/src/Android.bp
+++ b/src/Android.bp
@@ -206,6 +206,7 @@
         "Reactor/LLVMJIT.cpp",
         "Reactor/LLVMReactor.cpp",
         "Reactor/OptimalIntrinsics.cpp",
+        "Reactor/Pragma.cpp",
         "Reactor/Reactor.cpp",
     ],
 
diff --git a/src/Reactor/BUILD.gn b/src/Reactor/BUILD.gn
index d7ca841..4d9ddaa 100644
--- a/src/Reactor/BUILD.gn
+++ b/src/Reactor/BUILD.gn
@@ -49,6 +49,7 @@
     "EmulatedIntrinsics.cpp",
     "ExecutableMemory.cpp",
     "OptimalIntrinsics.cpp",
+    "Pragma.cpp",
     "Reactor.cpp",
   ]
 
diff --git a/src/Reactor/CMakeLists.txt b/src/Reactor/CMakeLists.txt
index 268c1f7..32af533 100644
--- a/src/Reactor/CMakeLists.txt
+++ b/src/Reactor/CMakeLists.txt
@@ -27,6 +27,9 @@
     Nucleus.hpp
     OptimalIntrinsics.cpp
     OptimalIntrinsics.hpp
+    Pragma.cpp
+    Pragma.hpp
+    PragmaInternals.hpp
     Print.hpp
     Reactor.cpp
     Reactor.hpp
diff --git a/src/Reactor/LLVMJIT.cpp b/src/Reactor/LLVMJIT.cpp
index 64dd21e..6bf9323 100644
--- a/src/Reactor/LLVMJIT.cpp
+++ b/src/Reactor/LLVMJIT.cpp
@@ -17,6 +17,7 @@
 #include "Debug.hpp"
 #include "ExecutableMemory.hpp"
 #include "LLVMAsm.hpp"
+#include "PragmaInternals.hpp"
 #include "Routine.hpp"
 
 // TODO(b/143539525): Eliminate when warning has been fixed.
@@ -823,7 +824,8 @@
 	module->setTargetTriple(LLVM_DEFAULT_TARGET_TRIPLE);
 	module->setDataLayout(JITGlobals::get()->getDataLayout());
 
-	if(REACTOR_ENABLE_MEMORY_SANITIZER_INSTRUMENTATION)
+	if(REACTOR_ENABLE_MEMORY_SANITIZER_INSTRUMENTATION ||
+	   getPragmaState(MemorySanitizerInstrumentation))
 	{
 		msanInstrumentation = true;
 	}
diff --git a/src/Reactor/Pragma.cpp b/src/Reactor/Pragma.cpp
new file mode 100644
index 0000000..136be5e
--- /dev/null
+++ b/src/Reactor/Pragma.cpp
@@ -0,0 +1,48 @@
+// Copyright 2021 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "Pragma.hpp"
+#include "PragmaInternals.hpp"
+
+#include "Debug.hpp"
+
+namespace rr {
+
+static thread_local bool memorySanitizerInstrumentation = false;
+
+void Pragma(PragmaBooleanOption option, bool enable)
+{
+	switch(option)
+	{
+	case MemorySanitizerInstrumentation:
+		memorySanitizerInstrumentation = enable;
+		break;
+	default:
+		UNSUPPORTED("Unknown pragma %d", int(option));
+	}
+}
+
+bool getPragmaState(PragmaBooleanOption option)
+{
+	switch(option)
+	{
+	case MemorySanitizerInstrumentation:
+		return memorySanitizerInstrumentation;
+	default:
+		UNSUPPORTED("Unknown pragma %d", int(option));
+		return false;
+	}
+}
+
+}  // namespace rr
\ No newline at end of file
diff --git a/src/Reactor/Pragma.hpp b/src/Reactor/Pragma.hpp
new file mode 100644
index 0000000..7f05095
--- /dev/null
+++ b/src/Reactor/Pragma.hpp
@@ -0,0 +1,29 @@
+// Copyright 2021 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef rr_Pragma_hpp
+#define rr_Pragma_hpp
+
+namespace rr {
+
+enum PragmaBooleanOption
+{
+	MemorySanitizerInstrumentation,
+};
+
+void Pragma(PragmaBooleanOption option, bool enable);
+
+}  // namespace rr
+
+#endif  // rr_Pragma_hpp
\ No newline at end of file
diff --git a/src/Reactor/PragmaInternals.hpp b/src/Reactor/PragmaInternals.hpp
new file mode 100644
index 0000000..9009d34
--- /dev/null
+++ b/src/Reactor/PragmaInternals.hpp
@@ -0,0 +1,26 @@
+// Copyright 2021 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef rr_PragmaInternals_hpp
+#define rr_PragmaInternals_hpp
+
+#include "Pragma.hpp"
+
+namespace rr {
+
+bool getPragmaState(PragmaBooleanOption option);
+
+}  // namespace rr
+
+#endif  // rr_Pragma_hpp
\ No newline at end of file
diff --git a/src/Reactor/Reactor.hpp b/src/Reactor/Reactor.hpp
index aa115c5..240e9dc 100644
--- a/src/Reactor/Reactor.hpp
+++ b/src/Reactor/Reactor.hpp
@@ -16,6 +16,7 @@
 #define rr_Reactor_hpp
 
 #include "Nucleus.hpp"
+#include "Pragma.hpp"
 #include "Routine.hpp"
 #include "Traits.hpp"
 
diff --git a/tests/ReactorUnitTests/ReactorUnitTests.cpp b/tests/ReactorUnitTests/ReactorUnitTests.cpp
index 499608e..9a39f01 100644
--- a/tests/ReactorUnitTests/ReactorUnitTests.cpp
+++ b/tests/ReactorUnitTests/ReactorUnitTests.cpp
@@ -136,9 +136,16 @@
 	EXPECT_EQ(result, 80);
 }
 
-#if REACTOR_ENABLE_MEMORY_SANITIZER_INSTRUMENTATION
 TEST(ReactorUnitTests, Uninitialized)
 {
+#if __has_feature(memory_sanitizer)
+	// Building the static C++ code with MemorySanitizer enabled does not
+	// automatically enable MemorySanitizer instrumentation for Reactor
+	// routines. False positives can also be prevented by unpoisoning all
+	// memory writes. This Pragma ensures proper instrumentation is enabled.
+	Pragma(MemorySanitizerInstrumentation, true);
+#endif
+
 	FunctionT<int()> function;
 	{
 		Int a;
@@ -178,8 +185,9 @@
 		    },
 		    "MemorySanitizer: use-of-uninitialized-value");
 	}
+
+	Pragma(MemorySanitizerInstrumentation, false);
 }
-#endif
 
 TEST(ReactorUnitTests, Unreachable)
 {