@@ -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_work | ||
Returns a list of GaugeMetric objects, containing the relevant metrics about | ||
the workflow | ||
""" | ||
+ queued_job_count | ||
+ running_job_coun | ||
# 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_ | ||
- [ | ||
- x | ||
- for x in github_repo.get_ | ||
- if x.name in WORKFLOWS_TO_TRA | ||
- ] | ||
- ) | ||
- running_workflow | ||
- [ | ||
- x | ||
- for x in github_repo.get_ | ||
- if x.name in WORKFLOWS_TO_TRA | ||
- ] | ||
- ) | ||
+ for queued_workflow in github_repo.get_ | ||
+ if queued_workflow. | ||
+ continue | ||
+ for queued_workflow_ | ||
+ job_name = queued_workflow_ | ||
+ # Workflows marked as queued can potentially only have some jobs | ||
+ # queued, so make sure to also count jobs currently in progress. | ||
+ if queued_workflow_ | ||
+ if job_name not in queued_job_count | ||
+ queued_job_count | ||
+ else: | ||
+ queued_job_count | ||
+ elif queued_workflow_ | ||
+ if job_name not in running_job_coun | ||
+ running_job_coun | ||
+ else: | ||
+ running_job_coun | ||
+ | ||
+ for running_workflow | ||
+ if running_workflow | ||
+ continue | ||
+ for running_workflow | ||
+ job_name = running_workflow | ||
+ if running_workflow | ||
+ continue | ||
+ | ||
+ if job_name not in running_job_coun | ||
+ running_job_coun | ||
+ else: | ||
+ running_job_coun | ||
workflow_metrics | ||
- workflow_metrics | ||
- GaugeMetric( | ||
- "workflow_queue_ | ||
- queued_workflow_ | ||
- time.time_ns(), | ||
+ for queued_job in queued_job_count | ||
+ workflow_metrics | ||
+ GaugeMetric( | ||
+ f"workflow_queue | ||
+ queued_job_count | ||
+ time.time_ns(), | ||
+ ) | ||
) | ||
- ) | ||
- workflow_metrics | ||
- GaugeMetric( | ||
- "running_workflo | ||
- running_workflow | ||
- time.time_ns(), | ||
+ for running_job in running_job_coun | ||
+ workflow_metrics | ||
+ GaugeMetric( | ||
+ f"running_workfl | ||
+ running_job_coun | ||
+ time.time_ns(), | ||
+ ) | ||
) | ||
- ) | ||
# Always send a hearbeat metric so we can monitor is this container is still able to log to Grafana. | ||
workflow_metrics | ||
GaugeMetric("met | ||
@@ -157,7 +178,7 @@ def get_per_workflow | ||
# longer in a testing state and we can directly assert the workflow | ||
# result. | ||
for step in workflow_job.ste | ||
- if step.conclusion != "success": | ||
+ if step.conclusion != "success" and step.conclusion != "skipped": | ||
job_result = 0 | ||
break | ||
@@ -179,6 +200,7 @@ def get_per_workflow | ||
job_result, | ||
created_at_ns, | ||
workflow_run.id, | ||
+ workflow_run.nam | ||
) | ||
) | ||
@@ -235,8 +257,6 @@ def upload_metrics(w | ||
def main(): | ||
# Authenticate with Github | ||
auth = Auth.Token(os.en | ||
- github_object = Github(auth=auth | ||
- github_repo = github_object.ge | ||
grafana_api_key = os.environ["GRAF | ||
grafana_metrics_ | ||
@@ -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.ge | ||
+ | ||
current_metrics = get_per_workflow | ||
current_metrics += get_sampled_work | ||
- # Always send a hearbeat metric so we can monitor is this container is still able to log to Grafana. | ||
- current_metrics. | ||
- GaugeMetric("met | ||
- ) | ||
upload_metrics(c | ||
print(f"Uploaded | ||
@@ -261,7 +280,7 @@ def main(): | ||
for workflow_metric in reversed(current | ||
if isinstance(workf | ||
workflows_to_tra | ||
- workflow_metric. | ||
+ workflow_metric. | ||
] = workflow_metric. | ||
time.sleep(SCRAP |
@@ -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 |
@@ -499,6 +499,7 @@ clang:static analyzer: | ||
- clang/tools/scan | ||
- clang/utils/anal | ||
- clang/docs/analy | ||
+ - clang/test/Analy | ||
pgo: | ||
- llvm/lib/Transfo |
@@ -27,7 +27,7 @@ jobs: | ||
container-filena | ||
steps: | ||
- name: Checkout LLVM | ||
- uses: actions/checkout | ||
+ uses: actions/checkout | ||
with: | ||
sparse-checkout: | ||
- name: Write Variables | ||
@@ -46,7 +46,7 @@ jobs: | ||
run: | | ||
docker save ${{ steps.vars.outpu | ||
- name: Upload container image | ||
- uses: actions/upload-a | ||
+ uses: actions/upload-a | ||
with: | ||
name: container | ||
path: ${{ steps.vars.outpu | ||
@@ -63,7 +63,7 @@ jobs: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_T | ||
steps: | ||
- name: Download container | ||
- uses: actions/download | ||
+ uses: actions/download | ||
with: | ||
name: container | ||
- name: Push Container |
@@ -32,7 +32,7 @@ jobs: | ||
runs-on: depot-ubuntu-22. | ||
steps: | ||
- name: Checkout LLVM | ||
- uses: actions/checkout | ||
+ uses: actions/checkout | ||
with: | ||
sparse-checkout: | ||
# podman is not installed by default on the ARM64 images. | ||
@@ -66,7 +66,7 @@ jobs: | ||
podman save ${{ steps.vars.outpu | ||
- name: Upload container image | ||
- uses: actions/upload-a | ||
+ uses: actions/upload-a | ||
with: | ||
name: container-${{ matrix.arch }} | ||
path: "*.tar" | ||
@@ -90,7 +90,7 @@ jobs: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_T | ||
steps: | ||
- name: Download container | ||
- uses: actions/download | ||
+ uses: actions/download | ||
- name: Push Container | ||
run: | |
@@ -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.ou | ||
-DCMAKE_CXX_COMP | ||
-DCMAKE_C_COMPIL | ||
- -DCMAKE_BUILD_TY | ||
+ -DCMAKE_BUILD_TY | ||
-DCMAKE_C_COMPIL | ||
-DCMAKE_CXX_COMP | ||
-DCMAKE_INSTALL_ |
@@ -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.ou | ||
-DCMAKE_CXX_COMP | ||
-DCMAKE_C_COMPIL | ||
- -DCMAKE_BUILD_TY | ||
+ -DCMAKE_BUILD_TY | ||
-DCMAKE_C_COMPIL | ||
-DCMAKE_CXX_COMP | ||
-DCMAKE_POLICY_D |
@@ -37,7 +37,7 @@ jobs: | ||
stage1: | ||
if: github.repositor | ||
runs-on: libcxx-self-host | ||
- container: ghcr.io/llvm/lib | ||
+ container: ghcr.io/llvm/lib | ||
continue-on-erro | ||
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.repositor | ||
runs-on: libcxx-self-host | ||
- container: ghcr.io/llvm/lib | ||
+ container: ghcr.io/llvm/lib | ||
needs: [ stage1 ] | ||
continue-on-erro | ||
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-cxx | ||
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 | ||
- name: ${{ matrix.config }} | ||
@@ -120,7 +124,7 @@ jobs: | ||
**/crash_diagnos | ||
stage3: | ||
if: github.repositor | ||
- needs: [ stage1, stage2 ] | ||
+ needs: [ stage2 ] | ||
continue-on-erro | ||
strategy: | ||
fail-fast: false | ||
@@ -163,14 +167,14 @@ jobs: | ||
- config: 'generic-msan' | ||
machine: libcxx-self-host | ||
runs-on: ${{ matrix.machine }} | ||
- container: ghcr.io/llvm/lib | ||
+ container: ghcr.io/llvm/lib | ||
steps: | ||
- uses: actions/checkout | ||
- name: ${{ matrix.config }} | ||
run: libcxx/utils/ci/ | ||
env: | ||
- CC: clang-20 | ||
- CXX: clang++-20 | ||
+ CC: clang-21 | ||
+ CXX: clang++-21 | ||
- uses: actions/upload-a | ||
if: always() | ||
with: | ||
@@ -184,7 +188,7 @@ jobs: | ||
**/crash_diagnos | ||
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: |
@@ -9,7 +9,6 @@ name: Build Docker images for libc++ CI | ||
permissions: | ||
contents: read | ||
- packages: write | ||
on: | ||
push: |
@@ -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/workflow | ||
push: | ||
branches: | ||
- 'main' | ||
- 'release/**' | ||
+concurrency: | ||
+ group: ${{ github.workflow }}-${{ github.event.pul | ||
+ cancel-in-progre | ||
+ | ||
jobs: | ||
premerge-checks- | ||
+ name: Linux Premerge Checks (Test Only - Please Ignore Results) | ||
if: >- | ||
github.repositor | ||
(github.event_na | ||
runs-on: llvm-premerge-li | ||
- concurrency: | ||
- group: ${{ github.workflow }}-linux-${{ github.event.pul | ||
- cancel-in-progre | ||
steps: | ||
- name: Checkout LLVM | ||
- uses: actions/checkout | ||
+ uses: actions/checkout | ||
with: | ||
fetch-depth: 2 | ||
- name: Setup ccache | ||
- uses: hendrikmuhs/ccac | ||
+ uses: hendrikmuhs/ccac | ||
with: | ||
max-size: "2000M" | ||
- name: Build and Test | ||
@@ -84,23 +84,21 @@ jobs: | ||
./.ci/monolithic | ||
premerge-checks- | ||
+ name: Windows Premerge Checks (Test Only - Please Ignore Results) | ||
if: >- | ||
github.repositor | ||
(github.event_na | ||
runs-on: llvm-premerge-wi | ||
- concurrency: | ||
- group: ${{ github.workflow }}-windows-${{ github.event.pul | ||
- cancel-in-progre | ||
defaults: | ||
run: | ||
shell: bash | ||
steps: | ||
- name: Checkout LLVM | ||
- uses: actions/checkout | ||
+ uses: actions/checkout | ||
with: | ||
fetch-depth: 2 | ||
- name: Setup ccache | ||
- uses: hendrikmuhs/ccac | ||
+ uses: hendrikmuhs/ccac | ||
with: | ||
variant: "sccache" | ||
max-size: "2000M" | ||
@@ -146,11 +144,9 @@ jobs: | ||
call C:\\BuildTools\\ | ||
bash .ci/monolithic-w | ||
- permerge-check-m | ||
+ premerge-check-m | ||
+ name: MacOS Premerge Checks | ||
runs-on: macos-14 | ||
- concurrency: | ||
- group: ${{ github.workflow }}-macos-${{ github.event.pul | ||
- cancel-in-progre | ||
if: >- | ||
github.repositor | ||
(startswith(gith | ||
@@ -158,11 +154,11 @@ jobs: | ||
(github.event_na | ||
steps: | ||
- name: Checkout LLVM | ||
- uses: actions/checkout | ||
+ uses: actions/checkout | ||
with: | ||
fetch-depth: 2 | ||
- name: Setup ccache | ||
- uses: hendrikmuhs/ccac | ||
+ uses: hendrikmuhs/ccac | ||
with: | ||
max-size: "2000M" | ||
- name: Install Ninja |
@@ -1,4 +1,5 @@ | ||
import github | ||
+import re | ||
import sys | ||
_SPECIAL_CASE_BI | ||
@@ -16,38 +17,73 @@ def _is_valid(upload | ||
return False | ||
+def _get_uploaders(r | ||
+ # 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[ | ||
+ ] | ||
+ ) | ||
+ # 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[ | ||
+ ] | ||
+ ) | ||
+ | ||
+ | ||
+def _get_major_relea | ||
+ # 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_a | ||
+ f'Could not parse release version from release title "{release_title} | ||
+ ) | ||
+ else: | ||
+ return int(match.groups | ||
+ | ||
+ | ||
+def _write_comment_a | ||
+ with open("comment", "w") as file: | ||
+ file.write(comme | ||
+ sys.exit(1) | ||
+ | ||
+ | ||
def main(): | ||
token = sys.argv[1] | ||
gh = github.Github(lo | ||
repo = gh.get_repo("llv | ||
- uploaders = set( | ||
- [ | ||
- "DimitryAndric", | ||
- "stefanp-ibm", | ||
- "lei137", | ||
- "omjavaid", | ||
- "nicolerabjohn", | ||
- "amy-kwan", | ||
- "mandlebug", | ||
- "zmodem", | ||
- "androm3da", | ||
- "tru", | ||
- "rovka", | ||
- "rorth", | ||
- "quinnlp", | ||
- "kamaub", | ||
- "abrisco", | ||
- "jakeegan", | ||
- "maryammo", | ||
- "tstellar", | ||
- "github-actions[ | ||
- ] | ||
- ) | ||
- | ||
for release in repo.get_release | ||
print("Release:" | ||
+ uploaders = _get_uploaders(_ | ||
for asset in release.get_asse | ||
created_at = asset.created_at | ||
updated_at = ( | ||
@@ -57,9 +93,9 @@ def main(): | ||
f"{asset.name} : {asset.uploader. | ||
) | ||
if not _is_valid(asset. | ||
- with open('comment', 'w') as file: | ||
- file.write(f'@{a | ||
- sys.exit(1) | ||
+ _write_comment_a | ||
+ f"@{asset.upload | ||
+ ) | ||
if __name__ == "__main__": |
@@ -27,6 +27,10 @@ on: | ||
required: true | ||
default: false | ||
type: boolean | ||
+ secrets: | ||
+ RELEASE_TASKS_US | ||
+ description: "Secret used to check user permissions." | ||
+ required: false | ||
pull_request: | ||
types: |
@@ -133,13 +133,18 @@ jobs: | ||
# add extra CMake args to disable them. | ||
# See https://github.c | ||
if [ "$RUNNER_OS" = "macOS" ]; then | ||
- target_cmake_fla | ||
+ target_cmake_fla | ||
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_fla | ||
fi | ||
- target_cmake_fla | ||
+ target_cmake_fla | ||
fi | ||
build_flang="tru |
@@ -38,6 +38,8 @@ Jianjian GUAN <jacquesguan@me. | ||
Jon Roelofs <jonathan_roelof | ||
Jon Roelofs <jonathan_roelof | ||
Jonathan Thackray <jonathan.thackr | ||
+klensy <nightouser@gmai | ||
+klensy <nightouser@gmai | ||
LLVM GN Syncbot <llvmgnsyncbot@g | ||
Martin Storsjö <martin@martin.s | ||
Med Ismail Bennani <ismail@bennani. |
@@ -202,3 +202,11 @@ endif() | ||
configure_file($ | ||
${CMAKE_CURRENT_ | ||
+ | ||
+set(BOLT_ENUM_T | ||
+foreach(t ${BOLT_TARGETS_T | ||
+ set(BOLT_ENUM_TA | ||
+endforeach(t) | ||
+ | ||
+configure_file( | ||
+ ${CMAKE_CURRENT_ |
@@ -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](#secur | ||
+ * [pac-ret analysis](#pac-r | ||
-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](htt | ||
+[Chromium](http | ||
+[Android](https | ||
+ | ||
+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 | ||
+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-binar | ||
+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-prote | ||
+used Linux distributions. | ||
+ | ||
+The hardening scheme mitigates | ||
+[Return-Oriente | ||
+attacks by making sure that return addresses are only ever stored to memory with | ||
+a cryptographic hash, called a | ||
+["Pointer Authentication Code" (PAC)](https://l | ||
+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 | ||
+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-prot | ||
+ | ||
+``` | ||
+ 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-binar | ||
+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<R | ||
+ 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<R | ||
+``` | ||
+ | ||
+The exact format of how `llvm-bolt-binar | ||
+evolve over time. | ||
+ | ||
+##### Example 2: multiple "last-overwritin | ||
+ | ||
+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<R | ||
+ 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: | ||
+ 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.c | ||
+ | ||
+BOLT cannot currently handle functions with `cfi_negate_ra_s | ||
+i.e. any binaries built with `-mbranch-protec | ||
+to be used on specifically such binaries, so this is a major limitation! Work is | ||
+going on in PR [#120064](https: | ||
+fix this. | ||
## How to add your own binary analysis | ||
@@ -359,15 +359,9 @@ public: | ||
/// Add a new relocation at the given /p Offset. | ||
void addRelocation(ui | ||
- 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.empl | ||
- } else { | ||
- PendingRelocatio | ||
- Relocation{Offse | ||
- } | ||
+ Relocations.empl | ||
} | ||
/// Add a dynamic relocation at the given /p Offset. |
@@ -46,13 +46,6 @@ public: | ||
/// Return the address and size of a symbol or std::nullopt if it cannot be | ||
/// found. | ||
virtual std::optional<Sy | ||
- | ||
- /// Return the address of a symbol or std::nullopt if it cannot be found. | ||
- std::optional<ui | ||
- if (const auto Info = lookupSymbolInfo | ||
- return Info->Address; | ||
- return std::nullopt; | ||
- } | ||
}; | ||
} // namespace bolt |
@@ -27,6 +27,7 @@ | ||
#include "llvm/MC/MCInstr | ||
#include "llvm/MC/MCInstr | ||
#include "llvm/MC/MCInstr | ||
+#include "llvm/MC/MCRegis | ||
#include "llvm/Support/Al | ||
#include "llvm/Support/Ca | ||
#include "llvm/Support/Er | ||
@@ -550,6 +551,22 @@ public: | ||
return Analysis->isRetu | ||
} | ||
+ virtual ErrorOr<MCPhysRe | ||
+ llvm_unreachable | ||
+ return getNoRegister(); | ||
+ } | ||
+ | ||
+ virtual bool isAuthentication | ||
+ MCPhysReg AuthenticatedReg | ||
+ llvm_unreachable | ||
+ return false; | ||
+ } | ||
+ | ||
+ virtual ErrorOr<MCPhysRe | ||
+ llvm_unreachable | ||
+ return getNoRegister(); | ||
+ } | ||
+ | ||
virtual bool isTerminator(con | ||
virtual bool isNoop(const MCInst &Inst) const { |
@@ -0,0 +1,23 @@ | ||
+//===-- TargetConfig.def | ||
+// | ||
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
+// See https://llvm.org | ||
+// SPDX-License-Ide | ||
+// | ||
+//===---------- | ||
+// | ||
+// 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(Targ | ||
+#endif | ||
+ | ||
+@BOLT_ENUM_TARG | ||
+ | ||
+#undef BOLT_TARGET |
@@ -0,0 +1,258 @@ | ||
+//===- bolt/Passes/NonP | ||
+// | ||
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
+// See https://llvm.org | ||
+// SPDX-License-Ide | ||
+// | ||
+//===---------- | ||
+ | ||
+#ifndef BOLT_PASSES_NONP | ||
+#define BOLT_PASSES_NONP | ||
+ | ||
+#include "bolt/Core/Binar | ||
+#include "bolt/Core/Binar | ||
+#include "bolt/Passes/Bin | ||
+#include "llvm/ADT/SmallS | ||
+#include "llvm/Support/ra | ||
+#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 | ||
+/// the corresponding instruction can be computed. | ||
+ | ||
+struct MCInstInBBRefere | ||
+ BinaryBasicBlock | ||
+ int64_t BBIndex; | ||
+ MCInstInBBRefere | ||
+ : BB(BB), BBIndex(BBIndex) | ||
+ MCInstInBBRefere | ||
+ static MCInstInBBRefere | ||
+ for (BinaryBasicBloc | ||
+ for (size_t I = 0; I < BB.size(); ++I) | ||
+ if (Inst == &BB.getInstructi | ||
+ return MCInstInBBRefere | ||
+ return {}; | ||
+ } | ||
+ bool operator==(const | ||
+ return BB == RHS.BB && BBIndex == RHS.BBIndex; | ||
+ } | ||
+ bool operator<(const MCInstInBBRefere | ||
+ if (BB != RHS.BB) | ||
+ return BB < RHS.BB; | ||
+ return BBIndex < RHS.BBIndex; | ||
+ } | ||
+ operator MCInst &() const { | ||
+ assert(BB != nullptr); | ||
+ return BB->getInstructi | ||
+ } | ||
+ 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 | ||
+ return BB->getFunction( | ||
+ } | ||
+}; | ||
+ | ||
+raw_ostream &operator<<(raw_ | ||
+ | ||
+struct MCInstInBFRefere | ||
+ BinaryFunction *BF; | ||
+ uint64_t Offset; | ||
+ MCInstInBFRefere | ||
+ : BF(BF), Offset(Offset) {} | ||
+ MCInstInBFRefere | ||
+ bool operator==(const | ||
+ return BF == RHS.BF && Offset == RHS.Offset; | ||
+ } | ||
+ bool operator<(const MCInstInBFRefere | ||
+ if (BF != RHS.BF) | ||
+ return BF < RHS.BF; | ||
+ return Offset < RHS.Offset; | ||
+ } | ||
+ operator MCInst &() const { | ||
+ assert(BF != nullptr); | ||
+ return *BF->getInstruct | ||
+ } | ||
+ | ||
+ uint64_t getOffset() const { return Offset; } | ||
+ | ||
+ uint64_t getAddress() const { return BF->getAddress() | ||
+}; | ||
+ | ||
+raw_ostream &operator<<(raw_ | ||
+ | ||
+struct MCInstReference { | ||
+ enum Kind { FunctionParent, BasicBlockParent | ||
+ Kind ParentKind; | ||
+ union U { | ||
+ MCInstInBBRefere | ||
+ MCInstInBFRefere | ||
+ U(MCInstInBBRefe | ||
+ U(MCInstInBFRefe | ||
+ } U; | ||
+ MCInstReference( | ||
+ : ParentKind(Basic | ||
+ MCInstReference( | ||
+ : ParentKind(Funct | ||
+ MCInstReference( | ||
+ : MCInstReference( | ||
+ MCInstReference( | ||
+ : MCInstReference( | ||
+ | ||
+ 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 | ||
+ 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.getAddre | ||
+ case FunctionParent: | ||
+ return U.BFRef.getAddre | ||
+ } | ||
+ llvm_unreachable | ||
+ } | ||
+ | ||
+ BinaryFunction *getFunction() const { | ||
+ switch (ParentKind) { | ||
+ case FunctionParent: | ||
+ return U.BFRef.BF; | ||
+ case BasicBlockParent | ||
+ return U.BBRef.BB->getF | ||
+ } | ||
+ llvm_unreachable | ||
+ } | ||
+ | ||
+ BinaryBasicBlock | ||
+ switch (ParentKind) { | ||
+ case FunctionParent: | ||
+ return nullptr; | ||
+ case BasicBlockParent | ||
+ return U.BBRef.BB; | ||
+ } | ||
+ llvm_unreachable | ||
+ } | ||
+}; | ||
+ | ||
+raw_ostream &operator<<(raw_ | ||
+ | ||
+struct GeneralDiagnosti | ||
+ std::string Text; | ||
+ GeneralDiagnosti | ||
+ bool operator==(const | ||
+ return Text == RHS.Text; | ||
+ } | ||
+}; | ||
+ | ||
+raw_ostream &operator<<(raw_ | ||
+ | ||
+namespace NonPacProtectedR | ||
+struct Annotation { | ||
+ MCInstReference RetInst; | ||
+ Annotation(MCIns | ||
+ virtual bool operator==(const | ||
+ return RetInst == RHS.RetInst; | ||
+ } | ||
+ Annotation &operator=(const | ||
+ if (this == &Other) | ||
+ return *this; | ||
+ RetInst = Other.RetInst; | ||
+ return *this; | ||
+ } | ||
+ virtual ~Annotation() {} | ||
+ virtual void generateReport(r | ||
+ const BinaryContext &BC) const = 0; | ||
+}; | ||
+ | ||
+struct Gadget : public Annotation { | ||
+ std::vector<MCIn | ||
+ virtual bool operator==(const | ||
+ return Annotation::oper | ||
+ OverwritingRetRe | ||
+ } | ||
+ Gadget(MCInstRef | ||
+ const std::vector<MCIn | ||
+ : Annotation(RetIn | ||
+ virtual void generateReport(r | ||
+ const BinaryContext &BC) const override; | ||
+}; | ||
+ | ||
+struct GenDiag : public Annotation { | ||
+ GeneralDiagnosti | ||
+ virtual bool operator==(const | ||
+ return Annotation::oper | ||
+ } | ||
+ GenDiag(MCInstRe | ||
+ : Annotation(RetIn | ||
+ virtual void generateReport(r | ||
+ const BinaryContext &BC) const override; | ||
+}; | ||
+ | ||
+class PacRetAnalysis; | ||
+ | ||
+struct FunctionAnalysis | ||
+ SmallSet<MCPhysR | ||
+ std::vector<std: | ||
+}; | ||
+ | ||
+class Analysis : public BinaryFunctionPa | ||
+ void runOnFunction(Bi | ||
+ MCPlusBuilder::A | ||
+ FunctionAnalysis | ||
+ computeDfState(P | ||
+ MCPlusBuilder::A | ||
+ | ||
+ std::map<const BinaryFunction *, FunctionAnalysis | ||
+ std::mutex AnalysisResultsM | ||
+ | ||
+public: | ||
+ explicit Analysis() : BinaryFunctionPa | ||
+ | ||
+ const char *getName() const override { return "non-pac-protect | ||
+ | ||
+ /// Pass entry point | ||
+ Error runOnFunctions(B | ||
+}; | ||
+ | ||
+} // namespace NonPacProtectedR | ||
+} // namespace bolt | ||
+} // namespace llvm | ||
+ | ||
+#endif |
@@ -94,7 +94,7 @@ private: | ||
/// Used for parsing specific pre-aggregated input files. | ||
struct AggregatedLBREnt | ||
- enum Type : char { BRANCH = 0, FT, FT_EXTERNAL_ORIG | ||
+ enum Type : char { BRANCH = 0, FT, FT_EXTERNAL_ORIG | ||
Location From; | ||
Location To; | ||
uint64_t Count; | ||
@@ -197,6 +197,10 @@ private: | ||
BoltAddressTrans | ||
+ /// Whether pre-aggregated profile needs to convert branch profile into call | ||
+ /// to continuation fallthrough profile. | ||
+ bool NeedsConvertRetP | ||
+ | ||
/// 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_ | ||
- bool IsPreagg); | ||
+ bool doBranch(uint64_ | ||
/// 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<PerfMemS | ||
/// Parse pre-aggregated LBR samples created by an external tool | ||
- ErrorOr<Aggregat | ||
+ std::error_code parseAggregatedL | ||
/// 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>:]<st | ||
- /// [<mispred_count> | ||
+ /// {B|F|f|T} [<start_id>:]<st | ||
+ /// <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. |
@@ -80,6 +80,10 @@ extern llvm::cl::opt<un | ||
/// Return true if we should process all functions in the binary. | ||
bool processAllFuncti | ||
+enum GadgetScannerKin | ||
+ | ||
+extern llvm::cl::list<G | ||
+ | ||
} // namespace opts | ||
namespace llvm { |
@@ -1759,7 +1759,11 @@ void BinaryContext::p | ||
dwarf::toString( | ||
if (std::optional<u | ||
auto Iter = DWOCUs.find(*DWO | ||
- 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->ge | ||
} |
@@ -15,6 +15,7 @@ | ||
#include "bolt/Core/DynoS | ||
#include "bolt/Core/HashU | ||
#include "bolt/Core/MCPlu | ||
+#include "bolt/Utils/Comm | ||
#include "bolt/Utils/Name | ||
#include "bolt/Utils/Name | ||
#include "bolt/Utils/Util | ||
@@ -498,6 +499,11 @@ void BinaryFunction:: | ||
if (!IslandOffset) | ||
return; | ||
+ // Print label if it exists at this offset. | ||
+ if (const BinaryData *BD = | ||
+ BC.getBinaryData | ||
+ OS << BD->getName() << ":\n"; | ||
+ | ||
const size_t IslandSize = getSizeOfDataInC | ||
BC.printData(OS, | ||
*IslandOffset); | ||
@@ -1066,7 +1072,7 @@ size_t BinaryFunction:: | ||
auto Iter = Islands->CodeOff | ||
if (Iter != Islands->CodeOff | ||
return *Iter - Offset; | ||
- return getSize() - Offset; | ||
+ return getMaxSize() - Offset; | ||
} | ||
std::optional<ui | ||
@@ -1748,8 +1754,7 @@ void BinaryFunction:: | ||
// 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.HasRelocati | ||
+ if (!BC.HasRelocati | ||
setSimple(false) | ||
const uint32_t Offset = KV.first; | ||
@@ -2078,7 +2083,7 @@ void BinaryFunction:: | ||
Error BinaryFunction:: | ||
auto &MIB = BC.MIB; | ||
- if (!isSimple()) { | ||
+ if (!isSimple() && !opts::Aggregate | ||
assert(!BC.HasRe | ||
"cannot process file with non-simple function in relocs mode"); | ||
return createNonFatalBO | ||
@@ -4254,10 +4259,10 @@ void BinaryFunction:: | ||
if (BC.HasRelocatio | ||
if (hasConstantIsla | ||
- const auto DataAddress = | ||
- Linker.lookupSym | ||
- assert(DataAddre | ||
- setOutputDataAdd | ||
+ const auto IslandLabelSymIn | ||
+ Linker.lookupSym | ||
+ assert(IslandLab | ||
+ setOutputDataAdd | ||
for (auto It : Islands->Offsets | ||
const uint64_t OldOffset = It.first; | ||
BinaryData *BD = BC.getBinaryData | ||
@@ -4265,10 +4270,10 @@ void BinaryFunction:: | ||
continue; | ||
MCSymbol *Symbol = It.second; | ||
- const auto NewAddress = Linker.lookupSym | ||
- assert(NewAddres | ||
+ const auto SymInfo = Linker.lookupSym | ||
+ assert(SymInfo && "Cannot find CI symbol"); | ||
auto &Section = *getCodeSection( | ||
- const auto NewOffset = *NewAddress - Section.getOutpu | ||
+ const auto NewOffset = SymInfo->Address | ||
BD->setOutputLoc | ||
} | ||
} | ||
@@ -4293,10 +4298,10 @@ void BinaryFunction:: | ||
FF.setAddress(Co | ||
FF.setImageSize( | ||
if (hasConstantIsla | ||
- const auto DataAddress = Linker.lookupSym | ||
+ const auto SymInfo = Linker.lookupSym | ||
getFunctionColdC | ||
- assert(DataAddre | ||
- setOutputColdDat | ||
+ assert(SymInfo && "Cannot find cold CI symbol"); | ||
+ setOutputColdDat | ||
} | ||
} | ||
} |
@@ -23,6 +23,7 @@ add_llvm_library | ||
LoopInversionPas | ||
LivenessAnalysis | ||
MCF.cpp | ||
+ NonPacProtectedR | ||
PatchEntries.cpp | ||
PettisAndHansen. | ||
PLTCall.cpp |
@@ -0,0 +1,526 @@ | ||
+//===- bolt/Passes/NonP | ||
+// | ||
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
+// See https://llvm.org | ||
+// SPDX-License-Ide | ||
+// | ||
+//===---------- | ||
+// | ||
+// 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/Non | ||
+#include "bolt/Core/Paral | ||
+#include "bolt/Passes/Dat | ||
+#include "llvm/ADT/SmallS | ||
+#include "llvm/MC/MCInst. | ||
+#include "llvm/Support/Fo | ||
+#include <memory> | ||
+ | ||
+#define DEBUG_TYPE "bolt-nonpacprot | ||
+ | ||
+namespace llvm { | ||
+namespace bolt { | ||
+ | ||
+raw_ostream &operator<<(raw_ | ||
+ OS << "MCInstBBRef<"; | ||
+ if (Ref.BB == nullptr) | ||
+ OS << "BB:(null)"; | ||
+ else | ||
+ OS << "BB:" << Ref.BB->getName( | ||
+ OS << ">"; | ||
+ return OS; | ||
+} | ||
+ | ||
+raw_ostream &operator<<(raw_ | ||
+ OS << "MCInstBFRef<"; | ||
+ if (Ref.BF == nullptr) | ||
+ OS << "BF:(null)"; | ||
+ else | ||
+ OS << "BF:" << Ref.BF->getPrint | ||
+ OS << ">"; | ||
+ return OS; | ||
+} | ||
+ | ||
+raw_ostream &operator<<(raw_ | ||
+ switch (Ref.ParentKind) | ||
+ case MCInstReference: | ||
+ OS << Ref.U.BBRef; | ||
+ return OS; | ||
+ case MCInstReference: | ||
+ OS << Ref.U.BFRef; | ||
+ return OS; | ||
+ } | ||
+ llvm_unreachable | ||
+} | ||
+ | ||
+namespace NonPacProtectedR | ||
+ | ||
+// 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-authenticati | ||
+// 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<Smal | ||
+ State() {} | ||
+ State(unsigned NumRegs, unsigned NumRegsToTrack) | ||
+ : NonAutClobRegs(N | ||
+ State &operator|=(cons | ||
+ NonAutClobRegs |= StateIn.NonAutCl | ||
+ for (unsigned I = 0; I < LastInstWritingR | ||
+ for (const MCInst *J : StateIn.LastInst | ||
+ LastInstWritingR | ||
+ return *this; | ||
+ } | ||
+ bool operator==(const | ||
+ return NonAutClobRegs == RHS.NonAutClobRe | ||
+ LastInstWritingR | ||
+ } | ||
+ bool operator!=(const | ||
+}; | ||
+ | ||
+static void printLastInsts( | ||
+ raw_ostream &OS, | ||
+ const std::vector<Smal | ||
+ OS << "Insts: "; | ||
+ for (unsigned I = 0; I < LastInstWritingR | ||
+ auto &Set = LastInstWritingR | ||
+ OS << "[" << I << "]("; | ||
+ for (const MCInst *MCInstP : Set) | ||
+ OS << MCInstP << " "; | ||
+ OS << ")"; | ||
+ } | ||
+} | ||
+ | ||
+raw_ostream &operator<<(raw_ | ||
+ OS << "pacret-state<"; | ||
+ OS << "NonAutClobRegs: | ||
+ printLastInsts(O | ||
+ OS << ">"; | ||
+ return OS; | ||
+} | ||
+ | ||
+class PacStatePrinter { | ||
+public: | ||
+ void print(raw_ostrea | ||
+ explicit PacStatePrinter( | ||
+ | ||
+private: | ||
+ const BinaryContext &BC; | ||
+}; | ||
+ | ||
+void PacStatePrinter: | ||
+ RegStatePrinter RegStatePrinter( | ||
+ OS << "pacret-state<"; | ||
+ OS << "NonAutClobRegs: | ||
+ RegStatePrinter. | ||
+ OS << ", "; | ||
+ printLastInsts(O | ||
+ OS << ">"; | ||
+} | ||
+ | ||
+class PacRetAnalysis | ||
+ : public DataflowAnalysis | ||
+ PacStatePrinter> | ||
+ using Parent = | ||
+ DataflowAnalysis | ||
+ friend Parent; | ||
+ | ||
+public: | ||
+ PacRetAnalysis(B | ||
+ const std::vector<MCPh | ||
+ : Parent(BF, AllocId), NumRegs(BF.getBi | ||
+ RegsToTrackInsts | ||
+ TrackingLastInst | ||
+ Reg2StateIdx(Reg | ||
+ ? 0 | ||
+ : *llvm::max_eleme | ||
+ -1) { | ||
+ for (unsigned I = 0; I < RegsToTrackInsts | ||
+ Reg2StateIdx[Reg | ||
+ } | ||
+ virtual ~PacRetAnalysis( | ||
+ | ||
+protected: | ||
+ const unsigned NumRegs; | ||
+ /// RegToTrackInstsF | ||
+ /// must compute which the last set of instructions writing to it are. | ||
+ const std::vector<MCPh | ||
+ const bool TrackingLastInst | ||
+ /// Reg2StateIdx maps Register to the index in the vector used in State to | ||
+ /// track which instructions last wrote to this register. | ||
+ std::vector<uint | ||
+ | ||
+ SmallPtrSet<cons | ||
+ MCPhysReg Reg) const { | ||
+ assert(Reg < Reg2StateIdx.siz | ||
+ assert(isTrackin | ||
+ return S.LastInstWritin | ||
+ } | ||
+ const SmallPtrSet<cons | ||
+ MCPhysReg Reg) const { | ||
+ assert(Reg < Reg2StateIdx.siz | ||
+ assert(isTrackin | ||
+ return S.LastInstWritin | ||
+ } | ||
+ | ||
+ bool isTrackingReg(MC | ||
+ return llvm::is_contain | ||
+ } | ||
+ | ||
+ void preflight() {} | ||
+ | ||
+ State getStartingState | ||
+ return State(NumRegs, RegsToTrackInsts | ||
+ } | ||
+ | ||
+ State getStartingState | ||
+ return State(NumRegs, RegsToTrackInsts | ||
+ } | ||
+ | ||
+ void doConfluence(Sta | ||
+ PacStatePrinter P(BC); | ||
+ LLVM_DEBUG({ | ||
+ dbgs() << " PacRetAnalysis:: | ||
+ 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(cons | ||
+ PacStatePrinter P(BC); | ||
+ LLVM_DEBUG({ | ||
+ dbgs() << " PacRetAnalysis:: | ||
+ BC.InstPrinter-> | ||
+ dbgs()); | ||
+ dbgs() << ", "; | ||
+ P.print(dbgs(), Cur); | ||
+ dbgs() << ")\n"; | ||
+ }); | ||
+ | ||
+ State Next = Cur; | ||
+ BitVector Written = BitVector(NumReg | ||
+ // 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 | ||
+ // Also, not all functions may respect the AAPCS ABI rules about | ||
+ // caller/callee-sa | ||
+ if (BC.MIB->isCall( | ||
+ Written.set(); | ||
+ else | ||
+ // FIXME: `getWrittenRegs` | ||
+ // 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 `getClobberedReg | ||
+ // It is unclear if there is any test case which shows a different | ||
+ // behaviour between using `getWrittenRegs` | ||
+ // first would like to see such a test case before making a decision | ||
+ // on whether using `getClobberedReg | ||
+ // Also see the discussion on this at | ||
+ // https://github.c | ||
+ BC.MIB->getWritt | ||
+ Next.NonAutClobR | ||
+ // Keep track of this instruction if it writes to any of the registers we | ||
+ // need to track that for: | ||
+ for (MCPhysReg Reg : RegsToTrackInsts | ||
+ if (Written[Reg]) | ||
+ lastWritingInsts | ||
+ | ||
+ ErrorOr<MCPhysRe | ||
+ if (AutReg && *AutReg != BC.MIB->getNoReg | ||
+ // FIXME: should we use `OnlySmaller=fal | ||
+ // FIXME about `getWrittenRegs` | ||
+ // at | ||
+ // https://github.c | ||
+ Next.NonAutClobR | ||
+ BC.MIB->getAlias | ||
+ if (TrackingLastIns | ||
+ lastWritingInsts | ||
+ } | ||
+ | ||
+ LLVM_DEBUG({ | ||
+ dbgs() << " .. result: ("; | ||
+ P.print(dbgs(), Next); | ||
+ dbgs() << ")\n"; | ||
+ }); | ||
+ | ||
+ return Next; | ||
+ } | ||
+ | ||
+ StringRef getAnnotationNam | ||
+ | ||
+public: | ||
+ std::vector<MCIn | ||
+ getLastClobberin | ||
+ const BitVector &UsedDirtyRegs) const { | ||
+ if (!TrackingLastIn | ||
+ return {}; | ||
+ auto MaybeState = getStateAt(Ret); | ||
+ if (!MaybeState) | ||
+ llvm_unreachable | ||
+ const State &S = *MaybeState; | ||
+ // Due to aliasing registers, multiple registers may have been tracked. | ||
+ std::set<const MCInst *> LastWritingInsts | ||
+ for (MCPhysReg TrackedReg : UsedDirtyRegs.se | ||
+ for (const MCInst *Inst : lastWritingInsts | ||
+ LastWritingInsts | ||
+ } | ||
+ std::vector<MCIn | ||
+ for (const MCInst *Inst : LastWritingInsts | ||
+ MCInstInBBRefere | ||
+ assert(Ref.BB != nullptr && "Expected Inst to be found"); | ||
+ Result.push_back | ||
+ } | ||
+ return Result; | ||
+ } | ||
+}; | ||
+ | ||
+FunctionAnalysi | ||
+Analysis::compu | ||
+ MCPlusBuilder::A | ||
+ PRA.run(); | ||
+ LLVM_DEBUG({ | ||
+ dbgs() << " After PacRetAnalysis:\ | ||
+ BF.dump(); | ||
+ }); | ||
+ | ||
+ FunctionAnalysis | ||
+ // Now scan the CFG for non-authenticati | ||
+ // overwritten, non-authenticate | ||
+ BinaryContext &BC = BF.getBinaryCont | ||
+ for (BinaryBasicBloc | ||
+ for (int64_t I = BB.size() - 1; I >= 0; --I) { | ||
+ MCInst &Inst = BB.getInstructio | ||
+ if (BC.MIB->isRetur | ||
+ ErrorOr<MCPhysRe | ||
+ if (MaybeRetReg.get | ||
+ Result.Diagnosti | ||
+ MCInstInBBRefere | ||
+ "Warning: pac-ret analysis could not analyze this return " | ||
+ "instruction")); | ||
+ continue; | ||
+ } | ||
+ MCPhysReg RetReg = *MaybeRetReg; | ||
+ LLVM_DEBUG({ | ||
+ dbgs() << " Found RET inst: "; | ||
+ BC.printInstruct | ||
+ dbgs() << " RetReg: " << BC.MRI->getName( | ||
+ << "; authenticatesReg | ||
+ << BC.MIB->isAuthen | ||
+ }); | ||
+ if (BC.MIB->isAuthe | ||
+ break; | ||
+ BitVector UsedDirtyRegs = PRA.getStateAt(I | ||
+ LLVM_DEBUG({ | ||
+ dbgs() << " NonAutClobRegs at Ret: "; | ||
+ RegStatePrinter RSP(BC); | ||
+ RSP.print(dbgs() | ||
+ dbgs() << "\n"; | ||
+ }); | ||
+ UsedDirtyRegs &= BC.MIB->getAlias | ||
+ LLVM_DEBUG({ | ||
+ dbgs() << " Intersection with RetReg: "; | ||
+ RegStatePrinter RSP(BC); | ||
+ RSP.print(dbgs() | ||
+ dbgs() << "\n"; | ||
+ }); | ||
+ if (UsedDirtyRegs.a | ||
+ // This return instruction needs to be reported | ||
+ Result.Diagnosti | ||
+ MCInstInBBRefere | ||
+ PRA.getLastClobb | ||
+ for (MCPhysReg RetRegWithGadget | ||
+ Result.Registers | ||
+ } | ||
+ } | ||
+ } | ||
+ } | ||
+ return Result; | ||
+} | ||
+ | ||
+void Analysis::runOnF | ||
+ MCPlusBuilder::A | ||
+ LLVM_DEBUG({ | ||
+ dbgs() << "Analyzing in function " << BF.getPrintName( | ||
+ << AllocatorId << "\n"; | ||
+ BF.dump(); | ||
+ }); | ||
+ | ||
+ if (BF.hasCFG()) { | ||
+ PacRetAnalysis PRA(BF, AllocatorId, {}); | ||
+ FunctionAnalysis | ||
+ if (!FAR.RegistersA | ||
+ // Redo the analysis, but now also track which instructions last wrote | ||
+ // to any of the registers in RetRegsWithGadge | ||
+ // diagnostics can be produced. | ||
+ std::vector<MCPh | ||
+ for (MCPhysReg R : FAR.RegistersAff | ||
+ RegsToTrack.push | ||
+ PacRetAnalysis PRWIA(BF, AllocatorId, RegsToTrack); | ||
+ FAR = computeDfState(P | ||
+ } | ||
+ | ||
+ // `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 | ||
+ { | ||
+ std::lock_guard< | ||
+ AnalysisResults[ | ||
+ } | ||
+ } | ||
+} | ||
+ | ||
+static void printBB(const BinaryContext &BC, const BinaryBasicBlock | ||
+ 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() | ||
+ const MCInst &Inst = BB->getInstructi | ||
+ if (BC.MIB->isCFI(I | ||
+ continue; | ||
+ BC.printInstruct | ||
+ } | ||
+} | ||
+ | ||
+static void reportFoundGadge | ||
+ raw_ostream &OS, const BinaryContext &BC, const MCInstReference OverwInst, | ||
+ const MCInstReference RetInst) { | ||
+ BinaryBasicBlock | ||
+ assert(OverwInst | ||
+ assert(RetInst.P | ||
+ MCInstInBBRefere | ||
+ if (BB == OverwInstBB.BB) { | ||
+ // overwriting inst and ret instruction are in the same basic block. | ||
+ assert(OverwInst | ||
+ OS << " This happens in the following basic block:\n"; | ||
+ printBB(BC, BB); | ||
+ } | ||
+} | ||
+ | ||
+void Gadget::generate | ||
+ GenDiag(RetInst, | ||
+ | ||
+ BinaryFunction *BF = RetInst.getFunct | ||
+ OS << " The " << OverwritingRetRe | ||
+ << " instructions that write to the return register after any " | ||
+ "authentication are:\n"; | ||
+ // Sort by address to ensure output is deterministic. | ||
+ std::vector<MCIn | ||
+ llvm::sort(ORRI, | ||
+ return A.getAddress() < B.getAddress(); | ||
+ }); | ||
+ for (unsigned I = 0; I < ORRI.size(); ++I) { | ||
+ MCInstReference InstRef = ORRI[I]; | ||
+ OS << " " << (I + 1) << ". "; | ||
+ BC.printInstruct | ||
+ }; | ||
+ LLVM_DEBUG({ | ||
+ dbgs() << " .. OverWritingRetRe | ||
+ for (MCInstReference | ||
+ dbgs() << " " << Ref << "\n"; | ||
+ } | ||
+ }); | ||
+ if (OverwritingRetR | ||
+ const MCInstReference OverwInst = OverwritingRetRe | ||
+ assert(OverwInst | ||
+ reportFoundGadge | ||
+ } | ||
+} | ||
+ | ||
+void GenDiag::generat | ||
+ BinaryFunction *BF = RetInst.getFunct | ||
+ BinaryBasicBlock | ||
+ | ||
+ OS << "\nGS-PACRET: " << Diag.Text; | ||
+ OS << " in function " << BF->getPrintName | ||
+ if (BB) | ||
+ OS << ", basic block " << BB->getName(); | ||
+ OS << ", at address " << llvm::format("%x | ||
+ OS << " The return instruction is "; | ||
+ BC.printInstruct | ||
+} | ||
+ | ||
+Error Analysis::runOnF | ||
+ ParallelUtilitie | ||
+ [&](BinaryFuncti | ||
+ runOnFunction(BF | ||
+ }; | ||
+ | ||
+ ParallelUtilitie | ||
+ return false; | ||
+ }; | ||
+ | ||
+ ParallelUtilitie | ||
+ BC, ParallelUtilitie | ||
+ SkipFunc, "NonPacProtected | ||
+ | ||
+ for (BinaryFunction *BF : BC.getAllBinaryF | ||
+ if (AnalysisResults | ||
+ for (const std::shared_ptr< | ||
+ AnalysisResults[ | ||
+ A->generateRepor | ||
+ } | ||
+ return Error::success() | ||
+} | ||
+ | ||
+} // namespace NonPacProtectedR | ||
+} // namespace bolt | ||
+} // namespace llvm |
@@ -711,7 +711,7 @@ bool DataAggregator:: | ||
} | ||
bool DataAggregator:: | ||
- 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->isRetur | ||
@@ -772,7 +772,8 @@ bool DataAggregator:: | ||
return false; | ||
// Record call to continuation trace. | ||
- if (IsPreagg && FromFunc != ToFunc && (IsReturn || IsCallCont)) { | ||
+ if (NeedsConvertRet | ||
+ (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:: | ||
ParentFunc = FromFunc; | ||
ParentFunc->Samp | ||
+ const uint64_t FuncAddress = FromFunc->getAdd | ||
std::optional<Bo | ||
- BAT ? BAT->getFallthro | ||
- Second.From) | ||
+ BAT && BAT->isBATFuncti | ||
+ ? BAT->getFallthro | ||
: getFallthroughsI | ||
if (!FTs) { | ||
LLVM_DEBUG( | ||
@@ -869,7 +871,7 @@ DataAggregator:: | ||
BinaryContext &BC = BF.getBinaryCont | ||
- if (!BF.isSimple()) | ||
+ if (BF.empty()) | ||
return std::nullopt; | ||
assert(BF.hasCFG | ||
@@ -1216,23 +1218,30 @@ ErrorOr<Location | ||
return Location(true, BuildID.get(), Offset.get()); | ||
} | ||
-ErrorOr<DataAgg | ||
-DataAggregator: | ||
+std::error_code | ||
while (checkAndConsume | ||
} | ||
ErrorOr<StringRe | ||
if (std::error_code | ||
return EC; | ||
+ // Pre-aggregated profile with branches and fallthroughs needs to convert | ||
+ // return profile into call to continuation fall-through. | ||
auto Type = AggregatedLBREnt | ||
if (TypeOrErr.get() | ||
+ NeedsConvertRetP | ||
Type = AggregatedLBREnt | ||
} else if (TypeOrErr.get() | ||
+ NeedsConvertRetP | ||
Type = AggregatedLBREnt | ||
} else if (TypeOrErr.get() | ||
+ NeedsConvertRetP | ||
Type = AggregatedLBREnt | ||
+ } else if (TypeOrErr.get() | ||
+ // Trace is expanded into B and [Ff] | ||
+ Type = AggregatedLBREnt | ||
} else { | ||
- reportError("exp | ||
+ reportError("exp | ||
return make_error_code( | ||
} | ||
@@ -1248,6 +1257,15 @@ DataAggregator:: | ||
if (std::error_code | ||
return EC; | ||
+ ErrorOr<Location | ||
+ if (Type == AggregatedLBREnt | ||
+ while (checkAndConsume | ||
+ } | ||
+ TraceFtEnd = parseLocationOrO | ||
+ if (std::error_code | ||
+ return EC; | ||
+ } | ||
+ | ||
while (checkAndConsume | ||
} | ||
ErrorOr<int64_t> | ||
@@ -1270,9 +1288,24 @@ DataAggregator:: | ||
return make_error_code( | ||
} | ||
- return AggregatedLBREnt | ||
- static_cast<uint | ||
- Type}; | ||
+ BinaryFunction *FromFunc = getBinaryFunctio | ||
+ BinaryFunction *ToFunc = getBinaryFunctio | ||
+ | ||
+ for (BinaryFunction *BF : {FromFunc, ToFunc}) | ||
+ if (BF) | ||
+ BF->setHasProfil | ||
+ | ||
+ uint64_t Count = static_cast<uint | ||
+ AggregatedLBREnt | ||
+ AggregatedLBRs.e | ||
+ if (Type == AggregatedLBREnt | ||
+ auto FtType = (FromFunc == ToFunc) ? AggregatedLBREnt | ||
+ : AggregatedLBREnt | ||
+ AggregatedLBREnt | ||
+ AggregatedLBRs.e | ||
+ } | ||
+ | ||
+ return std::error_code( | ||
} | ||
bool DataAggregator:: | ||
@@ -1585,8 +1618,7 @@ void DataAggregator:: | ||
for (const auto &AggrLBR : BranchLBRs) { | ||
const Trace &Loc = AggrLBR.first; | ||
const TakenBranchInfo &Info = AggrLBR.second; | ||
- doBranch(Loc.Fro | ||
- /*IsPreagg*/ false); | ||
+ doBranch(Loc.Fro | ||
} | ||
} | ||
@@ -1722,18 +1754,10 @@ std::error_code DataAggregator:: | ||
outs() << "PERF2BOLT: parsing pre-aggregated profile...\n"; | ||
NamedRegionTimer | ||
TimerGroupName, TimerGroupDesc, opts::TimeAggreg | ||
- while (hasData()) { | ||
- ErrorOr<Aggregat | ||
- if (std::error_code | ||
+ while (hasData()) | ||
+ if (std::error_code | ||
return EC; | ||
- for (const uint64_t Addr : {AggrEntry->From | ||
- if (BinaryFunction *BF = getBinaryFunctio | ||
- BF->setHasProfil | ||
- | ||
- AggregatedLBRs.e | ||
- } | ||
- | ||
return std::error_code( | ||
} | ||
@@ -1746,8 +1770,9 @@ void DataAggregator:: | ||
for (const AggregatedLBREnt | ||
switch (AggrEntry.Entry | ||
case AggregatedLBREnt | ||
+ case AggregatedLBREnt | ||
doBranch(AggrEnt | ||
- AggrEntry.Mispre | ||
+ AggrEntry.Mispre | ||
break; | ||
case AggregatedLBREnt | ||
case AggregatedLBREnt |
@@ -125,11 +125,11 @@ struct JITLinkLinker::C | ||
std::string SymName = (*Symbol.first). | ||
LLVM_DEBUG(dbgs( | ||
- if (auto Address = Linker.lookupSym | ||
+ if (auto SymInfo = Linker.lookupSym | ||
LLVM_DEBUG(dbgs( | ||
- << Twine::utohexstr | ||
+ << Twine::utohexstr | ||
AllResults[Symbo | ||
- orc::ExecutorAdd | ||
+ orc::ExecutorAdd | ||
continue; | ||
} | ||
@@ -20,6 +20,7 @@ | ||
#include "bolt/Passes/Bin | ||
#include "bolt/Passes/Cac | ||
#include "bolt/Passes/Ide | ||
+#include "bolt/Passes/Non | ||
#include "bolt/Passes/Reo | ||
#include "bolt/Profile/Bo | ||
#include "bolt/Profile/Da | ||
@@ -108,34 +109,33 @@ cl::opt<std::str | ||
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(BoltCate | ||
static cl::list<std::st | ||
- ForceFunctionNam | ||
- cl::desc("limit optimizations to functions from the " | ||
- "list; local symbols must be suffixed"), | ||
- cl::value_desc(" | ||
- cl::cat(BoltCate | ||
+ForceFunctionNa | ||
+ cl::CommaSeparat | ||
+ cl::desc("limit optimizations to functions from the list"), | ||
+ cl::value_desc(" | ||
+ cl::Hidden, | ||
+ cl::cat(BoltCate | ||
static cl::opt<std::str | ||
- FunctionNamesFil | ||
- cl::desc("file with list of functions to optimize; local " | ||
- "symbols must be suffixed"), | ||
- cl::Hidden, cl::cat(BoltCate | ||
+FunctionNamesFi | ||
+ cl::desc("file with list of functions to optimize"), | ||
+ cl::Hidden, | ||
+ cl::cat(BoltCate | ||
static cl::list<std::st | ||
"funcs-no-regex" | ||
- 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(" | ||
-static cl::opt<std::str | ||
- FunctionNamesFil | ||
- cl::desc("file with list of functions to optimize " | ||
- "(non-regex); local symbols must be suffixed"), | ||
- cl::Hidden, cl::cat(BoltCate | ||
+static cl::opt<std::str | ||
+ "funcs-file-no-r | ||
+ cl::desc("file with list of functions to optimize (non-regex)"), cl::Hidden, | ||
+ cl::cat(BoltCate | ||
cl::opt<bool> | ||
KeepTmp("keep-tm | ||
@@ -246,6 +246,13 @@ static cl::opt<bool> WriteBoltInfoSec | ||
"bolt-info", cl::desc("write bolt info section in the output binary"), | ||
cl::init(true), cl::Hidden, cl::cat(BoltOutp | ||
+cl::list<Gadget | ||
+ GadgetScannersTo | ||
+ cl::values(clEnu | ||
+ clEnumValN(GS_AL | ||
+ cl::ZeroOrMore, cl::CommaSeparat | ||
+ cl::cat(BinaryAn | ||
+ | ||
} // namespace opts | ||
// FIXME: implement a better way to mark sections for replacement. | ||
@@ -3431,7 +3438,8 @@ void RewriteInstance: | ||
}; | ||
ParallelUtilitie | ||
- return !shouldDisassemb | ||
+ // Construct CFG for non-simple functions in aggregation mode. | ||
+ return !(shouldDisassem | ||
}; | ||
ParallelUtilitie | ||
@@ -3491,7 +3499,24 @@ void RewriteInstance: | ||
BC->logBOLTError | ||
} | ||
-void RewriteInstance: | ||
+void RewriteInstance: | ||
+ NamedRegionTimer | ||
+ TimerGroupName, TimerGroupDesc, opts::TimeRewrit | ||
+ BinaryFunctionPa | ||
+ // 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::GadgetScan | ||
+ // if no command line option was given, act as if "all" was specified. | ||
+ if (opts::GadgetSca | ||
+ opts::GadgetScan | ||
+ for (GSK ScannerToRun : opts::GadgetScan | ||
+ if (ScannerToRun == GSK::GS_PACRET || ScannerToRun == GSK::GS_ALL) | ||
+ Manager.register | ||
+ std::make_unique | ||
+ } | ||
+ | ||
+ BC->logBOLTError | ||
+} | ||
void RewriteInstance: | ||
// Preregister sections before emission to set their order in the output. | ||
@@ -5908,9 +5933,9 @@ void RewriteInstance: | ||
} | ||
uint64_t RewriteInstance: | ||
- auto Value = Linker->lookupSy | ||
+ auto Value = Linker->lookupSy | ||
if (Value) | ||
- return *Value; | ||
+ return Value->Address; | ||
// Return the original value if we haven't emitted the symbol. | ||
BinaryData *BD = BC->getBinaryDat |
@@ -68,10 +68,11 @@ void HugifyRuntimeLib | ||
assert(!RuntimeS | ||
"We don't currently support linking multiple runtime libraries"); | ||
- RuntimeStartAddr | ||
- if (!RuntimeStartAd | ||
+ auto StartSymInfo = Linker.lookupSym | ||
+ if (!StartSymInfo) { | ||
errs() << "BOLT-ERROR: hugify library does not define __bolt_hugify_se | ||
<< LibPath << "\n"; | ||
exit(1); | ||
} | ||
+ RuntimeStartAddr | ||
} |
@@ -203,27 +203,35 @@ void InstrumentationR | ||
if (BC.isMachO()) | ||
return; | ||
- RuntimeFiniAddre | ||
- if (!RuntimeFiniAdd | ||
+ std::optional<BO | ||
+ Linker.lookupSym | ||
+ if (!FiniSymInfo) { | ||
errs() << "BOLT-ERROR: instrumentation library does not define " | ||
"__bolt_instr_fi | ||
<< LibPath << "\n"; | ||
exit(1); | ||
} | ||
- RuntimeStartAddr | ||
- if (!RuntimeStartAd | ||
+ RuntimeFiniAddre | ||
+ | ||
+ std::optional<BO | ||
+ Linker.lookupSym | ||
+ if (!StartSymInfo) { | ||
errs() << "BOLT-ERROR: instrumentation library does not define " | ||
"__bolt_instr_st | ||
<< LibPath << "\n"; | ||
exit(1); | ||
} | ||
+ RuntimeStartAddr | ||
+ | ||
outs() << "BOLT-INFO: output linked against instrumentation runtime " | ||
"library, lib entry point is 0x" | ||
- << Twine::utohexstr | ||
+ << Twine::utohexstr | ||
+ | ||
+ std::optional<BO | ||
+ Linker.lookupSym | ||
+ const uint64_t ClearSymAddress = ClearSymInfo ? ClearSymInfo->Ad | ||
outs() << "BOLT-INFO: clear procedure is 0x" | ||
- << Twine::utohexstr | ||
- Linker.lookupSym | ||
- << "\n"; | ||
+ << Twine::utohexstr | ||
emitTablesAsELFN | ||
} |
@@ -18,6 +18,7 @@ | ||
#include "llvm/Object/Arc | ||
#include "llvm/Object/Obj | ||
#include "llvm/Support/Pa | ||
+#include "llvm/Support/Pr | ||
#define DEBUG_TYPE "bolt-rtlib" | ||
@@ -38,6 +39,23 @@ std::string RuntimeLibrary:: | ||
llvm::sys::path: | ||
} | ||
llvm::sys::path: | ||
+ if (!llvm::sys::fs: | ||
+ // If it is a symlink, check the directory that the symlink points to. | ||
+ if (llvm::sys::fs:: | ||
+ SmallString<256> | ||
+ llvm::sys::fs::r | ||
+ if (llvm::ErrorOr<s | ||
+ llvm::sys::findP | ||
+ 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(LibP | ||
} | ||
@@ -23,6 +23,7 @@ | ||
#include "llvm/MC/MCFixup | ||
#include "llvm/MC/MCInstB | ||
#include "llvm/MC/MCInstr | ||
+#include "llvm/MC/MCRegis | ||
#include "llvm/MC/MCRegis | ||
#include "llvm/Support/Da | ||
#include "llvm/Support/De | ||
@@ -183,6 +184,88 @@ public: | ||
return false; | ||
} | ||
+ ErrorOr<MCPhysRe | ||
+ switch (Inst.getOpcode( | ||
+ case AArch64::AUTIAZ: | ||
+ case AArch64::AUTIBZ: | ||
+ case AArch64::AUTIASP | ||
+ case AArch64::AUTIBSP | ||
+ case AArch64::AUTIASP | ||
+ case AArch64::AUTIBSP | ||
+ case AArch64::AUTIASP | ||
+ case AArch64::AUTIBSP | ||
+ case AArch64::RETAA: | ||
+ case AArch64::RETAB: | ||
+ case AArch64::RETAASP | ||
+ case AArch64::RETABSP | ||
+ case AArch64::RETAASP | ||
+ case AArch64::RETABSP | ||
+ return AArch64::LR; | ||
+ | ||
+ case AArch64::AUTIA17 | ||
+ case AArch64::AUTIB17 | ||
+ case AArch64::AUTIA17 | ||
+ case AArch64::AUTIB17 | ||
+ 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( | ||
+ | ||
+ 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( | ||
+ | ||
+ // FIXME: BL?RA(A|B)Z? and LDRA(A|B) should probably be handled here too. | ||
+ | ||
+ default: | ||
+ return getNoRegister(); | ||
+ } | ||
+ } | ||
+ | ||
+ bool isAuthentication | ||
+ if (Reg == getNoRegister()) | ||
+ return false; | ||
+ ErrorOr<MCPhysRe | ||
+ return AuthenticatedReg | ||
+ } | ||
+ | ||
+ ErrorOr<MCPhysRe | ||
+ assert(isReturn( | ||
+ switch (Inst.getOpcode( | ||
+ case AArch64::RET: | ||
+ return Inst.getOperand( | ||
+ case AArch64::RETAA: | ||
+ case AArch64::RETAB: | ||
+ case AArch64::RETAASP | ||
+ case AArch64::RETABSP | ||
+ case AArch64::RETAASP | ||
+ case AArch64::RETABSP | ||
+ return AArch64::LR; | ||
+ case AArch64::ERET: | ||
+ case AArch64::ERETAA: | ||
+ case AArch64::ERETAB: | ||
+ return make_error_code( | ||
+ default: | ||
+ llvm_unreachable | ||
+ } | ||
+ } | ||
+ | ||
bool isADRP(const MCInst &Inst) const override { | ||
return Inst.getOpcode() | ||
} |
@@ -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 | ||
.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- | ||
# CHECK-NEXT: .word 0x4f82e010 | ||
# CHECK-NEXT: ret | ||
+# CHECK-BOLT-ONLY- | ||
# CHECK-NEXT: .short 0xff00 | ||
# CHECK-NEXT: .byte 0x42 | ||
-.size _start, .-_start | ||
## Force relocation mode. | ||
.reloc 0, R_AARCH64_NONE |
@@ -61,6 +61,11 @@ YAML-BAT-CHECK-N | ||
YAML-BAT-CHECK-N | ||
YAML-BAT-CHECK-N | ||
YAML-BAT-CHECK-N | ||
+# Check fallthroughs in non-BAT function | ||
+YAML-BAT-CHECK- | ||
+YAML-BAT-CHECK- | ||
+YAML-BAT-CHECK- | ||
+YAML-BAT-CHECK- | ||
# Calls from no-BAT to BAT function | ||
YAML-BAT-CHECK: - bid: 28 | ||
YAML-BAT-CHECK-N |
@@ -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 | ||
-# RUN: llvm-bolt %t.exe --pa -p %t.pa1 -o %t.out \ | ||
+# RUN: llvm-strip --strip-unneeded | ||
+# RUN: llvm-objcopy --remove-section | ||
+# RUN: llvm-bolt %t.strip --pa -p %t.pa1 -o %t.out \ | ||
# RUN: --print-cfg --print-only=mai | ||
## Check that getFallthroughsI | ||
## 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=mai | ||
## Check that we don't treat secondary entry points as call continuation sites. | ||
@@ -24,8 +26,21 @@ | ||
# RUN: --print-cfg --print-only=mai | ||
## Check fallthrough to a landing pad case. | ||
-# RUN: llvm-bolt %t.exe --pa -p %t.pa4 -o %t.out \ | ||
-# RUN: --print-cfg --print-only=mai | ||
+# RUN: llvm-bolt %t.strip --pa -p %t.pa3 -o %t.out \ | ||
+# RUN: --print-cfg --print-only=mai | ||
+ | ||
+## 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=mai | ||
+ | ||
+## 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=mai | ||
+## 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=mai | ||
.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: |
@@ -0,0 +1,24 @@ | ||
+## Checks that fallthroughs spanning entry points are accepted in aggregation | ||
+## mode. | ||
+ | ||
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-u | ||
+# 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 |
@@ -0,0 +1,26 @@ | ||
+## Check skip-inline flag behavior | ||
+ | ||
+# RUN: llvm-mc --filetype=obj --triple=x86_64- | ||
+# RUN: ld.lld %t.o -o %t.exe -q | ||
+# RUN: llvm-bolt %t.exe --inline-small-f | ||
+# RUN: -o %t.null | FileCheck %s --check-prefix=C | ||
+# RUN: llvm-bolt %t.exe --inline-small-f | ||
+# RUN: --print-only=mai | ||
+# CHECK-INLINE: Binary Function "main" | ||
+# CHECK-INLINE: ud2 | ||
+# CHECK-NO-INLINE: | ||
+# CHECK-NO-INLINE: | ||
+ | ||
+.globl _start | ||
+_start: | ||
+ call main | ||
+ | ||
+.globl main | ||
+main: | ||
+ call foo | ||
+ ret | ||
+ | ||
+.globl foo | ||
+foo: | ||
+ ud2 | ||
+ ret |
@@ -13,7 +13,7 @@ NONEXISTINGFILEA | ||
RUN: not llvm-bolt-binary | ||
NOELFFILEARG: llvm-bolt-binary | ||
-RUN: %clang %cflags %p/../../Inputs/ | ||
+RUN: %clang %cflags -Wl,--emit-reloc | ||
RUN: llvm-bolt-binary | ||
# Check that there are no BOLT-WARNING or BOLT-ERROR output lines | ||
VALIDELFFILEARG: | ||
@@ -30,4 +30,10 @@ HELP-NEXT: USAGE: llvm-bolt-binary | ||
HELP-EMPTY: | ||
HELP-NEXT: OPTIONS: | ||
HELP-EMPTY: | ||
+HELP-NEXT: BinaryAnalysis options: | ||
+HELP-EMPTY: | ||
+HELP-NEXT: --scanners=<valu | ||
+HELP-NEXT: =pacret - pac-ret | ||
+HELP-NEXT: =all - all | ||
+HELP-EMPTY: | ||
HELP-NEXT: Generic Options: |
@@ -0,0 +1,878 @@ | ||
+// RUN: %clang %cflags -march=armv9.5-a | ||
+// RUN: llvm-bolt-binary | ||
+ | ||
+ .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.]+} | ||
+// 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_o | ||
+ .type f_intermediate_o | ||
+f_intermediate_ | ||
+ 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_o | ||
+// 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_o | ||
+ | ||
+ .globl f_intermediate_o | ||
+ .type f_intermediate_o | ||
+f_intermediate_ | ||
+ 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_o | ||
+// 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_o | ||
+ | ||
+ .globl f_intermediate_r | ||
+ .type f_intermediate_r | ||
+f_intermediate_ | ||
+ 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_r | ||
+ ret | ||
+ .size f_intermediate_r | ||
+ | ||
+ .globl f_intermediate_o | ||
+ .type f_intermediate_o | ||
+f_intermediate_ | ||
+ 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_o | ||
+// 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_o | ||
+ | ||
+ .globl f_nonx30_ret | ||
+ .type f_nonx30_ret,@fu | ||
+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.]+} | ||
+// 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, | ||
+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, | ||
+ | ||
+ .globl f_detect_clobber | ||
+ .type f_detect_clobber | ||
+f_detect_clobbe | ||
+ 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-control | ||
+// CHECK-NOT: function f_detect_clobber | ||
+ b f_tail_called | ||
+ .size f_detect_clobber | ||
+ | ||
+ .globl f_tail_called | ||
+ .type f_tail_called,@f | ||
+f_tail_called: | ||
+ ret | ||
+ .size f_tail_called, .-f_tail_called | ||
+ | ||
+ .globl f_nonx30_ret_non | ||
+ .type f_nonx30_ret_non | ||
+f_nonx30_ret_no | ||
+// 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.c | ||
+// CHECK-NOT: f_nonx30_ret_non | ||
+ ret x1 | ||
+ .size f_nonx30_ret_non | ||
+ | ||
+ | ||
+ .globl f_callclobbered_ | ||
+ .type f_callclobbered_ | ||
+f_callclobbered | ||
+ bl g | ||
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_callclobbered_ | ||
+// 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_ | ||
+ | ||
+ .globl f_callclobbered_ | ||
+ .type f_callclobbered_ | ||
+f_callclobbered | ||
+ bl g | ||
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_callclobbered_ | ||
+// 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 | ||
+ ret x19 | ||
+ .size f_callclobbered_ | ||
+ | ||
+ | ||
+/// Now do a basic sanity check on every different Authentication instruction: | ||
+ | ||
+ .globl f_autiasp | ||
+ .type f_autiasp,@funct | ||
+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,@funct | ||
+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,@functi | ||
+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,@functi | ||
+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,@fun | ||
+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.]+} | ||
+// 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,@fun | ||
+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.]+} | ||
+// 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,@func | ||
+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.]+} | ||
+// 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,@func | ||
+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.]+} | ||
+// 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,@func | ||
+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,@func | ||
+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,@func | ||
+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.]+} | ||
+// 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,@func | ||
+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.]+} | ||
+// 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,@func | ||
+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,@func | ||
+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,@fun | ||
+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.]+} | ||
+// 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,@fun | ||
+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.]+} | ||
+// 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,@fun | ||
+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,@fun | ||
+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,@fun | ||
+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.]+} | ||
+// 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,@fun | ||
+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.]+} | ||
+// 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,@fun | ||
+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,@fun | ||
+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,@functio | ||
+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,@functio | ||
+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,@functi | ||
+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.]+} | ||
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: eretaa | ||
+ eretaa | ||
+ .size f_eretaa, .-f_eretaa | ||
+ | ||
+ .globl f_eretab | ||
+ .type f_eretab,@functi | ||
+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.]+} | ||
+// 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.]+} | ||
+// CHECK-NEXT: The return instruction is {{[0-9a-f]+}}: eret | ||
+ eret | ||
+ .size f_eret, .-f_eret | ||
+ | ||
+ .globl f_movx30reg | ||
+ .type f_movx30reg,@fun | ||
+f_movx30reg: | ||
+// CHECK-LABEL: GS-PACRET: non-protected ret found in function f_movx30reg, 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]+}}: 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,@fu | ||
+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,@fu | ||
+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,@fu | ||
+ | ||
+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,@fu | ||
+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,@fu | ||
+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,@fu | ||
+ | ||
+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,@f | ||
+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.]+} | ||
+// 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,@f | ||
+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.]+} | ||
+// 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 | ||
+ |
@@ -0,0 +1,75 @@ | ||
+// RUN: %clang %cflags -march=armv8.3-a | ||
+// RUN: llvm-bolt-binary | ||
+ | ||
+ | ||
+// Verify that we can also detect gadgets across basic blocks | ||
+ | ||
+ .globl f_crossbb1 | ||
+ .type f_crossbb1,@func | ||
+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,@func | ||
+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 | ||
+f_shrinkwrappin | ||
+ cbz x0, 1f | ||
+ paciasp | ||
+ stp x29, x30, [sp, #-16]! | ||
+ ldp x29, x30, [sp], #16 | ||
+ autiasp | ||
+1: | ||
+ ret | ||
+ .size f_shrinkwrapping | ||
+// CHECK-NOT: f_shrinkwrapping | ||
+ | ||
+ .globl f_multi_auth_ins | ||
+ .type f_multi_auth_ins | ||
+f_multi_auth_in | ||
+ 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_ins | ||
+// CHECK-NOT: f_multi_auth_ins | ||
+ | ||
+// TODO: also verify that false negatives exist in across-BB gadgets in functions | ||
+// for which bolt cannot reconstruct the call graph. |
@@ -1,7 +1,7 @@ | ||
if "AArch64" not in config.root.targ | ||
config.unsupport | ||
-flags = "--target=aarch6 | ||
+flags = "--target=aarch6 | ||
config.substitut | ||
config.substitut |
@@ -34,9 +34,9 @@ prefix_pat = re.compile(f"^# {args.prefix}: (.*)") | ||
fdata_pat = re.compile(r"([0 | ||
# Pre-aggregated profile: | ||
-# {B|F|f} [<start_id>:]<st | ||
-# [<mispred_count> | ||
-preagg_pat = re.compile(r"(?P | ||
+# {T|B|F|f} [<start_id>:]<st | ||
+# <count> [<mispred_count> | ||
+preagg_pat = re.compile(r"(?P | ||
# No-LBR profile: | ||
# <is symbol?> <closest elf symbol or DSO name> <relative address> <count> |
@@ -1,5 +1,5 @@ | ||
set(LLVM_LINK_CO | ||
- ${LLVM_TARGETS_T | ||
+ ${BOLT_TARGETS_T | ||
MC | ||
Object | ||
Support |
@@ -88,13 +88,15 @@ int main(int argc, char **argv) { | ||
llvm_shutdown_ob | ||
// Initialize targets and assembly printers/parsers | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
+#define BOLT_TARGET(targ | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ | ||
+#include "bolt/Core/Targe | ||
ParseCommandLine | ||
@@ -1,5 +1,5 @@ | ||
set(LLVM_LINK_CO | ||
- ${LLVM_TARGETS_T | ||
+ ${BOLT_TARGETS_T | ||
MC | ||
Object | ||
Support |
@@ -173,16 +173,6 @@ void boltMode(int argc, char **argv) { | ||
} | ||
} | ||
-static std::string GetExecutablePat | ||
- SmallString<256> | ||
- // Do a PATH lookup if Argv0 isn't a valid path. | ||
- if (!llvm::sys::fs: | ||
- if (llvm::ErrorOr<s | ||
- llvm::sys::findP | ||
- ExecutablePath = *P; | ||
- return std::string(Exec | ||
-} | ||
- | ||
int main(int argc, char **argv) { | ||
// Print a stack trace if we signal out. | ||
sys::PrintStackT | ||
@@ -190,16 +180,18 @@ int main(int argc, char **argv) { | ||
llvm_shutdown_ob | ||
- std::string ToolPath = GetExecutablePat | ||
+ std::string ToolPath = llvm::sys::fs::g | ||
// Initialize targets and assembly printers/parsers | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
+#define BOLT_TARGET(targ | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ | ||
+#include "bolt/Core/Targe | ||
ToolName = argv[0]; | ||
@@ -1,5 +1,5 @@ | ||
set(LLVM_LINK_CO | ||
- ${LLVM_TARGETS_T | ||
+ ${BOLT_TARGETS_T | ||
MC | ||
Object | ||
Support |
@@ -76,13 +76,15 @@ int main(int argc, char **argv) { | ||
opts::OutputFile | ||
// Initialize targets and assembly printers/parsers | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
+#define BOLT_TARGET(targ | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ | ||
+#include "bolt/Core/Targe | ||
ToolName = argv[0]; | ||
std::string ToolPath = GetExecutablePat |
@@ -1,5 +1,5 @@ | ||
set(LLVM_LINK_CO | ||
- ${LLVM_TARGETS_T | ||
+ ${BOLT_TARGETS_T | ||
) | ||
add_llvm_fuzzer( |
@@ -58,13 +58,16 @@ extern "C" int LLVMFuzzerTestOn | ||
extern "C" LLVM_ATTRIBUTE_U | ||
char ***argv) { | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
+ // Initialize targets and assembly printers/parsers | ||
+#define BOLT_TARGET(targ | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
+#include "bolt/Core/Targe | ||
return 0; | ||
} |
@@ -27,12 +27,15 @@ struct BinaryContextTes | ||
protected: | ||
void initalizeLLVM() { | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
+#define BOLT_TARGET(targ | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ | ||
+#include "bolt/Core/Targe | ||
} | ||
void prepareElf() { | ||
@@ -93,12 +96,13 @@ TEST_P(BinaryCon | ||
DataSize, 4); | ||
MCSymbol *RelSymbol1 = BC->getOrCreateG | ||
ASSERT_TRUE(RelS | ||
- BS.addRelocation | ||
+ BS.addPendingRel | ||
+ Relocation{8, RelSymbol1, ELF::R_AARCH64_C | ||
MCSymbol *RelSymbol2 = BC->getOrCreateG | ||
ASSERT_TRUE(RelS | ||
- BS.addRelocation | ||
+ BS.addPendingRel | ||
+ Relocation{12, RelSymbol2, ELF::R_AARCH64_C | ||
- std::error_code EC; | ||
SmallVector<char | ||
raw_svector_ostr | ||
@@ -134,12 +138,13 @@ TEST_P(BinaryCon | ||
(uint8_t *)Data, Size, 4); | ||
MCSymbol *RelSymbol1 = BC->getOrCreateG | ||
ASSERT_TRUE(RelS | ||
- BS.addRelocation | ||
+ BS.addPendingRel | ||
+ Relocation{8, RelSymbol1, ELF::R_AARCH64_J | ||
MCSymbol *RelSymbol2 = BC->getOrCreateG | ||
ASSERT_TRUE(RelS | ||
- BS.addRelocation | ||
+ BS.addPendingRel | ||
+ Relocation{12, RelSymbol2, ELF::R_AARCH64_J | ||
- std::error_code EC; | ||
SmallVector<char | ||
raw_svector_ostr | ||
@@ -2,7 +2,7 @@ set(LLVM_LINK_CO | ||
DebugInfoDWARF | ||
Object | ||
MC | ||
- ${LLVM_TARGETS_T | ||
+ ${BOLT_TARGETS_T | ||
) | ||
add_bolt_unittes |
@@ -37,12 +37,15 @@ struct MCPlusBuilderTes | ||
protected: | ||
void initalizeLLVM() { | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
+#define BOLT_TARGET(targ | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ | ||
+#include "bolt/Core/Targe | ||
} | ||
void prepareElf() { |
@@ -38,12 +38,15 @@ struct MemoryMapsTester | ||
protected: | ||
void initalizeLLVM() { | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
- llvm::Initialize | ||
+#define BOLT_TARGET(targ | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ LLVMInitialize## | ||
+ | ||
+#include "bolt/Core/Targe | ||
} | ||
void prepareElf() { |
@@ -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 |
@@ -494,18 +494,31 @@ genReferencesBlo | ||
static std::unique_ptr< | ||
writeFileDefinit | ||
std::optional<St | ||
- if (!L.IsFileInRoot | ||
+ if (!L.IsFileInRoot | ||
return std::make_unique | ||
HTMLTag::TAG_P, "Defined at line " + std::to_string(L | ||
" of file " + L.Filename); | ||
SmallString<128> | ||
- llvm::sys::path: | ||
+ llvm::sys::path: | ||
+ FileURL, llvm::sys::path: | ||
+ // 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: | ||
+ 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: | ||
auto Node = std::make_unique | ||
Node->Children.e | ||
auto LocNumberNode = | ||
std::make_unique | ||
// 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->A | ||
"href", (FileURL + "#" + std::to_string(L | ||
Node->Children.e |
@@ -70,11 +70,12 @@ getNewFieldsOrde | ||
} | ||
SmallVector<unsi | ||
for (const auto &Name : DesiredFieldsOrd | ||
- if (!NameToIndex.co | ||
+ auto It = NameToIndex.find | ||
+ if (It == NameToIndex.end( | ||
llvm::errs() << "Field " << Name << " not found in definition.\n"; | ||
return {}; | ||
} | ||
- NewFieldsOrder.p | ||
+ NewFieldsOrder.p | ||
} | ||
assert(NewFields | ||
return NewFieldsOrder; |
@@ -462,7 +462,7 @@ ClangTidyASTCons | ||
std::unique_ptr< | ||
ento::CreateAnal | ||
AnalysisConsumer | ||
- new AnalyzerDiagnost | ||
+ std::make_unique | ||
Consumers.push_b | ||
} | ||
#endif // CLANG_TIDY_ENABL |
@@ -117,6 +117,7 @@ clang_target_lin | ||
clangASTMatchers | ||
clangBasic | ||
clangLex | ||
+ clangSema | ||
clangTooling | ||
clangTransformer | ||
) |
@@ -20,6 +20,7 @@ | ||
#include "clang/Basic/Dia | ||
#include "clang/Basic/Sou | ||
#include "clang/Lex/Lexer | ||
+#include "clang/Sema/Heur | ||
#include "llvm/ADT/STLExt | ||
#include "llvm/ADT/SmallV | ||
#include "llvm/Support/Ca | ||
@@ -125,8 +126,8 @@ void StandaloneEmptyC | ||
DeclarationName Name = | ||
Context.Declarat | ||
- auto Candidates = MemberCall->getR | ||
- Name, [](const NamedDecl *ND) { | ||
+ auto Candidates = HeuristicResolve | ||
+ MemberCall->getR | ||
return isa<CXXMethodDec | ||
llvm::cast<CXXMe | ||
0 && | ||
@@ -174,8 +175,8 @@ void StandaloneEmptyC | ||
DeclarationName Name = | ||
Context.Declarat | ||
- auto Candidates = | ||
- ArgRecordDecl->l | ||
+ auto Candidates = HeuristicResolve | ||
+ ArgRecordDecl, Name, [](const NamedDecl *ND) { | ||
return isa<CXXMethodDec | ||
llvm::cast<CXXMe | ||
0 && |
@@ -82,7 +82,7 @@ void StringConstructo | ||
Finder->addMatch | ||
cxxConstructExpr | ||
hasDeclaration(c | ||
- hasArgument(0, hasType(qualType | ||
+ argumentCountIs( | ||
hasArgument(1, hasType(qualType | ||
anyOf( | ||
// Detect the expression: string('x', 40); | ||
@@ -102,7 +102,7 @@ void StringConstructo | ||
cxxConstructExpr | ||
hasDeclaration(c | ||
cxxRecordDecl(ha | ||
- hasArgument(0, hasType(CharPtrT | ||
+ argumentCountIs( | ||
hasArgument(1, hasType(isIntege | ||
anyOf( | ||
// Detect the expression: string("...", 0); | ||
@@ -114,7 +114,34 @@ void StringConstructo | ||
// Detect the expression: string("lit", 5) | ||
allOf(hasArgumen | ||
hasArgument(1, ignoringParenImp | ||
- integerLiteral() | ||
+ integerLiteral() | ||
+ .bind("construct | ||
+ 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->addMatch | ||
+ cxxConstructExpr | ||
+ hasDeclaration(c | ||
+ cxxRecordDecl(ha | ||
+ argumentCountIs( | ||
+ hasArgument(1, hasType(qualType | ||
+ hasArgument(2, hasType(qualType | ||
+ anyOf( | ||
+ // Detect the expression: string("...", 1, 0); | ||
+ hasArgument(2, ZeroExpr.bind("e | ||
+ // Detect the expression: string("...", -4, 1); | ||
+ hasArgument(1, NegativeExpr.bin | ||
+ // Detect the expression: string("...", 0, -4); | ||
+ hasArgument(2, NegativeExpr.bin | ||
+ // Detect the expression: string("lit", 0, 0x1234567); | ||
+ hasArgument(2, LargeLengthExpr. | ||
+ // Detect the expression: string("lit", 1, 5) | ||
+ allOf(hasArgumen | ||
+ hasArgument( | ||
+ 1, ignoringParenImp | ||
+ hasArgument(2, ignoringParenImp | ||
+ integerLiteral() | ||
.bind("construct | ||
this); | ||
@@ -155,14 +182,27 @@ void StringConstructo | ||
diag(Loc, "constructor creating an empty string"); | ||
} else if (Result.Nodes.ge | ||
diag(Loc, "negative value used as length parameter"); | ||
+ } else if (Result.Nodes.ge | ||
+ diag(Loc, "negative value used as position of the " | ||
+ "first character parameter"); | ||
} else if (Result.Nodes.ge | ||
if (WarnOnLargeLeng | ||
diag(Loc, "suspicious large length parameter"); | ||
} else if (Result.Nodes.ge | ||
const auto *Str = Result.Nodes.get | ||
- const auto *Lit = Result.Nodes.get | ||
- if (Lit->getValue() | ||
+ const auto *Length = Result.Nodes.get | ||
+ if (Length->getValu | ||
diag(Loc, "length is bigger than string literal size"); | ||
+ return; | ||
+ } | ||
+ if (const auto *Pos = Result.Nodes.get | ||
+ if (Pos->getValue() | ||
+ diag(Loc, "position of the first character parameter is bigger than " | ||
+ "string literal character range"); | ||
+ } else if (Length->getValu | ||
+ (Str->getLength( | ||
+ diag(Loc, "length is bigger than remaining string literal size"); | ||
+ } | ||
} | ||
} else if (const auto *Ptr = Result.Nodes.get | ||
Expr::EvalResult |
@@ -179,15 +179,15 @@ static bool checkOverrideByD | ||
bool VirtualNearMissC | ||
const CXXMethodDecl *BaseMD) { | ||
- auto Iter = PossibleMap.find | ||
- if (Iter != PossibleMap.end( | ||
+ auto [Iter, Inserted] = PossibleMap.try_ | ||
+ if (!Inserted) | ||
return Iter->second; | ||
bool IsPossible = !BaseMD->isImpli | ||
!isa<CXXDestruct | ||
!BaseMD->isOverl | ||
!isa<CXXConversi | ||
- PossibleMap[Base | ||
+ Iter->second = IsPossible; | ||
return IsPossible; | ||
} | ||
@@ -8,6 +8,8 @@ | ||
#include "ConstCorrectnes | ||
#include "../utils/FixItH | ||
+#include "../utils/Matche | ||
+#include "../utils/Option | ||
#include "clang/AST/ASTCo | ||
#include "clang/ASTMatche | ||
#include "clang/ASTMatche | ||
@@ -41,7 +43,9 @@ ConstCorrectness | ||
TransformValues( | ||
TransformReferen | ||
TransformPointer | ||
- Options.get("Tra | ||
+ Options.get("Tra | ||
+ AllowedTypes( | ||
+ utils::options:: | ||
if (AnalyzeValues == false && AnalyzeReference | ||
this->configurat | ||
"The check 'misc-const-corr | ||
@@ -57,6 +61,8 @@ void ConstCorrectness | ||
Options.store(Op | ||
Options.store(Op | ||
Options.store(Op | ||
+ Options.store(Op | ||
+ utils::options:: | ||
} | ||
void ConstCorrectness | ||
@@ -73,6 +79,12 @@ void ConstCorrectness | ||
hasType(referenc | ||
hasType(referenc | ||
+ const auto AllowedType = hasType(qualType | ||
+ hasDeclaration(n | ||
+ references(named | ||
+ pointerType(poin | ||
+ namedDecl(matche | ||
+ | ||
const auto AutoTemplateType | ||
anyOf(hasType(au | ||
hasType(pointerT | ||
@@ -87,7 +99,8 @@ void ConstCorrectness | ||
unless(anyOf(Con | ||
hasInitializer(i | ||
RValueReference, | ||
- hasType(cxxRecor | ||
+ hasType(cxxRecor | ||
+ AllowedType))); | ||
// Match the function scope for which the analysis of all local variables | ||
// shall be run. |
@@ -45,6 +45,7 @@ private: | ||
const bool TransformValues; | ||
const bool TransformReferen | ||
const bool TransformPointer | ||
+ const std::vector<Stri | ||
}; | ||
} // namespace clang::tidy::mis |
@@ -139,10 +139,8 @@ static bool areEquivalentExp | ||
return cast<BinaryOpera | ||
cast<BinaryOpera | ||
case Stmt::UnaryExprO | ||
- const auto *LeftUnaryExpr = | ||
- cast<UnaryExprOr | ||
- const auto *RightUnaryExpr = | ||
- cast<UnaryExprOr | ||
+ const auto *LeftUnaryExpr = cast<UnaryExprOr | ||
+ const auto *RightUnaryExpr = cast<UnaryExprOr | ||
if (LeftUnaryExpr-> | ||
return LeftUnaryExpr->g | ||
LeftUnaryExpr->g | ||
@@ -699,7 +697,8 @@ static bool retrieveRelation | ||
Symbol = OverloadedOperat | ||
OperandExpr = OverloadedOperat | ||
- Opcode = BinaryOperator:: | ||
+ Opcode = BinaryOperator:: | ||
+ OverloadedOperat | ||
if (!retrieveIntege | ||
return false; | ||
@@ -728,7 +727,8 @@ static bool retrieveRelation | ||
} | ||
// Checks for expressions like (X == 4) && (Y != 9) | ||
-static bool areSidesBinaryCo | ||
+static bool areSidesBinaryCo | ||
+ const ASTContext *AstCtx) { | ||
const auto *LhsBinOp = dyn_cast<BinaryO | ||
const auto *RhsBinOp = dyn_cast<BinaryO | ||
@@ -747,6 +747,28 @@ static bool areSidesBinaryCo | ||
return false; | ||
} | ||
+static bool areSidesBinaryCo | ||
+ const BinaryOperator *&BinOp, const ASTContext *AstCtx) { | ||
+ if (areSidesBinaryC | ||
+ 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->getSourceRang | ||
+ if (!Lsr.getBegin() | ||
+ !E->isIntegerCon | ||
+ return false; | ||
+ return true; | ||
+ }; | ||
+ | ||
+ return IsDefineExpr(Lhs | ||
+} | ||
+ | ||
// 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 retrieveConstExp | ||
} | ||
static bool isSameRawIdentif | ||
- const SourceManager &SM) { | ||
+ const SourceManager &SM) { | ||
if (T1.getKind() != T2.getKind()) | ||
return false; | ||
if (T1.isNot(tok::r | ||
@@ -808,8 +830,8 @@ static bool areExprsFromDiff | ||
const ASTContext *AstCtx) { | ||
if (!LhsExpr || !RhsExpr) | ||
return false; | ||
- SourceRange Lsr = LhsExpr->getSour | ||
- SourceRange Rsr = RhsExpr->getSour | ||
+ const SourceRange Lsr = LhsExpr->getSour | ||
+ const SourceRange Rsr = RhsExpr->getSour | ||
if (!Lsr.getBegin() | ||
return false; | ||
@@ -847,11 +869,83 @@ static bool areExprsMacroAnd | ||
if (!LhsExpr || !RhsExpr) | ||
return false; | ||
- SourceLocation LhsLoc = LhsExpr->getExpr | ||
- SourceLocation RhsLoc = RhsExpr->getExpr | ||
+ const SourceLocation LhsLoc = LhsExpr->getExpr | ||
+ const SourceLocation RhsLoc = RhsExpr->getExpr | ||
return LhsLoc.isMacroID | ||
} | ||
+ | ||
+static bool areStringsSameIg | ||
+ 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 areExprsSameMacr | ||
+ const ASTContext *Context) { | ||
+ | ||
+ if (!BinOp) | ||
+ return false; | ||
+ | ||
+ const Expr *Lhs = BinOp->getLHS(); | ||
+ const Expr *Rhs = BinOp->getRHS(); | ||
+ const SourceManager &SM = Context->getSour | ||
+ | ||
+ const SourceRange Lsr = Lhs->getSourceRa | ||
+ const SourceRange Rsr = Rhs->getSourceRa | ||
+ if (Lsr.getBegin(). | ||
+ // Left is macro so right macro too | ||
+ if (Rsr.getBegin(). | ||
+ // Both sides are macros so they are same macro or literal | ||
+ const llvm::StringRef L = Lexer::getSource | ||
+ CharSourceRange: | ||
+ const llvm::StringRef R = Lexer::getSource | ||
+ CharSourceRange: | ||
+ return areStringsSameIg | ||
+ } | ||
+ // Left is macro but right is not so they are not same macro or literal | ||
+ return false; | ||
+ } | ||
+ const auto *Lil = dyn_cast<Integer | ||
+ const auto *Ril = dyn_cast<Integer | ||
+ if (Lil && Ril) | ||
+ return Lil->getValue() == Ril->getValue(); | ||
+ | ||
+ const auto *LStrl = dyn_cast<StringL | ||
+ const auto *RStrl = dyn_cast<StringL | ||
+ if (Lil && Ril) { | ||
+ const llvm::StringRef L = Lexer::getSource | ||
+ CharSourceRange: | ||
+ Context->getLang | ||
+ const llvm::StringRef R = Lexer::getSource | ||
+ CharSourceRange: | ||
+ Context->getLang | ||
+ return L.compare(R) == 0; | ||
+ } | ||
+ | ||
+ const auto *Lbl = dyn_cast<CXXBool | ||
+ const auto *Rbl = dyn_cast<CXXBool | ||
+ if (Lbl && Rbl) | ||
+ return Lbl->getValue() == Rbl->getValue(); | ||
+ | ||
+ return false; | ||
+} | ||
} // namespace | ||
void RedundantExpress | ||
@@ -1089,7 +1183,6 @@ static bool exprEvaluatesToS | ||
((Opcode == BO_And || Opcode == BO_AndAssign) && ~Value == 0); | ||
} | ||
- | ||
void RedundantExpress | ||
const MatchFinder::Mat | ||
if (const auto *ComparisonOpera | ||
@@ -1134,8 +1227,8 @@ void RedundantExpress | ||
ConstExpr)) | ||
return; | ||
- if((Value != 0 && ~Value != 0) || Sym->getExprLoc( | ||
- return; | ||
+ if ((Value != 0 && ~Value != 0) || Sym->getExprLoc( | ||
+ return; | ||
SourceLocation Loc = IneffectiveOpera | ||
@@ -1276,19 +1369,23 @@ void RedundantExpress | ||
return; | ||
} | ||
- if (areSidesBinaryC | ||
+ if (areSidesBinaryC | ||
+ BinOp, Result.Context)) | ||
const Expr *LhsConst = nullptr, *RhsConst = nullptr; | ||
BinaryOperatorKi | ||
- | ||
- if (!retrieveConstE | ||
- LhsConst, RhsConst, Result.Context)) | ||
- return; | ||
- | ||
- if (areExprsFromDif | ||
- areExprsMacroAnd | ||
- return; | ||
+ if (areSidesBinaryC | ||
+ if (!retrieveConstE | ||
+ LhsConst, RhsConst, Result.Context)) | ||
+ return; | ||
+ | ||
+ if (areExprsFromDif | ||
+ areExprsMacroAnd | ||
+ return; | ||
+ } else { | ||
+ if (!areExprsSameMa | ||
+ return; | ||
+ } | ||
} | ||
- | ||
diag(BinOp->getO | ||
} | ||
@@ -199,10 +199,10 @@ void IncludeModernize | ||
// 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.ge | ||
- if (CStyledHeaderTo | ||
- IncludesToBeProc | ||
- IncludeMarker{CS | ||
- FilenameRange.ge | ||
+ if (auto It = CStyledHeaderToC | ||
+ It != CStyledHeaderToC | ||
+ IncludesToBeProc | ||
+ It->second, FileName, FilenameRange.ge | ||
} else if (DeleteHeaders.c | ||
IncludesToBeProc | ||
// NOLINTNEXTLINE(m |
@@ -119,13 +119,12 @@ void NonConstParamete | ||
T->getPointeeTyp | ||
return; | ||
- if (Parameters.find | ||
+ auto [It, Inserted] = Parameters.try_e | ||
+ if (!Inserted) | ||
return; | ||
- ParmInfo PI; | ||
- PI.IsReferenced = false; | ||
- PI.CanBeConst = true; | ||
- Parameters[Parm] | ||
+ It->second.IsRef | ||
+ It->second.CanBe | ||
} | ||
void NonConstParamete |
@@ -14,13 +14,14 @@ namespace clang::tidy::uti | ||
ExceptionSpecAna | ||
ExceptionSpecAna | ||
- // Check if the function has already been analyzed and reuse that result. | ||
- const auto CacheEntry = FunctionCache.fi | ||
- if (CacheEntry == FunctionCache.en | ||
+ // Check if function exist in cache or add temporary value to cache to protect | ||
+ // against endless recursion. | ||
+ const auto [CacheEntry, NotFound] = | ||
+ FunctionCache.tr | ||
+ if (NotFound) { | ||
ExceptionSpecAna | ||
- | ||
- // Cache the result of the analysis. | ||
- FunctionCache.tr | ||
+ // Update result with calculated value | ||
+ FunctionCache[Fu | ||
return State; | ||
} | ||
@@ -184,7 +184,7 @@ public: | ||
bool UseDirtyHeaders = false; | ||
// If true, parse emplace-like functions in the preamble. | ||
- bool PreambleParseFor | ||
+ bool PreambleParseFor | ||
/// 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 PreambleParseFor | ||
+ bool PreambleParseFor | ||
bool ImportInsertions | ||
@@ -18,10 +18,13 @@ | ||
namespace clang { | ||
namespace clangd { | ||
-Range MacroOccurrence: | ||
+CharSourceRange | ||
auto MainFile = SM.getMainFileID | ||
- return halfOpenToRange( | ||
- SM, syntax::FileRang | ||
+ return syntax::FileRang | ||
+} | ||
+ | ||
+Range MacroOccurrence: | ||
+ return halfOpenToRange( | ||
} | ||
void CollectMainFileM |
@@ -31,6 +31,7 @@ struct MacroOccurrence { | ||
// True if the occurence is used in a conditional directive, e.g. #ifdef MACRO | ||
bool InConditionalDir | ||
+ CharSourceRange toSourceRange(co | ||
Range toRange(const SourceManager &SM) const; | ||
}; | ||
@@ -40,7 +40,7 @@ public: | ||
// Options to run clang e.g. when parsing AST. | ||
struct ParseOptions { | ||
- bool PreambleParseFor | ||
+ bool PreambleParseFor | ||
bool ImportInsertions | ||
}; |
@@ -372,6 +372,15 @@ void enhanceLocatedSy | ||
}); | ||
} | ||
+bool objcMethodIsTouc | ||
+ SourceLocation Loc) { | ||
+ unsigned NumSels = OMD->getNumSelec | ||
+ for (unsigned I = 0; I < NumSels; ++I) | ||
+ if (SM.getSpellingL | ||
+ 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 @@ locateASTReferen | ||
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<O | ||
+ if (OMD->isThisDecl | ||
+ objcMethodIsTouc | ||
+ llvm::SmallVecto | ||
+ OMD->getOverridd | ||
+ if (!Overrides.empt | ||
+ for (const auto *Override : Overrides) | ||
+ AddResultDecl(Ov | ||
+ LocateASTReferen | ||
+ } | ||
+ AddResultDecl(OM | ||
+ 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<Loca | ||
} else if (const auto *RD = dyn_cast<CXXReco | ||
IDs.insert(getSy | ||
QueryKind = RelationKind::Ba | ||
+ } else if (const auto *OMD = dyn_cast<ObjCMet | ||
+ IDs.insert(getSy | ||
+ QueryKind = RelationKind::Ov | ||
+ } else if (const auto *ID = dyn_cast<ObjCInt | ||
+ IDs.insert(getSy | ||
+ QueryKind = RelationKind::Ba | ||
} | ||
} | ||
return findImplementors | ||
@@ -1302,6 +1337,21 @@ void getOverriddenMet | ||
} | ||
} | ||
+// Recursively finds all the overridden methods of `OMD` in complete type | ||
+// hierarchy. | ||
+void getOverriddenMet | ||
+ llvm::DenseSet<S | ||
+ if (!OMD) | ||
+ return; | ||
+ llvm::SmallVecto | ||
+ OMD->getOverridd | ||
+ for (const ObjCMethodDecl *Base : Overrides) { | ||
+ if (auto ID = getSymbolID(Base | ||
+ OverriddenMethod | ||
+ getOverriddenMet | ||
+ } | ||
+} | ||
+ | ||
std::optional<st | ||
stringifyContain | ||
// FIXME We might also want to display the signature here | ||
@@ -1438,6 +1488,12 @@ ReferencesResult | ||
getOverriddenMet | ||
} | ||
} | ||
+ // 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<O | ||
+ OverriddenBy.Sub | ||
+ getOverriddenMet | ||
+ } | ||
} | ||
} | ||
@@ -713,7 +713,8 @@ void SymbolCollector: | ||
// Add macro references. | ||
for (const auto &IDToRefs : MacroRefsToIndex | ||
for (const auto &MacroRef : IDToRefs.second) | ||
- const auto &Range = MacroRef.toRange | ||
+ const auto &SR = MacroRef.toSourc | ||
+ auto Range = halfOpenToRange( | ||
bool IsDefinition = MacroRef.IsDefin | ||
Ref R; | ||
R.Location.Start | ||
@@ -726,9 +727,7 @@ void SymbolCollector: | ||
if (IsDefinition) { | ||
Symbol S; | ||
S.ID = IDToRefs.first; | ||
- auto StartLoc = cantFail(sourceL | ||
- auto EndLoc = cantFail(sourceL | ||
- S.Name = toSourceCode(SM, | ||
+ S.Name = toSourceCode(SM, | ||
S.SymInfo.Kind = index::SymbolKin | ||
S.SymInfo.SubKin | ||
S.SymInfo.Proper |
@@ -329,7 +329,7 @@ TEST(ClangdAST, GetContainedAuto | ||
auto &&d, | ||
auto *&e, | ||
auto (*f)(int) | ||
- ){}; | ||
+ ){ return 0; }; | ||
int withoutAuto( | ||
int a, | ||
@@ -338,7 +338,7 @@ TEST(ClangdAST, GetContainedAuto | ||
int &&d, | ||
int *&e, | ||
int (*f)(int) | ||
- ){}; | ||
+ ){ return 0; }; | ||
)cpp"); | ||
TU.ExtraArgs.pus | ||
auto AST = TU.build(); |
@@ -113,7 +113,7 @@ TEST(WorkspaceSy | ||
TEST(WorkspaceSy | ||
TestTU TU; | ||
TU.Code = R"cpp( | ||
- int test() {} | ||
+ int test() { return 0; } | ||
static void test2() {} | ||
)cpp"; | ||
EXPECT_THAT(getS | ||
@@ -537,12 +537,14 @@ TEST(DocumentSym | ||
TestTU TU; | ||
TU.AdditionalFil | ||
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(getS | ||
@@ -780,7 +782,7 @@ TEST(DocumentSym | ||
TestTU TU; | ||
Annotations Source(R"cpp( | ||
template <class T> | ||
- T foo() {} | ||
+ T foo() { return T{}; } | ||
auto x = foo<int>(); | ||
auto y = foo<double>(); |
@@ -251,7 +251,7 @@ TEST(ParsedASTTe | ||
// this check runs the preprocessor, we need to make sure it does not break | ||
// our recording logic. | ||
TU.ClangTidyProv | ||
- TU.Code = "inline int foo() {}"; | ||
+ TU.Code = "inline int foo() { return 0; }"; | ||
auto AST = TU.build(); | ||
const syntax::TokenBuf |
@@ -108,7 +108,7 @@ TEST(QualityTest | ||
using flags::FLAGS_FOO | ||
- int ::header_main() {} | ||
+ int ::header_main() { return 0; } | ||
int main(); | ||
[[deprecated]] |
@@ -214,7 +214,7 @@ TEST(RenameTest, | ||
template<typenam | ||
class Foo { | ||
public: | ||
- static T [[f^oo]]() {} | ||
+ static T [[f^oo]]() { return T(); } | ||
}; | ||
void bar() { | ||
@@ -225,7 +225,7 @@ TEST(RenameTest, | ||
template<typenam | ||
class Foo { | ||
public: | ||
- T [[f^oo]]() {} | ||
+ T [[f^oo]]() { return T(); } | ||
}; | ||
void bar() { | ||
@@ -827,7 +827,7 @@ TEST(RenameTest, | ||
// 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, | ||
"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(CrossFileRe | ||
void [[foo]]() override {}; | ||
}; | ||
- void func(Base* b, Derived1* d1, | ||
+ void func(Base* b, Derived1* d1, | ||
Derived2* d2, NotDerived* nd) { | ||
b->[[foo]](); | ||
d1->[[foo]](); |
@@ -741,6 +741,7 @@ sizeof...($Templ | ||
$Class[[Foo]].$F | ||
self.$Field[[som | ||
self->$Field[[_s | ||
+ return 0; | ||
} | ||
@end | ||
)cpp", |
@@ -201,6 +201,7 @@ TEST(FoldingRang | ||
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(FoldingRang | ||
]]}; | ||
)cpp", | ||
R"cpp( | ||
- /*[[ Multi | ||
+ /*[[ Multi | ||
* line | ||
- * comment | ||
+ * comment | ||
]]*/ | ||
)cpp", | ||
R"cpp( | ||
//[[ Comment | ||
// 1]] | ||
- | ||
+ | ||
//[[ Comment | ||
// 2]] | ||
- | ||
+ | ||
// No folding for single line comment. | ||
/*[[ comment 3 |
@@ -1335,6 +1335,42 @@ TEST_F(SymbolCol | ||
OverriddenBy(CBa | ||
} | ||
+TEST_F(SymbolCo | ||
+ 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"; | ||
+ runSymbolCollect | ||
+ {"-xobjective-c+ | ||
+ const Symbol &AFoo = findSymbol(Symbo | ||
+ const Symbol &BFoo = findSymbol(Symbo | ||
+ const Symbol &DFoo = findSymbol(Symbo | ||
+ | ||
+ const Symbol &BBar = findSymbol(Symbo | ||
+ const Symbol &CBar = findSymbol(Symbo | ||
+ const Symbol &DBar = findSymbol(Symbo | ||
+ | ||
+ std::vector<Rela | ||
+ for (const Relation &R : Relations) | ||
+ if (R.Predicate == RelationKind::Ov | ||
+ Result.push_back | ||
+ EXPECT_THAT(Resu | ||
+ OverriddenBy(AFo | ||
+ OverriddenBy(BFo | ||
+} | ||
+ | ||
TEST_F(SymbolCol | ||
const std::string Header = R"( | ||
class W; |
@@ -36,6 +36,7 @@ TEST(SymbolInfoT | ||
void $decl[[foo]](); | ||
int bar() { | ||
fo^o(); | ||
+ return 0; | ||
} | ||
)cpp", | ||
{ExpectedSymbolD | ||
@@ -44,6 +45,7 @@ TEST(SymbolInfoT | ||
void $def[[foo]]() {} | ||
int bar() { | ||
fo^o(); | ||
+ return 0; | ||
} | ||
)cpp", | ||
{ExpectedSymbolD | ||
@@ -53,6 +55,7 @@ TEST(SymbolInfoT | ||
void $def[[foo]]() {} | ||
int bar() { | ||
fo^o(); | ||
+ return 0; | ||
} | ||
)cpp", | ||
{ExpectedSymbolD | ||
@@ -83,6 +86,7 @@ TEST(SymbolInfoT | ||
void $decl[[foo]](); | ||
int baz() { | ||
fo^o(); | ||
+ return 0; | ||
} | ||
} | ||
)cpp", | ||
@@ -96,6 +100,7 @@ TEST(SymbolInfoT | ||
namespace barbar { | ||
int baz() { | ||
bar::fo^o(); | ||
+ return 0; | ||
} | ||
} | ||
)cpp", | ||
@@ -108,6 +113,7 @@ TEST(SymbolInfoT | ||
namespace Nbaz { | ||
int baz() { | ||
::fo^o(); | ||
+ return 0; | ||
} | ||
} | ||
} | ||
@@ -121,6 +127,7 @@ TEST(SymbolInfoT | ||
namespace barbar { | ||
int baz() { | ||
fo^o(); | ||
+ return 0; | ||
} | ||
} | ||
)cpp", | ||
@@ -136,6 +143,7 @@ TEST(SymbolInfoT | ||
int baz() { | ||
bar::BarType b; | ||
fo^o(b); | ||
+ return 0; | ||
} | ||
} | ||
)cpp", |
@@ -95,7 +95,7 @@ TEST(HighlightsT | ||
)cpp", | ||
R"cpp(// Function | ||
- int [[^foo]](int) {} | ||
+ int [[^foo]](int) { return 0; } | ||
int main() { | ||
[[foo]]([[foo]]( | ||
auto *X = &[[foo]]; | ||
@@ -411,6 +411,85 @@ TEST(LocateSymbo | ||
sym("foo", Code.range("2"), | ||
} | ||
+TEST(LocateSymb | ||
+ auto Code = Annotations(R"ob | ||
+ @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 | ||
+ TU.ExtraArgs.pus | ||
+ auto AST = TU.build(); | ||
+ EXPECT_THAT( | ||
+ locateSymbolAt(A | ||
+ UnorderedElement | ||
+ sym("foo", Code.range("2"), | ||
+} | ||
+ | ||
+TEST(LocateSymb | ||
+ auto Code = Annotations(R"ob | ||
+ @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 | ||
+ TU.ExtraArgs.pus | ||
+ auto AST = TU.build(); | ||
+ EXPECT_THAT( | ||
+ locateSymbolAt(A | ||
+ UnorderedElement | ||
+} | ||
+ | ||
+TEST(LocateSymb | ||
+ auto Code = Annotations(R"ob | ||
+ @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 | ||
+ TU.ExtraArgs.pus | ||
+ auto AST = TU.build(); | ||
+ EXPECT_THAT( | ||
+ locateSymbolAt(A | ||
+ UnorderedElement | ||
+} | ||
+ | ||
TEST(LocateSymbo | ||
Annotations SymbolHeader(R"c | ||
class $p[[Proto]] {}; | ||
@@ -1834,6 +1913,41 @@ TEST(FindImpleme | ||
} | ||
} | ||
+TEST(FindImplem | ||
+ llvm::StringRef Test = R"objc( | ||
+ @interface $base^Base | ||
+ - (void)fo$foo^o; | ||
+ @end | ||
+ @protocol Protocol | ||
+ - (void)$protocol^ | ||
+ @end | ||
+ @interface $ChildDecl[[Chil | ||
+ - (void)concrete; | ||
+ - (void)$fooDecl[[ | ||
+ @end | ||
+ @implementation $ChildDef[[Child | ||
+ - (void)concrete {} | ||
+ - (void)$fooDef[[f | ||
+ - (void)$protocolD | ||
+ @end | ||
+ )objc"; | ||
+ | ||
+ Annotations Code(Test); | ||
+ auto TU = TestTU::withCode | ||
+ TU.ExtraArgs.pus | ||
+ auto AST = TU.build(); | ||
+ auto Index = TU.index(); | ||
+ EXPECT_THAT(find | ||
+ UnorderedElement | ||
+ Code.range("Chil | ||
+ EXPECT_THAT(find | ||
+ UnorderedElement | ||
+ sym("foo", Code.range("fooD | ||
+ EXPECT_THAT(find | ||
+ UnorderedElement | ||
+ Code.range("prot | ||
+} | ||
+ | ||
TEST(FindImpleme | ||
llvm::StringRef Test = R"cpp( | ||
struct Base { | ||
@@ -1963,6 +2077,7 @@ void checkFindRefs(ll | ||
Annotations T(Test); | ||
auto TU = TestTU::withCode | ||
TU.ExtraArgs.pus | ||
+ TU.ExtraArgs.pus | ||
auto AST = TU.build(); | ||
std::vector<Matc | ||
@@ -2025,7 +2140,7 @@ TEST(FindReferen | ||
)cpp", | ||
R"cpp(// Function | ||
- int $def[[foo]](int) | ||
+ int $def[[foo]](int) | ||
int main() { | ||
auto *X = &$(main)[[^foo]] | ||
$(main)[[foo]](4 | ||
@@ -2045,7 +2160,7 @@ TEST(FindReferen | ||
R"cpp(// Method call | ||
struct Foo { int $decl(Foo)[[foo] | ||
- int Foo::$def(Foo)[[ | ||
+ int Foo::$def(Foo)[[ | ||
int main() { | ||
Foo f; | ||
f.$(main)[[^foo] | ||
@@ -2143,7 +2258,7 @@ TEST(FindReferen | ||
)cpp", | ||
R"cpp(// Dependent code | ||
template <typename T> void $decl[[foo]](T t); | ||
- template <typename T> void bar(T t) { $(bar)[[foo]](t) | ||
+ template <typename T> void bar(T t) { $(bar)[[foo]](t) | ||
void baz(int x) { $(baz)[[f^oo]](x | ||
)cpp", | ||
R"cpp( | ||
@@ -2260,6 +2375,25 @@ TEST(FindReferen | ||
checkFindRefs(Te | ||
} | ||
+TEST(FindRefere | ||
+ llvm::StringRef Test = | ||
+ R"objc( | ||
+ @interface Base | ||
+ - (void)$decl(Base | ||
+ @end | ||
+ @interface Derived : Base | ||
+ - (void)$overrided | ||
+ @end | ||
+ @implementation Derived | ||
+ - (void)$overrided | ||
+ @end | ||
+ void test(Derived *derived, Base *base) { | ||
+ [derived func]; // No references to the overrides. | ||
+ [base $(test)[[func]]] | ||
+ })objc"; | ||
+ checkFindRefs(Te | ||
+} | ||
+ | ||
TEST(FindReferen | ||
llvm::StringRef Test = | ||
R"cpp( | ||
@@ -2284,6 +2418,27 @@ TEST(FindReferen | ||
checkFindRefs(Te | ||
} | ||
+TEST(FindRefere | ||
+ llvm::StringRef Test = | ||
+ R"objc( | ||
+ @interface BaseBase | ||
+ - (void)$(BaseBase | ||
+ @end | ||
+ @interface Base : BaseBase | ||
+ - (void)$(Base)[[f | ||
+ @end | ||
+ @interface Derived : Base | ||
+ - (void)$decl(Deri | ||
+ @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(Te | ||
+} | ||
+ | ||
TEST(FindReferen | ||
llvm::StringRef Test = | ||
R"cpp( | ||
@@ -2353,6 +2508,7 @@ TEST(FindReferen | ||
X $def(test)[[a]]; | ||
$(test)[[a]].ope | ||
if ($(test)[[a^]]) {} // ignore implicit conversion-opera | ||
+ return 0; | ||
} | ||
)cpp", | ||
}; | ||
@@ -2388,7 +2544,7 @@ TEST(FindReferen | ||
#define BAR 5 | ||
int bar1(); | ||
int bar2(); | ||
- class Bar {}; | ||
+ class Bar {}; | ||
)cpp"); | ||
TU.AdditionalFil | ||
namespace std { | ||
@@ -2405,7 +2561,7 @@ TEST(FindReferen | ||
std::vector<Matc | ||
for (const auto &R : T.ranges()) | ||
ExpectedLocation | ||
- for (const auto &P : T.points()) | ||
+ for (const auto &P : T.points()) | ||
EXPECT_THAT(find | ||
UnorderedElement | ||
<< "Failed for Refs at " << P << "\n" | ||
@@ -2480,6 +2636,7 @@ TEST(FindReferen | ||
Annotations IndexedMain(R"cp | ||
int indexed_main() { | ||
int a = [[MACRO]](1); | ||
+ return 0; | ||
} | ||
)cpp"); | ||
@@ -935,10 +935,11 @@ TEST_F(DefineInl | ||
// Check we put inline before cv-qualifiers. | ||
ExtraFiles["a.h" | ||
apply(R"cpp(#inc | ||
- const int fo^o() {})cpp", | ||
+ const int fo^o() { return 0; })cpp", | ||
&EditedFiles); | ||
- EXPECT_THAT(Edit | ||
- testPath("a.h"), | ||
+ EXPECT_THAT(Edit | ||
+ testing::Element | ||
+ testPath("a.h"), | ||
// No double inline. | ||
ExtraFiles["a.h" |
@@ -69,8 +69,8 @@ TEST_F(ExpandDed | ||
EXPECT_THAT(appl | ||
StartsWith("fail | ||
- EXPECT_EQ(apply( | ||
- "ns::Class * foo() { ns::Class * c = foo(); }"); | ||
+ EXPECT_EQ(apply( | ||
+ "ns::Class * foo() { ns::Class * c = foo(); return nullptr; }"); | ||
EXPECT_EQ( | ||
apply("void ns::Func() { au^to x = new ns::Class::Neste | ||
"void ns::Func() { ns::Class::Neste |
@@ -116,6 +116,7 @@ TEST_F(ExtractVa | ||
struct T { | ||
int bar(int a = [[1]]) { | ||
int b = [[z]]; | ||
+ return 0; | ||
} | ||
int z = [[1]]; | ||
} t; |
@@ -97,10 +97,24 @@ New check aliases | ||
Changes in existing checks | ||
^^^^^^^^^^^^^^^^ | ||
+- Improved :doc:`bugprone-s | ||
+ <clang-tidy/chec | ||
+ calls of ``std::string`` constructor with char pointer, start position and | ||
+ length parameters. | ||
+ | ||
- Improved :doc:`bugprone-u | ||
<clang-tidy/chec | ||
additional C++ member functions to match. | ||
+- Improved :doc:`misc-const | ||
+ <clang-tidy/chec | ||
+ `AllowedTypes`, that excludes specified types from const-correctnes | ||
+ checking. | ||
+ | ||
+- Improved :doc:`misc-redun | ||
+ <clang-tidy/chec | ||
+ examples and fixing some macro related false positives. | ||
+ | ||
Removed checks | ||
^^^^^^^^^^^^^^ | ||
@@ -37,20 +37,20 @@ Options | ||
.. option:: UseCXXStaticCast | ||
When suggesting fix-its for C++ code, should C++-style ``static_cast<>( | ||
- be suggested, or C-style casts. Defaults to ``true``. | ||
+ be suggested, or C-style casts. Defaults to `true`. | ||
.. option:: UseCXXHeadersInC | ||
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:: IgnoreConstantIn | ||
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: | ||
@@ -21,6 +21,7 @@ Examples: | ||
.. code-block:: c++ | ||
std::string("tes | ||
+ std::string("tes | ||
std::string_view | ||
Creating an empty string from constructors with parameters is considered | ||
@@ -31,8 +32,19 @@ Examples: | ||
.. code-block:: c++ | ||
std::string("tes | ||
+ std::string("tes | ||
std::string_view | ||
+Passing an invalid first character position parameter to constructor will | ||
+cause ``std::out_of_ra | ||
+ | ||
+Examples: | ||
+ | ||
+.. code-block:: c++ | ||
+ | ||
+ std::string("tes | ||
+ std::string("tes | ||
+ | ||
Options | ||
------- | ||
@@ -190,7 +190,7 @@ This check is an alias of check :doc:`bugprone-u | ||
with a fixed set of functions. | ||
Suppressing issues by casting to ``void`` is enabled by default and can be | ||
-disabled by setting `AllowCastToVoid | ||
+disabled by setting `AllowCastToVoid | ||
The check corresponds to a part of CERT C Coding Standard rule `ERR33-C. | ||
Detect and handle standard library errors |
@@ -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:: AnalyzeReference | ||
+.. option:: AnalyzeReference | ||
- 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:: WarnPointersAsVa | ||
+.. option:: WarnPointersAsVa | ||
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 | ||
-.. 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:: TransformReferen | ||
+.. option:: TransformReferen | ||
- 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:: TransformPointer | ||
+.. option:: TransformPointer | ||
- Provides fixit-hints for pointers if their pointee is not changed. This does not analyze if the | ||
- value-pointed-to | ||
+ Provides fixit-hints for pointers if their pointee is not changed. This does | ||
+ not analyze if the value-pointed-to | ||
Requires 'WarnPointersAsV | ||
@@ -196,3 +199,13 @@ Options | ||
// The following pointer may not become a 'int *const'. | ||
int *changing_pointe | ||
changing_pointee | ||
+ | ||
+.. option:: AllowedTypes | ||
+ | ||
+ A semicolon-separa | ||
+ const-correctnes | ||
+ ``[Rr]ef(erence) | ||
+ ``Reference`` and ``reference``. If a name in the list contains the sequence | ||
+ `::`, it is matched against the qualified type name | ||
+ (i.e. ``namespace::Typ | ||
+ name (i.e. ``Type``). Default is empty string. |
@@ -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: | ||
@@ -144,7 +144,7 @@ lives. | ||
When set to true convert loops when in C++20 or later mode using | ||
``std::ranges::r | ||
- Default value is ``true``. | ||
+ Default value is `true`. | ||
.. option:: MakeReverseRange | ||
@@ -8,4 +8,5 @@ double Circle::area() const { | ||
double Circle::perimete | ||
return 3.141 * radius_; | ||
-} |
@@ -1,6 +1,6 @@ | ||
// RUN: rm -rf %t && mkdir -p %t/docs %t/build | ||
// RUN: sed 's|$test_dir|%/S | ||
-// RUN: clang-doc --format=html --output=%t/docs | ||
+// RUN: clang-doc --format=html --output=%t/docs | ||
// RUN: FileCheck %s -input-file=%t/d | ||
// RUN: FileCheck %s -input-file=%t/d | ||
// RUN: FileCheck %s -input-file=%t/d | ||
@@ -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">F | ||
-// HTML-SHAPE: <h3 id="{{([0-9A-F]{ | ||
-// 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]{ | ||
-// 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]{ | ||
-// 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: | ||
+// HTML-SHAPE-NEXT: | ||
+// HTML-SHAPE-NEXT: | ||
+// HTML-SHAPE-NEXT: | ||
+// HTML-SHAPE-NEXT: | ||
+// HTML-SHAPE-NEXT: | ||
+// 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">F | ||
+// HTML-SHAPE: <h3 id="{{([0-9A-F]{ | ||
+// 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]{ | ||
+// 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]{ | ||
+// HTML-SHAPE: <p>public void ~Shape()</p> | ||
+// HTML-SHAPE: Defined at line | ||
+// HTML-SHAPE-NEXT: | ||
+// HTML-SHAPE-NEXT: | ||
+// HTML-SHAPE-NEXT: | ||
+// 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 | ||
-// 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">F | ||
-// HTML-CALC: <h3 id="{{([0-9A-F]{ | ||
-// HTML-CALC: <p>public int add(int a, int b)</p> | ||
-// HTML-CALC: <p>Defined at line 3 of file {{.*}}Calculator | ||
-// 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]{ | ||
-// HTML-CALC: <p>public int subtract(int a, int b)</p> | ||
-// HTML-CALC: <p>Defined at line 7 of file {{.*}}Calculator | ||
-// 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]{ | ||
-// HTML-CALC: <p>public int multiply(int a, int b)</p> | ||
-// HTML-CALC: <p>Defined at line 11 of file {{.*}}Calculator | ||
-// 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]{ | ||
-// HTML-CALC: <p>public double divide(int a, int b)</p> | ||
-// HTML-CALC: <p>Defined at line 15 of file {{.*}}Calculator | ||
-// 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://re | ||
+// HTML-CALC-NEXT: of file | ||
+// HTML-CALC-NEXT: <a href="https://re | ||
+// 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">F | ||
+// HTML-CALC: <h3 id="{{([0-9A-F]{ | ||
+// HTML-CALC: <p>public int add(int a, int b)</p> | ||
+// HTML-CALC: Defined at line | ||
+// HTML-CALC-NEXT: <a href="https://re | ||
+// HTML-CALC-NEXT: of file | ||
+// HTML-CALC-NEXT: <a href="https://re | ||
+// 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]{ | ||
+// HTML-CALC: <p>public int subtract(int a, int b)</p> | ||
+// HTML-CALC: Defined at line | ||
+// HTML-CALC-NEXT: <a href="https://re | ||
+// HTML-CALC-NEXT: of file | ||
+// HTML-CALC-NEXT: <a href="https://re | ||
+// 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]{ | ||
+// HTML-CALC: <p>public int multiply(int a, int b)</p> | ||
+// HTML-CALC: Defined at line | ||
+// HTML-CALC-NEXT: <a href="https://re | ||
+// HTML-CALC-NEXT: of file | ||
+// HTML-CALC-NEXT: <a href="https://re | ||
+// 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]{ | ||
+// HTML-CALC: <p>public double divide(int a, int b)</p> | ||
+// HTML-CALC: Defined at line | ||
+// HTML-CALC-NEXT: <a href="https://re | ||
+// HTML-CALC-NEXT: of file | ||
+// HTML-CALC-NEXT: <a href="https://re | ||
+// 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. | ||
-// 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 | ||
-// HTML-RECTANGLE: </p> | ||
-// HTML-RECTANGLE: <h2 id="Members">Mem | ||
-// 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">F | ||
-// HTML-RECTANGLE: <h3 id="{{([0-9A-F]{ | ||
-// HTML-RECTANGLE: <p>public void Rectangle(double | ||
-// HTML-RECTANGLE: <p>Defined at line 3 of file {{.*}}Rectangle. | ||
-// HTML-RECTANGLE: <div>brief</div> | ||
-// HTML-RECTANGLE: <p> Constructs a new Rectangle object.</p> | ||
-// HTML-RECTANGLE: <h3 id="{{([0-9A-F]{ | ||
-// HTML-RECTANGLE: <p>public double area()</p> | ||
-// HTML-RECTANGLE: <p>Defined at line 6 of file {{.*}}Rectangle. | ||
-// 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]{ | ||
-// HTML-RECTANGLE: <p>public double perimeter()</p> | ||
-// HTML-RECTANGLE: <p>Defined at line 10 of file {{.*}}Rectangle. | ||
-// 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-N | ||
+// HTML-RECTANGLE-N | ||
+// HTML-RECTANGLE-N | ||
+// HTML-RECTANGLE-N | ||
+// HTML-RECTANGLE-N | ||
+// HTML-RECTANGLE-N | ||
+// 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 | ||
+// HTML-RECTANGLE: </p> | ||
+// HTML-RECTANGLE: <h2 id="Members">Mem | ||
+// 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">F | ||
+// HTML-RECTANGLE: <h3 id="{{([0-9A-F]{ | ||
+// HTML-RECTANGLE: <p>public void Rectangle(double | ||
+// HTML-RECTANGLE: Defined at line | ||
+// HTML-RECTANGLE-N | ||
+// HTML-RECTANGLE-N | ||
+// HTML-RECTANGLE-N | ||
+// HTML-RECTANGLE: <div>brief</div> | ||
+// HTML-RECTANGLE: <p> Constructs a new Rectangle object.</p> | ||
+// HTML-RECTANGLE: <h3 id="{{([0-9A-F]{ | ||
+// HTML-RECTANGLE: <p>public double area()</p> | ||
+// HTML-RECTANGLE: Defined at line | ||
+// HTML-RECTANGLE-N | ||
+// HTML-RECTANGLE-N | ||
+// HTML-RECTANGLE-N | ||
+// 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]{ | ||
+// HTML-RECTANGLE: <p>public double perimeter()</p> | ||
+// HTML-RECTANGLE: Defined at line | ||
+// HTML-RECTANGLE-N | ||
+// HTML-RECTANGLE-N | ||
+// HTML-RECTANGLE-N | ||
+// 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</ | ||
-// 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 | ||
-// HTML-CIRCLE: </p> | ||
-// HTML-CIRCLE: <h2 id="Members">Mem | ||
-// HTML-CIRCLE: <p> Radius of the circle.</p> | ||
-// HTML-CIRCLE: <div>private double radius_</div> | ||
-// HTML-CIRCLE: <h2 id="Functions">F | ||
-// HTML-CIRCLE: <h3 id="{{([0-9A-F]{ | ||
-// HTML-CIRCLE: <p>public void Circle(double radius)</p> | ||
-// HTML-CIRCLE: <p>Defined at line 3 of file {{.*}}Circle.cpp | ||
-// HTML-CIRCLE: <div>brief</div> | ||
-// HTML-CIRCLE: <p> Constructs a new Circle object.</p> | ||
-// HTML-CIRCLE: <h3 id="{{([0-9A-F]{ | ||
-// HTML-CIRCLE: <p>public double area()</p> | ||
-// HTML-CIRCLE: <p>Defined at line 5 of file {{.*}}Circle.cpp | ||
-// 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]{ | ||
-// HTML-CIRCLE: <p>public double perimeter()</p> | ||
-// HTML-CIRCLE: <p>Defined at line 9 of file {{.*}}Circle.cpp | ||
-// 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 | ||
+// HTML-CIRCLE-NEXT | ||
+// HTML-CIRCLE-NEXT | ||
+// HTML-CIRCLE-NEXT | ||
+// HTML-CIRCLE-NEXT | ||
+// HTML-CIRCLE-NEXT | ||
+// 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 | ||
+// HTML-CIRCLE: </p> | ||
+// HTML-CIRCLE: <h2 id="Members">Mem | ||
+// HTML-CIRCLE: <p> Radius of the circle.</p> | ||
+// HTML-CIRCLE: <div>private double radius_</div> | ||
+// HTML-CIRCLE: <h2 id="Functions">F | ||
+// HTML-CIRCLE: <h3 id="{{([0-9A-F]{ | ||
+// HTML-CIRCLE: <p>public void Circle(double radius)</p> | ||
+// HTML-CIRCLE: Defined at line | ||
+// HTML-CIRCLE-NEXT | ||
+// HTML-CIRCLE-NEXT | ||
+// HTML-CIRCLE-NEXT | ||
+// HTML-CIRCLE: <div>brief</div> | ||
+// HTML-CIRCLE: <p> Constructs a new Circle object.</p> | ||
+// HTML-CIRCLE: <h3 id="{{([0-9A-F]{ | ||
+// HTML-CIRCLE: <p>public double area()</p> | ||
+// HTML-CIRCLE: Defined at line | ||
+// HTML-CIRCLE-NEXT | ||
+// HTML-CIRCLE-NEXT | ||
+// HTML-CIRCLE-NEXT | ||
+// 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]{ | ||
+// HTML-CIRCLE: <p>public double perimeter()</p> | ||
+// HTML-CIRCLE: Defined at line | ||
+// HTML-CIRCLE-NEXT | ||
+// HTML-CIRCLE-NEXT | ||
+// HTML-CIRCLE-NEXT | ||
+// 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 | ||
@@ -286,4 +339,4 @@ | ||
// MD-ALL-FILES: ## [GlobalNamespace | ||
// MD-INDEX: # C/C++ Reference | ||
-// MD-INDEX: * Namespace: [GlobalNamespace |
@@ -10,7 +10,7 @@ std::string StringsFunction( | ||
class SomeContainer {}; | ||
namespace strings_internal | ||
void InternalFunction | ||
-template <class P> P InternalTemplate | ||
+template <class P> void InternalTemplate | ||
} // namespace strings_internal | ||
namespace container_intern |
@@ -18,7 +18,7 @@ T lexical_cast(con | ||
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::lexic | ||
} | ||
-std::string fun(const std::wstring &) {} | ||
+std::string fun(const std::wstring &); | ||
void test_to_wstring( | ||
int a; |
@@ -1,4 +1,4 @@ | ||
-// RUN: %check_clang_tid | ||
+// RUN: %check_clang_tid | ||
void badly_chained_1( | ||
{ |
@@ -1,4 +1,4 @@ | ||
-// RUN: %check_clang_tid | ||
+// RUN: %check_clang_tid | ||
void badly_chained_1( | ||
{ |
@@ -1,5 +1,5 @@ | ||
// RUN: %check_clang_tid | ||
-// RUN: -- -fexceptions | ||
+// RUN: -- -fexceptions -Wno-error=retur | ||
namespace std { | ||
@@ -20,6 +20,7 @@ int throwsAndCallsRe | ||
} catch(...) { | ||
rethrower(); | ||
} | ||
+ return 1; | ||
} | ||
int throwsAndCallsCa | ||
@@ -29,6 +30,7 @@ int throwsAndCallsCa | ||
} catch(...) { | ||
callsRethrower() | ||
} | ||
+ return 1; | ||
} | ||
void rethrowerNoexcep |
@@ -665,6 +665,7 @@ int indirectly_recur | ||
int recursion_helper | ||
indirectly_recur | ||
+ return 0; | ||
} | ||
int indirectly_recur |
@@ -8,24 +8,25 @@ T accumulate(Input | ||
// 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, | ||
T reduce(Execution | ||
- InputIt first, InputIt last, T init) { (void)*first; } | ||
+ InputIt first, InputIt last, T init) { (void)*first; return init; } | ||
struct parallel_executi | ||
constexpr parallel_executi | ||
template <class InputIt1, class InputIt2, class T> | ||
T inner_product(In | ||
- InputIt2 first2, T value) { (void)*first1; (void)*first2; } | ||
+ InputIt2 first2, T value) { (void)*first1; (void)*first2; return value; } | ||
template <class ExecutionPolicy, | ||
T inner_product(Ex | ||
- InputIt2 first2, T value) { (void)*first1; (void)*first2; } | ||
+ InputIt2 first2, T value) { (void)*first1; (void)*first2; return value; } | ||
} // namespace std | ||
@@ -5,5 +5,6 @@ _BitInt(8) v_401_0() { | ||
_BitInt(5) y = 0; | ||
16777215wb ?: ++y; | ||
}); | ||
+ return 0; | ||
} | ||
-// CHECK-MESSAGES: warning | ||
+// CHECK-MESSAGES: warning |
@@ -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(cn | ||
- const struct timespec *time_point){}; | ||
+ const struct timespec *time_point){ return 0; }; | ||
struct Node1 list_c; | ||
static mtx_t lock; |
@@ -90,18 +90,18 @@ public: | ||
void wait(unique_lock | ||
template <class Clock, class Duration> | ||
cv_status wait_until(uniqu | ||
- const chrono::time_poi | ||
+ const chrono::time_poi | ||
template <class Clock, class Duration, class Predicate> | ||
bool wait_until(uniqu | ||
const chrono::time_poi | ||
- Predicate pred){}; | ||
+ Predicate pred){ return false; }; | ||
template <class Rep, class Period> | ||
cv_status wait_for(unique_ | ||
- const chrono::duration | ||
+ const chrono::duration | ||
template <class Rep, class Period, class Predicate> | ||
bool wait_for(unique_ | ||
const chrono::duration | ||
- Predicate pred){}; | ||
+ Predicate pred){ return false; }; | ||
}; | ||
} // namespace std |
@@ -11,6 +11,7 @@ struct basic_string { | ||
basic_string(con | ||
basic_string(con | ||
basic_string(uns | ||
+ basic_string(con | ||
}; | ||
typedef basic_string<cha | ||
typedef basic_string<wch | ||
@@ -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 TestUnsignedArgu | ||
+ std::string s0("test", 0u); | ||
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: constructor creating an empty string | ||
+ std::string s1(0x1000000ull, | ||
+ // 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 | ||
std::string_view |
@@ -27,7 +27,7 @@ public: | ||
constexpr basic_string_vie | ||
- constexpr basic_string_vie | ||
+ constexpr basic_string_vie | ||
}; | ||
template <typename CharT> |
@@ -89,6 +89,8 @@ int test_warning_pat | ||
if (strcmp(A, "a") < 0.) | ||
return 0; | ||
// CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' has suspicious implicit cast | ||
+ | ||
+ return 1; | ||
} | ||
int test_valid_patte |
@@ -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 | ||
// 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); | ||
} |
@@ -144,7 +144,7 @@ struct WithTemplBase : T { | ||
WithTemplBase(); | ||
}; | ||
-int test_no_crash() { | ||
+void test_no_crash() { | ||
auto foo = []() {}; | ||
WithTemplBase<de | ||
} |
@@ -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; | ||
} |
@@ -221,9 +221,9 @@ public: | ||
// CHECK-FIXES: const char *test_suite_name | ||
}; | ||
-const char *FooTestInfo::te | ||
+const char *FooTestInfo::te | ||
// CHECK-MESSAGES: [[@LINE-1]]:26: warning: Google Test APIs named with 'case' | ||
-// CHECK-FIXES: const char *FooTestInfo::te | ||
+// CHECK-FIXES: const char *FooTestInfo::te | ||
class BarTestInfo : public testing::TestInf | ||
public: | ||
@@ -491,26 +491,26 @@ public: | ||
// CHECK-FIXES: const testing::TestSui | ||
}; | ||
-testing::TestCa | ||
+testing::TestCa | ||
// 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::TestSui | ||
-int FooUnitTest::suc | ||
+// CHECK-FIXES: testing::TestSui | ||
+int FooUnitTest::suc | ||
// CHECK-MESSAGES: [[@LINE-1]]:18: warning: Google Test APIs named with 'case' | ||
-// CHECK-FIXES: int FooUnitTest::suc | ||
-int FooUnitTest::fai | ||
+// CHECK-FIXES: int FooUnitTest::suc | ||
+int FooUnitTest::fai | ||
// CHECK-MESSAGES: [[@LINE-1]]:18: warning: Google Test APIs named with 'case' | ||
-// CHECK-FIXES: int FooUnitTest::fai | ||
-int FooUnitTest::tot | ||
+// CHECK-FIXES: int FooUnitTest::fai | ||
+int FooUnitTest::tot | ||
// CHECK-MESSAGES: [[@LINE-1]]:18: warning: Google Test APIs named with 'case' | ||
-// CHECK-FIXES: int FooUnitTest::tot | ||
-int FooUnitTest::tes | ||
+// CHECK-FIXES: int FooUnitTest::tot | ||
+int FooUnitTest::tes | ||
// CHECK-MESSAGES: [[@LINE-1]]:18: warning: Google Test APIs named with 'case' | ||
-// CHECK-FIXES: int FooUnitTest::tes | ||
-const testing::TestCas | ||
+// CHECK-FIXES: int FooUnitTest::tes | ||
+const testing::TestCas | ||
// 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::TestSui | ||
+// CHECK-FIXES: const testing::TestSui | ||
// Type derived from testing::TestCas | ||
class BarUnitTest : public testing::UnitTes |
@@ -0,0 +1,180 @@ | ||
+// RUN: %check_clang_tid | ||
+// RUN: -config="{CheckO | ||
+// RUN: misc-const-corre | ||
+// RUN: misc-const-corre | ||
+// RUN: misc-const-corre | ||
+// RUN: misc-const-corre | ||
+// RUN: }" -- -fno-delayed-tem | ||
+ | ||
+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 negativeSmartPoi | ||
+ SmartPointer p1 = {}; | ||
+ SmartPointer* p2 = {}; | ||
+ SmartPointer& p3 = p1; | ||
+} | ||
+ | ||
+void negative_smart_p | ||
+ smart_pointer p1 = {}; | ||
+ smart_pointer* p2 = {}; | ||
+ smart_pointer& p3 = p1; | ||
+} | ||
+ | ||
+void negativeSmartPtr | ||
+ SmartPtr p1 = {}; | ||
+ SmartPtr* p2 = {}; | ||
+ SmartPtr& p3 = p1; | ||
+} | ||
+ | ||
+void negative_smart_p | ||
+ smart_ptr p1 = {}; | ||
+ smart_ptr* p2 = {}; | ||
+ smart_ptr& p3 = p1; | ||
+} | ||
+ | ||
+void negativeSmartRef | ||
+ SmartReference p1 = {}; | ||
+ SmartReference* p2 = {}; | ||
+ SmartReference& p3 = p1; | ||
+} | ||
+ | ||
+void negative_smart_r | ||
+ smart_reference p1 = {}; | ||
+ smart_reference* | ||
+ smart_reference& | ||
+} | ||
+ | ||
+void negativeSmartRef | ||
+ SmartRef p1 = {}; | ||
+ SmartRef* p2 = {}; | ||
+ SmartRef& p3 = p1; | ||
+} | ||
+ | ||
+void negative_smart_r | ||
+ smart_ref p1 = {}; | ||
+ smart_ref* p2 = {}; | ||
+ smart_ref& p3 = p1; | ||
+} | ||
+ | ||
+void positiveOtherTyp | ||
+ OtherType t = {}; | ||
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 't' of type 'OtherType' can be declared 'const' | ||
+ // CHECK-FIXES: OtherType const t = {}; | ||
+} | ||
+ | ||
+void negativeSomeComp | ||
+ ConstTemplate<in | ||
+ ConstTemplate<in | ||
+ ConstTemplate<in | ||
+} | ||
+ | ||
+void negativeQualifie | ||
+ qualified::Type t1 = {}; | ||
+ qualified::Type* | ||
+ qualified::Type& | ||
+ | ||
+ using qualified::Type; | ||
+ Type t4 = {}; | ||
+ Type* t5 = {}; | ||
+ Type& t6 = t4; | ||
+} | ||
+ | ||
+void negativeFullyQua | ||
+ fully::Qualified | ||
+ fully::Qualified | ||
+ fully::Qualified | ||
+ | ||
+ using fully::Qualified | ||
+ QualifiedType t4 = {}; | ||
+ QualifiedType* t5 = {}; | ||
+ QualifiedType& t6 = t4; | ||
+} | ||
+ | ||
+using MySP = SmartPointer; | ||
+using MyTemplate = ConstTemplate<in | ||
+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<i | ||
+ // CHECK-FIXES: MyTemplate const t1 = {}; | ||
+ | ||
+ MyTemplate* t2 = {}; | ||
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 't2' of type 'MyTemplate *' (aka 'ConstTemplate<i | ||
+ // CHECK-FIXES: MyTemplate* const t2 = {}; | ||
+ | ||
+ MyTemplate& t3 = t1; | ||
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 't3' of type 'MyTemplate &' (aka 'ConstTemplate<i | ||
+ // CHECK-FIXES: MyTemplate const& t3 = t1; | ||
+ | ||
+ MyTemplate2<int> | ||
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 't4' of type 'MyTemplate2<int | ||
+ // CHECK-FIXES: MyTemplate2<int> | ||
+ | ||
+ MyTemplate2<int> | ||
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 't5' of type 'MyTemplate2<int | ||
+ // CHECK-FIXES: MyTemplate2<int> | ||
+ | ||
+ MyTemplate2<int> | ||
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 't6' of type 'MyTemplate2<int | ||
+ // CHECK-FIXES: MyTemplate2<int> | ||
+} | ||
+ | ||
+template <typename T> | ||
+class Vector {}; | ||
+ | ||
+void positiveSmartPtr | ||
+ Vector<SmartPtr> | ||
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'vec' of type 'Vector<SmartPtr | ||
+ // CHECK-FIXES: Vector<SmartPtr> | ||
+} |
@@ -54,8 +54,8 @@ void template_instant | ||
struct ConstNonConstCla | ||
ConstNonConstCla | ||
ConstNonConstCla | ||
- double nonConstMethod() | ||
- double constMethod() const {} | ||
+ double nonConstMethod() | ||
+ double constMethod() const { return 0; } | ||
double modifyingMethod( | ||
double NonConstMember; |
@@ -283,8 +283,8 @@ void template_instant | ||
struct ConstNonConstCla | ||
ConstNonConstCla | ||
ConstNonConstCla | ||
- double nonConstMethod() | ||
- double constMethod() const {} | ||
+ double nonConstMethod() | ||
+ double constMethod() const { return 0; } | ||
double modifyingMethod( | ||
double NonConstMember; |
@@ -1,4 +1,5 @@ | ||
// RUN: %check_clang_tid | ||
+// RUN: %check_clang_tid | ||
typedef __INT64_TYPE__ I64; | ||
@@ -91,6 +92,223 @@ int TestSimpleEquiva | ||
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 TestDefineEquiva | ||
+ | ||
+ 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: | ||
+ if (VAL_2 <= VAL_2) return 0; | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: | ||
+ if (VAL_3 > VAL_3) return 0; | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: | ||
+ if (VAL_4 < VAL_4) return 0; | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: | ||
+ if (VAL_6 == VAL_6) return 2; | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: | ||
+ if (VAL_5 != VAL_5) return 2; | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: | ||
+ | ||
+ // Test prefixes | ||
+ if (+VAL_6 == +VAL_6) return 2; | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: | ||
+ if (-VAL_6 == -VAL_6) return 2; | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: | ||
+ if ((+VAL_6) == (+VAL_6)) return 2; | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: | ||
+ if ((-VAL_6) == (-VAL_6)) return 2; | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: | ||
+ | ||
+ 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: | ||
+ if (042 > 042) return 0; | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: | ||
+ | ||
+ int_val = (VAL_6 == VAL_6)?int_val1: | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: | ||
+ int_val = (042 > 042)?int_val1: int_val2; | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: | ||
+ | ||
+ | ||
+ // 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: | ||
+ }; | ||
+ | ||
+ TestStruct tsVar4 = { | ||
+ .mA = 3, | ||
+ .mB = int_val, | ||
+ .mC[0 ... 5 - 5] = int_val + 1, | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: | ||
+ }; | ||
+ | ||
+ 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: | ||
+ { | ||
+ } | ||
+ | ||
+ for (localIndex = LOOP_DEFINE - 1; LOOP_DEFINE > LOOP_DEFINE; localIndex--) | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:50: | ||
+ { | ||
+ } | ||
+ | ||
+ 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: | ||
+ ret += (-getValue(1)) > (-getValue( 1)); | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: | ||
+ | ||
+ ret += +getValue(1) > +getValue( 1); | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: | ||
+ ret += -getValue(1) > -getValue( 1); | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: | ||
+ | ||
+ ret += getValue(1) > getValue( 1); | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: | ||
+ ret += getValue(1) > getValue( 1 ); | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: | ||
+ ret += getValue(1) > getValue( 1); | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: | ||
+ ret += getValue( 1) > getValue( 1); | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: | ||
+ ret += getValue( 1 ) > getValue( 1); | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: | ||
+ ret += getValue( VAL_5 ) > getValue(VAL_5); | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: | ||
+ ret += getValue( VAL_5 ) > getValue( VAL_5); | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: | ||
+ ret += getValue( VAL_5 ) > getValue( VAL_5 ) ; | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: | ||
+ ret += getValue(VAL_5) > getValue( VAL_5 ) ; | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: | ||
+ ret += getValue(VAL_1+V | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: | ||
+ ret += getValue(VAL_1)+ | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:42: | ||
+ ret += (getValue(VAL_1) | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:44: | ||
+ return ret; | ||
+} | ||
+ | ||
template <int DX> | ||
int TestSimpleEquiva | ||
if (DX > 0 && DX > 0) return 1; |
@@ -33,9 +33,9 @@ void f(void (*fn)()) {;} | ||
// CHECK-MESSAGES: :[[@LINE-1]]:15: | ||
// CHECK-FIXES: {{^}}void f(void (* /*fn*/)()) {;}{{$}} | ||
-int *k([[clang::life | ||
+int *k([[clang::life | ||
// CHECK-MESSAGES: :[[@LINE-1]]:38: | ||
-// CHECK-FIXES: {{^}}int *k({{\[\[clang:: | ||
+// CHECK-FIXES: {{^}}int *k({{\[\[clang:: | ||
#define ATTR_BEFORE(x) [[clang::lifetim | ||
int* m(ATTR_BEFORE(co |
@@ -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_ret | ||
+int* func_cpp_inc_ret | ||
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'func_cpp_inc_re | ||
-// CHECK-FIXES: static int* func_cpp_inc_ret | ||
+// CHECK-FIXES: static int* func_cpp_inc_ret | ||
-const int* func_cpp_inc_ret | ||
+const int* func_cpp_inc_ret | ||
// CHECK-MESSAGES: :[[@LINE-1]]:12: | ||
-// CHECK-FIXES: static const int* func_cpp_inc_ret | ||
+// CHECK-FIXES: static const int* func_cpp_inc_ret | ||
-int const* func_cpp_inc_ret | ||
+int const* func_cpp_inc_ret | ||
// CHECK-MESSAGES: :[[@LINE-1]]:12: | ||
-// CHECK-FIXES: static int const* func_cpp_inc_ret | ||
+// CHECK-FIXES: static int const* func_cpp_inc_ret | ||
-int * const func_cpp_inc_ret | ||
+int * const func_cpp_inc_ret | ||
// CHECK-MESSAGES: :[[@LINE-1]]:13: | ||
-// CHECK-FIXES: static int * const func_cpp_inc_ret | ||
+// CHECK-FIXES: static int * const func_cpp_inc_ret | ||
-volatile const int* func_cpp_inc_ret | ||
+volatile const int* func_cpp_inc_ret | ||
// CHECK-MESSAGES: :[[@LINE-1]]:21: | ||
-// CHECK-FIXES: static volatile const int* func_cpp_inc_ret | ||
+// CHECK-FIXES: static volatile const int* func_cpp_inc_ret | ||
[[nodiscard]] void func_nodiscard() | ||
// CHECK-MESSAGES: :[[@LINE-1]]:20: |
@@ -214,8 +214,8 @@ class map : public bidirectional_it | ||
public: | ||
map() {} | ||
- iterator<pair<ke | ||
- const_iterator<i | ||
+ iterator<pair<ke | ||
+ const_iterator<i | ||
}; | ||
template <typename key, typename value> |
@@ -46,7 +46,7 @@ struct D { | ||
operator bool() const { return true; } | ||
void MemberFunction(i | ||
- int MemberFunctionWi | ||
+ int MemberFunctionWi | ||
static D *create(); | ||
}; | ||
@@ -342,7 +342,7 @@ void testCapturedSube | ||
struct E { | ||
void MemberFunction(i | ||
- int MemberFunctionWi | ||
+ int MemberFunctionWi | ||
int operator()(int x, int y) const { return x + y; } | ||
void testMemberFuncti |
@@ -1,11 +1,11 @@ | ||
// RUN: %check_clang_tid | ||
-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 | ||
} |
@@ -1,9 +1,13 @@ | ||
// RUN: %check_clang_tid | ||
-int not_main(int argc, char *argv[]) { | ||
- // CHECK-MESSAGES: :[[@LINE-1]]:24: | ||
+namespace X { | ||
+// Not main | ||
+int main(int argc, char *argv[]) { | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: | ||
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[]) { |
@@ -1,19 +1,23 @@ | ||
// RUN: %check_clang_tid | ||
-int not_main(int argc, char *argv[], char *argw[]) { | ||
- // CHECK-MESSAGES: :[[@LINE-1]]:24: | ||
- // CHECK-MESSAGES: :[[@LINE-2]]:38: | ||
+namespace X { | ||
+// Not main. | ||
+int main(int argc, char *argv[], char *argw[]) { | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: | ||
+ // CHECK-MESSAGES: :[[@LINE-2]]:34: | ||
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: | ||
- // CHECK-MESSAGES: :[[@LINE-2]]:46: | ||
+ auto main = [](int argc, char *argv[], char *argw[]) { | ||
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: | ||
+ // CHECK-MESSAGES: :[[@LINE-2]]:42: | ||
int f6[] = {1, 2}; | ||
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not declare C-style arrays, use 'std::array' instead | ||
}; |
@@ -170,6 +170,8 @@ const int *constArray() { | ||
// CHECK-FIXES: for (const int & I : ConstArr) | ||
// CHECK-FIXES-NEXT | ||
// CHECK-FIXES-NEXT | ||
+ | ||
+ return nullptr; | ||
} | ||
struct HasArr { |
@@ -49,7 +49,7 @@ public: | ||
template <typename... Args> | ||
void emplace_back(Arg | ||
template <typename... Args> | ||
- iterator emplace(const_it | ||
+ iterator emplace(const_it | ||
~vector(); | ||
}; | ||
@@ -69,7 +69,7 @@ public: | ||
void push_back(T &&) {} | ||
template <typename... Args> | ||
- iterator emplace(const_it | ||
+ iterator emplace(const_it | ||
template <typename... Args> | ||
void emplace_back(Arg | ||
template <typename... Args> | ||
@@ -93,7 +93,7 @@ public: | ||
void push_front(T &&) {} | ||
template <typename... Args> | ||
- iterator emplace(const_it | ||
+ iterator emplace(const_it | ||
template <typename... Args> | ||
void emplace_back(Arg | ||
template <typename... Args> | ||
@@ -116,7 +116,7 @@ public: | ||
template <typename... Args> | ||
void emplace_front(Ar | ||
template <typename... Args> | ||
- iterator emplace_after(co | ||
+ iterator emplace_after(co | ||
}; | ||
template <typename T> | ||
@@ -131,7 +131,7 @@ public: | ||
template <typename... Args> | ||
void emplace(Args &&...args){}; | ||
template <typename... Args> | ||
- iterator emplace_hint(con | ||
+ iterator emplace_hint(con | ||
}; | ||
template <typename Key, typename T> | ||
@@ -146,7 +146,7 @@ public: | ||
template <typename... Args> | ||
void emplace(Args &&...args){}; | ||
template <typename... Args> | ||
- iterator emplace_hint(con | ||
+ iterator emplace_hint(con | ||
}; | ||
template <typename T> | ||
@@ -161,7 +161,7 @@ public: | ||
template <typename... Args> | ||
void emplace(Args &&...args){}; | ||
template <typename... Args> | ||
- iterator emplace_hint(con | ||
+ iterator emplace_hint(con | ||
}; | ||
template <typename Key, typename T> | ||
@@ -176,7 +176,7 @@ public: | ||
template <typename... Args> | ||
void emplace(Args &&...args){}; | ||
template <typename... Args> | ||
- iterator emplace_hint(con | ||
+ iterator emplace_hint(con | ||
}; | ||
template <typename T> | ||
@@ -191,7 +191,7 @@ public: | ||
template <typename... Args> | ||
void emplace(Args &&...args){}; | ||
template <typename... Args> | ||
- iterator emplace_hint(con | ||
+ iterator emplace_hint(con | ||
}; | ||
template <typename Key, typename T> | ||
@@ -206,7 +206,7 @@ public: | ||
template <typename... Args> | ||
void emplace(Args &&...args){}; | ||
template <typename... Args> | ||
- iterator emplace_hint(con | ||
+ iterator emplace_hint(con | ||
}; | ||
template <typename T> | ||
@@ -221,7 +221,7 @@ public: | ||
template <typename... Args> | ||
void emplace(Args &&...args){}; | ||
template <typename... Args> | ||
- iterator emplace_hint(con | ||
+ iterator emplace_hint(con | ||
}; | ||
template <typename Key, typename T> | ||
@@ -236,7 +236,7 @@ public: | ||
template <typename... Args> | ||
void emplace(Args &&...args){}; | ||
template <typename... Args> | ||
- iterator emplace_hint(con | ||
+ iterator emplace_hint(con | ||
}; | ||
template <typename T> |
@@ -1,6 +1,6 @@ | ||
// RUN: %check_clang_tid | ||
// RUN: -config="{CheckO | ||
-// RUN: -- -fno-delayed-tem | ||
+// RUN: -- -fno-delayed-tem | ||
// Out of line definition. | ||
struct OL { |
@@ -203,13 +203,13 @@ public: | ||
// CHECK-MESSAGES: :[[@LINE-2]]:16: | ||
// CHECK-FIXES: {{^}} void j() const override | ||
- virtual MustUseResultObj | ||
+ virtual MustUseResultObj | ||
// CHECK-MESSAGES: :[[@LINE-1]]:31: | ||
- // CHECK-FIXES: {{^}} MustUseResultObj | ||
+ // CHECK-FIXES: {{^}} MustUseResultObj | ||
- virtual bool l() MUST_USE_RESULT UNUSED {} | ||
+ virtual bool l() MUST_USE_RESULT UNUSED; | ||
// CHECK-MESSAGES: :[[@LINE-1]]:16: | ||
- // CHECK-FIXES: {{^}} bool l() override MUST_USE_RESULT UNUSED {} | ||
+ // CHECK-FIXES: {{^}} bool l() override MUST_USE_RESULT UNUSED; | ||
virtual void r() & | ||
{} |
@@ -100,7 +100,7 @@ std::string StrFormat_field_ | ||
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); |
@@ -106,9 +106,9 @@ extern "C" int d2(int arg); | ||
inline int d3(int arg) noexcept(true); | ||
// CHECK-MESSAGES: :[[@LINE-1]]:12: | ||
// 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: | ||
-// 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-t | ||
// 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-t | ||
// 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-t | ||
-// 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-t | ||
// CHECK-FIXES: {{^}} auto operator=(const B&) -> B&;{{$}} | ||
- | ||
+ | ||
double base1(int, bool b); | ||
// CHECK-MESSAGES: :[[@LINE-1]]:12: | ||
// 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: | ||
-// 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: | ||
@@ -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: | ||
-// 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: | ||
// 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: | ||
-// 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: | ||
@@ -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; }; |
@@ -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 |
@@ -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 |
@@ -6,15 +6,15 @@ class basic_string { | ||
public: | ||
basic_string() {} | ||
~basic_string() {} | ||
- basic_string<T> *operator+=(cons | ||
- friend basic_string<T> operator+(const basic_string<T> &, const basic_string<T> &) {} | ||
+ basic_string<T> *operator+=(cons | ||
+ friend basic_string<T> operator+(const basic_string<T> &, const basic_string<T> &); | ||
}; | ||
typedef basic_string<cha | ||
typedef basic_string<wch | ||
} | ||
void f(std::string) {} | ||
-std::string g(std::string) {} | ||
+std::string g(std::string); | ||
int main() { | ||
std::string mystr1, mystr2; |
@@ -1,4 +1,6 @@ | ||
// RUN: %check_clang_tid | ||
+// RUN: %check_clang_tid | ||
+// RUN: -- --fix-errors -- -fexceptions -DENABLE_ERROR | ||
namespace std | ||
{ | ||
@@ -397,3 +399,18 @@ namespace gh68101 | ||
Container(Contai | ||
}; | ||
} // namespace gh68101 | ||
+ | ||
+namespace gh111436 | ||
+{ | ||
+ | ||
+template <typename value_type> class set { | ||
+ set(set &&) = default; | ||
+ | ||
+#ifdef ENABLE_ERROR | ||
+ set(initializer_ | ||
+ // CHECK-MESSAGES-E | ||
+ // CHECK-MESSAGES-E | ||
+#endif | ||
+}; | ||
+ | ||
+} // namespace gh111436 |
@@ -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-unn | ||
- // 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-unn | ||
+ // CHECK-FIXES: void f2(int n, const ABC& v2) { | ||
} |
@@ -4,4 +4,4 @@ void style_first_good | ||
void styleFirstBad(); | ||
-int thisIsMainLikeIg | ||
+int thisIsMainLikeIg |
@@ -4,4 +4,4 @@ void STYLE_SECOND_GOO | ||
void styleSecondBad() | ||
-int thisIsMainLikeNo | ||
+int thisIsMainLikeNo |
@@ -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( |
@@ -1,4 +1,4 @@ | ||
-// RUN: %check_clang_tid | ||
+// RUN: %check_clang_tid | ||
// p# = positive test | ||
// n# = negative test |
@@ -32,6 +32,7 @@ class A { | ||
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: method 'call_static_mem | ||
// CHECK-FIXES: {{^}} static int call_static_memb | ||
already_static() | ||
+ return 0; | ||
} | ||
int read_static() { |
@@ -547,6 +547,7 @@ struct_type GlobalTypedefTes | ||
// CHECK-FIXES: {{^}}struct_type | ||
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(ValueTy | ||
// CHECK-FIXES: {{^}}STATIC_MACR | ||
STATIC_MACRO void someFunc(const ValueType** p_a_v1, ValueType (*p_a_v2)()) {} | ||
// CHECK-FIXES: {{^}}STATIC_MACR | ||
-STATIC_MACRO ValueType someFunc() {} | ||
-// CHECK-FIXES: {{^}}STATIC_MACR | ||
+STATIC_MACRO ValueType someFunc() { return {}; } | ||
+// CHECK-FIXES: {{^}}STATIC_MACR | ||
STATIC_MACRO void someFunc(MyFunPt | ||
// CHECK-FIXES: {{^}}STATIC_MACR | ||
#undef STATIC_MACRO |
@@ -465,7 +465,7 @@ struct S { | ||
// CHECK-FIXES: S(bool a, bool b, bool c) : a(static_cast<in | ||
}; | ||
-bool f(S& s) { | ||
+void f(S& s) { | ||
functionTaking<b | ||
// CHECK-MESSAGES: :[[@LINE-1]]:24: | ||
// CHECK-FIXES: functionTaking<b |
@@ -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: | ||
-// 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: | ||
-// 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: | ||
-// 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: | ||
-// CHECK-FIXES: Z &operator--(Z& /*unused*/, int) {} | ||
+// CHECK-FIXES: Z &operator--(Z& /*unused*/, int) { return the_z; } | ||
namespace testing { | ||
namespace internal { |
@@ -20,7 +20,7 @@ static int f(void); | ||
static int f(void); // f | ||
// CHECK-MESSAGES: :[[@LINE-1]]:12: | ||
// CHECK-FIXES: {{^}}// f{{$}} | ||
-static int f(void) {} | ||
+static int f(void) { return 0; } | ||
inline void g(void) {} | ||
@@ -38,7 +38,7 @@ static int f(); | ||
static int f(); // f | ||
// CHECK-MESSAGES: :[[@LINE-1]]:12: | ||
// CHECK-FIXES: {{^}}// f{{$}} | ||
-static int f() {} | ||
+static int f() { return 0; } | ||
// Original check crashed for the code below. | ||
namespace std { |
@@ -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-sta |
@@ -382,7 +382,7 @@ enum opcode { Foo, | ||
Bar }; | ||
static value *SimplifyRightSh | ||
opcode Opcode, value *Op0, value *Op1, bool isExact, | ||
- const type1 &Q, unsigned MaxRecurse) {} | ||
+ const type1 &Q, unsigned MaxRecurse) { return nullptr; } | ||
static value *SimplifyLShrIns | ||
const type1 &Q, unsigned MaxRecurse) { | ||
if (value *V = SimplifyRightShi |
@@ -31,7 +31,7 @@ private: | ||
// CHECK-FIXES: _num2{}; | ||
}; | ||
-int should_use_empla | ||
+void should_use_empla | ||
v.push_back(Foo( | ||
// CHECK-FIXES: v.emplace_back() | ||
// CHECK-MESSAGES: warning: use emplace_back instead of push_back [hicpp-use-empla |
@@ -319,7 +319,12 @@ TEST(HTMLGenerat | ||
<a href="path/to/in | ||
P) | ||
</p> | ||
- <p>Defined at line 10 of file dir/test.cpp</p> | ||
+ <p> | ||
+ Defined at line | ||
+ <a href="https://ww | ||
+ of file | ||
+ <a href="https://ww | ||
+ </p> | ||
</div> | ||
<div id="sidebar-righ | ||
</main> |
@@ -136,6 +136,7 @@ Clang static analyzer | ||
| Balázs Benics | ||
| benicsbalazs\@gm | ||
+| balazs.benics\@s | ||
Compiler options | ||
~~~~~~~~~~~~~~~~ |
@@ -1410,6 +1410,9 @@ class CursorKind(BaseE | ||
# OpenMP scope directive. | ||
OMP_SCOPE_DIRECT | ||
+ # OpenMP stripe directive. | ||
+ OMP_STRIPE_DIREC | ||
+ | ||
# OpenACC Compute Construct. | ||
OPEN_ACC_COMPUTE | ||
@@ -190,8 +190,8 @@ foreach(target aarch64-unknown- | ||
set(RUNTIMES_${t | ||
# Enable FatLTO for Linux and baremetal runtimes | ||
- set(RUNTIMES_${t | ||
- set(RUNTIMES_${t | ||
+ set(RUNTIMES_${t | ||
+ set(RUNTIMES_${t | ||
# Use .build-id link. | ||
list(APPEND RUNTIME_BUILD_ID | ||
@@ -276,8 +276,8 @@ if(FUCHSIA_SDK) | ||
set(RUNTIMES_${t | ||
# Enable FatLTO for Fuchsia runtimes | ||
- set(RUNTIMES_${t | ||
- set(RUNTIMES_${t | ||
+ set(RUNTIMES_${t | ||
+ set(RUNTIMES_${t | ||
# Use .build-id link. | ||
list(APPEND RUNTIME_BUILD_ID | ||
@@ -378,8 +378,8 @@ foreach(target armv6m-none-eabi | ||
set(RUNTIMES_${t | ||
# Enable FatLTO for baremetal runtimes | ||
- set(RUNTIMES_${t | ||
- set(RUNTIMES_${t | ||
+ set(RUNTIMES_${t | ||
+ set(RUNTIMES_${t | ||
endforeach() | ||
foreach(target riscv32-unknown- | ||
@@ -433,8 +433,8 @@ foreach(target riscv32-unknown- | ||
set(RUNTIMES_${t | ||
# Enable FatLTO for baremetal runtimes | ||
- set(RUNTIMES_${t | ||
- set(RUNTIMES_${t | ||
+ set(RUNTIMES_${t | ||
+ set(RUNTIMES_${t | ||
endforeach() | ||
set(LLVM_BUILTIN |
@@ -29,6 +29,13 @@ endfunction() | ||
# cache file to CMake via -C. e.g. | ||
# | ||
# cmake -D LLVM_RELEASE_ENA | ||
+ | ||
+set (DEFAULT_PROJECT | ||
+# bolt only supports ELF, so only enable it for Linux. | ||
+if (${CMAKE_HOST_SY | ||
+ list(APPEND DEFAULT_PROJECTS | ||
+endif() | ||
+ | ||
set (DEFAULT_RUNTIME | ||
if (NOT WIN32) | ||
list(APPEND DEFAULT_RUNTIMES | ||
@@ -36,7 +43,7 @@ endif() | ||
set(LLVM_RELEASE | ||
set(LLVM_RELEASE | ||
set(LLVM_RELEASE | ||
-set(LLVM_RELEAS | ||
+set(LLVM_RELEAS | ||
# Note we don't need to add install here, since it is one of the pre-defined | ||
# steps. | ||
set(LLVM_RELEASE | ||
@@ -48,10 +55,8 @@ set(CLANG_ENABLE | ||
set(STAGE1_PROJE | ||
-# 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_RUNT | ||
+# Build all runtimes so we can statically link them into the stage2 compiler. | ||
+set(STAGE1_RUNT | ||
if (LLVM_RELEASE_EN | ||
list(APPEND STAGE1_PROJECTS "lld") | ||
@@ -90,9 +95,20 @@ else() | ||
set(CLANG_BOOTST | ||
endif() | ||
+if (LLVM_RELEASE_EN | ||
+ # 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= | ||
+ # step for the libcxx build to fail. CMAKE_INTERPROCE | ||
+ # enable ThinLTO, though. | ||
+ set(RUNTIMES_CMA | ||
+endif() | ||
+ | ||
# Stage 1 Common Config | ||
set(LLVM_ENABLE_ | ||
set(LLVM_ENABLE_ | ||
+set(LIBCXX_STAT | ||
# stage2-instrumen | ||
# Options that need to be set in both the instrumented stage (if we are doing | ||
@@ -102,10 +118,28 @@ set_instrument_a | ||
if (LLVM_RELEASE_EN | ||
set_instrument_a | ||
endif() | ||
+set_instrument_ | ||
+set_instrument_ | ||
+set(RELEASE_LIN | ||
+if(NOT ${CMAKE_HOST_SYS | ||
+ set(RELEASE_LINK | ||
+endif() | ||
+ | ||
+# Set flags for bolt | ||
+if (${CMAKE_HOST_SY | ||
+ set(RELEASE_LINK | ||
+endif() | ||
+ | ||
+set_instrument_ | ||
+set_instrument_ | ||
+set_instrument_ | ||
# Final Stage Config (stage2) | ||
set_final_stage_ | ||
set_final_stage_ | ||
+if (${CMAKE_HOST_SY | ||
+ set_final_stage_ | ||
+endif() | ||
set_final_stage_ | ||
set_final_stage_ | ||
@@ -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_ | ||
+ void alloc_buf(sized_ | ||
sbuf->buf = (int *)malloc(sizeof( | ||
sbuf->count = nelems; | ||
} | ||
// Transformed pseudo code: | ||
- void alloc_buf(sized_ | ||
+ void alloc_buf(sized_ | ||
// Materialize RHS values: | ||
int *tmp_ptr = (int *)malloc(sizeof( | ||
int tmp_count = nelems; | ||
@@ -959,7 +959,8 @@ that has the define. | ||
#if defined(__has_fe | ||
#define __counted_by(T) __attribute__((_ | ||
// ... 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-safet | ||
-`__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:`BoundsSafe | ||
-and please send your feedback to `Yeoul Na <mailto:yeoul_na |
@@ -134,7 +134,7 @@ same basic block and without side effect in between. | ||
int *__counted_by(co | ||
} sized_buf_t; | ||
- void alloc_buf(sized_ | ||
+ void alloc_buf(sized_ | ||
sbuf->buf = (int *)malloc(sizeof( | ||
sbuf->count = nelems; | ||
} |
@@ -2182,6 +2182,24 @@ the configuration (without a prefix: ``Auto``). | ||
aaaaaaaaaaaaaaaa | ||
} | ||
+.. _BinPackLongBrac | ||
+ | ||
+**BinPackLongBr | ||
+ If ``BinPackLongBra | ||
+ ``BinPackArgumen | ||
+ initializer list. | ||
+ | ||
+ .. code-block:: c++ | ||
+ | ||
+ BinPackLongBrace | ||
+ vector<int> x{ vector<int> x{1, 2, ..., | ||
+ 20, 21}; | ||
+ 1, | ||
+ 2, | ||
+ ..., | ||
+ 20, | ||
+ 21}; | ||
+ | ||
.. _BinPackParamete | ||
**BinPackParamet | ||
@@ -4764,15 +4782,24 @@ the configuration (without a prefix: ``Auto``). | ||
.. _Language: | ||
**Language** (``LanguageKind` | ||
- 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#. |
@@ -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 ``HLSLOutParamEx | ||
-``HLSLOutParamE | ||
-with two sub-expressions. | ||
+To represent this concept in Clang we introduce a new ``HLSLOutArgExpr | ||
+``HLSLOutArgExp | ||
-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 | ||
+* 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' | ||
|-ImplicitCastEx | ||
| `-DeclRefExpr 'void (int &)' lvalue Function 'Init' 'void (int &)' | ||
- |-HLSLOutParamEx | ||
- `-DeclRefExpr 'int' lvalue Var 'V' 'int' | ||
- | ||
-The ``HLSLOutParamEx | ||
-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 | ||
+ |-OpaqueValueExp | ||
+ | `-DeclRefExpr <col:10> 'int' lvalue Var 'V' 'int' | ||
+ |-OpaqueValueExp | ||
+ | `-ImplicitCastEx | ||
+ | `-OpaqueValueExp | ||
+ | `-DeclRefExpr <col:10> 'int' lvalue Var 'V' 'int' | ||
+ `-BinaryOperator | ||
+ |-OpaqueValueExp | ||
+ | `-DeclRefExpr <col:10> 'int' lvalue Var 'V' 'int' | ||
+ `-ImplicitCastEx | ||
+ `-OpaqueValueExp | ||
+ `-ImplicitCastEx | ||
+ `-OpaqueValueExp | ||
+ `-DeclRefExpr <col:10> 'int' lvalue Var 'V' 'int' | ||
+ | ||
+The ``HLSLOutArgExpr | ||
+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 ``HLSLOutParamEx | ||
+For this case the ``HLSLOutArgExpr | ||
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' | ||
|-ImplicitCastEx | ||
| `-DeclRefExpr 'void (int3 &)' lvalue Function 'inc_i32' 'void (int3 &)' | ||
- `-HLSLOutParamEx | ||
- |-ImplicitCastEx | ||
- | `-ImplicitCastEx | ||
- | `-OpaqueValueExp | ||
- `-ImplicitCastEx | ||
- `-ImplicitCastEx | ||
- `-DeclRefExpr 'float3' lvalue 'F' 'float3' | ||
- | ||
-In this formation the write-back casts are captured as the first sub-expression | ||
-and they cast from an ``OpaqueValueExp | ||
-``OpaqueValueEx | ||
-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 | ||
-aggregate lvalues. | ||
+ `-HLSLOutArgExpr | ||
+ |-OpaqueValueExp | ||
+ | `-DeclRefExpr <col:11> 'float3':'vector | ||
+ |-OpaqueValueExp | ||
+ | `-ImplicitCastEx | ||
+ | `-ImplicitCastEx | ||
+ | `-OpaqueValueExp | ||
+ | `-DeclRefExpr <col:11> 'float3':'vector | ||
+ `-BinaryOperator | ||
+ |-OpaqueValueExp | ||
+ | `-DeclRefExpr <col:11> 'float3':'vector | ||
+ `-ImplicitCastEx | ||
+ `-ImplicitCastEx | ||
+ `-OpaqueValueExp | ||
+ `-ImplicitCastEx | ||
+ `-ImplicitCastEx | ||
+ `-OpaqueValueExp | ||
+ `-DeclRefExpr <col:11> 'float3':'vector | ||
+ | ||
+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-initializat | ||
+ * For ``out`` arguments, emit an uninitialized temporary. | ||
+* Emit the call | ||
+* Emit the write-back BinaryOperator expression. |
@@ -1640,6 +1640,7 @@ Conditional ``explicit`` __cpp_conditiona | ||
``static operator()`` __cpp_static_cal | ||
Attributes on Lambda-Expressio | ||
Attributes on Structured Bindings __cpp_structured | ||
+Packs in Structured Bindings __cpp_structured | ||
Static assert with user-generated message __cpp_static_ass | ||
Pack Indexing __cpp_pack_index | ||
``= delete ("should have a reason");`` __cpp_deleted_fu | ||
@@ -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_t | ||
@@ -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_t | ||
if (prevSize == -1) | ||
return -1; | ||
@@ -2866,6 +2867,47 @@ etc.). | ||
Query for this feature with ``__has_builtin( | ||
+``__builtin_ass | ||
+--------------- | ||
+ | ||
+``__builtin_ass | ||
+knowledge that the pointer argument P is dereferenceable up to at least the | ||
+specified number of bytes. | ||
+ | ||
+**Syntax**: | ||
+ | ||
+.. code-block:: c++ | ||
+ | ||
+ __builtin_assume | ||
+ | ||
+**Example of Use**: | ||
+ | ||
+.. code-block:: c++ | ||
+ | ||
+ int foo(int *x, int y) { | ||
+ __builtin_assume | ||
+ int z = 0; | ||
+ if (y == 1) { | ||
+ // The optimizer may execute the load of x unconditionally due to | ||
+ // __builtin_assume | ||
+ // 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_offs | ||
---------------- |
@@ -364,6 +364,8 @@ implementation. | ||
+=============== | ||
| free-agent threads | :none:`unclaimed | ||
+--------------- | ||
+| threadset clause | :`worked on` | :none:`unclaimed | ||
++-------------- | ||
| Recording of task graphs | :none:`unclaimed | ||
+--------------- | ||
| Parallel inductions | :none:`unclaimed | ||
@@ -372,6 +374,8 @@ implementation. | ||
+--------------- | ||
| Loop transformation constructs | :none:`unclaimed | ||
+--------------- | ||
+| loop stripe transformation | :good:`done` | https://github.c | ||
++-------------- | ||
| work distribute construct | :none:`unclaimed | ||
+--------------- | ||
| task_iteration | :none:`unclaimed | ||
@@ -410,13 +414,13 @@ implementation. | ||
+--------------- | ||
| Extensions to interop construct | :none:`unclaimed | ||
+--------------- | ||
-| no_openmp_constr | ||
+| no_openmp_constr | ||
+--------------- | ||
| safe_sync and progress with identifier and API | :none:`unclaimed | ||
+--------------- | ||
-| OpenMP directives in concurrent loop regions | :none:`unclaimed | ||
+| OpenMP directives in concurrent loop regions | :good:`done` | :none:`unclaimed | ||
+--------------- | ||
-| atomics constructs on concurrent loop regions | :none:`unclaimed | ||
+| atomics constructs on concurrent loop regions | :good:`done` | :none:`unclaimed | ||
+--------------- | ||
| Loop construct with DO CONCURRENT | :none:`unclaimed | ||
+--------------- | ||
@@ -454,9 +458,7 @@ implementation. | ||
+--------------- | ||
| map-type modifiers in arbitrary position | :none:`unclaimed | ||
+--------------- | ||
-| atomic constructs in loop region | :none:`unclaimed | ||
-+-------------- | ||
-| Lift nesting restriction on concurrent loop | :none:`unclaimed | ||
+| Lift nesting restriction on concurrent loop | :good:`done` | :none:`unclaimed | ||
+--------------- | ||
| priority clause for target constructs | :none:`unclaimed | ||
+--------------- |
@@ -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.li | ||
+ | ||
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-comprehensiv | ||
New Compiler Flags | ||
---------------- | ||
+- New option ``-fprofile-cont | ||
+ The feature has `existed <https://clang.l | ||
+ 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_ | ||
+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__( | ||
+ ``__attribute__( | ||
+ 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 | ||
+ ``format`` attribute. ``format_matches | ||
+ 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(con | ||
+ fprintf(stderr, fmt, status_code, status_string); | ||
+ // ^ warning: format string is not a string literal [-Wformat-nonlit | ||
+ } | ||
+ | ||
+ void stuff(void) { | ||
+ print_status("%s | ||
+ // order of %s and %x is swapped but there is no diagnostic | ||
+ } | ||
+ | ||
+ Before the introducion of ``format_matches | ||
+ at compile-time. ``format_matches | ||
+ | ||
+ .. code-block:: c | ||
+ | ||
+ __attribute__((f | ||
+ void print_status(con | ||
+ fprintf(stderr, fmt, status_code, status_string); | ||
+ // ^ `fmt` verified as if it was "%x %s" here; no longer triggers | ||
+ // -Wformat-nonlite | ||
+ } | ||
+ | ||
+ void stuff(void) { | ||
+ print_status("%s | ||
+ // 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 | ||
+ 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-objec | ||
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-buffe | ||
+ about unsafe libc function calls. Those new warnings are emitted | ||
+ under the subgroup ``-Wunsafe-buffe | ||
+- Diagnostics on chained comparisons (``a < b < c``) are now an error by default. This can be disabled with | ||
+ ``-Wno-error=par | ||
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-init | ||
+- Clang no longer crashes when a coroutine is declared ``[[noreturn]]`` | ||
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- | ||
+ | ||
CUDA/HIP Language Changes | ||
^^^^^^^^^^^^^^^^ | ||
@@ -236,10 +315,19 @@ clang-format | ||
------------ | ||
- Adds ``BreakBeforeTem | ||
+- Adds ``BinPackLongBra | ||
+ 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_const | ||
+- Added support for 'omp stripe' directive. | ||
Improvements | ||
^^^^^^^^^^^^ |
@@ -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 |
@@ -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-namesp | ||
across the program. | ||
@@ -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= | ||
+ | ||
+ Fusion can be controlled with the ``FP_CONTRACT`` and ``clang fp contract`` | ||
+ pragmas. Please note that pragmas will be ingored with | ||
+ ``-ffp-contract= | ||
+ 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-pra | ||
+ * ``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-pra | ||
+ (default for HIP) | ||
.. option:: -f[no-]honor-inf | ||
@@ -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_FIL | ||
+ 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 <SourceBasedCode | ||
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` | ||
by the target, or ``single`` otherwise. | ||
+.. option:: -fprofile-contin | ||
+ | ||
+ 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-genera | ||
+ | ||
+ Running ``./code`` will collect the profile and write it to the | ||
+ ``default_xxxx.p | ||
+ does not call ``exit()``, in continuous mode the profile collected up to the | ||
+ point of termination will be available in ``default_xxxx.p | ||
+ the non-continuous mode, no profile file is generated. | ||
+ | ||
.. option:: -ftemporal-profi | ||
Enables the temporal profiling extension for IRPGO to improve startup time by |
@@ -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.Un | ||
+""""""""""""""" | ||
Bulk data, 316 bytes | ||
+ | ||
+The rules of when to use and not to use RetainPtr are same as alpha.webkit.Unc | ||
+ | ||
+These are examples of cases that we consider safe: | ||
+ | ||
+ .. code-block:: cpp | ||
+ | ||
+ void foo1() { | ||
+ RetainPtr<NSObje | ||
+ // The scope of unretained is EMBEDDED in the scope of retained. | ||
+ { | ||
+ NSObject* unretained = retained.get(); // ok | ||
+ } | ||
+ } | ||
+ | ||
+ void foo2(RetainPtr<N | ||
+ NSObject* unretained = retained_param.g | ||
+ } | ||
+ | ||
+ void FooClass::foo_me | ||
+ 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_unretaine | ||
+ void foo2() { | ||
+ NSObject* unretained = global_unretaine | ||
+ } | ||
+ | ||
+ void foo3() { | ||
+ RetainPtr<NSObje | ||
+ // The scope of unretained is not EMBEDDED in the scope of retained. | ||
+ NSObject* unretained = retained.get(); // warn | ||
+ } | ||
+ | ||
Debug Checkers | ||
--------------- | ||
@@ -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=f | ||
You can explore the generated JSON file in a Chromium browser using the ``chrome://traci | ||
or using `speedscope <https://speedsc | ||
@@ -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-constr | ||
- -setup-static-an | ||
- -verify ./clang/test/Ana | ||
+ clang -cc1 -analyze -verify clang/test/Analy | ||
+ -analyzer-checke | ||
-ftime-trace=tra | ||
.. image:: ../images/speeds | ||
@@ -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-funct | ||
+ | ||
+ | ||
+Performance analysis using ``perf`` | ||
+=============== | ||
+ | ||
+`Perf <https://perfwik | ||
+It's easy to start profiling, you only have 2 prerequisites. | ||
+Build with ``-fno-omit-fram | ||
+You can use release builds, but probably the easiest is to set the ``CMAKE_BUILD_TY | ||
+along with ``CMAKE_CXX_FLAG | ||
+Here is how to `get started <https://llvm.or | ||
+ | ||
+.. 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/Analy | ||
+ -analyzer-checke | ||
+ | ||
+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. | ||
+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.bre | ||
+ | ||
+ | ||
+.. code-block:: bash | ||
+ :caption: Converting the ``perf`` profile into a Flamegraph, then opening it in Firefox. | ||
+ | ||
+ perf script | /path/to/FlameGr | ||
+ /path/to/FlameGr | ||
+ firefox perf.svg | ||
+ | ||
+.. image:: ../images/flameg | ||
+ | ||
+ | ||
+Performance analysis using ``uftrace`` | ||
+=============== | ||
+ | ||
+`uftrace <https://github. | ||
+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`` | ||
+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-f | ||
+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/Analy | ||
+ -analyzer-checke | ||
+ uftrace dump --filter=".*::An | ||
+ | ||
+.. image:: ../images/uftrac | ||
+ | ||
+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` | ||
+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. | ||
+ | ||
+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` | ||
+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``. |
@@ -2158,6 +2158,10 @@ enum CXCursorKind { | ||
*/ | ||
CXCursor_OMPAssu | ||
+ /** OpenMP assume directive. | ||
+ */ | ||
+ CXCursor_OMPStri | ||
+ | ||
/** OpenACC Compute Construct. | ||
*/ | ||
CXCursor_OpenACC |
@@ -287,8 +287,8 @@ class ASTContext : public RefCountedBase<A | ||
/// This is lazily created. This is intentionally not serialized. | ||
mutable llvm::DenseMap<c | ||
ASTRecordLayouts | ||
- mutable llvm::DenseMap<c | ||
- ObjCLayouts; | ||
+ mutable llvm::DenseMap<c | ||
+ ObjCLayouts; | ||
/// A cache from types to size and alignment information. | ||
using TypeInfoMap = llvm::DenseMap<c | ||
@@ -500,6 +500,11 @@ class ASTContext : public RefCountedBase<A | ||
static constexpr unsigned GeneralTypesLog2 | ||
static constexpr unsigned FunctionProtoTyp | ||
+ /// A mapping from an ObjC class to its subclasses. | ||
+ llvm::DenseMap<c | ||
+ SmallVector<cons | ||
+ ObjCSubClasses; | ||
+ | ||
ASTContext &this_() { return *this; } | ||
public: | ||
@@ -1733,6 +1738,47 @@ public: | ||
unsigned NumPositiveBits, | ||
QualType &BestPromotionTy | ||
+ /// Determine whether the given integral value is representable within | ||
+ /// the given type T. | ||
+ bool isRepresentableI | ||
+ | ||
+ /// Compute NumNegativeBits and NumPositiveBits for an enum based on | ||
+ /// the constant values of its enumerators. | ||
+ template <typename RangeT> | ||
+ bool computeEnumBits( | ||
+ unsigned &NumPositiveBits | ||
+ NumNegativeBits = 0; | ||
+ NumPositiveBits = 0; | ||
+ bool MembersRepresent | ||
+ for (auto *Elem : EnumConstants) { | ||
+ EnumConstantDecl | ||
+ if (!ECD) | ||
+ continue; // Already issued a diagnostic. | ||
+ | ||
+ llvm::APSInt InitVal = ECD->getInitVal( | ||
+ if (InitVal.isUnsig | ||
+ // 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.getActiv | ||
+ NumPositiveBits = std::max({NumPos | ||
+ } else { | ||
+ NumNegativeBits = | ||
+ std::max(NumNega | ||
+ } | ||
+ | ||
+ MembersRepresent | ||
+ } | ||
+ | ||
+ // 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 (!NumPositiveBit | ||
+ NumPositiveBits = 1; | ||
+ | ||
+ return MembersRepresent | ||
+ } | ||
+ | ||
QualType | ||
getUnresolvedUsi | ||
@@ -2630,13 +2676,6 @@ public: | ||
void DumpRecordLayout | ||
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 & | ||
- getASTObjCImplem | ||
- | ||
/// 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 lookupFieldBitOf | ||
- const ObjCImplementati | ||
const ObjCIvarDecl *Ivar) const; | ||
/// Find the 'this' offset for the member path in a pointer-to-membe | ||
@@ -3133,7 +3171,12 @@ public: | ||
bool &CanUseFirst, bool &CanUseSecond, | ||
SmallVectorImpl< | ||
- void ResetObjCLayout( | ||
+ void ResetObjCLayout( | ||
+ | ||
+ void addObjCSubClass( | ||
+ const ObjCInterfaceDec | ||
+ ObjCSubClasses[D | ||
+ } | ||
//===----------- | ||
// Integer Predicates | ||
@@ -3523,9 +3566,7 @@ private: | ||
friend class DeclarationNameT | ||
friend class DeclContext; | ||
- const ASTRecordLayout & | ||
- getObjCLayout(co | ||
- const ObjCImplementati | ||
+ const ASTRecordLayout &getObjCLayout(c | ||
/// A set of deallocations that should be performed when the | ||
/// ASTContext is destroyed. |
@@ -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; | ||
+ /// HasValidPackoffs | ||
+ // on all declarations | ||
+ bool HasValidPackoffs | ||
+ // LayoutStruct - Layout struct for the buffer | ||
+ CXXRecordDecl *LayoutStruct; | ||
HLSLBufferDecl(D | ||
IdentifierInfo *ID, SourceLocation IDLoc, | ||
@@ -5059,6 +5064,10 @@ public: | ||
SourceLocation getRBraceLoc() const { return RBraceLoc; } | ||
void setRBraceLoc(Sou | ||
bool isCBuffer() const { return IsCBuffer; } | ||
+ void setHasValidPacko | ||
+ bool hasValidPackoffs | ||
+ const CXXRecordDecl *getLayoutStruct | ||
+ void addLayoutStruct( | ||
// Implement isa/cast/dyncast | ||
static bool classof(const Decl *D) { return classofKind(D->g |
@@ -492,7 +492,7 @@ public: | ||
/// perform non-Decl specific checks based on the object's type and strict | ||
/// flex array level. | ||
static bool isFlexibleArrayM | ||
- ASTContext &Context, const Decl *D, QualType Ty, | ||
+ const ASTContext &Context, const Decl *D, QualType Ty, | ||
LangOptions::Str | ||
bool IgnoreTemplateOr | ||
@@ -1720,14 +1720,6 @@ public: | ||
/// static analysis, or similar. | ||
bool hasMemberName(De | ||
- /// 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<cons | ||
- lookupDependentN | ||
- llvm::function_r | ||
- | ||
/// 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<E | ||
+ // Get the array of nested BindingDecls when the binding represents a pack. | ||
+ llvm::ArrayRef<B | ||
/// Get the decomposition declaration that this binding represents a | ||
/// decomposition of. | ||
@@ -4246,10 +4238,8 @@ class DecompositionDec | ||
for (auto *B : Bindings) { | ||
B->setDecomposed | ||
if (B->isParameterP | ||
- for (Expr *E : B->getBindingPac | ||
- auto *DRE = cast<DeclRefExpr | ||
- auto *NestedB = cast<BindingDecl | ||
- NestedB->setDeco | ||
+ for (BindingDecl *NestedBD : B->getBindingPac | ||
+ NestedBD->setDec | ||
} | ||
} | ||
} | ||
@@ -4278,25 +4268,21 @@ public: | ||
// Provide a flattened range to visit each binding. | ||
auto flat_bindings() const { | ||
llvm::ArrayRef<B | ||
- llvm::ArrayRef<E | ||
+ llvm::ArrayRef<B | ||
// Split the bindings into subranges split by the pack. | ||
- auto S1 = Bindings.take_un | ||
+ llvm::ArrayRef<B | ||
[](BindingDecl *BD) { return BD->isParameterP | ||
- Bindings = Bindings.drop_fr | ||
+ Bindings = Bindings.drop_fr | ||
if (!Bindings.empty | ||
- PackExprs = Bindings.front() | ||
+ PackBindings = Bindings.front() | ||
Bindings = Bindings.drop_fr | ||
} | ||
- auto S2 = llvm::map_range( | ||
- auto *DRE = cast<DeclRefExpr | ||
- return cast<BindingDecl | ||
- }); | ||
- | ||
- return llvm::concat<Bin | ||
- std::move(Bindin | ||
+ return llvm::concat<Bin | ||
+ std::move(PackBi | ||
+ std::move(Bindin | ||
} | ||
void printName(raw_os |
@@ -1960,6 +1960,8 @@ public: | ||
bool hasStrictPackMat | ||
+ void setStrictPackMat | ||
+ | ||
/// Get the point of instantiation (if any), or null if none. | ||
SourceLocation getPointOfInstan | ||
return PointOfInstantia |
@@ -542,7 +542,7 @@ public: | ||
/// When IgnoreTemplateOr | ||
/// resulting from the substitution of a macro or a template as special sizes. | ||
bool isFlexibleArrayM | ||
- ASTContext &Context, | ||
+ const ASTContext &Context, | ||
LangOptions::Str | ||
bool IgnoreTemplateOr | ||
@@ -4579,25 +4579,97 @@ public: | ||
/// ConvertVectorExp | ||
/// This AST node provides support for converting a vector type to another | ||
/// vector type of the same arity. | ||
-class ConvertVectorExp | ||
+class ConvertVectorExp | ||
+ : public Expr, | ||
+ private llvm::TrailingOb | ||
private: | ||
Stmt *SrcExpr; | ||
TypeSourceInfo *TInfo; | ||
SourceLocation BuiltinLoc, RParenLoc; | ||
+ friend TrailingObjects; | ||
friend class ASTReader; | ||
friend class ASTStmtReader; | ||
- explicit ConvertVectorExp | ||
+ explicit ConvertVectorExp | ||
+ : Expr(ConvertVect | ||
+ ConvertVectorExp | ||
+ } | ||
-public: | ||
ConvertVectorExp | ||
ExprValueKind VK, ExprObjectKind OK, | ||
- SourceLocation BuiltinLoc, SourceLocation RParenLoc) | ||
+ SourceLocation BuiltinLoc, SourceLocation RParenLoc, | ||
+ FPOptionsOverrid | ||
: Expr(ConvertVect | ||
TInfo(TI), BuiltinLoc(Built | ||
+ ConvertVectorExp | ||
+ if (hasStoredFPFeat | ||
+ setStoredFPFeatu | ||
setDependence(co | ||
} | ||
+ size_t numTrailingObjec | ||
+ return ConvertVectorExp | ||
+ } | ||
+ | ||
+ FPOptionsOverrid | ||
+ assert(ConvertVe | ||
+ return *getTrailingObje | ||
+ } | ||
+ | ||
+ const FPOptionsOverrid | ||
+ assert(ConvertVe | ||
+ return *getTrailingObje | ||
+ } | ||
+ | ||
+public: | ||
+ static ConvertVectorExp | ||
+ bool hasFPFeatures); | ||
+ | ||
+ static ConvertVectorExp | ||
+ TypeSourceInfo *TI, QualType DstType, | ||
+ ExprValueKind VK, ExprObjectKind OK, | ||
+ SourceLocation BuiltinLoc, | ||
+ SourceLocation RParenLoc, | ||
+ FPOptionsOverrid | ||
+ | ||
+ /// Get the FP contractibility status of this operator. Only meaningful for | ||
+ /// operations on floating point types. | ||
+ bool isFPContractable | ||
+ return getFPFeaturesInE | ||
+ } | ||
+ | ||
+ /// Is FPFeatures in Trailing Storage? | ||
+ bool hasStoredFPFeatu | ||
+ return ConvertVectorExp | ||
+ } | ||
+ | ||
+ /// Get FPFeatures from trailing storage. | ||
+ FPOptionsOverrid | ||
+ return getTrailingFPFea | ||
+ } | ||
+ | ||
+ /// Get the store FPOptionsOverrid | ||
+ FPOptionsOverrid | ||
+ return hasStoredFPFeatu | ||
+ } | ||
+ | ||
+ /// Set FPFeatures in trailing storage, used by Serialization & ASTImporter. | ||
+ void setStoredFPFeatu | ||
+ | ||
+ /// Get the FP features status of this operator. Only meaningful for | ||
+ /// operations on floating point types. | ||
+ FPOptions getFPFeaturesInE | ||
+ if (ConvertVectorEx | ||
+ return getStoredFPFeatu | ||
+ return FPOptions::defau | ||
+ } | ||
+ | ||
+ FPOptionsOverrid | ||
+ if (ConvertVectorEx | ||
+ return getStoredFPFeatu | ||
+ return FPOptionsOverrid | ||
+ } | ||
+ | ||
/// getSrcExpr - Return the Expr to be converted. | ||
Expr *getSrcExpr() const { return cast<Expr>(SrcEx | ||
@@ -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 FunctionParmPack | ||
: public Expr, | ||
- private llvm::TrailingOb | ||
+ private llvm::TrailingOb | ||
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 FunctionParmPack | ||
/// The number of expansions of this pack. | ||
unsigned NumParameters; | ||
- FunctionParmPack | ||
- SourceLocation NameLoc, unsigned NumParams, | ||
- VarDecl *const *Params); | ||
+ FunctionParmPack | ||
+ unsigned NumParams, ValueDecl *const *Params); | ||
public: | ||
static FunctionParmPack | ||
- VarDecl *ParamPack, | ||
+ ValueDecl *ParamPack, | ||
SourceLocation NameLoc, | ||
- ArrayRef<VarDecl | ||
+ ArrayRef<ValueDe | ||
static FunctionParmPack | ||
unsigned NumParams); | ||
/// Get the parameter pack which this expression refers to. | ||
- VarDecl *getParameterPac | ||
+ ValueDecl *getParameterPac | ||
/// Get the location of the parameter pack. | ||
SourceLocation getParameterPack | ||
/// Iterators over the parameters which the parameter pack expanded | ||
/// into. | ||
- using iterator = VarDecl * const *; | ||
- iterator begin() const { return getTrailingObjec | ||
+ using iterator = ValueDecl *const *; | ||
+ iterator begin() const { return getTrailingObjec | ||
iterator end() const { return begin() + NumParameters; } | ||
/// Get the number of parameters in this parameter pack. | ||
unsigned getNumExpansions | ||
/// Get an expansion of the parameter pack by index. | ||
- VarDecl *getExpansion(un | ||
+ ValueDecl *getExpansion(un | ||
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 ResolvedUnexpand | ||
- : public Expr, | ||
- private llvm::TrailingOb | ||
- friend class ASTStmtReader; | ||
- friend class ASTStmtWriter; | ||
- friend TrailingObjects; | ||
- | ||
- SourceLocation BeginLoc; | ||
- unsigned NumExprs; | ||
- | ||
- ResolvedUnexpand | ||
- | ||
-public: | ||
- static ResolvedUnexpand | ||
- unsigned NumExprs); | ||
- static ResolvedUnexpand | ||
- Create(ASTContex | ||
- static ResolvedUnexpand | ||
- SourceLocation BeginLoc, QualType T, | ||
- llvm::ArrayRef<E | ||
- | ||
- unsigned getNumExprs() const { return NumExprs; } | ||
- | ||
- llvm::MutableArr | ||
- return {getTrailingObje | ||
- } | ||
- | ||
- llvm::ArrayRef<E | ||
- return {getTrailingObje | ||
- } | ||
- | ||
- Expr *getExpansion(un | ||
- Expr *getExpansion(un | ||
- | ||
- // Iterators | ||
- child_range children() { | ||
- return child_range((Stm | ||
- (Stmt **)getTrailingOb | ||
- } | ||
- | ||
- SourceLocation getBeginLoc() const LLVM_READONLY { return BeginLoc; } | ||
- SourceLocation getEndLoc() const LLVM_READONLY { return BeginLoc; } | ||
- | ||
- // Returns the resolved pack of a decl or nullptr | ||
- static ResolvedUnexpand | ||
- | ||
- static bool classof(const Stmt *T) { | ||
- return T->getStmtClass( | ||
- } | ||
-}; | ||
- | ||
} // namespace clang | ||
#endif // LLVM_CLANG_AST_E |
@@ -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(ASTC | ||
+ MatchKind matchesArgType(A | ||
QualType getRepresentativ | ||
@@ -345,6 +345,7 @@ public: | ||
void VisitDeclaration | ||
void VisitNullPtrTemp | ||
void VisitIntegralTem | ||
+ void VisitStructuralV | ||
void VisitTemplateTem | ||
void VisitTemplateExp | ||
void VisitExpressionT |
@@ -22,32 +22,29 @@ | ||
#include <optional> | ||
namespace llvm { | ||
- class raw_ostream; | ||
+class raw_ostream; | ||
} | ||
namespace clang { | ||
- class ASTContext; | ||
- class BlockDecl; | ||
- class CXXConstructorDe | ||
- class CXXDestructorDec | ||
- class CXXMethodDecl; | ||
- class FunctionDecl; | ||
- struct MethodVFTableLoc | ||
- class NamedDecl; | ||
- class ObjCMethodDecl; | ||
- class StringLiteral; | ||
- struct ThisAdjustment; | ||
- struct ThunkInfo; | ||
- class VarDecl; | ||
+class ASTContext; | ||
+class BlockDecl; | ||
+class CXXConstructorDe | ||
+class CXXDestructorDec | ||
+class CXXMethodDecl; | ||
+class FunctionDecl; | ||
+struct MethodVFTableLoc | ||
+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<c | ||
- llvm::DenseMap<c | ||
- llvm::DenseMap<c | ||
- llvm::DenseMap<c | ||
+ llvm::DenseMap<c | ||
+ llvm::DenseMap<c | ||
+ llvm::DenseMap<c | ||
+ llvm::DenseMap<c | ||
public: | ||
ManglerKind getKind() const { return Kind; } | ||
@@ -73,7 +70,7 @@ public: | ||
ManglerKind Kind, bool IsAux = false) | ||
: Context(Context) | ||
- virtual ~MangleContext() | ||
+ virtual ~MangleContext() | ||
ASTContext &getASTContext() | ||
@@ -82,10 +79,10 @@ public: | ||
virtual void startNewFunction | ||
unsigned getBlockId(const | ||
- llvm::DenseMap<c | ||
- = Local? LocalBlockIds : GlobalBlockIds; | ||
+ llvm::DenseMap<c | ||
+ Local ? LocalBlockIds : GlobalBlockIds; | ||
std::pair<llvm:: | ||
- Result = BlockIds.insert( | ||
+ Result = BlockIds.insert( | ||
return Result.first->se | ||
} | ||
@@ -125,7 +122,7 @@ public: | ||
return false; | ||
} | ||
- virtual void needsUniqueInter | ||
+ virtual void needsUniqueInter | ||
// FIXME: consider replacing raw_ostream & with something like SmallString &. | ||
void mangleName(Globa | ||
@@ -143,10 +140,9 @@ public: | ||
virtual void mangleCXXRTTINam | ||
bool NormalizeInteger | ||
virtual void mangleStringLite | ||
- virtual void mangleMSGuidDecl | ||
+ virtual void mangleMSGuidDecl | ||
- void mangleGlobalBloc | ||
- const NamedDecl *ID, | ||
+ void mangleGlobalBloc | ||
raw_ostream &Out); | ||
void mangleCtorBlock( | ||
const BlockDecl *BD, raw_ostream &Out); | ||
@@ -314,6 +310,6 @@ private: | ||
class Implementation; | ||
std::unique_ptr< | ||
}; | ||
-} | ||
+} // namespace clang | ||
#endif |
@@ -2423,6 +2423,28 @@ public: | ||
OMPNoOpenMPRouti | ||
}; | ||
+/// This represents the 'no_openmp_const | ||
+//// '#pragma omp assume' directive. | ||
+/// | ||
+/// \code | ||
+/// #pragma omp assume no_openmp_constr | ||
+/// \endcode | ||
+/// In this example directive '#pragma omp assume' has a 'no_openmp_const | ||
+/// clause. | ||
+class OMPNoOpenMPConst | ||
+ : public OMPNoChildClause | ||
+public: | ||
+ /// Build 'no_openmp_const | ||
+ /// | ||
+ /// \param StartLoc Starting location of the clause. | ||
+ /// \param EndLoc Ending location of the clause. | ||
+ OMPNoOpenMPConst | ||
+ : OMPNoChildClause | ||
+ | ||
+ /// Build an empty clause. | ||
+ OMPNoOpenMPConst | ||
+}; | ||
+ | ||
/// This represents the 'no_parallelism' | ||
/// directive. | ||
/// |
@@ -367,6 +367,12 @@ CAST_OPERATION(H | ||
// Non-decaying array RValue cast (HLSL only). | ||
CAST_OPERATION(H | ||
+// Aggregate by Value cast (HLSL only). | ||
+CAST_OPERATION( | ||
+ | ||
+// Splat cast for Aggregates (HLSL only). | ||
+CAST_OPERATION( | ||
+ | ||
//===- Binary Operations ---------------- | ||
// Operators listed in order of precedence. | ||
// Note that additions to this should also update the StmtVisitor class, |
@@ -2950,7 +2950,6 @@ DEF_TRAVERSE_STM | ||
DEF_TRAVERSE_STM | ||
DEF_TRAVERSE_STM | ||
DEF_TRAVERSE_STM | ||
-DEF_TRAVERSE_ST | ||
DEF_TRAVERSE_STM | ||
if (S->getLifetimeE | ||
@@ -3056,6 +3055,9 @@ DEF_TRAVERSE_STM | ||
DEF_TRAVERSE_STM | ||
{ TRY_TO(TraverseO | ||
+DEF_TRAVERSE_ST | ||
+ { TRY_TO(TraverseO | ||
+ | ||
DEF_TRAVERSE_STM | ||
{ TRY_TO(TraverseO | ||
@@ -3544,6 +3546,12 @@ bool RecursiveASTVisi | ||
return true; | ||
} | ||
+template <typename Derived> | ||
+bool RecursiveASTVisi | ||
+ OMPNoOpenMPConst | ||
+ return true; | ||
+} | ||
+ | ||
template <typename Derived> | ||
bool RecursiveASTVisi | ||
OMPNoParallelism |
@@ -114,8 +114,6 @@ protected: | ||
bool isFirst() const { | ||
return isa<KnownLatest> | ||
- // FIXME: 'template' is required on the next line due to an | ||
- // apparent clang bug. | ||
isa<Uninitialize | ||
} | ||
@@ -1215,6 +1215,20 @@ protected: | ||
SourceLocation Loc; | ||
}; | ||
+ class ConvertVectorExp | ||
+ friend class ConvertVectorExp | ||
+ | ||
+ LLVM_PREFERRED_T | ||
+ 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_T | ||
+ unsigned HasFPFeatures : 1; | ||
+ }; | ||
+ | ||
union { | ||
// Same order as in StmtNodes.td. | ||
// Statements | ||
@@ -1293,6 +1307,7 @@ protected: | ||
// Clang Extensions | ||
OpaqueValueExprB | ||
+ ConvertVectorExp | ||
}; | ||
public: |
@@ -994,7 +994,8 @@ public: | ||
static bool classof(const Stmt *T) { | ||
Stmt::StmtClass C = T->getStmtClass( | ||
return C == OMPTileDirective | ||
- C == OMPReverseDirect | ||
+ C == OMPReverseDirect | ||
+ C == OMPStripeDirecti | ||
} | ||
}; | ||
@@ -5560,7 +5561,7 @@ class OMPTileDirective | ||
: OMPLoopTransform | ||
llvm::omp::OMPD_ | ||
NumLoops) { | ||
- setNumGeneratedL | ||
+ setNumGeneratedL | ||
} | ||
void setPreInits(Stmt | ||
@@ -5621,6 +5622,81 @@ public: | ||
} | ||
}; | ||
+/// This represents the '#pragma omp stripe' loop transformation directive. | ||
+class OMPStripeDirecti | ||
+ friend class ASTStmtReader; | ||
+ friend class OMPExecutableDir | ||
+ | ||
+ /// Default list of offsets. | ||
+ enum { | ||
+ PreInitsOffset = 0, | ||
+ TransformedStmtO | ||
+ }; | ||
+ | ||
+ explicit OMPStripeDirecti | ||
+ unsigned NumLoops) | ||
+ : OMPLoopTransform | ||
+ llvm::omp::OMPD_ | ||
+ NumLoops) { | ||
+ setNumGeneratedL | ||
+ } | ||
+ | ||
+ void setPreInits(Stmt | ||
+ Data->getChildre | ||
+ } | ||
+ | ||
+ void setTransformedSt | ||
+ Data->getChildre | ||
+ } | ||
+ | ||
+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 OMPStripeDirecti | ||
+ Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, | ||
+ ArrayRef<OMPClau | ||
+ Stmt *TransformedStmt | ||
+ | ||
+ /// 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 OMPStripeDirecti | ||
+ CreateEmpty(cons | ||
+ /// 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 *getTransformedS | ||
+ return Data->getChildre | ||
+ } | ||
+ | ||
+ /// Return preinits statement. | ||
+ Stmt *getPreInits() const { return Data->getChildre | ||
+ | ||
+ static bool classof(const Stmt *T) { | ||
+ return T->getStmtClass( | ||
+ } | ||
+}; | ||
+ | ||
/// This represents the '#pragma omp unroll' loop transformation directive. | ||
/// | ||
/// \code |
@@ -249,6 +249,7 @@ public: | ||
void VisitDeclaration | ||
void VisitNullPtrTemp | ||
void VisitIntegralTem | ||
+ void VisitStructuralV | ||
void VisitTemplateTem | ||
void VisitTemplateExp | ||
void VisitExpressionT | ||
@@ -424,6 +425,7 @@ public: | ||
void VisitOpenACCAste | ||
void VisitEmbedExpr(c | ||
void VisitAtomicExpr( | ||
+ void VisitConvertVect | ||
}; | ||
} // namespace clang |
@@ -6266,8 +6266,8 @@ public: | ||
LLVM_PREFERRED_T | ||
uint8_t RawBuffer : 1; | ||
- Attributes(llvm: | ||
- bool RawBuffer) | ||
+ Attributes(llvm: | ||
+ bool RawBuffer = false) | ||
: ResourceClass(Re | ||
Attributes() : Attributes(llvm: |
@@ -2489,7 +2489,28 @@ extern const internal::Variad | ||
extern const internal::Variad | ||
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{-f | ||
+/// | ||
+/// The matcher \matcher{fixedPo | ||
+/// \match{1.25hk}, \match{0.25hr}, \match{0.35uhr}, | ||
+/// \match{1.45uhk}, | ||
+/// match \nomatch{12.5} and \nomatch{2} from the code block. | ||
extern const internal::Variad | ||
fixedPointLitera | ||
@@ -451,7 +451,7 @@ public: | ||
bool synthesizeBodies | ||
bool addCXXNewAllocat | ||
bool markElidedCXXCon | ||
- CodeInjector *injector = nullptr); | ||
+ std::unique_ptr< | ||
AnalysisDeclCont | ||
@@ -63,23 +63,6 @@ public: | ||
getOrCreateConst | ||
const CallExpr *CE, Environment &Env); | ||
- /// Creates or returns a previously created `StorageLocation | ||
- /// a const method call `obj.getFoo()` where `RecordLoc` is the | ||
- /// `RecordStorageLo | ||
- /// | ||
- /// 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 *getOrCreateCons | ||
- const RecordStorageLoc | ||
- Environment &Env, llvm::function_r | ||
- | ||
/// Creates or returns a previously created `StorageLocation | ||
/// a const method call `obj.getFoo()` where `RecordLoc` is the | ||
/// `RecordStorageLo | ||
@@ -208,29 +191,6 @@ Value *CachedConstAcce | ||
return Val; | ||
} | ||
-template <typename Base> | ||
-StorageLocation | ||
-CachedConstAcce | ||
- const RecordStorageLoc | ||
- Environment &Env, llvm::function_r | ||
- assert(!CE->getT | ||
- assert(CE->isGLV | ||
- auto &ObjMap = ConstMethodRetur | ||
- const FunctionDecl *DirectCallee = CE->getDirectCal | ||
- if (DirectCallee == nullptr) | ||
- return nullptr; | ||
- auto it = ObjMap.find(Dire | ||
- if (it != ObjMap.end()) | ||
- return it->second; | ||
- | ||
- StorageLocation &Loc = | ||
- Env.createStorag | ||
- Initialize(Loc); | ||
- | ||
- ObjMap.insert({D | ||
- return &Loc; | ||
-} | ||
- | ||
template <typename Base> | ||
StorageLocation & | ||
CachedConstAcces |
@@ -459,6 +459,7 @@ class TargetArch<list< | ||
} | ||
def TargetARM : TargetArch<["arm | ||
def TargetAArch64 : TargetArch<["aar | ||
+def TargetAMDGPU : TargetArch<["amd | ||
def TargetAnyArm : TargetArch<!list | ||
def TargetAVR : TargetArch<["avr | ||
def TargetBPF : TargetArch<["bpf | ||
@@ -469,7 +470,9 @@ def TargetMSP430 : TargetArch<["msp | ||
def TargetM68k : TargetArch<["m68 | ||
def TargetRISCV : TargetArch<["ris | ||
def TargetX86 : TargetArch<["x86 | ||
+def TargetX86_64 : TargetArch<["x86 | ||
def TargetAnyX86 : TargetArch<["x86 | ||
+def TargetSPIRV : TargetArch<["spi | ||
def TargetWebAssembl | ||
def TargetNVPTX : TargetArch<["nvp | ||
def TargetWindows : TargetSpec { | ||
@@ -1831,6 +1834,16 @@ def Format : InheritableAttr { | ||
let Documentation = [FormatDocs]; | ||
} | ||
+def FormatMatches : InheritableAttr { | ||
+ let Spellings = [GCC<"format_mat | ||
+ let Args = [IdentifierArgum | ||
+ let AdditionalMember | ||
+ StringLiteral *getFormatString | ||
+ }]; | ||
+ let Subjects = SubjectList<[Obj | ||
+ let Documentation = [FormatMatchesDo | ||
+} | ||
+ | ||
def FormatArg : InheritableAttr { | ||
let Spellings = [GCC<"format_arg | ||
let Args = [ParamIdxArgumen | ||
@@ -3124,11 +3137,20 @@ def PragmaClangTextS | ||
let Documentation = [InternalOnly]; | ||
} | ||
-def CodeModel : InheritableAttr, | ||
+// 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, | ||
+ TargetSpecificAt | ||
+ TargetLoongArch. | ||
+ TargetAMDGPU.Arc | ||
let Spellings = [GCC<"model">]; | ||
- let Args = [EnumArgument<"M | ||
- ["normal", "medium", "extreme"], ["Small", "Medium", "Large"], | ||
- /*opt=*/0, /*fake=*/0, /*isExternalType | ||
+ let Args = [EnumArgument< | ||
+ "Model", "llvm::CodeModel | ||
+ /*is_string=*/1, | ||
+ ["Small", "Small", "Medium", "Large", "Large"], | ||
+ /*opt=*/0, /*fake=*/0, /*isExternalType | ||
let Subjects = SubjectList<[Non | ||
let Documentation = [CodeModelDocs]; | ||
} |
@@ -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 | ||
+"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 | ||
+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 FormatMatchesDoc | ||
+ 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_temperatu | ||
+ static int wind_speed; | ||
+ | ||
+ void say_hi(const char *fmt) { | ||
+ printf(fmt, first_name, todays_temperatu | ||
+ // ^ 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-nonlite | ||
+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_matche | ||
+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_temperatu | ||
+ static int wind_speed; | ||
+ | ||
+ __attribute__((_ | ||
+ void say_hi(const char *fmt) { | ||
+ printf(fmt, first_name, todays_temperatu | ||
+ 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 | ||
+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-documentati | ||
+prefer the former when it provides a useful example of an expected format | ||
+string. | ||
+ | ||
+In the implementation of a function with the ``format_matches | ||
+format verification works as if the format string was identical to the one | ||
+specified in the attribute. | ||
+ | ||
+.. code-block:: c | ||
+ | ||
+ __attribute__((_ | ||
+ 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__((_ | ||
+ // ^~ | ||
+ } | ||
+ | ||
+ | ||
+At the call sites of functions with the ``format_matches | ||
+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-signe | ||
+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 | ||
+* 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-pedan | ||
+ control similar behavior in ``-Wformat``. | ||
+ | ||
+At this time, clang implements ``format_matches | ||
+``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_r | ||
"omp_no_parallel | ||
+ "omp_no_openmp_c | ||
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 |
@@ -408,7 +408,8 @@ public: | ||
unsigned getRequiredVecto | ||
- /// 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(u | ||
return ID >= (Builtin::FirstT | ||
} |
@@ -521,6 +521,12 @@ def TruncF16F128 : Builtin, F16F128MathTempl | ||
let Prototype = "T(T)"; | ||
} | ||
+def Sincospi : Builtin, FPMathTemplate { | ||
+ let Spellings = ["__builtin_sinc | ||
+ let Attributes = [FunctionWithBui | ||
+ let Prototype = "void(T, T*, T*)"; | ||
+} | ||
+ | ||
// Access to floating point environment. | ||
def BuiltinFltRounds | ||
let Spellings = ["__builtin_flt_ | ||
@@ -839,6 +845,12 @@ def BuiltinAssumeAli | ||
let Prototype = "void*(void const*, size_t, ...)"; | ||
} | ||
+def BuiltinAssumeDer | ||
+ let Spellings = ["__builtin_assu | ||
+ let Attributes = [NoThrow, Const]; | ||
+ let Prototype = "void(void const*, _Constant size_t)"; | ||
+} | ||
+ | ||
def BuiltinFree : Builtin { | ||
let Spellings = ["__builtin_free | ||
let Attributes = [FunctionWithBui | ||
@@ -4765,6 +4777,12 @@ def HLSLAll : LangBuiltin<"HLS | ||
let Prototype = "bool(...)"; | ||
} | ||
+def HLSLAnd : LangBuiltin<"HLS | ||
+ let Spellings = ["__builtin_hlsl | ||
+ let Attributes = [NoThrow, Const]; | ||
+ let Prototype = "void(...)"; | ||
+} | ||
+ | ||
def HLSLAny : LangBuiltin<"HLS | ||
let Spellings = ["__builtin_hlsl | ||
let Attributes = [NoThrow, Const]; |
@@ -21,12 +21,14 @@ class SM<string version, 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_syn | ||
def __nvvm_redux_syn | ||
def __nvvm_redux_syn | ||
def __nvvm_redux_syn | ||
+def __nvvm_redux_syn | ||
+def __nvvm_redux_syn | ||
+def __nvvm_redux_syn | ||
+def __nvvm_redux_syn | ||
+def __nvvm_redux_syn | ||
+def __nvvm_redux_syn | ||
+def __nvvm_redux_syn | ||
+def __nvvm_redux_syn | ||
// Membar | ||
@@ -1,21 +0,0 @@ | ||
[diff truncated] |