LLVMReactor: Clamp RHS of bit shifts.

This is a temporary fix for MSAN failures, preventing SwiftShader from rolling into Chromium.

Bug: swiftshader:185
Change-Id: Ib6ee7e02dd6e061095e4d9b67cb73769999a7f80
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/72008
Reviewed-by: David Neto <dneto@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Presubmit-Ready: Ben Clayton <bclayton@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
diff --git a/src/Reactor/LLVMReactor.cpp b/src/Reactor/LLVMReactor.cpp
index def17d3..31dbfe3 100644
--- a/src/Reactor/LLVMReactor.cpp
+++ b/src/Reactor/LLVMReactor.cpp
@@ -356,6 +356,21 @@
 	return jit->builder->CreateTrunc(mulh, ty);
 }
 
+// TODO(crbug.com/swiftshader/185): A temporary workaround for failing chromium tests.
+llvm::Value *clampForShift(llvm::Value *rhs)
+{
+	llvm::Value *max;
+	if(auto *vec = llvm::dyn_cast<llvm::VectorType>(rhs->getType()))
+	{
+		max = llvm::ConstantVector::getSplat(vec->getNumElements(), llvm::ConstantInt::get(vec->getElementType(), 31));
+	}
+	else
+	{
+		max = llvm::ConstantInt::get(rhs->getType(), 31);
+	}
+	return jit->builder->CreateSelect(jit->builder->CreateICmpULE(rhs, max), rhs, max);
+}
+
 }  // namespace
 
 namespace rr {
@@ -806,13 +821,15 @@
 Value *Nucleus::createShl(Value *lhs, Value *rhs)
 {
 	RR_DEBUG_INFO_UPDATE_LOC();
-	return V(jit->builder->CreateFreeze(jit->builder->CreateShl(V(lhs), V(rhs))));
+	auto *clamped_rhs = clampForShift(V(rhs));
+	return V(jit->builder->CreateFreeze(jit->builder->CreateShl(V(lhs), clamped_rhs)));
 }
 
 Value *Nucleus::createLShr(Value *lhs, Value *rhs)
 {
 	RR_DEBUG_INFO_UPDATE_LOC();
-	return V(jit->builder->CreateFreeze(jit->builder->CreateLShr(V(lhs), V(rhs))));
+	auto *clamped_rhs = clampForShift(V(rhs));
+	return V(jit->builder->CreateFreeze(jit->builder->CreateLShr(V(lhs), clamped_rhs)));
 }
 
 Value *Nucleus::createAShr(Value *lhs, Value *rhs)
diff --git a/tests/ReactorUnitTests/ReactorUnitTests.cpp b/tests/ReactorUnitTests/ReactorUnitTests.cpp
index 6407e60..1c234f1 100644
--- a/tests/ReactorUnitTests/ReactorUnitTests.cpp
+++ b/tests/ReactorUnitTests/ReactorUnitTests.cpp
@@ -2061,6 +2061,169 @@
 	}
 }
 
+TEST(ReactorUnitTests, ShlSmallRHSScalar)
+{
+	// TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
+	if(Caps::backendName().find("LLVM") == std::string::npos) return;
+
+	FunctionT<unsigned()> function;
+	{
+		auto lhs = UInt(4);
+		auto rhs = UInt(8);
+		auto res = lhs << rhs;
+		Return(res);
+	}
+
+	auto routine = function(testName().c_str());
+
+	unsigned res = routine();
+	EXPECT_EQ(res, 1u << 10u);
+}
+
+TEST(ReactorUnitTests, ShlLargeRHSScalar)
+{
+	// TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
+	if(Caps::backendName().find("LLVM") == std::string::npos) return;
+
+	FunctionT<unsigned()> function;
+	{
+		auto lhs = UInt(1);
+		auto rhs = UInt(99);
+		auto res = lhs << rhs;
+		Return(res);
+	}
+
+	auto routine = function(testName().c_str());
+
+	unsigned res = routine();
+	EXPECT_EQ(res, 1u << 31u);
+}
+
+TEST(ReactorUnitTests, ShrSmallRHSScalar)
+{
+	// TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
+	if(Caps::backendName().find("LLVM") == std::string::npos) return;
+
+	FunctionT<unsigned()> function;
+	{
+		auto lhs = UInt(64);
+		auto rhs = UInt(4);
+		auto res = lhs >> rhs;
+		Return(res);
+	}
+
+	auto routine = function(testName().c_str());
+
+	unsigned res = routine();
+	EXPECT_EQ(res, 4);
+}
+
+TEST(ReactorUnitTests, ShrLargeRHSScalar)
+{
+	// TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
+	if(Caps::backendName().find("LLVM") == std::string::npos) return;
+
+	FunctionT<unsigned()> function;
+	{
+		auto lhs = UInt(4);
+		auto rhs = UInt(99);
+		auto res = lhs >> rhs;
+		Return(res);
+	}
+
+	auto routine = function(testName().c_str());
+
+	unsigned res = routine();
+	EXPECT_EQ(res, 0);
+}
+
+TEST(ReactorUnitTests, ShlRHSVector)
+{
+	// TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
+	if(Caps::backendName().find("LLVM") == std::string::npos) return;
+
+	FunctionT<void(unsigned *a, unsigned *b, unsigned *c, unsigned *d)> function;
+	{
+		Pointer<UInt> a = function.Arg<0>();
+		Pointer<UInt> b = function.Arg<1>();
+		Pointer<UInt> c = function.Arg<2>();
+		Pointer<UInt> d = function.Arg<3>();
+
+		auto lhs = UInt4(4, 3, 6, 5);
+		auto rhs = UInt4(8, 99, 2, 50);
+		UInt4 res = lhs << rhs;
+		*a = res.x;
+		*b = res.y;
+		*c = res.z;
+		*d = res.w;
+	}
+
+	auto routine = function(testName().c_str());
+
+	unsigned a = 0;
+	unsigned b = 0;
+	unsigned c = 0;
+	unsigned d = 0;
+	routine(&a, &b, &c, &d);
+	EXPECT_EQ(a, 1024);
+	EXPECT_EQ(b, 0x80000000);
+	EXPECT_EQ(c, 24);
+	EXPECT_EQ(d, 0x80000000);
+}
+
+TEST(ReactorUnitTests, ShrRHSVector)
+{
+	// TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
+	if(Caps::backendName().find("LLVM") == std::string::npos) return;
+
+	FunctionT<void(unsigned *a, unsigned *b, unsigned *c, unsigned *d)> function;
+	{
+		Pointer<UInt> a = function.Arg<0>();
+		Pointer<UInt> b = function.Arg<1>();
+		Pointer<UInt> c = function.Arg<2>();
+		Pointer<UInt> d = function.Arg<3>();
+
+		auto lhs = UInt4(745, 23, 234, 54);
+		auto rhs = UInt4(8, 99, 2, 50);
+		UInt4 res = lhs >> rhs;
+		*a = res.x;
+		*b = res.y;
+		*c = res.z;
+		*d = res.w;
+	}
+
+	auto routine = function(testName().c_str());
+
+	unsigned a = 0;
+	unsigned b = 0;
+	unsigned c = 0;
+	unsigned d = 0;
+	routine(&a, &b, &c, &d);
+	EXPECT_EQ(a, 2);
+	EXPECT_EQ(b, 0);
+	EXPECT_EQ(c, 58);
+	EXPECT_EQ(d, 0);
+}
+
+TEST(ReactorUnitTests, ShrLargeRHSVector)
+{
+	// TODO(crbug.com/swiftshader/185): Testing a temporary LLVM workaround
+	if(Caps::backendName().find("LLVM") == std::string::npos) return;
+
+	FunctionT<unsigned()> function;
+	{
+		auto lhs = UInt(4);
+		auto rhs = UInt(99);
+		auto res = lhs >> rhs;
+		Return(res);
+	}
+
+	auto routine = function(testName().c_str());
+
+	unsigned res = routine();
+	EXPECT_EQ(res, 0);
+}
+
 TEST(ReactorUnitTests, Call)
 {
 	struct Class