.ci/metrics/metrics.py
@@ -24,6 +24,7 @@ class JobMetrics:
status: int
created_at_ns: int
workflow_id: int
+ workflow_name: str
@dataclass
@@ -43,40 +44,60 @@ def get_sampled_workflow_metrics(github_repo: github.Repository):
Returns a list of GaugeMetric objects, containing the relevant metrics about
the workflow
"""
+ queued_job_counts = {}
+ running_job_counts = {}
# Other states are available (pending, waiting, etc), but the meaning
# is not documented (See #70540).
# "queued" seems to be the info we want.
- queued_workflow_count = len(
- [
- x
- for x in github_repo.get_workflow_runs(status="queued")
- if x.name in WORKFLOWS_TO_TRACK
- ]
- )
- running_workflow_count = len(
- [
- x
- for x in github_repo.get_workflow_runs(status="in_progress")
- if x.name in WORKFLOWS_TO_TRACK
- ]
- )
+ for queued_workflow in github_repo.get_workflow_runs(status="queued"):
+ if queued_workflow.name not in WORKFLOWS_TO_TRACK:
+ continue
+ for queued_workflow_job in queued_workflow.jobs():
+ job_name = queued_workflow_job.name
+ # Workflows marked as queued can potentially only have some jobs
+ # queued, so make sure to also count jobs currently in progress.
+ if queued_workflow_job.status == "queued":
+ if job_name not in queued_job_counts:
+ queued_job_counts[job_name] = 1
+ else:
+ queued_job_counts[job_name] += 1
+ elif queued_workflow_job.status == "in_progress":
+ if job_name not in running_job_counts:
+ running_job_counts[job_name] = 1
+ else:
+ running_job_counts[job_name] += 1
+
+ for running_workflow in github_repo.get_workflow_runs(status="in_progress"):
+ if running_workflow.name not in WORKFLOWS_TO_TRACK:
+ continue
+ for running_workflow_job in running_workflow.jobs():
+ job_name = running_workflow_job.name
+ if running_workflow_job.status != "in_progress":
+ continue
+
+ if job_name not in running_job_counts:
+ running_job_counts[job_name] = 1
+ else:
+ running_job_counts[job_name] += 1
workflow_metrics = []
- workflow_metrics.append(
- GaugeMetric(
- "workflow_queue_size",
- queued_workflow_count,
- time.time_ns(),
+ for queued_job in queued_job_counts:
+ workflow_metrics.append(
+ GaugeMetric(
+ f"workflow_queue_size_{queued_job}",
+ queued_job_counts[queued_job],
+ time.time_ns(),
+ )
)
- )
- workflow_metrics.append(
- GaugeMetric(
- "running_workflow_count",
- running_workflow_count,
- time.time_ns(),
+ for running_job in running_job_counts:
+ workflow_metrics.append(
+ GaugeMetric(
+ f"running_workflow_count_{running_job}",
+ running_job_counts[running_job],
+ time.time_ns(),
+ )
)
- )
# Always send a hearbeat metric so we can monitor is this container is still able to log to Grafana.
workflow_metrics.append(
GaugeMetric("metrics_container_heartbeat", 1, time.time_ns())
@@ -157,7 +178,7 @@ def get_per_workflow_metrics(
# longer in a testing state and we can directly assert the workflow
# result.
for step in workflow_job.steps:
- if step.conclusion != "success":
+ if step.conclusion != "success" and step.conclusion != "skipped":
job_result = 0
break
@@ -179,6 +200,7 @@ def get_per_workflow_metrics(
job_result,
created_at_ns,
workflow_run.id,
+ workflow_run.name,
)
)
@@ -235,8 +257,6 @@ def upload_metrics(workflow_metrics, metrics_userid, api_key):
def main():
# Authenticate with Github
auth = Auth.Token(os.environ["GITHUB_TOKEN"])
- github_object = Github(auth=auth)
- github_repo = github_object.get_repo("llvm/llvm-project")
grafana_api_key = os.environ["GRAFANA_API_KEY"]
grafana_metrics_userid = os.environ["GRAFANA_METRICS_USERID"]
@@ -248,12 +268,11 @@ def main():
# Enter the main loop. Every five minutes we wake up and dump metrics for
# the relevant jobs.
while True:
+ github_object = Github(auth=auth)
+ github_repo = github_object.get_repo("llvm/llvm-project")
+
current_metrics = get_per_workflow_metrics(github_repo, workflows_to_track)
current_metrics += get_sampled_workflow_metrics(github_repo)
- # Always send a hearbeat metric so we can monitor is this container is still able to log to Grafana.
- current_metrics.append(
- GaugeMetric("metrics_container_heartbeat", 1, time.time_ns())
- )
upload_metrics(current_metrics, grafana_metrics_userid, grafana_api_key)
print(f"Uploaded {len(current_metrics)} metrics", file=sys.stderr)
@@ -261,7 +280,7 @@ def main():
for workflow_metric in reversed(current_metrics):
if isinstance(workflow_metric, JobMetrics):
workflows_to_track[
- workflow_metric.job_name
+ workflow_metric.workflow_name
] = workflow_metric.workflow_id
time.sleep(SCRAPE_INTERVAL_SECONDS)
.github/CODEOWNERS
@@ -131,7 +131,7 @@
/bolt/ @aaupov @maksfb @rafaelauler @ayermolo @dcci @yota9
# Bazel build system.
-/utils/bazel/ @rupprecht @keith
+/utils/bazel/ @rupprecht @keith @aaronmondal
# InstallAPI and TextAPI
/llvm/**/TextAPI/ @cyndyishida
.github/new-prs-labeler.yml
@@ -499,6 +499,7 @@ clang:static analyzer:
- clang/tools/scan-build/**
- clang/utils/analyzer/**
- clang/docs/analyzer/**
+ - clang/test/Analysis/**
pgo:
- llvm/lib/Transforms/Instrumentation/CGProfile.cpp
.github/workflows/build-ci-container-windows.yml
@@ -27,7 +27,7 @@ jobs:
container-filename: ${{ steps.vars.outputs.container-filename }}
steps:
- name: Checkout LLVM
- uses: actions/checkout@v4
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout: .github/workflows/containers/github-action-ci-windows
- name: Write Variables
@@ -46,7 +46,7 @@ jobs:
run: |
docker save ${{ steps.vars.outputs.container-name-tag }} > ${{ steps.vars.outputs.container-filename }}
- name: Upload container image
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: container
path: ${{ steps.vars.outputs.container-filename }}
@@ -63,7 +63,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Download container
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: container
- name: Push Container
.github/workflows/build-ci-container.yml
@@ -32,7 +32,7 @@ jobs:
runs-on: depot-ubuntu-22.04-arm-16
steps:
- name: Checkout LLVM
- uses: actions/checkout@v4
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
sparse-checkout: .github/workflows/containers/github-action-ci/
# podman is not installed by default on the ARM64 images.
@@ -66,7 +66,7 @@ jobs:
podman save ${{ steps.vars.outputs.container-name-agent-tag }} > ${{ steps.vars.outputs.container-agent-filename }}
- name: Upload container image
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: container-${{ matrix.arch }}
path: "*.tar"
@@ -90,7 +90,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Download container
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
- name: Push Container
run: |
.github/workflows/libc-fullbuild-tests.yml
@@ -15,6 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
+ build_type: [Debug, Release, MinSizeRel]
include:
- os: ubuntu-24.04
ccache-variant: sccache
@@ -68,7 +69,7 @@ jobs:
cmake -B ${{ steps.strings.outputs.build-output-dir }}
-DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}
-DCMAKE_C_COMPILER=${{ matrix.c_compiler }}
- -DCMAKE_BUILD_TYPE=MinSizeRel
+ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
-DCMAKE_C_COMPILER_LAUNCHER=${{ matrix.ccache-variant }}
-DCMAKE_CXX_COMPILER_LAUNCHER=${{ matrix.ccache-variant }}
-DCMAKE_INSTALL_PREFIX=${{ steps.strings.outputs.build-install-dir }}
.github/workflows/libc-overlay-tests.yml
@@ -16,6 +16,7 @@ jobs:
# Set fail-fast to false to ensure that feedback is delivered for all matrix combinations.
fail-fast: false
matrix:
+ build_type: [Debug, Release, MinSizeRel]
include:
# TODO: add linux gcc when it is fixed
- os: ubuntu-24.04
@@ -95,7 +96,7 @@ jobs:
cmake -B ${{ steps.strings.outputs.build-output-dir }}
-DCMAKE_CXX_COMPILER=${{ matrix.compiler.cpp_compiler }}
-DCMAKE_C_COMPILER=${{ matrix.compiler.c_compiler }}
- -DCMAKE_BUILD_TYPE=MinSizeRel
+ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
-DCMAKE_C_COMPILER_LAUNCHER=${{ matrix.ccache-variant }}
-DCMAKE_CXX_COMPILER_LAUNCHER=${{ matrix.ccache-variant }}
-DCMAKE_POLICY_DEFAULT_CMP0141=NEW
.github/workflows/libcxx-build-and-test.yaml
@@ -37,7 +37,7 @@ jobs:
stage1:
if: github.repository_owner == 'llvm'
runs-on: libcxx-self-hosted-linux
- container: ghcr.io/llvm/libcxx-linux-builder:d8a0709b1090350a7fe3604d8ab78c7d62f10698
+ container: ghcr.io/llvm/libcxx-linux-builder:b319dfef21f6c7b0bc6a356d6b9f41a3b3b98ae9
continue-on-error: false
strategy:
fail-fast: false
@@ -48,8 +48,8 @@ jobs:
'generic-cxx26',
'generic-modules'
]
- cc: [ 'clang-20' ]
- cxx: [ 'clang++-20' ]
+ cc: [ 'clang-21' ]
+ cxx: [ 'clang++-21' ]
include:
- config: 'generic-gcc'
cc: 'gcc-14'
@@ -75,7 +75,7 @@ jobs:
stage2:
if: github.repository_owner == 'llvm'
runs-on: libcxx-self-hosted-linux
- container: ghcr.io/llvm/libcxx-linux-builder:d8a0709b1090350a7fe3604d8ab78c7d62f10698
+ container: ghcr.io/llvm/libcxx-linux-builder:b319dfef21f6c7b0bc6a356d6b9f41a3b3b98ae9
needs: [ stage1 ]
continue-on-error: false
strategy:
@@ -88,18 +88,22 @@ jobs:
'generic-cxx20',
'generic-cxx23'
]
- cc: [ 'clang-20' ]
- cxx: [ 'clang++-20' ]
+ cc: [ 'clang-21' ]
+ cxx: [ 'clang++-21' ]
include:
- config: 'generic-gcc-cxx11'
cc: 'gcc-14'
cxx: 'g++-14'
- - config: 'generic-cxx23'
- cc: 'clang-18'
- cxx: 'clang++-18'
+ - config: 'generic-cxx26'
+ cc: 'clang-20'
+ cxx: 'clang++-20'
- config: 'generic-cxx26'
cc: 'clang-19'
cxx: 'clang++-19'
+ # Release transition
+ - config: 'generic-cxx23'
+ cc: 'clang-18'
+ cxx: 'clang++-18'
steps:
- uses: actions/checkout@v4
- name: ${{ matrix.config }}
@@ -120,7 +124,7 @@ jobs:
**/crash_diagnostics/*
stage3:
if: github.repository_owner == 'llvm'
- needs: [ stage1, stage2 ]
+ needs: [ stage2 ]
continue-on-error: false
strategy:
fail-fast: false
@@ -163,14 +167,14 @@ jobs:
- config: 'generic-msan'
machine: libcxx-self-hosted-linux
runs-on: ${{ matrix.machine }}
- container: ghcr.io/llvm/libcxx-linux-builder:d8a0709b1090350a7fe3604d8ab78c7d62f10698
+ container: ghcr.io/llvm/libcxx-linux-builder:b319dfef21f6c7b0bc6a356d6b9f41a3b3b98ae9
steps:
- uses: actions/checkout@v4
- name: ${{ matrix.config }}
run: libcxx/utils/ci/run-buildbot ${{ matrix.config }}
env:
- CC: clang-20
- CXX: clang++-20
+ CC: clang-21
+ CXX: clang++-21
- uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
if: always()
with:
@@ -184,7 +188,7 @@ jobs:
**/crash_diagnostics/*
macos:
- needs: [ stage1 ]
+ needs: [ stage3 ]
strategy:
fail-fast: false
matrix:
@@ -228,7 +232,7 @@ jobs:
windows:
runs-on: windows-2022
- needs: [ stage1 ]
+ needs: [ stage2 ]
strategy:
fail-fast: false
matrix:
.github/workflows/libcxx-build-containers.yml
@@ -9,7 +9,6 @@ name: Build Docker images for libc++ CI
permissions:
contents: read
- packages: write
on:
push:
.github/workflows/premerge.yaml
@@ -14,29 +14,29 @@ on:
# do this is that it allows us to take advantage of concurrency groups
# to cancel in progress CI jobs whenever the PR is closed.
- closed
- paths:
- - .github/workflows/premerge.yaml
push:
branches:
- 'main'
- 'release/**'
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
+ cancel-in-progress: true
+
jobs:
premerge-checks-linux:
+ name: Linux Premerge Checks (Test Only - Please Ignore Results)
if: >-
github.repository_owner == 'llvm' &&
(github.event_name != 'pull_request' || github.event.action != 'closed')
runs-on: llvm-premerge-linux-runners
- concurrency:
- group: ${{ github.workflow }}-linux-${{ github.event.pull_request.number || github.sha }}
- cancel-in-progress: true
steps:
- name: Checkout LLVM
- uses: actions/checkout@v4
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 2
- name: Setup ccache
- uses: hendrikmuhs/ccache-action@v1.2.14
+ uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
with:
max-size: "2000M"
- name: Build and Test
@@ -84,23 +84,21 @@ jobs:
./.ci/monolithic-linux.sh "$(echo ${linux_projects} | tr ' ' ';')" "$(echo ${linux_check_targets})" "$(echo ${linux_runtimes} | tr ' ' ';')" "$(echo ${linux_runtime_check_targets})"
premerge-checks-windows:
+ name: Windows Premerge Checks (Test Only - Please Ignore Results)
if: >-
github.repository_owner == 'llvm' &&
(github.event_name != 'pull_request' || github.event.action != 'closed')
runs-on: llvm-premerge-windows-runners
- concurrency:
- group: ${{ github.workflow }}-windows-${{ github.event.pull_request.number || github.sha }}
- cancel-in-progress: true
defaults:
run:
shell: bash
steps:
- name: Checkout LLVM
- uses: actions/checkout@v4
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 2
- name: Setup ccache
- uses: hendrikmuhs/ccache-action@v1.2.14
+ uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
with:
variant: "sccache"
max-size: "2000M"
@@ -146,11 +144,9 @@ jobs:
call C:\\BuildTools\\Common7\\Tools\\VsDevCmd.bat -arch=amd64 -host_arch=amd64
bash .ci/monolithic-windows.sh "${{ steps.vars.outputs.windows-projects }}" "${{ steps.vars.outputs.windows-check-targets }}"
- permerge-check-macos:
+ premerge-check-macos:
+ name: MacOS Premerge Checks
runs-on: macos-14
- concurrency:
- group: ${{ github.workflow }}-macos-${{ github.event.pull_request.number || github.sha }}
- cancel-in-progress: true
if: >-
github.repository_owner == 'llvm' &&
(startswith(github.ref_name, 'release/') ||
@@ -158,11 +154,11 @@ jobs:
(github.event_name != 'pull_request' || github.event.action != 'closed')
steps:
- name: Checkout LLVM
- uses: actions/checkout@v4
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 2
- name: Setup ccache
- uses: hendrikmuhs/ccache-action@v1.2.14
+ uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
with:
max-size: "2000M"
- name: Install Ninja
.github/workflows/release-asset-audit.py
@@ -1,4 +1,5 @@
import github
+import re
import sys
_SPECIAL_CASE_BINARIES = {
@@ -16,38 +17,73 @@ def _is_valid(uploader_name, valid_uploaders, asset_name):
return False
+def _get_uploaders(release_version):
+ # Until llvm 18, assets were uploaded by community members, the release managers
+ # and the GitHub Actions bot.
+ if release_version <= 18:
+ return set(
+ [
+ "DimitryAndric",
+ "stefanp-ibm",
+ "lei137",
+ "omjavaid",
+ "nicolerabjohn",
+ "amy-kwan",
+ "mandlebug",
+ "zmodem",
+ "androm3da",
+ "tru",
+ "rovka",
+ "rorth",
+ "quinnlp",
+ "kamaub",
+ "abrisco",
+ "jakeegan",
+ "maryammo",
+ "tstellar",
+ "github-actions[bot]",
+ ]
+ )
+ # llvm 19 and beyond, only the release managers, bot and a much smaller
+ # number of community members.
+ elif release_version >= 19:
+ return set(
+ [
+ "zmodem",
+ "omjavaid",
+ "tru",
+ "tstellar",
+ "github-actions[bot]",
+ ]
+ )
+
+
+def _get_major_release_version(release_title):
+ # All release titles are of the form "LLVM X.Y.Z(-rcN)".
+ match = re.match("LLVM ([0-9]+)\.", release_title)
+ if match is None:
+ _write_comment_and_exit_with_error(
+ f'Could not parse release version from release title "{release_title}".'
+ )
+ else:
+ return int(match.groups()[0])
+
+
+def _write_comment_and_exit_with_error(comment):
+ with open("comment", "w") as file:
+ file.write(comment)
+ sys.exit(1)
+
+
def main():
token = sys.argv[1]
gh = github.Github(login_or_token=token)
repo = gh.get_repo("llvm/llvm-project")
- uploaders = set(
- [
- "DimitryAndric",
- "stefanp-ibm",
- "lei137",
- "omjavaid",
- "nicolerabjohn",
- "amy-kwan",
- "mandlebug",
- "zmodem",
- "androm3da",
- "tru",
- "rovka",
- "rorth",
- "quinnlp",
- "kamaub",
- "abrisco",
- "jakeegan",
- "maryammo",
- "tstellar",
- "github-actions[bot]",
- ]
- )
-
for release in repo.get_releases():
print("Release:", release.title)
+ uploaders = _get_uploaders(_get_major_release_version(release.title))
for asset in release.get_assets():
created_at = asset.created_at
updated_at = (
@@ -57,9 +93,9 @@ def main():
f"{asset.name} : {asset.uploader.login} [{created_at} {updated_at}] ( {asset.download_count} )"
)
if not _is_valid(asset.uploader.login, uploaders, asset.name):
- with open('comment', 'w') as file:
- file.write(f'@{asset.uploader.login} is not a valid uploader.')
- sys.exit(1)
+ _write_comment_and_exit_with_error(
+ f"@{asset.uploader.login} is not a valid uploader."
+ )
if __name__ == "__main__":
.github/workflows/release-binaries-all.yml
@@ -27,6 +27,10 @@ on:
required: true
default: false
type: boolean
+ secrets:
+ RELEASE_TASKS_USER_TOKEN:
+ description: "Secret used to check user permissions."
+ required: false
pull_request:
types:
.github/workflows/release-binaries.yml
@@ -133,13 +133,18 @@ jobs:
# add extra CMake args to disable them.
# See https://github.com/llvm/llvm-project/issues/99767
if [ "$RUNNER_OS" = "macOS" ]; then
- target_cmake_flags="$target_cmake_flags -DBOOTSTRAP_COMPILER_RT_ENABLE_IOS=OFF"
+ target_cmake_flags="$target_cmake_flags -DBOOTSTRAP_BOOTSTRAP_COMPILER_RT_ENABLE_IOS=OFF"
if [ "$RUNNER_ARCH" = "ARM64" ]; then
arches=arm64
else
arches=x86_64
+ # Disable Flang builds on macOS x86_64. The FortranLower library takes
+ # 2-3 hours to build on macOS, much slower than on Linux.
+ # The long build time causes the release build to time out on x86_64,
+ # so we need to disable flang there.
+ target_cmake_flags="$target_cmake_flags -DLLVM_RELEASE_ENABLE_PROJECTS='clang;lld;lldb;clang-tools-extra;polly;mlir'"
fi
- target_cmake_flags="$target_cmake_flags -DBOOTSTRAP_DARWIN_osx_ARCHS=$arches -DBOOTSTRAP_DARWIN_osx_BUILTIN_ARCHS=$arches"
+ target_cmake_flags="$target_cmake_flags -DBOOTSTRAP_BOOTSTRAP_DARWIN_osx_ARCHS=$arches -DBOOTSTRAP_BOOTSTRAP_DARWIN_osx_BUILTIN_ARCHS=$arches"
fi
build_flang="true"
.mailmap
@@ -38,6 +38,8 @@ Jianjian GUAN <jacquesguan@me.com> <Jianjian.Guan@streamcomputing.com>
Jon Roelofs <jonathan_roelofs@apple.com> <jonathan@codesourcery.com>
Jon Roelofs <jonathan_roelofs@apple.com> <jroelofs@jroelofs.com>
Jonathan Thackray <jonathan.thackray@arm.com> <jthackray@users.noreply.github.com>
+klensy <nightouser@gmail.com>
+klensy <nightouser@gmail.com> <klensy@users.noreply.github.com>
LLVM GN Syncbot <llvmgnsyncbot@gmail.com>
Martin Storsjö <martin@martin.st>
Med Ismail Bennani <ismail@bennani.ma> <m.i.b@apple.com>
bolt/CMakeLists.txt
@@ -202,3 +202,11 @@ endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/include/bolt/RuntimeLibs/RuntimeLibraryVariables.inc.in
${CMAKE_CURRENT_BINARY_DIR}/include/bolt/RuntimeLibs/RuntimeLibraryVariables.inc @ONLY)
+
+set(BOLT_ENUM_TARGETS "")
+foreach(t ${BOLT_TARGETS_TO_BUILD})
+ set(BOLT_ENUM_TARGETS "${BOLT_ENUM_TARGETS}BOLT_TARGET(${t})\n")
+endforeach(t)
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/include/bolt/Core/TargetConfig.def.in
+ ${CMAKE_CURRENT_BINARY_DIR}/include/bolt/Core/TargetConfig.def @ONLY)
bolt/docs/BinaryAnalysis.md
@@ -9,9 +9,182 @@ analyses implemented in the BOLT libraries.
## Which binary analyses are implemented?
-At the moment, no binary analyses are implemented.
+* [Security scanners](#security-scanners)
+ * [pac-ret analysis](#pac-ret-analysis)
-The goal is to make it easy using a plug-in framework to add your own analyses.
+### Security scanners
+
+For the past 25 years, a large numbers of exploits have been built and used in
+the wild to undermine computer security. The majority of these exploits abuse
+memory vulnerabilities in programs, see evidence from
+[Microsoft](https://youtu.be/PjbGojjnBZQ?si=oCHCa0SHgaSNr6Gr&t=836),
+[Chromium](https://www.chromium.org/Home/chromium-security/memory-safety/) and
+[Android](https://security.googleblog.com/2021/01/data-driven-security-hardening-in.html).
+
+It is not surprising therefore, that a large number of mitigations have been
+added to instruction sets and toolchains to make it harder to build an exploit
+using a memory vulnerability. Examples are: stack canaries, stack clash,
+pac-ret, shadow stacks, arm64e, and many more.
+
+These mitigations guarantee a so-called "security property" on the binaries they
+produce. For example, for stack canaries, the security property is roughly that
+a canary is located on the stack between the set of saved registers and the set
+of local variables. For pac-ret, it is roughly that either the return address is
+never stored/retrieved to/from memory; or, there are no writes to the register
+containing the return address between an instruction authenticating it and a
+return instruction using it.
+
+From time to time, however, a bug gets found in the implementation of such
+mitigations in toolchains. Also, code that is written in assembler by hand
+requires the developer to ensure these security properties by hand.
+
+In short, it is sometimes found that a few places in the binary code are not
+protected as well as expected given the requested mitigations. Attackers could
+make use of those places (sometimes called gadgets) to circumvent the protection
+that the mitigation should give.
+
+One of the reasons that such gadgets, or holes in the mitigation implementation,
+exist is that typically the amount of testing and verification for these
+security properties is limited to checking results on specific examples.
+
+In comparison, for testing functional correctness, or for testing performance,
+toolchain and software in general typically get tested with large test suites
+and benchmarks. In contrast, this typically does not get done for testing the
+security properties of binary code.
+
+Unlike functional correctness where compilation errors result in test failures,
+and performance where speed and size differences are measurable, broken security
+properties cannot be easily observed using existing testing and benchmarking
+tools.
+
+The security scanners implemented in `llvm-bolt-binary-analysis` aim to enable
+the testing of security hardening in arbitrary programs and not just specific
+examples.
+
+
+#### pac-ret analysis
+
+`pac-ret` protection is a security hardening scheme implemented in compilers
+such as GCC and Clang, using the command line option
+`-mbranch-protection=pac-ret`. This option is enabled by default on most widely
+used Linux distributions.
+
+The hardening scheme mitigates
+[Return-Oriented Programming (ROP)](https://llsoftsec.github.io/llsoftsecbook/#return-oriented-programming)
+attacks by making sure that return addresses are only ever stored to memory with
+a cryptographic hash, called a
+["Pointer Authentication Code" (PAC)](https://llsoftsec.github.io/llsoftsecbook/#pointer-authentication),
+in the upper bits of the pointer. This makes it substantially harder for
+attackers to divert control flow by overwriting a return address with a
+different value.
+
+The hardening scheme relies on compilers producing appropriate code sequences when
+processing return addresses, especially when these are stored to and retrieved
+from memory.
+
+The `pac-ret` binary analysis can be invoked using the command line option
+`--scanners=pac-ret`. It makes `llvm-bolt-binary-analysis` scan through the
+provided binary, checking each function for the following security property:
+
+> For each procedure and exception return instruction, the destination register
+> must have one of the following properties:
+>
+> 1. be immutable within the function, or
+> 2. the last write to the register must be by an authenticating instruction. This
+> includes combined authentication and return instructions such as `RETAA`.
+
+##### Example 1
+
+For example, a typical non-pac-ret-protected function looks as follows:
+
+```
+ stp x29, x30, [sp, #-0x10]!
+ mov x29, sp
+ bl g@PLT
+ add x0, x0, #0x3
+ ldp x29, x30, [sp], #0x10
+ ret
+```
+
+The return instruction `ret` implicitly uses register `x30` as the address to
+return to. Register `x30` was last written by instruction `ldp`, which is not an
+authenticating instruction. `llvm-bolt-binary-analysis --scanners=pac-ret` will
+report this as follows:
+
+```
+GS-PACRET: non-protected ret found in function f1, basic block .LBB00, at address 10310
+ The return instruction is 00010310: ret # pacret-gadget: pac-ret-gadget<Ret:MCInstBBRef<BB:.LBB00:6>, Overwriting:[MCInstBBRef<BB:.LBB00:5> ]>
+ The 1 instructions that write to the return register after any authentication are:
+ 1. 0001030c: ldp x29, x30, [sp], #0x10
+ This happens in the following basic block:
+ 000102fc: stp x29, x30, [sp, #-0x10]!
+ 00010300: mov x29, sp
+ 00010304: bl g@PLT
+ 00010308: add x0, x0, #0x3
+ 0001030c: ldp x29, x30, [sp], #0x10
+ 00010310: ret # pacret-gadget: pac-ret-gadget<Ret:MCInstBBRef<BB:.LBB00:6>, Overwriting:[MCInstBBRef<BB:.LBB00:5> ]>
+```
+
+The exact format of how `llvm-bolt-binary-analysis` reports this is expected to
+evolve over time.
+
+##### Example 2: multiple "last-overwriting" instructions
+
+A simple example that shows how there can be a set of "last overwriting"
+instructions of a register follows:
+
+```
+ paciasp
+ stp x29, x30, [sp, #-0x10]!
+ ldp x29, x30, [sp], #0x10
+ cbnz x0, 1f
+ autiasp
+1:
+ ret
+```
+
+This will produce the following diagnostic:
+
+```
+GS-PACRET: non-protected ret found in function f_crossbb1, basic block .Ltmp0, at address 102dc
+ The return instruction is 000102dc: ret # pacret-gadget: pac-ret-gadget<Ret:MCInstBBRef<BB:.Ltmp0:0>, Overwriting:[MCInstBBRef<BB:.LFT0:0> MCInstBBRef<BB:.LBB00:2> ]>
+ The 2 instructions that write to the return register after any authentication are:
+ 1. 000102d0: ldp x29, x30, [sp], #0x10
+ 2. 000102d8: autiasp
+```
+
+(Yes, this diagnostic could be improved because the second "overwriting"
+instruction, `autiasp`, is an authenticating instruction...)
+
+##### Known false positives or negatives
+
+The following are current known cases of false positives:
+
+1. Not handling "no-return" functions. See issue
+ [#115154](https://github.com/llvm/llvm-project/issues/115154) for details and
+ pointers to open PRs to fix this.
+2. Not recognizing that a move of a properly authenticated value between registers,
+ results in the destination register having a properly authenticated value.
+ For example, the scanner currently produces a false negative for the following
+ code sequence:
+ ```
+ autiasp
+ mov x16, x30
+ ret x16
+ ```
+
+The following are current known cases of false negatives:
+
+1. Not handling functions for which the CFG cannot be reconstructed by BOLT. The
+ plan is to implement support for this, picking up the implementation from the
+ [prototype branch](
+ https://github.com/llvm/llvm-project/compare/main...kbeyls:llvm-project:bolt-gadget-scanner-prototype).
+
+BOLT cannot currently handle functions with `cfi_negate_ra_state` correctly,
+i.e. any binaries built with `-mbranch-protection=pac-ret`. The scanner is meant
+to be used on specifically such binaries, so this is a major limitation! Work is
+going on in PR [#120064](https://github.com/llvm/llvm-project/pull/120064) to
+fix this.
## How to add your own binary analysis
bolt/include/bolt/Core/BinarySection.h
@@ -359,15 +359,9 @@ public:
/// Add a new relocation at the given /p Offset.
void addRelocation(uint64_t Offset, MCSymbol *Symbol, uint64_t Type,
- uint64_t Addend, uint64_t Value = 0,
- bool Pending = false) {
+ uint64_t Addend, uint64_t Value = 0) {
assert(Offset < getSize() && "offset not within section bounds");
- if (!Pending) {
- Relocations.emplace(Relocation{Offset, Symbol, Type, Addend, Value});
- } else {
- PendingRelocations.emplace_back(
- Relocation{Offset, Symbol, Type, Addend, Value});
- }
+ Relocations.emplace(Relocation{Offset, Symbol, Type, Addend, Value});
}
/// Add a dynamic relocation at the given /p Offset.
bolt/include/bolt/Core/Linker.h
@@ -46,13 +46,6 @@ public:
/// Return the address and size of a symbol or std::nullopt if it cannot be
/// found.
virtual std::optional<SymbolInfo> lookupSymbolInfo(StringRef Name) const = 0;
-
- /// Return the address of a symbol or std::nullopt if it cannot be found.
- std::optional<uint64_t> lookupSymbol(StringRef Name) const {
- if (const auto Info = lookupSymbolInfo(Name))
- return Info->Address;
- return std::nullopt;
- }
};
} // namespace bolt
bolt/include/bolt/Core/MCPlusBuilder.h
@@ -27,6 +27,7 @@
#include "llvm/MC/MCInstrAnalysis.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCRegister.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
@@ -550,6 +551,22 @@ public:
return Analysis->isReturn(Inst);
}
+ virtual ErrorOr<MCPhysReg> getAuthenticatedReg(const MCInst &Inst) const {
+ llvm_unreachable("not implemented");
+ return getNoRegister();
+ }
+
+ virtual bool isAuthenticationOfReg(const MCInst &Inst,
+ MCPhysReg AuthenticatedReg) const {
+ llvm_unreachable("not implemented");
+ return false;
+ }
+
+ virtual ErrorOr<MCPhysReg> getRegUsedAsRetDest(const MCInst &Inst) const {
+ llvm_unreachable("not implemented");
+ return getNoRegister();
+ }
+
virtual bool isTerminator(const MCInst &Inst) const;
virtual bool isNoop(const MCInst &Inst) const {
bolt/include/bolt/Core/TargetConfig.def.in
@@ -0,0 +1,23 @@
+//===-- TargetConfig.def.in - Information about available targets ---------===//
+//
+// 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 is configured by the build system to define the available bolt
+// targets.
+//
+// The variant of this file not ending with .in has been autogenerated by the
+// LLVM build. Do not edit!
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef BOLT_TARGET
+# error Please define the macro BOLT_TARGET(TargetName)
+#endif
+
+@BOLT_ENUM_TARGETS@
+
+#undef BOLT_TARGET
bolt/include/bolt/Passes/NonPacProtectedRetAnalysis.h
@@ -0,0 +1,258 @@
+//===- bolt/Passes/NonPacProtectedRetAnalysis.h -----------------*- 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 BOLT_PASSES_NONPACPROTECTEDRETANALYSIS_H
+#define BOLT_PASSES_NONPACPROTECTEDRETANALYSIS_H
+
+#include "bolt/Core/BinaryContext.h"
+#include "bolt/Core/BinaryFunction.h"
+#include "bolt/Passes/BinaryPasses.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+
+namespace llvm {
+namespace bolt {
+
+/// @brief MCInstReference represents a reference to an MCInst as stored either
+/// in a BinaryFunction (i.e. before a CFG is created), or in a BinaryBasicBlock
+/// (after a CFG is created). It aims to store the necessary information to be
+/// able to find the specific MCInst in either the BinaryFunction or
+/// BinaryBasicBlock data structures later, so that e.g. the InputAddress of
+/// the corresponding instruction can be computed.
+
+struct MCInstInBBReference {
+ BinaryBasicBlock *BB;
+ int64_t BBIndex;
+ MCInstInBBReference(BinaryBasicBlock *BB, int64_t BBIndex)
+ : BB(BB), BBIndex(BBIndex) {}
+ MCInstInBBReference() : BB(nullptr), BBIndex(0) {}
+ static MCInstInBBReference get(const MCInst *Inst, BinaryFunction &BF) {
+ for (BinaryBasicBlock &BB : BF)
+ for (size_t I = 0; I < BB.size(); ++I)
+ if (Inst == &BB.getInstructionAtIndex(I))
+ return MCInstInBBReference(&BB, I);
+ return {};
+ }
+ bool operator==(const MCInstInBBReference &RHS) const {
+ return BB == RHS.BB && BBIndex == RHS.BBIndex;
+ }
+ bool operator<(const MCInstInBBReference &RHS) const {
+ if (BB != RHS.BB)
+ return BB < RHS.BB;
+ return BBIndex < RHS.BBIndex;
+ }
+ operator MCInst &() const {
+ assert(BB != nullptr);
+ return BB->getInstructionAtIndex(BBIndex);
+ }
+ uint64_t getAddress() const {
+ // 4 bytes per instruction on AArch64.
+ // FIXME: the assumption of 4 byte per instruction needs to be fixed before
+ // this method gets used on any non-AArch64 binaries (but should be fine for
+ // pac-ret analysis, as that is an AArch64-specific feature).
+ return BB->getFunction()->getAddress() + BB->getOffset() + BBIndex * 4;
+ }
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const MCInstInBBReference &);
+
+struct MCInstInBFReference {
+ BinaryFunction *BF;
+ uint64_t Offset;
+ MCInstInBFReference(BinaryFunction *BF, uint64_t Offset)
+ : BF(BF), Offset(Offset) {}
+ MCInstInBFReference() : BF(nullptr), Offset(0) {}
+ bool operator==(const MCInstInBFReference &RHS) const {
+ return BF == RHS.BF && Offset == RHS.Offset;
+ }
+ bool operator<(const MCInstInBFReference &RHS) const {
+ if (BF != RHS.BF)
+ return BF < RHS.BF;
+ return Offset < RHS.Offset;
+ }
+ operator MCInst &() const {
+ assert(BF != nullptr);
+ return *BF->getInstructionAtOffset(Offset);
+ }
+
+ uint64_t getOffset() const { return Offset; }
+
+ uint64_t getAddress() const { return BF->getAddress() + getOffset(); }
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const MCInstInBFReference &);
+
+struct MCInstReference {
+ enum Kind { FunctionParent, BasicBlockParent };
+ Kind ParentKind;
+ union U {
+ MCInstInBBReference BBRef;
+ MCInstInBFReference BFRef;
+ U(MCInstInBBReference BBRef) : BBRef(BBRef) {}
+ U(MCInstInBFReference BFRef) : BFRef(BFRef) {}
+ } U;
+ MCInstReference(MCInstInBBReference BBRef)
+ : ParentKind(BasicBlockParent), U(BBRef) {}
+ MCInstReference(MCInstInBFReference BFRef)
+ : ParentKind(FunctionParent), U(BFRef) {}
+ MCInstReference(BinaryBasicBlock *BB, int64_t BBIndex)
+ : MCInstReference(MCInstInBBReference(BB, BBIndex)) {}
+ MCInstReference(BinaryFunction *BF, uint32_t Offset)
+ : MCInstReference(MCInstInBFReference(BF, Offset)) {}
+
+ bool operator<(const MCInstReference &RHS) const {
+ if (ParentKind != RHS.ParentKind)
+ return ParentKind < RHS.ParentKind;
+ switch (ParentKind) {
+ case BasicBlockParent:
+ return U.BBRef < RHS.U.BBRef;
+ case FunctionParent:
+ return U.BFRef < RHS.U.BFRef;
+ }
+ llvm_unreachable("");
+ }
+
+ bool operator==(const MCInstReference &RHS) const {
+ if (ParentKind != RHS.ParentKind)
+ return false;
+ switch (ParentKind) {
+ case BasicBlockParent:
+ return U.BBRef == RHS.U.BBRef;
+ case FunctionParent:
+ return U.BFRef == RHS.U.BFRef;
+ }
+ llvm_unreachable("");
+ }
+
+ operator MCInst &() const {
+ switch (ParentKind) {
+ case BasicBlockParent:
+ return U.BBRef;
+ case FunctionParent:
+ return U.BFRef;
+ }
+ llvm_unreachable("");
+ }
+
+ uint64_t getAddress() const {
+ switch (ParentKind) {
+ case BasicBlockParent:
+ return U.BBRef.getAddress();
+ case FunctionParent:
+ return U.BFRef.getAddress();
+ }
+ llvm_unreachable("");
+ }
+
+ BinaryFunction *getFunction() const {
+ switch (ParentKind) {
+ case FunctionParent:
+ return U.BFRef.BF;
+ case BasicBlockParent:
+ return U.BBRef.BB->getFunction();
+ }
+ llvm_unreachable("");
+ }
+
+ BinaryBasicBlock *getBasicBlock() const {
+ switch (ParentKind) {
+ case FunctionParent:
+ return nullptr;
+ case BasicBlockParent:
+ return U.BBRef.BB;
+ }
+ llvm_unreachable("");
+ }
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const MCInstReference &);
+
+struct GeneralDiagnostic {
+ std::string Text;
+ GeneralDiagnostic(const std::string &Text) : Text(Text) {}
+ bool operator==(const GeneralDiagnostic &RHS) const {
+ return Text == RHS.Text;
+ }
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const GeneralDiagnostic &Diag);
+
+namespace NonPacProtectedRetAnalysis {
+struct Annotation {
+ MCInstReference RetInst;
+ Annotation(MCInstReference RetInst) : RetInst(RetInst) {}
+ virtual bool operator==(const Annotation &RHS) const {
+ return RetInst == RHS.RetInst;
+ }
+ Annotation &operator=(const Annotation &Other) {
+ if (this == &Other)
+ return *this;
+ RetInst = Other.RetInst;
+ return *this;
+ }
+ virtual ~Annotation() {}
+ virtual void generateReport(raw_ostream &OS,
+ const BinaryContext &BC) const = 0;
+};
+
+struct Gadget : public Annotation {
+ std::vector<MCInstReference> OverwritingRetRegInst;
+ virtual bool operator==(const Gadget &RHS) const {
+ return Annotation::operator==(RHS) &&
+ OverwritingRetRegInst == RHS.OverwritingRetRegInst;
+ }
+ Gadget(MCInstReference RetInst,
+ const std::vector<MCInstReference> &OverwritingRetRegInst)
+ : Annotation(RetInst), OverwritingRetRegInst(OverwritingRetRegInst) {}
+ virtual void generateReport(raw_ostream &OS,
+ const BinaryContext &BC) const override;
+};
+
+struct GenDiag : public Annotation {
+ GeneralDiagnostic Diag;
+ virtual bool operator==(const GenDiag &RHS) const {
+ return Annotation::operator==(RHS) && Diag == RHS.Diag;
+ }
+ GenDiag(MCInstReference RetInst, const std::string &Text)
+ : Annotation(RetInst), Diag(Text) {}
+ virtual void generateReport(raw_ostream &OS,
+ const BinaryContext &BC) const override;
+};
+
+class PacRetAnalysis;
+
+struct FunctionAnalysisResult {
+ SmallSet<MCPhysReg, 1> RegistersAffected;
+ std::vector<std::shared_ptr<Annotation>> Diagnostics;
+};
+
+class Analysis : public BinaryFunctionPass {
+ void runOnFunction(BinaryFunction &Function,
+ MCPlusBuilder::AllocatorIdTy AllocatorId);
+ FunctionAnalysisResult
+ computeDfState(PacRetAnalysis &PRA, BinaryFunction &BF,
+ MCPlusBuilder::AllocatorIdTy AllocatorId);
+
+ std::map<const BinaryFunction *, FunctionAnalysisResult> AnalysisResults;
+ std::mutex AnalysisResultsMutex;
+
+public:
+ explicit Analysis() : BinaryFunctionPass(false) {}
+
+ const char *getName() const override { return "non-pac-protected-rets"; }
+
+ /// Pass entry point
+ Error runOnFunctions(BinaryContext &BC) override;
+};
+
+} // namespace NonPacProtectedRetAnalysis
+} // namespace bolt
+} // namespace llvm
+
+#endif
bolt/include/bolt/Profile/DataAggregator.h
@@ -94,7 +94,7 @@ private:
/// Used for parsing specific pre-aggregated input files.
struct AggregatedLBREntry {
- enum Type : char { BRANCH = 0, FT, FT_EXTERNAL_ORIGIN };
+ enum Type : char { BRANCH = 0, FT, FT_EXTERNAL_ORIGIN, TRACE };
Location From;
Location To;
uint64_t Count;
@@ -197,6 +197,10 @@ private:
BoltAddressTranslation *BAT{nullptr};
+ /// Whether pre-aggregated profile needs to convert branch profile into call
+ /// to continuation fallthrough profile.
+ bool NeedsConvertRetProfileToCallCont{false};
+
/// Update function execution profile with a recorded trace.
/// A trace is region of code executed between two LBR entries supplied in
/// execution order.
@@ -268,8 +272,7 @@ private:
uint64_t Mispreds);
/// Register a \p Branch.
- bool doBranch(uint64_t From, uint64_t To, uint64_t Count, uint64_t Mispreds,
- bool IsPreagg);
+ bool doBranch(uint64_t From, uint64_t To, uint64_t Count, uint64_t Mispreds);
/// Register a trace between two LBR entries supplied in execution order.
bool doTrace(const LBREntry &First, const LBREntry &Second,
@@ -298,7 +301,7 @@ private:
ErrorOr<PerfMemSample> parseMemSample();
/// Parse pre-aggregated LBR samples created by an external tool
- ErrorOr<AggregatedLBREntry> parseAggregatedLBREntry();
+ std::error_code parseAggregatedLBREntry();
/// Parse either buildid:offset or just offset, representing a location in the
/// binary. Used exclusively for pre-aggregated LBR samples.
@@ -384,14 +387,15 @@ private:
/// memory.
///
/// File format syntax:
- /// {B|F|f} [<start_id>:]<start_offset> [<end_id>:]<end_offset> <count>
- /// [<mispred_count>]
+ /// {B|F|f|T} [<start_id>:]<start_offset> [<end_id>:]<end_offset> [<ft_end>]
+ /// <count> [<mispred_count>]
///
/// B - indicates an aggregated branch
/// F - an aggregated fall-through
/// f - an aggregated fall-through with external origin - used to disambiguate
/// between a return hitting a basic block head and a regular internal
/// jump to the block
+ /// T - an aggregated trace: branch with a fall-through (from, to, ft_end)
///
/// <start_id> - build id of the object containing the start address. We can
/// skip it for the main binary and use "X" for an unknown object. This will
@@ -402,6 +406,8 @@ private:
///
/// <end_id>, <end_offset> - same for the end address.
///
+ /// <ft_end> - same for the fallthrough_end address.
+ ///
/// <count> - total aggregated count of the branch or a fall-through.
///
/// <mispred_count> - the number of times the branch was mispredicted.
bolt/include/bolt/Utils/CommandLineOpts.h
@@ -80,6 +80,10 @@ extern llvm::cl::opt<unsigned> Verbosity;
/// Return true if we should process all functions in the binary.
bool processAllFunctions();
+enum GadgetScannerKind { GS_PACRET, GS_ALL };
+
+extern llvm::cl::list<GadgetScannerKind> GadgetScannersToRun;
+
} // namespace opts
namespace llvm {
bolt/lib/Core/BinaryContext.cpp
@@ -1759,7 +1759,11 @@ void BinaryContext::preprocessDebugInfo() {
dwarf::toString(CU->getUnitDIE().find(dwarf::DW_AT_name), nullptr);
if (std::optional<uint64_t> DWOID = CU->getDWOId()) {
auto Iter = DWOCUs.find(*DWOID);
- assert(Iter != DWOCUs.end() && "DWO CU was not found.");
+ if (Iter == DWOCUs.end()) {
+ this->errs() << "BOLT-ERROR: DWO CU was not found for " << Name
+ << '\n';
+ exit(1);
+ }
Name = dwarf::toString(
Iter->second->getUnitDIE().find(dwarf::DW_AT_name), nullptr);
}
bolt/lib/Core/BinaryFunction.cpp
@@ -15,6 +15,7 @@
#include "bolt/Core/DynoStats.h"
#include "bolt/Core/HashUtilities.h"
#include "bolt/Core/MCPlusBuilder.h"
+#include "bolt/Utils/CommandLineOpts.h"
#include "bolt/Utils/NameResolver.h"
#include "bolt/Utils/NameShortener.h"
#include "bolt/Utils/Utils.h"
@@ -498,6 +499,11 @@ void BinaryFunction::print(raw_ostream &OS, std::string Annotation) {
if (!IslandOffset)
return;
+ // Print label if it exists at this offset.
+ if (const BinaryData *BD =
+ BC.getBinaryDataAtAddress(getAddress() + *IslandOffset))
+ OS << BD->getName() << ":\n";
+
const size_t IslandSize = getSizeOfDataInCodeAt(*IslandOffset);
BC.printData(OS, BC.extractData(getAddress() + *IslandOffset, IslandSize),
*IslandOffset);
@@ -1066,7 +1072,7 @@ size_t BinaryFunction::getSizeOfDataInCodeAt(uint64_t Offset) const {
auto Iter = Islands->CodeOffsets.upper_bound(Offset);
if (Iter != Islands->CodeOffsets.end())
return *Iter - Offset;
- return getSize() - Offset;
+ return getMaxSize() - Offset;
}
std::optional<uint64_t>
@@ -1748,8 +1754,7 @@ void BinaryFunction::postProcessEntryPoints() {
// In non-relocation mode there's potentially an external undetectable
// reference to the entry point and hence we cannot move this entry
// point. Optimizing without moving could be difficult.
- // In BAT mode, register any known entry points for CFG construction.
- if (!BC.HasRelocations && !BC.HasBATSection)
+ if (!BC.HasRelocations)
setSimple(false);
const uint32_t Offset = KV.first;
@@ -2078,7 +2083,7 @@ void BinaryFunction::recomputeLandingPads() {
Error BinaryFunction::buildCFG(MCPlusBuilder::AllocatorIdTy AllocatorId) {
auto &MIB = BC.MIB;
- if (!isSimple()) {
+ if (!isSimple() && !opts::AggregateOnly) {
assert(!BC.HasRelocations &&
"cannot process file with non-simple function in relocs mode");
return createNonFatalBOLTError("");
@@ -4254,10 +4259,10 @@ void BinaryFunction::updateOutputValues(const BOLTLinker &Linker) {
if (BC.HasRelocations || isInjected()) {
if (hasConstantIsland()) {
- const auto DataAddress =
- Linker.lookupSymbol(getFunctionConstantIslandLabel()->getName());
- assert(DataAddress && "Cannot find function CI symbol");
- setOutputDataAddress(*DataAddress);
+ const auto IslandLabelSymInfo =
+ Linker.lookupSymbolInfo(getFunctionConstantIslandLabel()->getName());
+ assert(IslandLabelSymInfo && "Cannot find function CI symbol");
+ setOutputDataAddress(IslandLabelSymInfo->Address);
for (auto It : Islands->Offsets) {
const uint64_t OldOffset = It.first;
BinaryData *BD = BC.getBinaryDataAtAddress(getAddress() + OldOffset);
@@ -4265,10 +4270,10 @@ void BinaryFunction::updateOutputValues(const BOLTLinker &Linker) {
continue;
MCSymbol *Symbol = It.second;
- const auto NewAddress = Linker.lookupSymbol(Symbol->getName());
- assert(NewAddress && "Cannot find CI symbol");
+ const auto SymInfo = Linker.lookupSymbolInfo(Symbol->getName());
+ assert(SymInfo && "Cannot find CI symbol");
auto &Section = *getCodeSection();
- const auto NewOffset = *NewAddress - Section.getOutputAddress();
+ const auto NewOffset = SymInfo->Address - Section.getOutputAddress();
BD->setOutputLocation(Section, NewOffset);
}
}
@@ -4293,10 +4298,10 @@ void BinaryFunction::updateOutputValues(const BOLTLinker &Linker) {
FF.setAddress(ColdStartSymbolInfo->Address);
FF.setImageSize(ColdStartSymbolInfo->Size);
if (hasConstantIsland()) {
- const auto DataAddress = Linker.lookupSymbol(
+ const auto SymInfo = Linker.lookupSymbolInfo(
getFunctionColdConstantIslandLabel()->getName());
- assert(DataAddress && "Cannot find cold CI symbol");
- setOutputColdDataAddress(*DataAddress);
+ assert(SymInfo && "Cannot find cold CI symbol");
+ setOutputColdDataAddress(SymInfo->Address);
}
}
}
bolt/lib/Passes/CMakeLists.txt
@@ -23,6 +23,7 @@ add_llvm_library(LLVMBOLTPasses
LoopInversionPass.cpp
LivenessAnalysis.cpp
MCF.cpp
+ NonPacProtectedRetAnalysis.cpp
PatchEntries.cpp
PettisAndHansen.cpp
PLTCall.cpp
bolt/lib/Passes/NonPacProtectedRetAnalysis.cpp
@@ -0,0 +1,526 @@
+//===- bolt/Passes/NonPacProtectedRetAnalysis.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 file implements a pass that looks for any AArch64 return instructions
+// that may not be protected by PAuth authentication instructions when needed.
+//
+//===----------------------------------------------------------------------===//
+
+#include "bolt/Passes/NonPacProtectedRetAnalysis.h"
+#include "bolt/Core/ParallelUtilities.h"
+#include "bolt/Passes/DataflowAnalysis.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/Support/Format.h"
+#include <memory>
+
+#define DEBUG_TYPE "bolt-nonpacprotectedret"
+
+namespace llvm {
+namespace bolt {
+
+raw_ostream &operator<<(raw_ostream &OS, const MCInstInBBReference &Ref) {
+ OS << "MCInstBBRef<";
+ if (Ref.BB == nullptr)
+ OS << "BB:(null)";
+ else
+ OS << "BB:" << Ref.BB->getName() << ":" << Ref.BBIndex;
+ OS << ">";
+ return OS;
+}
+
+raw_ostream &operator<<(raw_ostream &OS, const MCInstInBFReference &Ref) {
+ OS << "MCInstBFRef<";
+ if (Ref.BF == nullptr)
+ OS << "BF:(null)";
+ else
+ OS << "BF:" << Ref.BF->getPrintName() << ":" << Ref.getOffset();
+ OS << ">";
+ return OS;
+}
+
+raw_ostream &operator<<(raw_ostream &OS, const MCInstReference &Ref) {
+ switch (Ref.ParentKind) {
+ case MCInstReference::BasicBlockParent:
+ OS << Ref.U.BBRef;
+ return OS;
+ case MCInstReference::FunctionParent:
+ OS << Ref.U.BFRef;
+ return OS;
+ }
+ llvm_unreachable("");
+}
+
+namespace NonPacProtectedRetAnalysis {
+
+// The security property that is checked is:
+// When a register is used as the address to jump to in a return instruction,
+// that register must either:
+// (a) never be changed within this function, i.e. have the same value as when
+// the function started, or
+// (b) the last write to the register must be by an authentication instruction.
+
+// This property is checked by using dataflow analysis to keep track of which
+// registers have been written (def-ed), since last authenticated. Those are
+// exactly the registers containing values that should not be trusted (as they
+// could have changed since the last time they were authenticated). For pac-ret,
+// any return instruction using such a register is a gadget to be reported. For
+// PAuthABI, probably at least any indirect control flow using such a register
+// should be reported.
+
+// Furthermore, when producing a diagnostic for a found non-pac-ret protected
+// return, the analysis also lists the last instructions that wrote to the
+// register used in the return instruction.
+// The total set of registers used in return instructions in a given function is
+// small. It almost always is just `X30`.
+// In order to reduce the memory consumption of storing this additional state
+// during the dataflow analysis, this is computed by running the dataflow
+// analysis twice:
+// 1. In the first run, the dataflow analysis only keeps track of the security
+// property: i.e. which registers have been overwritten since the last
+// time they've been authenticated.
+// 2. If the first run finds any return instructions using a register last
+// written by a non-authenticating instruction, the dataflow analysis will
+// be run a second time. The first run will return which registers are used
+// in the gadgets to be reported. This information is used in the second run
+// to also track which instructions last wrote to those registers.
+
+struct State {
+ /// A BitVector containing the registers that have been clobbered, and
+ /// not authenticated.
+ BitVector NonAutClobRegs;
+ /// A vector of sets, only used in the second data flow run.
+ /// Each element in the vector represents one of the registers for which we
+ /// track the set of last instructions that wrote to this register. For
+ /// pac-ret analysis, the expectation is that almost all return instructions
+ /// only use register `X30`, and therefore, this vector will probably have
+ /// length 1 in the second run.
+ std::vector<SmallPtrSet<const MCInst *, 4>> LastInstWritingReg;
+ State() {}
+ State(unsigned NumRegs, unsigned NumRegsToTrack)
+ : NonAutClobRegs(NumRegs), LastInstWritingReg(NumRegsToTrack) {}
+ State &operator|=(const State &StateIn) {
+ NonAutClobRegs |= StateIn.NonAutClobRegs;
+ for (unsigned I = 0; I < LastInstWritingReg.size(); ++I)
+ for (const MCInst *J : StateIn.LastInstWritingReg[I])
+ LastInstWritingReg[I].insert(J);
+ return *this;
+ }
+ bool operator==(const State &RHS) const {
+ return NonAutClobRegs == RHS.NonAutClobRegs &&
+ LastInstWritingReg == RHS.LastInstWritingReg;
+ }
+ bool operator!=(const State &RHS) const { return !((*this) == RHS); }
+};
+
+static void printLastInsts(
+ raw_ostream &OS,
+ const std::vector<SmallPtrSet<const MCInst *, 4>> &LastInstWritingReg) {
+ OS << "Insts: ";
+ for (unsigned I = 0; I < LastInstWritingReg.size(); ++I) {
+ auto &Set = LastInstWritingReg[I];
+ OS << "[" << I << "](";
+ for (const MCInst *MCInstP : Set)
+ OS << MCInstP << " ";
+ OS << ")";
+ }
+}
+
+raw_ostream &operator<<(raw_ostream &OS, const State &S) {
+ OS << "pacret-state<";
+ OS << "NonAutClobRegs: " << S.NonAutClobRegs << ", ";
+ printLastInsts(OS, S.LastInstWritingReg);
+ OS << ">";
+ return OS;
+}
+
+class PacStatePrinter {
+public:
+ void print(raw_ostream &OS, const State &State) const;
+ explicit PacStatePrinter(const BinaryContext &BC) : BC(BC) {}
+
+private:
+ const BinaryContext &BC;
+};
+
+void PacStatePrinter::print(raw_ostream &OS, const State &S) const {
+ RegStatePrinter RegStatePrinter(BC);
+ OS << "pacret-state<";
+ OS << "NonAutClobRegs: ";
+ RegStatePrinter.print(OS, S.NonAutClobRegs);
+ OS << ", ";
+ printLastInsts(OS, S.LastInstWritingReg);
+ OS << ">";
+}
+
+class PacRetAnalysis
+ : public DataflowAnalysis<PacRetAnalysis, State, /*Backward=*/false,
+ PacStatePrinter> {
+ using Parent =
+ DataflowAnalysis<PacRetAnalysis, State, false, PacStatePrinter>;
+ friend Parent;
+
+public:
+ PacRetAnalysis(BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId,
+ const std::vector<MCPhysReg> &RegsToTrackInstsFor)
+ : Parent(BF, AllocId), NumRegs(BF.getBinaryContext().MRI->getNumRegs()),
+ RegsToTrackInstsFor(RegsToTrackInstsFor),
+ TrackingLastInsts(!RegsToTrackInstsFor.empty()),
+ Reg2StateIdx(RegsToTrackInstsFor.empty()
+ ? 0
+ : *llvm::max_element(RegsToTrackInstsFor) + 1,
+ -1) {
+ for (unsigned I = 0; I < RegsToTrackInstsFor.size(); ++I)
+ Reg2StateIdx[RegsToTrackInstsFor[I]] = I;
+ }
+ virtual ~PacRetAnalysis() {}
+
+protected:
+ const unsigned NumRegs;
+ /// RegToTrackInstsFor is the set of registers for which the dataflow analysis
+ /// must compute which the last set of instructions writing to it are.
+ const std::vector<MCPhysReg> RegsToTrackInstsFor;
+ const bool TrackingLastInsts;
+ /// Reg2StateIdx maps Register to the index in the vector used in State to
+ /// track which instructions last wrote to this register.
+ std::vector<uint16_t> Reg2StateIdx;
+
+ SmallPtrSet<const MCInst *, 4> &lastWritingInsts(State &S,
+ MCPhysReg Reg) const {
+ assert(Reg < Reg2StateIdx.size());
+ assert(isTrackingReg(Reg));
+ return S.LastInstWritingReg[Reg2StateIdx[Reg]];
+ }
+ const SmallPtrSet<const MCInst *, 4> &lastWritingInsts(const State &S,
+ MCPhysReg Reg) const {
+ assert(Reg < Reg2StateIdx.size());
+ assert(isTrackingReg(Reg));
+ return S.LastInstWritingReg[Reg2StateIdx[Reg]];
+ }
+
+ bool isTrackingReg(MCPhysReg Reg) const {
+ return llvm::is_contained(RegsToTrackInstsFor, Reg);
+ }
+
+ void preflight() {}
+
+ State getStartingStateAtBB(const BinaryBasicBlock &BB) {
+ return State(NumRegs, RegsToTrackInstsFor.size());
+ }
+
+ State getStartingStateAtPoint(const MCInst &Point) {
+ return State(NumRegs, RegsToTrackInstsFor.size());
+ }
+
+ void doConfluence(State &StateOut, const State &StateIn) {
+ PacStatePrinter P(BC);
+ LLVM_DEBUG({
+ dbgs() << " PacRetAnalysis::Confluence(\n";
+ dbgs() << " State 1: ";
+ P.print(dbgs(), StateOut);
+ dbgs() << "\n";
+ dbgs() << " State 2: ";
+ P.print(dbgs(), StateIn);
+ dbgs() << ")\n";
+ });
+
+ StateOut |= StateIn;
+
+ LLVM_DEBUG({
+ dbgs() << " merged state: ";
+ P.print(dbgs(), StateOut);
+ dbgs() << "\n";
+ });
+ }
+
+ State computeNext(const MCInst &Point, const State &Cur) {
+ PacStatePrinter P(BC);
+ LLVM_DEBUG({
+ dbgs() << " PacRetAnalysis::ComputeNext(";
+ BC.InstPrinter->printInst(&const_cast<MCInst &>(Point), 0, "", *BC.STI,
+ dbgs());
+ dbgs() << ", ";
+ P.print(dbgs(), Cur);
+ dbgs() << ")\n";
+ });
+
+ State Next = Cur;
+ BitVector Written = BitVector(NumRegs, false);
+ // Assume a call can clobber all registers, including callee-saved
+ // registers. There's a good chance that callee-saved registers will be
+ // saved on the stack at some point during execution of the callee.
+ // Therefore they should also be considered as potentially modified by an
+ // attacker/written to.
+ // Also, not all functions may respect the AAPCS ABI rules about
+ // caller/callee-saved registers.
+ if (BC.MIB->isCall(Point))
+ Written.set();
+ else
+ // FIXME: `getWrittenRegs` only sets the register directly written in the
+ // instruction, and the smaller aliasing registers. It does not set the
+ // larger aliasing registers. To also set the larger aliasing registers,
+ // we'd have to call `getClobberedRegs`.
+ // It is unclear if there is any test case which shows a different
+ // behaviour between using `getWrittenRegs` vs `getClobberedRegs`. We'd
+ // first would like to see such a test case before making a decision
+ // on whether using `getClobberedRegs` below would be better.
+ // Also see the discussion on this at
+ // https://github.com/llvm/llvm-project/pull/122304#discussion_r1939511909
+ BC.MIB->getWrittenRegs(Point, Written);
+ Next.NonAutClobRegs |= Written;
+ // Keep track of this instruction if it writes to any of the registers we
+ // need to track that for:
+ for (MCPhysReg Reg : RegsToTrackInstsFor)
+ if (Written[Reg])
+ lastWritingInsts(Next, Reg) = {&Point};
+
+ ErrorOr<MCPhysReg> AutReg = BC.MIB->getAuthenticatedReg(Point);
+ if (AutReg && *AutReg != BC.MIB->getNoRegister()) {
+ // FIXME: should we use `OnlySmaller=false` below? See similar
+ // FIXME about `getWrittenRegs` above and further discussion about this
+ // at
+ // https://github.com/llvm/llvm-project/pull/122304#discussion_r1939515516
+ Next.NonAutClobRegs.reset(
+ BC.MIB->getAliases(*AutReg, /*OnlySmaller=*/true));
+ if (TrackingLastInsts && isTrackingReg(*AutReg))
+ lastWritingInsts(Next, *AutReg).clear();
+ }
+
+ LLVM_DEBUG({
+ dbgs() << " .. result: (";
+ P.print(dbgs(), Next);
+ dbgs() << ")\n";
+ });
+
+ return Next;
+ }
+
+ StringRef getAnnotationName() const { return StringRef("PacRetAnalysis"); }
+
+public:
+ std::vector<MCInstReference>
+ getLastClobberingInsts(const MCInst Ret, BinaryFunction &BF,
+ const BitVector &UsedDirtyRegs) const {
+ if (!TrackingLastInsts)
+ return {};
+ auto MaybeState = getStateAt(Ret);
+ if (!MaybeState)
+ llvm_unreachable("Expected State to be present");
+ const State &S = *MaybeState;
+ // Due to aliasing registers, multiple registers may have been tracked.
+ std::set<const MCInst *> LastWritingInsts;
+ for (MCPhysReg TrackedReg : UsedDirtyRegs.set_bits()) {
+ for (const MCInst *Inst : lastWritingInsts(S, TrackedReg))
+ LastWritingInsts.insert(Inst);
+ }
+ std::vector<MCInstReference> Result;
+ for (const MCInst *Inst : LastWritingInsts) {
+ MCInstInBBReference Ref = MCInstInBBReference::get(Inst, BF);
+ assert(Ref.BB != nullptr && "Expected Inst to be found");
+ Result.push_back(MCInstReference(Ref));
+ }
+ return Result;
+ }
+};
+
+FunctionAnalysisResult
+Analysis::computeDfState(PacRetAnalysis &PRA, BinaryFunction &BF,
+ MCPlusBuilder::AllocatorIdTy AllocatorId) {
+ PRA.run();
+ LLVM_DEBUG({
+ dbgs() << " After PacRetAnalysis:\n";
+ BF.dump();
+ });
+
+ FunctionAnalysisResult Result;
+ // Now scan the CFG for non-authenticating return instructions that use an
+ // overwritten, non-authenticated register as return address.
+ BinaryContext &BC = BF.getBinaryContext();
+ for (BinaryBasicBlock &BB : BF) {
+ for (int64_t I = BB.size() - 1; I >= 0; --I) {
+ MCInst &Inst = BB.getInstructionAtIndex(I);
+ if (BC.MIB->isReturn(Inst)) {
+ ErrorOr<MCPhysReg> MaybeRetReg = BC.MIB->getRegUsedAsRetDest(Inst);
+ if (MaybeRetReg.getError()) {
+ Result.Diagnostics.push_back(std::make_shared<GenDiag>(
+ MCInstInBBReference(&BB, I),
+ "Warning: pac-ret analysis could not analyze this return "
+ "instruction"));
+ continue;
+ }
+ MCPhysReg RetReg = *MaybeRetReg;
+ LLVM_DEBUG({
+ dbgs() << " Found RET inst: ";
+ BC.printInstruction(dbgs(), Inst);
+ dbgs() << " RetReg: " << BC.MRI->getName(RetReg)
+ << "; authenticatesReg: "
+ << BC.MIB->isAuthenticationOfReg(Inst, RetReg) << "\n";
+ });
+ if (BC.MIB->isAuthenticationOfReg(Inst, RetReg))
+ break;
+ BitVector UsedDirtyRegs = PRA.getStateAt(Inst)->NonAutClobRegs;
+ LLVM_DEBUG({
+ dbgs() << " NonAutClobRegs at Ret: ";
+ RegStatePrinter RSP(BC);
+ RSP.print(dbgs(), UsedDirtyRegs);
+ dbgs() << "\n";
+ });
+ UsedDirtyRegs &= BC.MIB->getAliases(RetReg, /*OnlySmaller=*/true);
+ LLVM_DEBUG({
+ dbgs() << " Intersection with RetReg: ";
+ RegStatePrinter RSP(BC);
+ RSP.print(dbgs(), UsedDirtyRegs);
+ dbgs() << "\n";
+ });
+ if (UsedDirtyRegs.any()) {
+ // This return instruction needs to be reported
+ Result.Diagnostics.push_back(std::make_shared<Gadget>(
+ MCInstInBBReference(&BB, I),
+ PRA.getLastClobberingInsts(Inst, BF, UsedDirtyRegs)));
+ for (MCPhysReg RetRegWithGadget : UsedDirtyRegs.set_bits())
+ Result.RegistersAffected.insert(RetRegWithGadget);
+ }
+ }
+ }
+ }
+ return Result;
+}
+
+void Analysis::runOnFunction(BinaryFunction &BF,
+ MCPlusBuilder::AllocatorIdTy AllocatorId) {
+ LLVM_DEBUG({
+ dbgs() << "Analyzing in function " << BF.getPrintName() << ", AllocatorId "
+ << AllocatorId << "\n";
+ BF.dump();
+ });
+
+ if (BF.hasCFG()) {
+ PacRetAnalysis PRA(BF, AllocatorId, {});
+ FunctionAnalysisResult FAR = computeDfState(PRA, BF, AllocatorId);
+ if (!FAR.RegistersAffected.empty()) {
+ // Redo the analysis, but now also track which instructions last wrote
+ // to any of the registers in RetRegsWithGadgets, so that better
+ // diagnostics can be produced.
+ std::vector<MCPhysReg> RegsToTrack;
+ for (MCPhysReg R : FAR.RegistersAffected)
+ RegsToTrack.push_back(R);
+ PacRetAnalysis PRWIA(BF, AllocatorId, RegsToTrack);
+ FAR = computeDfState(PRWIA, BF, AllocatorId);
+ }
+
+ // `runOnFunction` is typically getting called from multiple threads in
+ // parallel. Therefore, use a lock to avoid data races when storing the
+ // result of the analysis in the `AnalysisResults` map.
+ {
+ std::lock_guard<std::mutex> Lock(AnalysisResultsMutex);
+ AnalysisResults[&BF] = FAR;
+ }
+ }
+}
+
+static void printBB(const BinaryContext &BC, const BinaryBasicBlock *BB,
+ size_t StartIndex = 0, size_t EndIndex = -1) {
+ if (EndIndex == (size_t)-1)
+ EndIndex = BB->size() - 1;
+ const BinaryFunction *BF = BB->getFunction();
+ for (unsigned I = StartIndex; I <= EndIndex; ++I) {
+ // FIXME: this assumes all instructions are 4 bytes in size. This is true
+ // for AArch64, but it might be good to extract this function so it can be
+ // used elsewhere and for other targets too.
+ uint64_t Address = BB->getOffset() + BF->getAddress() + 4 * I;
+ const MCInst &Inst = BB->getInstructionAtIndex(I);
+ if (BC.MIB->isCFI(Inst))
+ continue;
+ BC.printInstruction(outs(), Inst, Address, BF);
+ }
+}
+
+static void reportFoundGadgetInSingleBBSingleOverwInst(
+ raw_ostream &OS, const BinaryContext &BC, const MCInstReference OverwInst,
+ const MCInstReference RetInst) {
+ BinaryBasicBlock *BB = RetInst.getBasicBlock();
+ assert(OverwInst.ParentKind == MCInstReference::BasicBlockParent);
+ assert(RetInst.ParentKind == MCInstReference::BasicBlockParent);
+ MCInstInBBReference OverwInstBB = OverwInst.U.BBRef;
+ if (BB == OverwInstBB.BB) {
+ // overwriting inst and ret instruction are in the same basic block.
+ assert(OverwInstBB.BBIndex < RetInst.U.BBRef.BBIndex);
+ OS << " This happens in the following basic block:\n";
+ printBB(BC, BB);
+ }
+}
+
+void Gadget::generateReport(raw_ostream &OS, const BinaryContext &BC) const {
+ GenDiag(RetInst, "non-protected ret found").generateReport(OS, BC);
+
+ BinaryFunction *BF = RetInst.getFunction();
+ OS << " The " << OverwritingRetRegInst.size()
+ << " instructions that write to the return register after any "
+ "authentication are:\n";
+ // Sort by address to ensure output is deterministic.
+ std::vector<MCInstReference> ORRI = OverwritingRetRegInst;
+ llvm::sort(ORRI, [](const MCInstReference &A, const MCInstReference &B) {
+ return A.getAddress() < B.getAddress();
+ });
+ for (unsigned I = 0; I < ORRI.size(); ++I) {
+ MCInstReference InstRef = ORRI[I];
+ OS << " " << (I + 1) << ". ";
+ BC.printInstruction(OS, InstRef, InstRef.getAddress(), BF);
+ };
+ LLVM_DEBUG({
+ dbgs() << " .. OverWritingRetRegInst:\n";
+ for (MCInstReference Ref : OverwritingRetRegInst) {
+ dbgs() << " " << Ref << "\n";
+ }
+ });
+ if (OverwritingRetRegInst.size() == 1) {
+ const MCInstReference OverwInst = OverwritingRetRegInst[0];
+ assert(OverwInst.ParentKind == MCInstReference::BasicBlockParent);
+ reportFoundGadgetInSingleBBSingleOverwInst(OS, BC, OverwInst, RetInst);
+ }
+}
+
+void GenDiag::generateReport(raw_ostream &OS, const BinaryContext &BC) const {
+ BinaryFunction *BF = RetInst.getFunction();
+ BinaryBasicBlock *BB = RetInst.getBasicBlock();
+
+ OS << "\nGS-PACRET: " << Diag.Text;
+ OS << " in function " << BF->getPrintName();
+ if (BB)
+ OS << ", basic block " << BB->getName();
+ OS << ", at address " << llvm::format("%x", RetInst.getAddress()) << "\n";
+ OS << " The return instruction is ";
+ BC.printInstruction(OS, RetInst, RetInst.getAddress(), BF);
+}
+
+Error Analysis::runOnFunctions(BinaryContext &BC) {
+ ParallelUtilities::WorkFuncWithAllocTy WorkFun =
+ [&](BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocatorId) {
+ runOnFunction(BF, AllocatorId);
+ };
+
+ ParallelUtilities::PredicateTy SkipFunc = [&](const BinaryFunction &BF) {
+ return false;
+ };
+
+ ParallelUtilities::runOnEachFunctionWithUniqueAllocId(
+ BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFun,
+ SkipFunc, "NonPacProtectedRetAnalysis");
+
+ for (BinaryFunction *BF : BC.getAllBinaryFunctions())
+ if (AnalysisResults.count(BF) > 0) {
+ for (const std::shared_ptr<Annotation> &A :
+ AnalysisResults[BF].Diagnostics)
+ A->generateReport(outs(), BC);
+ }
+ return Error::success();
+}
+
+} // namespace NonPacProtectedRetAnalysis
+} // namespace bolt
+} // namespace llvm
bolt/lib/Profile/DataAggregator.cpp
@@ -711,7 +711,7 @@ bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,
}
bool DataAggregator::doBranch(uint64_t From, uint64_t To, uint64_t Count,
- uint64_t Mispreds, bool IsPreagg) {
+ uint64_t Mispreds) {
// Returns whether \p Offset in \p Func contains a return instruction.
auto checkReturn = [&](const BinaryFunction &Func, const uint64_t Offset) {
auto isReturn = [&](auto MI) { return MI && BC->MIB->isReturn(*MI); };
@@ -772,7 +772,8 @@ bool DataAggregator::doBranch(uint64_t From, uint64_t To, uint64_t Count,
return false;
// Record call to continuation trace.
- if (IsPreagg && FromFunc != ToFunc && (IsReturn || IsCallCont)) {
+ if (NeedsConvertRetProfileToCallCont && FromFunc != ToFunc &&
+ (IsReturn || IsCallCont)) {
LBREntry First{ToOrig - 1, ToOrig - 1, false};
LBREntry Second{ToOrig, ToOrig, false};
return doTrace(First, Second, Count);
@@ -830,9 +831,10 @@ bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second,
ParentFunc = FromFunc;
ParentFunc->SampleCountInBytes += Count * (Second.From - First.To);
+ const uint64_t FuncAddress = FromFunc->getAddress();
std::optional<BoltAddressTranslation::FallthroughListTy> FTs =
- BAT ? BAT->getFallthroughsInTrace(FromFunc->getAddress(), First.To,
- Second.From)
+ BAT && BAT->isBATFunction(FuncAddress)
+ ? BAT->getFallthroughsInTrace(FuncAddress, First.To, Second.From)
: getFallthroughsInTrace(*FromFunc, First, Second, Count);
if (!FTs) {
LLVM_DEBUG(
@@ -869,7 +871,7 @@ DataAggregator::getFallthroughsInTrace(BinaryFunction &BF,
BinaryContext &BC = BF.getBinaryContext();
- if (!BF.isSimple())
+ if (BF.empty())
return std::nullopt;
assert(BF.hasCFG() && "can only record traces in CFG state");
@@ -1216,23 +1218,30 @@ ErrorOr<Location> DataAggregator::parseLocationOrOffset() {
return Location(true, BuildID.get(), Offset.get());
}
-ErrorOr<DataAggregator::AggregatedLBREntry>
-DataAggregator::parseAggregatedLBREntry() {
+std::error_code DataAggregator::parseAggregatedLBREntry() {
while (checkAndConsumeFS()) {
}
ErrorOr<StringRef> TypeOrErr = parseString(FieldSeparator);
if (std::error_code EC = TypeOrErr.getError())
return EC;
+ // Pre-aggregated profile with branches and fallthroughs needs to convert
+ // return profile into call to continuation fall-through.
auto Type = AggregatedLBREntry::BRANCH;
if (TypeOrErr.get() == "B") {
+ NeedsConvertRetProfileToCallCont = true;
Type = AggregatedLBREntry::BRANCH;
} else if (TypeOrErr.get() == "F") {
+ NeedsConvertRetProfileToCallCont = true;
Type = AggregatedLBREntry::FT;
} else if (TypeOrErr.get() == "f") {
+ NeedsConvertRetProfileToCallCont = true;
Type = AggregatedLBREntry::FT_EXTERNAL_ORIGIN;
+ } else if (TypeOrErr.get() == "T") {
+ // Trace is expanded into B and [Ff]
+ Type = AggregatedLBREntry::TRACE;
} else {
- reportError("expected B, F or f");
+ reportError("expected T, B, F or f");
return make_error_code(llvm::errc::io_error);
}
@@ -1248,6 +1257,15 @@ DataAggregator::parseAggregatedLBREntry() {
if (std::error_code EC = To.getError())
return EC;
+ ErrorOr<Location> TraceFtEnd = std::error_code();
+ if (Type == AggregatedLBREntry::TRACE) {
+ while (checkAndConsumeFS()) {
+ }
+ TraceFtEnd = parseLocationOrOffset();
+ if (std::error_code EC = TraceFtEnd.getError())
+ return EC;
+ }
+
while (checkAndConsumeFS()) {
}
ErrorOr<int64_t> Frequency =
@@ -1270,9 +1288,24 @@ DataAggregator::parseAggregatedLBREntry() {
return make_error_code(llvm::errc::io_error);
}
- return AggregatedLBREntry{From.get(), To.get(),
- static_cast<uint64_t>(Frequency.get()), Mispreds,
- Type};
+ BinaryFunction *FromFunc = getBinaryFunctionContainingAddress(From->Offset);
+ BinaryFunction *ToFunc = getBinaryFunctionContainingAddress(To->Offset);
+
+ for (BinaryFunction *BF : {FromFunc, ToFunc})
+ if (BF)
+ BF->setHasProfileAvailable();
+
+ uint64_t Count = static_cast<uint64_t>(Frequency.get());
+ AggregatedLBREntry Entry{From.get(), To.get(), Count, Mispreds, Type};
+ AggregatedLBRs.emplace_back(Entry);
+ if (Type == AggregatedLBREntry::TRACE) {
+ auto FtType = (FromFunc == ToFunc) ? AggregatedLBREntry::FT
+ : AggregatedLBREntry::FT_EXTERNAL_ORIGIN;
+ AggregatedLBREntry TraceFt{To.get(), TraceFtEnd.get(), Count, 0, FtType};
+ AggregatedLBRs.emplace_back(TraceFt);
+ }
+
+ return std::error_code();
}
bool DataAggregator::ignoreKernelInterrupt(LBREntry &LBR) const {
@@ -1585,8 +1618,7 @@ void DataAggregator::processBranchEvents() {
for (const auto &AggrLBR : BranchLBRs) {
const Trace &Loc = AggrLBR.first;
const TakenBranchInfo &Info = AggrLBR.second;
- doBranch(Loc.From, Loc.To, Info.TakenCount, Info.MispredCount,
- /*IsPreagg*/ false);
+ doBranch(Loc.From, Loc.To, Info.TakenCount, Info.MispredCount);
}
}
@@ -1722,18 +1754,10 @@ std::error_code DataAggregator::parsePreAggregatedLBRSamples() {
outs() << "PERF2BOLT: parsing pre-aggregated profile...\n";
NamedRegionTimer T("parseAggregated", "Parsing aggregated branch events",
TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
- while (hasData()) {
- ErrorOr<AggregatedLBREntry> AggrEntry = parseAggregatedLBREntry();
- if (std::error_code EC = AggrEntry.getError())
+ while (hasData())
+ if (std::error_code EC = parseAggregatedLBREntry())
return EC;
- for (const uint64_t Addr : {AggrEntry->From.Offset, AggrEntry->To.Offset})
- if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Addr))
- BF->setHasProfileAvailable();
-
- AggregatedLBRs.emplace_back(std::move(AggrEntry.get()));
- }
-
return std::error_code();
}
@@ -1746,8 +1770,9 @@ void DataAggregator::processPreAggregated() {
for (const AggregatedLBREntry &AggrEntry : AggregatedLBRs) {
switch (AggrEntry.EntryType) {
case AggregatedLBREntry::BRANCH:
+ case AggregatedLBREntry::TRACE:
doBranch(AggrEntry.From.Offset, AggrEntry.To.Offset, AggrEntry.Count,
- AggrEntry.Mispreds, /*IsPreagg*/ true);
+ AggrEntry.Mispreds);
break;
case AggregatedLBREntry::FT:
case AggregatedLBREntry::FT_EXTERNAL_ORIGIN: {
bolt/lib/Rewrite/JITLinkLinker.cpp
@@ -125,11 +125,11 @@ struct JITLinkLinker::Context : jitlink::JITLinkContext {
std::string SymName = (*Symbol.first).str();
LLVM_DEBUG(dbgs() << "BOLT: looking for " << SymName << "\n");
- if (auto Address = Linker.lookupSymbol(SymName)) {
+ if (auto SymInfo = Linker.lookupSymbolInfo(SymName)) {
LLVM_DEBUG(dbgs() << "Resolved to address 0x"
- << Twine::utohexstr(*Address) << "\n");
+ << Twine::utohexstr(SymInfo->Address) << "\n");
AllResults[Symbol.first] = orc::ExecutorSymbolDef(
- orc::ExecutorAddr(*Address), JITSymbolFlags());
+ orc::ExecutorAddr(SymInfo->Address), JITSymbolFlags());
continue;
}
bolt/lib/Rewrite/RewriteInstance.cpp
@@ -20,6 +20,7 @@
#include "bolt/Passes/BinaryPasses.h"
#include "bolt/Passes/CacheMetrics.h"
#include "bolt/Passes/IdenticalCodeFolding.h"
+#include "bolt/Passes/NonPacProtectedRetAnalysis.h"
#include "bolt/Passes/ReorderFunctions.h"
#include "bolt/Profile/BoltAddressTranslation.h"
#include "bolt/Profile/DataAggregator.h"
@@ -108,34 +109,33 @@ cl::opt<std::string>
cl::opt<bool> DumpDotAll(
"dump-dot-all",
- cl::desc("dump function CFGs to graphviz format after each stage; "
+ cl::desc("dump function CFGs to graphviz format after each stage;"
"enable '-print-loops' for color-coded blocks"),
cl::Hidden, cl::cat(BoltCategory));
static cl::list<std::string>
- ForceFunctionNames("funcs", cl::CommaSeparated,
- cl::desc("limit optimizations to functions from the "
- "list; local symbols must be suffixed"),
- cl::value_desc("func1,func2,func3,..."), cl::Hidden,
- cl::cat(BoltCategory));
+ForceFunctionNames("funcs",
+ cl::CommaSeparated,
+ cl::desc("limit optimizations to functions from the list"),
+ cl::value_desc("func1,func2,func3,..."),
+ cl::Hidden,
+ cl::cat(BoltCategory));
static cl::opt<std::string>
- FunctionNamesFile("funcs-file",
- cl::desc("file with list of functions to optimize; local "
- "symbols must be suffixed"),
- cl::Hidden, cl::cat(BoltCategory));
+FunctionNamesFile("funcs-file",
+ cl::desc("file with list of functions to optimize"),
+ cl::Hidden,
+ cl::cat(BoltCategory));
static cl::list<std::string> ForceFunctionNamesNR(
"funcs-no-regex", cl::CommaSeparated,
- cl::desc("limit optimizations to functions from the list (non-regex); "
- "local symbols must be suffixed"),
+ cl::desc("limit optimizations to functions from the list (non-regex)"),
cl::value_desc("func1,func2,func3,..."), cl::Hidden, cl::cat(BoltCategory));
-static cl::opt<std::string>
- FunctionNamesFileNR("funcs-file-no-regex",
- cl::desc("file with list of functions to optimize "
- "(non-regex); local symbols must be suffixed"),
- cl::Hidden, cl::cat(BoltCategory));
+static cl::opt<std::string> FunctionNamesFileNR(
+ "funcs-file-no-regex",
+ cl::desc("file with list of functions to optimize (non-regex)"), cl::Hidden,
+ cl::cat(BoltCategory));
cl::opt<bool>
KeepTmp("keep-tmp",
@@ -246,6 +246,13 @@ static cl::opt<bool> WriteBoltInfoSection(
"bolt-info", cl::desc("write bolt info section in the output binary"),
cl::init(true), cl::Hidden, cl::cat(BoltOutputCategory));
+cl::list<GadgetScannerKind>
+ GadgetScannersToRun("scanners", cl::desc("which gadget scanners to run"),
+ cl::values(clEnumValN(GS_PACRET, "pacret", "pac-ret"),
+ clEnumValN(GS_ALL, "all", "all")),
+ cl::ZeroOrMore, cl::CommaSeparated,
+ cl::cat(BinaryAnalysisCategory));
+
} // namespace opts
// FIXME: implement a better way to mark sections for replacement.
@@ -3431,7 +3438,8 @@ void RewriteInstance::buildFunctionsCFG() {
};
ParallelUtilities::PredicateTy SkipPredicate = [&](const BinaryFunction &BF) {
- return !shouldDisassemble(BF) || !BF.isSimple();
+ // Construct CFG for non-simple functions in aggregation mode.
+ return !(shouldDisassemble(BF) && (BF.isSimple() || opts::AggregateOnly));
};
ParallelUtilities::runOnEachFunctionWithUniqueAllocId(
@@ -3491,7 +3499,24 @@ void RewriteInstance::runOptimizationPasses() {
BC->logBOLTErrorsAndQuitOnFatal(BinaryFunctionPassManager::runAllPasses(*BC));
}
-void RewriteInstance::runBinaryAnalyses() {}
+void RewriteInstance::runBinaryAnalyses() {
+ NamedRegionTimer T("runBinaryAnalyses", "run binary analysis passes",
+ TimerGroupName, TimerGroupDesc, opts::TimeRewrite);
+ BinaryFunctionPassManager Manager(*BC);
+ // FIXME: add a pass that warns about which functions do not have CFG,
+ // and therefore, analysis is most likely to be less accurate.
+ using GSK = opts::GadgetScannerKind;
+ // if no command line option was given, act as if "all" was specified.
+ if (opts::GadgetScannersToRun.empty())
+ opts::GadgetScannersToRun.addValue(GSK::GS_ALL);
+ for (GSK ScannerToRun : opts::GadgetScannersToRun) {
+ if (ScannerToRun == GSK::GS_PACRET || ScannerToRun == GSK::GS_ALL)
+ Manager.registerPass(
+ std::make_unique<NonPacProtectedRetAnalysis::Analysis>());
+ }
+
+ BC->logBOLTErrorsAndQuitOnFatal(Manager.runPasses());
+}
void RewriteInstance::preregisterSections() {
// Preregister sections before emission to set their order in the output.
@@ -5908,9 +5933,9 @@ void RewriteInstance::writeEHFrameHeader() {
}
uint64_t RewriteInstance::getNewValueForSymbol(const StringRef Name) {
- auto Value = Linker->lookupSymbol(Name);
+ auto Value = Linker->lookupSymbolInfo(Name);
if (Value)
- return *Value;
+ return Value->Address;
// Return the original value if we haven't emitted the symbol.
BinaryData *BD = BC->getBinaryDataByName(Name);
bolt/lib/RuntimeLibs/HugifyRuntimeLibrary.cpp
@@ -68,10 +68,11 @@ void HugifyRuntimeLibrary::link(BinaryContext &BC, StringRef ToolPath,
assert(!RuntimeStartAddress &&
"We don't currently support linking multiple runtime libraries");
- RuntimeStartAddress = Linker.lookupSymbol("__bolt_hugify_self").value_or(0);
- if (!RuntimeStartAddress) {
+ auto StartSymInfo = Linker.lookupSymbolInfo("__bolt_hugify_self");
+ if (!StartSymInfo) {
errs() << "BOLT-ERROR: hugify library does not define __bolt_hugify_self: "
<< LibPath << "\n";
exit(1);
}
+ RuntimeStartAddress = StartSymInfo->Address;
}
bolt/lib/RuntimeLibs/InstrumentationRuntimeLibrary.cpp
@@ -203,27 +203,35 @@ void InstrumentationRuntimeLibrary::link(
if (BC.isMachO())
return;
- RuntimeFiniAddress = Linker.lookupSymbol("__bolt_instr_fini").value_or(0);
- if (!RuntimeFiniAddress) {
+ std::optional<BOLTLinker::SymbolInfo> FiniSymInfo =
+ Linker.lookupSymbolInfo("__bolt_instr_fini");
+ if (!FiniSymInfo) {
errs() << "BOLT-ERROR: instrumentation library does not define "
"__bolt_instr_fini: "
<< LibPath << "\n";
exit(1);
}
- RuntimeStartAddress = Linker.lookupSymbol("__bolt_instr_start").value_or(0);
- if (!RuntimeStartAddress) {
+ RuntimeFiniAddress = FiniSymInfo->Address;
+
+ std::optional<BOLTLinker::SymbolInfo> StartSymInfo =
+ Linker.lookupSymbolInfo("__bolt_instr_start");
+ if (!StartSymInfo) {
errs() << "BOLT-ERROR: instrumentation library does not define "
"__bolt_instr_start: "
<< LibPath << "\n";
exit(1);
}
+ RuntimeStartAddress = StartSymInfo->Address;
+
outs() << "BOLT-INFO: output linked against instrumentation runtime "
"library, lib entry point is 0x"
- << Twine::utohexstr(RuntimeFiniAddress) << "\n";
+ << Twine::utohexstr(RuntimeStartAddress) << "\n";
+
+ std::optional<BOLTLinker::SymbolInfo> ClearSymInfo =
+ Linker.lookupSymbolInfo("__bolt_instr_clear_counters");
+ const uint64_t ClearSymAddress = ClearSymInfo ? ClearSymInfo->Address : 0;
outs() << "BOLT-INFO: clear procedure is 0x"
- << Twine::utohexstr(
- Linker.lookupSymbol("__bolt_instr_clear_counters").value_or(0))
- << "\n";
+ << Twine::utohexstr(ClearSymAddress) << "\n";
emitTablesAsELFNote(BC);
}
bolt/lib/RuntimeLibs/RuntimeLibrary.cpp
@@ -18,6 +18,7 @@
#include "llvm/Object/Archive.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
#define DEBUG_TYPE "bolt-rtlib"
@@ -38,6 +39,23 @@ std::string RuntimeLibrary::getLibPathByToolPath(StringRef ToolPath,
llvm::sys::path::append(LibPath, "lib" LLVM_LIBDIR_SUFFIX);
}
llvm::sys::path::append(LibPath, LibFileName);
+ if (!llvm::sys::fs::exists(LibPath)) {
+ // If it is a symlink, check the directory that the symlink points to.
+ if (llvm::sys::fs::is_symlink_file(ToolPath)) {
+ SmallString<256> RealPath;
+ llvm::sys::fs::real_path(ToolPath, RealPath);
+ if (llvm::ErrorOr<std::string> P =
+ llvm::sys::findProgramByName(RealPath)) {
+ outs() << "BOLT-INFO: library not found: " << LibPath << "\n"
+ << "BOLT-INFO: " << ToolPath << " is a symlink; will look up "
+ << LibFileName
+ << " at the target directory that the symlink points to\n";
+ return getLibPath(*P, LibFileName);
+ }
+ }
+ errs() << "BOLT-ERROR: library not found: " << LibPath << "\n";
+ exit(1);
+ }
return std::string(LibPath);
}
bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -23,6 +23,7 @@
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCInstBuilder.h"
#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCRegister.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Debug.h"
@@ -183,6 +184,88 @@ public:
return false;
}
+ ErrorOr<MCPhysReg> getAuthenticatedReg(const MCInst &Inst) const override {
+ switch (Inst.getOpcode()) {
+ case AArch64::AUTIAZ:
+ case AArch64::AUTIBZ:
+ case AArch64::AUTIASP:
+ case AArch64::AUTIBSP:
+ case AArch64::AUTIASPPCi:
+ case AArch64::AUTIBSPPCi:
+ case AArch64::AUTIASPPCr:
+ case AArch64::AUTIBSPPCr:
+ case AArch64::RETAA:
+ case AArch64::RETAB:
+ case AArch64::RETAASPPCi:
+ case AArch64::RETABSPPCi:
+ case AArch64::RETAASPPCr:
+ case AArch64::RETABSPPCr:
+ return AArch64::LR;
+
+ case AArch64::AUTIA1716:
+ case AArch64::AUTIB1716:
+ case AArch64::AUTIA171615:
+ case AArch64::AUTIB171615:
+ return AArch64::X17;
+
+ case AArch64::ERETAA:
+ case AArch64::ERETAB:
+ // The ERETA{A,B} instructions use either register ELR_EL1, ELR_EL2 or
+ // ELR_EL3, depending on the current Exception Level at run-time.
+ //
+ // Furthermore, these registers are not modelled by LLVM as a regular
+ // MCPhysReg.... So there is no way to indicate that through the current
+ // API.
+ //
+ // Therefore, return an error to indicate that LLVM/BOLT cannot model
+ // this.
+ return make_error_code(std::errc::result_out_of_range);
+
+ case AArch64::AUTIA:
+ case AArch64::AUTIB:
+ case AArch64::AUTDA:
+ case AArch64::AUTDB:
+ case AArch64::AUTIZA:
+ case AArch64::AUTIZB:
+ case AArch64::AUTDZA:
+ case AArch64::AUTDZB:
+ return Inst.getOperand(0).getReg();
+
+ // FIXME: BL?RA(A|B)Z? and LDRA(A|B) should probably be handled here too.
+
+ default:
+ return getNoRegister();
+ }
+ }
+
+ bool isAuthenticationOfReg(const MCInst &Inst, MCPhysReg Reg) const override {
+ if (Reg == getNoRegister())
+ return false;
+ ErrorOr<MCPhysReg> AuthenticatedReg = getAuthenticatedReg(Inst);
+ return AuthenticatedReg.getError() ? false : *AuthenticatedReg == Reg;
+ }
+
+ ErrorOr<MCPhysReg> getRegUsedAsRetDest(const MCInst &Inst) const override {
+ assert(isReturn(Inst));
+ switch (Inst.getOpcode()) {
+ case AArch64::RET:
+ return Inst.getOperand(0).getReg();
+ case AArch64::RETAA:
+ case AArch64::RETAB:
+ case AArch64::RETAASPPCi:
+ case AArch64::RETABSPPCi:
+ case AArch64::RETAASPPCr:
+ case AArch64::RETABSPPCr:
+ return AArch64::LR;
+ case AArch64::ERET:
+ case AArch64::ERETAA:
+ case AArch64::ERETAB:
+ return make_error_code(std::errc::result_out_of_range);
+ default:
+ llvm_unreachable("Unhandled return instruction");
+ }
+ }
+
bool isADRP(const MCInst &Inst) const override {
return Inst.getOpcode() == AArch64::ADRP;
}
bolt/test/AArch64/data-in-code.s
@@ -7,7 +7,8 @@
## Check disassembly of BOLT input.
# RUN: llvm-objdump %t.exe -d | FileCheck %s
-# RUN: llvm-bolt %t.exe -o %t.bolt --print-disasm | FileCheck %s
+# RUN: llvm-bolt %t.exe -o %t.bolt --print-disasm \
+# RUN: | FileCheck %s --check-prefixes CHECK,CHECK-BOLT-ONLY
.text
.balign 4
@@ -16,16 +17,21 @@
.type _start, %function
_start:
mov x0, #0x0
+ ldr x1, .L1
.word 0x4f82e010
ret
+.size _start, .-_start
+.L1:
.byte 0x0, 0xff, 0x42
# CHECK-LABEL: _start
# CHECK: mov x0, #0x0
+# CHECK-NEXT: ldr x1
+# CHECK-BOLT-ONLY-SAME: ISLANDat[[ADDR:]]
# CHECK-NEXT: .word 0x4f82e010
# CHECK-NEXT: ret
+# CHECK-BOLT-ONLY-NEXT: ISLANDat[[ADDR]]
# CHECK-NEXT: .short 0xff00
# CHECK-NEXT: .byte 0x42
-.size _start, .-_start
## Force relocation mode.
.reloc 0, R_AARCH64_NONE
bolt/test/X86/bolt-address-translation-yaml.test
@@ -61,6 +61,11 @@ YAML-BAT-CHECK-NEXT: - bid: 0
YAML-BAT-CHECK-NEXT: insns: 26
YAML-BAT-CHECK-NEXT: hash: 0xA900AE79CFD40000
YAML-BAT-CHECK-NEXT: succ: [ { bid: 3, cnt: 0 }, { bid: 1, cnt: 0 } ]
+# Check fallthroughs in non-BAT function
+YAML-BAT-CHECK-NEXT: - bid: 27
+YAML-BAT-CHECK-NEXT: insns: 3
+YAML-BAT-CHECK-NEXT: hash: 0x30A1EBA77A903F0
+YAML-BAT-CHECK-NEXT: succ: [ { bid: 28, cnt: 1 } ]
# Calls from no-BAT to BAT function
YAML-BAT-CHECK: - bid: 28
YAML-BAT-CHECK-NEXT: insns: 13
bolt/test/X86/callcont-fallthru.s
@@ -4,19 +4,21 @@
# RUN: %clang %cflags -fpic -shared -xc /dev/null -o %t.so
## Link against a DSO to ensure PLT entries.
# RUN: %clangxx %cxxflags %s %t.so -o %t -Wl,-q -nostdlib
-# RUN: link_fdata %s %t %t.pa1 PREAGG
+# RUN: link_fdata %s %t %t.pa1 PREAGG1
# RUN: link_fdata %s %t %t.pa2 PREAGG2
# RUN: link_fdata %s %t %t.pa3 PREAGG3
-# RUN: link_fdata %s %t %t.pa4 PREAGG4
+# RUN: link_fdata %s %t %t.pat PREAGGT1
+# RUN: link_fdata %s %t %t.pat2 PREAGGT2
## Check normal case: fallthrough is not LP or secondary entry.
-# RUN: llvm-strip --strip-unneeded %t -o %t.exe
-# RUN: llvm-bolt %t.exe --pa -p %t.pa1 -o %t.out \
+# RUN: llvm-strip --strip-unneeded %t -o %t.strip
+# RUN: llvm-objcopy --remove-section=.eh_frame %t.strip %t.noeh
+# RUN: llvm-bolt %t.strip --pa -p %t.pa1 -o %t.out \
# RUN: --print-cfg --print-only=main | FileCheck %s
## Check that getFallthroughsInTrace correctly handles a trace starting at plt
## call continuation
-# RUN: llvm-bolt %t.exe --pa -p %t.pa2 -o %t.out2 \
+# RUN: llvm-bolt %t.strip --pa -p %t.pa2 -o %t.out2 \
# RUN: --print-cfg --print-only=main | FileCheck %s --check-prefix=CHECK2
## Check that we don't treat secondary entry points as call continuation sites.
@@ -24,8 +26,21 @@
# RUN: --print-cfg --print-only=main | FileCheck %s --check-prefix=CHECK3
## Check fallthrough to a landing pad case.
-# RUN: llvm-bolt %t.exe --pa -p %t.pa4 -o %t.out \
-# RUN: --print-cfg --print-only=main | FileCheck %s --check-prefix=CHECK4
+# RUN: llvm-bolt %t.strip --pa -p %t.pa3 -o %t.out \
+# RUN: --print-cfg --print-only=main | FileCheck %s --check-prefix=CHECK3
+
+## Check pre-aggregated traces attach call continuation fallthrough count
+# RUN: llvm-bolt %t.noeh --pa -p %t.pat -o %t.out \
+# RUN: --print-cfg --print-only=main | FileCheck %s
+
+## Check pre-aggregated traces don't attach call continuation fallthrough count
+## to secondary entry point (unstripped)
+# RUN: llvm-bolt %t --pa -p %t.pat2 -o %t.out \
+# RUN: --print-cfg --print-only=main | FileCheck %s --check-prefix=CHECK3
+## Check pre-aggregated traces don't attach call continuation fallthrough count
+## to landing pad (stripped, LP)
+# RUN: llvm-bolt %t.strip --pa -p %t.pat2 -o %t.out \
+# RUN: --print-cfg --print-only=main | FileCheck %s --check-prefix=CHECK3
.globl foo
.type foo, %function
@@ -51,8 +66,9 @@ main:
movl %edi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
callq puts@PLT
-## Target is a call continuation
-# PREAGG: B X:0 #Ltmp1# 2 0
+## Target is an external-origin call continuation
+# PREAGG1: B X:0 #Ltmp1# 2 0
+# PREAGGT1: T X:0 #Ltmp1# #Ltmp4_br# 2
# CHECK: callq puts@PLT
# CHECK-NEXT: count: 2
@@ -63,14 +79,16 @@ Ltmp1:
Ltmp4:
cmpl $0x0, -0x14(%rbp)
+Ltmp4_br:
je Ltmp0
# CHECK2: je .Ltmp0
# CHECK2-NEXT: count: 3
movl $0xa, -0x18(%rbp)
callq foo
-## Target is a call continuation
-# PREAGG: B #Lfoo_ret# #Ltmp3# 1 0
+## Target is a binary-local call continuation
+# PREAGG1: B #Lfoo_ret# #Ltmp3# 1 0
+# PREAGGT1: T #Lfoo_ret# #Ltmp3# #Ltmp3_br# 1
# CHECK: callq foo
# CHECK-NEXT: count: 1
@@ -79,16 +97,12 @@ Ltmp4:
# CHECK2: callq foo
# CHECK2-NEXT: count: 3
-## Target is a secondary entry point
+## Target is a secondary entry point (unstripped) or a landing pad (stripped)
# PREAGG3: B X:0 #Ltmp3# 2 0
+# PREAGGT2: T X:0 #Ltmp3# #Ltmp3_br# 2
# CHECK3: callq foo
# CHECK3-NEXT: count: 0
-## Target is a landing pad
-# PREAGG4: B X:0 #Ltmp3# 2 0
-# CHECK4: callq puts@PLT
-# CHECK4-NEXT: count: 0
-
Ltmp3:
cmpl $0x0, -0x18(%rbp)
Ltmp3_br:
bolt/test/X86/entry-point-fallthru.s
@@ -0,0 +1,24 @@
+## Checks that fallthroughs spanning entry points are accepted in aggregation
+## mode.
+
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
+# RUN: ld.lld %t.o -o %t
+# RUN: link_fdata %s %t %t.preagg PREAGG
+# RUN: perf2bolt %t -p %t.preagg --pa -o %t.fdata | FileCheck %s
+# CHECK: traces mismatching disassembled function contents: 0
+
+ .globl main
+main:
+ .cfi_startproc
+ vmovaps %zmm31,%zmm3
+
+next:
+ add $0x4,%r9
+ add $0x40,%r10
+ dec %r14
+Ljmp:
+ jne main
+# PREAGG: T #Ljmp# #main# #Ljmp# 1
+ ret
+ .cfi_endproc
+.size main,.-main
bolt/test/X86/skip-inline.s
@@ -0,0 +1,26 @@
+## Check skip-inline flag behavior
+
+# RUN: llvm-mc --filetype=obj --triple=x86_64-unknown-unknown %s -o %t.o
+# RUN: ld.lld %t.o -o %t.exe -q
+# RUN: llvm-bolt %t.exe --inline-small-functions --print-finalized --print-only=main \
+# RUN: -o %t.null | FileCheck %s --check-prefix=CHECK-INLINE
+# RUN: llvm-bolt %t.exe --inline-small-functions --skip-inline=foo --print-finalized \
+# RUN: --print-only=main -o %t.null | FileCheck %s --check-prefix=CHECK-NO-INLINE
+# CHECK-INLINE: Binary Function "main"
+# CHECK-INLINE: ud2
+# CHECK-NO-INLINE: Binary Function "main"
+# CHECK-NO-INLINE: callq foo
+
+.globl _start
+_start:
+ call main
+
+.globl main
+main:
+ call foo
+ ret
+
+.globl foo
+foo:
+ ud2
+ ret
bolt/test/binary-analysis/AArch64/cmdline-args.test
@@ -13,7 +13,7 @@ NONEXISTINGFILEARG: llvm-bolt-binary-analysis: 'non-existing-file': No suc
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: %clang %cflags -Wl,--emit-relocs %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:
@@ -30,4 +30,10 @@ HELP-NEXT: USAGE: llvm-bolt-binary-analysis [options] <executable>
HELP-EMPTY:
HELP-NEXT: OPTIONS:
HELP-EMPTY:
+HELP-NEXT: BinaryAnalysis options:
+HELP-EMPTY:
+HELP-NEXT: --scanners=<value> - which gadget scanners to run
+HELP-NEXT: =pacret - pac-ret
+HELP-NEXT: =all - all
+HELP-EMPTY:
HELP-NEXT: Generic Options:
bolt/test/binary-analysis/AArch64/gs-pacret-autiasp.s
@@ -0,0 +1,878 @@
+// RUN: %clang %cflags -march=armv9.5-a+pauth-lr -mbranch-protection=pac-ret %s %p/../../Inputs/asm_main.c -o %t.exe
+// RUN: llvm-bolt-binary-analysis --scanners=pacret %t.exe 2>&1 | FileCheck %s
+
+ .text
+
+ .globl f1
+ .type f1,@function
+f1:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ // autiasp
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f1, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f1, .-f1
+
+
+ .globl f_intermediate_overwrite1
+ .type f_intermediate_overwrite1,@function
+f_intermediate_overwrite1:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ autiasp
+ ldp x29, x30, [sp], #16
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_intermediate_overwrite1, basic block {{[0-9a-zA-Z.]+}}
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_intermediate_overwrite1, .-f_intermediate_overwrite1
+
+ .globl f_intermediate_overwrite2
+ .type f_intermediate_overwrite2,@function
+f_intermediate_overwrite2:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autiasp
+ mov x30, x0
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_intermediate_overwrite2, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov x30, x0
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: {{[0-9a-f]+}}: mov x30, x0
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_intermediate_overwrite2, .-f_intermediate_overwrite2
+
+ .globl f_intermediate_read
+ .type f_intermediate_read,@function
+f_intermediate_read:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autiasp
+ mov x0, x30
+// CHECK-NOT: function f_intermediate_read
+ ret
+ .size f_intermediate_read, .-f_intermediate_read
+
+ .globl f_intermediate_overwrite3
+ .type f_intermediate_overwrite3,@function
+f_intermediate_overwrite3:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autiasp
+ mov w30, w0
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_intermediate_overwrite3, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov w30, w0
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: {{[0-9a-f]+}}: mov w30, w0
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_intermediate_overwrite3, .-f_intermediate_overwrite3
+
+ .globl f_nonx30_ret
+ .type f_nonx30_ret,@function
+f_nonx30_ret:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ mov x16, x30
+ autiasp
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_nonx30_ret, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret x16
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov x16, x30
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: mov x16, x30
+// CHECK-NEXT: {{[0-9a-f]+}}: autiasp
+// CHECK-NEXT: {{[0-9a-f]+}}: ret x16
+ ret x16
+ .size f_nonx30_ret, .-f_nonx30_ret
+
+ .globl f_nonx30_ret_ok
+ .type f_nonx30_ret_ok,@function
+f_nonx30_ret_ok:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ // FIXME: Should the scanner understand that an authenticated register (below x30,
+ // after the autiasp instruction), is OK to be moved to another register
+ // and then that register being used to return?
+ // This respects that pac-ret hardening intent, but the scanner currently
+ // will produce a false positive for this.
+ // Is it worthwhile to make the scanner more complex for this case?
+ // So far, scanning many millions of instructions across a linux distro,
+ // I haven't encountered such an example.
+ // The ".if 0" block below tests this case and currently fails.
+.if 0
+ autiasp
+ mov x16, x30
+.else
+ mov x16, x30
+ autia x16, sp
+.endif
+// CHECK-NOT: function f_nonx30_ret_ok
+ ret x16
+ .size f_nonx30_ret_ok, .-f_nonx30_ret_ok
+
+ .globl f_detect_clobbered_x30_passed_to_other
+ .type f_detect_clobbered_x30_passed_to_other,@function
+f_detect_clobbered_x30_passed_to_other:
+ str x30, [sp]
+ ldr x30, [sp]
+// FIXME: Ideally, the pac-ret scanner would report on the following instruction, which
+// performs a tail call, that x30 might be attacker-controlled.
+// CHECK-NOT: function f_detect_clobbered_x30_passed_to_other
+ b f_tail_called
+ .size f_detect_clobbered_x30_passed_to_other, .-f_detect_clobbered_x30_passed_to_other
+
+ .globl f_tail_called
+ .type f_tail_called,@function
+f_tail_called:
+ ret
+ .size f_tail_called, .-f_tail_called
+
+ .globl f_nonx30_ret_non_auted
+ .type f_nonx30_ret_non_auted,@function
+f_nonx30_ret_non_auted:
+// FIXME: x1 is not authenticated, so should this be reported?
+// Note that we assume it's fine for x30 to not be authenticated before
+// returning to, as assuming that x30 is not attacker controlled at function
+// entry is part (implicitly) of the pac-ret hardening scheme.
+// It's probably an open question whether for other hardening schemes, such as
+// PAuthABI, which registers should be considered "clean" or not at function entry.
+// In other words, which registers have to be authenticated before being used as
+// a pointer and which ones not?
+// For a more detailed discussion, see
+// https://github.com/llvm/llvm-project/pull/122304#discussion_r1923662744
+// CHECK-NOT: f_nonx30_ret_non_auted
+ ret x1
+ .size f_nonx30_ret_non_auted, .-f_nonx30_ret_non_auted
+
+
+ .globl f_callclobbered_x30
+ .type f_callclobbered_x30,@function
+f_callclobbered_x30:
+ bl g
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_callclobbered_x30, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl
+ ret
+ .size f_callclobbered_x30, .-f_callclobbered_x30
+
+ .globl f_callclobbered_calleesaved
+ .type f_callclobbered_calleesaved,@function
+f_callclobbered_calleesaved:
+ bl g
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_callclobbered_calleesaved, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret x19
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl
+ // x19, according to the Arm ABI (AAPCS) is a callee-saved register.
+ // Therefore, if function g respects the AAPCS, it should not write
+ // anything to x19. However, we can't know whether function g actually
+ // does respect the AAPCS rules, so the scanner should assume x19 can
+ // get overwritten, and report a gadget if the code does not properly
+ // deal with that.
+ // Furthermore, there's a good chance that callee-saved registers have
+ // been saved on the stack at some point during execution of the callee,
+ // and so should be considered as potentially modified by an
+ // attacker/written to.
+ ret x19
+ .size f_callclobbered_calleesaved, .-f_callclobbered_calleesaved
+
+
+/// Now do a basic sanity check on every different Authentication instruction:
+
+ .globl f_autiasp
+ .type f_autiasp,@function
+f_autiasp:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autiasp
+// CHECK-NOT: function f_autiasp
+ ret
+ .size f_autiasp, .-f_autiasp
+
+ .globl f_autibsp
+ .type f_autibsp,@function
+f_autibsp:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autibsp
+// CHECK-NOT: function f_autibsp
+ ret
+ .size f_autibsp, .-f_autibsp
+
+ .globl f_autiaz
+ .type f_autiaz,@function
+f_autiaz:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autiaz
+// CHECK-NOT: function f_autiaz
+ ret
+ .size f_autiaz, .-f_autiaz
+
+ .globl f_autibz
+ .type f_autibz,@function
+f_autibz:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autibz
+// CHECK-NOT: function f_autibz
+ ret
+ .size f_autibz, .-f_autibz
+
+ .globl f_autia1716
+ .type f_autia1716,@function
+f_autia1716:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autia1716
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_autia1716, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autia1716
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_autia1716, .-f_autia1716
+
+ .globl f_autib1716
+ .type f_autib1716,@function
+f_autib1716:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autib1716
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_autib1716, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autib1716
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_autib1716, .-f_autib1716
+
+ .globl f_autiax12
+ .type f_autiax12,@function
+f_autiax12:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autia x12, sp
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_autiax12, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autia x12, sp
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_autiax12, .-f_autiax12
+
+ .globl f_autibx12
+ .type f_autibx12,@function
+f_autibx12:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autib x12, sp
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_autibx12, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autib x12, sp
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_autibx12, .-f_autibx12
+
+ .globl f_autiax30
+ .type f_autiax30,@function
+f_autiax30:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autia x30, sp
+// CHECK-NOT: function f_autiax30
+ ret
+ .size f_autiax30, .-f_autiax30
+
+ .globl f_autibx30
+ .type f_autibx30,@function
+f_autibx30:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autib x30, sp
+// CHECK-NOT: function f_autibx30
+ ret
+ .size f_autibx30, .-f_autibx30
+
+
+ .globl f_autdax12
+ .type f_autdax12,@function
+f_autdax12:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autda x12, sp
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_autdax12, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autda x12, sp
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_autdax12, .-f_autdax12
+
+ .globl f_autdbx12
+ .type f_autdbx12,@function
+f_autdbx12:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autdb x12, sp
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_autdbx12, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autdb x12, sp
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_autdbx12, .-f_autdbx12
+
+ .globl f_autdax30
+ .type f_autdax30,@function
+f_autdax30:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autda x30, sp
+// CHECK-NOT: function f_autdax30
+ ret
+ .size f_autdax30, .-f_autdax30
+
+ .globl f_autdbx30
+ .type f_autdbx30,@function
+f_autdbx30:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autdb x30, sp
+// CHECK-NOT: function f_autdbx30
+ ret
+ .size f_autdbx30, .-f_autdbx30
+
+
+ .globl f_autizax12
+ .type f_autizax12,@function
+f_autizax12:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autiza x12
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_autizax12, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autiza x12
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_autizax12, .-f_autizax12
+
+ .globl f_autizbx12
+ .type f_autizbx12,@function
+f_autizbx12:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autizb x12
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_autizbx12, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autizb x12
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_autizbx12, .-f_autizbx12
+
+ .globl f_autizax30
+ .type f_autizax30,@function
+f_autizax30:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autiza x30
+// CHECK-NOT: function f_autizax30
+ ret
+ .size f_autizax30, .-f_autizax30
+
+ .globl f_autizbx30
+ .type f_autizbx30,@function
+f_autizbx30:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autizb x30
+// CHECK-NOT: function f_autizbx30
+ ret
+ .size f_autizbx30, .-f_autizbx30
+
+
+ .globl f_autdzax12
+ .type f_autdzax12,@function
+f_autdzax12:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autdza x12
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_autdzax12, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autdza x12
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_autdzax12, .-f_autdzax12
+
+ .globl f_autdzbx12
+ .type f_autdzbx12,@function
+f_autdzbx12:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autdzb x12
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_autdzbx12, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autdzb x12
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_autdzbx12, .-f_autdzbx12
+
+ .globl f_autdzax30
+ .type f_autdzax30,@function
+f_autdzax30:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autdza x30
+// CHECK-NOT: function f_autdzax30
+ ret
+ .size f_autdzax30, .-f_autdzax30
+
+ .globl f_autdzbx30
+ .type f_autdzbx30,@function
+f_autdzbx30:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autdzb x30
+// CHECK-NOT: function f_autdzbx30
+ ret
+ .size f_autdzbx30, .-f_autdzbx30
+
+ .globl f_retaa
+ .type f_retaa,@function
+f_retaa:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+// CHECK-NOT: function f_retaa
+ retaa
+ .size f_retaa, .-f_retaa
+
+ .globl f_retab
+ .type f_retab,@function
+f_retab:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+// CHECK-NOT: function f_retab
+ retab
+ .size f_retab, .-f_retab
+
+ .globl f_eretaa
+ .type f_eretaa,@function
+f_eretaa:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+// CHECK-LABEL: GS-PACRET: Warning: pac-ret analysis could not analyze this return instruction in function f_eretaa, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: eretaa
+ eretaa
+ .size f_eretaa, .-f_eretaa
+
+ .globl f_eretab
+ .type f_eretab,@function
+f_eretab:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+// CHECK-LABEL: GS-PACRET: Warning: pac-ret analysis could not analyze this return instruction in function f_eretab, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: eretab
+ eretab
+ .size f_eretab, .-f_eretab
+
+ .globl f_eret
+ .type f_eret,@function
+f_eret:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+// CHECK-LABEL: GS-PACRET: Warning: pac-ret analysis could not analyze this return instruction in function f_eret, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: eret
+ eret
+ .size f_eret, .-f_eret
+
+ .globl f_movx30reg
+ .type f_movx30reg,@function
+f_movx30reg:
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_movx30reg, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: mov x30, x22
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: mov x30, x22
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ mov x30, x22
+ ret
+ .size f_movx30reg, .-f_movx30reg
+
+ .globl f_autiasppci
+ .type f_autiasppci,@function
+f_autiasppci:
+0:
+ pacnbiasppc
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autiasppc 0b
+// CHECK-NOT: function f_autiasppci
+ ret
+ .size f_autiasppci, .-f_autiasppci
+
+ .globl f_autibsppci
+ .type f_autibsppci,@function
+f_autibsppci:
+0:
+ pacnbibsppc
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autibsppc 0b
+// CHECK-NOT: function f_autibsppci
+ ret
+ .size f_autibsppci, .-f_autibsppci
+
+ .globl f_autiasppcr
+ .type f_autiasppcr,@function
+
+f_autiasppcr:
+0:
+ pacnbiasppc
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ adr x28, 0b
+ autiasppcr x28
+// CHECK-NOT: function f_autiasppcr
+ ret
+ .size f_autiasppcr, .-f_autiasppcr
+
+f_autibsppcr:
+0:
+ pacnbibsppc
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ adr x28, 0b
+ autibsppcr x28
+// CHECK-NOT: function f_autibsppcr
+ ret
+ .size f_autibsppcr, .-f_autibsppcr
+
+ .globl f_retaasppci
+ .type f_retaasppci,@function
+f_retaasppci:
+0:
+ pacnbiasppc
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+// CHECK-NOT: function f_retaasppci
+ retaasppc 0b
+ .size f_retaasppci, .-f_retaasppci
+
+ .globl f_retabsppci
+ .type f_retabsppci,@function
+f_retabsppci:
+0:
+ pacnbibsppc
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+// CHECK-NOT: function f_retabsppci
+ retabsppc 0b
+ .size f_retabsppci, .-f_retabsppci
+
+ .globl f_retaasppcr
+ .type f_retaasppcr,@function
+
+f_retaasppcr:
+0:
+ pacnbiasppc
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ adr x28, 0b
+// CHECK-NOT: function f_retaasppcr
+ retaasppcr x28
+ .size f_retaasppcr, .-f_retaasppcr
+
+f_retabsppcr:
+0:
+ pacnbibsppc
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ adr x28, 0b
+// CHECK-NOT: function f_retabsppcr
+ retabsppcr x28
+ .size f_retabsppcr, .-f_retabsppcr
+
+ .globl f_autia171615
+ .type f_autia171615,@function
+f_autia171615:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autia171615
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_autia171615, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autia171615
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_autia171615, .-f_autia171615
+
+ .globl f_autib171615
+ .type f_autib171615,@function
+f_autib171615:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl g
+ add x0, x0, #3
+ ldp x29, x30, [sp], #16
+ autib171615
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_autib171615, basic block {{[0-9a-zA-Z.]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x3
+// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: {{[0-9a-f]+}}: autib171615
+// CHECK-NEXT: {{[0-9a-f]+}}: ret
+ ret
+ .size f_autib171615, .-f_autib171615
+
bolt/test/binary-analysis/AArch64/gs-pacret-multi-bb.s
@@ -0,0 +1,75 @@
+// RUN: %clang %cflags -march=armv8.3-a -mbranch-protection=pac-ret %s %p/../../Inputs/asm_main.c -o %t.exe
+// RUN: llvm-bolt-binary-analysis --scanners=pacret %t.exe 2>&1 | FileCheck %s
+
+
+// Verify that we can also detect gadgets across basic blocks
+
+ .globl f_crossbb1
+ .type f_crossbb1,@function
+f_crossbb1:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ ldp x29, x30, [sp], #16
+ cbnz x0, 1f
+ autiasp
+1:
+ ret
+ .size f_crossbb1, .-f_crossbb1
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_crossbb1, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 2 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+// CHECK-NEXT: 2. {{[0-9a-f]+}}: autiasp
+
+// A test that checks that the dataflow state tracking across when merging BBs
+// seems to work:
+ .globl f_mergebb1
+ .type f_mergebb1,@function
+f_mergebb1:
+ paciasp
+2:
+ stp x29, x30, [sp, #-16]!
+ ldp x29, x30, [sp], #16
+ sub x0, x0, #1
+ cbnz x0, 1f
+ autiasp
+ b 2b
+1:
+ ret
+ .size f_mergebb1, .-f_mergebb1
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_mergebb1, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: ret
+// CHECK-NEXT: The 1 instructions that write to the return register after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
+
+ .globl f_shrinkwrapping
+ .type f_shrinkwrapping,@function
+f_shrinkwrapping:
+ cbz x0, 1f
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ ldp x29, x30, [sp], #16
+ autiasp
+1:
+ ret
+ .size f_shrinkwrapping, .-f_shrinkwrapping
+// CHECK-NOT: f_shrinkwrapping
+
+ .globl f_multi_auth_insts
+ .type f_multi_auth_insts,@function
+f_multi_auth_insts:
+ paciasp
+ stp x29, x30, [sp, #-16]!
+ ldp x29, x30, [sp], #16
+ cbnz x0, 1f
+ autibsp
+ b 2f
+1:
+ autiasp
+2:
+ ret
+ .size f_multi_auth_insts, .-f_multi_auth_insts
+// CHECK-NOT: f_multi_auth_insts
+
+// TODO: also verify that false negatives exist in across-BB gadgets in functions
+// for which bolt cannot reconstruct the call graph.
bolt/test/binary-analysis/AArch64/lit.local.cfg
@@ -1,7 +1,7 @@
if "AArch64" not in config.root.targets:
config.unsupported = True
-flags = "--target=aarch64-linux-gnu -nostartfiles -nostdlib -ffreestanding -Wl,--emit-relocs"
+flags = "--target=aarch64-linux-gnu -nostartfiles -nostdlib -ffreestanding"
config.substitutions.insert(0, ("%cflags", f"%cflags {flags}"))
config.substitutions.insert(0, ("%cxxflags", f"%cxxflags {flags}"))
bolt/test/link_fdata.py
@@ -34,9 +34,9 @@ prefix_pat = re.compile(f"^# {args.prefix}: (.*)")
fdata_pat = re.compile(r"([01].*) (?P<exec>\d+) (?P<mispred>\d+)")
# Pre-aggregated profile:
-# {B|F|f} [<start_id>:]<start_offset> [<end_id>:]<end_offset> <count>
-# [<mispred_count>]
-preagg_pat = re.compile(r"(?P<type>[BFf]) (?P<offsets_count>.*)")
+# {T|B|F|f} [<start_id>:]<start_offset> [<end_id>:]<end_offset> [<ft_end>]
+# <count> [<mispred_count>]
+preagg_pat = re.compile(r"(?P<type>[TBFf]) (?P<offsets_count>.*)")
# No-LBR profile:
# <is symbol?> <closest elf symbol or DSO name> <relative address> <count>
bolt/tools/binary-analysis/CMakeLists.txt
@@ -1,5 +1,5 @@
set(LLVM_LINK_COMPONENTS
- ${LLVM_TARGETS_TO_BUILD}
+ ${BOLT_TARGETS_TO_BUILD}
MC
Object
Support
bolt/tools/binary-analysis/binary-analysis.cpp
@@ -88,13 +88,15 @@ int main(int argc, char **argv) {
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();
+#define BOLT_TARGET(target) \
+ LLVMInitialize##target##TargetInfo(); \
+ LLVMInitialize##target##TargetMC(); \
+ LLVMInitialize##target##AsmParser(); \
+ LLVMInitialize##target##Disassembler(); \
+ LLVMInitialize##target##Target(); \
+ LLVMInitialize##target##AsmPrinter();
+
+#include "bolt/Core/TargetConfig.def"
ParseCommandLine(argc, argv);
bolt/tools/driver/CMakeLists.txt
@@ -1,5 +1,5 @@
set(LLVM_LINK_COMPONENTS
- ${LLVM_TARGETS_TO_BUILD}
+ ${BOLT_TARGETS_TO_BUILD}
MC
Object
Support
bolt/tools/driver/llvm-bolt.cpp
@@ -173,16 +173,6 @@ void boltMode(int argc, char **argv) {
}
}
-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<std::string> P =
- llvm::sys::findProgramByName(ExecutablePath))
- ExecutablePath = *P;
- return std::string(ExecutablePath);
-}
-
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
sys::PrintStackTraceOnErrorSignal(argv[0]);
@@ -190,16 +180,18 @@ int main(int argc, char **argv) {
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
- std::string ToolPath = GetExecutablePath(argv[0]);
+ std::string ToolPath = llvm::sys::fs::getMainExecutable(argv[0], nullptr);
// Initialize targets and assembly printers/parsers.
- llvm::InitializeAllTargetInfos();
- llvm::InitializeAllTargetMCs();
- llvm::InitializeAllAsmParsers();
- llvm::InitializeAllDisassemblers();
-
- llvm::InitializeAllTargets();
- llvm::InitializeAllAsmPrinters();
+#define BOLT_TARGET(target) \
+ LLVMInitialize##target##TargetInfo(); \
+ LLVMInitialize##target##TargetMC(); \
+ LLVMInitialize##target##AsmParser(); \
+ LLVMInitialize##target##Disassembler(); \
+ LLVMInitialize##target##Target(); \
+ LLVMInitialize##target##AsmPrinter();
+
+#include "bolt/Core/TargetConfig.def"
ToolName = argv[0];
bolt/tools/heatmap/CMakeLists.txt
@@ -1,5 +1,5 @@
set(LLVM_LINK_COMPONENTS
- ${LLVM_TARGETS_TO_BUILD}
+ ${BOLT_TARGETS_TO_BUILD}
MC
Object
Support
bolt/tools/heatmap/heatmap.cpp
@@ -76,13 +76,15 @@ int main(int argc, char **argv) {
opts::OutputFilename = "-";
// Initialize targets and assembly printers/parsers.
- llvm::InitializeAllTargetInfos();
- llvm::InitializeAllTargetMCs();
- llvm::InitializeAllAsmParsers();
- llvm::InitializeAllDisassemblers();
-
- llvm::InitializeAllTargets();
- llvm::InitializeAllAsmPrinters();
+#define BOLT_TARGET(target) \
+ LLVMInitialize##target##TargetInfo(); \
+ LLVMInitialize##target##TargetMC(); \
+ LLVMInitialize##target##AsmParser(); \
+ LLVMInitialize##target##Disassembler(); \
+ LLVMInitialize##target##Target(); \
+ LLVMInitialize##target##AsmPrinter();
+
+#include "bolt/Core/TargetConfig.def"
ToolName = argv[0];
std::string ToolPath = GetExecutablePath(argv[0]);
bolt/tools/llvm-bolt-fuzzer/CMakeLists.txt
@@ -1,5 +1,5 @@
set(LLVM_LINK_COMPONENTS
- ${LLVM_TARGETS_TO_BUILD}
+ ${BOLT_TARGETS_TO_BUILD}
)
add_llvm_fuzzer(llvm-bolt-fuzzer
bolt/tools/llvm-bolt-fuzzer/llvm-bolt-fuzzer.cpp
@@ -58,13 +58,16 @@ extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) {
extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize(int *argc,
char ***argv) {
- llvm::InitializeAllTargetInfos();
- llvm::InitializeAllTargetMCs();
- llvm::InitializeAllAsmParsers();
- llvm::InitializeAllDisassemblers();
+ // Initialize targets and assembly printers/parsers.
+#define BOLT_TARGET(target) \
+ LLVMInitialize##target##TargetInfo(); \
+ LLVMInitialize##target##TargetMC(); \
+ LLVMInitialize##target##AsmParser(); \
+ LLVMInitialize##target##Disassembler(); \
+ LLVMInitialize##target##Target(); \
+ LLVMInitialize##target##AsmPrinter();
- llvm::InitializeAllTargets();
- llvm::InitializeAllAsmPrinters();
+#include "bolt/Core/TargetConfig.def"
return 0;
}
bolt/unittests/Core/BinaryContext.cpp
@@ -27,12 +27,15 @@ struct BinaryContextTester : public testing::TestWithParam<Triple::ArchType> {
protected:
void initalizeLLVM() {
- llvm::InitializeAllTargetInfos();
- llvm::InitializeAllTargetMCs();
- llvm::InitializeAllAsmParsers();
- llvm::InitializeAllDisassemblers();
- llvm::InitializeAllTargets();
- llvm::InitializeAllAsmPrinters();
+#define BOLT_TARGET(target) \
+ LLVMInitialize##target##TargetInfo(); \
+ LLVMInitialize##target##TargetMC(); \
+ LLVMInitialize##target##AsmParser(); \
+ LLVMInitialize##target##Disassembler(); \
+ LLVMInitialize##target##Target(); \
+ LLVMInitialize##target##AsmPrinter();
+
+#include "bolt/Core/TargetConfig.def"
}
void prepareElf() {
@@ -93,12 +96,13 @@ TEST_P(BinaryContextTester, FlushPendingRelocCALL26) {
DataSize, 4);
MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(4, "Func1");
ASSERT_TRUE(RelSymbol1);
- BS.addRelocation(8, RelSymbol1, ELF::R_AARCH64_CALL26, 0, 0, true);
+ BS.addPendingRelocation(
+ Relocation{8, RelSymbol1, ELF::R_AARCH64_CALL26, 0, 0});
MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(16, "Func2");
ASSERT_TRUE(RelSymbol2);
- BS.addRelocation(12, RelSymbol2, ELF::R_AARCH64_CALL26, 0, 0, true);
+ BS.addPendingRelocation(
+ Relocation{12, RelSymbol2, ELF::R_AARCH64_CALL26, 0, 0});
- std::error_code EC;
SmallVector<char> Vect(DataSize);
raw_svector_ostream OS(Vect);
@@ -134,12 +138,13 @@ TEST_P(BinaryContextTester, FlushPendingRelocJUMP26) {
(uint8_t *)Data, Size, 4);
MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(4, "Func1");
ASSERT_TRUE(RelSymbol1);
- BS.addRelocation(8, RelSymbol1, ELF::R_AARCH64_JUMP26, 0, 0, true);
+ BS.addPendingRelocation(
+ Relocation{8, RelSymbol1, ELF::R_AARCH64_JUMP26, 0, 0});
MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(16, "Func2");
ASSERT_TRUE(RelSymbol2);
- BS.addRelocation(12, RelSymbol2, ELF::R_AARCH64_JUMP26, 0, 0, true);
+ BS.addPendingRelocation(
+ Relocation{12, RelSymbol2, ELF::R_AARCH64_JUMP26, 0, 0});
- std::error_code EC;
SmallVector<char> Vect(Size);
raw_svector_ostream OS(Vect);
bolt/unittests/Core/CMakeLists.txt
@@ -2,7 +2,7 @@ set(LLVM_LINK_COMPONENTS
DebugInfoDWARF
Object
MC
- ${LLVM_TARGETS_TO_BUILD}
+ ${BOLT_TARGETS_TO_BUILD}
)
add_bolt_unittest(CoreTests
bolt/unittests/Core/MCPlusBuilder.cpp
@@ -37,12 +37,15 @@ struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> {
protected:
void initalizeLLVM() {
- llvm::InitializeAllTargetInfos();
- llvm::InitializeAllTargetMCs();
- llvm::InitializeAllAsmParsers();
- llvm::InitializeAllDisassemblers();
- llvm::InitializeAllTargets();
- llvm::InitializeAllAsmPrinters();
+#define BOLT_TARGET(target) \
+ LLVMInitialize##target##TargetInfo(); \
+ LLVMInitialize##target##TargetMC(); \
+ LLVMInitialize##target##AsmParser(); \
+ LLVMInitialize##target##Disassembler(); \
+ LLVMInitialize##target##Target(); \
+ LLVMInitialize##target##AsmPrinter();
+
+#include "bolt/Core/TargetConfig.def"
}
void prepareElf() {
bolt/unittests/Core/MemoryMaps.cpp
@@ -38,12 +38,15 @@ struct MemoryMapsTester : public testing::TestWithParam<Triple::ArchType> {
protected:
void initalizeLLVM() {
- llvm::InitializeAllTargetInfos();
- llvm::InitializeAllTargetMCs();
- llvm::InitializeAllAsmParsers();
- llvm::InitializeAllDisassemblers();
- llvm::InitializeAllTargets();
- llvm::InitializeAllAsmPrinters();
+#define BOLT_TARGET(target) \
+ LLVMInitialize##target##TargetInfo(); \
+ LLVMInitialize##target##TargetMC(); \
+ LLVMInitialize##target##AsmParser(); \
+ LLVMInitialize##target##Disassembler(); \
+ LLVMInitialize##target##Target(); \
+ LLVMInitialize##target##AsmPrinter();
+
+#include "bolt/Core/TargetConfig.def"
}
void prepareElf() {
bolt/utils/bughunter.sh
@@ -144,25 +144,7 @@ fi
# Collect function names
FF=$(mktemp -t -u --suffix=.txt func-names.XXX)
-nm --defined-only -p $INPUT_BINARY | grep " [TtWw] " | cut -d ' ' -f 2-3 | awk '
- {
- if ($1 ~ /[tw]/) {
- local[$2] += 1
- } else {
- globl[$2] = 1
- }
- } END {
- for (sym in local) {
- if (!sym)
- continue
- for (i = 1; i <= local[sym]; i++) {
- print sym "/" i
- }
- }
- for (sym in globl) {
- print sym
- }
- }' | sort -u > $FF
+nm --defined-only -p $INPUT_BINARY | grep " [TtWw] " | cut -d ' ' -f 3 | egrep -v "\._" | egrep -v '^$' | sort -u > $FF
# Use function names or numbers
if [[ -z "$MAX_FUNCS" ]] ; then
clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -494,18 +494,31 @@ genReferencesBlock(const std::vector<Reference> &References,
static std::unique_ptr<TagNode>
writeFileDefinition(const Location &L,
std::optional<StringRef> RepositoryUrl = std::nullopt) {
- if (!L.IsFileInRootDir || !RepositoryUrl)
+ if (!L.IsFileInRootDir && !RepositoryUrl)
return std::make_unique<TagNode>(
HTMLTag::TAG_P, "Defined at line " + std::to_string(L.LineNumber) +
" of file " + L.Filename);
SmallString<128> FileURL(*RepositoryUrl);
- llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
+ llvm::sys::path::append(
+ FileURL, llvm::sys::path::Style::posix,
+ // If we're on Windows, the file name will be in the wrong format, and
+ // append won't convert the full path being appended to the correct
+ // format, so we need to do that here.
+ llvm::sys::path::convert_to_slash(
+ L.Filename,
+ // The style here is the current style of the path, not the one we're
+ // targeting. If the string is already in the posix style, it will do
+ // nothing.
+ llvm::sys::path::Style::windows));
auto Node = std::make_unique<TagNode>(HTMLTag::TAG_P);
Node->Children.emplace_back(std::make_unique<TextNode>("Defined at line "));
auto LocNumberNode =
std::make_unique<TagNode>(HTMLTag::TAG_A, std::to_string(L.LineNumber));
// The links to a specific line in the source code use the github /
// googlesource notation so it won't work for all hosting pages.
+ // FIXME: we probably should have a configuration setting for line number
+ // rendering in the HTML. For example, GitHub uses #L22, while googlesource
+ // uses #22 for line numbers.
LocNumberNode->Attributes.emplace_back(
"href", (FileURL + "#" + std::to_string(L.LineNumber)).str());
Node->Children.emplace_back(std::move(LocNumberNode));
clang-tools-extra/clang-reorder-fields/ReorderFieldsAction.cpp
@@ -70,11 +70,12 @@ getNewFieldsOrder(const RecordDecl *Definition,
}
SmallVector<unsigned, 4> NewFieldsOrder;
for (const auto &Name : DesiredFieldsOrder) {
- if (!NameToIndex.count(Name)) {
+ auto It = NameToIndex.find(Name);
+ if (It == NameToIndex.end()) {
llvm::errs() << "Field " << Name << " not found in definition.\n";
return {};
}
- NewFieldsOrder.push_back(NameToIndex[Name]);
+ NewFieldsOrder.push_back(It->second);
}
assert(NewFieldsOrder.size() == NameToIndex.size());
return NewFieldsOrder;
clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -462,7 +462,7 @@ ClangTidyASTConsumerFactory::createASTConsumer(
std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
ento::CreateAnalysisConsumer(Compiler);
AnalysisConsumer->AddDiagnosticConsumer(
- new AnalyzerDiagnosticConsumer(Context));
+ std::make_unique<AnalyzerDiagnosticConsumer>(Context));
Consumers.push_back(std::move(AnalysisConsumer));
}
#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -117,6 +117,7 @@ clang_target_link_libraries(clangTidyBugproneModule
clangASTMatchers
clangBasic
clangLex
+ clangSema
clangTooling
clangTransformer
)
clang-tools-extra/clang-tidy/bugprone/StandaloneEmptyCheck.cpp
@@ -20,6 +20,7 @@
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
+#include "clang/Sema/HeuristicResolver.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
@@ -125,8 +126,8 @@ void StandaloneEmptyCheck::check(const MatchFinder::MatchResult &Result) {
DeclarationName Name =
Context.DeclarationNames.getIdentifier(&Context.Idents.get("clear"));
- auto Candidates = MemberCall->getRecordDecl()->lookupDependentName(
- Name, [](const NamedDecl *ND) {
+ auto Candidates = HeuristicResolver(Context).lookupDependentName(
+ MemberCall->getRecordDecl(), Name, [](const NamedDecl *ND) {
return isa<CXXMethodDecl>(ND) &&
llvm::cast<CXXMethodDecl>(ND)->getMinRequiredArguments() ==
0 &&
@@ -174,8 +175,8 @@ void StandaloneEmptyCheck::check(const MatchFinder::MatchResult &Result) {
DeclarationName Name =
Context.DeclarationNames.getIdentifier(&Context.Idents.get("clear"));
- auto Candidates =
- ArgRecordDecl->lookupDependentName(Name, [](const NamedDecl *ND) {
+ auto Candidates = HeuristicResolver(Context).lookupDependentName(
+ ArgRecordDecl, Name, [](const NamedDecl *ND) {
return isa<CXXMethodDecl>(ND) &&
llvm::cast<CXXMethodDecl>(ND)->getMinRequiredArguments() ==
0 &&
clang-tools-extra/clang-tidy/bugprone/StringConstructorCheck.cpp
@@ -82,7 +82,7 @@ void StringConstructorCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
cxxConstructExpr(
hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
- hasArgument(0, hasType(qualType(isInteger()))),
+ argumentCountIs(2), hasArgument(0, hasType(qualType(isInteger()))),
hasArgument(1, hasType(qualType(isInteger()))),
anyOf(
// Detect the expression: string('x', 40);
@@ -102,7 +102,7 @@ void StringConstructorCheck::registerMatchers(MatchFinder *Finder) {
cxxConstructExpr(
hasDeclaration(cxxConstructorDecl(ofClass(
cxxRecordDecl(hasAnyName(removeNamespaces(StringNames)))))),
- hasArgument(0, hasType(CharPtrType)),
+ argumentCountIs(2), hasArgument(0, hasType(CharPtrType)),
hasArgument(1, hasType(isInteger())),
anyOf(
// Detect the expression: string("...", 0);
@@ -114,7 +114,34 @@ void StringConstructorCheck::registerMatchers(MatchFinder *Finder) {
// Detect the expression: string("lit", 5)
allOf(hasArgument(0, ConstStrLiteral.bind("literal-with-length")),
hasArgument(1, ignoringParenImpCasts(
- integerLiteral().bind("int"))))))
+ integerLiteral().bind("length"))))))
+ .bind("constructor"),
+ this);
+
+ // Check the literal string constructor with char pointer, start position and
+ // length parameters. [i.e. string (const char* s, size_t pos, size_t count);]
+ Finder->addMatcher(
+ cxxConstructExpr(
+ hasDeclaration(cxxConstructorDecl(ofClass(
+ cxxRecordDecl(hasAnyName(removeNamespaces(StringNames)))))),
+ argumentCountIs(3), hasArgument(0, hasType(CharPtrType)),
+ hasArgument(1, hasType(qualType(isInteger()))),
+ hasArgument(2, hasType(qualType(isInteger()))),
+ anyOf(
+ // Detect the expression: string("...", 1, 0);
+ hasArgument(2, ZeroExpr.bind("empty-string")),
+ // Detect the expression: string("...", -4, 1);
+ hasArgument(1, NegativeExpr.bind("negative-pos")),
+ // Detect the expression: string("...", 0, -4);
+ hasArgument(2, NegativeExpr.bind("negative-length")),
+ // Detect the expression: string("lit", 0, 0x1234567);
+ hasArgument(2, LargeLengthExpr.bind("large-length")),
+ // Detect the expression: string("lit", 1, 5)
+ allOf(hasArgument(0, ConstStrLiteral.bind("literal-with-length")),
+ hasArgument(
+ 1, ignoringParenImpCasts(integerLiteral().bind("pos"))),
+ hasArgument(2, ignoringParenImpCasts(
+ integerLiteral().bind("length"))))))
.bind("constructor"),
this);
@@ -155,14 +182,27 @@ void StringConstructorCheck::check(const MatchFinder::MatchResult &Result) {
diag(Loc, "constructor creating an empty string");
} else if (Result.Nodes.getNodeAs<Expr>("negative-length")) {
diag(Loc, "negative value used as length parameter");
+ } else if (Result.Nodes.getNodeAs<Expr>("negative-pos")) {
+ diag(Loc, "negative value used as position of the "
+ "first character parameter");
} else if (Result.Nodes.getNodeAs<Expr>("large-length")) {
if (WarnOnLargeLength)
diag(Loc, "suspicious large length parameter");
} else if (Result.Nodes.getNodeAs<Expr>("literal-with-length")) {
const auto *Str = Result.Nodes.getNodeAs<StringLiteral>("str");
- const auto *Lit = Result.Nodes.getNodeAs<IntegerLiteral>("int");
- if (Lit->getValue().ugt(Str->getLength())) {
+ const auto *Length = Result.Nodes.getNodeAs<IntegerLiteral>("length");
+ if (Length->getValue().ugt(Str->getLength())) {
diag(Loc, "length is bigger than string literal size");
+ return;
+ }
+ if (const auto *Pos = Result.Nodes.getNodeAs<IntegerLiteral>("pos")) {
+ if (Pos->getValue().uge(Str->getLength())) {
+ diag(Loc, "position of the first character parameter is bigger than "
+ "string literal character range");
+ } else if (Length->getValue().ugt(
+ (Str->getLength() - Pos->getValue()).getZExtValue())) {
+ diag(Loc, "length is bigger than remaining string literal size");
+ }
}
} else if (const auto *Ptr = Result.Nodes.getNodeAs<Expr>("from-ptr")) {
Expr::EvalResult ConstPtr;
clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp
@@ -179,15 +179,15 @@ static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD,
bool VirtualNearMissCheck::isPossibleToBeOverridden(
const CXXMethodDecl *BaseMD) {
- auto Iter = PossibleMap.find(BaseMD);
- if (Iter != PossibleMap.end())
+ auto [Iter, Inserted] = PossibleMap.try_emplace(BaseMD);
+ if (!Inserted)
return Iter->second;
bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
!isa<CXXDestructorDecl>(BaseMD) && BaseMD->isVirtual() &&
!BaseMD->isOverloadedOperator() &&
!isa<CXXConversionDecl>(BaseMD);
- PossibleMap[BaseMD] = IsPossible;
+ Iter->second = IsPossible;
return IsPossible;
}
clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
@@ -8,6 +8,8 @@
#include "ConstCorrectnessCheck.h"
#include "../utils/FixItHintUtils.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
@@ -41,7 +43,9 @@ ConstCorrectnessCheck::ConstCorrectnessCheck(StringRef Name,
TransformValues(Options.get("TransformValues", true)),
TransformReferences(Options.get("TransformReferences", true)),
TransformPointersAsValues(
- Options.get("TransformPointersAsValues", false)) {
+ Options.get("TransformPointersAsValues", false)),
+ AllowedTypes(
+ utils::options::parseStringList(Options.get("AllowedTypes", ""))) {
if (AnalyzeValues == false && AnalyzeReferences == false)
this->configurationDiag(
"The check 'misc-const-correctness' will not "
@@ -57,6 +61,8 @@ void ConstCorrectnessCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "TransformValues", TransformValues);
Options.store(Opts, "TransformReferences", TransformReferences);
Options.store(Opts, "TransformPointersAsValues", TransformPointersAsValues);
+ Options.store(Opts, "AllowedTypes",
+ utils::options::serializeStringList(AllowedTypes));
}
void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
@@ -73,6 +79,12 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
hasType(referenceType(pointee(hasCanonicalType(templateTypeParmType())))),
hasType(referenceType(pointee(substTemplateTypeParmType()))));
+ const auto AllowedType = hasType(qualType(anyOf(
+ hasDeclaration(namedDecl(matchers::matchesAnyListedName(AllowedTypes))),
+ references(namedDecl(matchers::matchesAnyListedName(AllowedTypes))),
+ pointerType(pointee(hasDeclaration(
+ namedDecl(matchers::matchesAnyListedName(AllowedTypes))))))));
+
const auto AutoTemplateType = varDecl(
anyOf(hasType(autoType()), hasType(referenceType(pointee(autoType()))),
hasType(pointerType(pointee(autoType())))));
@@ -87,7 +99,8 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
unless(anyOf(ConstType, ConstReference, TemplateType,
hasInitializer(isInstantiationDependent()), AutoTemplateType,
RValueReference, FunctionPointerRef,
- hasType(cxxRecordDecl(isLambda())), isImplicit())));
+ hasType(cxxRecordDecl(isLambda())), isImplicit(),
+ AllowedType)));
// Match the function scope for which the analysis of all local variables
// shall be run.
clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h
@@ -45,6 +45,7 @@ private:
const bool TransformValues;
const bool TransformReferences;
const bool TransformPointersAsValues;
+ const std::vector<StringRef> AllowedTypes;
};
} // namespace clang::tidy::misc
clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp
@@ -139,10 +139,8 @@ static bool areEquivalentExpr(const Expr *Left, const Expr *Right) {
return cast<BinaryOperator>(Left)->getOpcode() ==
cast<BinaryOperator>(Right)->getOpcode();
case Stmt::UnaryExprOrTypeTraitExprClass:
- const auto *LeftUnaryExpr =
- cast<UnaryExprOrTypeTraitExpr>(Left);
- const auto *RightUnaryExpr =
- cast<UnaryExprOrTypeTraitExpr>(Right);
+ const auto *LeftUnaryExpr = cast<UnaryExprOrTypeTraitExpr>(Left);
+ const auto *RightUnaryExpr = cast<UnaryExprOrTypeTraitExpr>(Right);
if (LeftUnaryExpr->isArgumentType() && RightUnaryExpr->isArgumentType())
return LeftUnaryExpr->getKind() == RightUnaryExpr->getKind() &&
LeftUnaryExpr->getArgumentType() ==
@@ -699,7 +697,8 @@ static bool retrieveRelationalIntegerConstantExpr(
Symbol = OverloadedOperatorExpr->getArg(IntegerConstantIsFirstArg ? 1 : 0);
OperandExpr = OverloadedOperatorExpr;
- Opcode = BinaryOperator::getOverloadedOpcode(OverloadedOperatorExpr->getOperator());
+ Opcode = BinaryOperator::getOverloadedOpcode(
+ OverloadedOperatorExpr->getOperator());
if (!retrieveIntegerConstantExpr(Result, Id, Value, ConstExpr))
return false;
@@ -728,7 +727,8 @@ static bool retrieveRelationalIntegerConstantExpr(
}
// Checks for expressions like (X == 4) && (Y != 9)
-static bool areSidesBinaryConstExpressions(const BinaryOperator *&BinOp, const ASTContext *AstCtx) {
+static bool areSidesBinaryConstExpressions(const BinaryOperator *&BinOp,
+ const ASTContext *AstCtx) {
const auto *LhsBinOp = dyn_cast<BinaryOperator>(BinOp->getLHS());
const auto *RhsBinOp = dyn_cast<BinaryOperator>(BinOp->getRHS());
@@ -747,6 +747,28 @@ static bool areSidesBinaryConstExpressions(const BinaryOperator *&BinOp, const A
return false;
}
+static bool areSidesBinaryConstExpressionsOrDefinesOrIntegerConstant(
+ const BinaryOperator *&BinOp, const ASTContext *AstCtx) {
+ if (areSidesBinaryConstExpressions(BinOp, AstCtx))
+ return true;
+
+ const Expr *Lhs = BinOp->getLHS();
+ const Expr *Rhs = BinOp->getRHS();
+
+ if (!Lhs || !Rhs)
+ return false;
+
+ auto IsDefineExpr = [AstCtx](const Expr *E) {
+ const SourceRange Lsr = E->getSourceRange();
+ if (!Lsr.getBegin().isMacroID() || E->isValueDependent() ||
+ !E->isIntegerConstantExpr(*AstCtx))
+ return false;
+ return true;
+ };
+
+ return IsDefineExpr(Lhs) || IsDefineExpr(Rhs);
+}
+
// Retrieves integer constant subexpressions from binary operator expressions
// that have two equivalent sides.
// E.g.: from (X == 5) && (X == 5) retrieves 5 and 5.
@@ -785,7 +807,7 @@ static bool retrieveConstExprFromBothSides(const BinaryOperator *&BinOp,
}
static bool isSameRawIdentifierToken(const Token &T1, const Token &T2,
- const SourceManager &SM) {
+ const SourceManager &SM) {
if (T1.getKind() != T2.getKind())
return false;
if (T1.isNot(tok::raw_identifier))
@@ -808,8 +830,8 @@ static bool areExprsFromDifferentMacros(const Expr *LhsExpr,
const ASTContext *AstCtx) {
if (!LhsExpr || !RhsExpr)
return false;
- SourceRange Lsr = LhsExpr->getSourceRange();
- SourceRange Rsr = RhsExpr->getSourceRange();
+ const SourceRange Lsr = LhsExpr->getSourceRange();
+ const SourceRange Rsr = RhsExpr->getSourceRange();
if (!Lsr.getBegin().isMacroID() || !Rsr.getBegin().isMacroID())
return false;
@@ -847,11 +869,83 @@ static bool areExprsMacroAndNonMacro(const Expr *&LhsExpr,
if (!LhsExpr || !RhsExpr)
return false;
- SourceLocation LhsLoc = LhsExpr->getExprLoc();
- SourceLocation RhsLoc = RhsExpr->getExprLoc();
+ const SourceLocation LhsLoc = LhsExpr->getExprLoc();
+ const SourceLocation RhsLoc = RhsExpr->getExprLoc();
return LhsLoc.isMacroID() != RhsLoc.isMacroID();
}
+
+static bool areStringsSameIgnoreSpaces(const llvm::StringRef Left,
+ const llvm::StringRef Right) {
+ if (Left == Right)
+ return true;
+
+ // Do running comparison ignoring spaces
+ llvm::StringRef L = Left.trim();
+ llvm::StringRef R = Right.trim();
+ while (!L.empty() && !R.empty()) {
+ L = L.ltrim();
+ R = R.ltrim();
+ if (L.empty() && R.empty())
+ return true;
+ // If symbol compared are different ==> strings are not the same
+ if (L.front() != R.front())
+ return false;
+ L = L.drop_front();
+ R = R.drop_front();
+ }
+ return L.empty() && R.empty();
+}
+
+static bool areExprsSameMacroOrLiteral(const BinaryOperator *BinOp,
+ const ASTContext *Context) {
+
+ if (!BinOp)
+ return false;
+
+ const Expr *Lhs = BinOp->getLHS();
+ const Expr *Rhs = BinOp->getRHS();
+ const SourceManager &SM = Context->getSourceManager();
+
+ const SourceRange Lsr = Lhs->getSourceRange();
+ const SourceRange Rsr = Rhs->getSourceRange();
+ if (Lsr.getBegin().isMacroID()) {
+ // Left is macro so right macro too
+ if (Rsr.getBegin().isMacroID()) {
+ // Both sides are macros so they are same macro or literal
+ const llvm::StringRef L = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Lsr), SM, Context->getLangOpts(), 0);
+ const llvm::StringRef R = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Rsr), SM, Context->getLangOpts(), 0);
+ return areStringsSameIgnoreSpaces(L, R);
+ }
+ // Left is macro but right is not so they are not same macro or literal
+ return false;
+ }
+ const auto *Lil = dyn_cast<IntegerLiteral>(Lhs);
+ const auto *Ril = dyn_cast<IntegerLiteral>(Rhs);
+ if (Lil && Ril)
+ return Lil->getValue() == Ril->getValue();
+
+ const auto *LStrl = dyn_cast<StringLiteral>(Lhs);
+ const auto *RStrl = dyn_cast<StringLiteral>(Rhs);
+ if (Lil && Ril) {
+ const llvm::StringRef L = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(LStrl->getBeginLoc()), SM,
+ Context->getLangOpts(), 0);
+ const llvm::StringRef R = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(RStrl->getBeginLoc()), SM,
+ Context->getLangOpts(), 0);
+ return L.compare(R) == 0;
+ }
+
+ const auto *Lbl = dyn_cast<CXXBoolLiteralExpr>(Lhs);
+ const auto *Rbl = dyn_cast<CXXBoolLiteralExpr>(Rhs);
+ if (Lbl && Rbl)
+ return Lbl->getValue() == Rbl->getValue();
+
+ return false;
+}
} // namespace
void RedundantExpressionCheck::registerMatchers(MatchFinder *Finder) {
@@ -1089,7 +1183,6 @@ static bool exprEvaluatesToSymbolic(BinaryOperatorKind Opcode, APSInt Value) {
((Opcode == BO_And || Opcode == BO_AndAssign) && ~Value == 0);
}
-
void RedundantExpressionCheck::checkBitwiseExpr(
const MatchFinder::MatchResult &Result) {
if (const auto *ComparisonOperator = Result.Nodes.getNodeAs<BinaryOperator>(
@@ -1134,8 +1227,8 @@ void RedundantExpressionCheck::checkBitwiseExpr(
ConstExpr))
return;
- if((Value != 0 && ~Value != 0) || Sym->getExprLoc().isMacroID())
- return;
+ if ((Value != 0 && ~Value != 0) || Sym->getExprLoc().isMacroID())
+ return;
SourceLocation Loc = IneffectiveOperator->getOperatorLoc();
@@ -1276,19 +1369,23 @@ void RedundantExpressionCheck::check(const MatchFinder::MatchResult &Result) {
return;
}
- if (areSidesBinaryConstExpressions(BinOp, Result.Context)) {
+ if (areSidesBinaryConstExpressionsOrDefinesOrIntegerConstant(
+ BinOp, Result.Context)) {
const Expr *LhsConst = nullptr, *RhsConst = nullptr;
BinaryOperatorKind MainOpcode{}, SideOpcode{};
-
- if (!retrieveConstExprFromBothSides(BinOp, MainOpcode, SideOpcode,
- LhsConst, RhsConst, Result.Context))
- return;
-
- if (areExprsFromDifferentMacros(LhsConst, RhsConst, Result.Context) ||
- areExprsMacroAndNonMacro(LhsConst, RhsConst))
- return;
+ if (areSidesBinaryConstExpressions(BinOp, Result.Context)) {
+ if (!retrieveConstExprFromBothSides(BinOp, MainOpcode, SideOpcode,
+ LhsConst, RhsConst, Result.Context))
+ return;
+
+ if (areExprsFromDifferentMacros(LhsConst, RhsConst, Result.Context) ||
+ areExprsMacroAndNonMacro(LhsConst, RhsConst))
+ return;
+ } else {
+ if (!areExprsSameMacroOrLiteral(BinOp, Result.Context))
+ return;
+ }
}
-
diag(BinOp->getOperatorLoc(), "both sides of operator are equivalent");
}
clang-tools-extra/clang-tidy/modernize/DeprecatedHeadersCheck.cpp
@@ -199,10 +199,10 @@ void IncludeModernizePPCallbacks::InclusionDirective(
// 2. Insert `using namespace std;` to the beginning of TU.
// 3. Do nothing and let the user deal with the migration himself.
SourceLocation DiagLoc = FilenameRange.getBegin();
- if (CStyledHeaderToCxx.count(FileName) != 0) {
- IncludesToBeProcessed.emplace_back(
- IncludeMarker{CStyledHeaderToCxx[FileName], FileName,
- FilenameRange.getAsRange(), DiagLoc});
+ if (auto It = CStyledHeaderToCxx.find(FileName);
+ It != CStyledHeaderToCxx.end()) {
+ IncludesToBeProcessed.emplace_back(IncludeMarker{
+ It->second, FileName, FilenameRange.getAsRange(), DiagLoc});
} else if (DeleteHeaders.count(FileName) != 0) {
IncludesToBeProcessed.emplace_back(
// NOLINTNEXTLINE(modernize-use-emplace) - false-positive
clang-tools-extra/clang-tidy/readability/NonConstParameterCheck.cpp
@@ -119,13 +119,12 @@ void NonConstParameterCheck::addParm(const ParmVarDecl *Parm) {
T->getPointeeType()->isFloatingType()))
return;
- if (Parameters.find(Parm) != Parameters.end())
+ auto [It, Inserted] = Parameters.try_emplace(Parm);
+ if (!Inserted)
return;
- ParmInfo PI;
- PI.IsReferenced = false;
- PI.CanBeConst = true;
- Parameters[Parm] = PI;
+ It->second.IsReferenced = false;
+ It->second.CanBeConst = true;
}
void NonConstParameterCheck::setReferenced(const DeclRefExpr *Ref) {
clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp
@@ -14,13 +14,14 @@ namespace clang::tidy::utils {
ExceptionSpecAnalyzer::State
ExceptionSpecAnalyzer::analyze(const FunctionDecl *FuncDecl) {
- // Check if the function has already been analyzed and reuse that result.
- const auto CacheEntry = FunctionCache.find(FuncDecl);
- if (CacheEntry == FunctionCache.end()) {
+ // Check if function exist in cache or add temporary value to cache to protect
+ // against endless recursion.
+ const auto [CacheEntry, NotFound] =
+ FunctionCache.try_emplace(FuncDecl, State::NotThrowing);
+ if (NotFound) {
ExceptionSpecAnalyzer::State State = analyzeImpl(FuncDecl);
-
- // Cache the result of the analysis.
- FunctionCache.try_emplace(FuncDecl, State);
+ // Update result with calculated value
+ FunctionCache[FuncDecl] = State;
return State;
}
clang-tools-extra/clangd/ClangdServer.h
@@ -184,7 +184,7 @@ public:
bool UseDirtyHeaders = false;
// If true, parse emplace-like functions in the preamble.
- bool PreambleParseForwardingFunctions = false;
+ bool PreambleParseForwardingFunctions = true;
/// Whether include fixer insertions for Objective-C code should use #import
/// instead of #include.
@@ -501,7 +501,7 @@ private:
// Whether the client supports folding only complete lines.
bool LineFoldingOnly = false;
- bool PreambleParseForwardingFunctions = false;
+ bool PreambleParseForwardingFunctions = true;
bool ImportInsertions = false;
clang-tools-extra/clangd/CollectMacros.cpp
@@ -18,10 +18,13 @@
namespace clang {
namespace clangd {
-Range MacroOccurrence::toRange(const SourceManager &SM) const {
+CharSourceRange MacroOccurrence::toSourceRange(const SourceManager &SM) const {
auto MainFile = SM.getMainFileID();
- return halfOpenToRange(
- SM, syntax::FileRange(MainFile, StartOffset, EndOffset).toCharRange(SM));
+ return syntax::FileRange(MainFile, StartOffset, EndOffset).toCharRange(SM);
+}
+
+Range MacroOccurrence::toRange(const SourceManager &SM) const {
+ return halfOpenToRange(SM, toSourceRange(SM));
}
void CollectMainFileMacros::add(const Token &MacroNameTok, const MacroInfo *MI,
clang-tools-extra/clangd/CollectMacros.h
@@ -31,6 +31,7 @@ struct MacroOccurrence {
// True if the occurence is used in a conditional directive, e.g. #ifdef MACRO
bool InConditionalDirective;
+ CharSourceRange toSourceRange(const SourceManager &SM) const;
Range toRange(const SourceManager &SM) const;
};
clang-tools-extra/clangd/Compiler.h
@@ -40,7 +40,7 @@ public:
// Options to run clang e.g. when parsing AST.
struct ParseOptions {
- bool PreambleParseForwardingFunctions = false;
+ bool PreambleParseForwardingFunctions = true;
bool ImportInsertions = false;
};
clang-tools-extra/clangd/XRefs.cpp
@@ -372,6 +372,15 @@ void enhanceLocatedSymbolsFromIndex(llvm::MutableArrayRef<LocatedSymbol> Result,
});
}
+bool objcMethodIsTouched(const SourceManager &SM, const ObjCMethodDecl *OMD,
+ SourceLocation Loc) {
+ unsigned NumSels = OMD->getNumSelectorLocs();
+ for (unsigned I = 0; I < NumSels; ++I)
+ if (SM.getSpellingLoc(OMD->getSelectorLoc(I)) == Loc)
+ return true;
+ return false;
+}
+
// Decls are more complicated.
// The AST contains at least a declaration, maybe a definition.
// These are up-to-date, and so generally preferred over index results.
@@ -430,6 +439,26 @@ locateASTReferent(SourceLocation CurLoc, const syntax::Token *TouchedIdentifier,
continue;
}
}
+ // Special case: - (void)^method {} should jump to overrides, but the decl
+ // shouldn't, only the definition. Note that an Objective-C method can
+ // override a parent class or protocol.
+ //
+ // FIXME: Support jumping from a protocol decl to overrides on go-to
+ // definition.
+ if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(D)) {
+ if (OMD->isThisDeclarationADefinition() && TouchedIdentifier &&
+ objcMethodIsTouched(SM, OMD, TouchedIdentifier->location())) {
+ llvm::SmallVector<const ObjCMethodDecl *, 4> Overrides;
+ OMD->getOverriddenMethods(Overrides);
+ if (!Overrides.empty()) {
+ for (const auto *Override : Overrides)
+ AddResultDecl(Override);
+ LocateASTReferentMetric.record(1, "objc-overriden-method");
+ }
+ AddResultDecl(OMD);
+ continue;
+ }
+ }
// Special case: the cursor is on an alias, prefer other results.
// This targets "using ns::^Foo", where the target is more interesting.
@@ -1283,6 +1312,12 @@ std::vector<LocatedSymbol> findImplementations(ParsedAST &AST, Position Pos,
} else if (const auto *RD = dyn_cast<CXXRecordDecl>(ND)) {
IDs.insert(getSymbolID(RD));
QueryKind = RelationKind::BaseOf;
+ } else if (const auto *OMD = dyn_cast<ObjCMethodDecl>(ND)) {
+ IDs.insert(getSymbolID(OMD));
+ QueryKind = RelationKind::OverriddenBy;
+ } else if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(ND)) {
+ IDs.insert(getSymbolID(ID));
+ QueryKind = RelationKind::BaseOf;
}
}
return findImplementors(std::move(IDs), QueryKind, Index, AST.tuPath());
@@ -1302,6 +1337,21 @@ void getOverriddenMethods(const CXXMethodDecl *CMD,
}
}
+// Recursively finds all the overridden methods of `OMD` in complete type
+// hierarchy.
+void getOverriddenMethods(const ObjCMethodDecl *OMD,
+ llvm::DenseSet<SymbolID> &OverriddenMethods) {
+ if (!OMD)
+ return;
+ llvm::SmallVector<const ObjCMethodDecl *, 4> Overrides;
+ OMD->getOverriddenMethods(Overrides);
+ for (const ObjCMethodDecl *Base : Overrides) {
+ if (auto ID = getSymbolID(Base))
+ OverriddenMethods.insert(ID);
+ getOverriddenMethods(Base, OverriddenMethods);
+ }
+}
+
std::optional<std::string>
stringifyContainerForMainFileRef(const Decl *Container) {
// FIXME We might also want to display the signature here
@@ -1438,6 +1488,12 @@ ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
getOverriddenMethods(CMD, OverriddenMethods);
}
}
+ // Special case: Objective-C methods can override a parent class or
+ // protocol, we should be sure to report references to those.
+ if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(ND)) {
+ OverriddenBy.Subjects.insert(getSymbolID(OMD));
+ getOverriddenMethods(OMD, OverriddenMethods);
+ }
}
}
clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -713,7 +713,8 @@ void SymbolCollector::handleMacros(const MainFileMacros &MacroRefsToIndex) {
// Add macro references.
for (const auto &IDToRefs : MacroRefsToIndex.MacroRefs) {
for (const auto &MacroRef : IDToRefs.second) {
- const auto &Range = MacroRef.toRange(SM);
+ const auto &SR = MacroRef.toSourceRange(SM);
+ auto Range = halfOpenToRange(SM, SR);
bool IsDefinition = MacroRef.IsDefinition;
Ref R;
R.Location.Start.setLine(Range.start.line);
@@ -726,9 +727,7 @@ void SymbolCollector::handleMacros(const MainFileMacros &MacroRefsToIndex) {
if (IsDefinition) {
Symbol S;
S.ID = IDToRefs.first;
- auto StartLoc = cantFail(sourceLocationInMainFile(SM, Range.start));
- auto EndLoc = cantFail(sourceLocationInMainFile(SM, Range.end));
- S.Name = toSourceCode(SM, SourceRange(StartLoc, EndLoc));
+ S.Name = toSourceCode(SM, SR.getAsRange());
S.SymInfo.Kind = index::SymbolKind::Macro;
S.SymInfo.SubKind = index::SymbolSubKind::None;
S.SymInfo.Properties = index::SymbolPropertySet();
clang-tools-extra/clangd/unittests/ASTTests.cpp
@@ -329,7 +329,7 @@ TEST(ClangdAST, GetContainedAutoParamType) {
auto &&d,
auto *&e,
auto (*f)(int)
- ){};
+ ){ return 0; };
int withoutAuto(
int a,
@@ -338,7 +338,7 @@ TEST(ClangdAST, GetContainedAutoParamType) {
int &&d,
int *&e,
int (*f)(int)
- ){};
+ ){ return 0; };
)cpp");
TU.ExtraArgs.push_back("-std=c++20");
auto AST = TU.build();
clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
@@ -113,7 +113,7 @@ TEST(WorkspaceSymbols, Unnamed) {
TEST(WorkspaceSymbols, InMainFile) {
TestTU TU;
TU.Code = R"cpp(
- int test() {}
+ int test() { return 0; }
static void test2() {}
)cpp";
EXPECT_THAT(getSymbols(TU, "test"),
@@ -537,12 +537,14 @@ TEST(DocumentSymbols, InHeaderFile) {
TestTU TU;
TU.AdditionalFiles["bar.h"] = R"cpp(
int foo() {
+ return 0;
}
)cpp";
TU.Code = R"cpp(
int i; // declaration to finish preamble
#include "bar.h"
int test() {
+ return 0;
}
)cpp";
EXPECT_THAT(getSymbols(TU.build()),
@@ -780,7 +782,7 @@ TEST(DocumentSymbols, FuncTemplates) {
TestTU TU;
Annotations Source(R"cpp(
template <class T>
- T foo() {}
+ T foo() { return T{}; }
auto x = foo<int>();
auto y = foo<double>();
clang-tools-extra/clangd/unittests/ParsedASTTests.cpp
@@ -251,7 +251,7 @@ TEST(ParsedASTTest, NoCrashOnTokensWithTidyCheck) {
// this check runs the preprocessor, we need to make sure it does not break
// our recording logic.
TU.ClangTidyProvider = addTidyChecks("modernize-use-trailing-return-type");
- TU.Code = "inline int foo() {}";
+ TU.Code = "inline int foo() { return 0; }";
auto AST = TU.build();
const syntax::TokenBuffer &T = AST.getTokens();
clang-tools-extra/clangd/unittests/QualityTests.cpp
@@ -108,7 +108,7 @@ TEST(QualityTests, SymbolRelevanceSignalExtraction) {
using flags::FLAGS_FOO;
- int ::header_main() {}
+ int ::header_main() { return 0; }
int main();
[[deprecated]]
clang-tools-extra/clangd/unittests/RenameTests.cpp
@@ -214,7 +214,7 @@ TEST(RenameTest, WithinFileRename) {
template<typename T>
class Foo {
public:
- static T [[f^oo]]() {}
+ static T [[f^oo]]() { return T(); }
};
void bar() {
@@ -225,7 +225,7 @@ TEST(RenameTest, WithinFileRename) {
template<typename T>
class Foo {
public:
- T [[f^oo]]() {}
+ T [[f^oo]]() { return T(); }
};
void bar() {
@@ -827,7 +827,7 @@ TEST(RenameTest, WithinFileRename) {
// Issue 170: Rename symbol introduced by UsingDecl
R"cpp(
- namespace ns { void [[f^oo]](); }
+ namespace ns { void [[f^oo]](); }
using ns::[[f^oo]];
@@ -1307,7 +1307,7 @@ TEST(RenameTest, Renameable) {
"no symbol", false},
{R"cpp(// FIXME we probably want to rename both overloads here,
- // but renaming currently assumes there's only a
+ // but renaming currently assumes there's only a
// single canonical declaration.
namespace ns { int foo(int); char foo(char); }
using ns::^foo;
@@ -1776,7 +1776,7 @@ TEST(CrossFileRenameTests, WithUpToDateIndex) {
void [[foo]]() override {};
};
- void func(Base* b, Derived1* d1,
+ void func(Base* b, Derived1* d1,
Derived2* d2, NotDerived* nd) {
b->[[foo]]();
d1->[[foo]]();
clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
@@ -741,6 +741,7 @@ sizeof...($TemplateParameter[[Elements]]);
$Class[[Foo]].$Field_static[[sharedInstance]].$Field[[someProperty]] $Operator[[=]] 1;
self.$Field[[someProperty]] $Operator[[=]] self.$Field[[someProperty]] $Operator[[+]] self.$Field[[otherMethod]] $Operator[[+]] 1;
self->$Field[[_someProperty]] $Operator[[=]] $Field[[_someProperty]] $Operator[[+]] 1;
+ return 0;
}
@end
)cpp",
clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
@@ -201,6 +201,7 @@ TEST(FoldingRanges, ASTAll) {
R"cpp(
#define FOO int foo() {\
int Variable = 42; \
+ return 0; \
}
// Do not generate folding range for braces within macro expansion.
@@ -336,18 +337,18 @@ TEST(FoldingRanges, PseudoParserWithoutLineFoldings) {
]]};
)cpp",
R"cpp(
- /*[[ Multi
+ /*[[ Multi
* line
- * comment
+ * comment
]]*/
)cpp",
R"cpp(
//[[ Comment
// 1]]
-
+
//[[ Comment
// 2]]
-
+
// No folding for single line comment.
/*[[ comment 3
clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -1335,6 +1335,42 @@ TEST_F(SymbolCollectorTest, OverrideRelationsMultipleInheritance) {
OverriddenBy(CBar, DBar), OverriddenBy(CBaz, DBaz)));
}
+TEST_F(SymbolCollectorTest, ObjCOverrideRelationsSimpleInheritance) {
+ std::string Header = R"cpp(
+ @interface A
+ - (void)foo;
+ @end
+ @interface B : A
+ - (void)foo; // A::foo
+ - (void)bar;
+ @end
+ @interface C : B
+ - (void)bar; // B::bar
+ @end
+ @interface D : C
+ - (void)foo; // B::foo
+ - (void)bar; // C::bar
+ @end
+ )cpp";
+ runSymbolCollector(Header, /*Main=*/"",
+ {"-xobjective-c++", "-Wno-objc-root-class"});
+ const Symbol &AFoo = findSymbol(Symbols, "A::foo");
+ const Symbol &BFoo = findSymbol(Symbols, "B::foo");
+ const Symbol &DFoo = findSymbol(Symbols, "D::foo");
+
+ const Symbol &BBar = findSymbol(Symbols, "B::bar");
+ const Symbol &CBar = findSymbol(Symbols, "C::bar");
+ const Symbol &DBar = findSymbol(Symbols, "D::bar");
+
+ std::vector<Relation> Result;
+ for (const Relation &R : Relations)
+ if (R.Predicate == RelationKind::OverriddenBy)
+ Result.push_back(R);
+ EXPECT_THAT(Result, UnorderedElementsAre(
+ OverriddenBy(AFoo, BFoo), OverriddenBy(BBar, CBar),
+ OverriddenBy(BFoo, DFoo), OverriddenBy(CBar, DBar)));
+}
+
TEST_F(SymbolCollectorTest, CountReferences) {
const std::string Header = R"(
class W;
clang-tools-extra/clangd/unittests/SymbolInfoTests.cpp
@@ -36,6 +36,7 @@ TEST(SymbolInfoTests, All) {
void $decl[[foo]]();
int bar() {
fo^o();
+ return 0;
}
)cpp",
{ExpectedSymbolDetails{"foo", "", "c:@F@foo#", "decl"}}},
@@ -44,6 +45,7 @@ TEST(SymbolInfoTests, All) {
void $def[[foo]]() {}
int bar() {
fo^o();
+ return 0;
}
)cpp",
{ExpectedSymbolDetails{"foo", "", "c:@F@foo#", "def", "def"}}},
@@ -53,6 +55,7 @@ TEST(SymbolInfoTests, All) {
void $def[[foo]]() {}
int bar() {
fo^o();
+ return 0;
}
)cpp",
{ExpectedSymbolDetails{"foo", "", "c:@F@foo#", "decl", "def"}}},
@@ -83,6 +86,7 @@ TEST(SymbolInfoTests, All) {
void $decl[[foo]]();
int baz() {
fo^o();
+ return 0;
}
}
)cpp",
@@ -96,6 +100,7 @@ TEST(SymbolInfoTests, All) {
namespace barbar {
int baz() {
bar::fo^o();
+ return 0;
}
}
)cpp",
@@ -108,6 +113,7 @@ TEST(SymbolInfoTests, All) {
namespace Nbaz {
int baz() {
::fo^o();
+ return 0;
}
}
}
@@ -121,6 +127,7 @@ TEST(SymbolInfoTests, All) {
namespace barbar {
int baz() {
fo^o();
+ return 0;
}
}
)cpp",
@@ -136,6 +143,7 @@ TEST(SymbolInfoTests, All) {
int baz() {
bar::BarType b;
fo^o(b);
+ return 0;
}
}
)cpp",
clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -95,7 +95,7 @@ TEST(HighlightsTest, All) {
)cpp",
R"cpp(// Function
- int [[^foo]](int) {}
+ int [[^foo]](int) { return 0; }
int main() {
[[foo]]([[foo]](42));
auto *X = &[[foo]];
@@ -411,6 +411,85 @@ TEST(LocateSymbol, FindOverrides) {
sym("foo", Code.range("2"), std::nullopt)));
}
+TEST(LocateSymbol, FindOverridesFromDefObjC) {
+ auto Code = Annotations(R"objc(
+ @protocol Fooey
+ - (void)foo;
+ @end
+ @interface Base
+ - (void)foo;
+ @end
+ @interface Foo : Base<Fooey>
+ - (void)$1[[foo]];
+ @end
+
+ @interface Bar : Foo
+ - (void)$2[[foo]];
+ @end
+ @implementation Bar
+ - (void)$3[[fo^o]] {}
+ @end
+ )objc");
+ TestTU TU = TestTU::withCode(Code.code());
+ TU.ExtraArgs.push_back("-xobjective-c++");
+ auto AST = TU.build();
+ EXPECT_THAT(
+ locateSymbolAt(AST, Code.point(), TU.index().get()),
+ UnorderedElementsAre(sym("foo", Code.range("1"), std::nullopt),
+ sym("foo", Code.range("2"), Code.range("3"))));
+}
+
+TEST(LocateSymbol, NoOverridesFromDeclObjC) {
+ auto Code = Annotations(R"objc(
+ @protocol Fooey
+ - (void)foo;
+ @end
+ @interface Base
+ - (void)foo;
+ @end
+ @interface Foo : Base<Fooey>
+ - (void)foo;
+ @end
+
+ @interface Bar : Foo
+ - (void)$2[[fo^o]];
+ @end
+ @implementation Bar
+ - (void)$3[[foo]] {}
+ @end
+ )objc");
+ TestTU TU = TestTU::withCode(Code.code());
+ TU.ExtraArgs.push_back("-xobjective-c++");
+ auto AST = TU.build();
+ EXPECT_THAT(
+ locateSymbolAt(AST, Code.point(), TU.index().get()),
+ UnorderedElementsAre(sym("foo", Code.range("2"), Code.range("3"))));
+}
+
+TEST(LocateSymbol, ObjCNoOverridesOnUsage) {
+ auto Code = Annotations(R"objc(
+ @interface Foo
+ - (void)foo;
+ @end
+
+ @interface Bar : Foo
+ - (void)$1[[foo]];
+ @end
+ @implementation Bar
+ - (void)$2[[foo]] {}
+ @end
+ void doSomething(Bar *bar) {
+ [bar fo^o];
+ }
+ )objc");
+ TestTU TU = TestTU::withCode(Code.code());
+ TU.ExtraArgs.push_back("-xobjective-c++");
+ auto AST = TU.build();
+ EXPECT_THAT(
+ locateSymbolAt(AST, Code.point(), TU.index().get()),
+ UnorderedElementsAre(sym("foo", Code.range("1"), Code.range("2"))));
+}
+
TEST(LocateSymbol, WithIndexPreferredLocation) {
Annotations SymbolHeader(R"cpp(
class $p[[Proto]] {};
@@ -1834,6 +1913,41 @@ TEST(FindImplementations, Inheritance) {
}
}
+TEST(FindImplementations, InheritanceObjC) {
+ llvm::StringRef Test = R"objc(
+ @interface $base^Base
+ - (void)fo$foo^o;
+ @end
+ @protocol Protocol
+ - (void)$protocol^protocol;
+ @end
+ @interface $ChildDecl[[Child]] : Base <Protocol>
+ - (void)concrete;
+ - (void)$fooDecl[[foo]];
+ @end
+ @implementation $ChildDef[[Child]]
+ - (void)concrete {}
+ - (void)$fooDef[[foo]] {}
+ - (void)$protocolDef[[protocol]] {}
+ @end
+ )objc";
+
+ Annotations Code(Test);
+ auto TU = TestTU::withCode(Code.code());
+ TU.ExtraArgs.push_back("-xobjective-c++");
+ auto AST = TU.build();
+ auto Index = TU.index();
+ EXPECT_THAT(findImplementations(AST, Code.point("base"), Index.get()),
+ UnorderedElementsAre(sym("Child", Code.range("ChildDecl"),
+ Code.range("ChildDef"))));
+ EXPECT_THAT(findImplementations(AST, Code.point("foo"), Index.get()),
+ UnorderedElementsAre(
+ sym("foo", Code.range("fooDecl"), Code.range("fooDef"))));
+ EXPECT_THAT(findImplementations(AST, Code.point("protocol"), Index.get()),
+ UnorderedElementsAre(sym("protocol", Code.range("protocolDef"),
+ Code.range("protocolDef"))));
+}
+
TEST(FindImplementations, CaptureDefinition) {
llvm::StringRef Test = R"cpp(
struct Base {
@@ -1963,6 +2077,7 @@ void checkFindRefs(llvm::StringRef Test, bool UseIndex = false) {
Annotations T(Test);
auto TU = TestTU::withCode(T.code());
TU.ExtraArgs.push_back("-std=c++20");
+ TU.ExtraArgs.push_back("-xobjective-c++");
auto AST = TU.build();
std::vector<Matcher<ReferencesResult::Reference>> ExpectedLocations;
@@ -2025,7 +2140,7 @@ TEST(FindReferences, WithinAST) {
)cpp",
R"cpp(// Function
- int $def[[foo]](int) {}
+ int $def[[foo]](int) { return 0; }
int main() {
auto *X = &$(main)[[^foo]];
$(main)[[foo]](42);
@@ -2045,7 +2160,7 @@ TEST(FindReferences, WithinAST) {
R"cpp(// Method call
struct Foo { int $decl(Foo)[[foo]](); };
- int Foo::$def(Foo)[[foo]]() {}
+ int Foo::$def(Foo)[[foo]]() { return 0; }
int main() {
Foo f;
f.$(main)[[^foo]]();
@@ -2143,7 +2258,7 @@ TEST(FindReferences, WithinAST) {
)cpp",
R"cpp(// Dependent code
template <typename T> void $decl[[foo]](T t);
- template <typename T> void bar(T t) { $(bar)[[foo]](t); } // foo in bar is uninstantiated.
+ template <typename T> void bar(T t) { $(bar)[[foo]](t); } // foo in bar is uninstantiated.
void baz(int x) { $(baz)[[f^oo]](x); }
)cpp",
R"cpp(
@@ -2260,6 +2375,25 @@ TEST(FindReferences, IncludeOverrides) {
checkFindRefs(Test, /*UseIndex=*/true);
}
+TEST(FindReferences, IncludeOverridesObjC) {
+ llvm::StringRef Test =
+ R"objc(
+ @interface Base
+ - (void)$decl(Base)[[f^unc]];
+ @end
+ @interface Derived : Base
+ - (void)$overridedecl(Derived::func)[[func]];
+ @end
+ @implementation Derived
+ - (void)$overridedef[[func]] {}
+ @end
+ void test(Derived *derived, Base *base) {
+ [derived func]; // No references to the overrides.
+ [base $(test)[[func]]];
+ })objc";
+ checkFindRefs(Test, /*UseIndex=*/true);
+}
+
TEST(FindReferences, RefsToBaseMethod) {
llvm::StringRef Test =
R"cpp(
@@ -2284,6 +2418,27 @@ TEST(FindReferences, RefsToBaseMethod) {
checkFindRefs(Test, /*UseIndex=*/true);
}
+TEST(FindReferences, RefsToBaseMethodObjC) {
+ llvm::StringRef Test =
+ R"objc(
+ @interface BaseBase
+ - (void)$(BaseBase)[[func]];
+ @end
+ @interface Base : BaseBase
+ - (void)$(Base)[[func]];
+ @end
+ @interface Derived : Base
+ - (void)$decl(Derived)[[fu^nc]];
+ @end
+ void test(BaseBase *bb, Base *b, Derived *d) {
+ // refs to overridden methods in complete type hierarchy are reported.
+ [bb $(test)[[func]]];
+ [b $(test)[[func]]];
+ [d $(test)[[fu^nc]]];
+ })objc";
+ checkFindRefs(Test, /*UseIndex=*/true);
+}
+
TEST(FindReferences, MainFileReferencesOnly) {
llvm::StringRef Test =
R"cpp(
@@ -2353,6 +2508,7 @@ TEST(FindReferences, ExplicitSymbols) {
X $def(test)[[a]];
$(test)[[a]].operator bool();
if ($(test)[[a^]]) {} // ignore implicit conversion-operator AST node
+ return 0;
}
)cpp",
};
@@ -2388,7 +2544,7 @@ TEST(FindReferences, UsedSymbolsFromInclude) {
#define BAR 5
int bar1();
int bar2();
- class Bar {};
+ class Bar {};
)cpp");
TU.AdditionalFiles["system/vector"] = guard(R"cpp(
namespace std {
@@ -2405,7 +2561,7 @@ TEST(FindReferences, UsedSymbolsFromInclude) {
std::vector<Matcher<ReferencesResult::Reference>> ExpectedLocations;
for (const auto &R : T.ranges())
ExpectedLocations.push_back(AllOf(rangeIs(R), attrsAre(0u)));
- for (const auto &P : T.points())
+ for (const auto &P : T.points())
EXPECT_THAT(findReferences(AST, P, 0).References,
UnorderedElementsAreArray(ExpectedLocations))
<< "Failed for Refs at " << P << "\n"
@@ -2480,6 +2636,7 @@ TEST(FindReferences, NeedsIndexForMacro) {
Annotations IndexedMain(R"cpp(
int indexed_main() {
int a = [[MACRO]](1);
+ return 0;
}
)cpp");
clang-tools-extra/clangd/unittests/tweaks/DefineInlineTests.cpp
@@ -935,10 +935,11 @@ TEST_F(DefineInlineTest, AddInline) {
// Check we put inline before cv-qualifiers.
ExtraFiles["a.h"] = "const int foo();";
apply(R"cpp(#include "a.h"
- const int fo^o() {})cpp",
+ const int fo^o() { return 0; })cpp",
&EditedFiles);
- EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
- testPath("a.h"), "inline const int foo(){}")));
+ EXPECT_THAT(EditedFiles,
+ testing::ElementsAre(FileWithContents(
+ testPath("a.h"), "inline const int foo(){ return 0; }")));
// No double inline.
ExtraFiles["a.h"] = "inline void foo();";
clang-tools-extra/clangd/unittests/tweaks/ExpandDeducedTypeTests.cpp
@@ -69,8 +69,8 @@ TEST_F(ExpandDeducedTypeTest, Test) {
EXPECT_THAT(apply(R"cpp(au^to s = &"foobar";)cpp"),
StartsWith("fail: Could not expand type"));
- EXPECT_EQ(apply("ns::Class * foo() { au^to c = foo(); }"),
- "ns::Class * foo() { ns::Class * c = foo(); }");
+ EXPECT_EQ(apply("ns::Class * foo() { au^to c = foo(); return nullptr; }"),
+ "ns::Class * foo() { ns::Class * c = foo(); return nullptr; }");
EXPECT_EQ(
apply("void ns::Func() { au^to x = new ns::Class::Nested{}; }"),
"void ns::Func() { ns::Class::Nested * x = new ns::Class::Nested{}; }");
clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp
@@ -116,6 +116,7 @@ TEST_F(ExtractVariableTest, Test) {
struct T {
int bar(int a = [[1]]) {
int b = [[z]];
+ return 0;
}
int z = [[1]];
} t;
clang-tools-extra/docs/ReleaseNotes.rst
@@ -97,10 +97,24 @@ New check aliases
Changes in existing checks
^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Improved :doc:`bugprone-string-constructor
+ <clang-tidy/checks/bugprone/string-constructor>` check to find suspicious
+ calls of ``std::string`` constructor with char pointer, start position and
+ length parameters.
+
- Improved :doc:`bugprone-unsafe-functions
<clang-tidy/checks/bugprone/unsafe-functions>` check to allow specifying
additional C++ member functions to match.
+- Improved :doc:`misc-const-correctness
+ <clang-tidy/checks/misc/const-correctness>` check by adding the option
+ `AllowedTypes`, that excludes specified types from const-correctness
+ checking.
+
+- Improved :doc:`misc-redundant-expression
+ <clang-tidy/checks/misc/redundant-expression>` check by providing additional
+ examples and fixing some macro related false positives.
+
Removed checks
^^^^^^^^^^^^^^
clang-tools-extra/docs/clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result.rst
@@ -37,20 +37,20 @@ Options
.. option:: UseCXXStaticCastsInCppSources
When suggesting fix-its for C++ code, should C++-style ``static_cast<>()``'s
- be suggested, or C-style casts. Defaults to ``true``.
+ be suggested, or C-style casts. Defaults to `true`.
.. option:: UseCXXHeadersInCppSources
When suggesting to include the appropriate header in C++ code,
should ``<cstddef>`` header be suggested, or ``<stddef.h>``.
- Defaults to ``true``.
+ Defaults to `true`.
.. option:: IgnoreConstantIntExpr
If the multiplication operands are compile-time constants (like literals or
are ``constexpr``) and fit within the source expression type, do not emit a
diagnostic or suggested fix. Only considers expressions where the source
- expression is a signed integer type. Defaults to ``false``.
+ expression is a signed integer type. Defaults to `false`.
Examples:
clang-tools-extra/docs/clang-tidy/checks/bugprone/string-constructor.rst
@@ -21,6 +21,7 @@ Examples:
.. code-block:: c++
std::string("test", 200); // Will include random characters after "test".
+ std::string("test", 2, 5); // Will include random characters after "st".
std::string_view("test", 200);
Creating an empty string from constructors with parameters is considered
@@ -31,8 +32,19 @@ Examples:
.. code-block:: c++
std::string("test", 0); // Creation of an empty string.
+ std::string("test", 1, 0);
std::string_view("test", 0);
+Passing an invalid first character position parameter to constructor will
+cause ``std::out_of_range`` exception at runtime.
+
+Examples:
+
+.. code-block:: c++
+
+ std::string("test", -1, 10); // Negative first character position.
+ std::string("test", 10, 10); // First character position is bigger than string literal character range".
+
Options
-------
clang-tools-extra/docs/clang-tidy/checks/cert/err33-c.rst
@@ -190,7 +190,7 @@ This check is an alias of check :doc:`bugprone-unused-return-value <../bugprone/
with a fixed set of functions.
Suppressing issues by casting to ``void`` is enabled by default and can be
-disabled by setting `AllowCastToVoid` option to ``false``.
+disabled by setting `AllowCastToVoid` option to `false`.
The check corresponds to a part of CERT C Coding Standard rule `ERR33-C.
Detect and handle standard library errors
clang-tools-extra/docs/clang-tidy/checks/misc/const-correctness.rst
@@ -80,9 +80,10 @@ This limitation affects the capability to add ``const`` to methods which is not
Options
-------
-.. option:: AnalyzeValues (default = true)
+.. option:: AnalyzeValues
- Enable or disable the analysis of ordinary value variables, like ``int i = 42;``
+ Enable or disable the analysis of ordinary value variables, like
+ ``int i = 42;``. Default is `true`.
.. code-block:: c++
@@ -96,9 +97,10 @@ Options
// No warning
int const a[] = {42, 42, 42};
-.. option:: AnalyzeReferences (default = true)
+.. option:: AnalyzeReferences
- Enable or disable the analysis of reference variables, like ``int &ref = i;``
+ Enable or disable the analysis of reference variables, like
+ ``int &ref = i;``. Default is `true`.
.. code-block:: c++
@@ -108,11 +110,11 @@ Options
// No warning
int const& ref = i;
-.. option:: WarnPointersAsValues (default = false)
+.. option:: WarnPointersAsValues
This option enables the suggestion for ``const`` of the pointer itself.
Pointer values have two possibilities to be ``const``, the pointer
- and the value pointing to.
+ and the value pointing to. Default is `false`.
.. code-block:: c++
@@ -123,9 +125,10 @@ Options
// No warning
const int *const pointer_variable = &value;
-.. option:: TransformValues (default = true)
+.. option:: TransformValues
- Provides fixit-hints for value types that automatically add ``const`` if its a single declaration.
+ Provides fixit-hints for value types that automatically add ``const`` if
+ its a single declaration. Default is `true`.
.. code-block:: c++
@@ -143,10 +146,10 @@ Options
int result = value * 3;
result -= 10;
-.. option:: TransformReferences (default = true)
+.. option:: TransformReferences
- Provides fixit-hints for reference types that automatically add ``const`` if its a single
- declaration.
+ Provides fixit-hints for reference types that automatically add ``const`` if
+ its a single declaration. Default is `true`.
.. code-block:: c++
@@ -163,10 +166,10 @@ Options
int result = ref_value * 3;
result -= 10;
-.. option:: TransformPointersAsValues (default = false)
+.. option:: TransformPointersAsValues
- Provides fixit-hints for pointers if their pointee is not changed. This does not analyze if the
- value-pointed-to is unchanged!
+ Provides fixit-hints for pointers if their pointee is not changed. This does
+ not analyze if the value-pointed-to is unchanged! Default is `false`.
Requires 'WarnPointersAsValues' to be 'true'.
@@ -196,3 +199,13 @@ Options
// The following pointer may not become a 'int *const'.
int *changing_pointee = &value;
changing_pointee = &result;
+
+.. option:: AllowedTypes
+
+ A semicolon-separated list of names of types that will be excluded from
+ const-correctness checking. Regular expressions are accepted, e.g.
+ ``[Rr]ef(erence)?$`` matches every type with suffix ``Ref``, ``ref``,
+ ``Reference`` and ``reference``. If a name in the list contains the sequence
+ `::`, it is matched against the qualified type name
+ (i.e. ``namespace::Type``), otherwise it is matched against only the type
+ name (i.e. ``Type``). Default is empty string.
clang-tools-extra/docs/clang-tidy/checks/misc/redundant-expression.rst
@@ -19,12 +19,14 @@ Examples:
.. code-block:: c++
- ((x+1) | (x+1)) // (x+1) is redundant
- (p->x == p->x) // always true
- (p->x < p->x) // always false
- (speed - speed + 1 == 12) // speed - speed is always zero
- int b = a | 4 | a // identical expr on both sides
- ((x=1) | (x=1)) // expression is identical
+ ((x+1) | (x+1)) // (x+1) is redundant
+ (p->x == p->x) // always true
+ (p->x < p->x) // always false
+ (speed - speed + 1 == 12) // speed - speed is always zero
+ int b = a | 4 | a // identical expr on both sides
+ ((x=1) | (x=1)) // expression is identical
+ (DEFINE_1 | DEFINE_1) // same macro on the both sides
+ ((DEF_1 + DEF_2) | (DEF_1+DEF_2)) // expressions differ in spaces only
Floats are handled except in the case that NaNs are checked like so:
clang-tools-extra/docs/clang-tidy/checks/modernize/loop-convert.rst
@@ -144,7 +144,7 @@ lives.
When set to true convert loops when in C++20 or later mode using
``std::ranges::reverse_view``.
- Default value is ``true``.
+ Default value is `true`.
.. option:: MakeReverseRangeFunction
clang-tools-extra/test/clang-doc/Inputs/basic-project/src/Circle.cpp
@@ -8,4 +8,5 @@ double Circle::area() const {
double Circle::perimeter() const {
return 3.141 * radius_;
-}
clang-tools-extra/test/clang-doc/basic-project.test
@@ -1,6 +1,6 @@
// RUN: rm -rf %t && mkdir -p %t/docs %t/build
// RUN: sed 's|$test_dir|%/S|g' %S/Inputs/basic-project/database_template.json > %t/build/compile_commands.json
-// RUN: clang-doc --format=html --output=%t/docs --executor=all-TUs %t/build/compile_commands.json
+// RUN: clang-doc --format=html --output=%t/docs --executor=all-TUs %t/build/compile_commands.json --repository=https://repository.com
// RUN: FileCheck %s -input-file=%t/docs/index_json.js -check-prefix=JSON-INDEX
// RUN: FileCheck %s -input-file=%t/docs/GlobalNamespace/Shape.html -check-prefix=HTML-SHAPE
// RUN: FileCheck %s -input-file=%t/docs/GlobalNamespace/Calculator.html -check-prefix=HTML-CALC
@@ -54,130 +54,183 @@
// JSON-INDEX-NEXT: };
// JSON-INDEX-NEXT: }
-// HTML-SHAPE: <h1>class Shape</h1>
-// HTML-SHAPE: <p>Defined at line 8 of file {{.*}}Shape.h</p>
-// HTML-SHAPE: <div>brief</div>
-// HTML-SHAPE: <p> Abstract base class for shapes.</p>
-// HTML-SHAPE: <p> Provides a common interface for different types of shapes.</p>
-// HTML-SHAPE: <h2 id="Functions">Functions</h2>
-// HTML-SHAPE: <h3 id="{{([0-9A-F]{40})}}">area</h3>
-// HTML-SHAPE: <p>public double area()</p>
-// HTML-SHAPE: <div>brief</div>
-// HTML-SHAPE: <p> Calculates the area of the shape.</p>
-// HTML-SHAPE: <h3 id="{{([0-9A-F]{40})}}">perimeter</h3>
-// HTML-SHAPE: <p>public double perimeter()</p>
-// HTML-SHAPE: <div>brief</div>
-// HTML-SHAPE: <p> Calculates the perimeter of the shape.</p>
-// HTML-SHAPE: <div>return</div>
-// HTML-SHAPE: <p> double The perimeter of the shape.</p>
-// HTML-SHAPE: <h3 id="{{([0-9A-F]{40})}}">~Shape</h3>
-// HTML-SHAPE: <p>public void ~Shape()</p>
-// HTML-SHAPE: <p>Defined at line 13 of file {{.*}}Shape.h</p>
-// HTML-SHAPE: <div>brief</div>
-// HTML-SHAPE: <p> Virtual destructor.</p>
+// HTML-SHAPE: <h1>class Shape</h1>
+// HTML-SHAPE-NEXT: <p>
+// HTML-SHAPE-NEXT: Defined at line
+// HTML-SHAPE-NEXT: <a href="https://repository.com/./include/Shape.h#8">8</a>
+// HTML-SHAPE-NEXT: of file
+// HTML-SHAPE-NEXT: <a href="https://repository.com/./include/Shape.h">Shape.h</a>
+// HTML-SHAPE-NEXT: </p>
+// HTML-SHAPE: <div>brief</div>
+// HTML-SHAPE: <p> Abstract base class for shapes.</p>
+// HTML-SHAPE: <p> Provides a common interface for different types of shapes.</p>
+// HTML-SHAPE: <h2 id="Functions">Functions</h2>
+// HTML-SHAPE: <h3 id="{{([0-9A-F]{40})}}">area</h3>
+// HTML-SHAPE: <p>public double area()</p>
+// HTML-SHAPE: <div>brief</div>
+// HTML-SHAPE: <p> Calculates the area of the shape.</p>
+// HTML-SHAPE: <h3 id="{{([0-9A-F]{40})}}">perimeter</h3>
+// HTML-SHAPE: <p>public double perimeter()</p>
+// HTML-SHAPE: <div>brief</div>
+// HTML-SHAPE: <p> Calculates the perimeter of the shape.</p>
+// HTML-SHAPE: <div>return</div>
+// HTML-SHAPE: <p> double The perimeter of the shape.</p>
+// HTML-SHAPE: <h3 id="{{([0-9A-F]{40})}}">~Shape</h3>
+// HTML-SHAPE: <p>public void ~Shape()</p>
+// HTML-SHAPE: Defined at line
+// HTML-SHAPE-NEXT: <a href="https://repository.com/./include/Shape.h#13">13</a>
+// HTML-SHAPE-NEXT: of file
+// HTML-SHAPE-NEXT: <a href="https://repository.com/./include/Shape.h">Shape.h</a>
+// HTML-SHAPE: <div>brief</div>
+// HTML-SHAPE: <p> Virtual destructor.</p>
-// HTML-CALC: <h1>class Calculator</h1>
-// HTML-CALC: <p>Defined at line 8 of file {{.*}}Calculator.h</p>
-// HTML-CALC: <div>brief</div>
-// HTML-CALC: <p> A simple calculator class.</p>
-// HTML-CALC: <p> Provides basic arithmetic operations.</p>
-// HTML-CALC: <h2 id="Functions">Functions</h2>
-// HTML-CALC: <h3 id="{{([0-9A-F]{40})}}">add</h3>
-// HTML-CALC: <p>public int add(int a, int b)</p>
-// HTML-CALC: <p>Defined at line 3 of file {{.*}}Calculator.cpp</p>
-// HTML-CALC: <div>brief</div>
-// HTML-CALC: <p> Adds two integers.</p>
-// HTML-CALC: <div>return</div>
-// HTML-CALC: <p> int The sum of a and b.</p>
-// HTML-CALC: <h3 id="{{([0-9A-F]{40})}}">subtract</h3>
-// HTML-CALC: <p>public int subtract(int a, int b)</p>
-// HTML-CALC: <p>Defined at line 7 of file {{.*}}Calculator.cpp</p>
-// HTML-CALC: <div>brief</div>
-// HTML-CALC: <p> Subtracts the second integer from the first.</p>
-// HTML-CALC: <div>return</div>
-// HTML-CALC: <p> int The result of a - b.</p>
-// HTML-CALC: <h3 id="{{([0-9A-F]{40})}}">multiply</h3>
-// HTML-CALC: <p>public int multiply(int a, int b)</p>
-// HTML-CALC: <p>Defined at line 11 of file {{.*}}Calculator.cpp</p>
-// HTML-CALC: <div>brief</div>
-// HTML-CALC: <p> Multiplies two integers.</p>
-// HTML-CALC: <div>return</div>
-// HTML-CALC: <p> int The product of a and b.</p>
-// HTML-CALC: <h3 id="{{([0-9A-F]{40})}}">divide</h3>
-// HTML-CALC: <p>public double divide(int a, int b)</p>
-// HTML-CALC: <p>Defined at line 15 of file {{.*}}Calculator.cpp</p>
-// HTML-CALC: <div>brief</div>
-// HTML-CALC: <p> Divides the first integer by the second.</p>
-// HTML-CALC: <div>return</div>
-// HTML-CALC: <p> double The result of a / b.</p>
-// HTML-CALC: <div>throw</div>
-// HTML-CALC: <p>if b is zero.</p>
+// HTML-CALC: <h1>class Calculator</h1>
+// HTML-CALC-NEXT: <p>
+// HTML-CALC-NEXT: Defined at line
+// HTML-CALC-NEXT: <a href="https://repository.com/./include/Calculator.h#8">8</a>
+// HTML-CALC-NEXT: of file
+// HTML-CALC-NEXT: <a href="https://repository.com/./include/Calculator.h">Calculator.h</a>
+// HTML-CALC-NEXT: </p>
+// HTML-CALC: <div>brief</div>
+// HTML-CALC: <p> A simple calculator class.</p>
+// HTML-CALC: <p> Provides basic arithmetic operations.</p>
+// HTML-CALC: <h2 id="Functions">Functions</h2>
+// HTML-CALC: <h3 id="{{([0-9A-F]{40})}}">add</h3>
+// HTML-CALC: <p>public int add(int a, int b)</p>
+// HTML-CALC: Defined at line
+// HTML-CALC-NEXT: <a href="https://repository.com/./src/Calculator.cpp#3">3</a>
+// HTML-CALC-NEXT: of file
+// HTML-CALC-NEXT: <a href="https://repository.com/./src/Calculator.cpp">Calculator.cpp</a>
+// HTML-CALC: <div>brief</div>
+// HTML-CALC: <p> Adds two integers.</p>
+// HTML-CALC: <div>return</div>
+// HTML-CALC: <p> int The sum of a and b.</p>
+// HTML-CALC: <h3 id="{{([0-9A-F]{40})}}">subtract</h3>
+// HTML-CALC: <p>public int subtract(int a, int b)</p>
+// HTML-CALC: Defined at line
+// HTML-CALC-NEXT: <a href="https://repository.com/./src/Calculator.cpp#7">7</a>
+// HTML-CALC-NEXT: of file
+// HTML-CALC-NEXT: <a href="https://repository.com/./src/Calculator.cpp">Calculator.cpp</a>
+// HTML-CALC: <div>brief</div>
+// HTML-CALC: <p> Subtracts the second integer from the first.</p>
+// HTML-CALC: <div>return</div>
+// HTML-CALC: <p> int The result of a - b.</p>
+// HTML-CALC: <h3 id="{{([0-9A-F]{40})}}">multiply</h3>
+// HTML-CALC: <p>public int multiply(int a, int b)</p>
+// HTML-CALC: Defined at line
+// HTML-CALC-NEXT: <a href="https://repository.com/./src/Calculator.cpp#11">11</a>
+// HTML-CALC-NEXT: of file
+// HTML-CALC-NEXT: <a href="https://repository.com/./src/Calculator.cpp">Calculator.cpp</a>
+// HTML-CALC: <div>brief</div>
+// HTML-CALC: <p> Multiplies two integers.</p>
+// HTML-CALC: <div>return</div>
+// HTML-CALC: <p> int The product of a and b.</p>
+// HTML-CALC: <h3 id="{{([0-9A-F]{40})}}">divide</h3>
+// HTML-CALC: <p>public double divide(int a, int b)</p>
+// HTML-CALC: Defined at line
+// HTML-CALC-NEXT: <a href="https://repository.com/./src/Calculator.cpp#15">15</a>
+// HTML-CALC-NEXT: of file
+// HTML-CALC-NEXT: <a href="https://repository.com/./src/Calculator.cpp">Calculator.cpp</a>
+// HTML-CALC: <div>brief</div>
+// HTML-CALC: <p> Divides the first integer by the second.</p>
+// HTML-CALC: <div>return</div>
+// HTML-CALC: <p> double The result of a / b.</p>
+// HTML-CALC: <div>throw</div>
+// HTML-CALC: <p>if b is zero.</p>
-// HTML-RECTANGLE: <h1>class Rectangle</h1>
-// HTML-RECTANGLE: <p>Defined at line 10 of file {{.*}}Rectangle.h</p>
-// HTML-RECTANGLE: <p> Represents a rectangle with a given width and height.</p
-// HTML-RECTANGLE: <p>
-// HTML-RECTANGLE: Inherits from
-// HTML-RECTANGLE: <a href="Shape.html">Shape</a>
-// HTML-RECTANGLE: </p>
-// HTML-RECTANGLE: <h2 id="Members">Members</h2>
-// HTML-RECTANGLE: <p> Width of the rectangle.</p>
-// HTML-RECTANGLE: <div>private double width_</div>
-// HTML-RECTANGLE: <p> Height of the rectangle.</p>
-// HTML-RECTANGLE: <div>private double height_</div>
-// HTML-RECTANGLE: <h2 id="Functions">Functions</h2>
-// HTML-RECTANGLE: <h3 id="{{([0-9A-F]{40})}}">Rectangle</h3>
-// HTML-RECTANGLE: <p>public void Rectangle(double width, double height)</p>
-// HTML-RECTANGLE: <p>Defined at line 3 of file {{.*}}Rectangle.cpp</p>
-// HTML-RECTANGLE: <div>brief</div>
-// HTML-RECTANGLE: <p> Constructs a new Rectangle object.</p>
-// HTML-RECTANGLE: <h3 id="{{([0-9A-F]{40})}}">area</h3>
-// HTML-RECTANGLE: <p>public double area()</p>
-// HTML-RECTANGLE: <p>Defined at line 6 of file {{.*}}Rectangle.cpp</p>
-// HTML-RECTANGLE: <div>brief</div>
-// HTML-RECTANGLE: <p> Calculates the area of the rectangle.</p>
-// HTML-RECTANGLE: <div>return</div>
-// HTML-RECTANGLE: <p> double The area of the rectangle.</p>
-// HTML-RECTANGLE: <h3 id="{{([0-9A-F]{40})}}">perimeter</h3>
-// HTML-RECTANGLE: <p>public double perimeter()</p>
-// HTML-RECTANGLE: <p>Defined at line 10 of file {{.*}}Rectangle.cpp</p>
-// HTML-RECTANGLE: <div>brief</div>
-// HTML-RECTANGLE: <p> Calculates the perimeter of the rectangle.</p>
-// HTML-RECTANGLE: <div>return</div>
-// HTML-RECTANGLE: <p> double The perimeter of the rectangle.</p>
+// HTML-RECTANGLE: <h1>class Rectangle</h1>
+// HTML-RECTANGLE-NEXT: <p>
+// HTML-RECTANGLE-NEXT: Defined at line
+// HTML-RECTANGLE-NEXT: <a href="https://repository.com/./include/Rectangle.h#10">10</a>
+// HTML-RECTANGLE-NEXT: of file
+// HTML-RECTANGLE-NEXT: <a href="https://repository.com/./include/Rectangle.h">Rectangle.h</a>
+// HTML-RECTANGLE-NEXT: </p>
+// HTML-RECTANGLE: <p> Represents a rectangle with a given width and height.</p>
+// HTML-RECTANGLE: <p>
+// HTML-RECTANGLE: Inherits from
+// HTML-RECTANGLE: <a href="Shape.html">Shape</a>
+// HTML-RECTANGLE: </p>
+// HTML-RECTANGLE: <h2 id="Members">Members</h2>
+// HTML-RECTANGLE: <p> Width of the rectangle.</p>
+// HTML-RECTANGLE: <div>private double width_</div>
+// HTML-RECTANGLE: <p> Height of the rectangle.</p>
+// HTML-RECTANGLE: <div>private double height_</div>
+// HTML-RECTANGLE: <h2 id="Functions">Functions</h2>
+// HTML-RECTANGLE: <h3 id="{{([0-9A-F]{40})}}">Rectangle</h3>
+// HTML-RECTANGLE: <p>public void Rectangle(double width, double height)</p>
+// HTML-RECTANGLE: Defined at line
+// HTML-RECTANGLE-NEXT: <a href="https://repository.com/./src/Rectangle.cpp#3">3</a>
+// HTML-RECTANGLE-NEXT: of file
+// HTML-RECTANGLE-NEXT: <a href="https://repository.com/./src/Rectangle.cpp">Rectangle.cpp</a>
+// HTML-RECTANGLE: <div>brief</div>
+// HTML-RECTANGLE: <p> Constructs a new Rectangle object.</p>
+// HTML-RECTANGLE: <h3 id="{{([0-9A-F]{40})}}">area</h3>
+// HTML-RECTANGLE: <p>public double area()</p>
+// HTML-RECTANGLE: Defined at line
+// HTML-RECTANGLE-NEXT: <a href="https://repository.com/./src/Rectangle.cpp#6">6</a>
+// HTML-RECTANGLE-NEXT: of file
+// HTML-RECTANGLE-NEXT: <a href="https://repository.com/./src/Rectangle.cpp">Rectangle.cpp</a>
+// HTML-RECTANGLE: <div>brief</div>
+// HTML-RECTANGLE: <p> Calculates the area of the rectangle.</p>
+// HTML-RECTANGLE: <div>return</div>
+// HTML-RECTANGLE: <p> double The area of the rectangle.</p>
+// HTML-RECTANGLE: <h3 id="{{([0-9A-F]{40})}}">perimeter</h3>
+// HTML-RECTANGLE: <p>public double perimeter()</p>
+// HTML-RECTANGLE: Defined at line
+// HTML-RECTANGLE-NEXT: <a href="https://repository.com/./src/Rectangle.cpp#10">10</a>
+// HTML-RECTANGLE-NEXT: of file
+// HTML-RECTANGLE-NEXT: <a href="https://repository.com/./src/Rectangle.cpp">Rectangle.cpp</a>
+// HTML-RECTANGLE: <div>brief</div>
+// HTML-RECTANGLE: <p> Calculates the perimeter of the rectangle.</p>
+// HTML-RECTANGLE: <div>return</div>
+// HTML-RECTANGLE: <p> double The perimeter of the rectangle.</p>
-// HTML-CIRCLE: <h1>class Circle</h1>
-// HTML-CIRCLE: <p>Defined at line 10 of file {{.*}}Circle.h</p>
-// HTML-CIRCLE: <div>brief</div>
-// HTML-CIRCLE: <p> Circle class derived from Shape.</p>
-// HTML-CIRCLE: <p> Represents a circle with a given radius.</p>
-// HTML-CIRCLE: <p>
-// HTML-CIRCLE: Inherits from
-// HTML-CIRCLE: <a href="Shape.html">Shape</a>
-// HTML-CIRCLE: </p>
-// HTML-CIRCLE: <h2 id="Members">Members</h2>
-// HTML-CIRCLE: <p> Radius of the circle.</p>
-// HTML-CIRCLE: <div>private double radius_</div>
-// HTML-CIRCLE: <h2 id="Functions">Functions</h2>
-// HTML-CIRCLE: <h3 id="{{([0-9A-F]{40})}}">Circle</h3>
-// HTML-CIRCLE: <p>public void Circle(double radius)</p>
-// HTML-CIRCLE: <p>Defined at line 3 of file {{.*}}Circle.cpp</p>
-// HTML-CIRCLE: <div>brief</div>
-// HTML-CIRCLE: <p> Constructs a new Circle object.</p>
-// HTML-CIRCLE: <h3 id="{{([0-9A-F]{40})}}">area</h3>
-// HTML-CIRCLE: <p>public double area()</p>
-// HTML-CIRCLE: <p>Defined at line 5 of file {{.*}}Circle.cpp</p>
-// HTML-CIRCLE: <div>brief</div>
-// HTML-CIRCLE: <p> Calculates the area of the circle.</p>
-// HTML-CIRCLE: <div>return</div>
-// HTML-CIRCLE: <p> double The area of the circle.</p>
-// HTML-CIRCLE: <h3 id="{{([0-9A-F]{40})}}">perimeter</h3>
-// HTML-CIRCLE: <p>public double perimeter()</p>
-// HTML-CIRCLE: <p>Defined at line 9 of file {{.*}}Circle.cpp</p>
-// HTML-CIRCLE: <div>brief</div>
-// HTML-CIRCLE: <p> Calculates the perimeter of the circle.</p>
-// HTML-CIRCLE: <div>return</div>
-// HTML-CIRCLE: <p> double The perimeter of the circle.</p>
+// HTML-CIRCLE: <h1>class Circle</h1>
+// HTML-CIRCLE-NEXT: <p>
+// HTML-CIRCLE-NEXT: Defined at line
+// HTML-CIRCLE-NEXT: <a href="https://repository.com/./include/Circle.h#10">10</a>
+// HTML-CIRCLE-NEXT: of file
+// HTML-CIRCLE-NEXT: <a href="https://repository.com/./include/Circle.h">Circle.h</a>
+// HTML-CIRCLE-NEXT: </p>
+// HTML-CIRCLE: <div>brief</div>
+// HTML-CIRCLE: <p> Circle class derived from Shape.</p>
+// HTML-CIRCLE: <p> Represents a circle with a given radius.</p>
+// HTML-CIRCLE: <p>
+// HTML-CIRCLE: Inherits from
+// HTML-CIRCLE: <a href="Shape.html">Shape</a>
+// HTML-CIRCLE: </p>
+// HTML-CIRCLE: <h2 id="Members">Members</h2>
+// HTML-CIRCLE: <p> Radius of the circle.</p>
+// HTML-CIRCLE: <div>private double radius_</div>
+// HTML-CIRCLE: <h2 id="Functions">Functions</h2>
+// HTML-CIRCLE: <h3 id="{{([0-9A-F]{40})}}">Circle</h3>
+// HTML-CIRCLE: <p>public void Circle(double radius)</p>
+// HTML-CIRCLE: Defined at line
+// HTML-CIRCLE-NEXT: <a href="https://repository.com/./src/Circle.cpp#3">3</a>
+// HTML-CIRCLE-NEXT: of file
+// HTML-CIRCLE-NEXT: <a href="https://repository.com/./src/Circle.cpp">Circle.cpp</a>
+// HTML-CIRCLE: <div>brief</div>
+// HTML-CIRCLE: <p> Constructs a new Circle object.</p>
+// HTML-CIRCLE: <h3 id="{{([0-9A-F]{40})}}">area</h3>
+// HTML-CIRCLE: <p>public double area()</p>
+// HTML-CIRCLE: Defined at line
+// HTML-CIRCLE-NEXT: <a href="https://repository.com/./src/Circle.cpp#5">5</a>
+// HTML-CIRCLE-NEXT: of file
+// HTML-CIRCLE-NEXT: <a href="https://repository.com/./src/Circle.cpp">Circle.cpp</a>
+// HTML-CIRCLE: <div>brief</div>
+// HTML-CIRCLE: <p> Calculates the area of the circle.</p>
+// HTML-CIRCLE: <div>return</div>
+// HTML-CIRCLE: <p> double The area of the circle.</p>
+// HTML-CIRCLE: <h3 id="{{([0-9A-F]{40})}}">perimeter</h3>
+// HTML-CIRCLE: <p>public double perimeter()</p>
+// HTML-CIRCLE: Defined at line
+// HTML-CIRCLE-NEXT: <a href="https://repository.com/./src/Circle.cpp#9">9</a>
+// HTML-CIRCLE-NEXT: of file
+// HTML-CIRCLE-NEXT: <a href="https://repository.com/./src/Circle.cpp">Circle.cpp</a>
+// HTML-CIRCLE: <div>brief</div>
+// HTML-CIRCLE: <p> Calculates the perimeter of the circle.</p>
+// HTML-CIRCLE: <div>return</div>
+// HTML-CIRCLE: <p> double The perimeter of the circle.</p>
// MD-CALC: # class Calculator
// MD-CALC: *Defined at .{{[\/]}}include{{[\/]}}Calculator.h#8*
@@ -286,4 +339,4 @@
// MD-ALL-FILES: ## [GlobalNamespace](GlobalNamespace{{[\/]}}index.md)
// MD-INDEX: # C/C++ Reference
-// MD-INDEX: * Namespace: [GlobalNamespace](GlobalNamespace)
clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/absl/strings/internal-file.h
@@ -10,7 +10,7 @@ std::string StringsFunction(std::string s1) { return s1; }
class SomeContainer {};
namespace strings_internal {
void InternalFunction() {}
-template <class P> P InternalTemplateFunction(P a) {}
+template <class P> void InternalTemplateFunction(P a) { int; }
} // namespace strings_internal
namespace container_internal {
clang-tools-extra/test/clang-tidy/checkers/boost/use-to-string.cpp
@@ -18,7 +18,7 @@ T lexical_cast(const V &) {
struct my_weird_type {};
-std::string fun(const std::string &) {}
+std::string fun(const std::string &) { return {}; }
void test_to_string1() {
@@ -75,7 +75,7 @@ void test_to_string2() {
fun(boost::lexical_cast<std::string>(j));
}
-std::string fun(const std::wstring &) {}
+std::string fun(const std::wstring &);
void test_to_wstring() {
int a;
clang-tools-extra/test/clang-tidy/checkers/bugprone/chained-comparison.c
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy %s bugprone-chained-comparison %t
+// RUN: %check_clang_tidy --extra-arg=-Wno-error=parentheses %s bugprone-chained-comparison %t
void badly_chained_1(int x, int y, int z)
{
clang-tools-extra/test/clang-tidy/checkers/bugprone/chained-comparison.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-chained-comparison %t
+// RUN: %check_clang_tidy -std=c++98-or-later --extra-arg=-Wno-error=parentheses %s bugprone-chained-comparison %t
void badly_chained_1(int x, int y, int z)
{
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-coro.cpp
@@ -1,5 +1,5 @@
// RUN: %check_clang_tidy -std=c++20 %s bugprone-exception-escape %t -- \
-// RUN: -- -fexceptions
+// RUN: -- -fexceptions -Wno-error=return-type
namespace std {
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape-rethrow.cpp
@@ -20,6 +20,7 @@ int throwsAndCallsRethrower() noexcept {
} catch(...) {
rethrower();
}
+ return 1;
}
int throwsAndCallsCallsRethrower() noexcept {
@@ -29,6 +30,7 @@ int throwsAndCallsCallsRethrower() noexcept {
} catch(...) {
callsRethrower();
}
+ return 1;
}
void rethrowerNoexcept() noexcept {
clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -665,6 +665,7 @@ int indirectly_recursive(int n) noexcept;
int recursion_helper(int n) {
indirectly_recursive(n);
+ return 0;
}
int indirectly_recursive(int n) noexcept {
clang-tools-extra/test/clang-tidy/checkers/bugprone/fold-init-type.cpp
@@ -8,24 +8,25 @@ T accumulate(InputIt first, InputIt last, T init) {
// is instantiated. In practice this happens somewhere in the implementation
// of `accumulate`. For tests, do it here.
(void)*first;
+ return init;
}
template <class InputIt, class T>
-T reduce(InputIt first, InputIt last, T init) { (void)*first; }
+T reduce(InputIt first, InputIt last, T init) { (void)*first; return init; }
template <class ExecutionPolicy, class InputIt, class T>
T reduce(ExecutionPolicy &&policy,
- InputIt first, InputIt last, T init) { (void)*first; }
+ InputIt first, InputIt last, T init) { (void)*first; return init; }
struct parallel_execution_policy {};
constexpr parallel_execution_policy par{};
template <class InputIt1, class InputIt2, class T>
T inner_product(InputIt1 first1, InputIt1 last1,
- InputIt2 first2, T value) { (void)*first1; (void)*first2; }
+ InputIt2 first2, T value) { (void)*first1; (void)*first2; return value; }
template <class ExecutionPolicy, class InputIt1, class InputIt2, class T>
T inner_product(ExecutionPolicy &&policy, InputIt1 first1, InputIt1 last1,
- InputIt2 first2, T value) { (void)*first1; (void)*first2; }
+ InputIt2 first2, T value) { (void)*first1; (void)*first2; return value; }
} // namespace std
clang-tools-extra/test/clang-tidy/checkers/bugprone/inc-dec-in-conditions-bitint-no-crash.c
@@ -5,5 +5,6 @@ _BitInt(8) v_401_0() {
_BitInt(5) y = 0;
16777215wb ?: ++y;
});
+ return 0;
}
-// CHECK-MESSAGES: warning
+// CHECK-MESSAGES: warning
clang-tools-extra/test/clang-tidy/checkers/bugprone/spuriously-wake-up-functions.c
@@ -12,9 +12,9 @@ typedef struct cnd_t {
} cnd_t;
struct timespec {};
-int cnd_wait(cnd_t *cond, mtx_t *mutex){};
+int cnd_wait(cnd_t *cond, mtx_t *mutex){ return 0; };
int cnd_timedwait(cnd_t *cond, mtx_t *mutex,
- const struct timespec *time_point){};
+ const struct timespec *time_point){ return 0; };
struct Node1 list_c;
static mtx_t lock;
clang-tools-extra/test/clang-tidy/checkers/bugprone/spuriously-wake-up-functions.cpp
@@ -90,18 +90,18 @@ public:
void wait(unique_lock<mutex> &lock, Predicate pred);
template <class Clock, class Duration>
cv_status wait_until(unique_lock<mutex> &lock,
- const chrono::time_point<Clock, Duration> &abs_time){};
+ const chrono::time_point<Clock, Duration> &abs_time){ return cv_status::no_timeout; };
template <class Clock, class Duration, class Predicate>
bool wait_until(unique_lock<mutex> &lock,
const chrono::time_point<Clock, Duration> &abs_time,
- Predicate pred){};
+ Predicate pred){ return false; };
template <class Rep, class Period>
cv_status wait_for(unique_lock<mutex> &lock,
- const chrono::duration<Rep, Period> &rel_time){};
+ const chrono::duration<Rep, Period> &rel_time){ return cv_status::no_timeout; };
template <class Rep, class Period, class Predicate>
bool wait_for(unique_lock<mutex> &lock,
const chrono::duration<Rep, Period> &rel_time,
- Predicate pred){};
+ Predicate pred){ return false; };
};
} // namespace std
clang-tools-extra/test/clang-tidy/checkers/bugprone/string-constructor.cpp
@@ -11,6 +11,7 @@ struct basic_string {
basic_string(const C*, unsigned int size);
basic_string(const C *, const A &allocator = A());
basic_string(unsigned int size, C c);
+ basic_string(const C*, unsigned int pos, unsigned int size);
};
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
@@ -61,6 +62,21 @@ void Test() {
// CHECK-MESSAGES: [[@LINE-1]]:15: warning: constructing string from nullptr is undefined behaviour
std::string q7 = 0;
// CHECK-MESSAGES: [[@LINE-1]]:20: warning: constructing string from nullptr is undefined behaviour
+
+ std::string r1("test", 1, 0);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: constructor creating an empty string
+ std::string r2("test", 0, -4);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: negative value used as length parameter
+ std::string r3("test", -4, 1);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: negative value used as position of the first character parameter
+ std::string r4("test", 0, 0x1000000);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: suspicious large length parameter
+ std::string r5("test", 0, 5);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: length is bigger than string literal size
+ std::string r6("test", 3, 2);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: length is bigger than remaining string literal size
+ std::string r7("test", 4, 1);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: position of the first character parameter is bigger than string literal character range
}
void TestView() {
@@ -82,6 +98,17 @@ void TestView() {
// CHECK-MESSAGES: [[@LINE-1]]:25: warning: constructing string from nullptr is undefined behaviour
}
+void TestUnsignedArguments() {
+ std::string s0("test", 0u);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: constructor creating an empty string
+ std::string s1(0x1000000ull, 'x');
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: suspicious large length parameter
+ std::string s2("test", 3ull, 2u);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: length is bigger than remaining string literal size
+ std::string s3("test", 0u, 5ll);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: length is bigger than string literal size
+}
+
std::string StringFromZero() {
return 0;
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: constructing string from nullptr is undefined behaviour
@@ -101,6 +128,9 @@ void Valid() {
std::string s3("test");
std::string s4("test\000", 5);
std::string s6("te" "st", 4);
+ std::string s7("test", 0, 4);
+ std::string s8("test", 3, 1);
+ std::string s9("te" "st", 1, 2);
std::string_view emptyv();
std::string_view sv1("test", 4);
clang-tools-extra/test/clang-tidy/checkers/bugprone/stringview-nullptr.cpp
@@ -27,7 +27,7 @@ public:
constexpr basic_string_view(const basic_string_view &) {}
- constexpr basic_string_view &operator=(const basic_string_view &) {}
+ constexpr basic_string_view &operator=(const basic_string_view &) { return *this; }
};
template <typename CharT>
clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-string-compare.cpp
@@ -89,6 +89,8 @@ int test_warning_patterns() {
if (strcmp(A, "a") < 0.)
return 0;
// CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' has suspicious implicit cast
+
+ return 1;
}
int test_valid_patterns() {
clang-tools-extra/test/clang-tidy/checkers/fuchsia/default-arguments-calls.cpp
@@ -2,7 +2,7 @@
int foo(int value = 5) { return value; }
-int f() {
+void f() {
foo();
// CHECK-NOTES: [[@LINE-1]]:3: warning: calling a function that uses a default argument is disallowed [fuchsia-default-arguments-calls]
// CHECK-NOTES: [[@LINE-5]]:9: note: default parameter was declared here
@@ -10,7 +10,7 @@ int f() {
int bar(int value) { return value; }
-int n() {
+void n() {
foo(0);
bar(0);
}
clang-tools-extra/test/clang-tidy/checkers/fuchsia/multiple-inheritance.cpp
@@ -144,7 +144,7 @@ struct WithTemplBase : T {
WithTemplBase();
};
-int test_no_crash() {
+void test_no_crash() {
auto foo = []() {};
WithTemplBase<decltype(foo)>();
}
clang-tools-extra/test/clang-tidy/checkers/google/runtime-int-std.cpp
@@ -54,4 +54,5 @@ short bar(const short, unsigned short) {
tmpl<short>();
// CHECK-MESSAGES: [[@LINE-1]]:8: warning: consider replacing 'short' with 'std::int16_t'
+ return 0;
}
clang-tools-extra/test/clang-tidy/checkers/google/upgrade-googletest-case.cpp
@@ -221,9 +221,9 @@ public:
// CHECK-FIXES: const char *test_suite_name() const;
};
-const char *FooTestInfo::test_case_name() const {}
+const char *FooTestInfo::test_case_name() const { return nullptr; }
// CHECK-MESSAGES: [[@LINE-1]]:26: warning: Google Test APIs named with 'case'
-// CHECK-FIXES: const char *FooTestInfo::test_suite_name() const {}
+// CHECK-FIXES: const char *FooTestInfo::test_suite_name() const { return nullptr; }
class BarTestInfo : public testing::TestInfo {
public:
@@ -491,26 +491,26 @@ public:
// CHECK-FIXES: const testing::TestSuite *GetTestSuite(int) const;
};
-testing::TestCase *FooUnitTest::current_test_case() const {}
+testing::TestCase *FooUnitTest::current_test_case() const { return nullptr; }
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: Google Test APIs named with 'case'
// CHECK-MESSAGES: [[@LINE-2]]:33: warning: Google Test APIs named with 'case'
-// CHECK-FIXES: testing::TestSuite *FooUnitTest::current_test_suite() const {}
-int FooUnitTest::successful_test_case_count() const {}
+// CHECK-FIXES: testing::TestSuite *FooUnitTest::current_test_suite() const { return nullptr; }
+int FooUnitTest::successful_test_case_count() const { return 0; }
// CHECK-MESSAGES: [[@LINE-1]]:18: warning: Google Test APIs named with 'case'
-// CHECK-FIXES: int FooUnitTest::successful_test_suite_count() const {}
-int FooUnitTest::failed_test_case_count() const {}
+// CHECK-FIXES: int FooUnitTest::successful_test_suite_count() const { return 0; }
+int FooUnitTest::failed_test_case_count() const { return 0; }
// CHECK-MESSAGES: [[@LINE-1]]:18: warning: Google Test APIs named with 'case'
-// CHECK-FIXES: int FooUnitTest::failed_test_suite_count() const {}
-int FooUnitTest::total_test_case_count() const {}
+// CHECK-FIXES: int FooUnitTest::failed_test_suite_count() const { return 0; }
+int FooUnitTest::total_test_case_count() const { return 0; }
// CHECK-MESSAGES: [[@LINE-1]]:18: warning: Google Test APIs named with 'case'
-// CHECK-FIXES: int FooUnitTest::total_test_suite_count() const {}
-int FooUnitTest::test_case_to_run_count() const {}
+// CHECK-FIXES: int FooUnitTest::total_test_suite_count() const { return 0; }
+int FooUnitTest::test_case_to_run_count() const { return 0; }
// CHECK-MESSAGES: [[@LINE-1]]:18: warning: Google Test APIs named with 'case'
-// CHECK-FIXES: int FooUnitTest::test_suite_to_run_count() const {}
-const testing::TestCase *FooUnitTest::GetTestCase(int) const {}
+// CHECK-FIXES: int FooUnitTest::test_suite_to_run_count() const { return 0; }
+const testing::TestCase *FooUnitTest::GetTestCase(int) const { return 0; }
// CHECK-MESSAGES: [[@LINE-1]]:16: warning: Google Test APIs named with 'case'
// CHECK-MESSAGES: [[@LINE-2]]:39: warning: Google Test APIs named with 'case'
-// CHECK-FIXES: const testing::TestSuite *FooUnitTest::GetTestSuite(int) const {}
+// CHECK-FIXES: const testing::TestSuite *FooUnitTest::GetTestSuite(int) const { return 0; }
// Type derived from testing::TestCase
class BarUnitTest : public testing::UnitTest {
clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-allowed-types.cpp
@@ -0,0 +1,180 @@
+// RUN: %check_clang_tidy %s misc-const-correctness %t -- \
+// RUN: -config="{CheckOptions: {\
+// RUN: misc-const-correctness.AllowedTypes: '[Pp]ointer$;[Pp]tr$;[Rr]ef(erence)?$;qualified::Type;::fully::QualifiedType;ConstTemplate', \
+// RUN: misc-const-correctness.TransformPointersAsValues: true, \
+// RUN: misc-const-correctness.TransformReferences: true, \
+// RUN: misc-const-correctness.WarnPointersAsValues: true } \
+// RUN: }" -- -fno-delayed-template-parsing
+
+struct SmartPointer {
+};
+
+struct smart_pointer {
+};
+
+struct SmartPtr {
+};
+
+struct smart_ptr {
+};
+
+struct SmartReference {
+};
+
+struct smart_reference {
+};
+
+struct SmartRef {
+};
+
+struct smart_ref {
+};
+
+struct OtherType {
+};
+
+template <typename T> struct ConstTemplate {
+};
+
+namespace qualified {
+struct Type {
+};
+} // namespace qualified
+
+namespace fully {
+struct QualifiedType {
+};
+} // namespace fully
+
+void negativeSmartPointer() {
+ SmartPointer p1 = {};
+ SmartPointer* p2 = {};
+ SmartPointer& p3 = p1;
+}
+
+void negative_smart_pointer() {
+ smart_pointer p1 = {};
+ smart_pointer* p2 = {};
+ smart_pointer& p3 = p1;
+}
+
+void negativeSmartPtr() {
+ SmartPtr p1 = {};
+ SmartPtr* p2 = {};
+ SmartPtr& p3 = p1;
+}
+
+void negative_smart_ptr() {
+ smart_ptr p1 = {};
+ smart_ptr* p2 = {};
+ smart_ptr& p3 = p1;
+}
+
+void negativeSmartReference() {
+ SmartReference p1 = {};
+ SmartReference* p2 = {};
+ SmartReference& p3 = p1;
+}
+
+void negative_smart_reference() {
+ smart_reference p1 = {};
+ smart_reference* p2 = {};
+ smart_reference& p3 = p1;
+}
+
+void negativeSmartRef() {
+ SmartRef p1 = {};
+ SmartRef* p2 = {};
+ SmartRef& p3 = p1;
+}
+
+void negative_smart_ref() {
+ smart_ref p1 = {};
+ smart_ref* p2 = {};
+ smart_ref& p3 = p1;
+}
+
+void positiveOtherType() {
+ OtherType t = {};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 't' of type 'OtherType' can be declared 'const'
+ // CHECK-FIXES: OtherType const t = {};
+}
+
+void negativeSomeComplex() {
+ ConstTemplate<int> t1 = {};
+ ConstTemplate<int>* t2 = {};
+ ConstTemplate<int>& t3 = t1;
+}
+
+void negativeQualified() {
+ qualified::Type t1 = {};
+ qualified::Type* t2 = {};
+ qualified::Type& t3 = t1;
+
+ using qualified::Type;
+ Type t4 = {};
+ Type* t5 = {};
+ Type& t6 = t4;
+}
+
+void negativeFullyQualified() {
+ fully::QualifiedType t1 = {};
+ fully::QualifiedType* t2 = {};
+ fully::QualifiedType& t3 = t1;
+
+ using fully::QualifiedType;
+ QualifiedType t4 = {};
+ QualifiedType* t5 = {};
+ QualifiedType& t6 = t4;
+}
+
+using MySP = SmartPointer;
+using MyTemplate = ConstTemplate<int>;
+template <typename T> using MyTemplate2 = ConstTemplate<T>;
+
+void positiveTypedefs() {
+ MySP p1 = {};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p1' of type 'MySP' (aka 'SmartPointer') can be declared 'const'
+ // CHECK-FIXES: MySP const p1 = {};
+
+ MySP* p2 = {};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p2' of type 'MySP *' (aka 'SmartPointer *') can be declared 'const'
+ // CHECK-FIXES: MySP* const p2 = {};
+
+ MySP& p3 = p1;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p3' of type 'MySP &' (aka 'SmartPointer &') can be declared 'const'
+ // CHECK-FIXES: MySP const& p3 = p1;
+
+ MyTemplate t1 = {};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 't1' of type 'MyTemplate' (aka 'ConstTemplate<int>') can be declared 'const'
+ // CHECK-FIXES: MyTemplate const t1 = {};
+
+ MyTemplate* t2 = {};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 't2' of type 'MyTemplate *' (aka 'ConstTemplate<int> *') can be declared 'const'
+ // CHECK-FIXES: MyTemplate* const t2 = {};
+
+ MyTemplate& t3 = t1;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 't3' of type 'MyTemplate &' (aka 'ConstTemplate<int> &') can be declared 'const'
+ // CHECK-FIXES: MyTemplate const& t3 = t1;
+
+ MyTemplate2<int> t4 = {};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 't4' of type 'MyTemplate2<int>' (aka 'ConstTemplate<int>') can be declared 'const'
+ // CHECK-FIXES: MyTemplate2<int> const t4 = {};
+
+ MyTemplate2<int>* t5 = {};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 't5' of type 'MyTemplate2<int> *' (aka 'ConstTemplate<int> *') can be declared 'const'
+ // CHECK-FIXES: MyTemplate2<int>* const t5 = {};
+
+ MyTemplate2<int>& t6 = t4;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 't6' of type 'MyTemplate2<int> &' (aka 'ConstTemplate<int> &') can be declared 'const'
+ // CHECK-FIXES: MyTemplate2<int> const& t6 = t4;
+}
+
+template <typename T>
+class Vector {};
+
+void positiveSmartPtrWrapped() {
+ Vector<SmartPtr> vec = {};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'vec' of type 'Vector<SmartPtr>' can be declared 'const'
+ // CHECK-FIXES: Vector<SmartPtr> const vec = {};
+}
clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-transform-values.cpp
@@ -54,8 +54,8 @@ void template_instantiation() {
struct ConstNonConstClass {
ConstNonConstClass();
ConstNonConstClass(double &np_local0);
- double nonConstMethod() {}
- double constMethod() const {}
+ double nonConstMethod() { return 0; }
+ double constMethod() const { return 0; }
double modifyingMethod(double &np_arg0) const;
double NonConstMember;
clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp
@@ -283,8 +283,8 @@ void template_instantiation() {
struct ConstNonConstClass {
ConstNonConstClass();
ConstNonConstClass(double &np_local0);
- double nonConstMethod() {}
- double constMethod() const {}
+ double nonConstMethod() { return 0; }
+ double constMethod() const { return 0; }
double modifyingMethod(double &np_arg0) const;
double NonConstMember;
clang-tools-extra/test/clang-tidy/checkers/misc/redundant-expression.cpp
@@ -1,4 +1,5 @@
// RUN: %check_clang_tidy %s misc-redundant-expression %t -- -- -fno-delayed-template-parsing -Wno-array-compare-cxx26
+// RUN: %check_clang_tidy %s misc-redundant-expression %t -- -- -fno-delayed-template-parsing -Wno-array-compare-cxx26 -DTEST_MACRO
typedef __INT64_TYPE__ I64;
@@ -91,6 +92,223 @@ int TestSimpleEquivalent(int X, int Y) {
return 0;
}
+#ifndef TEST_MACRO
+#define VAL_1 2
+#define VAL_3 3
+#else
+#define VAL_1 3
+#define VAL_3 2
+#endif
+
+#define VAL_2 2
+
+#ifndef TEST_MACRO
+#define VAL_4 2 + 1
+#define VAL_6 3 + 1
+#else
+#define VAL_4 3 + 1
+#define VAL_6 2 + 1
+#endif
+
+#define VAL_5 2 + 1
+
+struct TestStruct
+{
+ int mA;
+ int mB;
+ int mC[10];
+};
+
+int TestDefineEquivalent() {
+
+ int int_val1 = 3;
+ int int_val2 = 4;
+ int int_val = 0;
+ const int cint_val2 = 4;
+
+ // Cases which should not be reported
+ if (VAL_1 != VAL_2) return 0;
+ if (VAL_3 != VAL_2) return 0;
+ if (VAL_1 == VAL_2) return 0;
+ if (VAL_3 == VAL_2) return 0;
+ if (VAL_1 >= VAL_2) return 0;
+ if (VAL_3 >= VAL_2) return 0;
+ if (VAL_1 <= VAL_2) return 0;
+ if (VAL_3 <= VAL_2) return 0;
+ if (VAL_1 < VAL_2) return 0;
+ if (VAL_3 < VAL_2) return 0;
+ if (VAL_1 > VAL_2) return 0;
+ if (VAL_3 > VAL_2) return 0;
+
+ if (VAL_4 != VAL_5) return 0;
+ if (VAL_6 != VAL_5) return 0;
+ if (VAL_6 == VAL_5) return 0;
+ if (VAL_4 >= VAL_5) return 0;
+ if (VAL_6 >= VAL_5) return 0;
+ if (VAL_4 <= VAL_5) return 0;
+ if (VAL_6 <= VAL_5) return 0;
+ if (VAL_4 > VAL_5) return 0;
+ if (VAL_6 > VAL_5) return 0;
+ if (VAL_4 < VAL_5) return 0;
+ if (VAL_6 < VAL_5) return 0;
+
+ if (VAL_1 != 2) return 0;
+ if (VAL_3 == 3) return 0;
+
+ if (VAL_1 >= VAL_1) return 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent
+ if (VAL_2 <= VAL_2) return 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent
+ if (VAL_3 > VAL_3) return 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent
+ if (VAL_4 < VAL_4) return 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent
+ if (VAL_6 == VAL_6) return 2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent
+ if (VAL_5 != VAL_5) return 2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent
+
+ // Test prefixes
+ if (+VAL_6 == +VAL_6) return 2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both sides of operator are equivalent
+ if (-VAL_6 == -VAL_6) return 2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both sides of operator are equivalent
+ if ((+VAL_6) == (+VAL_6)) return 2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: both sides of operator are equivalent
+ if ((-VAL_6) == (-VAL_6)) return 2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: both sides of operator are equivalent
+
+ if (1 >= 1) return 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent
+ if (0xFF <= 0xFF) return 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: both sides of operator are equivalent
+ if (042 > 042) return 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: both sides of operator are equivalent
+
+ int_val = (VAL_6 == VAL_6)?int_val1: int_val2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: both sides of operator are equivalent
+ int_val = (042 > 042)?int_val1: int_val2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: both sides of operator are equivalent
+
+
+ // Ternary operator cases which should not be reported
+ int_val = (VAL_4 == VAL_5)? int_val1: int_val2;
+ int_val = (VAL_3 != VAL_2)? int_val1: int_val2;
+ int_val = (VAL_6 != 10)? int_val1: int_val2;
+ int_val = (VAL_6 != 3)? int_val1: int_val2;
+ int_val = (VAL_6 != 4)? int_val1: int_val2;
+ int_val = (VAL_6 == 3)? int_val1: int_val2;
+ int_val = (VAL_6 == 4)? int_val1: int_val2;
+
+ TestStruct tsVar1 = {
+ .mA = 3,
+ .mB = int_val,
+ .mC[0 ... VAL_2 - 2] = int_val + 1,
+ };
+
+ TestStruct tsVar2 = {
+ .mA = 3,
+ .mB = int_val,
+ .mC[0 ... cint_val2 - 2] = int_val + 1,
+ };
+
+ TestStruct tsVar3 = {
+ .mA = 3,
+ .mB = int_val,
+ .mC[0 ... VAL_3 - VAL_3] = int_val + 1,
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: both sides of operator are equivalent
+ };
+
+ TestStruct tsVar4 = {
+ .mA = 3,
+ .mB = int_val,
+ .mC[0 ... 5 - 5] = int_val + 1,
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: both sides of operator are equivalent
+ };
+
+ return 1 + int_val + sizeof(tsVar1) + sizeof(tsVar2) +
+ sizeof(tsVar3) + sizeof(tsVar4);
+}
+
+#define LOOP_DEFINE 1
+
+unsigned int testLoops(const unsigned int arr1[LOOP_DEFINE])
+{
+ unsigned int localIndex;
+ for (localIndex = LOOP_DEFINE - 1; localIndex > 0; localIndex--)
+ {
+ }
+ for (localIndex = LOOP_DEFINE - 1; 10 > 10; localIndex--)
+ // CHECK-MESSAGES: :[[@LINE-1]]:41: warning: both sides of operator are equivalent
+ {
+ }
+
+ for (localIndex = LOOP_DEFINE - 1; LOOP_DEFINE > LOOP_DEFINE; localIndex--)
+ // CHECK-MESSAGES: :[[@LINE-1]]:50: warning: both sides of operator are equivalent
+ {
+ }
+
+ return localIndex;
+}
+
+#define getValue(a) a
+#define getValueM(a) a
+
+int TestParamDefine() {
+ int ret = 0;
+
+ // Negative cases
+ ret += getValue(VAL_6) == getValue(2);
+ ret += getValue(VAL_6) == getValue(3);
+ ret += getValue(VAL_5) == getValue(2);
+ ret += getValue(VAL_5) == getValue(3);
+ ret += getValue(1) > getValue( 2);
+ ret += getValue(VAL_1) == getValue(VAL_2);
+ ret += getValue(VAL_1) != getValue(VAL_2);
+ ret += getValue(VAL_1) == getValueM(VAL_1);
+ ret += getValue(VAL_1 + VAL_2) == getValueM(VAL_1 + VAL_2);
+ ret += getValue(1) == getValueM(1);
+ ret += getValue(false) == getValueM(false);
+ ret += -getValue(1) > +getValue( 1);
+
+ // Positive cases
+ ret += (+getValue(1)) > (+getValue( 1));
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: both sides of operator are equivalent
+ ret += (-getValue(1)) > (-getValue( 1));
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: both sides of operator are equivalent
+
+ ret += +getValue(1) > +getValue( 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: both sides of operator are equivalent
+ ret += -getValue(1) > -getValue( 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: both sides of operator are equivalent
+
+ ret += getValue(1) > getValue( 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: both sides of operator are equivalent
+ ret += getValue(1) > getValue( 1 );
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: both sides of operator are equivalent
+ ret += getValue(1) > getValue( 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: both sides of operator are equivalent
+ ret += getValue( 1) > getValue( 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: both sides of operator are equivalent
+ ret += getValue( 1 ) > getValue( 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: both sides of operator are equivalent
+ ret += getValue( VAL_5 ) > getValue(VAL_5);
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: both sides of operator are equivalent
+ ret += getValue( VAL_5 ) > getValue( VAL_5);
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: both sides of operator are equivalent
+ ret += getValue( VAL_5 ) > getValue( VAL_5 ) ;
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: both sides of operator are equivalent
+ ret += getValue(VAL_5) > getValue( VAL_5 ) ;
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: both sides of operator are equivalent
+ ret += getValue(VAL_1+VAL_2) > getValue(VAL_1 + VAL_2) ;
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: both sides of operator are equivalent
+ ret += getValue(VAL_1)+getValue(VAL_2) > getValue(VAL_1) + getValue( VAL_2) ;
+ // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: both sides of operator are equivalent
+ ret += (getValue(VAL_1)+getValue(VAL_2)) > (getValue(VAL_1) + getValue( VAL_2) ) ;
+ // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: both sides of operator are equivalent
+ return ret;
+}
+
template <int DX>
int TestSimpleEquivalentDependent() {
if (DX > 0 && DX > 0) return 1;
clang-tools-extra/test/clang-tidy/checkers/misc/unused-parameters.cpp
@@ -33,9 +33,9 @@ void f(void (*fn)()) {;}
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: parameter 'fn' is unused [misc-unused-parameters]
// CHECK-FIXES: {{^}}void f(void (* /*fn*/)()) {;}{{$}}
-int *k([[clang::lifetimebound]] int *i) {;}
+int *k([[clang::lifetimebound]] int *i) { return nullptr; }
// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: parameter 'i' is unused [misc-unused-parameters]
-// CHECK-FIXES: {{^}}int *k({{\[\[clang::lifetimebound\]\]}} int * /*i*/) {;}{{$}}
+// CHECK-FIXES: {{^}}int *k({{\[\[clang::lifetimebound\]\]}} int * /*i*/) { return nullptr; }{{$}}
#define ATTR_BEFORE(x) [[clang::lifetimebound]] x
int* m(ATTR_BEFORE(const int *i)) { return nullptr; }
clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-func.cpp
@@ -17,25 +17,25 @@ void func_cpp_inc() {}
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'func_cpp_inc'
// CHECK-FIXES: static void func_cpp_inc() {}
-int* func_cpp_inc_return_ptr() {}
+int* func_cpp_inc_return_ptr() { return nullptr; }
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'func_cpp_inc_return_ptr'
-// CHECK-FIXES: static int* func_cpp_inc_return_ptr() {}
+// CHECK-FIXES: static int* func_cpp_inc_return_ptr() { return nullptr; }
-const int* func_cpp_inc_return_const_ptr() {}
+const int* func_cpp_inc_return_const_ptr() { return nullptr; }
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: function 'func_cpp_inc_return_const_ptr'
-// CHECK-FIXES: static const int* func_cpp_inc_return_const_ptr() {}
+// CHECK-FIXES: static const int* func_cpp_inc_return_const_ptr() { return nullptr; }
-int const* func_cpp_inc_return_ptr_const() {}
+int const* func_cpp_inc_return_ptr_const() { return nullptr; }
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: function 'func_cpp_inc_return_ptr_const'
-// CHECK-FIXES: static int const* func_cpp_inc_return_ptr_const() {}
+// CHECK-FIXES: static int const* func_cpp_inc_return_ptr_const() { return nullptr; }
-int * const func_cpp_inc_return_const() {}
+int * const func_cpp_inc_return_const() { return nullptr; }
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: function 'func_cpp_inc_return_const'
-// CHECK-FIXES: static int * const func_cpp_inc_return_const() {}
+// CHECK-FIXES: static int * const func_cpp_inc_return_const() { return nullptr; }
-volatile const int* func_cpp_inc_return_volatile_const_ptr() {}
+volatile const int* func_cpp_inc_return_volatile_const_ptr() { return nullptr; }
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: function 'func_cpp_inc_return_volatile_const_ptr'
-// CHECK-FIXES: static volatile const int* func_cpp_inc_return_volatile_const_ptr() {}
+// CHECK-FIXES: static volatile const int* func_cpp_inc_return_volatile_const_ptr() { return nullptr; }
[[nodiscard]] void func_nodiscard() {}
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: function 'func_nodiscard'
clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-auto/containers.h
@@ -214,8 +214,8 @@ class map : public bidirectional_iterable<iterator<pair<key, value>>> {
public:
map() {}
- iterator<pair<key, value>> find(const key &) {}
- const_iterator<iterator<pair<key, value>>> find(const key &) const {}
+ iterator<pair<key, value>> find(const key &);
+ const_iterator<iterator<pair<key, value>>> find(const key &) const;
};
template <typename key, typename value>
clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-bind.cpp
@@ -46,7 +46,7 @@ struct D {
operator bool() const { return true; }
void MemberFunction(int x) {}
- int MemberFunctionWithReturn(int x) {}
+ int MemberFunctionWithReturn(int x) { return 0; }
static D *create();
};
@@ -342,7 +342,7 @@ void testCapturedSubexpressions() {
struct E {
void MemberFunction(int x) {}
- int MemberFunctionWithReturn(int x) {}
+ int MemberFunctionWithReturn(int x) { return 0; }
int operator()(int x, int y) const { return x + y; }
void testMemberFunctions() {
clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays-c++20.cpp
@@ -1,11 +1,11 @@
// RUN: %check_clang_tidy -std=c++20 %s modernize-avoid-c-arrays %t
-int f1(int data[], int size) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: do not declare C-style arrays, use 'std::span' instead
+void f1(int data[], int size) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not declare C-style arrays, use 'std::span' instead
int f4[] = {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not declare C-style arrays, use 'std::array' instead
}
-int f2(int data[100]) {
- // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: do not declare C-style arrays, use 'std::array' instead
+void f2(int data[100]) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not declare C-style arrays, use 'std::array' instead
}
clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays-ignores-main.cpp
@@ -1,9 +1,13 @@
// RUN: %check_clang_tidy -std=c++17 %s modernize-avoid-c-arrays %t
-int not_main(int argc, char *argv[]) {
- // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not declare C-style arrays, use 'std::array' or 'std::vector' instead
+namespace X {
+// Not main
+int main(int argc, char *argv[]) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: do not declare C-style arrays, use 'std::array' or 'std::vector' instead
int f4[] = {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not declare C-style arrays, use 'std::array' instead
+ return 0;
+}
}
int main(int argc, char *argv[]) {
clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays-ignores-three-arg-main.cpp
@@ -1,19 +1,23 @@
// RUN: %check_clang_tidy -std=c++17 %s modernize-avoid-c-arrays %t
-int not_main(int argc, char *argv[], char *argw[]) {
- // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not declare C-style arrays, use 'std::array' or 'std::vector' instead
- // CHECK-MESSAGES: :[[@LINE-2]]:38: warning: do not declare C-style arrays, use 'std::array' or 'std::vector' instead
+namespace X {
+// Not main.
+int main(int argc, char *argv[], char *argw[]) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: do not declare C-style arrays, use 'std::array' or 'std::vector' instead
+ // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: do not declare C-style arrays, use 'std::array' or 'std::vector' instead
int f4[] = {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not declare C-style arrays, use 'std::array' instead
+ return 0;
+}
}
int main(int argc, char *argv[], char *argw[]) {
int f5[] = {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not declare C-style arrays, use 'std::array' instead
- auto not_main = [](int argc, char *argv[], char *argw[]) {
- // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: do not declare C-style arrays, use 'std::array' or 'std::vector' instead
- // CHECK-MESSAGES: :[[@LINE-2]]:46: warning: do not declare C-style arrays, use 'std::array' or 'std::vector' instead
+ auto main = [](int argc, char *argv[], char *argw[]) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: do not declare C-style arrays, use 'std::array' or 'std::vector' instead
+ // CHECK-MESSAGES: :[[@LINE-2]]:42: warning: do not declare C-style arrays, use 'std::array' or 'std::vector' instead
int f6[] = {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not declare C-style arrays, use 'std::array' instead
};
clang-tools-extra/test/clang-tidy/checkers/modernize/loop-convert-basic.cpp
@@ -170,6 +170,8 @@ const int *constArray() {
// CHECK-FIXES: for (const int & I : ConstArr)
// CHECK-FIXES-NEXT: if (Something)
// CHECK-FIXES-NEXT: return &I;
+
+ return nullptr;
}
struct HasArr {
clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp
@@ -49,7 +49,7 @@ public:
template <typename... Args>
void emplace_back(Args &&... args){};
template <typename... Args>
- iterator emplace(const_iterator pos, Args &&...args){};
+ iterator emplace(const_iterator pos, Args &&...args);
~vector();
};
@@ -69,7 +69,7 @@ public:
void push_back(T &&) {}
template <typename... Args>
- iterator emplace(const_iterator pos, Args &&...args){};
+ iterator emplace(const_iterator pos, Args &&...args);
template <typename... Args>
void emplace_back(Args &&... args){};
template <typename... Args>
@@ -93,7 +93,7 @@ public:
void push_front(T &&) {}
template <typename... Args>
- iterator emplace(const_iterator pos, Args &&...args){};
+ iterator emplace(const_iterator pos, Args &&...args);
template <typename... Args>
void emplace_back(Args &&... args){};
template <typename... Args>
@@ -116,7 +116,7 @@ public:
template <typename... Args>
void emplace_front(Args &&...args){};
template <typename... Args>
- iterator emplace_after(const_iterator pos, Args &&...args){};
+ iterator emplace_after(const_iterator pos, Args &&...args);
};
template <typename T>
@@ -131,7 +131,7 @@ public:
template <typename... Args>
void emplace(Args &&...args){};
template <typename... Args>
- iterator emplace_hint(const_iterator pos, Args &&...args){};
+ iterator emplace_hint(const_iterator pos, Args &&...args);
};
template <typename Key, typename T>
@@ -146,7 +146,7 @@ public:
template <typename... Args>
void emplace(Args &&...args){};
template <typename... Args>
- iterator emplace_hint(const_iterator pos, Args &&...args){};
+ iterator emplace_hint(const_iterator pos, Args &&...args);
};
template <typename T>
@@ -161,7 +161,7 @@ public:
template <typename... Args>
void emplace(Args &&...args){};
template <typename... Args>
- iterator emplace_hint(const_iterator pos, Args &&...args){};
+ iterator emplace_hint(const_iterator pos, Args &&...args);
};
template <typename Key, typename T>
@@ -176,7 +176,7 @@ public:
template <typename... Args>
void emplace(Args &&...args){};
template <typename... Args>
- iterator emplace_hint(const_iterator pos, Args &&...args){};
+ iterator emplace_hint(const_iterator pos, Args &&...args);
};
template <typename T>
@@ -191,7 +191,7 @@ public:
template <typename... Args>
void emplace(Args &&...args){};
template <typename... Args>
- iterator emplace_hint(const_iterator pos, Args &&...args){};
+ iterator emplace_hint(const_iterator pos, Args &&...args);
};
template <typename Key, typename T>
@@ -206,7 +206,7 @@ public:
template <typename... Args>
void emplace(Args &&...args){};
template <typename... Args>
- iterator emplace_hint(const_iterator pos, Args &&...args){};
+ iterator emplace_hint(const_iterator pos, Args &&...args);
};
template <typename T>
@@ -221,7 +221,7 @@ public:
template <typename... Args>
void emplace(Args &&...args){};
template <typename... Args>
- iterator emplace_hint(const_iterator pos, Args &&...args){};
+ iterator emplace_hint(const_iterator pos, Args &&...args);
};
template <typename Key, typename T>
@@ -236,7 +236,7 @@ public:
template <typename... Args>
void emplace(Args &&...args){};
template <typename... Args>
- iterator emplace_hint(const_iterator pos, Args &&...args){};
+ iterator emplace_hint(const_iterator pos, Args &&...args);
};
template <typename T>
clang-tools-extra/test/clang-tidy/checkers/modernize/use-equals-default-copy.cpp
@@ -1,6 +1,6 @@
// RUN: %check_clang_tidy %s modernize-use-equals-default %t -- \
// RUN: -config="{CheckOptions: {modernize-use-equals-default.IgnoreMacros: false}}" \
-// RUN: -- -fno-delayed-template-parsing -fexceptions
+// RUN: -- -fno-delayed-template-parsing -fexceptions -Wno-error=return-type
// Out of line definition.
struct OL {
clang-tools-extra/test/clang-tidy/checkers/modernize/use-override.cpp
@@ -203,13 +203,13 @@ public:
// CHECK-MESSAGES: :[[@LINE-2]]:16: warning: prefer using
// CHECK-FIXES: {{^}} void j() const override
- virtual MustUseResultObject k() {} // Has an implicit attribute.
+ virtual MustUseResultObject k(); // Has an implicit attribute.
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: prefer using
- // CHECK-FIXES: {{^}} MustUseResultObject k() override {}
+ // CHECK-FIXES: {{^}} MustUseResultObject k() override;
- virtual bool l() MUST_USE_RESULT UNUSED {}
+ virtual bool l() MUST_USE_RESULT UNUSED;
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
- // CHECK-FIXES: {{^}} bool l() override MUST_USE_RESULT UNUSED {}
+ // CHECK-FIXES: {{^}} bool l() override MUST_USE_RESULT UNUSED;
virtual void r() &
{}
clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format.cpp
@@ -100,7 +100,7 @@ std::string StrFormat_field_width_and_precision() {
return s1 + s2 + s3 + s4 + s5 + s6;
}
-std::string StrFormat_macros() {
+void StrFormat_macros() {
// The function call is replaced even though it comes from a macro.
#define FORMAT absl::StrFormat
auto s1 = FORMAT("Hello %d", 42);
clang-tools-extra/test/clang-tidy/checkers/modernize/use-trailing-return-type.cpp
@@ -106,9 +106,9 @@ extern "C" int d2(int arg);
inline int d3(int arg) noexcept(true);
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
// CHECK-FIXES: {{^}}inline auto d3(int arg) noexcept(true) -> int;{{$}}
-inline int d4(int arg) try { } catch(...) { }
+inline int d4(int arg) try { return 0; } catch(...) { return 0; }
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
-// CHECK-FIXES: {{^}}inline auto d4(int arg) -> int try { } catch(...) { }{{$}}
+// CHECK-FIXES: {{^}}inline auto d4(int arg) -> int try { return 0; } catch(...) { return 0; }{{$}}
int d5(int arg) throw();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
// CHECK-FIXES: {{^}}auto d5(int arg) throw() -> int;{{$}}
@@ -167,9 +167,9 @@ namespace N {
}
// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
// CHECK-FIXES: {{^}} auto e1() -> int;{{$}}
-int N::e1() {}
+int N::e1() { return 0; }
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
-// CHECK-FIXES: {{^}}auto N::e1() -> int {}{{$}}
+// CHECK-FIXES: {{^}}auto N::e1() -> int { return 0; }{{$}}
//
// Functions with unsupported return types
@@ -260,14 +260,14 @@ struct B {
B& operator=(const B&);
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
// CHECK-FIXES: {{^}} auto operator=(const B&) -> B&;{{$}}
-
+
double base1(int, bool b);
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
// CHECK-FIXES: {{^}} auto base1(int, bool b) -> double;{{$}}
- virtual double base2(int, bool b) {}
+ virtual double base2(int, bool b) { return 0; }
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
-// CHECK-FIXES: {{^}} virtual auto base2(int, bool b) -> double {}{{$}}
+// CHECK-FIXES: {{^}} virtual auto base2(int, bool b) -> double { return 0; }{{$}}
virtual float base3() const = 0;
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
@@ -298,9 +298,9 @@ struct B {
// CHECK-FIXES: {{^}} virtual auto base9() const noexcept -> const char * { return ""; }{{$}}
};
-double B::base1(int, bool b) {}
+double B::base1(int, bool b) { return 0; }
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
-// CHECK-FIXES: {{^}}auto B::base1(int, bool b) -> double {}{{$}}
+// CHECK-FIXES: {{^}}auto B::base1(int, bool b) -> double { return 0; }{{$}}
struct D : B {
virtual double f1(int, bool b) final;
@@ -311,9 +311,9 @@ struct D : B {
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
// CHECK-FIXES: {{^}} virtual auto base2(int, bool b) -> double override;{{$}}
- virtual float base3() const override final { }
+ virtual float base3() const override final { return 0; }
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
-// CHECK-FIXES: {{^}} virtual auto base3() const -> float override final { }{{$}}
+// CHECK-FIXES: {{^}} virtual auto base3() const -> float override final { return 0; }{{$}}
const char * base9() const noexcept override { return ""; }
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use a trailing return type for this function [modernize-use-trailing-return-type]
@@ -586,13 +586,13 @@ void c(int arg) { return; }
struct D2 : B {
D2();
virtual ~D2();
-
+
virtual auto f1(int, bool b) -> double final;
virtual auto base2(int, bool b) -> double override;
- virtual auto base3() const -> float override final { }
+ virtual auto base3() const -> float override final { return 0; }
operator double();
};
auto l1 = [](int arg) {};
-auto l2 = [](int arg) -> double {};
+auto l2 = [](int arg) -> double { return 0; };
clang-tools-extra/test/clang-tidy/checkers/performance/Inputs/unnecessary-value-param/header-fixed.h
@@ -12,4 +12,4 @@ int f1(int n, ABC v1); // line 11
-int f2( int n, const ABC& v2); // line 15
+void f2( int n, const ABC& v2); // line 15
clang-tools-extra/test/clang-tidy/checkers/performance/Inputs/unnecessary-value-param/header.h
@@ -12,4 +12,4 @@ int f1(int n, ABC v1); // line 11
-int f2( int n, ABC v2); // line 15
+void f2( int n, ABC v2); // line 15
clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-string-concatenation.cpp
@@ -6,15 +6,15 @@ class basic_string {
public:
basic_string() {}
~basic_string() {}
- basic_string<T> *operator+=(const basic_string<T> &) {}
- friend basic_string<T> operator+(const basic_string<T> &, const basic_string<T> &) {}
+ basic_string<T> *operator+=(const basic_string<T> &);
+ friend basic_string<T> operator+(const basic_string<T> &, const basic_string<T> &);
};
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
}
void f(std::string) {}
-std::string g(std::string) {}
+std::string g(std::string);
int main() {
std::string mystr1, mystr2;
clang-tools-extra/test/clang-tidy/checkers/performance/noexcept-move-constructor.cpp
@@ -1,4 +1,6 @@
// RUN: %check_clang_tidy %s performance-noexcept-move-constructor %t -- -- -fexceptions
+// RUN: %check_clang_tidy -std=c++17 -check-suffixes=,ERR %s performance-noexcept-move-constructor %t \
+// RUN: -- --fix-errors -- -fexceptions -DENABLE_ERROR
namespace std
{
@@ -397,3 +399,18 @@ namespace gh68101
Container(Container&&) noexcept(std::is_nothrow_move_constructible<T>::value);
};
} // namespace gh68101
+
+namespace gh111436
+{
+
+template <typename value_type> class set {
+ set(set &&) = default;
+
+#ifdef ENABLE_ERROR
+ set(initializer_list<value_type> __l) {};
+ // CHECK-MESSAGES-ERR: :[[@LINE-1]]:7: error: member 'initializer_list' cannot have template arguments [clang-diagnostic-error]
+ // CHECK-MESSAGES-ERR: :[[@LINE-2]]:36: error: expected ')' [clang-diagnostic-error]
+#endif
+};
+
+} // namespace gh111436
clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-header.cpp
@@ -14,7 +14,7 @@ int f1(int n, ABC v1, ABC v2) {
// CHECK-FIXES: int f1(int n, const ABC& v1, const ABC& v2) {
return v1.get(n) + v2.get(n);
}
-int f2(int n, ABC v2) {
- // CHECK-MESSAGES: [[@LINE-1]]:19: warning: the parameter 'v2' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
- // CHECK-FIXES: int f2(int n, const ABC& v2) {
+void f2(int n, ABC v2) {
+ // CHECK-MESSAGES: [[@LINE-1]]:20: warning: the parameter 'v2' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+ // CHECK-FIXES: void f2(int n, const ABC& v2) {
}
clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/identifier-naming/global-style1/header.h
@@ -4,4 +4,4 @@ void style_first_good();
void styleFirstBad();
-int thisIsMainLikeIgnored(int argc, const char *argv[]) {}
+int thisIsMainLikeIgnored(int argc, const char *argv[]) { return 0; }
clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/identifier-naming/global-style2/header.h
@@ -4,4 +4,4 @@ void STYLE_SECOND_GOOD();
void styleSecondBad();
-int thisIsMainLikeNotIgnored(int argc, const char *argv[]) {}
+int thisIsMainLikeNotIgnored(int argc, const char *argv[]) { return 0; }
clang-tools-extra/test/clang-tidy/checkers/readability/const-return-type-macros.cpp
@@ -6,16 +6,16 @@
// Regression tests involving macros
#define CONCAT(a, b) a##b
-CONCAT(cons, t) int p22(){}
+CONCAT(cons, t) int p22(){ return 0; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
// We warn, but we can't give a fix
#define CONSTINT const int
-CONSTINT p23() {}
+CONSTINT p23() { return 0; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
#define CONST const
-CONST int p24() {}
+CONST int p24() { return 0; }
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
#define CREATE_FUNCTION() \
clang-tools-extra/test/clang-tidy/checkers/readability/const-return-type.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy -std=c++14-or-later %s readability-const-return-type %t
+// RUN: %check_clang_tidy -std=c++14-or-later %s readability-const-return-type %t -- -- -Wno-error=return-type
// p# = positive test
// n# = negative test
clang-tools-extra/test/clang-tidy/checkers/readability/convert-member-functions-to-static.cpp
@@ -32,6 +32,7 @@ class A {
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: method 'call_static_member' can be made static
// CHECK-FIXES: {{^}} static int call_static_member() {
already_static();
+ return 0;
}
int read_static() {
clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming.cpp
@@ -547,6 +547,7 @@ struct_type GlobalTypedefTestFunction(struct_type a_argument1) {
// CHECK-FIXES: {{^}}struct_type_t GlobalTypedefTestFunction(struct_type_t a_argument1) {
struct_type typedef_test_1;
// CHECK-FIXES: {{^}} struct_type_t typedef_test_1;
+ return {};
}
using my_struct_type = THIS___Structure;
@@ -777,8 +778,8 @@ STATIC_MACRO void someFunc(ValueType a_v1, const ValueType& a_v2) {}
// CHECK-FIXES: {{^}}STATIC_MACRO void someFunc(value_type_t a_v1, const value_type_t& a_v2) {}
STATIC_MACRO void someFunc(const ValueType** p_a_v1, ValueType (*p_a_v2)()) {}
// CHECK-FIXES: {{^}}STATIC_MACRO void someFunc(const value_type_t** p_a_v1, value_type_t (*p_a_v2)()) {}
-STATIC_MACRO ValueType someFunc() {}
-// CHECK-FIXES: {{^}}STATIC_MACRO value_type_t someFunc() {}
+STATIC_MACRO ValueType someFunc() { return {}; }
+// CHECK-FIXES: {{^}}STATIC_MACRO value_type_t someFunc() { return {}; }
STATIC_MACRO void someFunc(MyFunPtr, const MyFunPtr****) {}
// CHECK-FIXES: {{^}}STATIC_MACRO void someFunc(my_fun_ptr_t, const my_fun_ptr_t****) {}
#undef STATIC_MACRO
clang-tools-extra/test/clang-tidy/checkers/readability/implicit-bool-conversion.cpp
@@ -465,7 +465,7 @@ struct S {
// CHECK-FIXES: S(bool a, bool b, bool c) : a(static_cast<int>(a)), b(b), c(static_cast<int>(c)) {}
};
-bool f(S& s) {
+void f(S& s) {
functionTaking<bool>(s.a);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> 'bool'
// CHECK-FIXES: functionTaking<bool>(s.a != 0);
clang-tools-extra/test/clang-tidy/checkers/readability/named-parameter.cpp
@@ -37,8 +37,8 @@ void operator delete[](void *x) throw();
void operator delete[](void * /*x*/) throw();
struct X {
- X operator++(int) {}
- X operator--(int) {}
+ void operator++(int) {}
+ void operator--(int) {}
X(X&) = delete;
X &operator=(X&) = default;
@@ -86,22 +86,23 @@ void FDef2(int n, int) {}
void FNoDef(int);
class Z {};
+Z the_z;
-Z &operator++(Z&) {}
+Z &operator++(Z&) { return the_z; }
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: all parameters should be named in a function
-// CHECK-FIXES: Z &operator++(Z& /*unused*/) {}
+// CHECK-FIXES: Z &operator++(Z& /*unused*/) { return the_z; }
-Z &operator++(Z&, int) {}
+Z &operator++(Z&, int) { return the_z; }
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: all parameters should be named in a function
-// CHECK-FIXES: Z &operator++(Z& /*unused*/, int) {}
+// CHECK-FIXES: Z &operator++(Z& /*unused*/, int) { return the_z; }
-Z &operator--(Z&) {}
+Z &operator--(Z&) { return the_z; }
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: all parameters should be named in a function
-// CHECK-FIXES: Z &operator--(Z& /*unused*/) {}
+// CHECK-FIXES: Z &operator--(Z& /*unused*/) { return the_z; }
-Z &operator--(Z&, int) {}
+Z &operator--(Z&, int) { return the_z; }
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: all parameters should be named in a function
-// CHECK-FIXES: Z &operator--(Z& /*unused*/, int) {}
+// CHECK-FIXES: Z &operator--(Z& /*unused*/, int) { return the_z; }
namespace testing {
namespace internal {
clang-tools-extra/test/clang-tidy/checkers/readability/redundant-declaration.c
@@ -20,7 +20,7 @@ static int f(void);
static int f(void); // f
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant 'f' declaration
// CHECK-FIXES: {{^}}// f{{$}}
-static int f(void) {}
+static int f(void) { return 0; }
inline void g(void) {}
clang-tools-extra/test/clang-tidy/checkers/readability/redundant-declaration.cpp
@@ -38,7 +38,7 @@ static int f();
static int f(); // f
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant 'f' declaration
// CHECK-FIXES: {{^}}// f{{$}}
-static int f() {}
+static int f() { return 0; }
// Original check crashed for the code below.
namespace std {
clang-tools-extra/test/clang-tidy/checkers/readability/static-accessed-through-instance.cpp
@@ -264,7 +264,7 @@ struct Qptr {
}
};
-int func(Qptr qp) {
+void func(Qptr qp) {
qp->y = 10;
qp->K = 10;
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member accessed through instance [readability-static-accessed-through-instance]
clang-tools-extra/test/clang-tidy/checkers/readability/suspicious-call-argument.cpp
@@ -382,7 +382,7 @@ enum opcode { Foo,
Bar };
static value *SimplifyRightShift(
opcode Opcode, value *Op0, value *Op1, bool isExact,
- const type1 &Q, unsigned MaxRecurse) {}
+ const type1 &Q, unsigned MaxRecurse) { return nullptr; }
static value *SimplifyLShrInst(value *Op0, value *Op1, bool isExact,
const type1 &Q, unsigned MaxRecurse) {
if (value *V = SimplifyRightShift(Foo, Op0, Op1, isExact, Q, MaxRecurse))
clang-tools-extra/test/clang-tidy/infrastructure/duplicate-fixes-of-alias-checkers.cpp
@@ -31,7 +31,7 @@ private:
// CHECK-FIXES: _num2{};
};
-int should_use_emplace(std::vector<Foo> &v) {
+void should_use_emplace(std::vector<Foo> &v) {
v.push_back(Foo());
// CHECK-FIXES: v.emplace_back();
// CHECK-MESSAGES: warning: use emplace_back instead of push_back [hicpp-use-emplace,modernize-use-emplace]
clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
@@ -319,7 +319,12 @@ TEST(HTMLGeneratorTest, emitFunctionHTML) {
<a href="path/to/int.html">int</a>
P)
</p>
- <p>Defined at line 10 of file dir/test.cpp</p>
+ <p>
+ Defined at line
+ <a href="https://www.repository.com/dir/test.cpp#10">10</a>
+ of file
+ <a href="https://www.repository.com/dir/test.cpp">test.cpp</a>
+ </p>
</div>
<div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right"></div>
</main>
clang/Maintainers.rst
@@ -136,6 +136,7 @@ Clang static analyzer
| Balázs Benics
| benicsbalazs\@gmail.com (email), steakhal (Phabricator), steakhal (GitHub)
+| balazs.benics\@sonarsource.com (email), balazs-benics-sonarsource (GitHub)
Compiler options
~~~~~~~~~~~~~~~~
clang/bindings/python/clang/cindex.py
@@ -1410,6 +1410,9 @@ class CursorKind(BaseEnumeration):
# OpenMP scope directive.
OMP_SCOPE_DIRECTIVE = 306
+ # OpenMP stripe directive.
+ OMP_STRIPE_DIRECTIVE = 310
+
# OpenACC Compute Construct.
OPEN_ACC_COMPUTE_DIRECTIVE = 320
clang/cmake/caches/Fuchsia-stage2.cmake
@@ -190,8 +190,8 @@ foreach(target aarch64-unknown-linux-gnu;armv7-unknown-linux-gnueabihf;i386-unkn
set(RUNTIMES_${target}_LLVM_ENABLE_RUNTIMES "compiler-rt;libcxx;libcxxabi;libunwind" CACHE STRING "")
# Enable FatLTO for Linux and baremetal runtimes
- set(RUNTIMES_${target}_LLVM_ENABLE_LTO ON CACHE BOOL "")
- set(RUNTIMES_${target}_LLVM_ENABLE_FATLTO ON CACHE BOOL "")
+ set(RUNTIMES_${target}_LLVM_ENABLE_LTO OFF CACHE BOOL "")
+ set(RUNTIMES_${target}_LLVM_ENABLE_FATLTO OFF CACHE BOOL "")
# Use .build-id link.
list(APPEND RUNTIME_BUILD_ID_LINK "${target}")
@@ -276,8 +276,8 @@ if(FUCHSIA_SDK)
set(RUNTIMES_${target}+asan+noexcept_LIBCXX_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
# Enable FatLTO for Fuchsia runtimes
- set(RUNTIMES_${target}_LLVM_ENABLE_LTO ON CACHE BOOL "")
- set(RUNTIMES_${target}_LLVM_ENABLE_FATLTO ON CACHE BOOL "")
+ set(RUNTIMES_${target}_LLVM_ENABLE_LTO OFF CACHE BOOL "")
+ set(RUNTIMES_${target}_LLVM_ENABLE_FATLTO OFF CACHE BOOL "")
# Use .build-id link.
list(APPEND RUNTIME_BUILD_ID_LINK "${target}")
@@ -378,8 +378,8 @@ foreach(target armv6m-none-eabi;armv7m-none-eabi;armv7em-none-eabi;armv8m.main-n
set(RUNTIMES_${target}_LLVM_ENABLE_RUNTIMES "libc;libcxx" CACHE STRING "")
# Enable FatLTO for baremetal runtimes
- set(RUNTIMES_${target}_LLVM_ENABLE_LTO ON CACHE BOOL "")
- set(RUNTIMES_${target}_LLVM_ENABLE_FATLTO ON CACHE BOOL "")
+ set(RUNTIMES_${target}_LLVM_ENABLE_LTO OFF CACHE BOOL "")
+ set(RUNTIMES_${target}_LLVM_ENABLE_FATLTO OFF CACHE BOOL "")
endforeach()
foreach(target riscv32-unknown-elf)
@@ -433,8 +433,8 @@ foreach(target riscv32-unknown-elf)
set(RUNTIMES_${target}_LLVM_ENABLE_RUNTIMES "libc;libcxx" CACHE STRING "")
# Enable FatLTO for baremetal runtimes
- set(RUNTIMES_${target}_LLVM_ENABLE_LTO ON CACHE BOOL "")
- set(RUNTIMES_${target}_LLVM_ENABLE_FATLTO ON CACHE BOOL "")
+ set(RUNTIMES_${target}_LLVM_ENABLE_LTO OFF CACHE BOOL "")
+ set(RUNTIMES_${target}_LLVM_ENABLE_FATLTO OFF CACHE BOOL "")
endforeach()
set(LLVM_BUILTIN_TARGETS "${BUILTIN_TARGETS}" CACHE STRING "")
clang/cmake/caches/Release.cmake
@@ -29,6 +29,13 @@ endfunction()
# cache file to CMake via -C. e.g.
#
# cmake -D LLVM_RELEASE_ENABLE_PGO=ON -C Release.cmake
+
+set (DEFAULT_PROJECTS "clang;lld;lldb;clang-tools-extra;polly;mlir;flang")
+# bolt only supports ELF, so only enable it for Linux.
+if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux")
+ list(APPEND DEFAULT_PROJECTS "bolt")
+endif()
+
set (DEFAULT_RUNTIMES "compiler-rt;libcxx")
if (NOT WIN32)
list(APPEND DEFAULT_RUNTIMES "libcxxabi" "libunwind")
@@ -36,7 +43,7 @@ endif()
set(LLVM_RELEASE_ENABLE_LTO THIN CACHE STRING "")
set(LLVM_RELEASE_ENABLE_PGO ON CACHE BOOL "")
set(LLVM_RELEASE_ENABLE_RUNTIMES ${DEFAULT_RUNTIMES} CACHE STRING "")
-set(LLVM_RELEASE_ENABLE_PROJECTS "clang;lld;lldb;clang-tools-extra;bolt;polly;mlir;flang" CACHE STRING "")
+set(LLVM_RELEASE_ENABLE_PROJECTS ${DEFAULT_PROJECTS} CACHE STRING "")
# Note we don't need to add install here, since it is one of the pre-defined
# steps.
set(LLVM_RELEASE_FINAL_STAGE_TARGETS "clang;package;check-all;check-llvm;check-clang" CACHE STRING "")
@@ -48,10 +55,8 @@ set(CLANG_ENABLE_BOOTSTRAP ON CACHE BOOL "")
set(STAGE1_PROJECTS "clang")
-# Building Flang on Windows requires compiler-rt, so we need to build it in
-# stage1. compiler-rt is also required for building the Flang tests on
-# macOS.
-set(STAGE1_RUNTIMES "compiler-rt")
+# Build all runtimes so we can statically link them into the stage2 compiler.
+set(STAGE1_RUNTIMES "compiler-rt;libcxx;libcxxabi;libunwind")
if (LLVM_RELEASE_ENABLE_PGO)
list(APPEND STAGE1_PROJECTS "lld")
@@ -90,9 +95,20 @@ else()
set(CLANG_BOOTSTRAP_TARGETS ${LLVM_RELEASE_FINAL_STAGE_TARGETS} CACHE STRING "")
endif()
+if (LLVM_RELEASE_ENABLE_LTO)
+ # Enable LTO for the runtimes. We need to configure stage1 clang to default
+ # to using lld as the linker because the stage1 toolchain will be used to
+ # build and link the runtimes.
+ # FIXME: We can't use LLVM_ENABLE_LTO=Thin here, because it causes the CMake
+ # step for the libcxx build to fail. CMAKE_INTERPROCEDURAL_OPTIMIZATION does
+ # enable ThinLTO, though.
+ set(RUNTIMES_CMAKE_ARGS "-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DLLVM_ENABLE_LLD=ON" CACHE STRING "")
+endif()
+
# Stage 1 Common Config
set(LLVM_ENABLE_RUNTIMES ${STAGE1_RUNTIMES} CACHE STRING "")
set(LLVM_ENABLE_PROJECTS ${STAGE1_PROJECTS} CACHE STRING "")
+set(LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY ON CACHE STRING "")
# stage2-instrumented and Final Stage Config:
# Options that need to be set in both the instrumented stage (if we are doing
@@ -102,10 +118,28 @@ set_instrument_and_final_stage_var(LLVM_ENABLE_LTO "${LLVM_RELEASE_ENABLE_LTO}"
if (LLVM_RELEASE_ENABLE_LTO)
set_instrument_and_final_stage_var(LLVM_ENABLE_LLD "ON" BOOL)
endif()
+set_instrument_and_final_stage_var(LLVM_ENABLE_LIBCXX "ON" BOOL)
+set_instrument_and_final_stage_var(LLVM_STATIC_LINK_CXX_STDLIB "ON" BOOL)
+set(RELEASE_LINKER_FLAGS "-rtlib=compiler-rt --unwindlib=libunwind")
+if(NOT ${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin")
+ set(RELEASE_LINKER_FLAGS "${RELEASE_LINKER_FLAGS} -static-libgcc")
+endif()
+
+# Set flags for bolt
+if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux")
+ set(RELEASE_LINKER_FLAGS "${RELEASE_LINKER_FLAGS} -Wl,--emit-relocs,-znow")
+endif()
+
+set_instrument_and_final_stage_var(CMAKE_EXE_LINKER_FLAGS ${RELEASE_LINKER_FLAGS} STRING)
+set_instrument_and_final_stage_var(CMAKE_SHARED_LINKER_FLAGS ${RELEASE_LINKER_FLAGS} STRING)
+set_instrument_and_final_stage_var(CMAKE_MODULE_LINKER_FLAGS ${RELEASE_LINKER_FLAGS} STRING)
# Final Stage Config (stage2)
set_final_stage_var(LLVM_ENABLE_RUNTIMES "${LLVM_RELEASE_ENABLE_RUNTIMES}" STRING)
set_final_stage_var(LLVM_ENABLE_PROJECTS "${LLVM_RELEASE_ENABLE_PROJECTS}" STRING)
+if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux")
+ set_final_stage_var(CLANG_BOLT "INSTRUMENT" STRING)
+endif()
set_final_stage_var(CPACK_GENERATOR "TXZ" STRING)
set_final_stage_var(CPACK_ARCHIVE_THREADS "0" STRING)
clang/docs/BoundsSafety.rst
@@ -777,13 +777,13 @@ the transformed pseudo code of function ``alloc_buf()`` in the example below.
size_t count;
} sized_buf_t;
- void alloc_buf(sized_buf_t *sbuf, sized_t nelems) {
+ void alloc_buf(sized_buf_t *sbuf, size_t nelems) {
sbuf->buf = (int *)malloc(sizeof(int) * nelems);
sbuf->count = nelems;
}
// Transformed pseudo code:
- void alloc_buf(sized_buf_t *sbuf, sized_t nelems) {
+ void alloc_buf(sized_buf_t *sbuf, size_t nelems) {
// Materialize RHS values:
int *tmp_ptr = (int *)malloc(sizeof(int) * nelems);
int tmp_count = nelems;
@@ -959,7 +959,8 @@ that has the define.
#if defined(__has_feature) && __has_feature(bounds_safety)
#define __counted_by(T) __attribute__((__counted_by__(T)))
// ... other bounds annotations
- #else #define __counted_by(T) // defined as nothing
+ #else
+ #define __counted_by(T) // defined as nothing
// ... other bounds annotations
#endif
@@ -987,7 +988,7 @@ and it does not guarantee other types of memory safety properties. Consequently,
it may not prevent some of the secondary bounds safety violations caused by
other types of safety violations such as type confusion. For instance,
``-fbounds-safety`` does not perform type-safety checks on conversions between
-`__single`` pointers of different pointee types (e.g., ``char *__single`` →
+``__single`` pointers of different pointee types (e.g., ``char *__single`` →
``void *__single`` → ``int *__single``) beyond what the foundation languages
(C/C++) already offer.
@@ -1003,4 +1004,4 @@ Try it out
Your feedback on the programming model is valuable. You may want to follow the
instruction in :doc:`BoundsSafetyAdoptionGuide` to play with ``-fbounds-safety``
-and please send your feedback to `Yeoul Na <mailto:yeoul_na@apple.com>`_.
clang/docs/BoundsSafetyImplPlans.rst
@@ -134,7 +134,7 @@ same basic block and without side effect in between.
int *__counted_by(count) buf; size_t count;
} sized_buf_t;
- void alloc_buf(sized_buf_t *sbuf, sized_t nelems) {
+ void alloc_buf(sized_buf_t *sbuf, size_t nelems) {
sbuf->buf = (int *)malloc(sizeof(int) * nelems);
sbuf->count = nelems;
}
clang/docs/ClangFormatStyleOptions.rst
@@ -2182,6 +2182,24 @@ the configuration (without a prefix: ``Auto``).
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);
}
+.. _BinPackLongBracedList:
+
+**BinPackLongBracedList** (``Boolean``) :versionbadge:`clang-format 21` :ref:`¶ <BinPackLongBracedList>`
+ If ``BinPackLongBracedList`` is ``true`` it overrides
+ ``BinPackArguments`` if there are 20 or more items in a braced
+ initializer list.
+
+ .. code-block:: c++
+
+ BinPackLongBracedList: false vs. BinPackLongBracedList: true
+ vector<int> x{ vector<int> x{1, 2, ...,
+ 20, 21};
+ 1,
+ 2,
+ ...,
+ 20,
+ 21};
+
.. _BinPackParameters:
**BinPackParameters** (``BinPackParametersStyle``) :versionbadge:`clang-format 3.7` :ref:`¶ <BinPackParameters>`
@@ -4764,15 +4782,24 @@ the configuration (without a prefix: ``Auto``).
.. _Language:
**Language** (``LanguageKind``) :versionbadge:`clang-format 3.5` :ref:`¶ <Language>`
- Language, this format style is targeted at.
+ The language that this format style targets.
+
+ .. note::
+
+ You can specify the language (``C``, ``Cpp``, or ``ObjC``) for ``.h``
+ files by adding a ``// clang-format Language:`` line before the first
+ non-comment (and non-empty) line, e.g. ``// clang-format Language: Cpp``.
Possible values:
* ``LK_None`` (in configuration: ``None``)
Do not use.
+ * ``LK_C`` (in configuration: ``C``)
+ Should be used for C.
+
* ``LK_Cpp`` (in configuration: ``Cpp``)
- Should be used for C, C++.
+ Should be used for C++.
* ``LK_CSharp`` (in configuration: ``CSharp``)
Should be used for C#.
clang/docs/HLSL/FunctionCalls.rst
@@ -248,13 +248,14 @@ which is a term made up for HLSL. A cx-value is a temporary value which may be
the result of a cast, and stores its value back to an lvalue when the value
expires.
-To represent this concept in Clang we introduce a new ``HLSLOutParamExpr``. An
-``HLSLOutParamExpr`` has two forms, one with a single sub-expression and one
-with two sub-expressions.
+To represent this concept in Clang we introduce a new ``HLSLOutArgExpr``. An
+``HLSLOutArgExpr`` has three sub-expressions:
-The single sub-expression form is used when the argument expression and the
-function parameter are the same type, so no cast is required. As in this
-example:
+* An OpaqueValueExpr of the argument lvalue expression.
+* An OpaqueValueExpr of the copy-initialized parameter temporary.
+* A BinaryOpExpr assigning the first with the value of the second.
+
+Given this example:
.. code-block:: c++
@@ -267,23 +268,36 @@ example:
Init(V);
}
-The expected AST formulation for this code would be something like:
+The expected AST formulation for this code would be something like the example
+below. Due to the nature of OpaqueValueExpr nodes, the nodes repeat in the AST
+dump. The fake addresses ``0xSOURCE`` and ``0xTEMPORARY`` denote the source
+lvalue and argument temporary lvalue expressions.
.. code-block:: text
CallExpr 'void'
|-ImplicitCastExpr 'void (*)(int &)' <FunctionToPointerDecay>
| `-DeclRefExpr 'void (int &)' lvalue Function 'Init' 'void (int &)'
- |-HLSLOutParamExpr 'int' lvalue inout
- `-DeclRefExpr 'int' lvalue Var 'V' 'int'
-
-The ``HLSLOutParamExpr`` captures that the value is ``inout`` vs ``out`` to
-denote whether or not the temporary is initialized from the sub-expression. If
-no casting is required the sub-expression denotes the lvalue expression that the
-cx-value will be copied to when the value expires.
-
-The two sub-expression form of the AST node is required when the argument type
-is not the same as the parameter type. Given this example:
+ `-HLSLOutArgExpr <col:10> 'int' lvalue inout
+ |-OpaqueValueExpr 0xSOURCE <col:10> 'int' lvalue
+ | `-DeclRefExpr <col:10> 'int' lvalue Var 'V' 'int'
+ |-OpaqueValueExpr 0xTEMPORARY <col:10> 'int' lvalue
+ | `-ImplicitCastExpr <col:10> 'int' <LValueToRValue>
+ | `-OpaqueValueExpr 0xSOURCE <col:10> 'int' lvalue
+ | `-DeclRefExpr <col:10> 'int' lvalue Var 'V' 'int'
+ `-BinaryOperator <col:10> 'int' lvalue '='
+ |-OpaqueValueExpr 0xSOURCE <col:10> 'int' lvalue
+ | `-DeclRefExpr <col:10> 'int' lvalue Var 'V' 'int'
+ `-ImplicitCastExpr <col:10> 'int' <LValueToRValue>
+ `-OpaqueValueExpr 0xTEMPORARY <col:10> 'int' lvalue
+ `-ImplicitCastExpr <col:10> 'int' <LValueToRValue>
+ `-OpaqueValueExpr 0xSOURCE <col:10> 'int' lvalue
+ `-DeclRefExpr <col:10> 'int' lvalue Var 'V' 'int'
+
+The ``HLSLOutArgExpr`` captures that the value is ``inout`` vs ``out`` to
+denote whether or not the temporary is initialized from the sub-expression.
+
+The example below demonstrates argument casting:
.. code-block:: c++
@@ -295,7 +309,7 @@ is not the same as the parameter type. Given this example:
Trunc(F);
}
-For this case the ``HLSLOutParamExpr`` will have sub-expressions to record both
+For this case the ``HLSLOutArgExpr`` will have sub-expressions to record both
casting expression sequences for the initialization and write back:
.. code-block:: text
@@ -303,20 +317,31 @@ casting expression sequences for the initialization and write back:
-CallExpr 'void'
|-ImplicitCastExpr 'void (*)(int3 &)' <FunctionToPointerDecay>
| `-DeclRefExpr 'void (int3 &)' lvalue Function 'inc_i32' 'void (int3 &)'
- `-HLSLOutParamExpr 'int3' lvalue inout
- |-ImplicitCastExpr 'float3' <IntegralToFloating>
- | `-ImplicitCastExpr 'int3' <LValueToRValue>
- | `-OpaqueValueExpr 'int3' lvalue
- `-ImplicitCastExpr 'int3' <FloatingToIntegral>
- `-ImplicitCastExpr 'float3' <LValueToRValue>
- `-DeclRefExpr 'float3' lvalue 'F' 'float3'
-
-In this formation the write-back casts are captured as the first sub-expression
-and they cast from an ``OpaqueValueExpr``. In IR generation we can use the
-``OpaqueValueExpr`` as a placeholder for the ``HLSLOutParamExpr``'s temporary
-value on function return.
-
-In code generation this can be implemented with some targeted extensions to the
-Objective-C write-back support. Specifically extending CGCall.cpp's
-``EmitWriteback`` function to support casting expressions and emission of
-aggregate lvalues.
+ `-HLSLOutArgExpr <col:11> 'int3':'vector<int, 3>' lvalue inout
+ |-OpaqueValueExpr 0xSOURCE <col:11> 'float3':'vector<float, 3>' lvalue
+ | `-DeclRefExpr <col:11> 'float3':'vector<float, 3>' lvalue Var 'F' 'float3':'vector<float, 3>'
+ |-OpaqueValueExpr 0xTEMPORARY <col:11> 'int3':'vector<int, 3>' lvalue
+ | `-ImplicitCastExpr <col:11> 'vector<int, 3>' <FloatingToIntegral>
+ | `-ImplicitCastExpr <col:11> 'float3':'vector<float, 3>' <LValueToRValue>
+ | `-OpaqueValueExpr 0xSOURCE <col:11> 'float3':'vector<float, 3>' lvalue
+ | `-DeclRefExpr <col:11> 'float3':'vector<float, 3>' lvalue Var 'F' 'float3':'vector<float, 3>'
+ `-BinaryOperator <col:11> 'float3':'vector<float, 3>' lvalue '='
+ |-OpaqueValueExpr 0xSOURCE <col:11> 'float3':'vector<float, 3>' lvalue
+ | `-DeclRefExpr <col:11> 'float3':'vector<float, 3>' lvalue Var 'F' 'float3':'vector<float, 3>'
+ `-ImplicitCastExpr <col:11> 'vector<float, 3>' <IntegralToFloating>
+ `-ImplicitCastExpr <col:11> 'int3':'vector<int, 3>' <LValueToRValue>
+ `-OpaqueValueExpr 0xTEMPORARY <col:11> 'int3':'vector<int, 3>' lvalue
+ `-ImplicitCastExpr <col:11> 'vector<int, 3>' <FloatingToIntegral>
+ `-ImplicitCastExpr <col:11> 'float3':'vector<float, 3>' <LValueToRValue>
+ `-OpaqueValueExpr 0xSOURCE <col:11> 'float3':'vector<float, 3>' lvalue
+ `-DeclRefExpr <col:11> 'float3':'vector<float, 3>' lvalue Var 'F' 'float3':'vector<float, 3>'
+
+The AST representation is the same whether casting is required or not, which
+simplifies the code generation. IR generation does the following:
+
+* Emit the argument lvalue expression.
+* Initialize the argument:
+ * For ``inout`` arguments, emit the copy-initialization expression.
+ * For ``out`` arguments, emit an uninitialized temporary.
+* Emit the call
+* Emit the write-back BinaryOperator expression.
clang/docs/LanguageExtensions.rst
@@ -1640,6 +1640,7 @@ Conditional ``explicit`` __cpp_conditional_explicit C+
``static operator()`` __cpp_static_call_operator C++23 C++03
Attributes on Lambda-Expressions C++23 C++11
Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03
+Packs in Structured Bindings __cpp_structured_bindings C++26 C++03
Static assert with user-generated message __cpp_static_assert >= 202306L C++26 C++11
Pack Indexing __cpp_pack_indexing C++26 C++03
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
@@ -2629,7 +2630,7 @@ with the current table size.
.. code-block:: c++
typedef void (*__funcref funcref_t)();
- static __funcref table[0];
+ static funcref_t table[0];
size_t getSize() {
return __builtin_wasm_table_size(table);
@@ -2651,10 +2652,10 @@ or -1. It will return -1 if not enough space could be allocated.
.. code-block:: c++
typedef void (*__funcref funcref_t)();
- static __funcref table[0];
+ static funcref_t table[0];
// grow returns the new table size or -1 on error.
- int grow(__funcref fn, int delta) {
+ int grow(funcref_t fn, int delta) {
int prevSize = __builtin_wasm_table_grow(table, fn, delta);
if (prevSize == -1)
return -1;
@@ -2866,6 +2867,47 @@ etc.).
Query for this feature with ``__has_builtin(__builtin_assume_separate_storage)``.
+``__builtin_assume_dereferenceable``
+-------------------------------------
+
+``__builtin_assume_dereferenceable`` is used to provide the optimizer with the
+knowledge that the pointer argument P is dereferenceable up to at least the
+specified number of bytes.
+
+**Syntax**:
+
+.. code-block:: c++
+
+ __builtin_assume_dereferenceable(const void *, size_t)
+
+**Example of Use**:
+
+.. code-block:: c++
+
+ int foo(int *x, int y) {
+ __builtin_assume_dereferenceable(x, sizeof(int));
+ int z = 0;
+ if (y == 1) {
+ // The optimizer may execute the load of x unconditionally due to
+ // __builtin_assume_dereferenceable guaranteeing sizeof(int) bytes can
+ // be loaded speculatively without trapping.
+ z = *x;
+ }
+ return z;
+ }
+
+**Description**:
+
+The arguments to this function provide a start pointer ``P`` and a size ``S``.
+``S`` must be at least 1 and a constant. The optimizer may assume that ``S``
+bytes are dereferenceable starting at ``P``. Note that this does not necessarily
+imply that ``P`` is non-null as ``nullptr`` can be dereferenced in some cases.
+The assumption also does not imply that ``P`` is not dereferenceable past ``S``
+bytes.
+
+
+Query for this feature with ``__has_builtin(__builtin_assume_dereferenceable)``.
+
``__builtin_offsetof``
----------------------
clang/docs/OpenMPSupport.rst
@@ -364,6 +364,8 @@ implementation.
+=============================================================+===========================+===========================+==========================================================================+
| free-agent threads | :none:`unclaimed` | :none:`unclaimed` | |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
+| threadset clause | :`worked on` | :none:`unclaimed` | |
++-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| Recording of task graphs | :none:`unclaimed` | :none:`unclaimed` | |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| Parallel inductions | :none:`unclaimed` | :none:`unclaimed` | |
@@ -372,6 +374,8 @@ implementation.
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| Loop transformation constructs | :none:`unclaimed` | :none:`unclaimed` | |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
+| loop stripe transformation | :good:`done` | https://github.com/llvm/llvm-project/pull/119891 |
++-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| work distribute construct | :none:`unclaimed` | :none:`unclaimed` | |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| task_iteration | :none:`unclaimed` | :none:`unclaimed` | |
@@ -410,13 +414,13 @@ implementation.
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| Extensions to interop construct | :none:`unclaimed` | :none:`unclaimed` | |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
-| no_openmp_constructs | :none:`unclaimed` | :none:`unclaimed` | |
+| no_openmp_constructs | :good:`done` | :none:`unclaimed` | https://github.com/llvm/llvm-project/pull/125933 |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| safe_sync and progress with identifier and API | :none:`unclaimed` | :none:`unclaimed` | |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
-| OpenMP directives in concurrent loop regions | :none:`unclaimed` | :none:`unclaimed` | |
+| OpenMP directives in concurrent loop regions | :good:`done` | :none:`unclaimed` | https://github.com/llvm/llvm-project/pull/125621 |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
-| atomics constructs on concurrent loop regions | :none:`unclaimed` | :none:`unclaimed` | |
+| atomics constructs on concurrent loop regions | :good:`done` | :none:`unclaimed` | https://github.com/llvm/llvm-project/pull/125621 |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| Loop construct with DO CONCURRENT | :none:`unclaimed` | :none:`unclaimed` | |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
@@ -454,9 +458,7 @@ implementation.
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| map-type modifiers in arbitrary position | :none:`unclaimed` | :none:`unclaimed` | |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
-| atomic constructs in loop region | :none:`unclaimed` | :none:`unclaimed` | |
-+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
-| Lift nesting restriction on concurrent loop | :none:`unclaimed` | :none:`unclaimed` | |
+| Lift nesting restriction on concurrent loop | :good:`done` | :none:`unclaimed` | https://github.com/llvm/llvm-project/pull/125621 |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| priority clause for target constructs | :none:`unclaimed` | :none:`unclaimed` | |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
clang/docs/ReleaseNotes.rst
@@ -54,6 +54,8 @@ ABI Changes in This Version
AST Dumping Potentially Breaking Changes
----------------------------------------
+- Added support for dumping template arguments of structural value kinds.
+
Clang Frontend Potentially Breaking Changes
-------------------------------------------
@@ -69,6 +71,8 @@ C++ Language Changes
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
+- Implemented `P1061R10 Structured Bindings can introduce a Pack <https://wg21.link/P1061R10>`_.
+
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
@@ -91,6 +95,7 @@ C Language Changes
- Clang now allows an ``inline`` specifier on a typedef declaration of a
function type in Microsoft compatibility mode. #GH124869
+- Clang now allows ``restrict`` qualifier for array types with pointer elements (#GH92847).
C2y Feature Support
^^^^^^^^^^^^^^^^^^^
@@ -104,6 +109,10 @@ Non-comprehensive list of changes in this release
New Compiler Flags
------------------
+- New option ``-fprofile-continuous`` added to enable continuous profile syncing to file (#GH124353, `docs <https://clang.llvm.org/docs/UsersManual.html#cmdoption-fprofile-continuous>`_).
+ The feature has `existed <https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#running-the-instrumented-program>`_)
+ for a while and this is just a user facing option.
+
Deprecated Compiler Flags
-------------------------
@@ -115,8 +124,59 @@ Removed Compiler Flags
Attribute Changes in Clang
--------------------------
+Adding [[clang::unsafe_buffer_usage]] attribute to a method definition now turns off all -Wunsafe-buffer-usage
+related warnings within the method body.
- The ``no_sanitize`` attribute now accepts both ``gnu`` and ``clang`` names.
+- Clang now diagnoses use of declaration attributes on void parameters. (#GH108819)
+- Clang now allows ``__attribute__((model("small")))`` and
+ ``__attribute__((model("large")))`` on non-TLS globals in x86-64 compilations.
+ This forces the global to be considered small or large in regards to the
+ x86-64 code model, regardless of the code model specified for the compilation.
+
+- There is a new ``format_matches`` attribute to complement the existing
+ ``format`` attribute. ``format_matches`` allows the compiler to verify that
+ a format string argument is equivalent to a reference format string: it is
+ useful when a function accepts a format string without its accompanying
+ arguments to format. For instance:
+
+ .. code-block:: c
+
+ static int status_code;
+ static const char *status_string;
+
+ void print_status(const char *fmt) {
+ fprintf(stderr, fmt, status_code, status_string);
+ // ^ warning: format string is not a string literal [-Wformat-nonliteral]
+ }
+
+ void stuff(void) {
+ print_status("%s (%#08x)\n");
+ // order of %s and %x is swapped but there is no diagnostic
+ }
+
+ Before the introducion of ``format_matches``, this code cannot be verified
+ at compile-time. ``format_matches`` plugs that hole:
+
+ .. code-block:: c
+
+ __attribute__((format_matches(printf, 1, "%x %s")))
+ void print_status(const char *fmt) {
+ fprintf(stderr, fmt, status_code, status_string);
+ // ^ `fmt` verified as if it was "%x %s" here; no longer triggers
+ // -Wformat-nonliteral, would warn if arguments did not match "%x %s"
+ }
+
+ void stuff(void) {
+ print_status("%s (%#08x)\n");
+ // warning: format specifier 's' is incompatible with 'x'
+ // warning: format specifier 'x' is incompatible with 's'
+ }
+
+ Like with ``format``, the first argument is the format string flavor and the
+ second argument is the index of the format string parameter.
+ ``format_matches`` accepts an example valid format string as its third
+ argument. For more information, see the Clang attributes documentation.
Improvements to Clang's diagnostics
-----------------------------------
@@ -127,6 +187,14 @@ Improvements to Clang's diagnostics
- The ``-Wunique-object-duplication`` warning has been added to warn about objects
which are supposed to only exist once per program, but may get duplicated when
built into a shared library.
+- Fixed a bug where Clang's Analysis did not correctly model the destructor behavior of ``union`` members (#GH119415).
+- A statement attribute applied to a ``case`` label no longer suppresses
+ 'bypassing variable initialization' diagnostics (#84072).
+- The ``-Wunsafe-buffer-usage`` warning has been updated to warn
+ about unsafe libc function calls. Those new warnings are emitted
+ under the subgroup ``-Wunsafe-buffer-usage-in-libc-call``.
+- Diagnostics on chained comparisons (``a < b < c``) are now an error by default. This can be disabled with
+ ``-Wno-error=parentheses``.
Improvements to Clang's time-trace
----------------------------------
@@ -137,6 +205,9 @@ Improvements to Coverage Mapping
Bug Fixes in This Version
-------------------------
+- Clang now outputs correct values when #embed data contains bytes with negative
+ signed char values (#GH102798).
+
Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -150,9 +221,15 @@ Bug Fixes to C++ Support
^^^^^^^^^^^^^^^^^^^^^^^^
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
+- Clang now prints the correct instantiation context for diagnostics suppressed
+ by template argument deduction.
+- The initialization kind of elements of structured bindings
+ direct-list-initialized from an array is corrected to direct-initialization.
+- Clang no longer crashes when a coroutine is declared ``[[noreturn]]``. (#GH127327)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
+- Fixed type checking when a statement expression ends in an l-value of atomic type. (#GH106576)
Miscellaneous Bug Fixes
^^^^^^^^^^^^^^^^^^^^^^^
@@ -202,6 +279,8 @@ LoongArch Support
RISC-V Support
^^^^^^^^^^^^^^
+- Add support for `-mtune=generic-ooo` (a generic out-of-order model).
+
CUDA/HIP Language Changes
^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -236,10 +315,19 @@ clang-format
------------
- Adds ``BreakBeforeTemplateCloser`` option.
+- Adds ``BinPackLongBracedList`` option to override bin packing options in
+ long (20 item or more) braced list initializer lists.
+- Add the C language instead of treating it like C++.
+- Allow specifying the language (C, C++, or Objective-C) for a ``.h`` file by
+ adding a special comment (e.g. ``// clang-format Language: ObjC``) near the
+ top of the file.
libclang
--------
+- Fixed a buffer overflow in ``CXString`` implementation. The fix may result in
+ increased memory allocation.
+
Code Completion
---------------
@@ -278,6 +366,8 @@ Python Binding Changes
OpenMP Support
--------------
+- Added support 'no_openmp_constructs' assumption clause.
+- Added support for 'omp stripe' directive.
Improvements
^^^^^^^^^^^^
clang/docs/SourceBasedCodeCoverage.rst
@@ -94,6 +94,11 @@ directory structure will be created. Additionally, the following special
not specified (i.e the pattern is "%m"), it's assumed that ``N = 1``. The
merge pool specifier can only occur once per filename pattern.
+* "%b" expands out to the binary ID (build ID). It can be used with "%Nm" to
+ avoid binary signature collisions. To use it, the program should be compiled
+ with the build ID linker option (``--build-id`` for GNU ld or LLD,
+ ``/build-id`` for lld-link on Windows). Linux, Windows and AIX are supported.
+
* "%c" expands out to nothing, but enables a mode in which profile counter
updates are continuously synced to a file. This means that if the
instrumented program crashes, or is killed by a signal, perfect coverage
clang/docs/TypeSanitizer.rst
@@ -27,7 +27,7 @@ reduce these impacts.
The TypeSanitizer Algorithm
===========================
For each TBAA type-access descriptor, encoded in LLVM IR using TBAA Metadata, the instrumentation
-pass generates descriptor tales. Thus there is a unique pointer to each type (and access descriptor).
+pass generates descriptor tables. Thus there is a unique pointer to each type (and access descriptor).
These tables are comdat (except for anonymous-namespace types), so the pointer values are unique
across the program.
clang/docs/UsersManual.rst
@@ -1681,19 +1681,27 @@ for more details.
permitted to produce more precise results than performing the same
operations separately.
- The C standard permits intermediate floating-point results within an
+ The C and C++ standards permit intermediate floating-point results within an
expression to be computed with more precision than their type would
normally allow. This permits operation fusing, and Clang takes advantage
- of this by default. This behavior can be controlled with the ``FP_CONTRACT``
- and ``clang fp contract`` pragmas. Please refer to the pragma documentation
- for a description of how the pragmas interact with this option.
+ of this by default (``on``). Fusion across statements is not compliant with
+ the C and C++ standards but can be enabled using ``-ffp-contract=fast``.
+
+ Fusion can be controlled with the ``FP_CONTRACT`` and ``clang fp contract``
+ pragmas. Please note that pragmas will be ingored with
+ ``-ffp-contract=fast``, and refer to the pragma documentation for a
+ description of how the pragmas interact with the different ``-ffp-contract``
+ option values.
Valid values are:
- * ``fast`` (fuse across statements disregarding pragmas, default for CUDA)
- * ``on`` (fuse in the same statement unless dictated by pragmas, default for languages other than CUDA/HIP)
- * ``off`` (never fuse)
- * ``fast-honor-pragmas`` (fuse across statements unless dictated by pragmas, default for HIP)
+ * ``fast``: enable fusion across statements disregarding pragmas, breaking
+ compliance with the C and C++ standards (default for CUDA).
+ * ``on``: enable C and C++ standard complaint fusion in the same statement
+ unless dictated by pragmas (default for languages other than CUDA/HIP)
+ * ``off``: disable fusion
+ * ``fast-honor-pragmas``: fuse across statements unless dictated by pragmas
+ (default for HIP)
.. option:: -f[no-]honor-infinities
@@ -2965,7 +2973,8 @@ instrumentation:
environment variable to specify an alternate file. If non-default file name
is specified by both the environment variable and the command line option,
the environment variable takes precedence. The file name pattern specified
- can include different modifiers: ``%p``, ``%h``, ``%m``, ``%t``, and ``%c``.
+ can include different modifiers: ``%p``, ``%h``, ``%m``, ``%b``, ``%t``, and
+ ``%c``.
Any instance of ``%p`` in that file name will be replaced by the process
ID, so that you can easily distinguish the profile output from multiple
@@ -2987,11 +2996,11 @@ instrumentation:
``%p`` is that the storage requirement for raw profile data files is greatly
increased. To avoid issues like this, the ``%m`` specifier can used in the profile
name. When this specifier is used, the profiler runtime will substitute ``%m``
- with a unique integer identifier associated with the instrumented binary. Additionally,
+ with an integer identifier associated with the instrumented binary. Additionally,
multiple raw profiles dumped from different processes that share a file system (can be
on different hosts) will be automatically merged by the profiler runtime during the
dumping. If the program links in multiple instrumented shared libraries, each library
- will dump the profile data into its own profile data file (with its unique integer
+ will dump the profile data into its own profile data file (with its integer
id embedded in the profile name). Note that the merging enabled by ``%m`` is for raw
profile data generated by profiler runtime. The resulting merged "raw" profile data
file still needs to be converted to a different format expected by the compiler (
@@ -3001,6 +3010,12 @@ instrumentation:
$ LLVM_PROFILE_FILE="code-%m.profraw" ./code
+ Although rare, binary signatures used by the ``%m`` specifier can have
+ collisions. In this case, the ``%b`` specifier, which expands to the binary
+ ID (build ID in ELF and COFF), can be added. To use it, the program should be
+ compiled with the build ID linker option (``--build-id`` for GNU ld or LLD,
+ ``/build-id`` for lld-link on Windows). Linux, Windows and AIX are supported.
+
See `this <SourceBasedCodeCoverage.html#running-the-instrumented-program>`_ section
about the ``%t``, and ``%c`` modifiers.
@@ -3118,6 +3133,24 @@ indexed format, regardeless whether it is produced by frontend or the IR pass.
overhead. ``prefer-atomic`` will be transformed to ``atomic`` when supported
by the target, or ``single`` otherwise.
+.. option:: -fprofile-continuous
+
+ Enables the continuous instrumentation profiling where profile counter updates
+ are continuously synced to a file. This option sets any neccessary modifiers
+ (currently ``%c``) in the default profile filename and passes any necessary
+ flags to the middle-end to support this mode. Value profiling is not supported
+ in continuous mode.
+
+ .. code-block:: console
+
+ $ clang++ -O2 -fprofile-generate -fprofile-continuous code.cc -o code
+
+ Running ``./code`` will collect the profile and write it to the
+ ``default_xxxx.profraw`` file. However, if ``./code`` abruptly terminates or
+ does not call ``exit()``, in continuous mode the profile collected up to the
+ point of termination will be available in ``default_xxxx.profraw`` while in
+ the non-continuous mode, no profile file is generated.
+
.. option:: -ftemporal-profile
Enables the temporal profiling extension for IRPGO to improve startup time by
clang/docs/analyzer/checkers.rst
@@ -3668,6 +3668,51 @@ Here are some examples of situations that we warn about as they *might* be poten
RefCountable* uncounted = counted.get(); // warn
}
+alpha.webkit.UnretainedLocalVarsChecker
+"""""""""""""""""""""""""""""""""""""""
Bulk data, 316 bytes
+
+The rules of when to use and not to use RetainPtr are same as alpha.webkit.UncountedCallArgsChecker for ref-counted objects.
+
+These are examples of cases that we consider safe:
+
+ .. code-block:: cpp
+
+ void foo1() {
+ RetainPtr<NSObject> retained;
+ // The scope of unretained is EMBEDDED in the scope of retained.
+ {
+ NSObject* unretained = retained.get(); // ok
+ }
+ }
+
+ void foo2(RetainPtr<NSObject> retained_param) {
+ NSObject* unretained = retained_param.get(); // ok
+ }
+
+ void FooClass::foo_method() {
+ NSObject* unretained = this; // ok
+ }
+
+Here are some examples of situations that we warn about as they *might* be potentially unsafe. The logic is that either we're able to guarantee that a local variable is safe or it's considered unsafe.
+
+ .. code-block:: cpp
+
+ void foo1() {
+ NSObject* unretained = [[NSObject alloc] init]; // warn
+ }
+
+ NSObject* global_unretained;
+ void foo2() {
+ NSObject* unretained = global_unretained; // warn
+ }
+
+ void foo3() {
+ RetainPtr<NSObject> retained;
+ // The scope of unretained is not EMBEDDED in the scope of retained.
+ NSObject* unretained = retained.get(); // warn
+ }
+
Debug Checkers
---------------
clang/docs/analyzer/developer-docs/PerformanceInvestigation.rst
@@ -5,6 +5,9 @@ Performance Investigation
Multiple factors contribute to the time it takes to analyze a file with Clang Static Analyzer.
A translation unit contains multiple entry points, each of which take multiple steps to analyze.
+Performance analysis using ``-ftime-trace``
+===========================================
+
You can add the ``-ftime-trace=file.json`` option to break down the analysis time into individual entry points and steps within each entry point.
You can explore the generated JSON file in a Chromium browser using the ``chrome://tracing`` URL,
or using `speedscope <https://speedscope.app>`_.
@@ -19,9 +22,8 @@ Here is an example of a time trace produced with
.. code-block:: bash
:caption: Clang Static Analyzer invocation to generate a time trace of string.c analysis.
- clang -cc1 -nostdsysteminc -analyze -analyzer-constraints=range \
- -setup-static-analyzer -analyzer-checker=core,unix,alpha.unix.cstring,debug.ExprInspection \
- -verify ./clang/test/Analysis/string.c \
+ clang -cc1 -analyze -verify clang/test/Analysis/string.c \
+ -analyzer-checker=core,unix,alpha.unix.cstring,debug.ExprInspection \
-ftime-trace=trace.json -ftime-trace-granularity=1
.. image:: ../images/speedscope.png
@@ -45,3 +47,91 @@ Note: Both Chrome-tracing and speedscope tools might struggle with time traces a
Luckily, in most cases the default max-steps boundary of 225 000 produces the traces of approximately that size
for a single entry point.
You can use ``-analyze-function=get_global_options`` together with ``-ftime-trace`` to narrow down analysis to a specific entry point.
+
+
+Performance analysis using ``perf``
+===================================
+
+`Perf <https://perfwiki.github.io/main/>`_ is a tool for conducting sampling-based profiling.
+It's easy to start profiling, you only have 2 prerequisites.
+Build with ``-fno-omit-frame-pointer`` and debug info (``-g``).
+You can use release builds, but probably the easiest is to set the ``CMAKE_BUILD_TYPE=RelWithDebInfo``
+along with ``CMAKE_CXX_FLAGS="-fno-omit-frame-pointer"`` when configuring ``llvm``.
+Here is how to `get started <https://llvm.org/docs/CMake.html#quick-start>`_ if you are in trouble.
+
+.. code-block:: bash
+ :caption: Running the Clang Static Analyzer through ``perf`` to gather samples of the execution.
+
+ # -F: Sampling frequency, use `-F max` for maximal frequency
+ # -g: Enable call-graph recording for both kernel and user space
+ perf record -F 99 -g -- clang -cc1 -analyze -verify clang/test/Analysis/string.c \
+ -analyzer-checker=core,unix,alpha.unix.cstring,debug.ExprInspection
+
+Once you have the profile data, you can use it to produce a Flame graph.
+A Flame graph is a visual representation of the stack frames of the samples.
+Common stack frame prefixes are squashed together, making up a wider bar.
+The wider the bar, the more time was spent under that particular stack frame,
+giving a sense of how the overall execution time was spent.
+
+Clone the `FlameGraph <https://github.com/brendangregg/FlameGraph>`_ git repository,
+as we will use some scripts from there to convert the ``perf`` samples into a Flame graph.
+It's also useful to check out Brendan Gregg's (the author of FlameGraph)
+`homepage <https://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html>`_.
+
+
+.. code-block:: bash
+ :caption: Converting the ``perf`` profile into a Flamegraph, then opening it in Firefox.
+
+ perf script | /path/to/FlameGraph/stackcollapse-perf.pl > perf.folded
+ /path/to/FlameGraph/flamegraph.pl perf.folded > perf.svg
+ firefox perf.svg
+
+.. image:: ../images/flamegraph.png
+
+
+Performance analysis using ``uftrace``
+======================================
+
+`uftrace <https://github.com/namhyung/uftrace/wiki/Tutorial#getting-started>`_ is a great tool to generate rich profile data
+that you can use to focus and drill down into the timeline of your application.
+We will use it to generate Chromium trace JSON.
+In contrast to ``perf``, this approach statically instruments every function, so it should be more precise and thorough than the sampling-based approaches like ``perf``.
+In contrast to using ``-ftime-trace``, functions don't need to opt-in to be profiled using ``llvm::TimeTraceScope``.
+All functions are profiled due to automatic static instrumentation.
+
+There is only one prerequisite to use this tool.
+You need to build the binary you are about to instrument using ``-pg`` or ``-finstrument-functions``.
+This will make it run substantially slower but allows rich instrumentation.
+It will also consume many gigabites of storage for a single trace unless filter flags are used during recording.
+
+.. code-block:: bash
+ :caption: Recording with ``uftrace``, then dumping the result as a Chrome trace JSON.
+
+ uftrace record clang -cc1 -analyze -verify clang/test/Analysis/string.c \
+ -analyzer-checker=core,unix,alpha.unix.cstring,debug.ExprInspection
+ uftrace dump --filter=".*::AnalysisConsumer::HandleTranslationUnit" --time-filter=300 --chrome > trace.json
+
+.. image:: ../images/uftrace_detailed.png
+
+In this picture, you can see the functions below the Static Analyzer's entry point, which takes at least 300 nanoseconds to run, visualized by Chrome's ``about:tracing`` page
+You can also see how deep function calls we may have due to AST visitors.
+
+Using different filters can reduce the number of functions to record.
+For the common options, refer to the ``uftrace`` `documentation <https://github.com/namhyung/uftrace/blob/master/doc/uftrace-record.md#common-options>`_.
+
+Similar filters can be applied for dumping too. That way you can reuse the same (detailed)
+recording to selectively focus on some special part using a refinement of the filter flags.
+Remember, the trace JSON needs to fit into Chrome's ``about:tracing`` or `speedscope <https://speedscope.app>`_,
+thus it needs to be of a limited size.
+If you do not apply filters on recording, you will collect a large trace and every dump operation
+would need to sieve through the much larger recording which may be annoying if done repeatedly.
+
+If the trace JSON is still too large to load, have a look at the dump as plain text and look for frequent entries that refer to non-interesting parts.
+Once you have some of those, add them as ``--hide`` flags to the ``uftrace dump`` call.
+To see what functions appear frequently in the trace, use this command:
+
+.. code-block:: bash
+
+ cat trace.json | grep -Po '"name":"(.+)"' | sort | uniq -c | sort -nr | head -n 50
+
+``uftrace`` can also dump the report as a Flame graph using ``uftrace dump --framegraph``.
clang/include/clang-c/Index.h
@@ -2158,6 +2158,10 @@ enum CXCursorKind {
*/
CXCursor_OMPAssumeDirective = 309,
+ /** OpenMP assume directive.
+ */
+ CXCursor_OMPStripeDirective = 310,
+
/** OpenACC Compute Construct.
*/
CXCursor_OpenACCComputeConstruct = 320,
clang/include/clang/AST/ASTContext.h
@@ -287,8 +287,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// This is lazily created. This is intentionally not serialized.
mutable llvm::DenseMap<const RecordDecl*, const ASTRecordLayout*>
ASTRecordLayouts;
- mutable llvm::DenseMap<const ObjCContainerDecl*, const ASTRecordLayout*>
- ObjCLayouts;
+ mutable llvm::DenseMap<const ObjCInterfaceDecl *, const ASTRecordLayout *>
+ ObjCLayouts;
/// A cache from types to size and alignment information.
using TypeInfoMap = llvm::DenseMap<const Type *, struct TypeInfo>;
@@ -500,6 +500,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
static constexpr unsigned GeneralTypesLog2InitSize = 9;
static constexpr unsigned FunctionProtoTypesLog2InitSize = 12;
+ /// A mapping from an ObjC class to its subclasses.
+ llvm::DenseMap<const ObjCInterfaceDecl *,
+ SmallVector<const ObjCInterfaceDecl *, 4>>
+ ObjCSubClasses;
+
ASTContext &this_() { return *this; }
public:
@@ -1733,6 +1738,47 @@ public:
unsigned NumPositiveBits, QualType &BestType,
QualType &BestPromotionType);
+ /// Determine whether the given integral value is representable within
+ /// the given type T.
+ bool isRepresentableIntegerValue(llvm::APSInt &Value, QualType T);
+
+ /// Compute NumNegativeBits and NumPositiveBits for an enum based on
+ /// the constant values of its enumerators.
+ template <typename RangeT>
+ bool computeEnumBits(RangeT EnumConstants, unsigned &NumNegativeBits,
+ unsigned &NumPositiveBits) {
+ NumNegativeBits = 0;
+ NumPositiveBits = 0;
+ bool MembersRepresentableByInt = true;
+ for (auto *Elem : EnumConstants) {
+ EnumConstantDecl *ECD = cast_or_null<EnumConstantDecl>(Elem);
+ if (!ECD)
+ continue; // Already issued a diagnostic.
+
+ llvm::APSInt InitVal = ECD->getInitVal();
+ if (InitVal.isUnsigned() || InitVal.isNonNegative()) {
+ // If the enumerator is zero that should still be counted as a positive
+ // bit since we need a bit to store the value zero.
+ unsigned ActiveBits = InitVal.getActiveBits();
+ NumPositiveBits = std::max({NumPositiveBits, ActiveBits, 1u});
+ } else {
+ NumNegativeBits =
+ std::max(NumNegativeBits, (unsigned)InitVal.getSignificantBits());
+ }
+
+ MembersRepresentableByInt &= isRepresentableIntegerValue(InitVal, IntTy);
+ }
+
+ // If we have an empty set of enumerators we still need one bit.
+ // From [dcl.enum]p8
+ // If the enumerator-list is empty, the values of the enumeration are as if
+ // the enumeration had a single enumerator with value 0
+ if (!NumPositiveBits && !NumNegativeBits)
+ NumPositiveBits = 1;
+
+ return MembersRepresentableByInt;
+ }
+
QualType
getUnresolvedUsingType(const UnresolvedUsingTypenameDecl *Decl) const;
@@ -2630,13 +2676,6 @@ public:
void DumpRecordLayout(const RecordDecl *RD, raw_ostream &OS,
bool Simple = false) const;
- /// Get or compute information about the layout of the specified
- /// Objective-C implementation.
- ///
- /// This may differ from the interface if synthesized ivars are present.
- const ASTRecordLayout &
- getASTObjCImplementationLayout(const ObjCImplementationDecl *D) const;
-
/// Get our current best idea for the key function of the
/// given record decl, or nullptr if there isn't one.
///
@@ -2675,7 +2714,6 @@ public:
/// Get the offset of an ObjCIvarDecl in bits.
uint64_t lookupFieldBitOffset(const ObjCInterfaceDecl *OID,
- const ObjCImplementationDecl *ID,
const ObjCIvarDecl *Ivar) const;
/// Find the 'this' offset for the member path in a pointer-to-member
@@ -3133,7 +3171,12 @@ public:
bool &CanUseFirst, bool &CanUseSecond,
SmallVectorImpl<FunctionProtoType::ExtParameterInfo> &NewParamInfos);
- void ResetObjCLayout(const ObjCContainerDecl *CD);
+ void ResetObjCLayout(const ObjCInterfaceDecl *D);
+
+ void addObjCSubClass(const ObjCInterfaceDecl *D,
+ const ObjCInterfaceDecl *SubClass) {
+ ObjCSubClasses[D].push_back(SubClass);
+ }
//===--------------------------------------------------------------------===//
// Integer Predicates
@@ -3523,9 +3566,7 @@ private:
friend class DeclarationNameTable;
friend class DeclContext;
- const ASTRecordLayout &
- getObjCLayout(const ObjCInterfaceDecl *D,
- const ObjCImplementationDecl *Impl) const;
+ const ASTRecordLayout &getObjCLayout(const ObjCInterfaceDecl *D) const;
/// A set of deallocations that should be performed when the
/// ASTContext is destroyed.
clang/include/clang/AST/Decl.h
@@ -5039,6 +5039,11 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
SourceLocation KwLoc;
/// IsCBuffer - Whether the buffer is a cbuffer (and not a tbuffer).
bool IsCBuffer;
+ /// HasValidPackoffset - Whether the buffer has valid packoffset annotations
+ // on all declarations
+ bool HasValidPackoffset;
+ // LayoutStruct - Layout struct for the buffer
+ CXXRecordDecl *LayoutStruct;
HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc,
IdentifierInfo *ID, SourceLocation IDLoc,
@@ -5059,6 +5064,10 @@ public:
SourceLocation getRBraceLoc() const { return RBraceLoc; }
void setRBraceLoc(SourceLocation L) { RBraceLoc = L; }
bool isCBuffer() const { return IsCBuffer; }
+ void setHasValidPackoffset(bool PO) { HasValidPackoffset = PO; }
+ bool hasValidPackoffset() const { return HasValidPackoffset; }
+ const CXXRecordDecl *getLayoutStruct() const { return LayoutStruct; }
+ void addLayoutStruct(CXXRecordDecl *LS);
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
clang/include/clang/AST/DeclBase.h
@@ -492,7 +492,7 @@ public:
/// perform non-Decl specific checks based on the object's type and strict
/// flex array level.
static bool isFlexibleArrayMemberLike(
- ASTContext &Context, const Decl *D, QualType Ty,
+ const ASTContext &Context, const Decl *D, QualType Ty,
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel,
bool IgnoreTemplateOrMacroSubstitution);
clang/include/clang/AST/DeclCXX.h
@@ -1720,14 +1720,6 @@ public:
/// static analysis, or similar.
bool hasMemberName(DeclarationName N) const;
- /// Performs an imprecise lookup of a dependent name in this class.
- ///
- /// This function does not follow strict semantic rules and should be used
- /// only when lookup rules can be relaxed, e.g. indexing.
- std::vector<const NamedDecl *>
- lookupDependentName(DeclarationName Name,
- llvm::function_ref<bool(const NamedDecl *ND)> Filter);
-
/// Renders and displays an inheritance diagram
/// for this C++ class and all of its base classes (transitively) using
/// GraphViz.
@@ -4194,8 +4186,8 @@ public:
/// decomposition declaration, and when the initializer is type-dependent.
Expr *getBinding() const { return Binding; }
- // Get the array of Exprs when the binding represents a pack.
- llvm::ArrayRef<Expr *> getBindingPackExprs() const;
+ // Get the array of nested BindingDecls when the binding represents a pack.
+ llvm::ArrayRef<BindingDecl *> getBindingPackDecls() const;
/// Get the decomposition declaration that this binding represents a
/// decomposition of.
@@ -4246,10 +4238,8 @@ class DecompositionDecl final
for (auto *B : Bindings) {
B->setDecomposedDecl(this);
if (B->isParameterPack() && B->getBinding()) {
- for (Expr *E : B->getBindingPackExprs()) {
- auto *DRE = cast<DeclRefExpr>(E);
- auto *NestedB = cast<BindingDecl>(DRE->getDecl());
- NestedB->setDecomposedDecl(this);
+ for (BindingDecl *NestedBD : B->getBindingPackDecls()) {
+ NestedBD->setDecomposedDecl(this);
}
}
}
@@ -4278,25 +4268,21 @@ public:
// Provide a flattened range to visit each binding.
auto flat_bindings() const {
llvm::ArrayRef<BindingDecl *> Bindings = bindings();
- llvm::ArrayRef<Expr *> PackExprs;
+ llvm::ArrayRef<BindingDecl *> PackBindings;
// Split the bindings into subranges split by the pack.
- auto S1 = Bindings.take_until(
+ llvm::ArrayRef<BindingDecl *> BeforePackBindings = Bindings.take_until(
[](BindingDecl *BD) { return BD->isParameterPack(); });
- Bindings = Bindings.drop_front(S1.size());
+ Bindings = Bindings.drop_front(BeforePackBindings.size());
if (!Bindings.empty()) {
- PackExprs = Bindings.front()->getBindingPackExprs();
+ PackBindings = Bindings.front()->getBindingPackDecls();
Bindings = Bindings.drop_front();
}
- auto S2 = llvm::map_range(PackExprs, [](Expr *E) {
- auto *DRE = cast<DeclRefExpr>(E);
- return cast<BindingDecl>(DRE->getDecl());
- });
-
- return llvm::concat<BindingDecl *>(std::move(S1), std::move(S2),
- std::move(Bindings));
+ return llvm::concat<BindingDecl *const>(std::move(BeforePackBindings),
+ std::move(PackBindings),
+ std::move(Bindings));
}
void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
clang/include/clang/AST/DeclTemplate.h
@@ -1960,6 +1960,8 @@ public:
bool hasStrictPackMatch() const { return StrictPackMatch; }
+ void setStrictPackMatch(bool Val) { StrictPackMatch = Val; }
+
/// Get the point of instantiation (if any), or null if none.
SourceLocation getPointOfInstantiation() const {
return PointOfInstantiation;
clang/include/clang/AST/Expr.h
@@ -542,7 +542,7 @@ public:
/// When IgnoreTemplateOrMacroSubstitution is set, it doesn't consider sizes
/// resulting from the substitution of a macro or a template as special sizes.
bool isFlexibleArrayMemberLike(
- ASTContext &Context,
+ const ASTContext &Context,
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel,
bool IgnoreTemplateOrMacroSubstitution = false) const;
@@ -4579,25 +4579,97 @@ public:
/// ConvertVectorExpr - Clang builtin function __builtin_convertvector
/// This AST node provides support for converting a vector type to another
/// vector type of the same arity.
-class ConvertVectorExpr : public Expr {
+class ConvertVectorExpr final
+ : public Expr,
+ private llvm::TrailingObjects<ConvertVectorExpr, FPOptionsOverride> {
private:
Stmt *SrcExpr;
TypeSourceInfo *TInfo;
SourceLocation BuiltinLoc, RParenLoc;
+ friend TrailingObjects;
friend class ASTReader;
friend class ASTStmtReader;
- explicit ConvertVectorExpr(EmptyShell Empty) : Expr(ConvertVectorExprClass, Empty) {}
+ explicit ConvertVectorExpr(bool HasFPFeatures, EmptyShell Empty)
+ : Expr(ConvertVectorExprClass, Empty) {
+ ConvertVectorExprBits.HasFPFeatures = HasFPFeatures;
+ }
-public:
ConvertVectorExpr(Expr *SrcExpr, TypeSourceInfo *TI, QualType DstType,
ExprValueKind VK, ExprObjectKind OK,
- SourceLocation BuiltinLoc, SourceLocation RParenLoc)
+ SourceLocation BuiltinLoc, SourceLocation RParenLoc,
+ FPOptionsOverride FPFeatures)
: Expr(ConvertVectorExprClass, DstType, VK, OK), SrcExpr(SrcExpr),
TInfo(TI), BuiltinLoc(BuiltinLoc), RParenLoc(RParenLoc) {
+ ConvertVectorExprBits.HasFPFeatures = FPFeatures.requiresTrailingStorage();
+ if (hasStoredFPFeatures())
+ setStoredFPFeatures(FPFeatures);
setDependence(computeDependence(this));
}
+ size_t numTrailingObjects(OverloadToken<FPOptionsOverride>) const {
+ return ConvertVectorExprBits.HasFPFeatures ? 1 : 0;
+ }
+
+ FPOptionsOverride &getTrailingFPFeatures() {
+ assert(ConvertVectorExprBits.HasFPFeatures);
+ return *getTrailingObjects<FPOptionsOverride>();
+ }
+
+ const FPOptionsOverride &getTrailingFPFeatures() const {
+ assert(ConvertVectorExprBits.HasFPFeatures);
+ return *getTrailingObjects<FPOptionsOverride>();
+ }
+
+public:
+ static ConvertVectorExpr *CreateEmpty(const ASTContext &C,
+ bool hasFPFeatures);
+
+ static ConvertVectorExpr *Create(const ASTContext &C, Expr *SrcExpr,
+ TypeSourceInfo *TI, QualType DstType,
+ ExprValueKind VK, ExprObjectKind OK,
+ SourceLocation BuiltinLoc,
+ SourceLocation RParenLoc,
+ FPOptionsOverride FPFeatures);
+
+ /// Get the FP contractibility status of this operator. Only meaningful for
+ /// operations on floating point types.
+ bool isFPContractableWithinStatement(const LangOptions &LO) const {
+ return getFPFeaturesInEffect(LO).allowFPContractWithinStatement();
+ }
+
+ /// Is FPFeatures in Trailing Storage?
+ bool hasStoredFPFeatures() const {
+ return ConvertVectorExprBits.HasFPFeatures;
+ }
+
+ /// Get FPFeatures from trailing storage.
+ FPOptionsOverride getStoredFPFeatures() const {
+ return getTrailingFPFeatures();
+ }
+
+ /// Get the store FPOptionsOverride or default if not stored.
+ FPOptionsOverride getStoredFPFeaturesOrDefault() const {
+ return hasStoredFPFeatures() ? getStoredFPFeatures() : FPOptionsOverride();
+ }
+
+ /// Set FPFeatures in trailing storage, used by Serialization & ASTImporter.
+ void setStoredFPFeatures(FPOptionsOverride F) { getTrailingFPFeatures() = F; }
+
+ /// Get the FP features status of this operator. Only meaningful for
+ /// operations on floating point types.
+ FPOptions getFPFeaturesInEffect(const LangOptions &LO) const {
+ if (ConvertVectorExprBits.HasFPFeatures)
+ return getStoredFPFeatures().applyOverrides(LO);
+ return FPOptions::defaultWithoutTrailingStorage(LO);
+ }
+
+ FPOptionsOverride getFPOptionsOverride() const {
+ if (ConvertVectorExprBits.HasFPFeatures)
+ return getStoredFPFeatures();
+ return FPOptionsOverride();
+ }
+
/// getSrcExpr - Return the Expr to be converted.
Expr *getSrcExpr() const { return cast<Expr>(SrcExpr); }
clang/include/clang/AST/ExprCXX.h
@@ -4633,8 +4633,8 @@ public:
}
};
-/// Represents a reference to a function parameter pack or init-capture pack
-/// that has been substituted but not yet expanded.
+/// Represents a reference to a function parameter pack, init-capture pack,
+/// or binding pack that has been substituted but not yet expanded.
///
/// When a pack expansion contains multiple parameter packs at different levels,
/// this node is used to represent a function parameter pack at an outer level
@@ -4649,13 +4649,13 @@ public:
/// \endcode
class FunctionParmPackExpr final
: public Expr,
- private llvm::TrailingObjects<FunctionParmPackExpr, VarDecl *> {
+ private llvm::TrailingObjects<FunctionParmPackExpr, ValueDecl *> {
friend class ASTReader;
friend class ASTStmtReader;
friend TrailingObjects;
/// The function parameter pack which was referenced.
- VarDecl *ParamPack;
+ ValueDecl *ParamPack;
/// The location of the function parameter pack reference.
SourceLocation NameLoc;
@@ -4663,35 +4663,34 @@ class FunctionParmPackExpr final
/// The number of expansions of this pack.
unsigned NumParameters;
- FunctionParmPackExpr(QualType T, VarDecl *ParamPack,
- SourceLocation NameLoc, unsigned NumParams,
- VarDecl *const *Params);
+ FunctionParmPackExpr(QualType T, ValueDecl *ParamPack, SourceLocation NameLoc,
+ unsigned NumParams, ValueDecl *const *Params);
public:
static FunctionParmPackExpr *Create(const ASTContext &Context, QualType T,
- VarDecl *ParamPack,
+ ValueDecl *ParamPack,
SourceLocation NameLoc,
- ArrayRef<VarDecl *> Params);
+ ArrayRef<ValueDecl *> Params);
static FunctionParmPackExpr *CreateEmpty(const ASTContext &Context,
unsigned NumParams);
/// Get the parameter pack which this expression refers to.
- VarDecl *getParameterPack() const { return ParamPack; }
+ ValueDecl *getParameterPack() const { return ParamPack; }
/// Get the location of the parameter pack.
SourceLocation getParameterPackLocation() const { return NameLoc; }
/// Iterators over the parameters which the parameter pack expanded
/// into.
- using iterator = VarDecl * const *;
- iterator begin() const { return getTrailingObjects<VarDecl *>(); }
+ using iterator = ValueDecl *const *;
+ iterator begin() const { return getTrailingObjects<ValueDecl *>(); }
iterator end() const { return begin() + NumParameters; }
/// Get the number of parameters in this parameter pack.
unsigned getNumExpansions() const { return NumParameters; }
/// Get an expansion of the parameter pack by index.
- VarDecl *getExpansion(unsigned I) const { return begin()[I]; }
+ ValueDecl *getExpansion(unsigned I) const { return begin()[I]; }
SourceLocation getBeginLoc() const LLVM_READONLY { return NameLoc; }
SourceLocation getEndLoc() const LLVM_READONLY { return NameLoc; }
@@ -5319,59 +5318,6 @@ public:
}
};
-// Represents an unexpanded pack where the list of expressions are
-// known. These are used when structured bindings introduce a pack.
-class ResolvedUnexpandedPackExpr final
- : public Expr,
- private llvm::TrailingObjects<ResolvedUnexpandedPackExpr, Expr *> {
- friend class ASTStmtReader;
- friend class ASTStmtWriter;
- friend TrailingObjects;
-
- SourceLocation BeginLoc;
- unsigned NumExprs;
-
- ResolvedUnexpandedPackExpr(SourceLocation BL, QualType QT, unsigned NumExprs);
-
-public:
- static ResolvedUnexpandedPackExpr *CreateDeserialized(ASTContext &C,
- unsigned NumExprs);
- static ResolvedUnexpandedPackExpr *
- Create(ASTContext &C, SourceLocation BeginLoc, QualType T, unsigned NumExprs);
- static ResolvedUnexpandedPackExpr *Create(ASTContext &C,
- SourceLocation BeginLoc, QualType T,
- llvm::ArrayRef<Expr *> Exprs);
-
- unsigned getNumExprs() const { return NumExprs; }
-
- llvm::MutableArrayRef<Expr *> getExprs() {
- return {getTrailingObjects<Expr *>(), NumExprs};
- }
-
- llvm::ArrayRef<Expr *> getExprs() const {
- return {getTrailingObjects<Expr *>(), NumExprs};
- }
-
- Expr *getExpansion(unsigned Idx) { return getExprs()[Idx]; }
- Expr *getExpansion(unsigned Idx) const { return getExprs()[Idx]; }
-
- // Iterators
- child_range children() {
- return child_range((Stmt **)getTrailingObjects<Expr *>(),
- (Stmt **)getTrailingObjects<Expr *>() + getNumExprs());
- }
-
- SourceLocation getBeginLoc() const LLVM_READONLY { return BeginLoc; }
- SourceLocation getEndLoc() const LLVM_READONLY { return BeginLoc; }
-
- // Returns the resolved pack of a decl or nullptr
- static ResolvedUnexpandedPackExpr *getFromDecl(Decl *);
-
- static bool classof(const Stmt *T) {
- return T->getStmtClass() == ResolvedUnexpandedPackExprClass;
- }
-};
-
} // namespace clang
#endif // LLVM_CLANG_AST_EXPRCXX_H
clang/include/clang/AST/FormatString.h
@@ -292,7 +292,7 @@ public:
};
private:
- const Kind K;
+ Kind K;
QualType T;
const char *Name = nullptr;
bool Ptr = false;
@@ -338,6 +338,7 @@ public:
}
MatchKind matchesType(ASTContext &C, QualType argTy) const;
+ MatchKind matchesArgType(ASTContext &C, const ArgType &other) const;
QualType getRepresentativeType(ASTContext &C) const;
clang/include/clang/AST/JSONNodeDumper.h
@@ -345,6 +345,7 @@ public:
void VisitDeclarationTemplateArgument(const TemplateArgument &TA);
void VisitNullPtrTemplateArgument(const TemplateArgument &TA);
void VisitIntegralTemplateArgument(const TemplateArgument &TA);
+ void VisitStructuralValueTemplateArgument(const TemplateArgument &TA);
void VisitTemplateTemplateArgument(const TemplateArgument &TA);
void VisitTemplateExpansionTemplateArgument(const TemplateArgument &TA);
void VisitExpressionTemplateArgument(const TemplateArgument &TA);
clang/include/clang/AST/Mangle.h
@@ -22,32 +22,29 @@
#include <optional>
namespace llvm {
- class raw_ostream;
+class raw_ostream;
}
namespace clang {
- class ASTContext;
- class BlockDecl;
- class CXXConstructorDecl;
- class CXXDestructorDecl;
- class CXXMethodDecl;
- class FunctionDecl;
- struct MethodVFTableLocation;
- class NamedDecl;
- class ObjCMethodDecl;
- class StringLiteral;
- struct ThisAdjustment;
- struct ThunkInfo;
- class VarDecl;
+class ASTContext;
+class BlockDecl;
+class CXXConstructorDecl;
+class CXXDestructorDecl;
+class CXXMethodDecl;
+class FunctionDecl;
+struct MethodVFTableLocation;
+class NamedDecl;
+class ObjCMethodDecl;
+class StringLiteral;
+struct ThisAdjustment;
+struct ThunkInfo;
+class VarDecl;
/// MangleContext - Context for tracking state which persists across multiple
/// calls to the C++ name mangler.
class MangleContext {
public:
- enum ManglerKind {
- MK_Itanium,
- MK_Microsoft
- };
+ enum ManglerKind { MK_Itanium, MK_Microsoft };
private:
virtual void anchor();
@@ -59,10 +56,10 @@ private:
/// ASTContext.
bool IsAux = false;
- llvm::DenseMap<const BlockDecl*, unsigned> GlobalBlockIds;
- llvm::DenseMap<const BlockDecl*, unsigned> LocalBlockIds;
- llvm::DenseMap<const NamedDecl*, uint64_t> AnonStructIds;
- llvm::DenseMap<const FunctionDecl*, unsigned> FuncAnonStructSize;
+ llvm::DenseMap<const BlockDecl *, unsigned> GlobalBlockIds;
+ llvm::DenseMap<const BlockDecl *, unsigned> LocalBlockIds;
+ llvm::DenseMap<const NamedDecl *, uint64_t> AnonStructIds;
+ llvm::DenseMap<const FunctionDecl *, unsigned> FuncAnonStructSize;
public:
ManglerKind getKind() const { return Kind; }
@@ -73,7 +70,7 @@ public:
ManglerKind Kind, bool IsAux = false)
: Context(Context), Diags(Diags), Kind(Kind), IsAux(IsAux) {}
- virtual ~MangleContext() { }
+ virtual ~MangleContext() {}
ASTContext &getASTContext() const { return Context; }
@@ -82,10 +79,10 @@ public:
virtual void startNewFunction() { LocalBlockIds.clear(); }
unsigned getBlockId(const BlockDecl *BD, bool Local) {
- llvm::DenseMap<const BlockDecl *, unsigned> &BlockIds
- = Local? LocalBlockIds : GlobalBlockIds;
+ llvm::DenseMap<const BlockDecl *, unsigned> &BlockIds =
+ Local ? LocalBlockIds : GlobalBlockIds;
std::pair<llvm::DenseMap<const BlockDecl *, unsigned>::iterator, bool>
- Result = BlockIds.insert(std::make_pair(BD, BlockIds.size()));
+ Result = BlockIds.insert(std::make_pair(BD, BlockIds.size()));
return Result.first->second;
}
@@ -125,7 +122,7 @@ public:
return false;
}
- virtual void needsUniqueInternalLinkageNames() { }
+ virtual void needsUniqueInternalLinkageNames() {}
// FIXME: consider replacing raw_ostream & with something like SmallString &.
void mangleName(GlobalDecl GD, raw_ostream &);
@@ -143,10 +140,9 @@ public:
virtual void mangleCXXRTTIName(QualType T, raw_ostream &,
bool NormalizeIntegers = false) = 0;
virtual void mangleStringLiteral(const StringLiteral *SL, raw_ostream &) = 0;
- virtual void mangleMSGuidDecl(const MSGuidDecl *GD, raw_ostream&);
+ virtual void mangleMSGuidDecl(const MSGuidDecl *GD, raw_ostream &);
- void mangleGlobalBlock(const BlockDecl *BD,
- const NamedDecl *ID,
+ void mangleGlobalBlock(const BlockDecl *BD, const NamedDecl *ID,
raw_ostream &Out);
void mangleCtorBlock(const CXXConstructorDecl *CD, CXXCtorType CT,
const BlockDecl *BD, raw_ostream &Out);
@@ -314,6 +310,6 @@ private:
class Implementation;
std::unique_ptr<Implementation> Impl;
};
-}
+} // namespace clang
#endif
clang/include/clang/AST/OpenMPClause.h
@@ -2423,6 +2423,28 @@ public:
OMPNoOpenMPRoutinesClause() : OMPNoChildClause() {}
};
+/// This represents the 'no_openmp_constructs' clause in the
+//// '#pragma omp assume' directive.
+///
+/// \code
+/// #pragma omp assume no_openmp_constructs
+/// \endcode
+/// In this example directive '#pragma omp assume' has a 'no_openmp_constructs'
+/// clause.
+class OMPNoOpenMPConstructsClause final
+ : public OMPNoChildClause<llvm::omp::OMPC_no_openmp_constructs> {
+public:
+ /// Build 'no_openmp_constructs' clause.
+ ///
+ /// \param StartLoc Starting location of the clause.
+ /// \param EndLoc Ending location of the clause.
+ OMPNoOpenMPConstructsClause(SourceLocation StartLoc, SourceLocation EndLoc)
+ : OMPNoChildClause(StartLoc, EndLoc) {}
+
+ /// Build an empty clause.
+ OMPNoOpenMPConstructsClause() : OMPNoChildClause() {}
+};
+
/// This represents the 'no_parallelism' clause in the '#pragma omp assume'
/// directive.
///
clang/include/clang/AST/OperationKinds.def
@@ -367,6 +367,12 @@ CAST_OPERATION(HLSLVectorTruncation)
// Non-decaying array RValue cast (HLSL only).
CAST_OPERATION(HLSLArrayRValue)
+// Aggregate by Value cast (HLSL only).
+CAST_OPERATION(HLSLElementwiseCast)
+
+// Splat cast for Aggregates (HLSL only).
+CAST_OPERATION(HLSLAggregateSplatCast)
+
//===- Binary Operations -------------------------------------------------===//
// Operators listed in order of precedence.
// Note that additions to this should also update the StmtVisitor class,
clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2950,7 +2950,6 @@ DEF_TRAVERSE_STMT(FunctionParmPackExpr, {})
DEF_TRAVERSE_STMT(CXXFoldExpr, {})
DEF_TRAVERSE_STMT(AtomicExpr, {})
DEF_TRAVERSE_STMT(CXXParenListInitExpr, {})
-DEF_TRAVERSE_STMT(ResolvedUnexpandedPackExpr, {})
DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {
if (S->getLifetimeExtendedTemporaryDecl()) {
@@ -3056,6 +3055,9 @@ DEF_TRAVERSE_STMT(OMPSimdDirective,
DEF_TRAVERSE_STMT(OMPTileDirective,
{ TRY_TO(TraverseOMPExecutableDirective(S)); })
+DEF_TRAVERSE_STMT(OMPStripeDirective,
+ { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
DEF_TRAVERSE_STMT(OMPUnrollDirective,
{ TRY_TO(TraverseOMPExecutableDirective(S)); })
@@ -3544,6 +3546,12 @@ bool RecursiveASTVisitor<Derived>::VisitOMPNoOpenMPRoutinesClause(
return true;
}
+template <typename Derived>
+bool RecursiveASTVisitor<Derived>::VisitOMPNoOpenMPConstructsClause(
+ OMPNoOpenMPConstructsClause *) {
+ return true;
+}
+
template <typename Derived>
bool RecursiveASTVisitor<Derived>::VisitOMPNoParallelismClause(
OMPNoParallelismClause *) {
clang/include/clang/AST/Redeclarable.h
@@ -114,8 +114,6 @@ protected:
bool isFirst() const {
return isa<KnownLatest>(Link) ||
- // FIXME: 'template' is required on the next line due to an
- // apparent clang bug.
isa<UninitializedLatest>(cast<NotKnownLatest>(Link));
}
clang/include/clang/AST/Stmt.h
@@ -1215,6 +1215,20 @@ protected:
SourceLocation Loc;
};
+ class ConvertVectorExprBitfields {
+ friend class ConvertVectorExpr;
+
+ LLVM_PREFERRED_TYPE(ExprBitfields)
+ unsigned : NumExprBits;
+
+ //
+ /// This is only meaningful for operations on floating point
+ /// types when additional values need to be in trailing storage.
+ /// It is 0 otherwise.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned HasFPFeatures : 1;
+ };
+
union {
// Same order as in StmtNodes.td.
// Statements
@@ -1293,6 +1307,7 @@ protected:
// Clang Extensions
OpaqueValueExprBitfields OpaqueValueExprBits;
+ ConvertVectorExprBitfields ConvertVectorExprBits;
};
public:
clang/include/clang/AST/StmtOpenMP.h
@@ -994,7 +994,8 @@ public:
static bool classof(const Stmt *T) {
Stmt::StmtClass C = T->getStmtClass();
return C == OMPTileDirectiveClass || C == OMPUnrollDirectiveClass ||
- C == OMPReverseDirectiveClass || C == OMPInterchangeDirectiveClass;
+ C == OMPReverseDirectiveClass || C == OMPInterchangeDirectiveClass ||
+ C == OMPStripeDirectiveClass;
}
};
@@ -5560,7 +5561,7 @@ class OMPTileDirective final : public OMPLoopTransformationDirective {
: OMPLoopTransformationDirective(OMPTileDirectiveClass,
llvm::omp::OMPD_tile, StartLoc, EndLoc,
NumLoops) {
- setNumGeneratedLoops(3 * NumLoops);
+ setNumGeneratedLoops(2 * NumLoops);
}
void setPreInits(Stmt *PreInits) {
@@ -5621,6 +5622,81 @@ public:
}
};
+/// This represents the '#pragma omp stripe' loop transformation directive.
+class OMPStripeDirective final : public OMPLoopTransformationDirective {
+ friend class ASTStmtReader;
+ friend class OMPExecutableDirective;
+
+ /// Default list of offsets.
+ enum {
+ PreInitsOffset = 0,
+ TransformedStmtOffset,
+ };
+
+ explicit OMPStripeDirective(SourceLocation StartLoc, SourceLocation EndLoc,
+ unsigned NumLoops)
+ : OMPLoopTransformationDirective(OMPStripeDirectiveClass,
+ llvm::omp::OMPD_stripe, StartLoc, EndLoc,
+ NumLoops) {
+ setNumGeneratedLoops(2 * NumLoops);
+ }
+
+ void setPreInits(Stmt *PreInits) {
+ Data->getChildren()[PreInitsOffset] = PreInits;
+ }
+
+ void setTransformedStmt(Stmt *S) {
+ Data->getChildren()[TransformedStmtOffset] = S;
+ }
+
+public:
+ /// Create a new AST node representation for '#pragma omp stripe'.
+ ///
+ /// \param C Context of the AST.
+ /// \param StartLoc Location of the introducer (e.g. the 'omp' token).
+ /// \param EndLoc Location of the directive's end (e.g. the tok::eod).
+ /// \param Clauses The directive's clauses.
+ /// \param NumLoops Number of associated loops (number of items in the
+ /// 'sizes' clause).
+ /// \param AssociatedStmt The outermost associated loop.
+ /// \param TransformedStmt The loop nest after striping, or nullptr in
+ /// dependent contexts.
+ /// \param PreInits Helper preinits statements for the loop nest.
+ static OMPStripeDirective *
+ Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+ ArrayRef<OMPClause *> Clauses, unsigned NumLoops, Stmt *AssociatedStmt,
+ Stmt *TransformedStmt, Stmt *PreInits);
+
+ /// Build an empty '#pragma omp stripe' AST node for deserialization.
+ ///
+ /// \param C Context of the AST.
+ /// \param NumClauses Number of clauses to allocate.
+ /// \param NumLoops Number of associated loops to allocate.
+ static OMPStripeDirective *
+ CreateEmpty(const ASTContext &C, unsigned NumClauses, unsigned NumLoops);
+ /// Gets/sets the associated loops after striping.
+ ///
+ /// This is in de-sugared format stored as a CompoundStmt.
+ ///
+ /// \code
+ /// for (...)
+ /// ...
+ /// \endcode
+ ///
+ /// Note that if the generated loops a become associated loops of another
+ /// directive, they may need to be hoisted before them.
+ Stmt *getTransformedStmt() const {
+ return Data->getChildren()[TransformedStmtOffset];
+ }
+
+ /// Return preinits statement.
+ Stmt *getPreInits() const { return Data->getChildren()[PreInitsOffset]; }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == OMPStripeDirectiveClass;
+ }
+};
+
/// This represents the '#pragma omp unroll' loop transformation directive.
///
/// \code
clang/include/clang/AST/TextNodeDumper.h
@@ -249,6 +249,7 @@ public:
void VisitDeclarationTemplateArgument(const TemplateArgument &TA);
void VisitNullPtrTemplateArgument(const TemplateArgument &TA);
void VisitIntegralTemplateArgument(const TemplateArgument &TA);
+ void VisitStructuralValueTemplateArgument(const TemplateArgument &TA);
void VisitTemplateTemplateArgument(const TemplateArgument &TA);
void VisitTemplateExpansionTemplateArgument(const TemplateArgument &TA);
void VisitExpressionTemplateArgument(const TemplateArgument &TA);
@@ -424,6 +425,7 @@ public:
void VisitOpenACCAsteriskSizeExpr(const OpenACCAsteriskSizeExpr *S);
void VisitEmbedExpr(const EmbedExpr *S);
void VisitAtomicExpr(const AtomicExpr *AE);
+ void VisitConvertVectorExpr(const ConvertVectorExpr *S);
};
} // namespace clang
clang/include/clang/AST/Type.h
@@ -6266,8 +6266,8 @@ public:
LLVM_PREFERRED_TYPE(bool)
uint8_t RawBuffer : 1;
- Attributes(llvm::dxil::ResourceClass ResourceClass, bool IsROV,
- bool RawBuffer)
+ Attributes(llvm::dxil::ResourceClass ResourceClass, bool IsROV = false,
+ bool RawBuffer = false)
: ResourceClass(ResourceClass), IsROV(IsROV), RawBuffer(RawBuffer) {}
Attributes() : Attributes(llvm::dxil::ResourceClass::UAV, false, false) {}
clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -2489,7 +2489,28 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, FloatingLiteral>
extern const internal::VariadicDynCastAllOfMatcher<Stmt, ImaginaryLiteral>
imaginaryLiteral;
-/// Matches fixed point literals
+/// Matches fixed-point literals eg.
+/// 0.5r, 0.5hr, 0.5lr, 0.5uhr, 0.5ur, 0.5ulr
+/// 1.0k, 1.0hk, 1.0lk, 1.0uhk, 1.0uk, 1.0ulk
+/// Exponents 1.0e10k
+/// Hexadecimal numbers 0x0.2p2r
+///
+/// Does not match implicit conversions such as first two lines:
+/// \code
+/// short _Accum sa = 2;
+/// _Accum a = 12.5;
+/// _Accum b = 1.25hk;
+/// _Fract c = 0.25hr;
+/// _Fract v = 0.35uhr;
+/// _Accum g = 1.45uhk;
+/// _Accum decexp1 = 1.575e1k;
+/// \endcode
+/// \compile_args{-ffixed-point;-std=c99}
+///
+/// The matcher \matcher{fixedPointLiteral()} matches
+/// \match{1.25hk}, \match{0.25hr}, \match{0.35uhr},
+/// \match{1.45uhk}, \match{1.575e1k}, but does not
+/// match \nomatch{12.5} and \nomatch{2} from the code block.
extern const internal::VariadicDynCastAllOfMatcher<Stmt, FixedPointLiteral>
fixedPointLiteral;
clang/include/clang/Analysis/AnalysisDeclContext.h
@@ -451,7 +451,7 @@ public:
bool synthesizeBodies = false, bool addStaticInitBranches = false,
bool addCXXNewAllocator = true, bool addRichCXXConstructors = true,
bool markElidedCXXConstructors = true, bool addVirtualBaseBranches = true,
- CodeInjector *injector = nullptr);
+ std::unique_ptr<CodeInjector> injector = nullptr);
AnalysisDeclContext *getContext(const Decl *D);
clang/include/clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h
@@ -63,23 +63,6 @@ public:
getOrCreateConstMethodReturnValue(const RecordStorageLocation &RecordLoc,
const CallExpr *CE, Environment &Env);
- /// Creates or returns a previously created `StorageLocation` associated with
- /// a const method call `obj.getFoo()` where `RecordLoc` is the
- /// `RecordStorageLocation` of `obj`.
- ///
- /// The callback `Initialize` runs on the storage location if newly created.
- /// Returns nullptr if unable to find or create a value.
- ///
- /// Requirements:
- ///
- /// - `CE` should return a location (GLValue or a record type).
- ///
- /// DEPRECATED: switch users to the below overload which takes Callee and Type
- /// directly.
- StorageLocation *getOrCreateConstMethodReturnStorageLocation(
- const RecordStorageLocation &RecordLoc, const CallExpr *CE,
- Environment &Env, llvm::function_ref<void(StorageLocation &)> Initialize);
-
/// Creates or returns a previously created `StorageLocation` associated with
/// a const method call `obj.getFoo()` where `RecordLoc` is the
/// `RecordStorageLocation` of `obj`, `Callee` is the decl for `getFoo`.
@@ -208,29 +191,6 @@ Value *CachedConstAccessorsLattice<Base>::getOrCreateConstMethodReturnValue(
return Val;
}
-template <typename Base>
-StorageLocation *
-CachedConstAccessorsLattice<Base>::getOrCreateConstMethodReturnStorageLocation(
- const RecordStorageLocation &RecordLoc, const CallExpr *CE,
- Environment &Env, llvm::function_ref<void(StorageLocation &)> Initialize) {
- assert(!CE->getType().isNull());
- assert(CE->isGLValue() || CE->getType()->isRecordType());
- auto &ObjMap = ConstMethodReturnStorageLocations[&RecordLoc];
- const FunctionDecl *DirectCallee = CE->getDirectCallee();
- if (DirectCallee == nullptr)
- return nullptr;
- auto it = ObjMap.find(DirectCallee);
- if (it != ObjMap.end())
- return it->second;
-
- StorageLocation &Loc =
- Env.createStorageLocation(CE->getType().getNonReferenceType());
- Initialize(Loc);
-
- ObjMap.insert({DirectCallee, &Loc});
- return &Loc;
-}
-
template <typename Base>
StorageLocation &
CachedConstAccessorsLattice<Base>::getOrCreateConstMethodReturnStorageLocation(
clang/include/clang/Basic/Attr.td
@@ -459,6 +459,7 @@ class TargetArch<list<string> arches> : TargetSpec {
}
def TargetARM : TargetArch<["arm", "thumb", "armeb", "thumbeb"]>;
def TargetAArch64 : TargetArch<["aarch64", "aarch64_be", "aarch64_32"]>;
+def TargetAMDGPU : TargetArch<["amdgcn", "r600"]>;
def TargetAnyArm : TargetArch<!listconcat(TargetARM.Arches, TargetAArch64.Arches)>;
def TargetAVR : TargetArch<["avr"]>;
def TargetBPF : TargetArch<["bpfel", "bpfeb"]>;
@@ -469,7 +470,9 @@ def TargetMSP430 : TargetArch<["msp430"]>;
def TargetM68k : TargetArch<["m68k"]>;
def TargetRISCV : TargetArch<["riscv32", "riscv64"]>;
def TargetX86 : TargetArch<["x86"]>;
+def TargetX86_64 : TargetArch<["x86_64"]>;
def TargetAnyX86 : TargetArch<["x86", "x86_64"]>;
+def TargetSPIRV : TargetArch<["spirv", "spirv32", "spirv64"]>;
def TargetWebAssembly : TargetArch<["wasm32", "wasm64"]>;
def TargetNVPTX : TargetArch<["nvptx", "nvptx64"]>;
def TargetWindows : TargetSpec {
@@ -1831,6 +1834,16 @@ def Format : InheritableAttr {
let Documentation = [FormatDocs];
}
+def FormatMatches : InheritableAttr {
+ let Spellings = [GCC<"format_matches">];
+ let Args = [IdentifierArgument<"Type">, IntArgument<"FormatIdx">, ExprArgument<"ExpectedFormat">];
+ let AdditionalMembers = [{
+ StringLiteral *getFormatString() const;
+ }];
+ let Subjects = SubjectList<[ObjCMethod, Block, HasFunctionProto]>;
+ let Documentation = [FormatMatchesDocs];
+}
+
def FormatArg : InheritableAttr {
let Spellings = [GCC<"format_arg">];
let Args = [ParamIdxArgument<"FormatIdx">];
@@ -3124,11 +3137,20 @@ def PragmaClangTextSection : InheritableAttr {
let Documentation = [InternalOnly];
}
-def CodeModel : InheritableAttr, TargetSpecificAttr<TargetLoongArch> {
+// The code model attribute only applies to LoongArch and x86-64, but for NVPTX
+// compilations that share code with the host, we want to ignore the attribute
+// rather than warn on it.
+def CodeModel
+ : InheritableAttr,
+ TargetSpecificAttr<TargetArch<!listconcat(
+ TargetLoongArch.Arches, TargetX86_64.Arches, TargetNVPTX.Arches,
+ TargetAMDGPU.Arches, TargetSPIRV.Arches)>> {
let Spellings = [GCC<"model">];
- let Args = [EnumArgument<"Model", "llvm::CodeModel::Model", /*is_string=*/1,
- ["normal", "medium", "extreme"], ["Small", "Medium", "Large"],
- /*opt=*/0, /*fake=*/0, /*isExternalType=*/1, /*isCovered=*/0>];
+ let Args = [EnumArgument<
+ "Model", "llvm::CodeModel::Model",
+ /*is_string=*/1, ["small", "normal", "medium", "large", "extreme"],
+ ["Small", "Small", "Medium", "Large", "Large"],
+ /*opt=*/0, /*fake=*/0, /*isExternalType=*/1, /*isCovered=*/0>];
let Subjects = SubjectList<[NonTLSGlobalVar], ErrorDiag>;
let Documentation = [CodeModelDocs];
}
clang/include/clang/Basic/AttrDocs.td
@@ -62,6 +62,16 @@ def CodeModelDocs : Documentation {
let Content = [{
The ``model`` attribute allows overriding the translation unit's
code model (specified by ``-mcmodel``) for a specific global variable.
+
+On LoongArch, allowed values are "normal", "medium", "extreme".
+
+On x86-64, allowed values are ``"small"`` and ``"large"``. ``"small"`` is
+roughly equivalent to ``-mcmodel=small``, meaning the global is considered
+"small" placed closer to the ``.text`` section relative to "large" globals, and
+to prefer using 32-bit relocations to access the global. ``"large"`` is roughly
+equivalent to ``-mcmodel=large``, meaning the global is considered "large" and
+placed further from the ``.text`` section relative to "small" globals, and
+64-bit relocations must be used to access the global.
}];
let Heading = "model";
}
@@ -3876,6 +3886,132 @@ behavior of the program is undefined.
}];
}
+def FormatMatchesDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+
+The ``format`` attribute is the basis for the enforcement of diagnostics in the
+``-Wformat`` family, but it only handles the case where the format string is
+passed along with the arguments it is going to format. It cannot handle the case
+where the format string and the format arguments are passed separately from each
+other. For instance:
+
+.. code-block:: c
+
+ static const char *first_name;
+ static double todays_temperature;
+ static int wind_speed;
+
+ void say_hi(const char *fmt) {
+ printf(fmt, first_name, todays_temperature);
+ // ^ warning: format string is not a string literal
+ printf(fmt, first_name, wind_speed);
+ // ^ warning: format string is not a string literal
+ }
+
+ int main() {
+ say_hi("hello %s, it is %g degrees outside");
+ say_hi("hello %s, it is %d degrees outside!");
+ // ^ no diagnostic, but %d cannot format doubles
+ }
+
+In this example, ``fmt`` is expected to format a ``const char *`` and a
+``double``, but these values are not passed to ``say_hi``. Without the
+``format`` attribute (which cannot apply in this case), the -Wformat-nonliteral
+diagnostic unnecessarily triggers in the body of ``say_hi``, and incorrect
+``say_hi`` call sites do not trigger a diagnostic.
+
+To complement the ``format`` attribute, Clang also defines the
+``format_matches`` attribute. Its syntax is similar to the ``format``
+attribute's, but instead of taking the index of the first formatted value
+argument, it takes a C string literal with the expected specifiers:
+
+.. code-block:: c
+
+ static const char *first_name;
+ static double todays_temperature;
+ static int wind_speed;
+
+ __attribute__((__format_matches__(printf, 1, "%s %g")))
+ void say_hi(const char *fmt) {
+ printf(fmt, first_name, todays_temperature); // no dignostic
+ printf(fmt, first_name, wind_speed); // warning: format specifies type 'int' but the argument has type 'double'
+ }
+
+ int main() {
+ say_hi("hello %s, it is %g degrees outside");
+ say_hi("it is %g degrees outside, have a good day %s!");
+ // warning: format specifies 'double' where 'const char *' is required
+ // warning: format specifies 'const char *' where 'double' is required
+ }
+
+The third argument to ``format_matches`` is expected to evaluate to a **C string
+literal** even when the format string would normally be a different type for the
+given flavor, like a ``CFStringRef`` or a ``NSString *``.
+
+The only requirement on the format string literal is that it has specifiers
+that are compatible with the arguments that will be used. It can contain
+arbitrary non-format characters. For instance, for the purposes of compile-time
+validation, ``"%s scored %g%% on her test"`` and ``"%s%g"`` are interchangeable
+as the format string argument. As a means of self-documentation, users may
+prefer the former when it provides a useful example of an expected format
+string.
+
+In the implementation of a function with the ``format_matches`` attribute,
+format verification works as if the format string was identical to the one
+specified in the attribute.
+
+.. code-block:: c
+
+ __attribute__((__format_matches__(printf, 1, "%s %g")))
+ void say_hi(const char *fmt) {
+ printf(fmt, "person", 546);
+ // ^ warning: format specifies type 'double' but the
+ // argument has type 'int'
+ // note: format string is defined here:
+ // __attribute__((__format_matches__(printf, 1, "%s %g")))
+ // ^~
+ }
+
+
+At the call sites of functions with the ``format_matches`` attribute, format
+verification instead compares the two format strings to evaluate their
+equivalence. Each format flavor defines equivalence between format specifiers.
+Generally speaking, two specifiers are equivalent if they format the same type.
+For instance, in the ``printf`` flavor, ``%2i`` and ``%-0.5d`` are compatible.
+When ``-Wformat-signedness`` is disabled, ``%d`` and ``%u`` are compatible. For
+a negative example, ``%ld`` is incompatible with ``%d``.
+
+Do note the following un-obvious cases:
+
+* Passing ``NULL`` as the format string does not trigger format diagnostics.
+* When the format string is not NULL, it cannot _miss_ specifiers, even in
+ trailing positions. For instance, ``%d`` is not accepted when the required
+ format is ``%d %d %d``.
+* While checks for the ``format`` attribute tolerate sone size mismatches
+ that standard argument promotion renders immaterial (such as formatting an
+ ``int`` with ``%hhd``, which specifies a ``char``-sized integer), checks for
+ ``format_matches`` require specified argument sizes to match exactly.
+* Format strings expecting a variable modifier (such as ``%*s``) are
+ incompatible with format strings that would itemize the variable modifiers
+ (such as ``%i %s``), even if the two specify ABI-compatible argument lists.
+* All pointer specifiers, modifiers aside, are mutually incompatible. For
+ instance, ``%s`` is not compatible with ``%p``, and ``%p`` is not compatible
+ with ``%n``, and ``%hhn`` is incompatible with ``%s``, even if the pointers
+ are ABI-compatible or identical on the selected platform. However, ``%0.5s``
+ is compatible with ``%s``, since the difference only exists in modifier flags.
+ This is not overridable with ``-Wformat-pedantic`` or its inverse, which
+ control similar behavior in ``-Wformat``.
+
+At this time, clang implements ``format_matches`` only for format types in the
+``printf`` family. This includes variants such as Apple's NSString format and
+the FreeBSD ``kprintf``, but excludes ``scanf``. Using a known but unsupported
+format silently fails in order to be compatible with other implementations that
+would support these formats.
+
+ }];
+}
+
def FlagEnumDocs : Documentation {
let Category = DocCatDecl;
let Content = [{
@@ -5274,6 +5410,7 @@ optimization passes are aware of the following assumptions:
"omp_no_openmp"
"omp_no_openmp_routines"
"omp_no_parallelism"
+ "omp_no_openmp_constructs"
The OpenMP standard defines the meaning of OpenMP assumptions ("omp_XYZ" is
spelled "XYZ" in the `OpenMP 5.1 Standard`_).
@@ -8481,7 +8618,7 @@ that is a pointer to the type with the attribute.
}
The above example will result in a call to ``bar`` being passed the address of
-`y`` when ``y`` goes out of scope, then a call to ``foo`` being passed the
+``y`` when ``y`` goes out of scope, then a call to ``foo`` being passed the
address of ``x`` when ``x`` goes out of scope. If two or more variables share
the same scope, their ``cleanup`` callbacks are invoked in the reverse order
the variables were declared in. It is not possible to check the return value
clang/include/clang/Basic/Builtins.h
@@ -408,7 +408,8 @@ public:
unsigned getRequiredVectorWidth(unsigned ID) const;
- /// Return true if builtin ID belongs to AuxTarget.
+ /// Return true if the builtin ID belongs exclusively to the AuxTarget,
+ /// and false if it belongs to both primary and aux target, or neither.
bool isAuxBuiltinID(unsigned ID) const {
return ID >= (Builtin::FirstTSBuiltin + NumTargetBuiltins);
}
clang/include/clang/Basic/Builtins.td
@@ -521,6 +521,12 @@ def TruncF16F128 : Builtin, F16F128MathTemplate {
let Prototype = "T(T)";
}
+def Sincospi : Builtin, FPMathTemplate {
+ let Spellings = ["__builtin_sincospi"];
+ let Attributes = [FunctionWithBuiltinPrefix, NoThrow];
+ let Prototype = "void(T, T*, T*)";
+}
+
// Access to floating point environment.
def BuiltinFltRounds : Builtin {
let Spellings = ["__builtin_flt_rounds"];
@@ -839,6 +845,12 @@ def BuiltinAssumeAligned : Builtin {
let Prototype = "void*(void const*, size_t, ...)";
}
+def BuiltinAssumeDereferenceable : Builtin {
+ let Spellings = ["__builtin_assume_dereferenceable"];
+ let Attributes = [NoThrow, Const];
+ let Prototype = "void(void const*, _Constant size_t)";
+}
+
def BuiltinFree : Builtin {
let Spellings = ["__builtin_free"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow];
@@ -4765,6 +4777,12 @@ def HLSLAll : LangBuiltin<"HLSL_LANG"> {
let Prototype = "bool(...)";
}
+def HLSLAnd : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_and"];
+ let Attributes = [NoThrow, Const];
+ let Prototype = "void(...)";
+}
+
def HLSLAny : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_any"];
let Attributes = [NoThrow, Const];
clang/include/clang/Basic/BuiltinsNVPTX.td
@@ -21,12 +21,14 @@ class SM<string version, list<SMFeatures> newer_list> : SMFeatures {
!strconcat(f, "|", newer.Features));
}
+let Features = "sm_120a" in def SM_120a : SMFeatures;
+let Features = "sm_101a" in def SM_101a : SMFeatures;
let Features = "sm_100a" in def SM_100a : SMFeatures;
-
-def SM_100 : SM<"100", [SM_100a]>;
-
let Features = "sm_90a" in def SM_90a : SMFeatures;
+def SM_120 : SM<"120", [SM_120a]>;
+def SM_101 : SM<"101", [SM_101a, SM_120]>;
+def SM_100 : SM<"100", [SM_100a, SM_101]>;
def SM_90 : SM<"90", [SM_90a, SM_100]>;
def SM_89 : SM<"89", [SM_90]>;
def SM_87 : SM<"87", [SM_89]>;
@@ -669,6 +671,14 @@ def __nvvm_redux_sync_umax : NVPTXBuiltinSMAndPTX<"unsigned int(unsigned int, in
def __nvvm_redux_sync_and : NVPTXBuiltinSMAndPTX<"int(int, int)", SM_80, PTX70>;
def __nvvm_redux_sync_xor : NVPTXBuiltinSMAndPTX<"int(int, int)", SM_80, PTX70>;
def __nvvm_redux_sync_or : NVPTXBuiltinSMAndPTX<"int(int, int)", SM_80, PTX70>;
+def __nvvm_redux_sync_fmin : NVPTXBuiltinSMAndPTX<"float(float, int)", SM_100a, PTX86>;
+def __nvvm_redux_sync_fmin_abs : NVPTXBuiltinSMAndPTX<"float(float, int)", SM_100a, PTX86>;
+def __nvvm_redux_sync_fmin_NaN : NVPTXBuiltinSMAndPTX<"float(float, int)", SM_100a, PTX86>;
+def __nvvm_redux_sync_fmin_abs_NaN : NVPTXBuiltinSMAndPTX<"float(float, int)", SM_100a, PTX86>;
+def __nvvm_redux_sync_fmax : NVPTXBuiltinSMAndPTX<"float(float, int)", SM_100a, PTX86>;
+def __nvvm_redux_sync_fmax_abs : NVPTXBuiltinSMAndPTX<"float(float, int)", SM_100a, PTX86>;
+def __nvvm_redux_sync_fmax_NaN : NVPTXBuiltinSMAndPTX<"float(float, int)", SM_100a, PTX86>;
+def __nvvm_redux_sync_fmax_abs_NaN : NVPTXBuiltinSMAndPTX<"float(float, int)", SM_100a, PTX86>;
// Membar
clang/include/clang/Basic/BuiltinsSME.def→/dev/null
@@ -1,21 +0,0 @@
[diff truncated]