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