void DataDependenceAnalysis::analyze(ir::IRKernel& kernel) { auto dfg = static_cast<DataflowGraph*>( getAnalysis("DataflowGraphAnalysis")); report("Running data dependence analysis on kernel " << kernel.name); for(auto block = dfg->begin(); block != dfg->end(); ++block) { analyzeBlock(block, dfg, _nodes, _instructionToNodes); } }
void SCCP::analyze() { // Queue the first block to start iteration. cfgwork.push(ir->begin); while (!cfgwork.empty()) { while (!cfgwork.empty()) analyzeBlock(cfgwork.pop()); while (!ssawork.empty()) analyzeSSA(ssawork.pop()); } // TODO own cmdline flag? something less intrusive // if ((enable_verbose || enable_printir) && ir->size() > 1000) // printCounts(ir, eval_counts); }
bool AllocaMerging::runOnFunction(Function& F) { cheerp::PointerAnalyzer & PA = getAnalysis<cheerp::PointerAnalyzer>(); cheerp::Registerize & registerize = getAnalysis<cheerp::Registerize>(); cheerp::TypeSupport types(*F.getParent()); AllocaInfos allocaInfos; // Gather all the allocas for(BasicBlock& BB: F) analyzeBlock(registerize, BB, allocaInfos); if (allocaInfos.size() < 2) return false; bool Changed = false; BasicBlock& entryBlock=F.getEntryBlock(); // Look if we can merge allocas of the same type for(auto targetCandidate=allocaInfos.begin();targetCandidate!=allocaInfos.end();++targetCandidate) { AllocaInst* targetAlloca = targetCandidate->first; Type* targetType = targetAlloca->getAllocatedType(); // The range storing the sum of all ranges merged into target cheerp::Registerize::LiveRange targetRange(targetCandidate->second); // If the range is empty, we have an alloca that we can't analyze if (targetRange.empty()) continue; std::vector<AllocaInfos::iterator> mergeSet; auto sourceCandidate=targetCandidate; ++sourceCandidate; for(;sourceCandidate!=allocaInfos.end();++sourceCandidate) { AllocaInst* sourceAlloca = sourceCandidate->first; Type* sourceType = sourceAlloca->getAllocatedType(); // Bail out for non compatible types if(!areTypesEquivalent(types, PA, targetType, sourceType)) continue; const cheerp::Registerize::LiveRange& sourceRange = sourceCandidate->second; // Bail out if this source candidate is not analyzable if(sourceRange.empty()) continue; // Bail out if the allocas interfere if(targetRange.doesInterfere(sourceRange)) continue; // Add the range to the target range and the source alloca to the mergeSet mergeSet.push_back(sourceCandidate); PA.invalidate(sourceAlloca); targetRange.merge(sourceRange); } // If the merge set is empty try another target if(mergeSet.empty()) continue; PA.invalidate(targetAlloca); if(!Changed) registerize.invalidateLiveRangeForAllocas(F); // Make sure that this alloca is in the entry block if(targetAlloca->getParent()!=&entryBlock) targetAlloca->moveBefore(entryBlock.begin()); // We can merge the allocas for(const AllocaInfos::iterator& it: mergeSet) { AllocaInst* allocaToMerge = it->first; Instruction* targetVal=targetAlloca; if(targetVal->getType()!=allocaToMerge->getType()) { targetVal=new BitCastInst(targetVal, allocaToMerge->getType()); targetVal->insertAfter(targetAlloca); } allocaToMerge->replaceAllUsesWith(targetVal); allocaToMerge->eraseFromParent(); if(targetVal != targetAlloca) PA.getPointerKind(targetVal); allocaInfos.erase(it); NumAllocaMerged++; } PA.getPointerKind(targetAlloca); Changed = true; } if(Changed) registerize.computeLiveRangeForAllocas(F); return Changed; }
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; }