Skip to content

Commit 4908531

Browse files
committed
Merge from 'main' to 'sycl-web' (125 commits)
CONFLICT (content): Merge conflict in clang/lib/CodeGen/BackendUtil.cpp CONFLICT (content): Merge conflict in clang/lib/CodeGen/CodeGenAction.cpp
2 parents 642a419 + 8a4b903 commit 4908531

File tree

469 files changed

+16249
-6339
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

469 files changed

+16249
-6339
lines changed

.github/workflows/libcxx-build-and-test.yaml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ on:
2222
- 'runtimes/**'
2323
- 'cmake/**'
2424
- '.github/workflows/libcxx-build-and-test.yaml'
25+
schedule:
26+
# Run nightly at 8 AM UTC (or roughly 3 AM eastern)
27+
- cron: '0 3 * * *'
28+
29+
permissions:
30+
contents: read # Default everything to read-only
2531

2632
concurrency:
2733
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
@@ -157,7 +163,11 @@ jobs:
157163
'generic-no-unicode',
158164
'generic-no-wide-characters',
159165
'generic-static',
160-
'generic-with_llvm_unwinder'
166+
'generic-with_llvm_unwinder',
167+
# TODO Find a better place for the benchmark and bootstrapping builds to live. They're either very expensive
168+
# or don't provide much value since the benchmark run results are too noise on the bots.
169+
'benchmarks',
170+
'bootstrapping-build'
161171
]
162172
machine: [ 'libcxx-runners-8' ]
163173
std_modules: [ 'OFF' ]

.github/workflows/sync-release-repo.yml

Lines changed: 0 additions & 17 deletions
This file was deleted.

bolt/include/bolt/Core/BinaryContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,9 @@ class BinaryContext {
611611
/// Indicates if the binary contains split functions.
612612
bool HasSplitFunctions{false};
613613

614+
/// Indicates if the function ordering of the binary is finalized.
615+
bool HasFinalizedFunctionOrder{false};
616+
614617
/// Is the binary always loaded at a fixed address. Shared objects and
615618
/// position-independent executables (PIEs) are examples of binaries that
616619
/// will have HasFixedLoadAddress set to false.

bolt/include/bolt/Passes/SplitFunctions.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ enum SplitFunctionsStrategy : char {
2323
/// Split each function into a hot and cold fragment using profiling
2424
/// information.
2525
Profile2 = 0,
26+
/// Split each function into a hot, warm, and cold fragment using
27+
/// profiling information.
28+
CDSplit,
2629
/// Split each function into a hot and cold fragment at a randomly chosen
2730
/// split point (ignoring any available profiling information).
2831
Random2,
@@ -40,7 +43,7 @@ class SplitStrategy {
4043

4144
virtual ~SplitStrategy() = default;
4245
virtual bool canSplit(const BinaryFunction &BF) = 0;
43-
virtual bool keepEmpty() = 0;
46+
virtual bool compactFragments() = 0;
4447
virtual void fragment(const BlockIt Start, const BlockIt End) = 0;
4548
};
4649

bolt/lib/Core/BinaryFunction.cpp

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3296,7 +3296,7 @@ void BinaryFunction::fixBranches() {
32963296
BB->eraseInstruction(BB->findInstruction(UncondBranch));
32973297

32983298
// Basic block that follows the current one in the final layout.
3299-
const BinaryBasicBlock *NextBB =
3299+
const BinaryBasicBlock *const NextBB =
33003300
Layout.getBasicBlockAfter(BB, /*IgnoreSplits=*/false);
33013301

33023302
if (BB->succ_size() == 1) {
@@ -3313,39 +3313,54 @@ void BinaryFunction::fixBranches() {
33133313
assert(CondBranch && "conditional branch expected");
33143314
const BinaryBasicBlock *TSuccessor = BB->getConditionalSuccessor(true);
33153315
const BinaryBasicBlock *FSuccessor = BB->getConditionalSuccessor(false);
3316-
// Check whether we support reversing this branch direction
3317-
const bool IsSupported = !MIB->isUnsupportedBranch(*CondBranch);
3318-
if (NextBB && NextBB == TSuccessor && IsSupported) {
3316+
3317+
// Eliminate unnecessary conditional branch.
3318+
if (TSuccessor == FSuccessor) {
3319+
BB->removeDuplicateConditionalSuccessor(CondBranch);
3320+
if (TSuccessor != NextBB)
3321+
BB->addBranchInstruction(TSuccessor);
3322+
continue;
3323+
}
3324+
3325+
// Reverse branch condition and swap successors.
3326+
auto swapSuccessors = [&]() {
3327+
if (MIB->isUnsupportedBranch(*CondBranch))
3328+
return false;
33193329
std::swap(TSuccessor, FSuccessor);
3320-
{
3321-
auto L = BC.scopeLock();
3322-
MIB->reverseBranchCondition(*CondBranch, TSuccessor->getLabel(), Ctx);
3323-
}
33243330
BB->swapConditionalSuccessors();
3325-
} else {
3331+
auto L = BC.scopeLock();
3332+
MIB->reverseBranchCondition(*CondBranch, TSuccessor->getLabel(), Ctx);
3333+
return true;
3334+
};
3335+
3336+
// Check whether the next block is a "taken" target and try to swap it
3337+
// with a "fall-through" target.
3338+
if (TSuccessor == NextBB && swapSuccessors())
3339+
continue;
3340+
3341+
// Update conditional branch destination if needed.
3342+
if (MIB->getTargetSymbol(*CondBranch) != TSuccessor->getLabel()) {
33263343
auto L = BC.scopeLock();
33273344
MIB->replaceBranchTarget(*CondBranch, TSuccessor->getLabel(), Ctx);
33283345
}
3329-
if (TSuccessor == FSuccessor)
3330-
BB->removeDuplicateConditionalSuccessor(CondBranch);
3331-
if (!NextBB ||
3332-
((NextBB != TSuccessor || !IsSupported) && NextBB != FSuccessor)) {
3333-
// If one of the branches is guaranteed to be "long" while the other
3334-
// could be "short", then prioritize short for "taken". This will
3335-
// generate a sequence 1 byte shorter on x86.
3336-
if (IsSupported && BC.isX86() &&
3337-
TSuccessor->getFragmentNum() != FSuccessor->getFragmentNum() &&
3338-
BB->getFragmentNum() != TSuccessor->getFragmentNum()) {
3339-
std::swap(TSuccessor, FSuccessor);
3340-
{
3341-
auto L = BC.scopeLock();
3342-
MIB->reverseBranchCondition(*CondBranch, TSuccessor->getLabel(),
3343-
Ctx);
3344-
}
3345-
BB->swapConditionalSuccessors();
3346-
}
3347-
BB->addBranchInstruction(FSuccessor);
3346+
3347+
// No need for the unconditional branch.
3348+
if (FSuccessor == NextBB)
3349+
continue;
3350+
3351+
if (BC.isX86()) {
3352+
// We are going to generate two branches. Check if their targets are in
3353+
// the same fragment as this block. If only one target is in the same
3354+
// fragment, make it the destination of the conditional branch. There
3355+
// is a chance it will be a short branch which takes 5 bytes fewer than
3356+
// a long conditional branch. For unconditional branch, the difference
3357+
// is 4 bytes.
3358+
if (BB->getFragmentNum() != TSuccessor->getFragmentNum() &&
3359+
BB->getFragmentNum() == FSuccessor->getFragmentNum())
3360+
swapSuccessors();
33483361
}
3362+
3363+
BB->addBranchInstruction(FSuccessor);
33493364
}
33503365
// Cases where the number of successors is 0 (block ends with a
33513366
// terminator) or more than 2 (switch table) don't require branch

bolt/lib/Passes/ReorderFunctions.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,8 @@ void ReorderFunctions::runOnFunctions(BinaryContext &BC) {
427427

428428
reorder(std::move(Clusters), BFs);
429429

430+
BC.HasFinalizedFunctionOrder = true;
431+
430432
std::unique_ptr<std::ofstream> FuncsFile;
431433
if (!opts::GenerateFunctionOrderFile.empty()) {
432434
FuncsFile = std::make_unique<std::ofstream>(opts::GenerateFunctionOrderFile,

bolt/lib/Passes/SplitFunctions.cpp

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ static cl::opt<SplitFunctionsStrategy> SplitStrategy(
9292
cl::values(clEnumValN(SplitFunctionsStrategy::Profile2, "profile2",
9393
"split each function into a hot and cold fragment "
9494
"using profiling information")),
95+
cl::values(clEnumValN(SplitFunctionsStrategy::CDSplit, "cdsplit",
96+
"split each function into a hot, warm, and cold "
97+
"fragment using profiling information")),
9598
cl::values(clEnumValN(
9699
SplitFunctionsStrategy::Random2, "random2",
97100
"split each function into a hot and cold fragment at a randomly chosen "
@@ -126,7 +129,7 @@ struct SplitProfile2 final : public SplitStrategy {
126129
return BF.hasValidProfile() && hasFullProfile(BF) && !allBlocksCold(BF);
127130
}
128131

129-
bool keepEmpty() override { return false; }
132+
bool compactFragments() override { return true; }
130133

131134
void fragment(const BlockIt Start, const BlockIt End) override {
132135
for (BinaryBasicBlock *const BB : llvm::make_range(Start, End)) {
@@ -136,14 +139,59 @@ struct SplitProfile2 final : public SplitStrategy {
136139
}
137140
};
138141

142+
struct SplitCacheDirected final : public SplitStrategy {
143+
using BasicBlockOrder = BinaryFunction::BasicBlockOrderType;
144+
145+
bool canSplit(const BinaryFunction &BF) override {
146+
return BF.hasValidProfile() && hasFullProfile(BF) && !allBlocksCold(BF);
147+
}
148+
149+
// When some functions are hot-warm split and others are hot-warm-cold split,
150+
// we do not want to change the fragment numbers of the blocks in the hot-warm
151+
// split functions.
152+
bool compactFragments() override { return false; }
153+
154+
void fragment(const BlockIt Start, const BlockIt End) override {
155+
BasicBlockOrder BlockOrder(Start, End);
156+
BinaryFunction &BF = *BlockOrder.front()->getFunction();
157+
158+
size_t BestSplitIndex = findSplitIndex(BF, BlockOrder);
159+
160+
// Assign fragments based on the computed best split index.
161+
// All basic blocks with index up to the best split index become hot.
162+
// All remaining blocks are warm / cold depending on if count is
163+
// greater than 0 or not.
164+
FragmentNum Main(0);
165+
FragmentNum Cold(1);
166+
FragmentNum Warm(2);
167+
for (size_t Index = 0; Index < BlockOrder.size(); Index++) {
168+
BinaryBasicBlock *BB = BlockOrder[Index];
169+
if (Index <= BestSplitIndex)
170+
BB->setFragmentNum(Main);
171+
else
172+
BB->setFragmentNum(BB->getKnownExecutionCount() > 0 ? Warm : Cold);
173+
}
174+
}
175+
176+
private:
177+
/// Find the best index for splitting. The returned value is the index of the
178+
/// last hot basic block. Hence, "no splitting" is equivalent to returning the
179+
/// value which is one less than the size of the function.
180+
size_t findSplitIndex(const BinaryFunction &BF,
181+
const BasicBlockOrder &BlockOrder) {
182+
// Placeholder: hot-warm split after entry block.
183+
return 0;
184+
}
185+
};
186+
139187
struct SplitRandom2 final : public SplitStrategy {
140188
std::minstd_rand0 Gen;
141189

142190
SplitRandom2() : Gen(opts::RandomSeed.getValue()) {}
143191

144192
bool canSplit(const BinaryFunction &BF) override { return true; }
145193

146-
bool keepEmpty() override { return false; }
194+
bool compactFragments() override { return true; }
147195

148196
void fragment(const BlockIt Start, const BlockIt End) override {
149197
using DiffT = typename std::iterator_traits<BlockIt>::difference_type;
@@ -170,7 +218,7 @@ struct SplitRandomN final : public SplitStrategy {
170218

171219
bool canSplit(const BinaryFunction &BF) override { return true; }
172220

173-
bool keepEmpty() override { return false; }
221+
bool compactFragments() override { return true; }
174222

175223
void fragment(const BlockIt Start, const BlockIt End) override {
176224
using DiffT = typename std::iterator_traits<BlockIt>::difference_type;
@@ -217,10 +265,10 @@ struct SplitRandomN final : public SplitStrategy {
217265
struct SplitAll final : public SplitStrategy {
218266
bool canSplit(const BinaryFunction &BF) override { return true; }
219267

220-
bool keepEmpty() override {
268+
bool compactFragments() override {
221269
// Keeping empty fragments allows us to test, that empty fragments do not
222270
// generate symbols.
223-
return true;
271+
return false;
224272
}
225273

226274
void fragment(const BlockIt Start, const BlockIt End) override {
@@ -246,10 +294,26 @@ void SplitFunctions::runOnFunctions(BinaryContext &BC) {
246294
if (!opts::SplitFunctions)
247295
return;
248296

297+
// If split strategy is not CDSplit, then a second run of the pass is not
298+
// needed after function reordering.
299+
if (BC.HasFinalizedFunctionOrder &&
300+
opts::SplitStrategy != SplitFunctionsStrategy::CDSplit)
301+
return;
302+
249303
std::unique_ptr<SplitStrategy> Strategy;
250304
bool ForceSequential = false;
251305

252306
switch (opts::SplitStrategy) {
307+
case SplitFunctionsStrategy::CDSplit:
308+
// CDSplit runs two splitting passes: hot-cold splitting (SplitPrfoile2)
309+
// before function reordering and hot-warm-cold splitting
310+
// (SplitCacheDirected) after function reordering.
311+
if (BC.HasFinalizedFunctionOrder)
312+
Strategy = std::make_unique<SplitCacheDirected>();
313+
else
314+
Strategy = std::make_unique<SplitProfile2>();
315+
opts::AggressiveSplitting = true;
316+
break;
253317
case SplitFunctionsStrategy::Profile2:
254318
Strategy = std::make_unique<SplitProfile2>();
255319
break;
@@ -382,7 +446,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
382446
CurrentFragment = BB->getFragmentNum();
383447
}
384448

385-
if (!S.keepEmpty()) {
449+
if (S.compactFragments()) {
386450
FragmentNum CurrentFragment = FragmentNum::main();
387451
FragmentNum NewFragment = FragmentNum::main();
388452
for (BinaryBasicBlock *const BB : NewLayout) {
@@ -394,7 +458,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
394458
}
395459
}
396460

397-
BF.getLayout().update(NewLayout);
461+
const bool LayoutUpdated = BF.getLayout().update(NewLayout);
398462

399463
// For shared objects, invoke instructions and corresponding landing pads
400464
// have to be placed in the same fragment. When we split them, create
@@ -404,7 +468,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
404468
Trampolines = createEHTrampolines(BF);
405469

406470
// Check the new size to see if it's worth splitting the function.
407-
if (BC.isX86() && BF.isSplit()) {
471+
if (BC.isX86() && LayoutUpdated) {
408472
std::tie(HotSize, ColdSize) = BC.calculateEmittedSize(BF);
409473
LLVM_DEBUG(dbgs() << "Estimated size for function " << BF
410474
<< " post-split is <0x" << Twine::utohexstr(HotSize)
@@ -431,6 +495,11 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
431495
SplitBytesCold += ColdSize;
432496
}
433497
}
498+
499+
// Fix branches if the splitting decision of the pass after function
500+
// reordering is different from that of the pass before function reordering.
501+
if (LayoutUpdated && BC.HasFinalizedFunctionOrder)
502+
BF.fixBranches();
434503
}
435504

436505
SplitFunctions::TrampolineSetType

bolt/lib/Rewrite/BinaryPassManager.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,13 @@ void BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
430430
Manager.registerPass(
431431
std::make_unique<ReorderFunctions>(PrintReorderedFunctions));
432432

433+
// This is the second run of the SplitFunctions pass required by certain
434+
// splitting strategies (e.g. cdsplit). Running the SplitFunctions pass again
435+
// after ReorderFunctions allows the finalized function order to be utilized
436+
// to make more sophisticated splitting decisions, like hot-warm-cold
437+
// splitting.
438+
Manager.registerPass(std::make_unique<SplitFunctions>(PrintSplit));
439+
433440
// Print final dyno stats right while CFG and instruction analysis are intact.
434441
Manager.registerPass(
435442
std::make_unique<DynoStatsPrintPass>(

clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage-strict.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %check_clang_tidy %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: {bugprone-suspicious-enum-usage.StrictMode: true}}" --
1+
// RUN: %check_clang_tidy -std=c++17 %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: {bugprone-suspicious-enum-usage.StrictMode: true}}" --
22

33
enum A {
44
A = 1,
@@ -71,7 +71,7 @@ int trigger() {
7171
unsigned p = R;
7272
PP pp = Q;
7373
p |= pp;
74-
74+
7575
enum X x = Z;
7676
p = x | Z;
7777
return 0;

clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %check_clang_tidy %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: {bugprone-suspicious-enum-usage.StrictMode: false}}" --
1+
// RUN: %check_clang_tidy -std=c++17 %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: {bugprone-suspicious-enum-usage.StrictMode: false}}"
22

33
enum Empty {
44
};
@@ -79,7 +79,7 @@ int dont_trigger() {
7979
int d = c | H, e = b * a;
8080
a = B | C;
8181
b = X | Z;
82-
82+
8383
if (Tuesday != Monday + 1 ||
8484
Friday - Thursday != 1 ||
8585
Sunday + Wednesday == (Sunday | Wednesday))

0 commit comments

Comments
 (0)