bool AllocaArraysMerging::runOnFunction(Function& F) { class ArraysToMerge { private: std::map<AllocaInst*, uint32_t> arraysToMerge; uint32_t currentOffset; public: ArraysToMerge():currentOffset(0) { } bool empty() const { return arraysToMerge.empty(); } std::map<AllocaInst*, uint32_t>::iterator begin() { return arraysToMerge.begin(); } std::map<AllocaInst*, uint32_t>::iterator end() { return arraysToMerge.end(); } void add(AllocaInst* a) { arraysToMerge.insert(std::make_pair(a, currentOffset)); currentOffset+=cast<ArrayType>(a->getAllocatedType())->getNumElements(); } uint32_t getNewSize() const { return currentOffset; } }; cheerp::PointerAnalyzer & PA = getAnalysis<cheerp::PointerAnalyzer>(); cheerp::Registerize & registerize = getAnalysis<cheerp::Registerize>(); cheerp::GlobalDepsAnalyzer & GDA = getAnalysis<cheerp::GlobalDepsAnalyzer>(); std::list<std::pair<AllocaInst*, cheerp::Registerize::LiveRange>> allocaInfos; // Gather all the allocas for(BasicBlock& BB: F) analyzeBlock(registerize, BB, allocaInfos); if (allocaInfos.size() < 2) return false; bool Changed = false; // We can also try to merge arrays of the same type, if only pointers to values are passed around while(!allocaInfos.empty()) { // Build a map of array to be merged and their offseet into the new array ArraysToMerge arraysToMerge; auto targetCandidate = allocaInfos.begin(); AllocaInst* targetAlloca = targetCandidate->first; if(!targetAlloca->getAllocatedType()->isArrayTy() || // Check target uses !checkUsesForArrayMerging(targetAlloca)) { allocaInfos.erase(targetCandidate); continue; } Type* targetElementType = targetAlloca->getAllocatedType()->getSequentialElementType(); auto sourceCandidate=targetCandidate; ++sourceCandidate; // Now that we have computed the sourceCandidate we can invalidate the targetCandidate allocaInfos.erase(targetCandidate); while(sourceCandidate!=allocaInfos.end()) { AllocaInst* sourceAlloca = sourceCandidate->first; // Check that allocas are arrays of the same type if(!sourceAlloca->getAllocatedType()->isArrayTy()) { ++sourceCandidate; continue; } // Both are arrays, check the types if(targetElementType != sourceAlloca->getAllocatedType()->getSequentialElementType()) { ++sourceCandidate; continue; } // Verify that the source candidate has supported uses if(!checkUsesForArrayMerging(sourceAlloca)) { ++sourceCandidate; continue; } // We can merge the source and the target // If the set is empty add the target as well if(arraysToMerge.empty()) arraysToMerge.add(targetAlloca); arraysToMerge.add(sourceAlloca); auto oldCandidate = sourceCandidate; ++sourceCandidate; // Now that we have moved to the next candidate, we can invalidate the old one allocaInfos.erase(oldCandidate); } // If we have a non-empty set of alloca merge them if (arraysToMerge.empty()) continue; if(!Changed) registerize.invalidateLiveRangeForAllocas(F); // Build new alloca Type* newAllocaType = ArrayType::get(targetElementType, arraysToMerge.getNewSize()); // Add the new struct type to the GlobalDepsAnalyzer, it may need the createArray helper GDA.visitType(newAllocaType, /*forceTypedArray*/ false); AllocaInst* newAlloca = new AllocaInst(newAllocaType, "mergedArray", &(*F.getEntryBlock().begin())); Type* indexType = IntegerType::get(newAllocaType->getContext(), 32); // Change every use of every merged array with an appropiate GEP for(auto it: arraysToMerge) { AllocaInst* allocaToMerge = it.first; uint32_t baseOffset = it.second; SmallVector<User*, 4> users(allocaToMerge->users()); for(User* u: users) { if(GetElementPtrInst* oldGep = dyn_cast<GetElementPtrInst>(u)) { // Build 2 GEPs, one to reach the first element in the merged array // and the other for the rest of the offsets SmallVector<Value*, 4> indices; // Dereference array indices.push_back(ConstantInt::get(indexType, 0)); // Reach offset indices.push_back(ConstantInt::get(indexType, baseOffset)); Value* gep1 = GetElementPtrInst::Create(newAlloca, indices, "", oldGep); // Apply all the old offsets but the first one using a new GEP indices.clear(); indices.insert(indices.begin(), oldGep->idx_begin()+1, oldGep->idx_end()); Value* gep2 = GetElementPtrInst::Create(gep1, indices, "", oldGep); // Replace all uses with gep2 oldGep->replaceAllUsesWith(gep2); PA.invalidate(oldGep); oldGep->eraseFromParent(); } else if(BitCastInst* BI=dyn_cast<BitCastInst>(u)) { //Only used for lifetime intrinsics Value* newBitCast=new BitCastInst(newAlloca, BI->getType(), "", BI); BI->replaceAllUsesWith(newBitCast); PA.invalidate(BI); BI->eraseFromParent(); } else assert(false && "Unexpected use while merging arrays"); } // Kill the alloca itself now PA.invalidate(allocaToMerge); allocaToMerge->eraseFromParent(); Changed = true; } } if(Changed) registerize.computeLiveRangeForAllocas(F); return Changed; }