Skip to content

Commit 47ebe5e

Browse files
committed
Merge branch 'main' into initializer-list-early-layout-check
2 parents 326ceb6 + f450408 commit 47ebe5e

File tree

1,541 files changed

+62353
-26725
lines changed

Some content is hidden

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

1,541 files changed

+62353
-26725
lines changed

.git-blame-ignore-revs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,6 @@ f6d557ee34b6bbdb1dc32f29e34b4a4a8ad35e81
8181

8282
# [NFC] clang-format utils/TableGen (#80973)
8383
b9079baaddfed5e604fbfaa1d81a7a1c38e78c26
84+
85+
# [libc++][NFC] Run clang-format on libcxx/include again (#95874)
86+
e2c2ffbe7a1b5d9e32a2ce64279475b50c4cba5b

.github/workflows/pr-code-format.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
- name: Install clang-format
5656
uses: aminya/setup-cpp@v1
5757
with:
58-
clangformat: 18.1.1
58+
clangformat: 18.1.7
5959

6060
- name: Setup Python env
6161
uses: actions/setup-python@v5

.github/workflows/release-sources.yml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
name: Release Sources
2+
3+
permissions:
4+
contents: read
5+
6+
on:
7+
workflow_dispatch:
8+
inputs:
9+
release-version:
10+
description: Release Version
11+
required: true
12+
type: string
13+
workflow_call:
14+
inputs:
15+
release-version:
16+
description: Release Version
17+
required: true
18+
type: string
19+
# Run on pull_requests for testing purposes.
20+
pull_request:
21+
paths:
22+
- '.github/workflows/release-sources.yml'
23+
types:
24+
- opened
25+
- synchronize
26+
- reopened
27+
# When a PR is closed, we still start this workflow, but then skip
28+
# all the jobs, which makes it effectively a no-op. The reason to
29+
# do this is that it allows us to take advantage of concurrency groups
30+
# to cancel in progress CI jobs whenever the PR is closed.
31+
- closed
32+
33+
concurrency:
34+
group: ${{ github.workflow }}-${{ inputs.release-version || github.event.pull_request.number }}
35+
cancel-in-progress: True
36+
37+
jobs:
38+
inputs:
39+
name: Collect Job Inputs
40+
if: >-
41+
github.repository_owner == 'llvm' &&
42+
github.event.action != 'closed'
43+
outputs:
44+
ref: ${{ steps.inputs.outputs.ref }}
45+
export-args: ${{ steps.inputs.outputs.export-args }}
46+
runs-on: ubuntu-latest
47+
steps:
48+
- id: inputs
49+
run: |
50+
ref=${{ inputs.release-version || github.sha }}
51+
if [ -n "${{ inputs.release-version }}" ]; then
52+
export_args="-release ${{ inputs.release-version }} -final"
53+
else
54+
export_args="-git-ref ${{ github.sha }}"
55+
fi
56+
echo "ref=$ref" >> $GITHUB_OUTPUT
57+
echo "export-args=$export_args" >> $GITHUB_OUTPUT
58+
59+
release-sources:
60+
name: Package Release Sources
61+
if: github.repository_owner == 'llvm'
62+
runs-on: ubuntu-latest
63+
needs:
64+
- inputs
65+
permissions:
66+
id-token: write
67+
attestations: write
68+
steps:
69+
- name: Checkout LLVM
70+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
71+
with:
72+
ref: ${{ needs.inputs.outputs.ref }}
73+
fetch-tags: true
74+
- name: Install Dependencies
75+
run: |
76+
pip install --require-hashes -r ./llvm/utils/git/requirements.txt
77+
78+
- name: Check Permissions
79+
if: github.event_name != 'pull_request'
80+
env:
81+
GITHUB_TOKEN: ${{ github.token }}
82+
USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }}
83+
run: |
84+
./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --user ${{ github.actor }} --user-token "$USER_TOKEN" check-permissions
85+
- name: Create Tarballs
86+
run: |
87+
./llvm/utils/release/export.sh ${{ needs.inputs.outputs.export-args }}
88+
- name: Attest Build Provenance
89+
if: github.event_name != 'pull_request'
90+
id: provenance
91+
uses: actions/attest-build-provenance@897ed5eab6ed058a474202017ada7f40bfa52940 # v1.0.0
92+
with:
93+
subject-path: "*.xz"
94+
- if: github.event_name != 'pull_request'
95+
run: |
96+
mv ${{ steps.provenance.outputs.bundle-path }} .
97+
- name: Create Tarball Artifacts
98+
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 #v4.3.3
99+
with:
100+
path: |
101+
*.xz
102+
attestation.jsonl
103+
104+

.github/workflows/release-tasks.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,14 @@ jobs:
8585
with:
8686
release-version: ${{ needs.validate-tag.outputs.release-version }}
8787
upload: true
88+
89+
release-sources:
90+
name: Package Release Sources
91+
permissions:
92+
id-token: write
93+
attestations: write
94+
needs:
95+
- validate-tag
96+
uses: ./.github/workflows/release-sources.yml
97+
with:
98+
release-version: ${{ needs.validate-tag.outputs.release-version }}

bolt/docs/CommandLineArgumentReference.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,11 @@
802802

803803
The maximum size of a function to consider for inference.
804804

805+
- `--stale-matching-min-matched-block=<uint>`
806+
807+
Minimum percent of exact match block for a function to be considered for
808+
profile inference.
809+
805810
- `--stale-threshold=<uint>`
806811

807812
Maximum percentage of stale functions to tolerate (default: 100)
@@ -1161,4 +1166,4 @@
11611166

11621167
- `--print-options`
11631168

1164-
Print non-default options after command line parsing
1169+
Print non-default options after command line parsing

bolt/lib/Profile/StaleProfileMatching.cpp

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ cl::opt<bool>
5151
cl::desc("Infer counts from stale profile data."),
5252
cl::init(false), cl::Hidden, cl::cat(BoltOptCategory));
5353

54+
cl::opt<unsigned> StaleMatchingMinMatchedBlock(
55+
"stale-matching-min-matched-block",
56+
cl::desc("Percentage threshold of matched basic blocks at which stale "
57+
"profile inference is executed."),
58+
cl::init(0), cl::Hidden, cl::cat(BoltOptCategory));
59+
5460
cl::opt<unsigned> StaleMatchingMaxFuncSize(
5561
"stale-matching-max-func-size",
5662
cl::desc("The maximum size of a function to consider for inference."),
@@ -301,21 +307,21 @@ void BinaryFunction::computeBlockHashes(HashFunction HashFunction) const {
301307
BB->setHash(BlendedHashes[I].combine());
302308
}
303309
}
304-
310+
// TODO: mediate the difference between flow function construction here in BOLT
311+
// and in the compiler by splitting blocks with exception throwing calls at the
312+
// call and adding the landing pad as the successor.
305313
/// Create a wrapper flow function to use with the profile inference algorithm,
306314
/// and initialize its jumps and metadata.
307315
FlowFunction
308316
createFlowFunction(const BinaryFunction::BasicBlockOrderType &BlockOrder) {
309317
FlowFunction Func;
310318

311319
// Add a special "dummy" source so that there is always a unique entry point.
312-
// Because of the extra source, for all other blocks in FlowFunction it holds
313-
// that Block.Index == BB->getIndex() + 1
314320
FlowBlock EntryBlock;
315321
EntryBlock.Index = 0;
316322
Func.Blocks.push_back(EntryBlock);
317323

318-
// Create FlowBlock for every basic block in the binary function
324+
// Create FlowBlock for every basic block in the binary function.
319325
for (const BinaryBasicBlock *BB : BlockOrder) {
320326
Func.Blocks.emplace_back();
321327
FlowBlock &Block = Func.Blocks.back();
@@ -325,7 +331,12 @@ createFlowFunction(const BinaryFunction::BasicBlockOrderType &BlockOrder) {
325331
"incorrectly assigned basic block index");
326332
}
327333

328-
// Create FlowJump for each jump between basic blocks in the binary function
334+
// Add a special "dummy" sink block so there is always a unique sink.
335+
FlowBlock SinkBlock;
336+
SinkBlock.Index = Func.Blocks.size();
337+
Func.Blocks.push_back(SinkBlock);
338+
339+
// Create FlowJump for each jump between basic blocks in the binary function.
329340
std::vector<uint64_t> InDegree(Func.Blocks.size(), 0);
330341
for (const BinaryBasicBlock *SrcBB : BlockOrder) {
331342
std::unordered_set<const BinaryBasicBlock *> UniqueSuccs;
@@ -342,6 +353,16 @@ createFlowFunction(const BinaryFunction::BasicBlockOrderType &BlockOrder) {
342353
InDegree[Jump.Target]++;
343354
UniqueSuccs.insert(DstBB);
344355
}
356+
// TODO: set jump from exit block to landing pad to Unlikely.
357+
// If the block is an exit, add a dummy edge from it to the sink block.
358+
if (UniqueSuccs.empty()) {
359+
Func.Jumps.emplace_back();
360+
FlowJump &Jump = Func.Jumps.back();
361+
Jump.Source = SrcBB->getIndex() + 1;
362+
Jump.Target = Func.Blocks.size() - 1;
363+
InDegree[Jump.Target]++;
364+
}
365+
345366
// Collect jumps to landing pads
346367
for (const BinaryBasicBlock *DstBB : SrcBB->landing_pads()) {
347368
// Ignoring parallel edges
@@ -358,9 +379,9 @@ createFlowFunction(const BinaryFunction::BasicBlockOrderType &BlockOrder) {
358379
}
359380

360381
// Add dummy edges to the extra sources. If there are multiple entry blocks,
361-
// add an unlikely edge from 0 to the subsequent ones
382+
// add an unlikely edge from 0 to the subsequent ones. Skips the sink block.
362383
assert(InDegree[0] == 0 && "dummy entry blocks shouldn't have predecessors");
363-
for (uint64_t I = 1; I < Func.Blocks.size(); I++) {
384+
for (uint64_t I = 1; I < Func.Blocks.size() - 1; I++) {
364385
const BinaryBasicBlock *BB = BlockOrder[I - 1];
365386
if (BB->isEntryPoint() || InDegree[I] == 0) {
366387
Func.Jumps.emplace_back();
@@ -391,11 +412,10 @@ createFlowFunction(const BinaryFunction::BasicBlockOrderType &BlockOrder) {
391412
/// of the basic blocks in the binary, the count is "matched" to the block.
392413
/// Similarly, if both the source and the target of a count in the profile are
393414
/// matched to a jump in the binary, the count is recorded in CFG.
394-
void matchWeightsByHashes(BinaryContext &BC,
395-
const BinaryFunction::BasicBlockOrderType &BlockOrder,
396-
const yaml::bolt::BinaryFunctionProfile &YamlBF,
397-
FlowFunction &Func) {
398-
assert(Func.Blocks.size() == BlockOrder.size() + 1);
415+
size_t matchWeightsByHashes(
416+
BinaryContext &BC, const BinaryFunction::BasicBlockOrderType &BlockOrder,
417+
const yaml::bolt::BinaryFunctionProfile &YamlBF, FlowFunction &Func) {
418+
assert(Func.Blocks.size() == BlockOrder.size() + 2);
399419

400420
std::vector<FlowBlock *> Blocks;
401421
std::vector<BlendedBlockHash> BlendedHashes;
@@ -500,6 +520,8 @@ void matchWeightsByHashes(BinaryContext &BC,
500520
Block.HasUnknownWeight = false;
501521
Block.Weight = std::max(OutWeight[Block.Index], InWeight[Block.Index]);
502522
}
523+
524+
return MatchedBlocks.size();
503525
}
504526

505527
/// The function finds all blocks that are (i) reachable from the Entry block
@@ -575,13 +597,19 @@ void preprocessUnreachableBlocks(FlowFunction &Func) {
575597
/// Decide if stale profile matching can be applied for a given function.
576598
/// Currently we skip inference for (very) large instances and for instances
577599
/// having "unexpected" control flow (e.g., having no sink basic blocks).
578-
bool canApplyInference(const FlowFunction &Func) {
600+
bool canApplyInference(const FlowFunction &Func,
601+
const yaml::bolt::BinaryFunctionProfile &YamlBF,
602+
const uint64_t &MatchedBlocks) {
579603
if (Func.Blocks.size() > opts::StaleMatchingMaxFuncSize)
580604
return false;
581605

582-
bool HasExitBlocks = llvm::any_of(
583-
Func.Blocks, [&](const FlowBlock &Block) { return Block.isExit(); });
584-
if (!HasExitBlocks)
606+
if (MatchedBlocks * 100 <
607+
opts::StaleMatchingMinMatchedBlock * YamlBF.Blocks.size())
608+
return false;
609+
610+
// Returns false if the artificial sink block has no predecessors meaning
611+
// there are no exit blocks.
612+
if (Func.Blocks[Func.Blocks.size() - 1].isEntry())
585613
return false;
586614

587615
return true;
@@ -618,7 +646,7 @@ void assignProfile(BinaryFunction &BF,
618646
FlowFunction &Func) {
619647
BinaryContext &BC = BF.getBinaryContext();
620648

621-
assert(Func.Blocks.size() == BlockOrder.size() + 1);
649+
assert(Func.Blocks.size() == BlockOrder.size() + 2);
622650
for (uint64_t I = 0; I < BlockOrder.size(); I++) {
623651
FlowBlock &Block = Func.Blocks[I + 1];
624652
BinaryBasicBlock *BB = BlockOrder[I];
@@ -640,6 +668,9 @@ void assignProfile(BinaryFunction &BF,
640668
if (Jump->Flow == 0)
641669
continue;
642670

671+
// Skips the artificial sink block.
672+
if (Jump->Target == Func.Blocks.size() - 1)
673+
continue;
643674
BinaryBasicBlock &SuccBB = *BlockOrder[Jump->Target - 1];
644675
// Check if the edge corresponds to a regular jump or a landing pad
645676
if (BB->getSuccessor(SuccBB.getLabel())) {
@@ -725,18 +756,21 @@ bool YAMLProfileReader::inferStaleProfile(
725756
const BinaryFunction::BasicBlockOrderType BlockOrder(
726757
BF.getLayout().block_begin(), BF.getLayout().block_end());
727758

759+
// Tracks the number of matched blocks.
760+
728761
// Create a wrapper flow function to use with the profile inference algorithm.
729762
FlowFunction Func = createFlowFunction(BlockOrder);
730763

731764
// Match as many block/jump counts from the stale profile as possible
732-
matchWeightsByHashes(BF.getBinaryContext(), BlockOrder, YamlBF, Func);
765+
size_t MatchedBlocks =
766+
matchWeightsByHashes(BF.getBinaryContext(), BlockOrder, YamlBF, Func);
733767

734768
// Adjust the flow function by marking unreachable blocks Unlikely so that
735769
// they don't get any counts assigned.
736770
preprocessUnreachableBlocks(Func);
737771

738772
// Check if profile inference can be applied for the instance.
739-
if (!canApplyInference(Func))
773+
if (!canApplyInference(Func, YamlBF, MatchedBlocks))
740774
return false;
741775

742776
// Apply the profile inference algorithm.

bolt/lib/Rewrite/LinuxKernelRewriter.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
273273

274274
/// Handle alternative instruction info from .altinstructions.
275275
Error readAltInstructions();
276+
void processAltInstructionsPostCFG();
276277
Error tryReadAltInstructions(uint32_t AltInstFeatureSize,
277278
bool AltInstHasPadLen, bool ParseOnly);
278-
Error rewriteAltInstructions();
279279

280280
/// Read .pci_fixup
281281
Error readPCIFixupTable();
@@ -326,6 +326,8 @@ class LinuxKernelRewriter final : public MetadataRewriter {
326326
if (Error E = processORCPostCFG())
327327
return E;
328328

329+
processAltInstructionsPostCFG();
330+
329331
return Error::success();
330332
}
331333

@@ -335,9 +337,6 @@ class LinuxKernelRewriter final : public MetadataRewriter {
335337
if (Error E = rewriteExceptionTable())
336338
return E;
337339

338-
if (Error E = rewriteAltInstructions())
339-
return E;
340-
341340
if (Error E = rewriteParaInstructions())
342341
return E;
343342

@@ -1486,12 +1485,11 @@ Error LinuxKernelRewriter::tryReadAltInstructions(uint32_t AltInstFeatureSize,
14861485
return Error::success();
14871486
}
14881487

1489-
Error LinuxKernelRewriter::rewriteAltInstructions() {
1490-
// Disable output of functions with alt instructions before the rewrite
1491-
// support is complete.
1488+
void LinuxKernelRewriter::processAltInstructionsPostCFG() {
1489+
// Disable optimization and output of functions with alt instructions before
1490+
// the rewrite support is complete. Alt instructions can modify the control
1491+
// flow, hence we may end up deleting seemingly unreachable code.
14921492
skipFunctionsWithAnnotation("AltInst");
1493-
1494-
return Error::success();
14951493
}
14961494

14971495
/// When the Linux kernel needs to handle an error associated with a given PCI

0 commit comments

Comments
 (0)