@@ -31,6 +31,14 @@ static cl::opt<bool> DisableCGDataForMerging(
3131 " merging is still enabled within a module." ),
3232 cl::init(false ));
3333
34+ STATISTIC (NumMismatchedFunctionHash,
35+ " Number of mismatched function hash for global merge function" );
36+ STATISTIC (NumMismatchedInstCount,
37+ " Number of mismatched instruction count for global merge function" );
38+ STATISTIC (NumMismatchedConstHash,
39+ " Number of mismatched const hash for global merge function" );
40+ STATISTIC (NumMismatchedModuleId,
41+ " Number of mismatched Module Id for global merge function" );
3442STATISTIC (NumMergedFunctions,
3543 " Number of functions that are actually merged using function hash" );
3644STATISTIC (NumAnalyzedModues, " Number of modules that are analyzed" );
@@ -102,7 +110,7 @@ bool isEligibleFunction(Function *F) {
102110 return true ;
103111}
104112
105- static bool isEligibleInstructionForConstantSharing (const Instruction *I) {
113+ static bool isEligibleInstrunctionForConstantSharing (const Instruction *I) {
106114 switch (I->getOpcode ()) {
107115 case Instruction::Load:
108116 case Instruction::Store:
@@ -114,15 +122,10 @@ static bool isEligibleInstructionForConstantSharing(const Instruction *I) {
114122 }
115123}
116124
117- // This function takes an instruction, \p I, and an operand index, \p OpIdx.
118- // It returns true if the operand should be ignored in the hash computation.
119- // If \p OpIdx is out of range based on the other instruction context, it cannot
120- // be ignored.
121125static bool ignoreOp (const Instruction *I, unsigned OpIdx) {
122- if (OpIdx >= I->getNumOperands ())
123- return false ;
126+ assert (OpIdx < I->getNumOperands () && " Invalid operand index" );
124127
125- if (!isEligibleInstructionForConstantSharing (I))
128+ if (!isEligibleInstrunctionForConstantSharing (I))
126129 return false ;
127130
128131 if (!isa<Constant>(I->getOperand (OpIdx)))
@@ -200,9 +203,9 @@ void GlobalMergeFunc::analyze(Module &M) {
200203struct FuncMergeInfo {
201204 StableFunctionMap::StableFunctionEntry *SF;
202205 Function *F;
203- IndexInstrMap * IndexInstruction;
206+ std::unique_ptr< IndexInstrMap> IndexInstruction;
204207 FuncMergeInfo (StableFunctionMap::StableFunctionEntry *SF, Function *F,
205- IndexInstrMap * IndexInstruction)
208+ std::unique_ptr< IndexInstrMap> IndexInstruction)
206209 : SF(SF), F(F), IndexInstruction(std::move(IndexInstruction)) {}
207210};
208211
@@ -417,75 +420,101 @@ static ParamLocsVecTy computeParamInfo(
417420bool GlobalMergeFunc::merge (Module &M, const StableFunctionMap *FunctionMap) {
418421 bool Changed = false ;
419422
420- // Collect stable functions related to the current module.
421- DenseMap<stable_hash, SmallVector<std::pair<Function *, FunctionHashInfo>>>
422- HashToFuncs;
423- auto &Maps = FunctionMap->getFunctionMap ();
424- for (auto &F : M) {
425- if (!isEligibleFunction (&F))
426- continue ;
427- auto FI = llvm::StructuralHashWithDifferences (F, ignoreOp);
428- if (Maps.contains (FI.FunctionHash ))
429- HashToFuncs[FI.FunctionHash ].emplace_back (&F, std::move (FI));
430- }
431-
432- for (auto &[Hash, Funcs] : HashToFuncs) {
423+ // Build a map from stable function name to function.
424+ StringMap<Function *> StableNameToFuncMap;
425+ for (auto &F : M)
426+ StableNameToFuncMap[get_stable_name (F.getName ())] = &F;
427+ // Track merged functions
428+ DenseSet<Function *> MergedFunctions;
429+
430+ auto ModId = M.getModuleIdentifier ();
431+ for (auto &[Hash, SFS] : FunctionMap->getFunctionMap ()) {
432+ // Parameter locations based on the unique hash sequences
433+ // across the candidates.
433434 std::optional<ParamLocsVecTy> ParamLocsVec;
435+ Function *MergedFunc = nullptr ;
436+ std::string MergedModId;
434437 SmallVector<FuncMergeInfo> FuncMergeInfos;
435- auto &SFS = Maps.at (Hash);
436- assert (!SFS.empty ());
437- auto &RFS = SFS[0 ];
438-
439- // Iterate functions with the same hash.
440- for (auto &[F, FI] : Funcs) {
441- // Check if the function is compatible with any stable function
442- // in terms of the number of instructions and ignored operands.
443- if (RFS->InstCount != FI.IndexInstruction ->size ())
438+ for (auto &SF : SFS) {
439+ // Get the function from the stable name.
440+ auto I = StableNameToFuncMap.find (
441+ *FunctionMap->getNameForId (SF->FunctionNameId ));
442+ if (I == StableNameToFuncMap.end ())
443+ continue ;
444+ Function *F = I->second ;
445+ assert (F);
446+ // Skip if the function has been merged before.
447+ if (MergedFunctions.count (F))
448+ continue ;
449+ // Consider the function if it is eligible for merging.
450+ if (!isEligibleFunction (F))
444451 continue ;
445452
446- auto hasValidSharedConst =
447- [&](StableFunctionMap::StableFunctionEntry *SF) {
448- for (auto &[Index, Hash] : *SF->IndexOperandHashMap ) {
449- auto [InstIndex, OpndIndex] = Index;
450- assert (InstIndex < FI.IndexInstruction ->size ());
451- auto *Inst = FI.IndexInstruction ->lookup (InstIndex);
452- if (!ignoreOp (Inst, OpndIndex))
453- return false ;
454- }
455- return true ;
456- };
457- if (!hasValidSharedConst (RFS.get ()))
453+ auto FI = llvm::StructuralHashWithDifferences (*F, ignoreOp);
454+ uint64_t FuncHash = FI.FunctionHash ;
455+ if (Hash != FuncHash) {
456+ ++NumMismatchedFunctionHash;
458457 continue ;
458+ }
459459
460- for ( auto &SF : SFS ) {
461- assert (SF-> InstCount == FI. IndexInstruction -> size ()) ;
462- assert ( hasValidSharedConst (SF. get ())) ;
463- // Check if there is any stable function that is compatiable with the
464- // current one.
465- if (! checkConstHashCompatible ( *SF->IndexOperandHashMap ,
466- *FI. IndexOperandHashMap ))
467- continue ;
468- if (!ParamLocsVec. has_value ()) {
469- ParamLocsVec = computeParamInfo (SFS);
470- LLVM_DEBUG ( dbgs () << " [GlobalMergeFunc] Merging hash: " << Hash
471- << " with Params " << ParamLocsVec-> size () << " \n " ) ;
460+ if (SF-> InstCount != FI. IndexInstruction -> size () ) {
461+ ++NumMismatchedInstCount ;
462+ continue ;
463+ }
464+ bool HasValidSharedConst = true ;
465+ for ( auto &[Index, Hash] : *SF->IndexOperandHashMap ) {
466+ auto [InstIndex, OpndIndex] = Index;
467+ assert (InstIndex < FI. IndexInstruction -> size ()) ;
468+ auto *Inst = FI. IndexInstruction -> lookup (InstIndex);
469+ if (! ignoreOp (Inst, OpndIndex)) {
470+ HasValidSharedConst = false ;
471+ break ;
472472 }
473- if (!checkConstLocationCompatible (*SF, *FI.IndexInstruction ,
474- *ParamLocsVec))
475- continue ;
473+ }
474+ if (!HasValidSharedConst) {
475+ ++NumMismatchedConstHash;
476+ continue ;
477+ }
478+ if (!checkConstHashCompatible (*SF->IndexOperandHashMap ,
479+ *FI.IndexOperandHashMap )) {
480+ ++NumMismatchedConstHash;
481+ continue ;
482+ }
483+ if (!ParamLocsVec.has_value ()) {
484+ ParamLocsVec = computeParamInfo (SFS);
485+ LLVM_DEBUG (dbgs () << " [GlobalMergeFunc] Merging hash: " << Hash
486+ << " with Params " << ParamLocsVec->size () << " \n " );
487+ }
488+ if (!checkConstLocationCompatible (*SF, *FI.IndexInstruction ,
489+ *ParamLocsVec)) {
490+ ++NumMismatchedConstHash;
491+ continue ;
492+ }
476493
477- // If a stable function matching the current one is found,
478- // create a candidate for merging and proceed to the next function.
479- FuncMergeInfos.emplace_back (SF.get (), F, FI.IndexInstruction .get ());
480- break ;
494+ if (MergedFunc) {
495+ // Check if the matched functions fall into the same (first) module.
496+ // This module check is not strictly necessary as the functions can move
497+ // around. We just want to avoid merging functions from different
498+ // modules than the first one in the function map, as they may not end
499+ // up with being ICFed by the linker.
500+ if (MergedModId != *FunctionMap->getNameForId (SF->ModuleNameId )) {
501+ ++NumMismatchedModuleId;
502+ continue ;
503+ }
504+ } else {
505+ MergedFunc = F;
506+ MergedModId = *FunctionMap->getNameForId (SF->ModuleNameId );
481507 }
508+
509+ FuncMergeInfos.emplace_back (SF.get (), F, std::move (FI.IndexInstruction ));
510+ MergedFunctions.insert (F);
482511 }
483512 unsigned FuncMergeInfoSize = FuncMergeInfos.size ();
484513 if (FuncMergeInfoSize == 0 )
485514 continue ;
486515
487516 LLVM_DEBUG (dbgs () << " [GlobalMergeFunc] Merging function count "
488- << FuncMergeInfoSize << " for hash: " << Hash << " \n " );
517+ << FuncMergeInfoSize << " in " << ModId << " \n " );
489518
490519 for (auto &FMI : FuncMergeInfos) {
491520 Changed = true ;
0 commit comments