diff --git a/.ci/generate-buildkite-pipeline-premerge b/.ci/generate-buildkite-pipeline-premerge
index 7676ff716c418503dcdb7a09fdc36c9eba6a47f8..190dd1e5ba5af0f7e0cfba49d3dcae5bf27cf978 100755
--- a/.ci/generate-buildkite-pipeline-premerge
+++ b/.ci/generate-buildkite-pipeline-premerge
@@ -272,7 +272,7 @@ if [[ "${linux_projects}" != "" ]]; then
artifact_paths:
- 'artifacts/**/*'
- '*_result.json'
- - 'build/test-results.xml'
+ - 'build/test-results.*.xml'
agents: ${LINUX_AGENTS}
retry:
automatic:
@@ -295,7 +295,7 @@ if [[ "${windows_projects}" != "" ]]; then
artifact_paths:
- 'artifacts/**/*'
- '*_result.json'
- - 'build/test-results.xml'
+ - 'build/test-results.*.xml'
agents: ${WINDOWS_AGENTS}
retry:
automatic:
diff --git a/.ci/generate_test_report.py b/.ci/generate_test_report.py
new file mode 100644
index 0000000000000000000000000000000000000000..237f45e6f08e06376af3ca310bb7fc4409cb32cd
--- /dev/null
+++ b/.ci/generate_test_report.py
@@ -0,0 +1,423 @@
+# Script to parse many JUnit XML result files and send a report to the buildkite
+# agent as an annotation.
+#
+# To run the unittests:
+# python3 -m unittest discover -p generate_test_report.py
+
+import argparse
+import subprocess
+import unittest
+from io import StringIO
+from junitparser import JUnitXml, Failure
+from textwrap import dedent
+
+
+def junit_from_xml(xml):
+ return JUnitXml.fromfile(StringIO(xml))
+
+
+class TestReports(unittest.TestCase):
+ def test_title_only(self):
+ self.assertEqual(_generate_report("Foo", []), ("", None))
+
+ def test_no_tests_in_testsuite(self):
+ self.assertEqual(
+ _generate_report(
+ "Foo",
+ [
+ junit_from_xml(
+ dedent(
+ """\
+
+
+
+
+ """
+ )
+ )
+ ],
+ ),
+ ("", None),
+ )
+
+ def test_no_failures(self):
+ self.assertEqual(
+ _generate_report(
+ "Foo",
+ [
+ junit_from_xml(
+ dedent(
+ """\
+
+
+
+
+
+ """
+ )
+ )
+ ],
+ ),
+ (
+ dedent(
+ """\
+ # Foo
+
+ * 1 test passed"""
+ ),
+ "success",
+ ),
+ )
+
+ def test_report_single_file_single_testsuite(self):
+ self.assertEqual(
+ _generate_report(
+ "Foo",
+ [
+ junit_from_xml(
+ dedent(
+ """\
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """
+ )
+ )
+ ],
+ ),
+ (
+ dedent(
+ """\
+ # Foo
+
+ * 1 test passed
+ * 1 test skipped
+ * 2 tests failed
+
+ ## Failed Tests
+ (click to see output)
+
+ ### Bar
+
+ Bar/test_3/test_3
+
+ ```
+ Output goes here
+ ```
+
+
+ Bar/test_4/test_4
+
+ ```
+ Other output goes here
+ ```
+ """
+ ),
+ "error",
+ ),
+ )
+
+ MULTI_SUITE_OUTPUT = (
+ dedent(
+ """\
+ # ABC and DEF
+
+ * 1 test passed
+ * 1 test skipped
+ * 2 tests failed
+
+ ## Failed Tests
+ (click to see output)
+
+ ### ABC
+
+ ABC/test_2/test_2
+
+ ```
+ ABC/test_2 output goes here
+ ```
+
+
+ ### DEF
+
+ DEF/test_2/test_2
+
+ ```
+ DEF/test_2 output goes here
+ ```
+ """
+ ),
+ "error",
+ )
+
+ def test_report_single_file_multiple_testsuites(self):
+ self.assertEqual(
+ _generate_report(
+ "ABC and DEF",
+ [
+ junit_from_xml(
+ dedent(
+ """\
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """
+ )
+ )
+ ],
+ ),
+ self.MULTI_SUITE_OUTPUT,
+ )
+
+ def test_report_multiple_files_multiple_testsuites(self):
+ self.assertEqual(
+ _generate_report(
+ "ABC and DEF",
+ [
+ junit_from_xml(
+ dedent(
+ """\
+
+
+
+
+
+
+
+
+ """
+ )
+ ),
+ junit_from_xml(
+ dedent(
+ """\
+
+
+
+
+
+
+
+
+
+
+ """
+ )
+ ),
+ ],
+ ),
+ self.MULTI_SUITE_OUTPUT,
+ )
+
+ def test_report_dont_list_failures(self):
+ self.assertEqual(
+ _generate_report(
+ "Foo",
+ [
+ junit_from_xml(
+ dedent(
+ """\
+
+
+
+
+
+
+
+ """
+ )
+ )
+ ],
+ list_failures=False,
+ ),
+ (
+ dedent(
+ """\
+ # Foo
+
+ * 1 test failed
+
+ Failed tests and their output was too large to report. Download the build's log file to see the details."""
+ ),
+ "error",
+ ),
+ )
+
+ def test_report_size_limit(self):
+ self.assertEqual(
+ _generate_report(
+ "Foo",
+ [
+ junit_from_xml(
+ dedent(
+ """\
+
+
+
+
+
+
+
+ """
+ )
+ )
+ ],
+ size_limit=128,
+ ),
+ (
+ dedent(
+ """\
+ # Foo
+
+ * 1 test failed
+
+ Failed tests and their output was too large to report. Download the build's log file to see the details."""
+ ),
+ "error",
+ ),
+ )
+
+
+# Set size_limit to limit the byte size of the report. The default is 1MB as this
+# is the most that can be put into an annotation. If the generated report exceeds
+# this limit and failures are listed, it will be generated again without failures
+# listed. This minimal report will always fit into an annotation.
+# If include failures is False, total number of test will be reported but their names
+# and output will not be.
+def _generate_report(title, junit_objects, size_limit=1024 * 1024, list_failures=True):
+ if not junit_objects:
+ return ("", "success")
+
+ failures = {}
+ tests_run = 0
+ tests_skipped = 0
+ tests_failed = 0
+
+ for results in junit_objects:
+ for testsuite in results:
+ tests_run += testsuite.tests
+ tests_skipped += testsuite.skipped
+ tests_failed += testsuite.failures
+
+ for test in testsuite:
+ if (
+ not test.is_passed
+ and test.result
+ and isinstance(test.result[0], Failure)
+ ):
+ if failures.get(testsuite.name) is None:
+ failures[testsuite.name] = []
+ failures[testsuite.name].append(
+ (test.classname + "/" + test.name, test.result[0].text)
+ )
+
+ if not tests_run:
+ return ("", style)
+
+ style = "error" if tests_failed else "success"
+ report = [f"# {title}", ""]
+
+ tests_passed = tests_run - tests_skipped - tests_failed
+
+ def plural(num_tests):
+ return "test" if num_tests == 1 else "tests"
+
+ if tests_passed:
+ report.append(f"* {tests_passed} {plural(tests_passed)} passed")
+ if tests_skipped:
+ report.append(f"* {tests_skipped} {plural(tests_skipped)} skipped")
+ if tests_failed:
+ report.append(f"* {tests_failed} {plural(tests_failed)} failed")
+
+ if not list_failures:
+ report.extend(
+ [
+ "",
+ "Failed tests and their output was too large to report. "
+ "Download the build's log file to see the details.",
+ ]
+ )
+ elif failures:
+ report.extend(["", "## Failed Tests", "(click to see output)"])
+
+ for testsuite_name, failures in failures.items():
+ report.extend(["", f"### {testsuite_name}"])
+ for name, output in failures:
+ report.extend(
+ [
+ "",
+ f"{name}
",
+ "",
+ "```",
+ output,
+ "```",
+ " ",
+ ]
+ )
+
+ report = "\n".join(report)
+ if len(report.encode("utf-8")) > size_limit:
+ return _generate_report(title, junit_objects, size_limit, list_failures=False)
+
+ return report, style
+
+
+def generate_report(title, junit_files):
+ return _generate_report(title, [JUnitXml.fromfile(p) for p in junit_files])
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "title", help="Title of the test report, without Markdown formatting."
+ )
+ parser.add_argument("context", help="Annotation context to write to.")
+ parser.add_argument("junit_files", help="Paths to JUnit report files.", nargs="*")
+ args = parser.parse_args()
+
+ report, style = generate_report(args.title, args.junit_files)
+
+ if report:
+ p = subprocess.Popen(
+ [
+ "buildkite-agent",
+ "annotate",
+ "--context",
+ args.context,
+ "--style",
+ style,
+ ],
+ stdin=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ )
+
+ # The report can be larger than the buffer for command arguments so we send
+ # it over stdin instead.
+ _, err = p.communicate(input=report)
+ if p.returncode:
+ raise RuntimeError(f"Failed to send report to buildkite-agent:\n{err}")
diff --git a/.ci/monolithic-linux.sh b/.ci/monolithic-linux.sh
index b78dc59432b65c9dcdf5130fe6aea85fb2d03148..a4aeea7a16addc4206bf046a8f828f567af68bda 100755
--- a/.ci/monolithic-linux.sh
+++ b/.ci/monolithic-linux.sh
@@ -28,18 +28,26 @@ if [[ -n "${CLEAR_CACHE:-}" ]]; then
ccache --clear
fi
-function show-stats {
+function at-exit {
mkdir -p artifacts
ccache --print-stats > artifacts/ccache_stats.txt
+
+ # If building fails there will be no results files.
+ shopt -s nullglob
+ python3 "${MONOREPO_ROOT}"/.ci/generate_test_report.py ":linux: Linux x64 Test Results" \
+ "linux-x64-test-results" "${BUILD_DIR}"/test-results.*.xml
}
-trap show-stats EXIT
+trap at-exit EXIT
projects="${1}"
targets="${2}"
+lit_args="-v --xunit-xml-output ${BUILD_DIR}/test-results.xml --use-unique-output-file-name --timeout=1200 --time-tests"
+
echo "--- cmake"
pip install -q -r "${MONOREPO_ROOT}"/mlir/python/requirements.txt
pip install -q -r "${MONOREPO_ROOT}"/lldb/test/requirements.txt
+pip install -q -r "${MONOREPO_ROOT}"/.ci/requirements.txt
cmake -S "${MONOREPO_ROOT}"/llvm -B "${BUILD_DIR}" \
-D LLVM_ENABLE_PROJECTS="${projects}" \
-G Ninja \
@@ -47,7 +55,7 @@ cmake -S "${MONOREPO_ROOT}"/llvm -B "${BUILD_DIR}" \
-D LLVM_ENABLE_ASSERTIONS=ON \
-D LLVM_BUILD_EXAMPLES=ON \
-D COMPILER_RT_BUILD_LIBFUZZER=OFF \
- -D LLVM_LIT_ARGS="-v --xunit-xml-output ${BUILD_DIR}/test-results.xml --timeout=1200 --time-tests" \
+ -D LLVM_LIT_ARGS="${lit_args}" \
-D LLVM_ENABLE_LLD=ON \
-D CMAKE_CXX_FLAGS=-gmlt \
-D LLVM_CCACHE_BUILD=ON \
@@ -87,7 +95,8 @@ if [[ "${runtimes}" != "" ]]; then
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
-D CMAKE_INSTALL_PREFIX="${INSTALL_DIR}" \
-D LIBCXX_TEST_PARAMS="std=c++03" \
- -D LIBCXXABI_TEST_PARAMS="std=c++03"
+ -D LIBCXXABI_TEST_PARAMS="std=c++03" \
+ -D LLVM_LIT_ARGS="${lit_args}"
echo "--- ninja runtimes C++03"
@@ -104,7 +113,8 @@ if [[ "${runtimes}" != "" ]]; then
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
-D CMAKE_INSTALL_PREFIX="${INSTALL_DIR}" \
-D LIBCXX_TEST_PARAMS="std=c++26" \
- -D LIBCXXABI_TEST_PARAMS="std=c++26"
+ -D LIBCXXABI_TEST_PARAMS="std=c++26" \
+ -D LLVM_LIT_ARGS="${lit_args}"
echo "--- ninja runtimes C++26"
@@ -121,7 +131,8 @@ if [[ "${runtimes}" != "" ]]; then
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
-D CMAKE_INSTALL_PREFIX="${INSTALL_DIR}" \
-D LIBCXX_TEST_PARAMS="enable_modules=clang" \
- -D LIBCXXABI_TEST_PARAMS="enable_modules=clang"
+ -D LIBCXXABI_TEST_PARAMS="enable_modules=clang" \
+ -D LLVM_LIT_ARGS="${lit_args}"
echo "--- ninja runtimes clang modules"
diff --git a/.ci/monolithic-windows.sh b/.ci/monolithic-windows.sh
index 91e719c52d43632e73112e4e1a8b286af492c21b..4ead122212f4f04be414e20b0a2f0c34e4f4a6ea 100755
--- a/.ci/monolithic-windows.sh
+++ b/.ci/monolithic-windows.sh
@@ -27,17 +27,23 @@ if [[ -n "${CLEAR_CACHE:-}" ]]; then
fi
sccache --zero-stats
-function show-stats {
+function at-exit {
mkdir -p artifacts
sccache --show-stats >> artifacts/sccache_stats.txt
+
+ # If building fails there will be no results files.
+ shopt -s nullglob
+ python "${MONOREPO_ROOT}"/.ci/generate_test_report.py ":windows: Windows x64 Test Results" \
+ "windows-x64-test-results" "${BUILD_DIR}"/test-results.*.xml
}
-trap show-stats EXIT
+trap at-exit EXIT
projects="${1}"
targets="${2}"
echo "--- cmake"
pip install -q -r "${MONOREPO_ROOT}"/mlir/python/requirements.txt
+pip install -q -r "${MONOREPO_ROOT}"/.ci/requirements.txt
# The CMAKE_*_LINKER_FLAGS to disable the manifest come from research
# on fixing a build reliability issue on the build server, please
@@ -53,7 +59,7 @@ cmake -S "${MONOREPO_ROOT}"/llvm -B "${BUILD_DIR}" \
-D LLVM_ENABLE_ASSERTIONS=ON \
-D LLVM_BUILD_EXAMPLES=ON \
-D COMPILER_RT_BUILD_LIBFUZZER=OFF \
- -D LLVM_LIT_ARGS="-v --xunit-xml-output ${BUILD_DIR}/test-results.xml --timeout=1200 --time-tests" \
+ -D LLVM_LIT_ARGS="-v --xunit-xml-output ${BUILD_DIR}/test-results.xml --use-unique-output-file-name --timeout=1200 --time-tests" \
-D COMPILER_RT_BUILD_ORC=OFF \
-D CMAKE_C_COMPILER_LAUNCHER=sccache \
-D CMAKE_CXX_COMPILER_LAUNCHER=sccache \
diff --git a/.ci/requirements.txt b/.ci/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ad63858c9fdc2c7b7b61fe42a67059c1ddd46ce2
--- /dev/null
+++ b/.ci/requirements.txt
@@ -0,0 +1 @@
+junitparser==3.2.0
diff --git a/.github/new-issues-labeler.yml b/.github/new-issues-labeler.yml
index a5933d7fc9b37244828e63265ab9feb2426cdb52..860535bbe3083528dad66e64ba0045681b4ad4d3 100644
--- a/.github/new-issues-labeler.yml
+++ b/.github/new-issues-labeler.yml
@@ -27,3 +27,6 @@
'bolt':
- '/\bbolt(?!\-)\b/i'
+
+'infra:commit-access-request':
+ - '/Request Commit Access/'
diff --git a/.github/new-prs-labeler.yml b/.github/new-prs-labeler.yml
index c6746779d2dca632259552d6186dfb464ecd43d5..54290f15419d9a8d0289b3581f7797567cb3bd1e 100644
--- a/.github/new-prs-labeler.yml
+++ b/.github/new-prs-labeler.yml
@@ -587,6 +587,12 @@ llvm:ir:
- llvm/docs/LangRef.rst
- llvm/unittests/IR/**
+llvm:SandboxIR:
+ - llvm/lib/SandboxIR/**
+ - llvm/include/llvm/SandboxIR/**
+ - llvm/docs/SandboxIR.md
+ - llvm/unittests/SandboxIR/**
+
llvm:analysis:
- llvm/lib/Analysis/**
- llvm/include/llvm/Analysis/**
diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml
index 2184ddd49537b57c48ae0ad55c4829e60cc91083..472d18e73da78d63df3c30698bcb8159670c9ef6 100644
--- a/.github/workflows/libcxx-build-and-test.yaml
+++ b/.github/workflows/libcxx-build-and-test.yaml
@@ -45,12 +45,11 @@ env:
LLVM_SYMBOLIZER_PATH: "/usr/bin/llvm-symbolizer-19"
CLANG_CRASH_DIAGNOSTICS_DIR: "crash_diagnostics"
-
jobs:
stage1:
if: github.repository_owner == 'llvm'
- runs-on: libcxx-runners-set
- container: ghcr.io/libcxx/actions-builder:testing-2024-09-21
+ runs-on: libcxx-self-hosted-linux
+ container: ghcr.io/llvm/libcxx-linux-builder:0fd6f684b9c84c32d6cbfd9742402e788b2879f1
continue-on-error: false
strategy:
fail-fast: false
@@ -86,8 +85,8 @@ jobs:
**/crash_diagnostics/*
stage2:
if: github.repository_owner == 'llvm'
- runs-on: libcxx-runners-set
- container: ghcr.io/libcxx/actions-builder:testing-2024-09-21
+ runs-on: libcxx-self-hosted-linux
+ container: ghcr.io/llvm/libcxx-linux-builder:0fd6f684b9c84c32d6cbfd9742402e788b2879f1
needs: [ stage1 ]
continue-on-error: false
strategy:
@@ -161,21 +160,21 @@ jobs:
'generic-static',
'bootstrapping-build'
]
- machine: [ 'libcxx-runners-set' ]
+ machine: [ 'libcxx-self-hosted-linux' ]
include:
- config: 'generic-cxx26'
- machine: libcxx-runners-set
+ machine: libcxx-self-hosted-linux
- config: 'generic-asan'
- machine: libcxx-runners-set
+ machine: libcxx-self-hosted-linux
- config: 'generic-tsan'
- machine: libcxx-runners-set
+ machine: libcxx-self-hosted-linux
- config: 'generic-ubsan'
- machine: libcxx-runners-set
+ machine: libcxx-self-hosted-linux
# Use a larger machine for MSAN to avoid timeout and memory allocation issues.
- config: 'generic-msan'
- machine: libcxx-runners-set
+ machine: libcxx-self-hosted-linux
runs-on: ${{ matrix.machine }}
- container: ghcr.io/libcxx/actions-builder:testing-2024-09-21
+ container: ghcr.io/llvm/libcxx-linux-builder:0fd6f684b9c84c32d6cbfd9742402e788b2879f1
steps:
- uses: actions/checkout@v4
- name: ${{ matrix.config }}
@@ -202,13 +201,13 @@ jobs:
matrix:
include:
- config: generic-cxx03
- os: macos-latest
+ os: macos-15
- config: generic-cxx23
- os: macos-latest
+ os: macos-15
- config: generic-modules
- os: macos-latest
+ os: macos-15
- config: apple-configuration
- os: macos-latest
+ os: macos-15
- config: apple-system
os: macos-13
- config: apple-system-hardened
diff --git a/.github/workflows/libcxx-build-containers.yml b/.github/workflows/libcxx-build-containers.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2d040f712ce592b1190cfbdc5a8aeb1190ba1613
--- /dev/null
+++ b/.github/workflows/libcxx-build-containers.yml
@@ -0,0 +1,71 @@
+# This file defines an action that builds the various Docker images used to run
+# libc++ CI whenever modifications to those Docker files are pushed to `main`.
+#
+# The images are pushed to the LLVM package registry at https://github.com/orgs/llvm/packages
+# and tagged appropriately. The selection of which Docker image version is used by the libc++
+# CI nodes at any given point is controlled from the workflow files themselves.
+
+name: Build Docker images for libc++ CI
+
+permissions:
+ contents: read
+ packages: write
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - 'libcxx/utils/ci/**'
+ - '.github/workflows/libcxx-build-containers.yml'
+ pull_request:
+ branches:
+ - main
+ paths:
+ - 'libcxx/utils/ci/**'
+ - '.github/workflows/libcxx-build-containers.yml'
+
+jobs:
+ build-and-push:
+ runs-on: ubuntu-latest
+ if: github.repository_owner == 'llvm'
+ permissions:
+ packages: write
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Build the Linux builder image
+ working-directory: libcxx/utils/ci
+ run: docker compose build actions-builder
+ env:
+ TAG: ${{ github.sha }}
+
+ # - name: Build the Android builder image
+ # working-directory: libcxx/utils/ci
+ # run: docker compose build android-buildkite-builder
+ # env:
+ # TAG: ${{ github.sha }}
+
+ - name: Log in to GitHub Container Registry
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Push the Linux builder image
+ if: github.event_name == 'push'
+ working-directory: libcxx/utils/ci
+ run: |
+ docker compose push actions-builder
+ env:
+ TAG: ${{ github.sha }}
+
+ # - name: Push the Android builder image
+ # if: github.event_name == 'push'
+ # working-directory: libcxx/utils/ci
+ # run: |
+ # docker compose push android-buildkite-builder
+ # env:
+ # TAG: ${{ github.sha }}
diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 08ce892054874c1f842c096f9a12f1f0ae415a39..c9b0e103ed5145b6841f25c651e7ac6e4a39165e 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -723,12 +723,28 @@ public:
/// Stats for stale profile matching:
/// the total number of basic blocks in the profile
uint32_t NumStaleBlocks{0};
- /// the number of matched basic blocks
- uint32_t NumMatchedBlocks{0};
+ /// the number of exactly matched basic blocks
+ uint32_t NumExactMatchedBlocks{0};
+ /// the number of loosely matched basic blocks
+ uint32_t NumLooseMatchedBlocks{0};
+ /// the number of exactly pseudo probe matched basic blocks
+ uint32_t NumPseudoProbeExactMatchedBlocks{0};
+ /// the number of loosely pseudo probe matched basic blocks
+ uint32_t NumPseudoProbeLooseMatchedBlocks{0};
+ /// the number of call matched basic blocks
+ uint32_t NumCallMatchedBlocks{0};
/// the total count of samples in the profile
uint64_t StaleSampleCount{0};
- /// the count of matched samples
- uint64_t MatchedSampleCount{0};
+ /// the count of exactly matched samples
+ uint64_t ExactMatchedSampleCount{0};
+ /// the count of loosely matched samples
+ uint64_t LooseMatchedSampleCount{0};
+ /// the count of exactly pseudo probe matched samples
+ uint64_t PseudoProbeExactMatchedSampleCount{0};
+ /// the count of loosely pseudo probe matched samples
+ uint64_t PseudoProbeLooseMatchedSampleCount{0};
+ /// the count of call matched samples
+ uint64_t CallMatchedSampleCount{0};
/// the number of stale functions that have matching number of blocks in
/// the profile
uint64_t NumStaleFuncsWithEqualBlockCount{0};
diff --git a/bolt/include/bolt/Core/BinarySection.h b/bolt/include/bolt/Core/BinarySection.h
index d362961176b3262d259110562373eee30316f703..1093f6ad78a99016cd94a5f074282c7b39f6119a 100644
--- a/bolt/include/bolt/Core/BinarySection.h
+++ b/bolt/include/bolt/Core/BinarySection.h
@@ -87,6 +87,7 @@ class BinarySection {
// been renamed)
uint64_t OutputAddress{0}; // Section address for the rewritten binary.
uint64_t OutputSize{0}; // Section size in the rewritten binary.
+ // Can exceed OutputContents with padding.
uint64_t OutputFileOffset{0}; // File offset in the rewritten binary file.
StringRef OutputContents; // Rewritten section contents.
const uint64_t SectionNumber; // Order in which the section was created.
@@ -474,6 +475,11 @@ public:
/// Use name \p SectionName for the section during the emission.
void emitAsData(MCStreamer &Streamer, const Twine &SectionName) const;
+ /// Write finalized contents of the section. If OutputSize exceeds the size of
+ /// the OutputContents, append zero padding to the stream and return the
+ /// number of byte written which should match the OutputSize.
+ uint64_t write(raw_ostream &OS) const;
+
using SymbolResolverFuncTy = llvm::function_ref;
/// Flush all pending relocations to patch original contents of sections
@@ -497,6 +503,9 @@ public:
IsFinalized = true;
}
+ /// When writing section contents, add \p PaddingSize zero bytes at the end.
+ void addPadding(uint64_t PaddingSize) { OutputSize += PaddingSize; }
+
/// Reorder the contents of this section according to /p Order. If
/// /p Inplace is true, the entire contents of the section is reordered,
/// otherwise the new contents contain only the reordered data.
diff --git a/bolt/include/bolt/Core/Exceptions.h b/bolt/include/bolt/Core/Exceptions.h
index 422b86f6ddb7a3ac5709cb2644c034526b1626e2..f10cf776f09437f0b89a3a70aac73e692cfa8fec 100644
--- a/bolt/include/bolt/Core/Exceptions.h
+++ b/bolt/include/bolt/Core/Exceptions.h
@@ -43,17 +43,14 @@ public:
/// Generate .eh_frame_hdr from old and new .eh_frame sections.
///
- /// Take FDEs from the \p NewEHFrame unless their initial_pc is listed
- /// in \p FailedAddresses. All other entries are taken from the
+ /// Take FDEs from the \p NewEHFrame. All other entries are taken from the
/// \p OldEHFrame.
///
/// \p EHFrameHeaderAddress specifies location of .eh_frame_hdr,
/// and is required for relative addressing used in the section.
- std::vector
- generateEHFrameHeader(const DWARFDebugFrame &OldEHFrame,
- const DWARFDebugFrame &NewEHFrame,
- uint64_t EHFrameHeaderAddress,
- std::vector &FailedAddresses) const;
+ std::vector generateEHFrameHeader(const DWARFDebugFrame &OldEHFrame,
+ const DWARFDebugFrame &NewEHFrame,
+ uint64_t EHFrameHeaderAddress) const;
using FDEsMap = std::map;
diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index 32eda0b283b883bc91487eb3472d9b2a44219e88..3634fed9757ceb9c7e898f09c88bd18ef0c6df73 100644
--- a/bolt/include/bolt/Core/MCPlusBuilder.h
+++ b/bolt/include/bolt/Core/MCPlusBuilder.h
@@ -1536,6 +1536,14 @@ public:
llvm_unreachable("not implemented");
}
+ /// Match function \p BF to a long veneer for absolute code. Return true if
+ /// the match was successful and populate \p TargetAddress with an address of
+ /// the function veneer jumps to.
+ virtual bool matchAbsLongVeneer(const BinaryFunction &BF,
+ uint64_t &TargetAddress) const {
+ llvm_unreachable("not implemented");
+ }
+
virtual bool matchAdrpAddPair(const MCInst &Adrp, const MCInst &Add) const {
llvm_unreachable("not implemented");
return false;
diff --git a/bolt/include/bolt/Profile/ProfileYAMLMapping.h b/bolt/include/bolt/Profile/ProfileYAMLMapping.h
index 9865118f2696d3a2c78a5f742a0e774cd9a754d4..a8c9f7a10bbb90ad21c11bc5045839a69c9d053d 100644
--- a/bolt/include/bolt/Profile/ProfileYAMLMapping.h
+++ b/bolt/include/bolt/Profile/ProfileYAMLMapping.h
@@ -174,6 +174,9 @@ struct InlineTreeNode {
uint32_t CallSiteProbe;
// Index in PseudoProbeDesc.GUID, UINT32_MAX for same as previous (omitted)
uint32_t GUIDIndex;
+ // Decoded contents, ParentIndexDelta becomes absolute value.
+ uint64_t GUID;
+ uint64_t Hash;
bool operator==(const InlineTreeNode &) const { return false; }
};
} // end namespace bolt
diff --git a/bolt/include/bolt/Profile/YAMLProfileReader.h b/bolt/include/bolt/Profile/YAMLProfileReader.h
index a6f0fd6f3251f0debfc509efe72d4dad4518775b..91f51f30e3ca45ac5433b64df7d2f417c5c346df 100644
--- a/bolt/include/bolt/Profile/YAMLProfileReader.h
+++ b/bolt/include/bolt/Profile/YAMLProfileReader.h
@@ -14,6 +14,8 @@
#include
namespace llvm {
+class MCDecodedPseudoProbeInlineTree;
+
namespace bolt {
class YAMLProfileReader : public ProfileReaderBase {
@@ -43,6 +45,9 @@ public:
using ProfileLookupMap =
DenseMap;
+ using GUIDInlineTreeMap =
+ std::unordered_map;
+
/// A class for matching binary functions in functions in the YAML profile.
/// First, a call graph is constructed for both profiled and binary functions.
/// Then functions are hashed based on the names of their callee/caller
@@ -96,6 +101,61 @@ public:
YamlBFAdjacencyMap;
};
+ // A class for matching inline tree nodes between profile and binary.
+ // Provides the mapping from profile inline tree node id to a
+ // corresponding binary MCDecodedPseudoProbeInlineTree node.
+ //
+ // The whole mapping process is the following:
+ //
+ // (profile) (binary)
+ // | blocks ^
+ // v |
+ // yaml::bolt::BinaryBasicBlockProfile ~= FlowBlock
+ // ||| probes ^ (majority vote)
+ // v ||| BBPseudoProbeToBlock
+ // yaml::bolt::PseudoProbeInfo MCDecodedPseudoProbe
+ // | InlineTreeIndex ^
+ // v | probe id
+ // [ profile node id (uint32_t) -> MCDecodedPseudoProbeInlineTree *]
+ // InlineTreeNodeMapTy
+ class InlineTreeNodeMapTy {
+ DenseMap Map;
+
+ void mapInlineTreeNode(uint32_t ProfileNodeIdx,
+ const MCDecodedPseudoProbeInlineTree *BinaryNode) {
+ auto Res = Map.try_emplace(ProfileNodeIdx, BinaryNode);
+ assert(Res.second &&
+ "Duplicate mapping from profile node index to binary inline tree");
+ (void)Res;
+ }
+
+ public:
+ /// Returns matched InlineTree * for a given profile inline_tree_id.
+ const MCDecodedPseudoProbeInlineTree *
+ getInlineTreeNode(uint32_t ProfileInlineTreeNodeId) const {
+ auto It = Map.find(ProfileInlineTreeNodeId);
+ if (It == Map.end())
+ return nullptr;
+ return It->second;
+ }
+
+ // Match up \p YamlInlineTree with binary inline tree rooted at \p Root.
+ // Return the number of matched nodes.
+ //
+ // This function populates the mapping from profile inline tree node id to a
+ // corresponding binary MCDecodedPseudoProbeInlineTree node.
+ size_t matchInlineTrees(
+ const MCPseudoProbeDecoder &Decoder,
+ const std::vector &YamlInlineTree,
+ const MCDecodedPseudoProbeInlineTree *Root);
+ };
+
+ // Partial probe matching specification: matched inline tree and corresponding
+ // BinaryFunctionProfile
+ using ProbeMatchSpec =
+ std::pair>;
+
private:
/// Adjustments for basic samples profiles (without LBR).
bool NormalizeByInsnCount{false};
@@ -129,6 +189,13 @@ private:
/// BinaryFunction pointers indexed by YamlBP functions.
std::vector ProfileBFs;
+ // Pseudo probe function GUID to inline tree node
+ GUIDInlineTreeMap TopLevelGUIDToInlineTree;
+
+ // Mapping from a binary function to its partial match specification
+ // (YAML profile and its inline tree mapping to binary).
+ DenseMap> BFToProbeMatchSpecs;
+
/// Populate \p Function profile with the one supplied in YAML format.
bool parseFunctionProfile(BinaryFunction &Function,
const yaml::bolt::BinaryFunctionProfile &YamlBF);
@@ -139,7 +206,8 @@ private:
/// Infer function profile from stale data (collected on older binaries).
bool inferStaleProfile(BinaryFunction &Function,
- const yaml::bolt::BinaryFunctionProfile &YamlBF);
+ const yaml::bolt::BinaryFunctionProfile &YamlBF,
+ const ArrayRef ProbeMatchSpecs);
/// Initialize maps for profile matching.
void buildNameMaps(BinaryContext &BC);
@@ -156,6 +224,10 @@ private:
/// Matches functions using the call graph.
size_t matchWithCallGraph(BinaryContext &BC);
+ /// Matches functions using the call graph.
+ /// Populates BF->partial probe match spec map.
+ size_t matchWithPseudoProbes(BinaryContext &BC);
+
/// Matches functions with similarly named profiled functions.
size_t matchWithNameSimilarity(BinaryContext &BC);
diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h
index e5b7ad63007cab4294b986b256b2603d64253fb5..73d2857f946cce1cb82290ae5b3b97330bfdbfb1 100644
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -556,14 +556,6 @@ private:
return ErrOrSection ? &ErrOrSection.get() : nullptr;
}
- /// Keep track of functions we fail to write in the binary. We need to avoid
- /// rewriting CFI info for these functions.
- std::vector FailedAddresses;
-
- /// Keep track of which functions didn't fit in their original space in the
- /// last emission, so that we may either decide to split or not optimize them.
- std::set LargeFunctions;
-
/// Section header string table.
StringTableBuilder SHStrTab;
diff --git a/bolt/lib/Core/BinaryEmitter.cpp b/bolt/lib/Core/BinaryEmitter.cpp
index f6dfa249f9a9f54217b39eb399b184dcc2197acc..4b5d8154728ccd7dbd03defa6f85b94f5f5ad5dd 100644
--- a/bolt/lib/Core/BinaryEmitter.cpp
+++ b/bolt/lib/Core/BinaryEmitter.cpp
@@ -416,17 +416,6 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF,
BF.duplicateConstantIslands();
}
- if (!FF.empty() && FF.front()->isLandingPad()) {
- assert(!FF.front()->isEntryPoint() &&
- "Landing pad cannot be entry point of function");
- // If the first block of the fragment is a landing pad, it's offset from the
- // start of the area that the corresponding LSDA describes is zero. In this
- // case, the call site entries in that LSDA have 0 as offset to the landing
- // pad, which the runtime interprets as "no handler". To prevent this,
- // insert some padding.
- Streamer.emitBytes(BC.MIB->getTrapFillValue());
- }
-
// Track the first emitted instruction with debug info.
bool FirstInstr = true;
for (BinaryBasicBlock *const BB : FF) {
@@ -906,17 +895,6 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
if (Sites.empty())
return;
- // Calculate callsite table size. Size of each callsite entry is:
- //
- // sizeof(start) + sizeof(length) + sizeof(LP) + sizeof(uleb128(action))
- //
- // or
- //
- // sizeof(dwarf::DW_EH_PE_data4) * 3 + sizeof(uleb128(action))
- uint64_t CallSiteTableLength = llvm::size(Sites) * 4 * 3;
- for (const auto &FragmentCallSite : Sites)
- CallSiteTableLength += getULEB128Size(FragmentCallSite.second.Action);
-
Streamer.switchSection(BC.MOFI->getLSDASection());
const unsigned TTypeEncoding = BF.getLSDATypeEncoding();
@@ -937,74 +915,79 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
// Emit the LSDA header.
// If LPStart is omitted, then the start of the FDE is used as a base for
- // landing pad displacements. Then if a cold fragment starts with
- // a landing pad, this means that the first landing pad offset will be 0.
- // As a result, the exception handling runtime will ignore this landing pad
- // because zero offset denotes the absence of a landing pad.
- // For this reason, when the binary has fixed starting address we emit LPStart
- // as 0 and output the absolute value of the landing pad in the table.
+ // landing pad displacements. Then, if a cold fragment starts with a landing
+ // pad, this means that the first landing pad offset will be 0. However, C++
+ // runtime treats 0 as if there is no landing pad present, thus we *must* emit
+ // non-zero offsets for all valid LPs.
//
- // If the base address can change, we cannot use absolute addresses for
- // landing pads (at least not without runtime relocations). Hence, we fall
- // back to emitting landing pads relative to the FDE start.
- // As we are emitting label differences, we have to guarantee both labels are
- // defined in the same section and hence cannot place the landing pad into a
- // cold fragment when the corresponding call site is in the hot fragment.
- // Because of this issue and the previously described issue of possible
- // zero-offset landing pad we have to place landing pads in the same section
- // as the corresponding invokes for shared objects.
+ // As a solution, for fixed-address binaries we set LPStart to 0, and for
+ // position-independent binaries we set LP start to FDE start minus one byte
+ // for FDEs that start with a landing pad.
+ const bool NeedsLPAdjustment = !FF.empty() && FF.front()->isLandingPad();
std::function emitLandingPad;
if (BC.HasFixedLoadAddress) {
Streamer.emitIntValue(dwarf::DW_EH_PE_udata4, 1); // LPStart format
Streamer.emitIntValue(0, 4); // LPStart
emitLandingPad = [&](const MCSymbol *LPSymbol) {
- if (!LPSymbol)
- Streamer.emitIntValue(0, 4);
- else
+ if (LPSymbol)
Streamer.emitSymbolValue(LPSymbol, 4);
+ else
+ Streamer.emitIntValue(0, 4);
};
} else {
- Streamer.emitIntValue(dwarf::DW_EH_PE_omit, 1); // LPStart format
+ if (NeedsLPAdjustment) {
+ // Use relative LPStart format and emit LPStart as [SymbolStart - 1].
+ Streamer.emitIntValue(dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4, 1);
+ MCSymbol *DotSymbol = BC.Ctx->createTempSymbol("LPBase");
+ Streamer.emitLabel(DotSymbol);
+
+ const MCExpr *LPStartExpr = MCBinaryExpr::createSub(
+ MCSymbolRefExpr::create(StartSymbol, *BC.Ctx),
+ MCSymbolRefExpr::create(DotSymbol, *BC.Ctx), *BC.Ctx);
+ LPStartExpr = MCBinaryExpr::createSub(
+ LPStartExpr, MCConstantExpr::create(1, *BC.Ctx), *BC.Ctx);
+ Streamer.emitValue(LPStartExpr, 4);
+ } else {
+ // DW_EH_PE_omit means FDE start (StartSymbol) will be used as LPStart.
+ Streamer.emitIntValue(dwarf::DW_EH_PE_omit, 1);
+ }
emitLandingPad = [&](const MCSymbol *LPSymbol) {
- if (!LPSymbol)
- Streamer.emitIntValue(0, 4);
- else
- Streamer.emitAbsoluteSymbolDiff(LPSymbol, StartSymbol, 4);
+ if (LPSymbol) {
+ const MCExpr *LPOffsetExpr = MCBinaryExpr::createSub(
+ MCSymbolRefExpr::create(LPSymbol, *BC.Ctx),
+ MCSymbolRefExpr::create(StartSymbol, *BC.Ctx), *BC.Ctx);
+ if (NeedsLPAdjustment)
+ LPOffsetExpr = MCBinaryExpr::createAdd(
+ LPOffsetExpr, MCConstantExpr::create(1, *BC.Ctx), *BC.Ctx);
+ Streamer.emitULEB128Value(LPOffsetExpr);
+ } else {
+ Streamer.emitULEB128IntValue(0);
+ }
};
}
Streamer.emitIntValue(TTypeEncoding, 1); // TType format
- // See the comment in EHStreamer::emitExceptionTable() on to use
- // uleb128 encoding (which can use variable number of bytes to encode the same
- // value) to ensure type info table is properly aligned at 4 bytes without
- // iteratively fixing sizes of the tables.
- unsigned CallSiteTableLengthSize = getULEB128Size(CallSiteTableLength);
- unsigned TTypeBaseOffset =
- sizeof(int8_t) + // Call site format
- CallSiteTableLengthSize + // Call site table length size
- CallSiteTableLength + // Call site table length
- BF.getLSDAActionTable().size() + // Actions table size
- BF.getLSDATypeTable().size() * TTypeEncodingSize; // Types table size
- unsigned TTypeBaseOffsetSize = getULEB128Size(TTypeBaseOffset);
- unsigned TotalSize = sizeof(int8_t) + // LPStart format
- sizeof(int8_t) + // TType format
- TTypeBaseOffsetSize + // TType base offset size
- TTypeBaseOffset; // TType base offset
- unsigned SizeAlign = (4 - TotalSize) & 3;
+ MCSymbol *TTBaseLabel = nullptr;
+ if (TTypeEncoding != dwarf::DW_EH_PE_omit) {
+ TTBaseLabel = BC.Ctx->createTempSymbol("TTBase");
+ MCSymbol *TTBaseRefLabel = BC.Ctx->createTempSymbol("TTBaseRef");
+ Streamer.emitAbsoluteSymbolDiffAsULEB128(TTBaseLabel, TTBaseRefLabel);
+ Streamer.emitLabel(TTBaseRefLabel);
+ }
- if (TTypeEncoding != dwarf::DW_EH_PE_omit)
- // Account for any extra padding that will be added to the call site table
- // length.
- Streamer.emitULEB128IntValue(TTypeBaseOffset,
- /*PadTo=*/TTypeBaseOffsetSize + SizeAlign);
+ // Emit encoding of entries in the call site table. The format is used for the
+ // call site start, length, and corresponding landing pad.
+ if (BC.HasFixedLoadAddress)
+ Streamer.emitIntValue(dwarf::DW_EH_PE_sdata4, 1);
+ else
+ Streamer.emitIntValue(dwarf::DW_EH_PE_uleb128, 1);
- // Emit the landing pad call site table. We use signed data4 since we can emit
- // a landing pad in a different part of the split function that could appear
- // earlier in the address space than LPStart.
- Streamer.emitIntValue(dwarf::DW_EH_PE_sdata4, 1);
- Streamer.emitULEB128IntValue(CallSiteTableLength);
+ MCSymbol *CSTStartLabel = BC.Ctx->createTempSymbol("CSTStart");
+ MCSymbol *CSTEndLabel = BC.Ctx->createTempSymbol("CSTEnd");
+ Streamer.emitAbsoluteSymbolDiffAsULEB128(CSTEndLabel, CSTStartLabel);
+ Streamer.emitLabel(CSTStartLabel);
for (const auto &FragmentCallSite : Sites) {
const BinaryFunction::CallSite &CallSite = FragmentCallSite.second;
const MCSymbol *BeginLabel = CallSite.Start;
@@ -1015,11 +998,17 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
// Start of the range is emitted relative to the start of current
// function split part.
- Streamer.emitAbsoluteSymbolDiff(BeginLabel, StartSymbol, 4);
- Streamer.emitAbsoluteSymbolDiff(EndLabel, BeginLabel, 4);
+ if (BC.HasFixedLoadAddress) {
+ Streamer.emitAbsoluteSymbolDiff(BeginLabel, StartSymbol, 4);
+ Streamer.emitAbsoluteSymbolDiff(EndLabel, BeginLabel, 4);
+ } else {
+ Streamer.emitAbsoluteSymbolDiffAsULEB128(BeginLabel, StartSymbol);
+ Streamer.emitAbsoluteSymbolDiffAsULEB128(EndLabel, BeginLabel);
+ }
emitLandingPad(CallSite.LP);
Streamer.emitULEB128IntValue(CallSite.Action);
}
+ Streamer.emitLabel(CSTEndLabel);
// Write out action, type, and type index tables at the end.
//
@@ -1038,6 +1027,8 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
assert(TypeTable.size() == BF.getLSDATypeTable().size() &&
"indirect type table size mismatch");
+ Streamer.emitValueToAlignment(Align(TTypeAlignment));
+
for (int Index = TypeTable.size() - 1; Index >= 0; --Index) {
const uint64_t TypeAddress = TypeTable[Index];
switch (TTypeEncoding & 0x70) {
@@ -1063,6 +1054,10 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
}
}
}
+
+ if (TTypeEncoding != dwarf::DW_EH_PE_omit)
+ Streamer.emitLabel(TTBaseLabel);
+
for (uint8_t const &Byte : BF.getLSDATypeIndexTable())
Streamer.emitIntValue(Byte, 1);
}
diff --git a/bolt/lib/Core/BinarySection.cpp b/bolt/lib/Core/BinarySection.cpp
index 9ad49ca1b3a03832a31d77aad54e89dfa850e3dc..b16e0a4333aa2d7000ff47d92c6035bb5fc07d67 100644
--- a/bolt/lib/Core/BinarySection.cpp
+++ b/bolt/lib/Core/BinarySection.cpp
@@ -142,6 +142,15 @@ void BinarySection::emitAsData(MCStreamer &Streamer,
Streamer.emitLabel(BC.Ctx->getOrCreateSymbol("__hot_data_end"));
}
+uint64_t BinarySection::write(raw_ostream &OS) const {
+ const uint64_t NumValidContentBytes =
+ std::min(getOutputContents().size(), getOutputSize());
+ OS.write(getOutputContents().data(), NumValidContentBytes);
+ if (getOutputSize() > NumValidContentBytes)
+ OS.write_zeros(getOutputSize() - NumValidContentBytes);
+ return getOutputSize();
+}
+
void BinarySection::flushPendingRelocations(raw_pwrite_stream &OS,
SymbolResolverFuncTy Resolver) {
if (PendingRelocations.empty() && Patches.empty())
diff --git a/bolt/lib/Core/Exceptions.cpp b/bolt/lib/Core/Exceptions.cpp
index 6a46a49a983d8b5797bc8111fc7b89a13ee0d987..0b2e63b8ca6a790af4219122b24714d44b8664bc 100644
--- a/bolt/lib/Core/Exceptions.cpp
+++ b/bolt/lib/Core/Exceptions.cpp
@@ -108,8 +108,7 @@ Error BinaryFunction::parseLSDA(ArrayRef LSDASectionData,
DWARFDataExtractor Data(
StringRef(reinterpret_cast(LSDASectionData.data()),
LSDASectionData.size()),
- BC.DwCtx->getDWARFObj().isLittleEndian(),
- BC.DwCtx->getDWARFObj().getAddressSize());
+ BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize());
uint64_t Offset = getLSDAAddress() - LSDASectionAddress;
assert(Data.isValidOffset(Offset) && "wrong LSDA address");
@@ -666,16 +665,13 @@ bool CFIReaderWriter::fillCFIInfoFor(BinaryFunction &Function) const {
return true;
}
-std::vector CFIReaderWriter::generateEHFrameHeader(
- const DWARFDebugFrame &OldEHFrame, const DWARFDebugFrame &NewEHFrame,
- uint64_t EHFrameHeaderAddress,
- std::vector &FailedAddresses) const {
+std::vector
+CFIReaderWriter::generateEHFrameHeader(const DWARFDebugFrame &OldEHFrame,
+ const DWARFDebugFrame &NewEHFrame,
+ uint64_t EHFrameHeaderAddress) const {
// Common PC -> FDE map to be written into .eh_frame_hdr.
std::map PCToFDE;
- // Presort array for binary search.
- llvm::sort(FailedAddresses);
-
// Initialize PCToFDE using NewEHFrame.
for (dwarf::FrameEntry &Entry : NewEHFrame.entries()) {
const dwarf::FDE *FDE = dyn_cast(&Entry);
@@ -690,13 +686,7 @@ std::vector CFIReaderWriter::generateEHFrameHeader(
continue;
// Add the address to the map unless we failed to write it.
- if (!std::binary_search(FailedAddresses.begin(), FailedAddresses.end(),
- FuncAddress)) {
- LLVM_DEBUG(dbgs() << "BOLT-DEBUG: FDE for function at 0x"
- << Twine::utohexstr(FuncAddress) << " is at 0x"
- << Twine::utohexstr(FDEAddress) << '\n');
- PCToFDE[FuncAddress] = FDEAddress;
- }
+ PCToFDE[FuncAddress] = FDEAddress;
};
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: new .eh_frame contains "
diff --git a/bolt/lib/Passes/BinaryPasses.cpp b/bolt/lib/Passes/BinaryPasses.cpp
index 5a676185227ec1b00112ea847268928d983bcafc..03d3dd75a033683c2e0760751b793784d3f65e2c 100644
--- a/bolt/lib/Passes/BinaryPasses.cpp
+++ b/bolt/lib/Passes/BinaryPasses.cpp
@@ -588,10 +588,18 @@ Error CheckLargeFunctions::runOnFunctions(BinaryContext &BC) {
uint64_t HotSize, ColdSize;
std::tie(HotSize, ColdSize) =
BC.calculateEmittedSize(BF, /*FixBranches=*/false);
- if (HotSize > BF.getMaxSize()) {
+ uint64_t MainFragmentSize = HotSize;
+ if (BF.hasIslandsInfo()) {
+ MainFragmentSize +=
+ offsetToAlignment(BF.getAddress() + MainFragmentSize,
+ Align(BF.getConstantIslandAlignment()));
+ MainFragmentSize += BF.estimateConstantIslandSize();
+ }
+ if (MainFragmentSize > BF.getMaxSize()) {
if (opts::PrintLargeFunctions)
- BC.outs() << "BOLT-INFO: " << BF << " size exceeds allocated space by "
- << (HotSize - BF.getMaxSize()) << " bytes\n";
+ BC.outs() << "BOLT-INFO: " << BF << " size of " << MainFragmentSize
+ << " bytes exceeds allocated space by "
+ << (MainFragmentSize - BF.getMaxSize()) << " bytes\n";
BF.setSimple(false);
}
};
@@ -1549,10 +1557,48 @@ Error PrintProgramStats::runOnFunctions(BinaryContext &BC) {
"BOLT-INFO: inference found an exact match for %.2f%% of basic blocks"
" (%zu out of %zu stale) responsible for %.2f%% samples"
" (%zu out of %zu stale)\n",
- 100.0 * BC.Stats.NumMatchedBlocks / BC.Stats.NumStaleBlocks,
- BC.Stats.NumMatchedBlocks, BC.Stats.NumStaleBlocks,
- 100.0 * BC.Stats.MatchedSampleCount / BC.Stats.StaleSampleCount,
- BC.Stats.MatchedSampleCount, BC.Stats.StaleSampleCount);
+ 100.0 * BC.Stats.NumExactMatchedBlocks / BC.Stats.NumStaleBlocks,
+ BC.Stats.NumExactMatchedBlocks, BC.Stats.NumStaleBlocks,
+ 100.0 * BC.Stats.ExactMatchedSampleCount / BC.Stats.StaleSampleCount,
+ BC.Stats.ExactMatchedSampleCount, BC.Stats.StaleSampleCount);
+ BC.outs() << format(
+ "BOLT-INFO: inference found an exact pseudo probe match for %.2f%% of "
+ "basic blocks (%zu out of %zu stale) responsible for %.2f%% samples"
+ " (%zu out of %zu stale)\n",
+ 100.0 * BC.Stats.NumPseudoProbeExactMatchedBlocks /
+ BC.Stats.NumStaleBlocks,
+ BC.Stats.NumPseudoProbeExactMatchedBlocks, BC.Stats.NumStaleBlocks,
+ 100.0 * BC.Stats.PseudoProbeExactMatchedSampleCount /
+ BC.Stats.StaleSampleCount,
+ BC.Stats.PseudoProbeExactMatchedSampleCount, BC.Stats.StaleSampleCount);
+ BC.outs() << format(
+ "BOLT-INFO: inference found a loose pseudo probe match for %.2f%% of "
+ "basic blocks (%zu out of %zu stale) responsible for %.2f%% samples"
+ " (%zu out of %zu stale)\n",
+ 100.0 * BC.Stats.NumPseudoProbeLooseMatchedBlocks /
+ BC.Stats.NumStaleBlocks,
+ BC.Stats.NumPseudoProbeLooseMatchedBlocks, BC.Stats.NumStaleBlocks,
+ 100.0 * BC.Stats.PseudoProbeLooseMatchedSampleCount /
+ BC.Stats.StaleSampleCount,
+ BC.Stats.PseudoProbeLooseMatchedSampleCount, BC.Stats.StaleSampleCount);
+ BC.outs() << format(
+ "BOLT-INFO: inference found a call match for %.2f%% of basic "
+ "blocks"
+ " (%zu out of %zu stale) responsible for %.2f%% samples"
+ " (%zu out of %zu stale)\n",
+ 100.0 * BC.Stats.NumCallMatchedBlocks / BC.Stats.NumStaleBlocks,
+ BC.Stats.NumCallMatchedBlocks, BC.Stats.NumStaleBlocks,
+ 100.0 * BC.Stats.CallMatchedSampleCount / BC.Stats.StaleSampleCount,
+ BC.Stats.CallMatchedSampleCount, BC.Stats.StaleSampleCount);
+ BC.outs() << format(
+ "BOLT-INFO: inference found a loose match for %.2f%% of basic "
+ "blocks"
+ " (%zu out of %zu stale) responsible for %.2f%% samples"
+ " (%zu out of %zu stale)\n",
+ 100.0 * BC.Stats.NumLooseMatchedBlocks / BC.Stats.NumStaleBlocks,
+ BC.Stats.NumLooseMatchedBlocks, BC.Stats.NumStaleBlocks,
+ 100.0 * BC.Stats.LooseMatchedSampleCount / BC.Stats.StaleSampleCount,
+ BC.Stats.LooseMatchedSampleCount, BC.Stats.StaleSampleCount);
}
if (const uint64_t NumUnusedObjects = BC.getNumUnusedProfiledObjects()) {
diff --git a/bolt/lib/Passes/VeneerElimination.cpp b/bolt/lib/Passes/VeneerElimination.cpp
index 8bf0359477c6584dd93f46a4bc0ddf5191275c93..b386b2756a2b8731fe721e9bf6afdb71083db6a1 100644
--- a/bolt/lib/Passes/VeneerElimination.cpp
+++ b/bolt/lib/Passes/VeneerElimination.cpp
@@ -29,30 +29,47 @@ static llvm::cl::opt
namespace llvm {
namespace bolt {
+static bool isPossibleVeneer(const BinaryFunction &BF) {
+ return BF.isAArch64Veneer() || BF.getOneName().starts_with("__AArch64");
+}
+
Error VeneerElimination::runOnFunctions(BinaryContext &BC) {
if (!opts::EliminateVeneers || !BC.isAArch64())
return Error::success();
- std::map &BFs = BC.getBinaryFunctions();
std::unordered_map VeneerDestinations;
- uint64_t VeneersCount = 0;
- for (auto &It : BFs) {
- BinaryFunction &VeneerFunction = It.second;
- if (!VeneerFunction.isAArch64Veneer())
+ uint64_t NumEliminatedVeneers = 0;
+ for (BinaryFunction &BF : llvm::make_second_range(BC.getBinaryFunctions())) {
+ if (!isPossibleVeneer(BF))
continue;
- VeneersCount++;
- VeneerFunction.setPseudo(true);
- MCInst &FirstInstruction = *(VeneerFunction.begin()->begin());
- const MCSymbol *VeneerTargetSymbol =
- BC.MIB->getTargetSymbol(FirstInstruction, 1);
- assert(VeneerTargetSymbol && "Expecting target symbol for instruction");
- for (const MCSymbol *Symbol : VeneerFunction.getSymbols())
+ if (BF.isIgnored())
+ continue;
+
+ const MCSymbol *VeneerTargetSymbol = 0;
+ uint64_t TargetAddress;
+ if (BC.MIB->matchAbsLongVeneer(BF, TargetAddress)) {
+ if (BinaryFunction *TargetBF =
+ BC.getBinaryFunctionAtAddress(TargetAddress))
+ VeneerTargetSymbol = TargetBF->getSymbol();
+ } else {
+ MCInst &FirstInstruction = *(BF.begin()->begin());
+ if (BC.MIB->hasAnnotation(FirstInstruction, "AArch64Veneer"))
+ VeneerTargetSymbol = BC.MIB->getTargetSymbol(FirstInstruction, 1);
+ }
+
+ if (!VeneerTargetSymbol)
+ continue;
+
+ for (const MCSymbol *Symbol : BF.getSymbols())
VeneerDestinations[Symbol] = VeneerTargetSymbol;
+
+ NumEliminatedVeneers++;
+ BF.setPseudo(true);
}
BC.outs() << "BOLT-INFO: number of removed linker-inserted veneers: "
- << VeneersCount << "\n";
+ << NumEliminatedVeneers << '\n';
// Handle veneers to veneers in case they occur
for (auto &Entry : VeneerDestinations) {
@@ -65,9 +82,8 @@ Error VeneerElimination::runOnFunctions(BinaryContext &BC) {
}
uint64_t VeneerCallers = 0;
- for (auto &It : BFs) {
- BinaryFunction &Function = It.second;
- for (BinaryBasicBlock &BB : Function) {
+ for (BinaryFunction &BF : llvm::make_second_range(BC.getBinaryFunctions())) {
+ for (BinaryBasicBlock &BB : BF) {
for (MCInst &Instr : BB) {
if (!BC.MIB->isCall(Instr) || BC.MIB->isIndirectCall(Instr))
continue;
diff --git a/bolt/lib/Profile/StaleProfileMatching.cpp b/bolt/lib/Profile/StaleProfileMatching.cpp
index cd6e96f7e2cf47f14a7a2900a9cb8abf24498934..b66a3f478f1a7b0941851eff16a181c1ff85b14b 100644
--- a/bolt/lib/Profile/StaleProfileMatching.cpp
+++ b/bolt/lib/Profile/StaleProfileMatching.cpp
@@ -29,6 +29,7 @@
#include "bolt/Profile/YAMLProfileReader.h"
#include "llvm/ADT/Bitfields.h"
#include "llvm/ADT/Hashing.h"
+#include "llvm/MC/MCPseudoProbe.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/xxhash.h"
@@ -116,6 +117,11 @@ cl::opt StaleMatchingCostJumpUnknownFTInc(
"The cost of increasing an unknown fall-through jump count by one."),
cl::init(3), cl::ReallyHidden, cl::cat(BoltOptCategory));
+cl::opt StaleMatchingWithPseudoProbes(
+ "stale-matching-with-pseudo-probes",
+ cl::desc("Turns on stale matching with block pseudo probes."),
+ cl::init(false), cl::ReallyHidden, cl::cat(BoltOptCategory));
+
} // namespace opts
namespace llvm {
@@ -208,11 +214,48 @@ public:
}
}
- /// Find the most similar block for a given hash.
- const FlowBlock *matchBlock(BlendedBlockHash BlendedHash,
- uint64_t CallHash) const {
- const FlowBlock *BestBlock = matchWithOpcodes(BlendedHash);
- return BestBlock ? BestBlock : matchWithCalls(BlendedHash, CallHash);
+ /// Creates a mapping from a pseudo probe to a flow block.
+ void mapProbeToBB(const MCDecodedPseudoProbe *Probe, FlowBlock *Block) {
+ BBPseudoProbeToBlock[Probe] = Block;
+ }
+
+ enum MatchMethod : char {
+ MATCH_EXACT = 0,
+ MATCH_PROBE_EXACT,
+ MATCH_PROBE_LOOSE,
+ MATCH_OPCODE,
+ MATCH_CALL,
+ NO_MATCH
+ };
+
+ /// Find the most similar flow block for a profile block given blended hash.
+ std::pair
+ matchBlockStrict(BlendedBlockHash BlendedHash) {
+ const auto &[Block, ExactHash] = matchWithOpcodes(BlendedHash);
+ if (Block && ExactHash)
+ return {Block, MATCH_EXACT};
+ return {nullptr, NO_MATCH};
+ }
+
+ /// Find the most similar flow block for a profile block given pseudo probes.
+ std::pair matchBlockProbe(
+ const ArrayRef PseudoProbes,
+ const YAMLProfileReader::InlineTreeNodeMapTy &InlineTreeNodeMap) {
+ const auto &[ProbeBlock, ExactProbe] =
+ matchWithPseudoProbes(PseudoProbes, InlineTreeNodeMap);
+ if (ProbeBlock)
+ return {ProbeBlock, ExactProbe ? MATCH_PROBE_EXACT : MATCH_PROBE_LOOSE};
+ return {nullptr, NO_MATCH};
+ }
+
+ /// Find the most similar flow block for a profile block given its hashes.
+ std::pair
+ matchBlockLoose(BlendedBlockHash BlendedHash, uint64_t CallHash) {
+ if (const FlowBlock *CallBlock = matchWithCalls(BlendedHash, CallHash))
+ return {CallBlock, MATCH_CALL};
+ if (const FlowBlock *OpcodeBlock = matchWithOpcodes(BlendedHash).first)
+ return {OpcodeBlock, MATCH_OPCODE};
+ return {nullptr, NO_MATCH};
}
/// Returns true if the two basic blocks (in the binary and in the profile)
@@ -227,22 +270,26 @@ private:
using HashBlockPairType = std::pair;
std::unordered_map> OpHashToBlocks;
std::unordered_map> CallHashToBlocks;
+ DenseMap BBPseudoProbeToBlock;
// Uses OpcodeHash to find the most similar block for a given hash.
- const FlowBlock *matchWithOpcodes(BlendedBlockHash BlendedHash) const {
+ std::pair
+ matchWithOpcodes(BlendedBlockHash BlendedHash) const {
auto BlockIt = OpHashToBlocks.find(BlendedHash.OpcodeHash);
if (BlockIt == OpHashToBlocks.end())
- return nullptr;
+ return {nullptr, false};
FlowBlock *BestBlock = nullptr;
uint64_t BestDist = std::numeric_limits::max();
+ BlendedBlockHash BestHash;
for (const auto &[Hash, Block] : BlockIt->second) {
uint64_t Dist = Hash.distance(BlendedHash);
if (BestBlock == nullptr || Dist < BestDist) {
BestDist = Dist;
BestBlock = Block;
+ BestHash = Hash;
}
}
- return BestBlock;
+ return {BestBlock, isHighConfidenceMatch(BestHash, BlendedHash)};
}
// Uses CallHash to find the most similar block for a given hash.
@@ -266,6 +313,73 @@ private:
}
return BestBlock;
}
+
+ /// Matches a profile block with a binary block based on pseudo probes.
+ /// Returns the best matching block (or nullptr) and whether the match is
+ /// unambiguous.
+ std::pair matchWithPseudoProbes(
+ const ArrayRef BlockPseudoProbes,
+ const YAMLProfileReader::InlineTreeNodeMapTy &InlineTreeNodeMap) const {
+
+ if (!opts::StaleMatchingWithPseudoProbes)
+ return {nullptr, false};
+
+ DenseMap FlowBlockMatchCount;
+
+ auto matchProfileProbeToBlock = [&](uint32_t NodeId,
+ uint64_t ProbeId) -> const FlowBlock * {
+ const MCDecodedPseudoProbeInlineTree *BinaryNode =
+ InlineTreeNodeMap.getInlineTreeNode(NodeId);
+ if (!BinaryNode)
+ return nullptr;
+ const MCDecodedPseudoProbe Dummy(0, ProbeId, PseudoProbeType::Block, 0, 0,
+ nullptr);
+ ArrayRef BinaryProbes = BinaryNode->getProbes();
+ auto BinaryProbeIt = llvm::lower_bound(
+ BinaryProbes, Dummy, [](const auto &LHS, const auto &RHS) {
+ return LHS.getIndex() < RHS.getIndex();
+ });
+ if (BinaryProbeIt == BinaryNode->getProbes().end() ||
+ BinaryProbeIt->getIndex() != ProbeId)
+ return nullptr;
+ auto It = BBPseudoProbeToBlock.find(&*BinaryProbeIt);
+ if (It == BBPseudoProbeToBlock.end())
+ return nullptr;
+ return It->second;
+ };
+
+ auto matchPseudoProbeInfo = [&](const yaml::bolt::PseudoProbeInfo
+ &ProfileProbe,
+ uint32_t NodeId) {
+ for (uint64_t Index = 0; Index < 64; ++Index)
+ if (ProfileProbe.BlockMask & 1ull << Index)
+ ++FlowBlockMatchCount[matchProfileProbeToBlock(NodeId, Index + 1)];
+ for (const auto &ProfileProbes :
+ {ProfileProbe.BlockProbes, ProfileProbe.IndCallProbes,
+ ProfileProbe.CallProbes})
+ for (uint64_t ProfileProbe : ProfileProbes)
+ ++FlowBlockMatchCount[matchProfileProbeToBlock(NodeId, ProfileProbe)];
+ };
+
+ for (const yaml::bolt::PseudoProbeInfo &ProfileProbe : BlockPseudoProbes) {
+ if (!ProfileProbe.InlineTreeNodes.empty())
+ for (uint32_t ProfileInlineTreeNode : ProfileProbe.InlineTreeNodes)
+ matchPseudoProbeInfo(ProfileProbe, ProfileInlineTreeNode);
+ else
+ matchPseudoProbeInfo(ProfileProbe, ProfileProbe.InlineTreeIndex);
+ }
+ uint32_t BestMatchCount = 0;
+ uint32_t TotalMatchCount = 0;
+ const FlowBlock *BestMatchBlock = nullptr;
+ for (const auto &[FlowBlock, Count] : FlowBlockMatchCount) {
+ TotalMatchCount += Count;
+ if (Count < BestMatchCount || (Count == BestMatchCount && BestMatchBlock))
+ continue;
+ BestMatchBlock = FlowBlock;
+ BestMatchCount = Count;
+ }
+ return {BestMatchBlock, BestMatchCount == TotalMatchCount};
+ }
};
void BinaryFunction::computeBlockHashes(HashFunction HashFunction) const {
@@ -442,17 +556,18 @@ createFlowFunction(const BinaryFunction::BasicBlockOrderType &BlockOrder) {
/// is to extract as much information from the stale profile as possible. Here
/// we assume that each basic block is specified via a hash value computed from
/// its content and the hashes of the unchanged basic blocks stay the same
-/// across different revisions of the binary.
+/// across different revisions of the binary. Blocks may also have pseudo probe
+/// information in the profile and the binary which is used for matching.
/// Whenever there is a count in the profile with the hash corresponding to one
/// of the basic blocks in the binary, the count is "matched" to the block.
/// Similarly, if both the source and the target of a count in the profile are
/// matched to a jump in the binary, the count is recorded in CFG.
-size_t
-matchWeightsByHashes(BinaryContext &BC,
- const BinaryFunction::BasicBlockOrderType &BlockOrder,
- const yaml::bolt::BinaryFunctionProfile &YamlBF,
- FlowFunction &Func, HashFunction HashFunction,
- YAMLProfileReader::ProfileLookupMap &IdToYamlBF) {
+size_t matchWeights(
+ BinaryContext &BC, const BinaryFunction::BasicBlockOrderType &BlockOrder,
+ const yaml::bolt::BinaryFunctionProfile &YamlBF, FlowFunction &Func,
+ HashFunction HashFunction, YAMLProfileReader::ProfileLookupMap &IdToYamlBF,
+ const BinaryFunction &BF,
+ const ArrayRef ProbeMatchSpecs) {
assert(Func.Blocks.size() == BlockOrder.size() + 2);
@@ -482,16 +597,67 @@ matchWeightsByHashes(BinaryContext &BC,
<< Twine::utohexstr(BB->getHash()) << "\n");
}
StaleMatcher Matcher;
+ // Collects function pseudo probes for use in the StaleMatcher.
+ if (opts::StaleMatchingWithPseudoProbes) {
+ const MCPseudoProbeDecoder *Decoder = BC.getPseudoProbeDecoder();
+ assert(Decoder &&
+ "If pseudo probes are in use, pseudo probe decoder should exist");
+ const AddressProbesMap &ProbeMap = Decoder->getAddress2ProbesMap();
+ const uint64_t FuncAddr = BF.getAddress();
+ for (const MCDecodedPseudoProbe &Probe :
+ ProbeMap.find(FuncAddr, FuncAddr + BF.getSize()))
+ if (const BinaryBasicBlock *BB =
+ BF.getBasicBlockContainingOffset(Probe.getAddress() - FuncAddr))
+ Matcher.mapProbeToBB(&Probe, Blocks[BB->getIndex()]);
+ }
Matcher.init(Blocks, BlendedHashes, CallHashes);
- // Index in yaml profile => corresponding (matched) block
- DenseMap MatchedBlocks;
- // Match blocks from the profile to the blocks in CFG
+ using FlowBlockTy =
+ std::pair;
+ using ProfileBlockMatchMap = DenseMap;
+ // Binary profile => block index => matched block + its block profile
+ DenseMap
+ MatchedBlocks;
+
+ // Map of FlowBlock and matching method.
+ DenseMap MatchedFlowBlocks;
+
+ auto addMatchedBlock =
+ [&](std::pair BlockMethod,
+ const yaml::bolt::BinaryFunctionProfile &YamlBP,
+ const yaml::bolt::BinaryBasicBlockProfile &YamlBB) {
+ const auto &[MatchedBlock, Method] = BlockMethod;
+ if (!MatchedBlock)
+ return;
+ // Don't override earlier matches
+ if (MatchedFlowBlocks.contains(MatchedBlock))
+ return;
+ MatchedFlowBlocks.try_emplace(MatchedBlock, Method);
+ MatchedBlocks[&YamlBP][YamlBB.Index] = {MatchedBlock, &YamlBB};
+ };
+
+ // Match blocks from the profile to the blocks in CFG by strict hash.
+ for (const yaml::bolt::BinaryBasicBlockProfile &YamlBB : YamlBF.Blocks) {
+ // Update matching stats.
+ ++BC.Stats.NumStaleBlocks;
+ BC.Stats.StaleSampleCount += YamlBB.ExecCount;
+
+ assert(YamlBB.Hash != 0 && "empty hash of BinaryBasicBlockProfile");
+ BlendedBlockHash YamlHash(YamlBB.Hash);
+ addMatchedBlock(Matcher.matchBlockStrict(YamlHash), YamlBF, YamlBB);
+ }
+ // Match blocks from the profile to the blocks in CFG by pseudo probes.
+ for (const auto &[InlineNodeMap, YamlBP] : ProbeMatchSpecs) {
+ for (const yaml::bolt::BinaryBasicBlockProfile &BB : YamlBP.get().Blocks)
+ if (!BB.PseudoProbes.empty())
+ addMatchedBlock(Matcher.matchBlockProbe(BB.PseudoProbes, InlineNodeMap),
+ YamlBP, BB);
+ }
+ // Match blocks from the profile to the blocks in CFG with loose methods.
for (const yaml::bolt::BinaryBasicBlockProfile &YamlBB : YamlBF.Blocks) {
assert(YamlBB.Hash != 0 && "empty hash of BinaryBasicBlockProfile");
BlendedBlockHash YamlHash(YamlBB.Hash);
- const FlowBlock *MatchedBlock = nullptr;
std::string CallHashStr = hashBlockCalls(IdToYamlBF, YamlBB);
uint64_t CallHash = 0;
if (!CallHashStr.empty()) {
@@ -502,76 +668,104 @@ matchWeightsByHashes(BinaryContext &BC,
else
llvm_unreachable("Unhandled HashFunction");
}
- MatchedBlock = Matcher.matchBlock(YamlHash, CallHash);
- if (MatchedBlock == nullptr && YamlBB.Index == 0)
+ auto [MatchedBlock, Method] = Matcher.matchBlockLoose(YamlHash, CallHash);
+ if (MatchedBlock == nullptr && YamlBB.Index == 0) {
MatchedBlock = Blocks[0];
- if (MatchedBlock != nullptr) {
- const BinaryBasicBlock *BB = BlockOrder[MatchedBlock->Index - 1];
- MatchedBlocks[YamlBB.Index] = MatchedBlock;
+ // Report as loose match
+ Method = StaleMatcher::MATCH_OPCODE;
+ }
+ if (!MatchedBlock) {
+ LLVM_DEBUG(dbgs() << "Couldn't match yaml block (bid = " << YamlBB.Index
+ << ")" << " with hash " << Twine::utohexstr(YamlBB.Hash)
+ << "\n");
+ continue;
+ }
+ addMatchedBlock({MatchedBlock, Method}, YamlBF, YamlBB);
+ }
+
+ // Match jumps from the profile to the jumps from CFG
+ std::vector OutWeight(Func.Blocks.size(), 0);
+ std::vector InWeight(Func.Blocks.size(), 0);
+
+ for (const auto &[YamlBF, MatchMap] : MatchedBlocks) {
+ for (const auto &[YamlBBIdx, FlowBlockProfile] : MatchMap) {
+ const auto &[MatchedBlock, YamlBB] = FlowBlockProfile;
+ StaleMatcher::MatchMethod Method = MatchedFlowBlocks.lookup(MatchedBlock);
BlendedBlockHash BinHash = BlendedHashes[MatchedBlock->Index - 1];
- LLVM_DEBUG(dbgs() << "Matched yaml block (bid = " << YamlBB.Index << ")"
- << " with hash " << Twine::utohexstr(YamlBB.Hash)
+ LLVM_DEBUG(dbgs() << "Matched yaml block (bid = " << YamlBBIdx << ")"
+ << " with hash " << Twine::utohexstr(YamlBB->Hash)
<< " to BB (index = " << MatchedBlock->Index - 1 << ")"
<< " with hash " << Twine::utohexstr(BinHash.combine())
<< "\n");
+ (void)BinHash;
+ uint64_t ExecCount = YamlBB->ExecCount;
// Update matching stats accounting for the matched block.
- if (Matcher.isHighConfidenceMatch(BinHash, YamlHash)) {
- ++BC.Stats.NumMatchedBlocks;
- BC.Stats.MatchedSampleCount += YamlBB.ExecCount;
+ switch (Method) {
+ case StaleMatcher::MATCH_EXACT:
+ ++BC.Stats.NumExactMatchedBlocks;
+ BC.Stats.ExactMatchedSampleCount += ExecCount;
LLVM_DEBUG(dbgs() << " exact match\n");
- } else {
+ break;
+ case StaleMatcher::MATCH_PROBE_EXACT:
+ ++BC.Stats.NumPseudoProbeExactMatchedBlocks;
+ BC.Stats.PseudoProbeExactMatchedSampleCount += ExecCount;
+ LLVM_DEBUG(dbgs() << " exact pseudo probe match\n");
+ break;
+ case StaleMatcher::MATCH_PROBE_LOOSE:
+ ++BC.Stats.NumPseudoProbeLooseMatchedBlocks;
+ BC.Stats.PseudoProbeLooseMatchedSampleCount += ExecCount;
+ LLVM_DEBUG(dbgs() << " loose pseudo probe match\n");
+ break;
+ case StaleMatcher::MATCH_CALL:
+ ++BC.Stats.NumCallMatchedBlocks;
+ BC.Stats.CallMatchedSampleCount += ExecCount;
+ LLVM_DEBUG(dbgs() << " call match\n");
+ break;
+ case StaleMatcher::MATCH_OPCODE:
+ ++BC.Stats.NumLooseMatchedBlocks;
+ BC.Stats.LooseMatchedSampleCount += ExecCount;
LLVM_DEBUG(dbgs() << " loose match\n");
+ break;
+ case StaleMatcher::NO_MATCH:
+ LLVM_DEBUG(dbgs() << " no match\n");
}
- if (YamlBB.NumInstructions == BB->size())
- ++BC.Stats.NumStaleBlocksWithEqualIcount;
- } else {
- LLVM_DEBUG(
- dbgs() << "Couldn't match yaml block (bid = " << YamlBB.Index << ")"
- << " with hash " << Twine::utohexstr(YamlBB.Hash) << "\n");
}
- // Update matching stats.
- ++BC.Stats.NumStaleBlocks;
- BC.Stats.StaleSampleCount += YamlBB.ExecCount;
- }
-
- // Match jumps from the profile to the jumps from CFG
- std::vector OutWeight(Func.Blocks.size(), 0);
- std::vector InWeight(Func.Blocks.size(), 0);
- for (const yaml::bolt::BinaryBasicBlockProfile &YamlBB : YamlBF.Blocks) {
- for (const yaml::bolt::SuccessorInfo &YamlSI : YamlBB.Successors) {
- if (YamlSI.Count == 0)
- continue;
-
- // Try to find the jump for a given (src, dst) pair from the profile and
- // assign the jump weight based on the profile count
- const uint64_t SrcIndex = YamlBB.Index;
- const uint64_t DstIndex = YamlSI.Index;
-
- const FlowBlock *MatchedSrcBlock = MatchedBlocks.lookup(SrcIndex);
- const FlowBlock *MatchedDstBlock = MatchedBlocks.lookup(DstIndex);
-
- if (MatchedSrcBlock != nullptr && MatchedDstBlock != nullptr) {
- // Find a jump between the two blocks
- FlowJump *Jump = nullptr;
- for (FlowJump *SuccJump : MatchedSrcBlock->SuccJumps) {
- if (SuccJump->Target == MatchedDstBlock->Index) {
- Jump = SuccJump;
- break;
+ for (const yaml::bolt::BinaryBasicBlockProfile &YamlBB : YamlBF->Blocks) {
+ for (const yaml::bolt::SuccessorInfo &YamlSI : YamlBB.Successors) {
+ if (YamlSI.Count == 0)
+ continue;
+
+ // Try to find the jump for a given (src, dst) pair from the profile and
+ // assign the jump weight based on the profile count
+ const uint64_t SrcIndex = YamlBB.Index;
+ const uint64_t DstIndex = YamlSI.Index;
+
+ const FlowBlock *MatchedSrcBlock = MatchMap.lookup(SrcIndex).first;
+ const FlowBlock *MatchedDstBlock = MatchMap.lookup(DstIndex).first;
+
+ if (MatchedSrcBlock != nullptr && MatchedDstBlock != nullptr) {
+ // Find a jump between the two blocks
+ FlowJump *Jump = nullptr;
+ for (FlowJump *SuccJump : MatchedSrcBlock->SuccJumps) {
+ if (SuccJump->Target == MatchedDstBlock->Index) {
+ Jump = SuccJump;
+ break;
+ }
+ }
+ // Assign the weight, if the corresponding jump is found
+ if (Jump != nullptr) {
+ Jump->Weight = YamlSI.Count;
+ Jump->HasUnknownWeight = false;
}
}
- // Assign the weight, if the corresponding jump is found
- if (Jump != nullptr) {
- Jump->Weight = YamlSI.Count;
- Jump->HasUnknownWeight = false;
- }
+ // Assign the weight for the src block, if it is found
+ if (MatchedSrcBlock != nullptr)
+ OutWeight[MatchedSrcBlock->Index] += YamlSI.Count;
+ // Assign the weight for the dst block, if it is found
+ if (MatchedDstBlock != nullptr)
+ InWeight[MatchedDstBlock->Index] += YamlSI.Count;
}
- // Assign the weight for the src block, if it is found
- if (MatchedSrcBlock != nullptr)
- OutWeight[MatchedSrcBlock->Index] += YamlSI.Count;
- // Assign the weight for the dst block, if it is found
- if (MatchedDstBlock != nullptr)
- InWeight[MatchedDstBlock->Index] += YamlSI.Count;
}
}
@@ -585,7 +779,7 @@ matchWeightsByHashes(BinaryContext &BC,
Block.Weight = std::max(OutWeight[Block.Index], InWeight[Block.Index]);
}
- return MatchedBlocks.size();
+ return MatchedBlocks[&YamlBF].size();
}
/// The function finds all blocks that are (i) reachable from the Entry block
@@ -803,7 +997,8 @@ void assignProfile(BinaryFunction &BF,
}
bool YAMLProfileReader::inferStaleProfile(
- BinaryFunction &BF, const yaml::bolt::BinaryFunctionProfile &YamlBF) {
+ BinaryFunction &BF, const yaml::bolt::BinaryFunctionProfile &YamlBF,
+ const ArrayRef ProbeMatchSpecs) {
NamedRegionTimer T("inferStaleProfile", "stale profile inference", "rewrite",
"Rewrite passes", opts::TimeRewrite);
@@ -827,8 +1022,8 @@ bool YAMLProfileReader::inferStaleProfile(
// Match as many block/jump counts from the stale profile as possible
size_t MatchedBlocks =
- matchWeightsByHashes(BF.getBinaryContext(), BlockOrder, YamlBF, Func,
- YamlBP.Header.HashFunction, IdToYamLBF);
+ matchWeights(BF.getBinaryContext(), BlockOrder, YamlBF, Func,
+ YamlBP.Header.HashFunction, IdToYamLBF, BF, ProbeMatchSpecs);
// Adjust the flow function by marking unreachable blocks Unlikely so that
// they don't get any counts assigned.
diff --git a/bolt/lib/Profile/YAMLProfileReader.cpp b/bolt/lib/Profile/YAMLProfileReader.cpp
index a5dc8492b590033e720dd90db1a3e558980c3c24..e3872b373f417888ca9d43b2571c25b0be16bd7c 100644
--- a/bolt/lib/Profile/YAMLProfileReader.cpp
+++ b/bolt/lib/Profile/YAMLProfileReader.cpp
@@ -16,6 +16,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/edit_distance.h"
#include "llvm/Demangle/Demangle.h"
+#include "llvm/MC/MCPseudoProbe.h"
#include "llvm/Support/CommandLine.h"
using namespace llvm;
@@ -49,6 +50,8 @@ llvm::cl::opt
llvm::cl::opt ProfileUseDFS("profile-use-dfs",
cl::desc("use DFS order for YAML profile"),
cl::Hidden, cl::cat(BoltOptCategory));
+
+extern llvm::cl::opt StaleMatchingWithPseudoProbes;
} // namespace opts
namespace llvm {
@@ -349,8 +352,13 @@ bool YAMLProfileReader::parseFunctionProfile(
if (YamlBF.NumBasicBlocks != BF.size())
++BC.Stats.NumStaleFuncsWithEqualBlockCount;
- if (opts::InferStaleProfile && inferStaleProfile(BF, YamlBF))
- ProfileMatched = true;
+ if (!opts::InferStaleProfile)
+ return false;
+ ArrayRef ProbeMatchSpecs;
+ auto BFIt = BFToProbeMatchSpecs.find(&BF);
+ if (BFIt != BFToProbeMatchSpecs.end())
+ ProbeMatchSpecs = BFIt->second;
+ ProfileMatched = inferStaleProfile(BF, YamlBF, ProbeMatchSpecs);
}
if (ProfileMatched)
BF.markProfiled(YamlBP.Header.Flags);
@@ -585,6 +593,101 @@ size_t YAMLProfileReader::matchWithCallGraph(BinaryContext &BC) {
return MatchedWithCallGraph;
}
+size_t YAMLProfileReader::InlineTreeNodeMapTy::matchInlineTrees(
+ const MCPseudoProbeDecoder &Decoder,
+ const std::vector &DecodedInlineTree,
+ const MCDecodedPseudoProbeInlineTree *Root) {
+ // Match inline tree nodes by GUID, checksum, parent, and call site.
+ for (const auto &[InlineTreeNodeId, InlineTreeNode] :
+ llvm::enumerate(DecodedInlineTree)) {
+ uint64_t GUID = InlineTreeNode.GUID;
+ uint64_t Hash = InlineTreeNode.Hash;
+ uint32_t ParentId = InlineTreeNode.ParentIndexDelta;
+ uint32_t CallSiteProbe = InlineTreeNode.CallSiteProbe;
+ const MCDecodedPseudoProbeInlineTree *Cur = nullptr;
+ if (!InlineTreeNodeId) {
+ Cur = Root;
+ } else if (const MCDecodedPseudoProbeInlineTree *Parent =
+ getInlineTreeNode(ParentId)) {
+ for (const MCDecodedPseudoProbeInlineTree &Child :
+ Parent->getChildren()) {
+ if (Child.Guid == GUID) {
+ if (std::get<1>(Child.getInlineSite()) == CallSiteProbe)
+ Cur = &Child;
+ break;
+ }
+ }
+ }
+ // Don't match nodes if the profile is stale (mismatching binary FuncHash
+ // and YAML Hash)
+ if (Cur && Decoder.getFuncDescForGUID(Cur->Guid)->FuncHash == Hash)
+ mapInlineTreeNode(InlineTreeNodeId, Cur);
+ }
+ return Map.size();
+}
+
+// Decode index deltas and indirection through \p YamlPD. Return modified copy
+// of \p YamlInlineTree with populated decoded fields (GUID, Hash, ParentIndex).
+static std::vector
+decodeYamlInlineTree(const yaml::bolt::ProfilePseudoProbeDesc &YamlPD,
+ std::vector YamlInlineTree) {
+ uint32_t ParentId = 0;
+ uint32_t PrevGUIDIdx = 0;
+ for (yaml::bolt::InlineTreeNode &InlineTreeNode : YamlInlineTree) {
+ uint32_t GUIDIdx = InlineTreeNode.GUIDIndex;
+ if (GUIDIdx != UINT32_MAX)
+ PrevGUIDIdx = GUIDIdx;
+ else
+ GUIDIdx = PrevGUIDIdx;
+ uint32_t HashIdx = YamlPD.GUIDHashIdx[GUIDIdx];
+ ParentId += InlineTreeNode.ParentIndexDelta;
+ InlineTreeNode.GUID = YamlPD.GUID[GUIDIdx];
+ InlineTreeNode.Hash = YamlPD.Hash[HashIdx];
+ InlineTreeNode.ParentIndexDelta = ParentId;
+ }
+ return YamlInlineTree;
+}
+
+size_t YAMLProfileReader::matchWithPseudoProbes(BinaryContext &BC) {
+ if (!opts::StaleMatchingWithPseudoProbes)
+ return 0;
+
+ const MCPseudoProbeDecoder *Decoder = BC.getPseudoProbeDecoder();
+ const yaml::bolt::ProfilePseudoProbeDesc &YamlPD = YamlBP.PseudoProbeDesc;
+
+ // Set existing BF->YamlBF match into ProbeMatchSpecs for (local) probe
+ // matching.
+ assert(Decoder &&
+ "If pseudo probes are in use, pseudo probe decoder should exist");
+ for (auto [YamlBF, BF] : llvm::zip_equal(YamlBP.Functions, ProfileBFs)) {
+ // BF is preliminary name-matched function to YamlBF
+ // MatchedBF is final matched function
+ BinaryFunction *MatchedBF = YamlProfileToFunction.lookup(YamlBF.Id);
+ if (!BF)
+ BF = MatchedBF;
+ if (!BF)
+ continue;
+ uint64_t GUID = BF->getGUID();
+ if (!GUID)
+ continue;
+ auto It = TopLevelGUIDToInlineTree.find(GUID);
+ if (It == TopLevelGUIDToInlineTree.end())
+ continue;
+ const MCDecodedPseudoProbeInlineTree *Node = It->second;
+ assert(Node && "Malformed TopLevelGUIDToInlineTree");
+ auto &MatchSpecs = BFToProbeMatchSpecs[BF];
+ auto &InlineTreeMap =
+ MatchSpecs.emplace_back(InlineTreeNodeMapTy(), YamlBF).first;
+ std::vector ProfileInlineTree =
+ decodeYamlInlineTree(YamlPD, YamlBF.InlineTree);
+ // Erase unsuccessful match
+ if (!InlineTreeMap.matchInlineTrees(*Decoder, ProfileInlineTree, Node))
+ MatchSpecs.pop_back();
+ }
+
+ return 0;
+}
+
size_t YAMLProfileReader::matchWithNameSimilarity(BinaryContext &BC) {
if (opts::NameSimilarityFunctionMatchingThreshold == 0)
return 0;
@@ -716,6 +819,15 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
}
}
+ if (opts::StaleMatchingWithPseudoProbes) {
+ const MCPseudoProbeDecoder *Decoder = BC.getPseudoProbeDecoder();
+ assert(Decoder &&
+ "If pseudo probes are in use, pseudo probe decoder should exist");
+ for (const MCDecodedPseudoProbeInlineTree &TopLev :
+ Decoder->getDummyInlineRoot().getChildren())
+ TopLevelGUIDToInlineTree[TopLev.Guid] = &TopLev;
+ }
+
// Map profiled function ids to names.
for (yaml::bolt::BinaryFunctionProfile &YamlBF : YamlBP.Functions)
IdToYamLBF[YamlBF.Id] = &YamlBF;
@@ -725,6 +837,8 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
const size_t MatchedWithLTOCommonName = matchWithLTOCommonName();
const size_t MatchedWithCallGraph = matchWithCallGraph(BC);
const size_t MatchedWithNameSimilarity = matchWithNameSimilarity(BC);
+ [[maybe_unused]] const size_t MatchedWithPseudoProbes =
+ matchWithPseudoProbes(BC);
for (auto [YamlBF, BF] : llvm::zip_equal(YamlBP.Functions, ProfileBFs))
if (!YamlBF.Used && BF && !ProfiledFunctions.count(BF))
diff --git a/bolt/lib/Profile/YAMLProfileWriter.cpp b/bolt/lib/Profile/YAMLProfileWriter.cpp
index 4437be423ff431951e54f1a6ffc14fb4b6f0d8f0..e394858163560b4b9d6c6090d8578c1e4c7d715e 100644
--- a/bolt/lib/Profile/YAMLProfileWriter.cpp
+++ b/bolt/lib/Profile/YAMLProfileWriter.cpp
@@ -203,7 +203,7 @@ YAMLProfileWriter::convertBFInlineTree(const MCPseudoProbeDecoder &Decoder,
else
PrevGUIDIdx = GUIDIdx;
YamlInlineTree.emplace_back(yaml::bolt::InlineTreeNode{
- Node.ParentId - PrevParent, Node.InlineSite, GUIDIdx});
+ Node.ParentId - PrevParent, Node.InlineSite, GUIDIdx, 0, 0});
PrevParent = Node.ParentId;
}
return {YamlInlineTree, InlineTreeNodeId};
diff --git a/bolt/lib/Rewrite/PseudoProbeRewriter.cpp b/bolt/lib/Rewrite/PseudoProbeRewriter.cpp
index 09aa4fbb66bd42872a7159570ac354c5d08edd7d..9d6e914624a33f2265ddd6a9882a47aa1e4bfe3f 100644
--- a/bolt/lib/Rewrite/PseudoProbeRewriter.cpp
+++ b/bolt/lib/Rewrite/PseudoProbeRewriter.cpp
@@ -51,6 +51,7 @@ static cl::opt PrintPseudoProbes(
cl::Hidden, cl::cat(BoltCategory));
extern cl::opt ProfileWritePseudoProbes;
+extern cl::opt StaleMatchingWithPseudoProbes;
} // namespace opts
namespace {
@@ -92,14 +93,14 @@ public:
};
Error PseudoProbeRewriter::preCFGInitializer() {
- if (opts::ProfileWritePseudoProbes)
- parsePseudoProbe(true);
+ if (opts::ProfileWritePseudoProbes || opts::StaleMatchingWithPseudoProbes)
+ parsePseudoProbe(opts::ProfileWritePseudoProbes);
return Error::success();
}
Error PseudoProbeRewriter::postEmitFinalizer() {
- if (!opts::ProfileWritePseudoProbes)
+ if (!opts::StaleMatchingWithPseudoProbes)
parsePseudoProbe();
updatePseudoProbes();
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index a4c50cbc3e2bbf8925e59940dd7f6b9e41206098..7059a3dd231099beae7cfce4692030adae4b7ee4 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -3807,7 +3807,6 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) {
if (!Function.isEmitted())
continue;
- bool TooLarge = false;
ErrorOr FuncSection = Function.getCodeSection();
assert(FuncSection && "cannot find section for function");
FuncSection->setOutputAddress(Function.getAddress());
@@ -3818,11 +3817,8 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) {
MapSection(*FuncSection, Function.getAddress());
Function.setImageAddress(FuncSection->getAllocAddress());
Function.setImageSize(FuncSection->getOutputSize());
- if (Function.getImageSize() > Function.getMaxSize()) {
- assert(!BC->isX86() && "Unexpected large function.");
- TooLarge = true;
- FailedAddresses.emplace_back(Function.getAddress());
- }
+ assert(Function.getImageSize() <= Function.getMaxSize() &&
+ "Unexpected large function");
// Map jump tables if updating in-place.
if (opts::JumpTables == JTS_BASIC) {
@@ -3852,19 +3848,11 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) {
assert(ColdSection && "cannot find section for cold part");
// Cold fragments are aligned at 16 bytes.
NextAvailableAddress = alignTo(NextAvailableAddress, 16);
- if (TooLarge) {
- // The corresponding FDE will refer to address 0.
- FF.setAddress(0);
- FF.setImageAddress(0);
- FF.setImageSize(0);
- FF.setFileOffset(0);
- } else {
- FF.setAddress(NextAvailableAddress);
- FF.setImageAddress(ColdSection->getAllocAddress());
- FF.setImageSize(ColdSection->getOutputSize());
- FF.setFileOffset(getFileOffsetForAddress(NextAvailableAddress));
- ColdSection->setOutputAddress(FF.getAddress());
- }
+ FF.setAddress(NextAvailableAddress);
+ FF.setImageAddress(ColdSection->getAllocAddress());
+ FF.setImageSize(ColdSection->getOutputSize());
+ FF.setFileOffset(getFileOffsetForAddress(NextAvailableAddress));
+ ColdSection->setOutputAddress(FF.getAddress());
LLVM_DEBUG(
dbgs() << formatv(
@@ -3872,9 +3860,6 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) {
FF.getImageAddress(), FF.getAddress(), FF.getImageSize()));
MapSection(*ColdSection, FF.getAddress());
- if (TooLarge)
- BC->deregisterSection(*ColdSection);
-
NextAvailableAddress += FF.getImageSize();
}
@@ -3902,6 +3887,43 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) {
void RewriteInstance::mapAllocatableSections(
BOLTLinker::SectionMapper MapSection) {
+
+ if (opts::UseOldText || opts::StrictMode) {
+ auto tryRewriteSection = [&](BinarySection &OldSection,
+ BinarySection &NewSection) {
+ if (OldSection.getSize() < NewSection.getOutputSize())
+ return;
+
+ BC->outs() << "BOLT-INFO: rewriting " << OldSection.getName()
+ << " in-place\n";
+
+ NewSection.setOutputAddress(OldSection.getAddress());
+ NewSection.setOutputFileOffset(OldSection.getInputFileOffset());
+ MapSection(NewSection, OldSection.getAddress());
+
+ // Pad contents with zeros.
+ NewSection.addPadding(OldSection.getSize() - NewSection.getOutputSize());
+
+ // Prevent the original section name from appearing in the section header
+ // table.
+ OldSection.setAnonymous(true);
+ };
+
+ if (EHFrameSection) {
+ BinarySection *NewEHFrameSection =
+ getSection(getNewSecPrefix() + getEHFrameSectionName());
+ assert(NewEHFrameSection && "New contents expected for .eh_frame");
+ tryRewriteSection(*EHFrameSection, *NewEHFrameSection);
+ }
+ BinarySection *EHSection = getSection(".gcc_except_table");
+ BinarySection *NewEHSection =
+ getSection(getNewSecPrefix() + ".gcc_except_table");
+ if (EHSection) {
+ assert(NewEHSection && "New contents expected for .gcc_except_table");
+ tryRewriteSection(*EHSection, *NewEHSection);
+ }
+ }
+
// Allocate read-only sections first, then writable sections.
enum : uint8_t { ST_READONLY, ST_READWRITE };
for (uint8_t SType = ST_READONLY; SType <= ST_READWRITE; ++SType) {
@@ -4179,7 +4201,6 @@ void RewriteInstance::rewriteNoteSections() {
// New section size.
uint64_t Size = 0;
bool DataWritten = false;
- uint8_t *SectionData = nullptr;
// Copy over section contents unless it's one of the sections we overwrite.
if (!willOverwriteSection(SectionName)) {
Size = Section.sh_size;
@@ -4211,12 +4232,7 @@ void RewriteInstance::rewriteNoteSections() {
if (BSec->getAllocAddress()) {
assert(!DataWritten && "Writing section twice.");
(void)DataWritten;
- SectionData = BSec->getOutputData();
-
- LLVM_DEBUG(dbgs() << "BOLT-DEBUG: " << (Size ? "appending" : "writing")
- << " contents to section " << SectionName << '\n');
- OS.write(reinterpret_cast(SectionData), BSec->getOutputSize());
- Size += BSec->getOutputSize();
+ Size += BSec->write(OS);
}
BSec->setOutputFileOffset(NextAvailableOffset);
@@ -4247,8 +4263,7 @@ void RewriteInstance::rewriteNoteSections() {
<< " of size " << Section.getOutputSize() << " at offset 0x"
<< Twine::utohexstr(Section.getOutputFileOffset()) << '\n');
- OS.write(Section.getOutputContents().data(), Section.getOutputSize());
- NextAvailableOffset += Section.getOutputSize();
+ NextAvailableOffset += Section.write(OS);
}
}
@@ -4362,6 +4377,10 @@ RewriteInstance::getOutputSections(ELFObjectFile *File,
BinarySection *BinSec = BC->getSectionForSectionRef(SecRef);
assert(BinSec && "Matching BinarySection should exist.");
+ // Exclude anonymous sections.
+ if (BinSec->isAnonymous())
+ continue;
+
addSection(Section, *BinSec);
}
@@ -5714,8 +5733,8 @@ void RewriteInstance::rewriteFile() {
<< Twine::utohexstr(Section.getAllocAddress()) << "\n of size "
<< Section.getOutputSize() << "\n at offset "
<< Section.getOutputFileOffset() << '\n';
- OS.pwrite(reinterpret_cast(Section.getOutputData()),
- Section.getOutputSize(), Section.getOutputFileOffset());
+ OS.seek(Section.getOutputFileOffset());
+ Section.write(OS);
}
for (BinarySection &Section : BC->allocatableSections())
@@ -5806,42 +5825,64 @@ void RewriteInstance::writeEHFrameHeader() {
LLVM_DEBUG(dbgs() << "BOLT: writing a new " << getEHFrameHdrSectionName()
<< '\n');
- NextAvailableAddress =
- appendPadding(Out->os(), NextAvailableAddress, EHFrameHdrAlign);
+ // Try to overwrite the original .eh_frame_hdr if the size permits.
+ uint64_t EHFrameHdrOutputAddress = 0;
+ uint64_t EHFrameHdrFileOffset = 0;
+ std::vector NewEHFrameHdr;
+ BinarySection *OldEHFrameHdrSection = getSection(getEHFrameHdrSectionName());
+ if (OldEHFrameHdrSection) {
+ NewEHFrameHdr = CFIRdWrt->generateEHFrameHeader(
+ RelocatedEHFrame, NewEHFrame, OldEHFrameHdrSection->getAddress());
+ if (NewEHFrameHdr.size() <= OldEHFrameHdrSection->getSize()) {
+ BC->outs() << "BOLT-INFO: rewriting " << getEHFrameHdrSectionName()
+ << " in-place\n";
+ EHFrameHdrOutputAddress = OldEHFrameHdrSection->getAddress();
+ EHFrameHdrFileOffset = OldEHFrameHdrSection->getInputFileOffset();
+ } else {
+ OldEHFrameHdrSection->setOutputName(getOrgSecPrefix() +
+ getEHFrameHdrSectionName());
+ OldEHFrameHdrSection = nullptr;
+ }
+ }
+
+ // If there was not enough space, allocate more memory for .eh_frame_hdr.
+ if (!OldEHFrameHdrSection) {
+ NextAvailableAddress =
+ appendPadding(Out->os(), NextAvailableAddress, EHFrameHdrAlign);
- const uint64_t EHFrameHdrOutputAddress = NextAvailableAddress;
- const uint64_t EHFrameHdrFileOffset =
- getFileOffsetForAddress(NextAvailableAddress);
+ EHFrameHdrOutputAddress = NextAvailableAddress;
+ EHFrameHdrFileOffset = getFileOffsetForAddress(NextAvailableAddress);
- std::vector NewEHFrameHdr = CFIRdWrt->generateEHFrameHeader(
- RelocatedEHFrame, NewEHFrame, EHFrameHdrOutputAddress, FailedAddresses);
+ NewEHFrameHdr = CFIRdWrt->generateEHFrameHeader(
+ RelocatedEHFrame, NewEHFrame, EHFrameHdrOutputAddress);
+
+ NextAvailableAddress += NewEHFrameHdr.size();
+ if (!BC->BOLTReserved.empty() &&
+ (NextAvailableAddress > BC->BOLTReserved.end())) {
+ BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName()
+ << " into reserved space\n";
+ exit(1);
+ }
+
+ // Create a new entry in the section header table.
+ const unsigned Flags = BinarySection::getFlags(/*IsReadOnly=*/true,
+ /*IsText=*/false,
+ /*IsAllocatable=*/true);
+ BinarySection &EHFrameHdrSec = BC->registerOrUpdateSection(
+ getNewSecPrefix() + getEHFrameHdrSectionName(), ELF::SHT_PROGBITS,
+ Flags, nullptr, NewEHFrameHdr.size(), /*Alignment=*/1);
+ EHFrameHdrSec.setOutputFileOffset(EHFrameHdrFileOffset);
+ EHFrameHdrSec.setOutputAddress(EHFrameHdrOutputAddress);
+ EHFrameHdrSec.setOutputName(getEHFrameHdrSectionName());
+ }
Out->os().seek(EHFrameHdrFileOffset);
Out->os().write(NewEHFrameHdr.data(), NewEHFrameHdr.size());
- const unsigned Flags = BinarySection::getFlags(/*IsReadOnly=*/true,
- /*IsText=*/false,
- /*IsAllocatable=*/true);
- BinarySection *OldEHFrameHdrSection = getSection(getEHFrameHdrSectionName());
+ // Pad the contents if overwriting in-place.
if (OldEHFrameHdrSection)
- OldEHFrameHdrSection->setOutputName(getOrgSecPrefix() +
- getEHFrameHdrSectionName());
-
- BinarySection &EHFrameHdrSec = BC->registerOrUpdateSection(
- getNewSecPrefix() + getEHFrameHdrSectionName(), ELF::SHT_PROGBITS, Flags,
- nullptr, NewEHFrameHdr.size(), /*Alignment=*/1);
- EHFrameHdrSec.setOutputFileOffset(EHFrameHdrFileOffset);
- EHFrameHdrSec.setOutputAddress(EHFrameHdrOutputAddress);
- EHFrameHdrSec.setOutputName(getEHFrameHdrSectionName());
-
- NextAvailableAddress += EHFrameHdrSec.getOutputSize();
-
- if (!BC->BOLTReserved.empty() &&
- (NextAvailableAddress > BC->BOLTReserved.end())) {
- BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName()
- << " into reserved space\n";
- exit(1);
- }
+ Out->os().write_zeros(OldEHFrameHdrSection->getSize() -
+ NewEHFrameHdr.size());
// Merge new .eh_frame with the relocated original so that gdb can locate all
// FDEs.
diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index f58f7857e28aeb075dd5eaa13c35e1045fa01fc0..7e08e5c81d26ffaa21e6c2dd7d1c11c3f6de1a00 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -15,6 +15,8 @@
#include "MCTargetDesc/AArch64MCExpr.h"
#include "MCTargetDesc/AArch64MCTargetDesc.h"
#include "Utils/AArch64BaseInfo.h"
+#include "bolt/Core/BinaryBasicBlock.h"
+#include "bolt/Core/BinaryFunction.h"
#include "bolt/Core/MCPlusBuilder.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/MC/MCContext.h"
@@ -22,6 +24,7 @@
#include "llvm/MC/MCInstBuilder.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
@@ -1320,6 +1323,67 @@ public:
return 3;
}
+ /// Match the following pattern:
+ ///
+ /// LDR x16, .L1
+ /// BR x16
+ /// L1:
+ /// .quad Target
+ ///
+ /// Populate \p TargetAddress with the Target value on successful match.
+ bool matchAbsLongVeneer(const BinaryFunction &BF,
+ uint64_t &TargetAddress) const override {
+ if (BF.size() != 1 || BF.getMaxSize() < 16)
+ return false;
+
+ if (!BF.hasConstantIsland())
+ return false;
+
+ const BinaryBasicBlock &BB = BF.front();
+ if (BB.size() != 2)
+ return false;
+
+ const MCInst &LDRInst = BB.getInstructionAtIndex(0);
+ if (LDRInst.getOpcode() != AArch64::LDRXl)
+ return false;
+
+ if (!LDRInst.getOperand(0).isReg() ||
+ LDRInst.getOperand(0).getReg() != AArch64::X16)
+ return false;
+
+ const MCSymbol *TargetSym = getTargetSymbol(LDRInst, 1);
+ if (!TargetSym)
+ return false;
+
+ const MCInst &BRInst = BB.getInstructionAtIndex(1);
+ if (BRInst.getOpcode() != AArch64::BR)
+ return false;
+ if (!BRInst.getOperand(0).isReg() ||
+ BRInst.getOperand(0).getReg() != AArch64::X16)
+ return false;
+
+ const BinaryFunction::IslandInfo &IInfo = BF.getIslandInfo();
+ if (IInfo.HasDynamicRelocations)
+ return false;
+
+ auto Iter = IInfo.Offsets.find(8);
+ if (Iter == IInfo.Offsets.end() || Iter->second != TargetSym)
+ return false;
+
+ // Extract the absolute value stored inside the island.
+ StringRef SectionContents = BF.getOriginSection()->getContents();
+ StringRef FunctionContents = SectionContents.substr(
+ BF.getAddress() - BF.getOriginSection()->getAddress(), BF.getMaxSize());
+
+ const BinaryContext &BC = BF.getBinaryContext();
+ DataExtractor DE(FunctionContents, BC.AsmInfo->isLittleEndian(),
+ BC.AsmInfo->getCodePointerSize());
+ uint64_t Offset = 8;
+ TargetAddress = DE.getAddress(&Offset);
+
+ return true;
+ }
+
bool matchAdrpAddPair(const MCInst &Adrp, const MCInst &Add) const override {
if (!isADRP(Adrp) || !isAddXri(Add))
return false;
diff --git a/bolt/test/AArch64/veneer-lld-abs.s b/bolt/test/AArch64/veneer-lld-abs.s
new file mode 100644
index 0000000000000000000000000000000000000000..d10ff46e2cb01680528485e667da7a58ade886ad
--- /dev/null
+++ b/bolt/test/AArch64/veneer-lld-abs.s
@@ -0,0 +1,51 @@
+## Check that llvm-bolt correctly recognizes long absolute thunks generated
+## by LLD.
+
+# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
+# RUN: %clang %cflags -fno-PIC -no-pie %t.o -o %t.exe -nostdlib \
+# RUN: -fuse-ld=lld -Wl,-q
+# RUN: llvm-objdump -d %t.exe | FileCheck --check-prefix=CHECK-INPUT %s
+# RUN: llvm-objcopy --remove-section .rela.mytext %t.exe
+# RUN: llvm-bolt %t.exe -o %t.bolt --elim-link-veneers=true --lite=0
+# RUN: llvm-objdump -d -j .text %t.bolt | \
+# RUN: FileCheck --check-prefix=CHECK-OUTPUT %s
+
+.text
+.balign 4
+.global foo
+.type foo, %function
+foo:
+ adrp x1, foo
+ ret
+.size foo, .-foo
+
+.section ".mytext", "ax"
+.balign 4
+
+.global __AArch64AbsLongThunk_foo
+.type __AArch64AbsLongThunk_foo, %function
+__AArch64AbsLongThunk_foo:
+ ldr x16, .L1
+ br x16
+# CHECK-INPUT-LABEL: <__AArch64AbsLongThunk_foo>:
+# CHECK-INPUT-NEXT: ldr
+# CHECK-INPUT-NEXT: br
+.L1:
+ .quad foo
+.size __AArch64AbsLongThunk_foo, .-__AArch64AbsLongThunk_foo
+
+## Check that the thunk was removed from .text and _start() calls foo()
+## directly.
+
+# CHECK-OUTPUT-NOT: __AArch64AbsLongThunk_foo
+
+.global _start
+.type _start, %function
+_start:
+# CHECK-INPUT-LABEL: <_start>:
+# CHECK-OUTPUT-LABEL: <_start>:
+ bl __AArch64AbsLongThunk_foo
+# CHECK-INPUT-NEXT: bl {{.*}} <__AArch64AbsLongThunk_foo>
+# CHECK-OUTPUT-NEXT: bl {{.*}}
+ ret
+.size _start, .-_start
diff --git a/bolt/test/X86/match-blocks-with-pseudo-probes-inline.test b/bolt/test/X86/match-blocks-with-pseudo-probes-inline.test
new file mode 100644
index 0000000000000000000000000000000000000000..accb4742851ea574e5bafae272b4b4d3622208ac
--- /dev/null
+++ b/bolt/test/X86/match-blocks-with-pseudo-probes-inline.test
@@ -0,0 +1,65 @@
+## Test stale block matching with pseudo probes including inline tree matching.
+# RUN: split-file %s %t
+# RUN: llvm-bolt \
+# RUN: %S/../../../llvm/test/tools/llvm-profgen/Inputs/inline-cs-pseudoprobe.perfbin \
+# RUN: -o %t.bolt -data %t/yaml -infer-stale-profile -v=2 \
+# RUN: --stale-matching-with-pseudo-probes 2>&1 | FileCheck %s
+
+# CHECK: BOLT-WARNING: 3 (100.0% of all profiled) functions have invalid (possibly stale) profile
+# CHECK: BOLT-INFO: inference found an exact pseudo probe match for 100.00% of basic blocks (3 out of 3 stale)
+
+#--- yaml
+---
+header:
+ profile-version: 1
+ binary-name: 'inline-cs-pseudoprobe.perfbin'
+ binary-build-id: ''
+ profile-flags: [ lbr ]
+ profile-origin: perf data aggregator
+ profile-events: ''
+ dfs-order: false
+ hash-func: xxh3
+functions:
+ - name: bar
+ fid: 9
+ hash: 0x1
+ exec: 1
+ nblocks: 1
+ blocks:
+ - bid: 0
+ insns: 11
+ hash: 0x1
+ exec: 1
+ probes: [ { blx: 9 } ]
+ inline_tree: [ { } ]
+ - name: foo
+ fid: 10
+ hash: 0x2
+ exec: 1
+ nblocks: 6
+ blocks:
+ - bid: 0
+ insns: 3
+ hash: 0x2
+ exec: 1
+ succ: [ { bid: 3, cnt: 0 } ]
+ probes: [ { blx: 3 } ]
+ inline_tree: [ { g: 1 }, { g: 0, cs: 8 } ]
+ - name: main
+ fid: 11
+ hash: 0x3
+ exec: 1
+ nblocks: 6
+ blocks:
+ - bid: 0
+ insns: 3
+ hash: 0x3
+ exec: 1
+ succ: [ { bid: 3, cnt: 0 } ]
+ probes: [ { blx: 3, id: 1 }, { blx: 1 } ]
+ inline_tree: [ { g: 2 }, { g: 1, cs: 2 }, { g: 0, p: 1, cs: 8 } ]
+pseudo_probe_desc:
+ gs: [ 0xE413754A191DB537, 0x5CF8C24CDB18BDAC, 0xDB956436E78DD5FA ]
+ gh: [ 2, 0, 1 ]
+ hs: [ 0x200205A19C5B4, 0x10000FFFFFFFF, 0x10E852DA94 ]
+...
diff --git a/bolt/test/X86/match-blocks-with-pseudo-probes.test b/bolt/test/X86/match-blocks-with-pseudo-probes.test
new file mode 100644
index 0000000000000000000000000000000000000000..40cb64ee82919abcacf5007430ed95ed7fdb30a3
--- /dev/null
+++ b/bolt/test/X86/match-blocks-with-pseudo-probes.test
@@ -0,0 +1,63 @@
+## Tests stale block matching with pseudo probes.
+
+# REQUIRES: system-linux
+# RUN: split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/main.s -o %t.o
+# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -nostdlib
+# RUN: llvm-bolt %t.exe -o %t.out --data %t/yaml -v=2 \
+# RUN: --print-cfg --funcs=main --infer-stale-profile \
+# RUN: --stale-matching-with-pseudo-probes 2>&1 | FileCheck %s
+
+# CHECK: BOLT-INFO: inference found an exact pseudo probe match for 100.00% of basic blocks (1 out of 1 stale)
+
+#--- main.s
+ .text
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main,@function
+main: # @main
+# %bb.0:
+ pushq %rbp
+ movq %rsp, %rbp
+ movl $0, -4(%rbp)
+ .pseudoprobe 15822663052811949562 1 0 0 main
+ xorl %eax, %eax
+ popq %rbp
+ retq
+.Lfunc_end0:
+ .size main, .Lfunc_end0-main
+ # -- End function
+ .section .pseudo_probe_desc,"",@progbits
+ .quad -2624081020897602054
+ .quad 4294967295
+ .byte 4
+ .ascii "main"
+
+#--- yaml
+---
+header:
+ profile-version: 1
+ binary-name: 'match-blocks-with-pseudo-probes.s.tmp.exe'
+ binary-build-id: ''
+ profile-flags: [ lbr ]
+ profile-origin: branch profile reader
+ profile-events: ''
+ dfs-order: false
+ hash-func: xxh3
+functions:
+ - name: main
+ fid: 0
+ hash: 0x0000000000000001
+ exec: 1
+ nblocks: 6
+ blocks:
+ - bid: 1
+ hash: 0xFFFFFFFFFFFFFFF1
+ insns: 1
+ succ: [ { bid: 3, cnt: 1} ]
+ probes: [ { blx: 1 } ]
+ inline_tree: [ { g: 0 } ]
+pseudo_probe_desc:
+ gs: [ 0xDB956436E78DD5FA ]
+ gh: [ 0 ]
+ hs: [ 0xFFFFFFFF ]
diff --git a/bolt/test/X86/match-functions-with-calls-as-anchors.test b/bolt/test/X86/match-functions-with-calls-as-anchors.test
index 984d614fbf85f97e7ee2d33056f27e60cde6a757..f8ef2880a6ce9abc4146899748a138615e7c8f90 100644
--- a/bolt/test/X86/match-functions-with-calls-as-anchors.test
+++ b/bolt/test/X86/match-functions-with-calls-as-anchors.test
@@ -5,15 +5,16 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/main.s -o %t.o
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -nostdlib
# RUN: llvm-bolt %t.exe -o %t.out --data %t/yaml --profile-ignore-hash -v=1 \
-# RUN: --dyno-stats --print-cfg --infer-stale-profile=1 --debug 2>&1 | FileCheck %s
+# RUN: --dyno-stats --print-cfg --infer-stale-profile=1 --debug-only=bolt-prof \
+# RUN: 2>&1 | FileCheck %s
# CHECK: BOLT-INFO: applying profile inference for "qux"
# CHECK: Matched yaml block (bid = 1) with hash 4 to BB (index = 0) with hash 314e1bc10000
-# CHECK: loose match
+# CHECK: call match
# CHECK: BOLT-INFO: applying profile inference for "fred"
# CHECK: Matched yaml block (bid = 1) with hash 5 to BB (index = 0) with hash 7541bc10000
-# CHECK: loose match
+# CHECK: call match
#--- main.s
.globl foo # -- Begin function foo
diff --git a/bolt/test/X86/reader-stale-yaml.test b/bolt/test/X86/reader-stale-yaml.test
index 378abc38252462d4bbaaae4a2bcd9936905904fe..7a9540c48f80dfc046983ebf42411c526c3b99b9 100644
--- a/bolt/test/X86/reader-stale-yaml.test
+++ b/bolt/test/X86/reader-stale-yaml.test
@@ -77,10 +77,10 @@ CHECK2: pre-processing profile using YAML profile reader
CHECK2: applying profile inference for "SolveCubic"
CHECK2: Matched yaml block (bid = 0) with hash 4600940a609c0000 to BB (index = 0) with hash 4600940a609c0000
CHECK2-NEXT: exact match
-CHECK2: Matched yaml block (bid = 1) with hash 167a1f084f130088 to BB (index = 1) with hash 167a1f084f130088
-CHECK2-NEXT: exact match
CHECK2: Matched yaml block (bid = 13) with hash a8d50000f81902a7 to BB (index = 13) with hash a8d5aa43f81902a7
CHECK2-NEXT: loose match
+CHECK2: Matched yaml block (bid = 1) with hash 167a1f084f130088 to BB (index = 1) with hash 167a1f084f130088
+CHECK2-NEXT: exact match
CHECK2: Matched yaml block (bid = 3) with hash c516000073dc00a0 to BB (index = 3) with hash c516b1c973dc00a0
CHECK2-NEXT: loose match
CHECK2: Matched yaml block (bid = 5) with hash 6446e1ea500111 to BB (index = 5) with hash 6446e1ea500111
diff --git a/bolt/test/eh-frame-hdr.test b/bolt/test/eh-frame-hdr.test
new file mode 100644
index 0000000000000000000000000000000000000000..4d718c850e2f283138b891ac4a5b887fdbfbd5d4
--- /dev/null
+++ b/bolt/test/eh-frame-hdr.test
@@ -0,0 +1,12 @@
+# Check that llvm-bolt overwrites .eh_frame_hdr in-place.
+
+REQUIRES: system-linux
+
+RUN: %clang %cflags %p/Inputs/hello.c -o %t -Wl,-q
+RUN: llvm-bolt %t -o %t.bolt --use-old-text \
+RUN: | FileCheck %s --check-prefix=CHECK-BOLT
+RUN: llvm-readelf -WS %t.bolt | FileCheck %s
+
+CHECK-BOLT: rewriting .eh_frame_hdr in-place
+
+CHECK-NOT: .bolt.org.eh_frame_hdr
diff --git a/bolt/test/eh-frame-overwrite.test b/bolt/test/eh-frame-overwrite.test
new file mode 100644
index 0000000000000000000000000000000000000000..649d739ec6086a2bc12b246cd348019441213fe8
--- /dev/null
+++ b/bolt/test/eh-frame-overwrite.test
@@ -0,0 +1,8 @@
+# Check that llvm-bolt can overwrite .eh_frame section in-place.
+
+REQUIRES: system-linux
+
+RUN: %clang %cflags %p/Inputs/hello.c -o %t -Wl,-q
+RUN: llvm-bolt %t -o %t.bolt --strict | FileCheck %s
+
+CHECK: rewriting .eh_frame in-place
diff --git a/bolt/unittests/Core/MCPlusBuilder.cpp b/bolt/unittests/Core/MCPlusBuilder.cpp
index c66c2d0c0fb16e2a6b66f63e27f7789fcbd7f1e3..cd6f24c4570a798cb5480a3950872d0169f3da61 100644
--- a/bolt/unittests/Core/MCPlusBuilder.cpp
+++ b/bolt/unittests/Core/MCPlusBuilder.cpp
@@ -90,14 +90,15 @@ INSTANTIATE_TEST_SUITE_P(AArch64, MCPlusBuilderTester,
::testing::Values(Triple::aarch64));
TEST_P(MCPlusBuilderTester, AliasX0) {
- uint64_t AliasesX0[] = {AArch64::W0, AArch64::X0, AArch64::W0_W1,
+ uint64_t AliasesX0[] = {AArch64::W0, AArch64::W0_HI,
+ AArch64::X0, AArch64::W0_W1,
AArch64::X0_X1, AArch64::X0_X1_X2_X3_X4_X5_X6_X7};
size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0);
testRegAliases(Triple::aarch64, AArch64::X0, AliasesX0, AliasesX0Count);
}
TEST_P(MCPlusBuilderTester, AliasSmallerX0) {
- uint64_t AliasesX0[] = {AArch64::W0, AArch64::X0};
+ uint64_t AliasesX0[] = {AArch64::W0, AArch64::W0_HI, AArch64::X0};
size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0);
testRegAliases(Triple::aarch64, AArch64::X0, AliasesX0, AliasesX0Count, true);
}
diff --git a/clang-tools-extra/clang-include-fixer/IncludeFixer.cpp b/clang-tools-extra/clang-include-fixer/IncludeFixer.cpp
index 354f35cbadbeb9470af81324b4559901dfe9cd31..bba8f8acc77da9a81e7a2d814987791aef09b5ae 100644
--- a/clang-tools-extra/clang-include-fixer/IncludeFixer.cpp
+++ b/clang-tools-extra/clang-include-fixer/IncludeFixer.cpp
@@ -95,7 +95,8 @@ bool IncludeFixerActionFactory::runInvocation(
// Create the compiler's actual diagnostics engine. We want to drop all
// diagnostics here.
- Compiler.createDiagnostics(new clang::IgnoringDiagConsumer,
+ Compiler.createDiagnostics(Files->getVirtualFileSystem(),
+ new clang::IgnoringDiagConsumer,
/*ShouldOwnClient=*/true);
Compiler.createSourceManager(*Files);
diff --git a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
index 620a57194acb8e692bf7fd71b29fd7e476a65ccb..3d1f63fcf33a5ad229725ceec231b45f3f2abc70 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
@@ -20,7 +20,7 @@ namespace {
AST_MATCHER_P(FunctionDecl, isEnabled, llvm::StringSet<>,
FunctionsThatShouldNotThrow) {
- return FunctionsThatShouldNotThrow.count(Node.getNameAsString()) > 0;
+ return FunctionsThatShouldNotThrow.contains(Node.getNameAsString());
}
AST_MATCHER(FunctionDecl, isExplicitThrow) {
diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index 8f4b5e8092ddaaeb766fa7e626815c27c554f85f..960133159dbbf58e8d9522bb08739142b523daed 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -315,9 +315,10 @@ void UseAfterMoveFinder::getReinits(
"::std::unordered_map", "::std::unordered_multiset",
"::std::unordered_multimap"))))));
- auto StandardSmartPointerTypeMatcher = hasType(hasUnqualifiedDesugaredType(
- recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
- "::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr"))))));
+ auto StandardResettableOwnerTypeMatcher = hasType(
+ hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
+ hasAnyName("::std::unique_ptr", "::std::shared_ptr",
+ "::std::weak_ptr", "::std::optional", "::std::any"))))));
// Matches different types of reinitialization.
auto ReinitMatcher =
@@ -340,7 +341,7 @@ void UseAfterMoveFinder::getReinits(
callee(cxxMethodDecl(hasAnyName("clear", "assign")))),
// reset() on standard smart pointers.
cxxMemberCallExpr(
- on(expr(DeclRefMatcher, StandardSmartPointerTypeMatcher)),
+ on(expr(DeclRefMatcher, StandardResettableOwnerTypeMatcher)),
callee(cxxMethodDecl(hasName("reset")))),
// Methods that have the [[clang::reinitializes]] attribute.
cxxMemberCallExpr(
diff --git a/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp
index bebdce525e28871f8361607190aa84e82475d2fb..76fa2d916f0e86050e036d06c9ae9258ae267b9f 100644
--- a/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp
@@ -112,7 +112,7 @@ static bool checkOverridingFunctionReturnType(const ASTContext *Context,
// The class type D should have the same cv-qualification as or less
// cv-qualification than the class type B.
- if (DTy.isMoreQualifiedThan(BTy))
+ if (DTy.isMoreQualifiedThan(BTy, *Context))
return false;
return true;
diff --git a/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp
index 18420d0c8488d2192b23e9033c7827a88021fdc9..c5eaff88e0ed3b9a631420217cf93c0eb82a55c4 100644
--- a/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp
@@ -299,10 +299,11 @@ static bool applyDiceHeuristic(StringRef Arg, StringRef Param,
/// Checks if ArgType binds to ParamType regarding reference-ness and
/// cv-qualifiers.
-static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType) {
+static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType,
+ const ASTContext &Ctx) {
return !ParamType->isReferenceType() ||
ParamType.getNonReferenceType().isAtLeastAsQualifiedAs(
- ArgType.getNonReferenceType());
+ ArgType.getNonReferenceType(), Ctx);
}
static bool isPointerOrArray(QualType TypeToCheck) {
@@ -311,12 +312,12 @@ static bool isPointerOrArray(QualType TypeToCheck) {
/// Checks whether ArgType is an array type identical to ParamType's array type.
/// Enforces array elements' qualifier compatibility as well.
-static bool isCompatibleWithArrayReference(QualType ArgType,
- QualType ParamType) {
+static bool isCompatibleWithArrayReference(QualType ArgType, QualType ParamType,
+ const ASTContext &Ctx) {
if (!ArgType->isArrayType())
return false;
// Here, qualifiers belong to the elements of the arrays.
- if (!ParamType.isAtLeastAsQualifiedAs(ArgType))
+ if (!ParamType.isAtLeastAsQualifiedAs(ArgType, Ctx))
return false;
return ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType();
@@ -342,12 +343,13 @@ static QualType convertToPointeeOrArrayElementQualType(QualType TypeToConvert) {
/// every * in ParamType to the right of that cv-qualifier, except the last
/// one, must also be const-qualified.
static bool arePointersStillQualCompatible(QualType ArgType, QualType ParamType,
- bool &IsParamContinuouslyConst) {
+ bool &IsParamContinuouslyConst,
+ const ASTContext &Ctx) {
// The types are compatible, if the parameter is at least as qualified as the
// argument, and if it is more qualified, it has to be const on upper pointer
// levels.
bool AreTypesQualCompatible =
- ParamType.isAtLeastAsQualifiedAs(ArgType) &&
+ ParamType.isAtLeastAsQualifiedAs(ArgType, Ctx) &&
(!ParamType.hasQualifiers() || IsParamContinuouslyConst);
// Check whether the parameter's constness continues at the current pointer
// level.
@@ -359,9 +361,10 @@ static bool arePointersStillQualCompatible(QualType ArgType, QualType ParamType,
/// Checks whether multilevel pointers are compatible in terms of levels,
/// qualifiers and pointee type.
static bool arePointerTypesCompatible(QualType ArgType, QualType ParamType,
- bool IsParamContinuouslyConst) {
+ bool IsParamContinuouslyConst,
+ const ASTContext &Ctx) {
if (!arePointersStillQualCompatible(ArgType, ParamType,
- IsParamContinuouslyConst))
+ IsParamContinuouslyConst, Ctx))
return false;
do {
@@ -372,7 +375,7 @@ static bool arePointerTypesCompatible(QualType ArgType, QualType ParamType,
// Check whether cv-qualifiers permit compatibility on
// current level.
if (!arePointersStillQualCompatible(ArgType, ParamType,
- IsParamContinuouslyConst))
+ IsParamContinuouslyConst, Ctx))
return false;
if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
@@ -396,7 +399,7 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType,
return true;
// Check for constness and reference compatibility.
- if (!areRefAndQualCompatible(ArgType, ParamType))
+ if (!areRefAndQualCompatible(ArgType, ParamType, Ctx))
return false;
bool IsParamReference = ParamType->isReferenceType();
@@ -434,7 +437,7 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType,
// When ParamType is an array reference, ArgType has to be of the same-sized
// array-type with cv-compatible element type.
if (IsParamReference && ParamType->isArrayType())
- return isCompatibleWithArrayReference(ArgType, ParamType);
+ return isCompatibleWithArrayReference(ArgType, ParamType, Ctx);
bool IsParamContinuouslyConst =
!IsParamReference || ParamType.getNonReferenceType().isConstQualified();
@@ -444,7 +447,7 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType,
ParamType = convertToPointeeOrArrayElementQualType(ParamType);
// Check qualifier compatibility on the next level.
- if (!ParamType.isAtLeastAsQualifiedAs(ArgType))
+ if (!ParamType.isAtLeastAsQualifiedAs(ArgType, Ctx))
return false;
if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
@@ -472,8 +475,8 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType,
if (!(ParamType->isAnyPointerType() && ArgType->isAnyPointerType()))
return false;
- return arePointerTypesCompatible(ArgType, ParamType,
- IsParamContinuouslyConst);
+ return arePointerTypesCompatible(ArgType, ParamType, IsParamContinuouslyConst,
+ Ctx);
}
static bool isOverloadedUnaryOrBinarySymbolOperator(const FunctionDecl *FD) {
diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
index 9bfb7e2677533a1d1429adb5d786d2826b851bd1..68f3ecf6bdaa8166811eecc83f86a4b4e90a3a1c 100644
--- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -418,7 +418,7 @@ ExceptionAnalyzer::ExceptionInfo::filterIgnoredExceptions(
if (TD->getDeclName().isIdentifier()) {
if ((IgnoreBadAlloc &&
(TD->getName() == "bad_alloc" && TD->isInStdNamespace())) ||
- (IgnoredTypes.count(TD->getName()) > 0))
+ (IgnoredTypes.contains(TD->getName())))
TypesToDelete.push_back(T);
}
}
@@ -449,7 +449,8 @@ void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
const FunctionDecl *Func, const ExceptionInfo::Throwables &Caught,
llvm::SmallSet &CallStack) {
- if (!Func || CallStack.count(Func) || (!CallStack.empty() && !canThrow(Func)))
+ if (!Func || CallStack.contains(Func) ||
+ (!CallStack.empty() && !canThrow(Func)))
return ExceptionInfo::createNonThrowing();
if (const Stmt *Body = Func->getBody()) {
@@ -507,7 +508,7 @@ ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
for (unsigned I = 0; I < Try->getNumHandlers(); ++I) {
const CXXCatchStmt *Catch = Try->getHandler(I);
- // Everything is catched through 'catch(...)'.
+ // Everything is caught through 'catch(...)'.
if (!Catch->getExceptionDecl()) {
ExceptionInfo Rethrown = throwsException(
Catch->getHandlerBlock(), Uncaught.getExceptionTypes(), CallStack);
diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
index 0a8cf8668d3ca9a64ee46b1b37e532d48fa40565..6c2d693d64b50e414bc21a92e4cece7e7fa24be5 100644
--- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
+++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
@@ -101,8 +101,8 @@ public:
/// Recalculate the 'Behaviour' for example after filtering.
void reevaluateBehaviour();
- /// Keep track if the entity related to this 'ExceptionInfo' can in princple
- /// throw, if it's unknown or if it won't throw.
+ /// Keep track if the entity related to this 'ExceptionInfo' can in
+ /// principle throw, if it's unknown or if it won't throw.
State Behaviour;
/// Keep track if the entity contains any unknown elements to keep track
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index 06573a57554245184e8640344884149639d80e4d..05dd313d0a0d357c659f50cfdf87a5c16f266854 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -1419,15 +1419,12 @@ void ClangdLSPServer::applyConfiguration(
const ConfigurationSettings &Settings) {
// Per-file update to the compilation database.
llvm::StringSet<> ModifiedFiles;
- for (auto &Entry : Settings.compilationDatabaseChanges) {
- PathRef File = Entry.first;
- auto Old = CDB->getCompileCommand(File);
- auto New =
- tooling::CompileCommand(std::move(Entry.second.workingDirectory), File,
- std::move(Entry.second.compilationCommand),
+ for (auto &[File, Command] : Settings.compilationDatabaseChanges) {
+ auto Cmd =
+ tooling::CompileCommand(std::move(Command.workingDirectory), File,
+ std::move(Command.compilationCommand),
/*Output=*/"");
- if (Old != New) {
- CDB->setCompileCommand(File, std::move(New));
+ if (CDB->setCompileCommand(File, std::move(Cmd))) {
ModifiedFiles.insert(File);
}
}
diff --git a/clang-tools-extra/clangd/Compiler.cpp b/clang-tools-extra/clangd/Compiler.cpp
index c60ab8e1b8062a85646ad62feb80f1e3fcc26a03..161cc9ae0ca365cc739c3bbd5c5c5e0402c99980 100644
--- a/clang-tools-extra/clangd/Compiler.cpp
+++ b/clang-tools-extra/clangd/Compiler.cpp
@@ -110,8 +110,8 @@ buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D,
CIOpts.VFS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
CIOpts.CC1Args = CC1Args;
CIOpts.RecoverOnError = true;
- CIOpts.Diags =
- CompilerInstance::createDiagnostics(new DiagnosticOptions, &D, false);
+ CIOpts.Diags = CompilerInstance::createDiagnostics(
+ *CIOpts.VFS, new DiagnosticOptions, &D, false);
CIOpts.ProbePrecompiled = false;
std::unique_ptr CI = createInvocation(ArgStrs, CIOpts);
if (!CI)
@@ -148,7 +148,7 @@ prepareCompilerInstance(std::unique_ptr CI,
auto Clang = std::make_unique(
std::make_shared());
Clang->setInvocation(std::move(CI));
- Clang->createDiagnostics(&DiagsClient, false);
+ Clang->createDiagnostics(*VFS, &DiagsClient, false);
if (auto VFSWithRemapping = createVFSFromCompilerInvocation(
Clang->getInvocation(), Clang->getDiagnostics(), VFS))
diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
index 1d96667a8e9f4a79d6341dcc8e522d2f3783f13c..71e97ac4efd673b157cbe8179d6507146dd7f6b3 100644
--- a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
+++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
@@ -807,7 +807,7 @@ tooling::CompileCommand OverlayCDB::getFallbackCommand(PathRef File) const {
return Cmd;
}
-void OverlayCDB::setCompileCommand(PathRef File,
+bool OverlayCDB::setCompileCommand(PathRef File,
std::optional Cmd) {
// We store a canonical version internally to prevent mismatches between set
// and get compile commands. Also it assures clients listening to broadcasts
@@ -815,12 +815,19 @@ void OverlayCDB::setCompileCommand(PathRef File,
std::string CanonPath = removeDots(File);
{
std::unique_lock Lock(Mutex);
- if (Cmd)
- Commands[CanonPath] = std::move(*Cmd);
- else
+ if (Cmd) {
+ if (auto [It, Inserted] =
+ Commands.try_emplace(CanonPath, std::move(*Cmd));
+ !Inserted) {
+ if (It->second == *Cmd)
+ return false;
+ It->second = *Cmd;
+ }
+ } else
Commands.erase(CanonPath);
}
OnCommandChanged.broadcast({CanonPath});
+ return true;
}
DelegatingCDB::DelegatingCDB(const GlobalCompilationDatabase *Base)
diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.h b/clang-tools-extra/clangd/GlobalCompilationDatabase.h
index ea999fe8aee0177a13e563ea8105aa9b87a28f78..f8349c6efecb01f85bef834a27d6c99c715607db 100644
--- a/clang-tools-extra/clangd/GlobalCompilationDatabase.h
+++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.h
@@ -203,7 +203,9 @@ public:
tooling::CompileCommand getFallbackCommand(PathRef File) const override;
/// Sets or clears the compilation command for a particular file.
- void
+ /// Returns true if the command was changed (including insertion and removal),
+ /// false if it was unchanged.
+ bool
setCompileCommand(PathRef File,
std::optional CompilationCommand);
diff --git a/clang-tools-extra/clangd/ModulesBuilder.cpp b/clang-tools-extra/clangd/ModulesBuilder.cpp
index 2bce3a2082561610ee6336cc7fda71ec0673efe1..29508901f85bba8ec99739890b20dc670b7c6bda 100644
--- a/clang-tools-extra/clangd/ModulesBuilder.cpp
+++ b/clang-tools-extra/clangd/ModulesBuilder.cpp
@@ -188,7 +188,8 @@ bool IsModuleFileUpToDate(PathRef ModuleFilePath,
clang::clangd::IgnoreDiagnostics IgnoreDiags;
IntrusiveRefCntPtr Diags =
- CompilerInstance::createDiagnostics(new DiagnosticOptions, &IgnoreDiags,
+ CompilerInstance::createDiagnostics(*VFS, new DiagnosticOptions,
+ &IgnoreDiags,
/*ShouldOwnClient=*/false);
LangOptions LangOpts;
diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp
index c14c4d1ba103f85080646d4291246b86475413e4..ce88ec0eb88c1beb44c629c7e20251db92d58850 100644
--- a/clang-tools-extra/clangd/Preamble.cpp
+++ b/clang-tools-extra/clangd/Preamble.cpp
@@ -613,8 +613,9 @@ buildPreamble(PathRef FileName, CompilerInvocation CI,
for (const auto &L : ASTListeners)
L->sawDiagnostic(D, Diag);
});
+ auto VFS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
llvm::IntrusiveRefCntPtr PreambleDiagsEngine =
- CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(),
+ CompilerInstance::createDiagnostics(*VFS, &CI.getDiagnosticOpts(),
&PreambleDiagnostics,
/*ShouldOwnClient=*/false);
const Config &Cfg = Config::current();
@@ -651,7 +652,6 @@ buildPreamble(PathRef FileName, CompilerInvocation CI,
for (const auto &L : ASTListeners)
L->beforeExecute(CI);
});
- auto VFS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
llvm::SmallString<32> AbsFileName(FileName);
VFS->makeAbsolute(AbsFileName);
auto StatCache = std::make_shared(AbsFileName);
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index 761f96846d45389cdf374e681a6f592516128fd4..05c8041df7de75ed24fc2e9df470c7b50bbef369 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -511,6 +511,35 @@ bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R,
if (auto EditsNearCursor = Completion->getBoolean("editsNearCursor"))
R.CompletionFixes |= *EditsNearCursor;
}
+ if (auto *References = TextDocument->getObject("references")) {
+ if (auto ContainerSupport = References->getBoolean("container")) {
+ R.ReferenceContainer |= *ContainerSupport;
+ }
+ }
+ if (auto *Diagnostics = TextDocument->getObject("publishDiagnostics")) {
+ if (auto CodeActions = Diagnostics->getBoolean("codeActionsInline")) {
+ R.DiagnosticFixes |= *CodeActions;
+ }
+ }
+ if (auto *InactiveRegions =
+ TextDocument->getObject("inactiveRegionsCapabilities")) {
+ if (auto InactiveRegionsSupport =
+ InactiveRegions->getBoolean("inactiveRegions")) {
+ R.InactiveRegions |= *InactiveRegionsSupport;
+ }
+ }
+ }
+ if (auto *Window = Experimental->getObject("window")) {
+ if (auto Implicit =
+ Window->getBoolean("implicitWorkDoneProgressCreate")) {
+ R.ImplicitProgressCreation |= *Implicit;
+ }
+ }
+ if (auto *OffsetEncoding = Experimental->get("offsetEncoding")) {
+ R.offsetEncoding.emplace();
+ if (!fromJSON(*OffsetEncoding, *R.offsetEncoding,
+ P.field("offsetEncoding")))
+ return false;
}
}
diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index 5b28095758198d1c38d66735290f37ad2aa0624b..c7ef1a13e6e39ec5c4909af3f12248888738439f 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -452,6 +452,7 @@ struct ClientCapabilities {
std::optional WorkspaceSymbolKinds;
/// Whether the client accepts diagnostics with codeActions attached inline.
+ /// This is a clangd extension.
/// textDocument.publishDiagnostics.codeActionsInline.
bool DiagnosticFixes = false;
@@ -475,6 +476,7 @@ struct ClientCapabilities {
/// Client supports displaying a container string for results of
/// textDocument/reference (clangd extension)
+ /// textDocument.references.container
bool ReferenceContainer = false;
/// Client supports hierarchical document symbols.
@@ -563,6 +565,7 @@ struct ClientCapabilities {
/// Whether the client supports the textDocument/inactiveRegions
/// notification. This is a clangd extension.
+ /// textDocument.inactiveRegionsCapabilities.inactiveRegions
bool InactiveRegions = false;
};
bool fromJSON(const llvm::json::Value &, ClientCapabilities &,
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index 4fd11307857ff84dcfbf38b67dedf7e030fe1ee8..61fa66180376cd1e7194091626ffd1acbe9a1759 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -63,6 +63,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include
@@ -2275,7 +2276,7 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
// Initially store the ranges in a map keyed by SymbolID of the caller.
// This allows us to group different calls with the same caller
// into the same CallHierarchyIncomingCall.
- llvm::DenseMap> CallsIn;
+ llvm::DenseMap> CallsIn;
// We can populate the ranges based on a refs request only. As we do so, we
// also accumulate the container IDs into a lookup request.
LookupRequest ContainerLookup;
@@ -2285,7 +2286,7 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
elog("incomingCalls failed to convert location: {0}", Loc.takeError());
return;
}
- CallsIn[R.Container].push_back(Loc->range);
+ CallsIn[R.Container].push_back(*Loc);
ContainerLookup.IDs.insert(R.Container);
});
@@ -2294,9 +2295,21 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
Index->lookup(ContainerLookup, [&](const Symbol &Caller) {
auto It = CallsIn.find(Caller.ID);
assert(It != CallsIn.end());
- if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file()))
+ if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file())) {
+ std::vector FromRanges;
+ for (const Location &L : It->second) {
+ if (L.uri != CHI->uri) {
+ // Call location not in same file as caller.
+ // This can happen in some edge cases. There's not much we can do,
+ // since the protocol only allows returning ranges interpreted as
+ // being in the caller's file.
+ continue;
+ }
+ FromRanges.push_back(L.range);
+ }
Results.push_back(
- CallHierarchyIncomingCall{std::move(*CHI), std::move(It->second)});
+ CallHierarchyIncomingCall{std::move(*CHI), std::move(FromRanges)});
+ }
});
// Sort results by name of container.
llvm::sort(Results, [](const CallHierarchyIncomingCall &A,
diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
index 591a8b245260eab6c43a1bb7e385d3733e65623d..789c10bdd4822ae3aa02be42cc60c1c26a123a72 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
@@ -109,14 +109,13 @@ findContextForNS(llvm::StringRef TargetNS, const DeclContext *CurContext) {
// afterwards it can be shared with define-inline code action.
llvm::Expected
getFunctionSourceAfterReplacements(const FunctionDecl *FD,
- const tooling::Replacements &Replacements) {
+ const tooling::Replacements &Replacements,
+ bool TargetFileIsHeader) {
const auto &SM = FD->getASTContext().getSourceManager();
auto OrigFuncRange = toHalfOpenFileRange(
SM, FD->getASTContext().getLangOpts(), FD->getSourceRange());
if (!OrigFuncRange)
return error("Couldn't get range for function.");
- assert(!FD->getDescribedFunctionTemplate() &&
- "Define out-of-line doesn't apply to function templates.");
// Get new begin and end positions for the qualified function definition.
unsigned FuncBegin = SM.getFileOffset(OrigFuncRange->getBegin());
@@ -129,24 +128,38 @@ getFunctionSourceAfterReplacements(const FunctionDecl *FD,
if (!QualifiedFunc)
return QualifiedFunc.takeError();
+ auto Source = QualifiedFunc->substr(FuncBegin, FuncEnd - FuncBegin + 1);
std::string TemplatePrefix;
+ auto AddToTemplatePrefixIfApplicable = [&](const Decl *D) {
+ const TemplateParameterList *Params = D->getDescribedTemplateParams();
+ if (!Params)
+ return;
+ for (Decl *P : *Params) {
+ if (auto *TTP = dyn_cast(P))
+ TTP->removeDefaultArgument();
+ else if (auto *NTTP = dyn_cast(P))
+ NTTP->removeDefaultArgument();
+ else if (auto *TTPD = dyn_cast(P))
+ TTPD->removeDefaultArgument();
+ }
+ std::string S;
+ llvm::raw_string_ostream Stream(S);
+ Params->print(Stream, FD->getASTContext());
+ if (!S.empty())
+ *S.rbegin() = '\n'; // Replace space with newline
+ TemplatePrefix.insert(0, S);
+ };
+ AddToTemplatePrefixIfApplicable(FD);
if (auto *MD = llvm::dyn_cast(FD)) {
for (const CXXRecordDecl *Parent = MD->getParent(); Parent;
Parent =
llvm::dyn_cast_or_null(Parent->getParent())) {
- if (const TemplateParameterList *Params =
- Parent->getDescribedTemplateParams()) {
- std::string S;
- llvm::raw_string_ostream Stream(S);
- Params->print(Stream, FD->getASTContext());
- if (!S.empty())
- *S.rbegin() = '\n'; // Replace space with newline
- TemplatePrefix.insert(0, S);
- }
+ AddToTemplatePrefixIfApplicable(Parent);
}
}
- auto Source = QualifiedFunc->substr(FuncBegin, FuncEnd - FuncBegin + 1);
+ if (TargetFileIsHeader)
+ Source.insert(0, "inline ");
if (!TemplatePrefix.empty())
Source.insert(0, TemplatePrefix);
return Source;
@@ -202,7 +215,8 @@ deleteTokensWithKind(const syntax::TokenBuffer &TokBuf, tok::TokenKind Kind,
llvm::Expected
getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext,
const syntax::TokenBuffer &TokBuf,
- const HeuristicResolver *Resolver) {
+ const HeuristicResolver *Resolver,
+ bool TargetFileIsHeader) {
auto &AST = FD->getASTContext();
auto &SM = AST.getSourceManager();
@@ -225,6 +239,8 @@ getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext,
return;
for (const NamedDecl *ND : Ref.Targets) {
+ if (ND->getKind() == Decl::TemplateTypeParm)
+ return;
if (ND->getDeclContext() != Ref.Targets.front()->getDeclContext()) {
elog("Targets from multiple contexts: {0}, {1}",
printQualifiedName(*Ref.Targets.front()),
@@ -337,7 +353,8 @@ getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext,
if (Errors)
return std::move(Errors);
- return getFunctionSourceAfterReplacements(FD, DeclarationCleanups);
+ return getFunctionSourceAfterReplacements(FD, DeclarationCleanups,
+ TargetFileIsHeader);
}
struct InsertionPoint {
@@ -419,15 +436,15 @@ public:
Source->isOutOfLine())
return false;
- // Bail out if this is a function template or specialization, as their
+ // Bail out if this is a function template specialization, as their
// definitions need to be visible in all including translation units.
- if (Source->getDescribedFunctionTemplate())
- return false;
if (Source->getTemplateSpecializationInfo())
return false;
auto *MD = llvm::dyn_cast(Source);
if (!MD) {
+ if (Source->getDescribedFunctionTemplate())
+ return false;
// Can't outline free-standing functions in the same file.
return !SameFile;
}
@@ -450,6 +467,19 @@ public:
}
}
+ // For function templates, the same limitations as for class templates
+ // apply.
+ if (const TemplateParameterList *Params =
+ MD->getDescribedTemplateParams()) {
+ // FIXME: Is this really needed? It inhibits application on
+ // e.g. std::enable_if.
+ for (NamedDecl *P : *Params) {
+ if (!P->getIdentifier())
+ return false;
+ }
+ SameFile = true;
+ }
+
// The refactoring is meaningless for unnamed classes and namespaces,
// unless we're outlining in the same file
for (const DeclContext *DC = MD->getParent(); DC; DC = DC->getParent()) {
@@ -485,7 +515,8 @@ public:
auto FuncDef = getFunctionSourceCode(
Source, InsertionPoint->EnclosingNamespace, Sel.AST->getTokens(),
- Sel.AST->getHeuristicResolver());
+ Sel.AST->getHeuristicResolver(),
+ SameFile && isHeaderFile(Sel.AST->tuPath(), Sel.AST->getLangOpts()));
if (!FuncDef)
return FuncDef.takeError();
diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
index 0302839c58252e747778c4091352fa148933b5c2..cd07cbf73635c267e74166fbd471c487eba3e03f 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
@@ -56,6 +56,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
@@ -70,7 +71,6 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
-#include "llvm/Support/raw_os_ostream.h"
#include
namespace clang {
@@ -95,8 +95,8 @@ enum FunctionDeclKind {
OutOfLineDefinition
};
-// A RootStmt is a statement that's fully selected including all it's children
-// and it's parent is unselected.
+// A RootStmt is a statement that's fully selected including all its children
+// and its parent is unselected.
// Check if a node is a root statement.
bool isRootStmt(const Node *N) {
if (!N->ASTNode.get())
@@ -104,9 +104,12 @@ bool isRootStmt(const Node *N) {
// Root statement cannot be partially selected.
if (N->Selected == SelectionTree::Partial)
return false;
- // Only DeclStmt can be an unselected RootStmt since VarDecls claim the entire
- // selection range in selectionTree.
- if (N->Selected == SelectionTree::Unselected && !N->ASTNode.get())
+ // A DeclStmt can be an unselected RootStmt since VarDecls claim the entire
+ // selection range in selectionTree. Additionally, a CXXOperatorCallExpr of a
+ // binary operation can be unselected because its children claim the entire
+ // selection range in the selection tree (e.g. <<).
+ if (N->Selected == SelectionTree::Unselected && !N->ASTNode.get() &&
+ !N->ASTNode.get())
return false;
return true;
}
@@ -913,8 +916,8 @@ Expected ExtractFunction::apply(const Selection &Inputs) {
tooling::Replacements OtherEdit(
createForwardDeclaration(*ExtractedFunc, SM));
- if (auto PathAndEdit = Tweak::Effect::fileEdit(SM, SM.getFileID(*FwdLoc),
- OtherEdit))
+ if (auto PathAndEdit =
+ Tweak::Effect::fileEdit(SM, SM.getFileID(*FwdLoc), OtherEdit))
MultiFileEffect->ApplyEdits.try_emplace(PathAndEdit->first,
PathAndEdit->second);
else
diff --git a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
index b2278ff12735dc10ab359fa2ebffd7507f47ebd4..8821d3aad9c784a25c307f8053acce22ac50c9f7 100644
--- a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
@@ -491,6 +491,35 @@ TEST(CallHierarchy, HierarchyOnVar) {
fromRanges(Source.range("Callee")))));
}
+TEST(CallHierarchy, CallInDifferentFileThanCaller) {
+ Annotations Header(R"cpp(
+ #define WALDO void caller() {
+ )cpp");
+ Annotations Source(R"cpp(
+ void call^ee();
+ WALDO
+ callee();
+ }
+ )cpp");
+ auto TU = TestTU::withCode(Source.code());
+ TU.HeaderCode = Header.code();
+ auto AST = TU.build();
+ auto Index = TU.index();
+
+ std::vector Items =
+ prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
+ ASSERT_THAT(Items, ElementsAre(withName("callee")));
+
+ auto Incoming = incomingCalls(Items[0], Index.get());
+
+ // The only call site is in the source file, which is a different file from
+ // the declaration of the function containing the call, which is in the
+ // header. The protocol does not allow us to represent such calls, so we drop
+ // them. (The call hierarchy item itself is kept.)
+ EXPECT_THAT(Incoming,
+ ElementsAre(AllOf(from(withName("caller")), fromRanges())));
+}
+
} // namespace
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp b/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp
index a2ffdefe1bbcb2d7b1ef5fff183a5fef533a3284..c9e01e52dac1f3a6285000813e1526c9eb7d136c 100644
--- a/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp
+++ b/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp
@@ -92,11 +92,13 @@ TEST_F(OverlayCDBTest, GetCompileCommand) {
EXPECT_EQ(CDB.getCompileCommand(testPath("missing.cc")), std::nullopt);
auto Override = cmd(testPath("foo.cc"), "-DA=3");
- CDB.setCompileCommand(testPath("foo.cc"), Override);
+ EXPECT_TRUE(CDB.setCompileCommand(testPath("foo.cc"), Override));
+ EXPECT_FALSE(CDB.setCompileCommand(testPath("foo.cc"), Override));
EXPECT_THAT(CDB.getCompileCommand(testPath("foo.cc"))->CommandLine,
Contains("-DA=3"));
EXPECT_EQ(CDB.getCompileCommand(testPath("missing.cc")), std::nullopt);
- CDB.setCompileCommand(testPath("missing.cc"), Override);
+ EXPECT_TRUE(CDB.setCompileCommand(testPath("missing.cc"), Override));
+ EXPECT_FALSE(CDB.setCompileCommand(testPath("missing.cc"), Override));
EXPECT_THAT(CDB.getCompileCommand(testPath("missing.cc"))->CommandLine,
Contains("-DA=3"));
}
@@ -111,7 +113,7 @@ TEST_F(OverlayCDBTest, NoBase) {
OverlayCDB CDB(nullptr, {"-DA=6"});
EXPECT_EQ(CDB.getCompileCommand(testPath("bar.cc")), std::nullopt);
auto Override = cmd(testPath("bar.cc"), "-DA=5");
- CDB.setCompileCommand(testPath("bar.cc"), Override);
+ EXPECT_TRUE(CDB.setCompileCommand(testPath("bar.cc"), Override));
EXPECT_THAT(CDB.getCompileCommand(testPath("bar.cc"))->CommandLine,
Contains("-DA=5"));
@@ -128,10 +130,10 @@ TEST_F(OverlayCDBTest, Watch) {
Changes.push_back(ChangedFiles);
});
- Inner.setCompileCommand("A.cpp", tooling::CompileCommand());
- Outer.setCompileCommand("B.cpp", tooling::CompileCommand());
- Inner.setCompileCommand("A.cpp", std::nullopt);
- Outer.setCompileCommand("C.cpp", std::nullopt);
+ EXPECT_TRUE(Inner.setCompileCommand("A.cpp", tooling::CompileCommand()));
+ EXPECT_TRUE(Outer.setCompileCommand("B.cpp", tooling::CompileCommand()));
+ EXPECT_TRUE(Inner.setCompileCommand("A.cpp", std::nullopt));
+ EXPECT_TRUE(Outer.setCompileCommand("C.cpp", std::nullopt));
EXPECT_THAT(Changes, ElementsAre(ElementsAre("A.cpp"), ElementsAre("B.cpp"),
ElementsAre("A.cpp"), ElementsAre("C.cpp")));
}
@@ -151,7 +153,7 @@ TEST_F(OverlayCDBTest, Adjustments) {
tooling::CompileCommand BarCommand;
BarCommand.Filename = testPath("bar.cc");
BarCommand.CommandLine = {"clang++", "-DB=1", testPath("bar.cc")};
- CDB.setCompileCommand(testPath("bar.cc"), BarCommand);
+ EXPECT_TRUE(CDB.setCompileCommand(testPath("bar.cc"), BarCommand));
Cmd = *CDB.getCompileCommand(testPath("bar.cc"));
EXPECT_THAT(
Cmd.CommandLine,
@@ -412,7 +414,7 @@ TEST(GlobalCompilationDatabaseTest, NonCanonicalFilenames) {
llvm::SmallString<128> Root(testRoot());
llvm::sys::path::append(Root, "build", "..", "a.cc");
- DB.setCompileCommand(Root.str(), tooling::CompileCommand());
+ EXPECT_TRUE(DB.setCompileCommand(Root.str(), tooling::CompileCommand()));
EXPECT_THAT(DiscoveredFiles, UnorderedElementsAre(testPath("a.cc")));
DiscoveredFiles.clear();
@@ -432,7 +434,7 @@ TEST_F(OverlayCDBTest, GetProjectInfo) {
EXPECT_EQ(DB.getProjectInfo(Header)->SourceRoot, testRoot());
// Shouldn't change after an override.
- DB.setCompileCommand(File, tooling::CompileCommand());
+ EXPECT_TRUE(DB.setCompileCommand(File, tooling::CompileCommand()));
EXPECT_EQ(DB.getProjectInfo(File)->SourceRoot, testRoot());
EXPECT_EQ(DB.getProjectInfo(Header)->SourceRoot, testRoot());
}
diff --git a/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
index 6a9e90c3bfa70fc55a61e4bafcc83ab20017c59b..d2d2ae9e7bb6103fb6eda9e099d644be60466a24 100644
--- a/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
+++ b/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
@@ -111,11 +111,17 @@ TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
template struct Foo { void fo^o(){} };
)cpp");
- // Not available on function templates and specializations, as definition must
- // be visible to all translation units.
+ // Not available on function template specializations and free function
+ // templates.
EXPECT_UNAVAILABLE(R"cpp(
- template void fo^o() {};
- template <> void fo^o() {};
+ template void fo^o() {}
+ template <> void fo^o() {}
+ )cpp");
+
+ // Not available on member function templates with unnamed template
+ // parameters.
+ EXPECT_UNAVAILABLE(R"cpp(
+ struct Foo { template void ba^r() {} };
)cpp");
// Not available on methods of unnamed classes.
@@ -237,7 +243,7 @@ TEST_F(DefineOutlineTest, ApplyTest) {
Foo(T z) __attribute__((weak)) ;
int bar;
};template
-Foo::Foo(T z) __attribute__((weak)) : bar(2){}
+inline Foo::Foo(T z) __attribute__((weak)) : bar(2){}
)cpp",
""},
// Virt specifiers.
@@ -390,7 +396,7 @@ Foo::Foo(T z) __attribute__((weak)) : bar(2){}
};
};template
template
-typename O1::template O2::E O1::template O2::I::foo(T, U..., V, E) { return E1; }
+inline typename O1::template O2::E O1::template O2::I::foo(T, U..., V, E) { return E1; }
)cpp",
""},
// Destructors
@@ -399,6 +405,37 @@ typename O1::template O2::E O1::template O2::I::fo
"class A { ~A(); };",
"A::~A(){} ",
},
+
+ // Member template
+ {
+ R"cpp(
+ struct Foo {
+ template
+ T ^bar() { return {}; }
+ };)cpp",
+ R"cpp(
+ struct Foo {
+ template
+ T bar() ;
+ };template
+inline T Foo::bar() { return {}; }
+)cpp",
+ ""},
+
+ // Class template with member template
+ {
+ R"cpp(
+ template struct Foo {
+ template T ^bar(const T& t, const U& u) { return {}; }
+ };)cpp",
+ R"cpp(
+ template struct Foo {
+ template T bar(const T& t, const U& u) ;
+ };template
+template
+inline T Foo::bar(const T& t, const U& u) { return {}; }
+)cpp",
+ ""},
};
for (const auto &Case : Cases) {
SCOPED_TRACE(Case.Test);
diff --git a/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp
index dec63d454d52c6f71e911f4215cd9d8bf9280967..eff4d0f43595c57a9060070aa2836a96535015d1 100644
--- a/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp
+++ b/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp
@@ -190,6 +190,18 @@ F (extracted();)
}]]
)cpp";
EXPECT_EQ(apply(CompoundFailInput), "unavailable");
+
+ ExtraArgs.push_back("-std=c++14");
+ // FIXME: Expressions are currently not extracted
+ EXPECT_EQ(apply(R"cpp(
+ void call() { [[1+1]]; }
+ )cpp"),
+ "unavailable");
+ // FIXME: Single expression statements are currently not extracted
+ EXPECT_EQ(apply(R"cpp(
+ void call() { [[1+1;]] }
+ )cpp"),
+ "unavailable");
}
TEST_F(ExtractFunctionTest, DifferentHeaderSourceTest) {
@@ -571,6 +583,53 @@ int getNum(bool Superstitious, int Min, int Max) {
EXPECT_EQ(apply(Before), After);
}
+TEST_F(ExtractFunctionTest, OverloadedOperators) {
+ Context = File;
+ std::string Before = R"cpp(struct A {
+ int operator+(int x) { return x; }
+ };
+ A &operator<<(A &, int);
+ A &operator|(A &, int);
+
+ A stream{};
+
+ void foo(int, int);
+
+ int main() {
+ [[foo(1, 2);
+ foo(3, 4);
+ stream << 42;
+ stream + 42;
+ stream | 42;
+ foo(1, 2);
+ foo(3, 4);]]
+ })cpp";
+ std::string After =
+ R"cpp(struct A {
+ int operator+(int x) { return x; }
+ };
+ A &operator<<(A &, int);
+ A &operator|(A &, int);
+
+ A stream{};
+
+ void foo(int, int);
+
+ void extracted() {
+foo(1, 2);
+ foo(3, 4);
+ stream << 42;
+ stream + 42;
+ stream | 42;
+ foo(1, 2);
+ foo(3, 4);
+}
+int main() {
+ extracted();
+ })cpp";
+ EXPECT_EQ(apply(Before), After);
+}
+
} // namespace
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index db971f08ca3dbc1bfdc16940364a5594ed8a71f5..f967dfabd1c9401bdc3ac53fea63ef3701084b19 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -78,6 +78,9 @@ Code actions
- Added `Swap operands` tweak for certain binary operators.
+- Improved the extract-to-function code action to allow extracting statements
+ with overloaded operators like ``<<`` of ``std::ostream``.
+
Signature help
^^^^^^^^^^^^^^
@@ -191,6 +194,11 @@ Changes in existing checks
` check to allow specifying
additional functions to match.
+- Improved :doc:`bugprone-use-after-move
+ ` to avoid triggering on
+ ``reset()`` calls on moved-from ``std::optional`` and ``std::any`` objects,
+ similarly to smart pointers.
+
- Improved :doc:`cert-flp30-c ` check to
fix false positive that floating point variable is only used in increment
expression.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst
index 08bb5374bab1f4e74d4608009728229c9a06174f..965fc2d3c29e2462b3b8164fd620f9249e492b1a 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst
@@ -196,11 +196,13 @@ Any occurrence of the moved variable that is not a reinitialization (see below)
is considered to be a use.
An exception to this are objects of type ``std::unique_ptr``,
-``std::shared_ptr`` and ``std::weak_ptr``, which have defined move behavior
-(objects of these classes are guaranteed to be empty after they have been moved
-from). Therefore, an object of these classes will only be considered to be used
-if it is dereferenced, i.e. if ``operator*``, ``operator->`` or ``operator[]``
-(in the case of ``std::unique_ptr``) is called on it.
+``std::shared_ptr``, ``std::weak_ptr``, ``std::optional``, and ``std::any``.
+An exception to this are objects of type ``std::unique_ptr``,
+``std::shared_ptr``, ``std::weak_ptr``, ``std::optional``, and ``std::any``, which
+can be reinitialized via ``reset``. For smart pointers specifically, the
+moved-from objects have a well-defined state of being ``nullptr``s, and only
+``operator*``, ``operator->`` and ``operator[]`` are considered bad accesses as
+they would be dereferencing a ``nullptr``.
If multiple uses occur after a move, only the first of these is flagged.
@@ -222,7 +224,8 @@ The check considers a variable to be reinitialized in the following cases:
``unordered_multimap``.
- ``reset()`` is called on the variable and the variable is of type
- ``std::unique_ptr``, ``std::shared_ptr`` or ``std::weak_ptr``.
+ ``std::unique_ptr``, ``std::shared_ptr``, ``std::weak_ptr``,
+ ``std::optional``, or ``std::any``.
- A member function marked with the ``[[clang::reinitializes]]`` attribute is
called on the variable.
diff --git a/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp b/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp
index b5a7b9720903ebe748bae800332726237dc6b1e9..b1bbb2eb82414cb9890a1cccd0aa174ab65c7214 100644
--- a/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp
+++ b/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp
@@ -609,15 +609,6 @@ TEST_F(PragmaIncludeTest, ExportInUnnamedBuffer) {
)cpp";
Inputs.ExtraFiles["foo.h"] = "";
- auto Clang = std::make_unique(
- std::make_shared());
- Clang->createDiagnostics();
-
- Clang->setInvocation(std::make_unique());
- ASSERT_TRUE(CompilerInvocation::CreateFromArgs(
- Clang->getInvocation(), {Filename.data()}, Clang->getDiagnostics(),
- "clang"));
-
// Create unnamed memory buffers for all the files.
auto VFS = llvm::makeIntrusiveRefCnt();
VFS->addFile(Filename, /*ModificationTime=*/0,
@@ -626,6 +617,16 @@ TEST_F(PragmaIncludeTest, ExportInUnnamedBuffer) {
VFS->addFile(Extra.getKey(), /*ModificationTime=*/0,
llvm::MemoryBuffer::getMemBufferCopy(Extra.getValue(),
/*BufferName=*/""));
+
+ auto Clang = std::make_unique(
+ std::make_shared());
+ Clang->createDiagnostics(*VFS);
+
+ Clang->setInvocation(std::make_unique());
+ ASSERT_TRUE(CompilerInvocation::CreateFromArgs(
+ Clang->getInvocation(), {Filename.data()}, Clang->getDiagnostics(),
+ "clang"));
+
auto *FM = Clang->createFileManager(VFS);
ASSERT_TRUE(Clang->ExecuteAction(*Inputs.MakeAction()));
EXPECT_THAT(
diff --git a/clang-tools-extra/test/CMakeLists.txt b/clang-tools-extra/test/CMakeLists.txt
index d72a117166a08be689009d93a499d10ae7033fa7..7e4d99d8cfc1d3951d3bcba23bd34f17b1d223d5 100644
--- a/clang-tools-extra/test/CMakeLists.txt
+++ b/clang-tools-extra/test/CMakeLists.txt
@@ -50,8 +50,6 @@ set(CLANG_TOOLS_TEST_DEPS
clang-resource-headers
clang-tidy
- # Clang-tidy tests need clang for building modules.
- clang
)
# Add lit test dependencies.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp
index 6a4e3990e36dc5afc22b6958f89bdabda6e0a2c6..87dfec4f68061f2160cace37e600ee8379b67a7c 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp
@@ -33,6 +33,19 @@ struct weak_ptr {
bool expired() const;
};
+template
+struct optional {
+ optional();
+ T& operator*();
+ const T& operator*() const;
+ void reset();
+};
+
+struct any {
+ any();
+ void reset();
+};
+
template
struct pair {};
@@ -257,6 +270,14 @@ void standardSmartPtr() {
// CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
// CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
}
+ {
+ std::optional opt;
+ std::move(opt);
+ A val = *opt;
+ (void)val;
+ // CHECK-NOTES: [[@LINE-2]]:14: warning: 'opt' used after it was moved
+ // CHECK-NOTES: [[@LINE-4]]:5: note: move occurred here
+ }
{
// std::weak_ptr<> cannot be dereferenced directly, so we only check that
// member functions may be called on it after a move.
@@ -994,10 +1015,10 @@ void standardContainerAssignIsReinit() {
}
}
-// Resetting the standard smart pointer types using reset() is treated as a
+// Resetting the standard smart owning types using reset() is treated as a
// re-initialization. (We don't test std::weak_ptr<> because it can't be
// dereferenced directly.)
-void standardSmartPointerResetIsReinit() {
+void resetIsReinit() {
{
std::unique_ptr ptr;
std::move(ptr);
@@ -1010,6 +1031,20 @@ void standardSmartPointerResetIsReinit() {
ptr.reset(new A);
*ptr;
}
+ {
+ std::optional opt;
+ std::move(opt);
+ opt.reset();
+ std::optional opt2 = opt;
+ (void)opt2;
+ }
+ {
+ std::any a;
+ std::move(a);
+ a.reset();
+ std::any a2 = a;
+ (void)a2;
+ }
}
void reinitAnnotation() {
diff --git a/clang/Maintainers.rst b/clang/Maintainers.rst
index 26495f3d8779452b54ef23101a01a4ea70c91b58..b601f4da0b3a933d1365057e8dbec2e4cccad30f 100644
--- a/clang/Maintainers.rst
+++ b/clang/Maintainers.rst
@@ -134,6 +134,8 @@ Clang static analyzer
| Gábor Horváth
| xazax.hun\@gmail.com (email), xazax.hun (Phabricator), Xazax-hun (GitHub)
+| Balázs Benics
+| benicsbalazs\@gmail.com (email), steakhal (Phabricator), steakhal (GitHub)
Compiler options
~~~~~~~~~~~~~~~~
@@ -168,6 +170,12 @@ Constant Expressions
| mariya.podchishchaeva\@intel.com (email), Fznamznon (GitHub), fznamznon (Discord), Fznamznon (Discourse)
+Thread Safety Analysis
+~~~~~~~~~~~~~~~~~~~~~~
+| Aaron Puchert
+| aaron.puchert\@sap.com (email), aaronpuchert (GitHub), aaronpuchert (Discourse)
+
+
Tools
-----
These maintainers are responsible for user-facing tools under the Clang
diff --git a/clang/cmake/caches/CrossWinToARMLinux.cmake b/clang/cmake/caches/CrossWinToARMLinux.cmake
index 87118bbd33377d37b01701995052562c6b9ddedd..853217c6db61a30bbde97b3f7f17aa3f384598ec 100644
--- a/clang/cmake/caches/CrossWinToARMLinux.cmake
+++ b/clang/cmake/caches/CrossWinToARMLinux.cmake
@@ -119,7 +119,6 @@ if (NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "")
endif()
-set(CMAKE_CROSSCOMPILING ON CACHE BOOL "")
set(CMAKE_CL_SHOWINCLUDES_PREFIX "Note: including file: " CACHE STRING "")
# Required if COMPILER_RT_DEFAULT_TARGET_ONLY is ON
set(CMAKE_C_COMPILER_TARGET "${TOOLCHAIN_TARGET_TRIPLE}" CACHE STRING "")
@@ -219,6 +218,11 @@ set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_CXX_ABI
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ENABLE_NEW_DELETE_DEFINITIONS ON CACHE BOOL "")
# Merge libc++ and libc++abi libraries into the single libc++ library file.
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
+# Forcely disable the libc++ benchmarks on Windows build hosts
+# (current benchmark test configuration does not support the cross builds there).
+if (WIN32)
+ set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_INCLUDE_BENCHMARKS OFF CACHE BOOL "")
+endif(WIN32)
# Avoid searching for the python3 interpreter during the runtimes configuration for the cross builds.
# It starts searching the python3 package using the target's sysroot path, that usually is not compatible with the build host.
diff --git a/clang/cmake/caches/Fuchsia-stage2.cmake b/clang/cmake/caches/Fuchsia-stage2.cmake
index 5af98c7b3b3fba237702559ca6db8582a2b8b12e..747d9974828984a1d6aff6b190a96217ae5dd9cc 100644
--- a/clang/cmake/caches/Fuchsia-stage2.cmake
+++ b/clang/cmake/caches/Fuchsia-stage2.cmake
@@ -302,7 +302,7 @@ if(FUCHSIA_SDK)
set(LLVM_RUNTIME_MULTILIB_hwasan+noexcept_TARGETS "aarch64-unknown-fuchsia;riscv64-unknown-fuchsia" CACHE STRING "")
endif()
-foreach(target armv6m-none-eabi;armv7m-none-eabi;armv8m.main-none-eabi)
+foreach(target armv6m-none-eabi;armv7m-none-eabi;armv8m.main-none-eabi;armv8.1m.main-none-eabi)
list(APPEND BUILTIN_TARGETS "${target}")
set(BUILTINS_${target}_CMAKE_SYSTEM_NAME Generic CACHE STRING "")
set(BUILTINS_${target}_CMAKE_SYSTEM_PROCESSOR arm CACHE STRING "")
@@ -313,6 +313,9 @@ foreach(target armv6m-none-eabi;armv7m-none-eabi;armv8m.main-none-eabi)
if(${target} STREQUAL "armv8m.main-none-eabi")
set(BUILTINS_${target}_CMAKE_${lang}_local_flags "${BUILTINS_${target}_CMAKE_${lang}_local_flags} -mfloat-abi=softfp -march=armv8m.main+fp+dsp -mcpu=cortex-m33" CACHE STRING "")
endif()
+ if(${target} STREQUAL "armv8.1m.main-none-eabi")
+ set(BUILTINS_${target}_CMAKE_${lang}_local_flags "${BUILTINS_${target}_CMAKE_${lang}_local_flags} -mfloat-abi=hard -march=armv8.1-m.main+mve.fp+fp.dp -mcpu=cortex-m55" CACHE STRING "")
+ endif()
set(BUILTINS_${target}_CMAKE_${lang}_FLAGS "${BUILTINS_${target}_CMAKE_${lang}_local_flags}" CACHE STRING "")
endforeach()
foreach(type SHARED;MODULE;EXE)
@@ -333,6 +336,9 @@ foreach(target armv6m-none-eabi;armv7m-none-eabi;armv8m.main-none-eabi)
if(${target} STREQUAL "armv8m.main-none-eabi")
set(RUNTIMES_${target}_CMAKE_${lang}_local_flags "${RUNTIMES_${target}_CMAKE_${lang}_local_flags} -mfloat-abi=softfp -march=armv8m.main+fp+dsp -mcpu=cortex-m33" CACHE STRING "")
endif()
+ if(${target} STREQUAL "armv8.1m.main-none-eabi")
+ set(RUNTIMES_${target}_CMAKE_${lang}_local_flags "${RUNTIMES_${target}_CMAKE_${lang}_local_flags} -mfloat-abi=hard -march=armv8.1-m.main+mve.fp+fp.dp -mcpu=cortex-m55" CACHE STRING "")
+ endif()
set(RUNTIMES_${target}_CMAKE_${lang}_FLAGS "${RUNTIMES_${target}_CMAKE_${lang}_local_flags}" CACHE STRING "")
endforeach()
foreach(type SHARED;MODULE;EXE)
diff --git a/clang/docs/AddressSanitizer.rst b/clang/docs/AddressSanitizer.rst
index d937cbfdf583c4b377c6e8be91997104fdda5874..8d9295f246f0d35e1f225b305ded83a6c10b24d2 100644
--- a/clang/docs/AddressSanitizer.rst
+++ b/clang/docs/AddressSanitizer.rst
@@ -326,15 +326,13 @@ Supported Platforms
AddressSanitizer is supported on:
-* Linux i386/x86\_64 (tested on Ubuntu 12.04)
-* macOS 10.7 - 10.11 (i386/x86\_64)
+* Linux
+* macOS
* iOS Simulator
-* Android ARM
-* NetBSD i386/x86\_64
-* FreeBSD i386/x86\_64 (tested on FreeBSD 11-current)
-* Windows 8.1+ (i386/x86\_64)
-
-Ports to various other platforms are in progress.
+* Android
+* NetBSD
+* FreeBSD
+* Windows 8.1+
Current Status
==============
diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst
index b70ec35f3da229c4960b2055f96662e99a8825ca..39d389b816f12944bfe2fc931791d707e0e920ed 100644
--- a/clang/docs/InternalsManual.rst
+++ b/clang/docs/InternalsManual.rst
@@ -160,6 +160,10 @@ wording a diagnostic.
named in a diagnostic message. e.g., prefer wording like ``'this' pointer
cannot be null in well-defined C++ code`` over wording like ``this pointer
cannot be null in well-defined C++ code``.
+* Prefer diagnostic wording without contractions whenever possible. The single
+ quote in a contraction can be visually distracting due to its use with
+ syntactic constructs and contractions can be harder to understand for non-
+ native English speakers.
The Format String
^^^^^^^^^^^^^^^^^
@@ -315,6 +319,17 @@ Description:
than ``1`` are not supported. This formatter is currently hard-coded to use
English ordinals.
+**"human" format**
+
+Example:
+ ``"total size is %human0 bytes"``
+Class:
+ Integers
+Description:
+ This is a formatter which represents the argument number in a human readable
+ format: the value ``123`` stays ``123``, ``12345`` becomes ``12.34k``,
+ ``6666666` becomes ``6.67M``, and so on for 'G' and 'T'.
+
**"objcclass" format**
Example:
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 0998e6b30e229e0a6036b67c8902857106dadb16..ff8e841ee53a2bdb5af1c526b371b56614264de8 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -745,12 +745,10 @@ Let ``VT`` be a vector type and ``ET`` the element type of ``VT``.
======================================= ====================================================================== ==================================
Name Operation Supported element types
======================================= ====================================================================== ==================================
- ET __builtin_reduce_max(VT a) return x or y, whichever is larger; If exactly one argument is integer and floating point types
- a NaN, return the other argument. If both arguments are NaNs,
- fmax() return a NaN.
- ET __builtin_reduce_min(VT a) return x or y, whichever is smaller; If exactly one argument integer and floating point types
- is a NaN, return the other argument. If both arguments are
- NaNs, fmax() return a NaN.
+ ET __builtin_reduce_max(VT a) return the largest element of the vector. The floating point result integer and floating point types
+ will always be a number unless all elements of the vector are NaN.
+ ET __builtin_reduce_min(VT a) return the smallest element of the vector. The floating point result integer and floating point types
+ will always be a number unless all elements of the vector are NaN.
ET __builtin_reduce_add(VT a) \+ integer types
ET __builtin_reduce_mul(VT a) \* integer types
ET __builtin_reduce_and(VT a) & integer types
diff --git a/clang/docs/LeakSanitizer.rst b/clang/docs/LeakSanitizer.rst
index adcb6421c6a1f92dd0062c4664ad95c80dd3d425..ecdb87f0b259dd970916ce434feab2de9f2b2207 100644
--- a/clang/docs/LeakSanitizer.rst
+++ b/clang/docs/LeakSanitizer.rst
@@ -54,11 +54,11 @@ constraints in mind and may compromise the security of the resulting executable.
Supported Platforms
===================
-* Android aarch64/i386/x86_64
-* Fuchsia aarch64/x86_64
-* Linux arm/aarch64/mips64/ppc64/ppc64le/riscv64/s390x/i386/x86\_64
-* macOS aarch64/i386/x86\_64
-* NetBSD i386/x86_64
+* Android
+* Fuchsia
+* Linux
+* macOS
+* NetBSD
More Information
================
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7c210c17dda7d4666e88c24f4bbdcab7b272d21e..49464e457da68196af9378f4e5bcefab6c3f12c2 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -271,6 +271,8 @@ Resolutions to C++ Defect Reports
C Language Changes
------------------
+- Extend clang's ```` to define ``LONG_LONG_*`` macros for Android's bionic.
+
C2y Feature Support
^^^^^^^^^^^^^^^^^^^
@@ -278,7 +280,7 @@ C2y Feature Support
which adds the ``i`` and ``j`` suffixes for the creation of a ``_Complex``
constant value. Clang has always supported these suffixes as a GNU extension,
so ``-Wgnu-imaginary-constant`` no longer has effect in C modes, as this is
- not a C2y extension in C. ``-Wgnu-imaginary-constant`` still applies in C++
+ now a C2y extension in C. ``-Wgnu-imaginary-constant`` still applies in C++
modes.
- Clang updated conformance for `N3370 `_
@@ -318,6 +320,7 @@ C23 Feature Support
^^^^^^^^^^^^^^^^^^^
- Clang now supports `N3029 `_ Improved Normal Enumerations.
+- Clang now officially supports `N3030 `_ Enhancements to Enumerations. Clang already supported it as an extension, so there were no changes to compiler behavior.
Non-comprehensive list of changes in this release
-------------------------------------------------
@@ -352,6 +355,10 @@ Non-comprehensive list of changes in this release
The flexible array member (FAM) can now be accessed immediately without causing
issues with the sanitizer because the counter is automatically set.
+- ``__builtin_reduce_add`` function can now be used in constant expressions.
+- ``__builtin_reduce_mul`` function can now be used in constant expressions.
+- ``__builtin_reduce_and`` function can now be used in constant expressions.
+
New Compiler Flags
------------------
@@ -449,6 +456,9 @@ Attribute Changes in Clang
- Fix a bug where clang doesn't automatically apply the ``[[gsl::Owner]]`` or
``[[gsl::Pointer]]`` to STL explicit template specialization decls. (#GH109442)
+- Clang now supports ``[[clang::lifetime_capture_by(X)]]``. Similar to lifetimebound, this can be
+ used to specify when a reference to a function parameter is captured by another capturing entity ``X``.
+
Improvements to Clang's diagnostics
-----------------------------------
@@ -527,6 +537,33 @@ Improvements to Clang's diagnostics
- Clang now diagnoses ``[[deprecated]]`` attribute usage on local variables (#GH90073).
+- Improved diagnostic message for ``__builtin_bit_cast`` size mismatch (#GH115870).
+
+- Clang now omits shadow warnings for enum constants in separate class scopes (#GH62588).
+
+- When diagnosing an unused return value of a type declared ``[[nodiscard]]``, the type
+ itself is now included in the diagnostic.
+
+- Clang will now prefer the ``[[nodiscard]]`` declaration on function declarations over ``[[nodiscard]]``
+ declaration on the return type of a function. Previously, when both have a ``[[nodiscard]]`` declaration attached,
+ the one on the return type would be preferred. This may affect the generated warning message:
+
+ .. code-block:: c++
+
+ struct [[nodiscard("Reason 1")]] S {};
+ [[nodiscard("Reason 2")]] S getS();
+ void use()
+ {
+ getS(); // Now diagnoses "Reason 2", previously diagnoses "Reason 1"
+ }
+
+- Clang now diagnoses ``= delete("reason")`` extension warnings only in pedantic mode rather than on by default. (#GH109311).
+
+- Clang now diagnoses missing return value in functions containing ``if consteval`` (#GH116485).
+
+- Clang now correctly recognises code after a call to a ``[[noreturn]]`` constructor
+ as unreachable (#GH63009).
+
Improvements to Clang's time-trace
----------------------------------
@@ -547,6 +584,8 @@ Bug Fixes in This Version
the unsupported type instead of the ``register`` keyword (#GH109776).
- Fixed a crash when emit ctor for global variant with flexible array init (#GH113187).
- Fixed a crash when GNU statement expression contains invalid statement (#GH113468).
+- Fixed a failed assertion when using ``__attribute__((noderef))`` on an
+ ``_Atomic``-qualified type (#GH116124).
Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -654,6 +693,8 @@ Bug Fixes to C++ Support
- Name independent data members were not correctly initialized from default member initializers. (#GH114069)
- Fixed expression transformation for ``[[assume(...)]]``, allowing using pack indexing expressions within the
assumption if they also occur inside of a dependent lambda. (#GH114787)
+- Clang now uses valid deduced type locations when diagnosing functions with trailing return type
+ missing placeholder return type. (#GH78694)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -661,6 +702,8 @@ Bug Fixes to AST Handling
- Fixed a crash that occurred when dividing by zero in complex integer division. (#GH55390).
- Fixed a bug in ``ASTContext::getRawCommentForAnyRedecl()`` where the function could
sometimes incorrectly return null even if a comment was present. (#GH108145)
+- Clang now correctly parses the argument of the ``relates``, ``related``, ``relatesalso``,
+ and ``relatedalso`` comment commands.
Miscellaneous Bug Fixes
^^^^^^^^^^^^^^^^^^^^^^^
@@ -698,6 +741,19 @@ Target Specific Changes
AMDGPU Support
^^^^^^^^^^^^^^
+- Initial support for gfx950
+
+- Added headers ``gpuintrin.h`` and ``amdgpuintrin.h`` that contains common
+ definitions for GPU builtin functions. This header can be included for OpenMP,
+ CUDA, HIP, OpenCL, and C/C++.
+
+NVPTX Support
+^^^^^^^^^^^^^^
+
+- Added headers ``gpuintrin.h`` and ``nvptxintrin.h`` that contains common
+ definitions for GPU builtin functions. This header can be included for OpenMP,
+ CUDA, HIP, OpenCL, and C/C++.
+
X86 Support
^^^^^^^^^^^
@@ -744,6 +800,9 @@ X86 Support
- Support ISA of ``AMX-MOVRS``.
- Support ISA of ``AMX-AVX512``.
- Support ISA of ``AMX-TF32``.
+- Support ISA of ``MOVRS``.
+
+- Supported ``-march/tune=diamondrapids``
Arm and AArch64 Support
^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/docs/ThreadSafetyAnalysis.rst b/clang/docs/ThreadSafetyAnalysis.rst
index cc4089b97b4923e6a8c17148b0f47d47d1639db8..f6517afc3bfc2a9182b15ac656bfb9d7735acf34 100644
--- a/clang/docs/ThreadSafetyAnalysis.rst
+++ b/clang/docs/ThreadSafetyAnalysis.rst
@@ -933,11 +933,25 @@ implementation.
MutexLocker(Mutex *mu, defer_lock_t) EXCLUDES(mu) : mut(mu), locked(false) {}
// Same as constructors, but without tag types. (Requires C++17 copy elision.)
- static MutexLocker Lock(Mutex *mu) ACQUIRE(mu);
- static MutexLocker Adopt(Mutex *mu) REQUIRES(mu);
- static MutexLocker ReaderLock(Mutex *mu) ACQUIRE_SHARED(mu);
- static MutexLocker AdoptReaderLock(Mutex *mu) REQUIRES_SHARED(mu);
- static MutexLocker DeferLock(Mutex *mu) EXCLUDES(mu);
+ static MutexLocker Lock(Mutex *mu) ACQUIRE(mu) {
+ return MutexLocker(mu);
+ }
+
+ static MutexLocker Adopt(Mutex *mu) REQUIRES(mu) {
+ return MutexLocker(mu, adopt_lock);
+ }
+
+ static MutexLocker ReaderLock(Mutex *mu) ACQUIRE_SHARED(mu) {
+ return MutexLocker(mu, shared_lock);
+ }
+
+ static MutexLocker AdoptReaderLock(Mutex *mu) REQUIRES_SHARED(mu) {
+ return MutexLocker(mu, adopt_lock, shared_lock);
+ }
+
+ static MutexLocker DeferLock(Mutex *mu) EXCLUDES(mu) {
+ return MutexLocker(mu, defer_lock);
+ }
// Release *this and all associated mutexes, if they are still held.
// There is no warning if the scope was already unlocked before.
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 2ac840662f1dbb2a5802f82822c2cb49c253cca4..5c2b04145e6cd80f533699f7546e6adc96062610 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1324,9 +1324,9 @@ disabling all warnings wins.
Controlling Diagnostics via Suppression Mappings
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Warning suppression mappings enable users to suppress Clang's diagnostics in a
-per-file granular manner. Enabling enforcement of diagnostics in specific parts
-of the project, even if there are violations in some headers.
+Warning suppression mappings enable users to suppress Clang's diagnostics at a
+per-file granularity. This allows enforcing diagnostics in specific parts of the
+project even if there are violations in some headers.
.. code-block:: console
diff --git a/clang/docs/WarningSuppressionMappings.rst b/clang/docs/WarningSuppressionMappings.rst
index 54f8697acf67931403fc99aa275c635bec21e536..d96341ac6e563bcf2508e9dd5dff3353917ac10e 100644
--- a/clang/docs/WarningSuppressionMappings.rst
+++ b/clang/docs/WarningSuppressionMappings.rst
@@ -8,19 +8,19 @@ Warning suppression mappings
Introduction
============
-Warning suppression mappings enable users to suppress Clang's diagnostics in a
-per-file granular manner. Enabling enforcement of diagnostics in specific parts
-of the project, even if there are violations in some headers.
+Warning suppression mappings enable users to suppress Clang's diagnostics at a
+per-file granularity. This allows enforcing diagnostics in specific parts of the
+project even if there are violations in some headers.
Goal and usage
==============
Clang allows diagnostics to be configured at a translation-unit granularity.
If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
-also need to be clean. Hence turning on new warnings in large codebases requires
-cleaning up all the existing warnings. This might not be possible when some
-dependencies aren't in the project owner's control or because new violations are
-creeping up quicker than the clean up.
+also need to be clean. Hence, turning on new warnings in large codebases
+requires cleaning up all the existing warnings. This might not be possible when
+some dependencies aren't in the project owner's control or because new
+violations are creeping up quicker than the clean up.
Warning suppression mappings aim to alleviate some of these concerns by making
diagnostic configuration granularity finer, at a source file level.
diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 45e37b08f6ceab8c3fd4f5a8a0dee2aa04a66d45..2e72d239a714a5ad27a603ba81b9d30225766f4e 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -2198,7 +2198,9 @@ enum CXCursorKind {
*/
CXCursor_OpenACCLoopConstruct = 321,
- CXCursor_LastStmt = CXCursor_OpenACCLoopConstruct,
+ CXCursor_OpenACCCombinedConstruct = 322,
+
+ CXCursor_LastStmt = CXCursor_OpenACCCombinedConstruct,
/**
* Cursor that represents the translation unit itself.
diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
index 6ad1c8507011463e7878fe92b6eb50b6c272bf68..ff374ad3ada065a141c828077e5328dedd95ccc5 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -724,6 +724,11 @@ class TagInfo : public CommonTypeInfo {
LLVM_PREFERRED_TYPE(bool)
unsigned SwiftCopyable : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned SwiftEscapableSpecified : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned SwiftEscapable : 1;
+
public:
std::optional SwiftImportAs;
std::optional SwiftRetainOp;
@@ -736,7 +741,8 @@ public:
TagInfo()
: HasFlagEnum(0), IsFlagEnum(0), SwiftCopyableSpecified(false),
- SwiftCopyable(false) {}
+ SwiftCopyable(false), SwiftEscapableSpecified(false),
+ SwiftEscapable(false) {}
std::optional isFlagEnum() const {
if (HasFlagEnum)
@@ -757,6 +763,16 @@ public:
SwiftCopyable = Value.value_or(false);
}
+ std::optional isSwiftEscapable() const {
+ return SwiftEscapableSpecified ? std::optional(SwiftEscapable)
+ : std::nullopt;
+ }
+
+ void setSwiftEscapable(std::optional Value) {
+ SwiftEscapableSpecified = Value.has_value();
+ SwiftEscapable = Value.value_or(false);
+ }
+
TagInfo &operator|=(const TagInfo &RHS) {
static_cast(*this) |= RHS;
@@ -779,6 +795,9 @@ public:
if (!SwiftCopyableSpecified)
setSwiftCopyable(RHS.isSwiftCopyable());
+ if (!SwiftEscapableSpecified)
+ setSwiftEscapable(RHS.isSwiftEscapable());
+
return *this;
}
@@ -795,6 +814,7 @@ inline bool operator==(const TagInfo &LHS, const TagInfo &RHS) {
LHS.SwiftConformance == RHS.SwiftConformance &&
LHS.isFlagEnum() == RHS.isFlagEnum() &&
LHS.isSwiftCopyable() == RHS.isSwiftCopyable() &&
+ LHS.isSwiftEscapable() == RHS.isSwiftEscapable() &&
LHS.EnumExtensibility == RHS.EnumExtensibility;
}
diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h
index 088a2bd0fdd407b8aba6ec10c31b0dd5217740ad..8c3fa842ab8b9d997e98a77133be7cc69b66ff42 100644
--- a/clang/include/clang/AST/ASTImporter.h
+++ b/clang/include/clang/AST/ASTImporter.h
@@ -62,7 +62,8 @@ class TypeSourceInfo;
class ASTImporter {
friend class ASTNodeImporter;
public:
- using NonEquivalentDeclSet = llvm::DenseSet>;
+ using NonEquivalentDeclSet =
+ llvm::DenseSet>;
using ImportedCXXBaseSpecifierMap =
llvm::DenseMap;
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index a443a88bab1f2d2b07f0447e60a847b528060784..3d63d581a9be6033f661c959fa3347db5c5f45e7 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -800,6 +800,13 @@ public:
Visit(A);
}
+ void VisitLabelStmt(const LabelStmt *Node) {
+ if (Node->getDecl()->hasAttrs()) {
+ for (const auto *A : Node->getDecl()->getAttrs())
+ Visit(A);
+ }
+ }
+
void VisitCXXCatchStmt(const CXXCatchStmt *Node) {
Visit(Node->getExceptionDecl());
}
diff --git a/clang/include/clang/AST/ASTStructuralEquivalence.h b/clang/include/clang/AST/ASTStructuralEquivalence.h
index 029439c8e9a3ac334e91ea2cb9e246641435c971..67aa0023c25d0857e1fc983a41dd7ad31ace5a61 100644
--- a/clang/include/clang/AST/ASTStructuralEquivalence.h
+++ b/clang/include/clang/AST/ASTStructuralEquivalence.h
@@ -39,6 +39,10 @@ enum class StructuralEquivalenceKind {
};
struct StructuralEquivalenceContext {
+ /// Store declaration pairs already found to be non-equivalent.
+ /// key: (from, to, IgnoreTemplateParmDepth)
+ using NonEquivalentDeclSet = llvm::DenseSet>;
+
/// AST contexts for which we are checking structural equivalence.
ASTContext &FromCtx, &ToCtx;
@@ -52,7 +56,7 @@ struct StructuralEquivalenceContext {
/// Declaration (from, to) pairs that are known not to be equivalent
/// (which we have already complained about).
- llvm::DenseSet> &NonEquivalentDecls;
+ NonEquivalentDeclSet &NonEquivalentDecls;
StructuralEquivalenceKind EqKind;
@@ -72,12 +76,13 @@ struct StructuralEquivalenceContext {
/// Whether to ignore comparing the depth of template param(TemplateTypeParm)
bool IgnoreTemplateParmDepth;
- StructuralEquivalenceContext(
- ASTContext &FromCtx, ASTContext &ToCtx,
- llvm::DenseSet> &NonEquivalentDecls,
- StructuralEquivalenceKind EqKind, bool StrictTypeSpelling = false,
- bool Complain = true, bool ErrorOnTagTypeMismatch = false,
- bool IgnoreTemplateParmDepth = false)
+ StructuralEquivalenceContext(ASTContext &FromCtx, ASTContext &ToCtx,
+ NonEquivalentDeclSet &NonEquivalentDecls,
+ StructuralEquivalenceKind EqKind,
+ bool StrictTypeSpelling = false,
+ bool Complain = true,
+ bool ErrorOnTagTypeMismatch = false,
+ bool IgnoreTemplateParmDepth = false)
: FromCtx(FromCtx), ToCtx(ToCtx), NonEquivalentDecls(NonEquivalentDecls),
EqKind(EqKind), StrictTypeSpelling(StrictTypeSpelling),
ErrorOnTagTypeMismatch(ErrorOnTagTypeMismatch), Complain(Complain),
diff --git a/clang/include/clang/AST/CanonicalType.h b/clang/include/clang/AST/CanonicalType.h
index 6102eb017935309bf6282e5889d92634ced1e9ec..6699284d215bd00d3107de527346d89caf61763e 100644
--- a/clang/include/clang/AST/CanonicalType.h
+++ b/clang/include/clang/AST/CanonicalType.h
@@ -30,6 +30,7 @@ namespace clang {
template class CanProxy;
template struct CanProxyAdaptor;
+class ASTContext;
class CXXRecordDecl;
class EnumDecl;
class Expr;
@@ -164,14 +165,14 @@ public:
/// Determines whether this canonical type is more qualified than
/// the @p Other canonical type.
- bool isMoreQualifiedThan(CanQual Other) const {
- return Stored.isMoreQualifiedThan(Other.Stored);
+ bool isMoreQualifiedThan(CanQual Other, const ASTContext &Ctx) const {
+ return Stored.isMoreQualifiedThan(Other.Stored, Ctx);
}
/// Determines whether this canonical type is at least as qualified as
/// the @p Other canonical type.
- bool isAtLeastAsQualifiedAs(CanQual Other) const {
- return Stored.isAtLeastAsQualifiedAs(Other.Stored);
+ bool isAtLeastAsQualifiedAs(CanQual Other, const ASTContext &Ctx) const {
+ return Stored.isAtLeastAsQualifiedAs(Other.Stored, Ctx);
}
/// If the canonical type is a reference type, returns the type that
diff --git a/clang/include/clang/AST/CommentCommands.td b/clang/include/clang/AST/CommentCommands.td
index a410cd4039bee1e591aa692a27711be0184eab1b..f6dd67dc2590f0e775b87b9ba27dd4519db85a65 100644
--- a/clang/include/clang/AST/CommentCommands.td
+++ b/clang/include/clang/AST/CommentCommands.td
@@ -111,6 +111,11 @@ def Extends : InlineCommand<"extends">;
def Implements : InlineCommand<"implements">;
def MemberOf : InlineCommand<"memberof">;
+def Relates : InlineCommand<"relates">;
+def Related : InlineCommand<"related">;
+def RelatesAlso : InlineCommand<"relatesalso">;
+def RelatedAlso : InlineCommand<"relatedalso">;
+
//===----------------------------------------------------------------------===//
// BlockCommand
//===----------------------------------------------------------------------===//
@@ -248,11 +253,6 @@ def Page : VerbatimLineCommand<"page">;
def Mainpage : VerbatimLineCommand<"mainpage">;
def Subpage : VerbatimLineCommand<"subpage">;
-def Relates : VerbatimLineCommand<"relates">;
-def Related : VerbatimLineCommand<"related">;
-def RelatesAlso : VerbatimLineCommand<"relatesalso">;
-def RelatedAlso : VerbatimLineCommand<"relatedalso">;
-
def AddIndex : VerbatimLineCommand<"addindex">;
// These take a single argument mostly, but since they include a file they'll
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 466c65a9685ad32cf96905591f01bb8a728344f3..708c8656decbe017b7d678f29b6bfeeabf4f4373 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -3181,12 +3181,14 @@ public:
QualType getCallReturnType(const ASTContext &Ctx) const;
/// Returns the WarnUnusedResultAttr that is either declared on the called
- /// function, or its return type declaration.
- const Attr *getUnusedResultAttr(const ASTContext &Ctx) const;
+ /// function, or its return type declaration, together with a NamedDecl that
+ /// refers to the declaration the attribute is attached onto.
+ std::pair
+ getUnusedResultAttr(const ASTContext &Ctx) const;
/// Returns true if this call expression should warn on unused results.
bool hasUnusedResultAttr(const ASTContext &Ctx) const {
- return getUnusedResultAttr(Ctx) != nullptr;
+ return getUnusedResultAttr(Ctx).second != nullptr;
}
SourceLocation getRParenLoc() const { return RParenLoc; }
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 73f3b16eec86327c1c8013dd7325a050fedff9ca..7aac857a3ef7a25a3797917722c23fe48e730bf3 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -4106,15 +4106,6 @@ bool RecursiveASTVisitor::VisitOpenACCClauseList(
for (const auto *C : Clauses)
TRY_TO(VisitOpenACCClause(C));
-// if (const auto *WithCond = dyn_cast(C);
-// WithCond && WIthCond->hasConditionExpr()) {
-// TRY_TO(TraverseStmt(WithCond->getConditionExpr());
-// } else if (const auto *
-// }
-// OpenACCClauseWithCondition::getConditionExpr/hasConditionExpr
-//OpenACCClauseWithExprs::children (might be null?)
- // TODO OpenACC: When we have Clauses with expressions, we should visit them
- // here.
return true;
}
@@ -4129,6 +4120,8 @@ DEF_TRAVERSE_STMT(OpenACCComputeConstruct,
{ TRY_TO(TraverseOpenACCAssociatedStmtConstruct(S)); })
DEF_TRAVERSE_STMT(OpenACCLoopConstruct,
{ TRY_TO(TraverseOpenACCAssociatedStmtConstruct(S)); })
+DEF_TRAVERSE_STMT(OpenACCCombinedConstruct,
+ { TRY_TO(TraverseOpenACCAssociatedStmtConstruct(S)); })
// Traverse HLSL: Out argument expression
DEF_TRAVERSE_STMT(HLSLOutArgExpr, {})
diff --git a/clang/include/clang/AST/StmtOpenACC.h b/clang/include/clang/AST/StmtOpenACC.h
index 3eb50f9353ed19b85cbf2a7269618471e7191976..fa8793e740822f588b849471cc50be63c4887e26 100644
--- a/clang/include/clang/AST/StmtOpenACC.h
+++ b/clang/include/clang/AST/StmtOpenACC.h
@@ -166,11 +166,6 @@ class OpenACCComputeConstruct final
}
void setStructuredBlock(Stmt *S) { setAssociatedStmt(S); }
- // Serialization helper function that searches the structured block for 'loop'
- // constructs that should be associated with this, and sets their parent
- // compute construct to this one. This isn't necessary normally, since we have
- // the ability to record the state during parsing.
- void findAndSetChildLoops();
public:
static bool classof(const Stmt *T) {
@@ -204,6 +199,8 @@ class OpenACCLoopConstruct final
friend class ASTStmtWriter;
friend class ASTStmtReader;
friend class ASTContext;
+ friend class OpenACCAssociatedStmtConstruct;
+ friend class OpenACCCombinedConstruct;
friend class OpenACCComputeConstruct;
OpenACCLoopConstruct(unsigned NumClauses);
@@ -243,5 +240,57 @@ public:
return ParentComputeConstructKind;
}
};
+
+// This class represents a 'combined' construct, which has a bunch of rules
+// shared with both loop and compute constructs.
+class OpenACCCombinedConstruct final
+ : public OpenACCAssociatedStmtConstruct,
+ public llvm::TrailingObjects {
+ OpenACCCombinedConstruct(unsigned NumClauses)
+ : OpenACCAssociatedStmtConstruct(
+ OpenACCCombinedConstructClass, OpenACCDirectiveKind::Invalid,
+ SourceLocation{}, SourceLocation{}, SourceLocation{},
+ /*AssociatedStmt=*/nullptr) {
+ std::uninitialized_value_construct(
+ getTrailingObjects(),
+ getTrailingObjects() + NumClauses);
+ setClauseList(MutableArrayRef(getTrailingObjects(),
+ NumClauses));
+ }
+
+ OpenACCCombinedConstruct(OpenACCDirectiveKind K, SourceLocation Start,
+ SourceLocation DirectiveLoc, SourceLocation End,
+ ArrayRef Clauses,
+ Stmt *StructuredBlock)
+ : OpenACCAssociatedStmtConstruct(OpenACCCombinedConstructClass, K, Start,
+ DirectiveLoc, End, StructuredBlock) {
+ assert(isOpenACCCombinedDirectiveKind(K) &&
+ "Only parallel loop, serial loop, and kernels loop constructs "
+ "should be represented by this type");
+
+ std::uninitialized_copy(Clauses.begin(), Clauses.end(),
+ getTrailingObjects());
+ setClauseList(MutableArrayRef(getTrailingObjects(),
+ Clauses.size()));
+ }
+ void setStructuredBlock(Stmt *S) { setAssociatedStmt(S); }
+
+public:
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == OpenACCCombinedConstructClass;
+ }
+
+ static OpenACCCombinedConstruct *CreateEmpty(const ASTContext &C,
+ unsigned NumClauses);
+ static OpenACCCombinedConstruct *
+ Create(const ASTContext &C, OpenACCDirectiveKind K, SourceLocation Start,
+ SourceLocation DirectiveLoc, SourceLocation End,
+ ArrayRef Clauses, Stmt *StructuredBlock);
+ Stmt *getLoop() { return getAssociatedStmt(); }
+ const Stmt *getLoop() const {
+ return const_cast(this)->getLoop();
+ }
+};
} // namespace clang
#endif // LLVM_CLANG_AST_STMTOPENACC_H
diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 9c320c8ae3e54cd12c44389ce4407ea2c2bdaa9e..988b142a7672a30abeb9162be5707485f3a8a626 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -410,6 +410,7 @@ public:
void VisitHLSLOutArgExpr(const HLSLOutArgExpr *E);
void VisitOpenACCConstructStmt(const OpenACCConstructStmt *S);
void VisitOpenACCLoopConstruct(const OpenACCLoopConstruct *S);
+ void VisitOpenACCCombinedConstruct(const OpenACCCombinedConstruct *S);
void VisitOpenACCAsteriskSizeExpr(const OpenACCAsteriskSizeExpr *S);
void VisitEmbedExpr(const EmbedExpr *S);
void VisitAtomicExpr(const AtomicExpr *AE);
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 723127ba5e3fb5b4ce02bfaf9270e587dc5a094a..1c40328c83b68ac0e57e3f19c108388d1a792882 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -323,6 +323,7 @@ public:
/// * Objective C: the GC attributes (none, weak, or strong)
class Qualifiers {
public:
+ Qualifiers() = default;
enum TQ : uint64_t {
// NOTE: These flags must be kept in sync with DeclSpec::TQ.
Const = 0x1,
@@ -697,45 +698,27 @@ public:
/// every address space is a superset of itself.
/// CL2.0 adds:
/// __generic is a superset of any address space except for __constant.
- static bool isAddressSpaceSupersetOf(LangAS A, LangAS B) {
+ static bool isAddressSpaceSupersetOf(LangAS A, LangAS B,
+ const ASTContext &Ctx) {
// Address spaces must match exactly.
- return A == B ||
- // Otherwise in OpenCLC v2.0 s6.5.5: every address space except
- // for __constant can be used as __generic.
- (A == LangAS::opencl_generic && B != LangAS::opencl_constant) ||
- // We also define global_device and global_host address spaces,
- // to distinguish global pointers allocated on host from pointers
- // allocated on device, which are a subset of __global.
- (A == LangAS::opencl_global && (B == LangAS::opencl_global_device ||
- B == LangAS::opencl_global_host)) ||
- (A == LangAS::sycl_global && (B == LangAS::sycl_global_device ||
- B == LangAS::sycl_global_host)) ||
- // Consider pointer size address spaces to be equivalent to default.
- ((isPtrSizeAddressSpace(A) || A == LangAS::Default) &&
- (isPtrSizeAddressSpace(B) || B == LangAS::Default)) ||
- // Default is a superset of SYCL address spaces.
- (A == LangAS::Default &&
- (B == LangAS::sycl_private || B == LangAS::sycl_local ||
- B == LangAS::sycl_global || B == LangAS::sycl_global_device ||
- B == LangAS::sycl_global_host)) ||
- // In HIP device compilation, any cuda address space is allowed
- // to implicitly cast into the default address space.
- (A == LangAS::Default &&
- (B == LangAS::cuda_constant || B == LangAS::cuda_device ||
- B == LangAS::cuda_shared));
+ return A == B || isTargetAddressSpaceSupersetOf(A, B, Ctx);
}
+ static bool isTargetAddressSpaceSupersetOf(LangAS A, LangAS B,
+ const ASTContext &Ctx);
+
/// Returns true if the address space in these qualifiers is equal to or
/// a superset of the address space in the argument qualifiers.
- bool isAddressSpaceSupersetOf(Qualifiers other) const {
- return isAddressSpaceSupersetOf(getAddressSpace(), other.getAddressSpace());
+ bool isAddressSpaceSupersetOf(Qualifiers other, const ASTContext &Ctx) const {
+ return isAddressSpaceSupersetOf(getAddressSpace(), other.getAddressSpace(),
+ Ctx);
}
/// Determines if these qualifiers compatibly include another set.
/// Generally this answers the question of whether an object with the other
/// qualifiers can be safely used as an object with these qualifiers.
- bool compatiblyIncludes(Qualifiers other) const {
- return isAddressSpaceSupersetOf(other) &&
+ bool compatiblyIncludes(Qualifiers other, const ASTContext &Ctx) const {
+ return isAddressSpaceSupersetOf(other, Ctx) &&
// ObjC GC qualifiers can match, be added, or be removed, but can't
// be changed.
(getObjCGCAttr() == other.getObjCGCAttr() || !hasObjCGCAttr() ||
@@ -1273,11 +1256,11 @@ public:
/// Determine whether this type is more qualified than the other
/// given type, requiring exact equality for non-CVR qualifiers.
- bool isMoreQualifiedThan(QualType Other) const;
+ bool isMoreQualifiedThan(QualType Other, const ASTContext &Ctx) const;
/// Determine whether this type is at least as qualified as the other
/// given type, requiring exact equality for non-CVR qualifiers.
- bool isAtLeastAsQualifiedAs(QualType Other) const;
+ bool isAtLeastAsQualifiedAs(QualType Other, const ASTContext &Ctx) const;
QualType getNonReferenceType() const;
@@ -1425,11 +1408,12 @@ public:
/// address spaces overlap iff they are they same.
/// OpenCL C v2.0 s6.5.5 adds:
/// __generic overlaps with any address space except for __constant.
- bool isAddressSpaceOverlapping(QualType T) const {
+ bool isAddressSpaceOverlapping(QualType T, const ASTContext &Ctx) const {
Qualifiers Q = getQualifiers();
Qualifiers TQ = T.getQualifiers();
// Address spaces overlap if at least one of them is a superset of another
- return Q.isAddressSpaceSupersetOf(TQ) || TQ.isAddressSpaceSupersetOf(Q);
+ return Q.isAddressSpaceSupersetOf(TQ, Ctx) ||
+ TQ.isAddressSpaceSupersetOf(Q, Ctx);
}
/// Returns gc attribute of this type.
@@ -8115,24 +8099,26 @@ inline FunctionType::ExtInfo getFunctionExtInfo(QualType t) {
/// is more qualified than "const int", "volatile int", and
/// "int". However, it is not more qualified than "const volatile
/// int".
-inline bool QualType::isMoreQualifiedThan(QualType other) const {
+inline bool QualType::isMoreQualifiedThan(QualType other,
+ const ASTContext &Ctx) const {
Qualifiers MyQuals = getQualifiers();
Qualifiers OtherQuals = other.getQualifiers();
- return (MyQuals != OtherQuals && MyQuals.compatiblyIncludes(OtherQuals));
+ return (MyQuals != OtherQuals && MyQuals.compatiblyIncludes(OtherQuals, Ctx));
}
/// Determine whether this type is at last
/// as qualified as the Other type. For example, "const volatile
/// int" is at least as qualified as "const int", "volatile int",
/// "int", and "const volatile int".
-inline bool QualType::isAtLeastAsQualifiedAs(QualType other) const {
+inline bool QualType::isAtLeastAsQualifiedAs(QualType other,
+ const ASTContext &Ctx) const {
Qualifiers OtherQuals = other.getQualifiers();
// Ignore __unaligned qualifier if this type is a void.
if (getUnqualifiedType()->isVoidType())
OtherQuals.removeUnaligned();
- return getQualifiers().compatiblyIncludes(OtherQuals);
+ return getQualifiers().compatiblyIncludes(OtherQuals, Ctx);
}
/// If Type is a reference type (e.g., const
diff --git a/clang/include/clang/Analysis/Analyses/ThreadSafetyTIL.h b/clang/include/clang/Analysis/Analyses/ThreadSafetyTIL.h
index 2f202607bd3fa993b9654998a961bfee4edf432c..9f365d1a3b6557865221b1ee8dc2fc0a907b204a 100644
--- a/clang/include/clang/Analysis/Analyses/ThreadSafetyTIL.h
+++ b/clang/include/clang/Analysis/Analyses/ThreadSafetyTIL.h
@@ -1363,11 +1363,7 @@ public:
}
/// Return the list of basic blocks that this terminator can branch to.
- ArrayRef successors();
-
- ArrayRef successors() const {
- return const_cast(this)->successors();
- }
+ ArrayRef successors() const;
};
/// Jump to another basic block.
@@ -1391,7 +1387,7 @@ public:
unsigned index() const { return Index; }
/// Return the list of basic blocks that this terminator can branch to.
- ArrayRef successors() { return TargetBlock; }
+ ArrayRef successors() const { return TargetBlock; }
template
typename V::R_SExpr traverse(V &Vs, typename V::R_Ctx Ctx) {
@@ -1439,7 +1435,7 @@ public:
BasicBlock *elseBlock() { return Branches[1]; }
/// Return the list of basic blocks that this terminator can branch to.
- ArrayRef successors() { return llvm::ArrayRef(Branches); }
+ ArrayRef successors() const { return llvm::ArrayRef(Branches); }
template
typename V::R_SExpr traverse(V &Vs, typename V::R_Ctx Ctx) {
@@ -1470,7 +1466,7 @@ public:
static bool classof(const SExpr *E) { return E->opcode() == COP_Return; }
/// Return an empty list.
- ArrayRef successors() { return {}; }
+ ArrayRef successors() const { return {}; }
SExpr *returnValue() { return Retval; }
const SExpr *returnValue() const { return Retval; }
@@ -1490,7 +1486,7 @@ private:
SExpr* Retval;
};
-inline ArrayRef Terminator::successors() {
+inline ArrayRef Terminator::successors() const {
switch (opcode()) {
case COP_Goto: return cast(this)->successors();
case COP_Branch: return cast(this)->successors();
diff --git a/clang/include/clang/Analysis/CallGraph.h b/clang/include/clang/Analysis/CallGraph.h
index 78f8d115550178e44e3e9fe84f34e0d2cbe55c4a..c11d163f8fe20dc1a18db5291b3eb8e1eff5ee40 100644
--- a/clang/include/clang/Analysis/CallGraph.h
+++ b/clang/include/clang/Analysis/CallGraph.h
@@ -18,7 +18,8 @@
#define LLVM_CLANG_ANALYSIS_CALLGRAPH_H
#include "clang/AST/Decl.h"
-#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/GraphTraits.h"
#include "llvm/ADT/STLExtras.h"
@@ -39,7 +40,7 @@ class Stmt;
/// The call graph extends itself with the given declarations by implementing
/// the recursive AST visitor, which constructs the graph by visiting the given
/// declarations.
-class CallGraph : public RecursiveASTVisitor {
+class CallGraph : public DynamicRecursiveASTVisitor {
friend class CallGraphNode;
using FunctionMapTy =
@@ -109,7 +110,7 @@ public:
/// Part of recursive declaration visitation. We recursively visit all the
/// declarations to collect the root functions.
- bool VisitFunctionDecl(FunctionDecl *FD) {
+ bool VisitFunctionDecl(FunctionDecl *FD) override {
// We skip function template definitions, as their semantics is
// only determined when they are instantiated.
if (includeInGraph(FD) && FD->isThisDeclarationADefinition()) {
@@ -124,7 +125,7 @@ public:
}
/// Part of recursive declaration visitation.
- bool VisitObjCMethodDecl(ObjCMethodDecl *MD) {
+ bool VisitObjCMethodDecl(ObjCMethodDecl *MD) override {
if (includeInGraph(MD)) {
addNodesForBlocks(MD);
addNodeForDecl(MD, true);
@@ -133,11 +134,7 @@ public:
}
// We are only collecting the declarations, so do not step into the bodies.
- bool TraverseStmt(Stmt *S) { return true; }
-
- bool shouldWalkTypesOfTypeLocs() const { return false; }
- bool shouldVisitTemplateInstantiations() const { return true; }
- bool shouldVisitImplicitCode() const { return true; }
+ bool TraverseStmt(Stmt *S) override { return true; }
private:
/// Add the given declaration to the call graph.
diff --git a/clang/include/clang/Analysis/FlowSensitive/ASTOps.h b/clang/include/clang/Analysis/FlowSensitive/ASTOps.h
index ec4d041254877fdc4230adf1645bea6a3be12733..6294c810626a701af8f91a68e424bb3bea09c4a6 100644
--- a/clang/include/clang/Analysis/FlowSensitive/ASTOps.h
+++ b/clang/include/clang/Analysis/FlowSensitive/ASTOps.h
@@ -14,8 +14,9 @@
#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_ASTOPS_H
#include "clang/AST/Decl.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/AST/Expr.h"
-#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/Type.h"
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
#include "llvm/ADT/DenseSet.h"
@@ -88,14 +89,14 @@ private:
/// the function to analyze. Don't call `TraverseDecl()` on the function itself;
/// this won't work as `TraverseDecl()` contains code to avoid traversing nested
/// functions.
-template
-class AnalysisASTVisitor : public RecursiveASTVisitor {
+class AnalysisASTVisitor : public DynamicRecursiveASTVisitor {
public:
- bool shouldVisitImplicitCode() { return true; }
-
- bool shouldVisitLambdaBody() const { return false; }
+ AnalysisASTVisitor() {
+ ShouldVisitImplicitCode = true;
+ ShouldVisitLambdaBody = false;
+ }
- bool TraverseDecl(Decl *D) {
+ bool TraverseDecl(Decl *D) override {
// Don't traverse nested record or function declarations.
// - We won't be analyzing code contained in these anyway
// - We don't model fields that are used only in these nested declaration,
@@ -104,30 +105,30 @@ public:
if (isa_and_nonnull(D) || isa_and_nonnull(D))
return true;
- return RecursiveASTVisitor::TraverseDecl(D);
+ return DynamicRecursiveASTVisitor::TraverseDecl(D);
}
// Don't traverse expressions in unevaluated contexts, as we don't model
// fields that are only used in these.
// Note: The operand of the `noexcept` operator is an unevaluated operand, but
// nevertheless it appears in the Clang CFG, so we don't exclude it here.
- bool TraverseDecltypeTypeLoc(DecltypeTypeLoc) { return true; }
- bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc) { return true; }
- bool TraverseCXXTypeidExpr(CXXTypeidExpr *TIE) {
+ bool TraverseDecltypeTypeLoc(DecltypeTypeLoc) override { return true; }
+ bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc) override { return true; }
+ bool TraverseCXXTypeidExpr(CXXTypeidExpr *TIE) override {
if (TIE->isPotentiallyEvaluated())
- return RecursiveASTVisitor::TraverseCXXTypeidExpr(TIE);
+ return DynamicRecursiveASTVisitor::TraverseCXXTypeidExpr(TIE);
return true;
}
- bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *) {
+ bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *) override {
return true;
}
- bool TraverseBindingDecl(BindingDecl *BD) {
+ bool TraverseBindingDecl(BindingDecl *BD) override {
// `RecursiveASTVisitor` doesn't traverse holding variables for
// `BindingDecl`s by itself, so we need to tell it to.
if (VarDecl *HoldingVar = BD->getHoldingVar())
TraverseDecl(HoldingVar);
- return RecursiveASTVisitor::TraverseBindingDecl(BD);
+ return DynamicRecursiveASTVisitor::TraverseBindingDecl(BD);
}
};
diff --git a/clang/include/clang/Basic/AArch64SVEACLETypes.def b/clang/include/clang/Basic/AArch64SVEACLETypes.def
index 62f6087e96246626b468468e79322c91557ceca2..063cac1f4a58ee71bbbd511d32ef6729e24be59b 100644
--- a/clang/include/clang/Basic/AArch64SVEACLETypes.def
+++ b/clang/include/clang/Basic/AArch64SVEACLETypes.def
@@ -200,7 +200,7 @@ SVE_PREDICATE_TYPE_ALL("__clang_svboolx4_t", "svboolx4_t", SveBoolx4, SveBoolx4T
SVE_OPAQUE_TYPE("__SVCount_t", "__SVCount_t", SveCount, SveCountTy)
-AARCH64_VECTOR_TYPE_MFLOAT("__MFloat8_t", "__MFloat8_t", MFloat8, MFloat8Ty, 1, 8, 1)
+AARCH64_VECTOR_TYPE_MFLOAT("__mfp8", "__mfp8", MFloat8, MFloat8Ty, 1, 8, 1)
AARCH64_VECTOR_TYPE_MFLOAT("__MFloat8x8_t", "__MFloat8x8_t", MFloat8x8, MFloat8x8Ty, 8, 8, 1)
AARCH64_VECTOR_TYPE_MFLOAT("__MFloat8x16_t", "__MFloat8x16_t", MFloat8x16, MFloat8x16Ty, 16, 8, 1)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 38022ff9843da10227bdbb2fa03917480ac87083..2e8b0ac27af46ce5d681f7fe875f6d39321c5079 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -477,6 +477,9 @@ def TargetELF : TargetSpec {
def TargetELFOrMachO : TargetSpec {
let ObjectFormats = ["ELF", "MachO"];
}
+def TargetIFuncSupport : TargetSpec {
+ let CustomCode = [{ Target.supportsIFunc() }];
+}
def TargetWindowsArm64EC : TargetSpec {
let CustomCode = [{ Target.getTriple().isWindowsArm64EC() }];
}
@@ -1855,7 +1858,7 @@ def IBOutletCollection : InheritableAttr {
let Documentation = [Undocumented];
}
-def IFunc : Attr, TargetSpecificAttr {
+def IFunc : Attr, TargetSpecificAttr {
let Spellings = [GCC<"ifunc">];
let Args = [StringArgument<"Resolver">];
let Subjects = SubjectList<[Function]>;
@@ -1889,6 +1892,37 @@ def LifetimeBound : DeclOrTypeAttr {
let SimpleHandler = 1;
}
+def LifetimeCaptureBy : DeclOrTypeAttr {
+ let Spellings = [Clang<"lifetime_capture_by", 0>];
+ let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>;
+ let Args = [VariadicParamOrParamIdxArgument<"Params">];
+ let Documentation = [LifetimeCaptureByDocs];
+ let AdditionalMembers = [{
+private:
+ ArrayRef ArgIdents;
+ ArrayRef ArgLocs;
+
+public:
+ static constexpr int THIS = 0;
+ static constexpr int INVALID = -1;
+ static constexpr int UNKNOWN = -2;
+ static constexpr int GLOBAL = -3;
+
+ void setArgs(ArrayRef Idents, ArrayRef Locs) {
+ assert(Idents.size() == params_Size);
+ assert(Locs.size() == params_Size);
+ ArgIdents = Idents;
+ ArgLocs = Locs;
+ }
+ auto getArgIdents() const { return ArgIdents; }
+ auto getArgLocs() const { return ArgLocs; }
+ void setParamIdx(size_t Idx, int Val) {
+ assert(Idx < params_Size);
+ params_[Idx] = Val;
+ }
+}];
+}
+
def TrivialABI : InheritableAttr {
// This attribute does not have a C [[]] spelling because it requires the
// CPlusPlus language option.
@@ -4854,3 +4888,10 @@ def ClspvLibclcBuiltin: InheritableAttr {
let Documentation = [ClspvLibclcBuiltinDoc];
let SimpleHandler = 1;
}
+
+def NoTrivialAutoVarInit: InheritableAttr {
+ let Spellings = [Declspec<"no_init_all">];
+ let Subjects = SubjectList<[Function, Tag]>;
+ let Documentation = [NoTrivialAutoVarInitDocs];
+ let SimpleHandler = 1;
+}
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index b64dbef6332e6a8983982627841e86f5c2928f71..6fb2eb3eb3e663bebb63aa07884d410400f918e7 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3918,6 +3918,116 @@ have their lifetimes extended.
}];
}
+def LifetimeCaptureByDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+Similar to `lifetimebound`_, the ``lifetime_capture_by(X)`` attribute on a
+function parameter or implicit object parameter indicates that the capturing
+entity ``X`` may refer to the object referred by that parameter.
+
+Below is a list of types of the parameters and what they're considered to refer to:
+
+- A reference param (of non-view type) is considered to refer to its referenced object.
+- A pointer param (of non-view type) is considered to refer to its pointee.
+- View type param (type annotated with ``[[gsl::Pointer()]]``) is considered to refer
+ to its pointee (gsl owner). This holds true even if the view type appears as a reference
+ in the parameter. For example, both ``std::string_view`` and
+ ``const std::string_view &`` are considered to refer to a ``std::string``.
+- A ``std::initializer_list`` is considered to refer to its underlying array.
+- Aggregates (arrays and simple ``struct``\s) are considered to refer to all
+ objects that their transitive subobjects refer to.
+
+Clang would diagnose when a temporary object is used as an argument to such an
+annotated parameter.
+In this case, the capturing entity ``X`` could capture a dangling reference to this
+temporary object.
+
+.. code-block:: c++
+
+ void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) {
+ s.insert(a);
+ }
+ void use() {
+ std::set s;
+ addToSet(std::string(), s); // Warning: object whose reference is captured by 's' will be destroyed at the end of the full-expression.
+ // ^^^^^^^^^^^^^
+ std::string local;
+ addToSet(local, s); // Ok.
+ }
+
+The capturing entity ``X`` can be one of the following:
+
+- Another (named) function parameter.
+
+ .. code-block:: c++
+
+ void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) {
+ s.insert(a);
+ }
+
+- ``this`` (in case of member functions).
+
+ .. code-block:: c++
+
+ class S {
+ void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) {
+ s.insert(a);
+ }
+ std::set s;
+ };
+
+- `global`, `unknown`.
+
+ .. code-block:: c++
+
+ std::set s;
+ void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) {
+ s.insert(a);
+ }
+ void addSomewhere(std::string_view a [[clang::lifetime_capture_by(unknown)]]);
+
+The attribute can be applied to the implicit ``this`` parameter of a member
+function by writing the attribute after the function type:
+
+.. code-block:: c++
+
+ struct S {
+ const char *data(std::set& s) [[clang::lifetime_capture_by(s)]] {
+ s.insert(this);
+ }
+ };
+
+The attribute supports specifying more than one capturing entities:
+
+.. code-block:: c++
+
+ void addToSets(std::string_view a [[clang::lifetime_capture_by(s1, s2)]],
+ std::set& s1,
+ std::set& s2) {
+ s1.insert(a);
+ s2.insert(a);
+ }
+
+Limitation: The capturing entity ``X`` is not used by the analysis and is
+used for documentation purposes only. This is because the analysis is
+statement-local and only detects use of a temporary as an argument to the
+annotated parameter.
+
+.. code-block:: c++
+
+ void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s);
+ void use() {
+ std::set s;
+ if (foo()) {
+ std::string str;
+ addToSet(str, s); // Not detected.
+ }
+ }
+
+.. _`lifetimebound`: https://clang.llvm.org/docs/AttributeReference.html#lifetimebound
+ }];
+}
+
def TrivialABIDocs : Documentation {
let Category = DocCatDecl;
let Content = [{
@@ -3948,7 +4058,8 @@ purposes of calls. For example:
If a type is trivial for the purposes of calls, has a non-trivial destructor,
and is passed as an argument by value, the convention is that the callee will
-destroy the object before returning.
+destroy the object before returning. The lifetime of the copy of the parameter
+in the caller ends without a destructor call when the call begins.
If a type is trivial for the purpose of calls, it is assumed to be trivially
relocatable for the purpose of ``__is_trivially_relocatable``.
@@ -5981,12 +6092,19 @@ declared entity. The entity must not have weak linkage; for example, in C++,
it cannot be applied to a declaration if a definition at that location would be
considered inline.
-Not all targets support this attribute. ELF target support depends on both the
-linker and runtime linker, and is available in at least lld 4.0 and later,
-binutils 2.20.1 and later, glibc v2.11.1 and later, and FreeBSD 9.1 and later.
-Mach-O targets support it, but with slightly different semantics: the resolver
-is run at first call, instead of at load time by the runtime linker. Targets
-other than ELF and Mach-O currently do not support this attribute.
+Not all targets support this attribute:
+
+- ELF target support depends on both the linker and runtime linker, and is
+ available in at least lld 4.0 and later, binutils 2.20.1 and later, glibc
+ v2.11.1 and later, and FreeBSD 9.1 and later.
+- Mach-O targets support it, but with slightly different semantics: the resolver
+ is run at first call, instead of at load time by the runtime linker.
+- Windows target supports it on AArch64, but with different semantics: the
+ ``ifunc`` is replaced with a global function pointer, and the call is replaced
+ with an indirect call. The function pointer is initialized by a constructor
+ that calls the resolver.
+- Baremetal target supports it on AVR.
+- Other targets currently do not support this attribute.
}];
}
@@ -8642,6 +8760,18 @@ Attribute used by `clspv`_ (OpenCL-C to Vulkan SPIR-V compiler) to identify func
}];
}
+def NoTrivialAutoVarInitDocs : Documentation {
+ let Category = DocCatDecl;
+ let Content = [{
+The ``__declspec(no_init_all)`` attribute disables the automatic initialization that the
+`-ftrivial-auto-var-init`_ flag would have applied to locals in a marked function, or instances of
+a marked type. Note that this attribute has no effect for locals that are automatically initialized
+without the `-ftrivial-auto-var-init`_ flag.
+
+.. _`-ftrivial-auto-var-init`: ClangCommandLineReference.html#cmdoption-clang-ftrivial-auto-var-init
+}];
+}
+
def DocCatNonBlockingNonAllocating : DocumentationCategory<"Performance Constraint Attributes"> {
let Content = [{
The ``nonblocking``, ``blocking``, ``nonallocating`` and ``allocating`` attributes can be attached
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 4360e0bf9840f13f22e824262712aa12d17ff2bf..aa65f94e68f9c3d4ab8a139cc7e040cfa7af17a2 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -227,10 +227,10 @@ def FminimumNumF16F128 : Builtin, F16F128MathTemplate {
let Prototype = "T(T, T)";
}
-def Atan2F128 : Builtin {
- let Spellings = ["__builtin_atan2f128"];
+def Atan2F16F128 : Builtin, F16F128MathTemplate {
+ let Spellings = ["__builtin_atan2"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions];
- let Prototype = "__float128(__float128, __float128)";
+ let Prototype = "T(T, T)";
}
def CopysignF16 : Builtin {
@@ -1498,19 +1498,19 @@ def ReduceOr : Builtin {
def ReduceAnd : Builtin {
let Spellings = ["__builtin_reduce_and"];
- let Attributes = [NoThrow, Const, CustomTypeChecking];
+ let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
let Prototype = "void(...)";
}
def ReduceAdd : Builtin {
let Spellings = ["__builtin_reduce_add"];
- let Attributes = [NoThrow, Const, CustomTypeChecking];
+ let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
let Prototype = "void(...)";
}
def ReduceMul : Builtin {
let Spellings = ["__builtin_reduce_mul"];
- let Attributes = [NoThrow, Const, CustomTypeChecking];
+ let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
let Prototype = "void(...)";
}
@@ -1995,6 +1995,12 @@ def AtomicThreadFence : Builtin {
let Prototype = "void(int)";
}
+def ScopedAtomicThreadFence : Builtin {
+ let Spellings = ["__scoped_atomic_thread_fence"];
+ let Attributes = [NoThrow];
+ let Prototype = "void(int, int)";
+}
+
def AtomicSignalFence : Builtin {
let Spellings = ["__atomic_signal_fence"];
let Attributes = [NoThrow];
@@ -4895,6 +4901,12 @@ def HLSLSplitDouble: LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}
+def HLSLClip: LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_elementwise_clip"];
+ let Attributes = [NoThrow, Const];
+ let Prototype = "void(...)";
+}
+
// Builtins for XRay.
def XRayCustomEvent : Builtin {
let Spellings = ["__xray_customevent"];
diff --git a/clang/include/clang/Basic/BuiltinsAMDGPU.def b/clang/include/clang/Basic/BuiltinsAMDGPU.def
index 8f44afa40593865c0399fc08925060990ece6879..7ce8f2c1669d6702dcb79e4e72a1cd773941343d 100644
--- a/clang/include/clang/Basic/BuiltinsAMDGPU.def
+++ b/clang/include/clang/Basic/BuiltinsAMDGPU.def
@@ -431,6 +431,14 @@ TARGET_BUILTIN(__builtin_amdgcn_cvt_pk_fp8_f32, "iffiIb", "nc", "fp8-conversion-
TARGET_BUILTIN(__builtin_amdgcn_cvt_sr_bf8_f32, "ifiiIi", "nc", "fp8-conversion-insts")
TARGET_BUILTIN(__builtin_amdgcn_cvt_sr_fp8_f32, "ifiiIi", "nc", "fp8-conversion-insts")
+//===----------------------------------------------------------------------===//
+// GFX950 only builtins.
+//===----------------------------------------------------------------------===//
+TARGET_BUILTIN(__builtin_amdgcn_mfma_f32_16x16x32_f16, "V4fV8hV8hV4fIiIiIi", "nc", "gfx950-insts")
+TARGET_BUILTIN(__builtin_amdgcn_mfma_f32_32x32x16_f16, "V16fV8hV8hV16fIiIiIi", "nc", "gfx950-insts")
+
+TARGET_BUILTIN(__builtin_amdgcn_mfma_f32_32x32x16_bf16, "V16fV8yV8yV16fIiIiIi", "nc", "gfx950-insts")
+
//===----------------------------------------------------------------------===//
// GFX12+ only builtins.
//===----------------------------------------------------------------------===//
@@ -522,5 +530,7 @@ TARGET_BUILTIN(__builtin_amdgcn_swmmac_f32_16x16x32_fp8_bf8_w64, "V4fiV2iV4fs",
TARGET_BUILTIN(__builtin_amdgcn_swmmac_f32_16x16x32_bf8_fp8_w64, "V4fiV2iV4fs", "nc", "gfx12-insts,wavefrontsize64")
TARGET_BUILTIN(__builtin_amdgcn_swmmac_f32_16x16x32_bf8_bf8_w64, "V4fiV2iV4fs", "nc", "gfx12-insts,wavefrontsize64")
+TARGET_BUILTIN(__builtin_amdgcn_prng_b32, "UiUi", "nc", "prng-inst")
+
#undef BUILTIN
#undef TARGET_BUILTIN
diff --git a/clang/include/clang/Basic/BuiltinsLoongArchLASX.def b/clang/include/clang/Basic/BuiltinsLoongArchLASX.def
index f644b820a618988aaf63b26dec0ea320cc00d7c0..c4ea46a3bc5b51047092bc3eb9a34e08408cfb4a 100644
--- a/clang/include/clang/Basic/BuiltinsLoongArchLASX.def
+++ b/clang/include/clang/Basic/BuiltinsLoongArchLASX.def
@@ -371,7 +371,7 @@ TARGET_BUILTIN(__builtin_lasx_xvor_v, "V32UcV32UcV32Uc", "nc", "lasx")
TARGET_BUILTIN(__builtin_lasx_xvxor_v, "V32UcV32UcV32Uc", "nc", "lasx")
TARGET_BUILTIN(__builtin_lasx_xvnor_v, "V32UcV32UcV32Uc", "nc", "lasx")
TARGET_BUILTIN(__builtin_lasx_xvandn_v, "V32UcV32UcV32Uc", "nc", "lasx")
-TARGET_BUILTIN(__builtin_lasx_xvorn_v, "V32ScV32ScV32Sc", "nc", "lasx")
+TARGET_BUILTIN(__builtin_lasx_xvorn_v, "V32UcV32UcV32Uc", "nc", "lasx")
TARGET_BUILTIN(__builtin_lasx_xvandi_b, "V32UcV32UcIUi", "nc", "lasx")
TARGET_BUILTIN(__builtin_lasx_xvori_b, "V32UcV32UcIUi", "nc", "lasx")
diff --git a/clang/include/clang/Basic/BuiltinsLoongArchLSX.def b/clang/include/clang/Basic/BuiltinsLoongArchLSX.def
index b3056971986d199f0941cb6c01d2364078fcf912..a823783af88c4f80cc1851402285010087fc9647 100644
--- a/clang/include/clang/Basic/BuiltinsLoongArchLSX.def
+++ b/clang/include/clang/Basic/BuiltinsLoongArchLSX.def
@@ -355,7 +355,7 @@ TARGET_BUILTIN(__builtin_lsx_vor_v, "V16UcV16UcV16Uc", "nc", "lsx")
TARGET_BUILTIN(__builtin_lsx_vxor_v, "V16UcV16UcV16Uc", "nc", "lsx")
TARGET_BUILTIN(__builtin_lsx_vnor_v, "V16UcV16UcV16Uc", "nc", "lsx")
TARGET_BUILTIN(__builtin_lsx_vandn_v, "V16UcV16UcV16Uc", "nc", "lsx")
-TARGET_BUILTIN(__builtin_lsx_vorn_v, "V16ScV16ScV16Sc", "nc", "lsx")
+TARGET_BUILTIN(__builtin_lsx_vorn_v, "V16UcV16UcV16Uc", "nc", "lsx")
TARGET_BUILTIN(__builtin_lsx_vandi_b, "V16UcV16UcIUi", "nc", "lsx")
TARGET_BUILTIN(__builtin_lsx_vori_b, "V16UcV16UcIUi", "nc", "lsx")
diff --git a/clang/include/clang/Basic/BuiltinsX86.def b/clang/include/clang/Basic/BuiltinsX86.def
index c93ea27f164e34d000846e8677faea0390089b57..352b3a9ec594a7461483779afcdcdcb459bdeaf0 100644
--- a/clang/include/clang/Basic/BuiltinsX86.def
+++ b/clang/include/clang/Basic/BuiltinsX86.def
@@ -660,6 +660,9 @@ TARGET_BUILTIN(__builtin_ia32_vpdpbuud256, "V8iV8iV8iV8i", "ncV:256:", "avxvnnii
TARGET_BUILTIN(__builtin_ia32_vpdpbuuds128, "V4iV4iV4iV4i", "ncV:128:", "avxvnniint8|avx10.2-256")
TARGET_BUILTIN(__builtin_ia32_vpdpbuuds256, "V8iV8iV8iV8i", "ncV:256:", "avxvnniint8|avx10.2-256")
+// MOVRS
+TARGET_BUILTIN(__builtin_ia32_prefetchrs, "vvC*", "nc", "movrs")
+
TARGET_BUILTIN(__builtin_ia32_gather3div2df, "V2dV2dvC*V2OiUcIi", "nV:128:", "avx512vl")
TARGET_BUILTIN(__builtin_ia32_gather3div2di, "V2OiV2OivC*V2OiUcIi", "nV:128:", "avx512vl")
TARGET_BUILTIN(__builtin_ia32_gather3div4df, "V4dV4dvC*V4OiUcIi", "nV:256:", "avx512vl")
diff --git a/clang/include/clang/Basic/BuiltinsX86_64.def b/clang/include/clang/Basic/BuiltinsX86_64.def
index f853b4313dae072bc8d61f54bf140f8cc8275f6f..57928a14b3b399bf1c352ac23db05c095c29005f 100644
--- a/clang/include/clang/Basic/BuiltinsX86_64.def
+++ b/clang/include/clang/Basic/BuiltinsX86_64.def
@@ -139,6 +139,12 @@ TARGET_BUILTIN(__builtin_ia32_t2rpntlvwz1rs_internal, "vUsUsUsV256i*V256i*vC*z",
TARGET_BUILTIN(__builtin_ia32_t2rpntlvwz1t1_internal, "vUsUsUsV256i*V256i*vC*z", "n", "amx-transpose")
TARGET_BUILTIN(__builtin_ia32_t2rpntlvwz1rst1_internal, "vUsUsUsV256i*V256i*vC*z", "n", "amx-movrs,amx-transpose")
TARGET_BUILTIN(__builtin_ia32_ttransposed_internal, "V256iUsUsV256i", "n", "amx-transpose")
+TARGET_BUILTIN(__builtin_ia32_ttdpbf16ps_internal, "V256iUsUsUsV256iV256iV256i", "n", "amx-bf16,amx-transpose")
+TARGET_BUILTIN(__builtin_ia32_ttdpfp16ps_internal, "V256iUsUsUsV256iV256iV256i", "n", "amx-fp16,amx-transpose")
+TARGET_BUILTIN(__builtin_ia32_ttcmmimfp16ps_internal, "V256iUsUsUsV256iV256iV256i", "n", "amx-complex,amx-transpose")
+TARGET_BUILTIN(__builtin_ia32_ttcmmrlfp16ps_internal, "V256iUsUsUsV256iV256iV256i", "n", "amx-complex,amx-transpose")
+TARGET_BUILTIN(__builtin_ia32_tconjtcmmimfp16ps_internal, "V256iUsUsUsV256iV256iV256i", "n", "amx-complex,amx-transpose")
+TARGET_BUILTIN(__builtin_ia32_tconjtfp16_internal, "V256iUsUsV256i", "n", "amx-complex,amx-transpose")
TARGET_BUILTIN(__builtin_ia32_tcvtrowd2ps_internal, "V16fUsUsV256iUi", "n", "amx-avx512,avx10.2-512")
TARGET_BUILTIN(__builtin_ia32_tcvtrowps2pbf16h_internal, "V32yUsUsV256iUi", "n", "amx-avx512,avx10.2-512")
@@ -148,6 +154,10 @@ TARGET_BUILTIN(__builtin_ia32_tcvtrowps2phl_internal, "V32xUsUsV256iUi", "n", "a
TARGET_BUILTIN(__builtin_ia32_tilemovrow_internal, "V16iUsUsV256iUi", "n", "amx-avx512,avx10.2-512")
TARGET_BUILTIN(__builtin_ia32_tmmultf32ps_internal, "V256iUsUsUsV256iV256iV256i", "n", "amx-tf32")
TARGET_BUILTIN(__builtin_ia32_ttmmultf32ps_internal, "V256iUsUsUsV256iV256iV256i", "n", "amx-tf32,amx-transpose")
+TARGET_BUILTIN(__builtin_ia32_tdpbf8ps_internal, "V256iUsUsUsV256iV256iV256i", "n", "amx-fp8")
+TARGET_BUILTIN(__builtin_ia32_tdpbhf8ps_internal, "V256iUsUsUsV256iV256iV256i", "n", "amx-fp8")
+TARGET_BUILTIN(__builtin_ia32_tdphbf8ps_internal, "V256iUsUsUsV256iV256iV256i", "n", "amx-fp8")
+TARGET_BUILTIN(__builtin_ia32_tdphf8ps_internal, "V256iUsUsUsV256iV256iV256i", "n", "amx-fp8")
// AMX
TARGET_BUILTIN(__builtin_ia32_tile_loadconfig, "vvC*", "n", "amx-tile")
@@ -181,6 +191,12 @@ TARGET_BUILTIN(__builtin_ia32_t2rpntlvwz0t1, "vIUcvC*z", "n","amx-transpose")
TARGET_BUILTIN(__builtin_ia32_t2rpntlvwz1, "vIUcvC*z", "n", "amx-transpose")
TARGET_BUILTIN(__builtin_ia32_t2rpntlvwz1t1, "vIUcvC*z", "n","amx-transpose")
TARGET_BUILTIN(__builtin_ia32_ttransposed, "vIUcIUc", "n", "amx-transpose")
+TARGET_BUILTIN(__builtin_ia32_ttdpbf16ps, "vIUcIUcIUc", "n", "amx-bf16,amx-transpose")
+TARGET_BUILTIN(__builtin_ia32_ttdpfp16ps, "vIUcIUcIUc", "n", "amx-fp16,amx-transpose")
+TARGET_BUILTIN(__builtin_ia32_ttcmmimfp16ps, "vIUcIUcIUc", "n", "amx-complex,amx-transpose")
+TARGET_BUILTIN(__builtin_ia32_ttcmmrlfp16ps, "vIUcIUcIUc", "n", "amx-complex,amx-transpose")
+TARGET_BUILTIN(__builtin_ia32_tconjtcmmimfp16ps, "vIUcIUcIUc", "n", "amx-complex,amx-transpose")
+TARGET_BUILTIN(__builtin_ia32_tconjtfp16, "vIUcIUc", "n", "amx-complex,amx-transpose")
TARGET_BUILTIN(__builtin_ia32_tcvtrowd2ps, "V16fIUcUi", "n", "amx-avx512,avx10.2-512")
TARGET_BUILTIN(__builtin_ia32_tcvtrowps2pbf16h, "V32yIUcUi", "n", "amx-avx512,avx10.2-512")
@@ -212,6 +228,12 @@ TARGET_BUILTIN(__builtin_ia32_aand64, "vv*SOi", "n", "raoint")
TARGET_BUILTIN(__builtin_ia32_aor64, "vv*SOi", "n", "raoint")
TARGET_BUILTIN(__builtin_ia32_axor64, "vv*SOi", "n", "raoint")
+// MOVRS
+TARGET_BUILTIN(__builtin_ia32_movrsqi, "ScvC*", "n", "movrs")
+TARGET_BUILTIN(__builtin_ia32_movrshi, "SsvC*", "n", "movrs")
+TARGET_BUILTIN(__builtin_ia32_movrssi, "SivC*", "n", "movrs")
+TARGET_BUILTIN(__builtin_ia32_movrsdi, "SLLivC*", "n", "movrs")
+
// MOVRS and AVX10.2
TARGET_BUILTIN(__builtin_ia32_vmovrsb128, "V16cV16cC*", "nV:128:", "movrs,avx10.2-256")
TARGET_BUILTIN(__builtin_ia32_vmovrsb256, "V32cV32cC*", "nV:256:", "movrs,avx10.2-256")
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 104ad81ce7d1f35a749de09ec52c73ed7bb70810..684e8947595de3f1c883e8ac6e432eabe32f3b97 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -155,6 +155,7 @@ VALUE_CODEGENOPT(PatchableFunctionEntryOffset , 32, 0)
CODEGENOPT(HotPatch, 1, 0) ///< Supports the Microsoft /HOTPATCH flag and
///< generates a 'patchable-function' attribute.
+CODEGENOPT(TlsGuards , 1, 1) ///< Controls emission of tls guards via -fms-tls-guards
CODEGENOPT(JMCInstrument, 1, 0) ///< Set when -fjmc is enabled.
CODEGENOPT(InstrumentForProfiling , 1, 0) ///< Set when -pg is enabled.
CODEGENOPT(CallFEntry , 1, 0) ///< Set when -mfentry is enabled.
diff --git a/clang/include/clang/Basic/Cuda.h b/clang/include/clang/Basic/Cuda.h
index 7b4f435dc39f291e450632b7d11ae4d1f2833c59..c2a4addf488df1b4b84c8b77ce0b474ac020a181 100644
--- a/clang/include/clang/Basic/Cuda.h
+++ b/clang/include/clang/Basic/Cuda.h
@@ -103,9 +103,11 @@ enum class OffloadArch {
GFX909,
GFX90a,
GFX90c,
+ GFX9_4_GENERIC,
GFX940,
GFX941,
GFX942,
+ GFX950,
GFX10_1_GENERIC,
GFX1010,
GFX1011,
diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h
index 072392e80543741b75915b5e160dd7b04d360373..d271accca3d3b792fd465988891bdc41286b1735 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -23,7 +23,6 @@
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Compiler.h"
#include
@@ -561,7 +560,7 @@ private:
ArgToStringFnTy ArgToStringFn;
/// Whether the diagnostic should be suppressed in FilePath.
- llvm::unique_function
+ llvm::unique_function
DiagSuppressionMapping;
public:
@@ -973,8 +972,7 @@ public:
/// These take presumed locations into account, and can still be overriden by
/// clang-diagnostics pragmas.
void setDiagSuppressionMapping(llvm::MemoryBuffer &Input);
- bool isSuppressedViaMapping(diag::kind DiagId,
- llvm::StringRef FilePath) const;
+ bool isSuppressedViaMapping(diag::kind DiagId, StringRef FilePath) const;
/// Issue the message to the client.
///
diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index 457abea0b81471cc7fcd44aba7b23932e65570c0..f4a155bb00bb377e715ebe4bf5341ac028288fc9 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -364,9 +364,9 @@ def err_target_unsupported_abi_with_fpu : Error<
def err_ppc_impossible_musttail: Error<
"'musttail' attribute for this call is impossible because %select{"
- "long calls can not be tail called on PPC|"
- "indirect calls can not be tail called on PPC|"
- "external calls can not be tail called on PPC}0"
+ "long calls cannot be tail called on PPC|"
+ "indirect calls cannot be tail called on PPC|"
+ "external calls cannot be tail called on PPC}0"
>;
def err_aix_musttail_unsupported: Error<
"'musttail' attribute is not supported on AIX">;
@@ -389,14 +389,14 @@ def remark_sloc_usage : Remark<
"source manager location address space usage:">,
InGroup>, DefaultRemark, ShowInSystemHeader;
def note_total_sloc_usage : Note<
- "%0B (%1B) in local locations, %2B (%3B) "
- "in locations loaded from AST files, for a total of %4B (%5B) "
- "(%6%% of available space)">;
+ "%0B (%human0B) in local locations, %1B (%human1B) "
+ "in locations loaded from AST files, for a total of %2B (%human2B) "
+ "(%3%% of available space)">;
def note_file_sloc_usage : Note<
- "file entered %0 time%s0 using %1B (%2B) of space"
- "%plural{0:|: plus %3B (%4B) for macro expansions}3">;
+ "file entered %0 time%s0 using %1B (%human1B) of space"
+ "%plural{0:|: plus %2B (%human2B) for macro expansions}2">;
def note_file_misc_sloc_usage : Note<
- "%0 additional files entered using a total of %1B (%2B) of space">;
+ "%0 additional files entered using a total of %1B (%human1B) of space">;
// Modules
def err_module_format_unhandled : Error<
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 6ebf00d3886fe81932dd7c0d77c5d9260075b614..95ed967ad16ad7aa18b47134b791bf326d6128b8 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -557,7 +557,7 @@ def err_test_module_file_extension_format : Error<
"'blockname:major:minor:hashed:user info'">;
def err_drv_module_output_with_multiple_arch : Error<
- "option '-fmodule-output' can't be used with multiple arch options">;
+ "option '-fmodule-output' cannot be used with multiple arch options">;
def warn_drv_delayed_template_parsing_after_cxx20 : Warning<
"-fdelayed-template-parsing is deprecated after C++20">,
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 72eada50a56cc9efde8076d17a7ad63382a22934..df9bf94b5d0398feb9e70f394d2de46b3945ab90 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -453,6 +453,7 @@ def ShiftOpParentheses: DiagGroup<"shift-op-parentheses">;
def OverloadedShiftOpParentheses: DiagGroup<"overloaded-shift-op-parentheses">;
def DanglingAssignment: DiagGroup<"dangling-assignment">;
def DanglingAssignmentGsl : DiagGroup<"dangling-assignment-gsl">;
+def DanglingCapture : DiagGroup<"dangling-capture">;
def DanglingElse: DiagGroup<"dangling-else">;
def DanglingField : DiagGroup<"dangling-field">;
def DanglingInitializerList : DiagGroup<"dangling-initializer-list">;
@@ -462,6 +463,7 @@ def ReturnStackAddress : DiagGroup<"return-stack-address">;
def : DiagGroup<"return-local-addr", [ReturnStackAddress]>;
def Dangling : DiagGroup<"dangling", [DanglingAssignment,
DanglingAssignmentGsl,
+ DanglingCapture,
DanglingField,
DanglingInitializerList,
DanglingGsl,
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 1254f0247e50a6f7c67aacb26262769d910e478c..0c70dcb941e6560dd1d0a28e975d6a6d1eafa134 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -976,7 +976,7 @@ def warn_cxx98_compat_defaulted_deleted_function : Warning<
"%select{defaulted|deleted}0 function definitions are incompatible with C++98">,
InGroup, DefaultIgnore;
-def ext_delete_with_message : ExtWarn<
+def ext_delete_with_message : Extension<
"'= delete' with a message is a C++2c extension">, InGroup;
def warn_cxx23_delete_with_message : Warning<
"'= delete' with a message is incompatible with C++ standards before C++2c">,
diff --git a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
index 5446b32efbdd47e4ad6ee81779bafd1d18a70519..e060fffc7280a79278d7c54eb00b1de55dbce3c7 100644
--- a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
+++ b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
@@ -14,7 +14,7 @@ let Component = "Refactoring" in {
let CategoryName = "Refactoring Invocation Issue" in {
-def err_refactor_no_selection : Error<"refactoring action can't be initiated "
+def err_refactor_no_selection : Error<"refactoring action cannot be initiated "
"without a selection">;
def err_refactor_selection_no_symbol : Error<"there is no symbol at the given "
"location">;
@@ -26,7 +26,7 @@ def err_refactor_code_outside_of_function : Error<"the selected code is not a "
def err_refactor_extract_simple_expression : Error<"the selected expression "
"is too simple to extract">;
def err_refactor_extract_prohibited_expression : Error<"the selected "
- "expression can't be extracted">;
+ "expression cannot be extracted">;
}
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index cdc9ad9f806e1cfbe9f645d823462fa32a83c8f4..5ff489fa93b4bf770906a318e8e53e445964a45d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1151,7 +1151,7 @@ def err_pragma_attribute_matcher_subrule_contradicts_rule : Error<
def err_pragma_attribute_matcher_negated_subrule_contradicts_subrule : Error<
"negated attribute subject matcher sub-rule '%0' contradicts sub-rule '%1'">;
def err_pragma_attribute_invalid_matchers : Error<
- "attribute %0 can't be applied to %1">;
+ "attribute %0 cannot be applied to %1">;
def err_pragma_attribute_stack_mismatch : Error<
"'#pragma clang attribute %select{%1.|}0pop' with no matching"
" '#pragma clang attribute %select{%1.|}0push'">;
@@ -3383,6 +3383,20 @@ def err_callback_callee_is_variadic : Error<
"'callback' attribute callee may not be variadic">;
def err_callback_implicit_this_not_available : Error<
"'callback' argument at position %0 references unavailable implicit 'this'">;
+
+def err_capture_by_attribute_multiple : Error<
+ "multiple 'lifetime_capture' attributes specified">;
+def err_capture_by_attribute_no_entity : Error<
+ "'lifetime_capture_by' attribute specifies no capturing entity">;
+def err_capture_by_implicit_this_not_available : Error<
+ "'lifetime_capture_by' argument references unavailable implicit 'this'">;
+def err_capture_by_attribute_argument_unknown : Error<
+ "'lifetime_capture_by' attribute argument %0 is not a known function parameter"
+ "; must be a function parameter, 'this', 'global' or 'unknown'">;
+def err_capture_by_references_itself : Error<"'lifetime_capture_by' argument references itself">;
+def err_capture_by_param_uses_reserved_name : Error<
+ "parameter cannot be named '%select{global|unknown}0' while using 'lifetime_capture_by(%select{global|unknown}0)'">;
+
def err_init_method_bad_return_type : Error<
"init methods must return an object pointer type, not %0">;
def err_attribute_invalid_size : Error<
@@ -6136,7 +6150,7 @@ def err_mismatched_owning_module : Error<
"declaration of %0 in %select{the global module|module %2}1 follows "
"declaration in %select{the global module|module %4}3">;
def err_multiple_decl_in_different_modules : Error<
- "declaration %0 attached to named module '%1' can't be attached to "
+ "declaration %0 attached to named module '%1' cannot be attached to "
"other modules">;
def err_redefinition_different_type : Error<
"redefinition of %0 with a different type%diff{: $ vs $|}1,2">;
@@ -8546,7 +8560,7 @@ def err_typecheck_missing_return_type_incompatible : Error<
"literal|lambda expression}2 has unspecified explicit return type">;
def note_incomplete_class_and_qualified_id : Note<
- "conformance of forward class %0 to protocol %1 can not be confirmed">;
+ "conformance of forward class %0 to protocol %1 cannot be confirmed">;
def warn_incompatible_qualified_id : Warning<
"%select{%diff{assigning to $ from incompatible type $|"
"assigning to type from incompatible type}0,1"
@@ -9286,11 +9300,11 @@ def warn_unused_container_subscript_expr : Warning<
def warn_unused_call : Warning<
"ignoring return value of function declared with %0 attribute">,
InGroup;
-def warn_unused_constructor : Warning<
- "ignoring temporary created by a constructor declared with %0 attribute">,
+def warn_unused_return_type : Warning<
+ "ignoring %select{return value|temporary}0 of type %2 declared with %1 attribute%select{|: %4}3">,
InGroup;
-def warn_unused_constructor_msg : Warning<
- "ignoring temporary created by a constructor declared with %0 attribute: %1">,
+def warn_unused_constructor : Warning<
+ "ignoring temporary created by a constructor declared with %0 attribute%select{|: %2}1">,
InGroup;
def warn_side_effects_unevaluated_context : Warning<
"expression with side effects has no effect in an unevaluated context">,
@@ -9299,10 +9313,7 @@ def warn_side_effects_typeid : Warning<
"expression with side effects will be evaluated despite being used as an "
"operand to 'typeid'">, InGroup;
def warn_unused_result : Warning<
- "ignoring return value of function declared with %0 attribute">,
- InGroup;
-def warn_unused_result_msg : Warning<
- "ignoring return value of function declared with %0 attribute: %1">,
+ "ignoring return value of function declared with %0 attribute%select{|: %2}1">,
InGroup;
def warn_unused_result_typedef_unsupported_spelling : Warning<
"'[[%select{nodiscard|gnu::warn_unused_result}0]]' attribute ignored when "
@@ -9400,7 +9411,7 @@ let CategoryName = "Inline Assembly Issue" in {
"asm constraint has an unexpected number of alternatives: %0 vs %1">;
def err_asm_incomplete_type : Error<"asm operand has incomplete type %0">;
def err_asm_unknown_register_name : Error<"unknown register name '%0' in asm">;
- def err_asm_unwind_and_goto : Error<"unwind clobber can't be used with asm goto">;
+ def err_asm_unwind_and_goto : Error<"unwind clobber cannot be used with asm goto">;
def err_asm_invalid_global_var_reg : Error<"register '%0' unsuitable for "
"global register variables on this target">;
def err_asm_register_size_mismatch : Error<"size of register '%0' does not "
@@ -9419,7 +9430,7 @@ let CategoryName = "Inline Assembly Issue" in {
def err_asm_input_duplicate_match : Error<
"more than one input constraint matches the same output '%0'">;
def err_store_value_to_reg : Error<
- "impossible constraint in asm: can't store value into a register">;
+ "impossible constraint in asm: cannot store value into a register">;
def warn_asm_label_on_auto_decl : Warning<
"ignored asm label '%0' on automatic variable">;
@@ -10118,10 +10129,11 @@ def err_lifetimebound_ctor_dtor : Error<
"%select{constructor|destructor}0">;
def err_lifetimebound_parameter_void_return_type : Error<
"'lifetimebound' attribute cannot be applied to a parameter of a function "
- "that returns void">;
+ "that returns void; did you mean 'lifetime_capture_by(X)'">;
def err_lifetimebound_implicit_object_parameter_void_return_type : Error<
"'lifetimebound' attribute cannot be applied to an implicit object "
- "parameter of a function that returns void">;
+ "parameter of a function that returns void; "
+ "did you mean 'lifetime_capture_by(X)'">;
// CHECK: returning address/reference of stack memory
def warn_ret_stack_addr_ref : Warning<
@@ -10216,6 +10228,12 @@ def warn_dangling_pointer_assignment : Warning<
"object backing %select{|the pointer }0%1 "
"will be destroyed at the end of the full-expression">,
InGroup;
+def warn_dangling_reference_captured : Warning<
+ "object whose reference is captured by '%0' will be destroyed at the end of "
+ "the full-expression">, InGroup, DefaultIgnore;
+def warn_dangling_reference_captured_by_unknown : Warning<
+ "object whose reference is captured will be destroyed at the end of "
+ "the full-expression">, InGroup, DefaultIgnore;
// For non-floating point, expressions of the form x == x or x != x
// should result in a warning, since these always evaluate to a constant.
@@ -10946,7 +10964,7 @@ def err_opencl_builtin_pipe_invalid_access_modifier : Error<
def err_opencl_invalid_access_qualifier : Error<
"access qualifier can only be used for pipe and image type">;
def err_opencl_invalid_read_write : Error<
- "access qualifier %0 can not be used for %1 %select{|prior to OpenCL C version 2.0 or in version 3.0 "
+ "access qualifier %0 cannot be used for %1 %select{|prior to OpenCL C version 2.0 or in version 3.0 "
"and without __opencl_c_read_write_images feature}2">;
def err_opencl_multiple_access_qualifiers : Error<
"multiple access qualifiers">;
@@ -11446,7 +11464,7 @@ def err_omp_wrong_linear_modifier : Error<
def err_omp_wrong_linear_modifier_non_reference : Error<
"variable of non-reference type %0 can be used only with 'val' modifier, but used with '%1'">;
def err_omp_step_simple_modifier_exclusive : Error<
- "step simple modifier is exclusive and can't be use with 'val', 'uval' or 'ref' modifier">;
+ "step simple modifier is exclusive and cannot be use with 'val', 'uval' or 'ref' modifier">;
def err_omp_wrong_simdlen_safelen_values : Error<
"the value of 'simdlen' parameter must be less than or equal to the value of the 'safelen' parameter">;
def err_omp_wrong_if_directive_name_modifier : Error<
@@ -11520,7 +11538,7 @@ def err_omp_schedule_nonmonotonic_static : Error<
def err_omp_simple_clause_incompatible_with_ordered : Error<
"'%0' clause with '%1' modifier cannot be specified if an 'ordered' clause is specified">;
def err_omp_ordered_simd : Error<
- "'ordered' clause with a parameter can not be specified in '#pragma omp %0' directive">;
+ "'ordered' clause with a parameter cannot be specified in '#pragma omp %0' directive">;
def err_omp_variable_in_given_clause_and_dsa : Error<
"%0 variable cannot be in a %1 clause in '#pragma omp %2' directive">;
def err_omp_param_or_this_in_clause : Error<
@@ -12352,9 +12370,9 @@ def err_preserve_enum_value_not_const: Error<
"__builtin_preserve_enum_value argument %0 not a constant">;
def err_bit_cast_non_trivially_copyable : Error<
- "__builtin_bit_cast %select{source|destination}0 type must be trivially copyable">;
+ "'__builtin_bit_cast' %select{source|destination}0 type must be trivially copyable">;
def err_bit_cast_type_size_mismatch : Error<
- "__builtin_bit_cast source size does not equal destination size (%0 vs %1)">;
+ "size of '__builtin_bit_cast' source type %0 does not match destination type %1 (%2 vs %3 bytes)">;
// SYCL-specific diagnostics
def warn_sycl_kernel_num_of_template_params : Warning<
@@ -12639,11 +12657,11 @@ def note_acc_previous_clause_here : Note<"previous clause is here">;
def note_acc_previous_expr_here : Note<"previous expression is here">;
def err_acc_branch_in_out_compute_construct
: Error<"invalid %select{branch|return|throw}0 %select{out of|into}1 "
- "OpenACC Compute Construct">;
+ "OpenACC Compute/Combined Construct">;
def note_acc_branch_into_compute_construct
- : Note<"invalid branch into OpenACC Compute Construct">;
+ : Note<"invalid branch into OpenACC Compute/Combined Construct">;
def note_acc_branch_out_of_compute_construct
- : Note<"invalid branch out of OpenACC Compute Construct">;
+ : Note<"invalid branch out of OpenACC Compute/Combined Construct">;
def warn_acc_if_self_conflict
: Warning<"OpenACC construct 'self' has no effect when an 'if' clause "
"evaluates to true">,
@@ -12698,10 +12716,10 @@ def err_acc_var_not_pointer_type
def note_acc_expected_pointer_var : Note<"expected variable of pointer type">;
def err_acc_clause_after_device_type
: Error<"OpenACC clause '%0' may not follow a '%1' clause in a "
- "%select{'%3'|compute}2 construct">;
+ "'%2' construct">;
def err_acc_clause_cannot_combine
: Error<"OpenACC clause '%0' may not appear on the same construct as a "
- "'%1' clause on a 'loop' construct">;
+ "'%1' clause on a '%2' construct">;
def err_acc_reduction_num_gangs_conflict
: Error<
"OpenACC 'reduction' clause may not appear on a 'parallel' construct "
@@ -12717,7 +12735,7 @@ def err_acc_reduction_composite_member_type :Error<
"OpenACC 'reduction' composite variable must not have non-scalar field">;
def note_acc_reduction_composite_member_loc : Note<"invalid field is here">;
def err_acc_loop_not_for_loop
- : Error<"OpenACC 'loop' construct can only be applied to a 'for' loop">;
+ : Error<"OpenACC '%0' construct can only be applied to a 'for' loop">;
def note_acc_construct_here : Note<"'%0' construct is here">;
def err_acc_loop_spec_conflict
: Error<"OpenACC clause '%0' on '%1' construct conflicts with previous "
@@ -12775,16 +12793,16 @@ def err_reduction_op_mismatch
: Error<"OpenACC 'reduction' variable must have the same operator in all "
"nested constructs (%0 vs %1)">;
def err_acc_loop_variable_type
- : Error<"loop variable of loop associated with an OpenACC 'loop' construct "
+ : Error<"loop variable of loop associated with an OpenACC '%0' construct "
"must be of integer, pointer, or random-access-iterator type (is "
- "%0)">;
+ "%1)">;
def err_acc_loop_variable
- : Error<"OpenACC 'loop' construct must have initialization clause in "
+ : Error<"OpenACC '%0' construct must have initialization clause in "
"canonical form ('var = init' or 'T var = init')">;
def err_acc_loop_terminating_condition
- : Error<"OpenACC 'loop' construct must have a terminating condition">;
+ : Error<"OpenACC '%0' construct must have a terminating condition">;
def err_acc_loop_not_monotonic
- : Error<"OpenACC 'loop' variable must monotonically increase or decrease "
+ : Error<"OpenACC '%0' variable must monotonically increase or decrease "
"('++', '--', or compound assignment)">;
// AMDGCN builtins diagnostics
diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index 7f5d26118bdc714cee305464a9359aeda303a5ab..9088c867d53ce4fc2a510ef3709588877e3b948c 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -116,6 +116,7 @@ FEATURE(ptrauth_function_pointer_type_discrimination, LangOpts.PointerAuthFuncti
FEATURE(ptrauth_indirect_gotos, LangOpts.PointerAuthIndirectGotos)
FEATURE(ptrauth_init_fini, LangOpts.PointerAuthInitFini)
FEATURE(ptrauth_init_fini_address_discrimination, LangOpts.PointerAuthInitFiniAddressDiscrimination)
+FEATURE(ptrauth_elf_got, LangOpts.PointerAuthELFGOT)
EXTENSION(swiftcc,
PP.getTargetInfo().checkCallingConvention(CC_Swift) ==
clang::TargetInfo::CCCR_OK)
diff --git a/clang/include/clang/Basic/OpenACCKinds.h b/clang/include/clang/Basic/OpenACCKinds.h
index 4310c28bc935e8719a8a73e7a76cb3b5a91da34a..ea0bf23468cb8b47f831b58cf949621d812017d2 100644
--- a/clang/include/clang/Basic/OpenACCKinds.h
+++ b/clang/include/clang/Basic/OpenACCKinds.h
@@ -152,6 +152,12 @@ inline bool isOpenACCComputeDirectiveKind(OpenACCDirectiveKind K) {
K == OpenACCDirectiveKind::Kernels;
}
+inline bool isOpenACCCombinedDirectiveKind(OpenACCDirectiveKind K) {
+ return K == OpenACCDirectiveKind::ParallelLoop ||
+ K == OpenACCDirectiveKind::SerialLoop ||
+ K == OpenACCDirectiveKind::KernelsLoop;
+}
+
enum class OpenACCAtomicKind : uint8_t {
Read,
Write,
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index 6046b5409bd1b0bc4081f9608791a63ee90690ae..a11bc317c9c91c4ec01e24c0f40511b1f00a913a 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -315,6 +315,7 @@ def OpenACCAssociatedStmtConstruct
: StmtNode;
def OpenACCComputeConstruct : StmtNode;
def OpenACCLoopConstruct : StmtNode;
+def OpenACCCombinedConstruct : StmtNode;
// OpenACC Additional Expressions.
def OpenACCAsteriskSizeExpr : StmtNode;
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 25eda907d20a7bf30d0ef036d5f6776c00631cf8..9cd23d123f2bac89dc90434e7442e4bf8c32aba9 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -486,6 +486,13 @@ public:
/// \param AddrSpace address space of pointee in source language.
virtual uint64_t getNullPointerValue(LangAS AddrSpace) const { return 0; }
+ /// Returns true if an address space can be safely converted to another.
+ /// \param A address space of target in source language.
+ /// \param B address space of source in source language.
+ virtual bool isAddressSpaceSupersetOf(LangAS A, LangAS B) const {
+ return A == B;
+ }
+
/// Return the size of '_Bool' and C++ 'bool' for this target, in bits.
unsigned getBoolWidth() const { return BoolWidth; }
@@ -1497,6 +1504,10 @@ public:
bool supportsIFunc() const {
if (getTriple().isOSBinFormatMachO())
return true;
+ if (getTriple().isOSWindows() && getTriple().isAArch64())
+ return true;
+ if (getTriple().getArch() == llvm::Triple::ArchType::avr)
+ return true;
return getTriple().isOSBinFormatELF() &&
((getTriple().isOSLinux() && !getTriple().isMusl()) ||
getTriple().isOSFreeBSD());
@@ -1860,6 +1871,11 @@ private:
void CheckFixedPointBits() const;
};
+namespace targets {
+std::unique_ptr
+AllocateTarget(const llvm::Triple &Triple, const clang::TargetOptions &Opts);
+} // namespace targets
+
} // end namespace clang
#endif
diff --git a/clang/include/clang/Basic/arm_mve.td b/clang/include/clang/Basic/arm_mve.td
index 52185ca07da41ff15c43b608b2b1c60ef5da5ae0..93abbc47c54dd5c6a61d9366104291c2569ea50b 100644
--- a/clang/include/clang/Basic/arm_mve.td
+++ b/clang/include/clang/Basic/arm_mve.td
@@ -193,7 +193,7 @@ multiclass FMA {
def sq_m_n: Intrinsic