Eliminate loads following a single store.

Bug swiftshader:27

Change-Id: I11238decf114381126a7465c462d918a3f16b0d8
Reviewed-on: https://swiftshader-review.googlesource.com/8273
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 8ac70b1..a3b328e 100644
--- a/src/Reactor/Optimizer.cpp
+++ b/src/Reactor/Optimizer.cpp
@@ -31,11 +31,16 @@
 		void analyzeUses(Ice::Cfg *function);
 		void eliminateUnusedAllocas();
 		void eliminateUnitializedLoads();
+		void eliminateLoadsFollowingSingleStore();
+
+		void replace(Ice::Inst *instruction, Ice::Operand *newValue);
+		void deleteInstruction(Ice::Inst *instruction);
 
 		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);
+		static Ice::Operand *storeData(const Ice::Inst *instruction);
 
 		Ice::Cfg *function;
 		Ice::GlobalContext *context;
@@ -51,6 +56,8 @@
 		};
 
 		std::map<Ice::Operand*, Uses> uses;
+		std::map<Ice::Inst*, Ice::CfgNode*> node;
+		std::map<Ice::Variable*, Ice::Inst*> definition;
 	};
 
 	void Optimizer::run(Ice::Cfg *function)
@@ -62,6 +69,7 @@
 
 		eliminateUnusedAllocas();
 		eliminateUnitializedLoads();
+		eliminateLoadsFollowingSingleStore();
 	}
 
 	void Optimizer::eliminateUnusedAllocas()
@@ -139,9 +147,107 @@
 		}
 	}
 
+	void Optimizer::eliminateLoadsFollowingSingleStore()
+	{
+		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);
+			auto &addressUses = addressEntry->second;
+
+			if(!addressUses.areOnlyLoadStore())
+			{
+				continue;
+			}
+
+			if(addressUses.stores.size() == 1)
+			{
+				Ice::Inst *store = addressUses.stores[0];
+				Ice::Operand *storeValue = storeData(store);
+
+				for(Ice::Inst *load = &*++store->getIterator(), *next = nullptr; load != next; next = load, load = &*++store->getIterator())
+				{
+					if(load->isDeleted() || !isLoad(*load))
+					{
+						continue;
+					}
+
+					if(loadAddress(load) != address)
+					{
+						continue;
+					}
+
+					replace(load, storeValue);
+
+					for(int i = 0; i < addressUses.loads.size(); i++)
+					{
+						if(addressUses.loads[i] == load)
+						{
+							addressUses.loads[i] = addressUses.loads.back();
+							addressUses.loads.pop_back();
+							break;
+						}
+					}
+
+					for(int i = 0; i < addressUses.size(); i++)
+					{
+						if(addressUses[i] == load)
+						{
+							addressUses[i] = addressUses.back();
+							addressUses.pop_back();
+							break;
+						}
+					}
+
+					if(addressUses.size() == 1)
+					{
+						assert(addressUses[0] == store);
+
+						alloca.setDeleted();
+						store->setDeleted();
+						uses.erase(address);
+
+						auto &valueUses = uses[storeValue];
+
+						for(int i = 0; i < valueUses.size(); i++)
+						{
+							if(valueUses[i] == store)
+							{
+								valueUses[i] = valueUses.back();
+								valueUses.pop_back();
+								break;
+							}
+						}
+
+						if(valueUses.empty())
+						{
+							uses.erase(storeValue);
+						}
+
+						break;
+					}
+				}
+			}
+		}
+	}
+
 	void Optimizer::analyzeUses(Ice::Cfg *function)
 	{
 		uses.clear();
+		node.clear();
+		definition.clear();
 
 		for(Ice::CfgNode *basicBlock : function->getNodes())
 		{
@@ -152,6 +258,9 @@
 					continue;
 				}
 
+				node[&instruction] = basicBlock;
+				definition[instruction.getDest()] = &instruction;
+
 				for(int i = 0; i < instruction.getSrcSize(); i++)
 				{
 					int unique = 0;
@@ -173,6 +282,69 @@
 		}
 	}
 
+	void Optimizer::replace(Ice::Inst *instruction, Ice::Operand *newValue)
+	{
+		Ice::Variable *oldValue = instruction->getDest();
+
+		if(!newValue)
+		{
+			newValue = context->getConstantUndef(oldValue->getType());
+		}
+
+		for(Ice::Inst *use : uses[oldValue])
+		{
+			assert(!use->isDeleted());   // Should have been removed from uses already
+
+			for(int i = 0; i < use->getSrcSize(); i++)
+			{
+				if(use->getSrc(i) == oldValue)
+				{
+					use->replaceSource(i, newValue);
+				}
+			}
+
+			uses[newValue].insert(newValue, use);
+		}
+
+		uses.erase(oldValue);
+
+		deleteInstruction(instruction);
+	}
+
+	void Optimizer::deleteInstruction(Ice::Inst *instruction)
+	{
+		if(instruction->isDeleted())
+		{
+			return;
+		}
+
+		instruction->setDeleted();
+
+		for(int i = 0; i < instruction->getSrcSize(); i++)
+		{
+			Ice::Operand *src = instruction->getSrc(i);
+
+			const auto &srcEntry = uses.find(src);
+
+			if(srcEntry != uses.end())
+			{
+				auto &srcUses = srcEntry->second;
+
+				srcUses.erase(instruction);
+
+				if(srcUses.empty())
+				{
+					uses.erase(srcEntry);
+
+					if(Ice::Variable *var = llvm::dyn_cast<Ice::Variable>(src))
+					{
+						deleteInstruction(definition[var]);
+					}
+				}
+			}
+		}
+	}
+
 	bool Optimizer::isLoad(const Ice::Inst &instruction)
 	{
 		if(llvm::isa<Ice::InstLoad>(&instruction))
@@ -243,6 +415,26 @@
 		return nullptr;
 	}
 
+	Ice::Operand *Optimizer::storeData(const Ice::Inst *instruction)
+	{
+		assert(isStore(*instruction));
+
+		if(auto *store = llvm::dyn_cast<Ice::InstStore>(instruction))
+		{
+			return store->getData();
+		}
+
+		if(auto *instrinsic = llvm::dyn_cast<Ice::InstIntrinsicCall>(instruction))
+		{
+			if(instrinsic->getIntrinsicInfo().ID == Ice::Intrinsics::StoreSubVector)
+			{
+				return instrinsic->getSrc(1);
+			}
+		}
+
+		return nullptr;
+	}
+
 	bool Optimizer::Uses::areOnlyLoadStore() const
 	{
 		return size() == (loads.size() + stores.size());