diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 77ba81c58c5d63f7e5e054d9503e882afd3ae468..c4727a0c267d3ecb5a3a5495945ce417d5adc38a 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -115,6 +115,10 @@ clang/test/AST/Interp/ @tbaederr
/mlir/**/LLVMIR/**/BasicPtxBuilderInterface* @grypp
/mlir/**/NVVM*/ @grypp
+# MLIR Python Bindings
+/mlir/test/python/ @makslevental @stellaraccident
+/mlir/python/ @makslevental @stellaraccident
+
# BOLT
/bolt/ @aaupov @maksfb @rafaelauler @ayermolo @dcci
@@ -125,3 +129,6 @@ clang/test/AST/Interp/ @tbaederr
/llvm/**/TextAPI/ @cyndyishida
/clang/**/InstallAPI/ @cyndyishida
/clang/tools/clang-installapi/ @cyndyishida
+
+# ExtractAPI
+/clang/**/ExtractAPI @daniel-grumberg
diff --git a/.github/workflows/email-check.yaml b/.github/workflows/email-check.yaml
index ac53b5e527b0949020e0e73d90def5d2b6295cd0..8f32d020975f5d70e7eead9c34a0ea86fb836e81 100644
--- a/.github/workflows/email-check.yaml
+++ b/.github/workflows/email-check.yaml
@@ -1,7 +1,7 @@
name: "Check for private emails used in PRs"
on:
- pull_request_target:
+ pull_request:
types:
- opened
@@ -10,8 +10,6 @@ permissions:
jobs:
validate_email:
- permissions:
- pull-requests: write
runs-on: ubuntu-latest
if: github.repository == 'llvm/llvm-project'
steps:
@@ -25,20 +23,24 @@ jobs:
run: |
git log -1
echo "EMAIL=$(git show -s --format='%ae' HEAD~0)" >> $GITHUB_OUTPUT
+ # Create empty comment file
+ echo "[]" > comments
- name: Validate author email
if: ${{ endsWith(steps.author.outputs.EMAIL, 'noreply.github.com') }}
- uses: actions/github-script@v6
env:
- EMAIL: ${{ steps.author.outputs.EMAIL }}
+ COMMENT: >-
+ ⚠️ We detected that you are using a GitHub private e-mail address to contribute to the repo.
+ Please turn off [Keep my email addresses private](https://github.com/settings/emails) setting in your account.
+ See [LLVM Discourse](https://discourse.llvm.org/t/hidden-emails-on-github-should-we-do-something-about-it) for more information.
+ run: |
+ cat << EOF > comments
+ [{"body" : "$COMMENT"}]
+ EOF
+
+ - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0
+ if: always()
with:
- script: |
- const { EMAIL } = process.env
- await github.rest.issues.createComment({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- body: `⚠️ We detected that you are using a GitHub private e-mail address to contribute to the repo.
- Please turn off [Keep my email addresses private](https://github.com/settings/emails) setting in your account.
- See [LLVM Discourse](https://discourse.llvm.org/t/hidden-emails-on-github-should-we-do-something-about-it) for more information.
- `})
+ name: workflow-args
+ path: |
+ comments
diff --git a/.github/workflows/issue-write.yml b/.github/workflows/issue-write.yml
index 02a5f7c213e898d32d5e5f4d3d42db11df7359c7..e003be006c4e15405bd977e277b0f9a64e69d2af 100644
--- a/.github/workflows/issue-write.yml
+++ b/.github/workflows/issue-write.yml
@@ -2,7 +2,9 @@ name: Comment on an issue
on:
workflow_run:
- workflows: ["Check code formatting"]
+ workflows:
+ - "Check code formatting"
+ - "Check for private emails used in PRs"
types:
- completed
@@ -31,7 +33,7 @@ jobs:
script: |
var fs = require('fs');
const comments = JSON.parse(fs.readFileSync('./comments'));
- if (!comments) {
+ if (!comments || comments.length == 0) {
return;
}
@@ -77,6 +79,15 @@ jobs:
}
const gql_result = await github.graphql(gql_query, gql_variables);
console.log(gql_result);
+ // If the branch for the PR was deleted before this job has a chance
+ // to run, then the ref will be null. This can happen if someone:
+ // 1. Rebase the PR, which triggers some workflow.
+ // 2. Immediately merges the PR and deletes the branch.
+ // 3. The workflow finishes and triggers this job.
+ if (!gql_result.repository.ref) {
+ console.log("Ref has been deleted");
+ return;
+ }
console.log(gql_result.repository.ref.associatedPullRequests.nodes);
var pr_number = 0;
diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml
index 4a881ef5ff56af432500413950890ee8d6563dbc..1e9367732e591118445fef2c69acf3339c2cbf5d 100644
--- a/.github/workflows/libcxx-build-and-test.yaml
+++ b/.github/workflows/libcxx-build-and-test.yaml
@@ -38,11 +38,11 @@ env:
# LLVM POST-BRANCH bump version
# LLVM POST-BRANCH add compiler test for ToT - 1, e.g. "Clang 17"
# LLVM RELEASE bump remove compiler ToT - 3, e.g. "Clang 15"
- LLVM_HEAD_VERSION: "18" # Used compiler, update POST-BRANCH.
- LLVM_PREVIOUS_VERSION: "17"
- LLVM_OLDEST_VERSION: "16"
+ LLVM_HEAD_VERSION: "19" # Used compiler, update POST-BRANCH.
+ LLVM_PREVIOUS_VERSION: "18"
+ LLVM_OLDEST_VERSION: "17"
GCC_STABLE_VERSION: "13"
- LLVM_SYMBOLIZER_PATH: "/usr/bin/llvm-symbolizer-18"
+ LLVM_SYMBOLIZER_PATH: "/usr/bin/llvm-symbolizer-19"
CLANG_CRASH_DIAGNOSTICS_DIR: "crash_diagnostics"
@@ -59,8 +59,8 @@ jobs:
'generic-cxx26',
'generic-modules'
]
- cc: [ 'clang-18' ]
- cxx: [ 'clang++-18' ]
+ cc: [ 'clang-19' ]
+ cxx: [ 'clang++-19' ]
clang_tidy: [ 'ON' ]
include:
- config: 'generic-gcc'
@@ -100,22 +100,22 @@ jobs:
'generic-cxx20',
'generic-cxx23'
]
- cc: [ 'clang-18' ]
- cxx: [ 'clang++-18' ]
+ cc: [ 'clang-19' ]
+ cxx: [ 'clang++-19' ]
clang_tidy: [ 'ON' ]
include:
- config: 'generic-gcc-cxx11'
cc: 'gcc-13'
cxx: 'g++-13'
clang_tidy: 'OFF'
- - config: 'generic-cxx23'
- cc: 'clang-16'
- cxx: 'clang++-16'
- clang_tidy: 'OFF'
- config: 'generic-cxx23'
cc: 'clang-17'
cxx: 'clang++-17'
clang_tidy: 'OFF'
+ - config: 'generic-cxx26'
+ cc: 'clang-18'
+ cxx: 'clang++-18'
+ clang_tidy: 'ON'
steps:
- uses: actions/checkout@v4
- name: ${{ matrix.config }}
@@ -186,8 +186,8 @@ jobs:
- name: ${{ matrix.config }}
run: libcxx/utils/ci/run-buildbot ${{ matrix.config }}
env:
- CC: clang-18
- CXX: clang++-18
+ CC: clang-19
+ CXX: clang++-19
ENABLE_CLANG_TIDY: "OFF"
- uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
if: always()
diff --git a/.github/workflows/pr-code-format.yml b/.github/workflows/pr-code-format.yml
index 54dfe3aadbb423d98b106ea02d9f4f09e25574c3..983838858ba43ed90cca953b6b00668d6746f5df 100644
--- a/.github/workflows/pr-code-format.yml
+++ b/.github/workflows/pr-code-format.yml
@@ -1,4 +1,8 @@
name: "Check code formatting"
+
+permissions:
+ contents: read
+
on:
pull_request:
branches:
@@ -33,7 +37,7 @@ jobs:
- name: Fetch code formatting utils
uses: actions/checkout@v4
with:
- reository: ${{ github.repository }}
+ repository: ${{ github.repository }}
ref: ${{ github.base_ref }}
sparse-checkout: |
llvm/utils/git/requirements_formatting.txt
diff --git a/.github/workflows/release-lit.yml b/.github/workflows/release-lit.yml
index 36b0b6edd518fc74fecf397c8947ff706066a52e..0316ba406041d6fc63c812da994ea662276bee6e 100644
--- a/.github/workflows/release-lit.yml
+++ b/.github/workflows/release-lit.yml
@@ -58,7 +58,7 @@ jobs:
cd llvm/utils/lit
# Remove 'dev' suffix from lit version.
sed -i 's/ + "dev"//g' lit/__init__.py
- python3 setup.py sdist
+ python3 setup.py sdist bdist_wheel
- name: Upload lit to test.pypi.org
uses: pypa/gh-action-pypi-publish@release/v1
diff --git a/bolt/docs/BAT.md b/bolt/docs/BAT.md
index f23ef1abf8761cc58fc2249ef0da43ae0034e114..7ffb5d7c00816e11df469e3de6372e462d1eb616 100644
--- a/bolt/docs/BAT.md
+++ b/bolt/docs/BAT.md
@@ -81,9 +81,10 @@ Hot indices are delta encoded, implicitly starting at zero.
| `FuncHash` | 8b | Function hash for input function | Hot |
| `NumBlocks` | ULEB128 | Number of basic blocks in the original function | Hot |
| `NumSecEntryPoints` | ULEB128 | Number of secondary entry points in the original function | Hot |
+| `ColdInputSkew` | ULEB128 | Skew to apply to all input offsets | Cold |
| `NumEntries` | ULEB128 | Number of address translation entries for a function | Both |
-| `EqualElems` | ULEB128 | Number of equal offsets in the beginning of a function | Hot |
-| `BranchEntries` | Bitmask, `alignTo(EqualElems, 8)` bits | If `EqualElems` is non-zero, bitmask denoting entries with `BRANCHENTRY` bit | Hot |
+| `EqualElems` | ULEB128 | Number of equal offsets in the beginning of a function | Both |
+| `BranchEntries` | Bitmask, `alignTo(EqualElems, 8)` bits | If `EqualElems` is non-zero, bitmask denoting entries with `BRANCHENTRY` bit | Both |
Function header is followed by *Address Translation Table* with `NumEntries`
total entries, and *Secondary Entry Points* table with `NumSecEntryPoints`
@@ -99,8 +100,8 @@ entry is encoded. Input offsets implicitly start at zero.
| `BBHash` | Optional, 8b | Basic block hash in input binary | BB |
| `BBIdx` | Optional, Delta, ULEB128 | Basic block index in input binary | BB |
-For hot fragments, the table omits the first `EqualElems` input offsets
-where the input offset equals output offset.
+The table omits the first `EqualElems` input offsets where the input offset
+equals output offset.
`BRANCHENTRY` bit denotes whether a given offset pair is a control flow source
(branch or call instruction). If not set, it signifies a control flow target
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index 5089f849128010ba04efa57bdf485c131923e74e..26d2d01f86267127f4c4bea9daa5862b4b412738 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -1168,7 +1168,7 @@ public:
/// Pass an offset of the entry point in the input binary and a corresponding
/// global symbol to the callback function.
///
- /// Return true of all callbacks returned true, false otherwise.
+ /// Return true if all callbacks returned true, false otherwise.
bool forEachEntryPoint(EntryPointCallbackTy Callback) const;
/// Return MC symbol associated with the end of the function.
@@ -1402,7 +1402,8 @@ public:
/// Return true if the function has CFI instructions
bool hasCFI() const {
- return !FrameInstructions.empty() || !CIEFrameInstructions.empty();
+ return !FrameInstructions.empty() || !CIEFrameInstructions.empty() ||
+ IsInjected;
}
/// Return unique number associated with the function.
diff --git a/bolt/include/bolt/Profile/BoltAddressTranslation.h b/bolt/include/bolt/Profile/BoltAddressTranslation.h
index caf907cc43da3e8d4af24c6b5be5b5c41293e65c..68b993ee363cc0d0a3bbd59113e4a42c896da4cb 100644
--- a/bolt/include/bolt/Profile/BoltAddressTranslation.h
+++ b/bolt/include/bolt/Profile/BoltAddressTranslation.h
@@ -19,6 +19,7 @@
#include
namespace llvm {
+class MCSymbol;
class raw_ostream;
namespace object {
@@ -118,10 +119,12 @@ public:
/// True if a given \p Address is a function with translation table entry.
bool isBATFunction(uint64_t Address) const { return Maps.count(Address); }
- /// Returns branch offsets grouped by containing basic block in a given
- /// function.
- std::unordered_map>
- getBFBranches(uint64_t FuncOutputAddress) const;
+ /// For a given \p Symbol in the output binary and known \p InputOffset
+ /// return a corresponding pair of parent BinaryFunction and secondary entry
+ /// point in it.
+ std::pair
+ translateSymbol(const BinaryContext &BC, const MCSymbol &Symbol,
+ uint32_t InputOffset) const;
private:
/// Helper to update \p Map by inserting one or more BAT entries reflecting
@@ -146,9 +149,9 @@ private:
/// entries in function address translation map.
APInt calculateBranchEntriesBitMask(MapTy &Map, size_t EqualElems);
- /// Calculate the number of equal offsets (output = input) in the beginning
- /// of the function.
- size_t getNumEqualOffsets(const MapTy &Map) const;
+ /// Calculate the number of equal offsets (output = input - skew) in the
+ /// beginning of the function.
+ size_t getNumEqualOffsets(const MapTy &Map, uint32_t Skew) const;
std::map Maps;
@@ -158,6 +161,10 @@ private:
/// Map a function to its secondary entry points vector
std::unordered_map> SecondaryEntryPointsMap;
+ /// Return a secondary entry point ID for a function located at \p Address and
+ /// \p Offset within that function.
+ unsigned getSecondaryEntryPointId(uint64_t Address, uint32_t Offset) const;
+
/// Links outlined cold bocks to their original function
std::map ColdPartSource;
@@ -181,7 +188,7 @@ public:
EntryTy(unsigned Index, size_t Hash) : Index(Index), Hash(Hash) {}
};
- std::unordered_map Map;
+ std::map Map;
const EntryTy &getEntry(uint32_t BBInputOffset) const {
auto It = Map.find(BBInputOffset);
assert(It != Map.end());
@@ -206,6 +213,10 @@ public:
}
size_t getNumBasicBlocks() const { return Map.size(); }
+
+ auto begin() const { return Map.begin(); }
+ auto end() const { return Map.end(); }
+ auto upper_bound(uint32_t Offset) const { return Map.upper_bound(Offset); }
};
/// Map function output address to its hash and basic blocks hash map.
diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h
index 4fbe524b1c385d24d10cd3a0ea33ca53580f0fc9..84f76caae9dbb01ea38e59f5eb629e0492ddb800 100644
--- a/bolt/include/bolt/Profile/DataAggregator.h
+++ b/bolt/include/bolt/Profile/DataAggregator.h
@@ -225,6 +225,10 @@ private:
/// Aggregation statistics
uint64_t NumInvalidTraces{0};
uint64_t NumLongRangeTraces{0};
+ /// Specifies how many samples were recorded in cold areas if we are dealing
+ /// with profiling data collected in a bolted binary. For LBRs, incremented
+ /// for the source of the branch to avoid counting cold activity twice (one
+ /// for source and another for destination).
uint64_t NumColdSamples{0};
/// Looks into system PATH for Linux Perf and set up the aggregator to use it
@@ -245,14 +249,12 @@ private:
/// disassembled BinaryFunctions
BinaryFunction *getBinaryFunctionContainingAddress(uint64_t Address) const;
+ /// Perform BAT translation for a given \p Func and return the parent
+ /// BinaryFunction or nullptr.
+ BinaryFunction *getBATParentFunction(const BinaryFunction &Func) const;
+
/// Retrieve the location name to be used for samples recorded in \p Func.
- /// If doing BAT translation, link cold parts to the hot part names (used by
- /// the original binary). \p Count specifies how many samples were recorded
- /// at that location, so we can tally total activity in cold areas if we are
- /// dealing with profiling data collected in a bolted binary. For LBRs,
- /// \p Count should only be used for the source of the branch to avoid
- /// counting cold activity twice (one for source and another for destination).
- StringRef getLocationName(BinaryFunction &Func, uint64_t Count);
+ StringRef getLocationName(const BinaryFunction &Func) const;
/// Semantic actions - parser hooks to interpret parsed perf samples
/// Register a sample (non-LBR mode), i.e. a new hit at \p Address
@@ -467,9 +469,6 @@ private:
std::error_code writeBATYAML(BinaryContext &BC,
StringRef OutputFilename) const;
- /// Fixup profile collected on BOLTed binary, namely handle split functions.
- void fixupBATProfile(BinaryContext &BC);
-
/// Filter out binaries based on PID
void filterBinaryMMapInfo();
diff --git a/bolt/include/bolt/Profile/YAMLProfileWriter.h b/bolt/include/bolt/Profile/YAMLProfileWriter.h
index 882748627e7f54aec2a5d0b85d738ce93cf292ee..4a9355dfceac9ec174729553d7b80006a8429d80 100644
--- a/bolt/include/bolt/Profile/YAMLProfileWriter.h
+++ b/bolt/include/bolt/Profile/YAMLProfileWriter.h
@@ -15,6 +15,7 @@
namespace llvm {
namespace bolt {
+class BoltAddressTranslation;
class RewriteInstance;
class YAMLProfileWriter {
@@ -31,8 +32,16 @@ public:
/// Save execution profile for that instance.
std::error_code writeProfile(const RewriteInstance &RI);
- static yaml::bolt::BinaryFunctionProfile convert(const BinaryFunction &BF,
- bool UseDFS);
+ static yaml::bolt::BinaryFunctionProfile
+ convert(const BinaryFunction &BF, bool UseDFS,
+ const BoltAddressTranslation *BAT = nullptr);
+
+ /// Set CallSiteInfo destination fields from \p Symbol and return a target
+ /// BinaryFunction for that symbol.
+ static const BinaryFunction *
+ setCSIDestination(const BinaryContext &BC, yaml::bolt::CallSiteInfo &CSI,
+ const MCSymbol *Symbol, const BoltAddressTranslation *BAT,
+ uint32_t Offset = 0);
};
} // namespace bolt
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 47eae964e816c5565697bdc2ef974bdfecdff4dc..7c2d8c52287be19f8798a91db5649687314d5b19 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -555,6 +555,9 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
const uint64_t NextJTAddress,
JumpTable::AddressesType *EntriesAsAddress,
bool *HasEntryInFragment) const {
+ // Target address of __builtin_unreachable.
+ const uint64_t UnreachableAddress = BF.getAddress() + BF.getSize();
+
// Is one of the targets __builtin_unreachable?
bool HasUnreachable = false;
@@ -564,9 +567,15 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
// Number of targets other than __builtin_unreachable.
uint64_t NumRealEntries = 0;
- auto addEntryAddress = [&](uint64_t EntryAddress) {
- if (EntriesAsAddress)
- EntriesAsAddress->emplace_back(EntryAddress);
+ // Size of the jump table without trailing __builtin_unreachable entries.
+ size_t TrimmedSize = 0;
+
+ auto addEntryAddress = [&](uint64_t EntryAddress, bool Unreachable = false) {
+ if (!EntriesAsAddress)
+ return;
+ EntriesAsAddress->emplace_back(EntryAddress);
+ if (!Unreachable)
+ TrimmedSize = EntriesAsAddress->size();
};
ErrorOr Section = getSectionForAddress(Address);
@@ -618,8 +627,8 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
: *getPointerAtAddress(EntryAddress);
// __builtin_unreachable() case.
- if (Value == BF.getAddress() + BF.getSize()) {
- addEntryAddress(Value);
+ if (Value == UnreachableAddress) {
+ addEntryAddress(Value, /*Unreachable*/ true);
HasUnreachable = true;
LLVM_DEBUG(dbgs() << formatv("OK: {0:x} __builtin_unreachable\n", Value));
continue;
@@ -673,6 +682,13 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
addEntryAddress(Value);
}
+ // Trim direct/normal jump table to exclude trailing unreachable entries that
+ // can collide with a function address.
+ if (Type == JumpTable::JTT_NORMAL && EntriesAsAddress &&
+ TrimmedSize != EntriesAsAddress->size() &&
+ getBinaryFunctionAtAddress(UnreachableAddress))
+ EntriesAsAddress->resize(TrimmedSize);
+
// It's a jump table if the number of real entries is more than 1, or there's
// one real entry and one or more special targets. If there are only multiple
// special targets, then it's not a jump table.
diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp
index cbf95a7db08b52bc95856a90fd5266560fef7c2a..d16b7a94787c65d1e9b25324f3b1961b6b109102 100644
--- a/bolt/lib/Core/Relocation.cpp
+++ b/bolt/lib/Core/Relocation.cpp
@@ -774,60 +774,95 @@ static bool isPCRelativeRISCV(uint64_t Type) {
}
bool Relocation::isSupported(uint64_t Type) {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ return false;
+ case Triple::aarch64:
return isSupportedAArch64(Type);
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
return isSupportedRISCV(Type);
- return isSupportedX86(Type);
+ case Triple::x86_64:
+ return isSupportedX86(Type);
+ }
}
size_t Relocation::getSizeForType(uint64_t Type) {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return getSizeForTypeAArch64(Type);
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
return getSizeForTypeRISCV(Type);
- return getSizeForTypeX86(Type);
+ case Triple::x86_64:
+ return getSizeForTypeX86(Type);
+ }
}
bool Relocation::skipRelocationType(uint64_t Type) {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return skipRelocationTypeAArch64(Type);
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
return skipRelocationTypeRISCV(Type);
- return skipRelocationTypeX86(Type);
+ case Triple::x86_64:
+ return skipRelocationTypeX86(Type);
+ }
}
bool Relocation::skipRelocationProcess(uint64_t &Type, uint64_t Contents) {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return skipRelocationProcessAArch64(Type, Contents);
- if (Arch == Triple::riscv64)
- skipRelocationProcessRISCV(Type, Contents);
- return skipRelocationProcessX86(Type, Contents);
+ case Triple::riscv64:
+ return skipRelocationProcessRISCV(Type, Contents);
+ case Triple::x86_64:
+ return skipRelocationProcessX86(Type, Contents);
+ }
}
uint64_t Relocation::encodeValue(uint64_t Type, uint64_t Value, uint64_t PC) {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return encodeValueAArch64(Type, Value, PC);
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
return encodeValueRISCV(Type, Value, PC);
- return encodeValueX86(Type, Value, PC);
+ case Triple::x86_64:
+ return encodeValueX86(Type, Value, PC);
+ }
}
uint64_t Relocation::extractValue(uint64_t Type, uint64_t Contents,
uint64_t PC) {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return extractValueAArch64(Type, Contents, PC);
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
return extractValueRISCV(Type, Contents, PC);
- return extractValueX86(Type, Contents, PC);
+ case Triple::x86_64:
+ return extractValueX86(Type, Contents, PC);
+ }
}
bool Relocation::isGOT(uint64_t Type) {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return isGOTAArch64(Type);
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
return isGOTRISCV(Type);
- return isGOTX86(Type);
+ case Triple::x86_64:
+ return isGOTX86(Type);
+ }
}
bool Relocation::isX86GOTPCRELX(uint64_t Type) {
@@ -845,27 +880,42 @@ bool Relocation::isX86GOTPC64(uint64_t Type) {
bool Relocation::isNone(uint64_t Type) { return Type == getNone(); }
bool Relocation::isRelative(uint64_t Type) {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return Type == ELF::R_AARCH64_RELATIVE;
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
return Type == ELF::R_RISCV_RELATIVE;
- return Type == ELF::R_X86_64_RELATIVE;
+ case Triple::x86_64:
+ return Type == ELF::R_X86_64_RELATIVE;
+ }
}
bool Relocation::isIRelative(uint64_t Type) {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return Type == ELF::R_AARCH64_IRELATIVE;
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
llvm_unreachable("not implemented");
- return Type == ELF::R_X86_64_IRELATIVE;
+ case Triple::x86_64:
+ return Type == ELF::R_X86_64_IRELATIVE;
+ }
}
bool Relocation::isTLS(uint64_t Type) {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return isTLSAArch64(Type);
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
return isTLSRISCV(Type);
- return isTLSX86(Type);
+ case Triple::x86_64:
+ return isTLSX86(Type);
+ }
}
bool Relocation::isInstructionReference(uint64_t Type) {
@@ -882,49 +932,81 @@ bool Relocation::isInstructionReference(uint64_t Type) {
}
uint64_t Relocation::getNone() {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return ELF::R_AARCH64_NONE;
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
return ELF::R_RISCV_NONE;
- return ELF::R_X86_64_NONE;
+ case Triple::x86_64:
+ return ELF::R_X86_64_NONE;
+ }
}
uint64_t Relocation::getPC32() {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return ELF::R_AARCH64_PREL32;
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
return ELF::R_RISCV_32_PCREL;
- return ELF::R_X86_64_PC32;
+ case Triple::x86_64:
+ return ELF::R_X86_64_PC32;
+ }
}
uint64_t Relocation::getPC64() {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return ELF::R_AARCH64_PREL64;
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
llvm_unreachable("not implemented");
- return ELF::R_X86_64_PC64;
+ case Triple::x86_64:
+ return ELF::R_X86_64_PC64;
+ }
}
bool Relocation::isPCRelative(uint64_t Type) {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return isPCRelativeAArch64(Type);
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
return isPCRelativeRISCV(Type);
- return isPCRelativeX86(Type);
+ case Triple::x86_64:
+ return isPCRelativeX86(Type);
+ }
}
uint64_t Relocation::getAbs64() {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return ELF::R_AARCH64_ABS64;
- if (Arch == Triple::riscv64)
+ case Triple::riscv64:
return ELF::R_RISCV_64;
- return ELF::R_X86_64_64;
+ case Triple::x86_64:
+ return ELF::R_X86_64_64;
+ }
}
uint64_t Relocation::getRelative() {
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ llvm_unreachable("Unsupported architecture");
+ case Triple::aarch64:
return ELF::R_AARCH64_RELATIVE;
- return ELF::R_X86_64_RELATIVE;
+ case Triple::riscv64:
+ llvm_unreachable("not implemented");
+ case Triple::x86_64:
+ return ELF::R_X86_64_RELATIVE;
+ }
}
size_t Relocation::emit(MCStreamer *Streamer) const {
@@ -991,9 +1073,16 @@ void Relocation::print(raw_ostream &OS) const {
static const char *AArch64RelocNames[] = {
#include "llvm/BinaryFormat/ELFRelocs/AArch64.def"
};
- if (Arch == Triple::aarch64)
+ switch (Arch) {
+ default:
+ OS << "RType:" << Twine::utohexstr(Type);
+ break;
+
+ case Triple::aarch64:
OS << AArch64RelocNames[Type];
- else if (Arch == Triple::riscv64) {
+ break;
+
+ case Triple::riscv64:
// RISC-V relocations are not sequentially numbered so we cannot use an
// array
switch (Type) {
@@ -1006,8 +1095,12 @@ void Relocation::print(raw_ostream &OS) const {
break;
#include "llvm/BinaryFormat/ELFRelocs/RISCV.def"
}
- } else
+ break;
+
+ case Triple::x86_64:
OS << X86RelocNames[Type];
+ break;
+ }
OS << ", 0x" << Twine::utohexstr(Offset);
if (Symbol) {
OS << ", " << Symbol->getName();
diff --git a/bolt/lib/Profile/BoltAddressTranslation.cpp b/bolt/lib/Profile/BoltAddressTranslation.cpp
index bcd4a457ce3b491908b0a1617e66bee322cdd7c6..ac6e3d01f2718bf5a77f061294edaa25b11e5595 100644
--- a/bolt/lib/Profile/BoltAddressTranslation.cpp
+++ b/bolt/lib/Profile/BoltAddressTranslation.cpp
@@ -100,7 +100,7 @@ void BoltAddressTranslation::write(const BinaryContext &BC, raw_ostream &OS) {
LLVM_DEBUG(dbgs() << "Function name: " << Function.getPrintName() << "\n");
LLVM_DEBUG(dbgs() << " Address reference: 0x"
<< Twine::utohexstr(Function.getOutputAddress()) << "\n");
- LLVM_DEBUG(dbgs() << formatv(" Hash: {0:x}\n", getBFHash(OutputAddress)));
+ LLVM_DEBUG(dbgs() << formatv(" Hash: {0:x}\n", getBFHash(InputAddress)));
LLVM_DEBUG(dbgs() << " Secondary Entry Points: " << NumSecondaryEntryPoints
<< '\n');
@@ -153,12 +153,13 @@ APInt BoltAddressTranslation::calculateBranchEntriesBitMask(MapTy &Map,
return BitMask;
}
-size_t BoltAddressTranslation::getNumEqualOffsets(const MapTy &Map) const {
+size_t BoltAddressTranslation::getNumEqualOffsets(const MapTy &Map,
+ uint32_t Skew) const {
size_t EqualOffsets = 0;
for (const std::pair &KeyVal : Map) {
const uint32_t OutputOffset = KeyVal.first;
const uint32_t InputOffset = KeyVal.second >> 1;
- if (OutputOffset == InputOffset)
+ if (OutputOffset == InputOffset - Skew)
++EqualOffsets;
else
break;
@@ -196,18 +197,24 @@ void BoltAddressTranslation::writeMaps(std::map &Maps,
SecondaryEntryPointsMap.count(Address)
? SecondaryEntryPointsMap[Address].size()
: 0;
+ uint32_t Skew = 0;
if (Cold) {
- size_t HotIndex =
- std::distance(ColdPartSource.begin(), ColdPartSource.find(Address));
+ auto HotEntryIt = Maps.find(ColdPartSource[Address]);
+ assert(HotEntryIt != Maps.end());
+ size_t HotIndex = std::distance(Maps.begin(), HotEntryIt);
encodeULEB128(HotIndex - PrevIndex, OS);
PrevIndex = HotIndex;
+ // Skew of all input offsets for cold fragments is simply the first input
+ // offset.
+ Skew = Map.begin()->second >> 1;
+ encodeULEB128(Skew, OS);
} else {
// Function hash
size_t BFHash = getBFHash(HotInputAddress);
LLVM_DEBUG(dbgs() << "Hash: " << formatv("{0:x}\n", BFHash));
OS.write(reinterpret_cast(&BFHash), 8);
// Number of basic blocks
- size_t NumBasicBlocks = getBBHashMap(HotInputAddress).getNumBasicBlocks();
+ size_t NumBasicBlocks = NumBasicBlocksMap[HotInputAddress];
LLVM_DEBUG(dbgs() << "Basic blocks: " << NumBasicBlocks << '\n');
encodeULEB128(NumBasicBlocks, OS);
// Secondary entry points
@@ -216,24 +223,21 @@ void BoltAddressTranslation::writeMaps(std::map &Maps,
<< '\n');
}
encodeULEB128(NumEntries, OS);
- // For hot fragments only: encode the number of equal offsets
- // (output = input) in the beginning of the function. Only encode one offset
- // in these cases.
- const size_t EqualElems = Cold ? 0 : getNumEqualOffsets(Map);
- if (!Cold) {
- encodeULEB128(EqualElems, OS);
- if (EqualElems) {
- const size_t BranchEntriesBytes = alignTo(EqualElems, 8) / 8;
- APInt BranchEntries = calculateBranchEntriesBitMask(Map, EqualElems);
- OS.write(reinterpret_cast(BranchEntries.getRawData()),
- BranchEntriesBytes);
- LLVM_DEBUG({
- dbgs() << "BranchEntries: ";
- SmallString<8> BitMaskStr;
- BranchEntries.toString(BitMaskStr, 2, false);
- dbgs() << BitMaskStr << '\n';
- });
- }
+ // Encode the number of equal offsets (output = input - skew) in the
+ // beginning of the function. Only encode one offset in these cases.
+ const size_t EqualElems = getNumEqualOffsets(Map, Skew);
+ encodeULEB128(EqualElems, OS);
+ if (EqualElems) {
+ const size_t BranchEntriesBytes = alignTo(EqualElems, 8) / 8;
+ APInt BranchEntries = calculateBranchEntriesBitMask(Map, EqualElems);
+ OS.write(reinterpret_cast(BranchEntries.getRawData()),
+ BranchEntriesBytes);
+ LLVM_DEBUG({
+ dbgs() << "BranchEntries: ";
+ SmallString<8> BitMaskStr;
+ BranchEntries.toString(BitMaskStr, 2, false);
+ dbgs() << BitMaskStr << '\n';
+ });
}
const BBHashMapTy &BBHashMap = getBBHashMap(HotInputAddress);
size_t Index = 0;
@@ -314,10 +318,12 @@ void BoltAddressTranslation::parseMaps(std::vector &HotFuncs,
uint64_t HotAddress = Cold ? 0 : Address;
PrevAddress = Address;
uint32_t SecondaryEntryPoints = 0;
+ uint64_t ColdInputSkew = 0;
if (Cold) {
HotIndex += DE.getULEB128(&Offset, &Err);
HotAddress = HotFuncs[HotIndex];
ColdPartSource.emplace(Address, HotAddress);
+ ColdInputSkew = DE.getULEB128(&Offset, &Err);
} else {
HotFuncs.push_back(Address);
// Function hash
@@ -338,28 +344,25 @@ void BoltAddressTranslation::parseMaps(std::vector &HotFuncs,
getULEB128Size(SecondaryEntryPoints)));
}
const uint32_t NumEntries = DE.getULEB128(&Offset, &Err);
- // Equal offsets, hot fragments only.
- size_t EqualElems = 0;
+ // Equal offsets.
+ const size_t EqualElems = DE.getULEB128(&Offset, &Err);
APInt BEBitMask;
- if (!Cold) {
- EqualElems = DE.getULEB128(&Offset, &Err);
- LLVM_DEBUG(dbgs() << formatv("Equal offsets: {0}, {1} bytes\n",
- EqualElems, getULEB128Size(EqualElems)));
- if (EqualElems) {
- const size_t BranchEntriesBytes = alignTo(EqualElems, 8) / 8;
- BEBitMask = APInt(alignTo(EqualElems, 8), 0);
- LoadIntFromMemory(
- BEBitMask,
- reinterpret_cast(
- DE.getBytes(&Offset, BranchEntriesBytes, &Err).data()),
- BranchEntriesBytes);
- LLVM_DEBUG({
- dbgs() << "BEBitMask: ";
- SmallString<8> BitMaskStr;
- BEBitMask.toString(BitMaskStr, 2, false);
- dbgs() << BitMaskStr << ", " << BranchEntriesBytes << " bytes\n";
- });
- }
+ LLVM_DEBUG(dbgs() << formatv("Equal offsets: {0}, {1} bytes\n", EqualElems,
+ getULEB128Size(EqualElems)));
+ if (EqualElems) {
+ const size_t BranchEntriesBytes = alignTo(EqualElems, 8) / 8;
+ BEBitMask = APInt(alignTo(EqualElems, 8), 0);
+ LoadIntFromMemory(
+ BEBitMask,
+ reinterpret_cast(
+ DE.getBytes(&Offset, BranchEntriesBytes, &Err).data()),
+ BranchEntriesBytes);
+ LLVM_DEBUG({
+ dbgs() << "BEBitMask: ";
+ SmallString<8> BitMaskStr;
+ BEBitMask.toString(BitMaskStr, 2, false);
+ dbgs() << BitMaskStr << ", " << BranchEntriesBytes << " bytes\n";
+ });
}
MapTy Map;
@@ -374,7 +377,7 @@ void BoltAddressTranslation::parseMaps(std::vector &HotFuncs,
PrevAddress = OutputAddress;
int64_t InputDelta = 0;
if (J < EqualElems) {
- InputOffset = (OutputOffset << 1) | BEBitMask[J];
+ InputOffset = (OutputOffset + ColdInputSkew << 1) | BEBitMask[J];
} else {
InputDelta = DE.getSLEB128(&Offset, &Err);
InputOffset += InputDelta;
@@ -425,8 +428,9 @@ void BoltAddressTranslation::dump(raw_ostream &OS) {
for (const auto &MapEntry : Maps) {
const uint64_t Address = MapEntry.first;
const uint64_t HotAddress = fetchParentAddress(Address);
+ const bool IsHotFunction = HotAddress == 0;
OS << "Function Address: 0x" << Twine::utohexstr(Address);
- if (HotAddress == 0)
+ if (IsHotFunction)
OS << formatv(", hash: {0:x}", getBFHash(Address));
OS << "\n";
OS << "BB mappings:\n";
@@ -443,6 +447,8 @@ void BoltAddressTranslation::dump(raw_ostream &OS) {
OS << formatv(" hash: {0:x}", BBHashMap.getBBHash(Val));
OS << "\n";
}
+ if (IsHotFunction)
+ OS << "NumBlocks: " << NumBasicBlocksMap[Address] << '\n';
if (SecondaryEntryPointsMap.count(Address)) {
const std::vector &SecondaryEntryPoints =
SecondaryEntryPointsMap[Address];
@@ -574,27 +580,52 @@ void BoltAddressTranslation::saveMetadata(BinaryContext &BC) {
// Set BF/BB metadata
for (const BinaryBasicBlock &BB : BF)
BBHashMap.addEntry(BB.getInputOffset(), BB.getIndex(), BB.getHash());
+ NumBasicBlocksMap.emplace(BF.getAddress(), BF.size());
}
}
-std::unordered_map>
-BoltAddressTranslation::getBFBranches(uint64_t OutputAddress) const {
- std::unordered_map> Branches;
- auto FuncIt = Maps.find(OutputAddress);
- assert(FuncIt != Maps.end());
- std::vector InputOffsets;
- for (const auto &KV : FuncIt->second)
- InputOffsets.emplace_back(KV.second);
- // Sort with LSB BRANCHENTRY bit.
- llvm::sort(InputOffsets);
- uint32_t BBOffset{0};
- for (uint32_t InOffset : InputOffsets) {
- if (InOffset & BRANCHENTRY)
- Branches[BBOffset].push_back(InOffset >> 1);
- else
- BBOffset = InOffset >> 1;
- }
- return Branches;
+unsigned
+BoltAddressTranslation::getSecondaryEntryPointId(uint64_t Address,
+ uint32_t Offset) const {
+ auto FunctionIt = SecondaryEntryPointsMap.find(Address);
+ if (FunctionIt == SecondaryEntryPointsMap.end())
+ return 0;
+ const std::vector &Offsets = FunctionIt->second;
+ auto OffsetIt = std::find(Offsets.begin(), Offsets.end(), Offset);
+ if (OffsetIt == Offsets.end())
+ return 0;
+ // Adding one here because main entry point is not stored in BAT, and
+ // enumeration for secondary entry points starts with 1.
+ return OffsetIt - Offsets.begin() + 1;
+}
+
+std::pair
+BoltAddressTranslation::translateSymbol(const BinaryContext &BC,
+ const MCSymbol &Symbol,
+ uint32_t Offset) const {
+ // The symbol could be a secondary entry in a cold fragment.
+ uint64_t SymbolValue = cantFail(errorOrToExpected(BC.getSymbolValue(Symbol)));
+
+ const BinaryFunction *Callee = BC.getFunctionForSymbol(&Symbol);
+ assert(Callee);
+
+ // Containing function, not necessarily the same as symbol value.
+ const uint64_t CalleeAddress = Callee->getAddress();
+ const uint32_t OutputOffset = SymbolValue - CalleeAddress;
+
+ const uint64_t ParentAddress = fetchParentAddress(CalleeAddress);
+ const uint64_t HotAddress = ParentAddress ? ParentAddress : CalleeAddress;
+
+ const BinaryFunction *ParentBF = BC.getBinaryFunctionAtAddress(HotAddress);
+
+ const uint32_t InputOffset =
+ translate(CalleeAddress, OutputOffset, /*IsBranchSrc*/ false) + Offset;
+
+ unsigned SecondaryEntryId{0};
+ if (InputOffset)
+ SecondaryEntryId = getSecondaryEntryPointId(HotAddress, InputOffset);
+
+ return std::pair(ParentBF, SecondaryEntryId);
}
} // namespace bolt
diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp
index 05099aa25ce22738687c0e3106e63302fadc0540..0b2a4e86561f3aaa56a7385bf4fd8e5439f2d56a 100644
--- a/bolt/lib/Profile/DataAggregator.cpp
+++ b/bolt/lib/Profile/DataAggregator.cpp
@@ -604,8 +604,6 @@ Error DataAggregator::readProfile(BinaryContext &BC) {
// BAT YAML is handled by DataAggregator since normal YAML output requires
// CFG which is not available in BAT mode.
if (usesBAT()) {
- // Postprocess split function profile for BAT
- fixupBATProfile(BC);
if (opts::ProfileFormat == opts::ProfileFormatKind::PF_YAML)
if (std::error_code EC = writeBATYAML(BC, opts::OutputFilename))
report_error("cannot create output data file", EC);
@@ -664,18 +662,19 @@ DataAggregator::getBinaryFunctionContainingAddress(uint64_t Address) const {
/*UseMaxSize=*/true);
}
-StringRef DataAggregator::getLocationName(BinaryFunction &Func,
- uint64_t Count) {
+BinaryFunction *
+DataAggregator::getBATParentFunction(const BinaryFunction &Func) const {
+ if (BAT)
+ if (const uint64_t HotAddr = BAT->fetchParentAddress(Func.getAddress()))
+ return getBinaryFunctionContainingAddress(HotAddr);
+ return nullptr;
+}
+
+StringRef DataAggregator::getLocationName(const BinaryFunction &Func) const {
if (!BAT)
return Func.getOneName();
const BinaryFunction *OrigFunc = &Func;
- if (const uint64_t HotAddr = BAT->fetchParentAddress(Func.getAddress())) {
- NumColdSamples += Count;
- BinaryFunction *HotFunc = getBinaryFunctionContainingAddress(HotAddr);
- if (HotFunc)
- OrigFunc = HotFunc;
- }
// If it is a local function, prefer the name containing the file name where
// the local function was declared
for (StringRef AlternativeName : OrigFunc->getNames()) {
@@ -690,12 +689,17 @@ StringRef DataAggregator::getLocationName(BinaryFunction &Func,
return OrigFunc->getOneName();
}
-bool DataAggregator::doSample(BinaryFunction &Func, uint64_t Address,
+bool DataAggregator::doSample(BinaryFunction &OrigFunc, uint64_t Address,
uint64_t Count) {
+ BinaryFunction *ParentFunc = getBATParentFunction(OrigFunc);
+ BinaryFunction &Func = ParentFunc ? *ParentFunc : OrigFunc;
+ if (ParentFunc)
+ NumColdSamples += Count;
+
auto I = NamesToSamples.find(Func.getOneName());
if (I == NamesToSamples.end()) {
bool Success;
- StringRef LocName = getLocationName(Func, Count);
+ StringRef LocName = getLocationName(Func);
std::tie(I, Success) = NamesToSamples.insert(
std::make_pair(Func.getOneName(),
FuncSampleData(LocName, FuncSampleData::ContainerTy())));
@@ -715,22 +719,12 @@ bool DataAggregator::doIntraBranch(BinaryFunction &Func, uint64_t From,
FuncBranchData *AggrData = getBranchData(Func);
if (!AggrData) {
AggrData = &NamesToBranches[Func.getOneName()];
- AggrData->Name = getLocationName(Func, Count);
+ AggrData->Name = getLocationName(Func);
setBranchData(Func, AggrData);
}
- From -= Func.getAddress();
- To -= Func.getAddress();
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: bumpBranchCount: "
<< formatv("{0} @ {1:x} -> {0} @ {2:x}\n", Func, From, To));
- if (BAT) {
- From = BAT->translate(Func.getAddress(), From, /*IsBranchSrc=*/true);
- To = BAT->translate(Func.getAddress(), To, /*IsBranchSrc=*/false);
- LLVM_DEBUG(
- dbgs() << "BOLT-DEBUG: BAT translation on bumpBranchCount: "
- << formatv("{0} @ {1:x} -> {0} @ {2:x}\n", Func, From, To));
- }
-
AggrData->bumpBranchCount(From, To, Count, Mispreds);
return true;
}
@@ -744,30 +738,24 @@ bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,
StringRef SrcFunc;
StringRef DstFunc;
if (FromFunc) {
- SrcFunc = getLocationName(*FromFunc, Count);
+ SrcFunc = getLocationName(*FromFunc);
FromAggrData = getBranchData(*FromFunc);
if (!FromAggrData) {
FromAggrData = &NamesToBranches[FromFunc->getOneName()];
FromAggrData->Name = SrcFunc;
setBranchData(*FromFunc, FromAggrData);
}
- From -= FromFunc->getAddress();
- if (BAT)
- From = BAT->translate(FromFunc->getAddress(), From, /*IsBranchSrc=*/true);
recordExit(*FromFunc, From, Mispreds, Count);
}
if (ToFunc) {
- DstFunc = getLocationName(*ToFunc, 0);
+ DstFunc = getLocationName(*ToFunc);
ToAggrData = getBranchData(*ToFunc);
if (!ToAggrData) {
ToAggrData = &NamesToBranches[ToFunc->getOneName()];
ToAggrData->Name = DstFunc;
setBranchData(*ToFunc, ToAggrData);
}
- To -= ToFunc->getAddress();
- if (BAT)
- To = BAT->translate(ToFunc->getAddress(), To, /*IsBranchSrc=*/false);
recordEntry(*ToFunc, To, Mispreds, Count);
}
@@ -783,15 +771,32 @@ bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,
bool DataAggregator::doBranch(uint64_t From, uint64_t To, uint64_t Count,
uint64_t Mispreds) {
- BinaryFunction *FromFunc = getBinaryFunctionContainingAddress(From);
- BinaryFunction *ToFunc = getBinaryFunctionContainingAddress(To);
+ auto handleAddress = [&](uint64_t &Addr, bool IsFrom) -> BinaryFunction * {
+ if (BinaryFunction *Func = getBinaryFunctionContainingAddress(Addr)) {
+ Addr -= Func->getAddress();
+
+ if (BAT)
+ Addr = BAT->translate(Func->getAddress(), Addr, IsFrom);
+
+ if (BinaryFunction *ParentFunc = getBATParentFunction(*Func)) {
+ Func = ParentFunc;
+ if (IsFrom)
+ NumColdSamples += Count;
+ }
+
+ return Func;
+ }
+ return nullptr;
+ };
+
+ BinaryFunction *FromFunc = handleAddress(From, /*IsFrom=*/true);
+ BinaryFunction *ToFunc = handleAddress(To, /*IsFrom=*/false);
if (!FromFunc && !ToFunc)
return false;
// Treat recursive control transfers as inter-branches.
- if (FromFunc == ToFunc && (To != ToFunc->getAddress())) {
- recordBranch(*FromFunc, From - FromFunc->getAddress(),
- To - FromFunc->getAddress(), Count, Mispreds);
+ if (FromFunc == ToFunc && To != 0) {
+ recordBranch(*FromFunc, From, To, Count, Mispreds);
return doIntraBranch(*FromFunc, From, To, Count, Mispreds);
}
@@ -842,9 +847,14 @@ bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second,
<< FromFunc->getPrintName() << ":"
<< Twine::utohexstr(First.To) << " to "
<< Twine::utohexstr(Second.From) << ".\n");
- for (const std::pair &Pair : *FTs)
- doIntraBranch(*FromFunc, Pair.first + FromFunc->getAddress(),
- Pair.second + FromFunc->getAddress(), Count, false);
+ BinaryFunction *ParentFunc = getBATParentFunction(*FromFunc);
+ for (auto [From, To] : *FTs) {
+ if (BAT) {
+ From = BAT->translate(FromFunc->getAddress(), From, /*IsBranchSrc=*/true);
+ To = BAT->translate(FromFunc->getAddress(), To, /*IsBranchSrc=*/false);
+ }
+ doIntraBranch(ParentFunc ? *ParentFunc : *FromFunc, From, To, Count, false);
+ }
return true;
}
@@ -2273,29 +2283,6 @@ DataAggregator::writeAggregatedFile(StringRef OutputFilename) const {
return std::error_code();
}
-void DataAggregator::fixupBATProfile(BinaryContext &BC) {
- for (auto &[FuncName, Branches] : NamesToBranches) {
- BinaryData *BD = BC.getBinaryDataByName(FuncName);
- assert(BD);
- uint64_t FuncAddress = BD->getAddress();
- if (!BAT->isBATFunction(FuncAddress))
- continue;
- // Filter out cold fragments
- if (!BD->getSectionName().equals(BC.getMainCodeSectionName()))
- continue;
- // Convert inter-branches between hot and cold fragments into
- // intra-branches.
- for (auto &[OffsetFrom, CallToMap] : Branches.InterIndex) {
- for (auto &[CallToLoc, CallToIdx] : CallToMap) {
- if (CallToLoc.Name != FuncName)
- continue;
- Branches.IntraIndex[OffsetFrom][CallToLoc.Offset] = CallToIdx;
- Branches.InterIndex[OffsetFrom].erase(CallToLoc);
- }
- }
- }
-}
-
std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,
StringRef OutputFilename) const {
std::error_code EC;
@@ -2333,7 +2320,7 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,
if (BAT->isBATFunction(Function.getAddress()))
continue;
BP.Functions.emplace_back(
- YAMLProfileWriter::convert(Function, /*UseDFS=*/false));
+ YAMLProfileWriter::convert(Function, /*UseDFS=*/false, BAT));
}
for (const auto &KV : NamesToBranches) {
@@ -2345,9 +2332,6 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,
uint64_t FuncAddress = BD->getAddress();
if (!BAT->isBATFunction(FuncAddress))
continue;
- // Filter out cold fragments
- if (!BD->getSectionName().equals(BC.getMainCodeSectionName()))
- continue;
BinaryFunction *BF = BC.getBinaryFunctionAtAddress(FuncAddress);
assert(BF);
YamlBF.Name = FuncName.str();
@@ -2357,87 +2341,68 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,
YamlBF.NumBasicBlocks = BAT->getNumBasicBlocks(FuncAddress);
const BoltAddressTranslation::BBHashMapTy &BlockMap =
BAT->getBBHashMap(FuncAddress);
+ YamlBF.Blocks.resize(YamlBF.NumBasicBlocks);
- auto addSuccProfile = [&](yaml::bolt::BinaryBasicBlockProfile &YamlBB,
- uint64_t SuccOffset, unsigned SuccDataIdx) {
+ for (auto &&[Idx, YamlBB] : llvm::enumerate(YamlBF.Blocks))
+ YamlBB.Index = Idx;
+
+ for (auto BI = BlockMap.begin(), BE = BlockMap.end(); BI != BE; ++BI)
+ YamlBF.Blocks[BI->second.getBBIndex()].Hash = BI->second.getBBHash();
+
+ auto getSuccessorInfo = [&](uint32_t SuccOffset, unsigned SuccDataIdx) {
const llvm::bolt::BranchInfo &BI = Branches.Data.at(SuccDataIdx);
yaml::bolt::SuccessorInfo SI;
SI.Index = BlockMap.getBBIndex(SuccOffset);
SI.Count = BI.Branches;
SI.Mispreds = BI.Mispreds;
- YamlBB.Successors.emplace_back(SI);
+ return SI;
};
- std::unordered_map> BFBranches =
- BAT->getBFBranches(FuncAddress);
-
- auto addCallsProfile = [&](yaml::bolt::BinaryBasicBlockProfile &YamlBB,
- uint64_t Offset) {
- // Iterate over BRANCHENTRY records in the current block
- for (uint32_t BranchOffset : BFBranches[Offset]) {
- if (!Branches.InterIndex.contains(BranchOffset))
- continue;
- for (const auto &[CallToLoc, CallToIdx] :
- Branches.InterIndex.at(BranchOffset)) {
- const llvm::bolt::BranchInfo &BI = Branches.Data.at(CallToIdx);
- yaml::bolt::CallSiteInfo YamlCSI;
- YamlCSI.DestId = 0; // designated for unknown functions
- YamlCSI.EntryDiscriminator = 0;
- YamlCSI.Count = BI.Branches;
- YamlCSI.Mispreds = BI.Mispreds;
- YamlCSI.Offset = BranchOffset - Offset;
- BinaryData *CallTargetBD = BC.getBinaryDataByName(CallToLoc.Name);
- if (!CallTargetBD) {
- YamlBB.CallSites.emplace_back(YamlCSI);
- continue;
- }
- uint64_t CallTargetAddress = CallTargetBD->getAddress();
- BinaryFunction *CallTargetBF =
- BC.getBinaryFunctionAtAddress(CallTargetAddress);
- if (!CallTargetBF) {
- YamlBB.CallSites.emplace_back(YamlCSI);
- continue;
- }
- // Calls between hot and cold fragments must be handled in
- // fixupBATProfile.
- assert(CallTargetBF != BF && "invalid CallTargetBF");
- YamlCSI.DestId = CallTargetBF->getFunctionNumber();
- if (CallToLoc.Offset) {
- if (BAT->isBATFunction(CallTargetAddress)) {
- LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Unsupported secondary "
- "entry point in BAT function "
- << CallToLoc.Name << '\n');
- } else if (const BinaryBasicBlock *CallTargetBB =
- CallTargetBF->getBasicBlockAtOffset(
- CallToLoc.Offset)) {
- // Only record true call information, ignoring returns (normally
- // won't have a target basic block) and jumps to the landing
- // pads (not an entry point).
- if (CallTargetBB->isEntryPoint()) {
- YamlCSI.EntryDiscriminator =
- CallTargetBF->getEntryIDForSymbol(
- CallTargetBB->getLabel());
- }
- }
- }
- YamlBB.CallSites.emplace_back(YamlCSI);
- }
- }
+ auto getCallSiteInfo = [&](Location CallToLoc, unsigned CallToIdx,
+ uint32_t Offset) {
+ const llvm::bolt::BranchInfo &BI = Branches.Data.at(CallToIdx);
+ yaml::bolt::CallSiteInfo CSI;
+ CSI.DestId = 0; // designated for unknown functions
+ CSI.EntryDiscriminator = 0;
+ CSI.Count = BI.Branches;
+ CSI.Mispreds = BI.Mispreds;
+ CSI.Offset = Offset;
+ if (BinaryData *BD = BC.getBinaryDataByName(CallToLoc.Name))
+ YAMLProfileWriter::setCSIDestination(BC, CSI, BD->getSymbol(), BAT,
+ CallToLoc.Offset);
+ return CSI;
};
for (const auto &[FromOffset, SuccKV] : Branches.IntraIndex) {
- yaml::bolt::BinaryBasicBlockProfile YamlBB;
if (!BlockMap.isInputBlock(FromOffset))
continue;
- YamlBB.Index = BlockMap.getBBIndex(FromOffset);
- YamlBB.Hash = BlockMap.getBBHash(FromOffset);
+ const unsigned Index = BlockMap.getBBIndex(FromOffset);
+ yaml::bolt::BinaryBasicBlockProfile &YamlBB = YamlBF.Blocks[Index];
for (const auto &[SuccOffset, SuccDataIdx] : SuccKV)
- addSuccProfile(YamlBB, SuccOffset, SuccDataIdx);
- addCallsProfile(YamlBB, FromOffset);
- if (YamlBB.ExecCount || !YamlBB.Successors.empty() ||
- !YamlBB.CallSites.empty())
- YamlBF.Blocks.emplace_back(YamlBB);
+ if (BlockMap.isInputBlock(SuccOffset))
+ YamlBB.Successors.emplace_back(
+ getSuccessorInfo(SuccOffset, SuccDataIdx));
+ }
+ for (const auto &[FromOffset, CallTo] : Branches.InterIndex) {
+ auto BlockIt = BlockMap.upper_bound(FromOffset);
+ --BlockIt;
+ const unsigned BlockOffset = BlockIt->first;
+ const unsigned BlockIndex = BlockIt->second.getBBIndex();
+ yaml::bolt::BinaryBasicBlockProfile &YamlBB = YamlBF.Blocks[BlockIndex];
+ const uint32_t Offset = FromOffset - BlockOffset;
+ for (const auto &[CallToLoc, CallToIdx] : CallTo)
+ YamlBB.CallSites.emplace_back(
+ getCallSiteInfo(CallToLoc, CallToIdx, Offset));
+ llvm::sort(YamlBB.CallSites, [](yaml::bolt::CallSiteInfo &A,
+ yaml::bolt::CallSiteInfo &B) {
+ return A.Offset < B.Offset;
+ });
}
+ // Drop blocks without a hash, won't be useful for stale matching.
+ llvm::erase_if(YamlBF.Blocks,
+ [](const yaml::bolt::BinaryBasicBlockProfile &YamlBB) {
+ return YamlBB.Hash == (yaml::Hex64)0;
+ });
BP.Functions.emplace_back(YamlBF);
}
}
diff --git a/bolt/lib/Profile/YAMLProfileWriter.cpp b/bolt/lib/Profile/YAMLProfileWriter.cpp
index 0f082086c1fc24810d97e2a078c6648eda753585..ef04ba0d21ad75cf29421c24922738e9f0cba1d9 100644
--- a/bolt/lib/Profile/YAMLProfileWriter.cpp
+++ b/bolt/lib/Profile/YAMLProfileWriter.cpp
@@ -9,6 +9,7 @@
#include "bolt/Profile/YAMLProfileWriter.h"
#include "bolt/Core/BinaryBasicBlock.h"
#include "bolt/Core/BinaryFunction.h"
+#include "bolt/Profile/BoltAddressTranslation.h"
#include "bolt/Profile/ProfileReaderBase.h"
#include "bolt/Rewrite/RewriteInstance.h"
#include "llvm/Support/CommandLine.h"
@@ -25,17 +26,19 @@ extern llvm::cl::opt ProfileUseDFS;
namespace llvm {
namespace bolt {
-/// Set CallSiteInfo destination fields from \p Symbol and return a target
-/// BinaryFunction for that symbol.
-static const BinaryFunction *setCSIDestination(const BinaryContext &BC,
- yaml::bolt::CallSiteInfo &CSI,
- const MCSymbol *Symbol) {
+const BinaryFunction *YAMLProfileWriter::setCSIDestination(
+ const BinaryContext &BC, yaml::bolt::CallSiteInfo &CSI,
+ const MCSymbol *Symbol, const BoltAddressTranslation *BAT,
+ uint32_t Offset) {
CSI.DestId = 0; // designated for unknown functions
CSI.EntryDiscriminator = 0;
+
if (Symbol) {
uint64_t EntryID = 0;
- if (const BinaryFunction *const Callee =
+ if (const BinaryFunction *Callee =
BC.getFunctionForSymbol(Symbol, &EntryID)) {
+ if (BAT && BAT->isBATFunction(Callee->getAddress()))
+ std::tie(Callee, EntryID) = BAT->translateSymbol(BC, *Symbol, Offset);
CSI.DestId = Callee->getFunctionNumber();
CSI.EntryDiscriminator = EntryID;
return Callee;
@@ -45,7 +48,8 @@ static const BinaryFunction *setCSIDestination(const BinaryContext &BC,
}
yaml::bolt::BinaryFunctionProfile
-YAMLProfileWriter::convert(const BinaryFunction &BF, bool UseDFS) {
+YAMLProfileWriter::convert(const BinaryFunction &BF, bool UseDFS,
+ const BoltAddressTranslation *BAT) {
yaml::bolt::BinaryFunctionProfile YamlBF;
const BinaryContext &BC = BF.getBinaryContext();
@@ -98,7 +102,8 @@ YAMLProfileWriter::convert(const BinaryFunction &BF, bool UseDFS) {
continue;
for (const IndirectCallProfile &CSP : ICSP.get()) {
StringRef TargetName = "";
- const BinaryFunction *Callee = setCSIDestination(BC, CSI, CSP.Symbol);
+ const BinaryFunction *Callee =
+ setCSIDestination(BC, CSI, CSP.Symbol, BAT);
if (Callee)
TargetName = Callee->getOneName();
CSI.Count = CSP.Count;
@@ -109,7 +114,7 @@ YAMLProfileWriter::convert(const BinaryFunction &BF, bool UseDFS) {
StringRef TargetName = "";
const MCSymbol *CalleeSymbol = BC.MIB->getTargetSymbol(Instr);
const BinaryFunction *const Callee =
- setCSIDestination(BC, CSI, CalleeSymbol);
+ setCSIDestination(BC, CSI, CalleeSymbol, BAT);
if (Callee)
TargetName = Callee->getOneName();
diff --git a/bolt/lib/Rewrite/BinaryPassManager.cpp b/bolt/lib/Rewrite/BinaryPassManager.cpp
index 6c26bb7957269d71c06da0d33bfe80984b0f4e16..be4888ccfa56457fe2f59dcee91abdd05b947021 100644
--- a/bolt/lib/Rewrite/BinaryPassManager.cpp
+++ b/bolt/lib/Rewrite/BinaryPassManager.cpp
@@ -377,8 +377,9 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
Manager.registerPass(std::make_unique(PrintNormalized));
- Manager.registerPass(std::make_unique(NeverPrint),
- opts::StripRepRet);
+ if (BC.isX86())
+ Manager.registerPass(std::make_unique(NeverPrint),
+ opts::StripRepRet);
Manager.registerPass(std::make_unique(PrintICF),
opts::ICF);
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index 0c8ee0d417233bb5617cb7a9fc2ddfa59a3dd710..f22bede002da58cb268015bfae7f5c146c18da16 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -556,7 +556,7 @@ Error RewriteInstance::discoverStorage() {
if (Error E = SectionNameOrErr.takeError())
return E;
StringRef SectionName = SectionNameOrErr.get();
- if (SectionName == ".text") {
+ if (SectionName == BC->getMainCodeSectionName()) {
BC->OldTextSectionAddress = Section.getAddress();
BC->OldTextSectionSize = Section.getSize();
@@ -1864,7 +1864,8 @@ Error RewriteInstance::readSpecialSections() {
"Use -update-debug-sections to keep it.\n";
}
- HasTextRelocations = (bool)BC->getUniqueSectionByName(".rela.text");
+ HasTextRelocations = (bool)BC->getUniqueSectionByName(
+ ".rela" + std::string(BC->getMainCodeSectionName()));
HasSymbolTable = (bool)BC->getUniqueSectionByName(".symtab");
EHFrameSection = BC->getUniqueSectionByName(".eh_frame");
BuildIDSection = BC->getUniqueSectionByName(".note.gnu.build-id");
@@ -2305,9 +2306,13 @@ void RewriteInstance::processRelocations() {
return;
for (const SectionRef &Section : InputFile->sections()) {
- if (cantFail(Section.getRelocatedSection()) != InputFile->section_end() &&
- !BinarySection(*BC, Section).isAllocatable())
- readRelocations(Section);
+ section_iterator SecIter = cantFail(Section.getRelocatedSection());
+ if (SecIter == InputFile->section_end())
+ continue;
+ if (BinarySection(*BC, Section).isAllocatable())
+ continue;
+
+ readRelocations(Section);
}
if (NumFailedRelocations)
@@ -3441,7 +3446,8 @@ void RewriteInstance::emitAndLink() {
ErrorOr TextSection =
BC->getUniqueSectionByName(BC->getMainCodeSectionName());
if (BC->HasRelocations && TextSection)
- BC->renameSection(*TextSection, getOrgSecPrefix() + ".text");
+ BC->renameSection(*TextSection,
+ getOrgSecPrefix() + BC->getMainCodeSectionName());
//////////////////////////////////////////////////////////////////////////////
// Assign addresses to new sections.
@@ -4298,7 +4304,7 @@ RewriteInstance::getOutputSections(ELFObjectFile *File,
for (auto &SectionKV : OutputSections) {
ELFShdrTy &Section = SectionKV.second;
- // Ignore TLS sections as they don't take any space in the file.
+ // Ignore NOBITS sections as they don't take any space in the file.
if (Section.sh_type == ELF::SHT_NOBITS)
continue;
@@ -4306,10 +4312,9 @@ RewriteInstance::getOutputSections(ELFObjectFile *File,
// placed in different loadable segments.
if (PrevSection &&
PrevSection->sh_offset + PrevSection->sh_size > Section.sh_offset) {
- if (opts::Verbosity > 1) {
+ if (opts::Verbosity > 1)
BC->outs() << "BOLT-INFO: adjusting size for section "
<< PrevBinSec->getOutputName() << '\n';
- }
PrevSection->sh_size = Section.sh_offset - PrevSection->sh_offset;
}
@@ -4417,6 +4422,7 @@ void RewriteInstance::patchELFSectionHeaderTable(ELFObjectFile *File) {
raw_fd_ostream &OS = Out->os();
const ELFFile &Obj = File->getELFFile();
+ // Mapping from old section indices to new ones
std::vector NewSectionIndex;
std::vector OutputSections =
getOutputSections(File, NewSectionIndex);
@@ -4434,10 +4440,8 @@ void RewriteInstance::patchELFSectionHeaderTable(ELFObjectFile *File) {
// Write all section header entries while patching section references.
for (ELFShdrTy &Section : OutputSections) {
Section.sh_link = NewSectionIndex[Section.sh_link];
- if (Section.sh_type == ELF::SHT_REL || Section.sh_type == ELF::SHT_RELA) {
- if (Section.sh_info)
- Section.sh_info = NewSectionIndex[Section.sh_info];
- }
+ if (Section.sh_type == ELF::SHT_REL || Section.sh_type == ELF::SHT_RELA)
+ Section.sh_info = NewSectionIndex[Section.sh_info];
OS.write(reinterpret_cast(&Section), sizeof(Section));
}
diff --git a/bolt/test/X86/bolt-address-translation-yaml.test b/bolt/test/X86/bolt-address-translation-yaml.test
index 7fdf7709a8b9da3d2f103ec88c9b8ad1191893e7..af24c3d84a0f15f9f8d68eabf7b42daac3e39e69 100644
--- a/bolt/test/X86/bolt-address-translation-yaml.test
+++ b/bolt/test/X86/bolt-address-translation-yaml.test
@@ -36,9 +36,14 @@ YAML-BAT-CHECK-NEXT: - bid: 0
YAML-BAT-CHECK-NEXT: insns: 26
YAML-BAT-CHECK-NEXT: hash: 0xA900AE79CFD40000
YAML-BAT-CHECK-NEXT: succ: [ { bid: 3, cnt: 0 }, { bid: 1, cnt: 0 } ]
+# Calls from no-BAT to BAT function
+YAML-BAT-CHECK: - bid: 28
+YAML-BAT-CHECK-NEXT: insns: 13
+YAML-BAT-CHECK-NEXT: hash: 0xB2F04C1F25F00400
+YAML-BAT-CHECK-NEXT: calls: [ { off: 0x21, fid: [[#SOLVECUBIC:]], cnt: 25 }, { off: 0x2D, fid: [[#]], cnt: 9 } ]
# Function covered by BAT with calls
YAML-BAT-CHECK: - name: SolveCubic
-YAML-BAT-CHECK-NEXT: fid: [[#]]
+YAML-BAT-CHECK-NEXT: fid: [[#SOLVECUBIC]]
YAML-BAT-CHECK-NEXT: hash: 0x6AF7E61EA3966722
YAML-BAT-CHECK-NEXT: exec: 25
YAML-BAT-CHECK-NEXT: nblocks: 15
diff --git a/bolt/test/X86/bolt-address-translation.test b/bolt/test/X86/bolt-address-translation.test
index 63234b4c1d21851f447fa804669e780774a6e9c8..e6b21c14077b454e9d3e1719da8dc3c2f896b322 100644
--- a/bolt/test/X86/bolt-address-translation.test
+++ b/bolt/test/X86/bolt-address-translation.test
@@ -37,7 +37,7 @@
# CHECK: BOLT: 3 out of 7 functions were overwritten.
# CHECK: BOLT-INFO: Wrote 6 BAT maps
# CHECK: BOLT-INFO: Wrote 3 function and 58 basic block hashes
-# CHECK: BOLT-INFO: BAT section size (bytes): 924
+# CHECK: BOLT-INFO: BAT section size (bytes): 928
#
# usqrt mappings (hot part). We match against any key (left side containing
# the bolted binary offsets) because BOLT may change where it puts instructions
diff --git a/bolt/test/X86/patch-entries.test b/bolt/test/X86/patch-entries.test
index 54f358f273e793c30da84fe3a6134e77d4cb4759..4a725412dd616adeb760df155837c17b003213e0 100644
--- a/bolt/test/X86/patch-entries.test
+++ b/bolt/test/X86/patch-entries.test
@@ -7,4 +7,25 @@ REQUIRES: system-linux
RUN: %clang %cflags -no-pie -g %p/Inputs/patch-entries.c -fuse-ld=lld -o %t.exe \
RUN: -Wl,-q -I%p/../Inputs
-RUN: llvm-bolt -relocs %t.exe -o %t.out --update-debug-sections --force-patch
+RUN: llvm-bolt -relocs %t.exe -o %t.out --update-debug-sections --force-patch \
+RUN: --enable-bat
+
+# Check that patched functions can be disassembled (override FDE from the
+# original function)
+# PREAGG: B X:0 #foo.org.0# 1 0
+RUN: link_fdata %s %t.out %t.preagg PREAGG
+RUN: perf2bolt %t.out -p %t.preagg --pa -o %t.yaml --profile-format=yaml \
+RUN: -print-disasm -print-only=foo.org.0/1 2>&1 | FileCheck %s
+CHECK-NOT: BOLT-WARNING: sizes differ for function foo.org.0/1
+CHECK: Binary Function "foo.org.0/1(*2)" after disassembly {
+
+# Check the expected eh_frame contents
+RUN: llvm-nm --print-size %t.out > %t.foo
+RUN: llvm-objdump %t.out --dwarf=frames >> %t.foo
+RUN: FileCheck %s --input-file %t.foo --check-prefix=CHECK-FOO
+CHECK-FOO: 0000000000[[#%x,FOO:]] [[#%x,OPTSIZE:]] t foo
+CHECK-FOO: 0000000000[[#%x,ORG:]] [[#%x,ORGSIZE:]] t foo.org.0
+# patched FDE comes first
+CHECK-FOO: FDE {{.*}} pc=00[[#%x,ORG]]...00[[#%x,ORG+ORGSIZE]]
+# original FDE comes second
+CHECK-FOO: FDE {{.*}} pc=00[[#%x,ORG]]...00[[#%x,ORG+OPTSIZE]]
diff --git a/bolt/test/X86/yaml-secondary-entry-discriminator.s b/bolt/test/X86/yaml-secondary-entry-discriminator.s
index 43c2e2a7f05549289293a39808762cc5a68cc285..5d6e291fd7c22a79594afa6b5daa7d828bacc082 100644
--- a/bolt/test/X86/yaml-secondary-entry-discriminator.s
+++ b/bolt/test/X86/yaml-secondary-entry-discriminator.s
@@ -1,5 +1,5 @@
-# This reproduces a bug with BOLT setting incorrect discriminator for
-# secondary entry points in YAML profile.
+## This reproduces a bug with BOLT setting incorrect discriminator for
+## secondary entry points in YAML profile.
# REQUIRES: system-linux
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
@@ -11,20 +11,20 @@
# RUN: FileCheck %s -input-file %t.yaml
# CHECK: - name: main
# CHECK-NEXT: fid: 2
-# CHECK-NEXT: hash: 0xADF270D550151185
+# CHECK-NEXT: hash: {{.*}}
# CHECK-NEXT: exec: 0
# CHECK-NEXT: nblocks: 4
# CHECK-NEXT: blocks:
# CHECK: - bid: 1
# CHECK-NEXT: insns: 1
-# CHECK-NEXT: hash: 0x36A303CBA4360014
+# CHECK-NEXT: hash: {{.*}}
# CHECK-NEXT: calls: [ { off: 0x0, fid: 1, disc: 1, cnt: 1 } ]
# CHECK: - bid: 2
# CHECK-NEXT: insns: 5
-# CHECK-NEXT: hash: 0x8B2F5747CD0019
+# CHECK-NEXT: hash: {{.*}}
# CHECK-NEXT: calls: [ { off: 0x0, fid: 1, disc: 1, cnt: 1, mis: 1 } ]
-# Make sure that the profile is attached correctly
+## Make sure that the profile is attached correctly
# RUN: llvm-bolt %t.exe -o %t.out --data %t.yaml --print-profile \
# RUN: --print-only=main | FileCheck %s --check-prefix=CHECK-CFG
@@ -33,15 +33,80 @@
# CHECK-CFG: callq *%rax # Offset: [[#]] # CallProfile: 1 (1 misses) :
# CHECK-CFG-NEXT: { secondary_entry: 1 (1 misses) }
+## YAML BAT test of calling BAT secondary entry from non-BAT function
+## Now force-split func and skip main (making it call secondary entries)
+# RUN: llvm-bolt %t.exe -o %t.bat --data %t.fdata --funcs=func \
+# RUN: --split-functions --split-strategy=all --split-all-cold --enable-bat
+
+## Prepare pre-aggregated profile using %t.bat
+# RUN: link_fdata %s %t.bat %t.preagg PREAGG
+## Strip labels used for pre-aggregated profile
+# RUN: llvm-strip -NLcall -NLindcall %t.bat
+
+## Convert pre-aggregated profile using BAT
+# RUN: perf2bolt %t.bat -p %t.preagg --pa -o %t.bat.fdata -w %t.bat.yaml
+
+## Convert BAT fdata into YAML
+# RUN: llvm-bolt %t.exe -data %t.bat.fdata -w %t.bat.fdata-yaml -o /dev/null
+
+## Check fdata YAML - make sure that a direct call has discriminator field
+# RUN: FileCheck %s --input-file %t.bat.fdata-yaml -check-prefix CHECK-BAT-YAML
+
+## Check BAT YAML - make sure that a direct call has discriminator field
+# RUN: FileCheck %s --input-file %t.bat.yaml --check-prefix CHECK-BAT-YAML
+
+## YAML BAT test of calling BAT secondary entry from BAT function
+# RUN: llvm-bolt %t.exe -o %t.bat2 --data %t.fdata --funcs=main,func \
+# RUN: --split-functions --split-strategy=all --split-all-cold --enable-bat
+
+## Prepare pre-aggregated profile using %t.bat
+# RUN: link_fdata %s %t.bat2 %t.preagg2 PREAGG2
+
+## Strip labels used for pre-aggregated profile
+# RUN: llvm-strip -NLcall -NLindcall %t.bat2
+
+## Convert pre-aggregated profile using BAT
+# RUN: perf2bolt %t.bat2 -p %t.preagg2 --pa -o %t.bat2.fdata -w %t.bat2.yaml
+
+## Convert BAT fdata into YAML
+# RUN: llvm-bolt %t.exe -data %t.bat2.fdata -w %t.bat2.fdata-yaml -o /dev/null
+
+## Check fdata YAML - make sure that a direct call has discriminator field
+# RUN: FileCheck %s --input-file %t.bat2.fdata-yaml -check-prefix CHECK-BAT-YAML
+
+## Check BAT YAML - make sure that a direct call has discriminator field
+# RUN: FileCheck %s --input-file %t.bat2.yaml --check-prefix CHECK-BAT-YAML
+
+# CHECK-BAT-YAML: - name: main
+# CHECK-BAT-YAML-NEXT: fid: [[#]]
+# CHECK-BAT-YAML-NEXT: hash: 0xADF270D550151185
+# CHECK-BAT-YAML-NEXT: exec: 0
+# CHECK-BAT-YAML-NEXT: nblocks: 4
+# CHECK-BAT-YAML-NEXT: blocks:
+# CHECK-BAT-YAML: - bid: 1
+# CHECK-BAT-YAML-NEXT: insns: [[#]]
+# CHECK-BAT-YAML-NEXT: hash: 0x36A303CBA4360018
+# CHECK-BAT-YAML-NEXT: calls: [ { off: 0x0, fid: [[#]], disc: 1, cnt: 1
+
.globl func
.type func, @function
func:
# FDATA: 0 [unknown] 0 1 func 0 1 0
+# PREAGG: B X:0 #func# 1 1
+# PREAGG2: B X:0 #func# 1 1
.cfi_startproc
pushq %rbp
movq %rsp, %rbp
+## Placeholder code to make splitting profitable
+.rept 5
+ testq %rax, %rax
+.endr
.globl secondary_entry
secondary_entry:
+## Placeholder code to make splitting profitable
+.rept 5
+ testq %rax, %rax
+.endr
popq %rbp
retq
nopl (%rax)
@@ -58,17 +123,23 @@ main:
movl $0, -4(%rbp)
testq %rax, %rax
jne Lindcall
+.globl Lcall
Lcall:
call secondary_entry
# FDATA: 1 main #Lcall# 1 secondary_entry 0 1 1
+# PREAGG: B #Lcall# #secondary_entry# 1 1
+# PREAGG2: B #main.cold.0# #func.cold.0# 1 1
+.globl Lindcall
Lindcall:
callq *%rax
# FDATA: 1 main #Lindcall# 1 secondary_entry 0 1 1
+# PREAGG: B #Lindcall# #secondary_entry# 1 1
+# PREAGG2: B #main.cold.1# #func.cold.0# 1 1
xorl %eax, %eax
addq $16, %rsp
popq %rbp
retq
-# For relocations against .text
+## For relocations against .text
call exit
.cfi_endproc
.size main, .-main
diff --git a/bolt/test/runtime/X86/jt-confusion.s b/bolt/test/runtime/X86/jt-confusion.s
new file mode 100644
index 0000000000000000000000000000000000000000..f15c83b35b6a44e8e4733c66aad055be9fbe6a79
--- /dev/null
+++ b/bolt/test/runtime/X86/jt-confusion.s
@@ -0,0 +1,164 @@
+# REQUIRES: system-linux
+
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
+# RUN: llvm-strip --strip-unneeded %t.o
+# RUN: %clang %cflags -no-pie -nostartfiles -nostdlib -lc %t.o -o %t.exe -Wl,-q
+
+# RUN: llvm-bolt %t.exe -o %t.exe.bolt --relocs=1 --lite=0
+
+# RUN: %t.exe.bolt
+
+## Check that BOLT's jump table detection diffrentiates between
+## __builtin_unreachable() targets and function pointers.
+
+## The test case was built from the following two source files and
+## modiffied for standalone build. main became _start, etc.
+## $ $(CC) a.c -O1 -S -o a.s
+## $ $(CC) b.c -O0 -S -o b.s
+
+## a.c:
+
+## typedef int (*fptr)(int);
+## void check_fptr(fptr, int);
+##
+## int foo(int a) {
+## check_fptr(foo, 0);
+## switch (a) {
+## default:
+## __builtin_unreachable();
+## case 0:
+## return 3;
+## case 1:
+## return 5;
+## case 2:
+## return 7;
+## case 3:
+## return 11;
+## case 4:
+## return 13;
+## case 5:
+## return 17;
+## }
+## return 0;
+## }
+##
+## int main(int argc) {
+## check_fptr(main, 1);
+## return foo(argc);
+## }
+##
+## const fptr funcs[2] = {foo, main};
+
+## b.c.:
+
+## typedef int (*fptr)(int);
+## extern const fptr funcs[2];
+##
+## #define assert(C) { if (!(C)) (*(unsigned long long *)0) = 0; }
+## void check_fptr(fptr f, int i) {
+## assert(f == funcs[i]);
+## }
+
+
+ .text
+ .globl foo
+ .type foo, @function
+foo:
+.LFB0:
+ .cfi_startproc
+ pushq %rbx
+ .cfi_def_cfa_offset 16
+ .cfi_offset 3, -16
+ movl %edi, %ebx
+ movl $0, %esi
+ movl $foo, %edi
+ call check_fptr
+ movl %ebx, %ebx
+ jmp *.L4(,%rbx,8)
+.L8:
+ movl $5, %eax
+ jmp .L1
+.L7:
+ movl $7, %eax
+ jmp .L1
+.L6:
+ movl $11, %eax
+ jmp .L1
+.L5:
+ movl $13, %eax
+ jmp .L1
+.L3:
+ movl $17, %eax
+ jmp .L1
+.L10:
+ movl $3, %eax
+.L1:
+ popq %rbx
+ .cfi_def_cfa_offset 8
+ ret
+ .cfi_endproc
+.LFE0:
+ .size foo, .-foo
+ .globl _start
+ .type _start, @function
+_start:
+.LFB1:
+ .cfi_startproc
+ pushq %rbx
+ .cfi_def_cfa_offset 16
+ .cfi_offset 3, -16
+ movl %edi, %ebx
+ movl $1, %esi
+ movl $_start, %edi
+ call check_fptr
+ movl $1, %edi
+ call foo
+ popq %rbx
+ .cfi_def_cfa_offset 8
+ callq exit@PLT
+ .cfi_endproc
+.LFE1:
+ .size _start, .-_start
+ .globl check_fptr
+ .type check_fptr, @function
+check_fptr:
+.LFB2:
+ .cfi_startproc
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset 6, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register 6
+ movq %rdi, -8(%rbp)
+ movl %esi, -12(%rbp)
+ movl -12(%rbp), %eax
+ cltq
+ movq funcs(,%rax,8), %rax
+ cmpq %rax, -8(%rbp)
+ je .L33
+ movl $0, %eax
+ movq $0, (%rax)
+.L33:
+ nop
+ popq %rbp
+ .cfi_def_cfa 7, 8
+ ret
+ .cfi_endproc
+
+ .section .rodata
+ .align 8
+ .align 4
+.L4:
+ .quad .L10
+ .quad .L8
+ .quad .L7
+ .quad .L6
+ .quad .L5
+ .quad .L3
+
+ .globl funcs
+ .type funcs, @object
+ .size funcs, 16
+funcs:
+ .quad foo
+ .quad _start
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp
index 87fd8adf997082593a28d680478c06f06fcfd9fc..bbb35228ce47fbb168a5a0c779177d0ae1510058 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp
@@ -9,8 +9,8 @@
#include "MissingStdForwardCheck.h"
#include "../utils/Matchers.h"
#include "clang/AST/ASTContext.h"
-#include "clang/AST/ExprConcepts.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/IdentifierTable.h"
using namespace clang::ast_matchers;
@@ -79,6 +79,11 @@ AST_MATCHER_P(LambdaExpr, hasCaptureDefaultKind, LambdaCaptureDefault, Kind) {
return Node.getCaptureDefault() == Kind;
}
+AST_MATCHER(VarDecl, hasIdentifier) {
+ const IdentifierInfo *ID = Node.getIdentifier();
+ return ID != NULL && !ID->isPlaceholder();
+}
+
} // namespace
void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) {
@@ -125,12 +130,14 @@ void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) {
hasAncestor(expr(hasUnevaluatedContext())))));
Finder->addMatcher(
- parmVarDecl(parmVarDecl().bind("param"), isTemplateTypeParameter(),
- hasAncestor(functionDecl().bind("func")),
- hasAncestor(functionDecl(
- isDefinition(), equalsBoundNode("func"), ToParam,
- unless(anyOf(isDeleted(), hasDescendant(std::move(
- ForwardCallMatcher))))))),
+ parmVarDecl(
+ parmVarDecl().bind("param"), hasIdentifier(),
+ unless(hasAttr(attr::Kind::Unused)), isTemplateTypeParameter(),
+ hasAncestor(functionDecl().bind("func")),
+ hasAncestor(functionDecl(
+ isDefinition(), equalsBoundNode("func"), ToParam,
+ unless(anyOf(isDeleted(),
+ hasDescendant(std::move(ForwardCallMatcher))))))),
this);
}
diff --git a/clang-tools-extra/clang-tidy/hicpp/IgnoredRemoveResultCheck.cpp b/clang-tools-extra/clang-tidy/hicpp/IgnoredRemoveResultCheck.cpp
index 8020f8cd062510b5ac677cf1d73de8d8c38800f1..b1a18485ce168d2bf94528d1c5a6f453537c6e79 100644
--- a/clang-tools-extra/clang-tidy/hicpp/IgnoredRemoveResultCheck.cpp
+++ b/clang-tools-extra/clang-tidy/hicpp/IgnoredRemoveResultCheck.cpp
@@ -14,9 +14,9 @@ IgnoredRemoveResultCheck::IgnoredRemoveResultCheck(llvm::StringRef Name,
ClangTidyContext *Context)
: UnusedReturnValueCheck(Name, Context,
{
- "::std::remove",
- "::std::remove_if",
- "::std::unique",
+ "::std::remove$",
+ "::std::remove_if$",
+ "::std::unique$",
}) {
// The constructor for ClangTidyCheck needs to have been called
// before we can access options via Options.get().
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp
index b299afd540b9a3cf4a71babcc04e8077988ef235..1548fc454cfb3765eb1aaf30ffa5afc8d7001fe5 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp
@@ -29,6 +29,7 @@
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MathExtras.h"
#include
+#include
#include
#include
#include
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidReturnWithVoidValueCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidReturnWithVoidValueCheck.cpp
index e3400f614fa5640784b1d6dee31caade6a5a2c8b..48bca41f4a3b1e87dbd7488617702f17e5f6e15a 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidReturnWithVoidValueCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidReturnWithVoidValueCheck.cpp
@@ -7,19 +7,18 @@
//===----------------------------------------------------------------------===//
#include "AvoidReturnWithVoidValueCheck.h"
-#include "clang/AST/Stmt.h"
-#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/ASTMatchers/ASTMatchers.h"
+#include "../utils/BracesAroundStatement.h"
+#include "../utils/LexerUtils.h"
using namespace clang::ast_matchers;
namespace clang::tidy::readability {
-static constexpr auto IgnoreMacrosName = "IgnoreMacros";
-static constexpr auto IgnoreMacrosDefault = true;
+static constexpr char IgnoreMacrosName[] = "IgnoreMacros";
+static const bool IgnoreMacrosDefault = true;
-static constexpr auto StrictModeName = "StrictMode";
-static constexpr auto StrictModeDefault = true;
+static constexpr char StrictModeName[] = "StrictMode";
+static const bool StrictModeDefault = true;
AvoidReturnWithVoidValueCheck::AvoidReturnWithVoidValueCheck(
StringRef Name, ClangTidyContext *Context)
@@ -32,7 +31,10 @@ void AvoidReturnWithVoidValueCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
returnStmt(
hasReturnValue(allOf(hasType(voidType()), unless(initListExpr()))),
- optionally(hasParent(compoundStmt().bind("compound_parent"))))
+ optionally(hasParent(
+ compoundStmt(
+ optionally(hasParent(functionDecl().bind("function_parent"))))
+ .bind("compound_parent"))))
.bind("void_return"),
this);
}
@@ -42,10 +44,30 @@ void AvoidReturnWithVoidValueCheck::check(
const auto *VoidReturn = Result.Nodes.getNodeAs("void_return");
if (IgnoreMacros && VoidReturn->getBeginLoc().isMacroID())
return;
- if (!StrictMode && !Result.Nodes.getNodeAs("compound_parent"))
+ const auto *SurroundingBlock =
+ Result.Nodes.getNodeAs("compound_parent");
+ if (!StrictMode && !SurroundingBlock)
return;
- diag(VoidReturn->getBeginLoc(), "return statement within a void function "
- "should not have a specified return value");
+ DiagnosticBuilder Diag = diag(VoidReturn->getBeginLoc(),
+ "return statement within a void function "
+ "should not have a specified return value");
+ const SourceLocation SemicolonPos = utils::lexer::findNextTerminator(
+ VoidReturn->getEndLoc(), *Result.SourceManager, getLangOpts());
+ if (SemicolonPos.isInvalid())
+ return;
+ if (!SurroundingBlock) {
+ const auto BraceInsertionHints = utils::getBraceInsertionsHints(
+ VoidReturn, getLangOpts(), *Result.SourceManager,
+ VoidReturn->getBeginLoc());
+ if (BraceInsertionHints)
+ Diag << BraceInsertionHints.openingBraceFixIt()
+ << BraceInsertionHints.closingBraceFixIt();
+ }
+ Diag << FixItHint::CreateRemoval(VoidReturn->getReturnLoc());
+ if (!Result.Nodes.getNodeAs("function_parent") ||
+ SurroundingBlock->body_back() != VoidReturn)
+ Diag << FixItHint::CreateInsertion(SemicolonPos.getLocWithOffset(1),
+ " return;", true);
}
void AvoidReturnWithVoidValueCheck::storeOptions(
diff --git a/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp b/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp
index 81ca33cbbdfb4b5461b54b8a0b5903c0d1186942..85bd9c1e4f9a043074963487e72077c55bc089b3 100644
--- a/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "BracesAroundStatementsCheck.h"
+#include "../utils/BracesAroundStatement.h"
#include "../utils/LexerUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchers.h"
@@ -17,12 +18,10 @@ using namespace clang::ast_matchers;
namespace clang::tidy::readability {
static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
- const ASTContext *Context) {
+ const LangOptions &LangOpts) {
Token Tok;
- SourceLocation Beginning =
- Lexer::GetBeginningOfToken(Loc, SM, Context->getLangOpts());
- const bool Invalid =
- Lexer::getRawToken(Beginning, Tok, SM, Context->getLangOpts());
+ SourceLocation Beginning = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
+ const bool Invalid = Lexer::getRawToken(Beginning, Tok, SM, LangOpts);
assert(!Invalid && "Expected a valid token.");
if (Invalid)
@@ -33,64 +32,21 @@ static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
static SourceLocation
forwardSkipWhitespaceAndComments(SourceLocation Loc, const SourceManager &SM,
- const ASTContext *Context) {
+ const LangOptions &LangOpts) {
assert(Loc.isValid());
for (;;) {
while (isWhitespace(*SM.getCharacterData(Loc)))
Loc = Loc.getLocWithOffset(1);
- tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+ tok::TokenKind TokKind = getTokenKind(Loc, SM, LangOpts);
if (TokKind != tok::comment)
return Loc;
// Fast-forward current token.
- Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+ Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
}
}
-static SourceLocation findEndLocation(const Stmt &S, const SourceManager &SM,
- const ASTContext *Context) {
- SourceLocation Loc =
- utils::lexer::getUnifiedEndLoc(S, SM, Context->getLangOpts());
- if (!Loc.isValid())
- return Loc;
-
- // Start searching right after S.
- Loc = Loc.getLocWithOffset(1);
-
- for (;;) {
- assert(Loc.isValid());
- while (isHorizontalWhitespace(*SM.getCharacterData(Loc))) {
- Loc = Loc.getLocWithOffset(1);
- }
-
- if (isVerticalWhitespace(*SM.getCharacterData(Loc))) {
- // EOL, insert brace before.
- break;
- }
- tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
- if (TokKind != tok::comment) {
- // Non-comment token, insert brace before.
- break;
- }
-
- SourceLocation TokEndLoc =
- Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
- SourceRange TokRange(Loc, TokEndLoc);
- StringRef Comment = Lexer::getSourceText(
- CharSourceRange::getTokenRange(TokRange), SM, Context->getLangOpts());
- if (Comment.starts_with("/*") && Comment.contains('\n')) {
- // Multi-line block comment, insert brace before.
- break;
- }
- // else: Trailing comment, insert brace after the newline.
-
- // Fast-forward current token.
- Loc = TokEndLoc;
- }
- return Loc;
-}
-
BracesAroundStatementsCheck::BracesAroundStatementsCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
@@ -124,7 +80,7 @@ void BracesAroundStatementsCheck::check(
} else if (const auto *S = Result.Nodes.getNodeAs("do")) {
checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
} else if (const auto *S = Result.Nodes.getNodeAs("while")) {
- SourceLocation StartLoc = findRParenLoc(S, SM, Context);
+ SourceLocation StartLoc = findRParenLoc(S, SM, Context->getLangOpts());
if (StartLoc.isInvalid())
return;
checkStmt(Result, S->getBody(), StartLoc);
@@ -133,7 +89,7 @@ void BracesAroundStatementsCheck::check(
if (S->isConsteval())
return;
- SourceLocation StartLoc = findRParenLoc(S, SM, Context);
+ SourceLocation StartLoc = findRParenLoc(S, SM, Context->getLangOpts());
if (StartLoc.isInvalid())
return;
if (ForceBracesStmts.erase(S))
@@ -156,7 +112,7 @@ template
SourceLocation
BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
const SourceManager &SM,
- const ASTContext *Context) {
+ const LangOptions &LangOpts) {
// Skip macros.
if (S->getBeginLoc().isMacroID())
return {};
@@ -170,14 +126,14 @@ BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
}
SourceLocation PastCondEndLoc =
- Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, Context->getLangOpts());
+ Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, LangOpts);
if (PastCondEndLoc.isInvalid())
return {};
SourceLocation RParenLoc =
- forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, Context);
+ forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, LangOpts);
if (RParenLoc.isInvalid())
return {};
- tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, Context);
+ tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, LangOpts);
if (TokKind != tok::r_paren)
return {};
return RParenLoc;
@@ -188,86 +144,23 @@ BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
bool BracesAroundStatementsCheck::checkStmt(
const MatchFinder::MatchResult &Result, const Stmt *S,
SourceLocation StartLoc, SourceLocation EndLocHint) {
-
while (const auto *AS = dyn_cast(S))
S = AS->getSubStmt();
- const SourceManager &SM = *Result.SourceManager;
- const ASTContext *Context = Result.Context;
-
- // 1) If there's a corresponding "else" or "while", the check inserts "} "
- // right before that token.
- // 2) If there's a multi-line block comment starting on the same line after
- // the location we're inserting the closing brace at, or there's a non-comment
- // token, the check inserts "\n}" right before that token.
- // 3) Otherwise the check finds the end of line (possibly after some block or
- // line comments) and inserts "\n}" right before that EOL.
- if (!S || isa(S)) {
- // Already inside braces.
- return false;
- }
-
- // When TreeTransform, Stmt in constexpr IfStmt will be transform to NullStmt.
- // This NullStmt can be detected according to beginning token.
- const SourceLocation StmtBeginLoc = S->getBeginLoc();
- if (isa(S) && StmtBeginLoc.isValid() &&
- getTokenKind(StmtBeginLoc, SM, Context) == tok::l_brace)
- return false;
-
- if (StartLoc.isInvalid())
- return false;
-
- // Convert StartLoc to file location, if it's on the same macro expansion
- // level as the start of the statement. We also need file locations for
- // Lexer::getLocForEndOfToken working properly.
- StartLoc = Lexer::makeFileCharRange(
- CharSourceRange::getCharRange(StartLoc, S->getBeginLoc()), SM,
- Context->getLangOpts())
- .getBegin();
- if (StartLoc.isInvalid())
- return false;
- StartLoc =
- Lexer::getLocForEndOfToken(StartLoc, 0, SM, Context->getLangOpts());
-
- // StartLoc points at the location of the opening brace to be inserted.
- SourceLocation EndLoc;
- std::string ClosingInsertion;
- if (EndLocHint.isValid()) {
- EndLoc = EndLocHint;
- ClosingInsertion = "} ";
- } else {
- EndLoc = findEndLocation(*S, SM, Context);
- ClosingInsertion = "\n}";
- }
-
- assert(StartLoc.isValid());
-
- // Don't require braces for statements spanning less than certain number of
- // lines.
- if (ShortStatementLines && !ForceBracesStmts.erase(S)) {
- unsigned StartLine = SM.getSpellingLineNumber(StartLoc);
- unsigned EndLine = SM.getSpellingLineNumber(EndLoc);
- if (EndLine - StartLine < ShortStatementLines)
+ const auto BraceInsertionHints = utils::getBraceInsertionsHints(
+ S, Result.Context->getLangOpts(), *Result.SourceManager, StartLoc,
+ EndLocHint);
+ if (BraceInsertionHints) {
+ if (ShortStatementLines && !ForceBracesStmts.erase(S) &&
+ BraceInsertionHints.resultingCompoundLineExtent(*Result.SourceManager) <
+ ShortStatementLines)
return false;
+ auto Diag = diag(BraceInsertionHints.DiagnosticPos,
+ "statement should be inside braces");
+ if (BraceInsertionHints.offersFixIts())
+ Diag << BraceInsertionHints.openingBraceFixIt()
+ << BraceInsertionHints.closingBraceFixIt();
}
-
- auto Diag = diag(StartLoc, "statement should be inside braces");
-
- // Change only if StartLoc and EndLoc are on the same macro expansion level.
- // This will also catch invalid EndLoc.
- // Example: LLVM_DEBUG( for(...) do_something() );
- // In this case fix-it cannot be provided as the semicolon which is not
- // visible here is part of the macro. Adding braces here would require adding
- // another semicolon.
- if (Lexer::makeFileCharRange(
- CharSourceRange::getTokenRange(SourceRange(
- SM.getSpellingLoc(StartLoc), SM.getSpellingLoc(EndLoc))),
- SM, Context->getLangOpts())
- .isInvalid())
- return false;
-
- Diag << FixItHint::CreateInsertion(StartLoc, " {")
- << FixItHint::CreateInsertion(EndLoc, ClosingInsertion);
return true;
}
diff --git a/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.h b/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.h
index 249aa1aaaa91545549c847912feb3415a381e8cf..4cd37a7b2dd6cc169f9301d6a21b1613748399db 100644
--- a/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.h
@@ -52,7 +52,7 @@ private:
SourceLocation EndLocHint = SourceLocation());
template
SourceLocation findRParenLoc(const IfOrWhileStmt *S, const SourceManager &SM,
- const ASTContext *Context);
+ const LangOptions &LangOpts);
std::optional getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}
diff --git a/clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.cpp b/clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.cpp
index 67147164946ab405138aeafbb361f490f4382ad6..229e5583846b96c0a4ddc537bc78d7a4467fa355 100644
--- a/clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/DuplicateIncludeCheck.cpp
@@ -79,6 +79,10 @@ void DuplicateIncludeCallbacks::InclusionDirective(
bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
+ // Skip includes behind macros
+ if (FilenameRange.getBegin().isMacroID() ||
+ FilenameRange.getEnd().isMacroID())
+ return;
if (llvm::is_contained(Files.back(), FileName)) {
// We want to delete the entire line, so make sure that [Start,End] covers
// everything.
diff --git a/clang-tools-extra/clang-tidy/utils/BracesAroundStatement.cpp b/clang-tools-extra/clang-tidy/utils/BracesAroundStatement.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2a3b7bed08c1e00363d9ad2c899528d8e150f61e
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/utils/BracesAroundStatement.cpp
@@ -0,0 +1,168 @@
+//===--- BracesAroundStatement.cpp - clang-tidy -------- ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file provides utilities to put braces around a statement.
+///
+//===----------------------------------------------------------------------===//
+
+#include "BracesAroundStatement.h"
+#include "../utils/LexerUtils.h"
+#include "LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang::tidy::utils {
+
+BraceInsertionHints::operator bool() const { return DiagnosticPos.isValid(); }
+
+bool BraceInsertionHints::offersFixIts() const {
+ return OpeningBracePos.isValid() && ClosingBracePos.isValid();
+}
+
+unsigned BraceInsertionHints::resultingCompoundLineExtent(
+ const SourceManager &SourceMgr) const {
+ return SourceMgr.getSpellingLineNumber(ClosingBracePos) -
+ SourceMgr.getSpellingLineNumber(OpeningBracePos);
+}
+
+FixItHint BraceInsertionHints::openingBraceFixIt() const {
+ return OpeningBracePos.isValid()
+ ? FixItHint::CreateInsertion(OpeningBracePos, " {")
+ : FixItHint();
+}
+
+FixItHint BraceInsertionHints::closingBraceFixIt() const {
+ return ClosingBracePos.isValid()
+ ? FixItHint::CreateInsertion(ClosingBracePos, ClosingBrace)
+ : FixItHint();
+}
+
+static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ Token Tok;
+ SourceLocation Beginning = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
+ const bool Invalid = Lexer::getRawToken(Beginning, Tok, SM, LangOpts);
+ assert(!Invalid && "Expected a valid token.");
+
+ if (Invalid)
+ return tok::NUM_TOKENS;
+
+ return Tok.getKind();
+}
+
+static SourceLocation findEndLocation(const Stmt &S, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ SourceLocation Loc = lexer::getUnifiedEndLoc(S, SM, LangOpts);
+ if (!Loc.isValid())
+ return Loc;
+
+ // Start searching right after S.
+ Loc = Loc.getLocWithOffset(1);
+
+ for (;;) {
+ assert(Loc.isValid());
+ while (isHorizontalWhitespace(*SM.getCharacterData(Loc))) {
+ Loc = Loc.getLocWithOffset(1);
+ }
+
+ if (isVerticalWhitespace(*SM.getCharacterData(Loc))) {
+ // EOL, insert brace before.
+ break;
+ }
+ tok::TokenKind TokKind = getTokenKind(Loc, SM, LangOpts);
+ if (TokKind != tok::comment) {
+ // Non-comment token, insert brace before.
+ break;
+ }
+
+ SourceLocation TokEndLoc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
+ SourceRange TokRange(Loc, TokEndLoc);
+ StringRef Comment = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(TokRange), SM, LangOpts);
+ if (Comment.starts_with("/*") && Comment.contains('\n')) {
+ // Multi-line block comment, insert brace before.
+ break;
+ }
+ // else: Trailing comment, insert brace after the newline.
+
+ // Fast-forward current token.
+ Loc = TokEndLoc;
+ }
+ return Loc;
+}
+
+BraceInsertionHints getBraceInsertionsHints(const Stmt *const S,
+ const LangOptions &LangOpts,
+ const SourceManager &SM,
+ SourceLocation StartLoc,
+ SourceLocation EndLocHint) {
+ // 1) If there's a corresponding "else" or "while", the check inserts "} "
+ // right before that token.
+ // 2) If there's a multi-line block comment starting on the same line after
+ // the location we're inserting the closing brace at, or there's a non-comment
+ // token, the check inserts "\n}" right before that token.
+ // 3) Otherwise the check finds the end of line (possibly after some block or
+ // line comments) and inserts "\n}" right before that EOL.
+ if (!S || isa(S)) {
+ // Already inside braces.
+ return {};
+ }
+
+ // When TreeTransform, Stmt in constexpr IfStmt will be transform to NullStmt.
+ // This NullStmt can be detected according to beginning token.
+ const SourceLocation StmtBeginLoc = S->getBeginLoc();
+ if (isa(S) && StmtBeginLoc.isValid() &&
+ getTokenKind(StmtBeginLoc, SM, LangOpts) == tok::l_brace)
+ return {};
+
+ if (StartLoc.isInvalid())
+ return {};
+
+ // Convert StartLoc to file location, if it's on the same macro expansion
+ // level as the start of the statement. We also need file locations for
+ // Lexer::getLocForEndOfToken working properly.
+ StartLoc = Lexer::makeFileCharRange(
+ CharSourceRange::getCharRange(StartLoc, S->getBeginLoc()), SM,
+ LangOpts)
+ .getBegin();
+ if (StartLoc.isInvalid())
+ return {};
+ StartLoc = Lexer::getLocForEndOfToken(StartLoc, 0, SM, LangOpts);
+
+ // StartLoc points at the location of the opening brace to be inserted.
+ SourceLocation EndLoc;
+ std::string ClosingInsertion;
+ if (EndLocHint.isValid()) {
+ EndLoc = EndLocHint;
+ ClosingInsertion = "} ";
+ } else {
+ EndLoc = findEndLocation(*S, SM, LangOpts);
+ ClosingInsertion = "\n}";
+ }
+
+ assert(StartLoc.isValid());
+
+ // Change only if StartLoc and EndLoc are on the same macro expansion level.
+ // This will also catch invalid EndLoc.
+ // Example: LLVM_DEBUG( for(...) do_something() );
+ // In this case fix-it cannot be provided as the semicolon which is not
+ // visible here is part of the macro. Adding braces here would require adding
+ // another semicolon.
+ if (Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(SourceRange(
+ SM.getSpellingLoc(StartLoc), SM.getSpellingLoc(EndLoc))),
+ SM, LangOpts)
+ .isInvalid())
+ return {StartLoc};
+ return {StartLoc, EndLoc, ClosingInsertion};
+}
+
+} // namespace clang::tidy::utils
diff --git a/clang-tools-extra/clang-tidy/utils/BracesAroundStatement.h b/clang-tools-extra/clang-tidy/utils/BracesAroundStatement.h
new file mode 100644
index 0000000000000000000000000000000000000000..cb1c06c7aa1a1a9d5bdd56cd805aa26f19e56c8d
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/utils/BracesAroundStatement.h
@@ -0,0 +1,75 @@
+//===--- BracesAroundStatement.h - clang-tidy ------- -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file provides utilities to put braces around a statement.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+
+namespace clang::tidy::utils {
+
+/// A provider of fix-it hints to insert opening and closing braces. An instance
+/// of this type is the result of calling `getBraceInsertionsHints` below.
+struct BraceInsertionHints {
+ /// The position of a potential diagnostic. It coincides with the position of
+ /// the opening brace to insert, but can also just be the place to show a
+ /// diagnostic in case braces cannot be inserted automatically.
+ SourceLocation DiagnosticPos;
+
+ /// Constructor for a no-hint.
+ BraceInsertionHints() = default;
+
+ /// Constructor for a valid hint that cannot insert braces automatically.
+ BraceInsertionHints(SourceLocation DiagnosticPos)
+ : DiagnosticPos(DiagnosticPos) {}
+
+ /// Constructor for a hint offering fix-its for brace insertion. Both
+ /// positions must be valid.
+ BraceInsertionHints(SourceLocation OpeningBracePos,
+ SourceLocation ClosingBracePos, std::string ClosingBrace)
+ : DiagnosticPos(OpeningBracePos), OpeningBracePos(OpeningBracePos),
+ ClosingBracePos(ClosingBracePos), ClosingBrace(ClosingBrace) {
+ assert(offersFixIts());
+ }
+
+ /// Indicates whether the hint provides at least the position of a diagnostic.
+ operator bool() const;
+
+ /// Indicates whether the hint provides fix-its to insert braces.
+ bool offersFixIts() const;
+
+ /// The number of lines between the inserted opening brace and its closing
+ /// counterpart.
+ unsigned resultingCompoundLineExtent(const SourceManager &SourceMgr) const;
+
+ /// Fix-it to insert an opening brace.
+ FixItHint openingBraceFixIt() const;
+
+ /// Fix-it to insert a closing brace.
+ FixItHint closingBraceFixIt() const;
+
+private:
+ SourceLocation OpeningBracePos;
+ SourceLocation ClosingBracePos;
+ std::string ClosingBrace;
+};
+
+/// Create fix-it hints for braces that wrap the given statement when applied.
+/// The algorithm computing them respects comment before and after the statement
+/// and adds line breaks before the braces accordingly.
+BraceInsertionHints
+getBraceInsertionsHints(const Stmt *const S, const LangOptions &LangOpts,
+ const SourceManager &SM, SourceLocation StartLoc,
+ SourceLocation EndLocHint = SourceLocation());
+
+} // namespace clang::tidy::utils
diff --git a/clang-tools-extra/clang-tidy/utils/CMakeLists.txt b/clang-tools-extra/clang-tidy/utils/CMakeLists.txt
index f0160fa9df74879429e6f6be554d43bf633af0d6..9cff7d475425d792c2c2f373a3e88d34f7216513 100644
--- a/clang-tools-extra/clang-tidy/utils/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/utils/CMakeLists.txt
@@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangTidyUtils
Aliasing.cpp
ASTUtils.cpp
+ BracesAroundStatement.cpp
DeclRefExprUtils.cpp
DesignatedInitializers.cpp
ExceptionAnalyzer.cpp
diff --git a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp
index da1433aa2d05d472f5f581973fead7993d468794..962a243ce94d48bfcf3e9ed1ffa3278749cbb35a 100644
--- a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp
+++ b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp
@@ -31,13 +31,12 @@ struct DenseMapInfo {
using NamingCheckId = clang::tidy::RenamerClangTidyCheck::NamingCheckId;
static inline NamingCheckId getEmptyKey() {
- return {DenseMapInfo::getEmptyKey(),
- "EMPTY"};
+ return {DenseMapInfo::getEmptyKey(), "EMPTY"};
}
static inline NamingCheckId getTombstoneKey() {
return {DenseMapInfo::getTombstoneKey(),
- "TOMBSTONE"};
+ "TOMBSTONE"};
}
static unsigned getHashValue(NamingCheckId Val) {
@@ -170,14 +169,14 @@ public:
return;
if (SM.isWrittenInCommandLineFile(MacroNameTok.getLocation()))
return;
- Check->checkMacro(SM, MacroNameTok, Info);
+ Check->checkMacro(MacroNameTok, Info, SM);
}
/// MacroExpands calls expandMacro for macros in the main file
void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
SourceRange /*Range*/,
const MacroArgs * /*Args*/) override {
- Check->expandMacro(MacroNameTok, MD.getMacroInfo());
+ Check->expandMacro(MacroNameTok, MD.getMacroInfo(), SM);
}
private:
@@ -188,7 +187,7 @@ private:
class RenamerClangTidyVisitor
: public RecursiveASTVisitor {
public:
- RenamerClangTidyVisitor(RenamerClangTidyCheck *Check, const SourceManager *SM,
+ RenamerClangTidyVisitor(RenamerClangTidyCheck *Check, const SourceManager &SM,
bool AggressiveDependentMemberLookup)
: Check(Check), SM(SM),
AggressiveDependentMemberLookup(AggressiveDependentMemberLookup) {}
@@ -259,7 +258,7 @@ public:
// Fix overridden methods
if (const auto *Method = dyn_cast(Decl)) {
if (const CXXMethodDecl *Overridden = getOverrideMethod(Method)) {
- Check->addUsage(Overridden, Method->getLocation());
+ Check->addUsage(Overridden, Method->getLocation(), SM);
return true; // Don't try to add the actual decl as a Failure.
}
}
@@ -269,7 +268,7 @@ public:
if (isa(Decl))
return true;
- Check->checkNamedDecl(Decl, *SM);
+ Check->checkNamedDecl(Decl, SM);
return true;
}
@@ -367,9 +366,26 @@ public:
return true;
}
+ bool VisitDesignatedInitExpr(DesignatedInitExpr *Expr) {
+ for (const DesignatedInitExpr::Designator &D : Expr->designators()) {
+ if (!D.isFieldDesignator())
+ continue;
+ const FieldDecl *FD = D.getFieldDecl();
+ if (!FD)
+ continue;
+ const IdentifierInfo *II = FD->getIdentifier();
+ if (!II)
+ continue;
+ SourceRange FixLocation{D.getFieldLoc(), D.getFieldLoc()};
+ Check->addUsage(FD, FixLocation, SM);
+ }
+
+ return true;
+ }
+
private:
RenamerClangTidyCheck *Check;
- const SourceManager *SM;
+ const SourceManager &SM;
const bool AggressiveDependentMemberLookup;
};
@@ -399,7 +415,7 @@ void RenamerClangTidyCheck::registerPPCallbacks(
void RenamerClangTidyCheck::addUsage(
const RenamerClangTidyCheck::NamingCheckId &Decl, SourceRange Range,
- const SourceManager *SourceMgr) {
+ const SourceManager &SourceMgr) {
// Do nothing if the provided range is invalid.
if (Range.isInvalid())
return;
@@ -409,8 +425,7 @@ void RenamerClangTidyCheck::addUsage(
// spelling location to different source locations, and we only want to fix
// the token once, before it is expanded by the macro.
SourceLocation FixLocation = Range.getBegin();
- if (SourceMgr)
- FixLocation = SourceMgr->getSpellingLoc(FixLocation);
+ FixLocation = SourceMgr.getSpellingLoc(FixLocation);
if (FixLocation.isInvalid())
return;
@@ -424,15 +439,15 @@ void RenamerClangTidyCheck::addUsage(
if (!Failure.shouldFix())
return;
- if (SourceMgr && SourceMgr->isWrittenInScratchSpace(FixLocation))
+ if (SourceMgr.isWrittenInScratchSpace(FixLocation))
Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro;
- if (!utils::rangeCanBeFixed(Range, SourceMgr))
+ if (!utils::rangeCanBeFixed(Range, &SourceMgr))
Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro;
}
void RenamerClangTidyCheck::addUsage(const NamedDecl *Decl, SourceRange Range,
- const SourceManager *SourceMgr) {
+ const SourceManager &SourceMgr) {
// Don't keep track for non-identifier names.
auto *II = Decl->getIdentifier();
if (!II)
@@ -473,18 +488,24 @@ void RenamerClangTidyCheck::checkNamedDecl(const NamedDecl *Decl,
}
Failure.Info = std::move(Info);
- addUsage(Decl, Range);
+ addUsage(Decl, Range, SourceMgr);
}
void RenamerClangTidyCheck::check(const MatchFinder::MatchResult &Result) {
- RenamerClangTidyVisitor Visitor(this, Result.SourceManager,
+ if (!Result.SourceManager) {
+ // In principle SourceManager is not null but going only by the definition
+ // of MatchResult it must be handled. Cannot rename anything without a
+ // SourceManager.
+ return;
+ }
+ RenamerClangTidyVisitor Visitor(this, *Result.SourceManager,
AggressiveDependentMemberLookup);
Visitor.TraverseAST(*Result.Context);
}
-void RenamerClangTidyCheck::checkMacro(const SourceManager &SourceMgr,
- const Token &MacroNameTok,
- const MacroInfo *MI) {
+void RenamerClangTidyCheck::checkMacro(const Token &MacroNameTok,
+ const MacroInfo *MI,
+ const SourceManager &SourceMgr) {
std::optional MaybeFailure =
getMacroFailureInfo(MacroNameTok, SourceMgr);
if (!MaybeFailure)
@@ -499,11 +520,12 @@ void RenamerClangTidyCheck::checkMacro(const SourceManager &SourceMgr,
Failure.FixStatus = ShouldFixStatus::FixInvalidIdentifier;
Failure.Info = std::move(Info);
- addUsage(ID, Range);
+ addUsage(ID, Range, SourceMgr);
}
void RenamerClangTidyCheck::expandMacro(const Token &MacroNameTok,
- const MacroInfo *MI) {
+ const MacroInfo *MI,
+ const SourceManager &SourceMgr) {
StringRef Name = MacroNameTok.getIdentifierInfo()->getName();
NamingCheckId ID(MI->getDefinitionLoc(), Name);
@@ -512,7 +534,7 @@ void RenamerClangTidyCheck::expandMacro(const Token &MacroNameTok,
return;
SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
- addUsage(ID, Range);
+ addUsage(ID, Range, SourceMgr);
}
static std::string
diff --git a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.h b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.h
index 38228fb59bf62487674a5ba28bbe8449956d37e2..be5b6f0c7f76785d7f2aada7dd394cf3a568ddd4 100644
--- a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.h
+++ b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.h
@@ -108,18 +108,19 @@ public:
llvm::DenseMap;
/// Check Macros for style violations.
- void checkMacro(const SourceManager &SourceMgr, const Token &MacroNameTok,
- const MacroInfo *MI);
+ void checkMacro(const Token &MacroNameTok, const MacroInfo *MI,
+ const SourceManager &SourceMgr);
/// Add a usage of a macro if it already has a violation.
- void expandMacro(const Token &MacroNameTok, const MacroInfo *MI);
+ void expandMacro(const Token &MacroNameTok, const MacroInfo *MI,
+ const SourceManager &SourceMgr);
void addUsage(const RenamerClangTidyCheck::NamingCheckId &Decl,
- SourceRange Range, const SourceManager *SourceMgr = nullptr);
+ SourceRange Range, const SourceManager &SourceMgr);
/// Convenience method when the usage to be added is a NamedDecl.
void addUsage(const NamedDecl *Decl, SourceRange Range,
- const SourceManager *SourceMgr = nullptr);
+ const SourceManager &SourceMgr);
void checkNamedDecl(const NamedDecl *Decl, const SourceManager &SourceMgr);
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index 9e321dce4c5041901ef21708b2366f2722514b7a..89eee392837af4955a584fca6c0515949924baa1 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -89,7 +89,11 @@ const CodeCompleteOptions::CodeCompletionRankingModel
namespace {
-CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) {
+// Note: changes to this function should also be reflected in the
+// CodeCompletionResult overload where appropriate.
+CompletionItemKind
+toCompletionItemKind(index::SymbolKind Kind,
+ const llvm::StringRef *Signature = nullptr) {
using SK = index::SymbolKind;
switch (Kind) {
case SK::Unknown:
@@ -99,7 +103,10 @@ CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) {
case SK::NamespaceAlias:
return CompletionItemKind::Module;
case SK::Macro:
- return CompletionItemKind::Text;
+ // Use macro signature (if provided) to tell apart function-like and
+ // object-like macros.
+ return Signature && Signature->contains('(') ? CompletionItemKind::Function
+ : CompletionItemKind::Constant;
case SK::Enum:
return CompletionItemKind::Enum;
case SK::Struct:
@@ -150,6 +157,8 @@ CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) {
llvm_unreachable("Unhandled clang::index::SymbolKind.");
}
+// Note: changes to this function should also be reflected in the
+// index::SymbolKind overload where appropriate.
CompletionItemKind toCompletionItemKind(const CodeCompletionResult &Res,
CodeCompletionContext::Kind CtxKind) {
if (Res.Declaration)
@@ -379,7 +388,8 @@ struct CodeCompletionBuilder {
if (Completion.Scope.empty())
Completion.Scope = std::string(C.IndexResult->Scope);
if (Completion.Kind == CompletionItemKind::Missing)
- Completion.Kind = toCompletionItemKind(C.IndexResult->SymInfo.Kind);
+ Completion.Kind = toCompletionItemKind(C.IndexResult->SymInfo.Kind,
+ &C.IndexResult->Signature);
if (Completion.Name.empty())
Completion.Name = std::string(C.IndexResult->Name);
if (Completion.FilterText.empty())
diff --git a/clang-tools-extra/clangd/CompileCommands.cpp b/clang-tools-extra/clangd/CompileCommands.cpp
index 5b8128fca62668ca555f049b7652369cd11be39e..fddfffe7523d9516037e310bd0d20b0c9520d100 100644
--- a/clang-tools-extra/clangd/CompileCommands.cpp
+++ b/clang-tools-extra/clangd/CompileCommands.cpp
@@ -466,7 +466,8 @@ llvm::ArrayRef ArgStripper::rulesFor(llvm::StringRef Arg) {
static constexpr llvm::ArrayRef NAME( \
NAME##_init, std::size(NAME##_init) - 1);
#define OPTION(PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, \
- FLAGS, VISIBILITY, PARAM, HELP, METAVAR, VALUES) \
+ FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, \
+ METAVAR, VALUES) \
Prefixes[DriverID::OPT_##ID] = PREFIX;
#include "clang/Driver/Options.inc"
#undef OPTION
@@ -478,7 +479,8 @@ llvm::ArrayRef ArgStripper::rulesFor(llvm::StringRef Arg) {
const void *AliasArgs;
} AliasTable[] = {
#define OPTION(PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, \
- FLAGS, VISIBILITY, PARAM, HELP, METAVAR, VALUES) \
+ FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, \
+ METAVAR, VALUES) \
{DriverID::OPT_##ID, DriverID::OPT_##ALIAS, ALIASARGS},
#include "clang/Driver/Options.inc"
#undef OPTION
diff --git a/clang-tools-extra/clangd/IncludeCleaner.h b/clang-tools-extra/clangd/IncludeCleaner.h
index 387763de340767be7e3cf8598db2b1a6c7076fd5..624e2116be7da3015816dfec628d8de6ac7f363c 100644
--- a/clang-tools-extra/clangd/IncludeCleaner.h
+++ b/clang-tools-extra/clangd/IncludeCleaner.h
@@ -62,15 +62,6 @@ issueIncludeCleanerDiagnostics(ParsedAST &AST, llvm::StringRef Code,
const ThreadsafeFS &TFS,
HeaderFilter IgnoreHeader = {});
-/// Affects whether standard library includes should be considered for
-/// removal. This is off by default for now due to implementation limitations:
-/// - macros are not tracked
-/// - symbol names without a unique associated header are not tracked
-/// - references to std-namespaced C types are not properly tracked:
-/// instead of std::size_t -> we see ::size_t ->
-/// FIXME: remove this hack once the implementation is good enough.
-void setIncludeCleanerAnalyzesStdlib(bool B);
-
/// Converts the clangd include representation to include-cleaner
/// include representation.
include_cleaner::Includes convertIncludes(const ParsedAST &);
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 85b8fc549b016e4dc159ddf591266dba80511fbb..5c4e2150cf3123bb11ecb567887ee269ec6f0ca7 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -409,7 +409,7 @@ private:
// Framework headers are spelled as , not
// "path/FrameworkName.framework/Headers/Foo.h".
auto &HS = PP->getHeaderSearchInfo();
- if (const auto *HFI = HS.getExistingFileInfo(*FE, /*WantExternal*/ false))
+ if (const auto *HFI = HS.getExistingFileInfo(*FE))
if (!HFI->Framework.empty())
if (auto Spelling =
getFrameworkHeaderIncludeSpelling(*FE, HFI->Framework, HS))
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 49337bddf98d5d93c22f75169844bc08ded35426..8fbac73cb653bcc20d4b68a92a6e80f2bd881315 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -671,7 +671,8 @@ TEST(CompletionTest, Kinds) {
#define MACRO 10
int X = ^
)cpp",
- {func("indexFunction"), var("indexVariable"), cls("indexClass")});
+ {func("indexFunction"), var("indexVariable"), cls("indexClass"),
+ macro("indexObjMacro"), macro("indexFuncMacro", "(x, y)")});
EXPECT_THAT(Results.Completions,
AllOf(has("function", CompletionItemKind::Function),
has("variable", CompletionItemKind::Variable),
@@ -680,7 +681,9 @@ TEST(CompletionTest, Kinds) {
has("MACRO", CompletionItemKind::Constant),
has("indexFunction", CompletionItemKind::Function),
has("indexVariable", CompletionItemKind::Variable),
- has("indexClass", CompletionItemKind::Class)));
+ has("indexClass", CompletionItemKind::Class),
+ has("indexObjMacro", CompletionItemKind::Constant),
+ has("indexFuncMacro", CompletionItemKind::Function)));
Results = completions("nam^");
EXPECT_THAT(Results.Completions,
diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp
index 35db757b9c15b5d3258552783cbbe5351c8d1a3c..5ead74748f550cb4536c7edf9cc246a4d41b5a42 100644
--- a/clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -1983,10 +1983,14 @@ TEST(Hover, All) {
HI.Kind = index::SymbolKind::Macro;
HI.Definition =
R"cpp(#define MACRO \
- { return 0; }
+ { \
+ return 0; \
+ }
// Expands to
-{ return 0; })cpp";
+{
+ return 0;
+})cpp";
}},
{
R"cpp(// Forward class declaration
diff --git a/clang-tools-extra/clangd/unittests/TestIndex.cpp b/clang-tools-extra/clangd/unittests/TestIndex.cpp
index 278336bdde2ee5cd2f50a36eb8ffb892490f44c0..b13a5d32d175245b64a830189afc0d843d51ac68 100644
--- a/clang-tools-extra/clangd/unittests/TestIndex.cpp
+++ b/clang-tools-extra/clangd/unittests/TestIndex.cpp
@@ -38,7 +38,7 @@ static std::string replace(llvm::StringRef Haystack, llvm::StringRef Needle,
// Helpers to produce fake index symbols for memIndex() or completions().
// USRFormat is a regex replacement string for the unqualified part of the USR.
Symbol sym(llvm::StringRef QName, index::SymbolKind Kind,
- llvm::StringRef USRFormat) {
+ llvm::StringRef USRFormat, llvm::StringRef Signature) {
Symbol Sym;
std::string USR = "c:"; // We synthesize a few simple cases of USRs by hand!
size_t Pos = QName.rfind("::");
@@ -55,6 +55,7 @@ Symbol sym(llvm::StringRef QName, index::SymbolKind Kind,
Sym.SymInfo.Kind = Kind;
Sym.Flags |= Symbol::IndexedForCodeCompletion;
Sym.Origin = SymbolOrigin::Static;
+ Sym.Signature = Signature;
return Sym;
}
@@ -86,6 +87,10 @@ Symbol conceptSym(llvm::StringRef Name) {
return sym(Name, index::SymbolKind::Concept, "@CT@\\0");
}
+Symbol macro(llvm::StringRef Name, llvm::StringRef ArgList) {
+ return sym(Name, index::SymbolKind::Macro, "@macro@\\0", ArgList);
+}
+
Symbol objcSym(llvm::StringRef Name, index::SymbolKind Kind,
llvm::StringRef USRPrefix) {
Symbol Sym;
diff --git a/clang-tools-extra/clangd/unittests/TestIndex.h b/clang-tools-extra/clangd/unittests/TestIndex.h
index 9280b0b12a67fe7a9f57e4e2344f32d84febc953..0699b29392d720dfad46335e823b837724f36316 100644
--- a/clang-tools-extra/clangd/unittests/TestIndex.h
+++ b/clang-tools-extra/clangd/unittests/TestIndex.h
@@ -20,7 +20,7 @@ Symbol symbol(llvm::StringRef QName);
// Helpers to produce fake index symbols with proper SymbolID.
// USRFormat is a regex replacement string for the unqualified part of the USR.
Symbol sym(llvm::StringRef QName, index::SymbolKind Kind,
- llvm::StringRef USRFormat);
+ llvm::StringRef USRFormat, llvm::StringRef Signature = {});
// Creats a function symbol assuming no function arg.
Symbol func(llvm::StringRef Name);
// Creates a class symbol.
@@ -35,6 +35,8 @@ Symbol var(llvm::StringRef Name);
Symbol ns(llvm::StringRef Name);
// Create a C++20 concept symbol.
Symbol conceptSym(llvm::StringRef Name);
+// Create a macro symbol.
+Symbol macro(llvm::StringRef Name, llvm::StringRef ArgList = {});
// Create an Objective-C symbol.
Symbol objcSym(llvm::StringRef Name, index::SymbolKind Kind,
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 309b844615a1212c4983815ae6eabee086e68e3c..1405fb0df1f8ddbb07aae02cd1ef149027a49d5a 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -100,6 +100,8 @@ Improvements to clang-tidy
- Improved :program:`run-clang-tidy.py` script. Added argument `-source-filter`
to filter source files from the compilation database, via a RegEx. In a
similar fashion to what `-header-filter` does for header files.
+- Improved :program:`check_clang_tidy.py` script. Added argument `-export-fixes`
+ to aid in clang-tidy and test development.
New checks
^^^^^^^^^^
@@ -179,8 +181,9 @@ Changes in existing checks
- Improved :doc:`cppcoreguidelines-missing-std-forward
` check by no longer
- giving false positives for deleted functions and fix false negative when some
- parameters are forwarded, but other aren't.
+ giving false positives for deleted functions, by fixing false negatives when only
+ a few parameters are forwarded and by ignoring parameters without a name (unused
+ arguments).
- Improved :doc:`cppcoreguidelines-owning-memory
` check to properly handle
@@ -210,6 +213,10 @@ Changes in existing checks
- Improved :doc:`google-runtime-int `
check performance through optimizations.
+- Improved :doc:`hicpp-ignored-remove-result `
+ check by ignoring other functions with same prefixes as the target specific
+ functions.
+
- Improved :doc:`llvm-header-guard
` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.
@@ -251,10 +258,19 @@ Changes in existing checks
analyzed, se the check now handles the common patterns
`const auto e = (*vector_ptr)[i]` and `const auto e = vector_ptr->at(i);`.
+- Improved :doc:`readability-avoid-return-with-void-value
+ ` check by adding
+ fix-its.
+
+- Improved :doc:`readability-duplicate-include
+ ` check by excluding include
+ directives that form the filename using macro.
+
- Improved :doc:`readability-identifier-naming
` check in `GetConfigPerFile`
mode by resolving symbolic links to header files. Fixed handling of Hungarian
- Prefix when configured to `LowerCase`.
+ Prefix when configured to `LowerCase`. Added support for renaming designated
+ initializers. Added support for renaming macro arguments.
- Improved :doc:`readability-implicit-bool-conversion
` check to provide
diff --git a/clang-tools-extra/include-cleaner/lib/FindHeaders.cpp b/clang-tools-extra/include-cleaner/lib/FindHeaders.cpp
index fd2de6a17ad4a53c009c9c5d1337cce4da9f0d52..7b28d1c252d715e1eb8501a25ead5f1abc41b2ef 100644
--- a/clang-tools-extra/include-cleaner/lib/FindHeaders.cpp
+++ b/clang-tools-extra/include-cleaner/lib/FindHeaders.cpp
@@ -275,6 +275,12 @@ llvm::SmallVector headersForSymbol(const Symbol &S,
// are already ranked in the stdlib mapping.
if (H.kind() == Header::Standard)
continue;
+ // Don't apply name match hints to exporting headers. As they usually have
+ // names similar to the original header, e.g. foo_wrapper/foo.h vs
+ // foo/foo.h, but shouldn't be preferred (unless marked as the public
+ // interface).
+ if ((H.Hint & Hints::OriginHeader) == Hints::None)
+ continue;
if (nameMatch(SymbolName, H))
H.Hint |= Hints::PreferredHeader;
}
diff --git a/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp b/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp
index 5a2a41b2d99bdd78b52b63fda3ce5db38394d404..07302142a13e363730f6a3713b9e8df806e2efdf 100644
--- a/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp
+++ b/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp
@@ -628,5 +628,24 @@ TEST_F(HeadersForSymbolTest, StandardHeaders) {
tooling::stdlib::Header::named("")));
}
+TEST_F(HeadersForSymbolTest, ExporterNoNameMatch) {
+ Inputs.Code = R"cpp(
+ #include "exporter/foo.h"
+ #include "foo_public.h"
+ )cpp";
+ Inputs.ExtraArgs.emplace_back("-I.");
+ // Deliberately named as foo_public to make sure it doesn't get name-match
+ // boost and also gets lexicographically bigger order than "exporter/foo.h".
+ Inputs.ExtraFiles["foo_public.h"] = guard(R"cpp(
+ struct foo {};
+ )cpp");
+ Inputs.ExtraFiles["exporter/foo.h"] = guard(R"cpp(
+ #include "foo_public.h" // IWYU pragma: export
+ )cpp");
+ buildAST();
+ EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("foo_public.h"),
+ physicalHeader("exporter/foo.h")));
+}
+
} // namespace
} // namespace clang::include_cleaner
diff --git a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py
index 53ffca0bad8d06fd92d0e9c5663ecc8acc464208..6d4b466afa691a74b59185e360f0b9a8b6597261 100755
--- a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py
+++ b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py
@@ -8,25 +8,35 @@
#
# ===------------------------------------------------------------------------===#
-r"""
+"""
ClangTidy Test Helper
=====================
-This script runs clang-tidy in fix mode and verify fixes, messages or both.
+This script is used to simplify writing, running, and debugging tests compatible
+with llvm-lit. By default it runs clang-tidy in fix mode and uses FileCheck to
+verify messages and/or fixes.
+
+For debugging, with --export-fixes, the tool simply exports fixes to a provided
+file and does not run FileCheck.
-Usage:
- check_clang_tidy.py [-resource-dir=] \
- [-assume-filename=] \
- [-check-suffix=] \
- [-check-suffixes=] \
- [-std=c++(98|11|14|17|20)[-or-later]] \
- \
- -- [optional clang-tidy arguments]
+Extra arguments, those after the first -- if any, are passed to either
+clang-tidy or clang:
+* Arguments between the first -- and second -- are clang-tidy arguments.
+ * May be only whitespace if there are no clang-tidy arguments.
+ * clang-tidy's --config would go here.
+* Arguments after the second -- are clang arguments
+
+Examples
+--------
-Example:
// RUN: %check_clang_tidy %s llvm-include-order %t -- -- -isystem %S/Inputs
-Notes:
+or
+
+ // RUN: %check_clang_tidy %s llvm-include-order --export-fixes=fixes.yaml %t -std=c++20
+
+Notes
+-----
-std=c++(98|11|14|17|20)-or-later:
This flag will cause multiple runs within the same check_clang_tidy
execution. Make sure you don't have shared state across these runs.
@@ -34,6 +44,7 @@ Notes:
import argparse
import os
+import pathlib
import re
import subprocess
import sys
@@ -88,6 +99,7 @@ class CheckRunner:
self.has_check_fixes = False
self.has_check_messages = False
self.has_check_notes = False
+ self.export_fixes = args.export_fixes
self.fixes = MessagePrefix("CHECK-FIXES")
self.messages = MessagePrefix("CHECK-MESSAGES")
self.notes = MessagePrefix("CHECK-NOTES")
@@ -181,7 +193,13 @@ class CheckRunner:
[
"clang-tidy",
self.temp_file_name,
- "-fix",
+ ]
+ + [
+ "-fix"
+ if self.export_fixes is None
+ else "--export-fixes=" + self.export_fixes
+ ]
+ + [
"--checks=-*," + self.check_name,
]
+ self.clang_tidy_extra_args
@@ -255,12 +273,14 @@ class CheckRunner:
def run(self):
self.read_input()
- self.get_prefixes()
+ if self.export_fixes is None:
+ self.get_prefixes()
self.prepare_test_inputs()
clang_tidy_output = self.run_clang_tidy()
- self.check_fixes()
- self.check_messages(clang_tidy_output)
- self.check_notes(clang_tidy_output)
+ if self.export_fixes is None:
+ self.check_fixes()
+ self.check_messages(clang_tidy_output)
+ self.check_notes(clang_tidy_output)
def expand_std(std):
@@ -284,7 +304,11 @@ def csv(string):
def parse_arguments():
- parser = argparse.ArgumentParser()
+ parser = argparse.ArgumentParser(
+ prog=pathlib.Path(__file__).stem,
+ description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ )
parser.add_argument("-expect-clang-tidy-error", action="store_true")
parser.add_argument("-resource-dir")
parser.add_argument("-assume-filename")
@@ -298,7 +322,19 @@ def parse_arguments():
type=csv,
help="comma-separated list of FileCheck suffixes",
)
- parser.add_argument("-std", type=csv, default=["c++11-or-later"])
+ parser.add_argument(
+ "-export-fixes",
+ default=None,
+ type=str,
+ metavar="file",
+ help="A file to export fixes into instead of fixing.",
+ )
+ parser.add_argument(
+ "-std",
+ type=csv,
+ default=["c++11-or-later"],
+ help="Passed to clang. Special -or-later values are expanded.",
+ )
return parser.parse_known_args()
diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp
index 9a50eabf619bd57a8f18091b48796e2ef7a479f1..8116db58c937d44524b6f6e3b7dd26273bc2a288 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp
@@ -198,3 +198,16 @@ struct S {
};
} // namespace deleted_functions
+
+namespace unused_arguments {
+
+template
+void unused_argument1(F&&) {}
+
+template
+void unused_argument2([[maybe_unused]] F&& f) {}
+
+template
+void unused_argument3(F&& _) {}
+
+} // namespace unused_arguments
diff --git a/clang-tools-extra/test/clang-tidy/checkers/hicpp/ignored-remove-result.cpp b/clang-tools-extra/test/clang-tidy/checkers/hicpp/ignored-remove-result.cpp
index b068f08590989394dc5702f310515d9c4b7f8793..fc431024303ab2d39f1b2d50fbba01503b3b1ead 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/hicpp/ignored-remove-result.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/hicpp/ignored-remove-result.cpp
@@ -15,6 +15,10 @@ ForwardIt unique(ForwardIt, ForwardIt);
template
InputIt find(InputIt, InputIt, const T&);
+struct unique_disposable {
+ void* release();
+};
+
class error_code {
};
@@ -63,4 +67,6 @@ void noWarning() {
// bugprone-unused-return-value's checked return types.
errorFunc();
(void) errorFunc();
+
+ std::unique_disposable{}.release();
}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-return-with-void-value.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-return-with-void-value.cpp
index f00407c99ce57021a4be3fcad13feaa9bbd24140..7c948dba3e8f7c0d4991270f3647761ec3de3dbb 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-return-with-void-value.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-return-with-void-value.cpp
@@ -12,23 +12,30 @@ void f2() {
return f1();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ // CHECK-FIXES: f1();
}
void f3(bool b) {
if (b) return f1();
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ // CHECK-FIXES: if (b) { f1(); return;
+ // CHECK-NEXT: }
return f2();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ // CHECK-FIXES: f2();
+ // CHECK-FIXES-LENIENT: f2();
}
template
T f4() {}
void f5() {
- return f4();
- // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
- // CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ { return f4(); }
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ // CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:7: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ // CHECK-FIXES: { f4(); return; }
+ // CHECK-FIXES-LENIENT: { f4(); return; }
}
void f6() { return; }
@@ -41,6 +48,8 @@ void f9() {
return (void)f7();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ // CHECK-FIXES: (void)f7();
+ // CHECK-FIXES-LENIENT: (void)f7();
}
#define RETURN_VOID return (void)1
@@ -50,12 +59,12 @@ void f10() {
// CHECK-MESSAGES-INCLUDE-MACROS: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
}
-template
+template
struct C {
C(A) {}
};
-template
+template
C f11() { return {}; }
using VOID = void;
@@ -66,4 +75,36 @@ VOID f13() {
return f12();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ // CHECK-FIXES: f12(); return;
+ // CHECK-FIXES-LENIENT: f12(); return;
+ (void)1;
+}
+
+void f14() {
+ return /* comment */ f1() /* comment */ ;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ // CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ // CHECK-FIXES: /* comment */ f1() /* comment */ ; return;
+ // CHECK-FIXES-LENIENT: /* comment */ f1() /* comment */ ; return;
+ (void)1;
+}
+
+void f15() {
+ return/*comment*/f1()/*comment*/;//comment
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ // CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ // CHECK-FIXES: /*comment*/f1()/*comment*/; return;//comment
+ // CHECK-FIXES-LENIENT: /*comment*/f1()/*comment*/; return;//comment
+ (void)1;
+}
+
+void f16(bool b) {
+ if (b) return f1();
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ // CHECK-FIXES: if (b) { f1(); return;
+ // CHECK-NEXT: }
+ else return f2();
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
+ // CHECK-FIXES: else { f2(); return;
+ // CHECK-NEXT: }
}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/duplicate-include.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/duplicate-include.cpp
index dd954c705514fb777bb5e0570623f076fb5a7070..2119602ba454b49d111524fd42730f41e76b8ede 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/duplicate-include.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/duplicate-include.cpp
@@ -70,3 +70,18 @@ int r;
// CHECK-FIXES: {{^int q;$}}
// CHECK-FIXES-NEXT: {{^#include $}}
// CHECK-FIXES-NEXT: {{^int r;$}}
+
+namespace Issue_87303 {
+#define RESET_INCLUDE_CACHE
+// Expect no warnings
+
+#define MACRO_FILENAME "duplicate-include.h"
+#include MACRO_FILENAME
+#include "duplicate-include.h"
+
+#define MACRO_FILENAME_2
+#include
+#include MACRO_FILENAME_2
+
+#undef RESET_INCLUDE_CACHE
+} // Issue_87303
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming.cpp
index d2e89a7c9855c9a8b9407406cbc7227b9c24ef44..99149fe86aceecf662ead7f674103230bb263630 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming.cpp
@@ -108,10 +108,12 @@ USER_NS::object g_s2;
// NO warnings or fixes expected as USER_NS and object are declared in a header file
SYSTEM_MACRO(var1);
-// NO warnings or fixes expected as var1 is from macro expansion
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: invalid case style for global variable 'var1' [readability-identifier-naming]
+// CHECK-FIXES: {{^}}SYSTEM_MACRO(g_var1);
USER_MACRO(var2);
-// NO warnings or fixes expected as var2 is declared in a macro expansion
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: invalid case style for global variable 'var2' [readability-identifier-naming]
+// CHECK-FIXES: {{^}}USER_MACRO(g_var2);
#define BLA int FOO_bar
BLA;
@@ -602,9 +604,20 @@ static void static_Function() {
// CHECK-FIXES: {{^}}#define MY_TEST_MACRO(X) X()
void MY_TEST_Macro(function) {}
-// CHECK-FIXES: {{^}}void MY_TEST_MACRO(function) {}
-}
-}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: invalid case style for global function 'function' [readability-identifier-naming]
+// CHECK-FIXES: {{^}}void MY_TEST_MACRO(Function) {}
+
+#define MY_CAT_IMPL(l, r) l ## r
+#define MY_CAT(l, r) MY_CAT_IMPL(l, r)
+#define MY_MACRO2(foo) int MY_CAT(awesome_, MY_CAT(foo, __COUNTER__)) = 0
+#define MY_MACRO3(foo) int MY_CAT(awesome_, foo) = 0
+MY_MACRO2(myglob);
+MY_MACRO3(myglob);
+// No suggestions should occur even though the resulting decl of awesome_myglob#
+// or awesome_myglob are not entirely within a macro argument.
+
+} // namespace InlineNamespace
+} // namespace FOO_NS
template struct a {
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: invalid case style for struct 'a'
@@ -766,3 +779,13 @@ STATIC_MACRO void someFunc(MyFunPtr, const MyFunPtr****) {}
// CHECK-FIXES: {{^}}STATIC_MACRO void someFunc(my_fun_ptr_t, const my_fun_ptr_t****) {}
#undef STATIC_MACRO
}
+
+struct Some_struct {
+ int SomeMember;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for public member 'SomeMember' [readability-identifier-naming]
+// CHECK-FIXES: {{^}} int some_member;
+};
+Some_struct g_s1{ .SomeMember = 1 };
+// CHECK-FIXES: {{^}}Some_struct g_s1{ .some_member = 1 };
+Some_struct g_s2{.SomeMember=1};
+// CHECK-FIXES: {{^}}Some_struct g_s2{.some_member=1};
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/diagnostic.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/diagnostic.cpp
index d0efc5ca7637538859ab4865eaa3e28e44a60bf1..57d930b26e64c0f1920ff932e18e48e279a0ec0e 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/diagnostic.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/diagnostic.cpp
@@ -25,7 +25,7 @@
// RUN: not clang-tidy -checks='-*,modernize-use-override' %T/diagnostics/input.cpp -- -DCOMPILATION_ERROR 2>&1 | FileCheck -check-prefix=CHECK6 -implicit-check-not='{{warning:|error:}}' %s
// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-macro-redefined' %s -- -DMACRO_FROM_COMMAND_LINE -std=c++20 | FileCheck -check-prefix=CHECK4 -implicit-check-not='{{warning:|error:}}' %s
// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-macro-redefined,clang-diagnostic-literal-conversion' %s -- -DMACRO_FROM_COMMAND_LINE -std=c++20 -Wno-macro-redefined | FileCheck --check-prefix=CHECK7 -implicit-check-not='{{warning:|error:}}' %s
-// RUN: not clang-tidy -checks='-*,modernize-use-override' %s -- -std=c++20 -DPR64602 | FileCheck -check-prefix=CHECK8 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,modernize-use-override' %s -- -std=c++20 -DPR64602
// CHECK1: error: no input files [clang-diagnostic-error]
// CHECK1: error: no such file or directory: '{{.*}}nonexistent.cpp' [clang-diagnostic-error]
@@ -68,6 +68,4 @@ auto S<>::foo(auto)
{
return 1;
}
-// CHECK8: error: conflicting types for 'foo' [clang-diagnostic-error]
-// CHECK8: note: previous declaration is here
#endif
diff --git a/clang/CMakeLists.txt b/clang/CMakeLists.txt
index 284b2af24ddaa0405398e8ea1826856e93401ba6..f092766fa19f07f754960ec9524078d6b65070d4 100644
--- a/clang/CMakeLists.txt
+++ b/clang/CMakeLists.txt
@@ -165,6 +165,13 @@ if(CLANG_ENABLE_LIBXML2)
endif()
endif()
+if(CLANG_ENABLE_CIR)
+ if (NOT "${LLVM_ENABLE_PROJECTS}" MATCHES "MLIR|mlir")
+ message(FATAL_ERROR
+ "Cannot build ClangIR without MLIR in LLVM_ENABLE_PROJECTS")
+ endif()
+endif()
+
include(CheckIncludeFile)
check_include_file(sys/resource.h CLANG_HAVE_RLIMITS)
diff --git a/clang/cmake/caches/Apple-stage2.cmake b/clang/cmake/caches/Apple-stage2.cmake
index 72cdedd611bc9600c0aecbef59060bee7d22b6be..ede256a2da6b8fd6864bd8584aeddf5126dd5bb9 100644
--- a/clang/cmake/caches/Apple-stage2.cmake
+++ b/clang/cmake/caches/Apple-stage2.cmake
@@ -15,6 +15,7 @@ set(LLVM_ENABLE_ZLIB ON CACHE BOOL "")
set(LLVM_ENABLE_BACKTRACES OFF CACHE BOOL "")
set(LLVM_ENABLE_MODULES ON CACHE BOOL "")
set(LLVM_EXTERNALIZE_DEBUGINFO ON CACHE BOOL "")
+set(LLVM_ENABLE_EXPORTED_SYMBOLS_IN_EXECUTABLES OFF CACHE BOOL "")
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")
set(CLANG_SPAWN_CC1 ON CACHE BOOL "")
set(BUG_REPORT_URL "http://developer.apple.com/bugreporter/" CACHE STRING "")
diff --git a/clang/cmake/caches/Fuchsia.cmake b/clang/cmake/caches/Fuchsia.cmake
index df69d7d0dd414beb8be6752acae171cf6fd5272e..393d97a4cf1a3302db9be62d1982d0f485d52a3b 100644
--- a/clang/cmake/caches/Fuchsia.cmake
+++ b/clang/cmake/caches/Fuchsia.cmake
@@ -71,6 +71,8 @@ set(_FUCHSIA_BOOTSTRAP_PASSTHROUGH
Python3_LIBRARIES
Python3_INCLUDE_DIRS
Python3_RPATH
+ SWIG_DIR
+ SWIG_EXECUTABLE
CMAKE_FIND_PACKAGE_PREFER_CONFIG
CMAKE_SYSROOT
CMAKE_MODULE_LINKER_FLAGS
diff --git a/clang/cmake/caches/Release.cmake b/clang/cmake/caches/Release.cmake
index 1ca9138b98073118184385ba6eb6a8a0690132ad..bd1f688d61a7ea2aaa475aaec7d6a0970c12139f 100644
--- a/clang/cmake/caches/Release.cmake
+++ b/clang/cmake/caches/Release.cmake
@@ -4,7 +4,7 @@
# General Options
set(LLVM_RELEASE_ENABLE_LTO THIN CACHE STRING "")
-set(LLVM_RELEASE_ENABLE_PGO ON CACHE BOOL "")
+set(LLVM_RELEASE_ENABLE_PGO OFF CACHE BOOL "")
set(CMAKE_BUILD_TYPE RELEASE CACHE STRING "")
diff --git a/clang/docs/ClangFormat.rst b/clang/docs/ClangFormat.rst
index 80dc38a075c8fcf572ce093da3893007e23342b4..dbd9c91ae508e5d8175c5e1734e4fcdb34c80090 100644
--- a/clang/docs/ClangFormat.rst
+++ b/clang/docs/ClangFormat.rst
@@ -54,7 +54,7 @@ to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# code.
Objective-C: .m .mm
Proto: .proto .protodevel
TableGen: .td
- TextProto: .textpb .pb.txt .textproto .asciipb
+ TextProto: .txtpb .textpb .pb.txt .textproto .asciipb
Verilog: .sv .svh .v .vh
--cursor= - The position of the cursor when invoking
clang-format from an editor integration
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 2ee36f24d7ce4b37c4bdf56ba318549b48696a80..39f7cded36edbff5846925aa56319682a04ed476 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -3295,6 +3295,21 @@ the configuration (without a prefix: ``Auto``).
+.. _BreakFunctionDefinitionParameters:
+
+**BreakFunctionDefinitionParameters** (``Boolean``) :versionbadge:`clang-format 19` :ref:`¶ `
+ If ``true``, clang-format will always break before function definition
+ parameters.
+
+ .. code-block:: c++
+
+ true:
+ void functionDefinition(
+ int A, int B) {}
+
+ false:
+ void functionDefinition(int A, int B) {}
+
.. _BreakInheritanceList:
**BreakInheritanceList** (``BreakInheritanceListStyle``) :versionbadge:`clang-format 7` :ref:`¶ `
diff --git a/clang/docs/HIPSupport.rst b/clang/docs/HIPSupport.rst
index 543c82cf90244945a7cf08b89009a6a37e5c34bf..5ba84c2f670556491b51f3d716be759d8f653fec 100644
--- a/clang/docs/HIPSupport.rst
+++ b/clang/docs/HIPSupport.rst
@@ -208,6 +208,20 @@ Host Code Compilation
- These relocatable objects are then linked together.
- Host code within a TU can call host functions and launch kernels from another TU.
+Syntax Difference with CUDA
+===========================
+
+Clang's front end, used for both CUDA and HIP programming models, shares the same parsing and semantic analysis mechanisms. This includes the resolution of overloads concerning device and host functions. While there exists a comprehensive documentation on the syntax differences between Clang and NVCC for CUDA at `Dialect Differences Between Clang and NVCC `_, it is important to note that these differences also apply to HIP code compilation.
+
+Predefined Macros for Differentiation
+-------------------------------------
+
+To facilitate differentiation between HIP and CUDA code, as well as between device and host compilations within HIP, Clang defines specific macros:
+
+- ``__HIP__`` : This macro is defined only when compiling HIP code. It can be used to conditionally compile code specific to HIP, enabling developers to write portable code that can be compiled for both CUDA and HIP.
+
+- ``__HIP_DEVICE_COMPILE__`` : Defined exclusively during HIP device compilation, this macro allows for conditional compilation of device-specific code. It provides a mechanism to segregate device and host code, ensuring that each can be optimized for their respective execution environments.
+
Function Pointers Support
=========================
diff --git a/clang/docs/HLSL/FunctionCalls.rst b/clang/docs/HLSL/FunctionCalls.rst
index 7317de2163f8975b3d9d283a55aa8d4d9ba2ac55..6d65fe6e3fb20b90a3a755a13123042de2226958 100644
--- a/clang/docs/HLSL/FunctionCalls.rst
+++ b/clang/docs/HLSL/FunctionCalls.rst
@@ -157,22 +157,23 @@ Clang Implementation
of the changes in the prototype implementation are restoring Clang-3.7 code
that was previously modified to its original state.
-The implementation in clang depends on two new AST nodes and minor extensions to
-Clang's existing support for Objective-C write-back arguments. The goal of this
-design is to capture the semantic details of HLSL function calls in the AST, and
-minimize the amount of magic that needs to occur during IR generation.
-
-The two new AST nodes are ``HLSLArrayTemporaryExpr`` and ``HLSLOutParamExpr``,
-which respectively represent the temporaries used for passing arrays by value
-and the temporaries created for function outputs.
+The implementation in clang adds a new non-decaying array type, a new AST node
+to represent output parameters, and minor extensions to Clang's existing support
+for Objective-C write-back arguments. The goal of this design is to capture the
+semantic details of HLSL function calls in the AST, and minimize the amount of
+magic that needs to occur during IR generation.
Array Temporaries
-----------------
-The ``HLSLArrayTemporaryExpr`` represents temporary values for input
-constant-sized array arguments. This applies for all constant-sized array
-arguments regardless of whether or not the parameter is constant-sized or
-unsized.
+The new ``ArrayParameterType`` is a sub-class of ``ConstantArrayType``
+inheriting all the behaviors and methods of the parent except that it does not
+decay to a pointer during overload resolution or template type deduction.
+
+An argument of ``ConstantArrayType`` can be implicitly converted to an
+equivalent non-decayed ``ArrayParameterType`` if the underlying canonical
+``ConstantArrayType`` is the same. This occurs during overload resolution
+instead of array to pointer decay.
.. code-block:: c++
@@ -193,7 +194,7 @@ In the example above, the following AST is generated for the call to
CallExpr 'void'
|-ImplicitCastExpr 'void (*)(float [4])'
| `-DeclRefExpr 'void (float [4])' lvalue Function 'SizedArray' 'void (float [4])'
- `-HLSLArrayTemporaryExpr 'float [4]'
+ `-ImplicitCastExpr 'float [4]'
`-DeclRefExpr 'float [4]' lvalue Var 'arr' 'float [4]'
In the example above, the following AST is generated for the call to
@@ -204,7 +205,7 @@ In the example above, the following AST is generated for the call to
CallExpr 'void'
|-ImplicitCastExpr 'void (*)(float [])'
| `-DeclRefExpr 'void (float [])' lvalue Function 'UnsizedArray' 'void (float [])'
- `-HLSLArrayTemporaryExpr 'float [4]'
+ `-ImplicitCastExpr 'float [4]'
`-DeclRefExpr 'float [4]' lvalue Var 'arr' 'float [4]'
In both of these cases the argument expression is of known array size so we can
@@ -236,7 +237,7 @@ An expected AST should be something like:
CallExpr 'void'
|-ImplicitCastExpr 'void (*)(float [])'
| `-DeclRefExpr 'void (float [])' lvalue Function 'UnsizedArray' 'void (float [])'
- `-HLSLArrayTemporaryExpr 'float [4]'
+ `-ImplicitCastExpr 'float [4]'
`-DeclRefExpr 'float [4]' lvalue Var 'arr' 'float [4]'
Out Parameter Temporaries
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 7b23e4d1c2f30c1544b2eba17b2a53849a6bc6a9..05c8f765b556951a51febff0515b5b4904fec2c3 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1493,6 +1493,7 @@ Conditional ``explicit`` __cpp_conditional_explicit C+
``if consteval`` __cpp_if_consteval C++23 C++20
``static operator()`` __cpp_static_call_operator C++23 C++03
Attributes on Lambda-Expressions C++23 C++11
+``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
-------------------------------------------- -------------------------------- ------------- -------------
Designated initializers (N494) C99 C89
Array & element qualification (N2607) C23 C89
@@ -1610,6 +1611,7 @@ The following type trait primitives are supported by Clang. Those traits marked
* ``__is_pod`` (C++, GNU, Microsoft, Embarcadero):
Note, the corresponding standard trait was deprecated in C++20.
* ``__is_pointer`` (C++, Embarcadero)
+* ``__is_pointer_interconvertible_base_of`` (C++, GNU, Microsoft)
* ``__is_polymorphic`` (C++, GNU, Microsoft, Embarcadero)
* ``__is_reference`` (C++, Embarcadero)
* ``__is_referenceable`` (C++, GNU, Microsoft, Embarcadero):
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 76eaf0bf11c303dc4f0b46c3ff3b75dca65f04fc..de5fa99bff5dbe7e0f42385ca707629f6b204360 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -98,7 +98,8 @@ C++20 Feature Support
behavior can use the flag '-Xclang -fno-skip-odr-check-in-gmf'.
(#GH79240).
-- Implemented the `__is_layout_compatible` intrinsic to support
+- Implemented the `__is_layout_compatible` and `__is_pointer_interconvertible_base_of`
+ intrinsics to support
`P0466R5: Layout-compatibility and Pointer-interconvertibility Traits `_.
- Clang now implements [module.import]p7 fully. Clang now will import module
@@ -110,6 +111,10 @@ C++20 Feature Support
templates (`P1814R0 `_).
(#GH54051).
+- We have sufficient confidence and experience with the concepts implementation
+ to update the ``__cpp_concepts`` macro to `202002L`. This enables
+ ```` from libstdc++ to work correctly with Clang.
+
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
@@ -124,6 +129,8 @@ C++2c Feature Support
- Implemented `P2662R3 Pack Indexing `_.
+- Implemented `P2573R2: = delete("should have a reason"); `_
+
Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -143,6 +150,9 @@ Resolutions to C++ Defect Reports
compatibility of two types.
(`CWG2759: [[no_unique_address] and common initial sequence `_).
+- Clang now diagnoses declarative nested-name-specifiers with pack-index-specifiers.
+ (`CWG2858: Declarative nested-name-specifiers and pack-index-specifiers `_).
+
C Language Changes
------------------
@@ -193,8 +203,15 @@ Non-comprehensive list of changes in this release
with support for any unsigned integer type. Like the previous builtins, these
new builtins are constexpr and may be used in constant expressions.
+- ``__typeof_unqual__`` is available in all C modes as an extension, which behaves
+ like ``typeof_unqual`` from C23, similar to ``__typeof__`` and ``typeof``.
+
New Compiler Flags
------------------
+- ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and
+ sign change.
+- ``-fsanitize=implicit-integer-conversion`` a group that replaces the previous
+ group ``-fsanitize=implicit-conversion``.
- ``-Wmissing-designated-field-initializers``, grouped under ``-Wmissing-field-initializers``.
This diagnostic can be disabled to make ``-Wmissing-field-initializers`` behave
@@ -208,6 +225,9 @@ Modified Compiler Flags
- Added a new diagnostic flag ``-Wreturn-mismatch`` which is grouped under
``-Wreturn-type``, and moved some of the diagnostics previously controlled by
``-Wreturn-type`` under this new flag. Fixes #GH72116.
+- ``-fsanitize=implicit-conversion`` is now a group for both
+ ``-fsanitize=implicit-integer-conversion`` and
+ ``-fsanitize=implicit-bitfield-conversion``.
- Added ``-Wcast-function-type-mismatch`` under the ``-Wcast-function-type``
warning group. Moved the diagnostic previously controlled by
@@ -253,6 +273,21 @@ Attribute Changes in Clang
added a new extension query ``__has_extension(swiftcc)`` corresponding to the
``__attribute__((swiftcc))`` attribute.
+- The ``_Nullable`` and ``_Nonnull`` family of type attributes can now apply
+ to certain C++ class types, such as smart pointers:
+ ``void useObject(std::unique_ptr