diff --git a/.ci/compute-projects.sh b/.ci/compute-projects.sh
new file mode 100644
index 0000000000000..32baf26b4f0a0
--- /dev/null
+++ b/.ci/compute-projects.sh
@@ -0,0 +1,194 @@
+#!/usr/bin/env bash
+#===----------------------------------------------------------------------===##
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+#===----------------------------------------------------------------------===##
+
+#
+# This file contains functions to compute which projects should be built by CI
+# systems and is intended to provide common functionality applicable across
+# multiple systems during a transition period.
+#
+
+function compute-projects-to-test() {
+ isForWindows=$1
+ shift
+ projects=${@}
+ for project in ${projects}; do
+ echo "${project}"
+ case ${project} in
+ lld)
+ for p in bolt cross-project-tests; do
+ echo $p
+ done
+ ;;
+ llvm)
+ for p in bolt clang clang-tools-extra lld lldb mlir polly; do
+ echo $p
+ done
+ # Flang is not stable in Windows CI at the moment
+ if [[ $isForWindows == 0 ]]; then
+ echo flang
+ fi
+ ;;
+ clang)
+ # lldb is temporarily removed to alleviate Linux pre-commit CI waiting times
+ for p in clang-tools-extra compiler-rt cross-project-tests; do
+ echo $p
+ done
+ ;;
+ clang-tools-extra)
+ echo libc
+ ;;
+ mlir)
+ # Flang is not stable in Windows CI at the moment
+ if [[ $isForWindows == 0 ]]; then
+ echo flang
+ fi
+ ;;
+ *)
+ # Nothing to do
+ ;;
+ esac
+ done
+}
+
+function compute-runtimes-to-test() {
+ projects=${@}
+ for project in ${projects}; do
+ case ${project} in
+ clang)
+ for p in libcxx libcxxabi libunwind; do
+ echo $p
+ done
+ ;;
+ *)
+ # Nothing to do
+ ;;
+ esac
+ done
+}
+
+function add-dependencies() {
+ projects=${@}
+ for project in ${projects}; do
+ echo "${project}"
+ case ${project} in
+ bolt)
+ for p in clang lld llvm; do
+ echo $p
+ done
+ ;;
+ cross-project-tests)
+ for p in lld clang; do
+ echo $p
+ done
+ ;;
+ clang-tools-extra)
+ for p in llvm clang; do
+ echo $p
+ done
+ ;;
+ compiler-rt|libc|openmp)
+ echo clang lld
+ ;;
+ flang|lldb|libclc)
+ for p in llvm clang; do
+ echo $p
+ done
+ ;;
+ lld|mlir|polly)
+ echo llvm
+ ;;
+ *)
+ # Nothing to do
+ ;;
+ esac
+ done
+}
+
+function exclude-linux() {
+ projects=${@}
+ for project in ${projects}; do
+ case ${project} in
+ cross-project-tests) ;; # tests failing
+ openmp) ;; # https://github.com/google/llvm-premerge-checks/issues/410
+ *)
+ echo "${project}"
+ ;;
+ esac
+ done
+}
+
+function exclude-windows() {
+ projects=${@}
+ for project in ${projects}; do
+ case ${project} in
+ cross-project-tests) ;; # tests failing
+ compiler-rt) ;; # tests taking too long
+ openmp) ;; # TODO: having trouble with the Perl installation
+ libc) ;; # no Windows support
+ lldb) ;; # custom environment requirements (https://github.com/llvm/llvm-project/pull/94208#issuecomment-2146256857)
+ bolt) ;; # tests are not supported yet
+ *)
+ echo "${project}"
+ ;;
+ esac
+ done
+}
+
+# Prints only projects that are both present in $modified_dirs and the passed
+# list.
+function keep-modified-projects() {
+ projects=${@}
+ for project in ${projects}; do
+ if echo "$modified_dirs" | grep -q -E "^${project}$"; then
+ echo "${project}"
+ fi
+ done
+}
+
+function check-targets() {
+ # Do not use "check-all" here because if there is "check-all" plus a
+ # project specific target like "check-clang", that project's tests
+ # will be run twice.
+ projects=${@}
+ for project in ${projects}; do
+ case ${project} in
+ clang-tools-extra)
+ echo "check-clang-tools"
+ ;;
+ compiler-rt)
+ echo "check-compiler-rt"
+ ;;
+ cross-project-tests)
+ echo "check-cross-project"
+ ;;
+ libcxx)
+ echo "check-cxx"
+ ;;
+ libcxxabi)
+ echo "check-cxxabi"
+ ;;
+ libunwind)
+ echo "check-unwind"
+ ;;
+ lldb)
+ echo "check-lldb"
+ ;;
+ pstl)
+ # Currently we do not run pstl tests in CI.
+ ;;
+ libclc)
+ # Currently there is no testing for libclc.
+ ;;
+ *)
+ echo "check-${project}"
+ ;;
+ esac
+ done
+}
+
diff --git a/.ci/generate-buildkite-pipeline-premerge b/.ci/generate-buildkite-pipeline-premerge
index 190dd1e5ba5af..9d9ca32183944 100755
--- a/.ci/generate-buildkite-pipeline-premerge
+++ b/.ci/generate-buildkite-pipeline-premerge
@@ -52,184 +52,7 @@ modified_dirs=$(echo "$MODIFIED_FILES" | cut -d'/' -f1 | sort -u)
echo "Directories modified:" >&2
echo "$modified_dirs" >&2
-function compute-projects-to-test() {
- isForWindows=$1
- shift
- projects=${@}
- for project in ${projects}; do
- echo "${project}"
- case ${project} in
- lld)
- for p in bolt cross-project-tests; do
- echo $p
- done
- ;;
- llvm)
- for p in bolt clang clang-tools-extra lld lldb mlir polly; do
- echo $p
- done
- # Flang is not stable in Windows CI at the moment
- if [[ $isForWindows == 0 ]]; then
- echo flang
- fi
- ;;
- clang)
- # lldb is temporarily removed to alleviate Linux pre-commit CI waiting times
- for p in clang-tools-extra compiler-rt cross-project-tests; do
- echo $p
- done
- ;;
- clang-tools-extra)
- echo libc
- ;;
- mlir)
- # Flang is not stable in Windows CI at the moment
- if [[ $isForWindows == 0 ]]; then
- echo flang
- fi
- ;;
- *)
- # Nothing to do
- ;;
- esac
- done
-}
-
-function compute-runtimes-to-test() {
- projects=${@}
- for project in ${projects}; do
- case ${project} in
- clang)
- for p in libcxx libcxxabi libunwind; do
- echo $p
- done
- ;;
- *)
- # Nothing to do
- ;;
- esac
- done
-}
-
-function add-dependencies() {
- projects=${@}
- for project in ${projects}; do
- echo "${project}"
- case ${project} in
- bolt)
- for p in clang lld llvm; do
- echo $p
- done
- ;;
- cross-project-tests)
- for p in lld clang; do
- echo $p
- done
- ;;
- clang-tools-extra)
- for p in llvm clang; do
- echo $p
- done
- ;;
- compiler-rt|libc|openmp)
- echo clang lld
- ;;
- flang|lldb|libclc)
- for p in llvm clang; do
- echo $p
- done
- ;;
- lld|mlir|polly)
- echo llvm
- ;;
- *)
- # Nothing to do
- ;;
- esac
- done
-}
-
-function exclude-linux() {
- projects=${@}
- for project in ${projects}; do
- case ${project} in
- cross-project-tests) ;; # tests failing
- openmp) ;; # https://github.com/google/llvm-premerge-checks/issues/410
- *)
- echo "${project}"
- ;;
- esac
- done
-}
-
-function exclude-windows() {
- projects=${@}
- for project in ${projects}; do
- case ${project} in
- cross-project-tests) ;; # tests failing
- compiler-rt) ;; # tests taking too long
- openmp) ;; # TODO: having trouble with the Perl installation
- libc) ;; # no Windows support
- lldb) ;; # custom environment requirements (https://github.com/llvm/llvm-project/pull/94208#issuecomment-2146256857)
- bolt) ;; # tests are not supported yet
- *)
- echo "${project}"
- ;;
- esac
- done
-}
-
-# Prints only projects that are both present in $modified_dirs and the passed
-# list.
-function keep-modified-projects() {
- projects=${@}
- for project in ${projects}; do
- if echo "$modified_dirs" | grep -q -E "^${project}$"; then
- echo "${project}"
- fi
- done
-}
-
-function check-targets() {
- # Do not use "check-all" here because if there is "check-all" plus a
- # project specific target like "check-clang", that project's tests
- # will be run twice.
- projects=${@}
- for project in ${projects}; do
- case ${project} in
- clang-tools-extra)
- echo "check-clang-tools"
- ;;
- compiler-rt)
- echo "check-compiler-rt"
- ;;
- cross-project-tests)
- echo "check-cross-project"
- ;;
- libcxx)
- echo "check-cxx"
- ;;
- libcxxabi)
- echo "check-cxxabi"
- ;;
- libunwind)
- echo "check-unwind"
- ;;
- lldb)
- echo "check-lldb"
- ;;
- pstl)
- # Currently we do not run pstl tests in CI.
- ;;
- libclc)
- # Currently there is no testing for libclc.
- ;;
- *)
- echo "check-${project}"
- ;;
- esac
- done
-}
+. ./.ci/compute-projects.sh
# Project specific pipelines.
diff --git a/.ci/generate_test_report.py b/.ci/generate_test_report.py
index c44936b19dab9..ff601a0cde106 100644
--- a/.ci/generate_test_report.py
+++ b/.ci/generate_test_report.py
@@ -5,6 +5,7 @@
# python3 -m unittest discover -p generate_test_report.py
import argparse
+import os
import subprocess
import unittest
from io import StringIO
@@ -267,6 +268,46 @@ def test_report_dont_list_failures(self):
),
)
+ def test_report_dont_list_failures_link_to_log(self):
+ self.assertEqual(
+ _generate_report(
+ "Foo",
+ [
+ junit_from_xml(
+ dedent(
+ """\
+
+
+
+
+
+
+
+ """
+ )
+ )
+ ],
+ list_failures=False,
+ buildkite_info={
+ "BUILDKITE_ORGANIZATION_SLUG": "organization_slug",
+ "BUILDKITE_PIPELINE_SLUG": "pipeline_slug",
+ "BUILDKITE_BUILD_NUMBER": "build_number",
+ "BUILDKITE_JOB_ID": "job_id",
+ },
+ ),
+ (
+ dedent(
+ """\
+ # Foo
+
+ * 1 test failed
+
+ Failed tests and their output was too large to report. [Download](https://buildkite.com/organizations/organization_slug/pipelines/pipeline_slug/builds/build_number/jobs/job_id/download.txt) the build's log file to see the details."""
+ ),
+ "error",
+ ),
+ )
+
def test_report_size_limit(self):
self.assertEqual(
_generate_report(
@@ -308,7 +349,13 @@ def test_report_size_limit(self):
# 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):
+def _generate_report(
+ title,
+ junit_objects,
+ size_limit=1024 * 1024,
+ list_failures=True,
+ buildkite_info=None,
+):
if not junit_objects:
return ("", "success")
@@ -354,11 +401,21 @@ def plural(num_tests):
report.append(f"* {tests_failed} {plural(tests_failed)} failed")
if not list_failures:
+ if buildkite_info is not None:
+ log_url = (
+ "https://buildkite.com/organizations/{BUILDKITE_ORGANIZATION_SLUG}/"
+ "pipelines/{BUILDKITE_PIPELINE_SLUG}/builds/{BUILDKITE_BUILD_NUMBER}/"
+ "jobs/{BUILDKITE_JOB_ID}/download.txt".format(**buildkite_info)
+ )
+ download_text = f"[Download]({log_url})"
+ else:
+ download_text = "Download"
+
report.extend(
[
"",
"Failed tests and their output was too large to report. "
- "Download the build's log file to see the details.",
+ f"{download_text} the build's log file to see the details.",
]
)
elif failures:
@@ -381,13 +438,23 @@ def plural(num_tests):
report = "\n".join(report)
if len(report.encode("utf-8")) > size_limit:
- return _generate_report(title, junit_objects, size_limit, list_failures=False)
+ return _generate_report(
+ title,
+ junit_objects,
+ size_limit,
+ list_failures=False,
+ buildkite_info=buildkite_info,
+ )
return report, style
-def generate_report(title, junit_files):
- return _generate_report(title, [JUnitXml.fromfile(p) for p in junit_files])
+def generate_report(title, junit_files, buildkite_info):
+ return _generate_report(
+ title,
+ [JUnitXml.fromfile(p) for p in junit_files],
+ buildkite_info=buildkite_info,
+ )
if __name__ == "__main__":
@@ -399,7 +466,18 @@ def generate_report(title, junit_files):
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)
+ # All of these are required to build a link to download the log file.
+ env_var_names = [
+ "BUILDKITE_ORGANIZATION_SLUG",
+ "BUILDKITE_PIPELINE_SLUG",
+ "BUILDKITE_BUILD_NUMBER",
+ "BUILDKITE_JOB_ID",
+ ]
+ buildkite_info = {k: v for k, v in os.environ.items() if k in env_var_names}
+ if len(buildkite_info) != len(env_var_names):
+ buildkite_info = None
+
+ report, style = generate_report(args.title, args.junit_files, buildkite_info)
if report:
p = subprocess.Popen(
diff --git a/.ci/metrics/metrics.py b/.ci/metrics/metrics.py
index deb36bc1689a0..55025e50d1081 100644
--- a/.ci/metrics/metrics.py
+++ b/.ci/metrics/metrics.py
@@ -12,7 +12,7 @@
"https://influx-prod-13-prod-us-east-0.grafana.net/api/v1/push/influx/write"
)
GITHUB_PROJECT = "llvm/llvm-project"
-WORKFLOWS_TO_TRACK = ["Check code formatting"]
+WORKFLOWS_TO_TRACK = ["Check code formatting", "LLVM Premerge Checks"]
SCRAPE_INTERVAL_SECONDS = 5 * 60
diff --git a/.ci/monolithic-linux.sh b/.ci/monolithic-linux.sh
index a4aeea7a16add..4bfebd5f75279 100755
--- a/.ci/monolithic-linux.sh
+++ b/.ci/monolithic-linux.sh
@@ -34,8 +34,11 @@ function at-exit {
# 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
+ if command -v buildkite-agent 2>&1 >/dev/null
+ then
+ python3 "${MONOREPO_ROOT}"/.ci/generate_test_report.py ":linux: Linux x64 Test Results" \
+ "linux-x64-test-results" "${BUILD_DIR}"/test-results.*.xml
+ fi
}
trap at-exit EXIT
diff --git a/.ci/monolithic-windows.sh b/.ci/monolithic-windows.sh
index 4ead122212f4f..25cdd2f419f47 100755
--- a/.ci/monolithic-windows.sh
+++ b/.ci/monolithic-windows.sh
@@ -33,8 +33,11 @@ function at-exit {
# 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
+ if command -v buildkite-agent 2>&1 >/dev/null
+ then
+ python "${MONOREPO_ROOT}"/.ci/generate_test_report.py ":windows: Windows x64 Test Results" \
+ "windows-x64-test-results" "${BUILD_DIR}"/test-results.*.xml
+ fi
}
trap at-exit EXIT
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 86be15b72fb64..30d9f6b883ceb 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -94,3 +94,9 @@ b6262880b34629e9d7a72b5a42f315a3c9ed8139
39c7dc7207e76e72da21cf4fedda21b5311bf62d
e80bc777749331e9519575f416c342f7626dd14d
7e5cd8f1b6c5263ed5e2cc03d60c8779a8d3e9f7
+
+# NFC: clang-format test_demangle.pass.cpp but keep test "lines"
+d33bf2e9df578ff7e44fd22504d6ad5a122b7ee6
+
+# [lldb][NFC] clang-format MainLoopPosix.cpp
+66bdbfbaa08fa3d8e64a7fe136a8fb717f5cdbb7
diff --git a/.github/new-prs-labeler.yml b/.github/new-prs-labeler.yml
index 54290f15419d9..0aa05cd027a47 100644
--- a/.github/new-prs-labeler.yml
+++ b/.github/new-prs-labeler.yml
@@ -747,6 +747,12 @@ backend:RISC-V:
- llvm/**/*riscv*
- llvm/**/*RISCV*
+backend:Xtensa:
+ - clang/**/*xtensa*
+ - clang/**/*Xtensa*
+ - llvm/**/*xtensa*
+ - llvm/**/*Xtensa*
+
lld:coff:
- lld/**/COFF/**
- lld/Common/**
diff --git a/.github/workflows/build-ci-container-windows.yml b/.github/workflows/build-ci-container-windows.yml
new file mode 100644
index 0000000000000..bba34066a97cd
--- /dev/null
+++ b/.github/workflows/build-ci-container-windows.yml
@@ -0,0 +1,75 @@
+name: Build Windows CI Container
+
+permissions:
+ contents: read
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - .github/workflows/build-ci-container-windows.yml
+ - '.github/workflows/containers/github-action-ci-windows/**'
+ pull_request:
+ branches:
+ - main
+ paths:
+ - .github/workflows/build-ci-container-windows.yml
+ - '.github/workflows/containers/github-action-ci-windows/**'
+
+jobs:
+ build-ci-container-windows:
+ if: github.repository_owner == 'llvm'
+ runs-on: windows-2019
+ outputs:
+ container-name: ${{ steps.vars.outputs.container-name }}
+ container-name-tag: ${{ steps.vars.outputs.container-name-tag }}
+ container-filename: ${{ steps.vars.outputs.container-filename }}
+ steps:
+ - name: Checkout LLVM
+ uses: actions/checkout@v4
+ with:
+ sparse-checkout: .github/workflows/containers/github-action-ci-windows
+ - name: Write Variables
+ id: vars
+ run: |
+ $tag = [int64](Get-Date -UFormat %s)
+ $container_name="ghcr.io/$env:GITHUB_REPOSITORY_OWNER/ci-windows-2019"
+ echo "container-name=${container_name}" >> $env:GITHUB_OUTPUT
+ echo "container-name-tag=${container_name}:${tag}" >> $env:GITHUB_OUTPUT
+ echo "container-filename=ci-windows-${tag}.tar" >> $env:GITHUB_OUTPUT
+ - name: Build Container
+ working-directory: .github/workflows/containers/github-action-ci-windows
+ run: |
+ docker build -t ${{ steps.vars.outputs.container-name-tag }} .
+ - name: Save container image
+ run: |
+ docker save ${{ steps.vars.outputs.container-name-tag }} > ${{ steps.vars.outputs.container-filename }}
+ - name: Upload container image
+ uses: actions/upload-artifact@v4
+ with:
+ name: container
+ path: ${{ steps.vars.outputs.container-filename }}
+ retention-days: 14
+
+ push-ci-container:
+ if: github.event_name == 'push'
+ needs:
+ - build-ci-container-windows
+ permissions:
+ packages: write
+ runs-on: windows-2019
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ steps:
+ - name: Download container
+ uses: actions/download-artifact@v4
+ with:
+ name: container
+ - name: Push Container
+ run: |
+ docker load -i ${{ needs.build-ci-container-windows.outputs.container-filename }}
+ docker tag ${{ needs.build-ci-container-windows.outputs.container-name-tag }} ${{ needs.build-ci-container-windows.outputs.container-name }}:latest
+ docker login -u ${{ github.actor }} -p $env:GITHUB_TOKEN ghcr.io
+ docker push ${{ needs.build-ci-container-windows.outputs.container-name-tag }}
+ docker push ${{ needs.build-ci-container-windows.outputs.container-name }}:latest
diff --git a/.github/workflows/build-ci-container.yml b/.github/workflows/build-ci-container.yml
index f037a91f6e5d0..50729e0173506 100644
--- a/.github/workflows/build-ci-container.yml
+++ b/.github/workflows/build-ci-container.yml
@@ -60,7 +60,7 @@ jobs:
- name: Test Container
run: |
for image in ${{ steps.vars.outputs.container-name-tag }} ${{ steps.vars.outputs.container-name }}; do
- podman run --rm -it $image /usr/bin/bash -x -c 'printf '\''#include \nint main(int argc, char **argv) { std::cout << "Hello\\n"; }'\'' | clang++ -x c++ - && ./a.out | grep Hello'
+ podman run --rm -it $image /usr/bin/bash -x -c 'cd $HOME && printf '\''#include \nint main(int argc, char **argv) { std::cout << "Hello\\n"; }'\'' | clang++ -x c++ - && ./a.out | grep Hello'
done
push-ci-container:
@@ -80,8 +80,8 @@ jobs:
- name: Push Container
run: |
- podman load -i ${{ needs.build-ci-container.outptus.container-filename }}
- podman tag ${{ steps.vars.outputs.container-name-tag }} ${{ steps.vars.outputs.container-name }}:latest
+ podman load -i ${{ needs.build-ci-container.outputs.container-filename }}
+ podman tag ${{ needs.build-ci-container.outputs.container-name-tag }} ${{ needs.build-ci-container.outputs.container-name }}:latest
podman login -u ${{ github.actor }} -p $GITHUB_TOKEN ghcr.io
podman push ${{ needs.build-ci-container.outputs.container-name-tag }}
podman push ${{ needs.build-ci-container.outputs.container-name }}:latest
diff --git a/.github/workflows/containers/github-action-ci-windows/Dockerfile b/.github/workflows/containers/github-action-ci-windows/Dockerfile
new file mode 100644
index 0000000000000..bc56e20935500
--- /dev/null
+++ b/.github/workflows/containers/github-action-ci-windows/Dockerfile
@@ -0,0 +1,118 @@
+# Agent image for LLVM org cluster.
+# .net 4.8 is required by chocolately package manager.
+FROM mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-ltsc2019
+
+# Restore the default Windows shell for correct batch processing.
+SHELL ["cmd", "/S", "/C"]
+
+# Download the Build Tools bootstrapper.
+ADD https://aka.ms/vs/16/release/vs_buildtools.exe /TEMP/vs_buildtools.exe
+
+RUN powershell -Command Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
+
+# Download channel for fixed install.
+ARG CHANNEL_URL=https://aka.ms/vs/16/release/channel
+ADD ${CHANNEL_URL} /TEMP/VisualStudio.chman
+
+# Install Build Tools with C++ workload.
+# - Documentation for docker installation
+# https://docs.microsoft.com/en-us/visualstudio/install/build-tools-container?view=vs-2019
+# - Documentation on workloads
+# https://docs.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-build-tools?view=vs-2019#c-build-tools
+# - Documentation on flags
+# https://docs.microsoft.com/en-us/visualstudio/install/use-command-line-parameters-to-install-visual-studio?view=vs-2019
+RUN /TEMP/vs_buildtools.exe --quiet --wait --norestart --nocache \
+ --channelUri C:\TEMP\VisualStudio.chman \
+ --installChannelUri C:\TEMP\VisualStudio.chman \
+ --installPath C:\BuildTools \
+ --add Microsoft.VisualStudio.Workload.VCTools \
+ --add Microsoft.VisualStudio.Component.VC.ATL \
+ --includeRecommended \
+ || IF "%ERRORLEVEL%"=="3010" EXIT 0
+
+# Register DIA dll (Debug Interface Access) so it can be used to symbolize
+# the stack traces. Register dll for 32 and 64 bit.
+# see https://developercommunity.visualstudio.com/content/problem/290674/msdia140dll-is-not-registered-on-vs2017-hosts.html
+
+RUN regsvr32 /S "C:\BuildTools\DIA SDK\bin\amd64\msdia140.dll" & \
+ regsvr32 /S "C:\BuildTools\DIA SDK\bin\msdia140.dll"
+
+# install tools as described in https://llvm.org/docs/GettingStartedVS.html
+# and a few more that were not documented...
+RUN choco install -y ninja git
+# Pin an older version of Python; the current Python 3.10 fails when
+# doing "pip install" for the other dependencies, as it fails to find libxml
+# while compiling some package.
+RUN choco install -y python3 --version 3.9.7
+
+# ActivePerl is currently not installable via Chocolatey, see
+# http://disq.us/p/2ipditb. Install StrawberryPerl instead. Unfortunately,
+# StrawberryPerl not only installs Perl, but also a redundant C/C++ compiler
+# toolchain, and a copy of pkg-config which can cause misdetections for other
+# built products, see
+# https://github.com/StrawberryPerl/Perl-Dist-Strawberry/issues/11 for further
+# details. Remove the redundant and unnecessary parts of the StrawberryPerl
+# install.
+RUN choco install -y strawberryperl && \
+ rmdir /q /s c:\strawberry\c && \
+ del /q c:\strawberry\perl\bin\pkg-config*
+
+# libcxx requires clang(-cl) to be available
+RUN choco install -y sccache llvm
+RUN pip install psutil
+
+RUN curl -LO https://github.com/mstorsjo/llvm-mingw/releases/download/20230320/llvm-mingw-20230320-ucrt-x86_64.zip && \
+ powershell Expand-Archive llvm-mingw-*-ucrt-x86_64.zip -DestinationPath . && \
+ del llvm-mingw-*-ucrt-x86_64.zip && \
+ ren llvm-mingw-20230320-ucrt-x86_64 llvm-mingw
+
+# configure Python encoding
+ENV PYTHONIOENCODING=UTF-8
+
+# update the path variable
+# C:\Program Files\Git\usr\bin contains a usable bash and other unix tools.
+# C:\llvm-mingw\bin contains Clang configured for mingw targets and
+# corresponding sysroots. Both the 'llvm' package (with Clang defaulting
+# to MSVC targets) and this directory contains executables named
+# 'clang.exe' - add this last to let the other one have precedence.
+# To use these compilers, use the triple prefixed form, e.g.
+# x86_64-w64-mingw32-clang.
+# C:\buildtools and SDK paths are ones that are set by c:\BuildTools\Common7\Tools\VsDevCmd.bat -arch=amd64 -host_arch=amd64
+RUN powershell -Command \
+ [System.Environment]::SetEnvironmentVariable('PATH', \
+ [System.Environment]::GetEnvironmentVariable('PATH', 'machine') + ';C:\Program Files\Git\usr\bin;C:\llvm-mingw\bin' \
+ + ';C:\BuildTools\Common7\IDE\' \
+ + ';C:\BuildTools\Common7\IDE\CommonExt ensions\Microsoft\TeamFoundation\Team Explorer' \
+ + ';C:\BuildTools\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin' \
+ + ';C:\BuildTools\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja' \
+ + ';C:\BuildTools\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer' \
+ + ';C:\BuildTools\Common7\IDE\CommonExtensions\Microsoft\TestWindow' \
+ + ';C:\BuildTools\Common7\IDE\VC\VCPackages' \
+ + ';C:\BuildTools\Common7\Tools\' \
+ + ';C:\BuildTools\Common7\Tools\devinit' \
+ + ';C:\BuildTools\MSBuild\Current\Bin' \
+ + ';C:\BuildTools\MSBuild\Current\bin\Roslyn' \
+ + ';C:\BuildTools\VC\Tools\MSVC\14.29.30133\bin\HostX64\x64' \
+ + ';C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64\' \
+ + ';C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64' \
+ + ';C:\Program Files (x86)\Windows Kits\10\bin\x64' \
+ + ';C:\Windows\Microsoft.NET\Framework64\v4.0.30319' \
+ ,'machine')
+
+# support long file names during git checkout
+RUN git config --system core.longpaths true & \
+ git config --global core.autocrlf false
+
+# handle for debugging of files beeing locked by some processes.
+RUN choco install -y handle
+
+RUN pip3 install pywin32 buildbot-worker==2.8.4
+
+ARG RUNNER_VERSION=2.319.1
+ENV RUNNER_VERSION=$RUNNER_VERSION
+
+RUN powershell -Command \
+ Invoke-WebRequest -Uri https://github.com/actions/runner/releases/download/v${env:RUNNER_VERSION}/actions-runner-win-x64-${env:RUNNER_VERSION}.zip -OutFile actions-runner-win.zip ; \
+ Add-Type -AssemblyName System.IO.Compression.FileSystem ; \
+ [System.IO.Compression.ZipFile]::ExtractToDirectory('actions-runner-win.zip', $PWD) ;\
+ rm actions-runner-win.zip
diff --git a/.github/workflows/containers/github-action-ci/Dockerfile b/.github/workflows/containers/github-action-ci/Dockerfile
index 32a809ee268ea..58355d261c43c 100644
--- a/.github/workflows/containers/github-action-ci/Dockerfile
+++ b/.github/workflows/containers/github-action-ci/Dockerfile
@@ -2,7 +2,7 @@ FROM docker.io/library/ubuntu:22.04 as base
ENV LLVM_SYSROOT=/opt/llvm
FROM base as stage1-toolchain
-ENV LLVM_VERSION=19.1.2
+ENV LLVM_VERSION=19.1.5
RUN apt-get update && \
apt-get install -y \
@@ -41,13 +41,13 @@ RUN ninja -C ./build stage2-clang-bolt stage2-install-distribution && ninja -C .
FROM base
COPY --from=stage1-toolchain $LLVM_SYSROOT $LLVM_SYSROOT
-
+
# Need to install curl for hendrikmuhs/ccache-action
# Need nodejs for some of the GitHub actions.
# Need perl-modules for clang analyzer tests.
# Need git for SPIRV-Tools tests.
RUN apt-get update && \
- apt-get install -y \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y \
binutils \
cmake \
curl \
@@ -56,7 +56,22 @@ RUN apt-get update && \
ninja-build \
nodejs \
perl-modules \
- python3-psutil
+ python3-psutil \
+
+ # These are needed by the premerge pipeline. Pip is used to install
+ # dependent python packages and ccache is used for build caching. File and
+ # tzdata are used for tests.
+ python3-pip \
+ ccache \
+ file \
+ tzdata
ENV LLVM_SYSROOT=$LLVM_SYSROOT
ENV PATH=${LLVM_SYSROOT}/bin:${PATH}
+
+# Create a new user to avoid test failures related to a lack of expected
+# permissions issues in some tests. Set the user id to 1001 as that is the
+# user id that Github Actions uses to perform the checkout action.
+RUN useradd gha -u 1001 -m -s /bin/bash
+USER gha
+
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 3b9fca21d63cf..2845affc66100 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -94,6 +94,8 @@ jobs:
flang:
- 'flang/docs/**'
- 'flang/include/flang/Optimizer/Dialect/FIROps.td'
+ workflow:
+ - '.github/workflows/docs.yml'
- name: Fetch LLVM sources (PR)
if: ${{ github.event_name == 'pull_request' }}
uses: actions/checkout@v4
@@ -104,9 +106,9 @@ jobs:
with:
python-version: '3.11'
cache: 'pip'
- cache-dependency-path: 'llvm/docs/requirements.txt'
+ cache-dependency-path: 'llvm/docs/requirements-hashed.txt'
- name: Install python dependencies
- run: pip install -r llvm/docs/requirements.txt
+ run: pip install -r llvm/docs/requirements-hashed.txt
- name: Install system dependencies
run: |
sudo apt-get update
@@ -115,77 +117,99 @@ jobs:
- name: Setup output folder
run: mkdir built-docs
- name: Build LLVM docs
- if: steps.docs-changed-subprojects.outputs.llvm_any_changed == 'true'
+ if: |
+ steps.docs-changed-subprojects.outputs.llvm_any_changed == 'true' ||
+ steps.docs-changed-subprojects.outputs.workflow_any_changed == 'true'
run: |
cmake -B llvm-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C llvm-build docs-llvm-html docs-llvm-man
mkdir built-docs/llvm
cp -r llvm-build/docs/* built-docs/llvm/
- name: Build Clang docs
- if: steps.docs-changed-subprojects.outputs.clang_any_changed == 'true'
+ if: |
+ steps.docs-changed-subprojects.outputs.clang_any_changed == 'true' ||
+ steps.docs-changed-subprojects.outputs.workflow_any_changed == 'true'
run: |
cmake -B clang-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C clang-build docs-clang-html docs-clang-man
mkdir built-docs/clang
cp -r clang-build/docs/* built-docs/clang/
- name: Build clang-tools-extra docs
- if: steps.docs-changed-subprojects.outputs.clang-tools-extra_any_changed == 'true'
+ if: |
+ steps.docs-changed-subprojects.outputs.clang-tools-extra_any_changed == 'true' ||
+ steps.docs-changed-subprojects.outputs.workflow_any_changed == 'true'
run: |
cmake -B clang-tools-extra-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C clang-tools-extra-build docs-clang-tools-html docs-clang-tools-man
mkdir built-docs/clang-tools-extra
cp -r clang-tools-extra-build/docs/* built-docs/clang-tools-extra/
- name: Build LLDB docs
- if: steps.docs-changed-subprojects.outputs.lldb_any_changed == 'true'
+ if: |
+ steps.docs-changed-subprojects.outputs.lldb_any_changed == 'true' ||
+ steps.docs-changed-subprojects.outputs.workflow_any_changed == 'true'
run: |
cmake -B lldb-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;lldb" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C lldb-build docs-lldb-html docs-lldb-man
mkdir built-docs/lldb
cp -r lldb-build/docs/* built-docs/lldb/
- name: Build libunwind docs
- if: steps.docs-changed-subprojects.outputs.libunwind_any_changed == 'true'
+ if: |
+ steps.docs-changed-subprojects.outputs.libunwind_any_changed == 'true' ||
+ steps.docs-changed-subprojects.outputs.workflow_any_changed == 'true'
run: |
cmake -B libunwind-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_RUNTIMES="libunwind" -DLLVM_ENABLE_SPHINX=ON ./runtimes
TZ=UTC ninja -C libunwind-build docs-libunwind-html
mkdir built-docs/libunwind
cp -r libunwind-build/libunwind/docs/* built-docs/libunwind
- name: Build libcxx docs
- if: steps.docs-changed-subprojects.outputs.libcxx_any_changed == 'true'
+ if: |
+ steps.docs-changed-subprojects.outputs.libcxx_any_changed == 'true' ||
+ steps.docs-changed-subprojects.outputs.workflow_any_changed == 'true'
run: |
cmake -B libcxx-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_RUNTIMES="libcxxabi;libcxx;libunwind" -DLLVM_ENABLE_SPHINX=ON ./runtimes
TZ=UTC ninja -C libcxx-build docs-libcxx-html
mkdir built-docs/libcxx
cp -r libcxx-build/libcxx/docs/* built-docs/libcxx/
- name: Build libc docs
- if: steps.docs-changed-subprojects.outputs.libc_any_changed == 'true'
+ if: |
+ steps.docs-changed-subprojects.outputs.libc_any_changed == 'true' ||
+ steps.docs-changed-subprojects.outputs.workflow_any_changed == 'true'
run: |
cmake -B libc-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_RUNTIMES="libc" -DLLVM_ENABLE_SPHINX=ON ./runtimes
TZ=UTC ninja -C libc-build docs-libc-html
mkdir built-docs/libc
cp -r libc-build/libc/docs/* built-docs/libc/
- name: Build LLD docs
- if: steps.docs-changed-subprojects.outputs.lld_any_changed == 'true'
+ if: |
+ steps.docs-changed-subprojects.outputs.lld_any_changed == 'true' ||
+ steps.docs-changed-subprojects.outputs.workflow_any_changed == 'true'
run: |
cmake -B lld-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="lld" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C lld-build docs-lld-html
mkdir built-docs/lld
cp -r lld-build/docs/* built-docs/lld/
- name: Build OpenMP docs
- if: steps.docs-changed-subprojects.outputs.openmp_any_changed == 'true'
+ if: |
+ steps.docs-changed-subprojects.outputs.openmp_any_changed == 'true' ||
+ steps.docs-changed-subprojects.outputs.workflow_any_changed == 'true'
run: |
cmake -B openmp-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;openmp" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C openmp-build docs-openmp-html
mkdir built-docs/openmp
cp -r openmp-build/docs/* built-docs/openmp/
- name: Build Polly docs
- if: steps.docs-changed-subprojects.outputs.polly_any_changed == 'true'
+ if: |
+ steps.docs-changed-subprojects.outputs.polly_any_changed == 'true' ||
+ steps.docs-changed-subprojects.outputs.workflow_any_changed == 'true'
run: |
cmake -B polly-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="polly" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C polly-build docs-polly-html docs-polly-man
mkdir built-docs/polly
cp -r polly-build/docs/* built-docs/polly/
- name: Build Flang docs
- if: steps.docs-changed-subprojects.outputs.flang_any_changed == 'true'
+ if: |
+ steps.docs-changed-subprojects.outputs.flang_any_changed == 'true' ||
+ steps.docs-changed-subprojects.outputs.workflow_any_changed == 'true'
run: |
cmake -B flang-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;mlir;flang" -DLLVM_ENABLE_SPHINX=ON ./llvm
TZ=UTC ninja -C flang-build docs-flang-html
diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml
index cba8afbb54f0f..a28bf4d5daf6d 100644
--- a/.github/workflows/libcxx-build-and-test.yaml
+++ b/.github/workflows/libcxx-build-and-test.yaml
@@ -37,12 +37,13 @@ jobs:
stage1:
if: github.repository_owner == 'llvm'
runs-on: libcxx-self-hosted-linux
- container: ghcr.io/llvm/libcxx-linux-builder:0fd6f684b9c84c32d6cbfd9742402e788b2879f1
+ container: ghcr.io/llvm/libcxx-linux-builder:d8a0709b1090350a7fe3604d8ab78c7d62f10698
continue-on-error: false
strategy:
fail-fast: false
matrix:
config: [
+ 'frozen-cxx03-headers',
'generic-cxx03',
'generic-cxx26',
'generic-modules'
@@ -74,7 +75,7 @@ jobs:
stage2:
if: github.repository_owner == 'llvm'
runs-on: libcxx-self-hosted-linux
- container: ghcr.io/llvm/libcxx-linux-builder:0fd6f684b9c84c32d6cbfd9742402e788b2879f1
+ container: ghcr.io/llvm/libcxx-linux-builder:d8a0709b1090350a7fe3604d8ab78c7d62f10698
needs: [ stage1 ]
continue-on-error: false
strategy:
@@ -162,7 +163,7 @@ jobs:
- config: 'generic-msan'
machine: libcxx-self-hosted-linux
runs-on: ${{ matrix.machine }}
- container: ghcr.io/llvm/libcxx-linux-builder:0fd6f684b9c84c32d6cbfd9742402e788b2879f1
+ container: ghcr.io/llvm/libcxx-linux-builder:d8a0709b1090350a7fe3604d8ab78c7d62f10698
steps:
- uses: actions/checkout@v4
- name: ${{ matrix.config }}
diff --git a/.github/workflows/premerge.yaml b/.github/workflows/premerge.yaml
new file mode 100644
index 0000000000000..7a9762812cc18
--- /dev/null
+++ b/.github/workflows/premerge.yaml
@@ -0,0 +1,69 @@
+name: LLVM Premerge Checks
+
+permissions:
+ contents: read
+
+on:
+ pull_request:
+ paths:
+ - .github/workflows/premerge.yaml
+ push:
+ branches:
+ - 'main'
+
+jobs:
+ premerge-checks-linux:
+ if: github.repository_owner == 'llvm'
+ runs-on: llvm-premerge-linux-runners
+ concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
+ cancel-in-progress: true
+ container:
+ image: ghcr.io/llvm/ci-ubuntu-22.04:latest
+ defaults:
+ run:
+ shell: bash
+ steps:
+ - name: Checkout LLVM
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 2
+ - name: Setup ccache
+ uses: hendrikmuhs/ccache-action@v1.2.14
+ - name: Build and Test
+ run: |
+ git config --global --add safe.directory '*'
+
+ modified_files=$(git diff --name-only HEAD~1...HEAD)
+ modified_dirs=$(echo "$modified_files" | cut -d'/' -f1 | sort -u)
+
+ echo $modified_files
+ echo $modified_dirs
+
+ . ./.ci/compute-projects.sh
+
+ all_projects="bolt clang clang-tools-extra compiler-rt cross-project-tests flang libc libclc lld lldb llvm mlir openmp polly pstl"
+ modified_projects="$(keep-modified-projects ${all_projects})"
+
+ linux_projects_to_test=$(exclude-linux $(compute-projects-to-test 0 ${modified_projects}))
+ linux_check_targets=$(check-targets ${linux_projects_to_test} | sort | uniq)
+ linux_projects=$(add-dependencies ${linux_projects_to_test} | sort | uniq)
+
+ linux_runtimes_to_test=$(compute-runtimes-to-test ${linux_projects_to_test})
+ linux_runtime_check_targets=$(check-targets ${linux_runtimes_to_test} | sort | uniq)
+ linux_runtimes=$(echo ${linux_runtimes_to_test} | sort | uniq)
+
+ if [[ "${linux_projects}" == "" ]]; then
+ echo "No projects to build"
+ exit 0
+ fi
+
+ echo "Building projects: ${linux_projects}"
+ echo "Running project checks targets: ${linux_check_targets}"
+ echo "Building runtimes: ${linux_runtimes}"
+ echo "Running runtimes checks targets: ${linux_runtime_check_targets}"
+
+ export CC=/opt/llvm/bin/clang
+ export CXX=/opt/llvm/bin/clang++
+
+ ./.ci/monolithic-linux.sh "$(echo ${linux_projects} | tr ' ' ';')" "$(echo ${linux_check_targets})" "$(echo ${linux_runtimes} | tr ' ' ';')" "$(echo ${linux_runtime_check_targets})"
diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
new file mode 100644
index 0000000000000..f91b77d046de8
--- /dev/null
+++ b/bolt/docs/BinaryAnalysis.md
@@ -0,0 +1,20 @@
+# BOLT-based binary analysis
+
+As part of post-link-time optimizing, BOLT needs to perform a range of analyses
+on binaries such as recontructing control flow graphs, and more.
+
+The `llvm-bolt-binary-analysis` tool enables running requested binary analyses
+on binaries, and generating reports. It does this by building on top of the
+analyses implemented in the BOLT libraries.
+
+## Which binary analyses are implemented?
+
+At the moment, no binary analyses are implemented.
+
+The goal is to make it easy using a plug-in framework to add your own analyses.
+
+## How to add your own binary analysis
+
+_TODO: this section needs to be written. Ideally, we should have a simple
+"example" or "template" analysis that can be the starting point for implementing
+custom analyses_
diff --git a/bolt/docs/CommandLineArgumentReference.md b/bolt/docs/CommandLineArgumentReference.md
index 6d3b797da3787..91918d614a90f 100644
--- a/bolt/docs/CommandLineArgumentReference.md
+++ b/bolt/docs/CommandLineArgumentReference.md
@@ -498,9 +498,12 @@
Automatically put hot code on 2MB page(s) (hugify) at runtime. No manual call
to hugify is needed in the binary (which is what --hot-text relies on).
-- `--icf`
+- `--icf=`
Fold functions with identical code
+ - `all`: Enable identical code folding
+ - `none`: Disable identical code folding (default)
+ - `safe`: Enable safe identical code folding
- `--icp`
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index 7560908c250c3..e8b2757f7db21 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -428,6 +428,9 @@ class BinaryFunction {
/// Function order for streaming into the destination binary.
uint32_t Index{-1U};
+ /// Function is referenced by a non-control flow instruction.
+ bool HasAddressTaken{false};
+
/// Get basic block index assuming it belongs to this function.
unsigned getIndex(const BinaryBasicBlock *BB) const {
assert(BB->getIndex() < BasicBlocks.size());
@@ -822,6 +825,14 @@ class BinaryFunction {
return nullptr;
}
+ /// Return true if function is referenced in a non-control flow instruction.
+ /// This flag is set when the code and relocation analyses are being
+ /// performed, which occurs when safe ICF (Identical Code Folding) is enabled.
+ bool hasAddressTaken() const { return HasAddressTaken; }
+
+ /// Set whether function is referenced in a non-control flow instruction.
+ void setHasAddressTaken(bool AddressTaken) { HasAddressTaken = AddressTaken; }
+
/// Returns the raw binary encoding of this function.
ErrorOr> getData() const;
@@ -2135,6 +2146,9 @@ class BinaryFunction {
// adjustments.
void handleAArch64IndirectCall(MCInst &Instruction, const uint64_t Offset);
+ /// Analyze instruction to identify a function reference.
+ void analyzeInstructionForFuncReference(const MCInst &Inst);
+
/// Scan function for references to other functions. In relocation mode,
/// add relocations for external references. In non-relocation mode, detect
/// and mark new entry points.
diff --git a/bolt/include/bolt/Core/DIEBuilder.h b/bolt/include/bolt/Core/DIEBuilder.h
index d1acba0f26c78..bd22c536c56fc 100644
--- a/bolt/include/bolt/Core/DIEBuilder.h
+++ b/bolt/include/bolt/Core/DIEBuilder.h
@@ -162,7 +162,7 @@ class DIEBuilder {
/// Clone an attribute in reference format.
void cloneDieOffsetReferenceAttribute(
- DIE &Die, const DWARFUnit &U, const DWARFDie &InputDIE,
+ DIE &Die, DWARFUnit &U, const DWARFDie &InputDIE,
const DWARFAbbreviationDeclaration::AttributeSpec AttrSpec, uint64_t Ref);
/// Clone an attribute in block format.
diff --git a/bolt/include/bolt/Core/DebugNames.h b/bolt/include/bolt/Core/DebugNames.h
index 0e61a0e4f9d9f..cc4e13a481b2d 100644
--- a/bolt/include/bolt/Core/DebugNames.h
+++ b/bolt/include/bolt/Core/DebugNames.h
@@ -72,8 +72,8 @@ class DWARF5AcceleratorTable {
return std::move(FullTableBuffer);
}
/// Adds a DIE that is referenced across CUs.
- void addCrossCUDie(const DIE *Die) {
- CrossCUDies.insert({Die->getOffset(), Die});
+ void addCrossCUDie(DWARFUnit *Unit, const DIE *Die) {
+ CrossCUDies.insert({Die->getOffset(), {Unit, Die}});
}
/// Returns true if the DIE can generate an entry for a cross cu reference.
/// This only checks TAGs of a DIE because when this is invoked DIE might not
@@ -145,7 +145,7 @@ class DWARF5AcceleratorTable {
llvm::DenseMap CUOffsetsToPatch;
// Contains a map of Entry ID to Entry relative offset.
llvm::DenseMap EntryRelativeOffsets;
- llvm::DenseMap CrossCUDies;
+ llvm::DenseMap> CrossCUDies;
/// Adds Unit to either CUList, LocalTUList or ForeignTUList.
/// Input Unit being processed, and DWO ID if Unit is being processed comes
/// from a DWO section.
@@ -191,6 +191,29 @@ class DWARF5AcceleratorTable {
void emitData();
/// Emit augmentation string.
void emitAugmentationString() const;
+ /// Creates a new entry for a given DIE.
+ std::optional
+ addEntry(DWARFUnit &DU, const DIE &CurrDie,
+ const std::optional &DWOID,
+ const std::optional &Parent,
+ const std::optional &Name,
+ const uint32_t NumberParentsInChain);
+ /// Returns UnitID for a given DWARFUnit.
+ uint32_t getUnitID(const DWARFUnit &Unit,
+ const std::optional &DWOID, bool &IsTU);
+ std::optional getName(DWARFUnit &DU,
+ const std::optional &DWOID,
+ const std::string &NameToUse,
+ DIEValue ValName);
+ /// Processes a DIE with references to other DIEs for DW_AT_name and
+ /// DW_AT_linkage_name resolution.
+ /// If DW_AT_name exists method creates a new entry for this DIE and returns
+ /// it.
+ std::optional processReferencedDie(
+ DWARFUnit &Unit, const DIE &Die, const std::optional &DWOID,
+ const std::optional &Parent,
+ const std::string &NameToUse, const uint32_t NumberParentsInChain,
+ const dwarf::Attribute &Attr);
};
} // namespace bolt
} // namespace llvm
diff --git a/bolt/include/bolt/Passes/ADRRelaxationPass.h b/bolt/include/bolt/Passes/ADRRelaxationPass.h
index 1d35a335c0250..b9f92dec7f03b 100644
--- a/bolt/include/bolt/Passes/ADRRelaxationPass.h
+++ b/bolt/include/bolt/Passes/ADRRelaxationPass.h
@@ -25,7 +25,8 @@ namespace bolt {
class ADRRelaxationPass : public BinaryFunctionPass {
public:
- explicit ADRRelaxationPass() : BinaryFunctionPass(false) {}
+ explicit ADRRelaxationPass(const cl::opt &PrintPass)
+ : BinaryFunctionPass(PrintPass) {}
const char *getName() const override { return "adr-relaxation"; }
diff --git a/bolt/include/bolt/Passes/IdenticalCodeFolding.h b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
index b4206fa360744..f59e75c618605 100644
--- a/bolt/include/bolt/Passes/IdenticalCodeFolding.h
+++ b/bolt/include/bolt/Passes/IdenticalCodeFolding.h
@@ -11,6 +11,7 @@
#include "bolt/Core/BinaryFunction.h"
#include "bolt/Passes/BinaryPasses.h"
+#include "llvm/ADT/SparseBitVector.h"
namespace llvm {
namespace bolt {
@@ -20,22 +21,72 @@ namespace bolt {
///
class IdenticalCodeFolding : public BinaryFunctionPass {
protected:
- bool shouldOptimize(const BinaryFunction &BF) const override {
- if (BF.hasUnknownControlFlow())
- return false;
- if (BF.isFolded())
- return false;
- if (BF.hasSDTMarker())
- return false;
- return BinaryFunctionPass::shouldOptimize(BF);
- }
+ /// Return true if the function is safe to fold.
+ bool shouldOptimize(const BinaryFunction &BF) const override;
public:
+ enum class ICFLevel {
+ None, /// No ICF. (Default)
+ Safe, /// Safe ICF.
+ All, /// Aggressive ICF.
+ };
explicit IdenticalCodeFolding(const cl::opt &PrintPass)
: BinaryFunctionPass(PrintPass) {}
const char *getName() const override { return "identical-code-folding"; }
Error runOnFunctions(BinaryContext &BC) override;
+
+private:
+ /// Bit vector of memory addresses of vtables.
+ llvm::SparseBitVector<> VTableBitVector;
+
+ /// Return true if the memory address is in a vtable.
+ bool isAddressInVTable(uint64_t Address) const {
+ return VTableBitVector.test(Address / 8);
+ }
+
+ /// Mark memory address of a vtable as used.
+ void setAddressUsedInVTable(uint64_t Address) {
+ VTableBitVector.set(Address / 8);
+ }
+
+ /// Scan symbol table and mark memory addresses of
+ /// vtables.
+ void initVTableReferences(const BinaryContext &BC);
+
+ /// Analyze code section and relocations and mark functions that are not
+ /// safe to fold.
+ void markFunctionsUnsafeToFold(BinaryContext &BC);
+
+ /// Process static and dynamic relocations in the data sections to identify
+ /// function references, and mark them as unsafe to fold. It filters out
+ /// symbol references that are in vtables.
+ void analyzeDataRelocations(BinaryContext &BC);
+
+ /// Process functions that have been disassembled and mark functions that are
+ /// used in non-control flow instructions as unsafe to fold.
+ void analyzeFunctions(BinaryContext &BC);
+};
+
+class DeprecatedICFNumericOptionParser
+ : public cl::parser {
+public:
+ explicit DeprecatedICFNumericOptionParser(cl::Option &O)
+ : cl::parser(O) {}
+
+ bool parse(cl::Option &O, StringRef ArgName, StringRef Arg,
+ IdenticalCodeFolding::ICFLevel &Value) {
+ if (Arg == "0" || Arg == "1") {
+ Value = (Arg == "0") ? IdenticalCodeFolding::ICFLevel::None
+ : IdenticalCodeFolding::ICFLevel::All;
+ errs() << formatv("BOLT-WARNING: specifying numeric value \"{0}\" "
+ "for option -{1} is deprecated\n",
+ Arg, ArgName);
+ return false;
+ }
+ return cl::parser::parse(O, ArgName, Arg,
+ Value);
+ }
};
} // namespace bolt
diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h
index 73d2857f946cc..42094cb732107 100644
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -164,6 +164,9 @@ class RewriteInstance {
void preregisterSections();
+ /// run analyses requested in binary analysis mode.
+ void runBinaryAnalyses();
+
/// Run optimizations that operate at the binary, or post-linker, level.
void runOptimizationPasses();
diff --git a/bolt/include/bolt/Utils/CommandLineOpts.h b/bolt/include/bolt/Utils/CommandLineOpts.h
index 04bf7db5de952..111eb650c3746 100644
--- a/bolt/include/bolt/Utils/CommandLineOpts.h
+++ b/bolt/include/bolt/Utils/CommandLineOpts.h
@@ -18,6 +18,7 @@
namespace opts {
extern bool HeatmapMode;
+extern bool BinaryAnalysisMode;
extern llvm::cl::OptionCategory BoltCategory;
extern llvm::cl::OptionCategory BoltDiffCategory;
@@ -27,6 +28,7 @@ extern llvm::cl::OptionCategory BoltOutputCategory;
extern llvm::cl::OptionCategory AggregatorCategory;
extern llvm::cl::OptionCategory BoltInstrCategory;
extern llvm::cl::OptionCategory HeatmapCategory;
+extern llvm::cl::OptionCategory BinaryAnalysisCategory;
extern llvm::cl::opt AlignText;
extern llvm::cl::opt AlignFunctions;
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index ac96b836ed579..f88e34b8e8962 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -1961,7 +1961,15 @@ void BinaryContext::printInstruction(raw_ostream &OS, const MCInst &Instruction,
OS << "\tjit\t" << MIB->getTargetSymbol(Instruction)->getName()
<< " # ID: " << DynamicID;
} else {
- InstPrinter->printInst(&Instruction, 0, "", *STI, OS);
+ // If there are annotations on the instruction, the MCInstPrinter will fail
+ // to print the preferred alias as it only does so when the number of
+ // operands is as expected. See
+ // https://github.com/llvm/llvm-project/blob/782f1a0d895646c364a53f9dcdd6d4ec1f3e5ea0/llvm/lib/MC/MCInstPrinter.cpp#L142
+ // Therefore, create a temporary copy of the Inst from which the annotations
+ // are removed, and print that Inst.
+ MCInst InstNoAnnot = Instruction;
+ MIB->stripAnnotations(InstNoAnnot);
+ InstPrinter->printInst(&InstNoAnnot, 0, "", *STI, OS);
}
if (MIB->isCall(Instruction)) {
if (MIB->isTailCall(Instruction))
diff --git a/bolt/lib/Core/BinaryEmitter.cpp b/bolt/lib/Core/BinaryEmitter.cpp
index f34a94c577921..5019cf31beee3 100644
--- a/bolt/lib/Core/BinaryEmitter.cpp
+++ b/bolt/lib/Core/BinaryEmitter.cpp
@@ -46,13 +46,17 @@ BreakFunctionNames("break-funcs",
cl::Hidden,
cl::cat(BoltCategory));
-static cl::list
-FunctionPadSpec("pad-funcs",
- cl::CommaSeparated,
- cl::desc("list of functions to pad with amount of bytes"),
- cl::value_desc("func1:pad1,func2:pad2,func3:pad3,..."),
- cl::Hidden,
- cl::cat(BoltCategory));
+cl::list
+ FunctionPadSpec("pad-funcs", cl::CommaSeparated,
+ cl::desc("list of functions to pad with amount of bytes"),
+ cl::value_desc("func1:pad1,func2:pad2,func3:pad3,..."),
+ cl::Hidden, cl::cat(BoltCategory));
+
+cl::list FunctionPadBeforeSpec(
+ "pad-funcs-before", cl::CommaSeparated,
+ cl::desc("list of functions to pad with amount of bytes"),
+ cl::value_desc("func1:pad1,func2:pad2,func3:pad3,..."), cl::Hidden,
+ cl::cat(BoltCategory));
static cl::opt MarkFuncs(
"mark-funcs",
@@ -70,11 +74,12 @@ X86AlignBranchBoundaryHotOnly("x86-align-branch-boundary-hot-only",
cl::init(true),
cl::cat(BoltOptCategory));
-size_t padFunction(const BinaryFunction &Function) {
+size_t padFunction(const cl::list &Spec,
+ const BinaryFunction &Function) {
static std::map FunctionPadding;
- if (FunctionPadding.empty() && !FunctionPadSpec.empty()) {
- for (std::string &Spec : FunctionPadSpec) {
+ if (FunctionPadding.empty() && !Spec.empty()) {
+ for (const std::string &Spec : Spec) {
size_t N = Spec.find(':');
if (N == std::string::npos)
continue;
@@ -319,6 +324,32 @@ bool BinaryEmitter::emitFunction(BinaryFunction &Function,
Streamer.emitCodeAlignment(Function.getAlign(), &*BC.STI);
}
+ if (size_t Padding =
+ opts::padFunction(opts::FunctionPadBeforeSpec, Function)) {
+ // Handle padFuncsBefore after the above alignment logic but before
+ // symbol addresses are decided.
+ if (!BC.HasRelocations) {
+ BC.errs() << "BOLT-ERROR: -pad-before-funcs is not supported in "
+ << "non-relocation mode\n";
+ exit(1);
+ }
+
+ // Preserve Function.getMinAlign().
+ if (!isAligned(Function.getMinAlign(), Padding)) {
+ BC.errs() << "BOLT-ERROR: user-requested " << Padding
+ << " padding bytes before function " << Function
+ << " is not a multiple of the minimum function alignment ("
+ << Function.getMinAlign().value() << ").\n";
+ exit(1);
+ }
+
+ LLVM_DEBUG(dbgs() << "BOLT-DEBUG: padding before function " << Function
+ << " with " << Padding << " bytes\n");
+
+ // Since the padding is not executed, it can be null bytes.
+ Streamer.emitFill(Padding, 0);
+ }
+
MCContext &Context = Streamer.getContext();
const MCAsmInfo *MAI = Context.getAsmInfo();
@@ -373,7 +404,7 @@ bool BinaryEmitter::emitFunction(BinaryFunction &Function,
emitFunctionBody(Function, FF, /*EmitCodeOnly=*/false);
// Emit padding if requested.
- if (size_t Padding = opts::padFunction(Function)) {
+ if (size_t Padding = opts::padFunction(opts::FunctionPadSpec, Function)) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: padding function " << Function << " with "
<< Padding << " bytes\n");
Streamer.emitFill(Padding, MAI->getTextAlignFillValue());
@@ -730,30 +761,16 @@ void BinaryEmitter::emitJumpTables(const BinaryFunction &BF) {
continue;
if (opts::PrintJumpTables)
JT.print(BC.outs());
- if (opts::JumpTables == JTS_BASIC && BC.HasRelocations) {
+ if (opts::JumpTables == JTS_BASIC) {
JT.updateOriginal();
} else {
MCSection *HotSection, *ColdSection;
- if (opts::JumpTables == JTS_BASIC) {
- // In non-relocation mode we have to emit jump tables in local sections.
- // This way we only overwrite them when the corresponding function is
- // overwritten.
- std::string Name = ".local." + JT.Labels[0]->getName().str();
- std::replace(Name.begin(), Name.end(), '/', '.');
- BinarySection &Section =
- BC.registerOrUpdateSection(Name, ELF::SHT_PROGBITS, ELF::SHF_ALLOC);
- Section.setAnonymous(true);
- JT.setOutputSection(Section);
- HotSection = BC.getDataSection(Name);
- ColdSection = HotSection;
+ if (BF.isSimple()) {
+ HotSection = ReadOnlySection;
+ ColdSection = ReadOnlyColdSection;
} else {
- if (BF.isSimple()) {
- HotSection = ReadOnlySection;
- ColdSection = ReadOnlyColdSection;
- } else {
- HotSection = BF.hasProfile() ? ReadOnlySection : ReadOnlyColdSection;
- ColdSection = HotSection;
- }
+ HotSection = BF.hasProfile() ? ReadOnlySection : ReadOnlyColdSection;
+ ColdSection = HotSection;
}
emitJumpTable(JT, HotSection, ColdSection);
}
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index a9ccaea3c4385..1c5cd62a095b2 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -1504,6 +1504,20 @@ MCSymbol *BinaryFunction::registerBranch(uint64_t Src, uint64_t Dst) {
return Target;
}
+void BinaryFunction::analyzeInstructionForFuncReference(const MCInst &Inst) {
+ for (const MCOperand &Op : MCPlus::primeOperands(Inst)) {
+ if (!Op.isExpr())
+ continue;
+ const MCExpr &Expr = *Op.getExpr();
+ if (Expr.getKind() != MCExpr::SymbolRef)
+ continue;
+ const MCSymbol &Symbol = cast(Expr).getSymbol();
+ // Set HasAddressTaken for a function regardless of the ICF level.
+ if (BinaryFunction *BF = BC.getFunctionForSymbol(&Symbol))
+ BF->setHasAddressTaken(true);
+ }
+}
+
bool BinaryFunction::scanExternalRefs() {
bool Success = true;
bool DisassemblyFailed = false;
@@ -1624,6 +1638,8 @@ bool BinaryFunction::scanExternalRefs() {
[](const MCOperand &Op) { return Op.isExpr(); })) {
// Skip assembly if the instruction may not have any symbolic operands.
continue;
+ } else {
+ analyzeInstructionForFuncReference(Instruction);
}
// Emit the instruction using temp emitter and generate relocations.
diff --git a/bolt/lib/Core/DIEBuilder.cpp b/bolt/lib/Core/DIEBuilder.cpp
index 414912ea1c207..80ad583e079d4 100644
--- a/bolt/lib/Core/DIEBuilder.cpp
+++ b/bolt/lib/Core/DIEBuilder.cpp
@@ -622,7 +622,7 @@ DWARFDie DIEBuilder::resolveDIEReference(
}
void DIEBuilder::cloneDieOffsetReferenceAttribute(
- DIE &Die, const DWARFUnit &U, const DWARFDie &InputDIE,
+ DIE &Die, DWARFUnit &U, const DWARFDie &InputDIE,
const DWARFAbbreviationDeclaration::AttributeSpec AttrSpec, uint64_t Ref) {
DIE *NewRefDie = nullptr;
DWARFUnit *RefUnit = nullptr;
@@ -654,7 +654,7 @@ void DIEBuilder::cloneDieOffsetReferenceAttribute(
// Adding referenced DIE to DebugNames to be used when entries are created
// that contain cross cu references.
if (DebugNamesTable.canGenerateEntryWithCrossCUReference(U, Die, AttrSpec))
- DebugNamesTable.addCrossCUDie(DieInfo.Die);
+ DebugNamesTable.addCrossCUDie(&U, DieInfo.Die);
// no matter forward reference or backward reference, we are supposed
// to calculate them in `finish` due to the possible modification of
// the DIE.
diff --git a/bolt/lib/Core/DebugNames.cpp b/bolt/lib/Core/DebugNames.cpp
index 280c7c505eeda..366c22c38e616 100644
--- a/bolt/lib/Core/DebugNames.cpp
+++ b/bolt/lib/Core/DebugNames.cpp
@@ -143,7 +143,8 @@ static bool shouldIncludeVariable(const DWARFUnit &Unit, const DIE &Die) {
Unit.getFormParams().Format);
for (const DWARFExpression::Operation &Expr : LocExpr)
if (Expr.getCode() == dwarf::DW_OP_addrx ||
- Expr.getCode() == dwarf::DW_OP_form_tls_address)
+ Expr.getCode() == dwarf::DW_OP_form_tls_address ||
+ Expr.getCode() == dwarf::DW_OP_GNU_push_tls_address)
return true;
return false;
}
@@ -222,134 +223,113 @@ static uint64_t getEntryID(const BOLTDWARF5AccelTableData &Entry) {
return reinterpret_cast(&Entry);
}
-std::optional
-DWARF5AcceleratorTable::addAccelTableEntry(
- DWARFUnit &Unit, const DIE &Die, const std::optional &DWOID,
- const uint32_t NumberParentsInChain,
- std::optional &Parent) {
- if (Unit.getVersion() < 5 || !NeedToCreate)
- return std::nullopt;
- std::string NameToUse = "";
-
- auto getUnitID = [&](const DWARFUnit &Unit, bool &IsTU,
- uint32_t &DieTag) -> uint32_t {
- IsTU = Unit.isTypeUnit();
- DieTag = Die.getTag();
- if (IsTU) {
- if (DWOID) {
- const uint64_t TUHash = cast(&Unit)->getTypeHash();
- auto Iter = TUHashToIndexMap.find(TUHash);
- assert(Iter != TUHashToIndexMap.end() &&
- "Could not find TU hash in map");
- return Iter->second;
- }
- return LocalTUList.size() - 1;
+uint32_t DWARF5AcceleratorTable::getUnitID(const DWARFUnit &Unit,
+ const std::optional &DWOID,
+ bool &IsTU) {
+ IsTU = Unit.isTypeUnit();
+ if (IsTU) {
+ if (DWOID) {
+ const uint64_t TUHash = cast(&Unit)->getTypeHash();
+ auto Iter = TUHashToIndexMap.find(TUHash);
+ assert(Iter != TUHashToIndexMap.end() && "Could not find TU hash in map");
+ return Iter->second;
}
- return CUList.size() - 1;
- };
+ return LocalTUList.size() - 1;
+ }
+ return CUList.size() - 1;
+}
- if (!canProcess(Unit, Die, NameToUse, false))
+std::optional DWARF5AcceleratorTable::getName(
+ DWARFUnit &Unit, const std::optional &DWOID,
+ const std::string &NameToUse, DIEValue ValName) {
+ if ((!ValName || ValName.getForm() == dwarf::DW_FORM_string) &&
+ NameToUse.empty())
return std::nullopt;
-
- // Addes a Unit to either CU, LocalTU or ForeignTU list the first time we
- // encounter it.
- // Invoking it here so that we don't add Units that don't have any entries.
- if (&Unit != CurrentUnit) {
- CurrentUnit = &Unit;
- addUnit(Unit, DWOID);
+ std::string Name = "";
+ uint64_t NameIndexOffset = 0;
+ if (NameToUse.empty()) {
+ NameIndexOffset = ValName.getDIEInteger().getValue();
+ if (ValName.getForm() != dwarf::DW_FORM_strp)
+ NameIndexOffset = getNameOffset(BC, Unit, NameIndexOffset);
+ // Counts on strings end with '\0'.
+ Name = std::string(&StrSection.data()[NameIndexOffset]);
+ } else {
+ Name = NameToUse;
}
-
- auto getName = [&](DIEValue ValName) -> std::optional {
- if ((!ValName || ValName.getForm() == dwarf::DW_FORM_string) &&
- NameToUse.empty())
- return std::nullopt;
- std::string Name = "";
- uint64_t NameIndexOffset = 0;
- if (NameToUse.empty()) {
- NameIndexOffset = ValName.getDIEInteger().getValue();
- if (ValName.getForm() != dwarf::DW_FORM_strp)
- NameIndexOffset = getNameOffset(BC, Unit, NameIndexOffset);
- // Counts on strings end with '\0'.
- Name = std::string(&StrSection.data()[NameIndexOffset]);
- } else {
- Name = NameToUse;
- }
- auto &It = Entries[Name];
- if (It.Values.empty()) {
- if (DWOID && NameToUse.empty()) {
- // For DWO Unit the offset is in the .debug_str.dwo section.
- // Need to find offset for the name in the .debug_str section.
- llvm::hash_code Hash = llvm::hash_value(llvm::StringRef(Name));
- auto ItCache = StrCacheToOffsetMap.find(Hash);
- if (ItCache == StrCacheToOffsetMap.end())
- NameIndexOffset = MainBinaryStrWriter.addString(Name);
- else
- NameIndexOffset = ItCache->second;
- }
- if (!NameToUse.empty())
+ auto &It = Entries[Name];
+ if (It.Values.empty()) {
+ if (DWOID && NameToUse.empty()) {
+ // For DWO Unit the offset is in the .debug_str.dwo section.
+ // Need to find offset for the name in the .debug_str section.
+ llvm::hash_code Hash = llvm::hash_value(llvm::StringRef(Name));
+ auto ItCache = StrCacheToOffsetMap.find(Hash);
+ if (ItCache == StrCacheToOffsetMap.end())
NameIndexOffset = MainBinaryStrWriter.addString(Name);
- It.StrOffset = NameIndexOffset;
- // This the same hash function used in DWARF5AccelTableData.
- It.HashValue = caseFoldingDjbHash(Name);
+ else
+ NameIndexOffset = ItCache->second;
}
- return Name;
- };
+ if (!NameToUse.empty())
+ NameIndexOffset = MainBinaryStrWriter.addString(Name);
+ It.StrOffset = NameIndexOffset;
+ // This is the same hash function used in DWARF5AccelTableData.
+ It.HashValue = caseFoldingDjbHash(Name);
+ }
+ return Name;
+}
- auto addEntry =
- [&](DIEValue ValName) -> std::optional {
- std::optional Name = getName(ValName);
- if (!Name)
- return std::nullopt;
+std::optional DWARF5AcceleratorTable::addEntry(
+ DWARFUnit &DU, const DIE &CurrDie, const std::optional &DWOID,
+ const std::optional &Parent,
+ const std::optional &Name,
+ const uint32_t NumberParentsInChain) {
+ if (!Name)
+ return std::nullopt;
- auto &It = Entries[*Name];
- bool IsTU = false;
- uint32_t DieTag = 0;
- uint32_t UnitID = getUnitID(Unit, IsTU, DieTag);
- std::optional SecondIndex = std::nullopt;
- if (IsTU && DWOID) {
- auto Iter = CUOffsetsToPatch.find(*DWOID);
- if (Iter == CUOffsetsToPatch.end())
- BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not find "
- "DWO ID in CU offsets for second Unit Index "
- << *Name << ". For DIE at offset: "
- << Twine::utohexstr(CurrentUnitOffset + Die.getOffset())
- << ".\n";
- SecondIndex = Iter->second;
- }
- std::optional ParentOffset =
- (Parent ? std::optional(getEntryID(**Parent)) : std::nullopt);
- // This will be populated later in writeEntry.
- // This way only parent entries get tracked.
- // Keeping memory footprint down.
- if (ParentOffset)
- EntryRelativeOffsets.insert({*ParentOffset, 0});
- bool IsParentRoot = false;
- // If there is no parent and no valid Entries in parent chain this is a root
- // to be marked with a flag.
- if (!Parent && !NumberParentsInChain)
- IsParentRoot = true;
- It.Values.push_back(new (Allocator) BOLTDWARF5AccelTableData(
- Die.getOffset(), ParentOffset, DieTag, UnitID, IsParentRoot, IsTU,
- SecondIndex));
- return It.Values.back();
- };
+ auto &It = Entries[*Name];
+ bool IsTU = false;
+ uint32_t DieTag = CurrDie.getTag();
+ uint32_t UnitID = getUnitID(DU, DWOID, IsTU);
+ std::optional SecondIndex = std::nullopt;
+ if (IsTU && DWOID) {
+ auto Iter = CUOffsetsToPatch.find(*DWOID);
+ if (Iter == CUOffsetsToPatch.end())
+ BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not find "
+ "DWO ID in CU offsets for second Unit Index "
+ << *Name << ". For DIE at offset: "
+ << Twine::utohexstr(CurrentUnitOffset + CurrDie.getOffset())
+ << ".\n";
+ SecondIndex = Iter->second;
+ }
+ std::optional ParentOffset =
+ (Parent ? std::optional(getEntryID(**Parent)) : std::nullopt);
+ // This will be only populated in writeEntry, in order to keep only the parent
+ // entries, and keep the footprint down.
+ if (ParentOffset)
+ EntryRelativeOffsets.insert({*ParentOffset, 0});
+ bool IsParentRoot = false;
+ // If there is no parent and no valid Entries in parent chain this is a root
+ // to be marked with a flag.
+ if (!Parent && !NumberParentsInChain)
+ IsParentRoot = true;
+ It.Values.push_back(new (Allocator) BOLTDWARF5AccelTableData(
+ CurrDie.getOffset(), ParentOffset, DieTag, UnitID, IsParentRoot, IsTU,
+ SecondIndex));
+ return It.Values.back();
+}
- // Minor optimization not to add entry twice for DW_TAG_namespace if it has no
- // DW_AT_name.
- if (!(Die.getTag() == dwarf::DW_TAG_namespace &&
- !Die.findAttribute(dwarf::Attribute::DW_AT_name)))
- addEntry(Die.findAttribute(dwarf::Attribute::DW_AT_linkage_name));
- // For the purposes of determining whether a debugging information entry has a
- // particular attribute (such as DW_AT_name), if debugging information entry A
- // has a DW_AT_specification or DW_AT_abstract_origin attribute pointing to
- // another debugging information entry B, any attributes of B are considered
- // to be part of A.
- auto processReferencedDie = [&](const dwarf::Attribute &Attr)
- -> std::optional {
- const DIEValue Value = Die.findAttribute(Attr);
+std::optional
+DWARF5AcceleratorTable::processReferencedDie(
+ DWARFUnit &Unit, const DIE &Die, const std::optional &DWOID,
+ const std::optional &Parent,
+ const std::string &NameToUse, const uint32_t NumberParentsInChain,
+ const dwarf::Attribute &Attr) {
+ DIEValue Value = Die.findAttribute(Attr);
+ if (!Value)
+ return std::nullopt;
+ auto getReferenceDie = [&](const DIEValue &Value, const DIE *RefDieUsed)
+ -> std::optional> {
if (!Value)
return std::nullopt;
- const DIE *EntryDie = nullptr;
if (Value.getForm() == dwarf::DW_FORM_ref_addr) {
auto Iter = CrossCUDies.find(Value.getDIEInteger().getValue());
if (Iter == CrossCUDies.end()) {
@@ -359,24 +339,97 @@ DWARF5AcceleratorTable::addAccelTableEntry(
<< ".\n";
return std::nullopt;
}
- EntryDie = Iter->second;
- } else {
- const DIEEntry &DIEENtry = Value.getDIEEntry();
- EntryDie = &DIEENtry.getEntry();
+ return Iter->second;
}
-
- addEntry(EntryDie->findAttribute(dwarf::Attribute::DW_AT_linkage_name));
- return addEntry(EntryDie->findAttribute(dwarf::Attribute::DW_AT_name));
+ const DIEEntry &DIEENtry = Value.getDIEEntry();
+ return {{&Unit, &DIEENtry.getEntry()}};
};
- if (std::optional Entry =
- processReferencedDie(dwarf::Attribute::DW_AT_abstract_origin))
+ DIEValue AttrValLinkageName;
+ DIEValue AttrValName = Die.findAttribute(dwarf::Attribute::DW_AT_name);
+ DWARFUnit *RefUnit = &Unit;
+ const DIE *RefDieUsed = &Die;
+ // It is possible to have DW_TAG_subprogram only with DW_AT_linkage_name that
+ // DW_AT_abstract_origin/DW_AT_specification point to.
+ while (!AttrValName) {
+ std::optional> RefDUDie =
+ getReferenceDie(Value, RefDieUsed);
+ if (!RefDUDie)
+ break;
+ RefUnit = RefDUDie->first;
+ const DIE &RefDie = *RefDUDie->second;
+ RefDieUsed = &RefDie;
+ if (!AttrValLinkageName)
+ AttrValLinkageName =
+ RefDie.findAttribute(dwarf::Attribute::DW_AT_linkage_name);
+ AttrValName = RefDie.findAttribute(dwarf::Attribute::DW_AT_name);
+ Value = RefDie.findAttribute(dwarf::Attribute::DW_AT_abstract_origin);
+ if (!Value)
+ Value = RefDie.findAttribute(dwarf::Attribute::DW_AT_specification);
+ }
+ addEntry(Unit, Die, DWOID, Parent,
+ getName(*RefUnit, DWOID, NameToUse, AttrValLinkageName),
+ NumberParentsInChain);
+ return addEntry(Unit, Die, DWOID, Parent,
+ getName(*RefUnit, DWOID, NameToUse, AttrValName),
+ NumberParentsInChain);
+}
+
+std::optional
+DWARF5AcceleratorTable::addAccelTableEntry(
+ DWARFUnit &Unit, const DIE &Die, const std::optional &DWOID,
+ const uint32_t NumberParentsInChain,
+ std::optional &Parent) {
+ if (Unit.getVersion() < 5 || !NeedToCreate)
+ return std::nullopt;
+ std::string NameToUse = "";
+
+ if (!canProcess(Unit, Die, NameToUse, false))
+ return std::nullopt;
+
+ // Adds a Unit to either CU, LocalTU or ForeignTU list the first time we
+ // encounter it.
+ // Invoking it here so that we don't add Units that don't have any entries.
+ if (&Unit != CurrentUnit) {
+ CurrentUnit = &Unit;
+ addUnit(Unit, DWOID);
+ }
+
+ // Minor optimization not to add entry twice for DW_TAG_namespace if it has no
+ // DW_AT_name.
+ std::optional LinkageEntry = std::nullopt;
+ DIEValue NameVal = Die.findAttribute(dwarf::Attribute::DW_AT_name);
+ DIEValue LinkageNameVal =
+ Die.findAttribute(dwarf::Attribute::DW_AT_linkage_name);
+ if (!(Die.getTag() == dwarf::DW_TAG_namespace && !NameVal))
+ LinkageEntry = addEntry(Unit, Die, DWOID, Parent,
+ getName(Unit, DWOID, NameToUse, LinkageNameVal),
+ NumberParentsInChain);
+
+ std::optional NameEntry =
+ addEntry(Unit, Die, DWOID, Parent,
+ getName(Unit, DWOID, NameToUse, NameVal), NumberParentsInChain);
+ if (NameEntry)
+ return NameEntry;
+
+ // The DIE doesn't have DW_AT_name or DW_AT_linkage_name, so we need to see if
+ // we can follow other attributes to find them. For the purposes of
+ // determining whether a debug information entry has a particular
+ // attribute (such as DW_AT_name), if debug information entry A has a
+ // DW_AT_specification or DW_AT_abstract_origin attribute pointing to another
+ // debug information entry B, any attributes of B are considered to be
+ // part of A.
+ if (std::optional Entry = processReferencedDie(
+ Unit, Die, DWOID, Parent, NameToUse, NumberParentsInChain,
+ dwarf::Attribute::DW_AT_abstract_origin))
return *Entry;
- if (std::optional Entry =
- processReferencedDie(dwarf::Attribute::DW_AT_specification))
+ if (std::optional Entry = processReferencedDie(
+ Unit, Die, DWOID, Parent, NameToUse, NumberParentsInChain,
+ dwarf::Attribute::DW_AT_specification))
return *Entry;
- return addEntry(Die.findAttribute(dwarf::Attribute::DW_AT_name));
+ // This point can be hit by DW_TAG_varialbe that has no DW_AT_name.
+ return std::nullopt;
}
/// Algorithm from llvm implementation.
diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp
index 4e888a5b147ac..e9a9741bc3716 100644
--- a/bolt/lib/Core/Relocation.cpp
+++ b/bolt/lib/Core/Relocation.cpp
@@ -75,6 +75,8 @@ static bool isSupportedAArch64(uint64_t Type) {
case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+ case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0:
+ case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
case ELF::R_AARCH64_TLSDESC_LD64_LO12:
case ELF::R_AARCH64_TLSDESC_ADD_LO12:
@@ -183,6 +185,8 @@ static size_t getSizeForTypeAArch64(uint64_t Type) {
case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+ case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0:
+ case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
case ELF::R_AARCH64_TLSDESC_LD64_LO12:
case ELF::R_AARCH64_TLSDESC_ADD_LO12:
@@ -651,6 +655,8 @@ static bool isTLSAArch64(uint64_t Type) {
case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+ case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0:
+ case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case ELF::R_AARCH64_TLSDESC_LD64_LO12:
case ELF::R_AARCH64_TLSDESC_ADD_LO12:
case ELF::R_AARCH64_TLSDESC_CALL:
@@ -716,6 +722,8 @@ static bool isPCRelativeAArch64(uint64_t Type) {
case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case ELF::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+ case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0:
+ case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
case ELF::R_AARCH64_TLSDESC_LD64_LO12:
case ELF::R_AARCH64_TLSDESC_ADD_LO12:
diff --git a/bolt/lib/Passes/IdenticalCodeFolding.cpp b/bolt/lib/Passes/IdenticalCodeFolding.cpp
index 38e080c9dd621..8923562776cc4 100644
--- a/bolt/lib/Passes/IdenticalCodeFolding.cpp
+++ b/bolt/lib/Passes/IdenticalCodeFolding.cpp
@@ -15,6 +15,7 @@
#include "bolt/Core/ParallelUtilities.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/Timer.h"
#include
@@ -42,8 +43,41 @@ TimeICF("time-icf",
cl::ReallyHidden,
cl::ZeroOrMore,
cl::cat(BoltOptCategory));
+
+cl::opt
+ ICF("icf", cl::desc("fold functions with identical code"),
+ cl::init(bolt::IdenticalCodeFolding::ICFLevel::None),
+ cl::values(clEnumValN(bolt::IdenticalCodeFolding::ICFLevel::All, "all",
+ "Enable identical code folding"),
+ clEnumValN(bolt::IdenticalCodeFolding::ICFLevel::All, "1",
+ "Enable identical code folding"),
+ clEnumValN(bolt::IdenticalCodeFolding::ICFLevel::All, "",
+ "Enable identical code folding"),
+ clEnumValN(bolt::IdenticalCodeFolding::ICFLevel::None,
+ "none",
+ "Disable identical code folding (default)"),
+ clEnumValN(bolt::IdenticalCodeFolding::ICFLevel::None, "0",
+ "Disable identical code folding (default)"),
+ clEnumValN(bolt::IdenticalCodeFolding::ICFLevel::Safe,
+ "safe", "Enable safe identical code folding")),
+ cl::ZeroOrMore, cl::ValueOptional, cl::cat(BoltOptCategory));
} // namespace opts
+bool IdenticalCodeFolding::shouldOptimize(const BinaryFunction &BF) const {
+ if (BF.hasUnknownControlFlow())
+ return false;
+ if (BF.isFolded())
+ return false;
+ if (BF.hasSDTMarker())
+ return false;
+ if (BF.isPseudo())
+ return false;
+ if (opts::ICF == ICFLevel::Safe && BF.hasAddressTaken())
+ return false;
+ return BinaryFunctionPass::shouldOptimize(BF);
+}
+
/// Compare two jump tables in 2 functions. The function relies on consistent
/// ordering of basic blocks in both binary functions (e.g. DFS).
static bool equalJumpTables(const JumpTable &JumpTableA,
@@ -340,6 +374,74 @@ typedef std::unordered_map,
namespace llvm {
namespace bolt {
+void IdenticalCodeFolding::initVTableReferences(const BinaryContext &BC) {
+ for (const auto &[Address, Data] : BC.getBinaryData()) {
+ // Filter out all symbols that are not vtables.
+ if (!Data->getName().starts_with("_ZTV"))
+ continue;
+ for (uint64_t I = Address, End = I + Data->getSize(); I < End; I += 8)
+ setAddressUsedInVTable(I);
+ }
+}
+
+void IdenticalCodeFolding::analyzeDataRelocations(BinaryContext &BC) {
+ initVTableReferences(BC);
+ // For static relocations there should be a symbol for function references.
+ for (const BinarySection &Sec : BC.sections()) {
+ if (!Sec.hasSectionRef() || !Sec.isData())
+ continue;
+ for (const auto &Rel : Sec.relocations()) {
+ const uint64_t RelAddr = Rel.Offset + Sec.getAddress();
+ if (isAddressInVTable(RelAddr))
+ continue;
+ if (BinaryFunction *BF = BC.getFunctionForSymbol(Rel.Symbol))
+ BF->setHasAddressTaken(true);
+ }
+ // For dynamic relocations there are two cases:
+ // 1: No symbol and only addend.
+ // 2: There is a symbol, but it does not references a function in a binary.
+ for (const auto &Rel : Sec.dynamicRelocations()) {
+ const uint64_t RelAddr = Rel.Offset + Sec.getAddress();
+ if (isAddressInVTable(RelAddr))
+ continue;
+ if (BinaryFunction *BF = BC.getBinaryFunctionAtAddress(Rel.Addend))
+ BF->setHasAddressTaken(true);
+ }
+ }
+}
+
+void IdenticalCodeFolding::analyzeFunctions(BinaryContext &BC) {
+ ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
+ for (const BinaryBasicBlock &BB : BF)
+ for (const MCInst &Inst : BB)
+ if (!(BC.MIB->isCall(Inst) || BC.MIB->isBranch(Inst)))
+ BF.analyzeInstructionForFuncReference(Inst);
+ };
+ ParallelUtilities::PredicateTy SkipFunc =
+ [&](const BinaryFunction &BF) -> bool { return !BF.hasCFG(); };
+ ParallelUtilities::runOnEachFunction(
+ BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFun,
+ SkipFunc, "markUnsafe");
+
+ LLVM_DEBUG({
+ for (const auto &BFIter : BC.getBinaryFunctions()) {
+ if (!BFIter.second.hasAddressTaken())
+ continue;
+ dbgs() << "BOLT-DEBUG: skipping function with reference taken "
+ << BFIter.second.getOneName() << '\n';
+ }
+ });
+}
+
+void IdenticalCodeFolding::markFunctionsUnsafeToFold(BinaryContext &BC) {
+ NamedRegionTimer MarkFunctionsUnsafeToFoldTimer(
+ "markFunctionsUnsafeToFold", "markFunctionsUnsafeToFold", "ICF breakdown",
+ "ICF breakdown", opts::TimeICF);
+ if (!BC.isX86())
+ BC.outs() << "BOLT-WARNING: safe ICF is only supported for x86\n";
+ analyzeDataRelocations(BC);
+ analyzeFunctions(BC);
+}
Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
const size_t OriginalFunctionCount = BC.getBinaryFunctions().size();
@@ -385,7 +487,7 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
"ICF breakdown", opts::TimeICF);
for (auto &BFI : BC.getBinaryFunctions()) {
BinaryFunction &BF = BFI.second;
- if (!this->shouldOptimize(BF))
+ if (!shouldOptimize(BF))
continue;
CongruentBuckets[&BF].emplace(&BF);
}
@@ -475,7 +577,8 @@ Error IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
LLVM_DEBUG(SinglePass.stopTimer());
};
-
+ if (opts::ICF == ICFLevel::Safe)
+ markFunctionsUnsafeToFold(BC);
hashFunctions();
createCongruentBuckets();
diff --git a/bolt/lib/Passes/LongJmp.cpp b/bolt/lib/Passes/LongJmp.cpp
index c1b8c03324e0e..e6bd417705e6f 100644
--- a/bolt/lib/Passes/LongJmp.cpp
+++ b/bolt/lib/Passes/LongJmp.cpp
@@ -138,9 +138,9 @@ BinaryBasicBlock *LongJmpPass::lookupStubFromGroup(
const std::pair &RHS) {
return LHS.first < RHS.first;
});
- if (Cand == Candidates.end())
- return nullptr;
- if (Cand != Candidates.begin()) {
+ if (Cand == Candidates.end()) {
+ Cand = std::prev(Cand);
+ } else if (Cand != Candidates.begin()) {
const StubTy *LeftCand = std::prev(Cand);
if (Cand->first - DotAddress > DotAddress - LeftCand->first)
Cand = LeftCand;
diff --git a/bolt/lib/Passes/ReorderFunctions.cpp b/bolt/lib/Passes/ReorderFunctions.cpp
index 1256d71342b13..f8f6a01526dcc 100644
--- a/bolt/lib/Passes/ReorderFunctions.cpp
+++ b/bolt/lib/Passes/ReorderFunctions.cpp
@@ -28,7 +28,9 @@ extern cl::OptionCategory BoltOptCategory;
extern cl::opt Verbosity;
extern cl::opt RandomSeed;
-extern size_t padFunction(const bolt::BinaryFunction &Function);
+extern size_t padFunction(const cl::list &Spec,
+ const bolt::BinaryFunction &Function);
+extern cl::list FunctionPadSpec, FunctionPadBeforeSpec;
extern cl::opt ReorderFunctions;
cl::opt ReorderFunctions(
@@ -304,8 +306,12 @@ Error ReorderFunctions::runOnFunctions(BinaryContext &BC) {
return false;
if (B->isIgnored())
return true;
- const size_t PadA = opts::padFunction(*A);
- const size_t PadB = opts::padFunction(*B);
+ const size_t PadA =
+ opts::padFunction(opts::FunctionPadSpec, *A) +
+ opts::padFunction(opts::FunctionPadBeforeSpec, *A);
+ const size_t PadB =
+ opts::padFunction(opts::FunctionPadSpec, *B) +
+ opts::padFunction(opts::FunctionPadBeforeSpec, *B);
if (!PadA || !PadB) {
if (PadA)
return true;
diff --git a/bolt/lib/Rewrite/BinaryPassManager.cpp b/bolt/lib/Rewrite/BinaryPassManager.cpp
index b090604183348..2d851c751ae10 100644
--- a/bolt/lib/Rewrite/BinaryPassManager.cpp
+++ b/bolt/lib/Rewrite/BinaryPassManager.cpp
@@ -54,6 +54,9 @@ extern cl::opt PrintDynoStats;
extern cl::opt DumpDotAll;
extern cl::opt AsmDump;
extern cl::opt PLT;
+extern cl::opt
+ ICF;
static cl::opt
DynoStatsAll("dyno-stats-all",
@@ -65,9 +68,6 @@ static cl::opt
cl::desc("eliminate unreachable code"), cl::init(true),
cl::cat(BoltOptCategory));
-cl::opt ICF("icf", cl::desc("fold functions with identical code"),
- cl::cat(BoltOptCategory));
-
static cl::opt JTFootprintReductionFlag(
"jt-footprint-reduction",
cl::desc("make jump tables size smaller at the cost of using more "
@@ -126,6 +126,11 @@ static cl::opt PrintJTFootprintReduction(
cl::desc("print function after jt-footprint-reduction pass"), cl::Hidden,
cl::cat(BoltOptCategory));
+static cl::opt
+ PrintAdrRelaxation("print-adr-relaxation",
+ cl::desc("print functions after ADR Relaxation pass"),
+ cl::Hidden, cl::cat(BoltOptCategory));
+
static cl::opt
PrintLongJmp("print-longjmp",
cl::desc("print functions after longjmp pass"), cl::Hidden,
@@ -398,7 +403,7 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
opts::StripRepRet);
Manager.registerPass(std::make_unique(PrintICF),
- opts::ICF);
+ opts::ICF != IdenticalCodeFolding::ICFLevel::None);
Manager.registerPass(
std::make_unique(NeverPrint, opts::SpecializeMemcpy1),
@@ -423,7 +428,7 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
Manager.registerPass(std::make_unique(PrintInline));
Manager.registerPass(std::make_unique(PrintICF),
- opts::ICF);
+ opts::ICF != IdenticalCodeFolding::ICFLevel::None);
Manager.registerPass(std::make_unique(PrintPLT));
@@ -493,7 +498,8 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
Manager.registerPass(std::make_unique());
if (BC.isAArch64()) {
- Manager.registerPass(std::make_unique());
+ Manager.registerPass(
+ std::make_unique(PrintAdrRelaxation));
// Tighten branches according to offset differences between branch and
// targets. No extra instructions after this pass, otherwise we may have
diff --git a/bolt/lib/Rewrite/BoltDiff.cpp b/bolt/lib/Rewrite/BoltDiff.cpp
index 74b5ca18abce4..35f6710506646 100644
--- a/bolt/lib/Rewrite/BoltDiff.cpp
+++ b/bolt/lib/Rewrite/BoltDiff.cpp
@@ -28,7 +28,9 @@ using namespace bolt;
namespace opts {
extern cl::OptionCategory BoltDiffCategory;
extern cl::opt NeverPrint;
-extern cl::opt ICF;
+extern cl::opt
+ ICF;
static cl::opt IgnoreLTOSuffix(
"ignore-lto-suffix",
@@ -697,7 +699,7 @@ void RewriteInstance::compare(RewriteInstance &RI2) {
}
// Pre-pass ICF
- if (opts::ICF) {
+ if (opts::ICF != IdenticalCodeFolding::ICFLevel::None) {
IdenticalCodeFolding ICF(opts::NeverPrint);
outs() << "BOLT-DIFF: Starting ICF pass for binary 1";
BC->logBOLTErrorsAndQuitOnFatal(ICF.runOnFunctions(*BC));
diff --git a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
index 03b414b71caca..0532468c237e0 100644
--- a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
+++ b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
@@ -123,6 +123,30 @@ inline raw_ostream &operator<<(raw_ostream &OS, const ORCState &E) {
namespace {
+/// Extension to DataExtractor that supports reading addresses stored in
+/// PC-relative format.
+class AddressExtractor : public DataExtractor {
+ uint64_t DataAddress;
+
+public:
+ AddressExtractor(StringRef Data, uint64_t DataAddress, bool IsLittleEndian,
+ uint8_t AddressSize)
+ : DataExtractor(Data, IsLittleEndian, AddressSize),
+ DataAddress(DataAddress) {}
+
+ /// Extract 32-bit PC-relative address/pointer.
+ uint64_t getPCRelAddress32(Cursor &C) {
+ const uint64_t Base = DataAddress + C.tell();
+ return Base + (int32_t)getU32(C);
+ }
+
+ /// Extract 64-bit PC-relative address/pointer.
+ uint64_t getPCRelAddress64(Cursor &C) {
+ const uint64_t Base = DataAddress + C.tell();
+ return Base + (int64_t)getU64(C);
+ }
+};
+
class LinuxKernelRewriter final : public MetadataRewriter {
/// Information required for updating metadata referencing an instruction.
struct InstructionFixup {
@@ -423,13 +447,13 @@ Error LinuxKernelRewriter::processSMPLocks() {
return createStringError(errc::executable_format_error,
"bad size of .smp_locks section");
- DataExtractor DE = DataExtractor(SMPLocksSection->getContents(),
- BC.AsmInfo->isLittleEndian(),
- BC.AsmInfo->getCodePointerSize());
- DataExtractor::Cursor Cursor(0);
+ AddressExtractor AE(SMPLocksSection->getContents(), SectionAddress,
+ BC.AsmInfo->isLittleEndian(),
+ BC.AsmInfo->getCodePointerSize());
+ AddressExtractor::Cursor Cursor(0);
while (Cursor && Cursor.tell() < SectionSize) {
const uint64_t Offset = Cursor.tell();
- const uint64_t IP = SectionAddress + Offset + (int32_t)DE.getU32(Cursor);
+ const uint64_t IP = AE.getPCRelAddress32(Cursor);
// Consume the status of the cursor.
if (!Cursor)
@@ -499,20 +523,17 @@ Error LinuxKernelRewriter::readORCTables() {
return createStringError(errc::executable_format_error,
"ORC entries number mismatch detected");
- const uint64_t IPSectionAddress = ORCUnwindIPSection->getAddress();
- DataExtractor OrcDE = DataExtractor(ORCUnwindSection->getContents(),
- BC.AsmInfo->isLittleEndian(),
- BC.AsmInfo->getCodePointerSize());
- DataExtractor IPDE = DataExtractor(ORCUnwindIPSection->getContents(),
- BC.AsmInfo->isLittleEndian(),
- BC.AsmInfo->getCodePointerSize());
+ DataExtractor OrcDE(ORCUnwindSection->getContents(),
+ BC.AsmInfo->isLittleEndian(),
+ BC.AsmInfo->getCodePointerSize());
+ AddressExtractor IPAE(
+ ORCUnwindIPSection->getContents(), ORCUnwindIPSection->getAddress(),
+ BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize());
DataExtractor::Cursor ORCCursor(0);
DataExtractor::Cursor IPCursor(0);
uint64_t PrevIP = 0;
for (uint32_t Index = 0; Index < NumORCEntries; ++Index) {
- const uint64_t IP =
- IPSectionAddress + IPCursor.tell() + (int32_t)IPDE.getU32(IPCursor);
-
+ const uint64_t IP = IPAE.getPCRelAddress32(IPCursor);
// Consume the status of the cursor.
if (!IPCursor)
return createStringError(errc::executable_format_error,
@@ -856,15 +877,13 @@ Error LinuxKernelRewriter::validateORCTables() {
if (!ORCUnwindIPSection)
return Error::success();
- const uint64_t IPSectionAddress = ORCUnwindIPSection->getAddress();
- DataExtractor IPDE = DataExtractor(ORCUnwindIPSection->getOutputContents(),
- BC.AsmInfo->isLittleEndian(),
- BC.AsmInfo->getCodePointerSize());
- DataExtractor::Cursor IPCursor(0);
+ AddressExtractor IPAE(
+ ORCUnwindIPSection->getOutputContents(), ORCUnwindIPSection->getAddress(),
+ BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize());
+ AddressExtractor::Cursor IPCursor(0);
uint64_t PrevIP = 0;
for (uint32_t Index = 0; Index < NumORCEntries; ++Index) {
- const uint64_t IP =
- IPSectionAddress + IPCursor.tell() + (int32_t)IPDE.getU32(IPCursor);
+ const uint64_t IP = IPAE.getPCRelAddress32(IPCursor);
if (!IPCursor)
return createStringError(errc::executable_format_error,
"out of bounds while reading ORC IP table: %s",
@@ -916,16 +935,14 @@ Error LinuxKernelRewriter::readStaticCalls() {
"static call table size error");
const uint64_t SectionAddress = StaticCallSection->getAddress();
- DataExtractor DE(StaticCallSection->getContents(),
- BC.AsmInfo->isLittleEndian(),
- BC.AsmInfo->getCodePointerSize());
- DataExtractor::Cursor Cursor(StaticCallTableAddress - SectionAddress);
+ AddressExtractor AE(StaticCallSection->getContents(), SectionAddress,
+ BC.AsmInfo->isLittleEndian(),
+ BC.AsmInfo->getCodePointerSize());
+ AddressExtractor::Cursor Cursor(StaticCallTableAddress - SectionAddress);
uint32_t EntryID = 0;
while (Cursor && Cursor.tell() < Stop->getAddress() - SectionAddress) {
- const uint64_t CallAddress =
- SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor);
- const uint64_t KeyAddress =
- SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor);
+ const uint64_t CallAddress = AE.getPCRelAddress32(Cursor);
+ const uint64_t KeyAddress = AE.getPCRelAddress32(Cursor);
// Consume the status of the cursor.
if (!Cursor)
@@ -1027,18 +1044,15 @@ Error LinuxKernelRewriter::readExceptionTable() {
return createStringError(errc::executable_format_error,
"exception table size error");
- const uint64_t SectionAddress = ExceptionsSection->getAddress();
- DataExtractor DE(ExceptionsSection->getContents(),
- BC.AsmInfo->isLittleEndian(),
- BC.AsmInfo->getCodePointerSize());
- DataExtractor::Cursor Cursor(0);
+ AddressExtractor AE(
+ ExceptionsSection->getContents(), ExceptionsSection->getAddress(),
+ BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize());
+ AddressExtractor::Cursor Cursor(0);
uint32_t EntryID = 0;
while (Cursor && Cursor.tell() < ExceptionsSection->getSize()) {
- const uint64_t InstAddress =
- SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor);
- const uint64_t FixupAddress =
- SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor);
- const uint64_t Data = DE.getU32(Cursor);
+ const uint64_t InstAddress = AE.getPCRelAddress32(Cursor);
+ const uint64_t FixupAddress = AE.getPCRelAddress32(Cursor);
+ const uint64_t Data = AE.getU32(Cursor);
// Consume the status of the cursor.
if (!Cursor)
@@ -1134,9 +1148,9 @@ Error LinuxKernelRewriter::readParaInstructions() {
if (!ParavirtualPatchSection)
return Error::success();
- DataExtractor DE = DataExtractor(ParavirtualPatchSection->getContents(),
- BC.AsmInfo->isLittleEndian(),
- BC.AsmInfo->getCodePointerSize());
+ DataExtractor DE(ParavirtualPatchSection->getContents(),
+ BC.AsmInfo->isLittleEndian(),
+ BC.AsmInfo->getCodePointerSize());
uint32_t EntryID = 0;
DataExtractor::Cursor Cursor(0);
while (Cursor && !DE.eof(Cursor)) {
@@ -1235,15 +1249,14 @@ Error LinuxKernelRewriter::readBugTable() {
return createStringError(errc::executable_format_error,
"bug table size error");
- const uint64_t SectionAddress = BugTableSection->getAddress();
- DataExtractor DE(BugTableSection->getContents(), BC.AsmInfo->isLittleEndian(),
- BC.AsmInfo->getCodePointerSize());
- DataExtractor::Cursor Cursor(0);
+ AddressExtractor AE(
+ BugTableSection->getContents(), BugTableSection->getAddress(),
+ BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize());
+ AddressExtractor::Cursor Cursor(0);
uint32_t EntryID = 0;
while (Cursor && Cursor.tell() < BugTableSection->getSize()) {
const uint64_t Pos = Cursor.tell();
- const uint64_t InstAddress =
- SectionAddress + Pos + (int32_t)DE.getU32(Cursor);
+ const uint64_t InstAddress = AE.getPCRelAddress32(Cursor);
Cursor.seek(Pos + BUG_TABLE_ENTRY_SIZE);
if (!Cursor)
@@ -1402,23 +1415,20 @@ Error LinuxKernelRewriter::readAltInstructions() {
Error LinuxKernelRewriter::tryReadAltInstructions(uint32_t AltInstFeatureSize,
bool AltInstHasPadLen,
bool ParseOnly) {
- const uint64_t Address = AltInstrSection->getAddress();
- DataExtractor DE = DataExtractor(AltInstrSection->getContents(),
- BC.AsmInfo->isLittleEndian(),
- BC.AsmInfo->getCodePointerSize());
+ AddressExtractor AE(
+ AltInstrSection->getContents(), AltInstrSection->getAddress(),
+ BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize());
+ AddressExtractor::Cursor Cursor(0);
uint64_t EntryID = 0;
- DataExtractor::Cursor Cursor(0);
- while (Cursor && !DE.eof(Cursor)) {
- const uint64_t OrgInstAddress =
- Address + Cursor.tell() + (int32_t)DE.getU32(Cursor);
- const uint64_t AltInstAddress =
- Address + Cursor.tell() + (int32_t)DE.getU32(Cursor);
- const uint64_t Feature = DE.getUnsigned(Cursor, AltInstFeatureSize);
- const uint8_t OrgSize = DE.getU8(Cursor);
- const uint8_t AltSize = DE.getU8(Cursor);
+ while (Cursor && !AE.eof(Cursor)) {
+ const uint64_t OrgInstAddress = AE.getPCRelAddress32(Cursor);
+ const uint64_t AltInstAddress = AE.getPCRelAddress32(Cursor);
+ const uint64_t Feature = AE.getUnsigned(Cursor, AltInstFeatureSize);
+ const uint8_t OrgSize = AE.getU8(Cursor);
+ const uint8_t AltSize = AE.getU8(Cursor);
// Older kernels may have the padlen field.
- const uint8_t PadLen = AltInstHasPadLen ? DE.getU8(Cursor) : 0;
+ const uint8_t PadLen = AltInstHasPadLen ? AE.getU8(Cursor) : 0;
if (!Cursor)
return createStringError(
@@ -1537,19 +1547,17 @@ Error LinuxKernelRewriter::readPCIFixupTable() {
return createStringError(errc::executable_format_error,
"PCI fixup table size error");
- const uint64_t Address = PCIFixupSection->getAddress();
- DataExtractor DE = DataExtractor(PCIFixupSection->getContents(),
- BC.AsmInfo->isLittleEndian(),
- BC.AsmInfo->getCodePointerSize());
+ AddressExtractor AE(
+ PCIFixupSection->getContents(), PCIFixupSection->getAddress(),
+ BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize());
+ AddressExtractor::Cursor Cursor(0);
uint64_t EntryID = 0;
- DataExtractor::Cursor Cursor(0);
- while (Cursor && !DE.eof(Cursor)) {
- const uint16_t Vendor = DE.getU16(Cursor);
- const uint16_t Device = DE.getU16(Cursor);
- const uint32_t Class = DE.getU32(Cursor);
- const uint32_t ClassShift = DE.getU32(Cursor);
- const uint64_t HookAddress =
- Address + Cursor.tell() + (int32_t)DE.getU32(Cursor);
+ while (Cursor && !AE.eof(Cursor)) {
+ const uint16_t Vendor = AE.getU16(Cursor);
+ const uint16_t Device = AE.getU16(Cursor);
+ const uint32_t Class = AE.getU32(Cursor);
+ const uint32_t ClassShift = AE.getU32(Cursor);
+ const uint64_t HookAddress = AE.getPCRelAddress32(Cursor);
if (!Cursor)
return createStringError(errc::executable_format_error,
@@ -1654,18 +1662,15 @@ Error LinuxKernelRewriter::readStaticKeysJumpTable() {
"static keys jump table size error");
const uint64_t SectionAddress = StaticKeysJumpSection->getAddress();
- DataExtractor DE(StaticKeysJumpSection->getContents(),
- BC.AsmInfo->isLittleEndian(),
- BC.AsmInfo->getCodePointerSize());
- DataExtractor::Cursor Cursor(StaticKeysJumpTableAddress - SectionAddress);
+ AddressExtractor AE(StaticKeysJumpSection->getContents(), SectionAddress,
+ BC.AsmInfo->isLittleEndian(),
+ BC.AsmInfo->getCodePointerSize());
+ AddressExtractor::Cursor Cursor(StaticKeysJumpTableAddress - SectionAddress);
uint32_t EntryID = 0;
while (Cursor && Cursor.tell() < Stop->getAddress() - SectionAddress) {
- const uint64_t JumpAddress =
- SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor);
- const uint64_t TargetAddress =
- SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor);
- const uint64_t KeyAddress =
- SectionAddress + Cursor.tell() + (int64_t)DE.getU64(Cursor);
+ const uint64_t JumpAddress = AE.getPCRelAddress32(Cursor);
+ const uint64_t TargetAddress = AE.getPCRelAddress32(Cursor);
+ const uint64_t KeyAddress = AE.getPCRelAddress64(Cursor);
// Consume the status of the cursor.
if (!Cursor)
@@ -1859,21 +1864,18 @@ Error LinuxKernelRewriter::updateStaticKeysJumpTablePostEmit() {
return Error::success();
const uint64_t SectionAddress = StaticKeysJumpSection->getAddress();
- DataExtractor DE(StaticKeysJumpSection->getOutputContents(),
- BC.AsmInfo->isLittleEndian(),
- BC.AsmInfo->getCodePointerSize());
- DataExtractor::Cursor Cursor(StaticKeysJumpTableAddress - SectionAddress);
+ AddressExtractor AE(StaticKeysJumpSection->getOutputContents(),
+ SectionAddress, BC.AsmInfo->isLittleEndian(),
+ BC.AsmInfo->getCodePointerSize());
+ AddressExtractor::Cursor Cursor(StaticKeysJumpTableAddress - SectionAddress);
const BinaryData *Stop = BC.getBinaryDataByName("__stop___jump_table");
uint32_t EntryID = 0;
uint64_t NumShort = 0;
uint64_t NumLong = 0;
while (Cursor && Cursor.tell() < Stop->getAddress() - SectionAddress) {
- const uint64_t JumpAddress =
- SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor);
- const uint64_t TargetAddress =
- SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor);
- const uint64_t KeyAddress =
- SectionAddress + Cursor.tell() + (int64_t)DE.getU64(Cursor);
+ const uint64_t JumpAddress = AE.getPCRelAddress32(Cursor);
+ const uint64_t TargetAddress = AE.getPCRelAddress32(Cursor);
+ const uint64_t KeyAddress = AE.getPCRelAddress64(Cursor);
// Consume the status of the cursor.
if (!Cursor)
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index 76e1f0156f828..4329235d47049 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -19,6 +19,7 @@
#include "bolt/Core/Relocation.h"
#include "bolt/Passes/BinaryPasses.h"
#include "bolt/Passes/CacheMetrics.h"
+#include "bolt/Passes/IdenticalCodeFolding.h"
#include "bolt/Passes/ReorderFunctions.h"
#include "bolt/Profile/BoltAddressTranslation.h"
#include "bolt/Profile/DataAggregator.h"
@@ -78,7 +79,6 @@ namespace opts {
extern cl::list HotTextMoveSections;
extern cl::opt Hugify;
extern cl::opt Instrument;
-extern cl::opt JumpTables;
extern cl::opt KeepNops;
extern cl::opt Lite;
extern cl::list ReorderData;
@@ -86,6 +86,9 @@ extern cl::opt ReorderFunctions;
extern cl::opt TerminalTrap;
extern cl::opt TimeBuild;
extern cl::opt TimeRewrite;
+extern cl::opt
+ ICF;
cl::opt AllowStripped("allow-stripped",
cl::desc("allow processing of stripped binaries"),
@@ -699,6 +702,11 @@ Error RewriteInstance::run() {
if (opts::DiffOnly)
return Error::success();
+ if (opts::BinaryAnalysisMode) {
+ runBinaryAnalyses();
+ return Error::success();
+ }
+
preregisterSections();
runOptimizationPasses();
@@ -2051,6 +2059,13 @@ void RewriteInstance::adjustCommandLineOptions() {
exit(1);
}
+ if (!BC->HasRelocations &&
+ opts::ICF == IdenticalCodeFolding::ICFLevel::Safe) {
+ BC->errs() << "BOLT-ERROR: binary built without relocations. Safe ICF is "
+ "not supported\n";
+ exit(1);
+ }
+
if (opts::Instrument ||
(opts::ReorderFunctions != ReorderFunctions::RT_NONE &&
!opts::HotText.getNumOccurrences())) {
@@ -3475,6 +3490,8 @@ void RewriteInstance::runOptimizationPasses() {
BC->logBOLTErrorsAndQuitOnFatal(BinaryFunctionPassManager::runAllPasses(*BC));
}
+void RewriteInstance::runBinaryAnalyses() {}
+
void RewriteInstance::preregisterSections() {
// Preregister sections before emission to set their order in the output.
const unsigned ROFlags = BinarySection::getFlags(/*IsReadOnly*/ true,
@@ -3841,20 +3858,6 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) {
assert(Function.getImageSize() <= Function.getMaxSize() &&
"Unexpected large function");
- // Map jump tables if updating in-place.
- if (opts::JumpTables == JTS_BASIC) {
- for (auto &JTI : Function.JumpTables) {
- JumpTable *JT = JTI.second;
- BinarySection &Section = JT->getOutputSection();
- Section.setOutputAddress(JT->getAddress());
- Section.setOutputFileOffset(getFileOffsetForAddress(JT->getAddress()));
- LLVM_DEBUG(dbgs() << "BOLT-DEBUG: mapping JT " << Section.getName()
- << " to 0x" << Twine::utohexstr(JT->getAddress())
- << '\n');
- MapSection(Section, JT->getAddress());
- }
- }
-
if (!Function.isSplit())
continue;
@@ -5637,26 +5640,8 @@ void RewriteInstance::rewriteFile() {
if (Function->getImageAddress() == 0 || Function->getImageSize() == 0)
continue;
- if (Function->getImageSize() > Function->getMaxSize()) {
- assert(!BC->isX86() && "Unexpected large function.");
- if (opts::Verbosity >= 1)
- BC->errs() << "BOLT-WARNING: new function size (0x"
- << Twine::utohexstr(Function->getImageSize())
- << ") is larger than maximum allowed size (0x"
- << Twine::utohexstr(Function->getMaxSize())
- << ") for function " << *Function << '\n';
-
- // Remove jump table sections that this function owns in non-reloc mode
- // because we don't want to write them anymore.
- if (!BC->HasRelocations && opts::JumpTables == JTS_BASIC) {
- for (auto &JTI : Function->JumpTables) {
- JumpTable *JT = JTI.second;
- BinarySection &Section = JT->getOutputSection();
- BC->deregisterSection(Section);
- }
- }
- continue;
- }
+ assert(Function->getImageSize() <= Function->getMaxSize() &&
+ "Unexpected large function");
const auto HasAddress = [](const FunctionFragment &FF) {
return FF.empty() ||
diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index 7e08e5c81d26f..679c9774c767f 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -1449,6 +1449,8 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
case ELF::R_AARCH64_TLSDESC_LD64_LO12:
case ELF::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case ELF::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
+ case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0:
+ case ELF::R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case ELF::R_AARCH64_MOVW_UABS_G0:
case ELF::R_AARCH64_MOVW_UABS_G0_NC:
case ELF::R_AARCH64_MOVW_UABS_G1:
diff --git a/bolt/lib/Utils/CommandLineOpts.cpp b/bolt/lib/Utils/CommandLineOpts.cpp
index de82420a16713..17f090aa61ee9 100644
--- a/bolt/lib/Utils/CommandLineOpts.cpp
+++ b/bolt/lib/Utils/CommandLineOpts.cpp
@@ -29,6 +29,7 @@ const char *BoltRevision =
namespace opts {
bool HeatmapMode = false;
+bool BinaryAnalysisMode = false;
cl::OptionCategory BoltCategory("BOLT generic options");
cl::OptionCategory BoltDiffCategory("BOLTDIFF generic options");
@@ -38,6 +39,7 @@ cl::OptionCategory BoltOutputCategory("Output options");
cl::OptionCategory AggregatorCategory("Data aggregation options");
cl::OptionCategory BoltInstrCategory("BOLT instrumentation options");
cl::OptionCategory HeatmapCategory("Heatmap options");
+cl::OptionCategory BinaryAnalysisCategory("BinaryAnalysis options");
cl::opt AlignText("align-text",
cl::desc("alignment of .text section"), cl::Hidden,
diff --git a/bolt/test/AArch64/long-jmp-one-stub.s b/bolt/test/AArch64/long-jmp-one-stub.s
new file mode 100644
index 0000000000000..cd9e0875a43c4
--- /dev/null
+++ b/bolt/test/AArch64/long-jmp-one-stub.s
@@ -0,0 +1,32 @@
+## This test verifies that no unnecessary stubs are inserted when each DotAddress increases during a lookup.
+
+# REQUIRES: system-linux, asserts
+
+# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
+# RUN: %clang %cflags -O0 %t.o -o %t.exe -Wl,-q
+# RUN: link_fdata %s %t.o %t.fdata
+# RUN: llvm-bolt %t.exe -o %t.bolt \
+# RUN: --data %t.fdata | FileCheck %s
+
+# CHECK: BOLT-INFO: Inserted 1 stubs in the hot area and 0 stubs in the cold area.
+
+ .section .text
+ .global _start
+ .global far_away_func
+
+ .align 4
+ .global _start
+ .type _start, %function
+_start:
+# FDATA: 0 [unknown] 0 1 _start 0 0 100
+ bl far_away_func
+ bl far_away_func
+ ret
+ .space 0x8000000
+ .global far_away_func
+ .type far_away_func, %function
+far_away_func:
+ add x0, x0, #1
+ ret
+
+.reloc 0, R_AARCH64_NONE
diff --git a/bolt/test/AArch64/pad-before-funcs.s b/bolt/test/AArch64/pad-before-funcs.s
new file mode 100644
index 0000000000000..3ce0ee5e38389
--- /dev/null
+++ b/bolt/test/AArch64/pad-before-funcs.s
@@ -0,0 +1,29 @@
+# Test checks that --pad-before-funcs is working as expected.
+# It should be able to introduce a configurable offset for the _start symbol.
+# It should reject requests which don't obey the code alignment requirement.
+
+# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
+# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -Wl,--section-start=.text=0x4000
+# RUN: llvm-bolt %t.exe -o %t.bolt.0 --pad-funcs-before=_start:0
+# RUN: llvm-bolt %t.exe -o %t.bolt.4 --pad-funcs-before=_start:4
+# RUN: llvm-bolt %t.exe -o %t.bolt.8 --pad-funcs-before=_start:8
+
+# RUN: not llvm-bolt %t.exe -o %t.bolt.8 --pad-funcs-before=_start:1 2>&1 | FileCheck --check-prefix=CHECK-BAD-ALIGN %s
+
+# CHECK-BAD-ALIGN: user-requested 1 padding bytes before function _start(*2) is not a multiple of the minimum function alignment (4).
+
+# RUN: llvm-objdump --section=.text --disassemble %t.bolt.0 | FileCheck --check-prefix=CHECK-0 %s
+# RUN: llvm-objdump --section=.text --disassemble %t.bolt.4 | FileCheck --check-prefix=CHECK-4 %s
+# RUN: llvm-objdump --section=.text --disassemble %t.bolt.8 | FileCheck --check-prefix=CHECK-8 %s
+
+# Trigger relocation mode in bolt.
+.reloc 0, R_AARCH64_NONE
+
+.section .text
+.globl _start
+
+# CHECK-0: 0000000000400000 <_start>
+# CHECK-4: 0000000000400004 <_start>
+# CHECK-8: 0000000000400008 <_start>
+_start:
+ ret
diff --git a/bolt/test/CMakeLists.txt b/bolt/test/CMakeLists.txt
index d468ff984840f..6e18b028bddfc 100644
--- a/bolt/test/CMakeLists.txt
+++ b/bolt/test/CMakeLists.txt
@@ -37,6 +37,7 @@ list(APPEND BOLT_TEST_DEPS
lld
llvm-config
llvm-bolt
+ llvm-bolt-binary-analysis
llvm-bolt-heatmap
llvm-bat-dump
llvm-dwarfdump
diff --git a/bolt/test/RISCV/call-annotations.s b/bolt/test/RISCV/call-annotations.s
index ff99e0f1dfd84..f876544e214ca 100644
--- a/bolt/test/RISCV/call-annotations.s
+++ b/bolt/test/RISCV/call-annotations.s
@@ -16,13 +16,13 @@ f:
// CHECK-LABEL: Binary Function "_start" after building cfg {
// CHECK: auipc ra, f
-// CHECK-NEXT: jalr ra, -0x4(ra) # Offset: 4
-// CHECK-NEXT: jal ra, f # Offset: 8
-// CHECK-NEXT: jal zero, f # TAILCALL # Offset: 12
+// CHECK-NEXT: jalr -0x4(ra) # Offset: 4
+// CHECK-NEXT: jal f # Offset: 8
+// CHECK-NEXT: j f # TAILCALL # Offset: 12
// CHECK-LABEL: Binary Function "long_tail" after building cfg {
// CHECK: auipc t1, f
-// CHECK-NEXT: jalr zero, -0x18(t1) # TAILCALL # Offset: 8
+// CHECK-NEXT: jr -0x18(t1) # TAILCALL # Offset: 8
// CHECK-LABEL: Binary Function "compressed_tail" after building cfg {
// CHECK: jr a0 # TAILCALL # Offset: 0
diff --git a/bolt/test/RISCV/relax.s b/bolt/test/RISCV/relax.s
index ec390ea76b5c7..74f049b8f8dd9 100644
--- a/bolt/test/RISCV/relax.s
+++ b/bolt/test/RISCV/relax.s
@@ -5,9 +5,9 @@
// RUN: llvm-objdump -d %t.bolt | FileCheck --check-prefix=OBJDUMP %s
// CHECK: Binary Function "_start" after building cfg {
-// CHECK: jal ra, near_f
+// CHECK: jal near_f
// CHECK-NEXT: auipc ra, far_f
-// CHECK-NEXT: jalr ra, 0xc(ra)
+// CHECK-NEXT: jalr 0xc(ra)
// CHECK-NEXT: j near_f
// CHECK: Binary Function "_start" after fix-riscv-calls {
diff --git a/bolt/test/RISCV/reloc-branch.s b/bolt/test/RISCV/reloc-branch.s
index 6a8b5a28e19d9..780d978f36f44 100644
--- a/bolt/test/RISCV/reloc-branch.s
+++ b/bolt/test/RISCV/reloc-branch.s
@@ -7,7 +7,7 @@
.p2align 1
// CHECK: Binary Function "_start" after building cfg {
_start:
-// CHECK: beq zero, zero, .Ltmp0
+// CHECK: beqz zero, .Ltmp0
beq zero, zero, 1f
nop
// CHECK: .Ltmp0
diff --git a/bolt/test/RISCV/reloc-jal.s b/bolt/test/RISCV/reloc-jal.s
index ce54265fac05e..62a2f1dbea03a 100644
--- a/bolt/test/RISCV/reloc-jal.s
+++ b/bolt/test/RISCV/reloc-jal.s
@@ -14,7 +14,7 @@ f:
.globl _start
.p2align 1
_start:
-// CHECK: jal ra, f
+// CHECK: jal f
jal ra, f
ret
.size _start, .-_start
diff --git a/bolt/test/X86/dwarf5-debug-names-abstract-origin-linkage-name-only.s b/bolt/test/X86/dwarf5-debug-names-abstract-origin-linkage-name-only.s
new file mode 100644
index 0000000000000..8c9817ce91edb
--- /dev/null
+++ b/bolt/test/X86/dwarf5-debug-names-abstract-origin-linkage-name-only.s
@@ -0,0 +1,568 @@
+# RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %s -o %tmain.o
+# RUN: %clang %cflags -gdwarf-5 %tmain.o -o %tmain.exe
+# RUN: llvm-bolt %tmain.exe -o %tmain.exe.bolt --update-debug-sections
+# RUN: llvm-dwarfdump --debug-names %tmain.exe.bolt > %tlog.txt
+# RUN: cat %tlog.txt | FileCheck -check-prefix=BOLT %s
+
+## Tests that bolt can correctly generate debug_names when there is an DW_TAG_inlined_subroutine
+## with DW_AT_abstract_origin that points to DW_TAG_subprogram that only has DW_AT_linkage_name.
+
+# BOLT: Name Index @ 0x0 {
+# BOLT-NEXT: Header {
+# BOLT-NEXT: Length: 0xA2
+# BOLT-NEXT: Format: DWARF32
+# BOLT-NEXT: Version: 5
+# BOLT-NEXT: CU count: 1
+# BOLT-NEXT: Local TU count: 0
+# BOLT-NEXT: Foreign TU count: 0
+# BOLT-NEXT: Bucket count: 4
+# BOLT-NEXT: Name count: 4
+# BOLT-NEXT: Abbreviations table size: 0x19
+# BOLT-NEXT: Augmentation: 'BOLT'
+# BOLT-NEXT: }
+# BOLT-NEXT: Compilation Unit offsets [
+# BOLT-NEXT: CU[0]: 0x00000000
+# BOLT-NEXT: ]
+# BOLT-NEXT: Abbreviations [
+# BOLT-NEXT: Abbreviation [[ABBREV1:0x[0-9a-f]*]] {
+# BOLT-NEXT: Tag: DW_TAG_base_type
+# BOLT-NEXT: DW_IDX_die_offset: DW_FORM_ref4
+# BOLT-NEXT: DW_IDX_parent: DW_FORM_flag_present
+# BOLT-NEXT: }
+# BOLT-NEXT: Abbreviation [[ABBREV2:0x[0-9a-f]*]] {
+# BOLT-NEXT: Tag: DW_TAG_subprogram
+# BOLT-NEXT: DW_IDX_die_offset: DW_FORM_ref4
+# BOLT-NEXT: DW_IDX_parent: DW_FORM_flag_present
+# BOLT-NEXT: }
+# BOLT-NEXT: Abbreviation [[ABBREV3:0x[0-9a-f]*]] {
+# BOLT-NEXT: Tag: DW_TAG_inlined_subroutine
+# BOLT-NEXT: DW_IDX_die_offset: DW_FORM_ref4
+# BOLT-NEXT: DW_IDX_parent: DW_FORM_ref4
+# BOLT-NEXT: }
+# BOLT-NEXT: ]
+# BOLT-NEXT: Bucket 0 [
+# BOLT-NEXT: Name 1 {
+# BOLT-NEXT: Hash: 0xB888030
+# BOLT-NEXT: String: {{.+}} "int"
+# BOLT-NEXT: Entry @ {{.+}} {
+# BOLT-NEXT: Abbrev: 0x1
+# BOLT-NEXT: Tag: DW_TAG_base_type
+# BOLT-NEXT: DW_IDX_die_offset: 0x0000004a
+# BOLT-NEXT: DW_IDX_parent:
+# BOLT-NEXT: }
+# BOLT-NEXT: }
+# BOLT-NEXT: ]
+# BOLT-NEXT: Bucket 1 [
+# BOLT-NEXT: EMPTY
+# BOLT-NEXT: ]
+# BOLT-NEXT: Bucket 2 [
+# BOLT-NEXT: Name 2 {
+# BOLT-NEXT: Hash: 0x7C9A7F6A
+# BOLT-NEXT: String: {{.+}} "main"
+# BOLT-NEXT: Entry @ [[REF1:0x[0-9a-f]*]] {
+# BOLT-NEXT: Abbrev: [[ABBREV2]]
+# BOLT-NEXT: Tag: DW_TAG_subprogram
+# BOLT-NEXT: DW_IDX_die_offset: 0x0000004e
+# BOLT-NEXT: DW_IDX_parent:
+# BOLT-NEXT: }
+# BOLT-NEXT: }
+# BOLT-NEXT: Name 3 {
+# BOLT-NEXT: Hash: 0xB5063CFE
+# BOLT-NEXT: String: {{.+}} "_Z3fooi"
+# BOLT-NEXT: Entry @ {{.+}} {
+# BOLT-NEXT: Abbrev: [[ABBREV2]]
+# BOLT-NEXT: Tag: DW_TAG_subprogram
+# BOLT-NEXT: DW_IDX_die_offset: 0x00000024
+# BOLT-NEXT: DW_IDX_parent:
+# BOLT-NEXT: }
+# BOLT-NEXT: Entry @ 0x96 {
+# BOLT-NEXT: Abbrev: [[ABBREV3]]
+# BOLT-NEXT: Tag: DW_TAG_inlined_subroutine
+# BOLT-NEXT: DW_IDX_die_offset: 0x0000007e
+# BOLT-NEXT: DW_IDX_parent: Entry @ [[REF1]]
+# BOLT-NEXT: }
+# BOLT-NEXT: }
+# BOLT-NEXT: ]
+# BOLT-NEXT: Bucket 3 [
+# BOLT-NEXT: Name 4 {
+# BOLT-NEXT: Hash: 0x7C952063
+# BOLT-NEXT: String: {{.+}} "char"
+# BOLT-NEXT: Entry @ {{.+}} {
+# BOLT-NEXT: Abbrev: [[ABBREV1]]
+# BOLT-NEXT: Tag: DW_TAG_base_type
+# BOLT-NEXT: DW_IDX_die_offset: 0x0000009f
+# BOLT-NEXT: DW_IDX_parent:
+
+## int foo(int i) {
+## return i ++;
+## }
+## int main(int argc, char* argv[]) {
+## int i = 0;
+## [[clang::always_inline]] i = foo(argc);
+## return i;
+## }
+## Test was manually modified so that DW_TAG_subprogram only had DW_AT_linkage_name.
+
+ .text
+ .file "main.cpp"
+ .globl _Z3fooi
+ .p2align 4, 0x90
+ .type _Z3fooi,@function
+_Z3fooi:
+.Lfunc_begin0:
+ .file 0 "/abstractChain" "main.cpp" md5 0x2e29d55fc1320801a8057a4c50643ea1
+ .loc 0 1 0
+ .loc 0 2 12 prologue_end
+ .loc 0 2 3 epilogue_begin is_stmt 0
+ retq
+.Lfunc_end0:
+ .size _Z3fooi, .Lfunc_end0-_Z3fooi
+
+ .globl main
+ .p2align 4, 0x90
+ .type main,@function
+main:
+.Lfunc_begin1:
+ .loc 0 4 0 is_stmt 1
+.Ltmp2:
+ .loc 0 5 7 prologue_end
+ .loc 0 6 36
+ movl -12(%rbp), %eax
+.Ltmp3:
+ .loc 0 2 12
+.Ltmp4:
+ .loc 0 6 30
+ .loc 0 7 10
+ .loc 0 7 3 epilogue_begin is_stmt 0
+ retq
+.Ltmp5:
+.Lfunc_end1:
+ .size main, .Lfunc_end1-main
+ # -- End function
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 49 # DW_AT_abstract_origin
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 49 # DW_AT_abstract_origin
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 110 # DW_AT_linkage_name
+ .byte 37 # DW_FORM_strx1
+ #.byte 3 # DW_AT_name
+ #.byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 32 # DW_AT_inline
+ .byte 33 # DW_FORM_implicit_const
+ .byte 1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 7 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 8 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 9 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 10 # Abbreviation Code
+ .byte 29 # DW_TAG_inlined_subroutine
+ .byte 1 # DW_CHILDREN_yes
+ .byte 49 # DW_AT_abstract_origin
+ .byte 19 # DW_FORM_ref4
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 88 # DW_AT_call_file
+ .byte 11 # DW_FORM_data1
+ .byte 89 # DW_AT_call_line
+ .byte 11 # DW_FORM_data1
+ .byte 87 # DW_AT_call_column
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 11 # Abbreviation Code
+ .byte 15 # DW_TAG_pointer_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x98 DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 33 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end1-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .byte 2 # Abbrev [2] 0x23:0x15 DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ .long 56 # DW_AT_abstract_origin
+ .byte 3 # Abbrev [3] 0x2f:0x8 DW_TAG_formal_parameter
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 124
+ .long 64 # DW_AT_abstract_origin Manually Modified
+ .byte 0 # End Of Children Mark
+ .byte 4 # Abbrev [4] 0x38:0x12 DW_TAG_subprogram
+ .byte 3 # DW_AT_linkage_name
+ #.byte 4 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ .long 74 # DW_AT_type
+ # DW_AT_external
+ # DW_AT_inline
+ .byte 5 # Abbrev [5] 0x41:0x8 DW_TAG_formal_parameter
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ .long 74 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 6 # Abbrev [6] 0x4a:0x4 DW_TAG_base_type
+ .byte 5 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 7 # Abbrev [7] 0x4e:0x47 DW_TAG_subprogram
+ .byte 1 # DW_AT_low_pc
+ .long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 73 # DW_AT_type Manually Modified
+ # DW_AT_external
+ .byte 8 # Abbrev [8] 0x5d:0xb DW_TAG_formal_parameter
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 116
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 73 # DW_AT_type Manually Modified
+ .byte 8 # Abbrev [8] 0x68:0xb DW_TAG_formal_parameter
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 104
+ .byte 9 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 148 # DW_AT_type Manually Modified
+ .byte 9 # Abbrev [9] 0x73:0xb DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 100
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 5 # DW_AT_decl_line
+ .long 73 # DW_AT_type Manually Modified
+ .byte 10 # Abbrev [10] 0x7e:0x16 DW_TAG_inlined_subroutine
+ .long 56 # DW_AT_abstract_origin
+ .byte 2 # DW_AT_low_pc
+ .long .Ltmp4-.Ltmp3 # DW_AT_high_pc
+ .byte 0 # DW_AT_call_file
+ .byte 6 # DW_AT_call_line
+ .byte 32 # DW_AT_call_column
+ .byte 3 # Abbrev [3] 0x8b:0x8 DW_TAG_formal_parameter
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 124
+ .long 64 # DW_AT_abstract_origin Manually Modified
+ .byte 0 # End Of Children Mark
+ .byte 0 # End Of Children Mark
+ .byte 11 # Abbrev [11] 0x95:0x5 DW_TAG_pointer_type
+ .long 153 # DW_AT_type Manually Modified
+ .byte 11 # Abbrev [11] 0x9a:0x5 DW_TAG_pointer_type
+ .long 158 # DW_AT_type Manually Modified
+ .byte 6 # Abbrev [6] 0x9f:0x4 DW_TAG_base_type
+ .byte 10 # DW_AT_name
+ .byte 6 # DW_AT_encoding
+ .byte 1 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"",@progbits
+ .long 48 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 20.0.0git" # string offset=0
+.Linfo_string1:
+ .asciz "main.cpp" # string offset=24
+.Linfo_string2:
+ .asciz "/abstractChain" # string offset=33
+.Linfo_string3:
+ .asciz "foo" # string offset=85
+.Linfo_string4:
+ .asciz "_Z3fooi" # string offset=89
+.Linfo_string5:
+ .asciz "int" # string offset=97
+.Linfo_string6:
+ .asciz "i" # string offset=101
+.Linfo_string7:
+ .asciz "main" # string offset=103
+.Linfo_string8:
+ .asciz "argc" # string offset=108
+.Linfo_string9:
+ .asciz "argv" # string offset=113
+.Linfo_string10:
+ .asciz "char" # string offset=118
+ .section .debug_str_offsets,"",@progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string4
+ .long .Linfo_string3
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .long .Linfo_string10
+ .section .debug_addr,"",@progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+ .quad .Lfunc_begin1
+ .quad .Ltmp3
+.Ldebug_addr_end0:
+ .section .debug_names,"",@progbits
+ .long .Lnames_end0-.Lnames_start0 # Header: unit length
+.Lnames_start0:
+ .short 5 # Header: version
+ .short 0 # Header: padding
+ .long 1 # Header: compilation unit count
+ .long 0 # Header: local type unit count
+ .long 0 # Header: foreign type unit count
+ .long 5 # Header: bucket count
+ .long 5 # Header: name count
+ .long .Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+ .long 8 # Header: augmentation string size
+ .ascii "LLVM0700" # Header: augmentation string
+ .long .Lcu_begin0 # Compilation unit 0
+ .long 0 # Bucket 0
+ .long 1 # Bucket 1
+ .long 0 # Bucket 2
+ .long 3 # Bucket 3
+ .long 4 # Bucket 4
+ .long 2090499946 # Hash in Bucket 1
+ .long -1257882370 # Hash in Bucket 1
+ .long 193495088 # Hash in Bucket 3
+ .long 193491849 # Hash in Bucket 4
+ .long 2090147939 # Hash in Bucket 4
+ .long .Linfo_string7 # String in Bucket 1: main
+ .long .Linfo_string4 # String in Bucket 1: _Z3fooi
+ .long .Linfo_string5 # String in Bucket 3: int
+ .long .Linfo_string3 # String in Bucket 4: foo
+ .long .Linfo_string10 # String in Bucket 4: char
+ .long .Lnames3-.Lnames_entries0 # Offset in Bucket 1
+ .long .Lnames1-.Lnames_entries0 # Offset in Bucket 1
+ .long .Lnames2-.Lnames_entries0 # Offset in Bucket 3
+ .long .Lnames0-.Lnames_entries0 # Offset in Bucket 4
+ .long .Lnames4-.Lnames_entries0 # Offset in Bucket 4
+.Lnames_abbrev_start0:
+ .byte 1 # Abbrev code
+ .byte 46 # DW_TAG_subprogram
+ .byte 3 # DW_IDX_die_offset
+ .byte 19 # DW_FORM_ref4
+ .byte 4 # DW_IDX_parent
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # End of abbrev
+ .byte 0 # End of abbrev
+ .byte 2 # Abbrev code
+ .byte 29 # DW_TAG_inlined_subroutine
+ .byte 3 # DW_IDX_die_offset
+ .byte 19 # DW_FORM_ref4
+ .byte 4 # DW_IDX_parent
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # End of abbrev
+ .byte 0 # End of abbrev
+ .byte 3 # Abbrev code
+ .byte 36 # DW_TAG_base_type
+ .byte 3 # DW_IDX_die_offset
+ .byte 19 # DW_FORM_ref4
+ .byte 4 # DW_IDX_parent
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # End of abbrev
+ .byte 0 # End of abbrev
+ .byte 0 # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames3:
+.L2:
+ .byte 1 # Abbreviation code
+ .long 78 # DW_IDX_die_offset
+ .byte 0 # DW_IDX_parent
+ # End of list: main
+.Lnames1:
+.L0:
+ .byte 1 # Abbreviation code
+ .long 35 # DW_IDX_die_offset
+.L3: # DW_IDX_parent
+ .byte 2 # Abbreviation code
+ .long 126 # DW_IDX_die_offset
+ .long .L2-.Lnames_entries0 # DW_IDX_parent
+ .byte 0 # End of list: _Z3fooi
+.Lnames2:
+.L1:
+ .byte 3 # Abbreviation code
+ .long 74 # DW_IDX_die_offset
+ .byte 0 # DW_IDX_parent
+ # End of list: int
+.Lnames0:
+ .byte 1 # Abbreviation code
+ .long 35 # DW_IDX_die_offset
+ .byte 2 # DW_IDX_parent
+ # Abbreviation code
+ .long 126 # DW_IDX_die_offset
+ .long .L2-.Lnames_entries0 # DW_IDX_parent
+ .byte 0 # End of list: foo
+.Lnames4:
+.L4:
+ .byte 3 # Abbreviation code
+ .long 159 # DW_IDX_die_offset
+ .byte 0 # DW_IDX_parent
+ # End of list: char
+ .p2align 2, 0x0
+.Lnames_end0:
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","",@progbits
+ .addrsig
+ .section .debug_line,"",@progbits
+.Lline_table_start0:
diff --git a/bolt/test/X86/dwarf5-debug-names-abstract-origin-specification.s b/bolt/test/X86/dwarf5-debug-names-abstract-origin-specification.s
new file mode 100644
index 0000000000000..2075640d6761c
--- /dev/null
+++ b/bolt/test/X86/dwarf5-debug-names-abstract-origin-specification.s
@@ -0,0 +1,829 @@
+# RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %s -o %tmain.o
+# RUN: %clang %cflags -gdwarf-5 %tmain.o -o %tmain.exe
+# RUN: llvm-bolt %tmain.exe -o %tmain.exe.bolt --update-debug-sections
+# RUN: llvm-dwarfdump --debug-names %tmain.exe.bolt > %tlog.txt
+# RUN: cat %tlog.txt | FileCheck -check-prefix=BOLT %s
+
+## This test checks that BOLT correctly generates .debug_names section when there is transative
+## DW_AT_name/DW_AT_linkage_name resolution.
+
+# BOLT: Abbreviations [
+# BOLT-NEXT: Abbreviation [[ABBREV1:0x[0-9a-f]*]] {
+# BOLT-NEXT: Tag: DW_TAG_subprogram
+# BOLT-NEXT: DW_IDX_die_offset: DW_FORM_ref4
+# BOLT-NEXT: DW_IDX_parent: DW_FORM_flag_present
+# BOLT-NEXT: }
+# BOLT-NEXT: Abbreviation [[ABBREV2:0x[0-9a-f]*]] {
+# BOLT-NEXT: Tag: DW_TAG_class_type
+# BOLT-NEXT: DW_IDX_die_offset: DW_FORM_ref4
+# BOLT-NEXT: DW_IDX_parent: DW_FORM_flag_present
+# BOLT-NEXT: }
+# BOLT-NEXT: Abbreviation [[ABBREV3:0x[0-9a-f]*]] {
+# BOLT-NEXT: Tag: DW_TAG_inlined_subroutine
+# BOLT-NEXT: DW_IDX_die_offset: DW_FORM_ref4
+# BOLT-NEXT: DW_IDX_parent: DW_FORM_ref4
+# BOLT-NEXT: }
+# BOLT-NEXT: Abbreviation [[ABBREV4:0x[0-9a-f]*]] {
+# BOLT-NEXT: Tag: DW_TAG_base_type
+# BOLT-NEXT: DW_IDX_die_offset: DW_FORM_ref4
+# BOLT-NEXT: DW_IDX_parent: DW_FORM_flag_present
+# BOLT-NEXT: }
+# BOLT-NEXT: ]
+# BOLT-NEXT: Bucket 0 [
+# BOLT-NEXT: Name 1 {
+# BOLT-NEXT: Hash: 0xD72418AA
+# BOLT-NEXT: String: {{.+}} "_ZL3fooi"
+# BOLT-NEXT: Entry @ {{.+}} {
+# BOLT-NEXT: Abbrev: [[ABBREV1]]
+# BOLT-NEXT: Tag: DW_TAG_subprogram
+# BOLT-NEXT: DW_IDX_die_offset: 0x000000ba
+# BOLT-NEXT: DW_IDX_parent:
+# BOLT-NEXT: }
+# BOLT-NEXT: }
+# BOLT-NEXT: ]
+# BOLT-NEXT: Bucket 1 [
+# BOLT-NEXT: Name 2 {
+# BOLT-NEXT: Hash: 0x10614A06
+# BOLT-NEXT: String: {{.+}} "State"
+# BOLT-NEXT: Entry @ {{.+}} {
+# BOLT-NEXT: Abbrev: [[ABBREV2]]
+# BOLT-NEXT: Tag: DW_TAG_class_type
+# BOLT-NEXT: DW_IDX_die_offset: 0x0000002b
+# BOLT-NEXT: DW_IDX_parent:
+# BOLT-NEXT: }
+# BOLT-NEXT: Entry @ [[REF1:0x[0-9a-f]*]] {
+# BOLT-NEXT: Abbrev: [[ABBREV1]]
+# BOLT-NEXT: Tag: DW_TAG_subprogram
+# BOLT-NEXT: DW_IDX_die_offset: 0x00000089
+# BOLT-NEXT: DW_IDX_parent:
+# BOLT-NEXT: }
+# BOLT-NEXT: Entry @ {{.+}} {
+# BOLT-NEXT: Abbrev: [[ABBREV3]]
+# BOLT-NEXT: Tag: DW_TAG_inlined_subroutine
+# BOLT-NEXT: DW_IDX_die_offset: 0x000000a3
+# BOLT-NEXT: DW_IDX_parent: Entry @ [[REF1]]
+# BOLT-NEXT: }
+# BOLT-NEXT: }
+# BOLT-NEXT: ]
+# BOLT-NEXT: Bucket 2 [
+# BOLT-NEXT: EMPTY
+# BOLT-NEXT: ]
+# BOLT-NEXT: Bucket 3 [
+# BOLT-NEXT: Name 3 {
+# BOLT-NEXT: Hash: 0xB888030
+# BOLT-NEXT: String: {{.+}} "int"
+# BOLT-NEXT: Entry @ {{.+}} {
+# BOLT-NEXT: Abbrev: [[ABBREV4]]
+# BOLT-NEXT: Tag: DW_TAG_base_type
+# BOLT-NEXT: DW_IDX_die_offset: 0x00000085
+# BOLT-NEXT: DW_IDX_parent:
+# BOLT-NEXT: }
+# BOLT-NEXT: }
+# BOLT-NEXT: Name 4 {
+# BOLT-NEXT: Hash: 0x7C9A7F6A
+# BOLT-NEXT: String: {{.+}} "main"
+# BOLT-NEXT: Entry @ {{.+}} {
+# BOLT-NEXT: Abbrev: [[ABBREV1]]
+# BOLT-NEXT: Tag: DW_TAG_subprogram
+# BOLT-NEXT: DW_IDX_die_offset: 0x00000042
+# BOLT-NEXT: DW_IDX_parent:
+# BOLT-NEXT: }
+# BOLT-NEXT: }
+# BOLT-NEXT: ]
+# BOLT-NEXT: Bucket 4 [
+# BOLT-NEXT: EMPTY
+# BOLT-NEXT: ]
+# BOLT-NEXT: Bucket 5 [
+# BOLT-NEXT: Name 5 {
+# BOLT-NEXT: Hash: 0xB887389
+# BOLT-NEXT: String: {{.+}} "foo"
+# BOLT-NEXT: Entry @ {{.+}} {
+# BOLT-NEXT: Abbrev: [[ABBREV1]]
+# BOLT-NEXT: Tag: DW_TAG_subprogram
+# BOLT-NEXT: DW_IDX_die_offset: 0x000000ba
+# BOLT-NEXT: DW_IDX_parent:
+# BOLT-NEXT: }
+# BOLT-NEXT: }
+# BOLT-NEXT: Name 6 {
+# BOLT-NEXT: Hash: 0x7C952063
+# BOLT-NEXT: String: {{.+}} "char"
+# BOLT-NEXT: Entry @ {{.+}} {
+# BOLT-NEXT: Abbrev: [[ABBREV4]]
+# BOLT-NEXT: Tag: DW_TAG_base_type
+# BOLT-NEXT: DW_IDX_die_offset: 0x000000d9
+# BOLT-NEXT: DW_IDX_parent:
+# BOLT-NEXT: }
+# BOLT-NEXT: }
+# BOLT-NEXT: Name 7 {
+# BOLT-NEXT: Hash: 0xFBBDC812
+# BOLT-NEXT: String: {{.+}} "_ZN5StateC2Ev"
+# BOLT-NEXT: Entry @ {{.+}} {
+# BOLT-NEXT: Abbrev: [[ABBREV1]]
+# BOLT-NEXT: Tag: DW_TAG_subprogram
+# BOLT-NEXT: DW_IDX_die_offset: 0x00000089
+# BOLT-NEXT: DW_IDX_parent:
+# BOLT-NEXT: }
+# BOLT-NEXT: Entry @ {{.+}} {
+# BOLT-NEXT: Abbrev: [[ABBREV3]]
+# BOLT-NEXT: Tag: DW_TAG_inlined_subroutine
+# BOLT-NEXT: DW_IDX_die_offset: 0x000000a3
+# BOLT-NEXT: DW_IDX_parent: Entry @ [[REF1]]
+
+## static int foo(int i) {
+## return i ++;
+## }
+## class State {
+## public:
+## State() {[[clang::always_inline]] foo(3);}
+## };
+##
+## int main(int argc, char* argv[]) {
+## State S;
+## return 0;
+## }
+
+## Test manually modified to redirect DW_TAG_inlined_subroutine to DW_TAG_subprogram with DW_AT_specification.
+
+ .text
+ .file "main.cpp"
+ .file 0 "abstractChainTwo" "main.cpp" md5 0x17ad726b6a1fd49ee59559a1302da539
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main,@function
+main: # @main
+.Lfunc_begin0:
+ .loc 0 9 0 # main.cpp:9:0
+.Ltmp0:
+ .loc 0 10 9 prologue_end # main.cpp:10:9
+ callq _ZN5StateC2Ev
+ .loc 0 11 3 # main.cpp:11:3
+ .loc 0 11 3 epilogue_begin is_stmt 0 # main.cpp:11:3
+ retq
+.Ltmp1:
+.Lfunc_end0:
+ .size main, .Lfunc_end0-main
+ # -- End function
+ .section .text._ZN5StateC2Ev,"axG",@progbits,_ZN5StateC2Ev,comdat
+ .weak _ZN5StateC2Ev # -- Begin function _ZN5StateC2Ev
+ .p2align 4, 0x90
+ .type _ZN5StateC2Ev,@function
+_ZN5StateC2Ev: # @_ZN5StateC2Ev
+.Lfunc_begin1:
+ .loc 0 6 0 is_stmt 1 # main.cpp:6:0
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movq %rdi, -16(%rbp)
+ movl $3, -4(%rbp)
+.Ltmp2:
+ .loc 0 2 12 prologue_end # main.cpp:2:12
+ movl -4(%rbp), %eax
+ addl $1, %eax
+ movl %eax, -4(%rbp)
+.Ltmp3:
+ .loc 0 6 44 epilogue_begin # main.cpp:6:44
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp4:
+.Lfunc_end1:
+ .size _ZN5StateC2Ev, .Lfunc_end1-_ZN5StateC2Ev
+ .cfi_endproc
+ # -- End function
+ .text
+ .p2align 4, 0x90 # -- Begin function _ZL3fooi
+ .type _ZL3fooi,@function
+_ZL3fooi: # @_ZL3fooi
+.Lfunc_begin2:
+ .loc 0 1 0 # main.cpp:1:0
+ .cfi_startproc
+# %bb.0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ movl %edi, -4(%rbp)
+.Ltmp5:
+ .loc 0 2 12 prologue_end # main.cpp:2:12
+ movl -4(%rbp), %eax
+ movl %eax, %ecx
+ addl $1, %ecx
+ movl %ecx, -4(%rbp)
+ .loc 0 2 3 epilogue_begin is_stmt 0 # main.cpp:2:3
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp6:
+.Lfunc_end2:
+ .size _ZL3fooi, .Lfunc_end2-_ZL3fooi
+ .cfi_endproc
+ # -- End function
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 85 # DW_AT_ranges
+ .byte 35 # DW_FORM_rnglistx
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 116 # DW_AT_rnglists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 2 # DW_TAG_class_type
+ .byte 1 # DW_CHILDREN_yes
+ .byte 54 # DW_AT_calling_convention
+ .byte 11 # DW_FORM_data1
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 60 # DW_AT_declaration
+ .byte 25 # DW_FORM_flag_present
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 50 # DW_AT_accessibility
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 52 # DW_AT_artificial
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 15 # DW_TAG_pointer_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 7 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 8 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 9 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 110 # DW_AT_linkage_name
+ .byte 37 # DW_FORM_strx1
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 32 # DW_AT_inline
+ .byte 33 # DW_FORM_implicit_const
+ .byte 1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 10 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 11 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 12 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 100 # DW_AT_object_pointer
+ .byte 19 # DW_FORM_ref4
+ .byte 110 # DW_AT_linkage_name
+ .byte 37 # DW_FORM_strx1
+ .byte 71 # DW_AT_specification
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 13 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 52 # DW_AT_artificial
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 14 # Abbreviation Code
+ .byte 29 # DW_TAG_inlined_subroutine
+ .byte 1 # DW_CHILDREN_yes
+ .byte 49 # DW_AT_abstract_origin
+ .byte 19 # DW_FORM_ref4
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 88 # DW_AT_call_file
+ .byte 11 # DW_FORM_data1
+ .byte 89 # DW_AT_call_line
+ .byte 11 # DW_FORM_data1
+ .byte 87 # DW_AT_call_column
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 15 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 49 # DW_AT_abstract_origin
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 16 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 49 # DW_AT_abstract_origin
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0xd7 DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 33 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .quad 0 # DW_AT_low_pc
+ .byte 0 # DW_AT_ranges
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lrnglists_table_base0 # DW_AT_rnglists_base
+ .byte 2 # Abbrev [2] 0x2b:0x12 DW_TAG_class_type
+ .byte 5 # DW_AT_calling_convention
+ .byte 3 # DW_AT_name
+ .byte 1 # DW_AT_byte_size
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .byte 3 # Abbrev [3] 0x31:0xb DW_TAG_subprogram
+ .byte 3 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 6 # DW_AT_decl_line
+ # DW_AT_declaration
+ # DW_AT_external
+ .byte 1 # DW_AT_accessibility
+ # DW_ACCESS_public
+ .byte 4 # Abbrev [4] 0x36:0x5 DW_TAG_formal_parameter
+ .long 61 # DW_AT_type
+ # DW_AT_artificial
+ .byte 0 # End Of Children Mark
+ .byte 0 # End Of Children Mark
+ .byte 5 # Abbrev [5] 0x3d:0x5 DW_TAG_pointer_type
+ .long 43 # DW_AT_type
+ .byte 6 # Abbrev [6] 0x42:0x31 DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 9 # DW_AT_decl_line
+ .long 133 # DW_AT_type
+ # DW_AT_external
+ .byte 7 # Abbrev [7] 0x51:0xb DW_TAG_formal_parameter
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 120
+ .byte 10 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 9 # DW_AT_decl_line
+ .long 133 # DW_AT_type
+ .byte 7 # Abbrev [7] 0x5c:0xb DW_TAG_formal_parameter
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 112
+ .byte 11 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 9 # DW_AT_decl_line
+ .long 207 # DW_AT_type
+ .byte 8 # Abbrev [8] 0x67:0xb DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 111
+ .byte 13 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 10 # DW_AT_decl_line
+ .long 43 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 9 # Abbrev [9] 0x73:0x12 DW_TAG_subprogram
+ .byte 4 # DW_AT_linkage_name
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ .long 133 # DW_AT_type
+ # DW_AT_inline
+ .byte 10 # Abbrev [10] 0x7c:0x8 DW_TAG_formal_parameter
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ .long 133 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 11 # Abbrev [11] 0x85:0x4 DW_TAG_base_type
+ .byte 6 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 12 # Abbrev [12] 0x89:0x31 DW_TAG_subprogram
+ .byte 1 # DW_AT_low_pc
+ .long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ .long 154 # DW_AT_object_pointer
+ .byte 9 # DW_AT_linkage_name
+ .long 49 # DW_AT_specification
+ .byte 13 # Abbrev [13] 0x9a:0x9 DW_TAG_formal_parameter
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 112
+ .byte 14 # DW_AT_name
+ .long 221 # DW_AT_type
+ # DW_AT_artificial
+ .byte 14 # Abbrev [14] 0xa3:0x16 DW_TAG_inlined_subroutine
+ .long 137 # DW_AT_abstract_origin Manually Modified
+ .byte 2 # DW_AT_low_pc
+ .long .Ltmp3-.Ltmp2 # DW_AT_high_pc
+ .byte 0 # DW_AT_call_file
+ .byte 6 # DW_AT_call_line
+ .byte 37 # DW_AT_call_column
+ .byte 15 # Abbrev [15] 0xb0:0x8 DW_TAG_formal_parameter
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 124
+ .long 124 # DW_AT_abstract_origin
+ .byte 0 # End Of Children Mark
+ .byte 0 # End Of Children Mark
+ .byte 16 # Abbrev [16] 0xba:0x15 DW_TAG_subprogram
+ .byte 3 # DW_AT_low_pc
+ .long .Lfunc_end2-.Lfunc_begin2 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ .long 115 # DW_AT_abstract_origin
+ .byte 15 # Abbrev [15] 0xc6:0x8 DW_TAG_formal_parameter
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 124
+ .long 124 # DW_AT_abstract_origin
+ .byte 0 # End Of Children Mark
+ .byte 5 # Abbrev [5] 0xcf:0x5 DW_TAG_pointer_type
+ .long 212 # DW_AT_type
+ .byte 5 # Abbrev [5] 0xd4:0x5 DW_TAG_pointer_type
+ .long 217 # DW_AT_type
+ .byte 11 # Abbrev [11] 0xd9:0x4 DW_TAG_base_type
+ .byte 12 # DW_AT_name
+ .byte 6 # DW_AT_encoding
+ .byte 1 # DW_AT_byte_size
+ .byte 5 # Abbrev [5] 0xdd:0x5 DW_TAG_pointer_type
+ .long 43 # DW_AT_type
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_rnglists,"",@progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 1 # Offset entry count
+.Lrnglists_table_base0:
+ .long .Ldebug_ranges0-.Lrnglists_table_base0
+.Ldebug_ranges0:
+ .byte 1 # DW_RLE_base_addressx
+ .byte 0 # base address index
+ .byte 4 # DW_RLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # DW_RLE_offset_pair
+ .uleb128 .Lfunc_begin2-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end2-.Lfunc_begin0 # ending offset
+ .byte 3 # DW_RLE_startx_length
+ .byte 1 # start index
+ .uleb128 .Lfunc_end1-.Lfunc_begin1 # length
+ .byte 0 # DW_RLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_str_offsets,"",@progbits
+ .long 64 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 20.0.0git" # string offset=0
+.Linfo_string1:
+ .asciz "main.cpp" # string offset=24
+.Linfo_string2:
+ .asciz "abstractChainTwo" # string offset=33
+.Linfo_string3:
+ .asciz "State" # string offset=88
+.Linfo_string4:
+ .asciz "main" # string offset=94
+.Linfo_string5:
+ .asciz "_ZL3fooi" # string offset=99
+.Linfo_string6:
+ .asciz "foo" # string offset=108
+.Linfo_string7:
+ .asciz "int" # string offset=112
+.Linfo_string8:
+ .asciz "i" # string offset=116
+.Linfo_string9:
+ .asciz "_ZN5StateC2Ev" # string offset=118
+.Linfo_string10:
+ .asciz "argc" # string offset=132
+.Linfo_string11:
+ .asciz "argv" # string offset=137
+.Linfo_string12:
+ .asciz "char" # string offset=142
+.Linfo_string13:
+ .asciz "S" # string offset=147
+.Linfo_string14:
+ .asciz "this" # string offset=149
+ .section .debug_str_offsets,"",@progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string4
+ .long .Linfo_string9
+ .long .Linfo_string10
+ .long .Linfo_string11
+ .long .Linfo_string12
+ .long .Linfo_string13
+ .long .Linfo_string14
+ .section .debug_addr,"",@progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+ .quad .Lfunc_begin1
+ .quad .Ltmp2
+ .quad .Lfunc_begin2
+.Ldebug_addr_end0:
+ .section .debug_names,"",@progbits
+ .long .Lnames_end0-.Lnames_start0 # Header: unit length
+.Lnames_start0:
+ .short 5 # Header: version
+ .short 0 # Header: padding
+ .long 1 # Header: compilation unit count
+ .long 0 # Header: local type unit count
+ .long 0 # Header: foreign type unit count
+ .long 7 # Header: bucket count
+ .long 7 # Header: name count
+ .long .Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+ .long 8 # Header: augmentation string size
+ .ascii "LLVM0700" # Header: augmentation string
+ .long .Lcu_begin0 # Compilation unit 0
+ .long 1 # Bucket 0
+ .long 2 # Bucket 1
+ .long 0 # Bucket 2
+ .long 3 # Bucket 3
+ .long 0 # Bucket 4
+ .long 5 # Bucket 5
+ .long 0 # Bucket 6
+ .long -685500246 # Hash in Bucket 0
+ .long 274811398 # Hash in Bucket 1
+ .long 193495088 # Hash in Bucket 3
+ .long 2090499946 # Hash in Bucket 3
+ .long 193491849 # Hash in Bucket 5
+ .long 2090147939 # Hash in Bucket 5
+ .long -71448558 # Hash in Bucket 5
+ .long .Linfo_string5 # String in Bucket 0: _ZL3fooi
+ .long .Linfo_string3 # String in Bucket 1: State
+ .long .Linfo_string7 # String in Bucket 3: int
+ .long .Linfo_string4 # String in Bucket 3: main
+ .long .Linfo_string6 # String in Bucket 5: foo
+ .long .Linfo_string12 # String in Bucket 5: char
+ .long .Linfo_string9 # String in Bucket 5: _ZN5StateC2Ev
+ .long .Lnames5-.Lnames_entries0 # Offset in Bucket 0
+ .long .Lnames0-.Lnames_entries0 # Offset in Bucket 1
+ .long .Lnames2-.Lnames_entries0 # Offset in Bucket 3
+ .long .Lnames1-.Lnames_entries0 # Offset in Bucket 3
+ .long .Lnames4-.Lnames_entries0 # Offset in Bucket 5
+ .long .Lnames6-.Lnames_entries0 # Offset in Bucket 5
+ .long .Lnames3-.Lnames_entries0 # Offset in Bucket 5
+.Lnames_abbrev_start0:
+ .byte 1 # Abbrev code
+ .byte 29 # DW_TAG_inlined_subroutine
+ .byte 3 # DW_IDX_die_offset
+ .byte 19 # DW_FORM_ref4
+ .byte 4 # DW_IDX_parent
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # End of abbrev
+ .byte 0 # End of abbrev
+ .byte 2 # Abbrev code
+ .byte 46 # DW_TAG_subprogram
+ .byte 3 # DW_IDX_die_offset
+ .byte 19 # DW_FORM_ref4
+ .byte 4 # DW_IDX_parent
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # End of abbrev
+ .byte 0 # End of abbrev
+ .byte 3 # Abbrev code
+ .byte 2 # DW_TAG_class_type
+ .byte 3 # DW_IDX_die_offset
+ .byte 19 # DW_FORM_ref4
+ .byte 4 # DW_IDX_parent
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # End of abbrev
+ .byte 0 # End of abbrev
+ .byte 4 # Abbrev code
+ .byte 36 # DW_TAG_base_type
+ .byte 3 # DW_IDX_die_offset
+ .byte 19 # DW_FORM_ref4
+ .byte 4 # DW_IDX_parent
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # End of abbrev
+ .byte 0 # End of abbrev
+ .byte 0 # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames5:
+.L1:
+ .byte 1 # Abbreviation code
+ .long 163 # DW_IDX_die_offset
+ .long .L2-.Lnames_entries0 # DW_IDX_parent
+.L0:
+ .byte 2 # Abbreviation code
+ .long 186 # DW_IDX_die_offset
+ .byte 0 # DW_IDX_parent
+ # End of list: _ZL3fooi
+.Lnames0:
+.L5:
+ .byte 3 # Abbreviation code
+ .long 43 # DW_IDX_die_offset
+.L2: # DW_IDX_parent
+ .byte 2 # Abbreviation code
+ .long 137 # DW_IDX_die_offset
+ .byte 0 # DW_IDX_parent
+ # End of list: State
+.Lnames2:
+.L4:
+ .byte 4 # Abbreviation code
+ .long 133 # DW_IDX_die_offset
+ .byte 0 # DW_IDX_parent
+ # End of list: int
+.Lnames1:
+.L6:
+ .byte 2 # Abbreviation code
+ .long 66 # DW_IDX_die_offset
+ .byte 0 # DW_IDX_parent
+ # End of list: main
+.Lnames4:
+ .byte 1 # Abbreviation code
+ .long 163 # DW_IDX_die_offset
+ .long .L2-.Lnames_entries0 # DW_IDX_parent
+ .byte 2 # Abbreviation code
+ .long 186 # DW_IDX_die_offset
+ .byte 0 # DW_IDX_parent
+ # End of list: foo
+.Lnames6:
+.L3:
+ .byte 4 # Abbreviation code
+ .long 217 # DW_IDX_die_offset
+ .byte 0 # DW_IDX_parent
+ # End of list: char
+.Lnames3:
+ .byte 2 # Abbreviation code
+ .long 137 # DW_IDX_die_offset
+ .byte 0 # DW_IDX_parent
+ # End of list: _ZN5StateC2Ev
+ .p2align 2, 0x0
+.Lnames_end0:
+ .ident "clang version 20.0.0git"
+ .section ".note.GNU-stack","",@progbits
+ .addrsig
+ .section .debug_line,"",@progbits
+.Lline_table_start0:
diff --git a/bolt/test/X86/dwarf5-debug-names-gnu-push-tls-address.s b/bolt/test/X86/dwarf5-debug-names-gnu-push-tls-address.s
new file mode 100644
index 0000000000000..f84d0b6654e7a
--- /dev/null
+++ b/bolt/test/X86/dwarf5-debug-names-gnu-push-tls-address.s
@@ -0,0 +1,291 @@
+# RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %s -o %tmain.o
+# RUN: %clang %cflags -gdwarf-5 %tmain.o -o %tmain.exe
+# RUN: llvm-bolt %tmain.exe -o %tmain.exe.bolt --update-debug-sections
+# RUN: llvm-dwarfdump --debug-names --debug-info %tmain.exe.bolt > %tlog.txt
+# RUN: cat %tlog.txt | FileCheck -check-prefix=BOLT %s
+
+## This test checks that BOLT correctly generates .debug_names section when there is DW_TAG_variable
+## with DW_OP_GNU_push_tls_address in DW_AT_location.
+
+# BOLT: [[DIEOFFSET:0x[0-9a-f]*]]: DW_TAG_variable
+# BOLT-NEXT: DW_AT_name ("x")
+# BOLT-NEXT: DW_AT_type ({{.+}} "int")
+# BOLT-NEXT: DW_AT_external (true)
+# BOLT-NEXT: DW_AT_decl_file ("gnu_tls_push/main.cpp")
+# BOLT-NEXT: DW_AT_decl_line (1)
+# BOLT-NEXT: DW_AT_location (DW_OP_const8u 0x0, DW_OP_GNU_push_tls_address)
+# BOLT: Hash: 0x2B61D
+# BOLT-NEXT: String: {{.+}} "x"
+# BOLT-NEXT: Entry @ {{.+}} {
+# BOLT-NEXT: Abbrev: {{.+}}
+# BOLT-NEXT: Tag: DW_TAG_variable
+# BOLT-NEXT: DW_IDX_die_offset: [[DIEOFFSET]]
+# BOLT-NEXT: DW_IDX_parent:
+
+## thread_local int x = 0;
+## int main() {
+## x = 10;
+## return x;
+## }
+ .file "main.cpp"
+ .file 0 "gnu_tls_push" "main.cpp" md5 0x551db97d5e23dc6a81abdc5ade4d9d71
+ .globl main
+ .type main,@function
+main:
+.Lfunc_begin0:
+ .loc 0 2 0
+ .loc 0 3 3 prologue_end
+ .loc 0 3 5 is_stmt 0
+ .loc 0 4 10 is_stmt 1
+ .loc 0 4 3 epilogue_begin is_stmt 0
+ retq
+.Lfunc_end0:
+ .size main, .Lfunc_end0-main
+
+ .hidden _ZTW1x
+ .weak _ZTW1x
+ .type _ZTW1x,@function
+_ZTW1x:
+.Lfunc_begin1:
+ retq
+.Lfunc_end1:
+ .size _ZTW1x, .Lfunc_end1-_ZTW1x
+
+ .type x,@object
+ .section .tbss,"awT",@nobits
+ .globl x
+x:
+ .long 0
+ .size x, 4
+
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 0 # DW_CHILDREN_no
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x3e DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 33 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .byte 2 # Abbrev [2] 0x23:0x13 DW_TAG_variable
+ .byte 3 # DW_AT_name
+ .long 54 # DW_AT_type
+ # DW_AT_external
+ .byte 0 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ .byte 10 # DW_AT_location
+ .byte 14
+ .quad x@DTPOFF
+ .byte 224
+ .byte 3 # Abbrev [3] 0x36:0x4 DW_TAG_base_type
+ .byte 4 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 4 # Abbrev [4] 0x3a:0xf DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 54 # DW_AT_type
+ # DW_AT_external
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"",@progbits
+ .long 28 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 17.0.4" # string offset=0
+.Linfo_string1:
+ .asciz "main.cpp" # string offset=137
+.Linfo_string2:
+ .asciz "gnu_tls_push" # string offset=146
+.Linfo_string3:
+ .asciz "x" # string offset=184
+.Linfo_string4:
+ .asciz "int" # string offset=186
+.Linfo_string5:
+ .asciz "main" # string offset=190
+ .section .debug_str_offsets,"",@progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .section .debug_addr,"",@progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+.Ldebug_addr_end0:
+ .section .debug_names,"",@progbits
+ .long .Lnames_end0-.Lnames_start0 # Header: unit length
+.Lnames_start0:
+ .short 5 # Header: version
+ .short 0 # Header: padding
+ .long 1 # Header: compilation unit count
+ .long 0 # Header: local type unit count
+ .long 0 # Header: foreign type unit count
+ .long 3 # Header: bucket count
+ .long 3 # Header: name count
+ .long .Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+ .long 8 # Header: augmentation string size
+ .ascii "LLVM0700" # Header: augmentation string
+ .long .Lcu_begin0 # Compilation unit 0
+ .long 1 # Bucket 0
+ .long 2 # Bucket 1
+ .long 3 # Bucket 2
+ .long 177693 # Hash in Bucket 0
+ .long 2090499946 # Hash in Bucket 1
+ .long 193495088 # Hash in Bucket 2
+ .long .Linfo_string3 # String in Bucket 0: x
+ .long .Linfo_string5 # String in Bucket 1: main
+ .long .Linfo_string4 # String in Bucket 2: int
+ .long .Lnames1-.Lnames_entries0 # Offset in Bucket 0
+ .long .Lnames2-.Lnames_entries0 # Offset in Bucket 1
+ .long .Lnames0-.Lnames_entries0 # Offset in Bucket 2
+.Lnames_abbrev_start0:
+ .byte 1 # Abbrev code
+ .byte 52 # DW_TAG_variable
+ .byte 3 # DW_IDX_die_offset
+ .byte 19 # DW_FORM_ref4
+ .byte 4 # DW_IDX_parent
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # End of abbrev
+ .byte 0 # End of abbrev
+ .byte 2 # Abbrev code
+ .byte 46 # DW_TAG_subprogram
+ .byte 3 # DW_IDX_die_offset
+ .byte 19 # DW_FORM_ref4
+ .byte 4 # DW_IDX_parent
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # End of abbrev
+ .byte 0 # End of abbrev
+ .byte 3 # Abbrev code
+ .byte 36 # DW_TAG_base_type
+ .byte 3 # DW_IDX_die_offset
+ .byte 19 # DW_FORM_ref4
+ .byte 4 # DW_IDX_parent
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # End of abbrev
+ .byte 0 # End of abbrev
+ .byte 0 # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames1:
+.L2:
+ .byte 1 # Abbreviation code
+ .long 35 # DW_IDX_die_offset
+ .byte 0 # DW_IDX_parent
+ # End of list: x
+.Lnames2:
+.L0:
+ .byte 2 # Abbreviation code
+ .long 58 # DW_IDX_die_offset
+ .byte 0 # DW_IDX_parent
+ # End of list: main
+.Lnames0:
+.L1:
+ .byte 3 # Abbreviation code
+ .long 54 # DW_IDX_die_offset
+ .byte 0 # DW_IDX_parent
+ # End of list: int
+ .p2align 2, 0x0
+.Lnames_end0:
+ .ident "clang version 17.0.4 (https://git.internal.tfbnw.net/repos/git/rw/osmeta/external/llvm-project 8d1fd9f463cb31caf429b83cf7a5baea5f67e54a)"
+ .section ".note.GNU-stack","",@progbits
+ .addrsig
+ .section .debug_line,"",@progbits
+.Lline_table_start0:
diff --git a/bolt/test/X86/icf-safe-icp.test b/bolt/test/X86/icf-safe-icp.test
new file mode 100644
index 0000000000000..a9227d311edce
--- /dev/null
+++ b/bolt/test/X86/icf-safe-icp.test
@@ -0,0 +1,148 @@
+## Check that BOLT handles correctly folding functions with --icf=safe
+## that can be referenced through a non control flow instruction when ICP optimization is enabled.
+## This tests also checks that destructors are folded.
+
+# REQUIRES: system-linux, asserts
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t1.o
+# RUN: %clang %cflags %t1.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding Derived3Destructor into Derived2Destructor
+# ICFCHECK-NEXT: folding Derived3Func into Derived2Func
+
+# SAFEICFCHECK: skipping function with reference taken Derived3Func
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding Derived3Destructor into Derived2Destructor
+# SAFEICFCHECK-NEXT: ===---------
+
+
+## generate profile
+## clang++ -O2 -fprofile-generate=. main.cpp -c -o mainProf.o
+## PROF=test.profdata
+## clang++ -m64 -fprofile-use=$PROF \
+## -mllvm -disable-icp=true -mllvm -print-after-all \
+## -g0 -flto=thin -fwhole-program-vtables -fno-split-lto-unit -O2 \
+## -fdebug-types-section \
+## main.cpp -c -o mainProfLTO.bc
+## PASS='pgo-icall-prom'
+## clang++ -m64 -fprofile-use=$PROF \
+## -O3 -Rpass=$PASS \
+## -mllvm -print-before=$PASS \
+## -mllvm -print-after=$PASS \
+## -mllvm -filter-print-funcs=main \
+## -mllvm -debug-only=$PASS \
+## -x ir \
+## mainProfLTO.bc -c -o mainProfFinal.o
+
+## class Base {
+## public:
+## virtual int func(int a, int b) const = 0;
+##
+## virtual ~Base() {};
+## };
+##
+## class Derived2 : public Base {
+## int c = 5;
+## public:
+## __attribute__((noinline)) int func(int a, int b)const override { return a * (a - b) + this->c; }
+##
+## ~Derived2() {}
+## };
+##
+## class Derived3 : public Base {
+## int c = 500;
+## public:
+## __attribute__((noinline)) int func(int a, int b) const override { return a * (a - b) + this->c; }
+## ~Derived3() {}
+## };
+##
+## __attribute__((noinline)) Base *createType(int a) {
+## Base *base = nullptr;
+## if (a == 4)
+## base = new Derived2();
+## else
+## base = new Derived3();
+## return base;
+## }
+##
+## extern int returnFive();
+## extern int returnFourOrFive(int val);
+## int main(int argc, char **argv) {
+## int sum = 0;
+## int a = returnFourOrFive(argc);
+## int b = returnFive();
+## Base *ptr = createType(a);
+## Base *ptr2 = createType(b);
+## sum += ptr->func(b, a) + ptr2->func(b, a);
+## return 0;
+## }
+## clang++ -c helper.cpp -o helper.o
+## int FooVar = 1;
+## int BarVar = 2;
+##
+## int fooGlobalFuncHelper(int a, int b) {
+## return 5;
+## }
+## Manually modified to remove "extra" assembly.
+ .globl main
+ .type main,@function
+main:
+ leaq Derived3Func(%rip), %rcx
+ callq Derived3Func
+ .size main, .-main
+
+ .weak Derived2Func
+ .type Derived2Func,@function
+Derived2Func:
+ imull %esi, %eax
+ retq
+ .size Derived2Func, .-Derived2Func
+
+ .weak Derived2Destructor
+ .type Derived2Destructor,@function
+Derived2Destructor:
+ jmp _ZdlPvm@PLT
+ .size Derived2Destructor, .-Derived2Destructor
+
+ .weak Derived3Func
+ .type Derived3Func,@function
+Derived3Func:
+ imull %esi, %eax
+ retq
+ .size Derived3Func, .-Derived3Func
+
+ .weak _ZN4BaseD2Ev
+ .type _ZN4BaseD2Ev,@function
+_ZN4BaseD2Ev:
+ retq
+ .size _ZN4BaseD2Ev, .-_ZN4BaseD2Ev
+
+ .weak Derived3Destructor
+ .type Derived3Destructor,@function
+Derived3Destructor:
+ jmp _ZdlPvm@PLT
+ .size Derived3Destructor, .-Derived3Destructor
+
+ .type _ZTV8Derived2,@object
+ .section .data.rel.ro._ZTV8Derived2,"awG",@progbits,_ZTV8Derived2,comdat
+ .weak _ZTV8Derived2
+_ZTV8Derived2:
+ .quad 0
+ .quad _ZTI8Derived2
+ .quad Derived2Func
+ .quad _ZN4BaseD2Ev
+ .quad Derived2Destructor
+ .size _ZTV8Derived2, 40
+
+ .type _ZTV8Derived3,@object
+ .section .data.rel.ro._ZTV8Derived3,"awG",@progbits,_ZTV8Derived3,comdat
+ .weak _ZTV8Derived3
+_ZTV8Derived3:
+ .quad 0
+ .quad _ZTI8Derived3
+ .quad Derived3Func
+ .quad _ZN4BaseD2Ev
+ .quad Derived3Destructor
+ .size _ZTV8Derived3, 40
diff --git a/bolt/test/X86/icf-safe-process-rela-data.test b/bolt/test/X86/icf-safe-process-rela-data.test
new file mode 100644
index 0000000000000..cf71f55257777
--- /dev/null
+++ b/bolt/test/X86/icf-safe-process-rela-data.test
@@ -0,0 +1,64 @@
+## Check that BOLT handles correctly folding functions with --icf=safe that are only referenced from a .rela.data section.
+
+# REQUIRES: system-linux, asserts
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t1.o
+# RUN: %clang %cflags %t1.o -o %t.exe -Wl,-q -no-pie
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding barAddFunc into fooAddFunc
+
+# SAFEICFCHECK: skipping function with reference taken fooAddFunc
+# SAFEICFCHECK-NEXT: skipping function with reference taken barAddFunc
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: ===---------
+
+## clang++ main.cpp
+## Other functions removed for brevity.
+## int main(int argc, char **argv) {
+## const static int (*const funcGlobalBarAdd)(int, int) = barAddHdlper;
+## const int (* const funcGlobalBarMul)(int, int) = fooGlobalFuncHelper;
+## helper2(funcGlobalBarAdd, funcGlobalFooAdd, 3, 4)
+## }
+## Extra assembly removed.
+
+ .globl fooAddFunc
+ .type fooAddFunc,@function
+fooAddFunc:
+ addl -8(%rbp), %eax
+ retq
+ .size fooAddFunc, .-fooAddFunc
+
+ .globl barAddFunc
+ .type barAddFunc,@function
+barAddFunc:
+ addl -8(%rbp), %eax
+ retq
+ .size barAddFunc, .-barAddFunc
+
+ .globl helperFunc
+ .type helperFunc,@function
+helperFunc:
+ retq
+ .size helperFunc, .-helperFunc
+
+ .globl main
+ .type main,@function
+main:
+ movq localStaticVarBarAdd, %rdi
+ movq localStaticVarFooAdd, %rsi
+ callq helperFunc
+ retq
+ .size main, .-main
+
+ .type localStaticVarBarAdd,@object # @localStaticVarBarAdd
+ .data
+localStaticVarBarAdd:
+ .quad barAddFunc
+ .size localStaticVarBarAdd, 8
+
+ .type localStaticVarFooAdd,@object # @localStaticVarFooAdd
+localStaticVarFooAdd:
+ .quad fooAddFunc
+ .size localStaticVarFooAdd, 8
diff --git a/bolt/test/X86/icf-safe-test1-no-relocs.test b/bolt/test/X86/icf-safe-test1-no-relocs.test
new file mode 100644
index 0000000000000..b4e55a6d5504f
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test1-no-relocs.test
@@ -0,0 +1,20 @@
+## Check that BOLT reports an error for a binary with no relocations with the --icf=safe option.
+
+# REQUIRES: system-linux, asserts
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t1.o
+# RUN: %clang %cflags %t1.o -o %t.exe
+# RUN: not llvm-bolt --no-threads %t.exe --icf=safe -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+# SAFEICFCHECK: BOLT-ERROR: binary built without relocations. Safe ICF is not supported
+
+## int main(int argc, char **argv) {
+## return temp;
+## }
+ .globl main
+ .type main,@function
+main:
+ .cfi_startproc
+ retq
+.Lfunc_end8:
+ .size main, .-main
+ .cfi_endproc
diff --git a/bolt/test/X86/icf-safe-test1.test b/bolt/test/X86/icf-safe-test1.test
new file mode 100644
index 0000000000000..8a8e5ccf38e7c
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test1.test
@@ -0,0 +1,98 @@
+## Check that BOLT handles correctly folding functions with --icf=safe that can be referenced by non-control flow instructions.
+## It invokes BOLT twice first testing CFG path, and second when functions have to be disassembled.
+
+# REQUIRES: system-linux, asserts
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t1.o
+# RUN: %clang %cflags %t1.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug-only=bolt-icf \
+# RUN: -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug-only=bolt-icf \
+# RUN: -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug-only=bolt-icf \
+# RUN: --skip-funcs=helper1Func,main -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECKNOCFG %s
+
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding barAddFunc into fooAddFunc
+# ICFCHECK-NEXT: folding barSubFunc into fooSubFunc
+
+# SAFEICFCHECK: skipping function with reference taken barAddFunc
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: folding barSubFunc into fooSubFunc
+# SAFEICFCHECK-NEXT: ===---------
+
+# SAFEICFCHECKNOCFG: skipping function with reference taken barAddFunc
+# SAFEICFCHECKNOCFG-NEXT: ICF iteration 1
+# SAFEICFCHECKNOCFG-NEXT: folding barSubFunc into fooSubFunc
+# SAFEICFCHECKNOCFG-NEXT: ===---------
+
+## clang++ -c main.cpp -o main.o
+## extern int FooVar;
+## extern int BarVar;
+## [[clang::noinline]]
+## int fooSub(int a, int b) {
+## return a - b;
+## }
+## [[clang::noinline]]
+## int barSub(int a, int b) {
+## return a - b;
+## }
+## [[clang::noinline]]
+## int fooAdd(int a, int b) {
+## return a + b;
+## }
+## [[clang::noinline]]
+## int barAdd(int a, int b) {
+## return a + b;
+## }
+## int main(int argc, char **argv) {
+## int temp = helper1(barAdd, FooVar, BarVar) +
+## fooSub(FooVar, BarVar) +
+## barSub(FooVar, BarVar) + fooAdd(FooVar, BarVar);
+## return temp;
+## }
+ .globl fooSubFunc
+ .type fooSubFunc,@function
+fooSubFunc:
+ subl -8(%rbp), %eax
+ retq
+ .size fooSubFunc, .-fooSubFunc
+
+ .globl barSubFunc
+ .type barSubFunc,@function
+barSubFunc:
+ subl -8(%rbp), %eax
+ retq
+ .size barSubFunc, .-barSubFunc
+
+ .globl fooAddFunc
+ .type fooAddFunc,@function
+fooAddFunc:
+ addl -8(%rbp), %eax
+ retq
+ .size fooAddFunc, .-fooAddFunc
+
+ .globl barAddFunc
+ .type barAddFunc,@function
+barAddFunc:
+ addl -8(%rbp), %eax
+ retq
+ .size barAddFunc, .-barAddFunc
+
+ .globl helper1Func
+ .type helper1Func,@function
+helper1Func:
+ leaq barAddFunc(%rip), %rax
+ cmpq %rax, -16(%rbp)
+ retq
+ .size helper1Func, .-helper1Func
+
+ .globl main
+ .type main,@function
+main:
+ leaq barAddFunc(%rip), %rdi
+ callq helper1Func
+ callq fooSubFunc
+ callq barSubFunc
+ callq fooAddFunc
+ retq
+ .size main, .-main
diff --git a/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
new file mode 100644
index 0000000000000..ea2d8a5f11e06
--- /dev/null
+++ b/bolt/test/X86/icf-safe-test2GlobalConstPtrNoPic.test
@@ -0,0 +1,95 @@
+## Check that BOLT handles correctly folding functions with --icf=safe that can be referenced by non-control flow instructions,
+## when binary is built with -fno-PIC/-fno-PIE.
+
+# REQUIRES: system-linux, asserts
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t1.o
+# RUN: %clang %cflags %t1.o -o %t.exe -Wl,-q -no-pie
+# RUN: llvm-bolt --no-threads %t.exe --icf -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
+# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
+
+# ICFCHECK: ICF iteration 1
+# ICFCHECK-NEXT: folding barAddFunc into fooAddFunc
+# ICFCHECK-NEXT: folding barMulFunc into fooMulFunc
+
+# SAFEICFCHECK: skipping function with reference taken fooMulFunc
+# SAFEICFCHECK-NEXT: skipping function with reference taken barMulFunc
+# SAFEICFCHECK-NEXT: skipping function with reference taken barAddFunc
+# SAFEICFCHECK-NEXT: ICF iteration 1
+# SAFEICFCHECK-NEXT: ===---------
+
+## clang++ main.cpp -c -o -fno-PIC
+## Similar code gets generated for external reference function.
+## Other functions removed for brevity.
+## const static int (*const funcGlobalBarAdd)(int, int) = barAdd;
+## const int (*const funcGlobalBarMul)(int, int) = barMul;
+## int main(int argc, char **argv) {
+## int temp = helper1(funcGlobalBarAdd, FooVar, BarVar)
+## return temp;
+## }
+## Manually modified to remove "extra" assembly.
+ .globl fooMulFunc
+ .type fooMulFunc,@function
+fooMulFunc:
+ imull -8(%rbp), %eax
+ retq
+ .size fooMulFunc, .-fooMulFunc
+
+ .globl barMulFunc
+ .type barMulFunc,@function
+barMulFunc:
+ imull -8(%rbp), %eax
+ retq
+ .size barMulFunc, .-barMulFunc
+
+ .globl fooAddFunc
+ .type fooAddFunc,@function
+fooAddFunc:
+ addl -8(%rbp), %eax
+ retq
+ .size fooAddFunc, .-fooAddFunc
+
+ .globl barAddFunc
+ .type barAddFunc,@function
+barAddFunc:
+ addl -8(%rbp), %eax
+ retq
+ .size barAddFunc, .-barAddFunc
+
+ .globl helperFunc
+ .type helperFunc,@function
+helperFunc:
+ movabsq $barAddFunc, %rax
+ cmpq %rax, -16(%rbp)
+ retq
+ .size helperFunc, .-helperFunc
+
+ .globl main
+ .type main,@function
+main:
+ movl FooVar, %esi
+ movl BarVar, %edx
+ movabsq $barAddFunc, %rdi
+ callq helperFunc
+ movabsq $fooMulFunc, %rdi
+ movabsq $barMulFunc, %rsi
+ retq
+ .size main, .-main
+
+ .type FooVar,@object
+ .data
+ .globl FooVar
+FooVar:
+ .long 1
+ .size FooVar, 4
+
+ .type BarVar,@object
+ .globl BarVar
+BarVar:
+ .long 2
+ .size BarVar, 4
+
+ .type .L.str,@object
+ .section .rodata.str1.1,"aMS",@progbits,1
+.L.str:
+ .asciz "val: %d\n"
+ .size .L.str, 9
diff --git a/bolt/test/X86/linux-static-keys.s b/bolt/test/X86/linux-static-keys.s
index 0bd17a375d882..d34dd640ef879 100644
--- a/bolt/test/X86/linux-static-keys.s
+++ b/bolt/test/X86/linux-static-keys.s
@@ -35,13 +35,13 @@ _start:
.L0:
jmp L1
# CHECK: jit
-# CHECK-SAME: # ID: 1 {{.*}} # Likely: 0 # InitValue: 1
+# CHECK-SAME: # ID: 1 {{.*}} # Likely: 1 # InitValue: 0
nop
L1:
.nops 5
- jmp .L0
# CHECK: jit
-# CHECK-SAME: # ID: 2 {{.*}} # Likely: 1 # InitValue: 1
+# CHECK-SAME: # ID: 2 {{.*}} # Likely: 0 # InitValue: 0
+ jmp .L0
## Check that a branch profile associated with a NOP is handled properly when
## dynamic branch is created.
@@ -67,18 +67,24 @@ foo:
.type __start___jump_table, %object
__start___jump_table:
- .long .L0 - . # Jump address
- .long L1 - . # Target address
- .quad 1 # Key address
+ .long .L0 - . # Jump address
+ .long L1 - . # Target address
+ .quad fake_static_key + 1 - . # Key address; LSB = 1 : likely
- .long L1 - . # Jump address
- .long L2 - . # Target address
- .quad 0 # Key address
+ .long L1 - . # Jump address
+ .long L2 - . # Target address
+ .quad fake_static_key -. # Key address; LSB = 0 : unlikely
.globl __stop___jump_table
.type __stop___jump_table, %object
__stop___jump_table:
+## Staic keys (we just use the label ignoring the format of the keys).
+ .data
+ .align 8
+fake_static_key:
+ .quad 0
+
## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits
diff --git a/bolt/test/binary-analysis/AArch64/Inputs/dummy.txt b/bolt/test/binary-analysis/AArch64/Inputs/dummy.txt
new file mode 100644
index 0000000000000..2995a4d0e7491
--- /dev/null
+++ b/bolt/test/binary-analysis/AArch64/Inputs/dummy.txt
@@ -0,0 +1 @@
+dummy
\ No newline at end of file
diff --git a/bolt/test/binary-analysis/AArch64/cmdline-args.test b/bolt/test/binary-analysis/AArch64/cmdline-args.test
new file mode 100644
index 0000000000000..e414818644a3b
--- /dev/null
+++ b/bolt/test/binary-analysis/AArch64/cmdline-args.test
@@ -0,0 +1,33 @@
+# This file tests error messages produced on invalid command line arguments.
+# It also checks that help messages are generated as expected.
+
+# Verify that an error message is provided if an input file is missing or incorrect
+
+RUN: not llvm-bolt-binary-analysis 2>&1 | FileCheck -check-prefix=NOFILEARG %s
+NOFILEARG: llvm-bolt-binary-analysis: Not enough positional command line arguments specified!
+NOFILEARG-NEXT: Must specify at least 1 positional argument: See: {{.*}}llvm-bolt-binary-analysis --help
+
+RUN: not llvm-bolt-binary-analysis non-existing-file 2>&1 | FileCheck -check-prefix=NONEXISTINGFILEARG %s
+NONEXISTINGFILEARG: llvm-bolt-binary-analysis: 'non-existing-file': No such file or directory.
+
+RUN: not llvm-bolt-binary-analysis %p/Inputs/dummy.txt 2>&1 | FileCheck -check-prefix=NOELFFILEARG %s
+NOELFFILEARG: llvm-bolt-binary-analysis: '{{.*}}/Inputs/dummy.txt': The file was not recognized as a valid object file.
+
+RUN: %clang %cflags %p/../../Inputs/asm_foo.s %p/../../Inputs/asm_main.c -o %t.exe
+RUN: llvm-bolt-binary-analysis %t.exe 2>&1 | FileCheck -check-prefix=VALIDELFFILEARG --allow-empty %s
+# Check that there are no BOLT-WARNING or BOLT-ERROR output lines
+VALIDELFFILEARG: BOLT-INFO:
+VALIDELFFILEARG-NOT: BOLT-WARNING:
+VALIDELFFILEARG-NOT: BOLT-ERROR:
+
+# Check --help output
+
+RUN: llvm-bolt-binary-analysis --help 2>&1 | FileCheck -check-prefix=HELP %s
+
+HELP: OVERVIEW: BinaryAnalysis
+HELP-EMPTY:
+HELP-NEXT: USAGE: llvm-bolt-binary-analysis [options]
+HELP-EMPTY:
+HELP-NEXT: OPTIONS:
+HELP-EMPTY:
+HELP-NEXT: Generic Options:
diff --git a/bolt/test/binary-analysis/AArch64/lit.local.cfg b/bolt/test/binary-analysis/AArch64/lit.local.cfg
new file mode 100644
index 0000000000000..6f247dd52e82f
--- /dev/null
+++ b/bolt/test/binary-analysis/AArch64/lit.local.cfg
@@ -0,0 +1,7 @@
+if "AArch64" not in config.root.targets:
+ config.unsupported = True
+
+flags = "--target=aarch64-linux-gnu -nostartfiles -nostdlib -ffreestanding -Wl,--emit-relocs"
+
+config.substitutions.insert(0, ("%cflags", f"%cflags {flags}"))
+config.substitutions.insert(0, ("%cxxflags", f"%cxxflags {flags}"))
diff --git a/bolt/test/lit.cfg.py b/bolt/test/lit.cfg.py
index da3ae34ba3bdd..0d05229be2bf3 100644
--- a/bolt/test/lit.cfg.py
+++ b/bolt/test/lit.cfg.py
@@ -110,6 +110,7 @@
),
ToolSubst("llvm-boltdiff", unresolved="fatal"),
ToolSubst("llvm-bolt-heatmap", unresolved="fatal"),
+ ToolSubst("llvm-bolt-binary-analysis", unresolved="fatal"),
ToolSubst("llvm-bat-dump", unresolved="fatal"),
ToolSubst("perf2bolt", unresolved="fatal"),
ToolSubst("yaml2obj", unresolved="fatal"),
diff --git a/bolt/test/merge-fdata-bat-no-lbr.test b/bolt/test/merge-fdata-bat-no-lbr.test
new file mode 100644
index 0000000000000..fd5cd16263356
--- /dev/null
+++ b/bolt/test/merge-fdata-bat-no-lbr.test
@@ -0,0 +1,20 @@
+## Check that merge-fdata correctly handles merging two fdata files with both boltedcollection and no_lbr tags.
+
+# REQUIRES: system-linux
+
+# RUN: split-file %s %t
+# RUN: merge-fdata %t/a.fdata %t/b.fdata -o %t/merged.fdata
+# RUN: FileCheck %s --input-file %t/merged.fdata
+
+# CHECK: boltedcollection
+# CHECK: no_lbr
+# CHECK: main 2
+
+#--- a.fdata
+boltedcollection
+no_lbr
+main 1
+#--- b.fdata
+boltedcollection
+no_lbr
+main 1
diff --git a/bolt/test/merge-fdata-lbr-mode.test b/bolt/test/merge-fdata-lbr-mode.test
new file mode 100644
index 0000000000000..2cd3853194288
--- /dev/null
+++ b/bolt/test/merge-fdata-lbr-mode.test
@@ -0,0 +1,15 @@
+## Check that merge-fdata tool doesn't falsely print no_lbr when not in no-lbr mode
+
+# REQUIRES: system-linux
+
+# RUN: split-file %s %t
+# RUN: merge-fdata %t/a.fdata %t/b.fdata -o %t/merged.fdata
+# RUN: FileCheck %s --input-file %t/merged.fdata
+
+# CHECK-NOT: no_lbr
+# CHECK: 1 main 0 1 main 2 1 3
+
+#--- a.fdata
+1 main 0 1 main 2 0 1
+#--- b.fdata
+1 main 0 1 main 2 1 2
diff --git a/bolt/test/merge-fdata-mixed-bat-no-lbr.test b/bolt/test/merge-fdata-mixed-bat-no-lbr.test
new file mode 100644
index 0000000000000..eeb3a0e23b0cc
--- /dev/null
+++ b/bolt/test/merge-fdata-mixed-bat-no-lbr.test
@@ -0,0 +1,16 @@
+## Check that merge-fdata doesn't incorrectly merge two fdata files with boltedcollection and no_lbr tags.
+
+# REQUIRES: system-linux
+
+# RUN: split-file %s %t
+# RUN: not merge-fdata %t/a.fdata %t/b.fdata 2>&1 | FileCheck %s
+
+# CHECK: cannot mix profile with and without boltedcollection
+
+#--- a.fdata
+boltedcollection
+no_lbr
+main 1
+#--- b.fdata
+no_lbr
+main 1
diff --git a/bolt/test/merge-fdata-mixed-mode.test b/bolt/test/merge-fdata-mixed-mode.test
new file mode 100644
index 0000000000000..f897fec5d9db4
--- /dev/null
+++ b/bolt/test/merge-fdata-mixed-mode.test
@@ -0,0 +1,15 @@
+## Check that merge-fdata tool correctly reports error message
+## when trying to merge 'no-lbr' and 'lbr' profiles
+
+# REQUIRES: system-linux
+
+# RUN: split-file %s %t
+# RUN: not merge-fdata %t/a.fdata %t/b.fdata 2>&1 | FileCheck %s
+
+# CHECK: cannot mix profile with and without no_lbr
+
+#--- a.fdata
+no_lbr
+main 1
+#--- b.fdata
+main 1
diff --git a/bolt/test/merge-fdata-no-lbr-mode.test b/bolt/test/merge-fdata-no-lbr-mode.test
new file mode 100644
index 0000000000000..9dfad99f79994
--- /dev/null
+++ b/bolt/test/merge-fdata-no-lbr-mode.test
@@ -0,0 +1,18 @@
+## Check that merge-fdata tool correctly processes fdata files with header
+## string produced by no-lbr mode (no_lbr)
+
+# REQUIRES: system-linux
+
+# RUN: split-file %s %t
+# RUN: merge-fdata %t/a.fdata %t/b.fdata -o %t/merged.fdata
+# RUN: FileCheck %s --input-file %t/merged.fdata
+
+# CHECK: no_lbr
+# CHECK: main 2
+
+#--- a.fdata
+no_lbr
+main 1
+#--- b.fdata
+no_lbr
+main 1
diff --git a/bolt/test/unreadable-profile.test b/bolt/test/unreadable-profile.test
index fe1ca93f3221e..4c1cd8af0a62c 100644
--- a/bolt/test/unreadable-profile.test
+++ b/bolt/test/unreadable-profile.test
@@ -1,4 +1,4 @@
-REQUIRES: system-linux
+REQUIRES: system-linux, non-root-user
RUN: touch %t.profile && chmod 000 %t.profile
RUN: %clang %S/Inputs/hello.c -o %t
diff --git a/bolt/tools/CMakeLists.txt b/bolt/tools/CMakeLists.txt
index 22ea3b9bd805f..3383902cffc40 100644
--- a/bolt/tools/CMakeLists.txt
+++ b/bolt/tools/CMakeLists.txt
@@ -7,3 +7,4 @@ add_subdirectory(llvm-bolt-fuzzer)
add_subdirectory(bat-dump)
add_subdirectory(merge-fdata)
add_subdirectory(heatmap)
+add_subdirectory(binary-analysis)
diff --git a/bolt/tools/binary-analysis/CMakeLists.txt b/bolt/tools/binary-analysis/CMakeLists.txt
new file mode 100644
index 0000000000000..841fc5b371185
--- /dev/null
+++ b/bolt/tools/binary-analysis/CMakeLists.txt
@@ -0,0 +1,19 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ MC
+ Object
+ Support
+ )
+
+add_bolt_tool(llvm-bolt-binary-analysis
+ binary-analysis.cpp
+ DISABLE_LLVM_LINK_LLVM_DYLIB
+ )
+
+target_link_libraries(llvm-bolt-binary-analysis
+ PRIVATE
+ LLVMBOLTRewrite
+ LLVMBOLTUtils
+ )
+
+add_dependencies(bolt llvm-bolt-binary-analysis)
diff --git a/bolt/tools/binary-analysis/binary-analysis.cpp b/bolt/tools/binary-analysis/binary-analysis.cpp
new file mode 100644
index 0000000000000..b03fee3e025ae
--- /dev/null
+++ b/bolt/tools/binary-analysis/binary-analysis.cpp
@@ -0,0 +1,122 @@
+//===- bolt/tools/binary-analysis/binary-analysis.cpp ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a generic binary analysis tool, where multiple different specific
+// binary analyses can be plugged in to. The binary analyses are mostly built
+// on top of BOLT components.
+//
+//===----------------------------------------------------------------------===//
+
+#include "bolt/Rewrite/RewriteInstance.h"
+#include "bolt/Utils/CommandLineOpts.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/VirtualFileSystem.h"
+
+#define DEBUG_TYPE "bolt"
+
+using namespace llvm;
+using namespace object;
+using namespace bolt;
+
+namespace opts {
+
+static cl::OptionCategory *BinaryAnalysisCategories[] = {
+ &BinaryAnalysisCategory};
+
+static cl::opt InputFilename(cl::Positional,
+ cl::desc(""),
+ cl::Required,
+ cl::cat(BinaryAnalysisCategory),
+ cl::sub(cl::SubCommand::getAll()));
+
+} // namespace opts
+
+static StringRef ToolName = "llvm-bolt-binary-analysis";
+
+static void report_error(StringRef Message, std::error_code EC) {
+ assert(EC);
+ errs() << ToolName << ": '" << Message << "': " << EC.message() << ".\n";
+ exit(1);
+}
+
+static void report_error(StringRef Message, Error E) {
+ assert(E);
+ errs() << ToolName << ": '" << Message << "': " << toString(std::move(E))
+ << ".\n";
+ exit(1);
+}
+
+void ParseCommandLine(int argc, char **argv) {
+ cl::HideUnrelatedOptions(ArrayRef(opts::BinaryAnalysisCategories));
+ // Register the target printer for --version.
+ cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
+
+ cl::ParseCommandLineOptions(argc, argv, "BinaryAnalysis\n");
+}
+
+static std::string GetExecutablePath(const char *Argv0) {
+ SmallString<256> ExecutablePath(Argv0);
+ // Do a PATH lookup if Argv0 isn't a valid path.
+ if (!llvm::sys::fs::exists(ExecutablePath))
+ if (llvm::ErrorOr P =
+ llvm::sys::findProgramByName(ExecutablePath))
+ ExecutablePath = *P;
+ return std::string(ExecutablePath.str());
+}
+
+int main(int argc, char **argv) {
+ // Print a stack trace if we signal out.
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
+ PrettyStackTraceProgram X(argc, argv);
+
+ std::string ToolPath = GetExecutablePath(argv[0]);
+
+ llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+
+ // Initialize targets and assembly printers/parsers.
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllAsmParsers();
+ llvm::InitializeAllDisassemblers();
+
+ llvm::InitializeAllTargets();
+ llvm::InitializeAllAsmPrinters();
+
+ ParseCommandLine(argc, argv);
+
+ opts::BinaryAnalysisMode = true;
+
+ if (!sys::fs::exists(opts::InputFilename))
+ report_error(opts::InputFilename, errc::no_such_file_or_directory);
+
+ Expected> BinaryOrErr =
+ createBinary(opts::InputFilename);
+ if (Error E = BinaryOrErr.takeError())
+ report_error(opts::InputFilename, std::move(E));
+ Binary &Binary = *BinaryOrErr.get().getBinary();
+
+ if (auto *e = dyn_cast(&Binary)) {
+ auto RIOrErr = RewriteInstance::create(e, argc, argv, ToolPath);
+ if (Error E = RIOrErr.takeError())
+ report_error(opts::InputFilename, std::move(E));
+ RewriteInstance &RI = *RIOrErr.get();
+ if (Error E = RI.run())
+ report_error(opts::InputFilename, std::move(E));
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/bolt/tools/merge-fdata/merge-fdata.cpp b/bolt/tools/merge-fdata/merge-fdata.cpp
index 89ca46c1c0a8f..74a5f8ca2d477 100644
--- a/bolt/tools/merge-fdata/merge-fdata.cpp
+++ b/bolt/tools/merge-fdata/merge-fdata.cpp
@@ -22,6 +22,7 @@
#include "llvm/Support/Signals.h"
#include "llvm/Support/ThreadPool.h"
#include
+#include
#include
#include
@@ -265,55 +266,70 @@ bool isYAML(const StringRef Filename) {
void mergeLegacyProfiles(const SmallVectorImpl &Filenames) {
errs() << "Using legacy profile format.\n";
std::optional BoltedCollection;
+ std::optional NoLBRCollection;
std::mutex BoltedCollectionMutex;
- typedef StringMap ProfileTy;
+ struct CounterTy {
+ uint64_t Exec{0};
+ uint64_t Mispred{0};
+ CounterTy &operator+=(const CounterTy &O) {
+ Exec += O.Exec;
+ Mispred += O.Mispred;
+ return *this;
+ }
+ CounterTy operator+(const CounterTy &O) { return *this += O; }
+ };
+ typedef StringMap ProfileTy;
auto ParseProfile = [&](const std::string &Filename, auto &Profiles) {
const llvm::thread::id tid = llvm::this_thread::get_id();
if (isYAML(Filename))
report_error(Filename, "cannot mix YAML and legacy formats");
- ErrorOr> MB =
- MemoryBuffer::getFileOrSTDIN(Filename);
- if (std::error_code EC = MB.getError())
- report_error(Filename, EC);
- StringRef Buf = MB.get()->getBuffer();
+ std::ifstream FdataFile(Filename, std::ios::in);
+ std::string FdataLine;
+ std::getline(FdataFile, FdataLine);
+
+ auto checkMode = [&](const std::string &Key, std::optional &Flag) {
+ const bool KeyIsSet = FdataLine.rfind(Key, 0) == 0;
+
+ if (!Flag.has_value())
+ Flag = KeyIsSet;
+ else if (*Flag != KeyIsSet)
+ report_error(Filename, "cannot mix profile with and without " + Key);
+ if (KeyIsSet)
+ // Advance line
+ std::getline(FdataFile, FdataLine);
+ };
+
ProfileTy *Profile;
{
std::lock_guard Lock(BoltedCollectionMutex);
// Check if the string "boltedcollection" is in the first line
- if (Buf.starts_with("boltedcollection\n")) {
- if (!BoltedCollection.value_or(true))
- report_error(
- Filename,
- "cannot mix profile collected in BOLT and non-BOLT deployments");
- BoltedCollection = true;
- Buf = Buf.drop_front(17);
- } else {
- if (BoltedCollection.value_or(false))
- report_error(
- Filename,
- "cannot mix profile collected in BOLT and non-BOLT deployments");
- BoltedCollection = false;
- }
-
+ checkMode("boltedcollection", BoltedCollection);
+ // Check if the string "no_lbr" is in the first line
+ // (or second line if BoltedCollection is true)
+ checkMode("no_lbr", NoLBRCollection);
Profile = &Profiles[tid];
}
- SmallVector Lines;
- SplitString(Buf, Lines, "\n");
- for (StringRef Line : Lines) {
- size_t Pos = Line.rfind(" ");
- if (Pos == StringRef::npos)
- report_error(Filename, "Malformed / corrupted profile");
- StringRef Signature = Line.substr(0, Pos);
- uint64_t Count;
- if (Line.substr(Pos + 1, Line.size() - Pos).getAsInteger(10, Count))
- report_error(Filename, "Malformed / corrupted profile counter");
+ do {
+ StringRef Line(FdataLine);
+ CounterTy Count;
+ auto [Signature, ExecCount] = Line.rsplit(' ');
+ if (ExecCount.getAsInteger(10, Count.Exec))
+ report_error(Filename, "Malformed / corrupted execution count");
+ // Only LBR profile has misprediction field
+ if (!NoLBRCollection.value_or(false)) {
+ auto [SignatureLBR, MispredCount] = Signature.rsplit(' ');
+ Signature = SignatureLBR;
+ if (MispredCount.getAsInteger(10, Count.Mispred))
+ report_error(Filename, "Malformed / corrupted misprediction count");
+ }
+
Count += Profile->lookup(Signature);
Profile->insert_or_assign(Signature, Count);
- }
+ } while (std::getline(FdataFile, FdataLine));
};
// The final reduction has non-trivial cost, make sure each thread has at
@@ -330,14 +346,20 @@ void mergeLegacyProfiles(const SmallVectorImpl &Filenames) {
ProfileTy MergedProfile;
for (const auto &[Thread, Profile] : ParsedProfiles)
for (const auto &[Key, Value] : Profile) {
- uint64_t Count = MergedProfile.lookup(Key) + Value;
+ CounterTy Count = MergedProfile.lookup(Key) + Value;
MergedProfile.insert_or_assign(Key, Count);
}
if (BoltedCollection.value_or(false))
output() << "boltedcollection\n";
- for (const auto &[Key, Value] : MergedProfile)
- output() << Key << " " << Value << "\n";
+ if (NoLBRCollection.value_or(false))
+ output() << "no_lbr\n";
+ for (const auto &[Key, Value] : MergedProfile) {
+ output() << Key << " ";
+ if (!NoLBRCollection.value_or(false))
+ output() << Value.Mispred << " ";
+ output() << Value.Exec << "\n";
+ }
errs() << "Profile from " << Filenames.size() << " files merged.\n";
}
diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index 795eb4b904e3e..28b645cf021dd 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -157,17 +157,17 @@ static void genMarkdown(const ClangDocContext &CDCtx, const FunctionInfo &I,
for (const auto &N : I.Params) {
if (!First)
Stream << ", ";
- Stream << N.Type.Name + " " + N.Name;
+ Stream << N.Type.QualName + " " + N.Name;
First = false;
}
writeHeader(I.Name, 3, OS);
std::string Access = getAccessSpelling(I.Access).str();
if (Access != "")
- writeLine(genItalic(Access + " " + I.ReturnType.Type.Name + " " + I.Name +
- "(" + Stream.str() + ")"),
+ writeLine(genItalic(Access + " " + I.ReturnType.Type.QualName + " " +
+ I.Name + "(" + Stream.str() + ")"),
OS);
else
- writeLine(genItalic(I.ReturnType.Type.Name + " " + I.Name + "(" +
+ writeLine(genItalic(I.ReturnType.Type.QualName + " " + I.Name + "(" +
Stream.str() + ")"),
OS);
if (I.DefLoc)
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index b9db78cf7d688..f737fc75135a1 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -236,10 +236,10 @@ static RecordDecl *getRecordDeclForType(const QualType &T) {
return nullptr;
}
-TypeInfo getTypeInfoForType(const QualType &T) {
+TypeInfo getTypeInfoForType(const QualType &T, const PrintingPolicy &Policy) {
const TagDecl *TD = getTagDeclForType(T);
if (!TD)
- return TypeInfo(Reference(SymbolID(), T.getAsString()));
+ return TypeInfo(Reference(SymbolID(), T.getAsString(Policy)));
InfoType IT;
if (dyn_cast(TD)) {
@@ -250,7 +250,7 @@ TypeInfo getTypeInfoForType(const QualType &T) {
IT = InfoType::IT_default;
}
return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
- T.getAsString(), getInfoRelativePath(TD)));
+ T.getAsString(Policy), getInfoRelativePath(TD)));
}
static bool isPublic(const clang::AccessSpecifier AS,
@@ -379,10 +379,11 @@ static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
continue;
+ auto &LO = F->getLangOpts();
// Use getAccessUnsafe so that we just get the default AS_none if it's not
// valid, as opposed to an assert.
MemberTypeInfo &NewMember = I.Members.emplace_back(
- getTypeInfoForType(F->getTypeSourceInfo()->getType()),
+ getTypeInfoForType(F->getTypeSourceInfo()->getType(), LO),
F->getNameAsString(),
getFinalAccessSpecifier(Access, F->getAccessUnsafe()));
populateMemberTypeInfo(NewMember, F);
@@ -412,9 +413,10 @@ static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
}
static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
+ auto &LO = D->getLangOpts();
for (const ParmVarDecl *P : D->parameters()) {
FieldTypeInfo &FieldInfo = I.Params.emplace_back(
- getTypeInfoForType(P->getOriginalType()), P->getNameAsString());
+ getTypeInfoForType(P->getOriginalType(), LO), P->getNameAsString());
FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());
}
}
@@ -541,7 +543,8 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
bool &IsInAnonymousNamespace) {
populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
IsInAnonymousNamespace);
- I.ReturnType = getTypeInfoForType(D->getReturnType());
+ auto &LO = D->getLangOpts();
+ I.ReturnType = getTypeInfoForType(D->getReturnType(), LO);
parseParameters(I, D);
PopulateTemplateParameters(I.Template, D);
@@ -693,13 +696,11 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
// What this is a specialization of.
auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
- if (SpecOf.is()) {
- Specialization.SpecializationOf =
- getUSRForDecl(SpecOf.get());
- } else if (SpecOf.is()) {
- Specialization.SpecializationOf =
- getUSRForDecl(SpecOf.get());
- }
+ if (auto *CTD = dyn_cast(SpecOf))
+ Specialization.SpecializationOf = getUSRForDecl(CTD);
+ else if (auto *CTPSD =
+ dyn_cast(SpecOf))
+ Specialization.SpecializationOf = getUSRForDecl(CTPSD);
// Parameters to the specilization. For partial specializations, get the
// parameters "as written" from the ClassTemplatePartialSpecializationDecl
@@ -783,7 +784,8 @@ emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
return {};
Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
- Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
+ auto &LO = D->getLangOpts();
+ Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
if (Info.Underlying.Type.Name.empty()) {
// Typedef for an unnamed type. This is like "typedef struct { } Foo;"
// The record serializer explicitly checks for this syntax and constructs
@@ -809,7 +811,8 @@ emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
return {};
Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
- Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
+ auto &LO = D->getLangOpts();
+ Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
Info.IsUsing = true;
// Info is wrapped in its parent scope so is returned in the second position.
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index 9c8c93c5d16c7..959b11777e88d 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -646,9 +646,9 @@ void exportReplacements(const llvm::StringRef MainFilePath,
YAML << TUD;
}
-NamesAndOptions
+ChecksAndOptions
getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers) {
- NamesAndOptions Result;
+ ChecksAndOptions Result;
ClangTidyOptions Opts;
Opts.Checks = "*";
clang::tidy::ClangTidyContext Context(
@@ -661,7 +661,7 @@ getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers) {
}
for (const auto &Factory : Factories)
- Result.Names.insert(Factory.getKey());
+ Result.Checks.insert(Factory.getKey());
#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
SmallString<64> Buffer(AnalyzerCheckNamePrefix);
@@ -670,7 +670,7 @@ getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers) {
AllowEnablingAnalyzerAlphaCheckers)) {
Buffer.truncate(DefSize);
Buffer.append(AnalyzerCheck);
- Result.Names.insert(Buffer);
+ Result.Checks.insert(Buffer);
}
for (std::string OptionName : {
#define GET_CHECKER_OPTIONS
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.h b/clang-tools-extra/clang-tidy/ClangTidy.h
index 51d9e226c7946..4ffd49f6ebf50 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.h
+++ b/clang-tools-extra/clang-tidy/ClangTidy.h
@@ -58,12 +58,12 @@ class ClangTidyASTConsumerFactory {
std::vector getCheckNames(const ClangTidyOptions &Options,
bool AllowEnablingAnalyzerAlphaCheckers);
-struct NamesAndOptions {
- llvm::StringSet<> Names;
+struct ChecksAndOptions {
+ llvm::StringSet<> Checks;
llvm::StringSet<> Options;
};
-NamesAndOptions
+ChecksAndOptions
getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers = true);
/// Returns the effective check-specific options.
diff --git a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp
index f2ff27d85fb00..55ca4809f058a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp
@@ -12,20 +12,43 @@
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include
using namespace clang::ast_matchers;
+using clang::ast_matchers::internal::Matcher;
namespace clang::tidy::bugprone {
namespace {
-AST_MATCHER_P(QualType, hasCleanType, ast_matchers::internal::Matcher,
- InnerMatcher) {
+AST_MATCHER_P(QualType, hasCleanType, Matcher, InnerMatcher) {
return InnerMatcher.matches(
Node.getNonReferenceType().getUnqualifiedType().getCanonicalType(),
Finder, Builder);
}
+constexpr std::array NameList{
+ "::std::make_unique",
+ "::std::make_shared",
+};
+
+Matcher constructFrom(Matcher TypeMatcher,
+ Matcher ArgumentMatcher) {
+ return expr(
+ anyOf(
+ // construct optional
+ cxxConstructExpr(argumentCountIs(1U), hasType(TypeMatcher),
+ hasArgument(0U, ArgumentMatcher)),
+ // known template methods in std
+ callExpr(argumentCountIs(1),
+ callee(functionDecl(
+ matchers::matchesAnyListedName(NameList),
+ hasTemplateArgument(0, refersToType(TypeMatcher)))),
+ hasArgument(0, ArgumentMatcher))),
+ unless(anyOf(hasAncestor(typeLoc()),
+ hasAncestor(expr(matchers::hasUnevaluatedContext())))));
+}
+
} // namespace
OptionalValueConversionCheck::OptionalValueConversionCheck(
@@ -67,12 +90,9 @@ void OptionalValueConversionCheck::registerMatchers(MatchFinder *Finder) {
callExpr(argumentCountIs(1), callee(functionDecl(hasName("::std::move"))),
hasArgument(0, ignoringImpCasts(OptionalDereferenceMatcher)));
Finder->addMatcher(
- cxxConstructExpr(
- argumentCountIs(1U), hasType(BindOptionalType),
- hasArgument(0U, ignoringImpCasts(anyOf(OptionalDereferenceMatcher,
- StdMoveCallMatcher))),
- unless(anyOf(hasAncestor(typeLoc()),
- hasAncestor(expr(matchers::hasUnevaluatedContext())))))
+ expr(constructFrom(BindOptionalType,
+ ignoringImpCasts(anyOf(OptionalDereferenceMatcher,
+ StdMoveCallMatcher))))
.bind("expr"),
this);
}
diff --git a/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.h b/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.h
index 2d1570f7df8ab..e2fcccbfefb26 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.h
@@ -12,7 +12,6 @@
#include "../ClangTidyCheck.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
-#include
namespace clang::tidy::bugprone {
@@ -26,8 +25,7 @@ class UncheckedOptionalAccessCheck : public ClangTidyCheck {
public:
UncheckedOptionalAccessCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
- ModelOptions{
- Options.getLocalOrGlobal("IgnoreSmartPointerDereference", false)} {}
+ ModelOptions{Options.get("IgnoreSmartPointerDereference", false)} {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp
index 225e867c9b24f..d665c47d12bb4 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp
@@ -277,7 +277,7 @@ ProTypeMemberInitCheck::ProTypeMemberInitCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IgnoreArrays(Options.get("IgnoreArrays", false)),
- UseAssignment(Options.getLocalOrGlobal("UseAssignment", false)) {}
+ UseAssignment(Options.get("UseAssignment", false)) {}
void ProTypeMemberInitCheck::registerMatchers(MatchFinder *Finder) {
auto IsUserProvidedNonDelegatingConstructor =
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
index 7db9e29e8fd0e..8c386d5bc7945 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
@@ -119,11 +119,10 @@ void RvalueReferenceParamNotMovedCheck::check(
RvalueReferenceParamNotMovedCheck::RvalueReferenceParamNotMovedCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
- AllowPartialMove(Options.getLocalOrGlobal("AllowPartialMove", false)),
- IgnoreUnnamedParams(
- Options.getLocalOrGlobal("IgnoreUnnamedParams", false)),
+ AllowPartialMove(Options.get("AllowPartialMove", false)),
+ IgnoreUnnamedParams(Options.get("IgnoreUnnamedParams", false)),
IgnoreNonDeducedTemplateTypes(
- Options.getLocalOrGlobal("IgnoreNonDeducedTemplateTypes", false)) {}
+ Options.get("IgnoreNonDeducedTemplateTypes", false)) {}
void RvalueReferenceParamNotMovedCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
diff --git a/clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp b/clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
index 5e7a0e65690b7..7638bbc103d16 100644
--- a/clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
@@ -57,10 +57,9 @@ struct MissingIncludeInfo {
IncludeCleanerCheck::IncludeCleanerCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
- IgnoreHeaders(utils::options::parseStringList(
- Options.getLocalOrGlobal("IgnoreHeaders", ""))),
- DeduplicateFindings(
- Options.getLocalOrGlobal("DeduplicateFindings", true)) {
+ IgnoreHeaders(
+ utils::options::parseStringList(Options.get("IgnoreHeaders", ""))),
+ DeduplicateFindings(Options.get("DeduplicateFindings", true)) {
for (const auto &Header : IgnoreHeaders) {
if (!llvm::Regex{Header}.isValid())
configurationDiag("Invalid ignore headers regex '%0'") << Header;
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index c919d49b42873..bab1167fb15ff 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -36,6 +36,7 @@ add_clang_library(clangTidyModernizeModule STATIC
UseEmplaceCheck.cpp
UseEqualsDefaultCheck.cpp
UseEqualsDeleteCheck.cpp
+ UseIntegerSignComparisonCheck.cpp
UseNodiscardCheck.cpp
UseNoexceptCheck.cpp
UseNullptrCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index 1860759332063..fc46c72982fdc 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -37,6 +37,7 @@
#include "UseEmplaceCheck.h"
#include "UseEqualsDefaultCheck.h"
#include "UseEqualsDeleteCheck.h"
+#include "UseIntegerSignComparisonCheck.h"
#include "UseNodiscardCheck.h"
#include "UseNoexceptCheck.h"
#include "UseNullptrCheck.h"
@@ -76,6 +77,8 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck("modernize-pass-by-value");
CheckFactories.registerCheck(
"modernize-use-designated-initializers");
+ CheckFactories.registerCheck(
+ "modernize-use-integer-sign-comparison");
CheckFactories.registerCheck("modernize-use-ranges");
CheckFactories.registerCheck(
"modernize-use-starts-ends-with");
diff --git a/clang-tools-extra/clang-tidy/modernize/UseIntegerSignComparisonCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseIntegerSignComparisonCheck.cpp
new file mode 100644
index 0000000000000..8f807bc0a96d5
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseIntegerSignComparisonCheck.cpp
@@ -0,0 +1,171 @@
+//===--- UseIntegerSignComparisonCheck.cpp - clang-tidy -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseIntegerSignComparisonCheck.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::internal;
+
+namespace clang::tidy::modernize {
+
+/// Find if the passed type is the actual "char" type,
+/// not applicable to explicit "signed char" or "unsigned char" types.
+static bool isActualCharType(const clang::QualType &Ty) {
+ using namespace clang;
+ const Type *DesugaredType = Ty->getUnqualifiedDesugaredType();
+ if (const auto *BT = llvm::dyn_cast(DesugaredType))
+ return (BT->getKind() == BuiltinType::Char_U ||
+ BT->getKind() == BuiltinType::Char_S);
+ return false;
+}
+
+namespace {
+AST_MATCHER(clang::QualType, isActualChar) {
+ return clang::tidy::modernize::isActualCharType(Node);
+}
+} // namespace
+
+static BindableMatcher
+intCastExpression(bool IsSigned,
+ const std::string &CastBindName = std::string()) {
+ // std::cmp_{} functions trigger a compile-time error if either LHS or RHS
+ // is a non-integer type, char, enum or bool
+ // (unsigned char/ signed char are Ok and can be used).
+ auto IntTypeExpr = expr(hasType(hasCanonicalType(qualType(
+ isInteger(), IsSigned ? isSignedInteger() : isUnsignedInteger(),
+ unless(isActualChar()), unless(booleanType()), unless(enumType())))));
+
+ const auto ImplicitCastExpr =
+ CastBindName.empty() ? implicitCastExpr(hasSourceExpression(IntTypeExpr))
+ : implicitCastExpr(hasSourceExpression(IntTypeExpr))
+ .bind(CastBindName);
+
+ const auto CStyleCastExpr = cStyleCastExpr(has(ImplicitCastExpr));
+ const auto StaticCastExpr = cxxStaticCastExpr(has(ImplicitCastExpr));
+ const auto FunctionalCastExpr = cxxFunctionalCastExpr(has(ImplicitCastExpr));
+
+ return expr(anyOf(ImplicitCastExpr, CStyleCastExpr, StaticCastExpr,
+ FunctionalCastExpr));
+}
+
+static StringRef parseOpCode(BinaryOperator::Opcode Code) {
+ switch (Code) {
+ case BO_LT:
+ return "cmp_less";
+ case BO_GT:
+ return "cmp_greater";
+ case BO_LE:
+ return "cmp_less_equal";
+ case BO_GE:
+ return "cmp_greater_equal";
+ case BO_EQ:
+ return "cmp_equal";
+ case BO_NE:
+ return "cmp_not_equal";
+ default:
+ return "";
+ }
+}
+
+UseIntegerSignComparisonCheck::UseIntegerSignComparisonCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
+ utils::IncludeSorter::IS_LLVM),
+ areDiagsSelfContained()) {}
+
+void UseIntegerSignComparisonCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
+}
+
+void UseIntegerSignComparisonCheck::registerMatchers(MatchFinder *Finder) {
+ const auto SignedIntCastExpr = intCastExpression(true, "sIntCastExpression");
+ const auto UnSignedIntCastExpr = intCastExpression(false);
+
+ // Flag all operators "==", "<=", ">=", "<", ">", "!="
+ // that are used between signed/unsigned
+ const auto CompareOperator =
+ binaryOperator(hasAnyOperatorName("==", "<=", ">=", "<", ">", "!="),
+ hasOperands(SignedIntCastExpr, UnSignedIntCastExpr),
+ unless(isInTemplateInstantiation()))
+ .bind("intComparison");
+
+ Finder->addMatcher(CompareOperator, this);
+}
+
+void UseIntegerSignComparisonCheck::registerPPCallbacks(
+ const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
+ IncludeInserter.registerPreprocessor(PP);
+}
+
+void UseIntegerSignComparisonCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *SignedCastExpression =
+ Result.Nodes.getNodeAs("sIntCastExpression");
+ assert(SignedCastExpression);
+
+ // Ignore the match if we know that the signed int value is not negative.
+ Expr::EvalResult EVResult;
+ if (!SignedCastExpression->isValueDependent() &&
+ SignedCastExpression->getSubExpr()->EvaluateAsInt(EVResult,
+ *Result.Context)) {
+ const llvm::APSInt SValue = EVResult.Val.getInt();
+ if (SValue.isNonNegative())
+ return;
+ }
+
+ const auto *BinaryOp =
+ Result.Nodes.getNodeAs("intComparison");
+ if (BinaryOp == nullptr)
+ return;
+
+ const BinaryOperator::Opcode OpCode = BinaryOp->getOpcode();
+
+ const Expr *LHS = BinaryOp->getLHS()->IgnoreImpCasts();
+ const Expr *RHS = BinaryOp->getRHS()->IgnoreImpCasts();
+ if (LHS == nullptr || RHS == nullptr)
+ return;
+ const Expr *SubExprLHS = nullptr;
+ const Expr *SubExprRHS = nullptr;
+ SourceRange R1 = SourceRange(LHS->getBeginLoc());
+ SourceRange R2 = SourceRange(BinaryOp->getOperatorLoc());
+ SourceRange R3 = SourceRange(Lexer::getLocForEndOfToken(
+ RHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts()));
+ if (const auto *LHSCast = llvm::dyn_cast(LHS)) {
+ SubExprLHS = LHSCast->getSubExpr();
+ R1 = SourceRange(LHS->getBeginLoc(),
+ SubExprLHS->getBeginLoc().getLocWithOffset(-1));
+ R2.setBegin(Lexer::getLocForEndOfToken(
+ SubExprLHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts()));
+ }
+ if (const auto *RHSCast = llvm::dyn_cast(RHS)) {
+ SubExprRHS = RHSCast->getSubExpr();
+ R2.setEnd(SubExprRHS->getBeginLoc().getLocWithOffset(-1));
+ }
+ DiagnosticBuilder Diag =
+ diag(BinaryOp->getBeginLoc(),
+ "comparison between 'signed' and 'unsigned' integers");
+ const std::string CmpNamespace = ("std::" + parseOpCode(OpCode)).str();
+ const std::string CmpHeader = "";
+ // Prefer modernize-use-integer-sign-comparison when C++20 is available!
+ Diag << FixItHint::CreateReplacement(
+ CharSourceRange(R1, SubExprLHS != nullptr),
+ llvm::Twine(CmpNamespace + "(").str());
+ Diag << FixItHint::CreateReplacement(R2, ",");
+ Diag << FixItHint::CreateReplacement(CharSourceRange::getCharRange(R3), ")");
+
+ // If there is no include for cmp_{*} functions, we'll add it.
+ Diag << IncludeInserter.createIncludeInsertion(
+ Result.SourceManager->getFileID(BinaryOp->getBeginLoc()), CmpHeader);
+}
+
+} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/UseIntegerSignComparisonCheck.h b/clang-tools-extra/clang-tidy/modernize/UseIntegerSignComparisonCheck.h
new file mode 100644
index 0000000000000..a1074829d6eca
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseIntegerSignComparisonCheck.h
@@ -0,0 +1,42 @@
+//===--- UseIntegerSignComparisonCheck.h - clang-tidy -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEINTEGERSIGNCOMPARISONCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEINTEGERSIGNCOMPARISONCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "../utils/IncludeInserter.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+namespace clang::tidy::modernize {
+
+/// Replace comparisons between signed and unsigned integers with their safe
+/// C++20 ``std::cmp_*`` alternative, if available.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-integer-sign-comparison.html
+class UseIntegerSignComparisonCheck : public ClangTidyCheck {
+public:
+ UseIntegerSignComparisonCheck(StringRef Name, ClangTidyContext *Context);
+
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+ Preprocessor *ModuleExpanderPP) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus20;
+ }
+
+private:
+ utils::IncludeInserter IncludeInserter;
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEINTEGERSIGNCOMPARISONCHECK_H
diff --git a/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp
index 24eefdb082eb3..30fcba367db67 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp
@@ -7,9 +7,14 @@
//===----------------------------------------------------------------------===//
#include "UseUsingCheck.h"
-#include "clang/AST/ASTContext.h"
+#include "../utils/LexerUtils.h"
#include "clang/AST/DeclGroup.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TokenKinds.h"
#include "clang/Lex/Lexer.h"
+#include
using namespace clang::ast_matchers;
namespace {
@@ -83,6 +88,9 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
if (!ParentDecl)
return;
+ const SourceManager &SM = *Result.SourceManager;
+ const LangOptions &LO = getLangOpts();
+
// Match CXXRecordDecl only to store the range of the last non-implicit full
// declaration, to later check whether it's within the typdef itself.
const auto *MatchedTagDecl = Result.Nodes.getNodeAs(TagDeclName);
@@ -119,14 +127,51 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
return;
}
- PrintingPolicy PrintPolicy(getLangOpts());
- PrintPolicy.SuppressScope = true;
- PrintPolicy.ConstantArraySizeAsWritten = true;
- PrintPolicy.UseVoidForZeroParams = false;
- PrintPolicy.PrintInjectedClassNameWithArguments = false;
+ const TypeLoc TL = MatchedDecl->getTypeSourceInfo()->getTypeLoc();
+
+ auto [Type, QualifierStr] = [MatchedDecl, this, &TL, &SM,
+ &LO]() -> std::pair {
+ SourceRange TypeRange = TL.getSourceRange();
+
+ // Function pointer case, get the left and right side of the identifier
+ // without the identifier.
+ if (TypeRange.fullyContains(MatchedDecl->getLocation())) {
+ const auto RangeLeftOfIdentifier = CharSourceRange::getCharRange(
+ TypeRange.getBegin(), MatchedDecl->getLocation());
+ const auto RangeRightOfIdentifier = CharSourceRange::getCharRange(
+ Lexer::getLocForEndOfToken(MatchedDecl->getLocation(), 0, SM, LO),
+ Lexer::getLocForEndOfToken(TypeRange.getEnd(), 0, SM, LO));
+ const std::string VerbatimType =
+ (Lexer::getSourceText(RangeLeftOfIdentifier, SM, LO) +
+ Lexer::getSourceText(RangeRightOfIdentifier, SM, LO))
+ .str();
+ return {VerbatimType, ""};
+ }
+
+ StringRef ExtraReference = "";
+ if (MainTypeEndLoc.isValid() && TypeRange.fullyContains(MainTypeEndLoc)) {
+ // Each type introduced in a typedef can specify being a reference or
+ // pointer type seperately, so we need to sigure out if the new using-decl
+ // needs to be to a reference or pointer as well.
+ const SourceLocation Tok = utils::lexer::findPreviousAnyTokenKind(
+ MatchedDecl->getLocation(), SM, LO, tok::TokenKind::star,
+ tok::TokenKind::amp, tok::TokenKind::comma,
+ tok::TokenKind::kw_typedef);
+
+ ExtraReference = Lexer::getSourceText(
+ CharSourceRange::getCharRange(Tok, Tok.getLocWithOffset(1)), SM, LO);
- std::string Type = MatchedDecl->getUnderlyingType().getAsString(PrintPolicy);
- std::string Name = MatchedDecl->getNameAsString();
+ if (ExtraReference != "*" && ExtraReference != "&")
+ ExtraReference = "";
+
+ TypeRange.setEnd(MainTypeEndLoc);
+ }
+ return {
+ Lexer::getSourceText(CharSourceRange::getTokenRange(TypeRange), SM, LO)
+ .str(),
+ ExtraReference.str()};
+ }();
+ StringRef Name = MatchedDecl->getName();
SourceRange ReplaceRange = MatchedDecl->getSourceRange();
// typedefs with multiple comma-separated definitions produce multiple
@@ -143,7 +188,8 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
// This is the first (and possibly the only) TypedefDecl in a typedef. Save
// Type and Name in case we find subsequent TypedefDecl's in this typedef.
FirstTypedefType = Type;
- FirstTypedefName = Name;
+ FirstTypedefName = Name.str();
+ MainTypeEndLoc = TL.getEndLoc();
} else {
// This is additional TypedefDecl in a comma-separated typedef declaration.
// Start replacement *after* prior replacement and separate with semicolon.
@@ -153,10 +199,10 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
// If this additional TypedefDecl's Type starts with the first TypedefDecl's
// type, make this using statement refer back to the first type, e.g. make
// "typedef int Foo, *Foo_p;" -> "using Foo = int;\nusing Foo_p = Foo*;"
- if (Type.size() > FirstTypedefType.size() &&
- Type.substr(0, FirstTypedefType.size()) == FirstTypedefType)
- Type = FirstTypedefName + Type.substr(FirstTypedefType.size() + 1);
+ if (Type == FirstTypedefType && !QualifierStr.empty())
+ Type = FirstTypedefName;
}
+
if (!ReplaceRange.getEnd().isMacroID()) {
const SourceLocation::IntTy Offset =
MatchedDecl->getFunctionType() ? 0 : Name.size();
@@ -171,13 +217,12 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
LastTagDeclRange->second.isValid() &&
ReplaceRange.fullyContains(LastTagDeclRange->second)) {
Type = std::string(Lexer::getSourceText(
- CharSourceRange::getTokenRange(LastTagDeclRange->second),
- *Result.SourceManager, getLangOpts()));
+ CharSourceRange::getTokenRange(LastTagDeclRange->second), SM, LO));
if (Type.empty())
return;
}
- std::string Replacement = Using + Name + " = " + Type;
+ std::string Replacement = (Using + Name + " = " + Type + QualifierStr).str();
Diag << FixItHint::CreateReplacement(ReplaceRange, Replacement);
}
} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.h b/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.h
index 7054778d84a0c..1e54bbf23c984 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.h
@@ -26,6 +26,7 @@ class UseUsingCheck : public ClangTidyCheck {
std::string FirstTypedefType;
std::string FirstTypedefName;
+ SourceLocation MainTypeEndLoc;
public:
UseUsingCheck(StringRef Name, ClangTidyContext *Context);
diff --git a/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.cpp b/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.cpp
index dc6e0cf9c7d12..94cb7ec38087a 100644
--- a/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.cpp
+++ b/clang-tools-extra/clang-tidy/performance/InefficientVectorOperationCheck.cpp
@@ -77,7 +77,7 @@ InefficientVectorOperationCheck::InefficientVectorOperationCheck(
: ClangTidyCheck(Name, Context),
VectorLikeClasses(utils::options::parseStringList(
Options.get("VectorLikeClasses", "::std::vector"))),
- EnableProto(Options.getLocalOrGlobal("EnableProto", false)) {}
+ EnableProto(Options.get("EnableProto", false)) {}
void InefficientVectorOperationCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
diff --git a/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.h b/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.h
index acd8a6bfc50f5..3aa4bdc496194 100644
--- a/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.h
@@ -18,10 +18,11 @@ namespace clang::tidy::readability {
/// a call to `empty()`.
///
/// The emptiness of a container should be checked using the `empty()` method
-/// instead of the `size()` method. It shows clearer intent to use `empty()`.
-/// Furthermore some containers may implement the `empty()` method but not
-/// implement the `size()` method. Using `empty()` whenever possible makes it
-/// easier to switch to another container in the future.
+/// instead of the `size()`/`length()` method. It shows clearer intent to use
+/// `empty()`. Furthermore some containers may implement the `empty()` method
+/// but not implement the `size()` or `length()` method. Using `empty()`
+/// whenever possible makes it easier to switch to another container in the
+/// future.
class ContainerSizeEmptyCheck : public ClangTidyCheck {
public:
ContainerSizeEmptyCheck(StringRef Name, ClangTidyContext *Context);
diff --git a/clang-tools-extra/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h b/clang-tools-extra/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h
index 1c526577b403f..0c5ead860c161 100644
--- a/clang-tools-extra/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h
@@ -26,7 +26,7 @@ class InconsistentDeclarationParameterNameCheck : public ClangTidyCheck {
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)),
- Strict(Options.getLocalOrGlobal("Strict", false)) {}
+ Strict(Options.get("Strict", false)) {}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
diff --git a/clang-tools-extra/clang-tidy/readability/RedundantAccessSpecifiersCheck.h b/clang-tools-extra/clang-tidy/readability/RedundantAccessSpecifiersCheck.h
index a5389d063f6cf..566e5ea637986 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantAccessSpecifiersCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/RedundantAccessSpecifiersCheck.h
@@ -21,8 +21,7 @@ class RedundantAccessSpecifiersCheck : public ClangTidyCheck {
public:
RedundantAccessSpecifiersCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
- CheckFirstDeclaration(
- Options.getLocalOrGlobal("CheckFirstDeclaration", false)) {}
+ CheckFirstDeclaration(Options.get("CheckFirstDeclaration", false)) {}
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
diff --git a/clang-tools-extra/clang-tidy/readability/RedundantCastingCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantCastingCheck.cpp
index b9ff0e81cbc52..4d5adbe02f525 100644
--- a/clang-tools-extra/clang-tidy/readability/RedundantCastingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/RedundantCastingCheck.cpp
@@ -94,7 +94,7 @@ RedundantCastingCheck::RedundantCastingCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)),
- IgnoreTypeAliases(Options.getLocalOrGlobal("IgnoreTypeAliases", false)) {}
+ IgnoreTypeAliases(Options.get("IgnoreTypeAliases", false)) {}
void RedundantCastingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IgnoreMacros", IgnoreMacros);
diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index d42dafa8ffc36..b8d843cba7133 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -526,6 +526,24 @@ static bool verifyFileExtensions(
return AnyInvalid;
}
+static bool verifyOptions(const llvm::StringSet<> &ValidOptions,
+ const ClangTidyOptions::OptionMap &OptionMap,
+ StringRef Source) {
+ bool AnyInvalid = false;
+ for (auto Key : OptionMap.keys()) {
+ if (ValidOptions.contains(Key))
+ continue;
+ AnyInvalid = true;
+ auto &Output = llvm::WithColor::warning(llvm::errs(), Source)
+ << "unknown check option '" << Key << '\'';
+ llvm::StringRef Closest = closest(Key, ValidOptions);
+ if (!Closest.empty())
+ Output << "; did you mean '" << Closest << '\'';
+ Output << VerifyConfigWarningEnd;
+ }
+ return AnyInvalid;
+}
+
static SmallString<256> makeAbsolute(llvm::StringRef Input) {
if (Input.empty())
return {};
@@ -629,29 +647,17 @@ int clangTidyMain(int argc, const char **argv) {
if (VerifyConfig) {
std::vector RawOptions =
OptionsProvider->getRawOptions(FileName);
- NamesAndOptions Valid =
+ ChecksAndOptions Valid =
getAllChecksAndOptions(AllowEnablingAnalyzerAlphaCheckers);
bool AnyInvalid = false;
for (const auto &[Opts, Source] : RawOptions) {
if (Opts.Checks)
- AnyInvalid |= verifyChecks(Valid.Names, *Opts.Checks, Source);
-
+ AnyInvalid |= verifyChecks(Valid.Checks, *Opts.Checks, Source);
if (Opts.HeaderFileExtensions && Opts.ImplementationFileExtensions)
AnyInvalid |=
verifyFileExtensions(*Opts.HeaderFileExtensions,
*Opts.ImplementationFileExtensions, Source);
-
- for (auto Key : Opts.CheckOptions.keys()) {
- if (Valid.Options.contains(Key))
- continue;
- AnyInvalid = true;
- auto &Output = llvm::WithColor::warning(llvm::errs(), Source)
- << "unknown check option '" << Key << '\'';
- llvm::StringRef Closest = closest(Key, Valid.Options);
- if (!Closest.empty())
- Output << "; did you mean '" << Closest << '\'';
- Output << VerifyConfigWarningEnd;
- }
+ AnyInvalid |= verifyOptions(Valid.Options, Opts.CheckOptions, Source);
}
if (AnyInvalid)
return 1;
diff --git a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp
index 88e4886cd0df9..9104723c7f1c0 100644
--- a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp
+++ b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp
@@ -397,7 +397,7 @@ RenamerClangTidyCheck::RenamerClangTidyCheck(StringRef CheckName,
ClangTidyContext *Context)
: ClangTidyCheck(CheckName, Context),
AggressiveDependentMemberLookup(
- Options.getLocalOrGlobal("AggressiveDependentMemberLookup", false)) {}
+ Options.get("AggressiveDependentMemberLookup", false)) {}
RenamerClangTidyCheck::~RenamerClangTidyCheck() = default;
void RenamerClangTidyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
diff --git a/clang-tools-extra/clangd/CompileCommands.cpp b/clang-tools-extra/clangd/CompileCommands.cpp
index fddfffe7523d9..207e4c3e6722c 100644
--- a/clang-tools-extra/clangd/CompileCommands.cpp
+++ b/clang-tools-extra/clangd/CompileCommands.cpp
@@ -458,20 +458,6 @@ llvm::ArrayRef ArgStripper::rulesFor(llvm::StringRef Arg) {
PrevAlias[Self] = T;
NextAlias[T] = Self;
};
- // Also grab prefixes for each option, these are not fully exposed.
- llvm::ArrayRef Prefixes[DriverID::LastOption];
-
-#define PREFIX(NAME, VALUE) \
- static constexpr llvm::StringLiteral NAME##_init[] = VALUE; \
- static constexpr llvm::ArrayRef NAME( \
- NAME##_init, std::size(NAME##_init) - 1);
-#define OPTION(PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, \
- FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, \
- METAVAR, VALUES) \
- Prefixes[DriverID::OPT_##ID] = PREFIX;
-#include "clang/Driver/Options.inc"
-#undef OPTION
-#undef PREFIX
struct {
DriverID ID;
@@ -498,7 +484,9 @@ llvm::ArrayRef ArgStripper::rulesFor(llvm::StringRef Arg) {
llvm::SmallVector Rules;
// Iterate over each alias, to add rules for parsing it.
for (unsigned A = ID; A != DriverID::OPT_INVALID; A = NextAlias[A]) {
- if (!Prefixes[A].size()) // option groups.
+ llvm::SmallVector Prefixes;
+ DriverTable.appendOptionPrefixes(A, Prefixes);
+ if (Prefixes.empty()) // option groups.
continue;
auto Opt = DriverTable.getOption(A);
// Exclude - and -foo pseudo-options.
@@ -507,7 +495,7 @@ llvm::ArrayRef ArgStripper::rulesFor(llvm::StringRef Arg) {
auto Modes = getModes(Opt);
std::pair ArgCount = getArgCount(Opt);
// Iterate over each spelling of the alias, e.g. -foo vs --foo.
- for (StringRef Prefix : Prefixes[A]) {
+ for (StringRef Prefix : Prefixes) {
llvm::SmallString<64> Buf(Prefix);
Buf.append(Opt.getName());
llvm::StringRef Spelling = Result->try_emplace(Buf).first->getKey();
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 81125dbb1aeaf..6d0af20e31260 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -550,9 +550,14 @@ bool SymbolCollector::shouldCollectSymbol(const NamedDecl &ND,
// Avoid indexing internal symbols in protobuf generated headers.
if (isPrivateProtoDecl(ND))
return false;
+
+ // System headers that end with `intrin.h` likely contain useful symbols.
if (!Opts.CollectReserved &&
(hasReservedName(ND) || hasReservedScope(*ND.getDeclContext())) &&
- ASTCtx.getSourceManager().isInSystemHeader(ND.getLocation()))
+ ASTCtx.getSourceManager().isInSystemHeader(ND.getLocation()) &&
+ !ASTCtx.getSourceManager()
+ .getFilename(ND.getLocation())
+ .ends_with("intrin.h"))
return false;
return true;
diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
index 3b378153eafd5..d84e501b87ce7 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
@@ -49,7 +49,8 @@ class ExtractionContext {
llvm::StringRef VarName) const;
// Generate Replacement for declaring the selected Expr as a new variable
tooling::Replacement insertDeclaration(llvm::StringRef VarName,
- SourceRange InitChars) const;
+ SourceRange InitChars,
+ bool AddSemicolon) const;
private:
bool Extractable = false;
@@ -252,7 +253,8 @@ ExtractionContext::replaceWithVar(SourceRange Chars,
// returns the Replacement for declaring a new variable storing the extraction
tooling::Replacement
ExtractionContext::insertDeclaration(llvm::StringRef VarName,
- SourceRange InitializerChars) const {
+ SourceRange InitializerChars,
+ bool AddSemicolon) const {
llvm::StringRef ExtractionCode = toSourceCode(SM, InitializerChars);
const SourceLocation InsertionLoc =
toHalfOpenFileRange(SM, Ctx.getLangOpts(),
@@ -260,7 +262,9 @@ ExtractionContext::insertDeclaration(llvm::StringRef VarName,
->getBegin();
std::string ExtractedVarDecl =
printType(VarType, ExprNode->getDeclContext(), VarName) + " = " +
- ExtractionCode.str() + "; ";
+ ExtractionCode.str();
+ if (AddSemicolon)
+ ExtractedVarDecl += "; ";
return tooling::Replacement(SM, InsertionLoc, 0, ExtractedVarDecl);
}
@@ -419,12 +423,10 @@ const SelectionTree::Node *getCallExpr(const SelectionTree::Node *DeclRef) {
// Returns true if Inner (which is a direct child of Outer) is appearing as
// a statement rather than an expression whose value can be used.
-bool childExprIsStmt(const Stmt *Outer, const Expr *Inner) {
+bool childExprIsDisallowedStmt(const Stmt *Outer, const Expr *Inner) {
if (!Outer || !Inner)
return false;
// Exclude the most common places where an expr can appear but be unused.
- if (llvm::isa(Outer))
- return true;
if (llvm::isa(Outer))
return true;
// Control flow statements use condition etc, but not the body.
@@ -476,12 +478,9 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
const auto *Parent = OuterImplicit.Parent;
if (!Parent)
return false;
- // We don't want to extract expressions used as statements, that would leave
- // a `placeholder;` around that has no effect.
- // Unfortunately because the AST doesn't have ExprStmt, we have to check in
- // this roundabout way.
- if (childExprIsStmt(Parent->ASTNode.get(),
- OuterImplicit.ASTNode.get()))
+ // Filter non-applicable expression statements.
+ if (childExprIsDisallowedStmt(Parent->ASTNode.get(),
+ OuterImplicit.ASTNode.get()))
return false;
std::function IsFullySelected =
@@ -516,6 +515,12 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
return false;
}
+ // If e.g. a capture clause was selected, the target node is the lambda
+ // expression. We only want to offer the extraction if the entire lambda
+ // expression was selected.
+ if (llvm::isa(E))
+ return N->Selected == SelectionTree::Complete;
+
// The same logic as for assignments applies to initializations.
// However, we do allow extracting the RHS of an init capture, as it is
// a valid use case to move non-trivial expressions out of the capture clause.
@@ -599,10 +604,24 @@ Expected ExtractVariable::apply(const Selection &Inputs) {
// FIXME: get variable name from user or suggest based on type
std::string VarName = "placeholder";
SourceRange Range = Target->getExtractionChars();
- // insert new variable declaration
- if (auto Err = Result.add(Target->insertDeclaration(VarName, Range)))
+
+ const SelectionTree::Node &OuterImplicit =
+ Target->getExprNode()->outerImplicit();
+ assert(OuterImplicit.Parent);
+ bool IsExprStmt = llvm::isa_and_nonnull(
+ OuterImplicit.Parent->ASTNode.get());
+
+ // insert new variable declaration. add a semicolon if and only if
+ // we are not dealing with an expression statement, which already has
+ // a semicolon that stays where it is, as it's not part of the range.
+ if (auto Err =
+ Result.add(Target->insertDeclaration(VarName, Range, !IsExprStmt)))
return std::move(Err);
- // replace expression with variable name
+
+ // replace expression with variable name, unless it's an expression statement,
+ // in which case we remove it.
+ if (IsExprStmt)
+ VarName.clear();
if (auto Err = Result.add(Target->replaceWithVar(Range, VarName)))
return std::move(Err);
return Effect::mainFileEdit(Inputs.AST->getSourceManager(),
diff --git a/clang-tools-extra/clangd/unittests/DumpASTTests.cpp b/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
index 304682118c871..cb2c17ad4ef0d 100644
--- a/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
+++ b/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
@@ -49,7 +49,7 @@ declaration: Function - root
)"},
{R"cpp(
namespace root {
-struct S { static const int x = 0; };
+struct S { static const int x = 0; ~S(); };
int y = S::x + root::S().x;
}
)cpp",
@@ -60,10 +60,12 @@ declaration: Namespace - root
type: Qualified - const
type: Builtin - int
expression: IntegerLiteral - 0
+ declaration: CXXDestructor
+ type: Record - S
+ type: FunctionProto
+ type: Builtin - void
declaration: CXXConstructor
declaration: CXXConstructor
- declaration: CXXConstructor
- declaration: CXXDestructor
declaration: Var - y
type: Builtin - int
expression: ExprWithCleanups
@@ -74,7 +76,7 @@ declaration: Namespace - root
type: Record - S
expression: ImplicitCast - LValueToRValue
expression: Member - x
- expression: MaterializeTemporary - rvalue
+ expression: CXXBindTemporary
expression: CXXTemporaryObject - S
type: Elaborated
specifier: Namespace - root::
@@ -82,6 +84,37 @@ declaration: Namespace - root
)"},
{R"cpp(
namespace root {
+struct S { static const int x = 0; };
+int y = S::x + root::S().x;
+}
+ )cpp",
+ R"(
+declaration: Namespace - root
+ declaration: CXXRecord - S
+ declaration: Var - x
+ type: Qualified - const
+ type: Builtin - int
+ expression: IntegerLiteral - 0
+ declaration: CXXConstructor
+ declaration: CXXConstructor
+ declaration: CXXConstructor
+ declaration: CXXDestructor
+ declaration: Var - y
+ type: Builtin - int
+ expression: BinaryOperator - +
+ expression: ImplicitCast - LValueToRValue
+ expression: DeclRef - x
+ specifier: TypeSpec
+ type: Record - S
+ expression: ImplicitCast - LValueToRValue
+ expression: Member - x
+ expression: CXXTemporaryObject - S
+ type: Elaborated
+ specifier: Namespace - root::
+ type: Record - S
+ )"},
+ {R"cpp(
+namespace root {
template int tmpl() {
(void)tmpl();
return T::value;
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index e8088cb37fa51..7a9703c744e93 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -2111,6 +2111,20 @@ TEST_F(SymbolCollectorTest, Reserved) {
EXPECT_THAT(Symbols, IsEmpty());
}
+TEST_F(SymbolCollectorTest, ReservedSymbolInIntrinsicHeader) {
+ const char *Header = R"cpp(
+ #pragma once
+ void __foo();
+ )cpp";
+
+ TestHeaderName = "xintrin.h";
+ TestHeaderURI = URI::create(testPath(TestHeaderName)).toString();
+ InMemoryFileSystem = new llvm::vfs::InMemoryFileSystem;
+ CollectorOpts.FallbackDir = testRoot();
+ runSymbolCollector("#pragma GCC system_header\n" + std::string(Header), "");
+ EXPECT_THAT(Symbols, UnorderedElementsAre(qName("__foo")));
+}
+
TEST_F(SymbolCollectorTest, Concepts) {
const char *Header = R"cpp(
template
diff --git a/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp
index 656b62c9a1f4e..552e693c0363a 100644
--- a/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp
+++ b/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp
@@ -151,8 +151,8 @@ TEST_F(ExtractVariableTest, Test) {
// Variable DeclRefExpr
a = [[b]];
a = [[xyz()]];
- // statement expression
- [[xyz()]];
+ // expression statement of type void
+ [[v()]];
while (a)
[[++a]];
// label statement
@@ -493,6 +493,16 @@ TEST_F(ExtractVariableTest, Test) {
a = a + 1;
}
})cpp"},
+ {R"cpp(
+ int func() { return 0; }
+ int main() {
+ [[func()]];
+ })cpp",
+ R"cpp(
+ int func() { return 0; }
+ int main() {
+ auto placeholder = func();
+ })cpp"},
{R"cpp(
template
auto call(T t) { return t(); }
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index b2b66dca6ccf8..fa3a8e577a33a 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -115,6 +115,24 @@ Improvements to clang-tidy
- Improved :program:`run-clang-tidy.py` script. Fixed minor shutdown noise
happening on certain platforms when interrupting the script.
+- Removed :program:`clang-tidy`'s global options for most of checks. All options
+ are changed to local options except `IncludeStyle`, `StrictMode` and
+ `IgnoreMacros`.
+
+.. csv-table::
+ :header: "Check", "Options removed from global option"
+
+ :doc:`bugprone-reserved-identifier `, AggressiveDependentMemberLookup
+ :doc:`bugprone-unchecked-optional-access `, IgnoreSmartPointerDereference
+ :doc:`cppcoreguidelines-pro-type-member-init `, UseAssignment
+ :doc:`cppcoreguidelines-rvalue-reference-param-not-moved `, AllowPartialMove; IgnoreUnnamedParams; IgnoreNonDeducedTemplateTypes
+ :doc:`misc-include-cleaner `, IgnoreHeaders; DeduplicateFindings
+ :doc:`performance-inefficient-vector-operation `, EnableProto
+ :doc:`readability-identifier-naming `, AggressiveDependentMemberLookup
+ :doc:`readability-inconsistent-declaration-parameter-name `, Strict
+ :doc:`readability-redundant-access-specifiers `, CheckFirstDeclaration
+ :doc:`readability-redundant-casting `, IgnoreTypeAliases
+
New checks
^^^^^^^^^^
@@ -136,10 +154,16 @@ New checks
Gives warnings for tagged unions, where the number of tags is
different from the number of data members inside the union.
+- New :doc:`modernize-use-integer-sign-comparison
+ ` check.
+
+ Replace comparisons between signed and unsigned integers with their safe
+ C++20 ``std::cmp_*`` alternative, if available.
+
- New :doc:`portability-template-virtual-member-function
` check.
- Finds cases when an uninstantiated virtual member function in a template class
+ Finds cases when an uninstantiated virtual member function in a template class
causes cross-compiler incompatibility.
New check aliases
@@ -176,6 +200,10 @@ Changes in existing checks
` check by fixing
a crash when determining if an ``enable_if[_t]`` was found.
+- Improved :doc:`bugprone-optional-value-conversion
+ ` to support detecting
+ conversion directly by ``std::make_unique`` and ``std::make_shared``.
+
- Improved :doc:`bugprone-posix-return
` check to support integer literals
as LHS and posix call as RHS of comparison.
@@ -289,6 +317,9 @@ Changes in existing checks
member function calls too and to only expand macros starting with ``PRI``
and ``__PRI`` from ```` in the format string.
+- Improved :doc:`modernize-use-using
+ ` check by not expanding macros.
+
- Improved :doc:`performance-avoid-endl
` check to use ``std::endl`` as
placeholder when lexer cannot get source text.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/cplusplus.PureVirtualCall.rst b/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/cplusplus.PureVirtualCall.rst
deleted file mode 100644
index 9fab628b80d44..0000000000000
--- a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/cplusplus.PureVirtualCall.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-.. title:: clang-tidy - clang-analyzer-cplusplus.PureVirtualCall
-
-clang-analyzer-cplusplus.PureVirtualCall
-========================================
-
-Check pure virtual function calls during construction/destruction.
-
-The clang-analyzer-cplusplus.PureVirtualCall check is an alias of
-Clang Static Analyzer cplusplus.PureVirtualCall.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/cplusplus.SelfAssignment.rst b/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/cplusplus.SelfAssignment.rst
new file mode 100644
index 0000000000000..62e300660828b
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/cplusplus.SelfAssignment.rst
@@ -0,0 +1,13 @@
+.. title:: clang-tidy - clang-analyzer-cplusplus.SelfAssignment
+.. meta::
+ :http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-selfassignment
+
+clang-analyzer-cplusplus.SelfAssignment
+=======================================
+
+Checks C++ copy and move assignment operators for self assignment.
+
+The `clang-analyzer-cplusplus.SelfAssignment` check is an alias, please see
+`Clang Static Analyzer Available Checkers
+`_
+for more information.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/optin.osx.OSObjectCStyleCast.rst b/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/optin.osx.OSObjectCStyleCast.rst
deleted file mode 100644
index c2fef59f56894..0000000000000
--- a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/optin.osx.OSObjectCStyleCast.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-.. title:: clang-tidy - clang-analyzer-optin.osx.OSObjectCStyleCast
-
-clang-analyzer-optin.osx.OSObjectCStyleCast
-===========================================
-
-Checker for C-style casts of OSObjects.
-
-The clang-analyzer-optin.osx.OSObjectCStyleCast check is an alias of
-Clang Static Analyzer optin.osx.OSObjectCStyleCast.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/osx.MIG.rst b/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/osx.MIG.rst
deleted file mode 100644
index a7b8a1cfb14cd..0000000000000
--- a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/osx.MIG.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-.. title:: clang-tidy - clang-analyzer-osx.MIG
-
-clang-analyzer-osx.MIG
-======================
-
-Find violations of the Mach Interface Generator calling convention.
-
-The clang-analyzer-osx.MIG check is an alias of
-Clang Static Analyzer osx.MIG.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/osx.OSObjectRetainCount.rst b/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/osx.OSObjectRetainCount.rst
deleted file mode 100644
index c32982d407c28..0000000000000
--- a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/osx.OSObjectRetainCount.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-.. title:: clang-tidy - clang-analyzer-osx.OSObjectRetainCount
-
-clang-analyzer-osx.OSObjectRetainCount
-======================================
-
-Check for leaks and improper reference count management for OSObject.
-
-The clang-analyzer-osx.OSObjectRetainCount check is an alias of
-Clang Static Analyzer osx.OSObjectRetainCount.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/security.PutenvStackArray.rst b/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/security.PutenvStackArray.rst
index 0a5feff8d3ca8..5858078246d9b 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/security.PutenvStackArray.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/security.PutenvStackArray.rst
@@ -1,10 +1,17 @@
.. title:: clang-tidy - clang-analyzer-security.PutenvStackArray
+.. meta::
+ :http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#security-putenvstackarray-c
clang-analyzer-security.PutenvStackArray
========================================
-Finds calls to the function 'putenv' which pass a pointer to an automatic
-(stack-allocated) array as the argument.
+Finds calls to the putenv function which pass a pointer to a stack-allocated
+(automatic) array as the argument. Function putenv does not copy the passed
+string, only a pointer to the data is stored and this data can be read even by
+other threads. Content of a stack-allocated array is likely to be overwritten
+after exiting from the function.
-The clang-analyzer-security.PutenvStackArray check is an alias of
-Clang Static Analyzer security.PutenvStackArray.
+The `clang-analyzer-security.PutenvStackArray` check is an alias, please see
+`Clang Static Analyzer Available Checkers
+`_
+for more information.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/security.SetgidSetuidOrder.rst b/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/security.SetgidSetuidOrder.rst
index 82f22b11f77fb..b3ba78597a5ba 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/security.SetgidSetuidOrder.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/security.SetgidSetuidOrder.rst
@@ -1,10 +1,18 @@
.. title:: clang-tidy - clang-analyzer-security.SetgidSetuidOrder
+.. meta::
+ :http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#security-setgidsetuidorder-c
clang-analyzer-security.SetgidSetuidOrder
=========================================
-Warn on possible reversed order of 'setgid(getgid()))' and 'setuid(getuid())'
-(CERT: POS36-C).
+The checker checks for sequences of ``setuid(getuid())`` and ``setgid(getgid())``
+calls (in this order). If such a sequence is found and there is no other
+privilege-changing function call (``seteuid``, ``setreuid``, ``setresuid`` and
+the GID versions of these) in between, a warning is generated. The checker finds
+only exactly ``setuid(getuid())`` calls (and the GID versions), not for example
+if the result of ``getuid()`` is stored in a variable.
-The clang-analyzer-security.SetgidSetuidOrder check is an alias of
-Clang Static Analyzer security.SetgidSetuidOrder.
+The `clang-analyzer-security.SetgidSetuidOrder` check is an alias, please see
+`Clang Static Analyzer Available Checkers
+`_
+for more information.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/valist.CopyToSelf.rst b/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/valist.CopyToSelf.rst
deleted file mode 100644
index d0c82abd81901..0000000000000
--- a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/valist.CopyToSelf.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-.. title:: clang-tidy - clang-analyzer-valist.CopyToSelf
-
-clang-analyzer-valist.CopyToSelf
-================================
-
-Check for va_lists which are copied onto itself.
-
-The clang-analyzer-valist.CopyToSelf check is an alias of
-Clang Static Analyzer valist.CopyToSelf.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/valist.Uninitialized.rst b/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/valist.Uninitialized.rst
deleted file mode 100644
index 98b5dd023254a..0000000000000
--- a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/valist.Uninitialized.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-.. title:: clang-tidy - clang-analyzer-valist.Uninitialized
-
-clang-analyzer-valist.Uninitialized
-===================================
-
-Check for usages of uninitialized (or already released) va_lists.
-
-The clang-analyzer-valist.Uninitialized check is an alias of
-Clang Static Analyzer valist.Uninitialized.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/valist.Unterminated.rst b/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/valist.Unterminated.rst
deleted file mode 100644
index 85e21c5721063..0000000000000
--- a/clang-tools-extra/docs/clang-tidy/checks/clang-analyzer/valist.Unterminated.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-.. title:: clang-tidy - clang-analyzer-valist.Unterminated
-
-clang-analyzer-valist.Unterminated
-==================================
-
-Check for va_lists which are not released by a va_end call.
-
-The clang-analyzer-valist.Unterminated check is an alias of
-Clang Static Analyzer valist.Unterminated.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/narrowing-conversions.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/narrowing-conversions.rst
index 04260e75aa558..7cc0b2809b458 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/narrowing-conversions.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/narrowing-conversions.rst
@@ -27,6 +27,40 @@ This check will flag:
- All applications of binary operators with a narrowing conversions.
For example: ``int i; i+= 0.1;``.
+Arithmetic with smaller integer types than ``int`` trigger implicit conversions,
+as explained under `"Integral Promotion" on cppreference.com
+`_.
+This check diagnoses more instances of narrowing than the compiler warning
+`-Wconversion` does. The example below demonstrates this behavior.
+
+.. code-block:: c++
+
+ // The following function definition demonstrates usage of arithmetic with
+ // integer types smaller than `int` and how the narrowing conversion happens
+ // implicitly.
+ void computation(short argument1, short argument2) {
+ // Arithmetic written by humans:
+ short result = argument1 + argument2;
+ // Arithmetic actually performed by C++:
+ short result = static_cast(static_cast(argument1) + static_cast(argument2));
+ }
+
+ void recommended_resolution(short argument1, short argument2) {
+ short result = argument1 + argument2;
+ // ^ warning: narrowing conversion from 'int' to signed type 'short' is implementation-defined
+
+ // The cppcoreguidelines recommend to resolve this issue by using the GSL
+ // in one of two ways. Either by a cast that throws if a loss of precision
+ // would occur.
+ short result = gsl::narrow(argument1 + argument2);
+ // Or it can be resolved without checking the result risking invalid results.
+ short result = gsl::narrow_cast(argument1 + argument2);
+
+ // A classical `static_cast` will silence the warning as well if the GSL
+ // is not available.
+ short result = static_cast(argument1 + argument2);
+ }
+
Options
-------
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index d731b13fc0df4..4d8853a0f6d86 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -115,8 +115,8 @@ Clang-Tidy Checks
:doc:`bugprone-multiple-new-in-one-expression `,
:doc:`bugprone-multiple-statement-macro `,
:doc:`bugprone-no-escape `,
- :doc:`bugprone-nondeterministic-pointer-iteration-order `,
:doc:`bugprone-non-zero-enum-to-bool-conversion `,
+ :doc:`bugprone-nondeterministic-pointer-iteration-order `,
:doc:`bugprone-not-null-terminated-result `, "Yes"
:doc:`bugprone-optional-value-conversion `, "Yes"
:doc:`bugprone-parent-virtual-call `, "Yes"
@@ -301,6 +301,7 @@ Clang-Tidy Checks
:doc:`modernize-use-emplace `, "Yes"
:doc:`modernize-use-equals-default `, "Yes"
:doc:`modernize-use-equals-delete `, "Yes"
+ :doc:`modernize-use-integer-sign-comparison `, "Yes"
:doc:`modernize-use-nodiscard `, "Yes"
:doc:`modernize-use-noexcept `, "Yes"
:doc:`modernize-use-nullptr `, "Yes"
@@ -458,7 +459,7 @@ Check aliases
:doc:`clang-analyzer-cplusplus.NewDelete `, `Clang Static Analyzer cplusplus.NewDelete `_,
:doc:`clang-analyzer-cplusplus.NewDeleteLeaks `, `Clang Static Analyzer cplusplus.NewDeleteLeaks `_,
:doc:`clang-analyzer-cplusplus.PlacementNew `, `Clang Static Analyzer cplusplus.PlacementNew