Optimizer optimization

Optimizer::optimizeStoresInSingleBasicBlock() was taking an extremely long time due to
looping through all the instructions of a single basic block at every iteration. By
creating a much smaller list of only load/store operations the first time a single basic
block is encountered and reusing that list when the same block is encountered again,
this function now runs ~10X faster.

In my test case:
out/Default/cc_unittests --gtest_filter=ImageBackgroundFilter.BackgroundFilterRotated_GL
The sw::optimize function went from taking almost 16s to roughly 1.5s.

Bug b/67872293

Change-Id: I7e2e2aa8dc1bf2663988fb59b58d72d9ee986e36
Reviewed-on: https://swiftshader-review.googlesource.com/19408
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Reactor/Optimizer.cpp b/src/Reactor/Optimizer.cpp
index 2d4ac82..83ea5f2 100644
--- a/src/Reactor/Optimizer.cpp
+++ b/src/Reactor/Optimizer.cpp
@@ -266,6 +266,22 @@
 	{
 		Ice::CfgNode *entryBlock = function->getEntryNode();
 
+		struct LoadStoreInst
+		{
+			LoadStoreInst(Ice::Inst* inst, bool isStore)
+			 : inst(inst),
+			   address(isStore ? storeAddress(inst) : loadAddress(inst)),
+			   isStore(isStore)
+			{
+			}
+
+			Ice::Inst* inst;
+			Ice::Operand *address;
+			bool isStore;
+		};
+
+		std::unordered_map<Ice::CfgNode*, std::vector<LoadStoreInst> > loadStoreMap;
+
 		for(Ice::Inst &alloca : entryBlock->getInsts())
 		{
 			if(alloca.isDeleted())
@@ -301,56 +317,65 @@
 
 			if(singleBasicBlock)
 			{
-				auto &insts = singleBasicBlock->getInsts();
-				Ice::Inst *store = nullptr;
-				Ice::Operand *storeValue = nullptr;
-				bool unmatchedLoads = false;
-
-				for(Ice::Inst &inst : insts)
+				if(loadStoreMap.find(singleBasicBlock) == loadStoreMap.end())
 				{
-					if(inst.isDeleted())
+					std::vector<LoadStoreInst> &loadStoreVector = loadStoreMap[singleBasicBlock];
+					for(Ice::Inst &inst : singleBasicBlock->getInsts())
 					{
-						continue;
-					}
-
-					if(isStore(inst))
-					{
-						if(storeAddress(&inst) != address)
+						if(inst.isDeleted())
 						{
 							continue;
 						}
 
+						bool isStoreInst = isStore(inst);
+						bool isLoadInst = isLoad(inst);
+
+						if(isStoreInst || isLoadInst)
+						{
+							loadStoreVector.push_back(LoadStoreInst(&inst, isStoreInst));
+						}
+					}
+				}
+
+				Ice::Inst *store = nullptr;
+				Ice::Operand *storeValue = nullptr;
+				bool unmatchedLoads = false;
+
+				for (auto& loadStoreInst : loadStoreMap[singleBasicBlock])
+				{
+					Ice::Inst* inst = loadStoreInst.inst;
+
+					if((loadStoreInst.address != address) || inst->isDeleted())
+					{
+						continue;
+					}
+
+					if(loadStoreInst.isStore)
+					{
 						// New store found. If we had a previous one, try to eliminate it.
 						if(store && !unmatchedLoads)
 						{
 							// If the previous store is wider than the new one, we can't eliminate it
 							// because there could be a wide load reading its non-overwritten data.
-							if(storeSize(&inst) >= storeSize(store))
+							if(storeSize(inst) >= storeSize(store))
 							{
 								deleteInstruction(store);
 							}
 						}
 
-						store = &inst;
+						store = inst;
 						storeValue = storeData(store);
 						unmatchedLoads = false;
 					}
-					else if(isLoad(inst))
+					else
 					{
-						Ice::Inst *load = &inst;
-
-						if(loadAddress(load) != address)
-						{
-							continue;
-						}
-
-						if(!loadTypeMatchesStore(load, store))
+						if(!loadTypeMatchesStore(inst, store))
 						{
 							unmatchedLoads = true;
 							continue;
 						}
 
-						replace(load, storeValue);
+						replace(inst, storeValue);
 					}
 				}
 			}