Eliminate loading of uninitialized variables.

Bug swiftshader:27

Change-Id: I58259e00204550a397522fc26578c9f4d847f502
Reviewed-on: https://swiftshader-review.googlesource.com/8272
Tested-by: Nicolas Capens <capn@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <capn@google.com>
diff --git a/src/Reactor/Optimizer.cpp b/src/Reactor/Optimizer.cpp
index b528993..8ac70b1 100644
--- a/src/Reactor/Optimizer.cpp
+++ b/src/Reactor/Optimizer.cpp
@@ -30,19 +30,38 @@
 	private:
 		void analyzeUses(Ice::Cfg *function);
 		void eliminateUnusedAllocas();
+		void eliminateUnitializedLoads();
+
+		static bool isLoad(const Ice::Inst &instruction);
+		static bool isStore(const Ice::Inst &instruction);
+		static Ice::Operand *storeAddress(const Ice::Inst *instruction);
+		static Ice::Operand *loadAddress(const Ice::Inst *instruction);
 
 		Ice::Cfg *function;
+		Ice::GlobalContext *context;
 
-		std::map<Ice::Operand*, std::vector<Ice::Inst*>> uses;
+		struct Uses : std::vector<Ice::Inst*>
+		{
+			bool areOnlyLoadStore() const;
+			void insert(Ice::Operand *value, Ice::Inst *instruction);
+			void erase(Ice::Inst *instruction);
+
+			std::vector<Ice::Inst*> loads;
+			std::vector<Ice::Inst*> stores;
+		};
+
+		std::map<Ice::Operand*, Uses> uses;
 	};
 
 	void Optimizer::run(Ice::Cfg *function)
 	{
 		this->function = function;
+		this->context = function->getContext();
 
 		analyzeUses(function);
 
 		eliminateUnusedAllocas();
+		eliminateUnitializedLoads();
 	}
 
 	void Optimizer::eliminateUnusedAllocas()
@@ -65,6 +84,61 @@
 		}
 	}
 
+	void Optimizer::eliminateUnitializedLoads()
+	{
+		Ice::CfgNode *entryBlock = function->getEntryNode();
+
+		for(Ice::Inst &alloca : entryBlock->getInsts())
+		{
+			if(alloca.isDeleted())
+			{
+				continue;
+			}
+
+			if(!llvm::isa<Ice::InstAlloca>(alloca))
+			{
+				return;   // Allocas are all at the top
+			}
+
+			Ice::Operand *address = alloca.getDest();
+			const auto &addressEntry = uses.find(address);
+			const auto &addressUses = addressEntry->second;
+
+			if(!addressUses.areOnlyLoadStore())
+			{
+				continue;
+			}
+
+			if(addressUses.stores.empty())
+			{
+				for(Ice::Inst *load : addressUses.loads)
+				{
+					Ice::Variable *loadData = load->getDest();
+
+					for(Ice::Inst *use : uses[loadData])
+					{
+						for(int i = 0; i < use->getSrcSize(); i++)
+						{
+							if(use->getSrc(i) == loadData)
+							{
+								auto *undef = context->getConstantUndef(loadData->getType());
+
+								use->replaceSource(i, undef);
+							}
+						}
+					}
+
+					uses.erase(loadData);
+
+					load->setDeleted();
+				}
+
+				alloca.setDeleted();
+				uses.erase(addressEntry);
+			}
+		}
+	}
+
 	void Optimizer::analyzeUses(Ice::Cfg *function)
 	{
 		uses.clear();
@@ -91,12 +165,144 @@
 
 					if(i == unique)
 					{
-						uses[instruction.getSrc(i)].push_back(&instruction);
+						Ice::Operand *src = instruction.getSrc(i);
+						uses[src].insert(src, &instruction);
 					}
 				}
 			}
 		}
 	}
+
+	bool Optimizer::isLoad(const Ice::Inst &instruction)
+	{
+		if(llvm::isa<Ice::InstLoad>(&instruction))
+		{
+			return true;
+		}
+
+		if(auto intrinsicCall = llvm::dyn_cast<Ice::InstIntrinsicCall>(&instruction))
+		{
+			return intrinsicCall->getIntrinsicInfo().ID == Ice::Intrinsics::LoadSubVector;
+		}
+
+		return false;
+	}
+
+	bool Optimizer::isStore(const Ice::Inst &instruction)
+	{
+		if(llvm::isa<Ice::InstStore>(&instruction))
+		{
+			return true;
+		}
+
+		if(auto intrinsicCall = llvm::dyn_cast<Ice::InstIntrinsicCall>(&instruction))
+		{
+			return intrinsicCall->getIntrinsicInfo().ID == Ice::Intrinsics::StoreSubVector;
+		}
+
+		return false;
+	}
+
+	Ice::Operand *Optimizer::storeAddress(const Ice::Inst *instruction)
+	{
+		assert(isStore(*instruction));
+
+		if(auto *store = llvm::dyn_cast<Ice::InstStore>(instruction))
+		{
+			return store->getAddr();
+		}
+
+		if(auto *instrinsic = llvm::dyn_cast<Ice::InstIntrinsicCall>(instruction))
+		{
+			if(instrinsic->getIntrinsicInfo().ID == Ice::Intrinsics::StoreSubVector)
+			{
+				return instrinsic->getSrc(2);
+			}
+		}
+
+		return nullptr;
+	}
+
+	Ice::Operand *Optimizer::loadAddress(const Ice::Inst *instruction)
+	{
+		assert(isLoad(*instruction));
+
+		if(auto *load = llvm::dyn_cast<Ice::InstLoad>(instruction))
+		{
+			return load->getSourceAddress();
+		}
+
+		if(auto *instrinsic = llvm::dyn_cast<Ice::InstIntrinsicCall>(instruction))
+		{
+			if(instrinsic->getIntrinsicInfo().ID == Ice::Intrinsics::LoadSubVector)
+			{
+				return instrinsic->getSrc(1);
+			}
+		}
+
+		return nullptr;
+	}
+
+	bool Optimizer::Uses::areOnlyLoadStore() const
+	{
+		return size() == (loads.size() + stores.size());
+	}
+
+	void Optimizer::Uses::insert(Ice::Operand *value, Ice::Inst *instruction)
+	{
+		push_back(instruction);
+
+		if(isLoad(*instruction))
+		{
+			if(value == loadAddress(instruction))
+			{
+				loads.push_back(instruction);
+			}
+		}
+		else if(isStore(*instruction))
+		{
+			if(value == storeAddress(instruction))
+			{
+				stores.push_back(instruction);
+			}
+		}
+	}
+
+	void Optimizer::Uses::erase(Ice::Inst *instruction)
+	{
+		auto &uses = *this;
+
+		for(int i = 0; i < uses.size(); i++)
+		{
+			if(uses[i] == instruction)
+			{
+				uses[i] = back();
+				pop_back();
+
+				for(int i = 0; i < loads.size(); i++)
+				{
+					if(loads[i] == instruction)
+					{
+						loads[i] = loads.back();
+						loads.pop_back();
+						break;
+					}
+				}
+
+				for(int i = 0; i < stores.size(); i++)
+				{
+					if(stores[i] == instruction)
+					{
+						stores[i] = stores.back();
+						stores.pop_back();
+						break;
+					}
+				}
+
+				break;
+			}
+		}
+	}
 }
 
 namespace sw