12f09f445SMaksim Panchenko //===- bolt/Passes/IdenticalCodeFolding.cpp -------------------------------===//
2a34c753fSRafael Auler //
3a34c753fSRafael Auler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a34c753fSRafael Auler // See https://llvm.org/LICENSE.txt for license information.
5a34c753fSRafael Auler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a34c753fSRafael Auler //
7a34c753fSRafael Auler //===----------------------------------------------------------------------===//
8a34c753fSRafael Auler //
92f09f445SMaksim Panchenko // This file implements the IdenticalCodeFolding class.
102f09f445SMaksim Panchenko //
11a34c753fSRafael Auler //===----------------------------------------------------------------------===//
12a34c753fSRafael Auler
13a34c753fSRafael Auler #include "bolt/Passes/IdenticalCodeFolding.h"
14a34c753fSRafael Auler #include "bolt/Core/ParallelUtilities.h"
15a34c753fSRafael Auler #include "llvm/Support/CommandLine.h"
16a34c753fSRafael Auler #include "llvm/Support/ThreadPool.h"
17a34c753fSRafael Auler #include "llvm/Support/Timer.h"
18a34c753fSRafael Auler #include <atomic>
19a34c753fSRafael Auler #include <map>
20a34c753fSRafael Auler #include <set>
21a34c753fSRafael Auler #include <unordered_map>
22a34c753fSRafael Auler
23a34c753fSRafael Auler #define DEBUG_TYPE "bolt-icf"
24a34c753fSRafael Auler
25a34c753fSRafael Auler using namespace llvm;
26a34c753fSRafael Auler using namespace bolt;
27a34c753fSRafael Auler
28a34c753fSRafael Auler namespace opts {
29a34c753fSRafael Auler
30a34c753fSRafael Auler extern cl::OptionCategory BoltOptCategory;
31a34c753fSRafael Auler
32b92436efSFangrui Song static cl::opt<bool> UseDFS("icf-dfs",
33a34c753fSRafael Auler cl::desc("use DFS ordering when using -icf option"),
34b92436efSFangrui Song cl::ReallyHidden, cl::cat(BoltOptCategory));
35a34c753fSRafael Auler
36a34c753fSRafael Auler static cl::opt<bool>
37a34c753fSRafael Auler TimeICF("time-icf",
38a34c753fSRafael Auler cl::desc("time icf steps"),
39a34c753fSRafael Auler cl::ReallyHidden,
40a34c753fSRafael Auler cl::ZeroOrMore,
41a34c753fSRafael Auler cl::cat(BoltOptCategory));
42a34c753fSRafael Auler } // namespace opts
43a34c753fSRafael Auler
44a34c753fSRafael Auler namespace {
45a34c753fSRafael Auler using JumpTable = bolt::JumpTable;
46a34c753fSRafael Auler
47a34c753fSRafael Auler /// Compare two jump tables in 2 functions. The function relies on consistent
48a34c753fSRafael Auler /// ordering of basic blocks in both binary functions (e.g. DFS).
equalJumpTables(const JumpTable & JumpTableA,const JumpTable & JumpTableB,const BinaryFunction & FunctionA,const BinaryFunction & FunctionB)4940c2e0faSMaksim Panchenko bool equalJumpTables(const JumpTable &JumpTableA, const JumpTable &JumpTableB,
50a34c753fSRafael Auler const BinaryFunction &FunctionA,
51a34c753fSRafael Auler const BinaryFunction &FunctionB) {
52a34c753fSRafael Auler if (JumpTableA.EntrySize != JumpTableB.EntrySize)
53a34c753fSRafael Auler return false;
54a34c753fSRafael Auler
55a34c753fSRafael Auler if (JumpTableA.Type != JumpTableB.Type)
56a34c753fSRafael Auler return false;
57a34c753fSRafael Auler
58a34c753fSRafael Auler if (JumpTableA.getSize() != JumpTableB.getSize())
59a34c753fSRafael Auler return false;
60a34c753fSRafael Auler
61a34c753fSRafael Auler for (uint64_t Index = 0; Index < JumpTableA.Entries.size(); ++Index) {
62a34c753fSRafael Auler const MCSymbol *LabelA = JumpTableA.Entries[Index];
63a34c753fSRafael Auler const MCSymbol *LabelB = JumpTableB.Entries[Index];
64a34c753fSRafael Auler
65a34c753fSRafael Auler const BinaryBasicBlock *TargetA = FunctionA.getBasicBlockForLabel(LabelA);
66a34c753fSRafael Auler const BinaryBasicBlock *TargetB = FunctionB.getBasicBlockForLabel(LabelB);
67a34c753fSRafael Auler
68a34c753fSRafael Auler if (!TargetA || !TargetB) {
69a34c753fSRafael Auler assert((TargetA || LabelA == FunctionA.getFunctionEndLabel()) &&
70a34c753fSRafael Auler "no target basic block found");
71a34c753fSRafael Auler assert((TargetB || LabelB == FunctionB.getFunctionEndLabel()) &&
72a34c753fSRafael Auler "no target basic block found");
73a34c753fSRafael Auler
74a34c753fSRafael Auler if (TargetA != TargetB)
75a34c753fSRafael Auler return false;
76a34c753fSRafael Auler
77a34c753fSRafael Auler continue;
78a34c753fSRafael Auler }
79a34c753fSRafael Auler
80a34c753fSRafael Auler assert(TargetA && TargetB && "cannot locate target block(s)");
81a34c753fSRafael Auler
82a34c753fSRafael Auler if (TargetA->getLayoutIndex() != TargetB->getLayoutIndex())
83a34c753fSRafael Auler return false;
84a34c753fSRafael Auler }
85a34c753fSRafael Auler
86a34c753fSRafael Auler return true;
87a34c753fSRafael Auler }
88a34c753fSRafael Auler
89a34c753fSRafael Auler /// Helper function that compares an instruction of this function to the
90a34c753fSRafael Auler /// given instruction of the given function. The functions should have
91a34c753fSRafael Auler /// identical CFG.
92a34c753fSRafael Auler template <class Compare>
isInstrEquivalentWith(const MCInst & InstA,const BinaryBasicBlock & BBA,const MCInst & InstB,const BinaryBasicBlock & BBB,Compare Comp)93a34c753fSRafael Auler bool isInstrEquivalentWith(const MCInst &InstA, const BinaryBasicBlock &BBA,
94a34c753fSRafael Auler const MCInst &InstB, const BinaryBasicBlock &BBB,
95a34c753fSRafael Auler Compare Comp) {
96f92ab6afSAmir Ayupov if (InstA.getOpcode() != InstB.getOpcode())
97a34c753fSRafael Auler return false;
98a34c753fSRafael Auler
99a34c753fSRafael Auler const BinaryContext &BC = BBA.getFunction()->getBinaryContext();
100a34c753fSRafael Auler
101a34c753fSRafael Auler // In this function we check for special conditions:
102a34c753fSRafael Auler //
103a34c753fSRafael Auler // * instructions with landing pads
104a34c753fSRafael Auler //
105a34c753fSRafael Auler // Most of the common cases should be handled by MCPlus::equals()
106a34c753fSRafael Auler // that compares regular instruction operands.
107a34c753fSRafael Auler //
108a34c753fSRafael Auler // NB: there's no need to compare jump table indirect jump instructions
109a34c753fSRafael Auler // separately as jump tables are handled by comparing corresponding
110a34c753fSRafael Auler // symbols.
111a34c753fSRafael Auler const Optional<MCPlus::MCLandingPad> EHInfoA = BC.MIB->getEHInfo(InstA);
112a34c753fSRafael Auler const Optional<MCPlus::MCLandingPad> EHInfoB = BC.MIB->getEHInfo(InstB);
113a34c753fSRafael Auler
114a34c753fSRafael Auler if (EHInfoA || EHInfoB) {
115a34c753fSRafael Auler if (!EHInfoA && (EHInfoB->first || EHInfoB->second))
116a34c753fSRafael Auler return false;
117a34c753fSRafael Auler
118a34c753fSRafael Auler if (!EHInfoB && (EHInfoA->first || EHInfoA->second))
119a34c753fSRafael Auler return false;
120a34c753fSRafael Auler
121a34c753fSRafael Auler if (EHInfoA && EHInfoB) {
122a34c753fSRafael Auler // Action indices should match.
123a34c753fSRafael Auler if (EHInfoA->second != EHInfoB->second)
124a34c753fSRafael Auler return false;
125a34c753fSRafael Auler
126a34c753fSRafael Auler if (!EHInfoA->first != !EHInfoB->first)
127a34c753fSRafael Auler return false;
128a34c753fSRafael Auler
129a34c753fSRafael Auler if (EHInfoA->first && EHInfoB->first) {
130a34c753fSRafael Auler const BinaryBasicBlock *LPA = BBA.getLandingPad(EHInfoA->first);
131a34c753fSRafael Auler const BinaryBasicBlock *LPB = BBB.getLandingPad(EHInfoB->first);
132a34c753fSRafael Auler assert(LPA && LPB && "cannot locate landing pad(s)");
133a34c753fSRafael Auler
134a34c753fSRafael Auler if (LPA->getLayoutIndex() != LPB->getLayoutIndex())
135a34c753fSRafael Auler return false;
136a34c753fSRafael Auler }
137a34c753fSRafael Auler }
138a34c753fSRafael Auler }
139a34c753fSRafael Auler
140a34c753fSRafael Auler return BC.MIB->equals(InstA, InstB, Comp);
141a34c753fSRafael Auler }
142a34c753fSRafael Auler
143a34c753fSRafael Auler /// Returns true if this function has identical code and CFG with
144a34c753fSRafael Auler /// the given function \p BF.
145a34c753fSRafael Auler ///
146a34c753fSRafael Auler /// If \p CongruentSymbols is set to true, then symbolic operands that reference
147a34c753fSRafael Auler /// potentially identical but different functions are ignored during the
148a34c753fSRafael Auler /// comparison.
isIdenticalWith(const BinaryFunction & A,const BinaryFunction & B,bool CongruentSymbols)149a34c753fSRafael Auler bool isIdenticalWith(const BinaryFunction &A, const BinaryFunction &B,
150a34c753fSRafael Auler bool CongruentSymbols) {
151a34c753fSRafael Auler assert(A.hasCFG() && B.hasCFG() && "both functions should have CFG");
152a34c753fSRafael Auler
153a34c753fSRafael Auler // Compare the two functions, one basic block at a time.
154a34c753fSRafael Auler // Currently we require two identical basic blocks to have identical
155a34c753fSRafael Auler // instruction sequences and the same index in their corresponding
156a34c753fSRafael Auler // functions. The latter is important for CFG equality.
157a34c753fSRafael Auler
158*8477bc67SFabian Parzefall if (A.getLayout().block_size() != B.getLayout().block_size())
159a34c753fSRafael Auler return false;
160a34c753fSRafael Auler
161a34c753fSRafael Auler // Comparing multi-entry functions could be non-trivial.
162a34c753fSRafael Auler if (A.isMultiEntry() || B.isMultiEntry())
163a34c753fSRafael Auler return false;
164a34c753fSRafael Auler
165a34c753fSRafael Auler // Process both functions in either DFS or existing order.
166*8477bc67SFabian Parzefall const BinaryFunction::BasicBlockOrderType OrderA =
167*8477bc67SFabian Parzefall opts::UseDFS
168*8477bc67SFabian Parzefall ? A.dfs()
169*8477bc67SFabian Parzefall : BinaryFunction::BasicBlockOrderType(A.getLayout().block_begin(),
170*8477bc67SFabian Parzefall A.getLayout().block_end());
171*8477bc67SFabian Parzefall const BinaryFunction::BasicBlockOrderType OrderB =
172*8477bc67SFabian Parzefall opts::UseDFS
173*8477bc67SFabian Parzefall ? B.dfs()
174*8477bc67SFabian Parzefall : BinaryFunction::BasicBlockOrderType(B.getLayout().block_begin(),
175*8477bc67SFabian Parzefall B.getLayout().block_end());
176a34c753fSRafael Auler
177a34c753fSRafael Auler const BinaryContext &BC = A.getBinaryContext();
178a34c753fSRafael Auler
179a34c753fSRafael Auler auto BBI = OrderB.begin();
180a34c753fSRafael Auler for (const BinaryBasicBlock *BB : OrderA) {
181a34c753fSRafael Auler const BinaryBasicBlock *OtherBB = *BBI;
182a34c753fSRafael Auler
183a34c753fSRafael Auler if (BB->getLayoutIndex() != OtherBB->getLayoutIndex())
184a34c753fSRafael Auler return false;
185a34c753fSRafael Auler
186a34c753fSRafael Auler // Compare successor basic blocks.
187a34c753fSRafael Auler // NOTE: the comparison for jump tables is only partially verified here.
188a34c753fSRafael Auler if (BB->succ_size() != OtherBB->succ_size())
189a34c753fSRafael Auler return false;
190a34c753fSRafael Auler
191a34c753fSRafael Auler auto SuccBBI = OtherBB->succ_begin();
192a34c753fSRafael Auler for (const BinaryBasicBlock *SuccBB : BB->successors()) {
193a34c753fSRafael Auler const BinaryBasicBlock *SuccOtherBB = *SuccBBI;
194a34c753fSRafael Auler if (SuccBB->getLayoutIndex() != SuccOtherBB->getLayoutIndex())
195a34c753fSRafael Auler return false;
196a34c753fSRafael Auler ++SuccBBI;
197a34c753fSRafael Auler }
198a34c753fSRafael Auler
199a34c753fSRafael Auler // Compare all instructions including pseudos.
200a34c753fSRafael Auler auto I = BB->begin(), E = BB->end();
201a34c753fSRafael Auler auto OtherI = OtherBB->begin(), OtherE = OtherBB->end();
202a34c753fSRafael Auler while (I != E && OtherI != OtherE) {
203a34c753fSRafael Auler // Compare symbols.
204a34c753fSRafael Auler auto AreSymbolsIdentical = [&](const MCSymbol *SymbolA,
205a34c753fSRafael Auler const MCSymbol *SymbolB) {
206a34c753fSRafael Auler if (SymbolA == SymbolB)
207a34c753fSRafael Auler return true;
208a34c753fSRafael Auler
209a34c753fSRafael Auler // All local symbols are considered identical since they affect a
210a34c753fSRafael Auler // control flow and we check the control flow separately.
211a34c753fSRafael Auler // If a local symbol is escaped, then the function (potentially) has
212a34c753fSRafael Auler // multiple entry points and we exclude such functions from
213a34c753fSRafael Auler // comparison.
214a34c753fSRafael Auler if (SymbolA->isTemporary() && SymbolB->isTemporary())
215a34c753fSRafael Auler return true;
216a34c753fSRafael Auler
217a34c753fSRafael Auler // Compare symbols as functions.
218a34c753fSRafael Auler uint64_t EntryIDA = 0;
219a34c753fSRafael Auler uint64_t EntryIDB = 0;
220a34c753fSRafael Auler const BinaryFunction *FunctionA =
221a34c753fSRafael Auler BC.getFunctionForSymbol(SymbolA, &EntryIDA);
222a34c753fSRafael Auler const BinaryFunction *FunctionB =
223a34c753fSRafael Auler BC.getFunctionForSymbol(SymbolB, &EntryIDB);
224a34c753fSRafael Auler if (FunctionA && EntryIDA)
225a34c753fSRafael Auler FunctionA = nullptr;
226a34c753fSRafael Auler if (FunctionB && EntryIDB)
227a34c753fSRafael Auler FunctionB = nullptr;
228a34c753fSRafael Auler if (FunctionA && FunctionB) {
229a34c753fSRafael Auler // Self-referencing functions and recursive calls.
230a34c753fSRafael Auler if (FunctionA == &A && FunctionB == &B)
231a34c753fSRafael Auler return true;
232a34c753fSRafael Auler
233a34c753fSRafael Auler // Functions with different hash values can never become identical,
234a34c753fSRafael Auler // hence A and B are different.
235a34c753fSRafael Auler if (CongruentSymbols)
236a34c753fSRafael Auler return FunctionA->getHash() == FunctionB->getHash();
237a34c753fSRafael Auler
238a34c753fSRafael Auler return FunctionA == FunctionB;
239a34c753fSRafael Auler }
240a34c753fSRafael Auler
241a34c753fSRafael Auler // One of the symbols represents a function, the other one does not.
242f92ab6afSAmir Ayupov if (FunctionA != FunctionB)
243a34c753fSRafael Auler return false;
244a34c753fSRafael Auler
245a34c753fSRafael Auler // Check if symbols are jump tables.
246a34c753fSRafael Auler const BinaryData *SIA = BC.getBinaryDataByName(SymbolA->getName());
247a34c753fSRafael Auler if (!SIA)
248a34c753fSRafael Auler return false;
249a34c753fSRafael Auler const BinaryData *SIB = BC.getBinaryDataByName(SymbolB->getName());
250a34c753fSRafael Auler if (!SIB)
251a34c753fSRafael Auler return false;
252a34c753fSRafael Auler
253a34c753fSRafael Auler assert((SIA->getAddress() != SIB->getAddress()) &&
254a34c753fSRafael Auler "different symbols should not have the same value");
255a34c753fSRafael Auler
256a34c753fSRafael Auler const JumpTable *JumpTableA =
257a34c753fSRafael Auler A.getJumpTableContainingAddress(SIA->getAddress());
258a34c753fSRafael Auler if (!JumpTableA)
259a34c753fSRafael Auler return false;
260a34c753fSRafael Auler
261a34c753fSRafael Auler const JumpTable *JumpTableB =
262a34c753fSRafael Auler B.getJumpTableContainingAddress(SIB->getAddress());
263a34c753fSRafael Auler if (!JumpTableB)
264a34c753fSRafael Auler return false;
265a34c753fSRafael Auler
266a34c753fSRafael Auler if ((SIA->getAddress() - JumpTableA->getAddress()) !=
267a34c753fSRafael Auler (SIB->getAddress() - JumpTableB->getAddress()))
268a34c753fSRafael Auler return false;
269a34c753fSRafael Auler
270a34c753fSRafael Auler return equalJumpTables(*JumpTableA, *JumpTableB, A, B);
271a34c753fSRafael Auler };
272a34c753fSRafael Auler
273a34c753fSRafael Auler if (!isInstrEquivalentWith(*I, *BB, *OtherI, *OtherBB,
274f92ab6afSAmir Ayupov AreSymbolsIdentical))
275a34c753fSRafael Auler return false;
276a34c753fSRafael Auler
27740c2e0faSMaksim Panchenko ++I;
27840c2e0faSMaksim Panchenko ++OtherI;
279a34c753fSRafael Auler }
280a34c753fSRafael Auler
281a34c753fSRafael Auler // One of the identical blocks may have a trailing unconditional jump that
282a34c753fSRafael Auler // is ignored for CFG purposes.
283a34c753fSRafael Auler const MCInst *TrailingInstr =
284a34c753fSRafael Auler (I != E ? &(*I) : (OtherI != OtherE ? &(*OtherI) : 0));
285f92ab6afSAmir Ayupov if (TrailingInstr && !BC.MIB->isUnconditionalBranch(*TrailingInstr))
286a34c753fSRafael Auler return false;
287a34c753fSRafael Auler
288a34c753fSRafael Auler ++BBI;
289a34c753fSRafael Auler }
290a34c753fSRafael Auler
291a34c753fSRafael Auler // Compare exceptions action tables.
292a34c753fSRafael Auler if (A.getLSDAActionTable() != B.getLSDAActionTable() ||
293a34c753fSRafael Auler A.getLSDATypeTable() != B.getLSDATypeTable() ||
294f92ab6afSAmir Ayupov A.getLSDATypeIndexTable() != B.getLSDATypeIndexTable())
295a34c753fSRafael Auler return false;
296a34c753fSRafael Auler
297a34c753fSRafael Auler return true;
298a34c753fSRafael Auler }
299a34c753fSRafael Auler
300a34c753fSRafael Auler // This hash table is used to identify identical functions. It maps
301a34c753fSRafael Auler // a function to a bucket of functions identical to it.
302a34c753fSRafael Auler struct KeyHash {
operator ()__anon2344f6bf0111::KeyHash30340c2e0faSMaksim Panchenko size_t operator()(const BinaryFunction *F) const { return F->getHash(); }
304a34c753fSRafael Auler };
305a34c753fSRafael Auler
306a34c753fSRafael Auler /// Identify two congruent functions. Two functions are considered congruent,
307a34c753fSRafael Auler /// if they are identical/equal except for some of their instruction operands
308a34c753fSRafael Auler /// that reference potentially identical functions, i.e. functions that could
309a34c753fSRafael Auler /// be folded later. Congruent functions are candidates for folding in our
310a34c753fSRafael Auler /// iterative ICF algorithm.
311a34c753fSRafael Auler ///
312a34c753fSRafael Auler /// Congruent functions are required to have identical hash.
313a34c753fSRafael Auler struct KeyCongruent {
operator ()__anon2344f6bf0111::KeyCongruent314a34c753fSRafael Auler bool operator()(const BinaryFunction *A, const BinaryFunction *B) const {
315a34c753fSRafael Auler if (A == B)
316a34c753fSRafael Auler return true;
317a34c753fSRafael Auler return isIdenticalWith(*A, *B, /*CongruentSymbols=*/true);
318a34c753fSRafael Auler }
319a34c753fSRafael Auler };
320a34c753fSRafael Auler
321a34c753fSRafael Auler struct KeyEqual {
operator ()__anon2344f6bf0111::KeyEqual322a34c753fSRafael Auler bool operator()(const BinaryFunction *A, const BinaryFunction *B) const {
323a34c753fSRafael Auler if (A == B)
324a34c753fSRafael Auler return true;
325a34c753fSRafael Auler return isIdenticalWith(*A, *B, /*CongruentSymbols=*/false);
326a34c753fSRafael Auler }
327a34c753fSRafael Auler };
328a34c753fSRafael Auler
329a34c753fSRafael Auler typedef std::unordered_map<BinaryFunction *, std::set<BinaryFunction *>,
330a34c753fSRafael Auler KeyHash, KeyCongruent>
331a34c753fSRafael Auler CongruentBucketsMap;
332a34c753fSRafael Auler
333a34c753fSRafael Auler typedef std::unordered_map<BinaryFunction *, std::vector<BinaryFunction *>,
334a34c753fSRafael Auler KeyHash, KeyEqual>
335a34c753fSRafael Auler IdenticalBucketsMap;
336a34c753fSRafael Auler
hashInteger(uint64_t Value)337a34c753fSRafael Auler std::string hashInteger(uint64_t Value) {
338a34c753fSRafael Auler std::string HashString;
339f92ab6afSAmir Ayupov if (Value == 0)
340a34c753fSRafael Auler HashString.push_back(0);
341f92ab6afSAmir Ayupov
342a34c753fSRafael Auler while (Value) {
343a34c753fSRafael Auler uint8_t LSB = Value & 0xff;
344a34c753fSRafael Auler HashString.push_back(LSB);
345a34c753fSRafael Auler Value >>= 8;
346a34c753fSRafael Auler }
347a34c753fSRafael Auler
348a34c753fSRafael Auler return HashString;
349a34c753fSRafael Auler }
350a34c753fSRafael Auler
hashSymbol(BinaryContext & BC,const MCSymbol & Symbol)351a34c753fSRafael Auler std::string hashSymbol(BinaryContext &BC, const MCSymbol &Symbol) {
352a34c753fSRafael Auler std::string HashString;
353a34c753fSRafael Auler
354a34c753fSRafael Auler // Ignore function references.
355a34c753fSRafael Auler if (BC.getFunctionForSymbol(&Symbol))
356a34c753fSRafael Auler return HashString;
357a34c753fSRafael Auler
358a34c753fSRafael Auler llvm::ErrorOr<uint64_t> ErrorOrValue = BC.getSymbolValue(Symbol);
359a34c753fSRafael Auler if (!ErrorOrValue)
360a34c753fSRafael Auler return HashString;
361a34c753fSRafael Auler
362a34c753fSRafael Auler // Ignore jump table references.
363a34c753fSRafael Auler if (BC.getJumpTableContainingAddress(*ErrorOrValue))
364a34c753fSRafael Auler return HashString;
365a34c753fSRafael Auler
366a34c753fSRafael Auler return HashString.append(hashInteger(*ErrorOrValue));
367a34c753fSRafael Auler }
368a34c753fSRafael Auler
hashExpr(BinaryContext & BC,const MCExpr & Expr)369a34c753fSRafael Auler std::string hashExpr(BinaryContext &BC, const MCExpr &Expr) {
370a34c753fSRafael Auler switch (Expr.getKind()) {
371a34c753fSRafael Auler case MCExpr::Constant:
372a34c753fSRafael Auler return hashInteger(cast<MCConstantExpr>(Expr).getValue());
373a34c753fSRafael Auler case MCExpr::SymbolRef:
374a34c753fSRafael Auler return hashSymbol(BC, cast<MCSymbolRefExpr>(Expr).getSymbol());
375a34c753fSRafael Auler case MCExpr::Unary: {
376a34c753fSRafael Auler const auto &UnaryExpr = cast<MCUnaryExpr>(Expr);
377a34c753fSRafael Auler return hashInteger(UnaryExpr.getOpcode())
378a34c753fSRafael Auler .append(hashExpr(BC, *UnaryExpr.getSubExpr()));
379a34c753fSRafael Auler }
380a34c753fSRafael Auler case MCExpr::Binary: {
381a34c753fSRafael Auler const auto &BinaryExpr = cast<MCBinaryExpr>(Expr);
382a34c753fSRafael Auler return hashExpr(BC, *BinaryExpr.getLHS())
383a34c753fSRafael Auler .append(hashInteger(BinaryExpr.getOpcode()))
384a34c753fSRafael Auler .append(hashExpr(BC, *BinaryExpr.getRHS()));
385a34c753fSRafael Auler }
386a34c753fSRafael Auler case MCExpr::Target:
387a34c753fSRafael Auler return std::string();
388a34c753fSRafael Auler }
389a34c753fSRafael Auler
390a34c753fSRafael Auler llvm_unreachable("invalid expression kind");
391a34c753fSRafael Auler }
392a34c753fSRafael Auler
hashInstOperand(BinaryContext & BC,const MCOperand & Operand)393a34c753fSRafael Auler std::string hashInstOperand(BinaryContext &BC, const MCOperand &Operand) {
394f92ab6afSAmir Ayupov if (Operand.isImm())
395a34c753fSRafael Auler return hashInteger(Operand.getImm());
396f92ab6afSAmir Ayupov if (Operand.isReg())
397a34c753fSRafael Auler return hashInteger(Operand.getReg());
398f92ab6afSAmir Ayupov if (Operand.isExpr())
399a34c753fSRafael Auler return hashExpr(BC, *Operand.getExpr());
400a34c753fSRafael Auler
401a34c753fSRafael Auler return std::string();
402a34c753fSRafael Auler }
403a34c753fSRafael Auler
404a34c753fSRafael Auler } // namespace
405a34c753fSRafael Auler
406a34c753fSRafael Auler namespace llvm {
407a34c753fSRafael Auler namespace bolt {
408a34c753fSRafael Auler
runOnFunctions(BinaryContext & BC)409a34c753fSRafael Auler void IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
410a34c753fSRafael Auler const size_t OriginalFunctionCount = BC.getBinaryFunctions().size();
411a34c753fSRafael Auler uint64_t NumFunctionsFolded = 0;
412a34c753fSRafael Auler std::atomic<uint64_t> NumJTFunctionsFolded{0};
413a34c753fSRafael Auler std::atomic<uint64_t> BytesSavedEstimate{0};
414a34c753fSRafael Auler std::atomic<uint64_t> CallsSavedEstimate{0};
415a34c753fSRafael Auler std::atomic<uint64_t> NumFoldedLastIteration{0};
416a34c753fSRafael Auler CongruentBucketsMap CongruentBuckets;
417a34c753fSRafael Auler
418a34c753fSRafael Auler // Hash all the functions
419a34c753fSRafael Auler auto hashFunctions = [&]() {
420a34c753fSRafael Auler NamedRegionTimer HashFunctionsTimer("hashing", "hashing", "ICF breakdown",
421a34c753fSRafael Auler "ICF breakdown", opts::TimeICF);
422a34c753fSRafael Auler ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
423a34c753fSRafael Auler // Make sure indices are in-order.
424*8477bc67SFabian Parzefall BF.getLayout().updateLayoutIndices();
425a34c753fSRafael Auler
426a34c753fSRafael Auler // Pre-compute hash before pushing into hashtable.
427a34c753fSRafael Auler // Hash instruction operands to minimize hash collisions.
42840c2e0faSMaksim Panchenko BF.computeHash(opts::UseDFS, [&BC](const MCOperand &Op) {
429a34c753fSRafael Auler return hashInstOperand(BC, Op);
430a34c753fSRafael Auler });
431a34c753fSRafael Auler };
432a34c753fSRafael Auler
433a34c753fSRafael Auler ParallelUtilities::PredicateTy SkipFunc = [&](const BinaryFunction &BF) {
434a34c753fSRafael Auler return !shouldOptimize(BF);
435a34c753fSRafael Auler };
436a34c753fSRafael Auler
437a34c753fSRafael Auler ParallelUtilities::runOnEachFunction(
438a34c753fSRafael Auler BC, ParallelUtilities::SchedulingPolicy::SP_TRIVIAL, WorkFun, SkipFunc,
439a34c753fSRafael Auler "hashFunctions", /*ForceSequential*/ false, 2);
440a34c753fSRafael Auler };
441a34c753fSRafael Auler
442a34c753fSRafael Auler // Creates buckets with congruent functions - functions that potentially
443a34c753fSRafael Auler // could be folded.
444a34c753fSRafael Auler auto createCongruentBuckets = [&]() {
445a34c753fSRafael Auler NamedRegionTimer CongruentBucketsTimer("congruent buckets",
446a34c753fSRafael Auler "congruent buckets", "ICF breakdown",
447a34c753fSRafael Auler "ICF breakdown", opts::TimeICF);
448a34c753fSRafael Auler for (auto &BFI : BC.getBinaryFunctions()) {
449a34c753fSRafael Auler BinaryFunction &BF = BFI.second;
450a34c753fSRafael Auler if (!this->shouldOptimize(BF))
451a34c753fSRafael Auler continue;
452a34c753fSRafael Auler CongruentBuckets[&BF].emplace(&BF);
453a34c753fSRafael Auler }
454a34c753fSRafael Auler };
455a34c753fSRafael Auler
456a34c753fSRafael Auler // Partition each set of congruent functions into sets of identical functions
457a34c753fSRafael Auler // and fold them
458a34c753fSRafael Auler auto performFoldingPass = [&]() {
459a34c753fSRafael Auler NamedRegionTimer FoldingPassesTimer("folding passes", "folding passes",
460a34c753fSRafael Auler "ICF breakdown", "ICF breakdown",
461a34c753fSRafael Auler opts::TimeICF);
462a34c753fSRafael Auler Timer SinglePass("single fold pass", "single fold pass");
463a34c753fSRafael Auler LLVM_DEBUG(SinglePass.startTimer());
464a34c753fSRafael Auler
465a34c753fSRafael Auler ThreadPool *ThPool;
466a34c753fSRafael Auler if (!opts::NoThreads)
467a34c753fSRafael Auler ThPool = &ParallelUtilities::getThreadPool();
468a34c753fSRafael Auler
469a34c753fSRafael Auler // Fold identical functions within a single congruent bucket
470a34c753fSRafael Auler auto processSingleBucket = [&](std::set<BinaryFunction *> &Candidates) {
471a34c753fSRafael Auler Timer T("folding single congruent list", "folding single congruent list");
472a34c753fSRafael Auler LLVM_DEBUG(T.startTimer());
473a34c753fSRafael Auler
474a34c753fSRafael Auler // Identical functions go into the same bucket.
475a34c753fSRafael Auler IdenticalBucketsMap IdenticalBuckets;
476a34c753fSRafael Auler for (BinaryFunction *BF : Candidates) {
477a34c753fSRafael Auler IdenticalBuckets[BF].emplace_back(BF);
478a34c753fSRafael Auler }
479a34c753fSRafael Auler
480a34c753fSRafael Auler for (auto &IBI : IdenticalBuckets) {
481a34c753fSRafael Auler // Functions identified as identical.
482a34c753fSRafael Auler std::vector<BinaryFunction *> &Twins = IBI.second;
483a34c753fSRafael Auler if (Twins.size() < 2)
484a34c753fSRafael Auler continue;
485a34c753fSRafael Auler
486a34c753fSRafael Auler // Fold functions. Keep the order consistent across invocations with
487a34c753fSRafael Auler // different options.
488d2c87699SAmir Ayupov llvm::stable_sort(
489d2c87699SAmir Ayupov Twins, [](const BinaryFunction *A, const BinaryFunction *B) {
490d2c87699SAmir Ayupov return A->getFunctionNumber() < B->getFunctionNumber();
491a34c753fSRafael Auler });
492a34c753fSRafael Auler
493a34c753fSRafael Auler BinaryFunction *ParentBF = Twins[0];
494a34c753fSRafael Auler for (unsigned I = 1; I < Twins.size(); ++I) {
495a34c753fSRafael Auler BinaryFunction *ChildBF = Twins[I];
496a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "BOLT-DEBUG: folding " << *ChildBF << " into "
497a34c753fSRafael Auler << *ParentBF << '\n');
498a34c753fSRafael Auler
499a34c753fSRafael Auler // Remove child function from the list of candidates.
500a34c753fSRafael Auler auto FI = Candidates.find(ChildBF);
501a34c753fSRafael Auler assert(FI != Candidates.end() &&
502a34c753fSRafael Auler "function expected to be in the set");
503a34c753fSRafael Auler Candidates.erase(FI);
504a34c753fSRafael Auler
505a34c753fSRafael Auler // Fold the function and remove from the list of processed functions.
506a34c753fSRafael Auler BytesSavedEstimate += ChildBF->getSize();
507a34c753fSRafael Auler CallsSavedEstimate += std::min(ChildBF->getKnownExecutionCount(),
508a34c753fSRafael Auler ParentBF->getKnownExecutionCount());
509a34c753fSRafael Auler BC.foldFunction(*ChildBF, *ParentBF);
510a34c753fSRafael Auler
511a34c753fSRafael Auler ++NumFoldedLastIteration;
512a34c753fSRafael Auler
513a34c753fSRafael Auler if (ParentBF->hasJumpTables())
514a34c753fSRafael Auler ++NumJTFunctionsFolded;
515a34c753fSRafael Auler }
516a34c753fSRafael Auler }
517a34c753fSRafael Auler
518a34c753fSRafael Auler LLVM_DEBUG(T.stopTimer());
519a34c753fSRafael Auler };
520a34c753fSRafael Auler
521a34c753fSRafael Auler // Create a task for each congruent bucket
522a34c753fSRafael Auler for (auto &Entry : CongruentBuckets) {
523a34c753fSRafael Auler std::set<BinaryFunction *> &Bucket = Entry.second;
524a34c753fSRafael Auler if (Bucket.size() < 2)
525a34c753fSRafael Auler continue;
526a34c753fSRafael Auler
527a34c753fSRafael Auler if (opts::NoThreads)
528a34c753fSRafael Auler processSingleBucket(Bucket);
529a34c753fSRafael Auler else
530a34c753fSRafael Auler ThPool->async(processSingleBucket, std::ref(Bucket));
531a34c753fSRafael Auler }
532a34c753fSRafael Auler
533a34c753fSRafael Auler if (!opts::NoThreads)
534a34c753fSRafael Auler ThPool->wait();
535a34c753fSRafael Auler
536a34c753fSRafael Auler LLVM_DEBUG(SinglePass.stopTimer());
537a34c753fSRafael Auler };
538a34c753fSRafael Auler
539a34c753fSRafael Auler hashFunctions();
540a34c753fSRafael Auler createCongruentBuckets();
541a34c753fSRafael Auler
542a34c753fSRafael Auler unsigned Iteration = 1;
543a34c753fSRafael Auler // We repeat the pass until no new modifications happen.
544a34c753fSRafael Auler do {
545a34c753fSRafael Auler NumFoldedLastIteration = 0;
546a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "BOLT-DEBUG: ICF iteration " << Iteration << "...\n");
547a34c753fSRafael Auler
548a34c753fSRafael Auler performFoldingPass();
549a34c753fSRafael Auler
550a34c753fSRafael Auler NumFunctionsFolded += NumFoldedLastIteration;
551a34c753fSRafael Auler ++Iteration;
552a34c753fSRafael Auler
553a34c753fSRafael Auler } while (NumFoldedLastIteration > 0);
554a34c753fSRafael Auler
555a34c753fSRafael Auler LLVM_DEBUG({
556a34c753fSRafael Auler // Print functions that are congruent but not identical.
557a34c753fSRafael Auler for (auto &CBI : CongruentBuckets) {
558a34c753fSRafael Auler std::set<BinaryFunction *> &Candidates = CBI.second;
559a34c753fSRafael Auler if (Candidates.size() < 2)
560a34c753fSRafael Auler continue;
561a34c753fSRafael Auler dbgs() << "BOLT-DEBUG: the following " << Candidates.size()
562a34c753fSRafael Auler << " functions (each of size " << (*Candidates.begin())->getSize()
563a34c753fSRafael Auler << " bytes) are congruent but not identical:\n";
564a34c753fSRafael Auler for (BinaryFunction *BF : Candidates) {
565a34c753fSRafael Auler dbgs() << " " << *BF;
566f92ab6afSAmir Ayupov if (BF->getKnownExecutionCount())
567a34c753fSRafael Auler dbgs() << " (executed " << BF->getKnownExecutionCount() << " times)";
568a34c753fSRafael Auler dbgs() << '\n';
569a34c753fSRafael Auler }
570a34c753fSRafael Auler }
571a34c753fSRafael Auler });
572a34c753fSRafael Auler
573f92ab6afSAmir Ayupov if (NumFunctionsFolded)
57440c2e0faSMaksim Panchenko outs() << "BOLT-INFO: ICF folded " << NumFunctionsFolded << " out of "
57540c2e0faSMaksim Panchenko << OriginalFunctionCount << " functions in " << Iteration
57640c2e0faSMaksim Panchenko << " passes. " << NumJTFunctionsFolded
57740c2e0faSMaksim Panchenko << " functions had jump tables.\n"
578a34c753fSRafael Auler << "BOLT-INFO: Removing all identical functions will save "
579a34c753fSRafael Auler << format("%.2lf", (double)BytesSavedEstimate / 1024)
580a34c753fSRafael Auler << " KB of code space. Folded functions were called "
581a34c753fSRafael Auler << CallsSavedEstimate << " times based on profile.\n";
582a34c753fSRafael Auler }
583a34c753fSRafael Auler
584a34c753fSRafael Auler } // namespace bolt
585a34c753fSRafael Auler } // namespace llvm
586